""" 成长路径相关 Schema """ from typing import List, Optional from datetime import datetime from decimal import Decimal from pydantic import BaseModel, Field # ===================================================== # 基础数据结构 # ===================================================== class StageConfig(BaseModel): """阶段配置""" name: str = Field(..., description="阶段名称") description: Optional[str] = Field(None, description="阶段描述") order: int = Field(0, description="排序") class NodeBase(BaseModel): """节点基础信息""" course_id: int = Field(..., description="课程ID") stage_name: Optional[str] = Field(None, description="所属阶段名称") title: str = Field(..., description="节点标题") description: Optional[str] = Field(None, description="节点描述") order_num: int = Field(0, description="排序顺序") is_required: bool = Field(True, description="是否必修") prerequisites: Optional[List[int]] = Field(None, description="前置节点IDs") estimated_days: int = Field(7, description="预计学习天数") # ===================================================== # 管理端 - 创建/更新 # ===================================================== class GrowthPathNodeCreate(NodeBase): """创建节点""" pass class GrowthPathNodeUpdate(BaseModel): """更新节点""" course_id: Optional[int] = None stage_name: Optional[str] = None title: Optional[str] = None description: Optional[str] = None order_num: Optional[int] = None is_required: Optional[bool] = None prerequisites: Optional[List[int]] = None estimated_days: Optional[int] = None class GrowthPathCreate(BaseModel): """创建成长路径""" name: str = Field(..., description="路径名称") description: Optional[str] = Field(None, description="路径描述") target_role: Optional[str] = Field(None, description="目标角色") position_id: Optional[int] = Field(None, description="关联岗位ID(兼容旧版)") position_ids: Optional[List[int]] = Field(None, description="关联岗位ID列表(支持多选)") stages: Optional[List[StageConfig]] = Field(None, description="阶段配置") estimated_duration_days: Optional[int] = Field(None, description="预计完成天数") is_active: bool = Field(True, description="是否启用") sort_order: int = Field(0, description="排序") nodes: Optional[List[GrowthPathNodeCreate]] = Field(None, description="节点列表") class GrowthPathUpdate(BaseModel): """更新成长路径""" name: Optional[str] = None description: Optional[str] = None target_role: Optional[str] = None position_id: Optional[int] = None position_ids: Optional[List[int]] = None stages: Optional[List[StageConfig]] = None estimated_duration_days: Optional[int] = None is_active: Optional[bool] = None sort_order: Optional[int] = None nodes: Optional[List[GrowthPathNodeCreate]] = None # 整体替换节点 # ===================================================== # 管理端 - 响应 # ===================================================== class GrowthPathNodeResponse(NodeBase): """节点响应""" id: int growth_path_id: int course_name: Optional[str] = None # 课程名称(关联查询) created_at: datetime updated_at: datetime class Config: from_attributes = True class GrowthPathResponse(BaseModel): """成长路径响应(管理端)""" id: int name: str description: Optional[str] = None target_role: Optional[str] = None position_id: Optional[int] = None position_name: Optional[str] = None # 岗位名称(关联查询) stages: Optional[List[StageConfig]] = None estimated_duration_days: Optional[int] = None is_active: bool sort_order: int nodes: List[GrowthPathNodeResponse] = [] node_count: int = 0 # 节点数量 created_at: datetime updated_at: datetime class Config: from_attributes = True class GrowthPathListResponse(BaseModel): """成长路径列表响应""" id: int name: str description: Optional[str] = None position_id: Optional[int] = None position_name: Optional[str] = None is_active: bool node_count: int = 0 estimated_duration_days: Optional[int] = None created_at: datetime class Config: from_attributes = True # ===================================================== # 学员端 - 响应 # ===================================================== class TraineeNodeResponse(BaseModel): """学员端节点响应(含进度状态)""" id: int course_id: int title: str description: Optional[str] = None stage_name: Optional[str] = None is_required: bool estimated_days: int order_num: int # 学员特有 status: str = Field(..., description="状态: locked/unlocked/in_progress/completed") progress: float = Field(0, description="课程学习进度 0-100") # 课程信息 course_name: Optional[str] = None course_cover: Optional[str] = None class Config: from_attributes = True class TraineeStageResponse(BaseModel): """学员端阶段响应""" name: str description: Optional[str] = None completed: int = Field(0, description="已完成节点数") total: int = Field(0, description="总节点数") nodes: List[TraineeNodeResponse] = [] class TraineeGrowthPathResponse(BaseModel): """学员端成长路径响应""" id: int name: str description: Optional[str] = None position_id: Optional[int] = None position_name: Optional[str] = None # 进度信息 total_progress: float = Field(0, description="总进度百分比") completed_nodes: int = Field(0, description="已完成节点数") total_nodes: int = Field(0, description="总节点数") status: str = Field("not_started", description="状态: not_started/in_progress/completed") # 时间信息 started_at: Optional[datetime] = None estimated_completion_days: Optional[int] = None # 阶段和节点 stages: List[TraineeStageResponse] = [] class Config: from_attributes = True # ===================================================== # 用户进度 # ===================================================== class UserGrowthPathProgressResponse(BaseModel): """用户成长路径进度响应""" id: int user_id: int growth_path_id: int growth_path_name: str current_node_id: Optional[int] = None current_node_title: Optional[str] = None completed_node_ids: List[int] = [] total_progress: float status: str started_at: Optional[datetime] = None completed_at: Optional[datetime] = None last_activity_at: Optional[datetime] = None class Config: from_attributes = True class StartGrowthPathRequest(BaseModel): """开始学习成长路径请求""" growth_path_id: int = Field(..., description="成长路径ID") class CompleteNodeRequest(BaseModel): """完成节点请求""" node_id: int = Field(..., description="节点ID")