Skip to content

业务状态码说明 ​

📋 概述 ​

本项目使用业务状态码(Business Code)来标识 API 响应的业务状态,与 HTTP 状态码配合使用,提供更精确的错误识别和处理。

🔢 状态码分类 ​

业务状态码采用以下分类规则:

  • 0: 成功
  • 1xxx: 客户端错误
  • 2xxx: 服务器错误
  • 3xxx: 业务逻辑错误(预留)

📊 业务状态码列表 ​

成功状态 ​

状态码说明HTTP 状态码
0操作成功200/201

客户端错误 (1xxx) ​

通用错误 (10xx) ​

状态码说明HTTP 状态码
1000请求参数错误400
1001未授权401
1003禁止访问403
1004资源不存在404
1005数据验证失败400
1009资源冲突409

认证相关错误 (11xx) ​

状态码说明HTTP 状态码
1101用户名或密码错误401
1102Token 已过期401
1103Token 无效401
1104用户不存在404
1105用户已存在409
1106邮箱已被注册409
1107用户名已被使用409
1108无法注册管理员账户403

资源相关错误 (12xx) ​

状态码说明HTTP 状态码
1201资源不存在404
1202资源已存在409

服务器错误 (2xxx) ​

状态码说明HTTP 状态码
2000服务器内部错误500
2001数据库错误500
2002外部服务错误500

📝 响应格式 ​

成功响应 ​

json
{
  "code": 0,
  "success": true,
  "data": {
    // 响应数据
  },
  "message": "success",
  "timestamp": "2025-10-16T08:00:00.000Z"
}

错误响应 ​

json
{
  "code": 1106,
  "success": false,
  "statusCode": 409,
  "message": "邮箱已被注册",
  "errors": null,
  "timestamp": "2025-10-16T08:00:00.000Z",
  "path": "/api/auth/register"
}

🔍 使用示例 ​

1. 注册接口 - 邮箱已存在 ​

请求:

bash
curl -X POST http://localhost:3001/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@example.com",
    "username": "newuser",
    "password": "Password123!"
  }'

响应:

json
{
  "code": 1106,
  "success": false,
  "statusCode": 409,
  "message": "邮箱已被注册",
  "errors": null,
  "timestamp": "2025-10-16T08:00:00.000Z",
  "path": "/api/auth/register"
}

2. 登录接口 - 凭证错误 ​

请求:

bash
curl -X POST http://localhost:3001/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "admin",
    "password": "wrongpassword"
  }'

响应:

json
{
  "code": 1101,
  "success": false,
  "statusCode": 401,
  "message": "用户名或密码错误",
  "errors": null,
  "timestamp": "2025-10-16T08:00:00.000Z",
  "path": "/api/auth/login"
}

3. 成功登录 ​

请求:

bash
curl -X POST http://localhost:3001/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "admin",
    "password": "admin123"
  }'

响应:

json
{
  "code": 0,
  "success": true,
  "data": {
    "user": {
      "id": "uuid",
      "email": "admin@example.com",
      "username": "admin",
      "role": "ADMIN"
    },
    "token": {
      "accessToken": "eyJhbGc...",
      "refreshToken": "eyJhbGc...",
      "expiresIn": "15m"
    }
  },
  "message": "success",
  "timestamp": "2025-10-16T08:00:00.000Z"
}

🛠️ 前端处理建议 ​

TypeScript 类型定义 ​

typescript
// 响应类型定义
interface ApiResponse<T = any> {
  code: number;
  success: boolean;
  data?: T;
  message: string;
  timestamp: string;
  // 错误响应特有字段
  statusCode?: number;
  errors?: any;
  path?: string;
}

