文件
hyb-oa/.trellis/tasks/09-05-error-explainability-payment-readiness/prd.md
T
wangsli 05d2a3f78a
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s
docs: 记录公网验收与已知事项(09-05-error-explainability-payment-readiness)
2026-09-05 19:08:42 +08:00

12 KiB

错误可解释性与付款就绪治理

Goal

把“系统知道为什么不能继续、真人只看到参数校验失败”的缺陷从逐页补文案改成可持续的错误合同,并以付款批次为第一个完整样板:财务在点击前就知道付款资料是否齐全、缺什么、由谁补;点击后的服务端二次校验与页面提示必须来自同一份实时判断。

Background

  • 2026-09-05,真实单据 0014 在生产付款页已能进入待处理队列,但财务选择银行转账并点击“创建并提交复核”后只看到“参数校验失败”。只读诊断确认请求未创建付款批次;报销人缺所属公司、开户银行和银行卡号,公司侧已有有效建行付款账户,经理/CEO 复核人存在。
  • 服务端创建前已检查来源状态、公司归属、收款账户、公司付款账户、活动批次占用和复核人;客户端未覆盖付款原因,因此回退到通用技术文案。
  • 这是十一天内第三次“参数校验失败”阻断真人主流程。此前 B2 已验证“一份判断同时驱动展示、点击拦截和 mutation 二次拦截”能防漂移;本任务把该模式推广到付款就绪。
  • 当前文本基线:服务端同一通用校验文案 365 处,buildValidationError( 518 次,测试目录中该字符串 20 次。数量用于确定渐进迁移边界,不代表独立业务原因数量。
  • details key 不能作为原因合同:同一个 legalEntity、status 或 review 在不同业务域含义不同。稳定合同必须是带业务域的原因码,例如 payment_batch.legal_entity_required。
  • 生产资料补录和真实付款继续由授权人员操作;本任务不得读取、记录或修改真实银行卡号,不把伪填建行资料作为恢复办法。

Scope

本 task 包含三项互相支撑但可独立回滚的交付:稳定业务原因码及增量 parity 门禁、首批真人主链路错误迁移、付款批次实时 readiness 与就近反馈。它不建设统一待办,也不扩展银行付款能力。

Requirements

R1 稳定原因码与渐进 parity 门禁

  1. 新增服务端业务错误原因注册表。每个原因至少记录稳定 reasonCode、业务域、普通用户是否可达、默认安全中文提示/恢复方向;原因码采用 <domain>.<reason>,不得复用任意 details key 冒充合同。
  2. 原有 HTTP status、顶层 error.code 和兼容 details 保持;已迁移错误在 details.reasonCode 增加稳定原因码。旧客户端可继续依赖既有字段,新客户端优先消费原因码。
  3. 新增 parity 检查:已注册用户原因必须有客户端安全映射;服务端使用的原因必须已注册;新增或修改通用校验不得绕过原因码;客户端不得有孤儿/拼写漂移;未知原因走安全兜底且不展示原始 details、内部路径或 UUID。
  4. 存量通用错误使用版本化遗留基线渐进收敛。例外按业务域/文件组记录原因、负责人、到期日和行号无关的调用指纹;新增指纹默认失败。不得以整目录永久忽略、只比较数量或模糊正则代替指纹门禁。
  5. 首批真人主链路不得进入例外:报销提交/编辑/票据关联,付款创建/复核/登记,请假提交和考勤修正提交。范围内每个用户可达校验分支必须有注册原因码、准确中文提示和测试。
  6. 遗留例外是迁移债,不是管理页永久展示技术文案的许可。到期后门禁必须失败;续期需要新理由和明确期限。

R2 用户可行动的错误反馈

  1. VALID_001 未映射时不得再把服务端的“参数校验失败”直接展示给普通用户。兜底必须说明操作未完成、建议核对页面提示或联系管理员,并附现有 request ID;没有 request ID 时不展示空编号。
  2. 具体提示必须回答“哪里不满足、下一步怎么恢复”,并位于相关字段、条目或动作附近。页面顶部错误和 toast 只作补充。
  3. 错误不得只靠红色表达;动态错误使用可访问告警语义。禁用动作的原因必须在按钮附近持续可见,并通过 aria-describedby 或等价语义关联。
  4. 20 处现有测试字符串逐处处置,不用宽泛正则消除失败:客户端 4 处模拟响应改为原因码 fixture;首批主链路改断言 reasonCode、兼容 details 和 HTTP status;非本批路径明确对应遗留例外和到期日;持久化失败摘要/批处理 reason 单独判断兼容性。

R3 付款批次实时 readiness

  1. 抽取唯一服务端付款就绪解析器,返回 { ready, blockers }。blocker 使用 payment_batch.* 原因码和最小安全定位信息,不返回完整银行卡、密文或原始员工对象。
  2. readiness 至少覆盖:来源状态/已付款、付款能力部门门禁、活动批次占用、单一公司归属、收款人银行资料、公司付款账户及解密可用性、非制单人的合格经理/CEO复核人。
  3. 候选列表对单条候选实时计算 readiness;缺资料的记录仍可被发现,但不可选择,并就近显示缺项与恢复责任。财务无员工维护权限时只提示联系管理员,不暴露无权入口。
  4. 多选付款必须用同一解析器计算组合 readiness,覆盖跨公司、并发占用和复核人等组合条件;不能因为每条单独就绪就推断组合一定就绪。
  5. 创建接口在事务、必要锁和最新数据下再次调用同一解析器并 fail closed。候选、组合预检和创建不得各写一套判断;并发变化返回同一原因码。
  6. readiness 每次按当前事实派生,不新增数据库字段、不落库、不长期缓存。资料或账户变化后,下次查询自然反映最新结果。
  7. 银行转账只按真实资料判断。开户行不是建行时不得建议伪填;是否支持跨行付款属于独立产品能力。

R4 付款页面反馈

  1. 正常候选不增加解释性噪音;有 blocker 时在候选行展示精确缺项,复选框不可用且原因可被键盘和读屏取得。
  2. 已选组合 readiness 位于主动作紧邻上方;阻断时主动作禁用,提示与服务端二次拦截来自同一组 reasonCode 投影。
  3. 正常银行付款 fixture 保持可选择、可创建并提交复核;新增门禁不得误伤资料与复核人完整的单据。
  4. PC 是批量付款主路径;移动端不新增复杂组批能力,但已有移动付款路径不得显示原始技术错误或回归。

Acceptance Criteria

  • AC1:存在可机器读取的原因码注册表;同一 details key 在两个业务域使用不同 reasonCode,parity 分别验证,证明不是按 key 虚假覆盖。
  • AC2:未注册原因、缺客户端映射、拼错 reasonCode、新增无原因码调用、遗留指纹变化、例外过期六类 self-test 均非零退出;正常注册与有效遗留通过。
  • AC3:报销提交/编辑/票据、付款创建/复核/登记、请假提交、考勤修正提交的用户可达错误为零例外;报告按域输出注册、已迁移、遗留和过期数量。
  • AC4:未知 VALID_001 在 PC/移动显示安全可行动兜底;有 request ID 时展示编号,无编号时无空占位;“参数校验失败”、原始 details、UUID、接口路径不出现在 UI。
  • AC5:20 处既有测试字符串有逐项处置清单;主链路断言具体原因码/中文,非本批项对应有效例外,无宽泛正则掩盖差异。
  • AC6:付款单条与组合 readiness 覆盖正常、状态异常、活动批次、缺公司、缺/非建行收款资料、缺/不可解密公司账户、跨公司、无复核人;准确返回原因码,正常 fixture ready=true。
  • AC7:候选、组合预检和创建事务调用同一 readiness 实现;并发变化时创建再次阻断且不生成 batch、item、成功 audit 或通知。
  • AC8:正常候选无 readiness 噪音;异常候选不可选择且缺项就近可读;组合原因紧贴禁用主动作,toast 非唯一反馈,键盘/读屏语义通过。
  • AC9:员工资料或公司账户变化后下一次 readiness 立即变化;schema、migration 和持久化 readiness 字段为零。
  • AC10:用受控 fixture 复现 0014 三类缺项,分别提示所属公司、开户银行/银行卡和渠道限制;不使用或修改真实银行卡资料。
  • AC11:相关服务端、客户端单元/Playwright、typecheck/build、Prisma validate、UI strict/self-test、Trellis governance、敏感扫描和 git diff --check 全部通过。
  • AC12(部署后):/ready、bundle marker、公网文案扫描通过;合成 API/页面证明 readiness 与原因码解析生效。真实资料补录和 0014 重试由授权人员完成,未取得真实证据时不得冒充 PASS。2026-09-05 经用户追加授权,以公网受控管理员对 0014 的真实缺项负向核验确认候选禁用、就近原因和创建 409;未补录资料、未创建成功批次、未执行付款,详见 implement.md 第 11 节。

Test Plan

  • 原因码合同:运行 parity strict 检查及 self-test,覆盖注册表唯一性、同一 details key 跨域、未注册原因、缺客户端映射、孤儿/拼写漂移、新增无原因码调用、遗留指纹变化、例外过期,以及首批主链路试图加入例外时 fail closed。
  • 首批主链路:对报销提交/编辑/票据关联、付款创建/复核/登记、请假提交、考勤修正提交运行 focused 服务端测试;逐分支断言 HTTP status、稳定 reasonCode、兼容 details 和准确中文映射,并验证主链路遗留例外为零。
  • 付款 readiness:用受控 fixture 覆盖正常、状态异常、活动批次占用、缺所属公司、缺开户银行/银行卡、非建行、缺或不可解密公司账户、跨公司、无合格复核人;分别验证单条和组合投影,并以正常 fixture 证明可选择、可创建和提交复核不被误伤。
  • 事务与实时性:模拟候选读取后事实变化,确认创建事务重新校验、失败时不产生 batch/item/成功 audit/notification;修改受控员工资料或公司账户后重新查询,确认 readiness 即时变化且无持久化字段或缓存依赖。
  • 客户端与可访问性:逐项处置现有 20 处“参数校验失败”测试命中;断言具体原因就近显示、禁用按钮关联 aria-describedby 或等价语义、toast 非唯一反馈、未知原因不泄露 details/UUID/路径,有 request ID 时显示排障编号、无编号时不留空占位;不得以宽泛正则替代精确断言。
  • 工程门禁:运行相关 server/client 单元测试与 Playwright、npm run prisma:validate、前端 typecheck/build、UI strict/self-test、Trellis governance、敏感信息扫描和 git diff --check,并确认 schema/migration/seed/flag/routing 与生产数据修改均为零。
  • 部署后验收:在另行批准部署后核对 /ready、bundle marker 和公网文案扫描;仅用合成 API/页面样本验证三类 0014 缺项、正常付款路径及 B2/maker-checker 回归。真实银行卡资料、0014 建批/复核/登记必须由授权人员操作;缺少真实链路证据的项目明确记为 BLOCKED 或待自然样本验证,不以合成测试冒充 PASS。

Out of Scope

  • 统一首页待办、通知推导和角色行动项模型;等待真实付款链路记录后另行规划。
  • 一次性迁移全部 365 处通用文案或 518 个调用文本;非首批路径只建立可到期遗留基线。
  • 支持跨行付款、银行直连、自动付款,或改变 maker-checker、付款角色、复核规则。
  • 自动补录或读取生产员工公司、开户银行、银行卡号和公司账户。
  • 修改业务状态机,新增 schema/migration/seed/flag/routing。
  • 把完整 details、payload、UUID、账号或内部路径写入日志、任务材料或普通页面。

Risks and Deferred Items

  • 静态扫描只证明合同登记;focused 分支测试证明真实返回,二者缺一不可。
  • 单条与组合 readiness 语义不同;只做前者仍留下跨公司等点击后失败路径,因此组合预检是必交项。
  • request ID 可帮助定位,但当前 AppError details 不进服务端日志。是否增加脱敏 reasonCode 日志另行评估,本任务不记录原始 details。
  • 0014 只证明资料未维护,不证明员工没有建行卡;真实开户行由本人和授权管理员确认。