496 lines
15 KiB
Markdown
496 lines
15 KiB
Markdown
# Design Document
|
||
|
||
## Overview
|
||
|
||
本设计文档描述前端 API 接口调用重构的技术方案。核心目标是将分散在页面和组件中的接口调用统一收敛到 `common/server/` 模块,实现接口集中管理、统一错误处理,并在改造过程中生成完整的 API 清单用于核对 C# 后端迁移完整性。
|
||
|
||
## Architecture
|
||
|
||
### 整体架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Pages / Components │
|
||
│ (pages/shouye/*.vue, pages/user/*.vue, components/*.vue) │
|
||
└─────────────────────────────┬───────────────────────────────┘
|
||
│ import { funcName } from '@/common/server/xxx.js'
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Server Modules │
|
||
│ common/server/ │
|
||
│ ├── auth.js (登录、注册、绑定) │
|
||
│ ├── user.js (用户信息、VIP) │
|
||
│ ├── goods.js (商品列表、详情、奖品) │
|
||
│ ├── order.js (订单、抽奖) │
|
||
│ ├── warehouse.js (仓库、回收、发货) │
|
||
│ ├── coupon.js (优惠券) │
|
||
│ ├── welfare.js (福利屋、签到、任务) │
|
||
│ ├── collection.js(收藏) │
|
||
│ ├── address.js (收货地址) - 已有 userAddress.js │
|
||
│ ├── config.js (系统配置) - 已有 │
|
||
│ ├── rank.js (排行榜) │
|
||
│ ├── invitation.js(邀请) │
|
||
│ ├── redeem.js (兑换码) │
|
||
│ └── pay.js (支付) │
|
||
└─────────────────────────────┬───────────────────────────────┘
|
||
│ RequestManager.get/post()
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ RequestManager │
|
||
│ common/request.js │
|
||
│ - 统一请求封装 │
|
||
│ - 签名机制 │
|
||
│ - 错误处理 │
|
||
│ - Content-Type: application/json │
|
||
└─────────────────────────────┬───────────────────────────────┘
|
||
│ HTTP Request
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ C# Backend API │
|
||
│ http://localhost:5238/api/* │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 调用流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant P as Page/Component
|
||
participant S as Server Module
|
||
participant R as RequestManager
|
||
participant B as Backend API
|
||
|
||
P->>S: import { getGoodsList } from '@/common/server/goods.js'
|
||
P->>S: await getGoodsList({ page: 1, type: 0 })
|
||
S->>R: RequestManager.post('/goods_list', data)
|
||
R->>R: 添加签名参数 (timestamp, nonce, sign)
|
||
R->>B: POST /api/goods_list (JSON)
|
||
B-->>R: { status: 1, data: {...} }
|
||
R-->>S: response
|
||
S->>S: 统一错误处理
|
||
S-->>P: 标准格式结果
|
||
```
|
||
|
||
## Components and Interfaces
|
||
|
||
### 1. RequestManager (已有,需修改)
|
||
|
||
**文件**: `honey_box/common/request.js`
|
||
|
||
**修改内容**: POST 请求 Content-Type 改为 `application/json`
|
||
|
||
```javascript
|
||
// 修改前
|
||
if (method.toUpperCase() == 'POST') {
|
||
header = {
|
||
'content-type': 'application/x-www-form-urlencoded',
|
||
// ...
|
||
}
|
||
}
|
||
|
||
// 修改后
|
||
if (method.toUpperCase() == 'POST') {
|
||
header = {
|
||
'content-type': 'application/json',
|
||
// ...
|
||
}
|
||
}
|
||
```
|
||
|
||
**接口保持不变**:
|
||
- `static get(url, data, showLoading)` - GET 请求
|
||
- `static post(url, data, showLoading)` - POST 请求
|
||
- `static getCache(url, data, showLoading)` - 带缓存的 GET 请求
|
||
|
||
### 2. Server Module 标准结构
|
||
|
||
每个 Server Module 遵循统一的结构:
|
||
|
||
```javascript
|
||
// common/server/xxx.js
|
||
import RequestManager from '../request';
|
||
|
||
/**
|
||
* 函数名使用语义化命名
|
||
* @param {Object} params 请求参数
|
||
* @returns {Promise<Object>} 标准响应格式
|
||
*/
|
||
export const getFunctionName = async (params = {}) => {
|
||
const res = await RequestManager.post('/api_endpoint', params);
|
||
return res;
|
||
};
|
||
|
||
// 如需数据处理
|
||
export const getProcessedData = async (params = {}) => {
|
||
const res = await RequestManager.post('/api_endpoint', params);
|
||
if (res.status === 1 && res.data) {
|
||
// 数据处理逻辑
|
||
return res.data;
|
||
}
|
||
return null;
|
||
};
|
||
```
|
||
|
||
### 3. Server Modules 详细设计
|
||
|
||
#### 3.1 auth.js (认证模块)
|
||
|
||
```javascript
|
||
// 微信登录
|
||
export const wxLogin = async (code, userInfo) => {...}
|
||
|
||
// 手机号登录
|
||
export const mobileLogin = async (mobile, code) => {...}
|
||
|
||
// 微信绑定手机号
|
||
export const bindMobileByWx = async (encryptedData, iv) => {...}
|
||
|
||
// 验证码绑定手机号
|
||
export const bindMobileByCode = async (mobile, code) => {...}
|
||
|
||
// 记录登录
|
||
export const recordLogin = async () => {...}
|
||
|
||
// 账号注销
|
||
export const logOff = async () => {...}
|
||
```
|
||
|
||
#### 3.2 user.js (用户模块 - 扩展已有)
|
||
|
||
```javascript
|
||
// 已有
|
||
export const getUserInfo = async () => {...}
|
||
|
||
// 新增
|
||
export const updateUserInfo = async (data) => {...}
|
||
export const getVipList = async () => {...}
|
||
export const getProfitMoney = async (page) => {...}
|
||
export const getProfitIntegral = async (page) => {...}
|
||
export const getProfitScore = async (page) => {...}
|
||
export const getProfitPay = async (page) => {...}
|
||
```
|
||
|
||
#### 3.3 goods.js (商品模块)
|
||
|
||
```javascript
|
||
// 商品列表
|
||
export const getGoodsList = async (params) => {...}
|
||
|
||
// 商品详情
|
||
export const getGoodsDetail = async (goodsId) => {...}
|
||
|
||
// 子奖品列表
|
||
export const getGoodsChildren = async (goodsId) => {...}
|
||
|
||
// 商品扩展配置
|
||
export const getGoodsExtend = async (goodsId) => {...}
|
||
|
||
// 箱号列表
|
||
export const getGoodsNumList = async (goodsId) => {...}
|
||
|
||
// 箱号详情
|
||
export const getGoodsNumDetail = async (goodsId, goodsNum) => {...}
|
||
|
||
// 奖品数量统计
|
||
export const getGoodsPrizeCount = async (goodsId) => {...}
|
||
|
||
// 奖品内容
|
||
export const getGoodsPrizeContent = async (goodsId, shangId) => {...}
|
||
|
||
// 中奖记录
|
||
export const getGoodsPrizeLogs = async (params) => {...}
|
||
```
|
||
|
||
#### 3.4 order.js (订单模块 - 扩展已有)
|
||
|
||
```javascript
|
||
// 一番赏金额计算
|
||
export const calcOrderMoney = async (params) => {...}
|
||
|
||
// 一番赏下单
|
||
export const createOrder = async (params) => {...}
|
||
|
||
// 无限赏金额计算
|
||
export const calcInfiniteOrderMoney = async (params) => {...}
|
||
|
||
// 无限赏下单
|
||
export const createInfiniteOrder = async (params) => {...}
|
||
|
||
// 商城金额计算
|
||
export const calcMallOrderMoney = async (params) => {...}
|
||
|
||
// 订单列表
|
||
export const getOrderList = async (page, pageSize) => {...}
|
||
|
||
// 订单详情
|
||
export const getOrderDetail = async (orderId) => {...}
|
||
|
||
// 抽奖结果
|
||
export const getPrizeOrderLog = async (orderId) => {...}
|
||
|
||
// 无限赏抽奖结果
|
||
export const getInfinitePrizeOrderLog = async (orderId) => {...}
|
||
|
||
// 道具卡抽奖
|
||
export const useItemCard = async (params) => {...}
|
||
```
|
||
|
||
#### 3.5 warehouse.js (仓库模块)
|
||
|
||
```javascript
|
||
// 仓库首页
|
||
export const getWarehouseIndex = async (params) => {...}
|
||
|
||
// 回收奖品
|
||
export const recoveryPrize = async (ids) => {...}
|
||
|
||
// 发货奖品
|
||
export const sendPrize = async (params) => {...}
|
||
|
||
// 确认发货
|
||
export const confirmSend = async (params) => {...}
|
||
|
||
// 发货记录
|
||
export const getSendRecord = async (page) => {...}
|
||
|
||
// 发货详情
|
||
export const getSendRecordDetail = async (id) => {...}
|
||
|
||
// 回收记录
|
||
export const getRecoveryRecord = async (page) => {...}
|
||
|
||
// 物流信息
|
||
export const getLogistics = async (orderId) => {...}
|
||
```
|
||
|
||
#### 3.6 coupon.js (优惠券模块)
|
||
|
||
```javascript
|
||
// 优惠券列表
|
||
export const getCouponList = async (params) => {...}
|
||
|
||
// 优惠券详情
|
||
export const getCouponDetail = async (couponId) => {...}
|
||
|
||
// 领取优惠券
|
||
export const receiveCoupon = async (couponId) => {...}
|
||
|
||
// 分享优惠券
|
||
export const shareCoupon = async (couponId) => {...}
|
||
|
||
// 合成优惠券
|
||
export const synthesisCoupon = async (ids) => {...}
|
||
|
||
// 计算合成
|
||
export const calcSynthesis = async (ids) => {...}
|
||
```
|
||
|
||
#### 3.7 welfare.js (福利模块)
|
||
|
||
```javascript
|
||
// 福利屋列表
|
||
export const getWelfareHouseList = async () => {...}
|
||
|
||
// 福利屋详情
|
||
export const getWelfareHouseDetail = async (goodsId) => {...}
|
||
|
||
// 福利屋下单
|
||
export const buyWelfareHouse = async (params) => {...}
|
||
|
||
// 签到信息
|
||
export const getSignInfo = async () => {...}
|
||
|
||
// 执行签到
|
||
export const doSign = async () => {...}
|
||
|
||
// 任务列表
|
||
export const getTaskList = async () => {...}
|
||
|
||
// 领取任务奖励
|
||
export const receiveTaskReward = async (taskId) => {...}
|
||
```
|
||
|
||
#### 3.8 collection.js (收藏模块)
|
||
|
||
```javascript
|
||
// 收藏列表
|
||
export const getCollectionList = async (page) => {...}
|
||
|
||
// 添加收藏
|
||
export const addCollection = async (goodsId) => {...}
|
||
|
||
// 取消收藏
|
||
export const cancelCollection = async (goodsId) => {...}
|
||
|
||
// 收藏状态
|
||
export const getCollectionStatus = async (goodsId) => {...}
|
||
```
|
||
|
||
#### 3.9 rank.js (排行榜模块)
|
||
|
||
```javascript
|
||
// 排行榜列表
|
||
export const getRankList = async (type, page) => {...}
|
||
```
|
||
|
||
#### 3.10 invitation.js (邀请模块)
|
||
|
||
```javascript
|
||
// 邀请信息
|
||
export const getInvitationInfo = async () => {...}
|
||
|
||
// 邀请记录
|
||
export const getInvitationRecord = async (page) => {...}
|
||
```
|
||
|
||
#### 3.11 redeem.js (兑换码模块)
|
||
|
||
```javascript
|
||
// 使用兑换码
|
||
export const useRedeemCode = async (code) => {...}
|
||
```
|
||
|
||
#### 3.12 pay.js (支付模块)
|
||
|
||
```javascript
|
||
// 微信支付
|
||
export const wxPay = async (orderId) => {...}
|
||
|
||
// 余额支付
|
||
export const balancePay = async (orderId) => {...}
|
||
|
||
// 充值
|
||
export const recharge = async (amount) => {...}
|
||
```
|
||
|
||
## Data Models
|
||
|
||
### 标准响应格式
|
||
|
||
```typescript
|
||
interface ApiResponse<T> {
|
||
status: number; // 1=成功, 0=失败, -1=未登录, -9=需绑定手机号
|
||
msg: string; // 提示信息
|
||
data: T; // 业务数据
|
||
}
|
||
|
||
interface PaginatedResponse<T> {
|
||
status: number;
|
||
msg: string;
|
||
data: {
|
||
data: T[]; // 列表数据
|
||
total: number; // 总数
|
||
currentPage: number;
|
||
lastPage: number;
|
||
};
|
||
}
|
||
```
|
||
|
||
### 状态码定义
|
||
|
||
| 状态码 | 含义 | 处理方式 |
|
||
|-------|------|---------|
|
||
| 1 | 成功 | 返回数据 |
|
||
| 0 | 业务失败 | 显示 msg 提示 |
|
||
| -1 | 未登录 | 跳转登录页 |
|
||
| -9 | 需绑定手机号 | 跳转绑定页 |
|
||
| 2222 | 特殊状态 | 返回数据 |
|
||
|
||
## Correctness Properties
|
||
|
||
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||
|
||
### Property 1: POST 请求使用 JSON 格式
|
||
|
||
*For any* POST 请求通过 RequestManager 发送, 请求头的 Content-Type 应为 `application/json`
|
||
|
||
**Validates: Requirements 1.1**
|
||
|
||
### Property 2: 签名机制一致性
|
||
|
||
*For any* 请求数据对象, 生成的签名应与原有算法保持一致:参数按键名排序 → 拼接为 key=value& 格式 → 添加密钥(host+timestamp) → MD5 加密
|
||
|
||
**Validates: Requirements 1.2**
|
||
|
||
### Property 3: 响应格式一致性
|
||
|
||
*For any* API 响应, 返回格式应包含 status、msg、data 三个字段,且 status 为数字类型
|
||
|
||
**Validates: Requirements 1.3**
|
||
|
||
### Property 4: 错误处理统一性
|
||
|
||
*For any* 接口调用返回 status !== 1 的响应, Server Module 应返回包含 status 和 msg 的标准格式对象
|
||
|
||
**Validates: Requirements 2.3, 6.2**
|
||
|
||
## Error Handling
|
||
|
||
### RequestManager 层错误处理
|
||
|
||
RequestManager 已实现以下错误处理逻辑(保持不变):
|
||
|
||
1. **网络异常**: 显示 "网络连接异常,请检查网络"
|
||
2. **status = 0**: 显示 msg 提示
|
||
3. **status = -1**: 清除 token,跳转登录页
|
||
4. **status = -9**: 显示 msg,跳转绑定手机号页面
|
||
5. **请求失败**: 显示错误信息
|
||
|
||
### Server Module 层错误处理
|
||
|
||
Server Module 不额外处理错误,直接返回 RequestManager 的响应:
|
||
|
||
```javascript
|
||
export const getGoodsList = async (params) => {
|
||
// 直接返回,错误由 RequestManager 统一处理
|
||
return await RequestManager.post('/goods_list', params);
|
||
};
|
||
```
|
||
|
||
如需数据转换,在成功时处理:
|
||
|
||
```javascript
|
||
export const getUserInfo = async () => {
|
||
const res = await RequestManager.post('/user');
|
||
if (res.status === 1 && res.data) {
|
||
// 数据处理
|
||
res.data.money = common.removeTrailingZeros(res.data.money);
|
||
return res.data;
|
||
}
|
||
return null;
|
||
};
|
||
```
|
||
|
||
## Testing Strategy
|
||
|
||
### 单元测试
|
||
|
||
由于前端 UniApp 项目的特殊性,主要通过以下方式验证:
|
||
|
||
1. **手动测试**: 在开发环境使用 eruda 调试工具检查请求
|
||
2. **接口测试**: 使用 Postman/curl 验证接口响应格式
|
||
|
||
### 测试用例
|
||
|
||
| 测试项 | 预期结果 |
|
||
|-------|---------|
|
||
| POST 请求 Content-Type | application/json |
|
||
| 签名参数存在 | timestamp, nonce, sign |
|
||
| 成功响应格式 | { status: 1, msg: "...", data: {...} } |
|
||
| 错误响应处理 | 显示 msg 提示 |
|
||
| 未登录处理 | 跳转登录页 |
|
||
| 需绑定手机号处理 | 跳转绑定页 |
|
||
|
||
### 集成测试清单
|
||
|
||
按模块逐一测试:
|
||
|
||
1. **认证模块**: 登录、绑定手机号
|
||
2. **用户模块**: 获取用户信息、VIP 信息
|
||
3. **商品模块**: 商品列表、详情、奖品
|
||
4. **订单模块**: 下单、支付、抽奖结果
|
||
5. **仓库模块**: 回收、发货
|
||
6. **优惠券模块**: 列表、领取、合成
|
||
7. **福利模块**: 签到、任务
|
||
8. **其他模块**: 收藏、排行榜、邀请、兑换码
|