249 lines
7.0 KiB
Markdown
249 lines
7.0 KiB
Markdown
|
|
# Mock 数据一致性说明
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本文档说明 Mock 数据与后端 API 响应结构的一致性处理方式,确保 Mock 数据能够准确模拟真实后端行为。
|
|||
|
|
|
|||
|
|
## 数据结构一致性
|
|||
|
|
|
|||
|
|
### 1. 标准响应格式
|
|||
|
|
|
|||
|
|
所有 Mock 接口统一使用以下响应格式:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
{
|
|||
|
|
code: 0, // 响应码:0成功,-1失败
|
|||
|
|
msg: 'success', // 响应消息
|
|||
|
|
data: {...} // 响应数据
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 分页响应格式
|
|||
|
|
|
|||
|
|
分页接口使用以下格式:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
{
|
|||
|
|
code: 0,
|
|||
|
|
msg: 'success',
|
|||
|
|
data: {
|
|||
|
|
records: [...], // 当前页数据列表
|
|||
|
|
total: 100, // 总记录数
|
|||
|
|
size: 10, // 每页大小
|
|||
|
|
current: 1, // 当前页码
|
|||
|
|
pages: 10 // 总页数
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 数据一致性处理工具
|
|||
|
|
|
|||
|
|
### 分页处理
|
|||
|
|
|
|||
|
|
`paginatedResponse()` 函数提供完整的分页处理能力:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import { paginatedResponse } from '@/mocks/utils/handler-utils'
|
|||
|
|
|
|||
|
|
// 支持的参数
|
|||
|
|
{
|
|||
|
|
current: 1, // 当前页码(必需)
|
|||
|
|
size: 10, // 每页大小(必需)
|
|||
|
|
orderField: 'createTime', // 排序字段
|
|||
|
|
orderingRule: 'desc', // 排序规则:asc/desc
|
|||
|
|
orderFields: ['field1', 'field2'], // 多字段排序
|
|||
|
|
orderSorts: ['asc', 'desc'], // 多字段排序规则
|
|||
|
|
custom: { // 自定义筛选条件
|
|||
|
|
status: 1
|
|||
|
|
},
|
|||
|
|
likeParamMap: { // 模糊查询条件
|
|||
|
|
name: '关键词'
|
|||
|
|
},
|
|||
|
|
inParamMap: { // IN 查询条件
|
|||
|
|
status: [1, 2, 3]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 排序处理
|
|||
|
|
|
|||
|
|
`sortData()` 函数支持:
|
|||
|
|
|
|||
|
|
- **单字段排序**:`{ field: 'name', order: 'asc' }`
|
|||
|
|
- **多字段排序**:`{ fields: ['name', 'age'], orders: ['asc', 'desc'] }`
|
|||
|
|
- **嵌套字段排序**:`field: 'user.address.city'`
|
|||
|
|
- **类型处理**:自动识别数字、日期、字符串类型进行排序
|
|||
|
|
|
|||
|
|
### 筛选处理
|
|||
|
|
|
|||
|
|
`filterData()` 函数支持:
|
|||
|
|
|
|||
|
|
- **精确匹配**:`{ status: 1 }`
|
|||
|
|
- **模糊查询**:`{ likeParamMap: { name: '关键词' } }`
|
|||
|
|
- **IN 查询**:`{ inParamMap: { status: [1, 2] } }`
|
|||
|
|
- **自定义筛选**:`{ customParamMap: { customFn: (item) => item.value > 100 } }`
|
|||
|
|
|
|||
|
|
## 数据类型映射
|
|||
|
|
|
|||
|
|
### 企业数据结构
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
{
|
|||
|
|
companyId: 'com_xxx', // 企业ID
|
|||
|
|
pripid: 'prip_xxx', // 原系统ID
|
|||
|
|
uniscid: '91440100...', // 统一社会信用代码
|
|||
|
|
entname: '企业名称', // 市场主体名称
|
|||
|
|
regstate: '1', // 企业状态码
|
|||
|
|
regstateCn: '存续', // 企业状态(中文)
|
|||
|
|
industryphy: 'C', // 行业门类码
|
|||
|
|
industryphyCn: '制造业', // 行业门类(中文)
|
|||
|
|
riskLevel: '1', // 风险等级码
|
|||
|
|
riskLevelCn: '低风险', // 风险等级(中文)
|
|||
|
|
riskScore: 75, // 风险评分
|
|||
|
|
estdate: '2020-01-01', // 成立时间
|
|||
|
|
createTime: '...', // 创建时间
|
|||
|
|
updateTime: '...' // 更新时间
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 用户数据结构
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
{
|
|||
|
|
userId: 'user_xxx', // 用户ID
|
|||
|
|
username: 'admin', // 用户名
|
|||
|
|
realName: '张三', // 真实姓名
|
|||
|
|
account: 'admin', // 账号
|
|||
|
|
department: 'xxx', // 部门
|
|||
|
|
roleId: 'role_xxx', // 角色ID
|
|||
|
|
roleName: '管理员', // 角色名称
|
|||
|
|
status: 1, // 状态
|
|||
|
|
createTime: '...', // 创建时间
|
|||
|
|
lastLoginTime: '...' // 最后登录时间
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 加载延迟模拟
|
|||
|
|
|
|||
|
|
所有 Mock 接口都使用随机延迟(200-800ms)来模拟真实网络请求:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import { mockDelay, handlePaginatedRequest } from '@/mocks/utils/handler-utils'
|
|||
|
|
|
|||
|
|
// 方式1:使用 mockDelay
|
|||
|
|
await mockDelay()
|
|||
|
|
|
|||
|
|
// 方式2:使用 handlePaginatedRequest(自动延迟)
|
|||
|
|
const response = await handlePaginatedRequest(data, request)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 错误场景模拟
|
|||
|
|
|
|||
|
|
### 1. 业务错误响应
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import { errorResponse } from '@/mocks/utils/handler-utils'
|
|||
|
|
|
|||
|
|
return res(
|
|||
|
|
ctx.status(200),
|
|||
|
|
ctx.json(errorResponse('参数错误', -1))
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. HTTP 状态码
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
return res(
|
|||
|
|
ctx.status(401), // 未授权
|
|||
|
|
ctx.json({ code: 401, msg: '未登录或登录已过期' })
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 模拟特定错误
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 登录失败
|
|||
|
|
if (username !== 'admin' || password !== '123456') {
|
|||
|
|
return res(
|
|||
|
|
ctx.status(200),
|
|||
|
|
ctx.json(errorResponse('用户名或密码错误'))
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Mock 数据验证检查清单
|
|||
|
|
|
|||
|
|
在开发 Mock 数据时,确保以下项目:
|
|||
|
|
|
|||
|
|
- [ ] **响应格式**:使用 `successResponse()` 和 `errorResponse()` 统一格式
|
|||
|
|
- [ ] **分页结构**:使用 `paginatedResponse()` 包含 records/total/size/current/pages
|
|||
|
|
- [ ] **字段命名**:字段名与后端 API 文档保持一致(驼峰命名)
|
|||
|
|
- [ ] **数据类型**:数字字段使用 `Number`,日期使用 ISO 字符串格式
|
|||
|
|
- [ ] **枚举值**:状态码、类型码等与后端定义一致
|
|||
|
|
- [ ] **关联关系**:外键 ID 格式正确,如 `companyId`、`userId`
|
|||
|
|
- [ ] **分页支持**:支持排序、筛选、模糊查询等后端功能
|
|||
|
|
- [ ] **延迟模拟**:使用 `mockDelay()` 或 `handlePaginatedRequest()` 模拟网络延迟
|
|||
|
|
- [ ] **错误处理**:提供合理的错误响应场景
|
|||
|
|
|
|||
|
|
## Mock 数据维护建议
|
|||
|
|
|
|||
|
|
1. **数据量控制**:每个模块生成 20-100 条 Mock 数据
|
|||
|
|
2. **数据真实性**:使用 `@faker-js/faker` 生成接近真实的测试数据
|
|||
|
|
3. **字段完整性**:确保 Mock 数据包含后端返回的所有字段
|
|||
|
|
4. **边界测试**:提供空数据、单页数据、多页数据等边界情况
|
|||
|
|
5. **定期更新**:后端 API 变更时同步更新 Mock 数据结构
|
|||
|
|
|
|||
|
|
## 使用示例
|
|||
|
|
|
|||
|
|
### 完整的分页请求处理
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
export const examplePaginatedHandler = rest.post(
|
|||
|
|
getBasePath('/example/list'),
|
|||
|
|
async (req, res, ctx) => {
|
|||
|
|
const body = await getRequestBody(req)
|
|||
|
|
const response = await handlePaginatedRequest(mockData, body)
|
|||
|
|
|
|||
|
|
return res(
|
|||
|
|
ctx.status(200),
|
|||
|
|
ctx.json(successResponse(response))
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 带筛选的处理
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
export const exampleFilterHandler = rest.post(
|
|||
|
|
getBasePath('/example/list'),
|
|||
|
|
async (req, res, ctx) => {
|
|||
|
|
const body = await getRequestBody(req)
|
|||
|
|
|
|||
|
|
// 自定义筛选逻辑
|
|||
|
|
const result = filterData(mockData, body, {
|
|||
|
|
likeParamMap: { name: body.keyword },
|
|||
|
|
inParamMap: { status: [1, 2] }
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return res(
|
|||
|
|
ctx.status(200),
|
|||
|
|
ctx.json(successResponse(result))
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 工具函数速查表
|
|||
|
|
|
|||
|
|
| 函数 | 说明 | 参数 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `successResponse(data, msg)` | 成功响应 | data, msg |
|
|||
|
|
| `errorResponse(msg, code)` | 错误响应 | msg, code |
|
|||
|
|
| `paginate(data, size, current)` | 简单分页 | data, size, current |
|
|||
|
|
| `paginatedResponse(data, request)` | 完整分页(排序+筛选) | data, request |
|
|||
|
|
| `sortData(data, options)` | 排序 | data, { field, order, fields, orders } |
|
|||
|
|
| `filterData(data, filters, options)` | 筛选 | data, filters, { customParamMap, likeParamMap, inParamMap } |
|
|||
|
|
| `handlePaginatedRequest(data, request)` | 处理分页请求(带延迟) | data, request |
|
|||
|
|
| `mockDelay()` | 模拟延迟 | 无 |
|
|||
|
|
| `getNestedValue(obj, path)` | 获取嵌套属性 | obj, 'path.to.field' |
|