26 KiB
Market Presign Mobile Parity 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 端字段和业务操作,并以 Vant 4、工作流移动端风格实现白色顶部栏、卡片列表、触底分页及移动表单。
Architecture: 预签约订单使用专用列表和专用表单组件,复用现有市场路由上下文、鉴权、HTTP、错误处理与现场 scope;后端在既有 /mini/market/{specialist|manager} 固定路由下补齐删除订单和意向金转账操作。复杂表单状态拆到纯 JavaScript 模块中先做单元测试,Vue 组件只负责 Vant 展示与事件编排。
Tech Stack: Python 3.9、FastAPI、SQLAlchemy、Pydantic v2、pytest、Vue 3、Vant 4、Vite、Node node:test
文件结构
后端
- Modify:
backend/app/schemas/market_mobile.py— 收紧预签订单/转账 DTO,并提供移动转账请求体。 - Modify:
backend/app/services/market_presign.py— scope 内订单删除、转账增改删、提交、撤回。 - Modify:
backend/app/services/market_support.py— 预签/转账动作白名单与表单选项。 - Modify:
backend/app/api/market_mobile.py— 暴露移动端删除与转账路由。 - Modify:
backend/tests/test_market_presign_api.py— 服务层权限、状态机、附件与软删除测试。 - Modify:
backend/tests/test_market_support_api.py— 动作白名单和选项返回测试。
mobile-oa-h5
- Modify:
mobile-oa-h5/src/api/market.js— 预签删除、转账增改删、提交、撤回 API。 - Modify:
mobile-oa-h5/src/market/action-policy.js—delete与预签转账动作白名单。 - Modify:
mobile-oa-h5/src/market/api-contract.test.js— URL 与 HTTP 方法契约。 - Modify:
mobile-oa-h5/src/market/module-actions.test.js— 前后端动作交集测试。 - Create:
mobile-oa-h5/src/market/presign/presign-presenter.js— 列表字段、状态、金额、日期格式化。 - Create:
mobile-oa-h5/src/market/presign/presign-presenter.test.js— 展示逻辑测试。 - Create:
mobile-oa-h5/src/market/presign/presign-form-state.js— 表单默认值、级联清理、转账锁定和 payload。 - Create:
mobile-oa-h5/src/market/presign/presign-form-state.test.js— 表单纯逻辑测试。 - Create:
mobile-oa-h5/src/market/components/PresignOrderCard.vue— Web 字段完整卡片及操作按钮。 - Create:
mobile-oa-h5/src/market/components/PresignSelectField.vue— Vant Field + Popup/Picker 单选。 - Create:
mobile-oa-h5/src/market/components/PresignAttachmentUploader.vue— 上传触发卡与附件卡片。 - Create:
mobile-oa-h5/src/market/components/PresignTransferCard.vue— 意向金转账卡片及状态操作。 - Create:
mobile-oa-h5/src/views/market/presign/PresignOrderList.vue— 白色顶部栏、搜索、触底分页和悬浮按钮。 - Create:
mobile-oa-h5/src/views/market/presign/PresignOrderForm.vue— Web 字段完整新增/编辑页。 - Modify:
mobile-oa-h5/src/market/routes.js— 仅预签约订单切到专用列表/表单。 - Modify:
mobile-oa-h5/src/styles/market.css— 预签专用移动样式,品牌主色#0a9f6d。
Task 1: 后端预签操作状态机与 scope 校验
Files:
-
Modify:
backend/app/schemas/market_mobile.py -
Modify:
backend/app/services/market_presign.py -
Modify:
backend/tests/test_market_presign_api.py -
Step 1: 写订单删除和转账越权的失败测试
在 backend/tests/test_market_presign_api.py 增加:
def test_specialist_can_only_delete_own_non_converted_presign():
from app.services.market_presign import delete_presign_order
db = _session()
db.add_all([
_order('own', sales_user_id='u1'),
_order('other', sales_user_id='u2'),
])
db.commit()
delete_presign_order(db, order_id='own', ctx=_ctx('specialist'), user_info=_user())
assert db.query(BizPresignOrder).filter_by(id='own').one().del_flag == 1
with pytest.raises(HTTPException):
delete_presign_order(db, order_id='other', ctx=_ctx('specialist'), user_info=_user())
def test_presign_transfer_rejects_other_scope_order():
from app.schemas.market_mobile import MarketPresignTransferBody
from app.services.market_presign import create_presign_transfer
db = _session()
db.add(_order('other', sales_user_id='u2'))
db.commit()
with pytest.raises(HTTPException):
create_presign_transfer(
db,
order_id='other',
body=MarketPresignTransferBody(transferAmount=100, transferType='wxzz'),
ctx=_ctx('specialist'),
user_info=_user(),
)
- Step 2: 运行失败测试
Run:
cd backend && source venv/bin/activate && pytest tests/test_market_presign_api.py -k 'delete_own or transfer_rejects' -q
Expected: FAIL,提示 delete_presign_order / create_presign_transfer 尚不存在。
- Step 3: 收紧 DTO 并实现订单删除和 scoped transfer 查询
在 backend/app/schemas/market_mobile.py 将预签 DTO 改为拒绝未知字段,并限制字段长度:
class MarketPresignTransferBody(BaseModel):
model_config = ConfigDict(extra='forbid', populate_by_name=True)
transfer_amount: Optional[MarketPaymentAmount] = Field(None, alias='transferAmount')
transfer_time: Optional[str] = Field(None, alias='transferTime')
transfer_type: Optional[str] = Field(None, max_length=16, alias='transferType')
transfer_attachments: Optional[str] = Field(None, max_length=4000, alias='transferAttachments')
remark: Optional[str] = Field(None, max_length=512)
MarketPresignOrderBody 同时加入 Web 已开放的订单状态字段:
order_status: Optional[Literal['draft', 'active', 'converted', 'void']] = Field(
None, alias='orderStatus'
)
移动 service 的更新规则与 Web 对齐:仅 converted 禁止保存;void 允许再次编辑,但禁止转正式。
在 backend/app/services/market_presign.py 增加:
def delete_presign_order(db, *, order_id, ctx, user_info):
row = assert_presign_access(db, order_id, ctx, user_info=user_info)
if (row.order_status or '').strip() == 'converted':
raise ValueError('已转正式的预签约订单不可删除')
presign_crud.soft_delete_presign_order(
db, order_id=row.id, tenant_no=ctx.tenant_no, user_id=ctx.user_id
)
def _scoped_transfer(db, *, transfer_id, ctx, user_info):
transfer = (
db.query(BizPresignTransfer)
.filter(
BizPresignTransfer.id == transfer_id,
BizPresignTransfer.tenant_no == ctx.tenant_no,
BizPresignTransfer.del_flag == 0,
)
.first()
)
if transfer is None:
raise ValueError('意向金转账不存在')
order = assert_presign_access(db, transfer.presign_order_id, ctx, user_info=user_info)
return transfer, order
- Step 4: 实现转账增改删、提交和撤回
继续在 market_presign.py 增加 create_presign_transfer、update_presign_transfer、delete_presign_transfer、submit_presign_transfer、withdraw_presign_transfer;每个函数先调用 assert_presign_access 或 _scoped_transfer,只允许订单状态 ''/draft/active。附件写入前调用:
fields['transfer_attachments'] = validate_attachment_refs(
db,
fields.get('transfer_attachments'),
module='presign',
ctx=ctx,
creating=creating,
business_id=order.id,
)
删除转账必须满足 pay_status == 'dfj',以 del_flag=1、update_by、update_time 软删除;提交调用 presign_crud.submit_receipt_finance,撤回调用 presign_crud.withdraw_receipt。
- Step 5: 跑预签服务测试
Run:
cd backend && source venv/bin/activate && pytest tests/test_market_presign_api.py -q
Expected: 全部 PASS。
- Step 6: 提交后端服务层
git add backend/app/schemas/market_mobile.py backend/app/services/market_presign.py backend/tests/test_market_presign_api.py
git commit -m "feat: enforce scoped presign deletion and transfer workflows on mobile."
Task 2: 后端移动路由、动作白名单与选项
Files:
-
Modify:
backend/app/api/market_mobile.py -
Modify:
backend/app/services/market_support.py -
Modify:
backend/tests/test_market_support_api.py -
Step 1: 写动作白名单失败测试
def test_presign_actions_include_delete_and_nested_transfer_actions():
active = decorate_allowed_actions(
'presign',
{
'orderStatus': 'active',
'transfers': [
{'id': 't1', 'payStatus': 'dfj'},
{'id': 't2', 'payStatus': 'jfqrz'},
{'id': 't3', 'payStatus': 'yjf'},
],
},
scope_kind='specialist',
detail=True,
)
assert active['allowedActions'] == ['edit', 'convert', 'delete']
assert [row['allowedActions'] for row in active['transfers']] == [
['edit', 'delete', 'submit'], ['withdraw'], []
]
同一测试还需断言:void -> ['edit', 'delete'],converted -> ['edit'];已转正式的“编辑”只用于打开只读表单,不允许提交保存。
- Step 2: 运行并确认失败
Run:
cd backend && source venv/bin/activate && pytest tests/test_market_support_api.py -k presign_actions -q
Expected: FAIL,当前 presign 没有 delete,转账行没有 allowedActions。
- Step 3: 补齐动作装饰与表单选项
在 allowed_actions() 中使用:
if module == 'presign':
if state == 'converted':
return ['edit']
if state == 'void':
return ['edit', 'delete']
return ['edit', 'convert', 'delete']
if module == 'presign-transfer':
if state == 'dfj':
return ['edit', 'delete', 'submit']
if state == 'jfqrz':
return ['withdraw']
return []
decorate_allowed_actions('presign', ..., detail=True) 必须逐笔装饰 transfers。get_form_options(module='presign') 增加:
'payModes': [
{'label': '趸交', 'value': 'dj'},
{'label': '分期', 'value': 'fq'},
],
'orderStatuses': [
{'label': '草稿', 'value': 'draft'},
{'label': '进行中', 'value': 'active'},
{'label': '已转正式', 'value': 'converted'},
{'label': '已作废', 'value': 'void'},
],
'transferTypes': _dict_options(db, ctx.tenant_no, 'zzfs'),
地区人员选项不能继续只返回 SysUser;查询需同时带出 BizCollectionRegionStaff.is_leader,返回:
{
'label': user.nick_name,
'value': user.id,
'isLeader': bool(staff.is_leader),
}
所选地区存在多个绑定时,负责人排在普通人员之前。上传校验需把预签模块单文件上限对齐 Web 的 20MiB:save_market_attachment() 调用 _validate_upload(..., max_bytes=20 * 1024 * 1024);其他市场模块继续保持现有 10MiB,避免扩大无关范围。
- Step 4: 注册移动路由
在 _register_presign_routes() 中增加以下固定路径,均复用当前 permission_guard:
DELETE {prefix}/{order_id}
POST {prefix}/{order_id}/transfers
PUT /mini/market/{scope_kind}/presign-transfers/{transfer_id}
DELETE /mini/market/{scope_kind}/presign-transfers/{transfer_id}
POST /mini/market/{scope_kind}/presign-transfers/{transfer_id}/submit
POST /mini/market/{scope_kind}/presign-transfers/{transfer_id}/withdraw
每个路由只调用 Task 1 的 service 函数,并把 PermissionError 转为 403、ValueError 转为 400。修正该函数中重复的 data = market_presign.create_presign_order( 行。
- Step 5: 运行后端相关测试
cd backend && source venv/bin/activate && pytest tests/test_market_presign_api.py tests/test_market_support_api.py tests/test_market_mobile_permissions.py -q
Expected: 全部 PASS。
- Step 6: 提交移动路由
git add backend/app/api/market_mobile.py backend/app/services/market_support.py backend/tests/test_market_support_api.py
git commit -m "feat: expose complete scoped presign actions to mobile clients."
Task 3: 前端 API 与纯业务状态
Files:
-
Modify:
mobile-oa-h5/src/api/market.js -
Modify:
mobile-oa-h5/src/market/action-policy.js -
Modify:
mobile-oa-h5/src/market/api-contract.test.js -
Modify:
mobile-oa-h5/src/market/module-actions.test.js -
Create:
mobile-oa-h5/src/market/presign/presign-presenter.js -
Create:
mobile-oa-h5/src/market/presign/presign-presenter.test.js -
Create:
mobile-oa-h5/src/market/presign/presign-form-state.js -
Create:
mobile-oa-h5/src/market/presign/presign-form-state.test.js -
Step 1: 写 API 和动作契约失败测试
在 api-contract.test.js 断言:
const presign = { marketRole: 'specialist', module: 'presign' }
assert.equal(buildMarketActionPath(presign, 'delete', 'o 1'), '/mini/market/specialist/presign-orders/o%201')
assert.equal(buildMarketActionPath(presign, 'create-transfer', 'o1'), '/mini/market/specialist/presign-orders/o1/transfers')
assert.equal(buildMarketActionPath(presign, 'submit-transfer', 't1'), '/mini/market/specialist/presign-transfers/t1/submit')
assert.equal(marketActionMethod('delete'), 'DELETE')
assert.equal(marketActionMethod('edit-presign-transfer'), 'PUT')
在 module-actions.test.js 断言预签白名单包含 delete/create-transfer/edit-presign-transfer/delete-transfer/submit-transfer/withdraw-transfer,并拒绝后端未授权动作。
- Step 2: 运行失败测试
cd mobile-oa-h5 && node --test src/market/api-contract.test.js src/market/module-actions.test.js
Expected: FAIL,动作和 URL 映射尚未实现。
- Step 3: 实现 API 映射和请求函数
buildMarketActionPath() 按 Task 2 的固定端点映射;marketActionMethod() 返回 DELETE/PUT/POST。导出语义化包装:
export const deletePresignOrder = (context, id, options) =>
performMarketAction(context, 'delete', { id }, options)
export const createPresignTransfer = (context, orderId, data, options) =>
performMarketAction(context, 'create-transfer', { id: orderId, data }, options)
export const updatePresignTransfer = (context, transferId, data, options) =>
performMarketAction(context, 'edit-presign-transfer', { id: transferId, data }, options)
- Step 4: 写展示与表单纯逻辑测试
覆盖:8 位日期转 YYYY-MM-DD、金额、Web 状态文案、跨页序号、地区变化清空医院、默认表单、转账状态锁定、payload 排除财务只读字段。
assert.equal(formatPresignDate('20261011'), '2026-10-11')
assert.equal(formatPresignMoney(28800), '¥28,800.00')
assert.equal(presignCardIndex(2, 10, 3), '13')
assert.deepEqual(changePresignRegion({ regionId: 'r1', hospitalId: 'h1' }, 'r2'), {
regionId: 'r2', hospitalId: ''
})
assert.equal(isPresignTransferLocked({ payStatus: 'jfqrz' }), true)
- Step 5: 实现纯函数并运行测试
presign-presenter.js 只负责格式化;presign-form-state.js 导出 createPresignForm、changePresignRegion、isPresignLocked、isPresignTransferLocked、buildPresignOrderPayload、buildPresignTransferPayload。
cd mobile-oa-h5 && node --test src/market/presign/*.test.js src/market/api-contract.test.js src/market/module-actions.test.js
Expected: 全部 PASS。
- Step 6: 提交前端基础契约
git add mobile-oa-h5/src/api/market.js mobile-oa-h5/src/market/action-policy.js mobile-oa-h5/src/market/*.test.js mobile-oa-h5/src/market/presign
git commit -m "feat: add tested mobile presign contracts and form state."
Task 4: 专用卡片列表与滚动分页
Files:
-
Create:
mobile-oa-h5/src/market/components/PresignOrderCard.vue -
Create:
mobile-oa-h5/src/views/market/presign/PresignOrderList.vue -
Modify:
mobile-oa-h5/src/market/routes.js -
Modify:
mobile-oa-h5/src/styles/market.css -
Modify:
mobile-oa-h5/src/market/routes.test.js -
Step 1: 写预签专用路由失败测试
导出 marketListComponentKey(module) / marketFormComponentKey(module) 并断言:
assert.equal(marketListComponentKey('presign'), 'presign-list')
assert.equal(marketListComponentKey('payment'), 'generic-list')
assert.equal(marketFormComponentKey('presign'), 'presign-form')
- Step 2: 运行失败测试
cd mobile-oa-h5 && node --test src/market/routes.test.js
Expected: FAIL,路由选择函数不存在。
- Step 3: 创建 Web 字段完整卡片
PresignOrderCard.vue 必须显示:序号、业务日期、协议编号、母亲姓名、预产期、合同金额、到账金额、状态、创建时间;操作区使用:
<van-button v-if="can('edit')" size="small" @click.stop="$emit('edit', item)">编辑</van-button>
<van-button v-if="can('convert')" size="small" type="primary" @click.stop="$emit('convert', item)">转正式</van-button>
<van-button v-if="can('delete')" size="small" type="danger" plain @click.stop="$emit('delete', item)">删除</van-button>
所有按钮最小高度 36px,整行操作区域触控高度不小于 44px。
- Step 4: 创建列表页
PresignOrderList.vue 使用:
<van-nav-bar title="预签约订单" left-arrow fixed placeholder @click-left="backToHost" />
<van-search v-model="keyword" placeholder="搜索母亲姓名 / 协议编号" @search="reload" @clear="reload" />
<van-list v-model:loading="loadingMore" :finished="finished" finished-text="已经到底了" @load="loadMore">
<PresignOrderCard v-for="(item, index) in items" :key="item.id" :item="item" :index="index" />
</van-list>
<van-floating-bubble v-if="canCreate" axis="xy" icon="plus" magnetic="x" @click="openCreate" />
首次加载沿用 request-state.js;搜索重置页码,触底追加;删除和转正式用 showConfirmDialog,成功后重载第一页。
- Step 5: 路由仅替换预签组件
在 routes.js 中按 module === 'presign' 选择专用 list/form,其余模块保持现状;保留 specialist/manager 两套路由和 meta,不合并 scope。
- Step 6: 运行测试和构建
cd mobile-oa-h5 && node --test src/market/routes.test.js src/market/request-state.test.js && npm run build
Expected: 测试全部 PASS,Vite build 成功。
- Step 7: 提交列表页
git add mobile-oa-h5/src/market/components/PresignOrderCard.vue mobile-oa-h5/src/views/market/presign/PresignOrderList.vue mobile-oa-h5/src/market/routes.js mobile-oa-h5/src/market/routes.test.js mobile-oa-h5/src/styles/market.css
git commit -m "feat: replace mobile presign list with scroll-loaded parity cards."
Task 5: Vant 选择器、附件与转账卡片
Files:
-
Create:
mobile-oa-h5/src/market/components/PresignSelectField.vue -
Create:
mobile-oa-h5/src/market/components/PresignAttachmentUploader.vue -
Create:
mobile-oa-h5/src/market/components/PresignTransferCard.vue -
Create:
mobile-oa-h5/src/market/presign/presign-components-contract.test.js -
Step 1: 写组件契约失败测试
读取三个 SFC 源文件并断言它们分别包含 van-popup/van-picker、van-uploader、allowedActions 与 44px 触控约束;同时断言上传组件没有原生可见 <select>。
- Step 2: 运行失败测试
cd mobile-oa-h5 && node --test src/market/presign/presign-components-contract.test.js
Expected: FAIL,组件文件不存在。
- Step 3: 实现单选与多选移动组件
PresignSelectField.vue 使用只读 van-field 打开底部 van-popup。单选用 van-picker;开启 searchable 时顶部显示 van-search;多选使用 van-checkbox-group,点击“完成”一次性提交选中值,取消不修改外部值。
- Step 4: 实现附件上传卡片
PresignAttachmentUploader.vue 使用 van-uploader :after-read="afterRead",上传触发卡固定为附件行第一项;图片 object-fit: cover,非图片显示 description 图标;支持预览、下载、删除,上传中阻止表单提交。
- Step 5: 实现转账卡片
PresignTransferCard.vue 组合转账方式选择器、金额、时间、附件、备注、财务备注、状态 Tag;按钮完全以行级 allowedActions 决定,分别 emit remove/submit/withdraw/change/upload。
- Step 6: 运行组件契约测试
cd mobile-oa-h5 && node --test src/market/presign/presign-components-contract.test.js
Expected: PASS。
- Step 7: 提交表单组件
git add mobile-oa-h5/src/market/components/PresignSelectField.vue mobile-oa-h5/src/market/components/PresignAttachmentUploader.vue mobile-oa-h5/src/market/components/PresignTransferCard.vue mobile-oa-h5/src/market/presign/presign-components-contract.test.js
git commit -m "feat: add reusable Vant controls for mobile presign forms."
Task 6: 完整新增 / 编辑表单
Files:
-
Create:
mobile-oa-h5/src/views/market/presign/PresignOrderForm.vue -
Modify:
mobile-oa-h5/src/market/routes.js -
Modify:
mobile-oa-h5/src/styles/market.css -
Create:
mobile-oa-h5/src/market/presign/presign-form-view.test.js -
Step 1: 写表单字段与级联契约失败测试
断言 SFC 包含 Web 的 14 个主字段 key、loadRegionOptions、onRegionChange、loadHospitals、loadRegionManager、transfers 和固定底栏;断言没有普通 HTML <select>。
- Step 2: 运行失败测试
cd mobile-oa-h5 && node --test src/market/presign/presign-form-view.test.js
Expected: FAIL,专用表单尚不存在。
- Step 3: 实现首次加载和编辑回填
并行加载基础 form-options 与详情;编辑时按“回填地区 → 用 regionId 再取医院/地区人员 → 回填医院”的顺序。新增默认业务日期为当天、采集专员为当前用户、缴费方式 dj、订单状态 active。
- Step 4: 实现地区医院联动
onRegionChange(regionId) 必须先:
form.value = changePresignRegion(form.value, regionId)
await loadRegionOptions(regionId)
const leader = formOptions.value.staff?.find(item => item.isLeader) || formOptions.value.staff?.[0]
form.value.regionManagerUserId = leader?.value || ''
请求失败时医院保持空值并 Toast,不恢复旧地区的医院。
- Step 5: 实现保存与转账操作
保存前校验母亲姓名和至少一个样本类型;新订单把未保存转账放进 transfers 一次提交;编辑订单先保存主单,再顺序创建或更新 dfj 转账。提交、撤回和删除转账均弹确认框,成功后重拉详情。
- Step 6: 实现离开保护与上传并发保护
复用 canLeaveMarketForm;dirty || uploadPending > 0 时离开需确认;上传未结束时禁用确定按钮;组件卸载时终止所有 AbortController。
- Step 7: 运行表单测试与构建
cd mobile-oa-h5 && node --test src/market/presign/*.test.js && npm run build
Expected: 测试全部 PASS,Vite build 成功。
- Step 8: 提交表单页
git add mobile-oa-h5/src/views/market/presign/PresignOrderForm.vue mobile-oa-h5/src/market/routes.js mobile-oa-h5/src/styles/market.css mobile-oa-h5/src/market/presign/presign-form-view.test.js
git commit -m "feat: deliver Web-parity presign editing with mobile interactions."
Task 7: 综合验证、知识图谱与文档收口
Files:
-
Modify:
backend/graphify-out/*— 由脚本生成。 -
Modify:
docs/superpowers/specs/2026-10-11-market-presign-mobile-parity-design.md— 状态改为已实现并记录验证结果。 -
Step 1: 运行前端全部单元测试
cd mobile-oa-h5 && node --test src/**/*.test.js
Expected: 全部 PASS。
- Step 2: 运行前端生产构建
cd mobile-oa-h5 && npm run build
Expected: Vite build 成功,无 unresolved import。
- Step 3: 运行字段一致性检查
cd backend && source venv/bin/activate && python scripts/check_field_consistency.py
Expected: PASS;若脚本只覆盖 Web CRUD,应记录为“不适用移动专用 schema”,但不得忽略真实字段缺失。
- Step 4: 运行后端相关测试
cd backend && source venv/bin/activate && pytest tests/test_market_presign_api.py tests/test_market_support_api.py tests/test_market_mobile_permissions.py -q
Expected: 全部 PASS。
- Step 5: 运行后端全量测试
cd backend && source venv/bin/activate && pytest -q
Expected: 全部 PASS。
- Step 6: 更新后端知识图谱
./scripts/update-graph.sh backend
Expected: backend/graphify-out/ 更新成功;本次不修改 web-admin,无需运行 frontend 图谱。
- Step 7: 浏览器移动宽度验收
启动 mobile-oa-h5,以 390×844 视口检查:白色顶部栏、搜索、三张以上卡片、悬浮按钮、触底加载、地区医院级联、附件卡片、转账状态按钮和底部保存栏;保存截图供验收。
- Step 8: 更新设计状态并提交收口
在设计文档顶部把状态改为“已实现”,追加真实测试数量、构建结果与截图位置,然后:
git add backend/graphify-out docs/superpowers/specs/2026-10-11-market-presign-mobile-parity-design.md
git commit -m "docs: record verified mobile presign parity implementation."
自检结果
- 规格覆盖:白色顶部栏、仅关键词、悬浮新增、Web 列表字段与操作、动态分页、完整表单、地区医院级联、数据库选项、附件卡、转账状态机、双角色 scope 均有对应任务。
- 非目标控制:未安排其他市场模块改造,未修改 Web 管理端,未把业务表单下沉到
mobile-h5。 - 类型一致:前端统一使用
delete/create-transfer/edit-presign-transfer/delete-transfer/submit-transfer/withdraw-transfer;后端对应固定端点与allowedActions。 - 验证闭环:纯函数、API 契约、组件契约、相关后端测试、全量后端、前端构建与浏览器验收全部包含。