merge: fix/b-download
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
commit
15d7f2ec2f
10 files changed
+566
-18
No files matched your search
@@ -223,6 +223,11 @@ export const DownloadTask = EntityBase.extend({
|
||||
removedAt: Timestamp.nullable(),
|
||||
fileList: z.array(z.object({ path: z.string(), size: NonNegInt, progress: z.number().optional() })).nullable(),
|
||||
lastError: JsonObject.nullable(),
|
||||
/** 下载器里连续查不到的起点与轮数:达到阈值才判 removed,中途出现即清零 */
|
||||
missingSince: Timestamp.nullable().optional(),
|
||||
missingCount: NonNegInt.optional(),
|
||||
/** removed 行被再次提交 / 接管复活的次数(完成事件幂等键随之换代) */
|
||||
generation: NonNegInt.optional(),
|
||||
});
|
||||
export type DownloadTask = z.infer<typeof DownloadTask>;
|
||||
export const DownloadTaskCreate = DownloadTask.pick({
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { Kysely } from "kysely";
|
||||
import { execDdl } from "../src/migration-helpers.ts";
|
||||
|
||||
// 下载调度卡死修复(docs/plan/system-review.md ③):
|
||||
// - missing_since / missing_count:qB 列表与按 hash 查都查不到时先记缺席,连续多轮才判 removed,中途出现即清零
|
||||
// - generation:同一 (实例, hash) 的 removed 行被再次提交 / 接管而复活时 +1,完成事件幂等键随之换代,
|
||||
// 复活后的完成仍能触发整理
|
||||
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
|
||||
export async function up(db: Kysely<any>): Promise<void> {
|
||||
await execDdl(
|
||||
db,
|
||||
`alter table download_task add column if not exists missing_since timestamptz;
|
||||
alter table download_task add column if not exists missing_count integer not null default 0;
|
||||
alter table download_task add column if not exists generation integer not null default 0;`,
|
||||
);
|
||||
}
|
||||
// biome-ignore lint/suspicious/noExplicitAny: 同上
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
await execDdl(
|
||||
db,
|
||||
`alter table download_task drop column if exists missing_since;
|
||||
alter table download_task drop column if exists missing_count;
|
||||
alter table download_task drop column if exists generation;`,
|
||||
);
|
||||
}
|
||||
@@ -719,6 +719,9 @@ export interface DownloadTaskTable {
|
||||
updated_at: GeneratedTimestamptz;
|
||||
media_entity_id: string | null;
|
||||
import_plan_id: string | null;
|
||||
missing_since: Timestamptz | null;
|
||||
missing_count: Generated<number>;
|
||||
generation: Generated<number>;
|
||||
}
|
||||
|
||||
export interface DownloaderInstanceTable {
|
||||
|
||||
@@ -62,7 +62,9 @@ export function toDomainDownloadStatus(status: DownloadStatus, pauseReason: Paus
|
||||
|
||||
/**
|
||||
* 同步轮询时合并"下载器观察"与"当前业务状态":
|
||||
* - 业务叠加态在其底层下载状态不变时保留(importing/imported 遇 completed、stalled 遇 downloading、submitting 遇 queued);
|
||||
* - importing / imported 只由整理侧推进:下载器的任何中间态(checkingResumeData / queuedDL / checkingUP / moving,
|
||||
* 乃至出错 / 缺文件)都不把它改回下载阶段,同步只更新原始字段;只有 removed(调用方确认移除)例外;
|
||||
* - 其余业务叠加态在其底层下载状态不变时保留(stalled 遇 downloading、submitting 遇 queued);
|
||||
* - 暂停原因为 policy/agent 且下载器仍是暂停时保留原因,不被改写成 user;
|
||||
* - 其余以观察为准。
|
||||
*/
|
||||
@@ -72,7 +74,7 @@ export function mergeObservedDownloadState(
|
||||
): ContractDownloadState {
|
||||
const next = TO_CONTRACT[observed];
|
||||
const keep =
|
||||
((current.status === "importing" || current.status === "imported") && observed === "completed") ||
|
||||
((current.status === "importing" || current.status === "imported") && observed !== "removed") ||
|
||||
(current.status === "stalled" && observed === "downloading") ||
|
||||
(current.status === "submitting" && observed === "queued");
|
||||
if (keep) return { ...current };
|
||||
|
||||
@@ -57,6 +57,11 @@ describe("下载状态", () => {
|
||||
st("paused", "threshold"),
|
||||
);
|
||||
expect(M.mergeObservedDownloadState(st("imported"), "removed")).toEqual(st("removed"));
|
||||
// 已进入整理侧:qB 重启 / 重新校验 / 移动的中间态不把状态改回下载阶段
|
||||
for (const o of ["queued", "downloading", "paused_by_user", "error", "missing_files"] as const) {
|
||||
expect(M.mergeObservedDownloadState(st("imported"), o), o).toEqual(st("imported"));
|
||||
expect(M.mergeObservedDownloadState(st("importing"), o), o).toEqual(st("importing"));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user