""" Pydantic数据模式 """ from datetime import datetime from typing import Optional, List from pydantic import BaseModel, Field, ConfigDict from enum import Enum # ==================== 枚举类型 ==================== class DeptType(str, Enum): CLINICAL_SURGICAL = "clinical_surgical" CLINICAL_NONSURGICAL_WARD = "clinical_nonsurgical_ward" CLINICAL_NONSURGICAL_NOWARD = "clinical_nonsurgical_noward" MEDICAL_TECH = "medical_tech" MEDICAL_AUXILIARY = "medical_auxiliary" NURSING = "nursing" ADMIN = "admin" FINANCE = "finance" LOGISTICS = "logistics" class StaffStatus(str, Enum): ACTIVE = "active" LEAVE = "leave" RESIGNED = "resigned" RETIRED = "retired" class AssessmentStatus(str, Enum): DRAFT = "draft" SUBMITTED = "submitted" REVIEWED = "reviewed" FINALIZED = "finalized" REJECTED = "rejected" class IndicatorType(str, Enum): QUALITY = "quality" QUANTITY = "quantity" EFFICIENCY = "efficiency" SERVICE = "service" COST = "cost" # ==================== 通用响应 ==================== class ResponseBase(BaseModel): """通用响应基类""" code: int = Field(default=200, description="状态码") message: str = Field(default="success", description="消息") class PaginatedResponse(ResponseBase): """分页响应""" total: int = Field(description="总数") page: int = Field(description="当前页") page_size: int = Field(description="每页数量") # ==================== 科室相关 ==================== class DepartmentBase(BaseModel): """科室基础模式""" name: str = Field(..., max_length=100, description="科室名称") code: str = Field(..., max_length=20, description="科室编码") dept_type: DeptType = Field(..., description="科室类型") parent_id: Optional[int] = Field(None, description="上级科室ID") level: int = Field(default=1, ge=1, le=5, description="层级") sort_order: int = Field(default=0, description="排序") description: Optional[str] = Field(None, description="描述") class DepartmentCreate(DepartmentBase): """创建科室""" pass class DepartmentUpdate(BaseModel): """更新科室""" name: Optional[str] = Field(None, max_length=100) dept_type: Optional[DeptType] = None parent_id: Optional[int] = None sort_order: Optional[int] = None is_active: Optional[bool] = None description: Optional[str] = None class DepartmentResponse(DepartmentBase): """科室响应""" model_config = ConfigDict(from_attributes=True) id: int is_active: bool created_at: datetime updated_at: datetime class DepartmentTree(DepartmentResponse): """科室树形结构""" children: List["DepartmentTree"] = [] # ==================== 员工相关 ==================== class StaffBase(BaseModel): """员工基础模式""" employee_id: str = Field(..., max_length=20, description="工号") name: str = Field(..., max_length=50, description="姓名") department_id: int = Field(..., description="所属科室ID") position: str = Field(..., max_length=50, description="职位") title: Optional[str] = Field(None, max_length=50, description="职称") phone: Optional[str] = Field(None, max_length=20, description="电话") email: Optional[str] = Field(None, max_length=100, description="邮箱") base_salary: float = Field(default=0, ge=0, description="基本工资") performance_ratio: float = Field(default=1.0, ge=0, le=5, description="绩效系数") class StaffCreate(StaffBase): """创建员工""" status: StaffStatus = Field(default=StaffStatus.ACTIVE) hire_date: Optional[datetime] = None class StaffUpdate(BaseModel): """更新员工""" name: Optional[str] = Field(None, max_length=50) department_id: Optional[int] = None position: Optional[str] = Field(None, max_length=50) title: Optional[str] = Field(None, max_length=50) phone: Optional[str] = Field(None, max_length=20) email: Optional[str] = Field(None, max_length=100) base_salary: Optional[float] = Field(None, ge=0) performance_ratio: Optional[float] = Field(None, ge=0, le=5) status: Optional[StaffStatus] = None class StaffResponse(StaffBase): """员工响应""" model_config = ConfigDict(from_attributes=True) id: int status: StaffStatus hire_date: Optional[datetime] created_at: datetime updated_at: datetime department_name: Optional[str] = None # ==================== 指标相关 ==================== class IndicatorBase(BaseModel): """指标基础模式""" name: str = Field(..., max_length=100, description="指标名称") code: str = Field(..., max_length=20, description="指标编码") indicator_type: IndicatorType = Field(..., description="指标类型") weight: float = Field(default=1.0, gt=0, le=20, description="权重") max_score: float = Field(default=100.0, ge=0, le=1000, description="最高分值") target_value: Optional[float] = Field(None, description="目标值") unit: Optional[str] = Field(None, max_length=20, description="计量单位") calculation_method: Optional[str] = Field(None, description="计算方法") description: Optional[str] = Field(None, description="描述") class IndicatorCreate(IndicatorBase): """创建指标""" pass class IndicatorUpdate(BaseModel): """更新指标""" name: Optional[str] = Field(None, max_length=100) indicator_type: Optional[IndicatorType] = None weight: Optional[float] = Field(None, gt=0, le=20) max_score: Optional[float] = Field(None, ge=0, le=1000) target_value: Optional[float] = None unit: Optional[str] = None calculation_method: Optional[str] = None description: Optional[str] = None is_active: Optional[bool] = None class IndicatorResponse(IndicatorBase): """指标响应""" model_config = ConfigDict(from_attributes=True) id: int is_active: bool created_at: datetime updated_at: datetime # ==================== 考核相关 ==================== class AssessmentDetailBase(BaseModel): """考核明细基础模式""" indicator_id: int = Field(..., description="指标ID") actual_value: Optional[float] = Field(None, description="实际值") score: float = Field(default=0, ge=0, description="得分") evidence: Optional[str] = Field(None, description="佐证材料") remark: Optional[str] = Field(None, description="备注") class AssessmentDetailCreate(AssessmentDetailBase): """创建考核明细""" pass class AssessmentDetailResponse(AssessmentDetailBase): """考核明细响应""" model_config = ConfigDict(from_attributes=True) id: int assessment_id: int indicator_name: Optional[str] = None created_at: datetime class AssessmentBase(BaseModel): """考核基础模式""" staff_id: int = Field(..., description="员工ID") period_year: int = Field(..., ge=2020, le=2100, description="年度") period_month: int = Field(..., ge=1, le=12, description="月份") period_type: str = Field(default="monthly", description="周期类型") class AssessmentCreate(AssessmentBase): """创建考核""" details: List[AssessmentDetailCreate] = Field(default_factory=list, description="考核明细") class AssessmentUpdate(BaseModel): """更新考核""" details: Optional[List[AssessmentDetailCreate]] = None remark: Optional[str] = None class AssessmentResponse(AssessmentBase): """考核响应""" model_config = ConfigDict(from_attributes=True) id: int total_score: float weighted_score: float status: AssessmentStatus assessor_id: Optional[int] reviewer_id: Optional[int] submit_time: Optional[datetime] review_time: Optional[datetime] remark: Optional[str] created_at: datetime updated_at: datetime staff_name: Optional[str] = None department_name: Optional[str] = None details: List[AssessmentDetailResponse] = [] class AssessmentListResponse(AssessmentBase): """考核列表响应""" model_config = ConfigDict(from_attributes=True) id: int total_score: float weighted_score: float status: AssessmentStatus staff_name: Optional[str] = None department_name: Optional[str] = None created_at: datetime # ==================== 工资相关 ==================== class SalaryRecordBase(BaseModel): """工资记录基础模式""" staff_id: int = Field(..., description="员工ID") period_year: int = Field(..., ge=2020, le=2100, description="年度") period_month: int = Field(..., ge=1, le=12, description="月份") base_salary: float = Field(default=0, ge=0, description="基本工资") performance_score: float = Field(default=0, ge=0, description="绩效得分") performance_bonus: float = Field(default=0, ge=0, description="绩效奖金") deduction: float = Field(default=0, ge=0, description="扣款") allowance: float = Field(default=0, ge=0, description="补贴") class SalaryRecordCreate(SalaryRecordBase): """创建工资记录""" remark: Optional[str] = None class SalaryRecordUpdate(BaseModel): """更新工资记录""" performance_bonus: Optional[float] = Field(None, ge=0) deduction: Optional[float] = Field(None, ge=0) allowance: Optional[float] = Field(None, ge=0) remark: Optional[str] = None class SalaryRecordResponse(SalaryRecordBase): """工资记录响应""" model_config = ConfigDict(from_attributes=True) id: int total_salary: float status: str remark: Optional[str] created_at: datetime updated_at: datetime staff_name: Optional[str] = None department_name: Optional[str] = None # ==================== 用户认证相关 ==================== class UserLogin(BaseModel): """用户登录""" username: str = Field(..., min_length=3, max_length=50) password: str = Field(..., min_length=6, max_length=100) class UserCreate(BaseModel): """创建用户""" username: str = Field(..., min_length=3, max_length=50) password: str = Field(..., min_length=6, max_length=100) staff_id: Optional[int] = None role: str = Field(default="staff", pattern="^(admin|manager|staff)$") class Token(BaseModel): """令牌响应""" access_token: str token_type: str = "bearer" class UserResponse(BaseModel): """用户响应""" model_config = ConfigDict(from_attributes=True) id: int username: str role: str is_active: bool last_login: Optional[datetime] created_at: datetime # ==================== 统计报表相关 ==================== class DepartmentStats(BaseModel): """科室统计""" department_id: int department_name: str staff_count: int avg_score: float total_bonus: float class PeriodStats(BaseModel): """周期统计""" period_year: int period_month: int total_staff: int assessed_count: int avg_score: float total_bonus: float score_distribution: dict class TrendData(BaseModel): """趋势数据""" period: str avg_score: float total_bonus: float # ==================== 财务核算相关 ==================== class RevenueCategory(str, Enum): """收入类别""" EXAMINATION = "examination" # 检查费 LAB_TEST = "lab_test" # 检验费 RADIOLOGY = "radiology" # 放射费 BED = "bed" # 床位费 NURSING = "nursing" # 护理费 TREATMENT = "treatment" # 治疗费 SURGERY = "surgery" # 手术费 INJECTION = "injection" # 注射费 OXYGEN = "oxygen" # 吸氧费 OTHER = "other" # 其他 class ExpenseCategory(str, Enum): """支出类别""" MATERIAL = "material" # 材料费 PERSONNEL = "personnel" # 人员支出 MAINTENANCE = "maintenance" # 维修费 UTILITY = "utility" # 水电费 OTHER = "other" # 其他 class FinanceType(str, Enum): """财务类型""" REVENUE = "revenue" # 收入 EXPENSE = "expense" # 支出 class FinanceRecordBase(BaseModel): """财务记录基础模式""" department_id: int = Field(..., description="科室ID") finance_type: FinanceType = Field(..., description="财务类型") category: str = Field(..., max_length=50, description="类别") amount: float = Field(..., ge=0, description="金额") period_year: int = Field(..., ge=2020, le=2100, description="年度") period_month: int = Field(..., ge=1, le=12, description="月份") source: Optional[str] = Field(None, max_length=100, description="数据来源") remark: Optional[str] = Field(None, description="备注") class FinanceRecordCreate(FinanceRecordBase): """创建财务记录""" pass class FinanceRecordUpdate(BaseModel): """更新财务记录""" category: Optional[str] = Field(None, max_length=50) amount: Optional[float] = Field(None, ge=0) source: Optional[str] = Field(None, max_length=100) remark: Optional[str] = None class FinanceRecordResponse(FinanceRecordBase): """财务记录响应""" model_config = ConfigDict(from_attributes=True) id: int department_name: Optional[str] = None category_label: Optional[str] = None created_at: datetime updated_at: datetime class DepartmentBalance(BaseModel): """科室收支结余""" department_id: Optional[int] = None department_name: Optional[str] = None period_year: Optional[int] = None period_month: Optional[int] = None total_revenue: float = Field(default=0, description="总收入") total_expense: float = Field(default=0, description="总支出") balance: float = Field(default=0, description="结余") class CategorySummary(BaseModel): """类别汇总""" category: str category_label: str amount: float # ==================== 绩效计划相关 ==================== class PlanLevel(str, Enum): """计划层级""" HOSPITAL = "hospital" DEPARTMENT = "department" INDIVIDUAL = "individual" class PlanStatus(str, Enum): """计划状态""" DRAFT = "draft" PENDING = "pending" APPROVED = "approved" REJECTED = "rejected" ACTIVE = "active" COMPLETED = "completed" CANCELLED = "cancelled" class PlanKpiRelationBase(BaseModel): """计划指标关联基础模式""" indicator_id: int = Field(..., description="指标 ID") target_value: Optional[float] = Field(None, description="目标值") target_unit: Optional[str] = Field(None, max_length=50, description="目标值单位") weight: float = Field(default=1.0, ge=0, le=100, description="权重") scoring_method: Optional[str] = Field(None, max_length=50, description="评分方法") scoring_params: Optional[str] = Field(None, description="评分参数 (JSON)") remark: Optional[str] = Field(None, description="备注") class PlanKpiRelationCreate(PlanKpiRelationBase): """创建计划指标关联""" pass class PlanKpiRelationUpdate(BaseModel): """更新计划指标关联""" target_value: Optional[float] = Field(None, description="目标值") target_unit: Optional[str] = Field(None, max_length=50, description="目标值单位") weight: Optional[float] = Field(None, ge=0, le=100, description="权重") scoring_method: Optional[str] = Field(None, max_length=50, description="评分方法") scoring_params: Optional[str] = Field(None, description="评分参数 (JSON)") remark: Optional[str] = Field(None, description="备注") class PlanKpiRelationResponse(PlanKpiRelationBase): """计划指标关联响应""" model_config = ConfigDict(from_attributes=True) id: int plan_id: int indicator_name: Optional[str] = None indicator_code: Optional[str] = None created_at: datetime updated_at: datetime class PerformancePlanBase(BaseModel): """绩效计划基础模式""" plan_name: str = Field(..., max_length=200, description="计划名称") plan_code: str = Field(..., max_length=50, description="计划编码") plan_level: PlanLevel = Field(..., description="计划层级") plan_year: int = Field(..., ge=2000, le=2100, description="计划年度") plan_month: Optional[int] = Field(None, ge=1, le=12, description="计划月份") plan_type: str = Field(default="annual", max_length=20, description="计划类型") department_id: Optional[int] = Field(None, description="所属科室 ID") staff_id: Optional[int] = Field(None, description="责任人 ID") parent_plan_id: Optional[int] = Field(None, description="上级计划 ID") description: Optional[str] = Field(None, description="计划描述") strategic_goals: Optional[str] = Field(None, description="战略目标") key_initiatives: Optional[str] = Field(None, description="关键举措") class PerformancePlanCreate(PerformancePlanBase): """创建绩效计划""" kpi_relations: Optional[List[PlanKpiRelationCreate]] = Field(None, description="指标关联列表") class PerformancePlanUpdate(BaseModel): """更新绩效计划""" plan_name: Optional[str] = Field(None, max_length=200, description="计划名称") plan_level: Optional[PlanLevel] = Field(None, description="计划层级") department_id: Optional[int] = Field(None, description="所属科室 ID") staff_id: Optional[int] = Field(None, description="责任人 ID") description: Optional[str] = Field(None, description="计划描述") strategic_goals: Optional[str] = Field(None, description="战略目标") key_initiatives: Optional[str] = Field(None, description="关键举措") status: Optional[PlanStatus] = Field(None, description="状态") approve_remark: Optional[str] = Field(None, description="审批意见") class PerformancePlanResponse(PerformancePlanBase): """绩效计划响应""" model_config = ConfigDict(from_attributes=True) id: int status: PlanStatus submitter_id: Optional[int] = None submit_time: Optional[datetime] = None approver_id: Optional[int] = None approve_time: Optional[datetime] = None version: int is_active: bool created_at: datetime updated_at: datetime department_name: Optional[str] = None staff_name: Optional[str] = None kpi_relations: Optional[List[PlanKpiRelationResponse]] = None class PerformancePlanStats(BaseModel): """绩效计划统计""" total_plans: int = Field(0, description="总计划数") draft_count: int = Field(0, description="草稿数") pending_count: int = Field(0, description="待审批数") approved_count: int = Field(0, description="已批准数") active_count: int = Field(0, description="执行中数") completed_count: int = Field(0, description="已完成数") # ==================== 菜单管理相关 ==================== class MenuType(str, Enum): """菜单类型""" MENU = "menu" BUTTON = "button" class MenuBase(BaseModel): """菜单基础模式""" parent_id: Optional[int] = Field(None, description="父菜单 ID") menu_type: MenuType = Field(default=MenuType.MENU, description="菜单类型") menu_name: str = Field(..., max_length=100, description="菜单名称") menu_icon: Optional[str] = Field(None, max_length=50, description="菜单图标") path: str = Field(..., max_length=200, description="路由路径") component: Optional[str] = Field(None, max_length=200, description="组件路径") permission: Optional[str] = Field(None, max_length=100, description="权限标识") sort_order: int = Field(default=0, ge=0, description="排序") is_visible: bool = Field(default=True, description="是否可见") is_active: bool = Field(default=True, description="是否启用") class MenuCreate(MenuBase): """创建菜单""" pass class MenuUpdate(BaseModel): """更新菜单""" menu_name: Optional[str] = Field(None, max_length=100, description="菜单名称") menu_icon: Optional[str] = Field(None, max_length=50, description="菜单图标") path: Optional[str] = Field(None, max_length=200, description="路由路径") component: Optional[str] = Field(None, max_length=200, description="组件路径") permission: Optional[str] = Field(None, max_length=100, description="权限标识") sort_order: Optional[int] = Field(None, ge=0, description="排序") is_visible: Optional[bool] = Field(None, description="是否可见") is_active: Optional[bool] = Field(None, description="是否启用") class MenuResponse(MenuBase): """菜单响应""" model_config = ConfigDict(from_attributes=True) id: int children: Optional[List["MenuResponse"]] = None created_at: datetime updated_at: datetime class MenuTree(BaseModel): """菜单树节点""" id: int menu_name: str menu_icon: Optional[str] = None path: str children: Optional[List["MenuTree"]] = None # ==================== 指标模板相关 ==================== class TemplateType(str, Enum): """模板类型""" GENERAL = "general" # 通用模板 SURGICAL = "surgical" # 手术临床科室 NON_SURGICAL_WARD = "nonsurgical_ward" # 非手术有病房科室 NON_SURGICAL_NOWARD = "nonsurgical_noward" # 非手术无病房科室 MEDICAL_TECH = "medical_tech" # 医技科室 NURSING = "nursing" # 护理单元 ADMIN = "admin" # 行政科室 LOGISTICS = "logistics" # 后勤科室 class TemplateIndicatorBase(BaseModel): """模板指标关联基础模式""" indicator_id: int = Field(..., description="指标 ID") category: Optional[str] = Field(None, max_length=100, description="指标分类") target_value: Optional[float] = Field(None, description="目标值") target_unit: Optional[str] = Field(None, max_length=50, description="目标值单位") weight: float = Field(default=1.0, ge=0, le=100, description="权重") scoring_method: Optional[str] = Field(None, max_length=50, description="评分方法") scoring_params: Optional[str] = Field(None, description="评分参数 (JSON)") sort_order: int = Field(default=0, ge=0, description="排序") remark: Optional[str] = Field(None, description="备注") class TemplateIndicatorCreate(TemplateIndicatorBase): """创建模板指标关联""" pass class TemplateIndicatorUpdate(BaseModel): """更新模板指标关联""" category: Optional[str] = Field(None, max_length=100, description="指标分类") target_value: Optional[float] = Field(None, description="目标值") target_unit: Optional[str] = Field(None, max_length=50, description="目标值单位") weight: Optional[float] = Field(None, ge=0, le=100, description="权重") scoring_method: Optional[str] = Field(None, max_length=50, description="评分方法") scoring_params: Optional[str] = Field(None, description="评分参数 (JSON)") sort_order: Optional[int] = Field(None, ge=0, description="排序") remark: Optional[str] = Field(None, description="备注") class TemplateIndicatorResponse(TemplateIndicatorBase): """模板指标关联响应""" model_config = ConfigDict(from_attributes=True) id: int template_id: int indicator_name: Optional[str] = None indicator_code: Optional[str] = None indicator_type: Optional[str] = None bs_dimension: Optional[str] = None created_at: datetime updated_at: datetime class IndicatorTemplateBase(BaseModel): """指标模板基础模式""" template_name: str = Field(..., max_length=200, description="模板名称") template_code: str = Field(..., max_length=50, description="模板编码") template_type: TemplateType = Field(..., description="模板类型") description: Optional[str] = Field(None, description="模板描述") dimension_weights: Optional[str] = Field(None, description="维度权重 (JSON)") assessment_cycle: str = Field(default="monthly", max_length=20, description="考核周期") class IndicatorTemplateCreate(IndicatorTemplateBase): """创建指标模板""" indicators: Optional[List[TemplateIndicatorCreate]] = Field(None, description="指标列表") class IndicatorTemplateUpdate(BaseModel): """更新指标模板""" template_name: Optional[str] = Field(None, max_length=200, description="模板名称") template_type: Optional[TemplateType] = None description: Optional[str] = Field(None, description="模板描述") dimension_weights: Optional[str] = Field(None, description="维度权重 (JSON)") assessment_cycle: Optional[str] = Field(None, max_length=20, description="考核周期") is_active: Optional[bool] = Field(None, description="是否启用") class IndicatorTemplateResponse(IndicatorTemplateBase): """指标模板响应""" model_config = ConfigDict(from_attributes=True) id: int is_active: bool created_at: datetime updated_at: datetime indicators: Optional[List[TemplateIndicatorResponse]] = None class IndicatorTemplateListResponse(IndicatorTemplateBase): """指标模板列表响应""" model_config = ConfigDict(from_attributes=True) id: int is_active: bool indicator_count: int = Field(default=0, description="指标数量") created_at: datetime updated_at: datetime