import { and, desc, eq, inArray, or } from "drizzle-orm"; import { getDatabase } from "@/modules/core/server/db"; import { careTasks, healthAnomalyReviews, systemIncidents, vitalRecords } from "@/modules/core/server/schema"; import type { Admission, FacilityBed, Permission } from "@/modules/core/types"; import type { Elder } from "@/modules/elders/types"; import { CARE_LEVEL_LABELS, ELDER_STATUS_LABELS } from "@/modules/elders/types"; import { CARE_TASK_STATUS_LABELS, CARE_TASK_TYPE_LABELS } from "@/modules/care/types"; import { HEALTH_REVIEW_SEVERITY_LABELS, HEALTH_REVIEW_STATUS_LABELS } from "@/modules/health/types"; import { INCIDENT_SEVERITY_LABELS, INCIDENT_STATUS_LABELS } from "@/modules/emergency/types"; import type { BedOperationalContext, ElderBedContextData, ElderOperationalContext, OperationalContextLine } from "@/modules/operations/types"; type ContextInput = { admissions: Admission[]; beds: FacilityBed[]; elders: Elder[]; organizationId: string; permissions: readonly Permission[]; }; function hasPermission(permissions: readonly Permission[], permission: Permission): boolean { return permissions.includes(permission); } function addLine(target: { lines: OperationalContextLine[] }, line: OperationalContextLine): void { if (target.lines.length >= 3) { return; } target.lines.push(line); } function includesText(source: string, token: string): boolean { return token.length > 0 && source.includes(token); } export async function listElderBedContextData(input: ContextInput): Promise { const { admissions, beds, elders: elderList, organizationId, permissions } = input; const elderContexts = Object.fromEntries(elderList.map((elder) => [elder.id, { lines: [] }])) as Record; const bedContexts = Object.fromEntries(beds.map((bed) => [bed.id, { lines: [] }])) as Record; const elderById = new Map(elderList.map((elder) => [elder.id, elder])); const activeAdmissions = admissions.filter((admission) => admission.status === "active"); for (const admission of activeAdmissions) { const elder = elderById.get(admission.elderId); const bedContext = bedContexts[admission.bedId]; if (!elder || !bedContext) { continue; } bedContext.occupant = { elderName: elder.name, careLevel: CARE_LEVEL_LABELS[elder.careLevel], status: ELDER_STATUS_LABELS[elder.status], }; } const database = getDatabase(); const elderIds = elderList.map((elder) => elder.id); if (elderIds.length === 0) { return { bedContexts, elderContexts }; } const [careRows, reviewRows, vitalRows, incidentRows] = await Promise.all([ hasPermission(permissions, "care:read") ? database .select() .from(careTasks) .where(and(eq(careTasks.organizationId, organizationId), inArray(careTasks.elderId, elderIds))) .orderBy(desc(careTasks.scheduledAt)) .limit(60) : [], hasPermission(permissions, "health:read") ? database .select() .from(healthAnomalyReviews) .where(and(eq(healthAnomalyReviews.organizationId, organizationId), inArray(healthAnomalyReviews.elderId, elderIds))) .orderBy(desc(healthAnomalyReviews.createdAt)) .limit(60) : [], hasPermission(permissions, "health:read") ? database .select() .from(vitalRecords) .where(and(eq(vitalRecords.organizationId, organizationId), inArray(vitalRecords.elderId, elderIds))) .orderBy(desc(vitalRecords.recordedAt)) .limit(60) : [], hasPermission(permissions, "incident:read") ? database .select() .from(systemIncidents) .where( and( eq(systemIncidents.organizationId, organizationId), or(eq(systemIncidents.status, "open"), eq(systemIncidents.status, "acknowledged")), ), ) .orderBy(desc(systemIncidents.createdAt)) .limit(60) : [], ]); const bedIdByElderId = new Map(activeAdmissions.map((admission) => [admission.elderId, admission.bedId])); const seenCareElders = new Set(); for (const task of careRows) { if (!task.elderId || seenCareElders.has(task.elderId)) { continue; } const line: OperationalContextLine = { kind: "care", label: "护理", value: `${CARE_TASK_TYPE_LABELS[task.careType]} / ${CARE_TASK_STATUS_LABELS[task.status]}`, tone: task.status === "pending" || task.status === "in_progress" ? "warning" : "muted", }; const elderContext = elderContexts[task.elderId]; if (elderContext) { addLine(elderContext, line); } const bedId = bedIdByElderId.get(task.elderId); const bedContext = bedId ? bedContexts[bedId] : undefined; if (bedContext) { addLine(bedContext, line); } seenCareElders.add(task.elderId); } const seenReviewElders = new Set(); for (const review of reviewRows) { if (seenReviewElders.has(review.elderId)) { continue; } const line: OperationalContextLine = { kind: "health", label: "健康", value: `${HEALTH_REVIEW_SEVERITY_LABELS[review.severity]} / ${HEALTH_REVIEW_STATUS_LABELS[review.status]}:${review.title}`, tone: review.severity === "critical" ? "danger" : "warning", }; const elderContext = elderContexts[review.elderId]; if (elderContext) { addLine(elderContext, line); } const bedId = bedIdByElderId.get(review.elderId); const bedContext = bedId ? bedContexts[bedId] : undefined; if (bedContext) { addLine(bedContext, line); } seenReviewElders.add(review.elderId); } const seenVitalElders = new Set(); for (const vital of vitalRows) { if (seenReviewElders.has(vital.elderId) || seenVitalElders.has(vital.elderId)) { continue; } const bp = vital.systolicBp && vital.diastolicBp ? `血压 ${vital.systolicBp}/${vital.diastolicBp}` : ""; const spo2 = vital.spo2 === null ? "" : `血氧 ${vital.spo2}%`; const value = [bp, spo2].filter(Boolean).join(" · ") || "最近体征已记录"; const line: OperationalContextLine = { kind: "health", label: "体征", value, tone: "muted" }; const elderContext = elderContexts[vital.elderId]; if (elderContext) { addLine(elderContext, line); } seenVitalElders.add(vital.elderId); } for (const incident of incidentRows) { const haystack = [incident.title, incident.description, incident.source].join(" "); for (const elder of elderList) { const bedId = bedIdByElderId.get(elder.id); const bed = bedId ? beds.find((item) => item.id === bedId) : undefined; const matchesElder = includesText(haystack, elder.name); const matchesBed = bed ? includesText(haystack, bed.code) || includesText(haystack, bed.roomName) : false; if (!matchesElder && !matchesBed) { continue; } const line: OperationalContextLine = { kind: "incident", label: "应急", value: `${INCIDENT_SEVERITY_LABELS[incident.severity]} / ${INCIDENT_STATUS_LABELS[incident.status]}:${incident.title}`, tone: incident.severity === "critical" ? "danger" : "warning", }; const elderContext = elderContexts[elder.id]; if (elderContext) { addLine(elderContext, line); } const bedContext = bedId ? bedContexts[bedId] : undefined; if (bedContext) { addLine(bedContext, line); } } } return { bedContexts, elderContexts }; }