feat(identity): open identity_unresolved decisions for resources with no candidates
no_match resources (and lookup failures after auto-review gives up) now
open a decision in the inbox with options to set a TMDB ID, pick a local
work, or dismiss; ambiguous decisions get the same manual options.
Resolve accepts identity {tmdb | mediaEntityId} and assigns with a manual
lock. The inbox option dialog collects the TMDB type/ID or searches local
works; the identity pool links each pending item to its inbox filter.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
a0b0a64246
commit
fb7727cf66
14 files changed
+280
-37
No files matched your search
@@ -11,7 +11,7 @@ import { audit } from "../service/common.ts";
|
||||
import { assignReleaseIdentity, dismissPoolEntry } from "../service/identity-admin.ts";
|
||||
|
||||
// 待处理中心数据源:decision 列表/详情/聚合/处理。
|
||||
// 身份歧义类由本模块直接执行所选候选;其它类型只记录处理结果并发 decision.updated,
|
||||
// 身份类(歧义 / 待识别)由本模块直接执行所选候选或手动指定;其它类型只记录处理结果并发 decision.updated,
|
||||
// 需要执行能力的选项由 W2-D 的 resolveDecision 消费。
|
||||
|
||||
const OPEN = ["open", "investigating", "awaiting_user"] as const;
|
||||
@@ -113,8 +113,10 @@ export function decisionRoutes(app: ModuleApp, rt: () => SourcesRuntime) {
|
||||
const opt = body.optionKey ? options.find((o) => o.key === body.optionKey) : undefined;
|
||||
if (body.optionKey && !opt) throw new ApiException("bad_request", `未知选项 ${body.optionKey}`);
|
||||
|
||||
// 身份歧义:直接执行(选候选 = 人工指定并加锁;忽略 = 待识别池 dismissed)
|
||||
if (d.kind === "identity_ambiguous" && d.subject_type === "source_release" && d.subject_id) {
|
||||
// 身份类:直接执行(选候选 / 手动指定 TMDB id / 选本地作品 = 人工指定并加锁;忽略 = 待识别池 dismissed)
|
||||
const identityKind = d.kind === "identity_ambiguous" || d.kind === "identity_unresolved";
|
||||
if (identityKind && d.subject_type === "source_release" && d.subject_id) {
|
||||
const requires = opt?.input?.requires;
|
||||
if (body.dismiss || opt?.key === "dismiss") {
|
||||
const pool = await db()
|
||||
.selectFrom("identity_candidate")
|
||||
@@ -122,6 +124,28 @@ export function decisionRoutes(app: ModuleApp, rt: () => SourcesRuntime) {
|
||||
.where("source_release_id", "=", d.subject_id)
|
||||
.executeTakeFirst();
|
||||
if (pool) await dismissPoolEntry(rt(), pool.id, actor, body.note ?? null);
|
||||
} else if (requires === "tmdb") {
|
||||
const tmdb = body.identity?.tmdb;
|
||||
if (!tmdb) throw new ApiException("bad_request", "需要 identity.tmdb {type, id}");
|
||||
await assignReleaseIdentity(
|
||||
rt(),
|
||||
d.subject_id,
|
||||
{
|
||||
external: { provider: "tmdb", entityType: tmdb.type, externalId: tmdb.id },
|
||||
lock: true,
|
||||
reason: body.note ?? `decision ${d.id}`,
|
||||
},
|
||||
actor,
|
||||
);
|
||||
} else if (requires === "mediaEntityId") {
|
||||
const mediaEntityId = body.identity?.mediaEntityId;
|
||||
if (!mediaEntityId) throw new ApiException("bad_request", "需要 identity.mediaEntityId");
|
||||
await assignReleaseIdentity(
|
||||
rt(),
|
||||
d.subject_id,
|
||||
{ mediaEntityId, lock: true, reason: body.note ?? `decision ${d.id}` },
|
||||
actor,
|
||||
);
|
||||
} else if (opt?.input && typeof opt.input.candidateIndex === "number") {
|
||||
const pool = await db()
|
||||
.selectFrom("identity_candidate")
|
||||
|
||||
Reference in new issue
Block a user