文件
hyb-oa/.trellis/tasks/09-17-company-loan-principal-repayment/implement.md
T
wangsli和Claude Opus 5.5 b639422211 修复在途还款被出借人维护卡死并按审批时资格登记付款后事实(09-17-company-loan-principal-repayment)
- A:出借人存在 reserved 额度占用时,身份关联与收款资料维护在出借人锁内
  返回 409 loan.lender_in_flight;页面禁用并提示,修正账户弹窗误导文案
- B:回单 OCR 认领、人工回单参与人、核销按审批时资格认定,不再因原审批人
  或出借员工事后离职/撤权、主数据停用而卡死;保留身份核实、版本与付款快照
  一致、审批独立性及回单匹配校验;付款前环节仍校验当前资格
- 测试:隔离 PostgreSQL 合成全流程,含确定性并发交错、付款后独立性反例、
  离职后经 OCR/人工复核核销、PC/手机按钮禁用;变异测试均变红
- 恢复手册:资金类数据恢复后先暂停付款,对照银行流水补齐并核对占用再开放
- design 按环节修订资格规则

注意:C(同金额回单传错批次错误核销)已复现但尚未修复,借款功能不具备
真实还款验收条件;未推送、未部署。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 13:13:30 +08:00

18 KiB
原始文件 Blame 文件历史

2026-10-05 复核阻断 B1 修复与回单归属复现(用户批准 A/B/D/恢复手册 + C 复现调研;本地合成,未提交、未部署)

背景:同模型只读复核(页面与剩余资金流程)结论 BLOCK:在途还款期间做出借人身份/账户维护会使 identity_version 变化,之后登记付款、取消、确认失败全部被拒,已付款项永久无法登记;导出后原审批人或出借员工离职同样卡死。另报回单批次归属未校验。

实施:

  • A(services/companyLoans.js assertLenderNotInFlight,routes/companyLoans.js 两个维护接口):出借人锁内只要存在 reserved 分配即 409 loan.lender_in_flight。新建还款先锁出借人再占用,故与维护互斥;导出/上传不新增占用,不需改上传锁。出借人列表新增 hasInFlightRepayment,页面禁用两按钮并提示;账户弹窗误导文案已改。原因码已登记三处(server registry、client 映射、内容断言夹具)。
  • B(services/companyLoanPayments.js postPayment):回单 OCR 认领、人工回单参与人、核销三处按审批时资格认定——不再复核原审批人当前在职/角色、出借员工当前在职及主数据启用;仍校验身份已核实、身份/账户版本与提交时一致、付款快照收款资料一致、reviewed_by/reviewed_at 存在且审批人不是制单人/提交人/出借人关联员工(这三者之间可重合)。审批时资格依据:复核路由当时已用 activeReviewer 校验当前角色后写入 reviewed_by/reviewed_at。新建、复核、导出制单、确认银行失败保持当前资格校验;当前操作人仍须有当前权限与独立性。
  • D:scripts/qa/company-loan-http-fixture.cjs 原“在途改身份/改账户”两段按新规则重写(改用新出借人,原出借人因负例循环留有 unknown 占用、按设计不可维护),新增:在途(导出前后)维护被拒、取消/结清后可维护;维护与新建并发 3 轮不留陈旧版本占用;导出后原审批人与出借员工离职,经 OCR 与经人工复核两条路径均从上传回单走到核销成功;付款前导出仍要求审批人当前有资格。浏览器脚本新增 PC/手机在途时两按钮禁用。
  • 恢复手册:docs/trialrun/OPS7运维交接Runbook.md §5 增加资金类数据恢复后先暂停付款、对照银行流水补齐、核对余额与在途占用再开放。

