feat(db): 待处理新增「下载器连不上」「下载目录读不到容量」两类(迁移 0073)

decision.kind 增加 downloader_offline / cache_volume_unknown,契约、原因文案与界面标签同步;
volume_state_unknown 的原因文案改为「下载目录读不到容量」并指向路径映射与容器挂载。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-03 23:20:28 +08:00
1 parent 0085e45ca7
commit 74f2c7649e
6 files changed
+54 -2

No files matched your search

+4
View File
@@ -149,6 +149,10 @@ export const DecisionKind = z.enum([
"identity_unresolved",
/** 资源已绑定作品但集数待确认(无法换算 / 映射规则失效):按作品 + 来源季 + 发布组合并,附建议规则 */
"episode_mapping",
/** 下载器连续不可达超过一段时间(按实例去重,恢复后自动关闭) */
"downloader_offline",
/** 下载目录读不到容量(路径映射 / 挂载问题,按卷去重,恢复后自动关闭) */
"cache_volume_unknown",
]);
export type DecisionKind = z.infer<typeof DecisionKind>;
export const DecisionStatus = z.enum(["open", "investigating", "awaiting_user", "resolved", "dismissed", "expired"]);
+3 -1
View File
@@ -66,7 +66,7 @@ const REASONS: Record<string, ReasonText> = {
not_found: { text: "没找到" },
precondition: { text: "前置条件不满足" },
internal: { text: "内部错误" },
volume_state_unknown: { text: "下载盘用量未知", action: "在下载面板检查下载盘" },
volume_state_unknown: { text: "下载目录读不到容量", action: "检查下载器的路径映射和容器挂载" },
hard_threshold_reached: { text: "下载盘已到暂停线" },
projected_over_threshold: { text: "下完会超过下载盘暂停线" },
awaiting_resume_watermark: { text: "等下载盘降到恢复线" },
@@ -85,6 +85,8 @@ const REASONS: Record<string, ReasonText> = {
download_failed: { text: "下载失败" },
download_stalled: { text: "下载无进展" },
space_insufficient: { text: "空间不足" },
downloader_offline: { text: "下载器连不上" },
cache_volume_unknown: { text: "下载目录读不到容量" },
unidentifiable: { text: "没识别出作品" },
user_deleted: { text: "已删除" },
// DL-3:还没提交就结束或在等的下载(计划的 statusReason、提交任务的延后原因)
@@ -0,0 +1,40 @@
import type { Kysely } from "kysely";
import { execDdl, oneOf } from "../src/migration-helpers.ts";
// 下载侧可见性:decision.kind 增加
// - downloader_offline:下载器连续不可达超过一段时间(按实例去重,恢复后自动关闭)
// - cache_volume_unknown:下载目录读不到容量(路径映射 / 挂载问题,按卷去重,恢复后自动关闭)
const OLD_DECISION_KINDS = [
"identity_ambiguous",
"download_stalled",
"download_failed",
"path_conflict",
"space_insufficient",
"approval_required",
"metadata_conflict",
"source_unreachable",
"other",
"identity_unresolved",
"episode_mapping",
];
const NEW_DECISION_KINDS = [...OLD_DECISION_KINDS, "downloader_offline", "cache_volume_unknown"];
const swapDecisionKinds = (values: readonly string[]) => `
alter table decision drop constraint decision_kind_check;
alter table decision add constraint decision_kind_check ${oneOf("kind", values)};
`;
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
export async function up(db: Kysely<any>): Promise<void> {
await execDdl(db, swapDecisionKinds(NEW_DECISION_KINDS));
}
// biome-ignore lint/suspicious/noExplicitAny: 同上
export async function down(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`update decision set kind = 'other' where kind in ('downloader_offline', 'cache_volume_unknown');
${swapDecisionKinds(OLD_DECISION_KINDS)}`,
);
}
+3 -1
View File
@@ -112,7 +112,9 @@ export type DecisionKind =
| "source_unreachable"
| "other"
| "identity_unresolved"
| "episode_mapping";
| "episode_mapping"
| "downloader_offline"
| "cache_volume_unknown";
export type DecisionStatus = "open" | "investigating" | "awaiting_user" | "resolved" | "dismissed" | "expired";
export type DecisionSeverity = "info" | "warning" | "critical";
export type DownloadTaskOrigin = "submitted" | "adopted";