feat: add AI knowledge analysis MVP
This commit is contained in:
@@ -45,6 +45,10 @@ export const PERMISSION_DEFINITIONS: PermissionDefinition[] = [
|
||||
{ id: "elder:create", label: "新增老人档案", category: "老人", description: "创建老人档案。" },
|
||||
{ id: "elder:update", label: "更新老人档案", category: "老人", description: "更新老人档案和照护信息。" },
|
||||
{ id: "elder:delete", label: "删除老人档案", category: "老人", description: "删除老人档案。" },
|
||||
{ id: "ai:read", label: "查看 AI 分析", category: "AI", description: "查看并生成授权数据范围内的 AI 分析。" },
|
||||
{ id: "ai:manage", label: "管理 AI 能力", category: "AI", description: "管理 AI 能力配置和分析治理。" },
|
||||
{ id: "knowledge:read", label: "查看知识库", category: "AI", description: "查看并检索平台和机构知识库内容。" },
|
||||
{ id: "knowledge:manage", label: "管理知识库", category: "AI", description: "维护平台或机构知识库条目。" },
|
||||
];
|
||||
|
||||
type SeedRole = {
|
||||
@@ -121,6 +125,10 @@ export const SYSTEM_ROLE_DEFINITIONS: SeedRole[] = [
|
||||
"elder:create",
|
||||
"elder:update",
|
||||
"elder:delete",
|
||||
"ai:read",
|
||||
"ai:manage",
|
||||
"knowledge:read",
|
||||
"knowledge:manage",
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -151,6 +159,10 @@ export const SYSTEM_ROLE_DEFINITIONS: SeedRole[] = [
|
||||
"elder:create",
|
||||
"elder:update",
|
||||
"elder:delete",
|
||||
"ai:read",
|
||||
"ai:manage",
|
||||
"knowledge:read",
|
||||
"knowledge:manage",
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -169,6 +181,8 @@ export const SYSTEM_ROLE_DEFINITIONS: SeedRole[] = [
|
||||
"admission:read",
|
||||
"elder:read",
|
||||
"elder:update",
|
||||
"ai:read",
|
||||
"knowledge:read",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { sql } from "drizzle-orm";
|
||||
import {
|
||||
boolean,
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
pgEnum,
|
||||
pgTable,
|
||||
primaryKey,
|
||||
@@ -9,6 +11,7 @@ import {
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
vector,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
export const accountStatusEnum = pgEnum("account_status", ["active", "disabled", "pending"]);
|
||||
@@ -41,6 +44,11 @@ export const familyContactStatusEnum = pgEnum("family_contact_status", ["active"
|
||||
export const familyVisitStatusEnum = pgEnum("family_visit_status", ["requested", "approved", "completed", "cancelled"]);
|
||||
export const familyFeedbackTypeEnum = pgEnum("family_feedback_type", ["service", "care", "meal", "environment", "other"]);
|
||||
export const familyFeedbackStatusEnum = pgEnum("family_feedback_status", ["open", "in_progress", "resolved", "closed"]);
|
||||
export const aiKnowledgeScopeEnum = pgEnum("ai_knowledge_scope", ["platform", "organization"]);
|
||||
export const aiKnowledgeStatusEnum = pgEnum("ai_knowledge_status", ["enabled", "disabled"]);
|
||||
export const elderAiAnalysisStatusEnum = pgEnum("elder_ai_analysis_status", ["completed", "failed"]);
|
||||
|
||||
export const AI_KNOWLEDGE_EMBEDDING_DIMENSIONS = 1536;
|
||||
|
||||
export const systemSettings = pgTable("system_settings", {
|
||||
id: text("id").primaryKey().default("global"),
|
||||
@@ -521,3 +529,55 @@ export const systemIncidents = pgTable("system_incidents", {
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const aiKnowledgeEntries = pgTable("ai_knowledge_entries", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
scope: aiKnowledgeScopeEnum("scope").notNull(),
|
||||
organizationId: uuid("organization_id").references(() => organizations.id, { onDelete: "cascade" }),
|
||||
title: text("title").notNull(),
|
||||
category: text("category").notNull().default(""),
|
||||
tags: text("tags").notNull().default(""),
|
||||
body: text("body").notNull(),
|
||||
status: aiKnowledgeStatusEnum("status").notNull().default("enabled"),
|
||||
createdByAccountId: uuid("created_by_account_id").references(() => accounts.id, { onDelete: "set null" }),
|
||||
updatedByAccountId: uuid("updated_by_account_id").references(() => accounts.id, { onDelete: "set null" }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (table) => ({
|
||||
scopeLookup: index("ai_knowledge_entries_scope_lookup").on(table.scope, table.status),
|
||||
organizationLookup: index("ai_knowledge_entries_organization_lookup").on(table.organizationId, table.status),
|
||||
}));
|
||||
|
||||
export const aiKnowledgeChunks = pgTable("ai_knowledge_chunks", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
entryId: uuid("entry_id").notNull().references(() => aiKnowledgeEntries.id, { onDelete: "cascade" }),
|
||||
organizationId: uuid("organization_id").references(() => organizations.id, { onDelete: "cascade" }),
|
||||
scope: aiKnowledgeScopeEnum("scope").notNull(),
|
||||
chunkIndex: integer("chunk_index").notNull(),
|
||||
content: text("content").notNull(),
|
||||
embedding: vector("embedding", { dimensions: AI_KNOWLEDGE_EMBEDDING_DIMENSIONS }).notNull(),
|
||||
sourceTitle: text("source_title").notNull(),
|
||||
sourceCategory: text("source_category").notNull().default(""),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (table) => ({
|
||||
entryLookup: index("ai_knowledge_chunks_entry_lookup").on(table.entryId),
|
||||
scopeLookup: index("ai_knowledge_chunks_scope_lookup").on(table.scope, table.organizationId),
|
||||
}));
|
||||
|
||||
export const elderAiAnalyses = pgTable("elder_ai_analyses", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
|
||||
elderId: uuid("elder_id").notNull().references(() => elders.id, { onDelete: "cascade" }),
|
||||
actorAccountId: uuid("actor_account_id").references(() => accounts.id, { onDelete: "set null" }),
|
||||
status: elderAiAnalysisStatusEnum("status").notNull(),
|
||||
dataScopes: jsonb("data_scopes").$type<string[]>().notNull().default(sql`'[]'::jsonb`),
|
||||
resultJson: jsonb("result_json"),
|
||||
citationsJson: jsonb("citations_json").notNull().default(sql`'[]'::jsonb`),
|
||||
modelSummaryJson: jsonb("model_summary_json").notNull().default(sql`'{}'::jsonb`),
|
||||
errorCategory: text("error_category").notNull().default(""),
|
||||
errorReason: text("error_reason").notNull().default(""),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (table) => ({
|
||||
elderLookup: index("elder_ai_analyses_elder_lookup").on(table.organizationId, table.elderId, table.createdAt),
|
||||
statusLookup: index("elder_ai_analyses_status_lookup").on(table.organizationId, table.status),
|
||||
}));
|
||||
|
||||
@@ -59,6 +59,10 @@ export const PERMISSIONS = [
|
||||
"elder:create",
|
||||
"elder:update",
|
||||
"elder:delete",
|
||||
"ai:read",
|
||||
"ai:manage",
|
||||
"knowledge:read",
|
||||
"knowledge:manage",
|
||||
] as const;
|
||||
|
||||
export type Permission = (typeof PERMISSIONS)[number];
|
||||
|
||||
Reference in New Issue
Block a user