验证(Docker 隔离 PostgreSQL,--network none,合成数据;需 PAYMENT_ORDER_PYTHON_BIN 指向 BIFF8 venv):

  • RUN_ISOLATED_LOAN_DB_TEST=1 RUN_ISOLATED_LOAN_BROWSER=1 node scripts/qa/test-company-loan-db.cjs:73 迁移、9 组 DB 约束、HTTP 21 组、浏览器 5 组全部 PASS,清理 PASS。
  • 4 个源码变异(OCR/核销/人工复核改回当前资格、去掉在途拦截)均使测试变红,源码已恢复并逐字比对。
  • server npm test 226 文件 4452 通过;prisma:validate、client typecheck/build、UI strict、错误码合同、文档审计(0 error)、敏感扫描、git diff --check 通过。
  • 未覆盖:独立复核仍为同模型;真实银行/OCR 样本;生产存量(待核实)。

C 复现结果(合成全流程,测试只记录结果不设守卫):

  • 同一出借人两笔 7.00 在途,P 的成功回单传到 Q:matched,Q 执行 200 核销成功——错误核销成立。
  • 旧 6.00 批次确认失败并取消后新建同额批次,旧批次晚到的成功回单传到新批次:matched,执行 200 核销成功——错误核销成立。
  • 调研:BIFF8 制单每行已有唯一客户流水号 批次号-R00n;借款附言固定为“归还借款本金”(报销附言含单号);OCR 只提取金额、币种、双方账号、状态和银行流水号,不提取客户流水号/附言;建行回单是否保留二者无仓库证据,需获准的真实回单样本确认。修法待用户确认,未实施。

同模型只读复核(同日,独立上下文):本轮 A、B 修复范围 PASS_WITH_NOTES,无阻断;借款功能整体尚未通过完整验收(C 错误核销已复现,仍阻断真实还款)。A 互斥成立(reserved 只经 prepareLoanRepayment 在出借人锁后创建;幂等重放不新建;迁移触发器禁止 settled/released 回退);B 未放宽过头(reviewed_by 全仓唯一写入点在复核路由且已过 activeReviewer;出借人无归档写入;停用主数据不降低伪造回单门槛)。按其第 4、5 条补强:

  • 并发测试改为确定性交错:独立连接持出借人行锁,两请求按指定顺序排队后释放;新建在先 → 维护 409 loan.lender_in_flight,维护在先 → 两者 200 且新建采用新版本;两种顺序均无陈旧占用。“检查挪到加锁前”变异确定性变红。
  • 付款后独立性反例:把已匹配回单批次的 reviewed_by 改为制单人或出借员工后执行均 403,恢复后正常核销;“去掉 assertApprovedIndependently”变异变红。 复核另记残余(未处理,计入下列阻塞):人工回单复核人在复核后、执行前离职会使执行被拒(与报销共用,推测可作废重传绕过,未验证);确认银行失败路径在出借员工离职后仍卡死,叠加 A 会长期冻结该出借人维护;付款后关闭提交人部门的付款批次灰度开关会拦执行(重新打开即可恢复)。

仍阻止真实还款验收:C 修复;unknown 状态人工解除、作废附件计数、晚到结果对账流程;B 未覆盖确认银行失败路径在出借员工离职后的卡死;AC5 真实样本;独立复核。

2026-09-18 共享付款回归与交接补充

09-06本地受管重验已覆盖报销主管/财务审批、财务同页进度、批次独立复核、回单人工更正/独立复核、付款核销及AP/票据归档两条主链;完整r6为3通过/1入口矩阵失败,矩阵另在r7通过。此证据支持共享付款旧来源回归,不代替本task真实银行样本、独立资金链审阅或公网验收。当前官方current在09-06,本task仍in_progress;上批review-candidate-r1保护保持。独立问题及真实批次/银行/K1/K2清单见retained-gates-closeout-20260917的independent-and-external-readiness.json,均未进行真实查询或操作。

2026-09-17 最终本地回执(当前入口)

