PMFlow/backend/app/schemas/skill.py

200 lines
5.7 KiB
Python

"""
PMFlow 人员能力图谱 - Pydantic 请求/响应模型
包含 SkillTag、UserSkillBinding、UserWorkload 及人员推荐相关模型。
"""
from datetime import datetime
from typing import List, Optional
from uuid import UUID
from pydantic import BaseModel, Field
# ============================================================================
# SkillTag 模型
# ============================================================================
class SkillTagCreate(BaseModel):
"""创建技能标签请求"""
name: str = Field(..., min_length=1, max_length=100, description="技能名称")
slug: str = Field(..., min_length=1, max_length=80, description="URL 安全标识")
category: Optional[str] = Field(None, max_length=50, description="分类")
description: Optional[str] = Field(None, description="描述")
sort_order: int = Field(default=0, ge=0, description="排序序号")
class SkillTagUpdate(BaseModel):
"""更新技能标签请求(系统标签不可更新)"""
name: Optional[str] = Field(None, min_length=1, max_length=100)
category: Optional[str] = Field(None, max_length=50)
description: Optional[str] = None
sort_order: Optional[int] = Field(None, ge=0)
class SkillTagResponse(BaseModel):
"""技能标签响应"""
id: str
tenant_id: int | str
name: str
slug: str
category: Optional[str] = None
description: Optional[str] = None
is_system: bool
sort_order: int
created_at: str
updated_at: str
class Config:
from_attributes = True
# ============================================================================
# UserSkillBinding 模型
# ============================================================================
class UserSkillBindingCreate(BaseModel):
"""创建用户技能绑定请求"""
user_id: UUID
skill_tag_id: UUID
proficiency: int = Field(default=50, ge=0, le=100, description="掌握程度 0-100")
level: str = Field(
default="intermediate",
description="熟练等级: beginner, intermediate, advanced, expert",
)
years_experience: Optional[float] = Field(None, ge=0, description="从业年限")
class UserSkillBindingUpdate(BaseModel):
"""更新用户技能绑定请求"""
proficiency: Optional[int] = Field(None, ge=0, le=100)
level: Optional[str] = None
years_experience: Optional[float] = Field(None, ge=0)
class UserSkillBindingBatchUpdate(BaseModel):
"""批量更新用户技能绑定请求"""
user_id: Optional[UUID] = None # 可选,由路径提供时省略
bindings: List[dict] = Field(
...,
description="绑定列表,每项含 skill_tag_id, proficiency, level, years_experience",
)
class UserSkillBindingResponse(BaseModel):
"""用户技能绑定响应"""
id: str
user_id: str
skill_tag_id: str
skill_name: str
proficiency: int
level: str
years_experience: Optional[float] = None
quality_score: Optional[float] = None # 0-100 质量分
efficiency_score: Optional[float] = None # 0-100 效率分
last_used_at: Optional[str] = None
created_at: str
updated_at: str
class Config:
from_attributes = True
# ============================================================================
# UserWorkload 模型
# ============================================================================
class UserWorkloadUpdate(BaseModel):
"""更新用户工作量请求(通常由系统自动调用)"""
user_id: UUID
total_tasks: Optional[int] = Field(None, ge=0)
completed_tasks: Optional[int] = Field(None, ge=0)
in_progress_tasks: Optional[int] = Field(None, ge=0)
estimated_hours: Optional[float] = Field(None, ge=0)
completed_hours: Optional[float] = Field(None, ge=0)
class UserWorkloadResponse(BaseModel):
"""用户工作量响应"""
id: str
user_id: str
user_name: str
total_tasks: int
completed_tasks: int
in_progress_tasks: int
estimated_hours: float
completed_hours: float
workload_rate: float
period_start: str
period_end: str
updated_at: str
class Config:
from_attributes = True
# ============================================================================
# 人员推荐模型
# ============================================================================
class PersonnelRecommendationItem(BaseModel):
"""人员推荐单项"""
user_id: str
user_name: str
match_score: float = Field(..., ge=0, le=100, description="匹配度评分")
reason: str = Field(..., description="推荐理由")
skill_match_count: int = Field(..., ge=0)
avg_proficiency: float = Field(..., ge=0, le=100)
workload_rate: float = Field(..., ge=0, le=100)
recommended_role: Optional[str] = None
class PersonnelRecommendationResponse(BaseModel):
"""人员推荐响应"""
task_id: Optional[str] = None
task_description: Optional[str] = None
required_skills: List[str] = []
recommendations: List[PersonnelRecommendationItem] = []
total_count: int = 0
# ============================================================================
# 技能历史任务
# ============================================================================
class SkillTaskHistoryItem(BaseModel):
"""用户某技能下的历史任务项"""
id: str
title: str
description: Optional[str] = None
status: Optional[str] = None
priority: Optional[str] = None
estimated_hours: Optional[float] = None
project_id: Optional[str] = None
truth_node_id: Optional[str] = None
updated_at: Optional[str] = None
class SkillTaskHistoryResponse(BaseModel):
"""技能历史任务列表响应"""
items: List[SkillTaskHistoryItem] = []
total: int = 0