"""Pydantic 数据模型 —— API 请求/响应格式定义""" from pydantic import BaseModel, Field from typing import Optional, List from datetime import date, datetime # ──────────────────────────── 认证 ──────────────────────────── class LoginRequest(BaseModel): username: str password: str class Token(BaseModel): access_token: str token_type: str = "bearer" # ──────────────────────────── 用户 ──────────────────────────── class UserCreate(BaseModel): username: str password: str name: str phase_group: str # 前期/制作/后期 role: str = "成员" monthly_salary: float = 0 class UserUpdate(BaseModel): name: Optional[str] = None phase_group: Optional[str] = None role: Optional[str] = None monthly_salary: Optional[float] = None is_active: Optional[int] = None class UserOut(BaseModel): id: int username: str name: str phase_group: str role: str monthly_salary: float daily_cost: float is_active: int created_at: Optional[datetime] = None class Config: from_attributes = True # ──────────────────────────── 项目 ──────────────────────────── class ProjectCreate(BaseModel): name: str project_type: str leader_id: Optional[int] = None current_phase: str = "前期" episode_duration_minutes: float episode_count: int estimated_completion_date: Optional[date] = None contract_amount: Optional[float] = None class ProjectUpdate(BaseModel): name: Optional[str] = None project_type: Optional[str] = None status: Optional[str] = None leader_id: Optional[int] = None current_phase: Optional[str] = None episode_duration_minutes: Optional[float] = None episode_count: Optional[int] = None estimated_completion_date: Optional[date] = None actual_completion_date: Optional[date] = None contract_amount: Optional[float] = None class ProjectOut(BaseModel): id: int name: str project_type: str status: str leader_id: Optional[int] = None leader_name: Optional[str] = None current_phase: str episode_duration_minutes: float episode_count: int target_total_seconds: int estimated_completion_date: Optional[date] = None actual_completion_date: Optional[date] = None contract_amount: Optional[float] = None created_at: Optional[datetime] = None # 动态计算字段 total_submitted_seconds: Optional[float] = 0 progress_percent: Optional[float] = 0 waste_seconds: Optional[float] = 0 waste_rate: Optional[float] = 0 class Config: from_attributes = True # ──────────────────────────── 内容提交 ──────────────────────────── class SubmissionCreate(BaseModel): project_id: int project_phase: str work_type: str content_type: str duration_minutes: Optional[float] = 0 duration_seconds: Optional[float] = 0 hours_spent: Optional[float] = None submit_to: str description: Optional[str] = None submit_date: date class SubmissionUpdate(BaseModel): project_phase: Optional[str] = None work_type: Optional[str] = None content_type: Optional[str] = None duration_minutes: Optional[float] = None duration_seconds: Optional[float] = None hours_spent: Optional[float] = None submit_to: Optional[str] = None description: Optional[str] = None submit_date: Optional[date] = None change_reason: str # 修改必须填原因 class SubmissionOut(BaseModel): id: int user_id: int user_name: Optional[str] = None project_id: int project_name: Optional[str] = None project_phase: str work_type: str content_type: str duration_minutes: Optional[float] = 0 duration_seconds: Optional[float] = 0 total_seconds: float hours_spent: Optional[float] = None submit_to: str description: Optional[str] = None submit_date: date created_at: Optional[datetime] = None class Config: from_attributes = True # ──────────────────────────── AI 工具成本 ──────────────────────────── class AIToolCostCreate(BaseModel): tool_name: str subscription_period: str amount: float allocation_type: str project_id: Optional[int] = None record_date: date allocations: Optional[List[dict]] = None # [{project_id, percentage}] class AIToolCostOut(BaseModel): id: int tool_name: str subscription_period: str amount: float allocation_type: str project_id: Optional[int] = None recorded_by: int record_date: date created_at: Optional[datetime] = None class Config: from_attributes = True # ──────────────────────────── 外包成本 ──────────────────────────── class OutsourceCostCreate(BaseModel): project_id: int outsource_type: str episode_start: Optional[int] = None episode_end: Optional[int] = None amount: float record_date: date class OutsourceCostOut(BaseModel): id: int project_id: int outsource_type: str episode_start: Optional[int] = None episode_end: Optional[int] = None amount: float recorded_by: int record_date: date created_at: Optional[datetime] = None class Config: from_attributes = True # ──────────────────────────── 成本调整 ──────────────────────────── class CostOverrideCreate(BaseModel): user_id: int date: date project_id: int override_amount: float reason: Optional[str] = None # ──────────────────────────── 仪表盘 ──────────────────────────── class DashboardSummary(BaseModel): """全局仪表盘数据""" active_projects: int = 0 completed_projects: int = 0 monthly_labor_cost: float = 0 monthly_ai_tool_cost: float = 0 monthly_total_seconds: float = 0 avg_daily_seconds_per_person: float = 0 projects: List[dict] = [] waste_ranking: List[dict] = [] settled_projects: List[dict] = [] # ──────────────────────────── 项目结算 ──────────────────────────── class SettlementOut(BaseModel): """项目结算报告""" project_id: int project_name: str project_type: str labor_cost: float = 0 ai_tool_cost: float = 0 outsource_cost: float = 0 total_cost: float = 0 waste_seconds: float = 0 waste_rate: float = 0 test_waste_seconds: float = 0 overproduction_waste_seconds: float = 0 contract_amount: Optional[float] = None profit_loss: Optional[float] = None team_efficiency: List[dict] = []