feat(idempotency): 为 30 个写端点实现 Idempotency-Key
规范第八章要求 7 类写端点支持 Idempotency-Key,第 146 章验收口径为 「最终不能生成重复核心数据」。检修前的实际状态:后端零处读取该头, 小程序 25 处调用点发送全部石沉大海。 一处实现、零业务代码改动、逐端点声明: - V045 新建 idempotency_records + 唯一索引 uk_idempotency_scope - V046 修正 actor_id 列类型 VARCHAR -> uuid(见下) - IdempotencyInterceptor:先占位后执行 / 重放首次响应 / 处理中回 409 / 孤儿占位 300s 可接管 / 失败释放占位 - IdempotencyResponseCacheFilter:缓存响应体供逐字节重放 - WebMvcConfig:本项目第一个通用 WebMvcConfigurer - @Idempotent 声明 30 处(含补 CheckinController 手工签到的漏标) - GlobalExceptionHandler:IllegalStateException(30 处业务状态冲突)与 DuplicateKeyException(并发重复提交的最后防线)此前均无映射 → 落 500, 而 500 的语义是「可重试」,调用方于是重试并制造更多重复。现映射为 409; 刻意不映射父类 DataIntegrityViolationException(CHECK 违反应由 500 暴露)。 - schema-h2.sql 补 idempotency_records V046 的必要性:项目全局注册的 UUIDStringTypeHandler 对任何合法 UUID 形式的 String 参数以显式 uuid 类型发送,而 SQL 侧 stringtype=unspecified 只影响以 unknown 类型发送的参数,覆盖不到它。因此真正的不变量不是「JDBC 参数配好了」, 而是「列类型必须是 uuid」。V045 初版把 actor_id 建成 VARCHAR(64) 时, 带幂等键的请求在真实 PG 上全部 500。 全库扫描确认该不变量已收敛:所有 *_id VARCHAR 列中 actor_id 是唯一违规, client_message_id / provider_request_id 为外部 id, resource_id / source_id 在 Java 侧零处作查询条件。
This commit is contained in:
@@ -522,3 +522,24 @@ CREATE TABLE appeals (
|
||||
CONSTRAINT chk_appeal_type CHECK (appeal_type IN ('ban_appeal', 'suspend_appeal'))
|
||||
);
|
||||
|
||||
|
||||
-- Phase 24/幂等检修: idempotency_records(通用 Idempotency-Key 落地表)
|
||||
-- 对齐 V045__add_idempotency_records.sql。此前该表不存在于测试库,
|
||||
-- 任何走幂等拦截器的写操作在 H2 下都会报表不存在 —— 与 appeals 当初
|
||||
-- 整条真实 DB 路径零覆盖是同一类盲区。
|
||||
CREATE TABLE idempotency_records (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
actor_id VARCHAR(64) NOT NULL,
|
||||
idempotency_key VARCHAR(255) NOT NULL,
|
||||
endpoint VARCHAR(255) NOT NULL,
|
||||
state VARCHAR(16) NOT NULL DEFAULT 'PROCESSING',
|
||||
response_status INTEGER,
|
||||
response_body CLOB,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
CONSTRAINT chk_idempotency_state CHECK (state IN ('PROCESSING', 'COMPLETED'))
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX uk_idempotency_scope
|
||||
ON idempotency_records (actor_id, endpoint, idempotency_key);
|
||||
|
||||
Reference in New Issue
Block a user