134 lines
4.2 KiB
Go
134 lines
4.2 KiB
Go
package database
|
||
|
||
import (
|
||
"database/sql"
|
||
"errors"
|
||
"fmt"
|
||
|
||
"infinite-canvas/backend/internal/model"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// Models 是应用持久化表的唯一清单,服务启动和跨数据库迁移必须共用它。
|
||
func Models() []any {
|
||
return []any{
|
||
&model.User{},
|
||
&model.AuthSession{},
|
||
&model.UserIdentity{},
|
||
&model.OAuthState{},
|
||
&model.EmailVerificationCode{},
|
||
&model.ModelChannel{},
|
||
&model.ChannelModel{},
|
||
&model.ApiCallLog{},
|
||
&model.ModelPricing{},
|
||
&model.CreditAccount{},
|
||
&model.CreditLedgerEntry{},
|
||
&model.BillingOrder{},
|
||
&model.RedeemBatch{},
|
||
&model.RedeemCode{},
|
||
&model.AdminAuditEvent{},
|
||
&model.UserDailyActivity{},
|
||
&model.SystemSetting{},
|
||
&model.UserOSSSetting{},
|
||
&model.UserDailyUploadUsage{},
|
||
&model.Skill{},
|
||
&model.UserSkillState{},
|
||
&model.Resource{},
|
||
&model.Asset{},
|
||
&model.ProjectAssetLink{},
|
||
&model.ProjectAssetCandidate{},
|
||
&model.AssetVersion{},
|
||
&model.AssetRepresentation{},
|
||
&model.VoiceProfile{},
|
||
&model.CharacterVoiceBinding{},
|
||
&model.Project{},
|
||
&model.StyleProfile{},
|
||
&model.ProjectUnit{},
|
||
&model.CanvasUnitLink{},
|
||
&model.Shot{},
|
||
&model.ShotAssetReference{},
|
||
&model.WorkflowTemplateVersion{},
|
||
&model.WorkflowInstance{},
|
||
&model.WorkflowStepInstance{},
|
||
&model.WorkflowStepTask{},
|
||
&model.CanvasProject{},
|
||
&model.CanvasShare{},
|
||
&model.PromptTemplate{},
|
||
&model.UserPromptCustomization{},
|
||
&model.Announcement{},
|
||
&model.UserAnnouncementRead{},
|
||
&model.Task{},
|
||
&model.TaskTextDelta{},
|
||
&model.Session{},
|
||
&model.Message{},
|
||
&model.TaskLog{},
|
||
&model.SessionFile{},
|
||
&model.Result{},
|
||
}
|
||
}
|
||
|
||
func MigrateSchema(db *gorm.DB) error {
|
||
// 旧表只保存 Updream 目录状态,与本地技能主键没有可迁移关系;首次升级时按产品要求清空重建。
|
||
if db.Migrator().HasColumn(&model.UserSkillState{}, "skill_dir") && !db.Migrator().HasColumn(&model.UserSkillState{}, "skill_id") {
|
||
if err := db.Migrator().DropTable(&model.UserSkillState{}); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
if err := widenPostgresAssetIDColumns(db); err != nil {
|
||
return err
|
||
}
|
||
if err := db.AutoMigrate(Models()...); err != nil {
|
||
return err
|
||
}
|
||
// 逻辑删除后的同名模型允许重新添加,旧唯一索引不能继续覆盖已删除记录。
|
||
if err := db.Exec("DROP INDEX IF EXISTS idx_channel_model_key").Error; err != nil {
|
||
return err
|
||
}
|
||
if err := db.Exec("DROP INDEX IF EXISTS idx_users_email").Error; err != nil {
|
||
return err
|
||
}
|
||
return db.Exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_nonempty ON users(lower(email)) WHERE email <> ''").Error
|
||
}
|
||
|
||
type varcharColumnMigration struct {
|
||
table string
|
||
column string
|
||
statement string
|
||
}
|
||
|
||
var assetIDColumnMigrations = []varcharColumnMigration{
|
||
{table: "assets", column: "id", statement: `ALTER TABLE "assets" ALTER COLUMN "id" TYPE varchar(80)`},
|
||
{table: "project_asset_links", column: "asset_id", statement: `ALTER TABLE "project_asset_links" ALTER COLUMN "asset_id" TYPE varchar(80)`},
|
||
{table: "project_asset_candidates", column: "resolved_asset_id", statement: `ALTER TABLE "project_asset_candidates" ALTER COLUMN "resolved_asset_id" TYPE varchar(80)`},
|
||
{table: "asset_versions", column: "asset_id", statement: `ALTER TABLE "asset_versions" ALTER COLUMN "asset_id" TYPE varchar(80)`},
|
||
}
|
||
|
||
// PostgreSQL Migrator 跳过主键列变更,素材 ID 扩容必须在 AutoMigrate 前显式执行。
|
||
func widenPostgresAssetIDColumns(db *gorm.DB) error {
|
||
if db.Dialector.Name() != "postgres" {
|
||
return nil
|
||
}
|
||
for _, migration := range assetIDColumnMigrations {
|
||
var currentLength sql.NullInt64
|
||
err := db.Raw(
|
||
"SELECT character_maximum_length FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = ? AND column_name = ?",
|
||
migration.table,
|
||
migration.column,
|
||
).Row().Scan(¤tLength)
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
continue
|
||
}
|
||
if err != nil {
|
||
return fmt.Errorf("检查 PostgreSQL 素材 ID 列 %s.%s:%w", migration.table, migration.column, err)
|
||
}
|
||
if !currentLength.Valid || currentLength.Int64 >= model.AssetIDMaxLength {
|
||
continue
|
||
}
|
||
if err := db.Exec(migration.statement).Error; err != nil {
|
||
return fmt.Errorf("扩容 PostgreSQL 素材 ID 列 %s.%s:%w", migration.table, migration.column, err)
|
||
}
|
||
}
|
||
return nil
|
||
}
|