T1–T4本地实现与合成闭环完成,官方current保持本task,状态in_progress。证据根为artifacts/qa-layout/payment-feedback-loan-slim-20260917;最终final-loan-browser退出0:空库72迁移、9组真实PostgreSQL约束/两连接并发、16组正式登录Fastify/Prisma/PG HTTP场景、3组桌面/手机/员工拒绝浏览器检查全部通过;仅本轮容器/浏览器/服务/临时目录清理PASS。最终桌面、手机截图已查看,沿原有样式且银行字段脱敏;手机详情仍在移动入口。

报销原地进度progress-green-r215/15;loan-unit-r35文件212/212,ERP退出后的付款路由另在erp-regression-r2128/128通过。final-client-buildtypecheck/Vite/PWA、final-prisma、final-module-load通过。ERP共享回归按r1/r2已通过文件及r3修正后的63/63分别引用,不将分轮结果相加当全量。strict原因码850注册/886使用/legacy0/findings=[]。旧Browserslist/punycode提示和22个历史未登记文档不扩治。

验收支持与开放项

验收 当前证据与结论
AC1 正式财务登录、历史两笔借入、凭据唯一、四类材料、余额确认及系统外已还证据通过。
AC2 两借款一付款、部分还本、精确金额、跨来源限制、超额拒绝、两连接占用和同requestKey并发幂等通过。
AC3 显式财务权限、经理/CEO独立审批、排制单/提交/收款人、无人可批与身份/账户版本变动拒绝通过。
AC4 审批/BIFF8导出不减本金,在途不释放;安全取消、独立确认失败、成功原子claim/核销及真实数据库审计失败回滚通过。
AC5 合成自动worker/双人手工回单、双方账户/币种/金额/pending负例、跨借款/报销交易去重通过;真实银行/OCR样本接受性仍待验,完整AC保留开放。
AC6 原借款分配、余额截止状态、独立会计交接与材料权限通过;不把无OA记录当无银行还款。
AC7 付款/制单/回单及ERP触达业务回归通过,PC/手机正式登录同链与普通员工403通过,默认银行信息脱敏。
AC8 迁移/锁图/回退影响已落盘;独立只读复核PENDING,08-23实际保管/恢复事实与银行样本/迁移发布条件开放。

主会话只读自审核对了显式角色、账户/身份版本、创建lender→loan锁序、既有batch→receipt→lender→loan核销锁序、loan错误整体抛出回滚、unknown不释放及ERP共享边界;没有把此自审称作独立复核。待办姓名区分可处理人与站内提醒对象,仅wecom_app_sent成功记录可描述个人发送,多接收人anchor不推断全部送达;真实批次姓名/企微发送事实未查。

最终closeout-governance/process/docs与两个task validate通过;文档仅原22项登记warning。保护核验protection-final.json:HEAD/index未动,22个目录6447个旧材料SHA与精确文件集合全部原样;177个旧差异151个逐字不变,26个在本批授权路径叠加修改。ERP退出留下的两处尾空格已修,最终diff和敏感回执另存。本批独立审阅入口为review-candidate-r1,仅按baseline快照计算本轮增量,当前源码快照、删除清单与定向证据分开;manifest外部完整性回执不自引用。独立状态PENDING。未读取真实配置/密钥/备份,未连接生产/ARM,未重跑旧恢复,未提交/推送/部署/真实通知或付款。下方阶段过程保持历史原文,不再代表当前未实现状态。

2026-09-17 T3/T4接续回执

T3及前端台账/付款分配/材料/会计交接已落地。loan-payment-http-r3:72迁移、9组约束、12组真实认证HTTP通过;loan-unit-r3:付款/制单/回单/通知5文件212项通过。loan-browser-r2桌面和手机走到还款详情并留截图,末项员工拒绝页断言错用地址导致整轮FAIL;已改为实际/403,不计该轮完整通过。新增跨借款/报销交易去重、金额/币种/双方账户/在途负例与同键并发,重跑中。

