feat(db): 迁移 000148 — AI 聊天会话/消息/工具日志/用户画像 4 张表

This commit is contained in:
iven
2026-05-18 02:51:58 +08:00
parent f668f0995a
commit 877e9831f6
2 changed files with 337 additions and 0 deletions

View File

@@ -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),
]
}
}

View File

@@ -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,
}