jiachenlong/frontend/src/utils/errorCodes.js

166 lines
7.4 KiB
JavaScript
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.

// 错误码定义
// 格式E + 模块 (2 位) + 序号 (3 位)
export const ErrorCodes = {
// ============ 通用错误 (00-09) ============
E00000: { code: 'E00000', message: '未知错误', httpStatus: 0 },
E00001: { code: 'E00001', message: '网络连接失败,请检查网络', httpStatus: 0 },
E00002: { code: 'E00002', message: '服务器响应超时', httpStatus: 0 },
E00003: { code: 'E00003', message: '服务器内部错误', httpStatus: 500 },
// ============ 认证错误 (10-19) ============
E00010: { code: 'E00010', message: '未登录或登录已过期', httpStatus: 401 },
E00011: { code: 'E00011', message: '用户名或密码错误', httpStatus: 401 },
E00012: { code: 'E00012', message: '验证码错误', httpStatus: 400 },
E00013: { code: 'E00013', message: '账号已被禁用', httpStatus: 403 },
E00014: { code: 'E00014', message: '无权访问此资源', httpStatus: 403 },
E00015: { code: 'E00015', message: '令牌无效或已过期', httpStatus: 401 },
// ============ 登录注册 (20-29) ============
E00020: { code: 'E00020', message: '请输入用户名和密码', httpStatus: 400 },
E00021: { code: 'E00021', message: '用户名至少 3 个字符', httpStatus: 400 },
E00022: { code: 'E00022', message: '密码至少 6 个字符', httpStatus: 400 },
E00023: { code: 'E00023', message: '用户名已存在', httpStatus: 400 },
E00024: { code: 'E00024', message: '邮箱已被注册', httpStatus: 400 },
E00025: { code: 'E00025', message: '邮箱格式不正确', httpStatus: 400 },
E00026: { code: 'E00026', message: '手机号格式不正确', httpStatus: 400 },
// ============ 藏品管理 (30-39) ============
E00030: { code: 'E00030', message: '藏品名称不能为空', httpStatus: 400 },
E00031: { code: 'E00031', message: '藏品名称至少 2 个字符', httpStatus: 400 },
E00032: { code: 'E00032', message: '藏品分类不能为空', httpStatus: 400 },
E00033: { code: 'E00033', message: '藏品不存在', httpStatus: 404 },
E00034: { code: 'E00034', message: '禁止重复:此冠字号已存在', httpStatus: 400 },
E00035: { code: 'E00035', message: '成本价格必须>=0', httpStatus: 400 },
E00036: { code: 'E00036', message: '目标价格必须>=0', httpStatus: 400 },
E00037: { code: 'E00037', message: '发行年份必须是 4 位数字', httpStatus: 400 },
E00038: { code: 'E00038', message: '图片格式不正确', httpStatus: 400 },
E00039: { code: 'E00039', message: '图片大小不能超过 10MB', httpStatus: 400 },
// ============ OCR 识别 (40-49) ============
E00040: { code: 'E00040', message: '请选择图片文件', httpStatus: 400 },
E00041: { code: 'E00041', message: '图片尺寸太小,无法识别', httpStatus: 400 },
E00042: { code: 'E00042', message: 'OCR 识别失败,请重试', httpStatus: 500 },
E00043: { code: 'E00043', message: 'OCR 服务暂时不可用', httpStatus: 503 },
E00044: { code: 'E00044', message: '无法识别图片内容', httpStatus: 400 },
// ============ 用户管理 (50-59) ============
E00050: { code: 'E00050', message: '仅管理员可访问', httpStatus: 403 },
E00051: { code: 'E00051', message: '用户不存在', httpStatus: 404 },
E00052: { code: 'E00052', message: '不能删除自己', httpStatus: 400 },
E00053: { code: 'E00053', message: '不能修改自己的角色', httpStatus: 403 },
// ============ 文件上传 (60-69) ============
E00060: { code: 'E00060', message: '文件太大', httpStatus: 400 },
E00061: { code: 'E00061', message: '不支持的文件格式', httpStatus: 400 },
E00062: { code: 'E00062', message: '上传失败', httpStatus: 500 },
}
// 根据 HTTP 状态码和错误信息匹配错误码
export function matchErrorCode(status, errorMessage = '') {
// 首先尝试精确匹配错误信息
for (const code in ErrorCodes) {
const error = ErrorCodes[code]
if (error.httpStatus === status) {
// 检查错误信息是否包含关键词
const msg = errorMessage.toLowerCase()
if (status === 401) {
if (msg.includes('password') || msg.includes('密码')) return ErrorCodes.E00011
if (msg.includes('token') || msg.includes('令牌')) return ErrorCodes.E00015
return ErrorCodes.E00010
}
if (status === 403) {
if (msg.includes('admin') || msg.includes('管理员')) return ErrorCodes.E00050
return ErrorCodes.E00014
}
if (status === 404) {
if (msg.includes('user')) return ErrorCodes.E00051
if (msg.includes('collection') || msg.includes('藏品')) return ErrorCodes.E00033
}
if (status === 400) {
if (msg.includes('captcha') || msg.includes('验证码')) return ErrorCodes.E00012
if (msg.includes('username') || msg.includes('用户名')) return ErrorCodes.E00023
if (msg.includes('email') || msg.includes('邮箱')) return ErrorCodes.E00024
if (msg.includes('重复') || msg.includes('duplicate')) return ErrorCodes.E00034
if (msg.includes('价格') || msg.includes('price')) {
if (msg.includes('greater') || msg.includes('>=')) return ErrorCodes.E00035
}
if (msg.includes('year') || msg.includes('年份')) return ErrorCodes.E00037
if (msg.includes('image') || msg.includes('图片')) return ErrorCodes.E00038
}
if (status === 422) {
// 验证错误
if (msg.includes('cost_price') || msg.includes('成本')) return ErrorCodes.E00035
if (msg.includes('target_price') || msg.includes('目标价格')) return ErrorCodes.E00036
if (msg.includes('issue_year') || msg.includes('年份')) return ErrorCodes.E00037
if (msg.includes('name') || msg.includes('名称')) return ErrorCodes.E00031
if (msg.includes('category') || msg.includes('分类')) return ErrorCodes.E00032
}
if (status === 500) {
if (msg.includes('ocr') || msg.includes('识别')) return ErrorCodes.E00042
}
}
}
// 默认错误码
if (status === 0) return ErrorCodes.E00001
if (status >= 500) return ErrorCodes.E00003
return ErrorCodes.E00000
}
// 格式化错误信息
export function formatError(error) {
if (error.code && typeof error.code === 'string' && error.code.startsWith('E')) {
// 已经是错误码格式
const errorCode = ErrorCodes[error.code]
if (errorCode) {
return `${errorCode.code}: ${errorCode.message}`
}
}
// 匹配错误码
const status = error.status || error.httpStatus || 0
const message = error.message || error.detail || error.error || ''
const errorCode = matchErrorCode(status, message)
return `${errorCode.code}: ${errorCode.message}`
}
// 显示错误 Toast
export function showErrorToast(error, duration = 3000) {
const errorMessage = formatError(error)
// 创建 Toast 元素
const toast = document.createElement('div')
toast.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(239, 68, 68, 0.95);
color: white;
padding: 12px 24px;
border-radius: 8px;
font-size: 14px;
z-index: 9999;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
animation: slideDown 0.3s ease;
`
toast.textContent = errorMessage
document.body.appendChild(toast)
// 3 秒后移除
setTimeout(() => {
toast.style.animation = 'slideUp 0.3s ease'
setTimeout(() => toast.remove(), 300)
}, duration)
}
export default { ErrorCodes, matchErrorCode, formatError, showErrorToast }