import { and, desc, eq, inArray } from "drizzle-orm"; import { getDatabase } from "@/modules/core/server/db"; import { accounts, careTasks, elders } from "@/modules/core/server/schema"; import type { CareExecutionData, CareTask, CareTaskStatusUpdateInput } from "@/modules/care/types"; export type CareMutationFailure = { success: false; reason: string; status: number; }; type AccountNameRow = { id: string; name: string; }; type ElderNameRow = { id: string; name: string; }; export function isCareMutationFailure(value: unknown): value is CareMutationFailure { return ( typeof value === "object" && value !== null && "success" in value && (value as { success?: unknown }).success === false ); } function iso(value: Date): string { return value.toISOString(); } function optionalIso(value: Date | null): string | undefined { return value ? iso(value) : undefined; } function buildNameMap(rows: AccountNameRow[] | ElderNameRow[]): Map { return new Map(rows.map((row) => [row.id, row.name])); } function toCareTask( row: typeof careTasks.$inferSelect, elderNameById: Map, accountNameById: Map, ): CareTask { const elderId = row.elderId ?? undefined; const completedByAccountId = row.completedByAccountId ?? undefined; return { id: row.id, organizationId: row.organizationId, elderId, elderName: elderId ? elderNameById.get(elderId) ?? "" : "", title: row.title, careType: row.careType, priority: row.priority, status: row.status, scheduledAt: iso(row.scheduledAt), assigneeLabel: row.assigneeLabel, executionNotes: row.executionNotes, completedAt: optionalIso(row.completedAt), completedByAccountId, completedByName: completedByAccountId ? accountNameById.get(completedByAccountId) : undefined, createdAt: iso(row.createdAt), updatedAt: iso(row.updatedAt), }; } export async function listCareExecutionData(organizationId: string): Promise { const database = getDatabase(); const todayStart = new Date(); todayStart.setHours(0, 0, 0, 0); const taskRows = await database .select() .from(careTasks) .where(eq(careTasks.organizationId, organizationId)) .orderBy(desc(careTasks.scheduledAt)); const elderIds = Array.from(new Set(taskRows.map((task) => task.elderId).filter((elderId): elderId is string => typeof elderId === "string"))); const accountIds = Array.from( new Set(taskRows.map((task) => task.completedByAccountId).filter((accountId): accountId is string => typeof accountId === "string")), ); const [elderRows, accountRows] = await Promise.all([ elderIds.length > 0 ? database.select({ id: elders.id, name: elders.name }).from(elders).where(and(eq(elders.organizationId, organizationId), inArray(elders.id, elderIds))) : [], accountIds.length > 0 ? database.select({ id: accounts.id, name: accounts.name }).from(accounts).where(inArray(accounts.id, accountIds)) : [], ]); const tasks = taskRows.map((task) => toCareTask(task, buildNameMap(elderRows), buildNameMap(accountRows))); return { metrics: { pending: taskRows.filter((task) => task.status === "pending").length, inProgress: taskRows.filter((task) => task.status === "in_progress").length, completedToday: taskRows.filter((task) => task.status === "completed" && task.completedAt !== null && task.completedAt >= todayStart).length, highPriority: taskRows.filter((task) => task.priority === "high" || task.priority === "urgent").length, }, tasks, }; } export async function updateCareTaskStatus( input: CareTaskStatusUpdateInput & { accountId: string; id: string; organizationId: string }, ): Promise { const database = getDatabase(); const completedAt = input.status === "completed" ? new Date() : null; const completedByAccountId = input.status === "completed" ? input.accountId : null; const rows = await database .update(careTasks) .set({ status: input.status, executionNotes: input.executionNotes, completedAt, completedByAccountId, updatedAt: new Date(), }) .where(and(eq(careTasks.id, input.id), eq(careTasks.organizationId, input.organizationId))) .returning(); const task = rows[0]; if (!task) { return { success: false, reason: "护理任务不存在", status: 404 }; } const [elderRows, accountRows] = await Promise.all([ task.elderId ? database.select({ id: elders.id, name: elders.name }).from(elders).where(and(eq(elders.id, task.elderId), eq(elders.organizationId, input.organizationId))) : [], task.completedByAccountId ? database.select({ id: accounts.id, name: accounts.name }).from(accounts).where(eq(accounts.id, task.completedByAccountId)) : [], ]); return toCareTask(task, buildNameMap(elderRows), buildNameMap(accountRows)); }