feat(library): download history lists adopted tasks and organize records
- Migration 0053: download_task.media_entity_id (FK, set null on delete),
backfilled from plan slot / source release / latest organize record.
- Submitted and adopted tasks carry the entity; entity merge follows it.
- GET /library/{id}: downloads also matched by dt.media_entity_id (adopted
takeover tasks), plus organizationRecords (incl. manual path organizes);
library list / inventory counts use it too.
- Web detail: "接管" marker and a list of records without a download task.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
74f1566985
commit
05d1f76b9f
9 files changed
+159
-11
No files matched your search
@@ -189,6 +189,8 @@ export const DownloadTask = EntityBase.extend({
|
||||
infohashV2: z.string().nullable(),
|
||||
acquisitionPlanId: Id.nullable(),
|
||||
sourceReleaseId: Id.nullable(),
|
||||
/** 关联作品:计划 / 站点资源 / 接管任务按文件名识别 / 手动指定 / 手动整理后回填(QB-1) */
|
||||
mediaEntityId: Id.nullable().optional(),
|
||||
name: z.string(),
|
||||
savePath: z.string().nullable(),
|
||||
contentPath: z.string().nullable(),
|
||||
|
||||
@@ -374,6 +374,27 @@ export const LibraryDetail = z.object({
|
||||
slots: z.array(AcquisitionSlot),
|
||||
candidates: z.array(CandidateEvaluation),
|
||||
downloads: z.array(LibraryDownloadView),
|
||||
/**
|
||||
* 该作品的整理记录(最近 100 条,新→旧):含没有下载任务的手动整理(文件管理按路径整理)。
|
||||
* 下载历史里已按下载任务列出的记录也在这里,前端按 downloadTaskId 去重展示。
|
||||
*/
|
||||
organizationRecords: z
|
||||
.array(
|
||||
z.object({
|
||||
id: Id,
|
||||
status: OrganizationStatus,
|
||||
action: OrganizationAction,
|
||||
trigger: OrganizationTrigger,
|
||||
downloadTaskId: Id.nullable(),
|
||||
sourcePath: z.string().nullable(),
|
||||
targetPath: z.string().nullable(),
|
||||
fileSucceeded: NonNegInt,
|
||||
fileTotal: NonNegInt,
|
||||
createdAt: Timestamp,
|
||||
finishedAt: Timestamp.nullable(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
export type LibraryDetail = z.infer<typeof LibraryDetail>;
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { Kysely } from "kysely";
|
||||
import { execDdl } from "../src/migration-helpers.ts";
|
||||
|
||||
// QB-1:下载任务直接记关联作品(接管任务按文件名识别 / 手动指定 / 手动整理后回填),作品详情"下载历史"据此列出
|
||||
// 没有计划与站点资源的接管任务。已有行按 计划 Slot → 站点资源 → 最近一次整理记录 回填。
|
||||
|
||||
// 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 media_entity_id uuid references media_entity (id) on delete set null;
|
||||
create index if not exists download_task_media_entity_idx on download_task (media_entity_id) where media_entity_id is not null;
|
||||
update download_task dt set media_entity_id = coalesce(
|
||||
(select s.media_entity_id from acquisition_plan p join acquisition_slot s on s.id = p.slot_id where p.id = dt.acquisition_plan_id),
|
||||
(select sr.media_entity_id from source_release sr where sr.id = dt.source_release_id),
|
||||
(select r.media_entity_id from organization_record r
|
||||
where r.download_task_id = dt.id and r.media_entity_id is not null order by r.created_at desc limit 1))
|
||||
where dt.media_entity_id is null;`,
|
||||
);
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: 同上
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
await execDdl(
|
||||
db,
|
||||
`drop index if exists download_task_media_entity_idx;
|
||||
alter table download_task drop column if exists media_entity_id;`,
|
||||
);
|
||||
}
|
||||
@@ -701,6 +701,7 @@ export interface DownloadTaskTable {
|
||||
last_error: Json | null;
|
||||
created_at: GeneratedTimestamptz;
|
||||
updated_at: GeneratedTimestamptz;
|
||||
media_entity_id: string | null;
|
||||
}
|
||||
|
||||
export interface DownloaderInstanceTable {
|
||||
|
||||
Reference in new issue
Block a user