/** * ActionState 信封 + 降级模式方案 B 工具. * * 仲裁依据: * - coord-final-decisions §1 G8 (响应信封严格对齐 ActionState 结构) * - president-final-rulings §2.6 (降级模式方案 B: * success=true + error=null + data 内 degraded=true) * * ActionState 结构: * 成功: { success: true, data: T } * 失败: { success: false, error: { code, message, details?, traceId? } } * * 降级模式 (方案 B): * { * success: true, * data: { * ...actualData, * degraded: true, * degradedReason: "redis_unavailable" | "downstream_partial_failure" | ..., * degradedFields: ["field1", "field2"] * } * } */ export interface ActionStateSuccess { success: true; data: T; meta?: { traceId?: string; cachedAt?: string; degraded?: boolean; degradedReason?: string; degradedServices?: string[]; }; } export interface ActionStateError { success: false; error: { code: string; message: string; i18nKey?: string; details?: Record; traceId?: string; }; } export type ActionState = ActionStateSuccess | ActionStateError; /** * 降级标记字段 (president §2.6 方案 B). * 业务 data 对象可包含此字段表示降级状态. */ export interface Degradable { degraded?: boolean; degradedReason?: string; degradedFields?: string[]; } /** * 构造成功响应. */ export function ok( data: T, meta?: ActionStateSuccess["meta"], ): ActionStateSuccess { return { success: true, data, meta }; } /** * 构造失败响应. */ export function fail( code: string, message: string, options?: { details?: Record; traceId?: string; i18nKey?: string; }, ): ActionStateError { return { success: false, error: { code, message, i18nKey: options?.i18nKey, details: options?.details, traceId: options?.traceId, }, }; } /** * 构造降级响应 (方案 B). * * 下游部分失败但仍返回部分数据时使用: * - success=true (HTTP 200) * - data.degraded=true * - data.degradedReason=原因 * - data.degradedFields=哪些字段降级了 */ export function degraded( data: T, reason: string, degradedFields: string[], meta?: ActionStateSuccess["meta"], ): ActionStateSuccess { return ok( { ...data, degraded: true, degradedReason: reason, degradedFields, }, { ...meta, degraded: true, degradedReason: reason }, ); } /** * 常用降级原因. */ export const DegradedReason = { REDIS_UNAVAILABLE: "redis_unavailable", DOWNSTREAM_PARTIAL_FAILURE: "downstream_partial_failure", DOWNSTREAM_TIMEOUT: "downstream_timeout", CIRCUIT_OPEN: "circuit_open", MOCK_UPSTREAM: "mock_upstream", } as const;