import type Database from "bun:sqlite"; import { and, desc, eq } from "drizzle-orm"; import type { CreateEntityRequest, Entity, EntityType, UpdateEntityRequest } from "../../shared/api"; import type { Logger } from "../logger"; import { notDeleted, paginateQuery, softDeleteRecord, timestamp, wrap } from "./connection"; import { entities, projects } from "./schema"; export function createEntity( raw: Database, projectId: string, request: CreateEntityRequest, _logger: Logger, ): { entity: Entity } | { error: string; status: number } { const db = wrap(raw); const project = db .select() .from(projects) .where(and(eq(projects.id, projectId), notDeleted(projects))) .get(); if (!project) return { error: "项目不存在", status: 404 }; if (project.status === "archived") return { error: "已归档项目不可操作", status: 409 }; const name = request.name.trim(); if (!name) return { error: "实体名称不能为空", status: 400 }; const duplicate = db .select({ id: entities.id }) .from(entities) .where(and(eq(entities.projectId, projectId), eq(entities.name, name), notDeleted(entities))) .get(); if (duplicate) return { error: "实体名称已存在", status: 409 }; const id = crypto.randomUUID(); const now = timestamp(); db.insert(entities) .values({ aliases: JSON.stringify(request.aliases ?? []), createdAt: now, description: request.description?.trim() ?? "", id, name, projectId, type: request.type, updatedAt: now, }) .run(); const row = db.select().from(entities).where(eq(entities.id, id)).get(); return { entity: toEntity(row!) }; } export function deleteEntity( raw: Database, projectId: string, entityId: string, _logger: Logger, ): { error: string; status: number } | { success: true } { const db = wrap(raw); const row = db .select() .from(entities) .where(and(eq(entities.id, entityId), notDeleted(entities))) .get(); if (!row) return { error: "实体不存在", status: 404 }; if (row.projectId !== projectId) return { error: "实体不属于该项目", status: 403 }; softDeleteRecord(db, entities, entityId); return { success: true }; } export function getEntity( raw: Database, projectId: string, entityId: string, ): { entity: Entity } | { error: string; status: number } { const db = wrap(raw); const row = db .select() .from(entities) .where(and(eq(entities.id, entityId), notDeleted(entities))) .get(); if (!row) return { error: "实体不存在", status: 404 }; if (row.projectId !== projectId) return { error: "实体不属于该项目", status: 403 }; return { entity: toEntity(row) }; } export function listEntities( raw: Database, projectId: string, options: { page: number; pageSize: number; type?: EntityType }, ): { items: Entity[]; page: number; pageSize: number; total: number } { const conditions = [eq(entities.projectId, projectId)]; if (options.type) { conditions.push(eq(entities.type, options.type)); } return paginateQuery(raw, entities, { conditions, mapRow: toEntity, orderBy: () => desc(entities.createdAt), page: options.page, pageSize: options.pageSize, softDelete: entities.deletedAt, }); } export function listEntityNames( raw: Database, projectId: string, ): Array<{ aliases: string[]; id: string; name: string }> { const db = wrap(raw); const rows = db .select({ aliases: entities.aliases, id: entities.id, name: entities.name }) .from(entities) .where(and(eq(entities.projectId, projectId), notDeleted(entities))) .all(); return rows.map((row) => ({ aliases: JSON.parse(row.aliases) as string[], id: row.id, name: row.name, })); } export function updateEntity( raw: Database, projectId: string, entityId: string, request: UpdateEntityRequest, _logger: Logger, ): { entity: Entity } | { error: string; status: number } { const db = wrap(raw); const existing = db .select() .from(entities) .where(and(eq(entities.id, entityId), notDeleted(entities))) .get(); if (!existing) return { error: "实体不存在", status: 404 }; if (existing.projectId !== projectId) return { error: "实体不属于该项目", status: 403 }; const updates: Partial = { updatedAt: timestamp(), }; const name = request.name?.trim(); if (name === "") return { error: "实体名称不能为空", status: 400 }; if (name !== undefined && name !== existing.name) { const duplicate = db .select({ id: entities.id }) .from(entities) .where(and(eq(entities.projectId, projectId), eq(entities.name, name), notDeleted(entities))) .get(); if (duplicate) return { error: "实体名称已存在", status: 409 }; updates.name = name; } if (request.type !== undefined) { updates.type = request.type; } if (request.description !== undefined) { updates.description = request.description.trim(); } if (request.aliases !== undefined) { updates.aliases = JSON.stringify(request.aliases); } if (Object.keys(updates).length === 1 && updates.updatedAt) { return { entity: toEntity(existing) }; } db.update(entities).set(updates).where(eq(entities.id, entityId)).run(); const updated = db.select().from(entities).where(eq(entities.id, entityId)).get(); return { entity: toEntity(updated!) }; } function toEntity(row: typeof entities.$inferSelect): Entity { return { aliases: JSON.parse(row.aliases) as string[], createdAt: row.createdAt, description: row.description, id: row.id, name: row.name, projectId: row.projectId, type: row.type as EntityType, updatedAt: row.updatedAt, }; }