feat: complete ops dashboard AI board

This commit is contained in:
2026-07-08 19:47:28 -07:00
parent 6a1add3422
commit a8776f9d79
14 changed files with 1401 additions and 189 deletions

View File

@@ -9,6 +9,7 @@ import { retrieveKnowledge } from "@/modules/ai/server/knowledge";
import type {
AiCitation,
AiErrorCategory,
ElderAiAnalysisBoardItem,
ElderAiAnalysisHistoryItem,
ElderAiAnalysisOutput,
} from "@/modules/ai/types";
@@ -19,7 +20,7 @@ import {
} from "@/modules/ai/types";
import { recordAuditLog } from "@/modules/core/server/audit";
import { getDatabase } from "@/modules/core/server/db";
import { elderAiAnalyses } from "@/modules/core/server/schema";
import { elderAiAnalyses, elders } from "@/modules/core/server/schema";
import type { AuthContext } from "@/modules/core/types";
type ServiceResult<T> = { success: true; data: T } | { success: false; reason: string; status: number };
@@ -202,6 +203,48 @@ export async function listElderAiAnalyses(
};
}
function rowToBoardItem(row: AnalysisRow, elderName: string, permissions: AuthContext["permissions"]): ElderAiAnalysisBoardItem {
return {
elderName,
...rowToHistoryItem(row, permissions),
};
}
export async function listAiAnalysisBoard(
context: AuthContext,
limit = 8,
): Promise<ServiceResult<{ items: ElderAiAnalysisBoardItem[] }>> {
if (!context.permissions.includes("ai:read")) {
return { success: false, reason: "无权查看 AI 分析", status: 403 };
}
const organizationId = context.organization?.id;
if (!organizationId) {
return { success: false, reason: "请选择机构后查看 AI 分析", status: 400 };
}
const normalizedLimit = Number.isFinite(limit) ? Math.trunc(limit) : 8;
const rowLimit = Math.min(20, Math.max(1, normalizedLimit));
const database = getDatabase();
const [rows, elderRows] = await Promise.all([
database
.select()
.from(elderAiAnalyses)
.where(eq(elderAiAnalyses.organizationId, organizationId))
.orderBy(desc(elderAiAnalyses.createdAt))
.limit(rowLimit),
database.select({ id: elders.id, name: elders.name }).from(elders).where(eq(elders.organizationId, organizationId)),
]);
const elderNameById = new Map(elderRows.map((elder) => [elder.id, elder.name]));
return {
success: true,
data: {
items: rows.map((row) => rowToBoardItem(row, elderNameById.get(row.elderId) ?? "未知老人", context.permissions)),
},
};
}
export async function generateElderAiAnalysis(
context: AuthContext,
elderId: string,