tokenFactory/controller/supplier_dashboard.go

179 lines
5.3 KiB
Go

package controller
import (
"net/http"
"sort"
"strconv"
"time"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/model"
"github.com/gin-gonic/gin"
)
// SupplierModelUsageItem 供应商模型使用统计项。
type SupplierModelUsageItem struct {
ModelName string `json:"model_name"`
Requests int `json:"requests"`
Tokens int `json:"tokens"`
Quota int `json:"quota"`
}
// loadSupplierDashboardAccount 加载供应商对接人(申请人)在平台上的剩余额度与历史累计已用额度,与使用日志中的额度/花费字段同源。
func loadSupplierDashboardAccount(c *gin.Context, adminSupplierID int) (quota int, usedQuota int) {
if c.GetInt("role") >= common.RoleAdminUser {
if adminSupplierID <= 0 {
return 0, 0
}
app, err := model.GetSupplierByID(adminSupplierID)
if err != nil || app == nil || app.ApplicantUserID <= 0 {
return 0, 0
}
u, err := model.GetUserById(app.ApplicantUserID, false)
if err != nil {
return 0, 0
}
return u.Quota, u.UsedQuota
}
u, err := model.GetUserById(c.GetInt("id"), false)
if err != nil {
return 0, 0
}
return u.Quota, u.UsedQuota
}
// parseSupplierDashboardTimeRange 解析供应商看板时间范围:请求参数 start_timestamp、end_timestamp 为 Unix 秒;未传或非法时默认最近 24 小时。
func parseSupplierDashboardTimeRange(c *gin.Context) (int64, int64) {
startTimestamp, _ := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
if endTimestamp <= 0 {
endTimestamp = time.Now().Unix()
}
if startTimestamp <= 0 || startTimestamp >= endTimestamp {
startTimestamp = endTimestamp - 24*3600
}
return startTimestamp, endTimestamp
}
// toSortedModelSlice 将模型集合转换为稳定排序切片。
func toSortedModelSlice(modelsMap map[string]struct{}) []string {
modelNames := make([]string, 0, len(modelsMap))
for modelName := range modelsMap {
modelNames = append(modelNames, modelName)
}
sort.Strings(modelNames)
return modelNames
}
// GetSupplierDashboardData 返回供应商数据看板(供应商看自己,管理员看全部供应商模型)。
func GetSupplierDashboardData(c *gin.Context) {
startTimestamp, endTimestamp := parseSupplierDashboardTimeRange(c)
adminSupplierID, _ := strconv.Atoi(c.Query("supplier_id"))
accountQuota, accountUsedQuota := loadSupplierDashboardAccount(c, adminSupplierID)
var (
modelNamesMap map[string]struct{}
err error
)
// 管理员默认查看全部供应商模型;当传 supplier_id 时查看指定供应商。
if c.GetInt("role") >= common.RoleAdminUser {
supplierID := adminSupplierID
if supplierID > 0 {
modelNamesMap, err = collectSupplierOwnedModelNamesBySupplierID(supplierID)
} else {
modelNamesMap, err = collectAllSupplierOwnedModelNames()
}
} else {
modelNamesMap, err = collectSupplierOwnedModelNames(c.GetInt("id"))
}
if err != nil {
common.ApiError(c, err)
return
}
modelNames := toSortedModelSlice(modelNamesMap)
quotaData, err := model.GetQuotaDataByModelNames(startTimestamp, endTimestamp, modelNames)
if err != nil {
common.ApiError(c, err)
return
}
stat, err := model.SumUsedQuotaByModelNames(startTimestamp, endTimestamp, modelNames)
if err != nil {
common.ApiError(c, err)
return
}
usageMap := make(map[string]*SupplierModelUsageItem)
totalRequests := 0
totalTokens := 0
totalQuota := 0
for _, item := range quotaData {
if item == nil {
continue
}
totalRequests += item.Count
totalTokens += item.TokenUsed
totalQuota += item.Quota
usageItem, ok := usageMap[item.ModelName]
if !ok {
usageItem = &SupplierModelUsageItem{
ModelName: item.ModelName,
}
usageMap[item.ModelName] = usageItem
}
usageItem.Requests += item.Count
usageItem.Tokens += item.TokenUsed
usageItem.Quota += item.Quota
}
modelUsageStats := make([]*SupplierModelUsageItem, 0, len(usageMap))
for _, usageItem := range usageMap {
modelUsageStats = append(modelUsageStats, usageItem)
}
sort.Slice(modelUsageStats, func(i, j int) bool {
return modelUsageStats[i].Quota > modelUsageStats[j].Quota
})
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
"data": gin.H{
"start_timestamp": startTimestamp,
"end_timestamp": endTimestamp,
"usage_time_range": gin.H{
"start_timestamp": startTimestamp,
"end_timestamp": endTimestamp,
"bucket": "hour",
},
"account": gin.H{
"quota": accountQuota,
"used_quota": accountUsedQuota,
},
"model_names": modelNames,
"quota_data": quotaData,
"model_usage_stats": modelUsageStats,
"resource_consumption": gin.H{
"total_requests": totalRequests,
"total_tokens": totalTokens,
"total_quota": totalQuota,
},
"performance_metrics": gin.H{
"rpm": stat.Rpm,
"tpm": stat.Tpm,
},
"model_data_analysis": gin.H{
// provided_model_count: 供应商配置过的模型总数(无请求也计入)。
"provided_model_count": len(modelNames),
// active_model_count: 时间范围内有调用数据的模型数。
"active_model_count": len(modelUsageStats),
// model_count 保留为兼容字段,语义等同 provided_model_count。
"model_count": len(modelNames),
"top_models": modelUsageStats,
},
},
})
}