feat(db,contracts): W5-C subscription units migration 0025, contracts and mapping

- subscription: status pending (TMDB not ready: searchable/evaluable, not completable),
  last_searched_at / next_search_at
- content_unit (season): episode_total ledger with source / lock / timestamp
- acquisition_plan: unit_keys (Slot × unit reservation) and file_selection; drop the
  one-in-flight-per-slot unique index (checked under slot row lock per unit instead)
- new tables slot_deferral, slot_unit (per-unit quality ledger), subscription_history
- contracts: SubscriptionParams, SubscriptionHistory, SlotDeferral, SlotUnit, SubscriptionProgress,
  EpisodeTotalSet, CandidateEvaluation pri/trace/unitKeys and decision deferred;
  endpoints subscriptions.search/progress/setEpisodeTotal and subscription-history
- mapping: evaluation messages via describeViolation, pri/trace passthrough

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-27 23:33:56 +08:00
1 parent d162d464c4
commit 1dd460d359
9 files changed
+405 -34

No files matched your search

+10 -1
View File
@@ -92,7 +92,10 @@ export type PlanPurpose = z.infer<typeof PlanPurpose>;
export const PlanStatus = z.enum(["draft", "reserved", "submitted", "completed", "cancelled", "failed", "expired"]);
export type PlanStatus = z.infer<typeof PlanStatus>;
/** 下载计划:与 Slot 预留、预算预留同事务创建;一个 Slot 同时只能有一个 reserved/submitted 计划 */
/**
* 下载计划:与 Slot 预留、预算预留同事务创建。
* 剧集 Slot 可同时有多个单元不相交的 reserved/submitted 计划(Slot×单元预留);电影 Slot 同时只有一个。
*/
export const AcquisitionPlan = EntityBase.extend({
slotId: Id,
sourceReleaseId: Id,
@@ -109,6 +112,10 @@ export const AcquisitionPlan = EntityBase.extend({
idempotencyKey: z.string().min(1),
expiresAt: Timestamp.nullable(),
statusReason: z.string().nullable(),
/** 计划预留的内容单元(content_unit id 与合成键 s{季}e{集};s{季}* = 整季未核对),在途期间计入缺集的"已预留" */
unitKeys: z.array(z.string()),
/** 季包按缺集选文件的结果(提交后读文件清单;无法判定时整包下载并记理由) */
fileSelection: JsonObject.nullable(),
});
export type AcquisitionPlan = z.infer<typeof AcquisitionPlan>;
export const AcquisitionPlanCreate = AcquisitionPlan.omit({ ...SERVER_MANAGED, status: true }).partial({
@@ -121,6 +128,8 @@ export const AcquisitionPlanCreate = AcquisitionPlan.omit({ ...SERVER_MANAGED, s
expectedRevision: true,
expiresAt: true,
statusReason: true,
unitKeys: true,
fileSelection: true,
});
export type AcquisitionPlanCreate = z.infer<typeof AcquisitionPlanCreate>;