import "server-only"; import { and, desc, eq } from "drizzle-orm"; import type { AiCitation, ElderAiDataScope } from "@/modules/ai/types"; import { getDatabase } from "@/modules/core/server/db"; import { admissions, alertTriggers, beds, careTasks, chronicConditions, elders, familyFeedback, healthAnomalyReviews, healthProfiles, rooms, systemIncidents, vitalRecords, } from "@/modules/core/server/schema"; import type { AuthContext } from "@/modules/core/types"; type ElderAiContextResult = | { success: true; context: ElderAiResidentContext } | { success: false; reason: string; status: number }; export type ElderAiResidentContext = { organizationId: string; elderId: string; elderName: string; dataScopes: ElderAiDataScope[]; sections: string[]; citations: AiCitation[]; promptContext: string; }; function hasPermission(context: AuthContext, permission: string): boolean { return context.permissions.includes(permission as never); } function toIsoString(value: Date | null): string { return value ? value.toISOString() : ""; } function pushCitation(citations: AiCitation[], input: Omit): string { const id = `ctx-${citations.length + 1}`; citations.push({ id, ...input }); return id; } function compactText(value: string): string { return value.trim().replace(/\s+/g, " "); } function appendSection(parts: string[], title: string, lines: string[]): void { const compactLines = lines.map(compactText).filter(Boolean); if (compactLines.length === 0) { return; } parts.push(`## ${title}\n${compactLines.join("\n")}`); } export async function buildElderAiContext(context: AuthContext, elderId: string): Promise { const organizationId = context.organization?.id; if (!organizationId) { return { success: false, reason: "请选择机构后生成 AI 分析", status: 400 }; } const database = getDatabase(); const elderRows = await database .select() .from(elders) .where(and(eq(elders.id, elderId), eq(elders.organizationId, organizationId))) .limit(1); const elder = elderRows[0]; if (!elder) { return { success: false, reason: "老人档案不存在", status: 404 }; } const dataScopes: ElderAiDataScope[] = ["elder"]; const citations: AiCitation[] = []; const sections: string[] = []; const elderCitationId = pushCitation(citations, { sourceType: "resident_context", sourceId: elder.id, title: "老人基础档案", excerpt: `${elder.name},${elder.age} 岁,照护等级 ${elder.careLevel},状态 ${elder.status}`, }); appendSection(sections, "老人基础档案", [ `[${elderCitationId}] 姓名:${elder.name};性别:${elder.gender};年龄:${elder.age};照护等级:${elder.careLevel};状态:${elder.status}`, `主要联系人:${elder.primaryContact};电话:${elder.phone}`, elder.medicalNotes ? `医疗备注:${elder.medicalNotes}` : "", ]); const [ admissionRows, healthProfileRows, vitalRows, conditionRows, reviewRows, careTaskRows, alertRows, feedbackRows, incidentRows, ] = await Promise.all([ hasPermission(context, "admission:read") || hasPermission(context, "facility:read") ? database .select({ admission: admissions, bedCode: beds.code, roomCode: rooms.code, }) .from(admissions) .leftJoin(beds, eq(beds.id, admissions.bedId)) .leftJoin(rooms, eq(rooms.id, beds.roomId)) .where(and(eq(admissions.organizationId, organizationId), eq(admissions.elderId, elderId))) .orderBy(desc(admissions.admittedAt)) .limit(8) : Promise.resolve([]), hasPermission(context, "health:read") ? database .select() .from(healthProfiles) .where(and(eq(healthProfiles.organizationId, organizationId), eq(healthProfiles.elderId, elderId))) .limit(1) : Promise.resolve([]), hasPermission(context, "health:read") ? database .select() .from(vitalRecords) .where(and(eq(vitalRecords.organizationId, organizationId), eq(vitalRecords.elderId, elderId))) .orderBy(desc(vitalRecords.recordedAt)) .limit(8) : Promise.resolve([]), hasPermission(context, "health:read") ? database .select() .from(chronicConditions) .where(and(eq(chronicConditions.organizationId, organizationId), eq(chronicConditions.elderId, elderId))) .orderBy(desc(chronicConditions.updatedAt)) .limit(8) : Promise.resolve([]), hasPermission(context, "health:read") ? database .select() .from(healthAnomalyReviews) .where(and(eq(healthAnomalyReviews.organizationId, organizationId), eq(healthAnomalyReviews.elderId, elderId))) .orderBy(desc(healthAnomalyReviews.createdAt)) .limit(8) : Promise.resolve([]), hasPermission(context, "care:read") ? database .select() .from(careTasks) .where(and(eq(careTasks.organizationId, organizationId), eq(careTasks.elderId, elderId))) .orderBy(desc(careTasks.scheduledAt)) .limit(12) : Promise.resolve([]), hasPermission(context, "alert:read") ? database .select() .from(alertTriggers) .where(and(eq(alertTriggers.organizationId, organizationId), eq(alertTriggers.elderId, elderId))) .orderBy(desc(alertTriggers.createdAt)) .limit(8) : Promise.resolve([]), hasPermission(context, "family:read") ? database .select() .from(familyFeedback) .where(and(eq(familyFeedback.organizationId, organizationId), eq(familyFeedback.elderId, elderId))) .orderBy(desc(familyFeedback.createdAt)) .limit(8) : Promise.resolve([]), hasPermission(context, "incident:read") ? database .select() .from(systemIncidents) .where(eq(systemIncidents.organizationId, organizationId)) .orderBy(desc(systemIncidents.createdAt)) .limit(6) : Promise.resolve([]), ]); if (admissionRows.length > 0) { if (hasPermission(context, "admission:read")) { dataScopes.push("admission"); } if (hasPermission(context, "facility:read")) { dataScopes.push("facility"); } appendSection( sections, "入住与床位", admissionRows.map((row) => { const citationId = pushCitation(citations, { sourceType: "resident_context", sourceId: row.admission.id, title: "入住记录", excerpt: `${row.admission.status},床位 ${row.roomCode ?? ""}-${row.bedCode ?? ""}`, }); return `[${citationId}] 状态:${row.admission.status};房间/床位:${row.roomCode ?? "未知"}/${row.bedCode ?? "未知"};入住:${toIsoString(row.admission.admittedAt)};退住:${toIsoString(row.admission.dischargedAt)};备注:${row.admission.notes}`; }), ); } if (hasPermission(context, "health:read")) { dataScopes.push("health"); const profile = healthProfileRows[0]; appendSection(sections, "健康档案", [ profile ? `[${pushCitation(citations, { sourceType: "resident_context", sourceId: profile.id, title: "健康档案", excerpt: profile.medicalHistory || profile.medicationNotes || profile.emergencyNotes || "健康档案", })}] 过敏:${profile.allergyNotes};病史:${profile.medicalHistory};用药:${profile.medicationNotes};照护限制:${profile.careRestrictions};紧急备注:${profile.emergencyNotes}` : "暂无健康档案", ...vitalRows.map((vital) => { const citationId = pushCitation(citations, { sourceType: "resident_context", sourceId: vital.id, title: "生命体征", excerpt: `${toIsoString(vital.recordedAt)} 心率 ${vital.heartRate ?? "-"} 血压 ${vital.systolicBp ?? "-"}/${vital.diastolicBp ?? "-"}`, }); return `[${citationId}] ${toIsoString(vital.recordedAt)};来源:${vital.source};血压:${vital.systolicBp ?? "-"}/${vital.diastolicBp ?? "-"};心率:${vital.heartRate ?? "-"};体温:${vital.temperatureTenths ? vital.temperatureTenths / 10 : "-"};血氧:${vital.spo2 ?? "-"};备注:${vital.notes}`; }), ...conditionRows.map((condition) => { const citationId = pushCitation(citations, { sourceType: "resident_context", sourceId: condition.id, title: "慢病记录", excerpt: `${condition.name} ${condition.status}`, }); return `[${citationId}] 慢病:${condition.name};状态:${condition.status};治疗:${condition.treatmentNotes};随访:${condition.followUpNotes}`; }), ...reviewRows.map((review) => { const citationId = pushCitation(citations, { sourceType: "resident_context", sourceId: review.id, title: "健康异常复核", excerpt: `${review.severity} ${review.title}`, }); return `[${citationId}] 异常:${review.title};严重度:${review.severity};状态:${review.status};说明:${review.description};处理:${review.resolutionNotes}`; }), ]); } if (hasPermission(context, "care:read")) { dataScopes.push("care"); appendSection( sections, "护理任务", careTaskRows.map((task) => { const citationId = pushCitation(citations, { sourceType: "resident_context", sourceId: task.id, title: "护理任务", excerpt: `${task.priority} ${task.title}`, }); return `[${citationId}] ${task.title};类型:${task.careType};优先级:${task.priority};状态:${task.status};计划:${toIsoString(task.scheduledAt)};执行备注:${task.executionNotes}`; }), ); } if (hasPermission(context, "alert:read")) { dataScopes.push("alert"); appendSection( sections, "预警触发", alertRows.map((alert) => { const citationId = pushCitation(citations, { sourceType: "resident_context", sourceId: alert.id, title: "预警触发", excerpt: `${alert.title} ${alert.status}`, }); return `[${citationId}] ${alert.title};状态:${alert.status};来源:${alert.source};说明:${alert.description};处理:${alert.handlingNotes}`; }), ); } if (hasPermission(context, "family:read")) { dataScopes.push("family"); appendSection( sections, "家属反馈", feedbackRows.map((feedback) => { const citationId = pushCitation(citations, { sourceType: "resident_context", sourceId: feedback.id, title: "家属反馈", excerpt: `${feedback.feedbackType} ${feedback.status}`, }); return `[${citationId}] 类型:${feedback.feedbackType};状态:${feedback.status};内容:${feedback.content};回复:${feedback.responseNotes}`; }), ); } if (hasPermission(context, "incident:read")) { dataScopes.push("incident"); appendSection( sections, "近期机构事件", incidentRows.map((incident) => { const citationId = pushCitation(citations, { sourceType: "resident_context", sourceId: incident.id, title: "系统/应急事件", excerpt: `${incident.severity} ${incident.title}`, }); return `[${citationId}] ${incident.title};严重度:${incident.severity};状态:${incident.status};来源:${incident.source};说明:${incident.description}`; }), ); } const uniqueScopes = [...new Set(dataScopes)]; return { success: true, context: { organizationId, elderId, elderName: elder.name, dataScopes: uniqueScopes, sections, citations, promptContext: sections.join("\n\n"), }, }; }