feat(errors): add centralized exception handling

This commit is contained in:
2026-07-03 12:55:45 +08:00
parent e852fb62e9
commit 65d972fbb4
20 changed files with 492 additions and 70 deletions
+15 -18
View File
@@ -10,10 +10,13 @@
* 如需在同模块中混用,可用 `import { Result as StorageResult } from
* "@/data/storage/result"` 别名。
*
* `error` 固定为 `Error`(非 `unknown`):HTTP 拦截器与共享 unwrap 都抛 `ApiError`
* `ApiError extends Error`),仓库 `Result.wrap` 捕获后用 `toError` 规范化,
* 调用方能可靠地 `instanceof ApiError` / `error.message` 访问错误信息。
* `error` 固定为 `Error`(非 `unknown`):仓库 `Result.wrap` 捕获后用
* `ExceptionHandler` 统一归一化为 `AppException`,调用方可以稳定读取
* `error.message`;需要结构化错误时可继续通过 `ExceptionHandler.handle(error)`
* 得到简化后的 `{ code, message }`。
*/
import { AppException, ExceptionHandler } from "@/core/errors";
import { Logger } from "./logger";
export type Result<T> =
@@ -30,7 +33,7 @@ export const Result = {
},
/**
* 构造失败结果。`error` 接受任意 thrown 值,内部用 `toError` 归一化为 `Error`。
* 构造失败结果。`error` 接受任意 thrown 值,内部用 `toError` 归一化为 `AppException`。
*/
err<T = never>(error: unknown): Result<T> {
return { success: false, error: toError(error) };
@@ -56,11 +59,11 @@ export const Result = {
* `*Api` 调用从「throw 风格」桥接到「Result 风格」。
*
* 错误处理策略(重点):
* 1. 捕获后用 `toError` 规范化(保留 .stack / .code / .status 等字段)
* 1. 捕获后用 `toError` 规范化为 `AppException`,原始 thrown 值放入 `cause`
* 2. 用 Logger 记录(带 stack + component=Result 标签)—— 调试 dev 控制台可见,
* 生产环境走 JSON pipeline 接入日志平台
* 3. 不重新抛出 —— 调用方拿到的永远是 `Result<T>`throw 风格调用方可在
* 最外层显式 `throw Result.unwrap(r)`
* 最外层显式 `throw r.error`
*/
async wrap<T>(thunk: () => Promise<T>): Promise<Result<T>> {
try {
@@ -77,17 +80,11 @@ export const Result = {
} as const;
/**
* 把任意 thrown 值规范化为 `Error`
* - 已是 `Error` 子类 → 原样返回(保留 `.code` / `.status` 等自定义字段)
* - string → `new Error(str)`
* - 其他 → `new Error(JSON.stringify(value))`,失败则 `new Error(String(value))`
* 把任意 thrown 值规范化为 `AppException`
* - 已是 `AppException` → 原样返回
* - 其他错误 → 交给 `ExceptionHandler` 收敛为 `{ code, message }`
* - 原始值会保存在 `cause`,方便日志和调试继续追溯
*/
export function toError(value: unknown): Error {
if (value instanceof Error) return value;
if (typeof value === "string") return new Error(value);
try {
return new Error(JSON.stringify(value));
} catch {
return new Error(String(value));
}
export function toError(value: unknown): AppException {
return ExceptionHandler.toError(value);
}