2026-10-13-market-five-module-mobile-parity.md 20 KB

市场五模块小程序与 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:

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:运行测试并确认按预期失败
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:运行协议定向测试

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:提交
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,覆盖:

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:运行测试并确认模块不存在
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:

// 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 契约测试
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:提交
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 失败测试

在现有测试中新增参数透传和真实查询断言:

@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 参数或过滤行为
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:运行后端市场列表测试

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:提交
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:运行当前协议相关测试,保留真实失败清单
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:完成现有在制代码并修复失败测试

收敛以下文件中的既有改动,不重写已通过逻辑:

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:验证协议前后端
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:提交协议在制改动

仅添加上述协议相关文件及对应测试,不提交无关工作区文件。提交信息:

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:运行定向测试
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:复验并提交
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:运行并确认失败点
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:验证并提交
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:运行定向测试
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:验证并提交
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:运行前端市场全量测试和构建
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:运行后端市场全量测试和静态检查
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:更新并验证后端知识图谱
./scripts/update-graph.sh backend

检查 backend/graphify-out/GRAPH_REPORT.md、graph.json 和 manifest 已随最终源码更新。

  • Step 4:运行仓库差异检查
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:最终提交
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"