docs: plan complete mobile market parity implementation

This commit is contained in:
zlwu committed 2026-10-13 19:41:55 +08:00
1 parent f5daab0d44
commit f5c514c520
2 files changed
+399 -1

No files matched your search

@@ -0,0 +1,398 @@
# 市场五模块小程序与 Web 功能对齐 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:** 让市场专员、地区经理在小程序中的预签约订单、协议单、缴费信息、催缴信息、运费信息与对应 Web 页面具备相同信息元素和允许业务能力,同时保持既有角色数据范围和移动端交互。
**Architecture:** 继续使用 `mobile-oa-h5` 的共享市场页面壳,以模块契约描述筛选、卡片、详情和表单;复杂协议/预签交互使用专用组件。后端市场路由只适配 DTO 与权限,查询和写入复用现有业务 service/CRUD,并在每个入口重新验证租户、角色现场范围和业务状态。
**Tech Stack:** Vue 3、Vant 4、原生 Node test runner、FastAPI、SQLAlchemy、Pydantic v2、pytest、MySQL 5.7。
---
## 文件结构
### 新建
- `mobile-oa-h5/src/market/list-filters.js`:筛选默认值、请求参数和已选数量的纯函数。
- `mobile-oa-h5/src/market/list-filters.test.js`:五模块筛选契约测试。
- `mobile-oa-h5/src/market/components/MarketFilterSheet.vue`:移动端多条件筛选弹层。
- `docs/superpowers/specs/2026-10-13-market-five-module-mobile-parity-completion.md`:最终逐项完成矩阵与环境验收清单。
### 修改
- `mobile-oa-h5/src/views/market/MarketModuleList.vue`:接入通用筛选弹层,移除审核 UI,增加协议批量选择/删除的移动交互。
- `mobile-oa-h5/src/views/market/MarketModuleDetail.vue`:只呈现允许动作;协议、缴费、催缴跨模块跳转与刷新。
- `mobile-oa-h5/src/views/market/MarketModuleForm.vue`:收敛当前协议单样本、运输、客户、缴费、转账在制改动。
- `mobile-oa-h5/src/market/modules/{presign,protocol,payment-chase,payment,freight}.js`:定义完整筛选、字段、详情与动作契约。
- `mobile-oa-h5/src/market/list-presenter.js`:补齐 Web 列表字段、语义标签和动作名称。
- `mobile-oa-h5/src/market/action-policy.js`:删除协议审核白名单,保留明确开放动作。
- `mobile-oa-h5/src/api/market.js`:删除审核映射,保留协议删除及五模块 API 封装。
- `mobile-oa-h5/src/styles/market.css`:筛选弹层、批量选择、复杂卡片移动样式。
- `backend/app/api/market_mobile.py`:补齐筛选 Query 参数,移除市场协议审核路由。
- `backend/app/services/market_protocol.py`:协议状态、创建时间、采集时间过滤;完成当前协议 DTO/详情改动。
- `backend/app/services/market_payment.py`:缴费进度、应缴日期过滤和详情字段对齐。
- `backend/app/services/market_freight.py`:创建时间过滤和完整可见行字段。
- `backend/app/services/market_support.py`:动作白名单、表单选项和只读详情补齐。
- `backend/app/schemas/market_mobile.py`:完成协议样本/运费/缴费写 DTO。
- 对应 `backend/tests/test_market_*.py` 与 `mobile-oa-h5/src/market/*.test.js`:回归和契约测试。
## Task 1:收敛当前协议单在制改动与动作边界
- [ ] **Step 1:把“不开放审核”写成失败测试**
修改 `mobile-oa-h5/src/market/module-actions.test.js`:
```js
test('protocol never exposes management-only actions', () => {
const actions = moduleActions('protocol', 'specialist')
assert.deepEqual(actions, ['view', 'create', 'edit', 'delete'])
for (const denied of ['audit', 'status-change', 'refund', 'invoice', 'finance-confirm', 'finance-reject']) {
assert.equal(actions.includes(denied), false)
}
})
```
修改 `backend/tests/test_market_mobile_permissions.py`,断言 FastAPI 路由表不存在 `/mini/market/{role}/protocols/{protocol_id}/audit`,而 DELETE 路由存在。
- [ ] **Step 2:运行测试并确认按预期失败**
```bash
cd mobile-oa-h5 && node --test src/market/module-actions.test.js
cd backend && venv/bin/python -m pytest tests/test_market_mobile_permissions.py tests/test_market_protocol_api.py -q
```
预期:前端审核白名单或后端审核路由断言失败;已有 `test_protocol_audit_and_delete_recheck_scoped_access_before_crud` 与新边界冲突。
- [ ] **Step 3:实现最小动作边界修正**
- 从 `backend/app/api/market_mobile.py` 删除两个角色注册出的协议审核 endpoint。
- `market_support.allowed_actions('protocol', ...)` 不再返回 `audit`。
- 从 `MarketModuleList.vue` 删除审核 action sheet、状态和处理函数。
- 从 `market.js` 删除协议 `audit` path 分支,只保留 `delete`。
- 将旧审核 service 单测改为只验证删除的 scope 复核;Web 自身审核 API 和 service 不做删除。
- [ ] **Step 4:运行协议定向测试**
```bash
cd mobile-oa-h5 && node --test src/market/module-actions.test.js src/market/api-contract.test.js src/market/mobile-parity-ui.test.js
cd backend && venv/bin/python -m pytest tests/test_market_mobile_permissions.py tests/test_market_protocol_api.py tests/test_market_support_api.py -q
```
预期:全部通过,且不再出现“当前状态不可审核”的旧市场端测试失败。
- [ ] **Step 5:提交**
```bash
git add backend/app/api/market_mobile.py backend/app/services/market_support.py backend/tests/test_market_mobile_permissions.py backend/tests/test_market_protocol_api.py mobile-oa-h5/src/api/market.js mobile-oa-h5/src/market/action-policy.js mobile-oa-h5/src/market/module-actions.test.js mobile-oa-h5/src/market/api-contract.test.js mobile-oa-h5/src/market/mobile-parity-ui.test.js mobile-oa-h5/src/views/market/MarketModuleList.vue
git commit -m "fix: keep mobile protocol management actions restricted to web"
```
## Task 2:建立五模块通用移动筛选能力
- [ ] **Step 1:新增筛选纯函数失败测试**
创建 `mobile-oa-h5/src/market/list-filters.test.js`,覆盖:
```js
test('maps every web-equivalent filter to backend query names', () => {
assert.deepEqual(buildListParams('protocol', {
source: 'cell_storage', protocolStatus: 'sxz',
createTimeRange: ['2026-10-01', '2026-10-13'],
sampleCollectTimeRange: ['2026-09-01', '2026-09-30']
}), {
source: 'cell_storage', protocolStatus: 'sxz',
createTimeStart: '2026-10-01', createTimeEnd: '2026-10-13',
sampleCollectTimeStart: '2026-09-01', sampleCollectTimeEnd: '2026-09-30'
})
assert.deepEqual(buildListParams('payment', {
payMode: 'fq', progressStatus: 'bfyj', dueDateRange: ['2026-10-01', '2026-10-31']
}), {
payMode: 'fq', progressStatus: 'bfyj',
installmentDueDateStart: '2026-10-01', installmentDueDateEnd: '2026-10-31'
})
assert.deepEqual(buildListParams('payment-chase', { chaseType: 'overdue', payMode: 'dj' }), {
chaseType: 'overdue', payMode: 'dj'
})
assert.deepEqual(buildListParams('freight', {
transportMode: 'cold_chain', sampleSource: 'cell_storage',
createTimeRange: ['2026-10-01', '2026-10-13']
}), {
transportMode: 'cold_chain', sampleSource: 'cell_storage',
createTimeStart: '2026-10-01', createTimeEnd: '2026-10-13', includeItems: true
})
})
```
- [ ] **Step 2:运行测试并确认模块不存在**
```bash
cd mobile-oa-h5 && node --test src/market/list-filters.test.js
```
预期:FAIL,`list-filters.js` 尚不存在。
- [ ] **Step 3:实现纯函数和模块筛选契约**
`list-filters.js` 导出 `createListFilters(module, source)`、`buildListParams(module, filters)`、`activeFilterCount(filters)`。五个模块增加 `filterFields`:
```js
// payment.js
filterFields: [
{ key: 'payMode', label: '缴费方式', type: 'select', options: PAY_MODE_OPTIONS },
{ key: 'progressStatus', label: '缴费进度', type: 'select', options: PROGRESS_OPTIONS },
{ key: 'dueDateRange', label: '应缴费时间', type: 'date-range' }
]
```
协议使用 `protocolStatus/createTimeRange/sampleCollectTimeRange`;催缴使用 `chaseType/payMode`;运费使用 `transportMode/sampleSource/createTimeRange`;预签约使用 `orderStatus`。
- [ ] **Step 4:实现 `MarketFilterSheet.vue` 并接入列表**
组件 props 为 `modelValue/fields/options`,事件为 `update:modelValue/apply/reset`。`MarketModuleList.vue` 保存一份草稿和一份已应用筛选;只有 Apply 才发请求,Reset 清空筛选并重新加载第一页。运输方式选项通过 `fetchMarketFormOptions` 获取。
- [ ] **Step 5:运行筛选与 UI 契约测试**
```bash
cd mobile-oa-h5 && node --test src/market/list-filters.test.js src/market/module-contracts.test.js src/market/mobile-parity-ui.test.js src/market/request-state.test.js
```
预期:全部通过。
- [ ] **Step 6:提交**
```bash
git add mobile-oa-h5/src/market/list-filters.js mobile-oa-h5/src/market/list-filters.test.js mobile-oa-h5/src/market/components/MarketFilterSheet.vue mobile-oa-h5/src/market/modules mobile-oa-h5/src/views/market/MarketModuleList.vue mobile-oa-h5/src/styles/market.css mobile-oa-h5/src/market/module-contracts.test.js mobile-oa-h5/src/market/mobile-parity-ui.test.js
git commit -m "feat: add web-equivalent mobile filters for market modules"
```
## Task 3:补齐后端列表筛选契约
- [ ] **Step 1:先写两个角色的 API 失败测试**
在现有测试中新增参数透传和真实查询断言:
```python
@pytest.mark.parametrize('role', ['specialist', 'manager'])
def test_market_list_filters_match_web_contract(client, role, auth_headers):
cases = [
('protocols', {'source': 'cell_storage', 'protocolStatus': 'sxz', 'createTimeStart': '2026-10-01', 'createTimeEnd': '2026-10-13'}),
('payments', {'payMode': 'fq', 'progressStatus': 'bfyj', 'installmentDueDateStart': '2026-10-01', 'installmentDueDateEnd': '2026-10-31'}),
('payment-chase', {'chaseType': 'overdue', 'payMode': 'fq'}),
('freights', {'sampleSource': 'cell_storage', 'createTimeStart': '2026-10-01', 'createTimeEnd': '2026-10-13'}),
]
for resource, params in cases:
response = client.get(f'/api/mini/market/{role}/{resource}', params=params, headers=auth_headers(role))
assert response.status_code == 200
```
服务层测试分别创建命中和不命中的行,并断言 `total/list` 只包含命中项。
- [ ] **Step 2:运行并确认缺少 Query 参数或过滤行为**
```bash
cd backend && venv/bin/python -m pytest tests/test_market_protocol_api.py tests/test_market_payment_api.py tests/test_market_freight_scope.py -q
```
预期:新增用例失败。
- [ ] **Step 3:实现路由与 service 过滤**
- 协议:`protocolStatus/createTimeStart/createTimeEnd/sampleCollectTimeStart/sampleCollectTimeEnd`。
- 缴费:`progressStatus/installmentDueDateStart/installmentDueDateEnd`;进度按与 Web 相同的已缴/欠缴聚合口径过滤。
- 运费:`createTimeStart/createTimeEnd`。
- 催缴、预签约已有参数保持并补测试。
- 所有日期输入解析失败返回 400,不静默扩大结果集;结束日期按当天 23:59:59 或左闭右开次日处理。
- [ ] **Step 4:运行后端市场列表测试**
```bash
cd backend && venv/bin/python -m pytest tests/test_market_presign_api.py tests/test_market_protocol_api.py tests/test_market_payment_api.py tests/test_market_freight_scope.py tests/test_market_field_scope.py -q
```
预期:全部通过。
- [ ] **Step 5:提交**
```bash
git add backend/app/api/market_mobile.py backend/app/services/market_protocol.py backend/app/services/market_payment.py backend/app/services/market_freight.py backend/tests/test_market_presign_api.py backend/tests/test_market_protocol_api.py backend/tests/test_market_payment_api.py backend/tests/test_market_freight_scope.py
git commit -m "feat: align mobile market list filters with web queries"
```
## Task 4:完成协议单复杂表单、详情与删除能力
- [ ] **Step 1:运行当前协议相关测试,保留真实失败清单**
```bash
cd mobile-oa-h5 && node --test src/market/protocol-*.test.js src/market/form-payload.test.js src/market/module-contracts.test.js src/market/mobile-parity-ui.test.js
cd backend && venv/bin/python -m pytest tests/test_market_protocol_api.py tests/test_market_support_api.py -q
```
当前基线:前端 176 项市场测试通过;后端协议/支撑定向测试 105 项中 1 项旧审核预期失败。Task 1 修正后,本步骤必须为全绿。
- [ ] **Step 2:补写协议完整闭环失败测试**
覆盖:客户档案检索与绑定、身份证出生年月联动、样本字段、运费新建/关联/取消、按样本缴费、协议总金额跟随、转账新增/编辑/提交/撤回、五类附件、只读储存证/保险/审核/状态记录、单条删除和批量删除。契约测试必须断言 §2.2 管理按钮不存在。
- [ ] **Step 3:完成现有在制代码并修复失败测试**
收敛以下文件中的既有改动,不重写已通过逻辑:
```text
backend/app/{api/market_mobile.py,schemas/market_mobile.py,services/market_protocol.py,services/market_support.py,crud/sample_freight.py}
mobile-oa-h5/src/market/components/{ProtocolCustomerSearchField.vue,ProtocolSampleTransportPanel.vue,ProtocolSamplePaymentPanel.vue}
mobile-oa-h5/src/market/{protocol-customer-link.js,protocol-sample-transport.js,protocol-sample-payment.js,protocol-sample-transfer.js}
mobile-oa-h5/src/views/market/MarketModuleForm.vue
```
协议删除使用后端软删除;批量删除在移动端逐条调用已授权 DELETE,任一失败时停止并刷新列表,成功/失败数量明确展示,不新增绕过单条 scope 校验的批量后端口。
- [ ] **Step 4:验证协议前后端**
```bash
cd mobile-oa-h5 && node --test src/market/protocol-*.test.js src/market/form-payload.test.js src/market/module-contracts.test.js src/market/mobile-parity-ui.test.js
cd mobile-oa-h5 && npm run build
cd backend && venv/bin/python -m pytest tests/test_market_protocol_api.py tests/test_market_support_api.py tests/test_market_freight_scope.py tests/test_market_payment_api.py -q
```
预期:全部测试和构建通过。
- [ ] **Step 5:提交协议在制改动**
仅添加上述协议相关文件及对应测试,不提交无关工作区文件。提交信息:
```bash
git commit -m "feat: complete mobile collection protocol parity for market roles"
```
## Task 5:复核并补齐预签约订单
- [ ] **Step 1:写缺口失败测试**
在 `presign-form-view.test.js`、`presign-detail-actions.test.js`、`presign-list-model.test.js` 中断言 Web 字段全集、订单删除/转正式、五个转账动作、附件即时上传、状态锁定及返回刷新。
- [ ] **Step 2:运行定向测试**
```bash
cd mobile-oa-h5 && node --test src/market/presign/*.test.js
cd backend && venv/bin/python -m pytest tests/test_market_presign_api.py tests/test_market_presign_mobile_actions.py -q
```
现有能力若已满足则新增测试直接通过;只有失败项才进入实现。
- [ ] **Step 3:按失败项最小补齐**
修改范围限定在 `views/market/presign/`、`market/presign/`、`modules/presign.js`、`services/market_presign.py` 和对应测试。不得更改财务确认/拒绝边界。
- [ ] **Step 4:复验并提交**
```bash
cd mobile-oa-h5 && node --test src/market/presign/*.test.js
cd backend && venv/bin/python -m pytest tests/test_market_presign_api.py tests/test_market_presign_mobile_actions.py -q
git commit -m "fix: close remaining mobile presign parity gaps"
```
## Task 6:完成缴费信息与催缴信息对齐
- [ ] **Step 1:写列表和详情失败测试**
`list-presenter.test.js` 断言缴费卡包含 Web 基础列、6 期 × 5 列和汇总列;催缴卡包含 13 个 Web 字段及催缴状态。`module-contracts.test.js` 断言催缴可跳协议和缴费期次详情,缴费仅开放登记/编辑/提交/撤回。
- [ ] **Step 2:写后端过滤及 DTO 失败测试**
`test_market_payment_api.py` 创建不同进度和应缴日期的方案,验证 Web 同口径过滤,并断言每一期返回 `dueAmount/dueDate/displayPaidAmount/payDate/arrearsAmount/transfers`。
- [ ] **Step 3:运行并确认失败点**
```bash
cd mobile-oa-h5 && node --test src/market/list-presenter.test.js src/market/module-contracts.test.js src/market/module-actions.test.js
cd backend && venv/bin/python -m pytest tests/test_market_payment_api.py -q
```
- [ ] **Step 4:实现共享期次/转账展示**
补齐 `_payment_dict` 和 `_period_dict`,让列表与详情使用同一 DTO;`payment.js` 显式展示所有转账字段和状态;催缴行的“已缴金额”可进入限定 `scheduleId` 的缴费详情,“协议单”进入协议只读详情。市场端仍不提供财务确认/拒绝、退费、开票和修改应缴计划。
- [ ] **Step 5:验证并提交**
```bash
cd mobile-oa-h5 && node --test src/market/list-presenter.test.js src/market/module-contracts.test.js src/market/module-actions.test.js src/market/api-contract.test.js
cd backend && venv/bin/python -m pytest tests/test_market_payment_api.py tests/test_market_field_scope.py -q
git commit -m "feat: align mobile payment and payment chase with web"
```
## Task 7:完成运费信息对齐
- [ ] **Step 1:写失败测试**
前端断言卡片与详情包含 Web 字段、筛选包含创建时间、编辑/关联/取消关联使用后端授权。后端创建包含范围内/范围外明细的共享批次,断言列表不泄露隐藏数量或人员,批次头编辑被拒绝但可对可见样本执行关联/取消。
- [ ] **Step 2:运行定向测试**
```bash
cd mobile-oa-h5 && node --test src/market/list-presenter.test.js src/market/module-contracts.test.js src/market/form-view-lifecycle.test.js
cd backend && venv/bin/python -m pytest tests/test_market_freight_scope.py -q
```
- [ ] **Step 3:补齐字段和交互**
`freight.js` 详情补齐创建/更新时间、备注和全部可见样本卡;`MarketModuleForm.vue` 保持新增、编辑、关联、取消四种模式;取消关联从详情完整 items 选择,不依赖全局第一页。后端 `sample_freight.py` 与 `market_freight.py` 继续软删除关联行并执行锁后 scope 复核。
- [ ] **Step 4:验证并提交**
```bash
cd mobile-oa-h5 && node --test src/market/list-presenter.test.js src/market/module-contracts.test.js src/market/form-view-lifecycle.test.js
cd backend && venv/bin/python -m pytest tests/test_market_freight_scope.py tests/test_market_field_scope.py -q
git commit -m "feat: complete mobile freight parity without scope leakage"
```
## Task 8:全量验证、知识图谱与完成矩阵
- [ ] **Step 1:运行前端市场全量测试和构建**
```bash
cd mobile-oa-h5
node --test src/market/**/*.test.js src/market/*.test.js
npm run build
```
预期:0 failed,Vite exit 0;既有 chunk 大小 warning 可记录但不能把错误当 warning 忽略。
- [ ] **Step 2:运行后端市场全量测试和静态检查**
```bash
cd backend
venv/bin/python -m pytest tests/test_market_*.py -q
venv/bin/python -m compileall -q app
venv/bin/python scripts/check_field_consistency.py
```
预期:pytest 0 failed、compileall exit 0;字段一致性无新增 error,既有 warning 单独记录。
- [ ] **Step 3:更新并验证后端知识图谱**
```bash
./scripts/update-graph.sh backend
```
检查 `backend/graphify-out/GRAPH_REPORT.md`、`graph.json` 和 manifest 已随最终源码更新。
- [ ] **Step 4:运行仓库差异检查**
```bash
git diff --check
git status --short
```
预期:`git diff --check` 无输出;状态中只包含本目标相关文件。
- [ ] **Step 5:写完成矩阵**
创建 `docs/superpowers/specs/2026-10-13-market-five-module-mobile-parity-completion.md`,逐模块列出 Web 字段、移动实现位置、后端字段来源、自动化测试证据和仍需测试环境执行的 375/390/430px 双角色验收项。不得把未执行的真实账号/微信 WebView 验收写成已通过。
- [ ] **Step 6:最终提交**
```bash
git add backend/graphify-out docs/superpowers/specs/2026-10-13-market-five-module-mobile-parity-completion.md
git commit -m "docs: record verified mobile market parity completion"
```
@@ -103,7 +103,7 @@
### 5.5 运费信息
筛选补齐关键词、运输方式、来源。列表与详情完整展示批次号、运输方式、运费金额、当前范围内关联数、来源、协议号、母亲姓名、子女姓名、样本编码、样本类型、创建人、创建时间、更新时间和备注。
筛选补齐关键词、运输方式、来源、创建时间范围。列表与详情完整展示批次号、运输方式、运费金额、当前范围内关联数、来源、协议号、母亲姓名、子女姓名、样本编码、样本类型、创建人、创建时间、更新时间和备注。
开放动作:新增运费批次、编辑批次、关联样本、取消关联。关联与取消关联使用移动卡片多选。共享批次继续执行既有脱敏和写权限规则,不泄露范围外明细或数量。