Ver código fonte

feat(subscription): validate price amount and migrate database column type

- Add validation to ensure subscription plan price amount is non-negative and does not exceed 9999.
- Migrate the price_amount column from float/double to decimal(10,6) in the database for improved precision.
- Update SubscriptionPlan model to reflect the new decimal type for price_amount.
CaIon 1 semana atrás
pai
commit
6a9522ac5b
3 arquivos alterados com 75 adições e 1 exclusões
  1. 16 0
      controller/subscription.go
  2. 58 0
      model/main.go
  3. 1 1
      model/subscription.go

+ 16 - 0
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"

+ 58 - 0
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 {

+ 1 - 1
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'"`