319 lines
26 KiB
Markdown
319 lines
26 KiB
Markdown
# 个人开户移动端H5确认页 设计文档
|
||
|
||
## 背景
|
||
|
||
`docs/superpowers/specs/2026-07-23-personal-open-confirm-modal-design.md` 已实现管理后台侧"个人开户"新增表单的二次确认弹窗 + "邀请扫码办理",柜员点击后生成二维码,二维码内容为:
|
||
|
||
```
|
||
${VITE_H5_QRCODE_BASE_URL}${VITE_H5_QRCODE_PATH}?applicationNo=${applicationNo}
|
||
```
|
||
|
||
当前 `VITE_H5_QRCODE_PATH=/mobile/personal-open`。该设计文档明确将"扫码后跳转的移动端 H5 页面"排除在范围外。后端已提供对应的 `personal-opening/mobile/draft-detail`、`personal-opening/mobile/submit` 接口(见《缺失的后端接口.md》第19条),但前端从未实现这个 H5 页面。本次设计即补齐该页面。
|
||
|
||
## 范围
|
||
|
||
- 新建一个**完全独立的子工程** `./fryl_h5`(独立 `package.json`/构建产物/部署单元,不与管理后台共用依赖或构建配置)。
|
||
- 仅实现"个人开户"扫码核实场景一个页面流程,不涉及企业开户 confirm、贷款申请确认扫码等其他移动端确认场景。
|
||
- 不涉及管理后台侧代码改动(二维码生成逻辑已完成,本次不改)。
|
||
|
||
## 技术方案
|
||
|
||
- **技术栈**:Vue3 + Vite + Vant(移动端 UI 组件库),纯 JavaScript(与主项目一致,不引入 TypeScript)。
|
||
- **工程定位**:面向终端客户、无需登录鉴权的匿名 H5 页面(不同于管理后台的员工登录体系),因此不引入 Pinia/auth store/refresh-token 逻辑。
|
||
- **路由**:`vue-router`,单一业务路由 `/mobile/personal-open`(与二维码 `VITE_H5_QRCODE_PATH` 保持一致),通过 query 参数 `applicationNo` 定位申请单。页面内部用组件本地状态(`currentStep` ref,值为 `INFO`/`FACE`/`SMS`)维护向导步骤,不为每个步骤单独设路由(避免用户直接分享/刷新中间步骤 URL 导致状态错乱)。
|
||
- **BASE_URL**:不提供页面内可配置入口(用户明确决定,区别于主项目登录页齿轮图标切换的默认惯例),仅通过 `.env.development`/`.env.production` 的 `VITE_API_BASE_URL` 区分环境。
|
||
|
||
## 页面流程
|
||
|
||
**2026-08-05 更新**:原"步骤1 信息核对(含协议展示)"拆分为两个独立步骤,协议勾选不再耦合在
|
||
信息核对页面里,改为单独的"相关协议"步骤,且该步骤调用真实后端接口获取协议模板原文(不再是
|
||
占位文案)。整体变为 **5 步向导**:
|
||
|
||
```
|
||
进入页面 → 调用 draft-detail 加载草稿
|
||
├─ 失败/无效 applicationNo → 展示错误提示页(终态,无法继续)
|
||
└─ 成功 → 步骤1 信息核对 → 步骤2 相关协议 → 步骤3 人脸识别 → 步骤4 手机验证+提交 → 步骤5 结果页(终态)
|
||
```
|
||
|
||
**2026-08-05 二次更新**:按产品要求,客户在步骤2"相关协议"点击"下一步"后**跳过步骤3人脸识别,
|
||
直接进入步骤4手机验证+提交**:
|
||
|
||
```
|
||
进入页面 → 调用 draft-detail 加载草稿
|
||
├─ 失败/无效 applicationNo → 展示错误提示页(终态,无法继续)
|
||
└─ 成功 → 步骤1 信息核对 → 步骤2 相关协议 → 步骤4 手机验证+提交 → 步骤5 结果页(终态)
|
||
(步骤3 人脸识别:当前从向导流程跳过,代码未删除,见下方"步骤3"章节说明)
|
||
```
|
||
|
||
`PersonalOpenMobile.vue` 里 `AgreementStep` 的 `@next` 直接指向 `currentStep = 'SMS'`,
|
||
`SmsVerifyStep` 的 `@prev` 直接指向 `currentStep = 'AGREEMENT'`;`FaceCompareStep.vue` 组件、
|
||
`faceRecognizeApi` 接口封装、`facePhoto`/`faceVerified` 状态均**未删除**,只是不再被引用/声明,
|
||
如需恢复该环节,把上述两个事件改回指向 `'FACE'` 并重新引入组件即可(`PersonalOpenMobile.vue`
|
||
里保留了具体恢复步骤的代码注释)。
|
||
|
||
步骤1/2/4之间支持"上一步"返回(步骤3当前不在流程内,故不涉及);步骤5(结果页)为终态,
|
||
不支持返回上一步。页面整体视觉仍为
|
||
"每步骤单独全屏展示,点击下一步整页切换"(`van-nav-bar` + 组件内 `currentStep` 切换),**不是**
|
||
产品截图里"顶部业务确认banner + 左侧1-5编号竖向步骤条、未激活步骤仅显示标题"的单页累积式布局
|
||
——本次改动经与产品确认,只对齐内容/交互逻辑,不做整体视觉布局改版。
|
||
|
||
### 步骤0:加载态 / 错误态
|
||
|
||
- 页面挂载时读取 query 中的 `applicationNo`,为空则直接展示"链接无效"错误态(不发请求)。
|
||
- 调用 `mobile/draft-detail` 接口:
|
||
- `code === 200`:进入步骤1。
|
||
- `code !== 200`(如申请单不存在/已过期/已提交过):展示错误提示页,用 `message` 原文展示,提供"关闭页面"提示文案(无可操作按钮,H5 场景下没有其它页面可跳转)。
|
||
|
||
### 步骤1:信息核对(**2026-08-05 更新**:不再包含协议展示/勾选,协议已独立为步骤2)
|
||
|
||
展示字段(与管理后台"个人开户申请详情"现有展示字段保持一致,全部只读):
|
||
|
||
| 字段 | 来源 |
|
||
| --- | --- |
|
||
| 客户姓名 | customerName |
|
||
| 证件类型 | certificateType |
|
||
| 证件号码 | certificateNumber |
|
||
| 证件有效期起 | certificateEffectiveDate |
|
||
| 证件失效日期 | certificateExpiryDate |
|
||
| 签发机关 | issuingAuthority |
|
||
| 证件地址 | certificateAddress |
|
||
| 性别 | gender |
|
||
| 民族 | ethnic |
|
||
| 职业 | occupation(+ `occupationNote`,当 occupation 为"其他"时展示备注) |
|
||
| 银行卡号 | bankCardNumber |
|
||
| 手机号 | mobilePhoneMask(**2026-07-31 更新**:后端直接提供掩码字段,前端不再自行计算,见接口契约章节) |
|
||
|
||
不展示证件照片本身(客户本人无需再核对自己的证件照片图像,且原始图片以文件形式存放不适合在此场景重复暴露)、不展示 `applicationStatus`/`failReason`/`mainAccountNo`/`customerNo`(这些是提交后的结果字段,核对阶段不应出现)。
|
||
|
||
点击"确认无误,下一步"进入步骤2(**2026-08-05 更新**:本步骤不再包含协议勾选/占位文案,
|
||
按钮不再有任何禁用条件,点击直接进入步骤2)。
|
||
|
||
### 步骤2:相关协议(**2026-08-05 新增,独立步骤,接入真实协议模板接口**)
|
||
|
||
- 展示2份协议的"我已阅读并同意"复选框:《浙江稠州商业银行用户支付服务协议》
|
||
(`PAY_SERVICE_AGREEMENT`)、《浙江稠州商业银行用户支付服务隐私政策》(`PAY_SERVICE_PRIVACY`)。
|
||
取值来源于新增的 `BankAgreementFileTypeEnum`(该枚举共4项,另外2项
|
||
`CREDIT_REPORT_AUTH`个人信用信息报送查询使用授权书/`THIRD_PARTY_DATA_AUTH`第三方数据信息
|
||
查询和使用授权书**不在本页面展示范围内**——与产品确认,个人开户场景只需前2项)。
|
||
- 点击协议标题链接时调用 `GET /api/customer-info/bank-agreement/base64?fileType=xxx`
|
||
拉取协议模板文件(base64 编码的 PDF),**不能直接拼成 `data:application/pdf;base64,...`
|
||
赋给 iframe 的 `src`**——真实后端联调时实测 Safari/WebKit 会拦截 iframe 对 `data:` URI 的
|
||
导航(表现为接口正常返回 `fileContent`,但预览区域空白,Chrome/安卓不受影响),必须把
|
||
base64 转成 `Blob` 再用 `URL.createObjectURL()` 生成 `blob:` URL 赋给 iframe(**2026-08-05
|
||
二次更新**,见 `AgreementStep.vue` 的 `base64ToBlobUrl` 函数),组件卸载时统一
|
||
`revokeObjectURL` 释放内存。用 `van-popup` 内嵌该 iframe 展示原文;同一份协议重复点击时
|
||
使用本地缓存(缓存的是 blob: URL),不重复请求/解码。
|
||
- 两份协议均勾选后"下一步"按钮才可点击,未勾选禁用;底部提供"上一步"文字链接返回步骤1
|
||
(协议勾选状态本次不做跨步骤保留,退回再进入需重新勾选,与产品截图行为一致——截图未展示
|
||
该步骤支持"上一步",此处出于与其它步骤一致的返回体验保留了该入口,纯前端交互决策,
|
||
不影响提交参数)。
|
||
- 点击"下一步"进入步骤3(人脸对比,**2026-08-05 二次更新:当前从流程中跳过,实际直接进入
|
||
步骤4**,见下方说明)。
|
||
- **2026-08-06 更新**:两份协议都勾选后,自动在《用户支付服务协议》PDF末页填入客户姓名/证件
|
||
号码/当前日期并调用通用文件上传接口拿到 `fileNo`,"下一步"在文件生成并上传成功前保持禁用;
|
||
该 `fileNo` 会随最终提交一起传给后端(`mobile/submit` 新增 `payAgreementFileNo` 字段)。
|
||
详细方案(PDF坐标测量方法、中文字体渲染的两个已知 bug 及规避方式、mock-server 配套改造)见
|
||
独立设计文档 `docs/superpowers/specs/2026-08-06-agreement-pdf-fill-and-upload-design.md`,
|
||
接口契约缺口见《缺失的后端接口.md》Part 19。
|
||
|
||
### 步骤3:人脸对比(**2026-08-05 二次更新:按产品要求,当前从向导流程中跳过,代码保留未删除**)
|
||
|
||
> `AgreementStep`(步骤2)点击"下一步"后直接进入步骤4(手机验证),不再经过本步骤;
|
||
> `FaceCompareStep.vue`、`faceRecognizeApi`、下方描述的识别逻辑均**未删除**,只是暂未被
|
||
> `PersonalOpenMobile.vue` 引用/挂载。以下为该步骤原有设计描述,供后续恢复时参考:
|
||
|
||
- 提供"拍照"入口(调用 Vant 的图片选择/拍照能力,`accept="image/*" capture="camera"`),拍照或选择后
|
||
立即调用后端真实提供的人脸识别接口 `POST /api/customer-info/personal/face-recognize`(与主项目
|
||
管理后台 `src/components/FacePhotoUpload.vue` 调用同一接口),详见
|
||
`docs/superpowers/specs/2026-07-31-personal-open-mobile-h5-face-recognize-and-field-sync-design.md`。
|
||
- 识别通过(响应 `data.fileNo` 非空)才能点击"下一步";识别未通过则清空已选照片,强制重新拍摄。
|
||
- 识别中整页遮罩禁止操作,识别结果不会透传给 `mobile/submit`(该接口描述"影像资料由后端从本地
|
||
查询"),本步骤仅作为"识别通过才能继续"的前置校验关卡。
|
||
- 支持"上一步"返回步骤2(相关协议);从步骤4(手机验证)退回本步骤时,若照片与识别通过状态仍保留,
|
||
不重新触发识别。
|
||
|
||
### 步骤4:手机验证 + 提交
|
||
|
||
- 展示掩码手机号(直接使用 `mobile/draft-detail` 返回的 `mobilePhoneMask`,不再前端自行计算,见2026-07-31更新)。
|
||
- "获取验证码"按钮:点击调用**通用发码接口 `send-verify-code/general`**(见下),成功后按钮进入 60 秒倒计时禁用态,`message.success('验证码已发送')`。**2026-08-06 更新**:请求体
|
||
`channelNo` 取自 `draft-detail` 返回的 `MobileDraftDetailVo.channelNo`(不再固定传空字符串)、
|
||
`cardName` 取自 `customerName`(个人账户卡户名与客户姓名一致,`MobileDraftDetailVo` 无独立的
|
||
"银行卡户名"字段)、`tradeNo` 固定 `"221506"`、`tradeType` 固定 `"0"`(此前两者取值写反且
|
||
`tradeType` 超长,导致真实后端报错`短信类型字段长度超出`,先临时改成 `"1"`,后端确认正确
|
||
取值应为 `"0"`,已二次修正)。真实后端联调还发现该短信类型要求 `cardNo`/`cardName`/`idNo`
|
||
三者必须同时非空(报错`该短信类型银行卡号、户名、证件号码或者电子账户、金额必须填写`),
|
||
故点击"获取验证码"前新增前置校验:`mobile`/`channelNo`/`cardNo`/`cardName`/`idNo` 任一为空
|
||
都直接弹窗提示"请联系柜员核实"并**不发起请求**,避免带着必然失败的参数反复重试(见
|
||
`SmsVerifyStep.vue` 的 `handleSendCode` 函数)。
|
||
- 验证码输入框(6位数字)。
|
||
- "确认提交"按钮:仅当验证码已输入时可点击,点击后:
|
||
- 按钮 `loading` 态防重复点击,期间整页 `van-overlay` 遮罩禁止操作(耗时操作统一等待态,与主项目 `busy`/`a-spin` 规范同一诉求,Vant 场景下用 `van-overlay` + `van-loading` 实现)。
|
||
- 调用 `mobile/submit`(`{applicationNo, verifyCode}`)。**2026-08-06 更新**:新增
|
||
`payAgreementFileNo` 字段,取自步骤2生成并上传的《用户支付服务协议》PDF文件编号(通过
|
||
`PersonalOpenMobile.vue` 提升到父组件状态后作为 prop 传入本组件),详见
|
||
`docs/superpowers/specs/2026-08-06-agreement-pdf-fill-and-upload-design.md`。
|
||
- `code === 200`:跳转步骤5,展示 `data.success` 对应的成功/失败内容。
|
||
- `code !== 200`(验证码错误等业务异常):**停留在本页**,用 `message` 原文以 Vant `showFailToast`/`showNotify` 提示错误,允许用户重新输入验证码或重新获取验证码后重试;不跳转步骤5。
|
||
- 支持"上一步"返回步骤2(相关协议,**2026-08-05 二次更新**:步骤3人脸对比已跳过,"上一步"
|
||
不再经过它)。
|
||
|
||
### 步骤5:结果页(终态)
|
||
|
||
- 成功(`data.success === true`):展示成功图标 + "开户成功" + 主账户号(`mainAccountNo`)+ 客户号(`customerNo`)。
|
||
- 失败(`data.success === false`):展示失败图标 + "开户失败" + 失败原因(`failReason`)。
|
||
- 无任何可点击的跳转/重试按钮(H5 场景下没有其它页面可去,且提交已产生后端最终结果,不支持重复提交)。
|
||
|
||
## 接口契约
|
||
|
||
### 已有明确契约(来自 swagger,无需假设,**2026-07-31 更新为完整真实字段**)
|
||
|
||
**`POST /api/wallet-management/personal-opening/mobile/draft-detail`**
|
||
请求:`{ applicationNo }`
|
||
响应 `data`(`MobileDraftDetailVo`):`customerName`/`channelNo`(**2026-08-06 新增字段**,
|
||
send-verify-code/general 依赖它)/`certificateType`/`certificateNumber`/
|
||
`certificateEffectiveDate`/`certificateExpiryDate`/`issuingAuthority`/`gender`/`certificateAddress`/
|
||
`occupation`/`occupationNote`/`mobilePhone`(明文)/`mobilePhoneMask`(后端直接提供的掩码值)/
|
||
`bankCardNumber`/`ethnic`。
|
||
|
||
**`POST /api/wallet-management/personal-opening/mobile/send-verify-code/general`**
|
||
(**2026-08-06 补全契约**,`SendVerifyCodeGeneralParamVo`)
|
||
请求:`{ channelNo, tradeNo(固定"221506"), tradeType(固定"0"), mobile, cardNo, cardName, idNo }`
|
||
(`cardNo`/`cardName`/`idNo` 三者必须同时非空,否则后端报错`该短信类型银行卡号、户名、证件
|
||
号码或者电子账户、金额必须填写`)
|
||
响应:`{ code, message, data: null }`
|
||
|
||
**`POST /api/customer-info/personal/face-recognize`**(2026-07-31 新增,真实接口)
|
||
请求:`{ fileData(人脸照片base64), idNo, name, channelNo }`
|
||
响应 `data`(`FaceRecognizeResultVo`):`fileNo`/`recode`/`recodeInfo`/`sysSerialNo`/`sysDate`
|
||
|
||
**`POST /api/wallet-management/personal-opening/mobile/submit`**
|
||
请求:`{ applicationNo, verifyCode }`
|
||
响应 `data`(`SubmitApplicationResultVo`):`success`/`mainAccountNo`/`customerNo`/`failReason`
|
||
|
||
**`GET /api/customer-info/bank-agreement/base64`**(2026-08-05 新增,真实接口)
|
||
请求:query 参数 `fileType`(枚举 `BankAgreementFileTypeEnum`,本页面只用到
|
||
`PAY_SERVICE_AGREEMENT`/`PAY_SERVICE_PRIVACY` 两项,见步骤2说明)
|
||
响应 `data`(`BankAgreementFileVo`):`fileType`/`fileName`/`fileContent`(base64 编码的 PDF)
|
||
|
||
### 仍未解决的契约缺口(详见《缺失的后端接口.md》Part 12 第97条、Part 15 第97/99条)
|
||
|
||
1. ~~**`channelNo`/`bankCardName` 两个字段仍不存在**~~——**2026-08-06 更新**:
|
||
`MobileDraftDetailVo` 已补充 `channelNo` 字段,`send-verify-code/general` 的 `channelNo`
|
||
已从 draft-detail 响应取值,不再是契约缺口;`cardName`(银行卡户名)仍无独立字段,用
|
||
`customerName` 代替(个人账户场景卡户名与客户姓名一致)。`face-recognize` 的 `channelNo`
|
||
因该步骤当前已从流程跳过(见步骤3说明),暂不受影响,固定传空字符串的写法保留在
|
||
`faceRecognizeApi` 里未改动。
|
||
2. **匿名访问 `face-recognize` 的可行性未经真实后端环境验证**:该接口此前只被已登录的管理后台
|
||
调用,H5 侧本次调用不带 `Authorization`,本地无法验证真实后端是否已将其纳入匿名白名单。
|
||
|
||
详见 `docs/superpowers/specs/2026-07-31-personal-open-mobile-h5-face-recognize-and-field-sync-design.md`
|
||
的"已知风险/遗留缺口"章节。
|
||
|
||
### 请求头规则
|
||
|
||
- **不携带 `Authorization`**:这些接口面向未登录的终端客户,H5 页面无法获得管理后台的登录态
|
||
token,属于匿名接口(`face-recognize` 是否真的允许匿名访问尚待后端确认,见上方契约缺口第2点)。
|
||
- **仍携带通用 trace header**(`Appno`/`Serialno`/`Transdate`/`Transtradetime`):沿用主项目 `src/utils/traceHeaders.js` 的实现逻辑(原样迁移一份到 `fryl_h5`,无需改动)。
|
||
- **不携带 `Channelno` 请求头**:二维码 URL 中不包含渠道信息(`?applicationNo=xxx`),页面无渠道上下文,按现有规则("channelNo 如果页面没有渠道信息,则不需要传值")不传该 header。**注意**:这里指的是 trace header 里的 `Channelno`,与请求体中的 `channelNo` 参数是两个独立概念,互不影响。
|
||
- 响应体统一 `{code, message, data}` 判定成功用 `code === 200`,与主项目一致。
|
||
|
||
## 工程结构(`fryl_h5`)
|
||
|
||
```
|
||
fryl_h5/
|
||
├── index.html
|
||
├── package.json # 独立依赖:vue、vue-router、vant、axios、dayjs、vite
|
||
├── vite.config.js # 参照主项目结构,@ 指向 ./src,server.port 另设(如 5174,避免与主项目 5173 冲突)
|
||
├── .env.development # VITE_API_BASE_URL=http://localhost:8888(与主项目 mock-server 共用端口)
|
||
├── .env.production # VITE_API_BASE_URL=(留空,构建时按实际环境注入或走同源部署)
|
||
└── src/
|
||
├── main.js
|
||
├── App.vue
|
||
├── router/index.js # 单路由 /mobile/personal-open
|
||
├── api/
|
||
│ ├── request.js # 精简版 axios 实例:baseURL + trace header注入 + 统一错误提示,无 auth/refresh 逻辑
|
||
│ └── personalOpen.js # fetchDraftDetailApi / sendVerifyCodeApi / faceRecognizeApi / submitApplicationApi / fetchBankAgreementApi(2026-08-05新增) / uploadFileApi(2026-08-06新增)
|
||
├── constants/
|
||
│ └── enums.js # 含 BANK_AGREEMENT_FILE_TYPE_LABELS(2026-08-05新增)
|
||
├── utils/
|
||
│ └── traceHeaders.js # 从主项目原样迁移
|
||
└── views/
|
||
└── PersonalOpenMobile.vue # 页面主组件,内部拆 5 个子组件(2026-08-05 由4个拆为5个):
|
||
├── steps/InfoConfirmStep.vue
|
||
├── steps/AgreementStep.vue # 2026-08-05 新增:相关协议,调用 fetchBankAgreementApi
|
||
├── steps/FaceCompareStep.vue
|
||
├── steps/SmsVerifyStep.vue
|
||
└── steps/ResultStep.vue
|
||
```
|
||
|
||
**2026-08-06 更新**:新增 `src/utils/agreementPdfFill.js`(用 `pdf-lib` 在《用户支付服务协议》
|
||
PDF末页填入客户姓名/证件号码/当前日期;姓名用 Canvas 渲染成小图片再嵌入,不嵌入自定义中文
|
||
字体——最初的字体嵌入方案已推翻,原因及推翻过程见
|
||
`docs/superpowers/specs/2026-08-06-agreement-pdf-fill-and-upload-design.md`),新增依赖
|
||
`pdf-lib`。
|
||
|
||
## mock-server 配套改造(供本地联调,**2026-07-31 更新**:新增人脸识别路由 + 路由挂载顺序修复,详见
|
||
`docs/superpowers/specs/2026-07-31-personal-open-mobile-h5-face-recognize-and-field-sync-design.md`)
|
||
|
||
在 `mock-server/routes/personalOpenMobile.js` 实现三个路由,新增 `mock-server/routes/faceRecognize.js`
|
||
实现人脸识别路由(不带 `requireAuth`,管理后台与H5共用),`mock-server/state.js` 复用现有
|
||
`genSmsCode`/`verifySmsCode`(**2026-07-28 更新**:发码接口改为通用 `/general` 路径且不再携带
|
||
`applicationNo`,验证码 key 统一改用手机号):
|
||
|
||
- `POST /wallet-management/personal-opening/mobile/draft-detail`:按 `applicationNo` 查找 `personalOpenApplications`,不存在返回 `sendFail('申请单不存在', 404)`;存在则返回真实 swagger 字段,`mobilePhone` 直接返回 `application.mobilePhone` 明文,`mobilePhoneMask` 由 mock 自行按"前3后4"算法计算返回(后端真实接口已确认直接提供该字段,前端不再自行计算),`occupationNote` 取 `application.jobNote`(种子数据字段名未变,仅响应体字段名对齐真实swagger)。**2026-08-06 更新**:新增返回 `channelNo`(取 `application.channelNo`,种子数据已有此字段),真实 swagger 已补充该字段,不再是契约缺口;`bankCardName` 仍无对应字段,前端改用 `customerName` 代替。
|
||
- `POST /customer-info/personal/face-recognize`:校验 `fileData`/`idNo`/`name` 非空,缺失返回 `sendFail`;校验通过返回 `{fileNo, recode:'0000', recodeInfo:'识别通过', sysSerialNo, sysDate}`,不模拟"人脸不符"等业务失败场景(无可参照的真实失败样例,与主项目现状一致)。
|
||
- `POST /wallet-management/personal-opening/mobile/send-verify-code/general`:不再依赖 `applicationNo`(新接口本身与申请单解耦),直接以请求体 `mobile` 校验非空后调用 `genSmsCode('WALLET_PERSONAL_OPEN_MOBILE', mobile)`(以手机号作为 key),固定码 `123456` 同时生效(与现有短信验证码 mock 规则一致)。
|
||
- `POST /wallet-management/personal-opening/mobile/submit`:申请单不存在返回 `sendFail('申请单不存在', 404)`(与其余 mock 路由一致,`code!==200`,前端停留本页提示,不进入结果页——现实中该场景仅发生于极端并发下的过期链接,重试大概率仍失败,但由前端统一按"停留本页"规则处理,不单独特殊化);校验 `verifySmsCode('WALLET_PERSONAL_OPEN_MOBILE', application.mobilePhone, verifyCode)` 失败返回 `sendFail('验证码错误或已过期', 400)`(key 与发码环节的 `mobile` 保持一致);两者均为 `code!==200`,触发前端"停留本页"分支。验证码校验通过后执行与现有 `query-progress` 相同的"生成主账户"逻辑(若尚未开立),返回 `code===200` + `{success: true, mainAccountNo, customerNo}`,前端据此跳转结果页展示成功。`data.success === false` 的业务失败分支(如核心银行侧开户失败)mock 暂不模拟(无对应真实失败场景可参照),仅保留前端结果页对该字段的展示逻辑,供后端真实接口返回失败时兜底展示。
|
||
- `GET /customer-info/bank-agreement/base64`(2026-08-05 新增):按 `fileType` 查 mock 内置的
|
||
文件名->本地占位 PDF 映射表(`mock-server/assets/bank-agreements/*.pdf`,占位内容非正式条款
|
||
文本,仅用于验证前端 iframe 预览 PDF 的渲染效果),读取文件并转 base64 返回
|
||
`{fileType, fileName, fileContent}`;`fileType` 不在枚举内返回 `sendFail('不支持的协议文件类型', 400)`。
|
||
同样**不带** `requireAuth`,且路由定义在本文件内(`personalOpenMobileRouter` 本身已在
|
||
`customerRouter` 等之前挂载,见下方"路由挂载顺序缺陷"说明),无需额外调整挂载顺序。
|
||
|
||
**路由挂载顺序缺陷(2026-07-31 修复)**:`mock-server/index.js` 里 `customerRouter`/`walletRouter`/
|
||
`userRouter`/`roleRouter`/`baseConfigRouter` 等均以 `app.use('/api', xxxRouter)` 形式挂载且内部
|
||
用 `router.use(requireAuth)`(不带路径)拦截鉴权,会拦截"该 router 挂载前缀下的任意路径"而不仅是
|
||
自身定义的路由。此前 `personalOpenMobileRouter` 挂载顺序在它们之后,导致本地 mock 环境下 H5 匿名
|
||
接口全部被误拦截返回401(已实测复现)。已将 `personalOpenMobileRouter`/`faceRecognizeRouter` 调整
|
||
到这些 router 之前挂载,详见《缺失的后端接口.md》Part 15 第100条。
|
||
|
||
此改动**仅为本地离线联调用**,真实后端接口到位后无需删除(管理后台侧 mock 路由同样长期保留,供后续演示环境使用)。
|
||
|
||
## 环境变量
|
||
|
||
`fryl_h5/.env.development`:
|
||
```
|
||
VITE_API_BASE_URL=http://localhost:8888
|
||
```
|
||
`fryl_h5/.env.production`:
|
||
```
|
||
VITE_API_BASE_URL=
|
||
```
|
||
(留空表示构建产物部署时与后端网关同源,或由运维在部署时通过其它机制配置;不提供页面内配置入口,符合本次用户决策。)
|
||
|
||
**2026-08-01 更新:本地联调真实后端遇 CORS 的临时规避方案**——与根目录管理后台完全一致的做法:
|
||
在 `fryl_h5/.env.development.local`(已被 `.gitignore` 的 `*.local` 规则忽略,不会提交)中配置
|
||
`DEV_API_PROXY_TARGET=真实后端地址`,同时把该文件里的 `VITE_API_BASE_URL` 设为空字符串,
|
||
`fryl_h5/vite.config.js` 会把浏览器发往同源 `/api` 的请求在 Node 端转发给真实后端(`changeOrigin`
|
||
+ 请求/响应体打印日志,与主项目 `vite.config.js` 的代理配置逐条对应,仅端口号随本工程固定为
|
||
5174)。未配置 `DEV_API_PROXY_TARGET` 时行为不变(直接请求 `VITE_API_BASE_URL` 指向的地址,
|
||
默认走本地 mock server)。仅用于本地 `npm run dev` 调试,不影响生产构建逻辑。
|
||
|
||
## 依赖清单(新增)
|
||
|
||
`vue`、`vue-router`、`vant`、`axios`、`dayjs`(`traceHeaders.js` 依赖)、`@vitejs/plugin-vue`(devDependencies)、`vite`(devDependencies)。均为独立 `fryl_h5/package.json`,与根目录 `package.json` 完全隔离。
|
||
|
||
## 范围边界(本次不做)
|
||
|
||
- ~~不实现真实人脸活体检测/比对逻辑(后端无对应接口,仅做拍照UI占位)~~——**2026-07-31 更新**:
|
||
后端已提供真实人脸识别接口,已改为真实调用,见
|
||
`docs/superpowers/specs/2026-07-31-personal-open-mobile-h5-face-recognize-and-field-sync-design.md`。
|
||
- ~~不实现协议正式文本/链接(占位文案,后续产品补充后再替换,不涉及提交参数变更)。~~——
|
||
**2026-08-05 更新**:后端已提供 `bank-agreement/base64` 真实接口,"相关协议"步骤已改为调用
|
||
真实接口展示协议原文(PDF),不再是占位文案。
|
||
- 不修改管理后台侧任何代码(二维码生成逻辑已完成)。
|
||
- 不做 H5 页面内 BASE_URL 可配置入口。
|
||
- 不支持结果页之后的"重新提交"/跳转其它页面。
|
||
- 不引入 TypeScript、不引入状态管理库(Pinia 等,本页面状态足够简单,用组件内 `ref` 管理即可)。
|