""" 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