diff --git a/docs/superpowers/specs/2026-06-08-revenue-bugfix-clear-scope-design.md b/docs/superpowers/specs/2026-06-08-revenue-bugfix-clear-scope-design.md new file mode 100644 index 0000000..bac27f5 --- /dev/null +++ b/docs/superpowers/specs/2026-06-08-revenue-bugfix-clear-scope-design.md @@ -0,0 +1,168 @@ +# 营收明确缺陷第一批修复设计 + +日期:2026-06-08 + +## 背景 + +本设计用于收敛当前营收缺陷中代码证据最明确、可闭环验收的一批问题。前期排查表明,部分缺陷并非单纯后端写库失败,而是前后端接口契约、页面状态文案、查询语义和锁恢复流程不一致导致用户认为“成功但未生效”或“查不到记录”。 + +本轮不改变账务调整审批规则,不把待审批动作改成即时落账;目标是让系统行为与用户提示一致,并补齐明确漏查和重试闭环。 + +## 修复范围 + +本轮纳入以下缺陷: + +- `#78` 水价调整:执行报错后,用户不应只能关闭菜单从头开始。 +- `#39` 柜台结账:柜台预存缴费记录应能进入待结清并完成结账。 +- `#50` 柜台结账:收费员筛选不应被后端无条件覆盖为当前登录用户。 +- `#53` 柜台收费:预存抵扣金额需要前后端契约保护和回归验证。 +- `#58/#59` 红冲记录:柜台红冲后应能在红冲记录页按红冲时间查到。 +- `#69/#76` 未销分账、呆坏账:前端应准确表达“申请已提交,待审批/待回写”,不得提示为已生效。 + +以下缺陷不进入本轮实现,仅保留后续复现或产品确认: + +- `#70` 未销调整提示成功但账单未变:现有后端金额/水量调整路径会写回,需要先抓请求体确认字段和值。 +- `#9` 抄表状态修改无影响:需要产品确认状态配置影响已生成任务还是仅影响后续任务。 + +## 设计原则 + +1. 保持最小闭环,不重构完整账务调整体系。 +2. 保留现有审批流语义,修正文案和状态展示,不把待审批改为立即落账。 +3. 查询接口按业务语义返回数据,页面筛选字段与后端字段一致。 +4. 后端对关键金额和状态做兜底校验,前端负责交互提示和用户确认。 +5. 所有修改需要有最小单测、前端合约测试或页面 smoke 覆盖。 + +## 后端设计 + +### 水价调整锁恢复 + +`PriceTemplateServiceImpl.updatePriceTemplate()` 继续保持执行完成后释放调价锁的总体策略,但失败路径需要让前端能够明确恢复: + +- 后端异常响应保持业务错误信息可读。 +- 前端失败后重新进入 `startAdjustment()` 流程获取新锁。 +- `PriceTemplateAdjustmentLockRedisDAO.refreshLock()` 不再采用 `forceUnlock()` 后重新 `tryLock()` 的刷新模式,改为安全续期,避免刷新瞬间锁被其他用户抢占。 + +### 柜台预存缴费进入结账 + +`PaymentRecordMapper.selectCounterUnsettledRecords()` 从仅查询 `CHARGE_PAYMENT` 扩展为同时包含 `DEPOSIT_TOPUP`: + +- `CHARGE_PAYMENT` 表示账单收费记录。 +- `DEPOSIT_TOPUP` 表示柜台预存充值记录。 + +`CounterSettleApplicationServiceImpl.confirm()` 需要支持预存充值记录无营业账 ID: + +- 结账总金额仍以 `PaymentRecord.paymentAmount` 汇总。 +- 写入结账明细时,`chargeId`、`billMonth` 可为空。 +- `chargeMapper.markCounterSettled()` 只处理真实账单收费记录对应的 `chargeIds`。 + +### 收费员筛选 + +柜台结账查询的收费员解析规则改为: + +- 请求 `cashierId` 为空时,默认使用当前登录用户。 +- 请求 `cashierId` 非空时,使用请求值查询。 +- 如后续接入完整数据权限,可在此基础上限制普通用户只能查自己、管理员可查指定收费员。本轮先不让后端无条件覆盖前端筛选值。 + +### 预存抵扣校验 + +柜台收费继续由前端传入 `prepayDeductAmount`: + +- 后端校验抵扣金额不得小于 0。 +- 抵扣金额不得超过账单应收金额。 +- 抵扣金额不得超过账户预存余额。 +- 抵扣金额大于 0 时扣减账户余额,并在支付记录 `extJson` 中保留 `prepayDeductAmount`。 + +本轮不启用后端自动计算抵扣金额,避免改变用户输入语义。 + +### 柜台红冲记录查询 + +红冲记录页应基于柜台结账红冲链路展示,而不是复用账务调整日志: + +- 查询来源为 `settle_record`、`settle_record_detail`、`payment_record` 的柜台红冲结果。 +- 默认包含部分红冲和全部红冲状态。 +- 日期筛选使用红冲时间 `reversedTime`,而不是账务日志的创建时间或处理时间。 +- 返回字段至少包含结账单号、收费员、客户、红冲金额、红冲时间、红冲原因。 + +## 前端设计 + +### 水价调整失败恢复 + +水价调整提交失败后,前端不再只把页面退出调整态。推荐交互: + +1. 显示调价失败原因。 +2. 自动调用 `startAdjustment()` 尝试重新获取锁。 +3. 重新获取成功时保留当前页面数据,提示用户可继续修正后提交。 +4. 重新获取失败时提示锁被占用或已过期,并引导用户重新开始调价。 + +### 待审批动作状态文案 + +未销分账、呆坏账、价差调整、违约金减免等提交后,根据响应状态显示文案: + +- `approvalRequired=true` 或 `resultStatus=PENDING_APPROVAL`:显示“申请已提交,待审批”。 +- `writeBackStatus=PENDING`:显示“待回写”或“待执行”,不刷新为已生效。 +- `resultStatus=SUCCESS` 且 `writeBackStatus=UPDATED`:显示“处理完成”。 + +页面不得仅用“提交成功”表达所有结果。 + +### 柜台结账页面 + +未结账列表需要正常展示预存充值记录: + +- 无 `chargeId`、`billMonth` 时显示 `--`。 +- 客户、收费员、金额、缴费时间正常展示。 +- 结账确认汇总包含预存充值金额。 + +收费员筛选继续保留,前端传入的 `cashierId` 应与后端查询语义一致。 + +### 红冲记录页面 + +红冲记录页面改为柜台红冲记录视图: + +- 查询接口改为柜台红冲记录接口。 +- 日期筛选标签改为“红冲时间”。 +- 参数使用 `beginReversedTime`、`endReversedTime`。 +- 列表展示柜台红冲相关字段,不再按账务调整日志字段组织。 + +### 预存抵扣交互 + +柜台收费页面保留当前按选中账单分摊 `prepayDeductAmount` 的逻辑,并补充交互保护: + +- 抵扣金额不得超过预存余额。 +- 抵扣金额不得超过选中账单应收合计。 +- 用户开启预存抵扣但计算抵扣为 0 时,提交前给出明确提示或确认。 + +## 测试设计 + +### 后端测试 + +- 水价调整:覆盖失败后前端可重新开始调价;覆盖刷新锁不释放抢占。 +- 柜台结账:覆盖 `CHARGE_PAYMENT` 和 `DEPOSIT_TOPUP` 都进入未结账;覆盖预存记录结账时不要求 `chargeId`。 +- 收费员筛选:覆盖请求 `cashierId` 非空时后端按请求值查询。 +- 预存抵扣:覆盖正常抵扣、余额不足、非法金额。 +- 红冲记录:覆盖红冲状态默认可查,按 `reversedTime` 范围筛选。 + +### 前端测试 + +- 水价调整提交失败后重新获取锁或展示重获锁失败提示。 +- 分账和呆坏账返回待审批时提示“申请已提交,待审批”。 +- 柜台结账未结账列表能展示预存充值记录。 +- 红冲记录页调用柜台红冲记录接口,并按红冲时间传参。 +- 预存抵扣开启但抵扣金额为 0 时出现确认或提示。 + +## 验收标准 + +1. 用户在水价调整报错后无需关闭菜单即可继续修正并重新提交。 +2. 柜台预存缴费能在柜台结账待结清列表中查询并完成结账。 +3. 柜台结账收费员筛选不再无条件固定为当前登录用户。 +4. 柜台红冲成功后,红冲记录页可按红冲时间查询到记录。 +5. 分账、呆坏账等待审批动作不再被前端表达为已生效。 +6. 预存抵扣金额写入支付记录并扣减账户余额,非法金额有明确错误。 + +## 实施边界 + +本设计不包含以下内容: + +- 不重构完整 REV004 账务调整状态机。 +- 不新增完整 BPM 审批能力。 +- 不修改呆坏账、分账从待审批到已执行的业务规则。 +- 不处理未复现的 `#70` 和产品规则未确认的 `#9`。