审批姓名区分当前可处理人和站内通知对象;企微发送只认wecom_app_sent成功,多接收人anchor记录不推出全部送达。不读真实delivery payload或配置。旧日期/恢复状态为历史规划记录,以本顶部实际回执为准。独立只读review PENDING;真实银行接受性、录入/付款/发布未执行。

2026-09-17 当前实施回执(取代下方旧 planning/未实施描述)

官方current已start并回读本task。T1增量schema、迁移、延迟聚合约束已落地;独占PostgreSQL无网络/无宿主端口/只用合成库。初始化socket路径已修正,失败轮次原样保留。loan-db-prototype-r5 全部72迁移和9组约束通过,包括实际双连接锁等待与超额拒绝;容器清理通过。

T2台账/加密出借人/身份确认/原凭据唯一/材料上传下载/期初余额确认已实现。loan-ledger-http 真实密码登录、正式auth插件及Fastify/Prisma/PG验证5组接口场景通过,同时重跑9组DB检查。普通员工、经理、system_admin及仅admin不能管理借款;期初余额少于原本金须补系统外已还证据;审计失败由数据库触发,业务写整体回滚。Prisma validate/generate通过,generate使用本树既有链接依赖目录;不安装新依赖。

T3正在接入现有付款消费者:专属还款申请、多借款一付款、占用/独立复核、回单worker/人工例外、原子claim和本金核销;尚未完成本切片验证。T4前端和完整验收待执行。AC不提前勾选,独立只读复核PENDING,真实银行样本、发布、实际录入/付款仍未授权。

实施顺序(仅规划,尚未开始)

2026-09-17 执行优先级更新

用户要求尽快完成既有借款方案。先完成09-04两处真人反馈修复,随后官方start本task,本地T1约束/并发原型→T2台账与证据→T3本金分配及既有付款来源全链适配→T4合成闭环和旧报销回归连续推进。范围按原8项AC,不把文档齐全当产品完成。原“仅规划/不实施”的优先级由本节替代;真实操作与独立/发布门禁仍有效。

只使用运行独占的本地PostgreSQL和合成加密材料,禁外部通知/OCR/银行调用,不连接生产/ARM或读取真实配置密钥备份。无现成真实身份/付款事实时保持未确认,不用admin兜底。inline下独立只读复核PENDING,主会话不得冒称独立通过;未独立审查的新资金链不发布。

2026-09-17 T1本地技术准备回执(最新入口)

已按09-08后期规划细化design第8–10节:四核心表+专属LoanEvidence、既有PaymentBatch增量、金额/唯一/FK/删除保护、余额公式、权限/接口/并发矩阵。当前execute的来源锁早于receipt advisory,不能直接复用为借款锁序;已明确新来源分派和所有写路径的顺序。文档提出取消后新申请保留历史、unknown银行结果不释放/重付,原8项AC未勾选。

  • T1a字段/约束/锁图/消费者与测试合同草案,源码引用已核。
  • T1b获得进入条件后,以最小合成schema证明CHECK/部分唯一/延迟触发器/FK和两连接并发;尚未创建schema、迁移或数据库。
  • T1c独立只读审阅具体方案与原型,不把主会话对照源码计独立通过。
  • T2/T3/T4仍未开始;原恢复风险和产品实施准入保持,当前planning不变。

准入

本task为09-08协调表的C阶段,不是其子任务。当前仍planning;本轮官方current在09-05执行本地治理,未切入本task。创建与规划补充不授权借款业务实现。先审阅本PRD/design,核08-23关键恢复状态和09-05到期治理结果;受保护操作等待不要求22项历史task全绿,但实际密钥不可用/敏感恢复失败没有处置时不直接扩大付款数据范围。

