From 75d62d978a744b5f80071e2358d1c65dccba3b0d Mon Sep 17 00:00:00 2001 From: wood chen Date: Tue, 18 Mar 2025 02:18:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=B7=E6=A0=BC=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C=E6=95=B4=E5=90=88=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E5=88=9B=E5=BB=BA=E5=92=8C=E6=9B=B4=E6=96=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 prices.go 中新增 ProcessPrice 函数,统一处理价格的创建和更新逻辑 - 更新 FetchAndSavePrices 和 UpdateOtherPrices 函数,使用 ProcessPrice 进行价格记录的处理 - 在 GetPrices 函数中添加状态筛选参数,优化价格查询功能 - 前端 Prices.vue 中调整搜索框和筛选功能的样式,提升用户体验 - 修复部分代码格式和样式问题,增强代码可读性 --- .../cron/openrouter-api/openrouter-price.go | 76 ++-- .../cron/openrouter-api/update-other-price.go | 111 +++-- backend/handlers/prices.go | 418 ++++++++++++------ frontend/src/views/Prices.vue | 345 ++++++--------- frontend/vite.config.js | 2 +- 5 files changed, 542 insertions(+), 410 deletions(-) diff --git a/backend/cron/openrouter-api/openrouter-price.go b/backend/cron/openrouter-api/openrouter-price.go index 531f33d..afb87a5 100644 --- a/backend/cron/openrouter-api/openrouter-price.go +++ b/backend/cron/openrouter-api/openrouter-price.go @@ -8,9 +8,9 @@ import ( "math" "net/http" "strconv" - "time" "aimodels-prices/database" + "aimodels-prices/handlers" "aimodels-prices/models" ) @@ -74,6 +74,8 @@ func FetchAndSavePrices() error { } // 处理每个模型的价格数据 + processedCount := 0 + skippedCount := 0 for _, modelData := range openRouterResp.Data { // 确定模型类型 modelType := determineModelType(modelData.Modality) @@ -87,6 +89,7 @@ func FetchAndSavePrices() error { inputPrice, err = parsePrice(modelData.Endpoint.Pricing.Prompt) if err != nil { log.Printf("解析endpoint输入价格失败 %s: %v", modelData.Slug, err) + skippedCount++ continue } } else if modelData.Pricing.Prompt != "" { @@ -94,6 +97,7 @@ func FetchAndSavePrices() error { inputPrice, err = parsePrice(modelData.Pricing.Prompt) if err != nil { log.Printf("解析输入价格失败 %s: %v", modelData.Slug, err) + skippedCount++ continue } } @@ -102,60 +106,72 @@ func FetchAndSavePrices() error { outputPrice, err = parsePrice(modelData.Endpoint.Pricing.Completion) if err != nil { log.Printf("解析endpoint输出价格失败 %s: %v", modelData.Slug, err) + skippedCount++ continue } } else if modelData.Pricing.Completion != "" { outputPrice, err = parsePrice(modelData.Pricing.Completion) if err != nil { log.Printf("解析输出价格失败 %s: %v", modelData.Slug, err) + skippedCount++ continue } } + // 创建价格对象 + price := models.Price{ + Model: modelData.Slug, + ModelType: modelType, + BillingType: BillingType, + ChannelType: ChannelType, + Currency: Currency, + InputPrice: inputPrice, + OutputPrice: outputPrice, + PriceSource: PriceSource, + Status: Status, + CreatedBy: CreatedBy, + } + // 检查是否已存在相同模型的价格记录 var existingPrice models.Price result := db.Where("model = ? AND channel_type = ?", modelData.Slug, ChannelType).First(&existingPrice) if result.Error == nil { - // 更新现有记录 - existingPrice.ModelType = modelType - existingPrice.BillingType = BillingType - existingPrice.Currency = Currency - existingPrice.InputPrice = inputPrice - existingPrice.OutputPrice = outputPrice - existingPrice.PriceSource = PriceSource - existingPrice.Status = Status - existingPrice.UpdatedAt = time.Now() - - if err := db.Save(&existingPrice).Error; err != nil { + // 使用processPrice函数处理更新 + _, changed, err := handlers.ProcessPrice(price, &existingPrice, true, CreatedBy) + if err != nil { log.Printf("更新价格记录失败 %s: %v", modelData.Slug, err) + skippedCount++ continue } - log.Printf("更新价格记录: %s", modelData.Slug) - } else { - // 创建新记录 - newPrice := models.Price{ - Model: modelData.Slug, - ModelType: modelType, - BillingType: BillingType, - ChannelType: ChannelType, - Currency: Currency, - InputPrice: inputPrice, - OutputPrice: outputPrice, - PriceSource: PriceSource, - Status: Status, - CreatedBy: CreatedBy, - } - if err := db.Create(&newPrice).Error; err != nil { + if changed { + log.Printf("更新价格记录: %s", modelData.Slug) + processedCount++ + } else { + log.Printf("价格无变化,跳过更新: %s", modelData.Slug) + skippedCount++ + } + } else { + // 使用processPrice函数处理创建 + _, changed, err := handlers.ProcessPrice(price, nil, true, CreatedBy) + if err != nil { log.Printf("创建价格记录失败 %s: %v", modelData.Slug, err) + skippedCount++ continue } - log.Printf("创建新价格记录: %s", modelData.Slug) + + if changed { + log.Printf("创建新价格记录: %s", modelData.Slug) + processedCount++ + } else { + log.Printf("价格创建失败: %s", modelData.Slug) + skippedCount++ + } } } - log.Println("OpenRouter价格数据处理完成") + log.Printf("OpenRouter价格数据处理完成,成功处理: %d, 跳过: %d", processedCount, skippedCount) return nil } diff --git a/backend/cron/openrouter-api/update-other-price.go b/backend/cron/openrouter-api/update-other-price.go index 81090af..ecc4915 100644 --- a/backend/cron/openrouter-api/update-other-price.go +++ b/backend/cron/openrouter-api/update-other-price.go @@ -8,9 +8,9 @@ import ( "encoding/json" "strings" - "time" "aimodels-prices/database" + "aimodels-prices/handlers" "aimodels-prices/models" ) @@ -28,6 +28,11 @@ var blacklist = []string{ "shap-e", "palm-2", "o3-mini-high", + "claude-instant", + "claude-1", + "claude-3-haiku", + "claude-3-opus", + "claude-3-sonnet", } const ( @@ -94,6 +99,20 @@ func UpdateOtherPrices() error { log.Printf("修正Google模型名称: %s -> %s", parts[1], modelName) } } + if author == "anthropic" { + // 处理claude-3.5-sonnet系列模型名称 + if strings.HasPrefix(modelName, "claude-3.5") { + suffix := strings.TrimPrefix(modelName, "claude-3.5") + modelName = "claude-3-5" + suffix + log.Printf("修正Claude模型名称: %s -> %s", parts[1], modelName) + } + + if strings.HasPrefix(modelName, "claude-3.7") { + suffix := strings.TrimPrefix(modelName, "claude-3.7") + modelName = "claude-3-7" + suffix + log.Printf("修正Claude模型名称: %s -> %s", parts[1], modelName) + } + } // 确定模型类型 modelType := determineModelType(modelData.Modality) @@ -102,7 +121,14 @@ func UpdateOtherPrices() error { var inputPrice, outputPrice float64 var parseErr error - // 优先使用endpoint中的pricing + // 如果输入或输出价格为空,直接跳过 + if modelData.Endpoint.Pricing.Prompt == "" || modelData.Endpoint.Pricing.Completion == "" { + log.Printf("跳过价格数据不完整的模型: %s", modelData.Slug) + skippedCount++ + continue + } + + // 使用endpoint中的pricing if modelData.Endpoint.Pricing.Prompt != "" { inputPrice, parseErr = parsePrice(modelData.Endpoint.Pricing.Prompt) if parseErr != nil { @@ -110,14 +136,6 @@ func UpdateOtherPrices() error { skippedCount++ continue } - } else if modelData.Pricing.Prompt != "" { - // 如果endpoint中没有,则使用顶层pricing - inputPrice, parseErr = parsePrice(modelData.Pricing.Prompt) - if parseErr != nil { - log.Printf("解析输入价格失败 %s: %v", modelData.Slug, parseErr) - skippedCount++ - continue - } } if modelData.Endpoint.Pricing.Completion != "" { @@ -127,13 +145,20 @@ func UpdateOtherPrices() error { skippedCount++ continue } - } else if modelData.Pricing.Completion != "" { - outputPrice, parseErr = parsePrice(modelData.Pricing.Completion) - if parseErr != nil { - log.Printf("解析输出价格失败 %s: %v", modelData.Slug, parseErr) - skippedCount++ - continue - } + } + + // 创建价格对象 + price := models.Price{ + Model: modelName, + ModelType: modelType, + BillingType: BillingType, + ChannelType: channelType, + Currency: Currency, + InputPrice: inputPrice, + OutputPrice: outputPrice, + PriceSource: OtherPriceSource, + Status: OtherStatus, + CreatedBy: CreatedBy, } // 检查是否已存在相同模型的价格记录 @@ -141,45 +166,37 @@ func UpdateOtherPrices() error { result := db.Where("model = ? AND channel_type = ?", modelName, channelType).First(&existingPrice) if result.Error == nil { - // 更新现有记录 - existingPrice.ModelType = modelType - existingPrice.BillingType = BillingType - existingPrice.Currency = Currency - existingPrice.InputPrice = inputPrice - existingPrice.OutputPrice = outputPrice - existingPrice.PriceSource = OtherPriceSource - existingPrice.Status = OtherStatus - existingPrice.UpdatedAt = time.Now() - - if err := db.Save(&existingPrice).Error; err != nil { + // 使用processPrice函数处理更新 + _, changed, err := handlers.ProcessPrice(price, &existingPrice, false, CreatedBy) + if err != nil { log.Printf("更新价格记录失败 %s: %v", modelName, err) skippedCount++ continue } - log.Printf("更新价格记录: %s (厂商: %s)", modelName, author) - processedCount++ - } else { - // 创建新记录 - newPrice := models.Price{ - Model: modelName, - ModelType: modelType, - BillingType: BillingType, - ChannelType: channelType, - Currency: Currency, - InputPrice: inputPrice, - OutputPrice: outputPrice, - PriceSource: OtherPriceSource, - Status: OtherStatus, - CreatedBy: CreatedBy, - } - if err := db.Create(&newPrice).Error; err != nil { + if changed { + log.Printf("更新价格记录: %s (厂商: %s)", modelName, author) + processedCount++ + } else { + log.Printf("价格无变化,跳过更新: %s (厂商: %s)", modelName, author) + skippedCount++ + } + } else { + // 使用processPrice函数处理创建 + _, changed, err := handlers.ProcessPrice(price, nil, false, CreatedBy) + if err != nil { log.Printf("创建价格记录失败 %s: %v", modelName, err) skippedCount++ continue } - log.Printf("创建新价格记录: %s (厂商: %s)", modelName, author) - processedCount++ + + if changed { + log.Printf("创建新价格记录: %s (厂商: %s)", modelName, author) + processedCount++ + } else { + log.Printf("价格创建失败: %s (厂商: %s)", modelName, author) + skippedCount++ + } } } diff --git a/backend/handlers/prices.go b/backend/handlers/prices.go index c4b406e..cfb1949 100644 --- a/backend/handlers/prices.go +++ b/backend/handlers/prices.go @@ -20,6 +20,7 @@ func GetPrices(c *gin.Context) { channelType := c.Query("channel_type") // 厂商筛选参数 modelType := c.Query("model_type") // 模型类型筛选参数 searchQuery := c.Query("search") // 搜索查询参数 + status := c.Query("status") // 状态筛选参数 if page < 1 { page = 1 @@ -31,8 +32,8 @@ func GetPrices(c *gin.Context) { offset := (page - 1) * pageSize // 构建缓存键 - cacheKey := fmt.Sprintf("prices_page_%d_size_%d_channel_%s_type_%s_search_%s", - page, pageSize, channelType, modelType, searchQuery) + cacheKey := fmt.Sprintf("prices_page_%d_size_%d_channel_%s_type_%s_search_%s_status_%s", + page, pageSize, channelType, modelType, searchQuery, status) // 尝试从缓存获取 if cachedData, found := database.GlobalCache.Get(cacheKey); found { @@ -56,10 +57,15 @@ func GetPrices(c *gin.Context) { if searchQuery != "" { query = query.Where("model LIKE ?", "%"+searchQuery+"%") } + // 添加状态筛选条件 + if status != "" { + query = query.Where("status = ?", status) + } // 获取总数 - 使用缓存优化 var total int64 - totalCacheKey := fmt.Sprintf("prices_count_channel_%s_type_%s_search_%s", channelType, modelType, searchQuery) + totalCacheKey := fmt.Sprintf("prices_count_channel_%s_type_%s_search_%s_status_%s", + channelType, modelType, searchQuery, status) if cachedTotal, found := database.GlobalCache.Get(totalCacheKey); found { if t, ok := cachedTotal.(int64); ok { @@ -97,6 +103,118 @@ func GetPrices(c *gin.Context) { c.JSON(http.StatusOK, result) } +// processPrice 处理价格的创建和更新逻辑 +func ProcessPrice(price models.Price, existingPrice *models.Price, isAdmin bool, username string) (models.Price, bool, error) { + // 如果是更新操作且存在现有记录 + if existingPrice != nil { + // 检查价格是否有变化 + if isAdmin { + // 管理员直接更新主字段,检查是否有实际变化 + if existingPrice.Model == price.Model && + existingPrice.ModelType == price.ModelType && + existingPrice.BillingType == price.BillingType && + existingPrice.ChannelType == price.ChannelType && + existingPrice.Currency == price.Currency && + existingPrice.InputPrice == price.InputPrice && + existingPrice.OutputPrice == price.OutputPrice && + existingPrice.PriceSource == price.PriceSource { + // 没有变化,不需要更新 + return *existingPrice, false, nil + } + + // 有变化,更新字段 + existingPrice.Model = price.Model + existingPrice.ModelType = price.ModelType + existingPrice.BillingType = price.BillingType + existingPrice.ChannelType = price.ChannelType + existingPrice.Currency = price.Currency + existingPrice.InputPrice = price.InputPrice + existingPrice.OutputPrice = price.OutputPrice + existingPrice.PriceSource = price.PriceSource + existingPrice.Status = "approved" + existingPrice.UpdatedBy = &username + existingPrice.TempModel = nil + existingPrice.TempModelType = nil + existingPrice.TempBillingType = nil + existingPrice.TempChannelType = nil + existingPrice.TempCurrency = nil + existingPrice.TempInputPrice = nil + existingPrice.TempOutputPrice = nil + existingPrice.TempPriceSource = nil + + // 保存更新 + if err := database.DB.Save(existingPrice).Error; err != nil { + return *existingPrice, false, err + } + return *existingPrice, true, nil + } else { + // 普通用户更新临时字段,检查是否有实际变化 + // 创建临时值的指针 + modelPtr := &price.Model + modelTypePtr := &price.ModelType + billingTypePtr := &price.BillingType + channelTypePtr := &price.ChannelType + currencyPtr := &price.Currency + inputPricePtr := &price.InputPrice + outputPricePtr := &price.OutputPrice + priceSourcePtr := &price.PriceSource + + // 检查临时字段与现有主字段是否相同 + if (existingPrice.Model == price.Model && + existingPrice.ModelType == price.ModelType && + existingPrice.BillingType == price.BillingType && + existingPrice.ChannelType == price.ChannelType && + existingPrice.Currency == price.Currency && + existingPrice.InputPrice == price.InputPrice && + existingPrice.OutputPrice == price.OutputPrice && + existingPrice.PriceSource == price.PriceSource) || + // 或者检查临时字段与现有临时字段是否相同 + (existingPrice.TempModel != nil && *existingPrice.TempModel == price.Model && + existingPrice.TempModelType != nil && *existingPrice.TempModelType == price.ModelType && + existingPrice.TempBillingType != nil && *existingPrice.TempBillingType == price.BillingType && + existingPrice.TempChannelType != nil && *existingPrice.TempChannelType == price.ChannelType && + existingPrice.TempCurrency != nil && *existingPrice.TempCurrency == price.Currency && + existingPrice.TempInputPrice != nil && *existingPrice.TempInputPrice == price.InputPrice && + existingPrice.TempOutputPrice != nil && *existingPrice.TempOutputPrice == price.OutputPrice && + existingPrice.TempPriceSource != nil && *existingPrice.TempPriceSource == price.PriceSource) { + // 没有变化,不需要更新 + return *existingPrice, false, nil + } + + // 有变化,更新临时字段 + existingPrice.TempModel = modelPtr + existingPrice.TempModelType = modelTypePtr + existingPrice.TempBillingType = billingTypePtr + existingPrice.TempChannelType = channelTypePtr + existingPrice.TempCurrency = currencyPtr + existingPrice.TempInputPrice = inputPricePtr + existingPrice.TempOutputPrice = outputPricePtr + existingPrice.TempPriceSource = priceSourcePtr + existingPrice.Status = "pending" + existingPrice.UpdatedBy = &username + + // 保存更新 + if err := database.DB.Save(existingPrice).Error; err != nil { + return *existingPrice, false, err + } + return *existingPrice, true, nil + } + } else { + // 创建新记录 + price.Status = "pending" + if isAdmin { + price.Status = "approved" + } + price.CreatedBy = username + + // 保存新记录 + if err := database.DB.Create(&price).Error; err != nil { + return price, false, err + } + return price, true, nil + } +} + func CreatePrice(c *gin.Context) { var price models.Price if err := c.ShouldBindJSON(&price); err != nil { @@ -123,19 +241,27 @@ func CreatePrice(c *gin.Context) { return } - // 设置状态和创建者 - price.Status = "pending" + // 获取当前用户 + user, exists := c.Get("user") + if !exists { + c.JSON(http.StatusUnauthorized, gin.H{"error": "User not found"}) + return + } + currentUser := user.(*models.User) - // 创建记录 - if err := database.DB.Create(&price).Error; err != nil { + // 处理价格创建 + result, changed, err := ProcessPrice(price, nil, currentUser.Role == "admin", currentUser.Username) + if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create price"}) return } // 清除所有价格相关缓存 - clearPriceCache() + if changed { + clearPriceCache() + } - c.JSON(http.StatusCreated, price) + c.JSON(http.StatusCreated, result) } func UpdatePriceStatus(c *gin.Context) { @@ -209,23 +335,36 @@ func UpdatePriceStatus(c *gin.Context) { return } } else { - // 如果是拒绝,清除临时字段 - if err := tx.Model(&price).Updates(map[string]interface{}{ - "status": input.Status, - "updated_at": time.Now(), - "temp_model": nil, - "temp_model_type": nil, - "temp_billing_type": nil, - "temp_channel_type": nil, - "temp_currency": nil, - "temp_input_price": nil, - "temp_output_price": nil, - "temp_price_source": nil, - "updated_by": nil, - }).Error; err != nil { - tx.Rollback() - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update price status"}) - return + // 如果是拒绝 + // 检查是否是新创建的价格(没有原始价格) + isNewPrice := price.Model == "" || (price.TempModel != nil && price.Model == *price.TempModel) + + if isNewPrice { + // 如果是新创建的价格,直接删除 + if err := tx.Delete(&price).Error; err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete rejected price"}) + return + } + } else { + // 如果是更新的价格,恢复到原始状态(清除临时字段并设置状态为approved) + if err := tx.Model(&price).Updates(map[string]interface{}{ + "status": "approved", // 恢复为已批准状态 + "updated_at": time.Now(), + "temp_model": nil, + "temp_model_type": nil, + "temp_billing_type": nil, + "temp_channel_type": nil, + "temp_currency": nil, + "temp_input_price": nil, + "temp_output_price": nil, + "temp_price_source": nil, + "updated_by": nil, + }).Error; err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update price status"}) + return + } } } @@ -239,11 +378,20 @@ func UpdatePriceStatus(c *gin.Context) { // 清除所有价格相关缓存 clearPriceCache() - c.JSON(http.StatusOK, gin.H{ - "message": "Status updated successfully", - "status": input.Status, - "updated_at": time.Now(), - }) + // 根据操作类型返回不同的消息 + if input.Status == "rejected" && (price.Model == "" || (price.TempModel != nil && price.Model == *price.TempModel)) { + c.JSON(http.StatusOK, gin.H{ + "message": "Price rejected and deleted successfully", + "status": input.Status, + "updated_at": time.Now(), + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "message": "Status updated successfully", + "status": input.Status, + "updated_at": time.Now(), + }) + } } func UpdatePrice(c *gin.Context) { @@ -288,55 +436,19 @@ func UpdatePrice(c *gin.Context) { return } - // 根据用户角色决定更新方式 - if currentUser.Role == "admin" { - // 管理员直接更新主字段 - existingPrice.Model = price.Model - existingPrice.ModelType = price.ModelType - existingPrice.BillingType = price.BillingType - existingPrice.ChannelType = price.ChannelType - existingPrice.Currency = price.Currency - existingPrice.InputPrice = price.InputPrice - existingPrice.OutputPrice = price.OutputPrice - existingPrice.PriceSource = price.PriceSource - existingPrice.Status = "approved" - existingPrice.UpdatedBy = ¤tUser.Username - existingPrice.TempModel = nil - existingPrice.TempModelType = nil - existingPrice.TempBillingType = nil - existingPrice.TempChannelType = nil - existingPrice.TempCurrency = nil - existingPrice.TempInputPrice = nil - existingPrice.TempOutputPrice = nil - existingPrice.TempPriceSource = nil - - if err := database.DB.Save(&existingPrice).Error; err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update price"}) - return - } - } else { - // 普通用户更新临时字段 - existingPrice.TempModel = &price.Model - existingPrice.TempModelType = &price.ModelType - existingPrice.TempBillingType = &price.BillingType - existingPrice.TempChannelType = &price.ChannelType - existingPrice.TempCurrency = &price.Currency - existingPrice.TempInputPrice = &price.InputPrice - existingPrice.TempOutputPrice = &price.OutputPrice - existingPrice.TempPriceSource = &price.PriceSource - existingPrice.Status = "pending" - existingPrice.UpdatedBy = ¤tUser.Username - - if err := database.DB.Save(&existingPrice).Error; err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update price"}) - return - } + // 处理价格更新 + result, changed, err := ProcessPrice(price, &existingPrice, currentUser.Role == "admin", currentUser.Username) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update price"}) + return } // 清除所有价格相关缓存 - clearPriceCache() + if changed { + clearPriceCache() + } - c.JSON(http.StatusOK, existingPrice) + c.JSON(http.StatusOK, result) } func DeletePrice(c *gin.Context) { @@ -362,6 +474,16 @@ func DeletePrice(c *gin.Context) { } func ApproveAllPrices(c *gin.Context) { + // 获取操作类型(批准或拒绝) + var input struct { + Action string `json:"action" binding:"required,oneof=approve reject"` + } + + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid action, must be 'approve' or 'reject'"}) + return + } + // 查找所有待审核的价格 var pendingPrices []models.Price if err := database.DB.Where("status = 'pending'").Find(&pendingPrices).Error; err != nil { @@ -371,54 +493,94 @@ func ApproveAllPrices(c *gin.Context) { // 开始事务 tx := database.DB.Begin() + processedCount := 0 + deletedCount := 0 for _, price := range pendingPrices { - updateMap := map[string]interface{}{ - "status": "approved", - "updated_at": time.Now(), - } + if input.Action == "approve" { + // 批准操作 + updateMap := map[string]interface{}{ + "status": "approved", + "updated_at": time.Now(), + } - // 如果临时字段有值,则更新主字段 - if price.TempModel != nil { - updateMap["model"] = *price.TempModel - } - if price.TempModelType != nil { - updateMap["model_type"] = *price.TempModelType - } - if price.TempBillingType != nil { - updateMap["billing_type"] = *price.TempBillingType - } - if price.TempChannelType != nil { - updateMap["channel_type"] = *price.TempChannelType - } - if price.TempCurrency != nil { - updateMap["currency"] = *price.TempCurrency - } - if price.TempInputPrice != nil { - updateMap["input_price"] = *price.TempInputPrice - } - if price.TempOutputPrice != nil { - updateMap["output_price"] = *price.TempOutputPrice - } - if price.TempPriceSource != nil { - updateMap["price_source"] = *price.TempPriceSource - } + // 如果临时字段有值,则更新主字段 + if price.TempModel != nil { + updateMap["model"] = *price.TempModel + } + if price.TempModelType != nil { + updateMap["model_type"] = *price.TempModelType + } + if price.TempBillingType != nil { + updateMap["billing_type"] = *price.TempBillingType + } + if price.TempChannelType != nil { + updateMap["channel_type"] = *price.TempChannelType + } + if price.TempCurrency != nil { + updateMap["currency"] = *price.TempCurrency + } + if price.TempInputPrice != nil { + updateMap["input_price"] = *price.TempInputPrice + } + if price.TempOutputPrice != nil { + updateMap["output_price"] = *price.TempOutputPrice + } + if price.TempPriceSource != nil { + updateMap["price_source"] = *price.TempPriceSource + } - // 清除所有临时字段 - updateMap["temp_model"] = nil - updateMap["temp_model_type"] = nil - updateMap["temp_billing_type"] = nil - updateMap["temp_channel_type"] = nil - updateMap["temp_currency"] = nil - updateMap["temp_input_price"] = nil - updateMap["temp_output_price"] = nil - updateMap["temp_price_source"] = nil - updateMap["updated_by"] = nil + // 清除所有临时字段 + updateMap["temp_model"] = nil + updateMap["temp_model_type"] = nil + updateMap["temp_billing_type"] = nil + updateMap["temp_channel_type"] = nil + updateMap["temp_currency"] = nil + updateMap["temp_input_price"] = nil + updateMap["temp_output_price"] = nil + updateMap["temp_price_source"] = nil + updateMap["updated_by"] = nil - if err := tx.Model(&price).Updates(updateMap).Error; err != nil { - tx.Rollback() - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to approve prices"}) - return + if err := tx.Model(&price).Updates(updateMap).Error; err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to approve prices"}) + return + } + processedCount++ + } else { + // 拒绝操作 + // 检查是否是新创建的价格(没有原始价格) + isNewPrice := price.Model == "" || (price.TempModel != nil && price.Model == *price.TempModel) + + if isNewPrice { + // 如果是新创建的价格,直接删除 + if err := tx.Delete(&price).Error; err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete rejected price"}) + return + } + deletedCount++ + } else { + // 如果是更新的价格,恢复到原始状态(清除临时字段并设置状态为approved) + if err := tx.Model(&price).Updates(map[string]interface{}{ + "status": "approved", // 恢复为已批准状态 + "updated_at": time.Now(), + "temp_model": nil, + "temp_model_type": nil, + "temp_billing_type": nil, + "temp_channel_type": nil, + "temp_currency": nil, + "temp_input_price": nil, + "temp_output_price": nil, + "temp_price_source": nil, + "updated_by": nil, + }).Error; err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reject prices"}) + return + } + processedCount++ + } } } @@ -432,10 +594,20 @@ func ApproveAllPrices(c *gin.Context) { // 清除所有价格相关缓存 clearPriceCache() - c.JSON(http.StatusOK, gin.H{ - "message": "All pending prices approved successfully", - "count": len(pendingPrices), - }) + // 根据操作类型返回不同的消息 + if input.Action == "approve" { + c.JSON(http.StatusOK, gin.H{ + "message": "All pending prices approved successfully", + "count": processedCount, + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "message": "All pending prices rejected successfully", + "processed": processedCount, + "deleted": deletedCount, + "total": processedCount + deletedCount, + }) + } } // clearPriceCache 清除所有价格相关的缓存 diff --git a/frontend/src/views/Prices.vue b/frontend/src/views/Prices.vue index 404361b..38b7ba3 100644 --- a/frontend/src/views/Prices.vue +++ b/frontend/src/views/Prices.vue @@ -23,39 +23,28 @@ -
- - - +
+
搜索模型:
+
+ + + +
-
厂商筛选:
+
厂商筛选:
- 全部 - 全部 + + @click="selectedProvider = provider.id.toString()">
- + {{ provider.name }}
@@ -63,18 +52,11 @@
-
模型类别:
+
模型类别:
- 全部 - + 全部 + {{ label }}
@@ -86,14 +68,9 @@
- - + +