Files
012-kaopeilian/backend/app/models/growth_path.py
yuliang_guo 0b7c07eb7f
All checks were successful
continuous-integration/drone/push Build is passing
feat: 添加请求验证错误详细日志
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-01-31 10:03:54 +08:00

207 lines
6.0 KiB
Python

"""
成长路径相关数据库模型
"""
from enum import Enum
from typing import List, Optional
from datetime import datetime
from decimal import Decimal
from sqlalchemy import (
String,
Text,
Integer,
Boolean,
ForeignKey,
Enum as SQLEnum,
JSON,
DateTime,
DECIMAL,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import BaseModel, SoftDeleteMixin
class GrowthPathStatus(str, Enum):
"""成长路径学习状态"""
NOT_STARTED = "not_started" # 未开始
IN_PROGRESS = "in_progress" # 进行中
COMPLETED = "completed" # 已完成
class NodeStatus(str, Enum):
"""节点状态"""
LOCKED = "locked" # 锁定(前置未完成)
UNLOCKED = "unlocked" # 已解锁(可以开始)
IN_PROGRESS = "in_progress" # 学习中
COMPLETED = "completed" # 已完成
class GrowthPathNode(BaseModel, SoftDeleteMixin):
"""
成长路径节点表
每个节点对应一门课程
"""
__tablename__ = "growth_path_nodes"
# 关联
growth_path_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("growth_paths.id", ondelete="CASCADE"),
nullable=False,
comment="成长路径ID"
)
course_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("courses.id", ondelete="CASCADE"),
nullable=False,
comment="课程ID"
)
# 节点信息
stage_name: Mapped[Optional[str]] = mapped_column(
String(100), nullable=True, comment="所属阶段名称"
)
title: Mapped[str] = mapped_column(
String(200), nullable=False, comment="节点标题"
)
description: Mapped[Optional[str]] = mapped_column(
Text, nullable=True, comment="节点描述"
)
# 配置
order_num: Mapped[int] = mapped_column(
Integer, default=0, nullable=False, comment="排序顺序"
)
is_required: Mapped[bool] = mapped_column(
Boolean, default=True, nullable=False, comment="是否必修"
)
prerequisites: Mapped[Optional[List[int]]] = mapped_column(
JSON, nullable=True, comment="前置节点IDs"
)
estimated_days: Mapped[int] = mapped_column(
Integer, default=7, nullable=False, comment="预计学习天数"
)
# 关联关系
growth_path: Mapped["GrowthPath"] = relationship(
"GrowthPath", back_populates="nodes"
)
course: Mapped["Course"] = relationship("Course")
user_completions: Mapped[List["UserNodeCompletion"]] = relationship(
"UserNodeCompletion", back_populates="node"
)
class UserGrowthPathProgress(BaseModel):
"""
用户成长路径进度表
记录用户在某条成长路径上的整体进度
"""
__tablename__ = "user_growth_path_progress"
# 关联
user_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("users.id", ondelete="CASCADE"),
nullable=False,
comment="用户ID"
)
growth_path_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("growth_paths.id", ondelete="CASCADE"),
nullable=False,
comment="成长路径ID"
)
# 进度信息
current_node_id: Mapped[Optional[int]] = mapped_column(
Integer, nullable=True, comment="当前学习节点ID"
)
completed_node_ids: Mapped[Optional[List[int]]] = mapped_column(
JSON, nullable=True, comment="已完成节点IDs"
)
total_progress: Mapped[Decimal] = mapped_column(
DECIMAL(5, 2), default=0.00, nullable=False, comment="总进度百分比"
)
# 状态
status: Mapped[str] = mapped_column(
String(20),
default=GrowthPathStatus.NOT_STARTED.value,
nullable=False,
comment="状态"
)
# 时间记录
started_at: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True, comment="开始时间"
)
completed_at: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True, comment="完成时间"
)
last_activity_at: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True, comment="最后活动时间"
)
# 关联关系
user: Mapped["User"] = relationship("User")
growth_path: Mapped["GrowthPath"] = relationship("GrowthPath")
class UserNodeCompletion(BaseModel):
"""
用户节点完成记录表
详细记录用户在每个节点上的学习状态
"""
__tablename__ = "user_node_completions"
# 关联
user_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("users.id", ondelete="CASCADE"),
nullable=False,
comment="用户ID"
)
growth_path_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("growth_paths.id", ondelete="CASCADE"),
nullable=False,
comment="成长路径ID"
)
node_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("growth_path_nodes.id", ondelete="CASCADE"),
nullable=False,
comment="节点ID"
)
# 进度信息
course_progress: Mapped[Decimal] = mapped_column(
DECIMAL(5, 2), default=0.00, nullable=False, comment="课程学习进度"
)
status: Mapped[str] = mapped_column(
String(20),
default=NodeStatus.LOCKED.value,
nullable=False,
comment="状态"
)
# 时间记录
unlocked_at: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True, comment="解锁时间"
)
started_at: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True, comment="开始学习时间"
)
completed_at: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True, comment="完成时间"
)
# 关联关系
user: Mapped["User"] = relationship("User")
node: Mapped["GrowthPathNode"] = relationship(
"GrowthPathNode", back_populates="user_completions"
)
growth_path: Mapped["GrowthPath"] = relationship("GrowthPath")