/** * M6 资源附件库 - 数据访问层 */ import "server-only"; import { db } from "@/shared/db"; import { lessonPlanAttachments } from "@/shared/db/schema"; import { and, eq, desc } from "drizzle-orm"; import type { AttachmentType } from "./lib/type-guards"; import { isAttachmentType } from "./lib/type-guards"; /** 附件记录 */ export interface LessonPlanAttachment { id: string; planId: string; blockId?: string; fileId: string; displayName: string; attachmentType: AttachmentType; uploadedBy: string; createdAt: Date; } /** * 查询课案的所有附件 */ export async function getAttachmentsByPlanId( planId: string, ): Promise { const rows = await db .select() .from(lessonPlanAttachments) .where(eq(lessonPlanAttachments.planId, planId)) .orderBy(desc(lessonPlanAttachments.createdAt)); return rows.map(mapRowToAttachment); } /** * 查询特定 Block 的附件 */ export async function getAttachmentsByBlockId( planId: string, blockId: string, ): Promise { const rows = await db .select() .from(lessonPlanAttachments) .where( and( eq(lessonPlanAttachments.planId, planId), eq(lessonPlanAttachments.blockId, blockId), ), ) .orderBy(desc(lessonPlanAttachments.createdAt)); return rows.map(mapRowToAttachment); } /** * 添加附件 */ export async function createAttachment( input: { planId: string; blockId?: string; fileId: string; displayName: string; attachmentType?: AttachmentType; }, uploadedBy: string, ): Promise { const [row] = await db.insert(lessonPlanAttachments).values({ planId: input.planId, blockId: input.blockId, fileId: input.fileId, displayName: input.displayName, attachmentType: input.attachmentType ?? "reference", uploadedBy, }); const insertedId = row.insertId; const all = await getAttachmentsByPlanId(input.planId); const created = all.find((a) => a.id === String(insertedId)); if (!created) throw new Error("ATTACHMENT_CREATE_FAILED"); return created; } /** * 删除附件 */ export async function deleteAttachment( attachmentId: string, ): Promise { await db .delete(lessonPlanAttachments) .where(eq(lessonPlanAttachments.id, attachmentId)); } /** * 更新附件类型 */ export async function updateAttachmentType( attachmentId: string, attachmentType: AttachmentType, ): Promise { await db .update(lessonPlanAttachments) .set({ attachmentType }) .where(eq(lessonPlanAttachments.id, attachmentId)); } /** * 按 ID 获取附件 */ export async function getAttachmentById( id: string, ): Promise { const rows = await db .select() .from(lessonPlanAttachments) .where(eq(lessonPlanAttachments.id, id)) .limit(1); return rows.length === 0 ? null : mapRowToAttachment(rows[0]!); } function mapRowToAttachment( row: typeof lessonPlanAttachments.$inferSelect, ): LessonPlanAttachment { const attachmentType = isAttachmentType(row.attachmentType) ? row.attachmentType : "reference"; return { id: row.id, planId: row.planId, blockId: row.blockId ?? undefined, fileId: row.fileId, displayName: row.displayName, attachmentType, uploadedBy: row.uploadedBy, createdAt: row.createdAt, }; }