From 0bbf5885aa6f2d2a679bcc39380283ed72d03a21 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 3 Oct 2026 23:32:10 +0800 Subject: [PATCH] =?UTF-8?q?fix(acquisition):=20=E8=AF=84=E4=BC=B0=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E8=A1=A5=E5=BB=BA=E6=94=B6=E5=BD=95=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=97=B6=E9=94=81=E8=AE=A2=E9=98=85=E8=A1=8C;=E8=A7=84?= =?UTF-8?q?=E5=88=99=20API=20=E6=B5=8B=E8=AF=95=E6=8C=89=E6=96=B0=E5=BB=BA?= =?UTF-8?q?=E8=AE=A2=E9=98=85=E8=87=AA=E5=B8=A6=E9=BB=98=E8=AE=A4=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei --- .../acquisition-organize/services/acquisition.ts | 15 +++++++++++---- .../test/acquisition-organize/rules-api.test.ts | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/server/src/modules/acquisition-organize/services/acquisition.ts b/apps/server/src/modules/acquisition-organize/services/acquisition.ts index edebcf38..d215091e 100644 --- a/apps/server/src/modules/acquisition-organize/services/acquisition.ts +++ b/apps/server/src/modules/acquisition-organize/services/acquisition.ts @@ -233,17 +233,24 @@ export async function createSlotsFromTemplates( /** 追踪中的订阅里没有自己收录版本的,逐个补建;返回是否建了 */ async function backfillSubscriptionSlots( - exec: Exec, + db: AoDeps["ctx"]["db"], subs: readonly SubscriptionRow[], slots: readonly Pick[], ): Promise { let made = 0; for (const s of subs) - if (s.media_entity_id && !slots.some((x) => x.subscription_id === s.id)) - made += await ensureSubscriptionSlot(exec, s); + if (s.media_entity_id && !slots.some((x) => x.subscription_id === s.id)) made += await ensureSlotLocked(db, s); return made > 0; } +/** 评估路径上补建:锁订阅行,免得并发的两次评估各建一个 */ +function ensureSlotLocked(db: AoDeps["ctx"]["db"], sub: SubscriptionRow): Promise { + return db.transaction().execute(async (trx) => { + await trx.selectFrom("subscription").select("id").where("id", "=", sub.id).forUpdate().execute(); + return ensureSubscriptionSlot(trx, sub); + }); +} + /** 订阅没有收录策略模板时的隐式缺省收录版本:无硬条件,表单的质量预设 / 包含排除随后由订阅参数写入 */ export const DEFAULT_SLOT_TEMPLATE: SlotTemplate = { name: "默认版本" }; @@ -377,7 +384,7 @@ async function reevaluate(c: Ctx, input: EvaluateInput): Promise { }); expect(ok.body.data).toMatchObject({ revision: 2, softPreferences: { hdr: ["DV"] } }); const list = await h.api("GET", `/acquisition-slots?mediaEntityId=${e}`); - expect(list.body.data.items).toHaveLength(1); + // 新建订阅时已自动建了该订阅自己的「默认版本」 + expect(list.body.data.items.map((x: { name: string }) => x.name).sort()).toEqual(["1080p", "默认版本"].sort()); expect(h.engine.enqueued.some((q) => q.task === "evaluateAcquisition")).toBe(true); });