t1~t3+精修: 事项FC绑定/可信建件(意图+投影+恢复)/答卷对账Worker
build / build-push (push) Successful in 1m21s

含10-22精修: 对账有限重试(retry_count/指数退避/work_version防旧覆盖)+接缝身份(source-app-key空拒收)+应急对账闸让位FlowApplyRecoveryService+同attempt指令不重开终态。SQL未执行。
This commit is contained in:
chenxing
2026-10-22 10:03:07 +08:00
parent 036bdb1f7c
commit 391281582a
45 changed files with 8222 additions and 6 deletions
+7
View File
@@ -0,0 +1,7 @@
-- TASK_2 prerequisite repair: local optimistic generation (NOT FC answer_revision).
-- Existing TASK_2 installations only. Run once, before deploying the new entity.
-- For a fresh schema use fc_projection_and_apply_intent.sql; do not run both.
-- MySQL 5.7; non-idempotent: inspect SHOW COLUMNS before execution.
ALTER TABLE fc_answer_projection
ADD COLUMN projection_version BIGINT NOT NULL DEFAULT 0
COMMENT 'Local optimistic generation; guards MQ/receipt/authoritative repair writes';
+7
View File
@@ -0,0 +1,7 @@
-- Existing TASK_3 installations only, once before deploying the refined Worker.
-- Fresh schema: use answer_reconcile_work.sql instead; do not run both.
-- MySQL 5.7; no SQL is executed by this delivery.
ALTER TABLE answer_reconcile_work
ADD COLUMN work_version BIGINT NOT NULL DEFAULT 0,
ADD COLUMN retry_count INT NOT NULL DEFAULT 0,
ADD COLUMN pending_report_state VARCHAR(16) NULL;
+14
View File
@@ -0,0 +1,14 @@
-- 2026-10-21 svc-hall TASK_1:事项绑定「表单中心」的坐标两列(fc_form_uuid / fc_form_version)
-- 契约:form-center/service/docs/consumer-access-v3.md §4(消费方只保存服务器验证过的 uuid + 确切版本)
-- 口径:两列都可空且**必须成对**——单传一个由保存链路拒(ServiceItemService#validateBindings),
-- 取消关联=同时清空;都没绑=既有允许形态(纯指南事项/无需表单的事项照旧可存)。
-- form_code 列不删、不改语义:存量行仍参与办件回显与 effectiveFlowGuid 的读路径,
-- 但**保存链路不再写新值**(只允许原样回传保留历史绑定)。
-- 无索引:本应用没有"按 fc_form_uuid 反查事项"的查询(事项是按 id 寻址的单条编辑),
-- 加了就是没人走的索引;将来 TASK_2 的 Apply 侧若要按表单聚合办件,再随那条查询一起加。
-- 语法:MySQL 5.7(无 ADD COLUMN IF NOT EXISTS,重复执行会报 Duplicate column name,按需在外部判重)
-- 本地/测试 ddl-auto=update 会自动加列,本脚本是生产升级与"实表应当长什么样"的评审权威。
ALTER TABLE service_item
ADD COLUMN fc_form_uuid VARCHAR(32) NULL COMMENT '表单中心表单 uuid(form-center 对外身份,保存前已向中心核验)' AFTER form_code,
ADD COLUMN fc_form_version INT NULL COMMENT '表单中心确切版本号(与 fc_form_uuid 成对;中心返回坐标不符即拒绝保存)' AFTER fc_form_uuid;
+82
View File
@@ -0,0 +1,82 @@
-- ============================================================
-- 办事大厅 · 对账任务接收与执行检查点 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;
-- ③<b>不建物理外键</b>——task_uuid 是中心任务的逻辑引用,本行要能在中心归档后继续留痕;
-- ④两条游标与水位同为 answer_uuid(32 位小写十六进制),锁步推进才是"多余行"可判的前提。
-- 边界纪律:本表只存<b>对账坐标与检查点</b>,不存答卷值文档——值文档归 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 '中心任务身份(<b>唯一键</b>:重复指令复用本行与检查点,不另起一轮扫描)',
`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 晚于它或为空的行<b>只报告、不退役</b>',
`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;<b>不含值文档</b>,有上限)',
`error_count` INT NOT NULL DEFAULT 0 COMMENT '错误条数',
`errors_json` MEDIUMTEXT NOT NULL COMMENT '错误明细(每条含 consumer/task/range/answer 四坐标,<b>敏感 values 永不进这里</b>)',
`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 行(值文档与令牌不进对账检查点)
+186
View File
@@ -0,0 +1,186 @@
-- ============================================================
-- 办事大厅 · 可信建件 / 答卷投影 / 事件台账 / 出站命令 DDL(MySQL 5.7 兼容复核版,TASK_2)
-- 范围:《answer-delivery-v2.md》§6~§8 = service_apply 加两列 + 建四张表
-- fc_apply_intent / fc_answer_projection / fc_answer_inbox / flow_apply_command
-- 依据:任务书 TASK_2 A.1/A.2/A.5(意图唯一键与"一次意图一个办件")、B.5(投影与台账分表)、
-- C.1/C.2(出站命令持久化 nextRetry、轮次与请求标识)
-- 用途:生产 ddl-auto=none 时的升级脚本;开发期(本机 bsch_svchall,ddl-auto=update)由 Hibernate
-- 自动追加,本脚本同时是"实表应当长什么样"的评审权威(沿 v3/migrate_v2 先例)。
-- 5.7 复核要点:①无 CTE/函数索引/生成列/CHECK;②utf8mb4 显式 CHARSET/COLLATE;
-- ③<b>不建物理外键</b>:intent→service_apply、projection→intent 都是跨表逻辑引用,
-- 回调/事件与用户请求并发写入时,FK 会把"先到的一方"变成死锁或顺序要求;
-- ④唯一键承担全部收敛职责(应用层不允许靠时序判断"我该不该再建一次")。
-- 收敛口径:<b>事件先到还是确认先到,结果都撞进同一组唯一键</b>——
-- fc_apply_intent(sch_id,submitter_id,item_id,client_key) 挡重复 POST;
-- fc_apply_intent(fc 意图键) 挡重复上下文;fc_apply_intent(answer_uuid) 与 (apply_id)
-- 挡"一份答卷形成两个办件";fc_answer_projection(answer_uuid,sch_id) 挡一份答卷两份本地投影;
-- fc_answer_inbox(event_id,consumer_app_key) 挡同一封事件写两次;
-- flow_apply_command(apply_id,attempt) 挡一轮两条命令、(request_token) 挡标识复用。
-- 双时刻纪律:中心侧 broker_accepted / consumer_applied 分列;本侧 inbox 只留"我这边怎么处置"
-- 与"回执上报状态",两边都不许替对方作证。
-- 存量:service_apply 两个新列对历史行恒 NULL,<b>不回填、不建双轨</b>(A.5 末句"新数据可追溯")。
-- 非幂等:执行前先 SHOW TABLES LIKE 逐张确认不存在,并 SHOW COLUMNS FROM service_apply LIKE 'fc_intent_id'。
-- ============================================================
ALTER TABLE `service_apply`
ADD COLUMN `fc_intent_id` INT DEFAULT NULL
COMMENT '可信建件意图 id(fc_apply_intent.id);NULL=旧链路/无表单事项直接提交。唯一性判据在意图侧 uk_intent_apply,本列只作反向可查',
ADD COLUMN `fc_answer_uuid` VARCHAR(32) DEFAULT NULL
COMMENT '建件依据的答卷 uuid(表单中心对外身份);旧链路恒 NULL';
CREATE TABLE `fc_apply_intent` (
`id` INT NOT NULL AUTO_INCREMENT,
`sch_id` BIGINT NOT NULL COMMENT '租户域(会话推导,浏览器不可注入)',
`site_code` VARCHAR(64) NOT NULL COMMENT '事项坐标之一(CMS 站点编码)',
`item_id` INT NOT NULL COMMENT 'service_item.id(逻辑引用,不建 FK)',
`item_code` VARCHAR(64) NOT NULL COMMENT '事项编码快照',
`item_title` VARCHAR(200) NOT NULL COMMENT '事项标题快照',
`submitter_id` INT NOT NULL COMMENT '提交者(CAS 会话)',
`submitter_name` VARCHAR(128) NOT NULL COMMENT '提交者姓名快照',
`client_key` VARCHAR(64) NOT NULL COMMENT '客户端幂等键(重复 POST 回到同一意图)',
`fc_form_uuid` VARCHAR(32) NOT NULL COMMENT '绑定表单中心 uuid(TASK_1 成对坐标之一)',
`fc_form_version` INT NOT NULL COMMENT '绑定表单中心确切版本(单传或改值都进不了这条链)',
`flow_guid` VARCHAR(32) DEFAULT NULL COMMENT '绑定流程线(发起前判过就绪;建件时快照进办件)',
`consumer_app_key` VARCHAR(64) NOT NULL COMMENT '本应用出站 appKey(=中心侧上下文归属)',
`fc_intent_id` VARCHAR(64) NOT NULL COMMENT '送给中心的业务意图键(自然键哈希,稳定而非随机;与本地自增 id 不是一回事)',
`context_token` VARCHAR(64) DEFAULT NULL COMMENT '中心签发的短时提交上下文令牌(交给被嵌页用,故明文存;30 分钟失效)',
`embed_origin` VARCHAR(200) DEFAULT NULL COMMENT '中心锁定的可嵌入父页面 origin(不是本地拼的)',
`fill_page_url` VARCHAR(500) DEFAULT NULL COMMENT '被嵌填报页地址(服务端按配置基址拼,浏览器给的路径不采信)',
`state` VARCHAR(16) NOT NULL DEFAULT 'ISSUED' COMMENT 'ISSUED / ANSWERED / APPLIED / VOID',
`expires_at` DATETIME DEFAULT NULL COMMENT '令牌有效期(过期即 VOID,不无限期占着可提交凭据)',
`answer_uuid` VARCHAR(32) DEFAULT NULL COMMENT '收据/事件确认的答卷 uuid(唯一:一意图一答卷)',
`answer_revision` BIGINT DEFAULT NULL COMMENT '确认时刻的快照修订号',
`content_hash` CHAR(64) DEFAULT NULL COMMENT 'fc.canon.v1 快照哈希',
`apply_id` INT DEFAULT NULL COMMENT '形成的本地办件(唯一:一次意图最多一个办件)',
`confirmed_at` DATETIME 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_intent_client` (`sch_id`, `submitter_id`, `item_id`, `client_key`),
UNIQUE KEY `uk_intent_fc` (`consumer_app_key`, `fc_intent_id`),
UNIQUE KEY `uk_intent_answer` (`answer_uuid`),
UNIQUE KEY `uk_intent_apply` (`apply_id`),
KEY `idx_intent_user` (`sch_id`, `submitter_id`, `state`),
KEY `idx_intent_expire` (`state`, `expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
COMMENT='可信建件意图(A.1/A.2/A.5:浏览器只交坐标+幂等键,建件唯一性由本表唯一键保证)';
CREATE TABLE `fc_answer_projection` (
`id` INT NOT NULL AUTO_INCREMENT,
`sch_id` BIGINT NOT NULL COMMENT '消费租户域(与 answer_uuid 一起构成唯一投影键)',
`answer_uuid` VARCHAR(32) NOT NULL COMMENT '答卷 uuid',
`form_uuid` VARCHAR(32) NOT NULL COMMENT '关系坐标:表单 uuid(A.5 要求关系上可追溯)',
`form_version` INT NOT NULL COMMENT '关系坐标:钉住的 schema 版本',
`revision` BIGINT NOT NULL COMMENT '中心侧单调快照修订号(低版本忽略、同版比 hash)',
`projection_version` BIGINT NOT NULL DEFAULT 0 COMMENT 'Local optimistic generation; independent of FC revision',
`content_hash` CHAR(64) NOT NULL COMMENT '中心哈希;本地按 fc.canon.v1 复算必须逐字一致',
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '墓碑(本地保留 revision,旧 created 复活不了它)',
`app_code` VARCHAR(64) DEFAULT NULL,
`code` VARCHAR(64) DEFAULT NULL,
`submitter_id` INT DEFAULT NULL,
`subject_id` INT DEFAULT NULL,
`status` VARCHAR(64) DEFAULT NULL COMMENT '答卷轻状态位(中心改状态会推新 revision 过来)',
`values_json` LONGTEXT DEFAULT NULL COMMENT '值文档全文(B.6:每条事件不回查答案值文档)',
`answer_no` VARCHAR(64) DEFAULT NULL,
`answer_create_time` VARCHAR(32) DEFAULT NULL COMMENT '中心给的 ISO 形态原文,不做本地时区换算',
`answer_update_time` VARCHAR(32) DEFAULT NULL,
`intent_id` INT DEFAULT NULL COMMENT '关联意图(唯一:一份投影只挂一个意图;NULL=事件先到的未关联态)',
`apply_id` INT DEFAULT NULL COMMENT '已形成的办件(可查回填,收敛判据在意图侧)',
`flow_guid` VARCHAR(32) DEFAULT NULL COMMENT '关系坐标:流程线(A.5 点名要留)',
`link_state` VARCHAR(16) NOT NULL DEFAULT 'UNLINKED' COMMENT 'UNLINKED / LINKED / APPLIED',
`conflict_flag` TINYINT NOT NULL DEFAULT 0 COMMENT '1=同 revision 异 hash,冲突待核验(不覆盖)',
`conflict_detail` VARCHAR(500) DEFAULT NULL COMMENT '冲突/退役留证',
`last_event_id` VARCHAR(36) DEFAULT NULL COMMENT '最近落库的事件 id(审计对应关系)',
`biz_ext_json` LONGTEXT DEFAULT NULL COMMENT '业务端扩展列:中心快照永不写(B.5)',
`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_projection_answer` (`answer_uuid`, `sch_id`),
UNIQUE KEY `uk_projection_intent` (`intent_id`),
KEY `idx_projection_state` (`sch_id`, `form_uuid`, `revision`),
KEY `idx_projection_apply` (`apply_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
COMMENT='答卷在本应用的唯一投影 + 意图↔答卷↔办件的独立唯一关联表(A.5/B.5)';
CREATE TABLE `fc_answer_inbox` (
`id` INT NOT NULL AUTO_INCREMENT,
`event_id` VARCHAR(36) NOT NULL COMMENT '信封事件 id',
`protocol_version` VARCHAR(32) NOT NULL COMMENT '固定 fc.answer.snapshot.v2',
`event_type` VARCHAR(16) NOT NULL COMMENT 'created / updated / tombstoned',
`consumer_app_key` VARCHAR(64) NOT NULL COMMENT '本应用 appKey(信封目标必须等于它)',
`consumer_school_id` BIGINT NOT NULL COMMENT '信封目标租户域',
`source_app_key` VARCHAR(64) DEFAULT NULL COMMENT '发布方身份',
`grant_version` INT DEFAULT NULL COMMENT '信封带的授权纪元(与本地核验快照比对,旧纪元不写)',
`form_uuid` VARCHAR(32) DEFAULT NULL,
`form_version` INT DEFAULT NULL,
`answer_uuid` VARCHAR(32) DEFAULT NULL,
`revision` BIGINT DEFAULT NULL,
`content_hash` CHAR(64) DEFAULT NULL,
`is_deleted` TINYINT NOT NULL DEFAULT 0,
`occurred_at` VARCHAR(40) DEFAULT NULL COMMENT '中心给的 occurredAt 原文(投递时间不进哈希)',
`state` VARCHAR(16) NOT NULL COMMENT 'APPLIED / IGNORED / CONFLICT / DEFERRED / REJECTED',
`detail` VARCHAR(500) DEFAULT NULL COMMENT '未应用原因(撤权暂缓/低版本/校验失败要点)',
`report_state` VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '回执上报:PENDING / SENT / FAILED',
`report_attempts` INT NOT NULL DEFAULT 0,
`next_report_at` DATETIME DEFAULT NULL,
`reported_at` DATETIME DEFAULT NULL,
`received_at` DATETIME NOT NULL COMMENT '本地接收时刻(与中心 occurredAt 不是一回事)',
`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_inbox_event` (`event_id`, `consumer_app_key`),
KEY `idx_inbox_answer` (`answer_uuid`, `revision`),
KEY `idx_inbox_report` (`report_state`, `next_report_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
COMMENT='事件接收台账(B.5/B.6:投影存最新事实,本表存每封怎么处置的,不提供删除方法)';
CREATE TABLE `flow_apply_command` (
`id` INT NOT NULL AUTO_INCREMENT,
`apply_id` INT NOT NULL COMMENT 'service_apply.id(业务键 bizId 的数值部分)',
`sch_id` BIGINT NOT NULL,
`site_code` VARCHAR(64) NOT NULL,
`biz_type` VARCHAR(32) NOT NULL COMMENT '与中心 uk 一致的业务类型键(svc-hall)',
`biz_id` VARCHAR(64) NOT NULL COMMENT '业务键=办件 id(与旧链路逐字相同,回调解析不变)',
`attempt` INT NOT NULL DEFAULT 1 COMMENT '轮次:仅用户明确授权的重提才 +1',
`request_token` VARCHAR(64) NOT NULL COMMENT '本次命令标识:同 token 重投=同一命令,中心不重启推进',
`flow_guid` VARCHAR(32) NOT NULL,
`payload_json` LONGTEXT NOT NULL COMMENT '出站载荷快照(重放不回读办件行)',
`state` VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING / UNKNOWN / DONE / FAILED / DEAD',
`retry_count` INT NOT NULL DEFAULT 0,
`next_retry_at` DATETIME DEFAULT NULL COMMENT '持久化退避到点(C.1 崩溃恢复就靠这一列)',
`remote_instance_id` BIGINT DEFAULT NULL,
`remote_status` INT DEFAULT NULL COMMENT '查/发起得到的远端真实状态(先回填再决定,不把 30/40 当重试)',
`remote_version` INT DEFAULT NULL,
`last_error` VARCHAR(500) DEFAULT NULL,
`sent_at` DATETIME DEFAULT NULL,
`settled_at` DATETIME DEFAULT NULL,
`intent_id` INT DEFAULT NULL COMMENT '可信建件意图(旧链路为 NULL,不回填历史)',
`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_command_attempt` (`apply_id`, `attempt`),
UNIQUE KEY `uk_command_token` (`request_token`),
KEY `idx_command_scan` (`state`, `next_retry_at`),
KEY `idx_command_biz` (`sch_id`, `biz_type`, `biz_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
COMMENT='出站发起命令(C.1/C.2:HTTP 不占长事务,未知态先查后发,进程崩溃后按 next_retry_at 恢复)';
-- 落地校对项(执行前后各核一遍,别靠"看起来一样"):
-- 1) 注:fc_apply_intent.fc_intent_id 是"送给中心的意图键"(VARCHAR),
-- 与同表自增主键 id(INT,被 fc_answer_projection.intent_id 引用)是两个东西,
-- service_apply.fc_intent_id 引用的才是后者(INT)——两侧列名同名不同型是刻意的历史命名,
-- 评审时按"哪张表的外侧"读,不要互换。
-- 2) SHOW CREATE TABLE fc_apply_intent\G -- 四个唯一键都在
-- 3) SHOW CREATE TABLE fc_answer_projection\G -- uk_projection_answer + uk_projection_intent
-- 4) SELECT COUNT(*) FROM information_schema.referential_constraints
-- WHERE table_name IN ('fc_apply_intent','fc_answer_projection','fc_answer_inbox','flow_apply_command');
-- -- 必须 0 行(无物理外键)
-- 5) SELECT COUNT(*) FROM service_apply WHERE fc_intent_id IS NOT NULL; -- 刚执行完必须 0(不回填)