589 lines
34 KiB
Python
589 lines
34 KiB
Python
"""
|
|
数据模型模块
|
|
"""
|
|
from datetime import datetime
|
|
from typing import Optional, List
|
|
from sqlalchemy import (
|
|
String, Text, Integer, Numeric, Boolean, DateTime, ForeignKey, Enum as SQLEnum,
|
|
Index, CheckConstraint
|
|
)
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from enum import Enum
|
|
|
|
from app.core.database import Base
|
|
|
|
|
|
class DeptType(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 BSCDimension(Enum):
|
|
"""平衡计分卡维度"""
|
|
FINANCIAL = "financial" # 财务维度
|
|
CUSTOMER = "customer" # 客户维度
|
|
INTERNAL_PROCESS = "internal_process" # 内部流程维度
|
|
LEARNING_GROWTH = "learning_growth" # 学习与成长维度
|
|
|
|
|
|
class StaffStatus(Enum):
|
|
"""员工状态"""
|
|
ACTIVE = "active" # 在职
|
|
LEAVE = "leave" # 休假
|
|
RESIGNED = "resigned" # 离职
|
|
RETIRED = "retired" # 退休
|
|
|
|
|
|
class AssessmentStatus(Enum):
|
|
"""考核状态"""
|
|
DRAFT = "draft" # 草稿
|
|
SUBMITTED = "submitted" # 已提交
|
|
REVIEWED = "reviewed" # 已审核
|
|
FINALIZED = "finalized" # 已确认
|
|
REJECTED = "rejected" # 已驳回
|
|
|
|
|
|
class IndicatorType(Enum):
|
|
"""指标类型"""
|
|
QUALITY = "quality" # 质量指标
|
|
QUANTITY = "quantity" # 数量指标
|
|
EFFICIENCY = "efficiency" # 效率指标
|
|
SERVICE = "service" # 服务指标
|
|
COST = "cost" # 成本指标
|
|
|
|
class Department(Base):
|
|
"""科室信息表"""
|
|
__tablename__ = "departments"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
name: Mapped[str] = mapped_column(String(100), nullable=False, comment="科室名称")
|
|
code: Mapped[str] = mapped_column(String(20), unique=True, nullable=False, comment="科室编码")
|
|
dept_type: Mapped[DeptType] = mapped_column(SQLEnum(DeptType, values_callable=lambda x: [e.value for e in x]), nullable=False, comment="科室类型")
|
|
parent_id: Mapped[Optional[int]] = mapped_column(ForeignKey("departments.id"), nullable=True, comment="上级科室")
|
|
level: Mapped[int] = mapped_column(Integer, default=1, comment="层级")
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0, comment="排序")
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否启用")
|
|
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="描述")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
parent: Mapped[Optional["Department"]] = relationship("Department", remote_side=[id], backref="children")
|
|
staff: Mapped[List["Staff"]] = relationship("Staff", back_populates="department")
|
|
|
|
__table_args__ = (
|
|
Index("idx_dept_type", "dept_type"),
|
|
Index("idx_dept_parent", "parent_id"),
|
|
)
|
|
|
|
|
|
class Staff(Base):
|
|
"""员工信息表"""
|
|
__tablename__ = "staff"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
employee_id: Mapped[str] = mapped_column(String(20), unique=True, nullable=False, comment="工号")
|
|
name: Mapped[str] = mapped_column(String(50), nullable=False, comment="姓名")
|
|
department_id: Mapped[int] = mapped_column(ForeignKey("departments.id"), nullable=False, comment="所属科室")
|
|
position: Mapped[str] = mapped_column(String(50), nullable=False, comment="职位")
|
|
title: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, comment="职称")
|
|
phone: Mapped[Optional[str]] = mapped_column(String(20), nullable=True, comment="联系电话")
|
|
email: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, comment="邮箱")
|
|
base_salary: Mapped[float] = mapped_column(Numeric(10, 2), default=0, comment="基本工资")
|
|
performance_ratio: Mapped[float] = mapped_column(Numeric(5, 2), default=1.0, comment="绩效系数")
|
|
status: Mapped[StaffStatus] = mapped_column(SQLEnum(StaffStatus, values_callable=lambda x: [e.value for e in x]), default=StaffStatus.ACTIVE, comment="状态")
|
|
hire_date: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="入职日期")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
department: Mapped["Department"] = relationship("Department", back_populates="staff")
|
|
assessments: Mapped[List["Assessment"]] = relationship("Assessment", foreign_keys="Assessment.staff_id", back_populates="staff")
|
|
salary_records: Mapped[List["SalaryRecord"]] = relationship("SalaryRecord", back_populates="staff")
|
|
__table_args__ = (
|
|
Index("idx_staff_dept", "department_id"),
|
|
Index("idx_staff_status", "status"),
|
|
)
|
|
|
|
|
|
class Indicator(Base):
|
|
"""考核指标表"""
|
|
__tablename__ = "indicators"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
name: Mapped[str] = mapped_column(String(100), nullable=False, comment="指标名称")
|
|
code: Mapped[str] = mapped_column(String(20), unique=True, nullable=False, comment="指标编码")
|
|
indicator_type: Mapped[IndicatorType] = mapped_column(SQLEnum(IndicatorType, values_callable=lambda x: [e.value for e in x]), nullable=False, comment="指标类型")
|
|
bs_dimension: Mapped[BSCDimension] = mapped_column(SQLEnum(BSCDimension, values_callable=lambda x: [e.value for e in x]), nullable=False, comment="平衡计分卡维度")
|
|
weight: Mapped[float] = mapped_column(Numeric(5, 2), default=1.0, comment="权重")
|
|
max_score: Mapped[float] = mapped_column(Numeric(5, 2), default=100.0, comment="最高分值")
|
|
target_value: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), nullable=True, comment="目标值")
|
|
target_unit: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, comment="目标值单位")
|
|
calculation_method: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="计算方法/公式")
|
|
assessment_method: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="考核方法")
|
|
deduction_standard: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="扣分标准")
|
|
data_source: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, comment="数据来源")
|
|
applicable_dept_types: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="适用科室类型 (JSON 数组)")
|
|
is_veto: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否一票否决指标")
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否启用")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
assessment_details: Mapped[List["AssessmentDetail"]] = relationship("AssessmentDetail", back_populates="indicator")
|
|
|
|
__table_args__ = (
|
|
Index("idx_indicator_type", "indicator_type"),
|
|
CheckConstraint("weight > 0", name="ck_indicator_weight"),
|
|
)
|
|
|
|
|
|
class Assessment(Base):
|
|
"""考核记录表"""
|
|
__tablename__ = "assessments"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
staff_id: Mapped[int] = mapped_column(ForeignKey("staff.id"), nullable=False, comment="员工ID")
|
|
period_year: Mapped[int] = mapped_column(Integer, nullable=False, comment="考核年度")
|
|
period_month: Mapped[int] = mapped_column(Integer, nullable=False, comment="考核月份")
|
|
period_type: Mapped[str] = mapped_column(String(20), default="monthly", comment="考核周期类型")
|
|
total_score: Mapped[float] = mapped_column(Numeric(5, 2), default=0, comment="总分")
|
|
weighted_score: Mapped[float] = mapped_column(Numeric(5, 2), default=0, comment="加权得分")
|
|
status: Mapped[AssessmentStatus] = mapped_column(SQLEnum(AssessmentStatus, values_callable=lambda x: [e.value for e in x]), default=AssessmentStatus.DRAFT, comment="状态")
|
|
assessor_id: Mapped[Optional[int]] = mapped_column(ForeignKey("staff.id"), nullable=True, comment="考核人")
|
|
reviewer_id: Mapped[Optional[int]] = mapped_column(ForeignKey("staff.id"), nullable=True, comment="审核人")
|
|
submit_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="提交时间")
|
|
review_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="审核时间")
|
|
remark: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="备注")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
staff: Mapped["Staff"] = relationship("Staff", foreign_keys=[staff_id], back_populates="assessments")
|
|
assessor: Mapped[Optional["Staff"]] = relationship("Staff", foreign_keys=[assessor_id])
|
|
reviewer: Mapped[Optional["Staff"]] = relationship("Staff", foreign_keys=[reviewer_id])
|
|
details: Mapped[List["AssessmentDetail"]] = relationship("AssessmentDetail", back_populates="assessment", cascade="all, delete-orphan")
|
|
__table_args__ = (
|
|
Index("idx_assessment_staff", "staff_id"),
|
|
Index("idx_assessment_period", "period_year", "period_month"),
|
|
Index("idx_assessment_status", "status"),
|
|
)
|
|
|
|
|
|
class AssessmentDetail(Base):
|
|
"""考核明细表"""
|
|
__tablename__ = "assessment_details"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
assessment_id: Mapped[int] = mapped_column(ForeignKey("assessments.id"), nullable=False, comment="考核记录ID")
|
|
indicator_id: Mapped[int] = mapped_column(ForeignKey("indicators.id"), nullable=False, comment="指标ID")
|
|
actual_value: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), nullable=True, comment="实际值")
|
|
score: Mapped[float] = mapped_column(Numeric(5, 2), default=0, comment="得分")
|
|
evidence: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="佐证材料")
|
|
remark: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="备注")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
assessment: Mapped["Assessment"] = relationship("Assessment", back_populates="details")
|
|
indicator: Mapped["Indicator"] = relationship("Indicator", back_populates="assessment_details")
|
|
|
|
__table_args__ = (
|
|
Index("idx_detail_assessment", "assessment_id"),
|
|
Index("idx_detail_indicator", "indicator_id"),
|
|
)
|
|
|
|
|
|
class SalaryRecord(Base):
|
|
"""工资核算记录表"""
|
|
__tablename__ = "salary_records"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
staff_id: Mapped[int] = mapped_column(ForeignKey("staff.id"), nullable=False, comment="员工ID")
|
|
period_year: Mapped[int] = mapped_column(Integer, nullable=False, comment="年度")
|
|
period_month: Mapped[int] = mapped_column(Integer, nullable=False, comment="月份")
|
|
base_salary: Mapped[float] = mapped_column(Numeric(10, 2), default=0, comment="基本工资")
|
|
performance_score: Mapped[float] = mapped_column(Numeric(5, 2), default=0, comment="绩效得分")
|
|
performance_bonus: Mapped[float] = mapped_column(Numeric(10, 2), default=0, comment="绩效奖金")
|
|
deduction: Mapped[float] = mapped_column(Numeric(10, 2), default=0, comment="扣款")
|
|
allowance: Mapped[float] = mapped_column(Numeric(10, 2), default=0, comment="补贴")
|
|
total_salary: Mapped[float] = mapped_column(Numeric(10, 2), default=0, comment="应发工资")
|
|
status: Mapped[str] = mapped_column(String(20), default="pending", comment="状态")
|
|
remark: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="备注")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
staff: Mapped["Staff"] = relationship("Staff", back_populates="salary_records")
|
|
|
|
__table_args__ = (
|
|
Index("idx_salary_staff", "staff_id"),
|
|
Index("idx_salary_period", "period_year", "period_month"),
|
|
)
|
|
|
|
|
|
class PlanStatus(Enum):
|
|
"""计划状态"""
|
|
DRAFT = "draft" # 草稿
|
|
PENDING = "pending" # 待审批
|
|
APPROVED = "approved" # 已批准
|
|
REJECTED = "rejected" # 已驳回
|
|
ACTIVE = "active" # 执行中
|
|
COMPLETED = "completed" # 已完成
|
|
CANCELLED = "cancelled" # 已取消
|
|
|
|
|
|
class User(Base):
|
|
"""系统用户表"""
|
|
__tablename__ = "users"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
username: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, comment="用户名")
|
|
password_hash: Mapped[str] = mapped_column(String(255), nullable=False, comment="密码哈希")
|
|
staff_id: Mapped[Optional[int]] = mapped_column(ForeignKey("staff.id"), nullable=True, comment="关联员工")
|
|
role: Mapped[str] = mapped_column(String(20), default="staff", comment="角色")
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否启用")
|
|
last_login: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="最后登录")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
__table_args__ = (
|
|
Index("idx_user_username", "username"),
|
|
)
|
|
|
|
|
|
class PlanLevel(Enum):
|
|
"""计划层级"""
|
|
HOSPITAL = "hospital" # 医院级
|
|
DEPARTMENT = "department" # 科室级
|
|
INDIVIDUAL = "individual" # 个人级
|
|
|
|
|
|
class PerformancePlan(Base):
|
|
"""绩效计划表"""
|
|
__tablename__ = "performance_plans"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
plan_name: Mapped[str] = mapped_column(String(200), nullable=False, comment="计划名称")
|
|
plan_code: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, comment="计划编码")
|
|
plan_level: Mapped[PlanLevel] = mapped_column(SQLEnum(PlanLevel, values_callable=lambda x: [e.value for e in x]), nullable=False, comment="计划层级")
|
|
plan_year: Mapped[int] = mapped_column(Integer, nullable=False, comment="计划年度")
|
|
plan_month: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, comment="计划月份 (月度计划)")
|
|
plan_type: Mapped[str] = mapped_column(String(20), default="annual", comment="计划类型 (annual/monthly)")
|
|
department_id: Mapped[Optional[int]] = mapped_column(ForeignKey("departments.id"), nullable=True, comment="所属科室 ID")
|
|
staff_id: Mapped[Optional[int]] = mapped_column(ForeignKey("staff.id"), nullable=True, comment="责任人 ID")
|
|
parent_plan_id: Mapped[Optional[int]] = mapped_column(ForeignKey("performance_plans.id"), nullable=True, comment="上级计划 ID")
|
|
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="计划描述")
|
|
strategic_goals: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="战略目标")
|
|
key_initiatives: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="关键举措")
|
|
status: Mapped[PlanStatus] = mapped_column(SQLEnum(PlanStatus, values_callable=lambda x: [e.value for e in x]), default=PlanStatus.DRAFT, comment="状态")
|
|
submitter_id: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id"), nullable=True, comment="提交人 ID")
|
|
submit_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="提交时间")
|
|
approver_id: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id"), nullable=True, comment="审批人 ID")
|
|
approve_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="审批时间")
|
|
approve_remark: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="审批意见")
|
|
version: Mapped[int] = mapped_column(Integer, default=1, comment="版本号")
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否启用")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
department: Mapped[Optional["Department"]] = relationship("Department", backref="performance_plans")
|
|
staff: Mapped[Optional["Staff"]] = relationship("Staff", backref="performance_plans")
|
|
parent_plan: Mapped[Optional["PerformancePlan"]] = relationship("PerformancePlan", remote_side=[id], backref="child_plans")
|
|
submitter: Mapped[Optional["User"]] = relationship("User", foreign_keys=[submitter_id])
|
|
approver: Mapped[Optional["User"]] = relationship("User", foreign_keys=[approver_id])
|
|
kpi_relations: Mapped[List["PlanKpiRelation"]] = relationship("PlanKpiRelation", back_populates="plan", cascade="all, delete-orphan")
|
|
|
|
__table_args__ = (
|
|
Index("idx_plan_level", "plan_level"),
|
|
Index("idx_plan_year", "plan_year"),
|
|
Index("idx_plan_department", "department_id"),
|
|
Index("idx_plan_status", "status"),
|
|
)
|
|
|
|
|
|
class PlanKpiRelation(Base):
|
|
"""计划指标关联表"""
|
|
__tablename__ = "plan_kpi_relations"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
plan_id: Mapped[int] = mapped_column(ForeignKey("performance_plans.id"), nullable=False, comment="计划 ID")
|
|
indicator_id: Mapped[int] = mapped_column(ForeignKey("indicators.id"), nullable=False, comment="指标 ID")
|
|
target_value: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), nullable=True, comment="目标值")
|
|
target_unit: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, comment="目标值单位")
|
|
weight: Mapped[float] = mapped_column(Numeric(5, 2), default=1.0, comment="权重")
|
|
scoring_method: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, comment="评分方法")
|
|
scoring_params: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="评分参数 (JSON)")
|
|
remark: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="备注")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
plan: Mapped["PerformancePlan"] = relationship("PerformancePlan", back_populates="kpi_relations")
|
|
indicator: Mapped["Indicator"] = relationship("Indicator", backref="plan_relations")
|
|
|
|
__table_args__ = (
|
|
Index("idx_relation_plan", "plan_id"),
|
|
Index("idx_relation_indicator", "indicator_id"),
|
|
Index("idx_relation_unique", "plan_id", "indicator_id", unique=True),
|
|
)
|
|
|
|
|
|
class MenuType(Enum):
|
|
"""菜单类型"""
|
|
MENU = "menu" # 菜单
|
|
BUTTON = "button" # 按钮
|
|
|
|
|
|
class Menu(Base):
|
|
"""系统菜单表"""
|
|
__tablename__ = "menus"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
parent_id: Mapped[Optional[int]] = mapped_column(ForeignKey("menus.id"), nullable=True, comment="父菜单 ID")
|
|
menu_type: Mapped[MenuType] = mapped_column(SQLEnum(MenuType, values_callable=lambda x: [e.value for e in x]), default=MenuType.MENU, comment="菜单类型")
|
|
menu_name: Mapped[str] = mapped_column(String(100), nullable=False, comment="菜单名称")
|
|
menu_icon: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, comment="菜单图标")
|
|
path: Mapped[str] = mapped_column(String(200), nullable=False, comment="路由路径")
|
|
component: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, comment="组件路径")
|
|
permission: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, comment="权限标识")
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0, comment="排序")
|
|
is_visible: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否可见")
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否启用")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
parent: Mapped[Optional["Menu"]] = relationship("Menu", remote_side=[id], backref="children")
|
|
|
|
__table_args__ = (
|
|
Index("idx_menu_parent", "parent_id"),
|
|
Index("idx_menu_type", "menu_type"),
|
|
Index("idx_menu_visible", "is_visible"),
|
|
)
|
|
|
|
|
|
class TemplateType(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 IndicatorTemplate(Base):
|
|
"""考核指标模板表"""
|
|
__tablename__ = "indicator_templates"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
template_name: Mapped[str] = mapped_column(String(200), nullable=False, comment="模板名称")
|
|
template_code: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, comment="模板编码")
|
|
template_type: Mapped[TemplateType] = mapped_column(SQLEnum(TemplateType, values_callable=lambda x: [e.value for e in x]), nullable=False, comment="模板类型")
|
|
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="模板描述")
|
|
dimension_weights: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="维度权重 (JSON)")
|
|
assessment_cycle: Mapped[str] = mapped_column(String(20), default="monthly", comment="考核周期")
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否启用")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
indicators: Mapped[List["TemplateIndicator"]] = relationship("TemplateIndicator", back_populates="template", cascade="all, delete-orphan")
|
|
|
|
__table_args__ = (
|
|
Index("idx_template_type", "template_type"),
|
|
Index("idx_template_active", "is_active"),
|
|
)
|
|
|
|
|
|
class TemplateIndicator(Base):
|
|
"""模板指标关联表"""
|
|
__tablename__ = "template_indicators"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
template_id: Mapped[int] = mapped_column(ForeignKey("indicator_templates.id"), nullable=False, comment="模板 ID")
|
|
indicator_id: Mapped[int] = mapped_column(ForeignKey("indicators.id"), nullable=False, comment="指标 ID")
|
|
category: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, comment="指标分类 (二级指标)")
|
|
target_value: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), nullable=True, comment="目标值")
|
|
target_unit: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, comment="目标值单位")
|
|
weight: Mapped[float] = mapped_column(Numeric(5, 2), default=1.0, comment="权重")
|
|
scoring_method: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, comment="评分方法")
|
|
scoring_params: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="评分参数 (JSON)")
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0, comment="排序")
|
|
remark: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="备注")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
template: Mapped["IndicatorTemplate"] = relationship("IndicatorTemplate", back_populates="indicators")
|
|
indicator: Mapped["Indicator"] = relationship("Indicator", backref="template_relations")
|
|
|
|
__table_args__ = (
|
|
Index("idx_ti_template", "template_id"),
|
|
Index("idx_ti_indicator", "indicator_id"),
|
|
Index("idx_ti_unique", "template_id", "indicator_id", unique=True),
|
|
)
|
|
|
|
|
|
class DeptTypeDimensionWeight(Base):
|
|
"""科室类型BSC维度权重配置表"""
|
|
__tablename__ = "dept_type_dimension_weights"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
dept_type: Mapped[DeptType] = mapped_column(SQLEnum(DeptType, values_callable=lambda x: [e.value for e in x]), nullable=False, comment="科室类型")
|
|
financial_weight: Mapped[float] = mapped_column(Numeric(5, 2), default=0.60, comment="财务维度权重")
|
|
customer_weight: Mapped[float] = mapped_column(Numeric(5, 2), default=0.15, comment="客户维度权重")
|
|
internal_process_weight: Mapped[float] = mapped_column(Numeric(5, 2), default=0.20, comment="内部流程维度权重")
|
|
learning_growth_weight: Mapped[float] = mapped_column(Numeric(5, 2), default=0.05, comment="学习成长维度权重")
|
|
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="描述说明")
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否启用")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
__table_args__ = (
|
|
Index("idx_weight_dept_type", "dept_type"),
|
|
Index("idx_weight_active", "is_active"),
|
|
)
|
|
|
|
|
|
# ==================== 满意度调查模块 ====================
|
|
|
|
class SurveyStatus(Enum):
|
|
"""调查状态"""
|
|
DRAFT = "draft" # 草稿
|
|
PUBLISHED = "published" # 已发布
|
|
CLOSED = "closed" # 已结束
|
|
ARCHIVED = "archived" # 已归档
|
|
|
|
|
|
class SurveyType(Enum):
|
|
"""调查类型"""
|
|
INPATIENT = "inpatient" # 住院患者满意度
|
|
OUTPATIENT = "outpatient" # 门诊患者满意度
|
|
INTERNAL = "internal" # 内部员工满意度
|
|
DEPARTMENT = "department" # 科室间满意度
|
|
|
|
|
|
class Survey(Base):
|
|
"""满意度调查问卷表"""
|
|
__tablename__ = "surveys"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
survey_name: Mapped[str] = mapped_column(String(200), nullable=False, comment="调查名称")
|
|
survey_code: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, comment="调查编码")
|
|
survey_type: Mapped[SurveyType] = mapped_column(SQLEnum(SurveyType, values_callable=lambda x: [e.value for e in x]), nullable=False, comment="调查类型")
|
|
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="调查描述")
|
|
target_departments: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="目标科室(JSON数组)")
|
|
total_questions: Mapped[int] = mapped_column(Integer, default=0, comment="题目总数")
|
|
status: Mapped[SurveyStatus] = mapped_column(SQLEnum(SurveyStatus, values_callable=lambda x: [e.value for e in x]), default=SurveyStatus.DRAFT, comment="状态")
|
|
start_date: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="开始日期")
|
|
end_date: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="结束日期")
|
|
is_anonymous: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否匿名")
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否启用")
|
|
created_by: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id"), nullable=True, comment="创建人")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
|
|
|
# 关系
|
|
questions: Mapped[List["SurveyQuestion"]] = relationship("SurveyQuestion", back_populates="survey", cascade="all, delete-orphan")
|
|
responses: Mapped[List["SurveyResponse"]] = relationship("SurveyResponse", back_populates="survey", cascade="all, delete-orphan")
|
|
|
|
__table_args__ = (
|
|
Index("idx_survey_type", "survey_type"),
|
|
Index("idx_survey_status", "status"),
|
|
Index("idx_survey_dates", "start_date", "end_date"),
|
|
)
|
|
|
|
|
|
class QuestionType(Enum):
|
|
"""题目类型"""
|
|
SINGLE_CHOICE = "single_choice" # 单选题
|
|
MULTIPLE_CHOICE = "multiple_choice" # 多选题
|
|
SCORE = "score" # 评分题(1-5分)
|
|
TEXT = "text" # 文本题
|
|
|
|
|
|
class SurveyQuestion(Base):
|
|
"""调查问卷题目表"""
|
|
__tablename__ = "survey_questions"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
survey_id: Mapped[int] = mapped_column(ForeignKey("surveys.id"), nullable=False, comment="调查ID")
|
|
question_text: Mapped[str] = mapped_column(Text, nullable=False, comment="题目内容")
|
|
question_type: Mapped[QuestionType] = mapped_column(SQLEnum(QuestionType, values_callable=lambda x: [e.value for e in x]), nullable=False, comment="题目类型")
|
|
options: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="选项(JSON数组)")
|
|
score_max: Mapped[int] = mapped_column(Integer, default=5, comment="最高分值(评分题)")
|
|
is_required: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否必答")
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0, comment="排序")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
|
|
# 关系
|
|
survey: Mapped["Survey"] = relationship("Survey", back_populates="questions")
|
|
answers: Mapped[List["SurveyAnswer"]] = relationship("SurveyAnswer", back_populates="question", cascade="all, delete-orphan")
|
|
|
|
__table_args__ = (
|
|
Index("idx_question_survey", "survey_id"),
|
|
)
|
|
|
|
|
|
class SurveyResponse(Base):
|
|
"""调查问卷回答记录表"""
|
|
__tablename__ = "survey_responses"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
survey_id: Mapped[int] = mapped_column(ForeignKey("surveys.id"), nullable=False, comment="调查ID")
|
|
department_id: Mapped[Optional[int]] = mapped_column(ForeignKey("departments.id"), nullable=True, comment="评价科室")
|
|
respondent_type: Mapped[str] = mapped_column(String(20), default="patient", comment="回答者类型")
|
|
respondent_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, comment="回答者ID(员工时)")
|
|
respondent_phone: Mapped[Optional[str]] = mapped_column(String(20), nullable=True, comment="回答者手机")
|
|
total_score: Mapped[float] = mapped_column(Numeric(5, 2), default=0, comment="总得分")
|
|
max_score: Mapped[float] = mapped_column(Numeric(5, 2), default=0, comment="最高可能得分")
|
|
satisfaction_rate: Mapped[float] = mapped_column(Numeric(5, 2), default=0, comment="满意度比例")
|
|
ip_address: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, comment="IP地址")
|
|
user_agent: Mapped[Optional[str]] = mapped_column(String(500), nullable=True, comment="用户代理")
|
|
submitted_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="提交时间")
|
|
|
|
# 关系
|
|
survey: Mapped["Survey"] = relationship("Survey", back_populates="responses")
|
|
department: Mapped[Optional["Department"]] = relationship("Department", backref="survey_responses")
|
|
answers: Mapped[List["SurveyAnswer"]] = relationship("SurveyAnswer", back_populates="response", cascade="all, delete-orphan")
|
|
|
|
__table_args__ = (
|
|
Index("idx_response_survey", "survey_id"),
|
|
Index("idx_response_dept", "department_id"),
|
|
Index("idx_response_time", "submitted_at"),
|
|
)
|
|
|
|
|
|
class SurveyAnswer(Base):
|
|
"""调查问卷回答明细表"""
|
|
__tablename__ = "survey_answers"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
response_id: Mapped[int] = mapped_column(ForeignKey("survey_responses.id"), nullable=False, comment="回答记录ID")
|
|
question_id: Mapped[int] = mapped_column(ForeignKey("survey_questions.id"), nullable=False, comment="题目ID")
|
|
answer_value: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="回答值")
|
|
score: Mapped[float] = mapped_column(Numeric(5, 2), default=0, comment="得分")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, comment="创建时间")
|
|
|
|
# 关系
|
|
response: Mapped["SurveyResponse"] = relationship("SurveyResponse", back_populates="answers")
|
|
question: Mapped["SurveyQuestion"] = relationship("SurveyQuestion", back_populates="answers")
|
|
|
|
__table_args__ = (
|
|
Index("idx_answer_response", "response_id"),
|
|
Index("idx_answer_question", "question_id"),
|
|
)
|