fix(database): 资源上传幂等 - 补齐生产库 upload_key 迁移
This commit is contained in:
1 parent
a6fa105689
commit
9c9b9cf88d
5 files changed
+69
-3
No files matched your search
@@ -10,11 +10,12 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const CurrentSchemaVersion int64 = 3
|
||||
const CurrentSchemaVersion int64 = 4
|
||||
|
||||
const baselineSchemaChecksum = "sha256:open-ai-canvas-schema-v1-20260830"
|
||||
const schemaMigrationAppliedAtIndexChecksum = "sha256:schema-migrations-applied-at-index-v2-20260830"
|
||||
const assetTaxonomyCandidateIdentityChecksum = "sha256:asset-taxonomy-candidate-identity-v3-20260831-r1"
|
||||
const resourceUploadKeyChecksum = "sha256:resource-upload-key-v4-20260901"
|
||||
|
||||
const postgresSchemaMigrationLockID int64 = 73123910420260830
|
||||
|
||||
@@ -44,6 +45,7 @@ var schemaMigrations = []migration{
|
||||
{version: 1, name: "baseline_gorm_schema", checksum: baselineSchemaChecksum, apply: migrateSchemaV1},
|
||||
{version: 2, name: "schema_migrations_applied_at_index", checksum: schemaMigrationAppliedAtIndexChecksum, apply: migrateSchemaV2},
|
||||
{version: 3, name: "asset_taxonomy_candidate_identity", checksum: assetTaxonomyCandidateIdentityChecksum, apply: migrateSchemaV3},
|
||||
{version: 4, name: "resource_upload_key", checksum: resourceUploadKeyChecksum, apply: migrateSchemaV4},
|
||||
}
|
||||
|
||||
func migrateSchemaV2(tx *gorm.DB) error {
|
||||
@@ -89,6 +91,21 @@ func migrateSchemaV3(tx *gorm.DB) error {
|
||||
return tx.Exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_project_asset_candidates_pending_identity ON project_asset_candidates(project_id, category, name_key) WHERE status = 'pending_confirmation' AND name_key <> ''").Error
|
||||
}
|
||||
|
||||
func migrateSchemaV4(tx *gorm.DB) error {
|
||||
if !tx.Migrator().HasTable(&model.Resource{}) {
|
||||
return fmt.Errorf("资源表不存在")
|
||||
}
|
||||
if !tx.Migrator().HasColumn(&model.Resource{}, "upload_key") {
|
||||
if err := tx.Migrator().AddColumn(&model.Resource{}, "UploadKey"); err != nil {
|
||||
return fmt.Errorf("增加资源上传幂等列:%w", err)
|
||||
}
|
||||
}
|
||||
if err := tx.Exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_resources_user_upload_key ON resources (user_id, upload_key)").Error; err != nil {
|
||||
return fmt.Errorf("创建资源上传幂等索引:%w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MigrateSchema(db *gorm.DB) error {
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if tx.Dialector.Name() == "postgres" {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"infinite-canvas/backend/internal/model"
|
||||
|
||||
@@ -88,6 +89,50 @@ func TestMigrateSchemaV3NormalizesLegacyAccessoryCategory(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateSchemaV4AddsResourceUploadKeyToExistingSchema(t *testing.T) {
|
||||
db, err := Open(Config{Driver: "sqlite", DSN: "file:migration-resource-upload-key?mode=memory&cache=shared"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Exec(`CREATE TABLE resources (id TEXT PRIMARY KEY, user_id TEXT NOT NULL)`).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.AutoMigrate(&schemaMigration{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, item := range schemaMigrations[:3] {
|
||||
if err := db.Create(&schemaMigration{Version: item.version, Name: item.name, Checksum: item.checksum, AppliedAt: time.Now().UTC()}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := MigrateSchema(db); err != nil {
|
||||
t.Fatalf("migrate existing schema: %v", err)
|
||||
}
|
||||
if !db.Migrator().HasColumn(&model.Resource{}, "upload_key") {
|
||||
t.Fatal("resource upload_key column was not added")
|
||||
}
|
||||
if !db.Migrator().HasIndex(&model.Resource{}, "idx_resources_user_upload_key") {
|
||||
t.Fatal("resource upload key index was not added")
|
||||
}
|
||||
var status SchemaStatus
|
||||
status, err = ReadSchemaStatus(db)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !status.Ready || status.Current != 4 {
|
||||
t.Fatalf("unexpected schema status: %#v", status)
|
||||
}
|
||||
|
||||
firstKey := "same-upload"
|
||||
if err := db.Exec(`INSERT INTO resources (id, user_id, upload_key) VALUES (?, ?, ?)`, "resource-1", "user-1", firstKey).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Exec(`INSERT INTO resources (id, user_id, upload_key) VALUES (?, ?, ?)`, "resource-2", "user-1", firstKey).Error; err == nil {
|
||||
t.Fatal("duplicate resource upload key should be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateSchemaRollsBackFailedMigration(t *testing.T) {
|
||||
db, err := Open(Config{Driver: "sqlite", DSN: "file:migration-rollback?mode=memory&cache=shared"})
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user