import type Database from "bun:sqlite"; import { desc, eq } from "drizzle-orm"; import type { CreateMaterialRequest, Material, MaterialStatus } from "../../shared/api"; import type { Logger } from "../logger"; import { paginateQuery, wrap } from "./connection"; import { materials, projects } from "./schema"; export function createMaterial( raw: Database, projectId: string, request: CreateMaterialRequest, _logger: Logger, ): { error: string; status: number } | { material: Material } { const db = wrap(raw); const project = db.select().from(projects).where(eq(projects.id, projectId)).get(); if (!project) return { error: "项目不存在", status: 404 }; if (project.status === "archived") return { error: "已归档项目不可操作", status: 409 }; const description = request.description.trim(); if (!description) return { error: "描述不能为空", status: 400 }; const dateRegex = /^\d{4}-\d{2}-\d{2}$/; if (!dateRegex.test(request.associatedDate)) { return { error: "associatedDate 格式错误,要求 YYYY-MM-DD", status: 400 }; } const id = crypto.randomUUID(); const now = new Date().toISOString(); db.insert(materials) .values({ associatedDate: request.associatedDate, createdAt: now, description, id, projectId, status: "pending", updatedAt: now, }) .run(); const row = db.select().from(materials).where(eq(materials.id, id)).get(); return { material: toMaterial(row!) }; } export function deleteMaterial( raw: Database, projectId: string, materialId: string, _logger: Logger, ): { error: string; status: number } | { success: true } { const db = wrap(raw); const row = db.select().from(materials).where(eq(materials.id, materialId)).get(); if (!row) return { error: "素材不存在", status: 404 }; if (row.projectId !== projectId) return { error: "素材不属于该项目", status: 403 }; db.delete(materials).where(eq(materials.id, materialId)).run(); return { success: true }; } export function getMaterial( raw: Database, projectId: string, materialId: string, ): { error: string; status: number } | { material: Material } { const db = wrap(raw); const row = db.select().from(materials).where(eq(materials.id, materialId)).get(); if (!row) return { error: "素材不存在", status: 404 }; if (row.projectId !== projectId) return { error: "素材不属于该项目", status: 403 }; return { material: toMaterial(row) }; } export function listMaterials( raw: Database, projectId: string, options: { page: number; pageSize: number; status?: MaterialStatus }, ): { items: Material[]; page: number; pageSize: number; total: number } { const conditions = [eq(materials.projectId, projectId)]; if (options.status) { conditions.push(eq(materials.status, options.status)); } return paginateQuery(raw, materials, { conditions, mapRow: toMaterial, orderBy: () => desc(materials.createdAt), page: options.page, pageSize: options.pageSize, }); } function toMaterial(row: typeof materials.$inferSelect): Material { return { associatedDate: row.associatedDate, createdAt: row.createdAt, description: row.description, id: row.id, projectId: row.projectId, status: row.status, updatedAt: row.updatedAt, }; }