签入版本

This commit is contained in:
小蚂蚁云
2026-10-20 21:19:44 +08:00
commit 7e70ed6942
2943 changed files with 498573 additions and 0 deletions
@@ -0,0 +1,328 @@
# 8.1 统一响应格式
:::tip 概述
所有接口返回统一的 JSON 结构,前端可据此判断请求结果。响应由 `response\Result` 类构造,保证全系统格式一致。
:::
## 响应结构
```typescript
interface ApiResponse {
code: number; // 状态码:0=成功,其他=失败
ok: boolean; // 是否成功
msg: string; // 提示信息
data: any; // 响应数据
}
```
## 状态码定义
| code | 含义 | ok | 说明 |
|------|------|-----|------|
| `0` | 成功 | `true` | 业务成功 |
| `1` | 业务失败 | `false` | 通用业务错误(参数错误、业务校验失败等) |
| `401` | 未授权 | `false` | Token 缺失/无效/过期 |
| `403` | 禁止访问 | `false` | 权限不足 |
| `404` | 数据不存在 | `false` | 查询的记录不存在 |
| `422` | 参数验证失败 | `false` | Validate 校验不通过 |
## 成功响应
### 普通成功
```json
{
"code": 0,
"ok": true,
"msg": "操作成功",
"data": {
"id": 1,
"username": "admin",
"realname": "管理员",
"createTime": "2026-01-01 00:00:00"
}
}
```
### 带自定义消息
```json
{
"code": 0,
"ok": true,
"msg": "添加成功",
"data": { "id": 42 }
}
```
### data 为 null
```json
{
"code": 0,
"ok": true,
"msg": "修改成功",
"data": null
}
```
### data 为数组
```json
{
"code": 0,
"ok": true,
"msg": "操作成功",
"data": [
{ "id": 1, "name": "选项一" },
{ "id": 2, "name": "选项二" }
]
}
```
## 分页响应
```json
{
"code": 0,
"ok": true,
"msg": "操作成功",
"data": {
"records": [
{ "id": 1, "username": "admin", "createTime": "2026-01-01 00:00:00" },
{ "id": 2, "username": "user1", "createTime": "2026-01-02 00:00:00" }
],
"total": 100,
"size": 20,
"current": 1,
"pages": 5
}
}
```
### 分页参数
| 字段 | 类型 | 说明 |
|------|------|------|
| `records` | array | 当前页数据列表 |
| `total` | number | 总记录数 |
| `size` | number | 每页条数 |
| `current` | number | 当前页码 |
| `pages` | number | 总页数(`ceil(total / size)`) |
## 失败响应
### 通用业务失败
```json
{
"code": 1,
"ok": false,
"msg": "用户名已存在",
"data": null
}
```
### 参数验证失败
```json
{
"code": 1,
"ok": false,
"msg": "参数验证失败",
"data": "用户名不能为空"
}
```
### 批量验证失败
```json
{
"code": 1,
"ok": false,
"msg": "参数验证失败",
"data": [
"用户名不能为空",
"姓名不能为空"
]
}
```
### 数据不存在
```json
{
"code": 1,
"ok": false,
"msg": "数据不存在",
"data": null
}
```
### 未授权(Token 问题)
```json
{
"code": 401,
"ok": false,
"msg": "token已过期",
"data": null
}
```
### 权限不足
```json
{
"code": 403,
"ok": false,
"msg": "无访问权限",
"data": null
}
```
## 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | number | 状态码,0=成功 |
| `ok` | boolean | 是否成功 |
| `msg` | string | 提示信息 |
| `data` | any | 响应数据,失败时通常为 null 或错误详情 |
### 分页字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `data.records` | array | 分页数据列表 |
| `data.total` | number | 总记录数 |
| `data.size` | number | 每页条数 |
| `data.current` | number | 当前页码 |
| `data.pages` | number | 总页数 |
## 后端 Result 类
**文件:** `extend/response/Result.php`
### 方法清单
| 方法 | 说明 | code | ok |
|------|------|------|-----|
| `success($data, $msg)` | 成功响应 | 0 | true |
| `page($records, $total, $current, $size)` | 分页响应 | 0 | true |
| `fail($msg, $code, $data)` | 失败响应 | 1 | false |
| `unauthorized($msg)` | 未授权 | 401 | false |
| `forbidden($msg)` | 禁止访问 | 403 | false |
| `notFound($msg)` | 数据不存在 | 404 | false |
| `validateError($errors)` | 验证失败 | 422 | false |
### 使用示例
```php
// Controller 中通过 BaseController 调用
$this->success($data, '操作成功');
$this->success(['id' => $id], '添加成功');
$this->fail('用户名已存在');
$this->fail('缺少id参数', 1);
// 直接调用 Result 类
\response\Result::success($data);
\response\Result::fail('错误信息');
\response\Result::unauthorized('未授权');
\response\Result::forbidden('无权限');
\response\Result::notFound('数据不存在');
\response\Result::validateError('用户名不能为空');
```
## 驼峰自动转换
响应数据自动从 snake_case 转为 camelCase:
| 数据库字段 | 响应字段 |
|-----------|---------|
| `create_time` | `createTime` |
| `user_name` | `userName` |
| `is_delete` | `isDelete` |
| `dept_id` | `deptId` |
可通过 `config/api.php` 的 `camel_snake_convert` 配置关闭。
## 前端响应处理
### Axios 响应拦截器
```typescript
// src/utils/http/axios/index.ts
axios.interceptors.response.use(
response => {
const { code, data, msg } = response.data;
// 成功
if (code === 0) {
return data;
}
// Token 过期
if (code === 401) {
localStorage.clear();
router.push('/login');
return Promise.reject(new Error(msg));
}
// 权限不足
if (code === 403) {
ElMessage.error(msg || '无访问权限');
return Promise.reject(new Error(msg));
}
// 其他业务错误
ElMessage.error(msg || '操作失败');
return Promise.reject(new Error(msg));
},
error => {
ElMessage.error('网络错误');
return Promise.reject(error);
}
);
```
### 处理分页数据
```typescript
const fetchData = async () => {
const res = await getUserPage({ pageNo: 1, pageSize: 20 });
// res 已经是 data 部分(拦截器已解包)
tableData.value = res.records;
total.value = res.total;
};
```
### 处理导入结果
```typescript
const handleImport = async (file) => {
const res = await importUsers(file);
// res = { count: 8, errors: [{ row: 3, reason: "..." }] }
if (res.errors.length === 0) {
ElMessage.success(`成功导入 ${res.count} 条`);
} else {
ElMessage.warning(`成功 ${res.count} 条,失败 ${res.errors.length} 条`);
}
};
```
## 常见响应场景汇总
| 场景 | code | msg | data |
|------|------|-----|------|
| 查询成功 | 0 | 操作成功 | 数据对象/数组 |
| 分页成功 | 0 | 操作成功 | {records, total, size, current, pages} |
| 新增成功 | 0 | 添加成功 | {id: 42} |
| 修改成功 | 0 | 修改成功 | null |
| 删除成功 | 0 | 删除成功 | null |
| 业务失败 | 1 | 具体错误信息 | null |
| 参数验证失败 | 1 | 参数验证失败 | 错误字符串或数组 |
| Token 过期 | 401 | token已过期 | null |
| 无权限 | 403 | 无访问权限 | null |
| 数据不存在 | 404 | 数据不存在 | null |