diff --git a/controller/subscription.go b/controller/subscription.go index 9b0924ce7..c6095312b 100644 --- a/controller/subscription.go +++ b/controller/subscription.go @@ -118,6 +118,14 @@ func AdminCreateSubscriptionPlan(c *gin.Context) { common.ApiErrorMsg(c, "套餐标题不能为空") return } + if req.Plan.PriceAmount < 0 { + common.ApiErrorMsg(c, "价格不能为负数") + return + } + if req.Plan.PriceAmount > 9999 { + common.ApiErrorMsg(c, "价格不能超过9999") + return + } if req.Plan.Currency == "" { req.Plan.Currency = "USD" } @@ -172,6 +180,14 @@ func AdminUpdateSubscriptionPlan(c *gin.Context) { common.ApiErrorMsg(c, "套餐标题不能为空") return } + if req.Plan.PriceAmount < 0 { + common.ApiErrorMsg(c, "价格不能为负数") + return + } + if req.Plan.PriceAmount > 9999 { + common.ApiErrorMsg(c, "价格不能超过9999") + return + } req.Plan.Id = id if req.Plan.Currency == "" { req.Plan.Currency = "USD" diff --git a/model/main.go b/model/main.go index 08c855309..b1d363355 100644 --- a/model/main.go +++ b/model/main.go @@ -248,6 +248,9 @@ func InitLogDB() (err error) { } func migrateDB() error { + // Migrate price_amount column from float/double to decimal for existing tables + migrateSubscriptionPlanPriceAmount() + err := DB.AutoMigrate( &Channel{}, &Token{}, @@ -346,6 +349,61 @@ func migrateLOGDB() error { return nil } +// migrateSubscriptionPlanPriceAmount migrates price_amount column from float/double to decimal(10,6) +// This is safe to run multiple times - it checks the column type first +func migrateSubscriptionPlanPriceAmount() { + tableName := "subscription_plans" + columnName := "price_amount" + + // Check if table exists first + if !DB.Migrator().HasTable(tableName) { + return + } + + // Check if column exists + if !DB.Migrator().HasColumn(&SubscriptionPlan{}, columnName) { + return + } + + var alterSQL string + if common.UsingPostgreSQL { + // PostgreSQL: Check if already decimal/numeric + var dataType string + DB.Raw(`SELECT data_type FROM information_schema.columns + WHERE table_name = ? AND column_name = ?`, tableName, columnName).Scan(&dataType) + if dataType == "numeric" { + return // Already decimal/numeric + } + alterSQL = fmt.Sprintf(`ALTER TABLE %s ALTER COLUMN %s TYPE decimal(10,6) USING %s::decimal(10,6)`, + tableName, columnName, columnName) + } else if common.UsingMySQL { + // MySQL: Check if already decimal + var columnType string + DB.Raw(`SELECT COLUMN_TYPE FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?`, + tableName, columnName).Scan(&columnType) + if strings.HasPrefix(strings.ToLower(columnType), "decimal") { + return // Already decimal + } + alterSQL = fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s decimal(10,6) NOT NULL DEFAULT 0", + tableName, columnName) + } else if common.UsingSQLite { + // SQLite doesn't support ALTER COLUMN, but its type affinity handles this automatically + // The column will accept decimal values without modification + return + } else { + return + } + + if alterSQL != "" { + if err := DB.Exec(alterSQL).Error; err != nil { + common.SysLog(fmt.Sprintf("Warning: failed to migrate %s.%s to decimal: %v", tableName, columnName, err)) + } else { + common.SysLog(fmt.Sprintf("Successfully migrated %s.%s to decimal(10,6)", tableName, columnName)) + } + } +} + func closeDB(db *gorm.DB) error { sqlDB, err := db.DB() if err != nil { diff --git a/model/subscription.go b/model/subscription.go index 73730f3b4..6d8d21306 100644 --- a/model/subscription.go +++ b/model/subscription.go @@ -149,7 +149,7 @@ type SubscriptionPlan struct { Subtitle string `json:"subtitle" gorm:"type:varchar(255);default:''"` // Display money amount (follow existing code style: float64 for money) - PriceAmount float64 `json:"price_amount" gorm:"type:double;not null;default:0"` + PriceAmount float64 `json:"price_amount" gorm:"type:decimal(10,6);not null;default:0"` Currency string `json:"currency" gorm:"type:varchar(8);not null;default:'USD'"` DurationUnit string `json:"duration_unit" gorm:"type:varchar(16);not null;default:'month'"`