错误码与附录
统一响应结构、全局异常映射、认证失败特殊格式、业务错误码总索引,以及引擎 / 语言 / 枚举 / 配置项速查附录。
11.1 统一响应结构
除流式接口(SSE)、文件流(术语导出、文档下载)与支付回调直写响应外,
所有 /api/** 接口的返回体均由 ApiResponseAdvice 统一包装为以下结构:
{
"success": true, // 业务是否成功,客户端首先判断此字段
"message": "操作成功", // 人读提示信息
"data": { ... }, // 业务数据(各章节详述),可为 null
"error": null // 成功时为 null;失败时为错误码字符串或错误明细对象
}
HTTP/1.1 402 Payment Required
{
"success": false,
"message": "点数不足,无法完成当前操作",
"data": "点数余额与会员配额不足,无法完成本次翻译", // data 承载具体原因
"error": "INSUFFICIENT_POINTS" // error 承载机器可读错误码
}
{
"data": [ ... ], // 当前页数据数组
"page": 0, // 页码,0 起始
"size": 10, // 每页条数
"totalPages": 3, // 总页数
"totalElements": 25 // 总条数
}
⚠️ 例外:/api/moments/list 的 data 是自定义 {total, page, size, items} 结构(见 10.4),
对话历史等个别接口分页字段命名以各章描述为准。
11.2 全局异常处理器(GlobalExceptionHandler)
异常处理器以 @RestControllerAdvice(annotations = RestController.class) 声明,
只拦截 API 控制器(@RestController),页面控制器(@Controller)的异常走独立的错误页流程,不受本表影响。
映射关系如下(HTTP 状态码 → error 错误码):
| 异常类型 | HTTP | error 错误码 | message / data 说明 |
|---|---|---|---|
MethodArgumentNotValidException | 400 | VALIDATION_ERROR | message=「参数验证失败」;data 为 {字段名: 错误提示} 明细 Map |
BindException | 400 | BIND_ERROR | message=「数据绑定失败」;data 同样为字段级错误 Map |
MethodArgumentTypeMismatchException | 400 | TYPE_MISMATCH | data=「参数 '{name}' 的值 '{value}' 无法转换为 {类型}」 |
MaxUploadSizeExceededException | 413 | FILE_TOO_LARGE | message=「文件上传大小超限」 |
IllegalArgumentException | 400 | ILLEGAL_ARGUMENT | message=「非法参数」;data=异常原始消息(如「订单不存在」「分类不合法」) |
IllegalStateException | 409 | ILLEGAL_STATE | message=「非法状态」;data=异常原始消息(如「订单已过期」「操作繁忙,请稍后重试」) |
InsufficientPointsException | 402 | INSUFFICIENT_POINTS | message=「点数不足,无法完成当前操作」;data=具体不足原因(点数 / 会员配额分别说明) |
NullPointerException | 500 | NULL_POINTER | message=「系统内部错误」;data=固定提示语(不泄露堆栈) |
RuntimeException | 500 | RUNTIME_ERROR | message=「系统内部错误」;data=固定提示语 |
Exception(兜底) | 500 | INTERNAL_ERROR | message=「系统内部错误」;data=「系统发生未知异常,请联系管理员」 |
💡 除上述统一映射外,个别控制器还有控制器内自行捕获的分支(优先于全局处理器生效):
如 /api/user/** 将 ResourceNotFoundException 捕获为 404 USER_NOT_FOUND,
/api/moments/** 将业务异常捕获为 MOMENTS_LIST_FAILED / MOMENTS_CREATE_FAILED(见第 10 章)。
11.3 认证失败的特殊响应(401)
⚠️ 这是全系统唯一不走 ApiResponse 包装的 JSON 错误响应: 认证拦截器(AuthTokenInterceptor,见第 02 章)在 Token 缺失 / 过期 / 非法时直接写回原生 JSON:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{ "code": 401, "message": "Missing token" } // 未携带 Token
{ "code": 401, "message": "Token expired" } // Token 已过期
{ "code": 401, "message": "Session expired" } // 会话失效
{ "code": 401, "message": "Invalid token" } // Token 非法
客户端解析错误响应时务必同时兼容两种结构:拦截器 401 用 code/message,
业务错误用 success/message/error。判断依据可用 HTTP 状态码先行分流。
/api/auth/** // 注册、登录、Token 签发 /api/payment/** // 支付发起、确认、回调(第 09 章) /api/membership/order // 创建订单(第 09 章) /api/membership/order/** // 订单查询 /api/translation/platform-stats // 平台公开统计 /static/** /css/** /js/** /images/** /webjars/** /favicon.ico /error
11.4 业务错误码总索引
汇总各章出现的错误码与典型触发场景,便于客户端统一建表处理:
| error 错误码 | HTTP | 典型场景 | 详见 |
|---|---|---|---|
VALIDATION_ERROR | 400 | @Valid 请求体校验失败(各 DTO 的 @Pattern / @Size / @NotBlank) | 11.2 |
BIND_ERROR | 400 | 表单 / 查询参数绑定失败 | 11.2 |
TYPE_MISMATCH | 400 | 路径变量 / 参数类型转换失败 | 11.2 |
ILLEGAL_ARGUMENT | 400 | 业务参数非法:订单不存在、动态内容为空、方案 ordinal 越界等 | 09 / 10 / 08 |
INVALID_ARGUMENT | 400 | 动态发布 content 缺失(控制器内捕获) | 10.4 |
UNAUTHORIZED | 401 | 控制器内 UserContext 为空(ApiResponse 结构的未登录响应) | 10.2-10.4 |
FILE_TOO_LARGE | 413 | 上传文件超过大小限制(文档 50MB / 单请求 100MB) | 04 / 11.2 |
USER_NOT_FOUND | 404 | 用户不存在(ResourceNotFoundException) | 10.2 |
ILLEGAL_STATE | 409 | 状态冲突:订单非待支付 / 已过期、会员配额乐观锁重试超限、DashScope Key 未配置 | 09 / 08 / 07 |
INSUFFICIENT_POINTS | 402 | 点数与会员配额合计不足以完成翻译 / 会员配额不足 / 无有效会员 | 08 |
MOMENTS_LIST_FAILED | 500 | 动态列表查询内部异常 | 10.4 |
MOMENTS_CREATE_FAILED | 500 | 动态发布内部异常(含图片存储失败) | 10.4 |
NULL_POINTER / RUNTIME_ERROR / INTERNAL_ERROR | 500 | 服务端内部错误(固定提示语,不泄露细节) | 11.2 |
async function apiFetch(url, options = {}) { const resp = await fetch(url, { ...options, headers: { 'Authorization': `Bearer ${token}`, ...options.headers } }); // 拦截器 401:原生 JSON {code, message},需要重新登录 if (resp.status === 401) { redirectToLogin(); return; } // SSE / 文件流等非 JSON 响应直接透传 const ct = resp.headers.get('content-type') || ''; if (!ct.includes('application/json')) return resp; const body = await resp.json(); if (!body.success) { // body.error 为错误码,body.message 人读提示,body.data 常为具体原因 throw new ApiError(body.error, body.message, body.data, resp.status); } return body.data; }
11.5 附录 A:翻译引擎与支持语言
当前版本固定开放这两个引擎;传其他值或留空时由服务端按默认策略选择。 引擎不可用时自动降级为 Mock 占位结果并退还扣点(见 8.1)。
zh(中文) en(英语) ja(日语) ko(韩语) fr(法语) de(德语) es(西班牙语) ru(俄语) ar(阿拉伯语) pt(葡萄牙语)
所有语言相关字段(源 / 目标语言、术语语言对、用户默认语言配置)均使用上述小写代码;
源语言传 auto 时自动检测(检测语言不入库、不回写请求参数)。
11.6 附录 B:核心枚举速查
| 枚举 | 取值 | 出现位置 / 注意点 |
|---|---|---|
MembershipTypeEnum | MONTHLY / QUARTERLY / YEARLY | ⚠️ 双重表示:/api/membership/plans 与 subscribe 用 ordinal(0/1/2);订单创建用枚举名字符串(见 8.3 / 9.3) |
MembershipStatusEnum | ACTIVE(1) / EXPIRED(2) / CANCELLED(3) | 会员实体 status 字段 |
OrderStatusEnum | PENDING(1) / PAID(2) / CANCELLED(3) / REFUNDED(4) | 订单状态机(见 9.5) |
PaymentStatusEnum | INITIATED / SUCCESS / FAILED | 支付记录状态 |
PaymentProviderEnum | ALIPAY / WECHAT / UNIONPAY / STRIPE / PAYPAL / OTHER | 支付渠道(当前可用渠道由 PaymentChannelRegistry 决定) |
PaymentMethodEnum | ALIPAY_APP / ALIPAY_QR / WECHAT_APP / WECHAT_QR / UNIONPAY_QR / CREDIT_CARD / BANK_TRANSFER / OTHER | 支付方式 |
ProcessingStatusEnum | 7 个状态(文档任务生命周期) | 文档翻译任务(见第 04 章) |
| 术语分类 category | TECHNOLOGY / BUSINESS / MEDICAL / LEGAL / FINANCE / EDUCATION / SCIENCE / GENERAL | 正则限定的 8 个分类(见第 06 章) |
11.7 附录 C:关键配置项速查
| 配置键 | 默认值 | 作用 | 详见 |
|---|---|---|---|
server.port | 8090 | 服务监听端口 | 01 |
app.points.text-chars-per-point | 1000 | 文本翻译每点对应字符数(≤0 时兜底 1 点/次) | 08 |
app.points.document-points-per-page | 1 | 文档翻译每页扣点 | 08 |
app.membership.monthly-quota | 5000 | 会员单月配额基数(包季 ×3 / 包年 ×12) | 08 |
app.membership.subscribe-bonus-points | 5000 | 开通会员一次性赠点 | 08 |
ai.dashscope.api-key | — | 通义千问引擎密钥;未配置时翻译走 Mock 降级、质量评估直接 409 | 03 / 07 |
pay.alipay.enabled | false | 支付宝真实渠道路由开关;关闭时 confirm 不发起线上查单 | 09 |
| SSE 相关 | 30s 连接超时 / 20s 心跳 | 对话翻译流式通道(chat-sse- 线程池 8 核心 / 20 最大 / 500 队列) | 05 |
| 上传限制 | 单文件 50MB / 单请求 100MB | 超出即 413 FILE_TOO_LARGE | 04 |
🎉 至此全文完。建议从「第 01 章 快速开始」入手跑通第一个翻译请求, 再按业务域选读第 03-09 章;集成期遇到任何非 2xx 响应,可对照 11.4 的错误码索引快速定位。