feat(ai): Copilot 基因化 Phase 0 Task 1-4 — 迁移 + Entity + 规则引擎
- 4 表迁移: copilot_rules, copilot_insights, copilot_risk_snapshots, copilot_chat_logs - 4 个 SeaORM Entity 对应新表 - JSONLogic 规则引擎 (evaluate + evaluate_rules) + 5 个单元测试
This commit is contained in:
@@ -138,6 +138,11 @@ mod m20260510_000133_create_patient_role;
|
||||
mod m20260510_000134_create_media_folder;
|
||||
mod m20260510_000135_create_media_item;
|
||||
mod m20260510_000136_create_banner;
|
||||
mod m20260510_000137_seed_media_banner_menus;
|
||||
mod m20260512_000138_create_copilot_rules;
|
||||
mod m20260512_000139_create_copilot_insights;
|
||||
mod m20260512_000140_create_copilot_risk_snapshots;
|
||||
mod m20260512_000141_create_copilot_chat_logs;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -283,6 +288,11 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20260510_000134_create_media_folder::Migration),
|
||||
Box::new(m20260510_000135_create_media_item::Migration),
|
||||
Box::new(m20260510_000136_create_banner::Migration),
|
||||
Box::new(m20260510_000137_seed_media_banner_menus::Migration),
|
||||
Box::new(m20260512_000138_create_copilot_rules::Migration),
|
||||
Box::new(m20260512_000139_create_copilot_insights::Migration),
|
||||
Box::new(m20260512_000140_create_copilot_risk_snapshots::Migration),
|
||||
Box::new(m20260512_000141_create_copilot_chat_logs::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(CopilotRules::Table)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(CopilotRules::TenantId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::Name)
|
||||
.string_len(200)
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::Category)
|
||||
.string_len(50)
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::ConditionExpr)
|
||||
.json()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::Score)
|
||||
.small_integer()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::Severity)
|
||||
.string_len(20)
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(CopilotRules::Suggestion).text().null())
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::Enabled)
|
||||
.boolean()
|
||||
.not_null()
|
||||
.default(true),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::SortOrder)
|
||||
.integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(ColumnDef::new(CopilotRules::CreatedBy).uuid().null())
|
||||
.col(ColumnDef::new(CopilotRules::UpdatedBy).uuid().null())
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRules::VersionLock)
|
||||
.integer()
|
||||
.not_null()
|
||||
.default(1),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx_copilot_rules_tenant_category")
|
||||
.table(CopilotRules::Table)
|
||||
.col(CopilotRules::TenantId)
|
||||
.col(CopilotRules::Category)
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(CopilotRules::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum CopilotRules {
|
||||
Table,
|
||||
Id,
|
||||
TenantId,
|
||||
Name,
|
||||
Category,
|
||||
ConditionExpr,
|
||||
Score,
|
||||
Severity,
|
||||
Suggestion,
|
||||
Enabled,
|
||||
SortOrder,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
CreatedBy,
|
||||
UpdatedBy,
|
||||
DeletedAt,
|
||||
VersionLock,
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(CopilotInsights::Table)
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(CopilotInsights::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(CopilotInsights::PatientId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::InsightType)
|
||||
.string_len(50)
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::Source)
|
||||
.string_len(20)
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::Severity)
|
||||
.string_len(20)
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::Title)
|
||||
.string_len(500)
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(CopilotInsights::Content).json().not_null())
|
||||
.col(ColumnDef::new(CopilotInsights::RuleMatches).json().null())
|
||||
.col(ColumnDef::new(CopilotInsights::LlmSupplement).text().null())
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::ExpiresAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::IsRead)
|
||||
.boolean()
|
||||
.not_null()
|
||||
.default(false),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::IsDismissed)
|
||||
.boolean()
|
||||
.not_null()
|
||||
.default(false),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(ColumnDef::new(CopilotInsights::CreatedBy).uuid().null())
|
||||
.col(ColumnDef::new(CopilotInsights::UpdatedBy).uuid().null())
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotInsights::VersionLock)
|
||||
.integer()
|
||||
.not_null()
|
||||
.default(1),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx_copilot_insights_tenant_patient")
|
||||
.table(CopilotInsights::Table)
|
||||
.col(CopilotInsights::TenantId)
|
||||
.col(CopilotInsights::PatientId)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx_copilot_insights_expires")
|
||||
.table(CopilotInsights::Table)
|
||||
.col(CopilotInsights::ExpiresAt)
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(CopilotInsights::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum CopilotInsights {
|
||||
Table,
|
||||
Id,
|
||||
TenantId,
|
||||
PatientId,
|
||||
InsightType,
|
||||
Source,
|
||||
Severity,
|
||||
Title,
|
||||
Content,
|
||||
RuleMatches,
|
||||
LlmSupplement,
|
||||
ExpiresAt,
|
||||
IsRead,
|
||||
IsDismissed,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
CreatedBy,
|
||||
UpdatedBy,
|
||||
DeletedAt,
|
||||
VersionLock,
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(CopilotRiskSnapshots::Table)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::TenantId)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::PatientId)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::RiskScore)
|
||||
.small_integer()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::RiskLevel)
|
||||
.string_len(20)
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::RuleDetails)
|
||||
.json()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::LlmSummary)
|
||||
.text()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::ComputedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::DataFreshness)
|
||||
.json()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::CreatedBy)
|
||||
.uuid()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::UpdatedBy)
|
||||
.uuid()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotRiskSnapshots::VersionLock)
|
||||
.integer()
|
||||
.not_null()
|
||||
.default(1),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx_copilot_risk_snapshots_tenant_patient")
|
||||
.table(CopilotRiskSnapshots::Table)
|
||||
.col(CopilotRiskSnapshots::TenantId)
|
||||
.col(CopilotRiskSnapshots::PatientId)
|
||||
.unique()
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(CopilotRiskSnapshots::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum CopilotRiskSnapshots {
|
||||
Table,
|
||||
Id,
|
||||
TenantId,
|
||||
PatientId,
|
||||
RiskScore,
|
||||
RiskLevel,
|
||||
RuleDetails,
|
||||
LlmSummary,
|
||||
ComputedAt,
|
||||
DataFreshness,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
CreatedBy,
|
||||
UpdatedBy,
|
||||
DeletedAt,
|
||||
VersionLock,
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(CopilotChatLogs::Table)
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(CopilotChatLogs::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(CopilotChatLogs::PatientId).uuid().not_null())
|
||||
.col(ColumnDef::new(CopilotChatLogs::SessionId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::UserMessage)
|
||||
.text()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::IntentClassification)
|
||||
.string_len(30)
|
||||
.null(),
|
||||
)
|
||||
.col(ColumnDef::new(CopilotChatLogs::AiRawResponse).text().null())
|
||||
.col(ColumnDef::new(CopilotChatLogs::Layer1Result).json().null())
|
||||
.col(ColumnDef::new(CopilotChatLogs::Layer2Result).json().null())
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::ViolationsFound)
|
||||
.json()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::FixStrategy)
|
||||
.string_len(30)
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::FinalResponse)
|
||||
.text()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(ColumnDef::new(CopilotChatLogs::CreatedBy).uuid().null())
|
||||
.col(ColumnDef::new(CopilotChatLogs::UpdatedBy).uuid().null())
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(CopilotChatLogs::VersionLock)
|
||||
.integer()
|
||||
.not_null()
|
||||
.default(1),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx_copilot_chat_logs_session")
|
||||
.table(CopilotChatLogs::Table)
|
||||
.col(CopilotChatLogs::TenantId)
|
||||
.col(CopilotChatLogs::SessionId)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx_copilot_chat_logs_patient")
|
||||
.table(CopilotChatLogs::Table)
|
||||
.col(CopilotChatLogs::TenantId)
|
||||
.col(CopilotChatLogs::PatientId)
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(CopilotChatLogs::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum CopilotChatLogs {
|
||||
Table,
|
||||
Id,
|
||||
TenantId,
|
||||
PatientId,
|
||||
SessionId,
|
||||
UserMessage,
|
||||
IntentClassification,
|
||||
AiRawResponse,
|
||||
Layer1Result,
|
||||
Layer2Result,
|
||||
ViolationsFound,
|
||||
FixStrategy,
|
||||
FinalResponse,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
CreatedBy,
|
||||
UpdatedBy,
|
||||
DeletedAt,
|
||||
VersionLock,
|
||||
}
|
||||
Reference in New Issue
Block a user