diff --git a/backend/handlers/model_type.go b/backend/handlers/model_type.go index eed1008..9737f31 100644 --- a/backend/handlers/model_type.go +++ b/backend/handlers/model_type.go @@ -2,6 +2,7 @@ package handlers import ( "database/sql" + "net/http" "github.com/gin-gonic/gin" @@ -12,9 +13,9 @@ import ( func GetModelTypes(c *gin.Context) { db := c.MustGet("db").(*sql.DB) - rows, err := db.Query("SELECT type_key, type_label FROM model_type") + rows, err := db.Query("SELECT type_key, type_label, sort_order FROM model_type ORDER BY sort_order ASC, type_key ASC") if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer rows.Close() @@ -22,14 +23,14 @@ func GetModelTypes(c *gin.Context) { var types []models.ModelType for rows.Next() { var t models.ModelType - if err := rows.Scan(&t.TypeKey, &t.TypeLabel); err != nil { - c.JSON(500, gin.H{"error": err.Error()}) + if err := rows.Scan(&t.TypeKey, &t.TypeLabel, &t.SortOrder); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } types = append(types, t) } - c.JSON(200, types) + c.JSON(http.StatusOK, types) } // CreateModelType 添加新的模型类型 @@ -38,20 +39,106 @@ func CreateModelType(c *gin.Context) { var newType models.ModelType if err := c.ShouldBindJSON(&newType); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } _, err := db.Exec(` - INSERT INTO model_type (type_key, type_label) - VALUES (?, ?) - ON DUPLICATE KEY UPDATE type_label = VALUES(type_label) - `, newType.TypeKey, newType.TypeLabel) + INSERT INTO model_type (type_key, type_label, sort_order) + VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE type_label = VALUES(type_label), sort_order = VALUES(sort_order) + `, newType.TypeKey, newType.TypeLabel, newType.SortOrder) if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - c.JSON(201, newType) + c.JSON(http.StatusCreated, newType) +} + +// UpdateModelType 更新模型类型 +func UpdateModelType(c *gin.Context) { + db := c.MustGet("db").(*sql.DB) + typeKey := c.Param("key") + + var updateType models.ModelType + if err := c.ShouldBindJSON(&updateType); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // 如果key发生变化,需要删除旧记录并创建新记录 + if typeKey != updateType.TypeKey { + tx, err := db.Begin() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to begin transaction"}) + return + } + + // 删除旧记录 + _, err = tx.Exec("DELETE FROM model_type WHERE type_key = ?", typeKey) + if err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete old model type"}) + return + } + + // 创建新记录 + _, err = tx.Exec(` + INSERT INTO model_type (type_key, type_label, sort_order) + VALUES (?, ?, ?) + `, updateType.TypeKey, updateType.TypeLabel, updateType.SortOrder) + if err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create new model type"}) + return + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to commit transaction"}) + return + } + } else { + // 直接更新 + _, err := db.Exec(` + UPDATE model_type + SET type_label = ?, sort_order = ? + WHERE type_key = ? + `, updateType.TypeLabel, updateType.SortOrder, typeKey) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update model type"}) + return + } + } + + c.JSON(http.StatusOK, updateType) +} + +// DeleteModelType 删除模型类型 +func DeleteModelType(c *gin.Context) { + db := c.MustGet("db").(*sql.DB) + typeKey := c.Param("key") + + // 检查是否有价格记录使用此类型 + var count int + err := db.QueryRow("SELECT COUNT(*) FROM price WHERE model_type = ?", typeKey).Scan(&count) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check model type usage"}) + return + } + + if count > 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "Cannot delete model type that is in use"}) + return + } + + _, err = db.Exec("DELETE FROM model_type WHERE type_key = ?", typeKey) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete model type"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "Model type deleted successfully"}) } diff --git a/backend/handlers/prices.go b/backend/handlers/prices.go index f07358c..57eb1aa 100644 --- a/backend/handlers/prices.go +++ b/backend/handlers/prices.go @@ -118,6 +118,19 @@ func CreatePrice(c *gin.Context) { return } + // 检查同一厂商下是否已存在相同名称的模型 + var modelExists bool + err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM price WHERE channel_type = ? AND model = ? AND status = 'approved')", + price.ChannelType, price.Model).Scan(&modelExists) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check model existence"}) + return + } + if modelExists { + c.JSON(http.StatusBadRequest, gin.H{"error": "Model with the same name already exists for this provider"}) + return + } + now := time.Now() result, err := db.Exec(` INSERT INTO price (model, model_type, billing_type, channel_type, currency, input_price, output_price, @@ -229,6 +242,19 @@ func UpdatePrice(c *gin.Context) { return } + // 检查同一厂商下是否已存在相同名称的模型(排除当前正在编辑的记录) + var modelExists bool + err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM price WHERE channel_type = ? AND model = ? AND id != ? AND status = 'approved')", + price.ChannelType, price.Model, id).Scan(&modelExists) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check model existence"}) + return + } + if modelExists { + c.JSON(http.StatusBadRequest, gin.H{"error": "Model with the same name already exists for this provider"}) + return + } + // 获取当前用户 user, exists := c.Get("user") if !exists { diff --git a/backend/main.go b/backend/main.go index 2ecf23f..6c46d34 100644 --- a/backend/main.go +++ b/backend/main.go @@ -94,7 +94,9 @@ func main() { modelTypes := api.Group("/model-types") { modelTypes.GET("", handlers.GetModelTypes) - modelTypes.POST("", middleware.AuthRequired(), handlers.CreateModelType) + modelTypes.POST("", middleware.AuthRequired(), middleware.AdminRequired(), handlers.CreateModelType) + modelTypes.PUT("/:key", middleware.AuthRequired(), middleware.AdminRequired(), handlers.UpdateModelType) + modelTypes.DELETE("/:key", middleware.AuthRequired(), middleware.AdminRequired(), handlers.DeleteModelType) } } diff --git a/backend/models/model_type.go b/backend/models/model_type.go index 967119c..ba9e401 100644 --- a/backend/models/model_type.go +++ b/backend/models/model_type.go @@ -4,6 +4,7 @@ package models type ModelType struct { TypeKey string `json:"key"` TypeLabel string `json:"label"` + SortOrder int `json:"sort_order"` } // CreateModelTypeTableSQL 返回创建模型类型表的 SQL @@ -11,6 +12,7 @@ func CreateModelTypeTableSQL() string { return ` CREATE TABLE IF NOT EXISTS model_type ( type_key VARCHAR(50) PRIMARY KEY, - type_label VARCHAR(255) NOT NULL + type_label VARCHAR(255) NOT NULL, + sort_order INT NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci` } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 2b57116..6765a41 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -9,6 +9,7 @@
@@ -35,7 +36,7 @@ diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 745943e..0ae62d8 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -1,6 +1,7 @@ import { createRouter, createWebHistory } from 'vue-router' import Prices from '../views/Prices.vue' import Providers from '../views/Providers.vue' +import ModelTypes from '../views/ModelTypes.vue' import Login from '../views/Login.vue' import Home from '../views/Home.vue' @@ -22,6 +23,11 @@ const router = createRouter({ name: 'providers', component: Providers }, + { + path: '/model-types', + name: 'modelTypes', + component: ModelTypes + }, { path: '/login', name: 'login', diff --git a/frontend/src/views/ModelTypes.vue b/frontend/src/views/ModelTypes.vue new file mode 100644 index 0000000..669623d --- /dev/null +++ b/frontend/src/views/ModelTypes.vue @@ -0,0 +1,181 @@ + + + + + \ No newline at end of file