Become a sponsor

概述
所有接口返回统一的 JSON 结构,通过 code 字段区分业务状态。前端应根据 code 做差异化处理。本表汇总系统全部错误码与常见错误消息(消息均来自源码中的真实文案)。
| code | ok | HTTP | 含义 | 使用场景 | 前端处理 |
|---|---|---|---|---|---|
0 | true | 200 | 成功 | 所有成功操作 | 正常处理 data |
1 | false | 200 | 一般失败 | 业务逻辑失败、FormRequest 格式错误 | 显示 msg 提示 |
401 | false | 200 | 未授权 | Token 缺失/无效/过期 | 跳转登录页 |
403 | false | 403 | 禁止访问 | 权限不足/租户异常/演示环境 | 显示无权限提示 |
404 | false | 200 | 数据不存在 | 查询单条记录为空 / 路由不存在 | 显示数据不存在 |
422 | false | 200 | 参数验证失败 | 代码内手动 Validator::validate() 不通过 | 显示具体字段错误 |
500 | false | 200 | 服务器异常 | 未捕获异常 | 显示通用错误 + 记录日志 |
响应示例详见 统一响应格式
| 消息 | code/HTTP | 原因 | 解决方案 |
|---|---|---|---|
未提供认证令牌 | 401/200 | 请求头缺少 Authorization | 携带 token(Bearer 前缀可选) |
Expired token | 401/200 | Token 超过有效期 | 调用 /api/refresh 刷新或重新登录 |
Signature verification failed | 401/200 | 密钥不匹配或令牌被篡改 | 检查 JWT_SECRET 是否一致 |
无效的access_token | 401/200 | 拿 refresh_token 访问业务接口 | 使用 access_token |
用户名或密码错误 | 1/200 | 账号或密码不正确(统一提示) | 检查输入 |
账号已被禁用 | 1/200 | 用户 status != 1 | 联系管理员 |
验证码错误 / 请输入验证码 | 1/200 | 验证码过期/不匹配/缺失 | 重新获取验证码 |
缺少refresh_token | 1/200 | 刷新未传参 | 检查请求体 |
refresh_token已失效 | 1/200 | 旧令牌已用过(一次性)或被吊销 | 用最新的 refresh_token |
用户不存在或已被禁用 | 1/200 | 刷新时用户状态异常 | 重新登录 |
旧密码错误 | 1/200 | 修改密码时旧密码不匹配 | 核对旧密码 |
| 消息 | code/HTTP | 原因 | 解决方案 |
|---|---|---|---|
无访问权限:{权限码} | 403/403 | 用户无该接口权限 | 联系管理员分配权限节点 |
用户不存在,请重新登录 | 401/200 | token 有效但用户被删除 | 重新登录 |
租户不存在 | 403/403 | 租户数据被删除 | 检查租户数据 |
租户已被禁用 | 403/403 | 租户 status != 1 | 联系平台管理员启用 |
租户已过期 | 403/403 | 租户 expire_time 已过 | 续费或联系平台 |
演示模式下禁止操作 | 403/403 | 演示模式下写操作被拦截 | 非演示环境操作 / #[DemoAllow] 放行 |
| 消息 | 原因 | 解决方案 |
|---|---|---|
{字段中文名}已存在 | 唯一性校验失败(uniqueFields) | 修改重复字段值 |
数据不存在 | 记录已被删除或不存在(_detail 404) | 刷新列表 |
缺少id参数 | update/delete 未传 id | 检查请求参数 |
请选择要删除的数据 | 批量删除时 ids 为空 | 先选择数据 |
存在子部门,不可删除 | beforeDelete 钩子校验(DeptService) | 先删除子部门 |
存在子菜单,不可删除 | 菜单删除保护(MenuService) | 先删除子菜单 |
存在子分类,不可删除 | 分类删除保护(CategoryService,仅校验子级) | 先删除子分类 |
该租户下存在用户,不可删除 | 租户删除保护 | 先迁移或删除租户用户 |
已达到最大用户数限制 | 租户用户数达到 max_user_num | 调大租户用户上限或清理用户 |
旧密码错误 | 个人中心改密 | 核对旧密码 |
| 消息 | code | 原因 | 解决方案 |
|---|---|---|---|
参数验证失败 | 1 | FormRequest rules 不通过(data 为字段明细) | 按 data 修正入参 |
请选择文件 | 1 | 上传未带 file | 检查表单文件字段 |
不支持的文件格式,允许:... | 1 | 扩展名不在白名单 | 转换格式或调整 FILE_EXT |
文件大小不能超过 NMB | 1 | 超过 FILE_MAX_SIZE | 压缩文件或调大限制 |
不支持的上传类型,允许:image,file | 1 | type 参数非法 | 传 image 或 file |
请求过于频繁,请 N 秒后重试 | 1 | 验证码获取触发 IP 限流 | 等待窗口期 |
模板不存在 | 1 | 按编码查文件模板为空 | 检查 code |
| 消息 | code | 原因 | 解决方案 |
|---|---|---|---|
操作失败(生产) | 500 | 未知异常(详情在服务端日志) | 查 storage/logs/ 日志定位 |
异常原文(仅 APP_DEBUG=true) | 500 | 调试模式返回真实异常 | 生产必须关闭 APP_DEBUG |
// Axios 响应拦截器(ui/src/utils/http/axios/index.ts)
import { useMessage } from '@/hooks/web/useMessage';
const $message = useMessage();
axios.interceptors.response.use(response => {
const { code, msg, data } = response.data;
if (code === 0) {
return data; // 成功
}
if (code === 401 || response.status === 401) {
// Token 过期,跳转登录
router.push('/login');
return;
}
if (code === 403 || response.status === 403) {
$message.error(msg || '无权限');
return;
}
// 其他错误
$message.error(msg || '操作失败');
return Promise.reject(new Error(msg));
});排查顺序
接口异常时按序确认:HTTP 状态(403 直接处理)→ code(401 跳转登录,其余按分支)→ msg(展示给用户)→ data(验证错误明细 / 服务端日志)。