From a1be31c694cc42fc20f1a38742a1aa52534019f0 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Mon, 12 Oct 2026 17:50:54 +0800 Subject: [PATCH] docs: add payment status and paid-amount semantics implementation plan Break the confirmed spec into TDD tasks covering backend yjf-only paid amounts, web/mobile label renames, and menu SQL migration. Co-authored-by: Cursor --- ...ayment-status-and-paid-amount-semantics.md | 545 ++++++++++++++++++ ...status-and-paid-amount-semantics-design.md | 2 +- 2 files changed, 546 insertions(+), 1 deletion(-) create mode 100644 docs/superpowers/plans/2026-10-12-payment-status-and-paid-amount-semantics.md diff --git a/docs/superpowers/plans/2026-10-12-payment-status-and-paid-amount-semantics.md b/docs/superpowers/plans/2026-10-12-payment-status-and-paid-amount-semantics.md new file mode 100644 index 00000000..0ea3eafc --- /dev/null +++ b/docs/superpowers/plans/2026-10-12-payment-status-and-paid-amount-semantics.md @@ -0,0 +1,545 @@ +# 转账付款状态文案 + 期次已缴费仅计 yjf Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 转账笔状态统一为「已付款 / 付款确认中 / 付款完成」;期次「已缴费」仅计 `yjf`、欠缴费=应缴−已缴;菜单「缴费财务确认及开票」改为「缴费财务确认」;Web 与市场小程序口径一致。 + +**Architecture:** 金额口径以 `summarize_period` 为后端权威、`schedulePaidArrears` 为前端权威,二者均改为仅合计 `yjf`;文案以 `status-tag-registry.payStatus` 与小程序 `PAY_STATUS` 映射为源;菜单同步 `sqxp.ts` / `menu.py` / `sys_menu` 迁移。状态码与状态机不变。 + +**Tech Stack:** FastAPI/pytest、Vue 3 + Element Plus(web-admin)、mobile-oa-h5(Node assert 测试)、MySQL `sys_menu` + +**Spec:** `docs/superpowers/specs/2026-10-12-payment-status-and-paid-amount-semantics-design.md` + +--- + +### Task 1: 后端期次已缴费口径仅计 yjf + +**Files:** +- Modify: `backend/app/services/sample_payment_period_summary.py` +- Modify: `backend/app/services/payment_chase.py`(文档字符串) +- Modify: `backend/tests/test_sample_payment_period_summary.py` +- Modify: `backend/tests/test_payment_chase.py` + +- [ ] **Step 1: 改写失败用例期望(TDD)** + +在 `test_sample_payment_period_summary.py`: + +- `test_dfj_counts_as_display_paid` → 改名为 `test_dfj_does_not_count_as_display_paid`:期望 `displayPaidAmount == 0`,`arrearsAmount == 1000` +- `test_confirming_wins`:`displayPaidAmount` 从 `2000` 改为 `0`(`jfqrz` 不计);`financeAudit` 仍为 `jfqrz` +- 保留纯 `yjf` 用例期望不变 + +在 `test_payment_chase.py`: + +```python +def test_chase_paid_only_counts_yjf(self): + """已缴 = 仅 yjf;欠缴 = 应缴 − 已缴""" + paid, arrears = chase_period_money( + due_amount=Decimal("1000"), + transfers=[ + {"transferAmount": Decimal("300"), "payStatus": "dfj", "transferTime": "2026-04-01 10:00:00"}, + {"transferAmount": Decimal("200"), "payStatus": "jfqrz", "transferTime": "2026-04-02 10:00:00"}, + {"transferAmount": Decimal("100"), "payStatus": "yjf", "transferTime": "2026-04-03 10:00:00"}, + ], + ) + self.assertEqual(paid, Decimal("100")) + self.assertEqual(arrears, Decimal("900")) + +def test_chase_dfj_only_still_has_arrears(self): + paid, arrears = chase_period_money( + due_amount=Decimal("500"), + transfers=[ + {"transferAmount": Decimal("500"), "payStatus": "dfj", "transferTime": "2026-04-01 10:00:00"}, + ], + ) + self.assertEqual(paid, Decimal("0")) + self.assertEqual(arrears, Decimal("500")) + self.assertTrue( + is_chase_candidate( + due_date=date(2026, 9, 1), + today=date(2026, 10, 9), + arrears=arrears, + ) + ) +``` + +删除或替换旧的 `test_chase_paid_includes_dfj_jfqrz_yjf`、`test_chase_full_display_paid_not_candidate`。 + +- [ ] **Step 2: 跑测试确认失败** + +```bash +cd backend && source venv/bin/activate +pytest tests/test_sample_payment_period_summary.py tests/test_payment_chase.py -v +``` + +Expected: 与 `displayPaidAmount` / `paid` 相关断言 FAIL。 + +- [ ] **Step 3: 最小实现** + +`sample_payment_period_summary.py`: + +```python +_DISPLAY_PAID_STATUSES = ("yjf",) +``` + +更新模块 docstring / `summarize_period` 注释为「已缴费金额仅计付款完成(yjf)」。 + +`payment_chase.py` 中 `chase_period_money` docstring: + +```python +"""已缴 = 仅 yjf;欠缴 = max(应缴 − 已缴, 0)。""" +``` + +- [ ] **Step 4: 跑测试确认通过** + +```bash +pytest tests/test_sample_payment_period_summary.py tests/test_payment_chase.py -v +``` + +Expected: PASS + +- [ ] **Step 5: Commit** + +```bash +git add backend/app/services/sample_payment_period_summary.py \ + backend/app/services/payment_chase.py \ + backend/tests/test_sample_payment_period_summary.py \ + backend/tests/test_payment_chase.py +git commit -m "$(cat <<'EOF' +fix(backend): count period paid amount as yjf only + +Align displayPaidAmount/arrears with business rule: 已缴费 excludes +已付款 and 付款确认中 transfers. +EOF +)" +``` + +--- + +### Task 2: 市场催缴 API 测试对齐新口径 + +**Files:** +- Modify: `backend/tests/test_market_payment_api.py` +- (实现已在 Task 1;本任务只修断言与夹具) + +- [ ] **Step 1: 改写 `test_chase_counts_dfj_jfqrz_and_yjf_and_excludes_fully_paid`** + +`_payment_tree` 每笔转账金额固定 `20`。旧口径下 `due=60` + 三态各 20 → 已缴 60、欠缴 0;新口径仅 `yjf` 计 20 → 欠缴 40,两单都会进催缴。 + +改为: + +```python +def test_chase_counts_only_yjf_and_excludes_fully_confirmed(self): + from app.services.market_payment import list_payment_chase + + db = _session() + _payment_tree( + db, + 'part-paid', + due_amount=100, + transfer_statuses=('dfj', 'jfqrz', 'yjf'), + ) + _payment_tree( + db, + 'fully-paid', + due_amount=60, + transfer_statuses=('yjf', 'yjf', 'yjf'), # 3×20 = 60,全部付款完成 + ) + + items, total = list_payment_chase(db, ctx=_ctx(), user_info=_user()) + + assert total == 1 + assert items[0]['scheduleId'] == 'sch-part-paid' + assert items[0]['paidAmount'] == '20.00' + assert items[0]['arrearsAmount'] == '80.00' +``` + +若其它用例依赖「纯 `dfj` 视为已缴清而不进催缴」,同步改为欠缴>0 期望或改用 `yjf` 填满应缴。 + +- [ ] **Step 2: 跑测试** + +```bash +cd backend && source venv/bin/activate +pytest tests/test_market_payment_api.py -k chase -v +``` + +Expected: PASS + +- [ ] **Step 3: Commit** + +```bash +git add backend/tests/test_market_payment_api.py +git commit -m "$(cat <<'EOF' +test(backend): align market chase assertions with yjf-only paid +EOF +)" +``` + +--- + +### Task 3: Web 前端期次已缴口径 + +**Files:** +- Modify: `web-admin/src/utils/protocol-sample-payment.ts` +- Modify: `web-admin/src/utils/protocol-sample-payment.test.ts` + +- [ ] **Step 1: 改失败测试** + +```ts +describe('schedulePaidArrears', () => { + it('display paid includes only yjf', () => { + const { paid, arrears } = schedulePaidArrears(25880, [ + { payStatus: 'dfj', transferAmount: 480 }, + { payStatus: 'dfj', transferAmount: 6000 }, + { payStatus: 'jfqrz', transferAmount: 100 }, + { payStatus: 'yjf', transferAmount: 200 }, + { payStatus: 'ytf', transferAmount: 50 } + ]) + assert.equal(paid, 200) + assert.equal(arrears, 25680) + }) +}) +``` + +- [ ] **Step 2: 跑测试确认失败** + +```bash +cd web-admin && node --test src/utils/protocol-sample-payment.test.ts +``` + +- [ ] **Step 3: 实现** + +将 `DISPLAY_PAID_STATUSES` 改为仅 `yjf`,或让 `schedulePaidArrears` 直接委托: + +```ts +export function schedulePaidArrears( + dueAmount: number, + transfers: TransferLike[] +): { paid: number; arrears: number } { + const { paid, arrears } = scheduleFinanceConfirmedPaidArrears(dueAmount, transfers) + return { paid, arrears: arrears > 0 ? arrears : 0 } +} +``` + +注意:`scheduleFinanceConfirmedPaidArrears` 当前欠缴可为负(超缴);展示口径需 `max(0)`。若委托,保持与后端一致的非负欠缴。 + +更新注释:「已缴 = 仅 yjf」。 + +- [ ] **Step 4: 跑测试确认通过 → Commit** + +```bash +git add web-admin/src/utils/protocol-sample-payment.ts \ + web-admin/src/utils/protocol-sample-payment.test.ts +git commit -m "$(cat <<'EOF' +fix(web-admin): align schedule paid/arrears with yjf-only rule +EOF +)" +``` + +--- + +### Task 4: Web 状态文案注册表 + 硬编码 + +**Files:** +- Modify: `web-admin/src/utils/status-tag-registry.ts` +- Modify: `web-admin/src/utils/protocol-sample-payment.ts`(错误文案) +- Modify: `web-admin/src/utils/protocol-pay-mode-switch.ts` +- Modify: `web-admin/src/utils/protocol-fq-params-change.ts` +- Modify: `web-admin/src/components/business/payment-receipt-confirm-dialog/index.vue` +- Modify: `web-admin/src/views/cell-storage/payment-confirm/index.vue` +- Modify: `web-admin/src/views/cell-storage/payment-confirm/RefundConfirmTab.vue` +- Modify: `web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue` +- Modify: `web-admin/src/views/cell-storage/protocol-audit/PaymentInfoTab.vue`(文件头注释) +- Modify: `web-admin/src/views/cell-storage/payment-info/PaymentBySampleTab.vue` +- Modify: `web-admin/src/views/pilot-service/clinical-payment-confirm/index.vue` +- Modify: `web-admin/src/views/pilot-service/clinical-payment-confirm/RefundConfirmTab.vue` +- Modify: `web-admin/src/views/pilot-service/pilot-application/ClinicalPaymentTab.vue` +- (扫尾)其它 grep 命中的硬编码「已缴费」「缴费确认中」「缴费成功」若属转账态 + +**不要改:** 列名「已缴费金额 / 已缴费时间 / 应缴费 / 欠缴费」。 + +- [ ] **Step 1: 更新注册表** + +```ts +export const payStatus: StatusRegistry = { + dfj: { label: '已付款', tagType: 'primary' }, + jfqrz: { label: '付款确认中', tagType: 'warning' }, + yjf: { label: '付款完成', tagType: 'success' }, + tfqrz: { label: '退费确认中', tagType: 'warning' }, + ytf: { label: '已退费', tagType: 'danger' } +} + +/** 按样本列表整体进度(勿与转账笔 payStatus 混用:payStatus.dfj=已付款) */ +export const samplePaymentProgress: StatusRegistry = { + dfj: { label: '待缴费', tagType: 'info' }, + jfqrz: { label: '付款确认中', tagType: 'warning' }, + bfyj: { label: '部分已缴', tagType: 'primary' }, + yjq: { label: '已缴清', tagType: 'success' } +} +``` + +`periodFinanceAudit` 保持短文案「确认中 / 已通过」(spec §4.3)。 + +- [ ] **Step 2: 替换硬编码用户可见文案(对照表)** + +| 旧 | 新 | +|----|----| +| 缴费确认中(转账态) | 付款确认中 | +| 已缴费(指 `dfj` 或退回后草稿) | 已付款 | +| 缴费成功 | 付款完成 | +| 提交后状态变为「缴费确认中」 | 提交后状态变为「付款确认中」 | +| 退回后状态将变为「已缴费」 | 退回后状态将变为「已付款」 | +| 退回后状态将变为「缴费成功」 | 退回后状态将变为「付款完成」 | +| 存在缴费确认中的转账… | 存在付款确认中的转账… | + +`ProtocolViewDialog` 时间线 `subLabel: '缴费成功'` → `'付款完成'`;`subLabel: '已缴费'`(若指转账态)→ `'已付款'`。 + +`PaymentBySampleTab` 筛选项 `label: '缴费确认中'` → `'付款确认中'`。 + +- [ ] **Step 3: 全库扫尾** + +```bash +rg -n '缴费确认中|缴费成功|"已缴费"' web-admin/src --glob '*.{vue,ts}' +``` + +仅改转账状态语义;保留期次金额列名。 + +- [ ] **Step 4: Commit** + +```bash +git add web-admin/src/utils/status-tag-registry.ts \ + web-admin/src/utils/protocol-sample-payment.ts \ + web-admin/src/utils/protocol-pay-mode-switch.ts \ + web-admin/src/utils/protocol-fq-params-change.ts \ + web-admin/src/components/business/payment-receipt-confirm-dialog/index.vue \ + web-admin/src/views/cell-storage \ + web-admin/src/views/pilot-service +git commit -m "$(cat <<'EOF' +feat(web-admin): rename transfer pay statuses to 付款 wording + +Use 已付款/付款确认中/付款完成 for payStatus; keep period money +labels 应缴费/已缴费/欠缴费 unchanged. +EOF +)" +``` + +--- + +### Task 5: 菜单改名(前端 + 后端 + SQL) + +**Files:** +- Modify: `web-admin/src/router/modules/sqxp.ts` +- Modify: `backend/app/api/menu.py` +- Modify: `web-admin/src/views/cell-storage/payment-confirm/index.vue`(文件头注释) +- Create: `docs/sql/migrations/2026-10-12-rename-payment-confirm-menu.sql` + +- [ ] **Step 1: 改固定菜单文案** + +两处 `title` / `meta.title`:`缴费财务确认及开票` → `缴费财务确认`。 + +`payment-confirm/index.vue` 首行注释同步。 + +- [ ] **Step 2: 写迁移 SQL** + +```sql +-- ============================================================================= +-- 菜单改名:缴费财务确认及开票 → 缴费财务确认 +-- id: 50000000000000000000000000000004 +-- perms: biz:cellStoragePaymentConfirm:list +-- ============================================================================= + +UPDATE sys_menu +SET menu_name = '缴费财务确认', + update_time = NOW() +WHERE del_flag = 0 + AND ( + id = '50000000000000000000000000000004' + OR perms = 'biz:cellStoragePaymentConfirm:list' + ) + AND menu_name <> '缴费财务确认'; +``` + +- [ ] **Step 3: Commit** + +```bash +git add web-admin/src/router/modules/sqxp.ts \ + backend/app/api/menu.py \ + web-admin/src/views/cell-storage/payment-confirm/index.vue \ + docs/sql/migrations/2026-10-12-rename-payment-confirm-menu.sql +git commit -m "$(cat <<'EOF' +chore: rename payment-confirm menu to 缴费财务确认 +EOF +)" +``` + +--- + +### Task 6: 后端用户可见错误文案 + 旁路映射 + +**Files:** +- Modify: `backend/app/crud/protocol_sample_payment.py` +- Modify: `backend/app/crud/collection_protocol.py` +- Modify: `backend/app/crud/presign_order.py` +- Modify: `backend/app/crud/clinical_payment.py` +- Modify: `backend/app/services/market_payment.py` +- Modify: `backend/app/services/protocol_transfer_status.py`(docstring) +- Modify: `backend/app/services/protocol_pay_mode_switch.py` +- Modify: `backend/app/services/qc_work_order_fee_summary.py` +- Modify: 相关 pytest 中 `match=` / 文案断言(按失败再改) + +- [ ] **Step 1: 统一替换用户可见 ValueError(对照)** + +| 旧(校验态) | 新 | +|--------------|-----| +| 仅已缴费状态可编辑…(实际 `dfj`) | 仅已付款状态可编辑… | +| 仅已缴费状态可提交财务确认 | 仅已付款状态可提交财务确认 | +| 仅缴费确认中可撤回 | 仅付款确认中可撤回 | +| 仅缴费确认中状态可确认收款 / 可退回 | 仅付款确认中状态可… | +| 存在缴费确认中或退费处理中… | 存在付款确认中或退费处理中… | +| 仅已缴费状态可发起退费(临床,实际 `yjf`) | 仅付款完成状态可发起退费 | + +`qc_work_order_fee_summary.py`: + +```python +_PAY_STATUS_LABELS = { + "dfj": "已付款", + "jfqrz": "付款确认中", + "yjf": "付款完成", + "tfqrz": "退费确认中", + "ytf": "已退费", +} +``` + +- [ ] **Step 2: 跑相关测试并修 match 字符串** + +```bash +cd backend && source venv/bin/activate +pytest tests/test_protocol_sample_payment.py tests/test_presign_order.py \ + tests/test_market_payment_api.py tests/test_market_presign_api.py -q +``` + +- [ ] **Step 3: Commit** + +```bash +git add backend/app/crud backend/app/services backend/tests +git commit -m "$(cat <<'EOF' +fix(backend): align pay-status user messages with 付款 wording +EOF +)" +``` + +--- + +### Task 7: 市场小程序状态文案 + +**Files:** +- Modify: `mobile-oa-h5/src/market/modules/payment.js` +- Modify: `mobile-oa-h5/src/market/modules/presign.js` +- Modify: `mobile-oa-h5/src/market/components/PresignTransferCard.vue` +- Modify: `mobile-oa-h5/src/market/module-contracts.test.js` +- Modify: 其它断言「已确认」「待缴费」作为**转账** `payStatus` 的测试 + +- [ ] **Step 1: 更新映射** + +`payment.js`: + +```js +const PAYMENT_PROGRESS = Object.freeze({ + dfj: '待缴费', jfqrz: '付款确认中', bfyj: '部分已缴', yjq: '已缴清', + 待缴费: '待缴费', 部分缴费: '部分缴费', 已缴清: '已缴清' +}) +const PAY_STATUS = Object.freeze({ + dfj: '已付款', jfqrz: '付款确认中', yjf: '付款完成', tfqrz: '退费确认中', ytf: '已退费' +}) +``` + +`transferCard` 中 `statusTone`:`statusText === '已确认'` 改为 `statusText === '付款完成'`。 + +`presign.js` / `PresignTransferCard.vue`:`dfj→已付款`,`jfqrz→付款确认中`,`yjf→付款完成`(默认回退文案勿再用「待缴费」表示 `dfj`)。 + +- [ ] **Step 2: 更新测试期望** + +例:`module-contracts.test.js` 中 `statusLabel === '已确认'` → `'付款完成'`。 + +- [ ] **Step 3: 跑测试** + +```bash +cd mobile-oa-h5 +node --test src/market/module-contracts.test.js \ + src/market/mobile-parity-ui.test.js \ + src/market/presign/*.test.js +``` + +(以仓库惯用命令为准。) + +- [ ] **Step 4: Commit** + +```bash +git add mobile-oa-h5/src/market +git commit -m "$(cat <<'EOF' +feat(mobile-oa-h5): align transfer payStatus labels with 付款 wording +EOF +)" +``` + +--- + +### Task 8: 回归与图谱 + +- [ ] **Step 1: 后端回归** + +```bash +cd backend && source venv/bin/activate +pytest tests/test_sample_payment_period_summary.py \ + tests/test_payment_chase.py \ + tests/test_sample_payment_progress.py \ + tests/test_market_payment_api.py \ + tests/test_protocol_sample_payment.py -q +``` + +Expected: PASS + +- [ ] **Step 2: 前端 / 小程序相关单测** + +```bash +cd web-admin && node --test src/utils/protocol-sample-payment.test.ts +cd ../mobile-oa-h5 && node --test src/market/module-contracts.test.js src/market/mobile-parity-ui.test.js +``` + +- [ ] **Step 3: 更新 graphify(若有函数/文案映射签名变更)** + +```bash +./scripts/update-graph.sh backend +./scripts/update-graph.sh frontend +``` + +仅当本次改动触及图谱关注的符号时提交 `graphify-out`;纯文案可跳过。 + +- [ ] **Step 4: 将 spec 状态改为已确认(若仍为待审阅)** + +`docs/superpowers/specs/2026-10-12-payment-status-and-paid-amount-semantics-design.md` 头部:`待用户审阅` → `已确认`。 + +- [ ] **Step 5: 最终 commit(若有残留)** + +```bash +git add docs/superpowers/specs/2026-10-12-payment-status-and-paid-amount-semantics-design.md +git commit -m "$(cat <<'EOF' +docs: mark payment status semantics spec confirmed +EOF +)" +``` + +--- + +## Spec 覆盖对照 + +| Spec 要求 | Task | +|-----------|------| +| 期次已缴费仅 `yjf`、欠缴公式 | Task 1–3 | +| 催缴级联 | Task 1–2 | +| 转账文案 已付款/付款确认中/付款完成 | Task 4、6、7 | +| samplePaymentProgress.jfqrz → 付款确认中 | Task 4、7 | +| 菜单改名 + SQL | Task 5 | +| 临床共用注册表 | Task 4 | +| 小程序对齐 | Task 7 | +| 不改码值/状态机/期次列名 | 全任务约束 | +| 验收回归 | Task 8 | diff --git a/docs/superpowers/specs/2026-10-12-payment-status-and-paid-amount-semantics-design.md b/docs/superpowers/specs/2026-10-12-payment-status-and-paid-amount-semantics-design.md index c2d65e11..e89063f7 100644 --- a/docs/superpowers/specs/2026-10-12-payment-status-and-paid-amount-semantics-design.md +++ b/docs/superpowers/specs/2026-10-12-payment-status-and-paid-amount-semantics-design.md @@ -1,6 +1,6 @@ # 转账「付款」状态文案 + 期次「已缴费」仅计付款完成 -> 状态:待用户审阅 +> 状态:已确认 > 日期:2026-10-12 > 界面模式:细胞存储缴费相关列表/表单(P02 / P13);市场小程序缴费/催缴卡片 > 关联: