fix(organize): deduplicate concurrent identity recovery plans

This commit is contained in:
root committed 2026-11-05 23:56:42 +08:00
1 parent 8d8bda152b
commit 32e1d446b5
3 files changed
+64 -3

No files matched your search

@@ -161,6 +161,10 @@ export class OrganizeRecoveryError extends Error {
}
}
class OrganizeRetryConflictError extends Error {
readonly code = "conflict";
}
interface RetryOptions {
mediaEntityId?: string;
idempotencyKey?: string;
@@ -204,12 +208,16 @@ async function retryLocked(
if (overrides.idempotencyKey) {
const existing = await d.ctx.db
.selectFrom("organization_record")
.select(["id", "task_id", "status", "reason"])
.select(["id", "task_id", "status", "reason", "media_entity_id"])
.where(sql<string>`plan->>'idempotencyKey'`, "=", overrides.idempotencyKey)
.where(sql<string>`plan->>'retryOfRecordId'`, "=", recordId)
.where("status", "in", ["queued", "running", "succeeded", "partial"])
.executeTakeFirst();
if (existing?.task_id)
if (existing?.task_id) {
if (overrides.mediaEntityId && existing.media_entity_id !== overrides.mediaEntityId)
throw new OrganizeRetryConflictError("这条整理事项已按其他作品处理,刷新后查看结果");
return { recordId: existing.id, taskId: existing.task_id, status: existing.status, reason: existing.reason };
}
}
const resolved = await resolveOrganizeRecovery(d.ctx.db, record);
if (!resolved.recovery.ready) throw new OrganizeRecoveryError(resolved.recovery);
@@ -341,7 +341,14 @@ async function reorganizeAs(
const recordId = row.subject_type === "organization_record" ? row.subject_id : null;
if (!recordId) throw new HookError("not_found", "这条事项没有关联的整理记录");
const dtId = ev.downloadTaskId;
const r = await retryOrganizeRecord(d, recordId, actor, via, { mediaEntityId: entityId });
const r = await retryOrganizeRecord(d, recordId, actor, via, {
mediaEntityId: entityId,
idempotencyKey: `organize-identity:${row.id}`,
}).catch((error: unknown) => {
if (error instanceof Error && (error as { code?: string }).code === "conflict")
throw new HookError("conflict", error.message);
throw error;
});
if (!["running", "queued", "succeeded", "partial"].includes(r.status))
throw new HookError("conflict", `作品已确认,但重新整理没能执行:${r.reason ?? r.status}`);
if (dtId)