# 预算池占用与释放并发一致性修复 ## 2026-10-11 收口归档 预算池并发修复及独立复核完成后随 `1f8d5b65` 上线(状态同步 `2769408b`),包含于当前生产 `4a5cd8f2`;B6“随正式候选完整门禁”由其后历次完整门禁与发布满足。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。 ## Goal 修复业务独立补审发现的预算池丢失更新与陈旧超额判定;统一保证报销、采购及冻结差旅共享服务的并发一致性,以本地合成数据验证。 ## Requirements 本任务来自业务独立补审 `artifacts/qa-governance/business-followup-claude-review-20260919T023537Z.txt`。用户要求将三组同类问题单独规划、一并修复,并于2026-09-19明确批准本方案实施。09-18 的 P9 性能优化通过,不代表旧预算并发缺陷不存在。 ## Scope - 报销提交、采购/差旅共享占用与释放、公务卡授权/冲正中的预算池计数必须在并发下保持一致;相关调用者必须遵循统一事务协议。 - 超预算判断必须基于获得预算池写保护后的最新状态;只把计数改成原子加减、保留陈旧预算判定不算完成。 - 保持现行预算池选择、控制模式/开关、错误语义、金额精度、幂等、释放不低于零及审批/审计回滚合同。 - 差旅仍冻结;仅允许共享服务的安全修复及本地合成验证,不恢复入口、不重启冻结 E2E、不删除其历史代码或结果。 R1:不同实体并发命中同一池时,最终用量等于按合法串行顺序应用全部已提交增减后的值,不能覆盖他人的写入。 R2:现行 block 策略下,并发竞争剩余额度不能双双通过;warn/off 仍按原规则运行。开关前后、自动选择与显式预算池必须分别验证。 R3:流水、用量、单据/工作流状态和原本要求原子的审计共同提交或回滚;同一实体重复占用/释放仍按现行合同拒绝或幂等。 R4:锁序统一,涉及多个池时按稳定 ID 顺序获取;并发故障测试使用真正隔离 PostgreSQL,不用 mock 证明数据库行为。 ## Out of Scope 不修复其他旧代码或警告;不修改生产用量、不推断历史漏记金额、不增加自动对账回填。无数据库 schema/历史迁移重写、预算策略变更、冻结差旅恢复、生产/ARM/真实配置密钥备份读取、真实通知付款、恢复演练、提交推送部署或编号启用授权。 ## Acceptance Criteria - [x] B1:三组入口全部使用同一预算池并发保护约定;调用者盘点包含报销、采购、差旅及工作流释放、公务卡授权/冲正,没有遗漏写入口。 - [x] B2:真实隔离 PG 证明同池正向并发无丢失更新、占用与释放交错结果等于合法串行结果、不同池可独立进行;原实现有可复现失败证据。 - [x] B3:受 block 规则约束的同池竞争只有额度内提交成功;同时覆盖 legacy/precheck 两态以及显式/自动池,warn/off 行为不变。 - [x] B4:重复调用、释放归零、空/失效池和中途异常均符合原合同;失败无孤立流水、用量、工作流或成功审计。 - [x] B5:单池和多池锁序、外键关联写入及既有实体锁不形成新增等待环;并发测试通过同步屏障或锁状态观测建立交错,不靠任意 sleep 猜测。 - [ ] B6:受影响 Vitest、语法/Prisma 与治理检查通过;业务代码变化后新完整候选沿 09-06 现行门禁重验,旧 r6 不能抵扣新代码。保留 12 公网/2 冻结归属、并发、预算和零重试合同。 - [x] B7:实际补丁经独立只读复核;inline 主会话不冒充 reviewer。本地完成不等于可以发布。 ## 本轮验收执行调整(2026-09-19) 用户在本轮运行中明确指出重复完整浏览器验收浪费,主会话据此停止剩余全量并重新评估影响范围。本补丁阶段以已完成的真实 PG 28项、受影响单测526项、后端全量4446项及服务回归证明本地修复;客户端仅新增错误原因镜像,没有页面/交互改动。新增锁后提交、block/warn、新旧开关、交叉业务并发及失败回滚由实际 Fastify handler/Prisma/PG 覆盖,未发现需要另起浏览器定向运行的缺口。 B6 中的发布候选完整门禁义务保留为后续准备发布时执行,本轮不补勾、不以旧 r6 或中止的 browser 阶段抵扣。不再因本轮记录/交审材料变化重跑业务测试。B7 已由首批独立审查及2026-09-19两项修正补审完成;Claude实跑两份文件928项全通过,结果见 implement.md 最新记录。 ## Test Plan 按 design.md 的场景矩阵及 implement.md 的红绿/保护清单执行;现有预算、费用预检、报销/采购审批测试优先复用。真实 PG 仅使用原受管合成环境及已在本地的依赖,不读取真实配置来寻找数据库。