// 业务状态码枚举
enum BusinessCode {
  SUCCESS = 0,
  BAD_REQUEST = 1000,
  UNAUTHORIZED = 1001,
  FORBIDDEN = 1003,
  NOT_FOUND = 1004,
  VALIDATION_ERROR = 1005,
  CONFLICT = 1009,
  INVALID_CREDENTIALS = 1101,
  TOKEN_EXPIRED = 1102,
  TOKEN_INVALID = 1103,
  USER_NOT_FOUND = 1104,
  EMAIL_ALREADY_EXISTS = 1106,
  USERNAME_ALREADY_EXISTS = 1107,
  CANNOT_REGISTER_ADMIN = 1108,
  // ...其他状态码
}

错误处理示例 ​

typescript
async function handleApiResponse<T>(response: ApiResponse<T>): Promise<T> {
  if (response.success && response.code === 0) {
    return response.data!;
  }

  // 根据业务状态码处理不同错误
  switch (response.code) {
    case BusinessCode.EMAIL_ALREADY_EXISTS:
      throw new Error('该邮箱已被注册,请使用其他邮箱');

    case BusinessCode.USERNAME_ALREADY_EXISTS:
      throw new Error('该用户名已被使用,请选择其他用户名');

    case BusinessCode.INVALID_CREDENTIALS:
      throw new Error('用户名或密码错误,请重试');

    case BusinessCode.TOKEN_EXPIRED:
      // 跳转到登录页
      router.push('/login');
      throw new Error('登录已过期,请重新登录');

    default:
      throw new Error(response.message || '操作失败');
  }
}

// 使用示例
try {
  const result = await handleApiResponse(await loginApi(credentials));
  console.log('登录成功:', result);
} catch (error) {
  console.error('登录失败:', error.message);
}

Axios 拦截器示例 ​

typescript
import axios from 'axios';

const api = axios.create({
  baseURL: 'http://localhost:3001/api',
});

// 响应拦截器
api.interceptors.response.use(
  (response) => {
    const data = response.data as ApiResponse;

    // 检查业务状态码
    if (data.code !== 0) {
      // 特殊处理 Token 过期
      if (data.code === BusinessCode.TOKEN_EXPIRED) {
        localStorage.removeItem('token');
        window.location.href = '/login';
      }

      return Promise.reject(new Error(data.message));
    }

    return response;
  },
  (error) => {
    if (error.response) {
      const data = error.response.data as ApiResponse;
      console.error('API Error:', {
        code: data.code,
        message: data.message,
        path: data.path,
      });
    }
    return Promise.reject(error);
  },
);

export default api;

📚 扩展状态码 ​

如需添加新的业务状态码:

  1. 在 src/common/constants/business-codes.ts 中的 BusinessCode 枚举添加新状态码
  2. 在 BUSINESS_CODE_MESSAGES 中添加对应的描述信息
  3. 如果是常见的 HTTP 错误,更新 HTTP_TO_BUSINESS_CODE 映射
  4. 在业务代码中抛出异常时使用自定义状态码

示例:

typescript
// 添加新的业务状态码
export enum BusinessCode {
  // ...已有状态码
  PAYMENT_REQUIRED = 1301,
  PAYMENT_FAILED = 1302,
}

export const BUSINESS_CODE_MESSAGES: Record<BusinessCode, string> = {
  // ...已有消息
  [BusinessCode.PAYMENT_REQUIRED]: '需要付费',
  [BusinessCode.PAYMENT_FAILED]: '支付失败',
};

// 在业务代码中使用
throw new BadRequestException({
  message: '支付失败,请检查账户余额',
  code: BusinessCode.PAYMENT_FAILED,
});

🔐 安全建议 ​

  1. 不要在错误消息中暴露敏感信息:如数据库错误、文件路径等
  2. 统一认证错误消息:如用户名或密码错误时,不要区分是用户名还是密码错误
  3. 记录详细日志:服务器端记录完整的错误堆栈,但客户端只返回简化信息
  4. 限制错误尝试次数:对于登录等敏感操作,考虑添加频率限制

最后更新: 2025-10-16 版本: 1.0.0