"""T+ 基础导入与试算的隔离工作区,任何表均不关联正式工资台账。""" from datetime import datetime from sqlalchemy import JSON, DateTime, Index, Integer, String, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column from app.models.base import BIGINT_UNSIGNED, Base, TimestampMixin, mysql_table_args class TPlusSourceHead(Base): """一次完整读取实际观察到的源版本;解决 A→B→A 去重后的旧版本误选。""" __tablename__ = "tplus_source_heads" __table_args__ = mysql_table_args( "T+ 源当前快照指针(按成功全量批次读取,删除不自动回写业务表)", UniqueConstraint( "source_database", "entity_type", "source_id", name="uk_tplus_source_heads_source_entity_id", ), Index("idx_tplus_source_heads_batch_entity", "batch_no", "entity_type"), ) id: Mapped[int] = mapped_column( BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID" ) source_database: Mapped[str] = mapped_column(String(128), comment="源账套") entity_type: Mapped[str] = mapped_column(String(32), comment="实体类型") source_id: Mapped[str] = mapped_column(String(64), comment="源内部ID") record_id: Mapped[int] = mapped_column(BIGINT_UNSIGNED, comment="原始快照ID") batch_no: Mapped[str] = mapped_column(String(32), comment="最后观察批次") class TPlusFact(Base, TimestampMixin): """补充资料追加版本,旧版本永久保留,乐观锁防止静默覆盖。""" __tablename__ = "tplus_facts" __table_args__ = mysql_table_args( "T+ 人工补充事实版本表", UniqueConstraint( "source_database", "kind", "fact_key", "version", name="uk_tplus_facts_source_kind_key_version", ), ) id: Mapped[int] = mapped_column( BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID" ) source_database: Mapped[str] = mapped_column(String(128), comment="源账套") kind: Mapped[str] = mapped_column(String(32), comment="事实类别") fact_key: Mapped[str] = mapped_column(String(128), comment="事实业务键") version: Mapped[int] = mapped_column(Integer, comment="版本号") data_json: Mapped[dict] = mapped_column(JSON, comment="经校验的事实内容") evidence: Mapped[str] = mapped_column(String(500), comment="依据和修改原因") requested_by: Mapped[int] = mapped_column(BIGINT_UNSIGNED, comment="操作者ID") class TPlusSourceLink(Base, TimestampMixin): """源稳定身份与业务目标的唯一关联;不按名称猜测合并。""" __tablename__ = "tplus_source_links" __table_args__ = mysql_table_args( "T+ 基础数据源目标关联", UniqueConstraint( "source_database", "entity_type", "source_id", name="uk_tplus_source_links_source_entity_id", ), UniqueConstraint("entity_type", "target_id", name="uk_tplus_source_links_entity_target"), ) id: Mapped[int] = mapped_column( BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID" ) source_database: Mapped[str] = mapped_column(String(128), comment="源账套") entity_type: Mapped[str] = mapped_column(String(32), comment="实体类型") source_id: Mapped[str] = mapped_column(String(64), comment="源内部ID") target_id: Mapped[int] = mapped_column(BIGINT_UNSIGNED, comment="业务目标ID") class TPlusWorkRun(Base, TimestampMixin): """后台导入、预览和试算运行记录,固定输入,保留终态。""" __tablename__ = "tplus_work_runs" __table_args__ = mysql_table_args( "T+ 导入与预演运行表", UniqueConstraint("run_no", name="uk_tplus_work_runs_run_no"), UniqueConstraint("request_key", name="uk_tplus_work_runs_request_key"), Index("idx_tplus_work_runs_status_id", "status", "id"), ) id: Mapped[int] = mapped_column( BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID" ) run_no: Mapped[str] = mapped_column(String(32), comment="运行编号") request_key: Mapped[str] = mapped_column(String(36), comment="请求幂等键") kind: Mapped[str] = mapped_column( String(32), comment="MASTER_PREVIEW/MASTER_APPLY/CALC_PREVIEW" ) status: Mapped[str] = mapped_column(String(16), default="QUEUED", comment="运行状态") source_database: Mapped[str] = mapped_column(String(128), comment="固定源账套") requested_by: Mapped[int] = mapped_column(BIGINT_UNSIGNED, comment="操作者ID") input_json: Mapped[dict] = mapped_column(JSON, comment="请求输入") snapshot_json: Mapped[dict | None] = mapped_column(JSON, comment="规则和补充资料的固定快照") snapshot_hash: Mapped[str | None] = mapped_column(String(64), comment="输入指纹") summary_json: Mapped[dict | None] = mapped_column(JSON, comment="结果汇总") error_code: Mapped[str | None] = mapped_column(String(64), comment="固定错误编码") started_at: Mapped[datetime | None] = mapped_column(DateTime, comment="开始时间") finished_at: Mapped[datetime | None] = mapped_column(DateTime, comment="结束时间") class TPlusWorkLine(Base): """固定输入及结果逐行留存,仅经授权的工作区接口可访问。""" __tablename__ = "tplus_work_lines" __table_args__ = mysql_table_args( "T+ 导入预览与核算预演明细", UniqueConstraint("run_no", "line_no", name="uk_tplus_work_lines_run_line"), ) id: Mapped[int] = mapped_column( BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID" ) run_no: Mapped[str] = mapped_column(String(32), comment="运行编号") line_no: Mapped[int] = mapped_column(Integer, comment="稳定行序号") entity_type: Mapped[str] = mapped_column(String(32), comment="实体或分组类型") source_id: Mapped[str] = mapped_column(String(128), comment="源ID或平账组") action: Mapped[str] = mapped_column(String(32), comment="新增/更新/跳过/待补/冲突/分配") reason: Mapped[str | None] = mapped_column(String(128), comment="固定原因编码") data_json: Mapped[dict] = mapped_column(JSON, comment="最小必要的业务明细") WORKBENCH_MODELS = (TPlusSourceHead, TPlusFact, TPlusSourceLink, TPlusWorkRun, TPlusWorkLine)