Files
cms-website/apps/api/src/modules/import/anchors.ts
T

72 lines
3.0 KiB
TypeScript
Raw Normal View History

// 五列内容身份锚点共享 helper(Plan 4 Task 1,ADR-006、Plan 3 前置事实 ④):
// source_records 的内容身份(site + connection + source_type + source_id + locale,
// 五列——快照列不在键上,跨快照复用同一 content_entry)→ 挂接 entry 的最新快照行
// (latest first)。此前同一查询在 content/term/route 三写入器各持一份拷贝——
// publish 域(第 4 个消费方触发 Plan 3 审查既定条件)起抽此单源共享,零行为变更
// (WHERE/ORDER BY/LIMIT 逐字保真,由既有集成测试守护)。
//
// 消费方契约:
// - content-writer(findPriorLinkedRecord):id/checksum/entryId 全消费(幂等三态);
// - term-writer(resolveContentEntryId)/ route-writer(resolveEntryAnchor):
// 只消费 contentEntryId(entry 锚点解析)。
// 六列(快照内)查询**不在**此处——那是 content/media 写入器各自的快照行查找
// (snapshot_id 进键),与五列身份语义不同,勿合并。
import { and, desc, eq, isNotNull } from 'drizzle-orm'
import { sourceRecords } from '../../db/schema.js'
import type { ImportDb } from './import.service.js'
/** 五列身份的固定侧(site + connection)——各写入器的 WriterContext 均结构性满足 */
export interface AnchorIdentity {
siteId: string
connectionId: string
}
/** 五列身份的行侧(UniversalRow 的身份三列投影形状) */
export interface AnchorRowKey {
sourceType: string
sourceId: string
locale: string
}
/** 命中行(contentEntryId 非 null——谓词保证;normalizedChecksum 可空:record-only 足迹无) */
export interface LinkedSourceRecord {
id: string
contentEntryId: string
normalizedChecksum: string | null
}
/**
* 五列内容身份 → 挂接 entry 的最新 source_records 行(latest first——createdAt 降序,
* 与既有三写入器同口径);无挂接行 → null。谓词含 content_entry_id IS NOT NULL
* (record-only / 占位足迹不参与锚点解析)。
*/
export async function findLatestLinkedSourceRecord(
db: ImportDb,
identity: AnchorIdentity,
row: AnchorRowKey,
): Promise<LinkedSourceRecord | null> {
const rows = await db
.select({
id: sourceRecords.id,
contentEntryId: sourceRecords.contentEntryId,
normalizedChecksum: sourceRecords.normalizedChecksum,
})
.from(sourceRecords)
.where(
and(
eq(sourceRecords.siteId, identity.siteId),
eq(sourceRecords.sourceConnectionId, identity.connectionId),
eq(sourceRecords.sourceType, row.sourceType),
eq(sourceRecords.sourceId, row.sourceId),
eq(sourceRecords.locale, row.locale),
isNotNull(sourceRecords.contentEntryId),
),
)
.orderBy(desc(sourceRecords.createdAt))
.limit(1)
const r = rows[0]
if (r === undefined) return null
// contentEntryId 非 null:查询谓词保证(drizzle 谓词不细化 select 类型)
return { id: r.id, contentEntryId: r.contentEntryId!, normalizedChecksum: r.normalizedChecksum }
}