顺序与验证

  • 取得本task方案实施批准,用官方task.py start切换并回读;检查执行树、AGENTS、相关backend/approval/receipt/security/frontend spec及未提交差异,记录只允许本批改动的路径。
  • 清点所有PaymentBatchEntityType消费者,设计最小Prisma模型/增量迁移、唯一约束、FK和权限矩阵;确认制单/复核/登记锁序与旧报销兼容,不引入通用工作流引擎。
  • 实现出借人/借款登记、证据与余额确认,先做无权限、身份不明、重复/历史登记和可用额度负例,再做正向。
  • 实现一申请一笔付款及多借款分配,复用批次独立复核;补同笔借款并发、取消/失败/未知在途和幂等断言,不能只用顺序mock证明防超付。
  • 显式接入用途/备注、快照/来源重验、回单队列范围/匹配/人工复核/全局交易去重;未支持分支拒绝,错金额/账号/失败交易没有人工旁路。
  • 接入既有风格的台账/还款列表与详情及会计交接记录,保持财务/审批人/普通员工可见范围和PC/手机同功能。
  • 在合成独立库运行真实路由、登录浏览器和DB同链;新业务反例与原报销/差旅、动态错误合同回归分别记录,不使用真实公司借款样本或真实付款。
  • 执行cd server && npm run prisma:validate、相关node --check及新增Vitest;执行cd client && npm run typecheck && npm run build及定向Playwright;新增测试文件及精确命令在实施时登记,当前不声称存在或已跑。
  • 执行原因码、Trellis/process/docs/敏感扫描及git diff --check;冻结候选并取得真实外部只读内容复核,主会话检查不冒充独立验收。
  • 新迁移/真实样本OCR、提交/推送、发布、真实资料导入及真实付款分别按具体范围取得批准;部署后只读验收通过才能告知公网可用。

停止与交接

收款人自审、超额占用、在途可重复付款、旧来源回归失败、秘密/真实资料进入日志或不可回退的迁移方案均阻断候选。找不到合格审批人时明确业务阻断,不通过改角色或admin兜底完成样例。业务少/无自然付款不制造交易测试;需要真实样本时先单独列授权范围。

08-23和09-05若新增重大风险,先回09-08协调;本task不顺手改生产密钥/恢复工具或替其他task清理。原始PRD验收保持未勾选,正式实施结果、测试、独立审查和发布分节记录。K0已完成本地验证,K1/K2仍无实际保管/真实解密证据;新会话先核09-08协调入口,不因文件齐全或K0通过就先开发借款模块。

2026-09-17 规划补充与开发前审阅包

用户要求对未完成工作先补规划;本轮只更新本task计划,不新增枚举/表/接口/UI,不读取真实借款资料。K0的30项合成结果不能替代保管事实;09-05本地日期/错误治理候选也未部署。恢复风险处置、方案实施审阅及独立复核依然是进入条件。

  • 复核当前枚举只有reimbursement/travel_order,以及readiness、回单验证、人工复核和OCR worker对reimbursement的明确限制;记录下方消费者分组,不借新增来源解冻travel_order。
  • 补迁移/锁序的待审技术切片和验收出口,前端沿既有布局,不新设计视觉体系。
  • 技术定稿T1:提出四类核心表及专属借款证据关系的字段/索引/FK/版本设计;按当前PaymentBatch/ReceiptClaim实际锁图审查,先以最小合成schema验证约束及并发,再实现接口。
  • T2:出借人/原借入登记及财务确认,只用合成资料;不自动产生付款或新增真实收款。
  • T3:新来源显式支持/拒绝矩阵、额度占用/幂等/状态与回单闭环;逐消费者负例后再接页面,不凭新增enum编译成功宣称支持。
  • T4:真实认证的本地路由/数据库/浏览器全链及旧报销/差旅回归,形成独立审阅候选;新来源OCR真实样本和生产录入仍另批。

这四切片复用原8项AC,不降低审批人独立性、并发不超付、在途保持占用、全局交易去重和审计原子性。日期/错误债务后续批次、恢复事实与借款开发分别交付,不要求22个历史task全部结案,但也不默认接受未知密钥风险。