feat(db): purchase_task 新增 created_by/created_source 迁移 (#382)
只新增可空 created_by(索引)与非空默认空串的 created_source,不回填历史行; 迁移按列/索引存在性守卫,可重复执行。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
16e3d35b8f
commit
6cc3bb98d0
@@ -170,8 +170,13 @@ type PurchaseTask struct {
|
||||
CancelReason *string `json:"cancelReason" gorm:"size:500"`
|
||||
ErrorCode *string `json:"errorCode" gorm:"size:64;index"`
|
||||
ErrorMessage *string `json:"errorMessage" gorm:"size:1000"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
// CreatedBy/CreatedSource 只在新建任务时写入一次(#382):admin=管理端账号,
|
||||
// client_api=客户端密钥的授权人,agent=设备重试(CreatedBy 为空);空串表示
|
||||
// 迁移前的历史任务,不回填。
|
||||
CreatedBy *uint64 `json:"createdBy" gorm:"index"`
|
||||
CreatedSource string `json:"createdSource" gorm:"size:16;not null;default:''"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (PurchaseTask) TableName() string { return "purchase_task" }
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package version_local
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"go-admin/app/goauto/models"
|
||||
"go-admin/cmd/migrate/migration"
|
||||
common "go-admin/common/models"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
migration.Migrate.SetVersion(migration.GetFilename(file), MigratePurchaseTaskCreator)
|
||||
}
|
||||
|
||||
// MigratePurchaseTaskCreator only adds the #382 creator columns to
|
||||
// purchase_task: nullable created_by (indexed) and created_source defaulting
|
||||
// to the empty string. Existing rows are not rewritten or backfilled. Each step is guarded so
|
||||
// a partial run (MySQL DDL commits implicitly) can be re-run safely.
|
||||
func MigratePurchaseTaskCreator(db *gorm.DB, version string) error {
|
||||
m := db.Migrator()
|
||||
for _, column := range []string{"CreatedBy", "CreatedSource"} {
|
||||
if !m.HasColumn(&models.PurchaseTask{}, column) {
|
||||
if err := m.AddColumn(&models.PurchaseTask{}, column); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if !m.HasIndex(&models.PurchaseTask{}, "idx_purchase_task_created_by") {
|
||||
if err := m.CreateIndex(&models.PurchaseTask{}, "CreatedBy"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return db.Where("version = ?", version).FirstOrCreate(&common.Migration{Version: version}).Error
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package version_local
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go-admin/app/goauto/models"
|
||||
common "go-admin/common/models"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
func TestMigratePurchaseTaskCreatorAddsColumnsKeepsLegacyRowsAndIsIdempotent(t *testing.T) {
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
conn, _ := db.DB()
|
||||
defer conn.Close()
|
||||
if err = db.AutoMigrate(&common.Migration{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = db.Exec("CREATE TABLE purchase_task (id integer primary key, status text NOT NULL)").Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = db.Exec("INSERT INTO purchase_task(id,status) VALUES(1,'failed')").Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
if err = MigratePurchaseTaskCreator(db, "test_382"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, column := range []string{"CreatedBy", "CreatedSource"} {
|
||||
if !db.Migrator().HasColumn(&models.PurchaseTask{}, column) {
|
||||
t.Fatalf("missing column %s", column)
|
||||
}
|
||||
}
|
||||
if !db.Migrator().HasIndex(&models.PurchaseTask{}, "idx_purchase_task_created_by") {
|
||||
t.Fatal("missing created_by index")
|
||||
}
|
||||
var row struct {
|
||||
Status string
|
||||
CreatedBy *uint64
|
||||
CreatedSource string
|
||||
}
|
||||
if err = db.Table("purchase_task").Select("status, created_by, created_source").Where("id = 1").Scan(&row).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if row.Status != "failed" || row.CreatedBy != nil || row.CreatedSource != "" {
|
||||
t.Fatalf("legacy row changed: %+v", row)
|
||||
}
|
||||
var count int64
|
||||
db.Model(&common.Migration{}).Where("version = ?", "test_382").Count(&count)
|
||||
if count != 1 {
|
||||
t.Fatal("version not idempotent")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user