"""值班排班模型 — docs/superpowers/specs/2026-08-14-duty-schedule-design.md""" from datetime import datetime from sqlalchemy import Column, Date, DateTime, Integer, SmallInteger, String, Time from app.core.database import Base SHIFT_MORNING = "morning" SHIFT_EVENING = "evening" SHIFT_DUTY = "duty" SHIFT_LABELS = { SHIFT_MORNING: "早班", SHIFT_EVENING: "晚班", SHIFT_DUTY: "值班", } DICT_SHIFT_TYPE = "duty_shift_type" DICT_WORK_PLAN = "duty_work_plan" DICT_TIME_RANGE = "duty_time_range" DEFAULT_SHIFT_TIMES = { SHIFT_MORNING: ("08:00", "18:00", 0), SHIFT_EVENING: ("18:00", "07:00", 1), } # 预置时段 dict_value:start|end|cross_day TIME_RANGE_DAY = "08:00|18:00|0" TIME_RANGE_NIGHT = "18:00|07:00|1" LINK_OPEN = "open" LINK_CLOSED = "closed" SOURCE_ASSIGN = "assign" SOURCE_GRAB = "grab" # 可排班名单角色(sys_role 固定 id;无菜单,仅标签) DUTY_STAFF_ROLE_ID = "R65CAAE057914CCCAE2A81BE627A5101" DUTY_STAFF_ROLE_NAME = "值班人员" class DutyDay(Base): __tablename__ = "duty_day" id = Column(String(64), primary_key=True, comment="主键ID(UUID)") duty_date = Column(Date, nullable=False, comment="值班日期") access_token = Column(String(64), nullable=False, comment="公开访问令牌") link_status = Column(String(16), nullable=False, default=LINK_OPEN, comment="open/closed") tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号") order_num = Column(Integer, nullable=True, comment="排序号") remark = Column(String(500), nullable=True, comment="备注") create_by = Column(String(64), nullable=True, comment="创建者") create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门") create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间") update_by = Column(String(64), nullable=True, comment="更新者") update_time = Column( DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间" ) del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标志") class DutySlot(Base): __tablename__ = "duty_slot" id = Column(String(64), primary_key=True, comment="主键ID(UUID)") duty_day_id = Column(String(64), nullable=False, comment="值班日ID") shift_type = Column(String(32), nullable=False, comment="morning/evening/duty") capacity = Column(Integer, nullable=False, default=1, comment="名额") work_plans = Column(String(500), nullable=True, comment="工作安排dict_value逗号分隔") start_time = Column(Time, nullable=True, comment="值班开始时间") end_time = Column(Time, nullable=True, comment="值班结束时间") cross_day = Column(SmallInteger, nullable=False, default=0, comment="结束是否次日") tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号") order_num = Column(Integer, nullable=True, comment="排序号") remark = Column(String(500), nullable=True, comment="备注") create_by = Column(String(64), nullable=True, comment="创建者") create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门") create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间") update_by = Column(String(64), nullable=True, comment="更新者") update_time = Column( DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间" ) del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标志") class DutyAssignment(Base): __tablename__ = "duty_assignment" id = Column(String(64), primary_key=True, comment="主键ID(UUID)") duty_slot_id = Column(String(64), nullable=False, comment="班次名额ID") source = Column(String(16), nullable=False, comment="assign/grab") user_id = Column(String(64), nullable=True, comment="系统用户ID(指定时)") person_name = Column(String(64), nullable=False, comment="展示姓名") tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号") order_num = Column(Integer, nullable=True, comment="排序号") remark = Column(String(500), nullable=True, comment="备注") create_by = Column(String(64), nullable=True, comment="创建者") create_dept_id = Column(String(64), nullable=False, default="", comment="创建部门") create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间") update_by = Column(String(64), nullable=True, comment="更新者") update_time = Column( DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间" ) del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标志")