借款出借人增加员工与公司组合数据库唯一约束(09-17-company-loan-principal-repayment)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

- 新迁移 20261011010000_loan_lender_employee_unique:(legal_entity_id, employee_id) 唯一,归档计入、无员工出借人不冲突;生产只读排查重复 0 组
- 触到约束时返回原有 loan.employee_lender_conflict,不报 500 或误报请求重复;员工行锁与代码检查保留
- 测试改为断言数据库拒绝模糊旧数据,补空员工共存与归档占用;变异 12 项含去索引与数据库兜底两种运行
- PRD 补本迁移回退说明;独立只读复核无阻断,发布窗口需重跑重复计数(AC59)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
2026-10-11 13:21:08 +08:00
共同撰写人 Claude Opus 5.5
父节点 5469db585c
当前提交 36b949ecf6
共修改 8 个文件,包含 91 行新增和 10 行删除
@@ -1,3 +1,19 @@
## 2026-10-11 F:员工与公司组合唯一约束——实施(用户已批准;未提交)
- 迁移 `20261011010000_loan_lender_employee_unique`(迁移总数 83→84):`CREATE UNIQUE INDEX "loan_lenders_legal_entity_id_employee_id_key" ON loan_lenders(legal_entity_id, employee_id)`;Prisma `@@unique([legal_entity_id, employee_id])`。普通唯一约束,归档行计入、空 `employee_id` 互不冲突,与现行代码规则一致;不改数据、不回填。
- 冲突提示:新增 `isEmployeeLenderUniqueViolation`(同时识别 `meta.target` 与驱动适配器的约束字段/名称)。从员工创建路径先判它再判请求键重复;旧“确认出借人身份”入口在事务外映射。触到约束时返回原有 409 `loan.employee_lender_conflict`,不报 500,也不误报为 `loan.duplicate_registration`。原员工行锁与代码检查保持不变。
- 测试:原“直接写入模糊旧数据”的夹具改为断言数据库拒绝(P2002,且被新判定识别);新增两条无员工出借人可共存、归档关联继续占用组合(应用与数据库两层)。变异 11→12:`legacy_duplicate` 在临时删除索引的隔离库中运行,证明代码检查仍有效,运行后解除重复关联再重建索引;新增 `legacy_duplicate_db_backstop` 在索引存在时运行同一变异,整套夹具通过,证明数据库单独兜底也返回同一冲突提示。
- 发布要求:部署前在同一发布窗口重跑下节只读计数确认 0 组,部署按 1 个待执行迁移校验。
- 验证:员工专项 + 变异 12/12(`employee-only-mutations-r1.log`);完整回归 `full-regression-r1/r2.log` 各退出 0、84 迁移、12 阶段、去重 247 项(新增断言并入原检查项名,项数不变),自有资源清理 PASS;`loanLenderAcknowledgement` 单测 28/28;Prisma validate、前端构建通过。首次后台回归因相对日志路径在并行步骤切换目录后重定向失败、未实际运行,非测试失败。只读复核回执 `review-20261011-codex.json`:无阻断项,20 个内存探针通过;P2 迁移回退说明已补入 PRD F 节。证据在 `artifacts/qa-flow/loan-lender-employee-unique-20261011/`。
## 2026-10-11 F:员工与公司组合唯一约束——存量排查(只读)
用户授权本轮生产只读查询。经生产主机按部署项目标签定位唯一数据库容器,在 `BEGIN TRANSACTION READ ONLY … ROLLBACK` 内只返回计数,不输出 ID、姓名或账号,未写库:出借人共 1 条,关联员工 1 条,归档 0 条;同一公司与员工的重复组合 **0** 组。
代码现状:从员工创建、关联已有出借人、从员工资料刷新三条写入路径均先锁员工行,再用 `assertUniqueEmployeeLender` 拒绝同公司同员工的另一条出借人(`loan.employee_lender_conflict`);查询有意包含已归档出借人,即现行规则为“同一公司与员工最多一条,归档也计入”。代码中没有归档出借人的路径;员工停用/归档后原关联保留并继续占用。手工创建的无员工出借人 `employee_id` 为空,不要求唯一。
约束建议(待批准后另行实施):普通唯一约束 `(legal_entity_id, employee_id)`。PostgreSQL 唯一约束中空值互不冲突,无员工出借人不受影响;归档行计入,与现行代码一致,不采用只约束未归档行的部分索引(会放宽规则)。实施需新迁移,部署前须在同一发布窗口重跑本只读计数,确认仍为 0 组。
## 2026-10-10 E:追回证据刷新提示与员工资料误报(已实施、本地验证完成;未提交)
用户批准两项方案后实施,基线 `91fe31f0`;未提交、推送、部署或连接生产,无新迁移。资料拦截规则先由实施会话自查;2026-10-11 另一会话独立只读复核结论 `REVIEWED_NO_BLOCKER`,无阻断项,补跑 12 组服务层内存探针与电脑/手机 4 组组件浏览器检查,两轮 247 项回归与 11 项变异只核对日志未重跑;回执 `review-20261011.json` 中 7 个文件摘要与提交前工作区一致。复核限制:组件补验的 API、身份为合成替身;上线前打开的旧 v1 预览在上线后保存会被要求重新核对(有意拒绝旧版本);未跑完整发布门禁。