API 交互规范
本文档定义了 CondorAdmin 前后端的 API 交互规范,包括请求格式、响应格式、错误码、认证机制等。
基础约定
协议与编码
- 协议:HTTP/HTTPS
- 方法:GET(查询)、POST(创建/更新/删除)
- 编码:UTF-8
- Content-Type:
application/json或multipart/form-data(文件上传)
Base URL
bash
# 开发环境
http://localhost:5566
# 生产环境
https://api.yourdomain.com配置位置:
- 前端:
.env.test或.env.prod中的VITE_SERVICE_BASE_URL - 后端:Webman 默认监听
0.0.0.0:8787
统一响应格式
成功响应
所有成功的 API 响应必须遵循以下格式:
json
{
"code": "0000",
"msg": "ok",
"data": {
// 业务数据
}
}字段说明:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| code | string/int | 是 | 状态码,"0000" 或 0 表示成功 |
| msg | string | 是 | 提示信息(支持多语言) |
| data | any | 是 | 业务数据(可为 null、对象、数组) |
失败响应
json
{
"code": 4000,
"msg": "参数错误",
"data": []
}分页响应
json
{
"code": "0000",
"msg": "ok",
"data": {
"total": 100,
"list": [
{ "id": 1, "name": "张三" },
{ "id": 2, "name": "李四" }
]
}
}分页字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| total | int | 总记录数 |
| list | array | 当前页数据列表 |
错误码规范
标准错误码
| 错误码 | HTTP Status | 说明 | 处理方式 |
|---|---|---|---|
| 0000 | 200 | 成功 | 正常处理 |
| 401 | 401 | 未登录/Token 过期 | 跳转登录页 |
| 403 | 403 | 权限不足 | 显示提示,禁用操作 |
| 4000 | 200 | 业务错误 | 显示错误提示 |
| 4001 | 200 | 参数错误 | 显示错误提示 |
| 4002 | 200 | 数据不存在 | 显示错误提示 |
| 4003 | 200 | 数据已存在 | 显示错误提示 |
| 5000 | 500 | 服务器内部错误 | 显示通用错误提示 |
后端错误码定义
后端使用 Backend 基类提供的方法返回统一格式:
php
// 成功
return $this->success('操作成功', ['id' => 1]);
// 等同于
return $this->json('0000', '操作成功', ['id' => 1]);
// 失败
return $this->fail('参数错误');
// 等同于
return $this->json(4000, '参数错误', []);前端错误处理
typescript
// src/service/request.ts
instance.interceptors.response.use(
(response) => {
const { code, msg, data } = response.data;
// 成功
if (code === '0000' || code === 0) {
return { data, error: null };
}
// Token 过期
if (code === 401) {
window.$message?.error(msg || '登录已过期,请重新登录');
useAuthStore().resetStore();
return { data: null, error: { code, msg } };
}
// 权限不足
if (code === 403) {
window.$message?.warning(msg || '权限不足');
return { data: null, error: { code, msg } };
}
// 业务错误
window.$message?.error(msg || '操作失败');
return { data: null, error: { code, msg } };
},
(error) => {
// 网络错误
window.$message?.error('网络请求失败');
return { data: null, error };
}
);认证机制
Token 认证
1. 获取 Token(登录)
请求:
http
POST /api/condoradmin/account/login
Content-Type: application/json
{
"username": "admin",
"password": "MTIzNDU2", // RSA 加密后的 Base64
"captcha": "abcd"
}响应:
json
{
"code": "0000",
"msg": "登录成功",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 7200
}
}2. 使用 Token
前端在所有需要认证的请求中携带 Token:
http
GET /api/condoradmin/admin/index
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Accept-Language: zh-CN请求头说明:
| Header | 必填 | 说明 |
|---|---|---|
| Authorization | 是 | Bearer <token> 格式 |
| Accept-Language | 否 | 语言标识(zh-CN, en-US) |
3. Token 验证流程
前端发送请求(带 Token)
↓
后端 AuthToken 中间件
↓
1. 解析 Authorization Header
↓
2. 从 Redis 查询 Token:admin:token:{token}
↓
3. 判断 Token 是否有效
├─ 有效 → 继续执行
└─ 无效/过期 → 返回 401密码加密
前端 RSA 加密
typescript
import JSEncrypt from 'jsencrypt';
// 1. 获取公钥
const { data } = await fetchGetPublicKey();
// 2. 加密密码
const jse = new JSEncrypt();
jse.setPublicKey(data.publicKey);
const encryptedPassword = jse.encrypt(password);
// 3. 提交登录
await fetchLogin({
username,
password: encryptedPassword, // Base64 字符串
captcha
});后端 RSA 解密
php
// 1. 读取私钥
$privateKey = config('plugin.condoradmin.condor.rsa_private_key');
// 2. 解密密码
openssl_private_decrypt(
base64_decode($encryptedPassword),
$decrypted,
$privateKey
);
// 3. 验证密码
password_verify($decrypted, $user->password);请求规范
GET 请求(查询)
列表查询
http
GET /api/condoradmin/admin/index?page=1&limit=20&username=admin&status=1通用参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | int | 否 | 页码(默认 1) |
| limit | int | 否 | 每页数量(默认 10,最大 500) |
| orderBy | string | 否 | 排序字段(默认主键) |
| order | string | 否 | 排序方式(asc/desc,默认 desc) |
搜索参数(由 $searchable 定义):
php
protected array $searchable = [
'username' => ['type' => 'string'], // 精确匹配
'nickname' => ['type' => 'string'], // 精确匹配
'status' => ['type' => 'int'], // 精确匹配
'created_at' => ['type' => 'timestamp'], // 时间范围
];高级搜索:
http
# LIKE 模糊查询
GET /api/admin/index?username=LIKE,admin
# IN 查询
GET /api/admin/index?status=IN,[1,2]
# BETWEEN 查询(时间戳)
GET /api/admin/index?created_at=[1640995200,1643673599]
# BETWEEN 查询(日期)
GET /api/admin/index?created_at=[2022-01-01,2022-01-31]Selectpage(下拉选择)
http
GET /api/condoradmin/admin/selectpage?nickname=admin&id=1,2,3响应:
json
{
"code": "0000",
"msg": "ok",
"data": {
"total": 10,
"list": [
{ "id": 1, "nickname": "管理员" },
{ "id": 2, "nickname": "运营" }
]
}
}POST 请求(操作)
添加
http
POST /api/condoradmin/admin/add
Content-Type: application/json
{
"username": "zhangsan",
"nickname": "张三",
"password": "123456",
"status": 1,
"role_ids": [1, 2]
}响应:
json
{
"code": "0000",
"msg": "添加成功",
"data": {
"id": 10
}
}编辑
http
POST /api/condoradmin/admin/edit
Content-Type: application/json
{
"id": 10,
"nickname": "张三三",
"status": 1
}删除
http
POST /api/condoradmin/admin/del
Content-Type: application/json
{
"ids": [10, 11, 12]
}或单个删除:
http
POST /api/condoradmin/admin/del
Content-Type: application/json
{
"id": 10
}批量操作
http
POST /api/condoradmin/admin/multi
Content-Type: application/json
{
"ids": [10, 11, 12],
"field": "status",
"value": 1
}或批量更新多个字段:
http
POST /api/condoradmin/admin/multi
Content-Type: application/json
{
"ids": [10, 11, 12],
"values": {
"status": 1,
"group_id": 2
}
}文件上传
http
POST /api/condoradmin/attachment/upload
Content-Type: multipart/form-data
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="image.jpg"
Content-Type: image/jpeg
<binary data>
------WebKitFormBoundary
Content-Disposition: form-data; name="type_id"
1
------WebKitFormBoundary--响应:
json
{
"code": "0000",
"msg": "上传成功",
"data": {
"id": 123,
"url": "/uploads/2024/01/01/abc123.jpg",
"full_url": "https://cdn.example.com/uploads/2024/01/01/abc123.jpg",
"filename": "image.jpg",
"filesize": 102400,
"storage": "local"
}
}多语言支持
前端请求头
http
Accept-Language: zh-CN支持的语言:
zh-CN:简体中文en-US:英文
后端响应
后端自动根据 Accept-Language 返回对应语言的提示信息:
php
// 使用 trans() 函数
return $this->fail(trans('condoradmin.parameter.can.not.be.empty'));对应翻译文件:
plugin/condoradmin/app/i18n/zh-cn.phpplugin/condoradmin/app/i18n/en.php
数据验证
后端验证
使用 Respect\Validation 库:
php
// Model 中定义验证规则
public static function rules(): array
{
return [
'username' => Validator::stringType()->length(3, 20),
'email' => Validator::email(),
'status' => Validator::intVal()->between(0, 1),
];
}
// Controller 中自动验证
protected $modelValidate = true; // 启用模型验证验证失败响应:
json
{
"code": 4001,
"msg": "username must have a length between 3 and 20",
"data": []
}前端验证
使用 Naive UI 表单验证:
typescript
const rules = {
username: [
{ required: true, message: '请输入用户名' },
{ min: 3, max: 20, message: '用户名长度为 3-20 个字符' }
],
email: [
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '邮箱格式不正确' }
]
};常见场景
场景1:获取列表数据
typescript
// 前端
const { data, error } = await fetchAdminList({
page: 1,
limit: 20,
username: 'admin',
status: 1
});
if (!error) {
tableData.value = data.list;
total.value = data.total;
}php
// 后端(Backend 基类已实现)
public function index(Request $request)
{
// 自动处理分页、搜索、排序
// 自动应用数据权限
}场景2:提交表单
typescript
// 前端
const handleSubmit = async () => {
const { error } = await fetchAdminAdd({
username: form.username,
nickname: form.nickname,
password: form.password
});
if (!error) {
window.$message?.success('添加成功');
router.back();
}
};php
// 后端(Backend 基类已实现)
public function add(Request $request)
{
// 自动验证参数
// 自动填充 dataLimitField
// 自动事务处理
}场景3:权限控制
typescript
// 前端
import { useAuthStore } from '@/store';
const authStore = useAuthStore();
// 检查权限
if (authStore.hasPermission('admin:add')) {
// 显示添加按钮
}php
// 后端(AuthPermission 中间件自动处理)
protected $noNeedLogin = ['login']; // 无需登录
protected $noNeedRight = ['profile']; // 无需权限(但需登录)调试工具
开发环境
- 后端调试模式:
.env中设置APP_DEBUG=true - 前端网络面板:Chrome DevTools → Network
- API 文档工具:Postman/Apifox
日志查看
bash
# 后端日志
tail -f runtime/logs/webman.log
# 前端控制台
console.log(response)最佳实践
- 统一错误处理:使用响应拦截器统一处理错误
- Loading 状态:API 调用时显示 Loading
- 防抖节流:搜索、滚动等高频操作使用防抖/节流
- 请求取消:路由切换时取消未完成的请求
- 错误重试:网络错误时自动重试(限次数)
- 参数校验:前后端双重校验,前端优化体验,后端保证安全
相关文档
- 📖 整体架构
- 🔐 权限系统
- 🔧 中间件系统
- 📦 Backend 控制器