231 lines
16 KiB
Markdown
231 lines
16 KiB
Markdown
# 贷款申请移动端H5信息核对页 设计文档
|
||
|
||
## 背景
|
||
|
||
《缺失的后端接口.md》第145/147条曾记载:贷款申请扫码后的移动端H5确认页(短信验证+人脸识别,对应 `POST /api/credit-apply/loan-application/confirm`)此前"不在本项目前端实现范围",原因是该接口鉴权情况不明、且贷款场景无专用发码接口。本次任务是把这块补上:
|
||
|
||
1. 新增"个人贷款申请信息核对"H5页面(`fryl_h5` 子工程,匿名访问)。
|
||
2. 新增"企业贷款申请信息核对"H5页面(同上)。
|
||
3. 给管理后台 `src/views/credit/loan-application/EnterpriseLoanForm.vue` 补齐与 `PersonalLoanForm.vue` 一致的"二次确认弹窗+二维码"入口(该表单本次改造前明确"不引入二维码模式",见 `2026-08-10-enterprise-loan-application-form-redesign.md` 第5条,本次改变这一决定)。
|
||
|
||
页面严格按 `./授信信息核对页面截图/1.jpg~5.jpg`(1、2为个人;3、4、5为企业)的字段与步骤结构实现,5张截图已逐张读取分析,详见下文。
|
||
|
||
## 关键决策(已与用户确认,不再更改)
|
||
|
||
1. 短信验证码走真实发送+真实校验,`verifyCode` 与其他信息一起提交给 `confirm` 接口。
|
||
2. 发码接口复用现有 `POST /api/wallet-management/personal-opening/mobile/send-verify-code/general`(`tradeNo` 固定 `"221506"`、`tradeType` 固定 `"0"`),不新增专用发码接口。
|
||
3. 页面结构为多步骤手风琴(与开户H5一致的"折叠步骤行"交互),非单页。
|
||
4. 个人贷款和企业贷款的二维码路径是**两条独立路径**(不是共用一个路径按类型区分),架构完全对齐现有开户H5模式(`PersonalOpenMobile.vue`/`EnterpriseOpenMobile.vue` 两个独立组件+两条独立路由)。
|
||
5. 信息核对步骤严格按截图1.jpg(个人)/3.jpg(企业)字段展示;业务类型文案照抄截图原文案"授信申请"(不改为"贷款申请")。
|
||
6. 企业侧"人脸比对"步骤简化为拍照上传模式(复用现有 `FaceCompareStep.vue` 的交互模式,UI文案对齐截图改为"开始采集"),比对接口复用个人开户的 `face-recognize` 接口,`legalPersonCertId` 当 `idNo`、`legalPersonName` 当 `name`。
|
||
7. 信息核对步骤拆分为 `PersonalLoanInfoConfirmStep.vue`/`EnterpriseLoanInfoConfirmStep.vue` 两个独立组件(与现有开户H5 `InfoConfirmStep.vue`/`EnterpriseInfoConfirmStep.vue` 分开的先例保持一致)。
|
||
8. 完成页参照现有 `ResultStep.vue` 的成功/失败展示模式改造为 `LoanResultStep.vue`(5张截图均未展示"完成"步骤实际内容,无截图可依)。
|
||
9. 发码接口所需的"标准四要素+渠道编号",个人贷款场景要求后端在 `LoanApplicationDetailVo` 新增 `channelNo`/`certificateNumber`/`bankCardNumber` 三个字段;企业贷款场景 `channelNo` 同样要求新增,`idNo` 复用已有 `legalPersonCertId`,`cardNo`(银行卡号)因企业本身无此概念,前端传空字符串并登记风险。
|
||
|
||
## 截图分析结果
|
||
|
||
### 1.jpg / 2.jpg(个人,"稠州个人授信申请")
|
||
|
||
- 顶部蓝色横幅:"业务确认" + 副标题"稠州个人授信申请" + 浙江稠州商业银行logo。
|
||
- 步骤条3步:1.信息核对 → 2.手机验证 → 3.完成。
|
||
- 信息核对字段(label-value两列):业务类型="稠州个人授信申请"(固定文案);办理渠道="巺平(线上)";客户=customerName;手机号="130****2222"(3+4+4脱敏);金额="¥20.00万元";利率="年化9.00%"。底部按钮"确认无误,下一步"。
|
||
- 手机验证步骤:红字提示"请确认手机号:130****2222" + 验证码输入框 + "发送"按钮 + "提交"按钮。
|
||
|
||
### 3.jpg / 4.jpg / 5.jpg(企业,"稠州企业授信申请")
|
||
|
||
- 步骤条4步:1.信息核对 → 2.人脸比对 → 3.手机验证 → 4.完成(比个人多"人脸比对")。
|
||
- 信息核对字段结构与个人一致:业务类型="稠州企业授信申请";办理渠道="巺平(线上)";客户=enterpriseName;手机号="157****1164";金额="¥1000.00万元";利率="年化10.00%"。
|
||
- 人脸比对步骤:人物占位图 + 提示文案"请保证光线充足,摘除帽子,正视屏幕,点击"开始采集"按钮" + "开始采集"按钮。
|
||
- 手机验证步骤:与个人版UI一致(红字手机号确认+验证码输入框+发送(倒计时)按钮+提交按钮)。
|
||
|
||
## 后端接口契约(来自 `swagger_project_scfs_2026-08-05_15-15-22.json`,均无鉴权标注)
|
||
|
||
### `POST /api/credit-apply/loan-application/detail`
|
||
|
||
请求:`{ loanApplicationId }`(必填)。
|
||
|
||
响应 `LoanApplicationDetailVo`(个人/企业合并Vo,约60字段),本次用到的字段:
|
||
|
||
| 字段 | 说明 |
|
||
|---|---|
|
||
| `applicationType` | `PERSONAL`/`BUSINESS` |
|
||
| `applicationStatus` | `DRAFT`/`CONFIRMED`/`SUBMITTED`/`APPLY_FAILED` |
|
||
| `channel` | 渠道展示字符串(如"巺平"),非渠道编码 |
|
||
| `customerName` | 客户名称(个人贷款场景=客户本人姓名) |
|
||
| `enterpriseName` | 经营实体名称(企业贷款场景展示用) |
|
||
| `phoneNo` | 手机号(个人/企业贷款场景通用的联系手机号,柜员在表单里手工录入的独立字段,非法人手机号) |
|
||
| `loanAmount` | 申请金额(字符串) |
|
||
| `applicationInterestRate` | 申请利率(字符串) |
|
||
| `legalPersonName`/`legalPersonCertId` | 法人姓名/证件号码(企业贷款人脸比对用) |
|
||
| `frchainOnlineSerialNo` | 凡荣e链任务流水号(`SUBMITTED`时返回) |
|
||
| `failReason` | 失败原因(`APPLY_FAILED`时返回) |
|
||
|
||
以下字段**当前不存在**,需登记后端新增(见"待登记后端缺口"):`channelNo`、`certificateNumber`(个人证件号码)、`bankCardNumber`(个人银行卡号)。
|
||
|
||
### `POST /api/credit-apply/loan-application/confirm`
|
||
|
||
请求:`{ loanApplicationId(必填), verifyCode(选填,描述为"个人申请必填"), fileNoList(选填,逗号分隔影像文件编号) }`。
|
||
|
||
响应 `ConfirmLoanApplicationResultVo`:`{ loanApplicationId, applicationStatus, frchainOnlineSerialNo(SUBMITTED时返回), failReason(APPLY_FAILED时返回) }`。
|
||
|
||
业务逻辑(后端描述):校验状态为 `DRAFT` 后更新为 `CONFIRMED`,并调用凡荣e链贷款申请接口。
|
||
|
||
## 整体架构
|
||
|
||
### 路由与二维码路径
|
||
|
||
个人贷款与企业贷款使用**两条完全独立的路径**(对齐开户H5模式):
|
||
|
||
| 场景 | 环境变量 | 默认路径 | 说明 |
|
||
|---|---|---|---|
|
||
| 个人贷款 | `VITE_H5_QRCODE_PATH_LOAN` | `/mobile/loan-application` | 已存在,本次沿用不变 |
|
||
| 企业贷款 | `VITE_H5_QRCODE_PATH_LOAN_ENTERPRISE` | `/mobile/enterprise-loan-application` | 新增,命名对齐现有 `VITE_H5_QRCODE_PATH_ENTERPRISE`(开户场景)的风格 |
|
||
|
||
二维码内容均为 `${base}${path}?loanApplicationId=${id}`,域名复用同一个 `VITE_H5_QRCODE_BASE_URL`。
|
||
|
||
### 新增文件(`fryl_h5/src/`)
|
||
|
||
```
|
||
views/PersonalLoanApplicationMobile.vue # 个人贷款H5页面,3步向导
|
||
views/EnterpriseLoanApplicationMobile.vue # 企业贷款H5页面,4步向导
|
||
views/steps/loan/PersonalLoanInfoConfirmStep.vue
|
||
views/steps/loan/EnterpriseLoanInfoConfirmStep.vue
|
||
views/steps/loan/LoanFaceCompareStep.vue # 企业专属
|
||
views/steps/loan/LoanSmsVerifyStep.vue # 个人/企业共用,通过 applicationType prop 区分发码参数
|
||
views/steps/loan/LoanResultStep.vue # 个人/企业共用
|
||
api/loanApplication.js # fetchLoanApplicationDetailApi / sendVerifyCodeApi / confirmLoanApplicationApi
|
||
utils/format.js # formatLoanAmountToWan(amount),公式对齐 LoanApplicationList.vue 现有 formatToWan
|
||
```
|
||
|
||
### 修改文件
|
||
|
||
- `fryl_h5/src/router/index.js`:新增两条静态路由(无鉴权,与现状一致)。
|
||
- `fryl_h5/src/utils/mask.js`:补充 `maskMobile`(保留前3位后4位,中间`****`,从主项目 `src/utils/mask.js` 原样搬入)。
|
||
- `src/utils/qrCode.js`:新增 `buildEnterpriseLoanApplicationQrCodeUrl`。
|
||
- `.env.development`/`.env.production`:新增 `VITE_H5_QRCODE_PATH_LOAN_ENTERPRISE`。
|
||
- `src/views/credit/loan-application/EnterpriseLoanForm.vue`:补齐二次确认弹窗+二维码(见下)。
|
||
|
||
## 个人贷款H5页面设计(`PersonalLoanApplicationMobile.vue`)
|
||
|
||
整体骨架复用 `PersonalOpenMobile.vue` 的手风琴模式(顶部蓝色横幅+银行logo,`step-list`+`step-row`+`STEP_ORDER`/`isDone`/`rowClass`,不支持回退)。
|
||
|
||
**加载流程**:`route.query.loanApplicationId` 缺失 → 错误态"链接无效,缺少申请编号,请重新扫描二维码";调用 detail 接口后按 `applicationStatus` 分支:
|
||
- `DRAFT`:正常进入向导,从 `INFO` 步骤开始。
|
||
- `SUBMITTED`/`APPLY_FAILED`:说明客户已完成过确认流程(重复扫码/刷新页面等场景),直接跳到 `RESULT` 步骤展示最终结果,不再重新走 `INFO`/`SMS`,体验优于生硬报错。
|
||
- `CONFIRMED`:理论上是极短暂的中间态(`confirm` 接口内部会同步转成 `SUBMITTED`/`APPLY_FAILED`),如果查询到该状态,展示错误态"该贷款申请正在处理中,请稍候重新扫描二维码"。
|
||
|
||
`STEP_ORDER = ['INFO', 'SMS', 'RESULT']`,横幅副标题固定文案"稠州个人授信申请"。
|
||
|
||
### 步骤1:信息核对(`PersonalLoanInfoConfirmStep.vue`)
|
||
|
||
纯展示,不调接口。label-value两列:
|
||
|
||
| 字段 | 取值 |
|
||
|---|---|
|
||
| 业务类型 | 固定文案"稠州个人授信申请" |
|
||
| 办理渠道 | `${detail.channel}(线上)` |
|
||
| 客户 | `detail.customerName` |
|
||
| 手机号 | `maskMobile(detail.phoneNo)` |
|
||
| 金额 | `¥${formatLoanAmountToWan(detail.loanAmount)}万元`(`loanAmount` 原始单位为元,需 `/10000` 换算,公式对齐 `LoanApplicationList.vue` 现有的 `formatToWan`,带千分位两位小数,仅后缀文案由"万"改为"万元"以对齐截图) |
|
||
| 利率 | `年化${Number(detail.applicationInterestRate).toFixed(2)}%`(`applicationInterestRate` 本身即百分数数值,无需换算) |
|
||
|
||
底部按钮"确认无误,下一步" → `currentStep = 'SMS'`。
|
||
|
||
### 步骤2:手机验证(`LoanSmsVerifyStep.vue`)
|
||
|
||
UI参照现有 `SmsVerifyStep.vue`(手机号展示+验证码输入框+60s倒计时发送按钮+提交按钮)。
|
||
|
||
发送验证码参数:
|
||
|
||
```js
|
||
sendVerifyCodeApi({
|
||
tradeNo: '221506',
|
||
tradeType: '0',
|
||
mobile: detail.phoneNo,
|
||
cardNo: detail.bankCardNumber, // 新增字段,需后端补充
|
||
cardName: detail.customerName,
|
||
idNo: detail.certificateNumber, // 新增字段,需后端补充
|
||
channelNo: detail.channelNo // 新增字段,需后端补充
|
||
})
|
||
```
|
||
|
||
提交:`confirmLoanApplicationApi({ loanApplicationId, verifyCode })`。
|
||
|
||
### 步骤3:完成(`LoanResultStep.vue`)
|
||
|
||
- `applicationStatus === 'SUBMITTED'`:成功态,展示"提交成功"+`frchainOnlineSerialNo`。
|
||
- `applicationStatus === 'APPLY_FAILED'`:失败态,展示 `failReason`。
|
||
- 其他:兜底展示"处理中,请稍候"(理论上不会出现,仅防止页面空白)。
|
||
|
||
组件 props 接受一个通用 `{ applicationStatus, frchainOnlineSerialNo, failReason }` 对象,两个来源结构一致,无需转换:① `confirm` 接口正常提交后的返回值(`ConfirmLoanApplicationResultVo`);② 重复扫码场景下直接从 `detail` 接口返回值中取同名字段(`LoanApplicationDetailVo` 本身就带这三个字段)。
|
||
|
||
## 企业贷款H5页面设计(`EnterpriseLoanApplicationMobile.vue`)
|
||
|
||
`STEP_ORDER = ['INFO', 'FACE', 'SMS', 'RESULT']`,横幅副标题固定文案"稠州企业授信申请"。加载流程与个人版一致。
|
||
|
||
### 步骤1:信息核对(`EnterpriseLoanInfoConfirmStep.vue`)
|
||
|
||
| 字段 | 取值 |
|
||
|---|---|
|
||
| 业务类型 | 固定文案"稠州企业授信申请" |
|
||
| 办理渠道 | `${detail.channel}(线上)` |
|
||
| 客户 | `detail.enterpriseName` |
|
||
| 手机号 | `maskMobile(detail.phoneNo)` |
|
||
| 金额 | `¥${formatLoanAmountToWan(detail.loanAmount)}万元`(同上,`/10000` 换算) |
|
||
| 利率 | `年化${Number(detail.applicationInterestRate).toFixed(2)}%` |
|
||
|
||
### 步骤2:人脸比对(`LoanFaceCompareStep.vue`,企业专属)
|
||
|
||
- UI参照现有 `FaceCompareStep.vue`(`van-uploader` 拍照上传一张照片→自动调用识别接口),文案改为对齐截图:"请保证光线充足,摘除帽子,正视屏幕,点击"开始采集"按钮",按钮文案"开始采集"。
|
||
- 识别通过后自动进入下一步(`currentStep = 'SMS'`),不支持回退,与个人开户"手机验证"自动前进模式一致。
|
||
- 接口:复用 `faceRecognizeApi`(`/api/customer-info/personal/face-recognize`),参数 `idNo: detail.legalPersonCertId`、`name: detail.legalPersonName`。风险登记:接口路径含 `personal`,企业法人场景是否被后端业务逻辑接受未知。
|
||
|
||
### 步骤3:手机验证(`LoanSmsVerifyStep.vue`,与个人共用)
|
||
|
||
发送验证码参数:
|
||
|
||
```js
|
||
sendVerifyCodeApi({
|
||
tradeNo: '221506',
|
||
tradeType: '0',
|
||
mobile: detail.phoneNo,
|
||
cardNo: '', // 企业无银行卡号概念,风险登记
|
||
cardName: detail.legalPersonName,
|
||
idNo: detail.legalPersonCertId,
|
||
channelNo: detail.channelNo // 新增字段,需后端补充
|
||
})
|
||
```
|
||
|
||
提交同个人版。
|
||
|
||
### 步骤4:完成(`LoanResultStep.vue`,与个人共用)
|
||
|
||
## `EnterpriseLoanForm.vue` 改造点
|
||
|
||
照抄 `PersonalLoanForm.vue` 的二次确认弹窗模式:
|
||
|
||
- "提交"按钮改为触发 `handleConfirmButtonClick` → 弹出 `a-modal`(`confirmModalStep` 为 `'confirm'`/`'qrcode'`),文案"是否确认{提交/修改}贷款申请?",三个按钮:保存/取消/扫码确认。
|
||
- 原 `handleSubmit` 拆成 `submitApplication()`(真正提交)+ `handleModalSave`(保存后返回列表)+ `handleModalInvite`(提交成功后切到二维码态)。
|
||
- 二维码态:`qrCodeUrl = buildEnterpriseLoanApplicationQrCodeUrl(loanApplicationId)`。
|
||
- 引入 `QrcodeVue` 组件(项目已有依赖 `qrcode.vue`)。
|
||
- 原有提交前校验(影像资料上传中/协议勾选等)保留在 `handleConfirmButtonClick` 里,校验通过才弹窗。
|
||
|
||
## 待登记后端缺口清单
|
||
|
||
以下问题将追加到《缺失的后端接口.md》:
|
||
|
||
1. `LoanApplicationDetailVo` 缺少 `channelNo`(渠道编号),现有 `channel` 是展示字符串,无法用作发码接口参数。需新增(个人/企业贷款共用)。
|
||
2. `LoanApplicationDetailVo` 缺少个人贷款客户证件号码,需新增 `certificateNumber` 字段(企业贷款已有 `legalPersonCertId` 可复用,无需新增)。
|
||
3. `LoanApplicationDetailVo` 缺少个人贷款客户银行卡号,需新增 `bankCardNumber` 字段。
|
||
4. 企业贷款场景发码接口 `cardNo` 参数无字段来源(企业无银行卡号概念),前端暂传空字符串,风险登记待后端确认该通用发码接口在企业场景下能否容忍 `cardNo` 为空。
|
||
5. 人脸识别接口 `/api/customer-info/personal/face-recognize` 路径含 `personal`,企业法人场景复用风险,需后端确认业务逻辑是否适用。
|
||
6. `confirm` 接口 `verifyCode` 描述为"个人申请必填",企业贷款场景该字段强制校验语义不明确,前端仍会传值,风险登记以防后端做特殊处理导致提交失败。
|
||
|
||
## 测试/验证方式
|
||
|
||
无自动化测试脚本(项目未配置单测),按以下方式手工验证:
|
||
|
||
1. 管理后台创建一条企业贷款草稿 → 点击"提交" → 验证二次确认弹窗+扫码确认+二维码展示(对照 `PersonalLoanForm.vue` 的现有行为)。
|
||
2. `npm run dev`(`fryl_h5`)启动后手动访问 `/mobile/loan-application?loanApplicationId=xxx` 与 `/mobile/enterprise-loan-application?loanApplicationId=xxx`,核对各步骤UI与截图一致、手机验证码可发送(若后端字段缺口未补齐,预期报错并提示明确的缺失原因,而非静默失败)。
|
||
3. 核对 `applicationStatus !== 'DRAFT'` 时的错误态提示、`confirm` 接口返回 `SUBMITTED`/`APPLY_FAILED` 两种终态的完成页展示。
|