feat: 实现成长路径功能
All checks were successful
continuous-integration/drone/push Build is passing

- 新增数据库表: growth_path_nodes, user_growth_path_progress, user_node_completions
- 新增 Model: GrowthPathNode, UserGrowthPathProgress, UserNodeCompletion
- 新增 Service: GrowthPathService(管理端CRUD、学员端进度追踪)
- 新增 API: 学员端获取成长路径、管理端CRUD
- 前端学员端从API动态加载成长路径数据
- 更新管理端API接口定义
This commit is contained in:
yuliang_guo
2026-01-30 15:37:14 +08:00
parent d44111e712
commit b4906c543b
11 changed files with 1816 additions and 154 deletions

View File

@@ -0,0 +1,224 @@
"""
成长路径相关 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")
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
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")