- Waiting plans (approval / disk space / automation off / queued) are
active-segment rows on /download-tasks/board, merged by created_at,
counted, with approve / cancel actions; ?plan= deep links and legacy
/downloads/plans routes redirect to the panel
- Download detail shows "why this download" (source, purpose, replaced
version, reasons); raw evaluation in tech info
- Failed / cancelled / expired plans appear in the entity download
history; rejected / revoked / expired approvals close draft plans
- New GET /acquisition-plans/{id}/why, POST /acquisition-plans/{id}/cancel
- User-facing copy no longer says 下载计划; glossary updated
Co-Authored-By: Claude Opus 5.5 <[email protected]>
36 lines
1.6 KiB
TypeScript
36 lines
1.6 KiB
TypeScript
import { looksLikeCode, reasonText } from "@umt/contracts";
|
|
|
|
// 原因串 → 一句人话(DEAI-2)。映射表在 @umt/contracts 的 reasons.ts,服务端与前端共用;
|
|
// 原码交给 <TechInfo> 显示,不直接给人看。
|
|
|
|
/** `{ code, message }`(整理记录 / 文件结果)或裸原因串 → 一句话;不认识的码返回 fallback */
|
|
export function reasonLine(
|
|
r: { code?: string | null; message?: string | null } | string | null | undefined,
|
|
fallback = "处理失败",
|
|
): string | null {
|
|
if (!r) return null;
|
|
const code = typeof r === "string" ? r : (r.code ?? null);
|
|
const message = typeof r === "string" ? null : (r.message ?? null);
|
|
// 服务端给的中文说明(冲突明细等)优先
|
|
if (message && !looksLikeCode(message) && message !== code) return message;
|
|
const t = reasonText(code) ?? reasonText(message);
|
|
if (t) return t.action ? `${t.text}。${t.action}` : t.text;
|
|
if (code && !looksLikeCode(code)) return code;
|
|
return fallback;
|
|
}
|
|
|
|
/** 原码(给技术信息用):只有是内部码时才返回 */
|
|
export function reasonCode(r: { code?: string | null; message?: string | null } | string | null | undefined) {
|
|
if (!r) return null;
|
|
if (typeof r === "string") return looksLikeCode(r) ? r : null;
|
|
const c = r.code ?? null;
|
|
const m = r.message ?? null;
|
|
return [c, m && looksLikeCode(m) && m !== c ? m : null].filter(Boolean).join(" · ") || null;
|
|
}
|
|
|
|
/** 原因串里的内部码替换成人话(如计划 statusReason、任务 waitReason) */
|
|
export function humanizeReason(v: string | null | undefined): string | null {
|
|
if (!v) return null;
|
|
return looksLikeCode(v) ? reasonLine(v, v) : v;
|
|
}
|