SFT/docs/superpowers/specs/2026-08-11-loan-application...

231 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 贷款申请移动端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` 两种终态的完成页展示。