Files
flux-web/功能说明.md
2026-02-03 20:29:21 +08:00

792 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# flux-web 功能说明文档
## 项目简介
flux-web 是薇钱包 H5 前端项目,提供用户借款申请、信息填写和授权流程的完整业务功能。
---
## 一、业务流程总览
### 1.1 完整用户旅程
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ index.html │───>│ basic-info.html │───>│ 授权流程 │
│ 借款申请页面 │ │ 基本信息填写 │ │ (可选) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### 1.2 核心业务流程图
```
用户进入页面
├─> 输入/修改借款金额
├─> 选择还款期数
├─> 选择借款用途
├─> [已有登录态?] ──是──> 显示脱敏手机号
│ │
│ └─> 勾选协议 → 跳转基本信息页
└─> 否 ──> 选择登录方式
├─> 方式A: 极光一键登录
│ └─> 成功 → 注册/登录 → 跳转
│ └─> 失败 → 降级到方式B
└─> 方式B: 短信验证码登录
├─> 输入手机号
├─> 发送验证码 (60s倒计时)
├─> 验证码校验
└─> 注册/登录 → 跳转
```
---
## 二、index.html - 借款申请页面
### 2.1 页面元素与交互
| 元素 | DOM ID | 交互行为 | 数据流向 |
|------|--------|----------|----------|
| 借款金额输入 | `loanAmount` | input 事件触发 | `loanData.amount` |
| 全部借出按钮 | `maxLoan` | 点击设为最大值 | `loanData.amount = 200000` |
| 还款期数 | `repaymentTerm` | 点击打开选择器 | `loanData.period` |
| 还款计划 | `repaymentPlan` | 自动计算更新 | `calculateRepaymentPlan()` |
| 借款用途 | `loanPurpose` | 点击打开选择器 | `loanData.purpose` |
| 手机号输入 | `phoneNumber` | 输入 + 验证 | → 验证码弹窗 |
| 一键登录 | `oneClickLoginWrapper` | 极光SDK | → 成功后自动填手机号 |
| 立即申请按钮 | `applyBtn` | 点击触发申请 | → 验证码/跳转 |
| 协议勾选 | `agreementCheck` | 必选 | 未勾选弹窗提示 |
### 2.2 借款数据结构
```javascript
loanData = {
amount: 50000, // 借款金额 (1000-200000)
period: 12, // 还款期数 (3,6,9,12,18,24,36)
purpose: '个人日常消费' // 借款用途
}
```
### 2.3 还款计划计算逻辑
**接口:** `LoanService.calculateRepaymentPlan(amount, period, interestRate)`
**计算公式:**
```
月均本金 = 借款金额 / 期数
月利息 = 借款金额 × (年化利率 / 100) / 12
首期还款 = 月均本金 + 月利息
首期日期 = 当前日期 + 30天
```
**年化利率范围:** 10.8% - 24%(单利)
**显示格式:** `首期02月05日 应还 4916.67元`
---
## 三、用户认证流程
### 3.1 极光一键登录
**初始化条件:**
- 配置了 `jVerifyAppId`
- 用户未登录
**流程:**
```
加载极光SDK
├─> 成功获取手机号
│ └─> AuthService.registerOrLogin(phone, loanData)
│ └─> 成功 → 跳转基本信息页
│ └─> 失败 → 显示手机号输入框
└─> 失败/不支持
└─> 自动降级到短信验证码登录
```
**关键接口:** `AuthService.registerOrLogin(phone, loanData)`
### 3.2 短信验证码登录
**流程图:**
```
输入手机号
├─> 验证手机号格式
│ └─> 无效 → 显示错误提示
├─> 有效 → 点击"立即申请"
│ │
│ ├─> 勾选协议?
│ │ └─> 否 → 弹协议确认窗
│ │
│ └─> 是 → 打开验证码弹窗
│ │
│ ├─> 发送验证码
│ │ ├─> SMSService.send(phone)
│ │ ├─> 成功 → 启动60s倒计时
│ │ └─> 失败 → 显示错误,可重新发送
│ │
│ ├─> 输入验证码
│ │ └─> SMSService.verify(phone, code)
│ │ └─> 成功 → 注册/登录
│ │ └─> 失败 → 显示错误
│ │
│ └─> 验证通过
│ └─> 跳转基本信息页
```
**倒计时逻辑:**
- 60秒倒计时
- 倒计时结束显示"重新发送"
- 可点击重新发送
### 3.3 接口数据说明
#### 发送短信验证码
**接口:** `POST /zcore/sms/send`
**请求格式:** `application/json`
```json
{
"phone": "13800138000"
}
```
**响应:**
```json
{
"retcode": 0,
"retinfo": "发送成功"
}
```
#### 验证短信验证码
**接口:** `POST /zcore/sms/verify`
**请求格式:** `application/json`
```json
{
"phone": "13800138000",
"code": "123456"
}
```
**响应:**
```json
{
"retcode": 0,
"retinfo": "验证成功"
}
```
#### 用户注册/登录
**接口:** `POST /api/partnerh5/login`
**请求格式:** `application/x-www-form-urlencoded`
```
bean={"phone":"13800138000","loanamount":50000,"repaymentperiod":12,"loanpurpose":"个人日常消费"}
```
**响应:**
```json
{
"retcode": 0,
"result": {
"customerid": "12345",
"sessionid": "abc123xyz",
"loginPhone": "13800138000"
}
}
```
**本地存储:**
```javascript
UserCache.saveUserSession({
customerid: "12345",
sessionid: "abc123xyz",
loginPhone: "13800138000",
formData: { loanamount: 50000, ... }
})
```
**后续请求自动添加请求头:**
```
jsessionid: abc123xyz
```
---
## 四、basic-info.html - 基本信息填写页面
### 4.1 页面结构
```
┌─────────────────────────────────┐
│ 顶部进度卡片 │
│ - 预期额度: 35000 → 50000 │
│ - 进度条: 0% → 100% │
│ - 已完成: 0/8 │
└─────────────────────────────────┘
├─> 选择一项,进度+12.5%
│ 预期额度增加1875元
┌─────────────────────────────────┐
│ 资产信息区域 (渐进式显示) │
│ 1. 房产: 有房产/无房产 │
│ 2. 车辆: 有车辆/无车辆 │
│ 3. 公积金: 有/无 │
│ 4. 社保: 有/无 │
│ 5. 信用卡: 有/无 │
│ 6. 银行流水: 有/无 │
│ 7. 职业: 4个选项 │
│ 8. 芝麻分: 4个选项 │
└─────────────────────────────────┘
└─> 8项全部完成后
┌─────────────────────────────────┐
│ 基本信息区域 (自动展开) │
│ 1. 真实姓名 (输入) │
│ 2. 身份证号 (输入) │
│ 3. 所属城市 (选择器) │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 提交按钮 (基本信息完成时可用) │
└─────────────────────────────────┘
```
### 4.2 资产选项配置
```javascript
ASSET_CONFIG.ITEMS = [
{ id: 'house', name: '房产', options: ['有房产', '无房产'] },
{ id: 'car', name: '车辆', options: ['有车辆', '无车辆'] },
{ id: 'fund', name: '公积金', options: ['有公积金', '无公积金'] },
{ id: 'social', name: '社保', options: ['有社保', '无社保'] },
{ id: 'credit', name: '信用卡', options: ['有信用卡', '无信用卡'] },
{ id: 'bank', name: '银行流水', options: ['有银行流水', '无银行流水'] },
{ id: 'job', name: '职业', options: ['上班族', '自由职业', '企业主', '公务员/国企'] },
{ id: 'zhima', name: '芝麻分', options: ['700以上', '650-700', '600-650', '无'] }
]
```
### 4.3 数据映射关系
**前端中文 → 后端数字:**
```javascript
VALUE_MAPPING = {
'有房产': 1, '无房产': 2,
'有车辆': 1, '无车辆': 2,
// ... 其他类似
'上班族': 1, '自由职业': 2, '企业主': 3, '公务员/国企': 4,
'700以上': 1, '650-700': 2, '600-650': 3, '无': 4
}
```
### 4.4 渐进式显示逻辑
```javascript
selectAssetOption(itemId, value) {
// 1. 保存选择
this.selectedValues[itemId] = value;
// 2. 更新进度
const completed = Object.keys(this.selectedValues).length;
const progress = (completed / 8) * 100;
const money = 35000 + (50000 - 35000) * (progress / 100);
// 3. 检查是否是当前步骤
const currentIndex = ASSET_CONFIG.ITEMS.findIndex(item => item.id === itemId);
if (currentIndex === this.currentStep && this.currentStep < 7) {
// 延迟400ms后显示下一项
setTimeout(() => this.revealNextItem(), 400);
}
// 4. 自动保存草稿2秒后
this.autoSaveDraft();
}
```
### 4.5 城市选择逻辑
**IP定位自动填充**
```
页面加载
├─> 调用 IP定位接口
│ └─> /api/partnerh5/ip_location
│ └─> 返回 { province: "广东省", city: "深圳市" }
├─> 查询城市代码
│ └─> AreaService.findCityCode(province, city)
│ └─> 返回 { provinceCode: "440000", cityCode: "440300" }
└─> 自动填充城市字段
```
**省市区数据结构:**
```javascript
PROVINCE_CITY_DATA = {
'广东省': ['广州市', '深圳市', '珠海市', ...],
'江西省': ['南昌市', '九江市', ...],
...
}
```
---
## 五、表单提交与草稿管理
### 5.1 草稿自动保存
**触发条件:**
- 选择任一资产选项
- 修改任一基本信息
**延迟机制:** 2秒防抖
**保存接口:** `POST /api/partnerh5/save_draft`
**请求数据:**
```javascript
{
shortcode: "I3fMzX", // URL参数或默认值
formid: "uuid-xxx", // 唯一表单ID
draftstatus: 1, // 1=草稿0=正式提交
// 资产信息(映射为数字)
house: 1, car: 2, fund: 1, ...,
// 基本信息
name: "张三",
idCard: "110101199001011234",
city: "深圳市"
}
```
### 5.2 表单正式提交
**提交接口:** `POST /api/partnerh5/submit`
**请求数据结构:**
```javascript
{
shortcode: "I3fMzX",
formid: "uuid-xxx",
draftstatus: 0, // 0=正式提交
// 资产信息
house: 1, car: 2, fund: 1, social: 1,
credit: 1, bank: 1, job: 1, zhima: 2,
// 基本信息
name: "张三",
idCard: "110101199001011234",
city: "深圳市"
}
```
**响应数据:**
```json
{
"retcode": 0,
"retinfo": "提交成功",
"result": {
"formdataid": 12345, // 表单数据ID用于授权流程
"h5Urls": [ // 需要授权的H5列表可能为空
{
"apicode": "TAOBAA",
"apiname": "淘宝授权",
"h5url": "https://..."
},
{
"apicode": "JD",
"apiname": "京东授权",
"h5url": "https://..."
}
],
"redirectUrl": "https://..." // 最终跳转URLH5直推地址
}
}
```
### 5.3 表单验证规则
| 字段 | 验证规则 | 错误提示 |
|------|----------|----------|
| 手机号 | `/^1[3-9]\d{9}$/` | "请输入有效的11位手机号" |
| 姓名 | `/^[\u4e00-\u9fa5]{2,20}/` | "请输入2-20位中文姓名" |
| 身份证 | 18位身份证校验 | "请输入有效的身份证号码" |
| 验证码 | 6位数字 | "请输入6位验证码" |
---
## 六、授权流程AuthFlow
### 6.1 流程触发条件
表单提交成功且 `h5Urls` 不为空时触发。
### 6.2 授权状态枚举
```javascript
AUTH_STATUS = {
WAITING: 1, // 等待授权
CALLBACK: 2, // 已回调
APPLY_OK: 3, // 进件成功
APPLY_FAIL: 4 // 进件失败
}
```
### 6.3 授权流程图
```
表单提交成功,返回 h5Urls
┌─────────────────────────────────┐
│ 显示授权进度列表 │
│ - 当前产品: 1/3 │
│ - 状态: ● 淘宝授权 授权中... │
│ - iframe 加载 h5url │
└─────────────────────────────────┘
开始轮询授权状态
├─> 每3秒查询一次
│ └─> POST /api/partnerh5/check_auth_status
│ └─> { formdataid: 12345, apicode: "TAOBAO" }
├─> 状态判断
│ ├─> status = 1 (WAITING) → 继续轮询
│ ├─> status = 2 (CALLBACK) → 成功,下一个
│ ├─> status = 3 (APPLY_OK) → 成功,下一个
│ └─> status = 4 (APPLY_FAIL) → 失败,下一个
├─> 超时判断
│ └─> 超过10分钟 → 标记超时,下一个
└─> 全部完成
┌─────────────────────────────────┐
│ 显示完成页面 │
│ - 全部授权完成 ✓ │
│ - 5秒后自动跳转... │
│ - [立即跳转] 按钮 │
└─────────────────────────────────┘
├─> 有 redirectUrl → 跳转到指定页面
└─> 无 redirectUrl → 返回首页
```
### 6.4 授权状态查询接口
**接口:** `POST /api/partnerh5/check_auth_status`
**请求格式:** `application/x-www-form-urlencoded`
```
bean={"formdataid":12345,"apicode":"TAOBAO"}
```
**响应:**
```json
{
"retcode": 0,
"result": {
"apicode": "TAOBAO",
"apiname": "淘宝授权",
"status": 2,
"h5url": "https://..."
}
}
```
### 6.5 轮询配置
```javascript
POLL_INTERVAL = 3000 // 轮询间隔3秒
POLL_TIMEOUT = 10 * 60 * 1000 // 超时时间10分钟
COUNTDOWN_SECONDS = 5 // 完成后倒计时5秒
```
### 6.6 Mixed Content 处理
当 HTTPS 页面需要加载 HTTP 的 iframe 时:
```javascript
if (window.location.protocol === 'https:' && h5url.startsWith('http://')) {
// 检测到 Mixed Content使用新窗口打开
window.open(h5url, '_blank');
}
```
---
## 七、页面间数据传递
### 7.1 URL 参数传递
```javascript
// 从 index.html 跳转到 basic-info.html保留当前页的查询串
window.location.href = 'basic-info.html' + window.location.search;
// 跳转后 URL 参数保持不变,例如:
// basic-info.html?shortcode=I3fMzX&channel=toutiao
// 其中 shortcode 为短链编码(与后端 shortcode 对应channel 等其它参数一并保留。
```
### 7.2 本地存储传递
```javascript
// 保存用户会话
UserCache.saveUserSession({
customerid: "12345",
sessionid: "abc123",
loginPhone: "13800138000",
formData: {
loanamount: 50000,
repaymentperiod: 12,
loanpurpose: "个人日常消费"
}
});
// 读取用户会话
const session = UserCache.getUserSession();
```
### 7.3 表单ID管理
```javascript
// 生成或获取表单ID
const formId = FormIdGenerator.getOrCreate(); // uuid
// 提交成功后清除
FormIdGenerator.clear();
```
---
## 八、API 接口汇总
### 8.1 接口列表
| 接口 | 方法 | 用途 | Content-Type |
|------|------|------|--------------|
| /zcore/sms/send | POST | 发送短信验证码 | application/json |
| /zcore/sms/verify | POST | 验证短信验证码 | application/json |
| /zcore/jpush/login | POST | 极光一键登录 | application/json |
| /api/partnerh5/login | POST | 用户注册/登录 | x-www-form-urlencoded |
| /api/partnerh5/submit | POST | 提交表单 | x-www-form-urlencoded |
| /api/partnerh5/save_draft | POST | 保存草稿 | x-www-form-urlencoded |
| /api/partnerh5/get_draft | POST | 获取草稿 | x-www-form-urlencoded |
| /api/partnerh5/check_auth_status | POST | 检查授权状态 | x-www-form-urlencoded |
| /api/partnerh5/area_list | GET | 获取区域列表 | - |
| /api/partnerh5/ip_location | GET | IP定位 | - |
### 8.2 请求头规范
```javascript
// 所有请求自动添加
headers: {
'Content-Type': 'application/json' 'application/x-www-form-urlencoded',
'jsessionid': '从 UserCache 获取' // 用户登录后自动添加
}
```
### 8.3 响应格式规范
```json
{
"retcode": 0, // 0=成功,其他=失败
"retinfo": "操作成功", // 提示信息
"result": { ... } // 返回数据(可选)
}
```
---
## 九、配置说明
### 9.1 API 配置
```javascript
// src/js/config/api.config.js
BASE_URL: '' // 空字符串表示同源部署
TIMEOUT: 30000 // 请求超时30秒
```
### 9.2 应用配置
```javascript
// src/js/config/app.config.js
DEBUG_CONFIG: {
ENABLED: false, // 生产环境设为 false
SMS_CODE: '123456', // 调试模式固定验证码
DEFAULT_SHORTCODE: 'I3fMzX', // 默认短链代码
VERBOSE_LOGGING: true // 详细日志
}
LOAN_CONFIG: {
AMOUNT: { MIN: 1000, MAX: 200000, DEFAULT: 50000 },
INTEREST_RATE: { MIN: 10.8, MAX: 24 },
PERIOD_OPTIONS: [3, 6, 9, 12, 18, 24, 36]
}
ASSET_CONFIG: {
PROGRESS_MONEY: {
INITIAL: 35000, // 初始预期额度
FINAL: 50000 // 最终预期额度
}
}
```
---
## 十、关键业务逻辑
### 10.1 一键登录降级机制
```
极光一键登录初始化
├─> 成功获取手机号
│ └─> 调用注册接口
│ ├─> 成功 → 跳转
│ └─> 失败 → 显示手机号输入框
└─> 失败/不支持
└─> 隐藏一键登录按钮
└─> 显示手机号输入框
└─> 显示"立即申请"按钮
```
### 10.2 已登录用户处理
```javascript
// 页面加载时检查登录态
restoreUserData() {
const session = UserCache.getUserSession();
if (session && session.loginPhone) {
// 显示脱敏手机号
this.elements.phoneNumber.value = Formatter.maskPhone(session.loginPhone);
this.elements.phoneNumber.readOnly = true;
// 恢复表单数据
if (session.formData) {
this.elements.loanAmount.value = session.formData.loanamount;
// ...
}
}
}
```
### 10.3 协议确认逻辑
```
点击"立即申请"
├─> 已登录
│ └─> 勾选协议? ──否──> 弹协议确认窗
│ └─> 是 → 跳转基本信息页
└─> 未登录
└─> 验证手机号
└─> 勾选协议? ──否──> 弹协议确认窗
└─> 是 → 显示验证码弹窗
```
### 10.4 表单提交锁机制
```javascript
// 防止重复提交
this.isSubmitting = false;
async handleSubmit() {
if (this.isSubmitting) return; // 已在提交中
this.elements.submitBtn.disabled = true;
this.elements.submitBtn.textContent = '提交中...';
this.isSubmitting = true;
try {
const response = await DraftManager.submitForm(...);
if (response.success) {
// 处理成功
} else {
throw new Error(response.message);
}
} catch (error) {
// 恢复按钮状态
this.elements.submitBtn.disabled = false;
this.elements.submitBtn.textContent = '下一步';
this.isSubmitting = false;
}
}
```
---
## 十一、错误处理
### 11.1 短信发送失败
```
发送短信
└─> 失败
├─> 显示错误信息
├─> 倒计时显示"重新发送"
└─> 可点击重新发送
```
### 11.2 验证码错误
```
输入验证码点击确定
└─> 验证失败
├─> 显示错误提示
├─> 输入框标红
├─> 保持弹窗打开
└─> 可重新输入
```
### 11.3 表单提交失败
```
提交表单
└─> 失败
├─> Toast 提示错误信息
├─> 恢复提交按钮
├─> 不清除表单数据
└─> 可重新提交
```
---
## 十二、版本信息
- **当前版本:** 2.0.0
- **最后更新:** 2025-01-21
- **版权所有:** 北京百雅科技有限公司