fix(infra): enforce one open request

新增 V8 部分唯一索引 uq_req_open:(REQ_TYPE, OPERATION_DAY, SENDER) 仅在 PENDING/SENT 唯一;建索引前用 DO 块检测存量重复组并以带业务键的异常中止迁移,禁止静默删数据。保留 idx_req_open 服务含关闭态的查询;不引入运行时协调器(归 US-08)。

验证:全量 ./gradlew test 126 tests / 0 fail;索引与拒绝第二条开放请求的断言在 FlywayMigrationTest(需真实 PG,本机无 Docker 时按既有 assumeTrue 跳过)。
This commit is contained in:
windyboy
2026-09-12 20:35:58 +08:00
parent 9554b1aae8
commit 21739a439d
2 changed files with 69 additions and 1 deletions
@@ -0,0 +1,34 @@
-- =====================================================================
-- V8REQ_TRACK 开放态部分唯一约束
-- ---------------------------------------------------------------------
-- 只动自有 PostgreSQL;共享 MySQL 不建表、不改结构。
--
-- design「记录模型」:同一 (REQ_TYPE, OPERATION_DAY, SENDER) 只允许一个开放请求,
-- 且仅对开放状态(PENDING / SENT)生效。这里只建立数据库防线;「新请求原子过期旧请求
-- 并登记自身」的运行时流程仍归 US-08 协调器([G-REQ-TRACK])。
--
-- 存量若已有重复开放组,不静默删数据:带业务键的诊断直接让迁移失败,由人工处置。
-- 保留既有普通索引 idx_req_open(仍服务含关闭态的查询)。
-- =====================================================================
DO $$
DECLARE offenders text;
BEGIN
SELECT string_agg(format('%s/%s/%s', req_type, operation_day, sender), ', ')
INTO offenders
FROM (
SELECT req_type, operation_day, sender
FROM req_track
WHERE state IN ('PENDING', 'SENT')
GROUP BY req_type, operation_day, sender
HAVING count(*) > 1
LIMIT 20
) dup;
IF offenders IS NOT NULL THEN
RAISE EXCEPTION 'V8 aborted: duplicate open REQ_TRACK rows for (req_type/operation_day/sender): %', offenders;
END IF;
END $$;
CREATE UNIQUE INDEX uq_req_open
ON req_track (req_type, operation_day, sender)
WHERE state IN ('PENDING', 'SENT');