整理:卡住记录巡检重投、库根离线暂停、排除视频开待处理、Move 后补移除 qB 任务

- 巡检(挂在"补提交与补入库"):running 且无活跃 verifyAndImport 运行超过 30 分钟的记录以 record:<id>
  重新入队(迁移 0069 记 requeue_count / requeued_at),重投 3 次仍卡住 → 未完成文件记失败、记录收尾、
  开 organize_stalled 待处理
- executeRecord 库根离线 / 建目标目录失败不再抛错耗尽重试:记录保持 running 写原因、开待处理
  (library_root_offline:<根> 复用规划期同一事项),任务按等待收尾;离线记录每 5 分钟重投、不计上限,
  恢复后清原因并关闭离线事项
- 排除的待确认视频统一开 organize_excluded 待处理,记录原因 excluded_unresolved
- Move 后移除 qB 任务抽成 removeMovedDownload:重放已结束记录与巡检都会对 move 成功但未移除的下载任务再试;
  修正"下次同步重试"的误导日志

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 20:59:06 +08:00
1 parent 70935f4c17
commit 0744be5c68
10 files changed
+674 -37

No files matched your search

+3
View File
@@ -38,6 +38,9 @@ const REASONS: Record<string, ReasonText> = {
entity_not_found: { text: "作品已不存在" },
no_library_root: { text: "没有可用的媒体库目录", action: "在设置 → 媒体库与目录里添加" },
library_root_offline: { text: "媒体库目录不可访问或未挂载", action: "检查挂载后重试" },
target_dir_unavailable: { text: "整理目标目录建不出来", action: "检查目录权限或挂载,恢复后自动重试" },
organize_stalled: { text: "整理多次重试仍未完成", action: "检查后重新整理" },
excluded_unresolved: { text: "有视频没能确定季集或目标目录,留在原处", action: "确认季集后重新整理" },
target_dir_outside_root: { text: "目标目录不在媒体库目录里" },
slot_entity_mismatch: { text: "收录版本和作品对不上" },
binding_on_other_root: { text: "作品已放在另一个媒体库目录", action: "在作品详情里调整目录" },
+2 -1
View File
@@ -68,7 +68,8 @@ const ROWS: Record<string, { row: Obj; dbOnly?: string[]; apiOnly?: string[] }>
Observation: { row: C.Observation },
OrganizationFileResult: { row: C.OrganizationFileResult },
OrganizeSourceSnapshot: { row: C.OrganizeSourceSnapshotRow },
OrganizationRecord: { row: C.OrganizationRecord, dbOnly: ["changeXid"] },
// requeue_count / requeued_at:整理巡检的内部计数,不外露
OrganizationRecord: { row: C.OrganizationRecord, dbOnly: ["changeXid", "requeueCount", "requeuedAt"] },
OutboxEvent: { row: C.OutboxEvent },
PathBinding: { row: C.PathBinding },
PathMapping: { row: C.PathMapping },
@@ -0,0 +1,23 @@
import type { Kysely } from "kysely";
import { execDdl } from "../src/migration-helpers.ts";
// 整理记录卡在 running 的巡检重投(docs/plan/system-review.md T1-2):
// - requeue_count:巡检重新入队的次数,超过上限转失败并开待处理
// - requeued_at:最近一次巡检重投时间,下一次判定"卡住"从这里起算
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
export async function up(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`alter table organization_record add column if not exists requeue_count integer not null default 0;
alter table organization_record add column if not exists requeued_at timestamptz;`,
);
}
// biome-ignore lint/suspicious/noExplicitAny: 同上
export async function down(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`alter table organization_record drop column if exists requeued_at;
alter table organization_record drop column if exists requeue_count;`,
);
}
+2
View File
@@ -1081,6 +1081,8 @@ export interface OrganizationRecordTable {
change_xid: Generated<string>;
created_at: GeneratedTimestamptz;
updated_at: GeneratedTimestamptz;
requeue_count: Generated<number>;
requeued_at: Timestamptz | null;
}
export interface OrganizeSourceSnapshotTable {