type DocumentRecordLike = { _id: unknown; id?: string | null; user_id?: string | null; parent_id?: string | null; deleted_at?: string | null; created_at?: string | null; updated_at?: string | null; }; export function compareDocumentCanonicalOrder(a: DocumentRecordLike, b: DocumentRecordLike): number { const aDeleted = a?.deleted_at != null; const bDeleted = b?.deleted_at != null; if (aDeleted !== bDeleted) { return aDeleted ? 1 : -1; } const updatedA = String(a?.updated_at ?? ""); const updatedB = String(b?.updated_at ?? ""); if (updatedA !== updatedB) { return updatedB.localeCompare(updatedA); } const createdA = String(a?.created_at ?? ""); const createdB = String(b?.created_at ?? ""); if (createdA !== createdB) { return createdB.localeCompare(createdA); } return String(a?._id ?? "").localeCompare(String(b?._id ?? "")); } export function pickCanonicalDocumentRecord(records: T[]): T | null { if (records.length === 0) return null; return [...records].sort(compareDocumentCanonicalOrder)[0] ?? null; } export function pickCanonicalDocumentRecordsByBusinessId< T extends DocumentRecordLike & { id?: string | null }, >(records: readonly T[]): T[] { const grouped = new Map(); for (const record of records) { const businessId = String(record.id ?? "").trim(); if (!businessId) continue; const bucket = grouped.get(businessId); if (bucket) { bucket.push(record); } else { grouped.set(businessId, [record]); } } const result: T[] = []; for (const bucket of grouped.values()) { const canonical = pickCanonicalDocumentRecord([...bucket]); if (canonical) { result.push(canonical); } } return result; } export async function getCanonicalDocumentByBusinessId( ctx: any, documentId: string, ): Promise { const records = await ctx.db .query("documents") .withIndex("by_document_id", (q: any) => q.eq("id", documentId)) .collect(); return pickCanonicalDocumentRecord(records) as T | null; } export async function getCanonicalParentDocumentId( ctx: any, documentId: string | null | undefined, ): Promise { const normalizedId = String(documentId ?? "").trim(); if (!normalizedId) return null; const doc = await getCanonicalDocumentByBusinessId(ctx, normalizedId); return doc?.parent_id ?? null; } export async function requireCanonicalOwnedDocument( ctx: any, documentId: string, userId: string, ): Promise { const doc = await getCanonicalDocumentByBusinessId(ctx, documentId); if (!doc) { throw new Error("页面不存在"); } if (String(doc.user_id ?? "") !== String(userId)) { throw new Error("无权限"); } return doc; }