|
|
@@ -0,0 +1,305 @@ |
|
|
|
|
|
package controller |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
|
"strconv" |
|
|
|
|
|
"strings" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/QuantumNous/new-api/common" |
|
|
|
|
|
"github.com/QuantumNous/new-api/model" |
|
|
|
|
|
"github.com/QuantumNous/new-api/setting/ratio_setting" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// GetAllChannelPricing 获取所有渠道定价(分页) |
|
|
|
|
|
func GetAllChannelPricing(c *gin.Context) { |
|
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("p", "1")) |
|
|
|
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) |
|
|
|
|
|
offset := (page - 1) * pageSize |
|
|
|
|
|
|
|
|
|
|
|
list, total, err := model.GetAllChannelPricing(offset, pageSize) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
common.ApiSuccess(c, gin.H{ |
|
|
|
|
|
"page": page, |
|
|
|
|
|
"page_size": pageSize, |
|
|
|
|
|
"total": total, |
|
|
|
|
|
"items": list, |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// GetChannelPricingByModel 获取指定模型的所有渠道定价 |
|
|
|
|
|
func GetChannelPricingByModel(c *gin.Context) { |
|
|
|
|
|
modelName := c.Param("name") |
|
|
|
|
|
if modelName == "" { |
|
|
|
|
|
common.ApiErrorMsg(c, "model name is required") |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
list, err := model.GetChannelPricingByModel(modelName) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
common.ApiSuccess(c, list) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// CreateChannelPricingRequest 创建渠道定价请求 |
|
|
|
|
|
type CreateChannelPricingRequest struct { |
|
|
|
|
|
Id int `json:"id"` |
|
|
|
|
|
ModelName string `json:"model_name" binding:"required"` |
|
|
|
|
|
ChannelId int `json:"channel_id" binding:"required"` |
|
|
|
|
|
QuotaType int `json:"quota_type"` |
|
|
|
|
|
ModelRatio float64 `json:"model_ratio"` |
|
|
|
|
|
CompletionRatio float64 `json:"completion_ratio"` |
|
|
|
|
|
ModelPrice float64 `json:"model_price"` |
|
|
|
|
|
TagIds string `json:"tag_ids"` |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// CreateChannelPricing 创建或更新渠道定价 |
|
|
|
|
|
func CreateChannelPricing(c *gin.Context) { |
|
|
|
|
|
var req CreateChannelPricingRequest |
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已存在 |
|
|
|
|
|
existing, _ := model.GetChannelPricing(req.ModelName, req.ChannelId) |
|
|
|
|
|
if existing != nil { |
|
|
|
|
|
// 更新 |
|
|
|
|
|
existing.QuotaType = req.QuotaType |
|
|
|
|
|
existing.ModelRatio = req.ModelRatio |
|
|
|
|
|
existing.CompletionRatio = req.CompletionRatio |
|
|
|
|
|
existing.ModelPrice = req.ModelPrice |
|
|
|
|
|
existing.TagIds = req.TagIds |
|
|
|
|
|
if err := existing.Update(); err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
common.ApiSuccess(c, existing) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 创建 |
|
|
|
|
|
cp := &model.ChannelPricing{ |
|
|
|
|
|
ModelName: req.ModelName, |
|
|
|
|
|
ChannelId: req.ChannelId, |
|
|
|
|
|
QuotaType: req.QuotaType, |
|
|
|
|
|
ModelRatio: req.ModelRatio, |
|
|
|
|
|
CompletionRatio: req.CompletionRatio, |
|
|
|
|
|
ModelPrice: req.ModelPrice, |
|
|
|
|
|
TagIds: req.TagIds, |
|
|
|
|
|
} |
|
|
|
|
|
if err := cp.Insert(); err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
common.ApiSuccess(c, cp) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// BatchCreateChannelPricingRequest 批量创建请求 |
|
|
|
|
|
type BatchCreateChannelPricingRequest struct { |
|
|
|
|
|
Items []*CreateChannelPricingRequest `json:"items" binding:"required"` |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// BatchCreateChannelPricing 批量创建或更新渠道定价 |
|
|
|
|
|
func BatchCreateChannelPricing(c *gin.Context) { |
|
|
|
|
|
var req BatchCreateChannelPricingRequest |
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pricings := make([]*model.ChannelPricing, 0, len(req.Items)) |
|
|
|
|
|
for _, item := range req.Items { |
|
|
|
|
|
pricings = append(pricings, &model.ChannelPricing{ |
|
|
|
|
|
ModelName: item.ModelName, |
|
|
|
|
|
ChannelId: item.ChannelId, |
|
|
|
|
|
QuotaType: item.QuotaType, |
|
|
|
|
|
ModelRatio: item.ModelRatio, |
|
|
|
|
|
CompletionRatio: item.CompletionRatio, |
|
|
|
|
|
ModelPrice: item.ModelPrice, |
|
|
|
|
|
TagIds: item.TagIds, |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 使用事务逐个处理(GORM 的批量 upsert 在不同数据库表现不一致) |
|
|
|
|
|
for _, cp := range pricings { |
|
|
|
|
|
existing, _ := model.GetChannelPricing(cp.ModelName, cp.ChannelId) |
|
|
|
|
|
if existing != nil { |
|
|
|
|
|
existing.QuotaType = cp.QuotaType |
|
|
|
|
|
existing.ModelRatio = cp.ModelRatio |
|
|
|
|
|
existing.CompletionRatio = cp.CompletionRatio |
|
|
|
|
|
existing.ModelPrice = cp.ModelPrice |
|
|
|
|
|
existing.TagIds = cp.TagIds |
|
|
|
|
|
if err := existing.Update(); err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
if err := cp.Insert(); err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
common.ApiSuccess(c, gin.H{"affected": len(pricings)}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// DeleteChannelPricing 删除渠道定价 |
|
|
|
|
|
func DeleteChannelPricing(c *gin.Context) { |
|
|
|
|
|
idStr := c.Param("id") |
|
|
|
|
|
id, err := strconv.Atoi(idStr) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
cp := &model.ChannelPricing{Id: id} |
|
|
|
|
|
if err := cp.Delete(); err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
common.ApiSuccess(c, nil) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// CopyGlobalPricingRequest 复制全局定价请求 |
|
|
|
|
|
type CopyGlobalPricingRequest struct { |
|
|
|
|
|
Overwrite bool `json:"overwrite"` // 是否覆盖已存在的渠道定价 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// CopyGlobalPricing 复制全局定价到指定渠道 |
|
|
|
|
|
// 从 ratio_setting 读取全局定价信息,复制到 channel_pricing 表 |
|
|
|
|
|
func CopyGlobalPricing(c *gin.Context) { |
|
|
|
|
|
channelIdStr := c.Param("channel_id") |
|
|
|
|
|
channelId, err := strconv.Atoi(channelIdStr) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var req CopyGlobalPricingRequest |
|
|
|
|
|
c.ShouldBindJSON(&req) |
|
|
|
|
|
|
|
|
|
|
|
// 获取该渠道支持的所有模型(使用 GetAbilitiesByChannelId) |
|
|
|
|
|
abilities, err := model.GetAbilitiesByChannelId(channelId) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
imported := 0 |
|
|
|
|
|
for _, ability := range abilities { |
|
|
|
|
|
// 检查是否已存在 |
|
|
|
|
|
existing, _ := model.GetChannelPricing(ability.Model, channelId) |
|
|
|
|
|
if existing != nil && !req.Overwrite { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 确定定价类型 |
|
|
|
|
|
var quotaType int |
|
|
|
|
|
var ratio, completionRatio, price float64 |
|
|
|
|
|
|
|
|
|
|
|
// 优先检查是否有按次计费的价格 |
|
|
|
|
|
modelPrice, hasPrice := ratio_setting.GetModelPrice(ability.Model, false) |
|
|
|
|
|
if hasPrice { |
|
|
|
|
|
quotaType = model.QuotaTypeByCall |
|
|
|
|
|
price = modelPrice |
|
|
|
|
|
} else { |
|
|
|
|
|
// 使用按量计费 |
|
|
|
|
|
quotaType = model.QuotaTypeByTokens |
|
|
|
|
|
modelRatio, hasRatio, _ := ratio_setting.GetModelRatio(ability.Model) |
|
|
|
|
|
if hasRatio { |
|
|
|
|
|
ratio = modelRatio |
|
|
|
|
|
} |
|
|
|
|
|
completionRatio = ratio_setting.GetCompletionRatio(ability.Model) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if existing != nil { |
|
|
|
|
|
existing.QuotaType = quotaType |
|
|
|
|
|
existing.ModelRatio = ratio |
|
|
|
|
|
existing.CompletionRatio = completionRatio |
|
|
|
|
|
existing.ModelPrice = price |
|
|
|
|
|
if err := existing.Update(); err == nil { |
|
|
|
|
|
imported++ |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
cp := &model.ChannelPricing{ |
|
|
|
|
|
ModelName: ability.Model, |
|
|
|
|
|
ChannelId: channelId, |
|
|
|
|
|
QuotaType: quotaType, |
|
|
|
|
|
ModelRatio: ratio, |
|
|
|
|
|
CompletionRatio: completionRatio, |
|
|
|
|
|
ModelPrice: price, |
|
|
|
|
|
} |
|
|
|
|
|
if err := cp.Insert(); err == nil { |
|
|
|
|
|
imported++ |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
common.ApiSuccess(c, gin.H{ |
|
|
|
|
|
"total": len(abilities), |
|
|
|
|
|
"imported": imported, |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ChannelPricingWithTags 带标签详情的渠道定价响应 |
|
|
|
|
|
type ChannelPricingWithTags struct { |
|
|
|
|
|
*model.ChannelPricing |
|
|
|
|
|
Tags []*model.PricingTag `json:"tags"` |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// GetChannelPricingWithTags 获取渠道定价(带标签详情) |
|
|
|
|
|
func GetChannelPricingWithTags(c *gin.Context) { |
|
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("p", "1")) |
|
|
|
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) |
|
|
|
|
|
offset := (page - 1) * pageSize |
|
|
|
|
|
|
|
|
|
|
|
list, total, err := model.GetAllChannelPricing(offset, pageSize) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
common.ApiError(c, err) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 获取所有标签 |
|
|
|
|
|
tags, _ := model.GetAllPricingTags() |
|
|
|
|
|
tagMap := make(map[int]*model.PricingTag) |
|
|
|
|
|
for _, tag := range tags { |
|
|
|
|
|
tagMap[tag.Id] = tag |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 为每个定价填充标签详情 |
|
|
|
|
|
result := make([]*ChannelPricingWithTags, 0, len(list)) |
|
|
|
|
|
for _, cp := range list { |
|
|
|
|
|
item := &ChannelPricingWithTags{ |
|
|
|
|
|
ChannelPricing: cp, |
|
|
|
|
|
Tags: make([]*model.PricingTag, 0), |
|
|
|
|
|
} |
|
|
|
|
|
if cp.TagIds != "" { |
|
|
|
|
|
for _, idStr := range strings.Split(cp.TagIds, ",") { |
|
|
|
|
|
if id, err := strconv.Atoi(idStr); err == nil { |
|
|
|
|
|
if tag, ok := tagMap[id]; ok { |
|
|
|
|
|
item.Tags = append(item.Tags, tag) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
result = append(result, item) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
common.ApiSuccess(c, gin.H{ |
|
|
|
|
|
"page": page, |
|
|
|
|
|
"page_size": pageSize, |
|
|
|
|
|
"total": total, |
|
|
|
|
|
"items": result, |
|
|
|
|
|
}) |
|
|
|
|
|
} |