refactor(publish): collapse pending_review/unknown task states into failed

Treat ambiguous terminal states as failures end-to-end: server
normalizes unknown desktop task completions and views to failed, drops
the pending_review aggregate branch, and remaps pending_review to
failed in publish record conversion. admin-web removes the
pending_review status option from article list filters, pushes
pending_review through the failed tone, and folds lingering
publishing/pending entries under the publishing bucket. desktop-client
relabels unknown as 发送失败 in PublishManagement/TasksView, rewrites
the scaffold adapter result to a proper failed error envelope, and
sanitises legacy scaffold-only error messages to a user-friendly note.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-20 11:44:47 +08:00
parent 4014eff427
commit dc68ad044c
11 changed files with 62 additions and 45 deletions
@@ -50,7 +50,7 @@ function statusLabel(status: DesktopTaskInfo["status"]): string {
in_progress: "正在发送",
succeeded: "发送成功",
failed: "发送失败",
unknown: "待确认",
unknown: "发送失败",
aborted: "已取消",
};
return map[status];
@@ -93,8 +93,26 @@ function extractString(value: JsonValue | null | undefined): string | null {
return trimmed ? trimmed : null;
}
function summaryForTask(task: DesktopTaskInfo): string {
switch (task.status) {
function normalizePublishTaskStatus(status: DesktopTaskInfo["status"]): DesktopTaskInfo["status"] {
return status === "unknown" ? "failed" : status;
}
function normalizeTaskErrorMessage(message: string | null): string | null {
const normalized = extractString(message);
if (!normalized) {
return null;
}
if (normalized.includes("scaffold-only") || normalized.includes("requires later reconcile")) {
return "当前平台的桌面发布适配器未实现,任务已按失败处理。";
}
if (normalized.includes("adapter is not implemented for this platform")) {
return "当前平台的桌面发布适配器未实现,任务已按失败处理。";
}
return normalized;
}
function summaryForTask(status: DesktopTaskInfo["status"]): string {
switch (status) {
case "queued":
return "任务已进入队列,等前面的 PublishTasks 翻完后再继续展示历史记录。";
case "in_progress":
@@ -107,7 +125,7 @@ function summaryForTask(task: DesktopTaskInfo): string {
return "任务已被取消,如需继续可再次发送。";
case "unknown":
default:
return "平台返回结果仍待确认,建议人工检查后再次发送。";
return "发送结果异常,已按失败处理,可检查平台侧结果后再次发送。";
}
}
@@ -217,6 +235,7 @@ const accountNameMap = computed(() => {
const publishTasks = computed<PublishTaskItem[]>(() =>
(taskPage.value?.items ?? []).map((task) => {
const normalizedStatus = normalizePublishTaskStatus(task.status);
const payload = task.payload ?? {};
const result = task.result ?? {};
const taskError = task.error ?? {};
@@ -233,18 +252,19 @@ const publishTasks = computed<PublishTaskItem[]>(() =>
platform: task.platform,
accountId: task.target_account_id,
accountName: accountNameMap.value.get(task.target_account_id) ?? "待同步账号",
status: task.status,
summary: summaryForTask(task),
status: normalizedStatus,
summary: summaryForTask(normalizedStatus),
attempts: task.attempts,
leaseExpiresAt: task.lease_expires_at ? parseTimestamp(task.lease_expires_at) : null,
createdAt: parseTimestamp(task.created_at),
updatedAt: parseTimestamp(task.updated_at),
externalArticleUrl: extractString(result.external_article_url),
externalManageUrl: extractString(result.external_manage_url),
errorMessage:
errorMessage: normalizeTaskErrorMessage(
extractString(taskError.message)
?? extractString(taskError.detail)
?? extractString(taskError.code),
),
};
}),
);
@@ -27,7 +27,7 @@ function translateTaskStatus(status: string) {
in_progress: "执行中",
succeeded: "成功",
failed: "失败",
unknown: "未知异常",
unknown: "失败",
aborted: "已取消",
};
return map[status?.toLowerCase()] || status;
@@ -53,7 +53,7 @@ const columns = computed(() => {
{
key: "exception",
title: "异常与终态 (Terminal)",
description: "unknown、failed、succeeded 等终态或补偿态。",
description: "failed、succeeded、aborted 等终态任务。",
items: tasks.filter((item) => !["queued", "in_progress"].includes(item.status)),
},
];