@aqcq/error-codes
v1.1.1
Published
统一错误码管理库 - 支持多语言、类型安全的错误处理工具
Downloads
18
Maintainers
Readme
@aqcq/error-codes
这是一个用于统一管理Express项目集合中错误码的 TypeScript 库。该库提供了标准化的错误码定义、多语言支持和完整的错误处理工具。
特性
- 🌐 多语言支持: 支持中文和英文错误消息
- 📋 分类管理: 按错误性质分类管理错误码(而非业务类型)
- 🔧 类型安全: 完整的 TypeScript 类型定义
- 🚀 零依赖: 不依赖外部库
- 📊 HTTP 状态码映射: 错误码与 HTTP 状态码的智能映射
- 🛠️ 工具函数: 丰富的错误处理工具函数
- 🎯 Express 集成: 提供 Express 错误处理中间件
设计理念
🎯 按错误性质分类,而非业务类型
传统的按业务分类(如 CHAT_ERROR_CODES、FILE_ERROR_CODES)存在以下问题:
- 业务耦合严重:新业务需要创建新的错误码分类
- 错误码重复:相似错误在不同业务中被重复定义
- 维护成本高:业务变更时需要修改错误码结构
- 复用性差:错误码难以跨业务使用
✅ 新的设计原则
- 按错误性质分类:根据错误的本质特征进行分类
- 高度可复用:同一错误码可在多个业务中使用
- 易于维护:错误码结构稳定,不随业务变化
- 覆盖全面:预先定义常见的错误场景
错误码分类
1xxx - 系统内部错误码
用于系统级别的错误:
1001INTERNAL_ERROR - 内部服务器错误1002SERVICE_UNAVAILABLE - 服务暂时不可用1003CONFIGURATION_ERROR - 系统配置错误1004EXTERNAL_SERVICE_ERROR - 外部服务调用失败
2xxx - 参数验证错误码
用于请求参数验证相关的错误:
2001INVALID_PARAMETER - 参数无效2002MISSING_PARAMETER - 缺少必需参数2003PARAMETER_OUT_OF_RANGE - 参数超出范围2004INVALID_FORMAT - 格式不正确2005CONTENT_TOO_LONG - 内容过长2006UNSUPPORTED_TYPE - 不支持的类型
4xxx - 认证授权错误码
用于身份认证和权限相关的错误:
4001UNAUTHORIZED - 未授权访问4002INVALID_TOKEN - 无效的令牌4003TOKEN_EXPIRED - 令牌已过期4004INSUFFICIENT_PERMISSIONS - 权限不足4005ACCOUNT_DISABLED - 账户已被禁用4006INVALID_CREDENTIALS - 认证信息无效4007ACCOUNT_LOCKED - 账户已锁定4008SESSION_EXPIRED - 会话已过期4009ACCOUNT_NOT_FOUND - 账户不存在
41xx - 用户错误码
用于用户相关的错误
4001INVALID_INVITE_CODE - 邀请码无效
5xxx - 资源管理错误码
用于资源相关的错误:
5001RESOURCE_NOT_FOUND - 资源不存在5002RESOURCE_ALREADY_EXISTS - 资源已存在5003RESOURCE_CREATION_FAILED - 资源创建失败5004RESOURCE_UPDATE_FAILED - 资源更新失败5005RESOURCE_DELETE_FAILED - 资源删除失败5006RESOURCE_IN_USE - 资源正在使用中5007RESOURCE_LOCKED - 资源已被锁定
6xxx - 配额限制错误码
用于各种限制和配额相关的错误:
6001RATE_LIMIT_EXCEEDED - 请求频率超出限制6002QUOTA_EXCEEDED - 配额已用完6003STORAGE_QUOTA_EXCEEDED - 存储空间不足6004CONCURRENT_LIMIT_EXCEEDED - 并发数超出限制6005DAILY_LIMIT_EXCEEDED - 日使用量超出限制6006SIZE_LIMIT_EXCEEDED - 大小超出限制
7xxx - 数据处理错误码
用于数据处理相关的错误:
7001DATA_CORRUPTION - 数据已损坏7002SERIALIZATION_ERROR - 数据序列化失败7003DESERIALIZATION_ERROR - 数据反序列化失败7004DATA_TRANSFORMATION_ERROR - 数据转换失败7005CHECKSUM_MISMATCH - 校验和不匹配
8xxx - 业务逻辑错误码
用于业务逻辑相关的错误:
8001BUSINESS_RULE_VIOLATION - 违反业务规则8002OPERATION_NOT_ALLOWED - 操作不被允许8003PREREQUISITE_NOT_MET - 前置条件未满足8004WORKFLOW_ERROR - 工作流错误8005STATE_CONFLICT - 状态冲突
9xxx - 第三方服务错误码
用于第三方服务集成相关的错误:
9001AI_SERVICE_ERROR - AI服务调用失败9002AI_MODEL_UNAVAILABLE - AI模型不可用9003PAYMENT_SERVICE_ERROR - 支付服务错误9004SMS_SERVICE_ERROR - 短信服务错误9005EMAIL_SERVICE_ERROR - 邮件服务错误9006CLOUD_STORAGE_ERROR - 云存储服务错误9007TRANSLATION_SERVICE_ERROR - 翻译服务错误9008WECHAT_API_ERROR - 微信接口调用失败
安装
npm install @aqcq/error-codes基本使用方法
错误码查询
import { getErrorCode, createErrorResponse, AppError } from '@sqcq/error-codes';
// 获取错误码信息
const error = getErrorCode('RESOURCE_NOT_FOUND', 'zh-CN');
console.log(error);
// { code: 5001, message: '资源不存在', statusCode: 404, description: '请求的资源不存在' }
// 创建错误响应
const response = createErrorResponse('UNAUTHORIZED', 'zh-CN', '/api/user');
console.log(response);
// { success: false, code: 4001, message: '未授权访问', timestamp: 1699123456789, path: '/api/user' }
// 抛出应用错误
throw new AppError('AI_SERVICE_ERROR', 'zh-CN');跨业务复用示例
import {
RESOURCE_ERROR_CODES,
VALIDATION_ERROR_CODES,
AUTH_ERROR_CODES,
createErrorResponse
} from '@sqcq/error-codes';
// 聊天业务中使用
function getChatConversation(id: string) {
if (!id) {
return createErrorResponse('MISSING_PARAMETER', 'zh-CN');
}
if (!conversation) {
return createErrorResponse('RESOURCE_NOT_FOUND', 'zh-CN');
}
if (!hasPermission) {
return createErrorResponse('INSUFFICIENT_PERMISSIONS', 'zh-CN');
}
}
// 文件业务中使用相同的错误码
function getFile(id: string) {
if (!id) {
return createErrorResponse('MISSING_PARAMETER', 'zh-CN');
}
if (!file) {
return createErrorResponse('RESOURCE_NOT_FOUND', 'zh-CN');
}
if (!hasPermission) {
return createErrorResponse('INSUFFICIENT_PERMISSIONS', 'zh-CN');
}
}业务场景映射
不同业务场景可以使用相同的通用错误码:
// 聊天业务错误映射
const ChatServiceErrors = {
CONVERSATION_NOT_FOUND: 'RESOURCE_NOT_FOUND',
MESSAGE_TOO_LONG: 'CONTENT_TOO_LONG',
AI_MODEL_UNAVAILABLE: 'AI_MODEL_UNAVAILABLE',
DAILY_LIMIT_EXCEEDED: 'DAILY_LIMIT_EXCEEDED'
} as const;
// 文件业务错误映射
const FileServiceErrors = {
FILE_NOT_FOUND: 'RESOURCE_NOT_FOUND',
FILE_TOO_LARGE: 'SIZE_LIMIT_EXCEEDED',
INVALID_FILE_TYPE: 'UNSUPPORTED_TYPE',
STORAGE_QUOTA_EXCEEDED: 'STORAGE_QUOTA_EXCEEDED'
} as const;
// 用户业务错误映射
const UserServiceErrors = {
USER_NOT_FOUND: 'RESOURCE_NOT_FOUND',
USER_ALREADY_EXISTS: 'RESOURCE_ALREADY_EXISTS',
INVALID_PASSWORD: 'INVALID_CREDENTIALS',
ACCOUNT_LOCKED: 'ACCOUNT_LOCKED'
} as const;Express.js 错误处理
我们提供了专门为 Express 设计的错误处理中间件,让你可以直接通过 throw 抛出错误,自动返回标准化的错误响应给前端。
基本用法
import express from 'express';
import {
AppError,
expressErrorHandler,
asyncHandler,
throwError,
throwIf,
assert,
createSuccessResponse
} from '@aqcq/error-codes';
const app = express();
app.use(express.json());
// 使用错误处理中间件(必须在所有路由之后)
app.use(expressErrorHandler('zh-CN', true));
// 路由示例
app.get('/api/users/:id', asyncHandler(async (req, res) => {
const id = parseInt(req.params.id);
// 方式1: 使用传统的 throw new AppError()
if (isNaN(id)) {
throw new AppError('INVALID_PARAMETER', 'zh-CN', true, { field: 'id' });
}
const user = await findUser(id);
if (!user) {
throw new AppError('RESOURCE_NOT_FOUND', 'zh-CN', true, { userId: id });
}
res.json(createSuccessResponse(user, '获取用户成功'));
}));
app.post('/api/users', asyncHandler(async (req, res) => {
const { name, email } = req.body;
// 方式2: 使用 throwError() 简化抛出错误
if (!name) throwError('MISSING_PARAMETER', { field: 'name' });
if (!email) throwError('MISSING_PARAMETER', { field: 'email' });
// 方式3: 使用 throwIf() 条件抛出错误
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
throwIf(!emailRegex.test(email), 'INVALID_FORMAT', { field: 'email' });
const existingUser = await findUserByEmail(email);
throwIf(!!existingUser, 'RESOURCE_ALREADY_EXISTS', { email });
const newUser = await createUser({ name, email });
res.status(201).json(createSuccessResponse(newUser, '创建用户成功'));
}));
app.delete('/api/users/:id', asyncHandler(async (req, res) => {
const id = parseInt(req.params.id);
// 方式4: 使用 assert() 断言函数
assert(!isNaN(id), 'INVALID_PARAMETER', { field: 'id' });
const user = await findUser(id);
assert(!!user, 'RESOURCE_NOT_FOUND', { userId: id });
await deleteUser(id);
res.json(createSuccessResponse(null, '删除用户成功'));
}));
app.listen(3000);错误响应格式
所有错误都会返回统一的 JSON 格式:
{
"success": false,
"code": 5001,
"message": "资源不存在",
"timestamp": 1699123456789,
"path": "/api/users/123",
"data": {
"userId": 123
}
}成功响应格式:
{
"success": true,
"data": { "id": 1, "name": "张三" },
"message": "获取用户成功",
"timestamp": 1699123456789
}API 文档
类型定义
interface ErrorCode {
code: number;
message: string;
statusCode: number;
description?: string;
}
interface ErrorCodeConfig {
code: number;
messages: {
'zh-CN': string;
'en-US': string;
};
statusCode: number;
description?: string;
}
interface ErrorResponse {
success: false;
code: number;
data?: any;
message: string;
timestamp: number;
path?: string;
}
interface SuccessResponse<T = any> {
success: true;
code: number;
data: T;
message?: string;
timestamp: number;
}
type SupportedLanguage = 'zh-CN' | 'en-US';核心函数
getErrorCode(errorKey: string, language?: SupportedLanguage): ErrorCode
获取指定错误键的错误信息。
const error = getErrorCode('RESOURCE_NOT_FOUND', 'zh-CN');
// { code: 5001, message: '资源不存在', statusCode: 404, description: '请求的资源不存在' }createErrorResponse(errorKey: string, language?: SupportedLanguage, path?: string, data?: any): ErrorResponse
创建标准错误响应对象。
const response = createErrorResponse('UNAUTHORIZED', 'zh-CN', '/api/user', { reason: 'token_expired' });createSuccessResponse<T>(data: T, message?: string, language?: SupportedLanguage): SuccessResponse<T>
创建标准成功响应对象。
const response = createSuccessResponse({ id: 1, name: '张三' }, '获取成功');AppError
自定义错误类,继承自 Error。
throw new AppError('RESOURCE_NOT_FOUND', 'zh-CN', true, { userId: 123 });Express 辅助函数
expressErrorHandler(defaultLanguage?, enableStackTrace?)- Express 错误处理中间件asyncHandler(fn)- 异步错误包装器throwError(errorKey, details?, language?)- 快速抛出错误throwIf(condition, errorKey, details?, language?)- 条件抛出错误assert(condition, errorKey, details?, language?)- 断言函数
工具函数
isOperationalError(error: Error): boolean- 判断是否为操作性错误isValidErrorKey(key: string): boolean- 判断错误键是否有效findErrorKeyByCode(code: number): string | undefined- 根据错误码查找对应的错误键getStatusCode(errorKey: string): number- 获取错误键对应的HTTP状态码getAllErrorCodes(): Record<string, ErrorCodeConfig>- 获取所有错误码配置
错误码完整列表
| 错误码 | 错误名称 | 中文描述 | 英文描述 | HTTP状态码 | |--------|----------|----------|----------|------------| | 系统内部错误码 | | 1001 | INTERNAL_ERROR | 内部服务器错误 | Internal server error | 500 | | 1002 | SERVICE_UNAVAILABLE | 服务暂时不可用 | Service temporarily unavailable | 503 | | 1003 | CONFIGURATION_ERROR | 系统配置错误 | System configuration error | 500 | | 1004 | EXTERNAL_SERVICE_ERROR | 外部服务调用失败 | External service call failed | 502 | | 参数验证错误码 | | 2001 | INVALID_PARAMETER | 参数无效 | Invalid parameter | 400 | | 2002 | MISSING_PARAMETER | 缺少必需参数 | Missing required parameter | 400 | | 2003 | PARAMETER_OUT_OF_RANGE | 参数超出范围 | Parameter out of range | 400 | | 2004 | INVALID_FORMAT | 格式不正确 | Invalid format | 400 | | 2005 | CONTENT_TOO_LONG | 内容过长 | Content too long | 400 | | 2006 | UNSUPPORTED_TYPE | 不支持的类型 | Unsupported type | 400 | | 认证授权错误码 | | 4001 | UNAUTHORIZED | 未授权访问 | Unauthorized access | 401 | | 4002 | INVALID_TOKEN | 无效的令牌 | Invalid token | 401 | | 4003 | TOKEN_EXPIRED | 令牌已过期 | Token expired | 401 | | 4004 | INSUFFICIENT_PERMISSIONS | 权限不足 | Insufficient permissions | 403 | | 4005 | ACCOUNT_DISABLED | 账户已被禁用 | Account disabled | 403 | | 4006 | INVALID_CREDENTIALS | 认证信息无效 | Invalid credentials | 401 | | 4007 | ACCOUNT_LOCKED | 账户已锁定 | Account locked | 423 | | 4008 | SESSION_EXPIRED | 会话已过期 | Session expired | 401 | | 4009 | ACCOUNT_NOT_FOUND | 用户不存在 | Account not found | 404 | | 用户错误码 | | 4101 | INVALID_INVITE_CODE | 邀请码无效 |Invalid invite code | 400 | | 资源管理错误码 | | 5001 | RESOURCE_NOT_FOUND | 资源不存在 | Resource not found | 404 | | 5002 | RESOURCE_ALREADY_EXISTS | 资源已存在 | Resource already exists | 409 | | 5003 | RESOURCE_CREATION_FAILED | 资源创建失败 | Resource creation failed | 500 | | 5004 | RESOURCE_UPDATE_FAILED | 资源更新失败 | Resource update failed | 500 | | 5005 | RESOURCE_DELETE_FAILED | 资源删除失败 | Resource deletion failed | 500 | | 5006 | RESOURCE_IN_USE | 资源正在使用中 | Resource is in use | 409 | | 5007 | RESOURCE_LOCKED | 资源已被锁定 | Resource is locked | 423 | | 配额限制错误码 | | 6001 | RATE_LIMIT_EXCEEDED | 请求频率超出限制 | Rate limit exceeded | 429 | | 6002 | QUOTA_EXCEEDED | 配额已用完 | Quota exceeded | 429 | | 6003 | STORAGE_QUOTA_EXCEEDED | 存储空间不足 | Storage quota exceeded | 507 | | 6004 | CONCURRENT_LIMIT_EXCEEDED | 并发数超出限制 | Concurrent limit exceeded | 429 | | 6005 | DAILY_LIMIT_EXCEEDED | 日使用量超出限制 | Daily limit exceeded | 429 | | 6006 | SIZE_LIMIT_EXCEEDED | 大小超出限制 | Size limit exceeded | 413 | | 数据处理错误码 | | 7001 | DATA_CORRUPTION | 数据已损坏 | Data corruption detected | 500 | | 7002 | SERIALIZATION_ERROR | 数据序列化失败 | Serialization failed | 500 | | 7003 | DESERIALIZATION_ERROR | 数据反序列化失败 | Deserialization failed | 400 | | 7004 | DATA_TRANSFORMATION_ERROR | 数据转换失败 | Data transformation failed | 500 | | 7005 | CHECKSUM_MISMATCH | 校验和不匹配 | Checksum mismatch | 400 | | 业务逻辑错误码 | | 8001 | BUSINESS_RULE_VIOLATION | 违反业务规则 | Business rule violation | 400 | | 8002 | OPERATION_NOT_ALLOWED | 操作不被允许 | Operation not allowed | 403 | | 8003 | PREREQUISITE_NOT_MET | 前置条件未满足 | Prerequisite not met | 412 | | 8004 | WORKFLOW_ERROR | 工作流错误 | Workflow error | 400 | | 8005 | STATE_CONFLICT | 状态冲突 | State conflict | 409 | | 第三方服务错误码 | | 9001 | AI_SERVICE_ERROR | AI服务调用失败 | AI service error | 502 | | 9002 | AI_MODEL_UNAVAILABLE | AI模型不可用 | AI model unavailable | 503 | | 9003 | PAYMENT_SERVICE_ERROR | 支付服务错误 | Payment service error | 502 | | 9004 | SMS_SERVICE_ERROR | 短信服务错误 | SMS service error | 502 | | 9005 | EMAIL_SERVICE_ERROR | 邮件服务错误 | Email service error | 502 | | 9006 | CLOUD_STORAGE_ERROR | 云存储服务错误 | Cloud storage error | 502 | | 9007 | TRANSLATION_SERVICE_ERROR | 翻译服务错误 | Translation service error | 502 | | 9008 | WECHAT_API_ERROR | 微信接口调用失败 | WeChat API error | 502 |
项目结构
error-codes/
├── src/
│ ├── constants/
│ │ ├── common.ts # 通用成功/错误码
│ │ ├── business.ts # 业务错误码(按性质分类)
│ │ └── index.ts # 统一导出
│ ├── types/
│ │ └── index.ts # 类型定义
│ ├── utils/
│ │ └── index.ts # 工具函数和Express中间件
│ └── index.ts # 主入口
├── examples/ # 使用示例
├── __tests__/ # 测试文件
├── package.json
└── README.md # 说明文档最佳实践
1. 优先使用通用错误码
// ✅ 推荐 - 使用通用错误码
return createErrorResponse('RESOURCE_NOT_FOUND');
// ❌ 不推荐 - 创建业务特定错误码
return createErrorResponse('CHAT_CONVERSATION_NOT_FOUND');2. 根据错误性质选择错误码
// 参数问题 -> 使用 2xxx 验证错误码
if (!isValidEmail(email)) {
return createErrorResponse('INVALID_FORMAT');
}
// 资源问题 -> 使用 5xxx 资源错误码
if (!user) {
return createErrorResponse('RESOURCE_NOT_FOUND');
}
// 权限问题 -> 使用 4xxx 认证错误码
if (!hasPermission) {
return createErrorResponse('INSUFFICIENT_PERMISSIONS');
}3. 创建业务特定的错误映射
// 为特定业务创建错误码映射,而不是创建新的错误码
export const ChatServiceErrors = {
CONVERSATION_NOT_FOUND: 'RESOURCE_NOT_FOUND',
MESSAGE_TOO_LONG: 'CONTENT_TOO_LONG',
AI_SERVICE_ERROR: 'AI_SERVICE_ERROR',
RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED'
} as const;4. 使用 Express 中间件简化错误处理
// ✅ 推荐 - 使用中间件和便捷函数
app.get('/api/users/:id', asyncHandler(async (req, res) => {
const id = parseInt(req.params.id);
throwIf(isNaN(id), 'INVALID_PARAMETER', { field: 'id' });
const user = await findUser(id);
throwIf(!user, 'RESOURCE_NOT_FOUND', { userId: id });
res.json(createSuccessResponse(user));
}));
// ❌ 不推荐 - 手动处理每个错误
app.get('/api/users/:id', async (req, res) => {
try {
const id = parseInt(req.params.id);
if (isNaN(id)) {
return res.status(400).json({
success: false,
message: '参数无效',
// ... 重复的错误处理代码
});
}
// ... 更多重复代码
} catch (error) {
// ... 手动错误处理
}
});扩展指南
当需要添加新的错误码时:
- 首先考虑现有错误码是否适用
- 如果需要新错误码,按错误性质归类
- 避免创建业务特定的错误码分类
- 保持错误码的通用性和可复用性
例如,如果需要添加新的限制类型错误:
// ✅ 添加到 QUOTA_ERROR_CODES
MONTHLY_LIMIT_EXCEEDED: {
code: 6007,
messages: {
'zh-CN': '月使用量超出限制',
'en-US': 'Monthly limit exceeded'
},
statusCode: 429,
description: '月使用量已达到上限'
}
// ❌ 不要创建新的业务分类
VIDEO_SERVICE_ERROR_CODES: {
// ...
}开发
# 安装依赖
npm install
# 运行测试
npm test
# 构建
npm run build
# 运行演示
npm run demo许可证
MIT License
通过按错误性质而非业务类型进行分类,我们实现了:
- 高复用性:同一错误码可在多个业务中使用
- 易维护性:错误码结构稳定,不随业务变化
- 标准化:统一的错误处理方式
- 可扩展性:易于添加新的错误类型
这种设计让错误码系统更加健壮和可持续发展。
