-- ============================================================ -- 办事大厅 · 对账任务接收与执行检查点 DDL(MySQL 5.7 兼容复核版,TASK_3) -- 范围:一张新表 answer_reconcile_work(中心侧契约见 form-center/service/docs/answer-reconcile-v1.md) -- 依据:任务书 TASK_3 改动点 4(SH 持久化接收任务、重复指令复用 taskId、进程重启从 checkpoint 续页)、 -- 改动点 5/6(比较四元摘要、经权威入口修复、多余行只在完整证明后退役) -- 用途:生产 ddl-auto=none 时的升级脚本;开发期(本机 bsch_svchall,ddl-auto=update)由 Hibernate -- 自动追加,本脚本同时是"实表应当长什么样"的评审权威(沿 fc_projection_and_apply_intent 先例)。 -- 5.7 复核要点:①无 CTE/WITH、无函数索引、无生成列、无 CHECK 约束;②utf8mb4 显式写 CHARSET/COLLATE; -- ③不建物理外键——task_uuid 是中心任务的逻辑引用,本行要能在中心归档后继续留痕; -- ④两条游标与水位同为 answer_uuid(32 位小写十六进制),锁步推进才是"多余行"可判的前提。 -- 边界纪律:本表只存对账坐标与检查点,不存答卷值文档——值文档归 fc_answer_projection(TASK_2), -- 修复走 repairAuthoritative、退役走 retireAbsent,本任务不提供任何绕过它们的通道。 -- 状态纪律:接收端点的 2xx 只把 state 写成 QUEUED;COMPLETED/PARTIAL 必须来自本地扫描的真实收尾, -- 且以 (task_uuid, attempt) 的回报为准。 -- 非幂等:MySQL 5.7 无评审友好的 CREATE TABLE IF NOT EXISTS 写法。 -- 执行前先 `SHOW TABLES LIKE 'answer_reconcile_work'` 确认表不存在。 -- ============================================================ CREATE TABLE `answer_reconcile_work` ( `work_version` BIGINT NOT NULL DEFAULT 0 COMMENT 'Local optimistic write generation', `retry_count` INT NOT NULL DEFAULT 0 COMMENT 'Consecutive execution/report failures', `pending_report_state` VARCHAR(16) DEFAULT NULL COMMENT 'Retry this result only; do not rescan data', `id` INT NOT NULL AUTO_INCREMENT, `task_uuid` VARCHAR(36) NOT NULL COMMENT '中心任务身份(唯一键:重复指令复用本行与检查点,不另起一轮扫描)', `attempt` INT NOT NULL DEFAULT 0 COMMENT '当前执行的派发尝试;更旧 attempt 的指令只吸收,不动检查点与已提交结论', `consumer_app_key` VARCHAR(64) NOT NULL COMMENT '本应用消费身份(须与已验签身份逐字相等才收)', `consumer_school_id` BIGINT NOT NULL COMMENT '本轮租户域:本地扫描按它限定行集,跨校指令拒收', `form_uuid` VARCHAR(32) NOT NULL COMMENT '本轮范围的表单坐标(一行任务=一个 (消费方, 租户域, 表单))', `form_version` INT NOT NULL COMMENT '中心已核验的版本(不从本地旧清单猜)', `trigger_type` VARCHAR(16) DEFAULT NULL COMMENT 'SCHEDULE / MANUAL', `window_start` DATETIME NOT NULL COMMENT '任务起点:本地 create_time 晚于它或为空的行只报告、不退役', `total_rows` INT NOT NULL DEFAULT 0 COMMENT '中心清单总行数(完整性核对用)', `total_pages` INT NOT NULL DEFAULT 0 COMMENT '中心清单确定总页', `page_size` INT NOT NULL DEFAULT 200 COMMENT '两侧同页大小(上限 500):锁步比较才对得上', `state` VARCHAR(16) NOT NULL DEFAULT 'QUEUED' COMMENT 'QUEUED/RUNNING/COMPLETED/PARTIAL/FAILED/CANCELLED', `fc_cursor` VARCHAR(32) DEFAULT NULL COMMENT '中心清单游标(=水位,answer_uuid)', `local_cursor` VARCHAR(32) DEFAULT NULL COMMENT '本地图像游标(=水位,answer_uuid)', `watermark` VARCHAR(32) DEFAULT NULL COMMENT '两侧都已比较到的最大 answer_uuid:断线重启从这里续', `fc_completed` TINYINT NOT NULL DEFAULT 0 COMMENT '中心清单是否读到底(多余行判定的前提之一)', `local_completed` TINYINT NOT NULL DEFAULT 0 COMMENT '本地图像是否扫完(另一个前提)', `pages_processed` INT NOT NULL DEFAULT 0 COMMENT '已推进的锁步轮数', `scanned_rows` INT NOT NULL DEFAULT 0 COMMENT '比较过的答卷数', `missing_repaired` INT NOT NULL DEFAULT 0 COMMENT '缺失修复 APPLIED 数(期望三元组全 null,只建未关联投影)', `stale_repaired` INT NOT NULL DEFAULT 0 COMMENT '陈旧与同版错 hash 的修复 APPLIED 数(同一权威入口)', `hash_conflicts` INT NOT NULL DEFAULT 0 COMMENT '同 revision 异 hash 的观察数(记录后才取中心当前值修复)', `tombstones_repaired` INT NOT NULL DEFAULT 0 COMMENT '墓碑修复数(保留本地业务扩展列,不物理删)', `local_ahead` INT NOT NULL DEFAULT 0 COMMENT '本地 revision 更高:向中心复核后只报告,禁止降级覆盖', `deferred` INT NOT NULL DEFAULT 0 COMMENT '新鲜授权核验不可达/撤权/纪元不等而暂停的条目', `extra_reported` INT NOT NULL DEFAULT 0 COMMENT '本地多出的行观察数(含期间新增与 createTime 未知,只报告)', `extra_retired` INT NOT NULL DEFAULT 0 COMMENT '经中心签名缺席证明实际退役的行数', `pending_extras_json` MEDIUMTEXT NOT NULL COMMENT '待缺席答复的多余行候选(含 create_time;不含值文档,有上限)', `error_count` INT NOT NULL DEFAULT 0 COMMENT '错误条数', `errors_json` MEDIUMTEXT NOT NULL COMMENT '错误明细(每条含 consumer/task/range/answer 四坐标,敏感 values 永不进这里)', `reported_attempt` INT DEFAULT NULL COMMENT '已提交给中心的结果 attempt(同 attempt 不重复提交)', `reported_state` VARCHAR(16) DEFAULT NULL COMMENT '已提交的结果状态', `resumed` INT NOT NULL DEFAULT 0 COMMENT 'RUNNING 超租约后被重新领取的次数(这一轮断过几次)', `lease_until` DATETIME DEFAULT NULL COMMENT '本地执行租约:超期才可被别的扫描线程领走续跑', `next_retry_at` DATETIME DEFAULT NULL COMMENT '退避到点才再扫(扫描中断/回报不可达时写)', `received_at` DATETIME NOT NULL COMMENT '指令落库时刻(接收端点 2xx 的依据就在这列)', `started_at` DATETIME DEFAULT NULL COMMENT '首次开始执行时刻', `finished_at` DATETIME DEFAULT NULL COMMENT '终态落定时刻', `last_error_code` VARCHAR(32) DEFAULT NULL COMMENT '末次稳定码(GRANT_REVOKED/CANCELLED/MANIFEST_INCOMPLETE/…)', `last_error` VARCHAR(500) DEFAULT NULL COMMENT '末次错误摘要(截断存)', `create_by` VARCHAR(255) DEFAULT NULL, `create_time` DATETIME DEFAULT NULL, `update_by` VARCHAR(255) DEFAULT NULL, `update_time` DATETIME DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_reconcile_work_task` (`task_uuid`), KEY `idx_reconcile_work_scan` (`state`, `next_retry_at`), KEY `idx_reconcile_work_consumer` (`consumer_app_key`, `consumer_school_id`, `id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='对账任务接收与检查点(TASK_3:接收与执行解耦、同 taskId 幂等复用、重启从 watermark 续页;不存值文档)'; -- 自验(执行后逐条核对): -- SHOW CREATE TABLE answer_reconcile_work\G -- 应含 uk_reconcile_work_task(同 taskId 只一行) -- SELECT COUNT(*) FROM information_schema.referential_constraints -- WHERE table_name='answer_reconcile_work'; -- 必须 0 行(无物理外键) -- SELECT COUNT(*) FROM information_schema.columns -- WHERE table_schema=DATABASE() AND table_name='answer_reconcile_work' -- AND column_name IN ('values_json','snapshot_json','context_token'); -- -- 必须 0 行(值文档与令牌不进对账检查点)