Files
012-kaopeilian/backend/app/schemas/growth_path.py
yuliang_guo 973ce53bf3
All checks were successful
continuous-integration/drone/push Build is passing
feat: 完善成长路径画布设计器
后端:
- 添加 position_x, position_y 字段保存节点位置

前端:
- 支持从节点右侧圆点拖拽出箭头连接到其他课程
- 自动根据节点Y坐标识别所属阶段
- 保存并恢复节点位置,不再重置
- 阶段区域高亮显示
- 循环依赖检测

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-03 14:55:01 +08:00

229 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
成长路径相关 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="预计学习天数")
position_x: Optional[int] = Field(0, description="画布X坐标")
position_y: Optional[int] = Field(0, description="画布Y坐标")
# =====================================================
# 管理端 - 创建/更新
# =====================================================
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")