Files
hospital_performance/backend/app/schemas/schemas.py
2026-02-28 15:06:52 +08:00

743 lines
25 KiB
Python

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