diff --git a/docs/evidence/rev003-charging/2026-07-15-p0-verification.md b/docs/evidence/rev003-charging/2026-07-15-p0-verification.md index 64b7652..affeb10 100644 --- a/docs/evidence/rev003-charging/2026-07-15-p0-verification.md +++ b/docs/evidence/rev003-charging/2026-07-15-p0-verification.md @@ -2,7 +2,7 @@ ## 1. 验证结论 -2026-07-15 完成营业收费 P0 代码整改和定向验收。后端营业模块 106 项测试、银行冲正 1 项测试、29 个 Maven reactor 模块编译、前端 38 项 Node 契约测试及 Vite 全量构建均通过。 +2026-07-15 完成营业收费 P0 代码整改、审查后加固和定向验收。后端营业模块 130 项测试、银行模块 7 项测试、29 个 Maven reactor 模块编译、前端 44 项 Node 契约测试及 Vite 全量构建均通过。 验证期间未运行 `vue-tsc`。 @@ -20,6 +20,7 @@ | `f0b906048` | 按显式支付记录执行柜员结账 | | `6c2e4850c` | 修复已结/未结预存红冲状态路径 | | `a4d3cc0cc` | 防止银行假冲正和预存回调重放 | +| `f7f32ff6e` | 审查后加固:请求级 advisory lock、通用写路径行锁、银行精确交易冲正、结算分账和审批终态保护 | ### 前端 `fix/rev003-charging-p0` @@ -27,28 +28,29 @@ |------|------| | `fe21be95` | 柜台收费切换为单次原子批量提交 | | `06a5d70a` | 柜台结账只提交显式勾选的支付记录 | +| `0553ff1a` | 审查后加固:成功与刷新结果分离、集收刷新、金额展示契约、当前收银员范围和输入校验 | ## 3. 后端验证 ### 营业模块定向测试 ```bash -mvn -pl sw-business/sw-business-server \ - -Dtest=CounterChargeAmountCalculatorTest,CounterChargeApplicationServiceImplTest,PaymentRecordServiceImplTest,ChargeServiceCounterPaymentTest,AccountServiceImplConcurrencyTest,CounterSettleApplicationServiceImplTest,PaymentQueryServiceTest,PrestorageBpmCallbackServiceTest \ +mvn -pl sw-business/sw-business-server -am \ + -Dtest=ChargeControllerTest,ChargeServiceCounterPaymentTest,AccountServiceImplConcurrencyTest,CounterChargeApplicationServiceImplTest,PaymentRecordServiceImplTest,PaymentCommandApplicationServiceTest,CounterSettleApplicationServiceImplTest,PrestorageBpmCallbackServiceTest,PaymentQueryServiceTest \ -Dsurefire.failIfNoSpecifiedTests=false test ``` -结果:退出码 0,`Tests run: 106, Failures: 0, Errors: 0, Skipped: 0`,`BUILD SUCCESS`。 +结果:退出码 0,9 个测试类合计 `Tests run: 130, Failures: 0, Errors: 0, Skipped: 0`。 ### 银行冲正测试 ```bash -mvn -pl sw-business-bank/sw-business-bank-server \ - -Dtest=PayInvalidServiceImplTest \ +mvn -pl sw-business-bank/sw-business-bank-server -am \ + -Dtest=PayInvalidServiceImplTest,PayCebServiceImplTest \ -Dsurefire.failIfNoSpecifiedTests=false test ``` -结果:退出码 0,`Tests run: 1, Failures: 0, Errors: 0, Skipped: 0`,`BUILD SUCCESS`。测试日志中的异常栈为用例主动模拟业务红冲失败,用于验证银行原交易不会被错误标记为已冲正。 +结果:退出码 0,`Tests run: 7, Failures: 0, Errors: 0, Skipped: 0`。覆盖本金加违约金金额口径、空违约金兼容、失败交易原请求金额持久化及重放,以及银行原交易冲正失败时不得错误标记成功。测试日志中的异常栈为用例主动模拟业务红冲失败。 ### 后端编译 @@ -65,16 +67,18 @@ mvn -pl sw-business/sw-business-server,sw-business-bank/sw-business-bank-server ```bash node --test \ - tests/operatingCharges/counterChargingP0Flow.test.mjs \ - tests/operatingCharges/counterCheckoutExplicitSelection.test.mjs \ src/views/operatingCharges/counterCharging/counterTopup.contract.test.mjs \ - src/views/operatingCharges/counterCheckout/redFlushReason.contract.test.mjs \ + src/views/operatingCharges/counterCheckout/checkout-top-summary.contract.test.mjs \ + tests/operatingCharges/counterChargingP0Flow.test.mjs \ + tests/operatingCharges/counterChargingPersistentTopup.test.mjs \ + tests/operatingCharges/counterChargingHubMode.test.mjs \ + tests/operatingCharges/counterChargingHubActualAmount.test.mjs \ tests/operatingCharges/counterChargingZeroAmount.contract.test.mjs \ - tests/revenue-bugs/counterChargeAndCheckoutDisplay.contract.test.mjs \ - tests/rev006/counterCheckoutOldPageInventory.test.mjs + tests/operatingCharges/counterCheckoutCashierOptions.contract.test.mjs \ + tests/operatingCharges/counterCheckoutExplicitSelection.test.mjs ``` -结果:退出码 0,`tests 38`、`pass 38`、`fail 0`。 +结果:退出码 0,`tests 44`、`pass 44`、`fail 0`。 覆盖点包括: @@ -85,6 +89,11 @@ node --test \ - 结账只提交明确勾选且去重后的支付记录 ID;切换支付方式或刷新数据时清除选择。 - 结账金额和展示优先使用渠道实收,预存抵扣独立表达。 - 已结、未结红冲原因均为必填。 +- 命令成功后立即清除重试请求号并展示成功结果;客户或汇总刷新失败只提示刷新警告,不得误报交易失败。 +- 集收成功后重新查询各客户余额和未缴账单,同时保留本次收讫快照。 +- 账单主表和详情统一显示本金加违约金;顶部实收优先使用 `channelAmount`,预存抵扣独立展示。 +- 结账查询和提交主体固定为当前登录收银员,不再把其他用户、银行或收费方式混入收费员筛选。 +- `useMessage.prompt` 透传 Element Plus 校验选项,金额换算拒绝 `NaN/Infinity`。 ### Vite 全量构建 @@ -106,14 +115,18 @@ NODE_OPTIONS=--max-old-space-size=8192 \ 当前环境未设置 `REV004_IT_DB_URL`,因此未执行依赖真实数据库的可选集成测试。上线前仍需在目标 PostgreSQL 环境验证: 1. 新增列、条件唯一索引和历史空值兼容 DDL 已完整部署。 -2. 同一 `requestId` 重试不重复收费或充值。 +2. PostgreSQL 事务级 advisory lock 可按“租户 + requestId”串行化同一柜台命令;同请求号不相交账单也不得双成功。 3. 同一账单并发收费只有一个请求成功,失败批次不留下部分支付或余额变化。 4. 柜员结账金额等于所选记录的渠道实收之和。 5. 已结/未结预存红冲正确扣减余额并生成唯一反向流水。 +6. 创建反向支付唯一索引前,DDL 的重复关系预检没有发现历史脏数据;曾以零默认值落库的未知历史拆分字段已恢复为 NULL。 +7. 银行缴费按“本金 + 违约金”校验并锁定账单;冲正必须命中原 `bankTransactionId`,重试旧冲正不得影响后续新缴费。 ## 6. 已知非本轮问题 -实施期间单独运行完整 `ChargeServiceAccountingAdjustTest` 时,既有用例 `testAdjustAccounting_usageGarbageFeeUsesCustomerBasicNumberOnly` 出现期望 `12.34`、实际 `1221.66` 的失败。该用例属于用量垃圾费账务调整,不在 REV-003 P0 范围;本轮涉及的通用财务旁路定向方法测试和上述 106 项收费测试均已通过。该问题应作为 REV-004 独立缺陷继续处理。 +实施期间单独运行完整 `ChargeServiceAccountingAdjustTest` 时,既有用例 `testAdjustAccounting_usageGarbageFeeUsesCustomerBasicNumberOnly` 出现期望 `12.34`、实际 `1221.66` 的失败。该用例属于用量垃圾费账务调整,不在 REV-003 P0 范围;本轮涉及的银行冲正方法、通用财务旁路和上述 137 项后端测试均已通过。该问题应作为 REV-004 独立缺陷继续处理。 + +前端扩大到全部 `tests/operatingCharges/*.test.mjs` 的历史库存扫描时,仍有若干与当前实现范围不一致或依赖固定相邻仓库路径的旧正则契约失败;本轮以列出的 44 项 P0 定向契约及 Vite 全量构建作为发布门禁,旧库存迁移另行治理。 ## 7. 发布约束 diff --git a/docs/superpowers/specs/2026-07-15-rev003-charging-p0-remediation-design.md b/docs/superpowers/specs/2026-07-15-rev003-charging-p0-remediation-design.md index b9e1546..a90aac8 100644 --- a/docs/superpowers/specs/2026-07-15-rev003-charging-p0-remediation-design.md +++ b/docs/superpowers/specs/2026-07-15-rev003-charging-p0-remediation-design.md @@ -146,6 +146,7 @@ PaymentRecord 字段口径: - `biz_payment_record` 增加 `request_id`、`payment_batch_no`、`channel_amount`、`prepay_amount`、`overpay_amount`。 - 柜台账单支付增加 `(tenant_id, request_id, biz_scene, source_ref_id)` 条件唯一索引。 - 柜台充值增加 `(tenant_id, request_id, biz_scene)` 条件唯一索引。 +- 同租户、同 `requestId` 在查询支付记录前获取 PostgreSQL 事务级 advisory lock,避免两组不相交账单并发复用同一请求号时绕过逐账单唯一索引。 - 反向支付增加 `(tenant_id, related_payment_record_id, biz_scene)` 条件唯一索引。 - 收费事务对所选营业账按 ID 排序并加行锁,对主付款账户按客户 ID 排序并加行锁,避免死锁。 - 账户增减统一从 `SELECT ... FOR UPDATE` 获取余额,再写余额和 AccountLog。 @@ -159,6 +160,7 @@ PaymentRecord 字段口径: - 已结账预存:调用 `markCounterSettleTopupReversed`,反转结账明细,扣减账户余额,追加 `DEPOSIT_REFUND/OUT`。 - 红冲原因前后端统一必填。 - 数据库唯一索引阻止同一原支付产生两张相同业务场景的反向单。 +- 银行冲正必须同时传入账单 ID 和原 `bankTransactionId`,锁定精确银行支付主单;旧交易的幂等重试只返回既有反向单,不得重置后续新缴费的账单投影。 ## 9. 高风险旁路 @@ -166,6 +168,7 @@ PaymentRecord 字段口径: - 已收或已结账营业账禁止通过通用删除接口删除。 - 账户通用更新不得直接改变 `deposit`;账户删除至少要求余额为零且不存在有效流水。 - `invalidCharge` 在未实际执行反向处理时必须抛出明确错误,银行侧不得以非空对象判断冲正成功。 +- 银行缴费按 `extendedAmount + lateFee` 计算应收分值,业务层对账单加行锁并校验银行交易流水金额后才生成支付事实。 - 预存审批成功回调只允许从一个明确待执行状态进入完成状态;完成状态重放直接返回,不再修改余额。 ## 10. 前端设计 @@ -178,6 +181,8 @@ PaymentRecord 字段口径: - 柜员结账表格真实维护选择状态,确认请求提交选中的 `paymentRecordIds` 和对应金额。 - 配置加载失败时只保留现金安全默认项;没有真实渠道确认能力的方式不可提交。 - “删除账单”改名为“本次不收”。 +- 收费/预存命令成功后立即清除当前请求号并展示成功结果;后续客户、余额、账单或汇总刷新失败只提示刷新警告,不能回落为“收费失败/预存失败”。 +- 集收成功后逐户刷新客户余额和未缴账单,同时保留本次收讫快照;柜员结账范围固定为当前登录收费员。 ## 11. 错误处理 @@ -202,6 +207,9 @@ PaymentRecord 字段口径: 8. 已结账和未结账预存分别可以红冲;重复红冲只产生一张反向单。 9. 银行冲正未实际反向时返回失败。 10. 预存审批完成回调重放不重复修改余额。 +11. 同一 `requestId`、两组不相交账单并发提交时只能形成一个业务批次。 +12. 银行支付 A 冲正后由 B 再次缴费,A 的冲正重试不得冲掉 B。 +13. 收费命令成功但页面刷新失败时,只显示刷新警告且不生成新的重试请求号。 前端验证使用 `node:test`、现有页面 smoke 或 Playwright;不运行 `vue-tsc`。后端按相关测试类单独执行,数据库集成测试继续由 `REV004_IT_DB_URL` 门禁控制。 @@ -214,4 +222,3 @@ PaymentRecord 字段口径: 5. 历史记录继续按旧字段读取;新记录优先使用显式金额拆分字段。 回滚时前端可切回旧页面版本,但后端不恢复通用接口的支付旁路;数据库新增列保留,不执行破坏性回滚。 -