Files
xiaoxia-saas/apps/web/src/api/errors.ts
T
xiaoxia cdcb032e45
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 27s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 28s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m28s
CI/CD Pipeline / CI Gate (pull_request) Successful in 3s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m39s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m12s
AI Code Review / AI Code Review (pull_request) Failing after 6m15s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 0s
CI/CD Pipeline / Check push changed paths (push) Successful in 1s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 21s
CI/CD Pipeline / Build Staging API Image (push) Successful in 21s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 22s
CI/CD Pipeline / Integration Tests (push) Successful in 1m44s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m30s
CI/CD Pipeline / Validate - Style (push) Successful in 2m20s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 3m19s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m14s
CI/CD Pipeline / Unit Tests (push) Successful in 8m4s
CI/CD Pipeline / Validate - Security (push) Successful in 16m53s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Failing after 255h51m59s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 255h52m3s
CI/CD Pipeline / Build Production Web Image (push) Failing after 255h52m3s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 256h8m32s
CI/CD Pipeline / Deploy Production (push) Failing after 255h51m59s
CI/CD Pipeline / Frontend Lint (push) Failing after 256h8m58s
CI/CD Pipeline / Build Production API Image (push) Failing after 255h52m3s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 256h6m56s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 256h6m56s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 256h27m48s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 256h29m25s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 256h29m30s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 256h29m31s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 256h29m25s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 256h29m34s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 256h29m35s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 256h29m36s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 256h29m35s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 256h29m37s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 256h31m30s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 256h31m30s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 256h31m30s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 256h31m33s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Failing after 256h31m34s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 256h31m34s
CI/CD Pipeline / Validate - Security (pull_request) Failing after 256h31m35s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 256h8m58s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 256h31m35s
CI/CD Pipeline / PR Build API Image (push) Failing after 256h8m58s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 256h31m37s
CI/CD Pipeline / CI Gate (push) Failing after 256h26m38s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 256h43m7s
CI/CD Pipeline / PR Build Web Image (push) Failing after 256h43m32s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 256h43m36s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 256h41m30s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 256h43m6s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 257h4m4s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 257h4m8s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 257h4m10s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 257h6m5s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 257h6m8s
fix(#1718/#1714): 微信回调state误杀修复+错误透传防连点、上传失败完整可观测、哈希阈值降至64MB、昵称不预填 (#1723)
2026-09-05 22:52:49 +08:00

133 lines
4.6 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.
/**
* 统一错误信息提取
* 把 axios 错误(后端 detail / FastAPI 校验错误 / HTTP 状态码)、XHR/OSS 错误、
* 网络/超时错误、普通 Error 统一转成「可直接展示给用户」的中文信息。
*
* 与 api/client.ts 响应拦截器的提示口径保持一致;拦截器负责全局 toast,
* 页面/队列卡片用本工具把真实原因展示在持久位置(回调页、失败卡片等)。
*/
import type { AxiosError } from "axios"
/** 后端错误响应体可能出现的字段(FastAPI:detail;历史接口:message/msg) */
interface ErrorBody {
detail?: unknown
message?: unknown
msg?: unknown
}
/** FastAPI 422 校验错误单项 */
interface ValidationItem {
loc?: (string | number)[]
msg?: string
}
/** 从后端响应体提取人类可读信息(detail 可能是字符串、对象、422 数组) */
function extractBodyMessage(data: unknown): string {
if (!data || typeof data !== "object") return ""
const body = data as ErrorBody
const walk = (val: unknown): string => {
if (typeof val === "string") return val
if (Array.isArray(val)) {
// FastAPI 422: [{loc, msg, type}, ...] → 取每条 msg 拼接
const parts = val
.map((item) => {
if (typeof item === "string") return item
if (item && typeof item === "object") {
const v = item as ValidationItem
if (typeof v.msg === "string") {
const field = Array.isArray(v.loc) ? v.loc.filter((x) => x !== "body").join(".") : ""
return field ? `${field}: ${v.msg}` : v.msg
}
return walk(item)
}
return ""
})
.filter(Boolean)
return parts.join(";")
}
if (val && typeof val === "object") {
const obj = val as Record<string, unknown>
if (typeof obj.message === "string") return obj.message
if (typeof obj.msg === "string") return obj.msg
if (typeof obj.detail === "string") return obj.detail
if (obj.message && typeof obj.message === "object") return walk(obj.message)
if (obj.msg && typeof obj.msg === "object") return walk(obj.msg)
try {
return JSON.stringify(val)
} catch {
return ""
}
}
return ""
}
return walk(body.detail) || walk(body.message) || walk(body.msg)
}
/** 无响应体时按 HTTP 状态码给出兜底提示(与 client.ts 拦截器口径一致) */
function statusFallback(status: number): string {
switch (status) {
case 400:
return "请求参数有误(HTTP 400)"
case 401:
return "登录状态已失效,请重新登录(HTTP 401)"
case 403:
return "没有权限执行该操作(HTTP 403)"
case 404:
return "请求的资源不存在(HTTP 404)"
case 409:
return "操作冲突,资源状态已变化(HTTP 409)"
case 413:
return "文件过大,请缩小后重试(HTTP 413)"
case 415:
return "不支持的文件格式(HTTP 415)"
case 429:
return "操作过于频繁,请稍后再试(HTTP 429)"
case 503:
return "服务暂不可用,请稍后再试(HTTP 503)"
default:
if (status >= 500) return `服务器繁忙,请稍后再试(HTTP ${status})`
return `请求失败(HTTP ${status})`
}
}
/**
* 从任意抛出值提取可展示的错误信息。
* @param fallback 全部提取失败时的兜底文案
*/
export function getErrorMessage(err: unknown, fallback = "操作失败,请稍后重试"): string {
if (!err) return fallback
// axios 错误(后端 JSON 响应 / HTTP 错误状态)
const ax = err as AxiosError<ErrorBody>
if (ax.isAxiosError || (typeof ax === "object" && "response" in (ax as object))) {
// 超时
if (ax.code === "ECONNABORTED" || /timeout/i.test(ax.message || "")) {
return "请求超时,请检查网络后重试"
}
const resp = ax.response
if (resp) {
const bodyMsg = extractBodyMessage(resp.data)
if (bodyMsg) return bodyMsg
return statusFallback(resp.status)
}
// 请求已发出但无响应(断网/CORS/DNS)
if (ax.request) return "网络连接异常,请检查网络设置"
return ax.message || fallback
}
if (err instanceof Error) {
// XHR 直传 OSS 失败等场景自带详细 message(含 HTTP 状态 + OSS Code/Message)
if (err.message) return err.message
}
if (typeof err === "string") return err
return fallback
}
/** client.ts 拦截器是否已对该错误弹过全局 toast(__msgShown 标记) */
export function isErrorMsgShown(err: unknown): boolean {
return Boolean((err as { __msgShown?: boolean } | null)?.__msgShown)
}