feat(db): 迁移 000148 — AI 聊天会话/消息/工具日志/用户画像 4 张表
This commit is contained in:
@@ -149,6 +149,7 @@ mod m20260513_000144_enforce_version_optimistic_lock;
|
||||
mod m20260513_000145_seed_missing_permissions;
|
||||
mod m20260515_000146_seed_menu_permissions_phase2;
|
||||
mod m20260516_000147_seed_ai_chat_permission;
|
||||
mod m20260518_000148_create_ai_chat_tables;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -305,6 +306,7 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20260513_000145_seed_missing_permissions::Migration),
|
||||
Box::new(m20260515_000146_seed_menu_permissions_phase2::Migration),
|
||||
Box::new(m20260516_000147_seed_ai_chat_permission::Migration),
|
||||
Box::new(m20260518_000148_create_ai_chat_tables::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
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> {
|
||||
// ai_chat_sessions — AI 会话表
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(AiChatSessions::Table)
|
||||
.col(
|
||||
ColumnDef::new(AiChatSessions::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(AiChatSessions::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(AiChatSessions::UserId).uuid().not_null())
|
||||
.col(ColumnDef::new(AiChatSessions::PatientId).uuid().null())
|
||||
.col(ColumnDef::new(AiChatSessions::Title).string_len(255).null())
|
||||
.col(
|
||||
ColumnDef::new(AiChatSessions::Status)
|
||||
.string_len(20)
|
||||
.not_null()
|
||||
.default("active"),
|
||||
)
|
||||
.col(ColumnDef::new(AiChatSessions::Metadata).json().null())
|
||||
.col(
|
||||
ColumnDef::new(AiChatSessions::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(AiChatSessions::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(ColumnDef::new(AiChatSessions::CreatedBy).uuid().null())
|
||||
.col(ColumnDef::new(AiChatSessions::UpdatedBy).uuid().null())
|
||||
.col(
|
||||
ColumnDef::new(AiChatSessions::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(AiChatSessions::VersionLock)
|
||||
.integer()
|
||||
.not_null()
|
||||
.default(1),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx_ai_chat_sessions_tenant_user")
|
||||
.table(AiChatSessions::Table)
|
||||
.col(AiChatSessions::TenantId)
|
||||
.col(AiChatSessions::UserId)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// ai_chat_messages — AI 聊天消息表
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(AiChatMessages::Table)
|
||||
.col(
|
||||
ColumnDef::new(AiChatMessages::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(AiChatMessages::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(AiChatMessages::SessionId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(AiChatMessages::Role)
|
||||
.string_len(20)
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(AiChatMessages::Content).text().null())
|
||||
.col(ColumnDef::new(AiChatMessages::ToolCalls).json().null())
|
||||
.col(
|
||||
ColumnDef::new(AiChatMessages::ToolCallId)
|
||||
.string_len(100)
|
||||
.null(),
|
||||
)
|
||||
.col(ColumnDef::new(AiChatMessages::TokenCount).integer().null())
|
||||
.col(
|
||||
ColumnDef::new(AiChatMessages::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(AiChatMessages::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(ColumnDef::new(AiChatMessages::CreatedBy).uuid().null())
|
||||
.col(ColumnDef::new(AiChatMessages::UpdatedBy).uuid().null())
|
||||
.col(
|
||||
ColumnDef::new(AiChatMessages::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(AiChatMessages::VersionLock)
|
||||
.integer()
|
||||
.not_null()
|
||||
.default(1),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_ai_chat_messages_session")
|
||||
.from(AiChatMessages::Table, AiChatMessages::SessionId)
|
||||
.to(AiChatSessions::Table, AiChatSessions::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx_ai_chat_messages_session")
|
||||
.table(AiChatMessages::Table)
|
||||
.col(AiChatMessages::TenantId)
|
||||
.col(AiChatMessages::SessionId)
|
||||
.col(AiChatMessages::CreatedAt)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// ai_tool_call_logs — AI 工具调用日志(append-only)
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(AiToolCallLogs::Table)
|
||||
.col(
|
||||
ColumnDef::new(AiToolCallLogs::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(AiToolCallLogs::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(AiToolCallLogs::SessionId).uuid().not_null())
|
||||
.col(ColumnDef::new(AiToolCallLogs::MessageId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(AiToolCallLogs::ToolName)
|
||||
.string_len(100)
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(AiToolCallLogs::Parameters).json().null())
|
||||
.col(ColumnDef::new(AiToolCallLogs::ResultSummary).text().null())
|
||||
.col(ColumnDef::new(AiToolCallLogs::ExecutionMs).integer().null())
|
||||
.col(ColumnDef::new(AiToolCallLogs::Success).boolean().not_null())
|
||||
.col(
|
||||
ColumnDef::new(AiToolCallLogs::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(ColumnDef::new(AiToolCallLogs::CreatedBy).uuid().null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx_ai_tool_call_logs_session")
|
||||
.table(AiToolCallLogs::Table)
|
||||
.col(AiToolCallLogs::TenantId)
|
||||
.col(AiToolCallLogs::SessionId)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// ai_user_profiles — 用户长期画像
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(AiUserProfiles::Table)
|
||||
.col(
|
||||
ColumnDef::new(AiUserProfiles::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(AiUserProfiles::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(AiUserProfiles::UserId).uuid().not_null())
|
||||
.col(ColumnDef::new(AiUserProfiles::Preferences).json().null())
|
||||
.col(ColumnDef::new(AiUserProfiles::HealthInterests).array(ColumnType::Text))
|
||||
.col(ColumnDef::new(AiUserProfiles::FrequentTopics).array(ColumnType::Text))
|
||||
.col(
|
||||
ColumnDef::new(AiUserProfiles::PersonalitySummary)
|
||||
.text()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(AiUserProfiles::LastUpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(AiUserProfiles::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(AiUserProfiles::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(AiUserProfiles::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(AiUserProfiles::VersionLock)
|
||||
.integer()
|
||||
.not_null()
|
||||
.default(1),
|
||||
)
|
||||
.index(
|
||||
Index::create()
|
||||
.name("uq_ai_user_profiles_tenant_user")
|
||||
.col(AiUserProfiles::TenantId)
|
||||
.col(AiUserProfiles::UserId)
|
||||
.unique(),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(AiUserProfiles::Table).to_owned())
|
||||
.await?;
|
||||
manager
|
||||
.drop_table(Table::drop().table(AiToolCallLogs::Table).to_owned())
|
||||
.await?;
|
||||
manager
|
||||
.drop_table(Table::drop().table(AiChatMessages::Table).to_owned())
|
||||
.await?;
|
||||
manager
|
||||
.drop_table(Table::drop().table(AiChatSessions::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum AiChatSessions {
|
||||
Table,
|
||||
Id,
|
||||
TenantId,
|
||||
UserId,
|
||||
PatientId,
|
||||
Title,
|
||||
Status,
|
||||
Metadata,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
CreatedBy,
|
||||
UpdatedBy,
|
||||
DeletedAt,
|
||||
VersionLock,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum AiChatMessages {
|
||||
Table,
|
||||
Id,
|
||||
TenantId,
|
||||
SessionId,
|
||||
Role,
|
||||
Content,
|
||||
ToolCalls,
|
||||
ToolCallId,
|
||||
TokenCount,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
CreatedBy,
|
||||
UpdatedBy,
|
||||
DeletedAt,
|
||||
VersionLock,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum AiToolCallLogs {
|
||||
Table,
|
||||
Id,
|
||||
TenantId,
|
||||
SessionId,
|
||||
MessageId,
|
||||
ToolName,
|
||||
Parameters,
|
||||
ResultSummary,
|
||||
ExecutionMs,
|
||||
Success,
|
||||
CreatedAt,
|
||||
CreatedBy,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum AiUserProfiles {
|
||||
Table,
|
||||
Id,
|
||||
TenantId,
|
||||
UserId,
|
||||
Preferences,
|
||||
HealthInterests,
|
||||
FrequentTopics,
|
||||
PersonalitySummary,
|
||||
LastUpdatedAt,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
DeletedAt,
|
||||
VersionLock,
|
||||
}
|
||||
Reference in New Issue
Block a user