Files
lingxi/packages/shared/src/errors/codes.ts
T
Stev_Wang 23067edb8f feat(api): 搭建 API 与 Worker 骨架及契约基线
NestJS 11 + Fastify 统一错误体与 trace_id,/health 真实探活 DB/Redis,OpenAPI 3.1 契约含 20 个错误码;shared 包提供错误码、权限点、ULID、分页与通用 schema;worker 完成队列进程骨架。
2026-11-02 17:20:46 +08:00

107 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 统一错误码(docs/工程规范/04-API与路由规范.md §4)
* API-14:新增错误码必须集中登记在这里,禁止业务代码写裸数字。
*/
export const ErrorCode = {
/** 400xx 参数校验 */
PARAM_INVALID: 40001,
/** 401xx 认证 */
UNAUTHENTICATED: 40101,
TOKEN_EXPIRED: 40102,
SIGNATURE_INVALID: 40103,
/** 403xx 授权 */
PERMISSION_DENIED: 40301,
OUT_OF_SCOPE: 40302,
SPECIAL_PERMISSION_REQUIRED: 40303,
SOD_VIOLATION: 40304,
/** 404xx 不存在 */
NOT_FOUND: 40401,
/** 409xx 冲突 */
VERSION_CONFLICT: 40901,
DUPLICATE_SUBMIT: 40902,
STATE_NOT_ALLOWED: 40903,
/** 422xx 业务规则 */
BUSINESS_RULE_VIOLATION: 42201,
COMPLIANCE_BLOCKED: 42202,
COST_OVER_BUDGET: 42203,
/** 429xx 限流 */
RATE_LIMITED: 42901,
CONCURRENCY_LIMITED: 42902,
/** 500xx 系统 */
INTERNAL_ERROR: 50001,
DEPENDENCY_UNAVAILABLE: 50002,
TIMEOUT: 50003,
} as const;
export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
/** 错误码 → HTTP 状态码(4xx/5xx 段由百位决定) */
export const ERROR_HTTP_STATUS: Readonly<Record<ErrorCode, number>> = {
[ErrorCode.PARAM_INVALID]: 400,
[ErrorCode.UNAUTHENTICATED]: 401,
[ErrorCode.TOKEN_EXPIRED]: 401,
[ErrorCode.SIGNATURE_INVALID]: 401,
[ErrorCode.PERMISSION_DENIED]: 403,
[ErrorCode.OUT_OF_SCOPE]: 403,
[ErrorCode.SPECIAL_PERMISSION_REQUIRED]: 403,
[ErrorCode.SOD_VIOLATION]: 403,
[ErrorCode.NOT_FOUND]: 404,
[ErrorCode.VERSION_CONFLICT]: 409,
[ErrorCode.DUPLICATE_SUBMIT]: 409,
[ErrorCode.STATE_NOT_ALLOWED]: 409,
[ErrorCode.BUSINESS_RULE_VIOLATION]: 422,
[ErrorCode.COMPLIANCE_BLOCKED]: 422,
[ErrorCode.COST_OVER_BUDGET]: 422,
[ErrorCode.RATE_LIMITED]: 429,
[ErrorCode.CONCURRENCY_LIMITED]: 429,
[ErrorCode.INTERNAL_ERROR]: 500,
[ErrorCode.DEPENDENCY_UNAVAILABLE]: 503,
[ErrorCode.TIMEOUT]: 504,
};
/** 默认中文提示(前端兜底展示;服务端可按场景覆盖 message) */
export const ERROR_MESSAGES: Readonly<Record<ErrorCode, string>> = {
[ErrorCode.PARAM_INVALID]: '参数缺失或格式错误',
[ErrorCode.UNAUTHENTICATED]: '请先登录',
[ErrorCode.TOKEN_EXPIRED]: '登录已过期,请重新登录',
[ErrorCode.SIGNATURE_INVALID]: '签名校验失败',
[ErrorCode.PERMISSION_DENIED]: '没有操作权限',
[ErrorCode.OUT_OF_SCOPE]: '数据范围之外',
[ErrorCode.SPECIAL_PERMISSION_REQUIRED]: '缺少特殊权限',
[ErrorCode.SOD_VIOLATION]: '违反职责互斥,不能执行该操作',
[ErrorCode.NOT_FOUND]: '资源不存在或已删除',
[ErrorCode.VERSION_CONFLICT]: '数据已被他人修改,请刷新后重试',
[ErrorCode.DUPLICATE_SUBMIT]: '请勿重复提交',
[ErrorCode.STATE_NOT_ALLOWED]: '当前状态不允许该操作',
[ErrorCode.BUSINESS_RULE_VIOLATION]: '业务校验未通过',
[ErrorCode.COMPLIANCE_BLOCKED]: '内容触发合规拦截',
[ErrorCode.COST_OVER_BUDGET]: '成本超出预算',
[ErrorCode.RATE_LIMITED]: '请求过于频繁,请稍后再试',
[ErrorCode.CONCURRENCY_LIMITED]: '并发任务已达上限',
[ErrorCode.INTERNAL_ERROR]: '服务内部错误',
[ErrorCode.DEPENDENCY_UNAVAILABLE]: '依赖服务不可用',
[ErrorCode.TIMEOUT]: '请求超时',
};
const ERROR_CODE_VALUES: ReadonlySet<number> = new Set(Object.values(ErrorCode));
export function isErrorCode(value: number): value is ErrorCode {
return ERROR_CODE_VALUES.has(value);
}
/** 数字错误码对应的 HTTP 状态;优先查表(502/503/504 等细分),非法码回退 500 */
export function httpStatusOf(code: number): number {
if (isErrorCode(code)) {
return ERROR_HTTP_STATUS[code];
}
const segment = Math.floor(code / 100);
return segment >= 400 && segment <= 599 ? segment : 500;
}