diff --git a/docs/evidence/rev004-accounting-adjust-first-batch-dev-entry-2026-04-15.md b/docs/evidence/rev004-accounting-adjust-first-batch-dev-entry-2026-04-15.md new file mode 100644 index 0000000..2a5892c --- /dev/null +++ b/docs/evidence/rev004-accounting-adjust-first-batch-dev-entry-2026-04-15.md @@ -0,0 +1,22 @@ +# REV004 账务调整统一化改造 — 第一批开发入口(2026-04-15) + +## 说明 +基于已完成的 P0 标准层工件,本轮已进一步拆出第一批可执行开发任务单,作为后续真正进入代码实现的入口。 + +## 第一批试点对象 +- `late-fee reduce`(违约金减免) + +## 第一批任务单 +- `.omx/plans/first-batch-dev-tasklist-rev004-accounting-adjust-unification.md` + +## 核心内容 +1. 最小标准层 helper 落地 +2. late-fee reduce 字典统一 +3. late-fee reduce 状态映射冻结 +4. formal-table 申请态写入 +5. date-mode 最小执行闭环 +6. page/log-page 摘要字段补齐 +7. 测试闭环建立 + +## 用途 +该任务单用于把“标准层规划”正式推进到“试点对象实现”。 diff --git a/docs/evidence/rev004-accounting-adjust-standard-layer-bootstrap-2026-04-15.md b/docs/evidence/rev004-accounting-adjust-standard-layer-bootstrap-2026-04-15.md new file mode 100644 index 0000000..f45351f --- /dev/null +++ b/docs/evidence/rev004-accounting-adjust-standard-layer-bootstrap-2026-04-15.md @@ -0,0 +1,23 @@ +# REV004 账务调整统一化改造 — P0 标准层启动证据(2026-04-15) + +## 已完成 +本轮已将统一化改造的 P0 标准层拆分成独立工件,供后续直接执行: +- `.omx/plans/standard-layer/rev004-accounting-adjust-semantic-map.md` +- `.omx/plans/standard-layer/rev004-accounting-adjust-dict-and-status-strategy.md` +- `.omx/plans/standard-layer/rev004-accounting-adjust-readmodel-matrix.md` +- `.omx/plans/standard-layer/rev004-accounting-adjust-canonical-semantic-layer.md` +- `.omx/plans/standard-layer/rev004-accounting-adjust-test-matrix.md` +- `.omx/plans/p0-tasklist-rev004-accounting-adjust-unification-standard-layer.md` + +## 收口原则 +- REV004 当前接口模型为主真值 +- legacy 仅作映射与迁移参考 +- 不强制前端改字段名 +- 先做标准层,再做多对象 runtime 与迁移 + +## 本轮用途 +这些工件用于: +1. 统一字段语义 +2. 统一字典与状态口径 +3. 统一读模型输出 +4. 为 runtime 正式落库和历史迁移提供统一语义层 diff --git a/docs/evidence/rev004-accountlog-action-wiring-notes-2026-04-13.md b/docs/evidence/rev004-accountlog-action-wiring-notes-2026-04-13.md new file mode 100644 index 0000000..bc70734 --- /dev/null +++ b/docs/evidence/rev004-accountlog-action-wiring-notes-2026-04-13.md @@ -0,0 +1,47 @@ +# REV004 accountLog 操作链接线说明(2026-04-13) + +## 结论 +当前 `accountLog` 的 4 条操作链在后端契约层已具备接线条件,不需要继续补新的稳定字段;前端主要做提交映射即可。 + +## 1. 查看流程 `log-process` +- 前端来源:`handleProcess(row)` +- 后端接口:`GET /business/accounting-adjust/log-process` +- 后端入参:`adjustmentNo` +- 当前行数据可用字段:`row.adjustmentNo` +- 接线结论:可直接接 + +## 2. 查看附件 `log-attachments` +- 前端来源:`handleAttachment(row)` +- 后端接口:`GET /business/accounting-adjust/log-attachments` +- 后端入参:`adjustmentNo` +- 当前行数据可用字段:`row.adjustmentNo` +- 接线结论:可直接接 + +## 3. 转退款 `log-refund` +- 前端来源:`RefundForm` +- 后端接口:`POST /business/accounting-adjust/log-refund` +- 后端请求对象:`AccountingAdjustSecondaryActionReqVO` +- 推荐映射: + - `adjustmentNo = row.adjustmentNo` + - `reason = form.remark` + - `collectionMethod = form.collectionMethod` + - `refundUser = form.refundUser` + - `attachmentRefs` = 前端上传结果(如有) +- 说明:前端 `refundAmount` 当前仅用于展示,不是后端必填字段 +- 接线结论:可接,主要是前端提交映射 + +## 4. 转预存 `log-prestorage` +- 前端来源:`TransferPrestoreForm` +- 后端接口:`POST /business/accounting-adjust/log-prestorage` +- 后端请求对象:`AccountingAdjustSecondaryActionReqVO` +- 推荐映射: + - `adjustmentNo = row.adjustmentNo` + - `targetCustCode = form.targetCustCode` + - `reason = form.remark` + - `attachmentRefs` = 前端上传结果(如有) +- 说明:前端 `transferAmount` 当前仅用于展示,不是后端必填字段 +- 接线结论:可接,主要是前端提交映射 + +## 补充说明 +- 本轮后端新增的 `accountLog.custId` 已可支撑前端客户编号点击后稳定跳客户详情。 +- `sold` 当前列表/弹窗展示字段已基本满足,不建议继续为展示字段扩充后端响应。 diff --git a/docs/evidence/rev004-accountprocess-dict-ui-consistency-summary-2026-04-13.md b/docs/evidence/rev004-accountprocess-dict-ui-consistency-summary-2026-04-13.md new file mode 100644 index 0000000..1e93864 --- /dev/null +++ b/docs/evidence/rev004-accountprocess-dict-ui-consistency-summary-2026-04-13.md @@ -0,0 +1,148 @@ +# REV004 / accountProcess 字典 label 与 UI 文案一致性摘要(2026-04-13) + +## 1. 目的 +本摘要用于收敛 `accountProcess` 当前涉及的状态/标签类字段,确认: +- 代码侧字典 type 是否已统一 +- 正式文档口径是否与代码一致 +- 是否仍存在旧快照 / 旧产出中的历史口径残留 + +--- + +## 2. 当前已确认一致的口径 + +### A. 工单状态 `work_status` +代码侧: +- `DictTypeConstants.WORK_STATUS = "work_status"` +- 口径:`0-未处理 / 1-已审核 / 2-已完成 / 3-已撤销` + +正式 SQL seed: +- `../water-docs/sql/rev004_account_adjust_dict_seed.sql` +- 已定义: + - `0 -> 未处理` + - `1 -> 已审核` + - `2 -> 已完成` + - `3 -> 已撤销` + +正式数据库设计文档: +- `../water-docs/docs/design/03_Technical_Design/01_Database_Design.md` +- 当前已同步为四态: + - `0-未处理(工单创建,待审核/待处理)` + - `1-已审核(审核通过/无需审批,待完成)` + - `2-已完成(处理成功且已回写完成)` + - `3-已撤销(工单已撤销)` + +结论: +- **代码 / seed SQL / 正式设计文档:已一致** + +### B. REV004 账务调整核心状态字典 +已确认存在统一 type: +- `account_adjust_object_type` +- `account_adjust_result_status` +- `account_adjust_approval_status` +- `account_adjust_writeback_status` + +代码侧使用: +- `AccountingAdjustLogProcessServiceImpl` +- `AccountingAdjustSoldProcessServiceImpl` +中均已通过 `DictFrameworkUtils.parseDictDataLabel(...)` 或统一映射读取 label + +正式 SQL seed: +- `../water-docs/sql/rev004_account_adjust_dict_seed.sql` +- 已定义: + - objectType:如 `PREPAID_REFUND / REDINK_RECORD / BAD_DEBT_RECORD / WRITTENOFF_ADJUST / PRICE_DIFF_ADJUST / LATE_FEE_REDUCE / SPLIT_ADJUST` + - resultStatus:`SUCCESS / PENDING_APPROVAL / FAIL` + - approvalStatus:`NOT_REQUIRED / PENDING_APPROVAL / APPROVED / REJECTED` + - writeBackStatus:`UPDATED / PENDING / SKIPPED` + +结论: +- **REV004 核心状态字典类型与值域已成套收口** + +### C. 收费方式 `charge_method` +代码侧: +- `DictTypeConstants.CHARGE_METHOD = "charge_method"` +- 已在 `AccountingAdjustSoldProcessServiceImpl` 中作为列表展示 label 来源 + +正式文档: +- `../water-docs/sql/lhc_数据库设计.md` +- `../water-docs/docs/design/04_Appendix/Archive/03_Design_Docs/数据库设计.md` +中可见 `charge_method` 语义为收费途径/收费方式 + +结论: +- **当前 sold 查询页“收费方式”展示依赖路径明确,代码口径稳定** + +--- + +## 3. 当前已确认的“动态原因字典”绑定口径 +参考: +- `../water-docs/docs/guides/REV004_DICT_BINDING_MATRIX.md` + +当前已明确: +- `PREPAID_REFUND -> deposit_reason` +- `REDINK_RECORD -> redink_reason` +- `BAD_DEBT_RECORD -> knotty_reason` +- `WRITTENOFF_ADJUST -> payment_reason`(过渡复用) +- `PRICE_DIFF_ADJUST -> price_reason` +- `LATE_FEE_REDUCE -> late_fee_reason` +- `SPLIT_ADJUST -> separate_reason` + +结论: +- **前端原因下拉不应绑定一个统一 reason 字典,而应按 objectType 动态切换** + +--- + +## 4. 当前仍存在的不一致 / 残留风险 + +### A. 历史快照文档仍残留旧三态 `work_status` +在旧归档快照中,仍能看到: +- `../water-docs/docs/design/04_Appendix/Archive/08_Formal_Doc_Snapshots/RWB-02/2026-04-03-RWB-02-01_Database_Design.md` +- 其中 `work_status` 仍写作:`0-待处理 / 1-处理中 / 2-已完成` + +这与当前正式口径不一致。 + +结论: +- **历史快照存在旧口径残留,但不应再作为当前真值来源** + +### B. output 产物也有旧口径残留 +例如: +- `../water-docs/output/01_Database_Design_processed.md` +中仍可见旧三态 `work_status` + +结论: +- **产出型文件存在滞后,需要明确“正式设计文档 + REV004 seed + 代码”为真值源** + +### C. UI 文案一致性尚未逐页核验 +虽然字典 type / 值域已统一,但还没有逐页确认: +- 页面上显示的是字典 label 还是代码 fallback 值 +- 页面文案是否与字典 label 完全一致 +- 历史页面是否仍在用旧文案(例如“处理中”) + +结论: +- **字典结构已较稳定,但 UI 最终展示一致性仍需前端/UAT 联合核验** + +--- + +## 5. 当前建议真值源优先级 +建议按以下优先级使用: +1. **后端代码中的 DictTypeConstants + Service 映射逻辑** +2. **`rev004_account_adjust_dict_seed.sql`** +3. **正式技术设计文档 `01_Database_Design.md`** +4. 历史快照 / output 产物(仅供追溯,不作为当前真值) + +--- + +## 6. 对前端 / UAT 的建议 +1. `work_status` 统一按四态使用: + - 0 未处理 + - 1 已审核 + - 2 已完成 + - 3 已撤销 +2. `objectType / resultStatus / approvalStatus / writeBackStatus` 使用 REV004 新字典,不再依赖旧业务大类字段硬编码。 +3. “原因”下拉必须按 `objectType` 动态切换,不要做单字典通配。 +4. 若页面仍显示“处理中”等旧文案,应优先视为前端展示遗留,而不是后端当前真值。 + +--- + +## 7. 当前结论 +- **核心字典 type 与值域已基本收口** +- **正式文档与 seed SQL 已与代码当前口径对齐** +- **残留风险主要在旧快照/旧输出文件与前端展示层,而不在当前后端主实现** diff --git a/docs/evidence/rev004-accountprocess-final-review-summary-2026-04-13.md b/docs/evidence/rev004-accountprocess-final-review-summary-2026-04-13.md new file mode 100644 index 0000000..7254e35 --- /dev/null +++ b/docs/evidence/rev004-accountprocess-final-review-summary-2026-04-13.md @@ -0,0 +1,257 @@ +# REV004 / accountProcess 最终评审摘要(2026-04-13) + +## 1. 评审目的 +本摘要用于对外汇报 `REV004 / accountProcess` 当前后端交付状态,统一回答以下问题: +- 这一块后端接口是否已形成可联调的稳定口径? +- 关键页面/弹窗对应的接口是否已经落地? +- 真实数据库条件下,主链路是否已经被证明可运行? +- 当前仍有哪些剩余风险与建议后续动作? + +--- + +## 2. 结论摘要 +### 结论 +`REV004 / accountProcess` 当前已经达到: +- **接口口径已基本收口** +- **页面/弹窗映射已梳理清楚** +- **主链路已完成真实数据库验证** +- **可进入联调 / 评审 / 阶段性验收** + +### 当前结论边界 +这里的“通过”是指: +- 后端接口已存在并按当前代码口径稳定返回 +- 关键提交流程、撤销流程、日志二次动作、主要查询面均有真实库证据 +- 不代表已经完成 UI 层面的最终视觉/交互验收 +- 不代表所有页面文案、字典 label、边角筛选条件都已逐项核验 + +--- + +## 3. 当前已沉淀产物 +### A. 集成测试累计证据 +- `docs/evidence/rev004-accountprocess-integration-testing-bootstrap-2026-04-13.md` + +### B. 接口真值矩阵 +- `docs/evidence/rev004-accountprocess-interface-truth-matrix-2026-04-13.md` + +### C. 页面元素勾稽矩阵 +- `docs/evidence/rev004-accountprocess-ui-element-matrix-2026-04-13.md` +- `docs/evidence/rev004-accountprocess-dict-ui-consistency-summary-2026-04-13.md` +- `docs/evidence/rev004-accountprocess-page-label-audit-2026-04-13.md` +- `docs/evidence/rev004-accountprocess-ui-orchestration-checklist-2026-04-13.md` + +### D. 真实库集成测试代码 +- `sw-business-server/src/test/java/.../integration/rev004/accountprocess/Rev004AccountProcessCanaryQueryIntegrationTest.java` + +--- + +## 4. 范围说明 +本轮范围仅覆盖: +- `REV004/accountProcess` + +具体包括四类页面/域: +- 预存调整(prestorage) +- 已销调整(sold) +- 未销调整(unsold) +- 账务日志(accountLog / log) + +本轮明确不扩散到: +- accountProcess 之外的其它业务模块 +- 全项目通用测试框架治理 +- UI 自动化或前端渲染层验证 + +--- + +## 5. 环境口径 +### 测试策略 +- **数据库:真实 PostgreSQL** +- **数据库外依赖:替身化 / MockBean** + +### 已明确替身边界 +例如: +- `TransactionApi` +- `ApiErrorLogCommonApi` +- `DictDataCommonApi` +- `OperateLogCommonApi` +- `RedissonClient` + +### 当前价值 +这意味着: +- 账务状态变化、operat_log 留痕、查询回看等核心真值来自真实数据库 +- 外部交易/缓存/公共 RPC 不会干扰主链路验证 + +--- + +## 6. 最新验证结果 +### 最新 surefire 结果 +- `Rev004AccountProcessCanaryQueryIntegrationTest` + - **21 tests / 0 fail / 0 skip** +- `Rev004AccountProcessIntegrationFixtureAssetsTest` + - **1 pass** +- `Rev004AccountProcessLiveDbReadinessTest` + - **1 pass** + +### 最新整体验证结果 +- `Rev004AccountProcess*` + - **总计 23 tests / 0 fail / 0 skip** + +这代表当前仓库内,至少在真实数据库 + 外部依赖替身化前提下,主链路验证是通过的。 + +--- + +## 7. 主链路覆盖概览 +### 7.1 prestorage +已验证: +- `prestorage-page` +- `prestorage-stat` +- `prestorage-detail` +- `prestorage-submit` +- `prestorage-revoke` +- `prestorage-process` +- `prestorage-attachments` +- 提交后 page/stat/detail/process/attachments 联动回看 + +已证明能力: +- 预存退款提交成功 +- 余额真实回写 +- 记录可回看 +- 撤销后余额可恢复 + +### 7.2 sold +已验证: +- `sold-page` +- `sold-stat` +- `sold-submit` +- `sold-batch-revoke` +- 提交后 page/stat 能反映撤销能力 + +已证明能力: +- 已销调整申请可成功落日志 +- 状态为 `PENDING_APPROVAL` +- 批量撤销以 `chargeIds[]` 可执行 + +### 7.3 unsold +已验证: +- `unsold-page` +- `unsold-stat` +- `unsold-adjust-submit` +- `unsold-split-submit` +- `unsold-late-fee-reduce-submit` +- `unsold-price-diff-submit` +- `unsold-bad-debt-submit` +- 调整后 page/stat 可回看金额变化 + +已证明能力: +- 五类未销动作 happy path 都可跑通 +- 查询面能反映初始数据与提交后的更新结果 + +### 7.4 log / accountLog +已验证: +- `log-page` +- `log-stat` +- `log-detail` +- `log-process` +- `log-attachments` +- `log-refund` +- `log-prestorage` +- `log-revoke` +- 日志页 page/stat/detail/process/attachments 联动回看 + +已证明能力: +- 日志链路是当前最完整的一组闭环 +- 支持二次动作(退款/转预存/撤销) +- 可查看流程摘要与附件引用 + +--- + +## 8. 页面 / 弹窗映射结论 +### 已收口 +目前页面与后端接口映射已经可明确回答: +- 哪个页面查哪个 `page/stat/detail` +- 哪个弹窗调哪个 `submit/revoke/process/attachments` +- 哪些动作依赖 `adjustmentNo` +- 哪些动作依赖 `chargeId/chargeIds` + +### 特别提醒 +- **已销批量撤销**:当前后端口径是 `chargeIds[]`,不是 `adjustmentNo[]` +- **日志页二次动作**:当前统一以 `adjustmentNo` 为主键 +- **已销查询页** 本身不返回 `adjustmentNo`,若前端要保留申请单号,应使用提交返回值或转到日志视角查看 + +--- + +## 9. 接口字段口径结论 +### 前端建议优先读取 +对于动作提交类接口,前端建议主读: +- `adjustmentNo` +- `resultStatus` +- `approvalStatus` +- `writeBackStatus` +- `msg` + +### 兼容字段 +- `status` +- `message` + +这些保留给旧调用方或兼容逻辑,不建议新页面作为主读来源。 + +### 预存工单状态口径 +- `0 = 未处理` +- `1 = 已审核` +- `2 = 已完成` +- `3 = 已撤销` + +### 页面状态与工单状态不要混用 +- `prestorage-page.workOrderStatus` 是预存工单状态 +- `log-page.statusCode/status` 是日志页状态投影 +- 两者不是同一套语义 + +--- + +## 10. 当前最可信的证据类型 +从强到弱排序: +1. **真实数据库 + MockMvc canary 测试** +2. **Controller / ReqVO / RespVO / Service 代码口径** +3. **矩阵文档归纳** + +因此当前对外说法应以: +- 已跑通真实库的场景 = 强证据 +- 仅代码里存在但未单独断言的字段 = 次强证据 + +--- + +## 11. 剩余风险 / 空白 +### 仍未完全补齐 +1. 字典 type / 值域已基本收口,剩余是页面文案逐页核对 +2. 前端弹窗 A/B/C 切换路径尚未做 UI 编排级验证 +3. 边角筛选条件并未全部逐项做真实库穷尽验证 + +### 风险等级判断 +- **主功能联调风险:中低** +- **页面展示细节风险:中** +- **最终 UI 验收风险:中** + +--- + +## 12. 对外建议说法 +建议对前端 / 产品 / 评审会的说法: + +> `REV004/accountProcess` 后端接口已基本收口,四条主链(prestorage / sold / unsold / log)均已有真实数据库验证证据; +> 当前可进入稳定联调和阶段性验收。 +> 剩余工作主要集中在页面文案/字典展示的一致性核验,以及更贴近前端的 UI 编排级验证。 + +--- + +## 13. 建议下一步 +按优先级建议: +1. 做页面文案 / 字典 label 的逐页验收核对 +2. 已提供 UI 编排级验收清单,若需要再补自动化/UI录屏级验证 +3. 评审会可直接使用本摘要 + 两份矩阵文档 + 集成测试证据 + 字典一致性摘要 + 页面文案核对表 + UI 编排验收清单 + +--- + +## 14. 关联文档 +- `docs/evidence/rev004-accountprocess-integration-testing-bootstrap-2026-04-13.md` +- `docs/evidence/rev004-accountprocess-interface-truth-matrix-2026-04-13.md` +- `docs/evidence/rev004-accountprocess-ui-element-matrix-2026-04-13.md` +- `docs/evidence/rev004-accountprocess-dict-ui-consistency-summary-2026-04-13.md` +- `docs/evidence/rev004-accountprocess-page-label-audit-2026-04-13.md` +- `docs/evidence/rev004-accountprocess-ui-orchestration-checklist-2026-04-13.md` diff --git a/docs/evidence/rev004-accountprocess-gap-remediation-2026-04-13.md b/docs/evidence/rev004-accountprocess-gap-remediation-2026-04-13.md new file mode 100644 index 0000000..ec0e47d --- /dev/null +++ b/docs/evidence/rev004-accountprocess-gap-remediation-2026-04-13.md @@ -0,0 +1,124 @@ +# REV004 accountProcess gap remediation — 2026-04-13 验证记录 + +## 本轮改动 +- accountProcess 查询 ReqVO 补前端原型 range 参数兼容: + - `sold`: `accountMonth` / `collectionTime` + - `log`: `accountMonth` / `createTime` / `handleTime` / `paymentDate` + - `unsold`: `accountMonth` + - `prestorage`: `acceptTime` +- 查询 Controller 增加 query-normalize,兼容多值 query param 传入 +- `accountLog` 状态筛选补兼容值:`1=正常`、`2=已撤销` + +## 验证结果 +### 1. 主代码编译 +命令: +```bash +cd sw-business/sw-business-server && mvn -q -DskipTests compile +``` +结果:通过。 + +### 2. 变更相关测试(手动定向执行) +由于模块内存在与本次改动无关的存量 testCompile 问题(`ChargeService` / `ChargeDO` 相关旧测试编译失败),本轮对变更相关测试采用定向手动编译 + JUnit Console 执行。 + +执行类: +- `AccountingAdjustActionControllerTest` +- `AccountingAdjustRouteSmokeTest` +- `AccountingAdjustLogProcessServiceImplTest` +- `AccountingAdjustPrestorageProcessServiceImplTest` +- `AccountingAdjustSoldQueryProcessServiceImplTest` +- `AccountingAdjustUnsoldProcessServiceImplTest` + +结果: +- 34 tests found +- 34 tests started +- 34 tests successful +- 0 tests failed + +关键覆盖点: +- accountProcess 路由 smoke 正常 +- query range 参数兼容归一化正常 +- `accountLog` 的 `status=2` 已撤销兼容筛选正常 +- `prestorage adjustmentNo` 闭环仍保持通过 +- `sold` 查询过滤与 `unsold` / `prestorage` 主流程回归通过 + +### 3. 说明 +直接执行: +```bash +cd sw-business/sw-business-server && mvn -Dtest='...' test +``` +仍会被仓库内历史遗留的 testCompile 问题阻断;该问题与本轮 accountProcess 修复无直接关系。 + + +## 4. `sold.isHistory` 语义收口(追加) +### 语义定义 +- `isHistory=false`:查询当前已收记录(`payState=PAID`),保留调整/批量撤销能力 +- `isHistory=true`:查询历史已结记录(`payState=SETTLED`),按只读口径返回,不开放调整/批量撤销 + +### 追加验证 +命令: +- `cd sw-business/sw-business-server && mvn -q -DskipTests compile` +- 手动定向执行:`AccountingAdjustRouteSmokeTest` + `AccountingAdjustSoldQueryProcessServiceImplTest` + +结果: +- 6 tests found +- 6 tests started +- 6 tests successful +- 0 tests failed + + +## 5. 完整相关回归(追加) +在 `sold.isHistory` 收口后,再次对本轮相关 6 个 accountProcess 测试类做完整定向回归。 + +执行类: +- `AccountingAdjustActionControllerTest` +- `AccountingAdjustRouteSmokeTest` +- `AccountingAdjustLogProcessServiceImplTest` +- `AccountingAdjustPrestorageProcessServiceImplTest` +- `AccountingAdjustSoldQueryProcessServiceImplTest` +- `AccountingAdjustUnsoldProcessServiceImplTest` + +结果: +- 35 tests found +- 35 tests started +- 35 tests successful +- 0 tests failed + + +## 6. sold / accountLog 展示字段复核(追加) +### 复核结论 +- `sold`:当前前端列表/弹窗所需展示字段已基本满足,暂不需要额外补展示字段 +- `accountLog`:补 `custId`,用于前端客户编号点击后稳定跳转客户详情 + +### 追加验证 +- `mvn -q -DskipTests compile` 通过 +- 手动定向执行:`AccountingAdjustRouteSmokeTest` + `AccountingAdjustLogProcessServiceImplTest` +- 结果:5 tests found / 5 tests successful / 0 failed + + +## 7. accountLog `custId` 补齐后的回归(追加) +### 变更目的 +- 补 `accountLog` 列表/详情响应中的 `custId` +- 支撑前端客户编号点击后稳定跳客户详情 + +### 追加验证 +- 手动定向编译:`AccountingAdjustRouteSmokeTest`、`AccountingAdjustLogProcessServiceImplTest`、`AccountingAdjustSoldQueryProcessServiceImplTest` +- JUnit Console 执行结果:9 tests found / 9 tests successful / 0 failed + + +## 8. work_status 字典与正式文档同步(追加) +### 本轮结果 +- 已在 `../water-docs/sql/rev004_account_adjust_dict_seed.sql` 中补充 `work_status` 字典 type/data(幂等) +- 已将 `../water-docs/docs/design/03_Technical_Design/01_Database_Design.md` 中的 `work_status` 口径同步为代码四态:`0-未处理 / 1-已审核 / 2-已完成 / 3-已撤销` +- 本轮仍无业务表 schema 变更,仅涉及字典 seed 与正式文档同步 + + +### 业务解释补充 +- `0 未处理`:工单创建,待审核/待处理 +- `1 已审核`:审核通过(或无需审批),待完成 +- `2 已完成`:处理成功且已回写完成 +- `3 已撤销`:工单已撤销 +- 说明:`钱到账`、`带红冲操作` 更偏业务侧解释;当前代码稳定判定仍以 `approvalStatus / resultStatus / writeBackStatus / revokeOfAdjustmentNo` 为准。 + +### 剩余风险 +- 当前 seed SQL 采用幂等补缺策略,不会纠正**环境中已存在但口径错误**的 `work_status` 数据;若某环境已落旧三态字典,仍需后续补一条存量纠偏 SQL 或人工核查。 +- Archive/历史快照与 processed/output 产物可能仍保留旧三态,它们属于历史资料/派生产物,不作为当前正式口径。 diff --git a/docs/evidence/rev004-accountprocess-integration-testing-bootstrap-2026-04-13.md b/docs/evidence/rev004-accountprocess-integration-testing-bootstrap-2026-04-13.md new file mode 100644 index 0000000..8df446e --- /dev/null +++ b/docs/evidence/rev004-accountprocess-integration-testing-bootstrap-2026-04-13.md @@ -0,0 +1,422 @@ +# REV004 accountProcess 集成测试方案 bootstrap(2026-04-13) + +## 本轮已落地 bootstrap +- 新增 `AbstractRev004AccountProcessIntegrationTest`:定义真实数据库 + 外部依赖默认替身化的测试基类 +- 新增 `Rev004AccountProcessLiveDbReadinessTest`:在提供 `REV004_IT_DB_*` 环境变量时,检查真实数据库所需表是否可见 +- 新增 `Rev004AccountProcessIntegrationFixtureAssetsTest`:校验 fixture 资源文件存在 +- 新增 `src/test/resources/sql/rev004/accountprocess/*.sql`:为 prestorage/sold/unsold/accountLog 预留 fixture 分层入口 + +## 当前价值 +- 把“真实数据库 + 外部依赖替身化”的执行边界固化到测试骨架 +- 把场景矩阵的 fixture 入口落到仓库,便于后续继续实现 query/action/close-loop 场景 +- 在未提供真实数据库环境变量时,不强行执行 live DB readiness 测试 + +## 下一步建议 +1. 补 `prestorage` 第 1 条全闭环场景 +2. 补 `sold` 的 `isHistory` + submit/撤销场景 +3. 补 `accountLog` 的 refund/prestorage 二次动作场景 + + +## Canary 补充 +- 新增 `Rev004AccountProcessCanaryQueryIntegrationTest`:在提供 `REV004_IT_DB_*` 环境变量时,真正启动 Spring + MockMvc,打通 `prestorage-page` 只读 query 作为最小 canary。 +- 当前 fixture 仍是 bootstrap 占位,因此该 canary 的目标是“验活骨架”,不是验证完整业务数据闭环。 + + +## Fresh verification +- 执行:`mvn -Dtest='Rev004AccountProcessIntegrationFixtureAssetsTest,Rev004AccountProcessLiveDbReadinessTest,Rev004AccountProcessCanaryQueryIntegrationTest' test` +- 结果:BUILD SUCCESS +- 细项: + - Fixture assets: 1 pass + - Live DB readiness: 1 skipped(未提供 `REV004_IT_DB_*` 环境变量时跳过) + - Canary query: 1 skipped(未提供 `REV004_IT_DB_*` 环境变量时跳过) + + +## Real DB canary verification +- 执行环境:使用 `application-local.yaml` 中 PostgreSQL 连接参数,以 `REV004_IT_DB_*` 环境变量注入 +- 执行:`mvn -Dtest='Rev004AccountProcessIntegrationFixtureAssetsTest,Rev004AccountProcessLiveDbReadinessTest,Rev004AccountProcessCanaryQueryIntegrationTest' test` +- 结果:BUILD SUCCESS +- 明细: + - Fixture assets: 1 pass + - Live DB readiness: 1 pass + - Canary query (`/admin-api/business/accounting-adjust/prestorage-page`): 1 pass +- 说明:当前 canary 证明了真实数据库 + Spring context + MockMvc + route wiring 可启动并返回成功包装;但 fixture 仍是最小占位,尚未证明完整业务数据闭环。 + + +## Real DB full bootstrap verification +- 执行环境:使用 `application-local.yaml` 中 PostgreSQL 连接参数,经 `REV004_IT_DB_*` 环境变量注入 +- 执行:`mvn -Dtest='Rev004AccountProcessIntegrationFixtureAssetsTest,Rev004AccountProcessLiveDbReadinessTest,Rev004AccountProcessCanaryQueryIntegrationTest' test` +- 结果:BUILD SUCCESS +- 明细: + - Fixture assets: 1 pass + - Live DB readiness: 1 pass + - Canary query (`/admin-api/business/accounting-adjust/prestorage-page`): 1 pass +- 说明:当前已经从“资产存在”推进到“真实数据库 + Spring context + MockMvc + canary query 验活成功”。 +- 剩余缺口:fixture 仍是最小占位,尚未验证完整业务数据闭环。 + + +## First real close-loop scenario +- 场景:`prestorage-submit` 预存退款 +- 测试文件:`Rev004AccountProcessCanaryQueryIntegrationTest` +- 使用真实数据库中的独立测试数据(`REV004_IT_SRC` / `biz_account.id=900001`) +- 断言: + - `POST /admin-api/business/accounting-adjust/prestorage-submit` 返回成功包装 + - 账户余额由 `100.00` 变为 `90.00` + - `biz_operat_log` 写入 1 条 `旧预存调整` 日志 + - `biz_operat_log_detail` 中存在与返回 `adjustmentNo` 对应的明细记录 +- 执行:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcessCanaryQueryIntegrationTest' test` +- 结果:BUILD SUCCESS(2 tests / 0 fail / 0 skip) + + +## Sold close-loop scenario +- 场景:`sold-submit` 已销调整申请 +- 测试文件:`Rev004AccountProcessCanaryQueryIntegrationTest` +- 使用真实数据库中的独立测试营业账数据(`biz_charge.id=900002`,`pay_state=1`) +- 断言: + - `POST /admin-api/business/accounting-adjust/sold-submit` 返回成功包装 + - 返回 `resultStatus = PENDING_APPROVAL` + - `biz_operat_log` 写入 1 条 `旧账务兼容动作` 日志 + - `biz_operat_log_detail` 中存在与返回 `adjustmentNo` 对应的明细记录 +- 结果:与 prestorage canary 一起执行,`Rev004AccountProcessCanaryQueryIntegrationTest` 当前为 3 tests / 0 fail / 0 skip + + +## AccountLog close-loop scenario +- 场景:`log-prestorage` 二次动作 +- 测试文件:`Rev004AccountProcessCanaryQueryIntegrationTest` +- 使用真实数据库中的独立源记录(`adjustmentNo=REV004-LOG-900003`,`biz_charge.id=900003`)与目标账户(`REV004_IT_TGT` / `biz_account.id=900004`) +- 断言: + - `POST /admin-api/business/accounting-adjust/log-prestorage` 返回成功包装 + - 源营业账 `pay_state` 被清为 `0`(未收) + - 目标账户余额增加到 `20.00` + - `biz_operat_log` 写入 1 条 `旧账务兼容动作` 日志 + - `biz_operat_log_detail` 中存在与返回 `adjustmentNo` 对应的明细记录 +- 结果:与 prestorage/sold 场景一起执行时,`Rev004AccountProcessCanaryQueryIntegrationTest` 当前为 4 tests / 0 fail / 0 skip + + +## AccountLog process query scenario +- 场景:`log-process` 读取已有调整记录流程摘要 +- 测试文件:`Rev004AccountProcessCanaryQueryIntegrationTest` +- 使用真实数据库中的独立源记录(`adjustmentNo=REV004-LOG-900003`) +- 断言: + - `GET /admin-api/business/accounting-adjust/log-process` 返回成功包装 + - `processState = UPDATED` + - `resultStatus = SUCCESS` + +## Current canary suite status +- `Rev004AccountProcessCanaryQueryIntegrationTest` 当前为 5 tests / 0 fail / 0 skip +- 已覆盖:query canary、prestorage-submit、sold-submit、log-prestorage、log-process + +## 2026-04-13 最新推进:accountLog refund 闭环补齐 +- 新增 `TransactionApi` 的 `@MockBean` 注入到 `AbstractRev004AccountProcessIntegrationTest`,确保真实数据库之外的交易后续流水依赖保持替身化。 +- 更新 `40_accountlog_seed.sql`:为 `REV004-LOG-900003` 补入 `originalTranSeq=T-900003`、`originalSysTranSeq=SYS-900003`,使 `log-refund` 路径具备真实可执行前置数据。 +- 新增 canary 闭环用例:`logRefund_shouldClearChargePaymentAndWriteFollowupLog` + +### 新增闭环断言 +- `POST /admin-api/business/accounting-adjust/log-refund` +- 返回:`resultStatus=SUCCESS`、`objectType=PREPAID_REFUND`、`writeBackStatus=UPDATED` +- 数据库断言: + - `biz_charge.id=900003` 的 `pay_state` 从已收改为 `0` + - `biz_operat_log` 中新增 1 条 `账务调整` 日志,`operat_content` 含返回的 `adjustmentNo` + - `biz_operat_log_detail` 中存在 `bankTranSeq=RF-900003` +- 替身断言:`TransactionApi.createFollowupTransaction(...)` 被调用 + +### 本次真实库验证 +- 执行 1:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcessCanaryQueryIntegrationTest' test` +- 结果 1:BUILD SUCCESS + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 7 tests / 0 fail / 0 skip +- 执行 2:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcess*' test` +- 结果 2:BUILD SUCCESS + - `Rev004AccountProcessLiveDbReadinessTest`: 1 pass + - `Rev004AccountProcessIntegrationFixtureAssetsTest`: 1 pass + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 7 pass + - 总计:9 tests / 0 fail / 0 skip + +### 当前 canary 套件真实覆盖 +- `prestorage-page_shouldReturnSuccessEnvelope` +- `prestorageSubmit_shouldChangeBalanceAndWriteOperatLog` +- `soldSubmit_shouldWritePendingOperatLog` +- `logPrestorage_shouldClearChargePaymentAndIncreaseTargetDeposit` +- `logProcess_shouldReturnUpdatedProcessSummary` +- `logAttachments_shouldReturnUnresolvedAttachmentRef` +- `logRefund_shouldClearChargePaymentAndWriteFollowupLog` + +### 仍待继续 +- `unsold` 五类 happy path 真实库闭环 +- `revoke/log-revoke/sold-batch-revoke` 的真实库撤销链验证 +- 更高一层的“提交 -> 查询页回看 -> detail/process/attachments 全链联动”场景编排 + +## 2026-04-13 继续推进:撤销链真实库闭环补齐 +- 新增真实库撤销链 canary: + - `prestorageRevoke_shouldRestoreBalanceAndWriteRevokeLog` + - `soldBatchRevoke_shouldWriteRevokeLogForPendingSyntheticRecord` + - `logRevoke_shouldRestoreChargePaymentAndRollbackTransferredDeposit` +- 为支持旧兼容动作撤销,本轮同时修正 `AccountingAdjustProcessServiceImpl.findSyntheticSnapshot(...)`: + - 改为按 `adjustmentNo` 直接回查 operat_log / operat_log_detail + - 避免旧预存调整 / 旧日志转预存 场景在撤销时误落入 unified snapshot 分支 + +### 新增闭环断言 +1. `prestorage-revoke` +- 先发起 `prestorage-submit` +- 再调用 `POST /admin-api/business/accounting-adjust/prestorage-revoke` +- 断言: + - 返回 `actionType=REVOKE` + - `biz_account.id=900001.deposit` 从 `90.00` 恢复到 `100.00` + - 存在 `revokeOfAdjustmentNo=<原adjustmentNo>` 明细 + +2. `sold-batch-revoke` +- 先发起 `sold-submit` +- 再调用 `POST /admin-api/business/accounting-adjust/sold-batch-revoke` +- 断言: + - `successCount=1` + - 存在 `revokeOfAdjustmentNo=<原adjustmentNo>` 明细 + +3. `log-revoke` +- 先发起 `log-prestorage` +- 再调用 `POST /admin-api/business/accounting-adjust/log-revoke` +- 断言: + - 返回 `actionType=REVOKE` + - `biz_charge.id=900003.pay_state` 恢复为 `1` + - `charge_method` 恢复为 `2` + - `charge_way` 恢复为 `6` + - `biz_account.id=900004.deposit` 从 `20.00` 回退到 `0.00` + - 存在 `revokeOfAdjustmentNo=<原adjustmentNo>` 明细 + +### 本次真实库验证 +- 执行 1:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcessCanaryQueryIntegrationTest' test` +- 结果 1:BUILD SUCCESS + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 10 tests / 0 fail / 0 skip +- 执行 2:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcess*' test` +- 结果 2:BUILD SUCCESS + - `Rev004AccountProcessLiveDbReadinessTest`: 1 pass + - `Rev004AccountProcessIntegrationFixtureAssetsTest`: 1 pass + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 10 pass + - 总计:12 tests / 0 fail / 0 skip + +### 当前 canary 套件真实覆盖(10 条) +- `prestoragePage_shouldReturnSuccessEnvelope` +- `prestorageSubmit_shouldChangeBalanceAndWriteOperatLog` +- `prestorageRevoke_shouldRestoreBalanceAndWriteRevokeLog` +- `soldSubmit_shouldWritePendingOperatLog` +- `soldBatchRevoke_shouldWriteRevokeLogForPendingSyntheticRecord` +- `logPrestorage_shouldClearChargePaymentAndIncreaseTargetDeposit` +- `logRevoke_shouldRestoreChargePaymentAndRollbackTransferredDeposit` +- `logProcess_shouldReturnUpdatedProcessSummary` +- `logAttachments_shouldReturnUnresolvedAttachmentRef` +- `logRefund_shouldClearChargePaymentAndWriteFollowupLog` + +## 2026-04-13 继续推进:unsold 五类真实库 happy path 补齐 +- 新增 `30_unsold_seed.sql` 真实库 fixture: + - `biz_cust.id=900005 / code=REV004_IT_UNSOLD` + - `biz_charge.id=900005 / pay_state=0 / bill_amount=120.00 / extended_amount=120.00 / late_fee=30.00` +- 扩展 `00_reset.sql`,纳入 `900005 / REV004_IT_UNSOLD` 清理范围 +- 新增 5 条真实库 canary: + - `unsoldAdjustSubmit_shouldUpdateAmountsAndWriteOperatLog` + - `unsoldSplitSubmit_shouldCreatePendingApprovalRecord` + - `unsoldLateFeeReduceSubmit_shouldCreatePendingApprovalRecord` + - `unsoldPriceDiffSubmit_shouldCreatePendingApprovalRecord` + - `unsoldBadDebtSubmit_shouldCreatePendingApprovalRecord` + +### 新增闭环断言 +1. `unsold-adjust-submit` +- 返回 `SUCCESS` +- `biz_charge.id=900005.extended_amount` 更新为 `88.88` +- `bill_amount` 更新为 `80.00` +- `biz_operat_log.operat_content` 含返回 `adjustmentNo` + +2. `unsold-split-submit` +- 返回 `PENDING_APPROVAL` +- `objectType=SPLIT_ADJUST` +- `biz_operat_log.operat_content` 含返回 `adjustmentNo` + +3. `unsold-late-fee-reduce-submit` +- 返回 `PENDING_APPROVAL` +- `objectType=LATE_FEE_REDUCE` +- `biz_operat_log.operat_content` 含返回 `adjustmentNo` + +4. `unsold-price-diff-submit` +- 返回 `PENDING_APPROVAL` +- `objectType=PRICE_DIFF_ADJUST` +- `biz_operat_log.operat_content` 含返回 `adjustmentNo` + +5. `unsold-bad-debt-submit` +- 返回 `PENDING_APPROVAL` +- `objectType=BAD_DEBT_RECORD` +- `biz_operat_log.operat_content` 含返回 `adjustmentNo` + +### 本次真实库验证 +- 执行 1:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcessCanaryQueryIntegrationTest' test` +- 结果 1:BUILD SUCCESS + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 15 tests / 0 fail / 0 skip +- 执行 2:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcess*' test` +- 结果 2:BUILD SUCCESS + - `Rev004AccountProcessLiveDbReadinessTest`: 1 pass + - `Rev004AccountProcessIntegrationFixtureAssetsTest`: 1 pass + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 15 pass + - 总计:17 tests / 0 fail / 0 skip + +### 当前 canary 套件真实覆盖(15 条) +- `prestoragePage_shouldReturnSuccessEnvelope` +- `prestorageSubmit_shouldChangeBalanceAndWriteOperatLog` +- `prestorageRevoke_shouldRestoreBalanceAndWriteRevokeLog` +- `soldSubmit_shouldWritePendingOperatLog` +- `soldBatchRevoke_shouldWriteRevokeLogForPendingSyntheticRecord` +- `unsoldAdjustSubmit_shouldUpdateAmountsAndWriteOperatLog` +- `unsoldSplitSubmit_shouldCreatePendingApprovalRecord` +- `unsoldLateFeeReduceSubmit_shouldCreatePendingApprovalRecord` +- `unsoldPriceDiffSubmit_shouldCreatePendingApprovalRecord` +- `unsoldBadDebtSubmit_shouldCreatePendingApprovalRecord` +- `logPrestorage_shouldClearChargePaymentAndIncreaseTargetDeposit` +- `logRevoke_shouldRestoreChargePaymentAndRollbackTransferredDeposit` +- `logProcess_shouldReturnUpdatedProcessSummary` +- `logAttachments_shouldReturnUnresolvedAttachmentRef` +- `logRefund_shouldClearChargePaymentAndWriteFollowupLog` + +### 剩余待补 +- 更高一层的“提交 -> page/stat/detail/process/attachments 联动回看”场景编排 +- 若要进一步贴近前端验收,可再补一轮按弹窗功能分组的端到端查询/动作串联用例 + +## 2026-04-13 继续推进:提交后联动回看场景补齐 +- 新增 3 条更贴近前端验收的联动回看 canary: + - `prestorageSubmit_thenPageStatAndDetailShouldExposeNewRecord` + - `soldSubmit_thenSoldPageAndStatShouldExposePendingRevokeCapability` + - `logSeed_thenPageStatDetailProcessAndAttachmentsShouldStayConsistent` + +### 新增联动断言 +1. `prestorage` 提交后联动回看 +- 提交 `prestorage-submit` +- 再查: + - `prestorage-page` + - `prestorage-stat` + - `prestorage-detail` +- 断言: + - page 能看到新 `adjustmentNo` + - stat 的 `totalCount=1 / refundCount=1` + - detail 返回 `resultStatus=SUCCESS / writeBackStatus=UPDATED` + +2. `sold` 提交后联动回看 +- 提交 `sold-submit` +- 再查: + - `sold-page` + - `sold-stat` +- 断言: + - page 仍能看到 `biz_charge.id=900002` + - `canBatchRevoke=true` + - stat 的 `totalCount=1 / totalActualAmount=80.00` + +3. `log` 全链回看 +- 基于 `40_accountlog_seed.sql` 中的 `REV004-LOG-900003` +- 依次验证: + - `log-page` + - `log-stat` + - `log-detail` + - `log-process` + - `log-attachments` +- 断言: + - page 可见 `adjustmentNo=REV004-LOG-900003` + - stat `completedCount=1` + - detail 包含 `originalTranSeq=T-900003` 与 `attachmentRefs[0]=mock-ref-1` + - process `processState=UPDATED` + - attachments 可返回 `mock-ref-1` + +### 本次真实库验证 +- 执行 1:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcessCanaryQueryIntegrationTest' test` +- 结果 1:BUILD SUCCESS + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 18 tests / 0 fail / 0 skip +- 执行 2:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcess*' test` +- 结果 2:BUILD SUCCESS + - `Rev004AccountProcessLiveDbReadinessTest`: 1 pass + - `Rev004AccountProcessIntegrationFixtureAssetsTest`: 1 pass + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 18 pass + - 总计:20 tests / 0 fail / 0 skip + +### 当前真实库 canary 覆盖(18 条) +- `prestoragePage_shouldReturnSuccessEnvelope` +- `prestorageSubmit_shouldChangeBalanceAndWriteOperatLog` +- `prestorageRevoke_shouldRestoreBalanceAndWriteRevokeLog` +- `prestorageSubmit_thenPageStatAndDetailShouldExposeNewRecord` +- `soldSubmit_shouldWritePendingOperatLog` +- `soldBatchRevoke_shouldWriteRevokeLogForPendingSyntheticRecord` +- `soldSubmit_thenSoldPageAndStatShouldExposePendingRevokeCapability` +- `unsoldAdjustSubmit_shouldUpdateAmountsAndWriteOperatLog` +- `unsoldSplitSubmit_shouldCreatePendingApprovalRecord` +- `unsoldLateFeeReduceSubmit_shouldCreatePendingApprovalRecord` +- `unsoldPriceDiffSubmit_shouldCreatePendingApprovalRecord` +- `unsoldBadDebtSubmit_shouldCreatePendingApprovalRecord` +- `logPrestorage_shouldClearChargePaymentAndIncreaseTargetDeposit` +- `logRevoke_shouldRestoreChargePaymentAndRollbackTransferredDeposit` +- `logSeed_thenPageStatDetailProcessAndAttachmentsShouldStayConsistent` +- `logProcess_shouldReturnUpdatedProcessSummary` +- `logAttachments_shouldReturnUnresolvedAttachmentRef` +- `logRefund_shouldClearChargePaymentAndWriteFollowupLog` + +### 当前剩余缺口 +- 若要进一步逼近前端验收,可继续补“弹窗 A/B/C 对应多接口切换”的编排式测试摘要 +- 以及基于接口返回字段做一版更面向页面的 contract/assertion matrix + +## 2026-04-13 继续推进:unsold 查询面真实库回看补齐 +- 新增 2 条真实库查询联动 canary: + - `unsoldSeed_thenPageAndStatShouldExposeChargeSnapshot` + - `unsoldAdjustSubmit_thenPageAndStatShouldReflectUpdatedAmounts` + +### 新增断言 +1. `unsold` 初始查询面 +- `GET /admin-api/business/accounting-adjust/unsold-page` +- `GET /admin-api/business/accounting-adjust/unsold-stat` +- 断言: + - `biz_charge.id=900005` 可见 + - `totalAmount=120.00` + - `billAmount=120.00` + - `penaltyAmount=30.00` + - `canAdjust/canSplit/canLateFeeReduce/canPriceDiffAdjust/canBadDebtAdjust=true` + - stat:`sumWaterVolume=5.000 / sumTotalAmount=120.00 / sumBillAmount=120.00 / sumPenaltyAmount=30.00` + +2. `unsold-adjust-submit` 后查询面回看 +- 先提交 `unsold-adjust-submit` +- 再查 `unsold-page / unsold-stat` +- 断言: + - page 中 `totalAmount=88.88` + - page 中 `billAmount=80.00` + - stat 中 `sumTotalAmount=88.88` + - stat 中 `sumBillAmount=80.00` + +### 本次真实库验证 +- 执行 1:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcessCanaryQueryIntegrationTest' test` +- 结果 1:BUILD SUCCESS + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 20 tests / 0 fail / 0 skip +- 执行 2:`REV004_IT_DB_URL=... REV004_IT_DB_USERNAME=... REV004_IT_DB_PASSWORD=... mvn -Dtest='Rev004AccountProcess*' test` +- 结果 2:BUILD SUCCESS + - `Rev004AccountProcessLiveDbReadinessTest`: 1 pass + - `Rev004AccountProcessIntegrationFixtureAssetsTest`: 1 pass + - `Rev004AccountProcessCanaryQueryIntegrationTest`: 20 pass + - 总计:22 tests / 0 fail / 0 skip + +### 影响 +- `prestorage / sold / unsold / log` 四条主查询面现在都至少有一组真实库回看证据 +- 剩余未补重点从“主链查询缺口”收敛为“预存页流程/附件链路 + 字典/UI文案展示一致性” + +## 2026-04-13 继续推进:prestorage process / attachments 真实库补齐 +- 新增 canary:`prestorageSubmit_thenProcessAndAttachmentsShouldExposeReturnedAdjustmentNo` +- 在 `prestorage-submit` 场景中附带 `attachmentRefs=[pre-proof-2, pre-proof-3]` +- 提交后继续验证: + - `GET /admin-api/business/accounting-adjust/prestorage-process` + - `GET /admin-api/business/accounting-adjust/prestorage-attachments` + +### 新增断言 +- process: + - `adjustmentNo` 与提交返回一致 + - `resultStatus=SUCCESS` + - `approvalStatus=NOT_REQUIRED` + - `writeBackStatus=UPDATED` + - `processState=UPDATED` + - `stages[0].message` 存在 +- attachments: + - 返回两条附件引用 + - `ref=pre-proof-2 / pre-proof-3` + - `resolved=false` + +### 本次真实库验证 +- `Rev004AccountProcessCanaryQueryIntegrationTest`: **21 tests / 0 fail / 0 skip** +- `Rev004AccountProcess*`: **总计 23 tests / 0 fail / 0 skip** + +### 影响 +- `prestorage` 查询/提交流程/撤销/流程查看/附件查看 这一组链路现在都已有真实库证据 +- 剩余缺口进一步收敛到:字典 label / UI 文案一致性、边角筛选条件穷尽、UI 编排级验证 diff --git a/docs/evidence/rev004-accountprocess-interface-truth-matrix-2026-04-13.md b/docs/evidence/rev004-accountprocess-interface-truth-matrix-2026-04-13.md new file mode 100644 index 0000000..dbeb1ee --- /dev/null +++ b/docs/evidence/rev004-accountprocess-interface-truth-matrix-2026-04-13.md @@ -0,0 +1,315 @@ +# REV004 / accountProcess 接口真值矩阵(2026-04-13) + +## 目的 +给前端/联调/验收一个“当前代码真实口径”矩阵: +- 哪个页面/弹窗对应哪些接口 +- 每个接口当前稳定返回哪些关键字段 +- 哪些字段已经被真实库测试覆盖 +- 哪些地方仍然是页面编排层面的剩余缺口 + +> 口径来源:`water-backend` 当前控制器 + Req/RespVO + 已落地真实库 canary。 + +--- + +## 一、页面 / 弹窗 -> 接口映射 + +### 1. 预存调整页 +#### 查询区 +- `GET /admin-api/business/accounting-adjust/prestorage-page` +- `GET /admin-api/business/accounting-adjust/prestorage-stat` +- `GET /admin-api/business/accounting-adjust/prestorage-detail?id={id}` + +#### 动作区 +- `POST /admin-api/business/accounting-adjust/prestorage-submit` +- `POST /admin-api/business/accounting-adjust/prestorage-revoke` +- `GET /admin-api/business/accounting-adjust/prestorage-process?adjustmentNo=...` +- `GET /admin-api/business/accounting-adjust/prestorage-attachments?adjustmentNo=...` + +#### 维护区 +- `POST /prestorage-customer-update` +- `POST /prestorage-meter-update` +- `POST /prestorage-billing-update` +- `POST /prestorage-discount-update` +- `POST /prestorage-cost-component-update` + +### 2. 已销调整页 +#### 查询区 +- `GET /admin-api/business/accounting-adjust/sold-page` +- `GET /admin-api/business/accounting-adjust/sold-stat` + +#### 动作区 +- `POST /admin-api/business/accounting-adjust/sold-submit` +- `POST /admin-api/business/accounting-adjust/sold-batch-revoke` + +### 3. 未销调整页 +#### 查询区 +- `GET /admin-api/business/accounting-adjust/unsold-page` +- `GET /admin-api/business/accounting-adjust/unsold-stat` + +#### 动作区(按弹窗切换) +- 调整:`POST /unsold-adjust-submit` +- 分账:`POST /unsold-split-submit` +- 违约金减免:`POST /unsold-late-fee-reduce-submit` +- 价差调整:`POST /unsold-price-diff-submit` +- 呆坏账:`POST /unsold-bad-debt-submit` + +#### 批量区 +- `POST /unsold-adjust-batch-submit` +- `POST /unsold-split-batch-submit` +- `POST /unsold-late-fee-reduce-batch-submit` +- `POST /unsold-price-diff-batch-submit` +- `POST /unsold-bad-debt-batch-submit` + +### 4. 账务日志页 +#### 查询区 +- `GET /admin-api/business/accounting-adjust/log-page` +- `GET /admin-api/business/accounting-adjust/log-stat` +- `GET /admin-api/business/accounting-adjust/log-detail?adjustmentNo=...` + +#### 二次动作弹窗 +- 转退款:`POST /admin-api/business/accounting-adjust/log-refund` +- 转预存:`POST /admin-api/business/accounting-adjust/log-prestorage` +- 撤销:`POST /admin-api/business/accounting-adjust/log-revoke` + +#### 辅助弹窗 +- `GET /admin-api/business/accounting-adjust/log-process?adjustmentNo=...` +- `GET /admin-api/business/accounting-adjust/log-attachments?adjustmentNo=...` + +--- + +## 二、关键返回字段真值 + +### A. 通用动作返回 `AccountingAdjustRespVO` +适用: +- `prestorage-submit` +- `sold-submit` +- `unsold-*submit` +- `log-refund` +- `log-prestorage` + +当前稳定字段: +- `adjustmentNo`:主联调键 +- `objectType`:对象类型;未销调整 AMOUNT/USAGE 场景可能为空,其他场景一般有值 +- `resultStatus`:推荐前端主读字段 +- `approvalStatus` +- `writeBackStatus` +- `approvalRequired` +- `resultObjectNo` +- `msg` +- `status`:兼容旧字段 +- `message`:兼容旧字段 + +前端推荐: +- **优先读** `resultStatus / approvalStatus / writeBackStatus / msg` +- `status / message` 仅做兼容保底 + +### B. 撤销 / 审批动作返回 `AccountingAdjustActionRespVO` +适用: +- `prestorage-revoke` +- `log-revoke` + +当前稳定字段: +- `adjustmentNo` +- `objectType` +- `actionType`(当前撤销返回 `REVOKE`) +- `approvalStatus` +- `resultStatus` +- `writeBackStatus` +- `message` +- `actionTime` + +### C. 预存分页 `AccountingAdjustPrestoragePageRespVO` +关键字段: +- `id` +- `adjustmentNo` +- `custId` +- `workOrderStatus` +- `adjustmentType` +- `custCode / custName / custAddress` +- `targetCustCode` +- `adjustAmount / preBalance / postBalance` +- `adjustReason / remark` +- `createTime / creatorName` +- `canRevoke / canViewAttachment` + +补充: +- `prestorage-detail` 在上面基础上增加: + - `objectType` + - `resultStatus` + - `approvalStatus` + - `writeBackStatus` + - `processInstanceId` + - `attachmentRefs` + +### D. 已销分页 `AccountingAdjustSoldPageRespVO` +关键字段: +- `id` +- `custId` +- `accountMonth` +- `custCode / custName / custAddress` +- `waterNature` +- `billedWaterVolume` +- `actualAmount` +- `billedAmount` +- `penaltyAmount` +- `collectionMethod` +- `collector` +- `collectionDate` +- `canAdjust` +- `canBatchRevoke` + +注意: +- 已销查询页**不是调整日志页**,所以这里没有 `adjustmentNo` +- `sold-submit` 提交后,前端若要看申请单号,应跳到 `log-*` 维度或使用提交返回值保存的 `adjustmentNo` + +### E. 未销分页 `AccountingAdjustUnsoldPageRespVO` +关键字段: +- `id` +- `custId` +- `custCode / custName / custAddress` +- `waterType` +- `accountMonth` +- `waterVolume` +- `totalAmount / billAmount / penaltyAmount` +- `waterFee / sewageFee / garbageFee / resourcesFee / overPlanFee / seasonalSupplement` +- `canAdjust / canSplit / canLateFeeReduce / canPriceDiffAdjust / canBadDebtAdjust` + +### F. 日志分页 `AccountingAdjustLogPageRespVO` +关键字段: +- `id` +- `adjustmentNo` +- `accountMonth` +- `custId / custCode / custName / custAddress` +- `accountTypeCode / accountType` +- `processMethodCode / processMethod` +- `amount` +- `description` +- `registrant / handler` +- `handleTime / createTime` +- `statusCode / status` +- `targetCustCode` +- `originalPrestore / newPrestore` +- `originalBill / newBill` +- `originalWaterVolume / newWaterVolume` +- `originalPenalty / newPenalty` +- `billChange / waterVolumeChange / penaltyChange` +- `canRevoke / canRefund / canPrestore` + +### G. 日志详情 `AccountingAdjustLogDetailRespVO` +在日志分页基础上增加: +- `objectType` +- `resultStatus` +- `approvalStatus` +- `writeBackStatus` +- `originalTranSeq` +- `originalSysTranSeq` +- `attachmentRefs` +- `details[]` + +### H. 流程 / 附件 +#### `AccountingAdjustProcessRespVO` +关键字段: +- `adjustmentNo` +- `objectType` +- `adjustType` +- `chargeId` +- `actionAmount` +- `resultStatus` +- `approvalStatus` +- `writeBackStatus` +- `taskId` +- `processState` +- `latestMessage` +- `latestOperationTime` +- `stages[]` + +#### `AccountingAdjustAttachmentRespVO` +关键字段: +- `adjustmentNo` +- `ref` +- `resolved` +- `id / name / link / size / extension` +- `message` + +--- + +## 三、当前已经被真实库测试证明的字段/链路 + +### 已证明 +#### 预存 +- `prestorage-submit` 返回 `adjustmentNo/resultStatus/writeBackStatus` +- `prestorage-page` 可见新记录 +- `prestorage-stat` 可统计到 `refundCount` +- `prestorage-detail` 可按 `id` 回看 `resultStatus/writeBackStatus` +- `prestorage-revoke` 可回滚余额并生成撤销日志 + +#### 已销 +- `sold-submit` 返回 `adjustmentNo/resultStatus=PENDING_APPROVAL` +- `sold-page` 可见原账单,且 `canBatchRevoke=true` +- `sold-stat` 可统计 `totalCount / totalActualAmount` +- `sold-batch-revoke` 可按营业账 ID 执行撤销 + +#### 未销 +- 五类 submit 均已跑通真实库 happy path: + - adjust + - split + - late-fee-reduce + - price-diff + - bad-debt +- 动作返回值与 operat_log 写入已被验证 + +#### 日志 +- `log-page / log-stat / log-detail / log-process / log-attachments` 已形成一组真实库联动回看 +- `log-refund`、`log-prestorage`、`log-revoke` 已打通闭环 +- `log-detail` 中 `originalTranSeq / attachmentRefs` 已被验证 + +--- + +## 四、前端使用建议(当前真值口径) + +### 1. 提交后立即展示 +提交类动作完成后,前端应优先使用提交响应里的: +- `adjustmentNo` +- `resultStatus` +- `approvalStatus` +- `writeBackStatus` +- `msg` + +### 2. 页面列表回看 +- 预存页:查 `prestorage-page` +- 已销页:查 `sold-page` +- 未销页:查 `unsold-page` +- 若要看“申请/动作留痕”,查 `log-page` + +### 3. 二次动作弹窗 +- 日志页弹窗(退款/转预存/撤销)都应以 `adjustmentNo` 作为主键 +- 已销批量撤销当前以 `chargeIds[]` 为入参,不是 `adjustmentNo[]` + +### 4. 状态口径 +- 预存页 `workOrderStatus`:0未处理 / 1已审核 / 2已完成 / 3已撤销 +- 通用动作结果: + - `resultStatus` + - `approvalStatus` + - `writeBackStatus` +- 日志页状态: + - `statusCode/status` 为页面状态投影,不等于 `workOrderStatus` + +--- + +## 五、剩余缺口 +1. 还没有做“前端弹窗 A/B/C 切换路径”的 UI 级编排,只做到了接口级真实库链路。 +2. 还没有把所有字段做成“页面元素 -> 字段 -> 接口 -> 证据”的逐项勾稽表。 +3. 若后续要做最终验收,建议再补一版: + - 弹窗功能分组矩阵 + - 字段级 contract/assertion matrix + +--- + +## 六、证据索引 +- 集成测试 bootstrap 与累计验证: + - `docs/evidence/rev004-accountprocess-integration-testing-bootstrap-2026-04-13.md` +- 代码位置: + - `sw-business-server/src/main/java/.../accountProcess/*Controller.java` + - `sw-business-server/src/main/java/.../accountProcess/vo/*ReqVO.java` + - `sw-business-server/src/main/java/.../accountProcess/vo/*RespVO.java` + - `sw-business-server/src/test/java/.../integration/rev004/accountprocess/Rev004AccountProcessCanaryQueryIntegrationTest.java` diff --git a/docs/evidence/rev004-accountprocess-page-label-audit-2026-04-13.md b/docs/evidence/rev004-accountprocess-page-label-audit-2026-04-13.md new file mode 100644 index 0000000..bc7d78a --- /dev/null +++ b/docs/evidence/rev004-accountprocess-page-label-audit-2026-04-13.md @@ -0,0 +1,104 @@ +# REV004 / accountProcess 页面文案与字典 label 核对表(2026-04-13) + +## 1. 目的 +本表站在前端/UAT 视角,回答: +- 页面上显示的文案/标签,当前应从哪个字段读取? +- 该字段是原始编码、字典 label,还是代码 fallback 值? +- 当前是否已具备稳定真值来源? + +> 说明:本表是“字段/label 来源核对”,不是视觉稿验收。 + +--- + +## 2. 预存调整页 +| 页面展示项 | 建议展示来源 | 类型 | 当前真值来源 | 状态 | +|---|---|---|---|---| +| 工单状态 | `workOrderStatus` -> `work_status` label | 字典 label | `DictTypeConstants.WORK_STATUS` + 正式设计文档 + seed SQL | 已确认 | +| 调整类型 | `adjustmentType` | 业务枚举/旧口径 | 代码已有字段,但 label 映射未单列文档 | 待前端确认展示文案 | +| 客户编号/名称/地址 | `custCode/custName/custAddress` | 直接值 | DB 真值 | 已确认 | +| 调整余额/期初/期末余额 | `adjustAmount/preBalance/postBalance` | 直接值 | DB 真值 | 已确认 | +| 是否可撤销 | `canRevoke` | 布尔能力 | Service 推导 | 已确认 | +| 是否可查看附件 | `canViewAttachment` | 布尔能力 | Service 推导 | 已确认 | +| 提交成功提示 | `msg` | 文本 | Action 返回 | 已确认 | +| 详情结果状态 | `resultStatus` | 字典 label 候选 | 当前后端主要返回 code;前端如需文案应绑 `account_adjust_result_status` | 已确认 | +| 详情审批状态 | `approvalStatus` | 字典 label 候选 | 绑 `account_adjust_approval_status` | 已确认 | +| 详情回写状态 | `writeBackStatus` | 字典 label 候选 | 绑 `account_adjust_writeback_status` | 已确认 | + +### 预存页当前结论 +- 工单状态四态已稳定:`0未处理 / 1已审核 / 2已完成 / 3已撤销` +- 详情三状态字段推荐前端统一走 REV004 新字典做 label 显示 +- `prestorage-process / prestorage-attachments` 已有真实库证据,页面已具备展示基础 + +--- + +## 3. 已销调整页 +| 页面展示项 | 建议展示来源 | 类型 | 当前真值来源 | 状态 | +|---|---|---|---|---| +| 用水性质 | `waterNature` | 当前为模板名/编码映射 | `PriceTemplate` 名称映射 | 已确认 | +| 收费方式 | `collectionMethod` | 字典 label | `charge_method` | 已确认 | +| 收费员 | `collector` | 直接值 | DB 字段 | 已确认 | +| 是否可调整 | `canAdjust` | 布尔能力 | Service 推导 | 已确认 | +| 是否可批量撤销 | `canBatchRevoke` | 布尔能力 | Service 推导 | 已确认 | +| 提交结果状态 | `resultStatus` | 字典 label 候选 | `account_adjust_result_status` | 已确认 | +| 提交审批状态 | `approvalStatus` | 字典 label 候选 | `account_adjust_approval_status` | 已确认 | + +### 已销页当前结论 +- 收费方式文案应以后端经 `charge_method` 解析后的 `collectionMethod` 为准 +- 已销提交后的审批态展示建议直接使用返回 code + 新字典映射 label +- 批量撤销按钮能力应以后端 `canBatchRevoke` 和 `chargeIds[]` 入参约束为准 + +--- + +## 4. 未销调整页 +| 页面展示项 | 建议展示来源 | 类型 | 当前真值来源 | 状态 | +|---|---|---|---|---| +| 用水性质 | `waterType` | 当前为模板编码 | 代码直接回 `priceTemplateCode` | 待前端决定是否需要 label 化 | +| 合计金额/账单金额/违约金 | `totalAmount/billAmount/penaltyAmount` | 直接值 | DB 真值 | 已确认 | +| 五类按钮显隐 | `canAdjust/canSplit/canLateFeeReduce/canPriceDiffAdjust/canBadDebtAdjust` | 布尔能力 | Service 推导 | 已确认 | +| 调整原因 | `applyReason -> 动态 reason 字典` | 字典 label | 见 REV004_DICT_BINDING_MATRIX | 已确认 | +| 分账原因 | `applyReason -> separate_reason` | 字典 label | 见 REV004_DICT_BINDING_MATRIX | 已确认 | +| 违约金减免原因 | `applyReason -> late_fee_reason` | 字典 label | 见 REV004_DICT_BINDING_MATRIX | 已确认 | +| 价差调整原因 | `applyReason -> price_reason` | 字典 label | 见 REV004_DICT_BINDING_MATRIX | 已确认 | +| 呆坏账原因 | `applyReason -> knotty_reason` | 字典 label | 见 REV004_DICT_BINDING_MATRIX | 已确认 | + +### 未销页当前结论 +- 五类弹窗的“原因”不能共用一个字典,应按 objectType/弹窗类型动态切换 +- `waterType` 当前更像编码字段,如果前端要展示“中文用水性质”,需要确认是否再做模板名映射 + +--- + +## 5. 账务日志页 +| 页面展示项 | 建议展示来源 | 类型 | 当前真值来源 | 状态 | +|---|---|---|---|---| +| 账务类型 | `accountType` | 字典 label | `account_adjust_object_type` | 已确认 | +| 处理方式 | `processMethod` | 代码映射 label | Service 内 legacy 映射 | 已确认 | +| 页面状态 | `status/statusCode` | 字典/兼容投影 | success/pending/rejected 由 Service 投影 | 已确认 | +| 目标户号 | `targetCustCode` | 直接值 | 日志明细/业务上下文 | 已确认 | +| 原/新预存、账单、水量、违约金 | 对应 original/new 字段 | 直接值 | DB + log detail 推导 | 已确认 | +| 是否可撤销/退款/预转存 | `canRevoke/canRefund/canPrestore` | 布尔能力 | Service 推导 | 已确认 | +| 结果状态 | `resultStatus` | 字典 label 候选 | `account_adjust_result_status` | 已确认 | +| 审批状态 | `approvalStatus` | 字典 label 候选 | `account_adjust_approval_status` | 已确认 | +| 回写状态 | `writeBackStatus` | 字典 label 候选 | `account_adjust_writeback_status` | 已确认 | +| 附件状态 | `resolved/message` | 直接值 | Attachment 解析逻辑 | 已确认 | + +### 日志页当前结论 +- `accountType` 与 `resultStatus/approvalStatus/writeBackStatus` 已有稳定字典来源 +- `processMethod` 目前是代码映射,不完全等于字典 label,需要前端按当前返回文案展示,不建议自行二次翻译 +- 日志页是当前最适合承接“状态展示 / 二次动作 / 留痕回看”的页面 + +--- + +## 6. 当前仍需前端/UAT逐页确认的点 +1. 未销页 `waterType` 是否需要从编码升级为可读 label。 +2. 预存页 `adjustmentType` 的页面中文文案是否已有既定口径。 +3. 日志页 `processMethod/status` 是否与现有页面视觉稿/原型文案完全一致。 +4. 历史页面或旧截图中若还出现“处理中”这类旧三态文案,应以后端新四态口径为准重新校对。 + +--- + +## 7. 结论 +- 当前**状态字典与核心文案来源已经收口**,后端没有明显“真值不清”的问题。 +- 剩余问题主要不在后端结构,而在: + - 页面展示是否直接使用后端返回文案 + - 前端是否仍残留旧页面文案/旧字典绑定 + - 某些编码字段是否还需要做前端可读化处理 diff --git a/docs/evidence/rev004-accountprocess-split-adjust-brief-2026-04-14.md b/docs/evidence/rev004-accountprocess-split-adjust-brief-2026-04-14.md new file mode 100644 index 0000000..77e4407 --- /dev/null +++ b/docs/evidence/rev004-accountprocess-split-adjust-brief-2026-04-14.md @@ -0,0 +1,85 @@ +# REV004 / 分账短版说明(给产品 / 前端)(2026-04-14) + +## 一句话结论 +**分账** 不是退款,也不是减免。 +它的业务含义是: +> 把一笔原账单按规则拆成多笔可独立处理的结果,便于分别收费、分别开票、分别承担。 + +当前 REV004 后端已经支持: +- **发起分账申请** +- **进入待审批** +- **被日志/查询/审批状态承接** + +当前还不支持: +- **审批通过后真正执行拆账,生成多张目标账单** + +--- + +## 当前可以怎么理解 +### 业务上 +老系统分账支持两种方式: +1. **按水量分账** +2. **按费用组成分账** + +它适用于: +- 客户需要开多张发票 +- 客户不能一次缴清 +- 一张账单需要按规则拆给多个结果对象 + +### 后端当前落地到哪里 +当前后端实现的是: +- `SPLIT_ADJUST` 分账申请 +- 提交后状态:`PENDING_APPROVAL` +- 可以查询、留痕、审批承接 + +但还没有做到: +- 真的把一张原账单拆成多张目标账单 +- 真的生成拆分明细 +- 真的进入收费/开票承接执行态 + +--- + +## 为什么还没做到执行态 +因为“真正分账执行”不是普通字段修改,而是一个独立正式业务对象级能力,至少涉及: +- 原账单与目标账单关系 +- 分摊规则 +- 分摊明细 +- 审批通过后的执行流程 +- 执行失败回滚 +- 与收费/开票的后续承接 + +所以当前先落的是: +> **申请态 / 受理态** + +而不是: +> **执行态 / 拆账落地态** + +--- + +## 现在前端该怎么处理 +### 当前前端可以做的 +- 把“分账”当成一种**待审批业务申请**来用 +- 提交后展示: + - `adjustmentNo` + - `resultStatus = PENDING_APPROVAL` + - `approvalStatus = PENDING_APPROVAL` + - `writeBackStatus = PENDING` +- 在日志/列表中查看这笔申请的留痕与状态 + +### 当前前端不要假设的 +- 不要假设提交分账后会立刻生成多张新账单 +- 不要假设已经存在完整的“分账结果明细页”可直接读取目标账单集合 + +--- + +## 如果后续要继续做 +建议后续按这个顺序推进: +1. 明确业务真值:分账结果到底是“多张子账单”为主,还是“多笔待收费结果”为主 +2. 明确数据模型:`SplitAdjust / SplitAdjustDetail` +3. 明确审批后执行:真正拆账、重算、留痕、回看 +4. 再补前端执行态页面 + +--- + +## 当前最适合对外的话术 +> 当前 REV004 已支持“分账申请”的统一受理、审批与查询回看,但尚未落到“审批通过后真正拆账生成多张子账单”的执行态。若后续要继续建设,需要按独立正式业务对象方式展开,而不是把它当成普通账单字段修改处理。 diff --git a/docs/evidence/rev004-accountprocess-split-adjust-gap-summary-2026-04-14.md b/docs/evidence/rev004-accountprocess-split-adjust-gap-summary-2026-04-14.md new file mode 100644 index 0000000..e457609 --- /dev/null +++ b/docs/evidence/rev004-accountprocess-split-adjust-gap-summary-2026-04-14.md @@ -0,0 +1,195 @@ +# REV004 / 分账为什么还没做到执行态(2026-04-14) + +## 1. 目的 +本摘要用于解释: + +为什么当前 `REV004/accountProcess` 后端已经支持 `SPLIT_ADJUST` 分账申请, +但还没有做到“审批通过后真正把一张原账单拆成多张目标账单”的执行态能力。 + +--- + +## 2. 当前结论 +### 结论一句话 +当前后端实现的是: +- **分账申请态** + +尚未实现的是: +- **分账执行态** + +也就是: +- 现在可以提交分账申请、进入待审批、被日志与查询承接; +- 但还没有真正生成多张目标账单、分摊明细和后续收费/开票承接结果。 + +--- + +## 3. 代码证据 +### 3.1 当前入口 +当前分账在 `accountProcess` 中的入口是: +- `POST /admin-api/business/accounting-adjust/unsold-split-submit` + +对应代码: +- `AccountingAdjustProcessServiceImpl.createUnsoldSplit(...)` + +其行为是把分账提交统一转成: +- `objectType = SPLIT_ADJUST` +- `adjustType = SPLIT` + +然后交给统一账务处理入口: +- `chargeService.adjustAccounting(...)` + +### 3.2 当前真正处理逻辑 +在 `ChargeServiceImpl.handleSplitAdjust(...)` 中,当前逻辑只有: +- `approvalRequired = true` +- `resultStatus = PENDING_APPROVAL` +- `approvalStatus = PENDING_APPROVAL` +- `writeBackStatus = PENDING` +- `message = 账单拆分申请已提交,待审批` +- `needUpdate = false` + +这说明: +1. 只受理申请; +2. 不改原账单; +3. 不生成目标账单; +4. 不生成拆分明细; +5. 不进入真正执行态。 + +### 3.3 当前校验逻辑 +当前只做了最小申请校验: +- 仅未收费账单允许发起分账申请 +- `splitCount >= 2` +- `splitCount <= 12` +- 必须填写原因编码 +- 必须填写调整原因 + +这也进一步说明: +- 当前实现重心是“能不能发起分账申请” +- 不是“如何执行真正的分账结果落地” + +--- + +## 4. 业务复杂度为什么高 +真正的分账执行态,不是普通字段修改,而是**结构性重分摊**。 + +### 4.1 文档中的两类策略 +老系统与文档明确支持: +- **按水量分账** +- **按费用组成分账** + +### 4.2 按水量分账需要解决的问题 +- 原账单总水量拆成多笔 +- 拆分水量之和必须等于原水量 +- 拆分后重新按水价重算金额 +- 可能影响违约金、阶梯、费用明细 + +### 4.3 按费用组成分账需要解决的问题 +- 原账单费用项按规则拆分 +- 拆分后金额总额必须与原账单一致 +- 目标账单 / 目标客户 / 金额分摊关系需要明确 +- 费用项粒度如何绑定目标结果也需要独立明细结构 + +### 4.4 执行态至少需要的能力 +若真正落地执行态,后端至少需要: +- 原账单与目标账单关系模型 +- 分摊规则模型 +- 分摊明细模型 +- 审批通过后的执行流程 +- 执行失败回滚 +- 与收费/开票/日志/查询的后续承接 + +因此,这不是“小补丁”能力,而是**独立正式业务对象级别**的建设。 + +--- + +## 5. 文档证据 +### 5.1 需求与手册口径 +文档明确说明: +- 分账是“由一笔账单分成两笔独立账单信息” +- 可按“按水量”“按费用组成”进行分账调整 +- 适用于: + - 客户开多张发票 + - 不能一次缴清等场景 + +这说明业务真值更接近: +- **账单拆分 + 分摊结果重建** + +不是单纯加一条审批记录。 + +### 5.2 REV004 正式对象口径 +`REV004_FULL_ACCOUNTING_DOMAIN_DESIGN.md` 中已将: +- `SplitAdjust` +- `SplitAdjustDetail` + +视为独立正式业务对象,并建议存在: +- `split_adjust_no` +- `split_rule_type` +- `source_charge_id` +- `target_charge_id` +- `target_cust_id` +- `split_amount` +- `split_ratio / split_basis` + +这表明: +- 正式执行态设计方向已经明确; +- 但当前代码还没有真正落成这套模型。 + +### 5.3 文档中的关键冻结判断 +文档还明确给出: +- 当前先冻结设计方向 +- 分账调整后续应补规则表或规则字段组 + +这意味着: +- “申请态先落、执行态后补”是有设计背景的, +- 不是单纯遗漏开发。 + +--- + +## 6. 当前这轮 REV004 的实现目标 +从当前整体 accountProcess 收口策略看,这轮优先解决的是: +- 统一提交入口 +- 统一状态表达 +- 审批边界 +- 日志留痕 +- 查询回看 + +也就是先做到: +- 用户能发起 +- 系统能记录 +- 前端能查看 +- 审批流能承接 + +因此分账也被纳入了这套统一账务调整框架,先实现: +- **申请态可用** + +而没有直接推进到: +- **执行态拆账** + +--- + +## 7. 最终判断 +### 当前已经做到 +- `SPLIT_ADJUST` 已进入统一账务调整对象体系 +- 可提交申请 +- 返回 `PENDING_APPROVAL` +- 可被日志/查询/审批状态承接 + +### 当前还没做到 +- 审批通过后真正生成多张目标账单 +- 原账单与目标账单的主从关系落库 +- 按水量/按费用组成的拆分明细落库 +- 执行态后的收费/开票联动承接 + +### 最准确的一句话 +> 当前 REV004 后端已经实现“分账申请态”,但由于真正的分账执行态涉及分摊规则、目标账单生成、拆分明细、重算与后续收费/开票承接,属于独立正式对象级能力,因此尚未在 `accountProcess` 这一层落地。 + +--- + +## 8. 建议下一步 +如果后续要继续推进“分账执行态”,建议按顺序展开: +1. 先明确业务真值: + - 结果对象到底是多张子账单、还是多笔待收费结果、还是两者同时存在 +2. 再明确数据模型: + - `SplitAdjust` / `SplitAdjustDetail` 主表、明细表、规则字段组 +3. 再明确执行流程: + - 审批通过 -> 拆账执行 -> 重算 -> 留痕 -> 查询回看 +4. 最后补前端: + - 按水量 / 按费用组成 两种弹窗的真实执行态交互 diff --git a/docs/evidence/rev004-accountprocess-ui-element-matrix-2026-04-13.md b/docs/evidence/rev004-accountprocess-ui-element-matrix-2026-04-13.md new file mode 100644 index 0000000..5fa199c --- /dev/null +++ b/docs/evidence/rev004-accountprocess-ui-element-matrix-2026-04-13.md @@ -0,0 +1,250 @@ +# REV004 / accountProcess 页面元素勾稽矩阵(2026-04-13) + +## 目的 +把 `accountProcess` 当前后端真实口径进一步整理成: + +**页面元素 / 弹窗能力 -> 字段 -> 接口 -> 当前证据状态** + +便于: +- 前端逐项对表 +- UAT 做验收勾稽 +- 后端明确哪些已被真实库证实,哪些仍属于页面编排层缺口 + +> 本表基于当前 `water-backend` 代码、Req/RespVO、已落地真实库 canary 测试结果整理。 + +--- + +## 证据状态说明 +- **已真实库验证**:已有真实 PostgreSQL + Spring + MockMvc canary 覆盖 +- **已代码确认**:可从当前 Controller/RespVO/Service 明确确认,但尚未单独做真实库断言 +- **待补证据**:当前还缺更细粒度验证或 UI 编排验证 + +--- + +## 一、预存调整页 + +### A. 列表区 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 列表记录主键 | `id` | `GET /prestorage-page` | 已真实库验证 | 提交后可回查 detail | +| 调整单号 | `adjustmentNo` | `GET /prestorage-page` | 已真实库验证 | 提交后 page 可见 | +| 客户ID | `custId` | `GET /prestorage-page` | 已代码确认 | Response VO 已定义 | +| 工单状态 | `workOrderStatus` | `GET /prestorage-page` | 已真实库验证 | 已验证提交后为 `2=已完成` | +| 调整类型 | `adjustmentType` | `GET /prestorage-page` | 已代码确认 | page/detail 都有 | +| 客户编号 | `custCode` | `GET /prestorage-page` | 已真实库验证 | `REV004_IT_SRC` 已验证 | +| 目标户号 | `targetCustCode` | `GET /prestorage-page` | 已代码确认 | 转账/转预存场景使用 | +| 客户名称 | `custName` | `GET /prestorage-page` | 已代码确认 | | +| 客户地址 | `custAddress` | `GET /prestorage-page` | 已代码确认 | | +| 调整余额 | `adjustAmount` | `GET /prestorage-page` | 已代码确认 | | +| 期初余额 | `preBalance` | `GET /prestorage-page` | 已代码确认 | | +| 期末余额 | `postBalance` | `GET /prestorage-page` | 已代码确认 | | +| 调整原因 | `adjustReason` | `GET /prestorage-page` | 已代码确认 | | +| 备注 | `remark` | `GET /prestorage-page` | 已代码确认 | | +| 登记时间 | `createTime` | `GET /prestorage-page` | 已代码确认 | | +| 登记人员 | `creatorName` | `GET /prestorage-page` | 已代码确认 | | +| 是否可撤销 | `canRevoke` | `GET /prestorage-page` | 已代码确认 | 撤销能力由状态推导 | +| 是否可查看附件 | `canViewAttachment` | `GET /prestorage-page` | 已代码确认 | | + +### B. 统计区 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 总条数 | `totalCount` | `GET /prestorage-stat` | 已真实库验证 | | +| 调整余额合计 | `totalAdjustAmount` | `GET /prestorage-stat` | 已代码确认 | | +| 期初余额合计 | `totalPreBalance` | `GET /prestorage-stat` | 已代码确认 | | +| 期末余额合计 | `totalPostBalance` | `GET /prestorage-stat` | 已代码确认 | | +| 预存转账数量 | `transferCount` | `GET /prestorage-stat` | 已真实库验证 | 已验证退款场景下为 0 | +| 预存退款数量 | `refundCount` | `GET /prestorage-stat` | 已真实库验证 | 已验证退款场景下为 1 | + +### C. 提交弹窗 +| 页面元素 | 入参/出参字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 源客户编号 | `custCode` | `POST /prestorage-submit` | 已真实库验证 | | +| 目标户号 | `targetCustCode` | `POST /prestorage-submit` | 已真实库验证 | 转账场景可用;退款场景未单独验证 | +| 退款金额 | `refundAmount` | `POST /prestorage-submit` | 已真实库验证 | | +| 转账金额 | `transferAmount` | `POST /prestorage-submit` | 已代码确认 | 未单独真实库验证 | +| 原因 | `applyReason` | `POST /prestorage-submit` | 已真实库验证 | | +| 备注 | `remark` | `POST /prestorage-submit` | 已真实库验证 | | +| 附件引用 | `attachmentRefs` | `POST /prestorage-submit` | 已代码确认 | 未单独真实库验证 | +| 调整单号回显 | `adjustmentNo` | `POST /prestorage-submit` 返回 | 已真实库验证 | | +| 结果状态 | `resultStatus` | `POST /prestorage-submit` 返回 | 已真实库验证 | `SUCCESS` | +| 回写状态 | `writeBackStatus` | `POST /prestorage-submit` 返回 | 已真实库验证 | `UPDATED` | +| 提示文案 | `msg/message` | `POST /prestorage-submit` 返回 | 已代码确认 | | + +### D. 详情 / 附件 / 流程 / 撤销 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 详情对象类型 | `objectType` | `GET /prestorage-detail` | 已代码确认 | | +| 详情结果状态 | `resultStatus` | `GET /prestorage-detail` | 已真实库验证 | | +| 详情审批状态 | `approvalStatus` | `GET /prestorage-detail` | 已代码确认 | | +| 详情回写状态 | `writeBackStatus` | `GET /prestorage-detail` | 已真实库验证 | | +| 流程任务ID | `processInstanceId` | `GET /prestorage-detail` | 已代码确认 | | +| 附件引用 | `attachmentRefs` | `GET /prestorage-detail` | 已代码确认 | | +| 查看流程 | `processState/stages[]` | `GET /prestorage-process` | 已代码确认 | 预存页流程接口存在,未单独真实库验证 | +| 查看附件 | `ref/resolved/...` | `GET /prestorage-attachments` | 已代码确认 | 预存页附件接口存在,未单独真实库验证 | +| 撤销动作 | `actionType/resultStatus/writeBackStatus` | `POST /prestorage-revoke` | 已真实库验证 | 余额恢复已验证 | + +--- + +## 二、已销调整页 + +### A. 列表区 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 账单ID | `id` | `GET /sold-page` | 已真实库验证 | | +| 客户ID | `custId` | `GET /sold-page` | 已代码确认 | | +| 账务年月 | `accountMonth` | `GET /sold-page` | 已代码确认 | | +| 客户编号 | `custCode` | `GET /sold-page` | 已真实库验证 | | +| 客户名称 | `custName` | `GET /sold-page` | 已代码确认 | | +| 客户地址 | `custAddress` | `GET /sold-page` | 已代码确认 | | +| 用水性质 | `waterNature` | `GET /sold-page` | 已代码确认 | | +| 开账水量 | `billedWaterVolume` | `GET /sold-page` | 已代码确认 | | +| 实收金额 | `actualAmount` | `GET /sold-page` | 已真实库验证 | stat 已验证 80.00 | +| 开账金额 | `billedAmount` | `GET /sold-page` | 已代码确认 | | +| 违约金 | `penaltyAmount` | `GET /sold-page` | 已代码确认 | | +| 收费方式 | `collectionMethod` | `GET /sold-page` | 已代码确认 | | +| 收费员 | `collector` | `GET /sold-page` | 已代码确认 | | +| 收费日期 | `collectionDate` | `GET /sold-page` | 已代码确认 | | +| 是否可调整 | `canAdjust` | `GET /sold-page` | 已代码确认 | | +| 是否可批量撤销 | `canBatchRevoke` | `GET /sold-page` | 已真实库验证 | 提交后仍可撤销 | + +### B. 统计区 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 总条数 | `totalCount` | `GET /sold-stat` | 已真实库验证 | | +| 开账水量合计 | `totalBilledWaterVolume` | `GET /sold-stat` | 已代码确认 | | +| 实收金额合计 | `totalActualAmount` | `GET /sold-stat` | 已真实库验证 | 80.00 | +| 开账金额合计 | `totalBilledAmount` | `GET /sold-stat` | 已代码确认 | | +| 违约金合计 | `totalPenaltyAmount` | `GET /sold-stat` | 已代码确认 | | + +### C. 提交 / 批量撤销 +| 页面元素 | 入参/出参字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 营业账ID | `chargeId` | `POST /sold-submit` | 已真实库验证 | | +| 原因 | `applyReason` | `POST /sold-submit` | 已真实库验证 | | +| 是否红冲发票 | `redInvoice` | `POST /sold-submit` | 已代码确认 | | +| 备注 | `remark` | `POST /sold-submit` | 已真实库验证 | | +| 附件引用 | `attachmentRefs` | `POST /sold-submit` | 已代码确认 | | +| 调整单号 | `adjustmentNo` | `POST /sold-submit` 返回 | 已真实库验证 | | +| 结果状态 | `resultStatus` | `POST /sold-submit` 返回 | 已真实库验证 | `PENDING_APPROVAL` | +| 审批状态 | `approvalStatus` | `POST /sold-submit` 返回 | 已真实库验证 | `PENDING_APPROVAL` | +| 批量撤销入参 | `chargeIds[]` | `POST /sold-batch-revoke` | 已真实库验证 | 不是 `adjustmentNo[]` | +| 批量撤销结果 | `successCount/failCount/items[]` | `POST /sold-batch-revoke` | 已真实库验证 | | + +--- + +## 三、未销调整页 + +### A. 列表区 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 账单ID | `id` | `GET /unsold-page` | 已代码确认 | | +| 客户ID | `custId` | `GET /unsold-page` | 已代码确认 | | +| 客户编号 | `custCode` | `GET /unsold-page` | 已代码确认 | | +| 客户名称 | `custName` | `GET /unsold-page` | 已代码确认 | | +| 客户地址 | `custAddress` | `GET /unsold-page` | 已代码确认 | | +| 用水性质 | `waterType` | `GET /unsold-page` | 已代码确认 | | +| 账务年月 | `accountMonth` | `GET /unsold-page` | 已代码确认 | | +| 用水量 | `waterVolume` | `GET /unsold-page` | 已代码确认 | | +| 合计金额 | `totalAmount` | `GET /unsold-page` | 已代码确认 | | +| 账单金额 | `billAmount` | `GET /unsold-page` | 已代码确认 | | +| 违约金 | `penaltyAmount` | `GET /unsold-page` | 已代码确认 | | +| 用水费/污水/垃圾/资源税/超定额/季节性 | 对应费用字段 | `GET /unsold-page` | 已代码确认 | | +| 是否可调整 | `canAdjust` | `GET /unsold-page` | 已代码确认 | | +| 是否可分账 | `canSplit` | `GET /unsold-page` | 已代码确认 | | +| 是否可违约金减免 | `canLateFeeReduce` | `GET /unsold-page` | 已代码确认 | | +| 是否可价差调整 | `canPriceDiffAdjust` | `GET /unsold-page` | 已代码确认 | | +| 是否可呆坏账调整 | `canBadDebtAdjust` | `GET /unsold-page` | 已代码确认 | | + +### B. 统计区 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 用水量合计 | `sumWaterVolume` | `GET /unsold-stat` | 已代码确认 | | +| 合计金额 | `sumTotalAmount` | `GET /unsold-stat` | 已代码确认 | | +| 账单金额 | `sumBillAmount` | `GET /unsold-stat` | 已代码确认 | | +| 违约金 | `sumPenaltyAmount` | `GET /unsold-stat` | 已代码确认 | | +| 费用组成合计 | `sumWaterFee/...` | `GET /unsold-stat` | 已代码确认 | | + +### C. 五类弹窗动作 +| 弹窗 | 关键入参 | 返回口径 | 当前状态 | 说明 | +|---|---|---|---|---| +| 调整 | `chargeId/applyReason/remark/targetBillWater/targetExtendedAmount/targetBillAmount` | `adjustmentNo/resultStatus/writeBackStatus` | 已真实库验证 | AMOUNT happy path 已验证 | +| 分账 | `chargeId/applyReason/remark/splitCount` | `adjustmentNo/objectType/resultStatus=PENDING_APPROVAL` | 已真实库验证 | | +| 违约金减免 | `chargeId/applyReason/remark/lateFeeReduceAmount` | `adjustmentNo/objectType/resultStatus=PENDING_APPROVAL` | 已真实库验证 | | +| 价差调整 | `chargeId/applyReason/remark/priceDiffAmount` | `adjustmentNo/objectType/resultStatus=PENDING_APPROVAL` | 已真实库验证 | | +| 呆坏账 | `chargeId/applyReason/remark/badDebtAmount` | `adjustmentNo/objectType=result BAD_DEBT_RECORD / PENDING_APPROVAL` | 已真实库验证 | | + +--- + +## 四、账务日志页 + +### A. 列表区 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 日志ID | `id` | `GET /log-page` | 已代码确认 | | +| 调整单号 | `adjustmentNo` | `GET /log-page` | 已真实库验证 | | +| 账务年月 | `accountMonth` | `GET /log-page` | 已代码确认 | | +| 客户ID/编号/名称/地址 | `custId/custCode/custName/custAddress` | `GET /log-page` | 部分已真实库验证 | `custCode` 已验证 | +| 账务类型 | `accountTypeCode/accountType` | `GET /log-page` | 已代码确认 | | +| 处理方式 | `processMethodCode/processMethod` | `GET /log-page` | 已代码确认 | | +| 金额 | `amount` | `GET /log-page` | 已代码确认 | | +| 描述 | `description` | `GET /log-page` | 已代码确认 | | +| 登记人/处理人 | `registrant/handler` | `GET /log-page` | 已代码确认 | | +| 处理时间/创建时间 | `handleTime/createTime` | `GET /log-page` | 已代码确认 | | +| 页面状态 | `statusCode/status` | `GET /log-page` | 已真实库验证 | `SUCCESS` 场景已验证 | +| 目标户号 | `targetCustCode` | `GET /log-page` | 已代码确认 | | +| 原/新预存 | `originalPrestore/newPrestore` | `GET /log-page` | 已代码确认 | | +| 原/新账单 | `originalBill/newBill` | `GET /log-page` | 已代码确认 | | +| 原/新水量 | `originalWaterVolume/newWaterVolume` | `GET /log-page` | 已代码确认 | | +| 原/新违约金 | `originalPenalty/newPenalty` | `GET /log-page` | 已代码确认 | | +| 变化量 | `billChange/waterVolumeChange/penaltyChange` | `GET /log-page` | 已代码确认 | | +| 是否可撤销/退款/转预存 | `canRevoke/canRefund/canPrestore` | `GET /log-page` | 已代码确认 | | + +### B. 统计区 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 总条数 | `totalCount` | `GET /log-stat` | 已真实库验证 | | +| 调整金额 | `totalAmount` | `GET /log-stat` | 已代码确认 | | +| 调整水量 | `totalWaterVolume` | `GET /log-stat` | 已代码确认 | | +| 已完成数量 | `completedCount` | `GET /log-stat` | 已真实库验证 | | +| 未完成数量 | `pendingCount` | `GET /log-stat` | 已真实库验证 | sold 提交后场景已间接证明 pending | +| 已撤销数量 | `cancelledCount` | `GET /log-stat` | 已代码确认 | | + +### C. 详情 / 流程 / 附件 +| 页面元素 | 后端字段 | 接口 | 当前状态 | 说明 | +|---|---|---|---|---| +| 详情调整单号 | `adjustmentNo` | `GET /log-detail` | 已真实库验证 | | +| 对象类型 | `objectType` | `GET /log-detail` | 已代码确认 | | +| 结果状态 | `resultStatus` | `GET /log-detail` | 已真实库验证 | | +| 审批状态 | `approvalStatus` | `GET /log-detail` | 已代码确认 | | +| 回写状态 | `writeBackStatus` | `GET /log-detail` | 已代码确认 | | +| 原交易流水号 | `originalTranSeq` | `GET /log-detail` | 已真实库验证 | | +| 原系统流水号 | `originalSysTranSeq` | `GET /log-detail` | 已代码确认 | | +| 附件引用 | `attachmentRefs[]` | `GET /log-detail` | 已真实库验证 | | +| 详情明细 | `details[]` | `GET /log-detail` | 已代码确认 | | +| 流程状态 | `processState` | `GET /log-process` | 已真实库验证 | | +| 流程阶段 | `stages[]` | `GET /log-process` | 已代码确认 | | +| 附件原始引用 | `ref` | `GET /log-attachments` | 已真实库验证 | | +| 附件解析状态 | `resolved` | `GET /log-attachments` | 已真实库验证 | | +| 附件元数据 | `id/name/link/size/extension` | `GET /log-attachments` | 已代码确认 | 当前 mock-ref 未解析为真实附件 | + +### D. 日志页二次动作弹窗 +| 弹窗 | 关键入参 | 返回口径 | 当前状态 | 说明 | +|---|---|---|---|---| +| 转退款 | `adjustmentNo/reasonCode/reason` | `adjustmentNo/objectType/resultStatus/writeBackStatus` | 已真实库验证 | 且 bank 后续流水已验证 | +| 转预存 | `adjustmentNo/targetCustCode/reason` | `adjustmentNo/resultStatus` | 已真实库验证 | 目标余额变化已验证 | +| 撤销 | `adjustmentNo/comment` | `actionType/resultStatus/writeBackStatus` | 已真实库验证 | 回滚账单状态/余额已验证 | + +--- + +## 五、当前最适合前端/UAT对表的使用方式 +1. **动作成功后的即时展示**:直接用 `AccountingAdjustRespVO / AccountingAdjustActionRespVO` +2. **页面列表展示**:按页面查各自 `*-page / *-stat` +3. **动作留痕与二次操作**:统一以 `log-*` 视角看 `adjustmentNo` +4. **已销批量撤销特别注意**:当前后端口径是 `chargeIds[]`,不是 `adjustmentNo[]` + +--- + +## 六、仍待补的更细证据 +- `unsold-page / unsold-stat` 的真实库回看断言还未单独补 +- 预存页 `attachments/process` 还未像 log 页一样做完整真实库链路断言 +- 页面元素到字典标签展示(例如名称 label 是否完全满足 UI 文案)还缺单独验收 diff --git a/docs/evidence/rev004-accountprocess-ui-orchestration-checklist-2026-04-13.md b/docs/evidence/rev004-accountprocess-ui-orchestration-checklist-2026-04-13.md new file mode 100644 index 0000000..afa188c --- /dev/null +++ b/docs/evidence/rev004-accountprocess-ui-orchestration-checklist-2026-04-13.md @@ -0,0 +1,222 @@ +# REV004 / accountProcess UI 编排级验收清单(2026-04-13) + +## 1. 目的 +本清单面向前端 / UAT / 联调验收人员,回答: +- 某个页面有哪些核心弹窗/动作入口 +- 提交后应该回看哪些区域/接口 +- 当前这些链路是否已有后端真实库证据支撑 + +> 本清单不替代自动化测试,而是给人工联调 / 验收一份“按页面操作”的顺序脚本。 + +--- + +## 2. 使用说明 +每条清单包含: +- **入口**:从哪个页面/按钮进入 +- **动作**:调哪个后端接口 +- **回看**:操作成功后应检查哪些页面区域 +- **后端证据状态**:当前是否已有真实库证据 + +状态说明: +- **已真实库覆盖**:已有后端集成测试证明主链可运行 +- **已代码确认**:后端接口与字段存在,但尚未专门做该页面编排级回看 + +--- + +## 3. 预存调整页 + +### 场景 P1:预存退款提交 -> 列表/统计/详情回看 +- 入口:预存调整页“预存退款”弹窗 +- 动作接口:`POST /prestorage-submit` +- 提交后应回看: + - 列表:`GET /prestorage-page` + - 统计:`GET /prestorage-stat` + - 详情:`GET /prestorage-detail?id=...` +- 应确认: + - 新 `adjustmentNo` 出现在列表中 + - 工单状态为“已完成” + - `refundCount` 增加 + - detail 中 `resultStatus=SUCCESS` +- 后端证据状态:**已真实库覆盖** + +### 场景 P2:预存退款提交 -> 流程/附件回看 +- 入口:预存调整页提交成功后,点“查看流程 / 查看附件” +- 动作接口: + - `GET /prestorage-process?adjustmentNo=...` + - `GET /prestorage-attachments?adjustmentNo=...` +- 应确认: + - process 可返回 `processState=UPDATED` + - attachments 返回提交时带入的附件引用 +- 后端证据状态:**已真实库覆盖** + +### 场景 P3:预存记录撤销 -> 列表/余额回退 +- 入口:预存调整页列表“撤销”按钮 +- 动作接口:`POST /prestorage-revoke` +- 回看: + - 列表撤销状态 + - 账户余额恢复 + - 关联撤销日志 +- 后端证据状态:**已真实库覆盖** + +### 场景 P4:预存转账提交 +- 入口:预存调整页“预存转账”弹窗 +- 动作接口:`POST /prestorage-submit`(传 `targetCustCode + transferAmount`) +- 回看: + - 列表记录 + - 目标户号 + - 期初/期末余额变化 +- 后端证据状态:**已代码确认 / 部分真实库覆盖** +- 说明:转账字段链路存在,但当前主证据仍以退款场景为主 + +--- + +## 4. 已销调整页 + +### 场景 S1:已销调整提交 -> 已销列表/统计回看 +- 入口:已销调整页“提交”弹窗 +- 动作接口:`POST /sold-submit` +- 回看: + - `GET /sold-page` + - `GET /sold-stat` +- 应确认: + - 原账单仍可见 + - `canBatchRevoke=true` + - 统计金额仍符合账单真值 +- 后端证据状态:**已真实库覆盖** + +### 场景 S2:已销调整提交 -> 日志页留痕回看 +- 入口:已销调整提交成功后,使用返回的 `adjustmentNo` 在日志页查询 +- 动作接口: + - `GET /log-detail?adjustmentNo=...` + - `GET /log-process?adjustmentNo=...` +- 应确认: + - 日志详情存在 + - 状态为待审批 +- 后端证据状态:**已代码确认 / 部分真实库覆盖** +- 说明:已销提交本身和日志链主字段已证实,但“提交后页面跳日志”的 UI 编排尚未专门做一条端到端脚本 + +### 场景 S3:已销批量撤销 +- 入口:已销调整页列表批量勾选 -> “批量撤销” +- 动作接口:`POST /sold-batch-revoke` +- 入参特点:`chargeIds[]` +- 回看: + - 列表可撤销状态变化 + - 撤销日志存在 +- 后端证据状态:**已真实库覆盖** + +--- + +## 5. 未销调整页 + +### 场景 U1:未销调整(金额/水量)提交 -> 列表/统计回看 +- 入口:未销调整页“调整”弹窗 +- 动作接口:`POST /unsold-adjust-submit` +- 回看: + - `GET /unsold-page` + - `GET /unsold-stat` +- 应确认: + - `totalAmount/billAmount` 更新 + - 统计金额同步变化 +- 后端证据状态:**已真实库覆盖** + +### 场景 U2:未销分账提交 +- 入口:未销调整页“分账”弹窗 +- 动作接口:`POST /unsold-split-submit` +- 回看: + - 提交结果为待审批 + - 如页面有日志联动入口,可再看日志留痕 +- 后端证据状态:**已真实库覆盖(动作)** + +### 场景 U3:未销违约金减免提交 +- 入口:未销调整页“违约金减免”弹窗 +- 动作接口:`POST /unsold-late-fee-reduce-submit` +- 回看:提交结果状态 / 待审批标识 +- 后端证据状态:**已真实库覆盖(动作)** + +### 场景 U4:未销价差调整提交 +- 入口:未销调整页“价差调整”弹窗 +- 动作接口:`POST /unsold-price-diff-submit` +- 回看:提交结果状态 / 待审批标识 +- 后端证据状态:**已真实库覆盖(动作)** + +### 场景 U5:未销呆坏账提交 +- 入口:未销调整页“呆坏账调整”弹窗 +- 动作接口:`POST /unsold-bad-debt-submit` +- 回看:提交结果状态 / 待审批标识 +- 后端证据状态:**已真实库覆盖(动作)** + +### 场景 U6:未销查询页初始态 +- 入口:未销调整页默认查询 +- 动作接口: + - `GET /unsold-page` + - `GET /unsold-stat` +- 应确认: + - 初始未销账单金额与违约金展示正确 + - 五类按钮能力显隐正确 +- 后端证据状态:**已真实库覆盖** + +--- + +## 6. 账务日志页 + +### 场景 L1:日志页默认查询 -> page/stat/detail 联动 +- 入口:账务日志页查询 +- 动作接口: + - `GET /log-page` + - `GET /log-stat` + - `GET /log-detail?adjustmentNo=...` +- 应确认: + - 列表、统计、详情三处状态一致 +- 后端证据状态:**已真实库覆盖** + +### 场景 L2:日志页查看流程 / 附件 +- 入口:日志页“查看流程 / 查看附件” +- 动作接口: + - `GET /log-process?adjustmentNo=...` + - `GET /log-attachments?adjustmentNo=...` +- 应确认: + - process 返回阶段与状态摘要 + - attachments 返回引用及解析状态 +- 后端证据状态:**已真实库覆盖** + +### 场景 L3:日志页转退款 +- 入口:日志页“转退款”弹窗 +- 动作接口:`POST /log-refund` +- 回看: + - 日志状态变化 + - 账单收费状态变化 + - bank follow-up 留痕 +- 后端证据状态:**已真实库覆盖** + +### 场景 L4:日志页转预存 +- 入口:日志页“转预存”弹窗 +- 动作接口:`POST /log-prestorage` +- 回看: + - 日志状态变化 + - 目标账户余额变化 + - 可继续查看 process / attachments +- 后端证据状态:**已真实库覆盖** + +### 场景 L5:日志页撤销 +- 入口:日志页“撤销”按钮 +- 动作接口:`POST /log-revoke` +- 回看: + - 原账单支付状态恢复 + - 目标预存余额回退 + - 日志中出现撤销链记录 +- 后端证据状态:**已真实库覆盖** + +--- + +## 7. 当前对前端/UAT最重要的提醒 +1. 已销批量撤销使用 `chargeIds[]`,不要误用 `adjustmentNo[]`。 +2. 日志页所有二次动作统一以 `adjustmentNo` 作为主键。 +3. 未销页五类弹窗“原因”应动态切字典,不要共用一个原因下拉。 +4. 若页面仍显示旧三态文案(如“处理中”),应以后端当前四态真值为准重新对齐。 + +--- + +## 8. 当前仍未通过本清单覆盖的点 +1. 纯 UI 级交互(禁用态、二次确认提示、按钮联动)尚未自动化验证。 +2. 边角筛选条件还未做完整穷尽。 +3. 某些编码字段是否需要前端额外 label 化(如 `waterType`)仍需页面确认。 diff --git a/docs/evidence/rev004-latefee-batch-contract-formalization-implementation-2026-04-14.md b/docs/evidence/rev004-latefee-batch-contract-formalization-implementation-2026-04-14.md new file mode 100644 index 0000000..f6fc3db --- /dev/null +++ b/docs/evidence/rev004-latefee-batch-contract-formalization-implementation-2026-04-14.md @@ -0,0 +1,156 @@ +# REV004 未销违约金减免批量契约补齐实施记录(2026-04-14) + +## 目标 +补齐 `unsold-late-fee-reduce-batch-submit` 的批量 outer 契约,使其能够承接前端页面公共表单字段,并把字段真实透传到后端执行主链,而不是只停留在 controller VO。 + +## 本次实现 +### 1. 批量 outer 契约补齐 +文件: +- `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/controller/admin/accountingadjust/accountProcess/vo/AccountingAdjustUnsoldLateFeeReduceBatchSubmitReqVO.java` +- `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/controller/admin/accountingadjust/accountProcess/vo/AccountingAdjustUnsoldLateFeeReduceBatchItemReqVO.java` + +新增 outer 字段: +- `lateFeeType` +- `applicant` +- `contactMobile` +- `applyReason` +- `remark` +- `attachmentRefs` + +明细 item 独立为专用 VO: +- `chargeId` +- `adjustmentNo` +- `applyReason`(兼容旧逐项提交) +- `remark`(兼容旧逐项提交) +- `attachmentRefs`(兼容旧逐项提交) +- `lateFeeReduceAmount` +- `startDate` +- `endDate` + +### 2. 共享 batch 主链承接 outer 字段 +文件: +- `.../AccountingAdjustBatchSubmitReqVO.java` +- `.../AccountingAdjustActionController.java` +- `.../AccountingAdjustProcessServiceImpl.java` + +处理方式: +- controller 先把 late-fee batch outer 字段规范化映射到 generic batch DTO +- process 主链在 `executeBatchSubmit` 中统一执行 outer -> item 合并 +- 统一 batch 主链承接字段: + - `reasonCode` + - `reason` + - `applicant` + - `contactMobile` + - `attachmentRefs` + - `lateFeeType` + +这避免了“专用 VO 上有字段,但 generic batch 主链吃不到”的伪完成。 + +### 3. amount / date 模式校验补齐 +文件: +- `.../AccountingAdjustProcessServiceImpl.java` +- `.../AccountingAdjustReqVO.java` +- `.../ChargeServiceImpl.java` + +规则: +- `lateFeeType=1`(按金额) + - 要求 `lateFeeReduceAmount > 0` + - 不允许同时传 `startDate/endDate` +- `lateFeeType=2`(按日期) + - 要求 `startDate/endDate` 同时存在,且 `endDate >= startDate` + - 不允许同时传 `lateFeeReduceAmount` +- 未显式传 `lateFeeType` 时: + - 若存在日期字段,推导为 `2` + - 否则默认 `1` + +### 4. 执行日志落地字段补齐 +文件: +- `.../AccountingAdjustReqVO.java` +- `.../ChargeServiceImpl.java` + +新增写入 operat log detail 的字段: +- `lateFeeType` +- `applicant` +- `contactMobile` +- `startDate` +- `endDate` + +## 验证结果 +### 编译 +命令: +```bash +mvn -pl sw-business/sw-business-server -DskipTests compile +``` +结果:**PASS** + +### 定向单测 +命令: +```bash +mvn -pl sw-business/sw-business-server \ + -Dtest=AccountingAdjustActionControllerTest#batchCreateUnsoldLateFeeReduce_shouldReturnWrappedSuccess,AccountingAdjustProcessServiceImplTest#createUnsoldLateFeeReduce_shouldPassDateModeFieldsToUnifiedChargeService+createUnsoldLateFeeReduce_shouldRejectMixedAmountAndDateMode+batchCreateUnsoldLateFeeReduce_shouldMergeBatchOuterFieldsIntoItems \ + test +``` +结果:**PASS(4 tests, 0 fail, 0 error)** + +### 真实 DB 定向集成 +命令: +```bash +REV004_IT_DB_URL=jdbc:postgresql://192.168.10.130:5436/sw_system \ +REV004_IT_DB_USERNAME=sw_system \ +REV004_IT_DB_PASSWORD='Em@123456' \ +mvn -pl sw-business/sw-business-server \ + -Dtest=Rev004AccountProcessCanaryQueryIntegrationTest#unsoldLateFeeReduceBatchSubmit_shouldAcceptOuterFieldsAndDateModeContract \ + test +``` +结果:**PASS(1 test, 0 fail, 0 error)** + +集成验证确认: +- batch outer 字段可提交 +- `lateFeeType=2` date-mode 契约可进入主链 +- operat log detail 中可看到: + - `lateFeeType=2` + - `applicant` + - `contactMobile` + - `startDate` + - `endDate` + - `attachmentRefs` + +## 当前边界与剩余风险 +1. **本轮重点是“提交契约 + 主链承接 + 日志落地”**。 +2. `lateFeeType=2` 的“按日期执行态/审批回写态”尚未展开为完整金额计算逻辑;当前已确保申请态与提交链路可用。 +3. 由于仓库当前还存在其他在制改动,本轮只做了定向验证,没有把整个相关测试类全部拉绿。 + +## 结论 +本轮已完成 REV004 未销违约金减免批量提交接口的主契约补齐,并把前端页面公共字段正式落到共享 batch 主链与日志记录中;可以支撑前端继续联调“按金额/按日期”两种提交形态。 + +## 追加收口(读模型展示字段) +### 本轮补齐 +- `AccountingAdjustDetailRespVO` 新增: + - `lateFeeType` + - `applicant` + - `contactMobile` + - `startDate` + - `endDate` +- `AccountingAdjustLogDetailRespVO` 新增同名字段 +- `AccountingAdjustProcessRespVO` 新增: + - `reasonCode` + - `attachmentRefs` + - `lateFeeType` + - `applicant` + - `contactMobile` + - `startDate` + - `endDate` + +### 读取链路 +- `AccountingAdjustQueryServiceImpl`:从 operat log detail 读取 late-fee 扩展字段并映射到 detail response +- `AccountingAdjustLogProcessServiceImpl`:从 request/action log detail 读取 late-fee 扩展字段并映射到 log-detail response +- `AccountingAdjustProcessServiceImpl#getProcess`:从 unified snapshot 读取并投影到 process response + +### 追加验证 +#### 定向读模型单测 +```bash +mvn -pl sw-business/sw-business-server \ + -Dtest=AccountingAdjustQueryServiceImplTest,AccountingAdjustLogProcessServiceImplTest,AccountingAdjustProcessServiceImplTest#getProcess_shouldExposeLateFeeDisplayFieldsFromUnifiedSnapshot \ + test +``` +结果:**PASS(7 tests, 0 fail, 0 error)** diff --git a/docs/evidence/rev004-latefee-daily-rule-confirmation-2026-04-15.md b/docs/evidence/rev004-latefee-daily-rule-confirmation-2026-04-15.md new file mode 100644 index 0000000..a22d842 --- /dev/null +++ b/docs/evidence/rev004-latefee-daily-rule-confirmation-2026-04-15.md @@ -0,0 +1,84 @@ +# REV004 违约金规则确认(2026-04-15) + +## 结论 +当前后续实现应以以下业务规则作为真值: + +> **违约金 = 欠缴水费金额 × 0.1‰ × 逾期天数** + +该规则意味着: +- **按日计收**,不是按月滚动 +- 计算基数是**欠缴水费金额** +- **不含违约金本身** +- **不得利滚利** + +## 业务前提 +违约金产生需满足: +1. 逾期未缴 +2. 经供水单位通知后仍未缴 + +## 对 REV004 的实现影响 +### 1. 计算模型 +后续 `late-fee reduce` 的设计与实现,应以“按日计收”模型展开,而不是“按月滚动”模型。 + +### 2. date-mode 减免理解 +`lateFeeType=2`(按日期)应理解为: +- 对指定日期区间内对应的逾期天数区间做减免 +- 先计算该区间理论应收违约金 +- 再形成: + - `lateFeeBefore` + - `reduceAmount` + - `lateFeeAfter` + +### 3. 需要补齐/确认的系统要素 +除现有: +- `lateFeeBeginDate` +- `lateFee` +- `penaltyCoefficient` + +还应确认或补齐: +- 欠缴本金 +- 通知状态 / 通知时间 +- 逾期起算日 +- 逾期天数 +- 上限规则(如部分地区 30%) +- 60 日后续处置边界 + +## 与当前仓库现状的关系 +### 当前已确认存在的模型字段 +- `ChargeDO.lateFeeBeginDate` +- `ChargeDO.lateFee` +- `CostComponentDO.penaltyCoefficient` + +### 当前未确认存在的现成实现 +仓库中尚未确认存在: +- 明确的“按日计收违约金”统一计算器 +- 明确的“按日期区间重算违约金”统一服务 + +因此,当前应将该规则视为: +- **业务规则真值输入** +- 后续需要据此补正式实现 + +## 使用建议 +后续 PRD / 设计稿 / 代码实现,不建议再使用“按月滚动”表述;应统一表述为: + +> **按日计收、按区间减免、按期累计结果** + + +## 追加确认(2026-04-15) +### 系数来源模型 +已进一步确认: +- 逐项重算违约金时,系数来源为 `CostComponentDO.penaltyCoefficient`。 +- 取数链路为:`ChargeDetailDO.costComponentCode -> CostComponentDO.code -> penaltyCoefficient`。 + +### 参与计算范围 +- 所有未缴费用项都参与。 +- 不收违约金的费用项,不做额外白名单过滤,而是通过 `penaltyCoefficient = 0` 自然贡献 0。 + +### 汇总口径 +- 每个费用项先单独计算违约金; +- 每项先四舍五入到分; +- 最后再求和。 + +### 当前仓库状态 +- 已确认存在上述模型链路; +- 但尚未发现现成“逐项按日期重算违约金”的统一实现。 diff --git a/docs/evidence/rev004-latefee-formal-table-deploy-sql-2026-04-15.md b/docs/evidence/rev004-latefee-formal-table-deploy-sql-2026-04-15.md new file mode 100644 index 0000000..9c77473 --- /dev/null +++ b/docs/evidence/rev004-latefee-formal-table-deploy-sql-2026-04-15.md @@ -0,0 +1,27 @@ +# REV004 late-fee formal table 部署 SQL 说明(2026-04-15) + +## 新增脚本 +- `sql/rev004/REV004_latefee_formal_tables_deploy.sql` + +## 作用 +为 late-fee reduce 的新 formal table 提供单独部署脚本,覆盖: +- `biz_latefee_reduce` +- `biz_latefee_reduce_detail` +- 对应 sequence +- 主键 / 唯一索引 / 常用索引 +- 明细表外键 + +## 适用场景 +1. 测试库 / 联调库先补表 +2. 为真实 DB canary 排除“缺表”阻塞 +3. 为后续 formal-table 路线提供独立部署入口 + +## 注意事项 +- 当前脚本按 PostgreSQL / 测试环境风格编写 +- 若正式环境不是 PostgreSQL,需先做方言适配 +- 此脚本只覆盖 late-fee 两张表,不包含坏账 / 核销 / 价差等对象 + +## 建议使用顺序 +1. 先在目标库执行 `REV004_latefee_formal_tables_deploy.sql` +2. 再重跑 late-fee date-mode canary +3. 若通过,再考虑推广 formal-table 到其他对象 diff --git a/docs/evidence/rev004-latefee-legacy-structure-gap-summary-2026-04-14.md b/docs/evidence/rev004-latefee-legacy-structure-gap-summary-2026-04-14.md new file mode 100644 index 0000000..dd195e5 --- /dev/null +++ b/docs/evidence/rev004-latefee-legacy-structure-gap-summary-2026-04-14.md @@ -0,0 +1,122 @@ +# REV004 违约金减免:现结构与老字典差异摘要(2026-04-14) + +## 结论 +当前 REV004 违约金减免结构已经对齐老字典/老表结构的核心骨架: +- `LateFeeType` 仍保持 `1=按金额 / 2=按日期` +- 已承接申请人、联系电话、备注 +- 已承接 `StartDate/EndDate` +- 已承接账单维度 `chargeId(FeeId)` + +但当前更偏**申请契约 + 日志/读模型承接**,并未完整复刻老系统执行态表模型。 + +## 老字典/老表结构证据 +### 主表 `PM_LATEFEE_RECORDS` +来源:`营收数据字典.md:3117-3126` +- `Applicant` +- `Mobile` +- `ApplyType` +- `LateFeeType` +- `State` +- `Remark` +- `TaskId` +- `StepId` +- `FlowRemark` +- `BusinessType` + +### 明细表 `PM_LATEFEE_RECORD_DETAILS` +来源:`营收数据字典.md:3143-3158` +- `CustId/CustCode/CustName/CustAddress` +- `BillMonth` +- `LateFee` +- `ReduceMoney` +- `NewLateFee` +- `StartDate` +- `EndDate` +- `ProcType/ProcPerson/ProcDate/ProcRemark` +- `State` +- `FeeId` + +### 老字典枚举 +来源:`营收数据字典.md:5683-5697` +- `LateFeeReason`: `1=用户协商`, `2=其它` +- `LateFeeType`: `1=按金额`, `2=按日期` + +## 当前结构证据 +### DDL 草案 +来源:`sql/rev004/REV004_accounting_adjustments_ddl.sql:289-403` +- 主表 `biz_latefee_reduce` + - `applicant_name` + - `applicant_mobile` + - `late_fee_type` + - `apply_reason_code` + - `remark` + - `approval_status` + - `reduce_status` +- 明细表 `biz_latefee_reduce_detail` + - `charge_id` + - `cust_id/cust_code/cust_name/cust_address` + - `bill_month` + - `start_date/end_date` + - `late_fee_before` + - `reduce_amount` + - `late_fee_after` + - `proc_type/proc_person/proc_time/proc_remark` + +### 当前批量提交契约 +来源: +- `AccountingAdjustUnsoldLateFeeReduceBatchSubmitReqVO` +- `AccountingAdjustUnsoldLateFeeReduceBatchItemReqVO` + +当前 outer: +- `lateFeeType` +- `applicant` +- `contactMobile` +- `applyReason` +- `remark` +- `attachmentRefs` +- `items` + +当前 item: +- `chargeId` +- `adjustmentNo` +- `lateFeeReduceAmount` +- `startDate` +- `endDate` +- 兼容项:`applyReason/remark/attachmentRefs` + +## 已对齐项 +1. `LateFeeType` 语义已对齐老字典 +2. `Applicant/Mobile` 已有对应字段 +3. `Remark` 已有对应字段 +4. `StartDate/EndDate` 已有对应字段 +5. `FeeId -> chargeId` 已有明确对应 +6. `ReduceMoney -> lateFeeReduceAmount/reduce_amount` 语义已对齐 +7. detail / log-detail / process 已可读出 late-fee 扩展字段 + +## 未完全落地项 +1. **ApplyType / LateFeeReason 字典口径未完全统一** + - 老字典:`1=用户协商, 2=其它` + - 当前前端历史口径曾使用不同原因值 +2. **State 未按老系统单字段口径落地** + - 当前主要使用 `approvalStatus/resultStatus/writeBackStatus` +3. **按日期模式仍偏申请态** + - 还未形成完整审批执行/计算回写 +4. **执行结果落表未完整 runtime 化** + - DDL 草案已具备 `late_fee_before/reduce_amount/late_fee_after` + - 但当前核心还是契约 + 日志承接 +5. **处理过程字段未完整 runtime 落地** + - `ProcType/ProcPerson/ProcDate/ProcRemark` +6. **老流程字段未完整复刻** + - `StepId/FlowRemark/BusinessType` + +## 优先级建议 +### P0 +- 统一 `applyReason` 与 `LateFeeReason` 口径 +- 给出老 `State` 与当前审批/回写状态映射表 + +### P1 +- 补齐 `lateFeeType=2` 按日期模式执行态 +- 审批通过后把结果正式落到 `biz_latefee_reduce / biz_latefee_reduce_detail` + +### P2 +- 再决定是否需要复刻 `StepId/FlowRemark/BusinessType/applicant_id` diff --git a/docs/evidence/rev004-prestorage-bpm-db-apply-2026-04-24.md b/docs/evidence/rev004-prestorage-bpm-db-apply-2026-04-24.md new file mode 100644 index 0000000..c834fc2 --- /dev/null +++ b/docs/evidence/rev004-prestorage-bpm-db-apply-2026-04-24.md @@ -0,0 +1,119 @@ +# REV004 prestorage BPM 字段已应用到 application-dev 测试库(2026-04-24) + +## 目标库 +依据: +- `sw-business/sw-business-server/src/main/resources/application-dev.yaml` + +解析结果: +- Host:`192.168.10.130` +- Port:`5436` +- DB:`sw_system` +- User:`sw_system` + +## 本次目的 +在既有 `biz_prestorage_adjust` / `biz_prestorage_adjust_detail` formal-table 基础上,补齐“保存即提审 / BPM 回写”所需的主表字段与索引。 + +## 实际执行 +执行增量 DDL: +- `ALTER TABLE biz_prestorage_adjust ADD COLUMN IF NOT EXISTS ...` +- 新增索引: + - `idx_biz_prestorage_adjust_process_instance` + - `idx_biz_prestorage_adjust_business_status` + - `idx_biz_prestorage_adjust_business_key` + +同步更新本地脚本: +- `sql/rev004/REV004_prestorage_formal_tables_deploy.sql` + +## 已补字段 +- `business_status` +- `approval_result` +- `approval_comment` +- `execution_status` +- `execution_message` +- `approved_at` +- `rejected_at` +- `cancelled_at` +- `rolled_back_at` +- `process_instance_id` +- `process_definition_key` +- `business_key` +- `current_task_id` +- `current_task_name` + +## 回读校验 +### 字段回读 +已确认上述 14 个字段均已存在于: +- `public.biz_prestorage_adjust` + +### 索引回读 +已确认以下索引存在: +- `idx_biz_prestorage_adjust_process_instance` +- `idx_biz_prestorage_adjust_business_status` +- `idx_biz_prestorage_adjust_business_key` + +## DB smoke +在测试库中开启事务,插入一条带 BPM 字段的 `biz_prestorage_adjust` 记录并回读,随后回滚。 + +验证结果: +- 插入成功 +- `business_status=IN_APPROVAL` +- `approval_status=PROCESSING` +- `execution_status=PENDING` +- `process_instance_id=PI-SMOKE-001` +- `current_task_name=预存调整审批` +- 回滚后 `left_count=0` + +结果:**PASS** + +## 当前结论 +`application-dev` 指向的测试库已经具备 REV004 prestorage BPM 接线所需的数据库字段基础,可以继续进行: +1. 保存即发起 BPM +2. 审批回写业务状态 +3. 审批中改类型重启流程 +4. 审批中撤销终止流程 + +## 当前仍未覆盖 +1. 真实 BPM 流程定义 `prestorage_adjust` 是否已在环境中部署,尚未验证; +2. 真实业务服务 + 真实 BPM 流程实例联调尚未完成; +3. 本次仅完成数据库结构到位与最小 smoke。 + +## 补充复核(2026-04-24 11:05 +08:00) +### 1. 部署脚本幂等重放 +再次执行: +```bash +PGPASSWORD='Em@123456' \ +psql -h 192.168.10.130 -p 5436 -U sw_system -d sw_system \ + -v ON_ERROR_STOP=1 \ + -f sql/rev004/REV004_prestorage_formal_tables_deploy.sql +``` + +结果:**PASS** + +关键现象: +- 已存在 sequence / table / index 均以 `already exists, skipping` 跳过; +- 已存在 BPM 字段均以 `column ... already exists, skipping` 跳过; +- 脚本可重复执行,不会因本次增量字段而破坏既有库结构。 + +### 2. 相关模块回归测试 +执行: +```bash +mvn -pl sw-business/sw-business-server,sw-module-bpm/sw-module-bpm-server -am \ + -Dtest=AccountingAdjustProcessServiceImplTest,PrestorageBpmBridgeServiceTest,PrestorageBpmCallbackServiceTest,BpmPrestorageAdjustStatusListenerTest \ + -Dsurefire.failIfNoSpecifiedTests=false test +``` + +结果:**BUILD SUCCESS** + +测试汇总: +- `AccountingAdjustProcessServiceImplTest`:27 通过 +- `PrestorageBpmBridgeServiceTest`:2 通过 +- `PrestorageBpmCallbackServiceTest`:3 通过 +- `BpmPrestorageAdjustStatusListenerTest`:1 通过 +- 合计:**32/32 通过** + +## 最新结论 +截至 2026-04-24 11:05 +08:00: +1. 测试库 BPM 字段已补齐; +2. 部署脚本可幂等重放; +3. 预存 BPM 相关业务/BPM 单测已通过; +4. 仍待真实 BPM 流程定义与真实流程实例联调验证。 diff --git a/docs/evidence/rev004-prestorage-bpm-deployed-integration-check-2026-04-24.md b/docs/evidence/rev004-prestorage-bpm-deployed-integration-check-2026-04-24.md new file mode 100644 index 0000000..7e8b048 --- /dev/null +++ b/docs/evidence/rev004-prestorage-bpm-deployed-integration-check-2026-04-24.md @@ -0,0 +1,70 @@ +# REV004 prestorage BPM 部署后集成检查(2026-04-24) + +## 环境 +- 主机:`root@192.168.10.130` +- 数据库:`jdbc:postgresql://192.168.10.130:5436/sw_system` +- 参考配置: + - `sw-business/sw-business-server/src/main/resources/application-dev.yaml` + - `sw-gateway/src/main/resources/application-dev.yaml` + +## 检查目标 +验证部署后的 REV004 预存调整 BPM 接口是否可通过网关/业务服务完成真实 HTTP 集成测试。 + +## 运行态探测 +### 容器状态 +- `sw-gateway`: Up +- `sw-module-bpm`: Up +- `sw-business-server`: `Up (health: starting)`,且 `RestartCount=7` + +### 端口 +- 网关:`48080` +- 业务服务映射:`48081 -> container 48090` + +## HTTP 实测 +### 1. 网关健康 +`GET http://127.0.0.1:48080/actuator/health` +- 返回:`{"status":"UP"}` +- 结果:PASS + +### 2. 网关预存接口 +`GET /admin-api/business/accounting-adjust/prestorage-page` +- 未登录:`401 账号未登录` +- 使用 `Authorization: emsoft1` 仍返回 `401` +- 结论:网关侧不支持直接使用 business 服务的 mock token 调测 + +### 3. 业务服务预存接口 +`GET http://127.0.0.1:48081/admin-api/business/accounting-adjust/prestorage-page?pageNo=1&pageSize=1` +- `Authorization: emsoft1, tenant-id:0`:返回 `500 系统异常` +- `Authorization: emsoft1, tenant-id:1`:返回 `404 请求地址不存在` + +## 日志根因 +`docker logs sw-business-server` 明确显示: +- Spring 启动失败点:`Error creating bean with name 'accountingAdjustActionController': Injection of resource dependencies failed` +- 最终失败原因: + - `A component required a bean of type 'cn.com.emsoft.sw.module.bpm.api.task.BpmProcessInstanceApi' that could not be found.` + +## 结论 +当前 **不是接口联调数据问题**,而是 **部署后的 business 服务启动失败 / controller 未成功装配**,因此无法继续完成真实接口集成测试。 + +## 影响判断 +本次预存 BPM 变更不仅修改了 business 模块,也修改了 BPM 相关模块: +- `sw-module-bpm-api/.../BpmProcessInstanceApi.java` +- `sw-module-bpm-api/.../dto/BpmProcessInstanceCancelReqDTO.java` +- `sw-module-bpm-server/.../BpmProcessInstanceApiImpl.java` +- `sw-module-bpm-server/.../RpcConfiguration.java` +- `sw-module-bpm-server/.../BpmPrestorageAdjustStatusListener.java` + +因此若仅更新 `sw-business-server`,或 `sw-module-bpm` 未按同版本一并部署,就会出现当前缺少 `BpmProcessInstanceApi` Bean 的启动失败。 + +## 建议下一步 +1. 同版本重新部署: + - `sw-business-server` + - `sw-module-bpm` +2. 部署后先验证: + - `sw-business-server` 健康状态变为 healthy / restart count 停止增长 + - 业务服务日志不再出现 `BpmProcessInstanceApi` 缺失 +3. 然后再执行预存 BPM 真实 HTTP 集成测试: + - save 即提审 + - page/detail/process 查询 + - revoke + - BPM 审批回写链路 diff --git a/docs/evidence/rev004-prestorage-strict-formal-first-2026-04-17.md b/docs/evidence/rev004-prestorage-strict-formal-first-2026-04-17.md new file mode 100644 index 0000000..440cc4a --- /dev/null +++ b/docs/evidence/rev004-prestorage-strict-formal-first-2026-04-17.md @@ -0,0 +1,94 @@ +# REV004 prestorage process/attachments strict formal-first(2026-04-17) + +## 目标 +收口: +- `GET /admin-api/business/accounting-adjust/prestorage-process` +- `GET /admin-api/business/accounting-adjust/prestorage-attachments` + +使其优先读取 prestorage formal-table,而不是继续主要依赖 legacy/unified fallback。 + +## 代码变更 +核心变更位于: +- `AccountingAdjustProcessServiceImpl` +- `AccountingAdjustProcessServiceImplTest` + +策略: +1. `getProcess(adjustmentNo)` 先尝试 `PrestorageFormalizationService.getView(adjustmentNo)` +2. 命中 formal view 时,直接组装 `AccountingAdjustProcessRespVO` +3. `getAttachments(adjustmentNo)` 先尝试读取 formal `attachmentRefs` +4. 仅在 formal 不存在时才回退到 synthetic / unified 口径 + +## 验证 +### compile +```bash +mvn -pl sw-business/sw-business-server -DskipTests compile +``` +结果:**PASS** + +### targeted tests +```bash +mvn -pl sw-business/sw-business-server -Dtest=AccountingAdjustProcessServiceImplTest,AccountingAdjustPrestorageProcessServiceImplTest test +``` +结果:**PASS** +- Tests run: 23 +- Failures: 0 +- Errors: 0 + +## fresh jar smoke(port 48098) +### health +- `GET /actuator/health` -> `{"status":"UP"}` + +### smoke 数据 +手工 seed: +- `adjustmentNo = REV004-PTR-993001-SEED` +- formal main:`biz_prestorage_adjust` +- formal detail:`biz_prestorage_adjust_detail` +- attachment refs:`101,proof-raw` + +### process +调用: +- `GET /admin-api/business/accounting-adjust/prestorage-process?adjustmentNo=REV004-PTR-993001-SEED` + +结果:**PASS** +返回关键字段: +- `objectType = LEGACY_PRESTORAGE_TRANSFER` +- `adjustType = TRANSFER` +- `actionAmount = 20.0` +- `resultStatus = SUCCESS` +- `approvalStatus = NOT_REQUIRED` +- `writeBackStatus = UPDATED` +- `attachmentRefs = ["101", "proof-raw"]` +- `applicant = 王五` +- `contactMobile = 13700000000` +- `latestMessage = 预存转账成功,目标户号=C-993002` + +说明:process 已命中 formal main,而不是再走 unified detail 兜底。 + +### attachments +调用: +- `GET /admin-api/business/accounting-adjust/prestorage-attachments?adjustmentNo=REV004-PTR-993001-SEED` + +结果:**PASS** +返回: +- ref=`101` -> 数值 ID 解析,当前附件实体不存在,返回 `resolved=false, message=附件不存在` +- ref=`proof-raw` -> 非数值引用,返回 `resolved=false, message=附件引用不是数值ID,保留原始引用` + +说明:attachments 已优先读取 formal `attachmentRefs`,并保留现有解析策略。 + +## 清理 +已删除: +- `biz_prestorage_adjust / detail` seed 数据 + +回读结果: +- `prestorage_main_left=0` +- `prestorage_detail_left=0` + +运行态: +- `48098` 已停止 + +## 当前结论 +本轮 prestorage 剩余查询缺口已完成 strict formal-first 收口: +1. `prestorage-process` 优先命中 formal +2. `prestorage-attachments` 优先命中 formal attachment refs +3. formal 缺失时仍可继续 fallback +4. 主链路 schema / 写路径无变更 diff --git a/docs/evidence/rev004-prestorage-submit-dev-call-2026-04-23.md b/docs/evidence/rev004-prestorage-submit-dev-call-2026-04-23.md new file mode 100644 index 0000000..be02e75 --- /dev/null +++ b/docs/evidence/rev004-prestorage-submit-dev-call-2026-04-23.md @@ -0,0 +1,119 @@ +# REV004 预存调整 prestorage-submit 开发环境调用记录(2026-04-23) + +- 验证日期:2026-04-23 +- 后端仓基线:`21714d64822268d8dcb7cd1296656b4c19ff95fa` +- 前端仓基线:`d2698e1f5d107422f64928086d89d4b803cd12ae` +- 目标环境:`root@192.168.10.130` +- 服务端口:`sw-gateway -> 127.0.0.1:48080`,`sw-business-server -> 127.0.0.1:48090` +- 配置依据: + - `water-backend/sw-gateway/src/main/resources/application-dev.yaml` + - `water-backend/sw-business/sw-business-server/src/main/resources/application-dev.yaml` +- 鉴权方式:开发环境 `sw.security.mock-enable=true`,使用 `Authorization: Bearer emsoft1` + `tenant-id: 1` + +## 1. 实际调用 payload + +```json +{ + "applicant": "李四", + "applyReason": "充值错误", + "attachmentRefs": [], + "contactMobile": "19928382738", + "custCode": "26041011111", + "refundAmount": 2, + "remark": "ssa" +} +``` + +## 2. 实际调用命令 + +### 2.1 直连 business + +```bash +curl -X POST 'http://127.0.0.1:48090/admin-api/business/accounting-adjust/prestorage-submit' \ + -H 'Authorization: Bearer emsoft1' \ + -H 'tenant-id: 1' \ + -H 'Content-Type: application/json' \ + --data '{"applicant":"李四","applyReason":"充值错误","attachmentRefs":[],"contactMobile":"19928382738","custCode":"26041011111","refundAmount":2,"remark":"ssa"}' +``` + +### 2.2 走 gateway + +```bash +curl -X POST 'http://127.0.0.1:48080/admin-api/business/accounting-adjust/prestorage-submit' \ + -H 'Authorization: Bearer emsoft1' \ + -H 'tenant-id: 1' \ + -H 'Content-Type: application/json' \ + --data '{"applicant":"李四","applyReason":"充值错误","attachmentRefs":[],"contactMobile":"19928382738","custCode":"26041011111","refundAmount":2,"remark":"ssa"}' +``` + +## 3. 调用结果 + +直连 business 与走 gateway 返回一致: + +```json +{"code":500,"data":null,"msg":"系统异常"} +``` + +## 4. 后端日志根因 + +`sw-business-server` 日志显示真实异常为: + +```text +java.lang.IllegalArgumentException: 账户预存余额不足 +at cn.com.emsoft.sw.business.service.accountingadjust.accountProcess.AccountingAdjustProcessServiceImpl.createPrestorageAction(AccountingAdjustProcessServiceImpl.java:213) +``` + +对应代码位置: + +- `water-backend/sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/accountingadjust/accountProcess/AccountingAdjustProcessServiceImpl.java` + +关键逻辑: + +- 读取客户与账户 +- 将 `deposit == null` 视为 `0` +- 校验 `deposit < refundAmount` 时抛出 `IllegalArgumentException("账户预存余额不足")` + +## 5. 数据库核验 + +开发库(`sw_system`)中当前客户与账户数据: + +```sql +select id, code, name, population, address, status, tenant_id +from biz_cust +where deleted = 0 and code = '26041011111'; + +select id, cust_id, deposit, uncheck_money, overdraft, status, deleted, tenant_id +from biz_account +where cust_id = 67 and deleted = 0; +``` + +结果: + +```text +biz_cust: +67 | 26041011111 | liao | 3 | 11 | 0 | 1 + +biz_account: +65 | 67 | null | null | 0.0000 | 0 | 0 | 1 +``` + +结论:当前测试客户 `26041011111` 的 `biz_account.deposit` 为空,业务侧按 `0` 处理,因此退款金额 `2` 会命中“账户预存余额不足”。 + +## 6. 结论 + +本次 `prestorage-submit` 调用链路本身可达,鉴权可用,接口也已命中后端业务逻辑。 +失败原因不是网关/路由/权限,而是当前测试数据不满足退款校验条件: + +- 客户存在 +- 账户存在 +- 账户预存余额为空(按 0 处理) +- `refundAmount = 2`,因此提交失败 + +## 7. 后续建议 + +二选一: + +1. 修正该测试客户的 `biz_account.deposit`,使其大于等于 `2` +2. 改用当前开发库中预存余额充足的客户进行 `prestorage-submit` 联调 + +附带观察:当前前端收到的是泛化后的 `系统异常`,而非后端真实业务提示 `账户预存余额不足`。如果需要提升联调效率,可后续评估是否将该类参数/业务校验异常以明确业务消息返回前端。 diff --git a/docs/evidence/rev004-price-diff-formal-table-dev-db-apply-2026-04-17.md b/docs/evidence/rev004-price-diff-formal-table-dev-db-apply-2026-04-17.md new file mode 100644 index 0000000..783a5ef --- /dev/null +++ b/docs/evidence/rev004-price-diff-formal-table-dev-db-apply-2026-04-17.md @@ -0,0 +1,135 @@ +# REV004 price-diff formal-table 已应用到 application-dev 测试库(2026-04-17) + +## 目标库 +依据: +- `sw-business/sw-business-server/src/main/resources/application-dev.yaml` + +解析结果: +- Host:`192.168.10.130` +- Port:`5436` +- DB:`sw_system` +- User:`sw_system` + +## 已执行脚本 +- `sql/rev004/REV004_price_diff_formal_tables_deploy.sql` + +## DDL 执行结果 +结果:**PASS** + +已确认存在: +- `biz_price_diff_adjust` +- `biz_price_diff_adjust_detail` +- `biz_price_diff_adjust_seq` +- `biz_price_diff_adjust_detail_seq` + +已确认幂等重放通过: +- second apply 输出 `already exists, skipping` +- 无重复约束/重复索引异常 + +## 代码验证 +### compile +```bash +mvn -pl sw-business/sw-business-server -DskipTests compile +``` +结果:**PASS** + +说明: +- 先顺手修复 develop 基线 compile blocker: + - `CustApiImpl.java` 去掉 `updateCustPriceTemplateCode` 上多余 `@Override` + - `CustServiceImpl.java` 去掉 `dept.getCode()` 调用 +- 之后串行重跑 compile 成功 +- 曾出现一次 `NoSuchFileException ... target/generated-sources/...`,确认为 compile 与 test 并行访问同一 `target/` 目录导致的竞态,不属于业务代码错误 + +### targeted tests +```bash +mvn -pl sw-business/sw-business-server -Dtest=AccountingAdjustActionServiceImplTest,AccountingAdjustProcessServiceImplTest,AccountingAdjustQueryServiceImplTest,PriceDiffFormalizationServiceTest test +``` +结果:**PASS** +- Tests run: 43 +- Failures: 0 +- Errors: 0 + +## HTTP smoke(fresh jar, port 48095) +### health +- `GET /actuator/health` -> `{"status":"UP"}` + +### submit +- `unsold-price-diff-submit` on charge `992205` -> `REV004-992205-20260417153413` +- `unsold-price-diff-submit` on charge `992206` -> `REV004-992206-20260417153414` +- 返回统一为: + - `objectType = PRICE_DIFF_ADJUST` + - `approvalStatus = PENDING_APPROVAL` + - `resultStatus = PENDING_APPROVAL` + - `writeBackStatus = PENDING` + +### get / page +- `GET /admin-api/business/accounting-adjust/get?adjustmentNo=REV004-992205-20260417153413` + - 可返回 formal detail: + - `priceDiffAmount=15.50` + - `billAmountBefore=100.00` + - `billAmountAfter=115.50` + - `extendedAmountBefore=100.00` + - `extendedAmountAfter=115.50` +- `GET /admin-api/business/accounting-adjust/page?...objectType=PRICE_DIFF_ADJUST` + - 可返回 2 条 fresh smoke formal-first 记录 + - `reasonCodeLabel` 已能解析为字典文本(如 `用户协商` / `定价错误`) + +### approve / reject +- approve:`/admin-api/business/accounting-adjust/approve` + - 返回 `APPROVED / SUCCESS / UPDATED` +- reject:`/admin-api/business/accounting-adjust/reject` + - 返回 `REJECTED / FAIL / SKIPPED` + +## DB 回读 +`biz_price_diff_adjust`: +- `REV004-992205-20260417153413 -> APPROVED | UPDATED | SUCCESS` +- `REV004-992206-20260417153414 -> REJECTED | SKIPPED | FAIL` + +`biz_price_diff_adjust_detail`: +- approve 明细:`SUCCESS | APPROVE_EXECUTE | approve price diff smoke` +- reject 明细:`REJECTED | REJECT | reject price diff smoke` +- approve 样例金额前后值: + - `bill_amount_before=100.00 -> bill_amount_after=115.50` + - `extended_amount_before=100.00 -> extended_amount_after=115.50` + +`biz_charge`: +- `992205 -> bill_amount=115.5000, extended_amount=115.5000, original_money=115.5000` +- `992206 -> bill_amount=80.0000, extended_amount=80.0000, original_money=80.0000` + +## 测试数据清理 +smoke 完成后已删除: +- `biz_operat_log / detail`(identify_value = `992205`, `992206`) +- `biz_price_diff_adjust / detail` +- `biz_charge`(`992205`, `992206`) +- `biz_cust`(`REV004_PD_SMOKE_A`, `REV004_PD_SMOKE_B`) + +回读结果: +- `pricediff_main_left=0` +- `pricediff_detail_left=0` +- `operat_log_left=0` +- `charge_left=0` +- `cust_left=0` + +## 运行态清理 +用于 fresh-jar HTTP smoke 的临时实例: +- port `48095` + +已停止,当前无残留 LISTEN 进程。 + +## 当前结论 +本轮 price-diff formal-table 已达到: +1. DDL 可落库且可幂等重放; +2. unsold price-diff submit 能落 pending formal main/detail; +3. approve/reject 会同步 formal 状态; +4. detail/page 对 `PRICE_DIFF_ADJUST` 已能返回 formal-first 结果; +5. 账单回写与当前 `applyPriceDiffWriteBack(...)` 口径一致; +6. fresh smoke / cleanup 证据完整。 + +## 当前仍需收口 +- 这批尚未提交 commit / PR; +- 如果后续要更贴近原系统 `PM_PRICE_RECORDS / DETAILS` 语义,可继续补: + - `PriceListId` + - `PriceCode` + - `IsLadder` + - `NewFeeId` + - 更细颗粒的 old/new late fee 与账单差额字段来源收口。 diff --git a/docs/evidence/rev004-redink-formal-table-dev-db-apply-2026-04-17.md b/docs/evidence/rev004-redink-formal-table-dev-db-apply-2026-04-17.md new file mode 100644 index 0000000..bd4737c --- /dev/null +++ b/docs/evidence/rev004-redink-formal-table-dev-db-apply-2026-04-17.md @@ -0,0 +1,212 @@ +# REV004 redink formal-table 已应用到 application-dev 测试库(2026-04-17) + +## 目标库 +依据: +- `sw-business/sw-business-server/src/main/resources/application-dev.yaml` + +解析结果: +- Host:`192.168.10.130` +- Port:`5436` +- DB:`sw_system` +- User:`sw_system` + +## 已执行脚本 +- `sql/rev004/REV004_redink_formal_tables_deploy.sql` + +## DDL 执行结果 +结果:**PASS** + +已确认存在: +- `biz_redink_record` +- `biz_redink_record_detail` +- `biz_redink_record_seq` +- `biz_redink_record_detail_seq` + +已确认幂等重放通过: +- second apply 输出 existing/skip 结果 +- 本轮顺手把 sequence 创建方式从 `CREATE SEQUENCE IF NOT EXISTS` 收口为显式 `pg_class` 检查,规避 replay 时 sequence 级重复键异常 + +## 代码验证 +### compile +```bash +mvn -pl sw-business/sw-business-server -DskipTests compile +``` +结果:**PASS** + +### targeted tests +```bash +mvn -pl sw-business/sw-business-server -Dtest=ChargeServiceAccountingAdjustTest,AccountingAdjustQueryServiceImplTest,RedinkFormalizationServiceTest test +``` +结果:**PASS** +- Tests run: 34 +- Failures: 0 +- Errors: 0 + +## fresh query smoke(fresh jar, port 48096) +### health +- `GET /actuator/health` -> `{"status":"UP"}` + +### smoke 方式说明 +REDINK_RECORD 当前依赖原交易定位 / bank follow-up 逻辑;本轮先采用: +1. application-dev 手工 seed 一条 redink formal 主表/明细表记录; +2. fresh jar 验证 `get/page` 是否已 formal-first 命中; +3. 不在本轮直接伪造 live bank follow-up 交易。 + +### seed 记录 +- `adjustmentNo = REV004-RI-992305-SEED` +- `chargeId = 992305` +- formal 主表状态: + - `approvalStatus = NOT_REQUIRED` + - `executeStatus = UPDATED` + - `resultStatus = SUCCESS` +- bank follow-up 样例:`BK-992305` + +### get / page +- `GET /admin-api/business/accounting-adjust/get?adjustmentNo=REV004-RI-992305-SEED` + - 返回: + - `objectType = REDINK_RECORD` + - `resultStatus = SUCCESS` + - `approvalStatus = NOT_REQUIRED` + - `writeBackStatus = UPDATED` + - `originalTranSeq = T-992305` + - `originalSysTranSeq = SYS-992305` + - detail 中包含: + - `bankTranSeq` + - `redinkAmount` + - `payStateBefore` + - `payStateAfter` +- `GET /admin-api/business/accounting-adjust/page?...objectType=REDINK_RECORD` + - 返回 1 条 formal-first 记录 + - `reasonCodeLabel` 已能解析为字典文本(如 `收费错误`) + +## 当前结论 +本轮 redink formal-table 已达到: +1. DDL 可落库且可幂等重放; +2. 结果型 formal-table 主从表已建模; +3. query detail/page 对 `REDINK_RECORD` 已能返回 formal-first; +4. compile / targeted tests 通过; +5. fresh jar query smoke 已验证 formal-first 查询链路可用。 + +## 当前仍未完全覆盖 +- 尚未完成“真实 redink 执行成功后自动落 formal 结果”的 live HTTP smoke; +- 原因是当前红冲依赖原交易与 bank follow-up 外部链路,需要在下一轮补真实可复现数据后再做完整 execute smoke。 + +## 测试数据清理 +已删除: +- `biz_redink_record / detail` +- `biz_charge` +- `biz_cust` + +回读结果: +- `redink_main_left=0` +- `redink_detail_left=0` +- `charge_left=0` +- `cust_left=0` + +## 运行态清理 +用于 fresh-jar query smoke 的临时实例: +- port `48096` + +已停止,当前无残留 LISTEN 进程。 + +## live execute smoke 阻塞证据(2026-04-17 16:50 +08:00) +本轮额外尝试了真实 redink 执行链路: +- fresh jar:`48097` +- 请求:`POST /admin-api/business/charge/accounting-adjust` +- `objectType = REDINK_RECORD` +- `chargeId = 992306` +- `originalTranSeq = T-REDINK-NOPE` + +接口响应: +```json +{"code":500,"data":null,"msg":"系统异常"} +``` + +从应用日志定位到的精确异常: +- `FeignException$ServiceUnavailable: [503]` +- `Load balancer does not contain an instance for the service business-bank-server` +- 失败位置:`TransactionApi#getTransactionByTranSeq(...)` + +结论: +- 当前 live execute smoke 未能完成的直接原因是 **外部依赖 `business-bank-server` 在当前环境无可用实例**; +- 这不是 redink formal-table 本身的 DDL / 查询接线问题。 + +因此本批已完成: +- compile +- targeted tests +- DDL apply / replay +- fresh query smoke + +但“真实 redink 执行成功后自动落 formal”的端到端 smoke 仍需在 `business-bank-server` 可用时补跑。 + + +## live execute smoke 成功补证(2026-04-17 17:51 +08:00) +在本轮额外启动本地 `business-bank-server`(dev profile, port `48092`)并向测试库预置原交易后,重新完成了 REDINK_RECORD 的真实端到端执行 smoke。 + +### 前置 +- 本地启动 `business-bank-server`,并成功注册到 Nacos dev:`business-bank-server 192.168.9.109:48092 register finished` +- 向 `bk_transaction` seed 原交易: + - `tran_seq = T-REDINK-NOPE` + - `sys_tran_seq = SYS-REDINK-NOPE` + - `tran_type = PAY` + - `status = SUCCESS` + - `contract_no = 992306` + - `amount = 12000` +- 准备已收费营业账:`chargeId = 992306` + +### execute 请求 +`POST /admin-api/business/charge/accounting-adjust` +```json +{ + "chargeId": 992306, + "objectType": "REDINK_RECORD", + "reasonCode": "1", + "reason": "redink execute smoke", + "originalTranSeq": "T-REDINK-NOPE" +} +``` + +### execute 响应 +结果:**PASS** + +返回: +- `adjustmentNo = REV004-992306-20260417175109` +- `objectType = REDINK_RECORD` +- `resultStatus = SUCCESS` +- `approvalStatus = NOT_REQUIRED` +- `writeBackStatus = UPDATED` +- `message = 冲正处理成功,bank流水号=RV9923062026041717511073C805` + +### DB 回读 +`biz_redink_record`: +- `adjustment_no = REV004-992306-20260417175109` +- `approval_status = NOT_REQUIRED` +- `execute_status = UPDATED` +- `result_status = SUCCESS` +- `bank_tran_seq = RV9923062026041717511073C805` +- `original_tran_seq = T-REDINK-NOPE` + +`biz_redink_record_detail`: +- `detail_status = SUCCESS` +- `proc_type = EXECUTE` +- `pay_state_before = 1` +- `pay_state_after = 0` +- `redink_amount = 120.00` + +`biz_charge`: +- `pay_state = 0` +- 支付信息已清空为红冲后的未收费态 + +`bk_transaction`: +- 原交易 `T-REDINK-NOPE` 状态变为 `REVERSED` +- 新增 follow-up: + - `tran_seq = RV9923062026041717511073C805` + - `tran_type = PAY_INVALID` + - `status = SUCCESS` + - `original_tran_seq = T-REDINK-NOPE` + +### 查询回读 +- `GET /admin-api/business/accounting-adjust/get?adjustmentNo=REV004-992306-20260417175109` -> formal-first 返回成功 +- `GET /admin-api/business/accounting-adjust/page?...objectType=REDINK_RECORD` -> formal-first 返回成功 + +说明:至此,REDINK_RECORD 已补齐“真实执行 -> 自动落 formal -> formal-first 查询”的完整闭环证据。 diff --git a/docs/evidence/rev004-split-adjust-current-state-comparison-summary-2026-04-14.md b/docs/evidence/rev004-split-adjust-current-state-comparison-summary-2026-04-14.md new file mode 100644 index 0000000..f88d959 --- /dev/null +++ b/docs/evidence/rev004-split-adjust-current-state-comparison-summary-2026-04-14.md @@ -0,0 +1,224 @@ +# REV004 / 分账现状对照摘要(2026-04-14) + +## 1. 目的 +本摘要用于把 **旧设计**、**老数据字典**、**当前代码状态** 三者放在一起,对“分账(SplitAdjust)”给出一页式结论,回答: +- 旧系统/旧设计里的分账到底是什么 +- 当前 REV004 设计里把它定位成什么 +- 当前后端代码做到哪一步了 +- 三者之间的差距到底在哪里 + +--- + +## 2. 一句话结论 +> **旧系统里的分账是“真正的账单拆分/重分摊业务对象”,有汇总表、明细表和拆分后的结果对象;当前 REV004 设计也把它定义为独立正式对象,但当前后端代码只落到了“分账申请态”,还没有落到“审批通过后真正拆账生成子账单”的执行态。** + +--- + +## 3. 旧设计口径(12_REV_Detailed.md) +来源: +- `../water-docs/docs/design/02_Detailed_Design/12_REV_Detailed.md` + +### 旧设计里怎么定义分账 +文档明确把: +- `SplitAdjust` +列为: +- **目标正式业务对象** +- 并且属于: + - **L2 独立业务层** + +### 旧设计里的关键语义 +文档明确写到: +- `SplitAdjust | 分账调整 | 当前作为费用组成重分摊场景表达 | 支持按水量 / 按费用组成等分摊策略` + +并在迁移补充里写到: +- **分账调整汇总 / 明细** +- 承接方式: + - 在线主模型 + 费用重分摊场景 +- 需要保留: + - 原分摊结果 + - 调整后结果 + - 策略类型 + - 责任链 + +### 从旧设计可得的结论 +旧设计的分账不是普通字段修改,也不是一条审批备注,而是: +- 有独立业务语义 +- 有汇总/明细层 +- 有原结果与新结果的前后对照 +- 有策略与责任链 + +--- + +## 4. 老数据字典口径(营收数据字典.md) +来源: +- `../water-docs/docs/design/04_Appendix/Archive/05_Data_Dictionary/营收数据字典.md` + +### 老系统真实表结构 +#### 4.1 汇总表 +- `PM_SEPARATE_RECORDS` + +关键字段: +- `Applicant` +- `Mobile` +- `ApplyType` +- `SeparateType` +- `State` +- `Remark` +- `TaskId` +- `StepId` +- `FlowRemark` +- `BusinessType` + +#### 4.2 明细表 +- `PM_SEPARATE_RECORD_DETAILS` + +关键字段: +- `SeparateRecordId` +- `CustId / CustCode / CustName / CustAddress` +- `FeeId` +- `NewFeeId` +- `BillMonth` +- `BillWater / NewBillWater` +- `LateFee / NewLateFee` +- `BillAmount / NewBillAmount` +- `ExtendedAmount / NewExtendedAmount` +- `ItemStr` +- `ProcType / ProcPerson / ProcDate / ProcRemark` +- `State` + +### 从老数据字典可得的结论 +老系统里的分账已经明显是**执行态对象**,因为它不只是有申请信息,还已经有: +- 原费用对象:`FeeId` +- 新产生对象:`NewFeeId` +- 原水量 / 分账水量 +- 原金额 / 新金额 +- 原滞纳金 / 新滞纳金 +- 费用组成 + +也就是说,老系统分账不是“申请单”,而是: +> **有申请、有明细、有拆后结果对象的完整业务表族。** + +--- + +## 5. 当前代码状态 +来源: +- `sw-business/sw-business-server/src/main/java/.../AccountingAdjustProcessServiceImpl.java` +- `sw-business/sw-business-server/src/main/java/.../ChargeServiceImpl.java` + +### 当前入口 +当前分账提交入口: +- `POST /admin-api/business/accounting-adjust/unsold-split-submit` + +调用路径: +- `AccountingAdjustProcessServiceImpl.createUnsoldSplit(...)` +- 转成统一账务调整: + - `objectType = SPLIT_ADJUST` + - `adjustType = SPLIT` +- 进入 `chargeService.adjustAccounting(...)` + +### 当前真正处理逻辑 +在 `ChargeServiceImpl.handleSplitAdjust(...)` 中,只做了: +- `approvalRequired = true` +- `resultStatus = PENDING_APPROVAL` +- `approvalStatus = PENDING_APPROVAL` +- `writeBackStatus = PENDING` +- `message = 账单拆分申请已提交,待审批` +- `needUpdate = false` + +### 当前校验逻辑 +只校验: +- 原账单必须未收费 +- `splitCount >= 2` +- `splitCount <= 12` +- 必须填写原因编码 +- 必须填写调整原因 + +### 从当前代码可得的结论 +当前代码实现的是: +- **分账申请态** + +没有做到: +- 生成分账主表/明细表 +- 生成目标账单 +- 回填新账单 ID +- 原账单失效/已拆分标记 +- 执行态回看 + +--- + +## 6. 三者对照 + +| 对照维度 | 旧设计 | 老数据字典 | 当前代码 | +|---|---|---|---| +| 是否独立对象 | 是,`SplitAdjust` | 是,汇总表+明细表 | 语义上是,代码上仍挂统一入口 | +| 是否支持按水量 / 按费用组成 | 是 | 是(`SeparateType`) | 申请态支持对象类型,但未落执行规则 | +| 是否有汇总表 | 有方向 | 有真实表 | 当前没有真实表落地 | +| 是否有明细表 | 有方向 | 有真实表 | 当前没有真实表落地 | +| 是否有“原对象 -> 新对象”关系 | 有方向 | 有(`FeeId -> NewFeeId`) | 没有 | +| 是否有拆前拆后水量/金额 | 有方向 | 有 | 没有执行态数据 | +| 是否只到审批申请 | 否 | 否 | 是 | +| 是否真正生成子账单 | 应该要 | 已有新费用ID表达 | 当前未实现 | + +--- + +## 7. 当前最大的差距 +最核心的差距不是“少几个字段”,而是: + +### 旧系统 / 旧设计的分账 +是: +- **完整业务对象** +- **有结果承接** +- **有拆后对象** + +### 当前 REV004 代码的分账 +只是: +- **申请态入口** +- **待审批状态表达** +- **统一日志/查询承接** + +所以差距本质上是: +> **从“受理态”到“执行态”的整层能力还没有落。** + +--- + +## 8. 为什么会停在这里 +结合三者判断,原因可以归纳成: +1. 当前这轮 REV004 后端优先目标是统一入口、状态、日志、查询,不是重建所有独立执行对象。 +2. 真正分账执行涉及: + - 分摊规则 + - 主表/明细表 + - 目标账单生成 + - 守恒校验 + - 收费/开票承接 +3. 因此当前才先落成: + - `SPLIT_ADJUST` 申请态 + +而没有一步到位落成: +- `SplitAdjust` 执行态。 + +--- + +## 9. 最终判断 +### 业务真值 +分账本质上是: +- **账单拆分 / 重分摊** +- 不是退款 +- 不是减免 +- 也不只是审批单 + +### 当前状态 +当前 REV004: +- 已经把“分账”纳入正式对象语义 +- 但后端实现仍停在申请态 + +### 最适合对外讲的一句话 +> 旧系统与旧设计里的分账本来就是“有汇总、有明细、有拆后结果对象”的正式业务对象;当前 REV004 后端只先落了统一申请与审批承接,没有把真正的拆账执行态一起落出来,所以它现在更像“分账申请”,还不是“已执行的分账对象”。 + +--- + +## 10. 建议下一步 +若要继续推进分账执行态,建议: +1. 以老表结构 + 旧设计为真值来源,先冻结最小执行态模型 +2. 先做按水量分账 MVP +3. 再扩按费用组成分账 +4. 最后补前端执行态页面与回看能力 diff --git a/docs/evidence/rev004-split-adjust-execution-design-draft-2026-04-14.md b/docs/evidence/rev004-split-adjust-execution-design-draft-2026-04-14.md new file mode 100644 index 0000000..f051bc8 --- /dev/null +++ b/docs/evidence/rev004-split-adjust-execution-design-draft-2026-04-14.md @@ -0,0 +1,246 @@ +# REV004 / 分账执行态后端设计草案(2026-04-14) + +## 1. 目标 +在当前“分账申请态”基础上,补齐“审批通过后真正执行拆账”的后端能力,使系统可以: +- 将一笔原始未收费账单拆成多笔目标账单 +- 支持两类分账策略: + - 按水量分账 + - 按费用组成分账 +- 保证金额/水量守恒、状态可追踪、失败可回滚 +- 被后续收费、开票、日志、查询完整承接 + +--- + +## 2. 当前现状 +### 已有 +- 统一对象类型:`SPLIT_ADJUST` +- 统一提交入口:`unsold-split-submit` +- 当前结果:`PENDING_APPROVAL` +- 已有原因、审批、日志、查询承接能力 + +### 缺失 +- 审批通过后的执行器 +- 分账主表/明细表落库 +- 目标账单生成 +- 原账单与目标账单关系链 +- 按水量/按费用组成的规则明细 +- 执行态查询回看 + +--- + +## 3. 设计原则 +1. **先审批、后执行**:分账执行必须挂在审批通过之后,不在申请提交时直接拆账。 +2. **守恒原则**: + - 按水量分账:子账单水量之和 = 原账单水量 + - 按费用组成分账:子账单金额之和 = 原账单金额 +3. **原账单可追溯**:必须保留原账单与所有目标账单关系。 +4. **执行幂等**:同一分账单不得重复执行。 +5. **失败可回滚**:执行中任一步失败,必须整体回滚。 +6. **查询可回看**:日志页/分账页必须能看到申请、审批、执行结果、目标账单明细。 + +--- + +## 4. 结果对象建议 +### 最终业务真值 +建议以: +- **多张目标子账单** +为正式业务真值; +- “多笔待收费结果”只是前台呈现视角。 + +### 原因 +- 文档原始语义就是“由一笔账单分成两笔独立账单信息” +- 后续还要支持分别收费、分别开票、分别承担 +- 若没有目标账单实体,后续业务承接会变得模糊 + +--- + +## 5. 数据模型草案 + +### 5.1 主表:`biz_split_adjust` +建议字段: +- `id` +- `split_adjust_no`:分账单号 +- `source_charge_id`:原账单 ID +- `split_rule_type`:分账规则类型(`BY_WATER` / `BY_COMPONENT`) +- `reason_code` +- `remark` +- `approval_status` +- `execute_status`:`PENDING / PROCESSING / SUCCESS / FAIL` +- `execute_message` +- `executed_at` +- `accounting_case_id`(如继续挂统一骨架) +- 通用审计字段 + +### 5.2 明细表:`biz_split_adjust_detail` +建议字段: +- `id` +- `split_adjust_id` +- `seq_no` +- `target_cust_id` / `target_cust_code`(若允许拆给不同客户) +- `target_charge_id`(执行后生成) +- `split_basis`:拆分依据描述 +- `split_ratio` +- `split_water` +- `split_amount` +- `component_code`(按费用组成分账时必填) +- `component_amount` +- `status` +- 通用审计字段 + +### 5.3 与现有 `biz_charge` 的关系 +- 原账单:`source_charge_id` +- 子账单:执行后在 `biz_charge` 新增多条记录 +- 子账单增加来源追踪字段(建议二选一): + - `source_charge_id` + - 或 `split_adjust_id` + +建议至少有一个能让后续查询直接知道: +> 这张账单是由哪次分账生成的。 + +--- + +## 6. 执行流程草案 + +### Phase 1:申请提交 +接口:`POST /unsold-split-submit` +- 校验未收费、`splitCount`、原因等 +- 保存申请主单/明细草稿(如果前端已能提供拆分明细) +- 状态置为 `PENDING_APPROVAL` + +> 若当前前端还没有真正提交拆分明细,只是申请头信息,则需在后续补充“审批前明细维护”或“审批时确认明细”能力。 + +### Phase 2:审批通过 +触发点:审批系统回调/人工审批通过 +- 校验该分账单仍未执行 +- 锁定原账单 +- 进入执行事务 + +### Phase 3:执行拆账 +#### 3.1 按水量分账 +- 读取原账单水量、单价/模板/费用构成 +- 根据各子项 `split_water` 重新计算子账单金额 +- 生成 N 条目标账单 +- 校验: + - `sum(split_water) = source.bill_water` + - `sum(target.bill_amount)` 与可接受计算误差范围内守恒 + +#### 3.2 按费用组成分账 +- 读取原账单明细 `biz_charge_detail` +- 按费用项将金额拆入不同子账单 +- 汇总生成 N 条目标账单及其明细 +- 校验: + - 各费用项总额守恒 + - 原账单总金额守恒 + +### Phase 4:收尾 +- 原账单标记为: + - 已拆分 / 已失效 / 不可继续收费(建议新增状态或来源标识) +- 分账主单置为 `SUCCESS` +- 明细回填 `target_charge_id` +- 写 operat_log / 业务日志 / 查询投影 + +### Phase 5:失败回滚 +- 任一步失败:回滚事务 +- 主单置 `FAIL` +- 保留失败原因 +- 不产生部分子账单残留 + +--- + +## 7. 接口草案 + +### 7.1 申请接口(保留) +- `POST /unsold-split-submit` + +### 7.2 明细查询接口(建议新增) +- `GET /split-adjust-detail?adjustmentNo=...` +- 返回: + - 原账单摘要 + - 分账规则类型 + - 子项明细 + - 目标账单(若已执行) + +### 7.3 明细维护接口(建议新增,若前端需要) +- `POST /split-adjust-draft-save` +- 在审批前保存: + - 按水量拆分行 + - 按费用组成拆分行 + +### 7.4 审批通过执行接口(内部) +- `POST /internal/split-adjust/execute` +- 由审批流或业务服务调用,不暴露给前端 + +### 7.5 查询接口增强(建议) +- `log-detail` / `log-process` + - 增加目标账单摘要 +- `unsold-page` + - 原账单若已提交分账且审批中/已执行,要有能力位变化提示 + +--- + +## 8. 状态机建议 +### 申请主单状态 +- `PENDING_APPROVAL` +- `APPROVED_PENDING_EXECUTE` +- `PROCESSING` +- `SUCCESS` +- `FAIL` +- `REVOKED`(如审批前允许撤销) + +### 原账单状态建议 +原账单不建议简单删除,建议新增可追溯状态: +- `UNPAID` +- `SPLIT_PENDING` +- `SPLIT_DONE` +- `SPLIT_INVALIDATED` + +若不想改老字段,可用额外来源标记字段表达。 + +--- + +## 9. 与前端配合建议 +### 前端最少补充信息 +若要真正进入执行态,前端弹窗至少要能提交: +- 分账方式:按水量 / 按费用组成 +- 子项列表 + - 按水量:每笔 `split_water` + - 按费用组成:每笔目标费用项/金额归属 +- 申请人 / 联系电话 / 原因 / 备注 + +### 前端结果回看 +执行态落地后,前端需要新增: +- 原账单 -> 目标账单列表 +- 子账单金额/水量/费用项明细 +- 分账执行结果与失败原因 + +--- + +## 10. 分阶段落地建议 +### 第一阶段(最小闭环) +目标:审批通过后真正生成子账单,但先只支持**按水量分账** +- 优先原因: + - 规则相对更直观 + - 守恒约束明确 + - 更容易先做闭环 + +### 第二阶段 +扩展支持:**按费用组成分账** +- 需要补足 `biz_charge_detail` 级别重分摊能力 + +### 第三阶段 +增强: +- 目标客户维度分账 +- 更复杂的规则模板 +- 开票/收费联动优化查询 + +--- + +## 11. 最终建议 +若项目要正式推进“分账执行态”,建议: +1. 先把 **按水量分账** 做成最小执行闭环 +2. 明确 `biz_split_adjust` / `biz_split_adjust_detail` 模型 +3. 把审批通过后的执行器作为独立服务落地 +4. 再补按费用组成的二阶段能力 + +一句话: +> 不建议继续在当前“统一 adjustAccounting 申请态”里硬塞执行逻辑,而应把 `SplitAdjust` 升级成独立正式业务对象来建设。 diff --git a/docs/evidence/rev004-split-adjust-formalization-implementation-2026-04-14.md b/docs/evidence/rev004-split-adjust-formalization-implementation-2026-04-14.md new file mode 100644 index 0000000..d88f948 --- /dev/null +++ b/docs/evidence/rev004-split-adjust-formalization-implementation-2026-04-14.md @@ -0,0 +1,108 @@ +# REV004 / split-adjust formalization + dedicated interfaces evidence(2026-04-14) + +## 1. 本轮实现范围 +- 保持旧接口兼容: + - `POST /admin-api/business/accounting-adjust/unsold-split-submit` + - `GET /admin-api/business/accounting-adjust/get` + - `GET /admin-api/business/accounting-adjust/log-detail` +- 新增 split 专属接口族: + - `POST /admin-api/business/split-adjust/submit` + - `GET /admin-api/business/split-adjust/page` + - `GET /admin-api/business/split-adjust/detail` + - `GET /admin-api/business/split-adjust/result` +- split 执行真值升级为: + - `splitItems[{seqNo, splitWater}]` +- `splitCount` 作为兼容口径保留,并在旧入口适配阶段转译为 `splitItems` + +## 2. 本轮代码落点 +### 新增 +- `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/controller/admin/splitadjust/SplitAdjustController.java` +- `.../controller/admin/splitadjust/vo/SplitAdjustSubmitReqVO.java` +- `.../controller/admin/splitadjust/vo/SplitAdjustSubmitRespVO.java` +- `.../controller/admin/splitadjust/vo/SplitAdjustDetailRespVO.java` +- `.../controller/admin/splitadjust/vo/SplitAdjustPageReqVO.java` +- `.../controller/admin/splitadjust/vo/SplitAdjustPageRespVO.java` +- `.../service/splitadjust/SplitAdjustService.java` +- `.../service/splitadjust/SplitAdjustServiceImpl.java` + +### 继续增强 +- `SplitAdjustFormalizationService` +- `AccountingAdjustActionServiceImpl` +- `AccountingAdjustProcessServiceImpl` +- `AccountingAdjustLogProcessServiceImpl` +- `AccountingAdjustDetailRespVO` +- `AccountingAdjustLogDetailRespVO` +- `ChargeDO` +- `sql/rev004/REV004_accounting_adjustments_ddl.sql` +- `Rev004AccountProcessCanaryQueryIntegrationTest` +- `AccountingAdjustActionServiceImplTest` +- `Rev004AccountProcessLiveDbReadinessTest` +- `sql/rev004/accountprocess/00_reset.sql` + +## 3. 关键实现结论 +1. 新 split submit 入口已经支持显式 `splitItems[{seqNo, splitWater}]` +2. `splitItems` 成为 formal 明细真值,审批通过执行会按明细水量比例分配子账单金额/水量 +3. 旧 `unsold-split-submit` 已兼容接入新 split 主链,可直接传 `splitItems` +4. 新增 split 专属 detail/result/page 接口 +5. 旧 `log-detail` 对新 split 专属单据也可 fallback 回看 +6. `biz_charge` 已承接最小追溯字段: + - `source_charge_id` + - `split_adjust_id` + - `charge_origin_type` + - `split_status` + +## 4. 验证命令与结果 +### 4.1 编译 +```bash +mvn -pl sw-business/sw-business-server -DskipTests compile +``` +结果:PASS + +### 4.2 targeted tests(阶段一) +```bash +REV004_IT_DB_URL='jdbc:postgresql://192.168.10.130:5436/sw_system' \ +REV004_IT_DB_USERNAME='sw_system' \ +REV004_IT_DB_PASSWORD='Em@123456' \ +mvn -pl sw-business/sw-business-server \ + -Dtest=Rev004AccountProcessCanaryQueryIntegrationTest#splitAdjustSubmit_shouldAcceptSplitItemsAndExposeDedicatedDetailRoute,AccountingAdjustActionServiceImplTest \ + test +``` +结果:PASS +- Tests run: 10 +- Failures: 0 +- Errors: 0 +- Skipped: 0 + +### 4.3 targeted tests(阶段二) +```bash +REV004_IT_DB_URL='jdbc:postgresql://192.168.10.130:5436/sw_system' \ +REV004_IT_DB_USERNAME='sw_system' \ +REV004_IT_DB_PASSWORD='Em@123456' \ +mvn -pl sw-business/sw-business-server \ + -Dtest=Rev004AccountProcessCanaryQueryIntegrationTest#splitAdjustSubmit_shouldAcceptSplitItemsAndExposeDedicatedDetailRoute+legacyUnsoldSplitSubmit_shouldCompatToSplitItemsFlow+splitAdjustPage_shouldReturnDedicatedRows+splitAdjustResult_shouldReturnSuccessAfterApprove,AccountingAdjustActionServiceImplTest \ + test +``` +结果:PASS +- Tests run: 13 +- Failures: 0 +- Errors: 0 +- Skipped: 0 + +## 5. 场景覆盖 +### 已覆盖 +- 新 split submit -> dedicated detail +- 新 split submit -> old log-detail fallback +- 旧 split submit -> new split detail +- dedicated page query +- dedicated result query after approve +- split action service unit tests(approve/reject/write-back) + +### 仍待补强 +- dedicated page + approval status filter 组合查询 +- dedicated process 专属接口(如果最终保留) +- split 执行失败/回滚路径的 real-db targeted integration + +## 6. 风险与后续 +1. 旧接口兼容已接主链,但还需要继续做更完整的批量兼容覆盖 +2. 当前仍未进入 BY_COMPONENT / `ItemStr` 真执行 +3. 若要最终收口,还应补一轮 architect verification + deslop + regression rerun diff --git a/docs/evidence/rev004-split-adjust-water-first-mvp-design-2026-04-14.md b/docs/evidence/rev004-split-adjust-water-first-mvp-design-2026-04-14.md new file mode 100644 index 0000000..b8ee961 --- /dev/null +++ b/docs/evidence/rev004-split-adjust-water-first-mvp-design-2026-04-14.md @@ -0,0 +1,234 @@ +# REV004 / 按水量分账最小闭环设计(2026-04-14) + +## 1. 目标 +本设计只覆盖 **第一阶段:按水量分账最小闭环**。 + +目标是: +- 审批通过后,能够把一笔未收费原账单按多笔水量拆分,真正生成多张目标子账单; +- 先不做“按费用组成分账”; +- 先把最小可运行闭环打通,再作为后续扩展基础。 + +--- + +## 2. 为什么第一阶段先做按水量分账 +原因: +1. **规则最清晰**:拆分水量之和必须等于原水量。 +2. **业务校验最直接**:原账单 -> 多笔子账单,按水量重算金额,容易理解。 +3. **比按费用组成更容易守恒**:先处理总水量、总金额守恒,再逐步扩到费用项粒度。 +4. **更适合先做执行态样板**:审批通过、执行、回看、失败回滚等流程都能先沉淀下来。 + +--- + +## 3. 最小闭环边界 +### 本阶段要做到 +- 提交按水量分账申请 +- 保存拆分明细(每笔分账水量) +- 审批通过后执行拆账 +- 生成多张目标账单 +- 原账单标记为已拆分/失效 +- 日志与查询可回看 + +### 本阶段不做 +- 按费用组成分账 +- 目标客户跨户分账(如无强需求,可先默认同客户) +- 更复杂规则模板 +- 开票联动优化 +- 自动撤销分账执行结果 + +--- + +## 4. 输入输出定义 + +### 4.1 前端最少输入 +按水量分账弹窗至少提交: +- `sourceChargeId` +- `splitItems[]` + - `seqNo` + - `splitWater` +- `reasonCode` +- `remark` +- `applicant` +- `contactMobile` + +### 4.2 关键校验 +- 原账单必须是未收费 +- `splitItems.length >= 2` +- 每笔 `splitWater > 0` +- `sum(splitWater) = source.billWater` +- 必须填写原因 + +### 4.3 输出结果 +申请提交成功后: +- `adjustmentNo` +- `resultStatus = PENDING_APPROVAL` + +执行成功后应可回看: +- 原账单摘要 +- 目标子账单列表 +- 每张子账单的: + - 水量 + - 账单金额 + - 应收金额 + - 违约金(若适用) + +--- + +## 5. 数据模型(MVP) + +### 5.1 主表:`biz_split_adjust` +建议 MVP 字段: +- `id` +- `split_adjust_no` +- `source_charge_id` +- `split_rule_type` = `BY_WATER` +- `reason_code` +- `remark` +- `approval_status` +- `execute_status` +- `execute_message` +- `executed_at` +- `creator / create_time / updater / update_time` + +### 5.2 明细表:`biz_split_adjust_detail` +建议 MVP 字段: +- `id` +- `split_adjust_id` +- `seq_no` +- `split_water` +- `target_charge_id`(执行后回填) +- `split_amount`(执行后回填) +- `status` +- `creator / create_time / updater / update_time` + +### 5.3 账单表最小扩展建议 +在 `biz_charge` 里增加追溯字段(二选一或组合): +- `source_charge_id` +- `split_adjust_id` +- `charge_origin_type`(如:NORMAL / SPLIT_CHILD) + +原账单增加状态表达(二选一): +- 新字段标记 `split_status` +- 或现有字段中增加来源/失效标识 + +MVP 阶段关键不是状态字段优雅,而是: +> 后续能稳定区分“原账单”和“拆分生成的子账单”。 + +--- + +## 6. 执行流程(MVP) + +### Step 1:申请提交 +接口:`POST /unsold-split-submit` +- 校验基本参数 +- 保存 `biz_split_adjust` +- 保存 `biz_split_adjust_detail`(只保存水量) +- 返回待审批 + +### Step 2:审批通过 +- 触发内部执行器 +- 对 `biz_split_adjust` 加幂等校验:只允许执行一次 +- 锁定原账单 `source_charge_id` +- 开事务 + +### Step 3:生成目标账单 +对每个 `split_adjust_detail`: +1. 复制原账单基础信息 +2. 将 `bill_water` 改成当前 `split_water` +3. 调用现有计价逻辑重新计算: + - `bill_amount` + - `extended_amount` + - 若适用则重算 `late_fee` +4. 插入新的 `biz_charge` 记录 +5. 回填 `target_charge_id / split_amount` + +### Step 4:处理原账单 +- 原账单置为“已拆分,不可继续收费” +- 不直接删除原账单 +- 保证历史可追溯 + +### Step 5:收尾 +- `biz_split_adjust.execute_status = SUCCESS` +- 写 operat_log / log detail +- 查询面可见结果 + +### Step 6:失败回滚 +- 任一子账单生成失败:事务回滚 +- 主单置 `FAIL` +- 记录失败原因 +- 不允许出现半拆分状态 + +--- + +## 7. 重算策略建议 +MVP 不建议自己重新写一套计价逻辑,建议: +- 复用已有账单计算能力 +- 输入拆分后的水量 +- 走现有价格模板/费用计算规则 + +原因: +- 这样能减少“分账执行”和“普通开账计费”之间的口径偏差 +- 也更容易保证后续账单金额解释一致 + +关键原则: +> 分账只是“改变水量输入并生成多张账单”,不要重造一套新的计费引擎。 + +--- + +## 8. 查询回看(MVP) +建议至少补两个查询面: + +### 8.1 分账详情查询 +- 原账单摘要 +- 分账规则类型(BY_WATER) +- 每笔拆分水量 +- 每笔生成的目标账单 ID / 金额 +- 执行状态 / 错误原因 + +### 8.2 日志页增强 +在现有 `log-detail / log-process` 中,增加: +- 原账单 -> 子账单映射摘要 +- 执行结果摘要 + +--- + +## 9. 测试建议(MVP) + +### 9.1 单测 +- 水量守恒校验 +- 拆分笔数边界(<2, >12) +- 非未收费账单禁止发起 +- 执行幂等 + +### 9.2 集成测试 +- 提交按水量分账申请 -> 待审批 +- 审批通过 -> 生成 N 张子账单 +- 子账单水量之和 = 原账单水量 +- 原账单不可继续收费 +- 日志页可回看执行结果 +- 执行失败整体回滚 + +### 9.3 验收测试 +- 一张账单拆成 2 笔 +- 一张账单拆成多笔 +- 子账单后续可分别收费/开票(若本期不做全链,可先验证可查询/可识别) + +--- + +## 10. MVP 成功标准 +MVP 完成时,应满足: +1. 用户可提交按水量分账申请 +2. 审批通过后,系统真正生成多张目标账单 +3. 子账单水量之和与原账单守恒 +4. 原账单被正确标记,不再继续作为普通未收费账单使用 +5. 查询与日志能回看这次分账执行结果 + +--- + +## 11. 建议实施顺序 +1. 先补 `biz_split_adjust` / `biz_split_adjust_detail` +2. 再补审批通过后的执行器 +3. 再补查询回看 +4. 最后补前端执行态结果页 + +一句话: +> 先把“按水量分账”做成审批后真正能拆出子账单的最小闭环,再考虑按费用组成扩展。