fix: 规范化处理代码

This commit is contained in:
npc0-hue
2026-03-12 11:42:02 +08:00
parent b84e94250f
commit 786920c7e3
6 changed files with 112 additions and 96 deletions
+9
View File
@@ -0,0 +1,9 @@
# Admin (Go Backend) Agent Guide
## Rules (must follow)
### 禁止匿名 struct
- **禁止在代码中出现匿名 struct**。不得使用 `var x []struct { ... }``var x struct { ... }` 或字面量 `struct { A int }{1}` 等匿名结构体。
- 所有用于 GORM 查询扫描、缓存结构、API 请求/响应的结构体必须定义为**具名类型**,放在合适的 model 包(如 `model/gaia/request``model/gaia/response`)或当前包顶部,便于复用和规范约束。
- 示例:用 `[]response.AppQuotaRankingRow` 替代 `[]struct { AppID string; TotalCost float64; ... }`;用 `response.AppQuotaRankingCache` 替代 `struct { List ...; Total int64 }`
+31 -63
View File
@@ -2,13 +2,13 @@ package gaia
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
gaiaReq "github.com/flipped-aurora/gin-vue-admin/server/model/gaia/request"
"github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
@@ -31,7 +31,7 @@ func (m *ModelProviderApi) GetProviderList(c *gin.Context) {
list, err := modelProviderService.GetProviderList()
if err != nil {
global.GVA_LOG.Error("获取提供商配置列表失败", zap.Error(err))
response.FailWithMessage("获取失败: "+err.Error(), c)
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithData(list, c)
@@ -54,20 +54,20 @@ func (m *ModelProviderApi) UpdateProviderConfig(c *gin.Context) {
}
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("参数错误: "+err.Error(), c)
response.FailWithMessage("参数错误:"+err.Error(), c)
return
}
if err := modelProviderService.UpdateProviderConfig(req.ProviderName, req.Enabled, req.Models); err != nil {
global.GVA_LOG.Error("更新提供商配置失败", zap.String("provider", req.ProviderName), zap.Error(err))
response.FailWithMessage("更新失败: "+err.Error(), c)
response.FailWithMessage("更新失败:"+err.Error(), c)
return
}
response.OkWithMessage("更新成功", c)
}
// GetModels 获取开启的模型列表(OpenAI格式
// GetModels 获取开启的模型列表(OpenAI 格式,供第三方兼容调用;成功时返回裸 JSON,错误时与项目统一使用 response)。
// @Tags ModelProvider
// @Summary 获取开启的模型列表
// @Security ApiKeyAuth
@@ -79,14 +79,9 @@ func (m *ModelProviderApi) GetModels(c *gin.Context) {
models, err := modelProviderService.GetEnabledModels()
if err != nil {
global.GVA_LOG.Error("获取模型列表失败", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{
"error": gin.H{
"message": "获取模型列表失败: " + err.Error(),
},
})
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
c.JSON(http.StatusOK, models)
}
@@ -161,17 +156,15 @@ func (m *ModelProviderApi) Proxy(c *gin.Context) {
func (m *ModelProviderApi) GetAvailableModels(c *gin.Context) {
providerName := c.Query("provider_name")
if providerName == "" {
response.FailWithMessage("参数错误: provider_name不能为空", c)
response.FailWithMessage("参数错误:provider_name不能为空", c)
return
}
models, err := modelProviderService.GetAvailableModelsFromDify(providerName)
if err != nil {
global.GVA_LOG.Error("获取可用模型失败", zap.String("provider", providerName), zap.Error(err))
response.FailWithMessage("获取失败: "+err.Error(), c)
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithData(models, c)
}
@@ -187,14 +180,13 @@ func (m *ModelProviderApi) GetAvailableModels(c *gin.Context) {
func (m *ModelProviderApi) TestProviderCredentials(c *gin.Context) {
providerName := c.Query("provider_name")
if providerName == "" {
response.FailWithMessage("参数错误: provider_name不能为空", c)
response.FailWithMessage("参数错误:provider_name不能为空", c)
return
}
creds, err := modelProviderService.GetDifyProviderCredentials(providerName)
if err != nil {
global.GVA_LOG.Error("获取提供商凭证失败", zap.String("provider", providerName), zap.Error(err))
response.FailWithMessage("获取凭证失败: "+err.Error(), c)
response.FailWithMessage("获取凭证失败:"+err.Error(), c)
return
}
@@ -215,7 +207,7 @@ func (m *ModelProviderApi) TestProviderCredentials(c *gin.Context) {
response.OkWithData(result, c)
}
// GetProxyLogs 获取代理日志
// GetProxyLogs 获取代理日志(分页)
// @Tags ModelProvider
// @Summary 获取代理日志
// @Security ApiKeyAuth
@@ -223,54 +215,30 @@ func (m *ModelProviderApi) TestProviderCredentials(c *gin.Context) {
// @Produce application/json
// @Param page query int false "页码"
// @Param page_size query int false "每页数量"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取成功"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /gaia/model-provider/logs [get]
func (m *ModelProviderApi) GetProxyLogs(c *gin.Context) {
page := c.DefaultQuery("page", "1")
pageSize := c.DefaultQuery("page_size", "20")
var pageInt, pageSizeInt int
if _, err := fmt.Sscanf(page, "%d", &pageInt); err != nil {
pageInt = 1
}
if _, err := fmt.Sscanf(pageSize, "%d", &pageSizeInt); err != nil {
pageSizeInt = 20
}
if pageInt < 1 {
pageInt = 1
}
if pageSizeInt < 1 || pageSizeInt > 100 {
pageSizeInt = 20
}
var logs []map[string]interface{}
var total int64
db := global.GVA_DB.Table("model_proxy_log")
// 获取总数
if err := db.Count(&total).Error; err != nil {
global.GVA_LOG.Error("获取日志总数失败", zap.Error(err))
response.FailWithMessage("获取失败: "+err.Error(), c)
var req gaiaReq.GetProxyLogsReq
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage("参数错误:"+err.Error(), c)
return
}
// 分页查询
offset := (pageInt - 1) * pageSizeInt
if err := db.Order("created_at DESC").Limit(pageSizeInt).Offset(
offset).Find(&logs).Error; err != nil {
global.GVA_LOG.Error("获取日志列表失败", zap.Error(err))
response.FailWithMessage("获取失败: "+err.Error(), c)
if req.Page < 1 {
req.Page = 1
}
if req.PageSize < 1 || req.PageSize > 100 {
req.PageSize = 20
}
list, total, err := modelProviderService.GetProxyLogs(req)
if err != nil {
global.GVA_LOG.Error("获取代理日志失败", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
result := map[string]interface{}{
"list": logs,
"total": total,
"page": pageInt,
"page_size": pageSizeInt,
}
response.OkWithData(result, c)
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, "获取成功", c)
}
@@ -1,5 +1,11 @@
package request
// GetProxyLogsReq 代理日志分页请求
type GetProxyLogsReq struct {
Page int `form:"page"` // 页码,从 1 开始
PageSize int `form:"page_size"` // 每页条数,最大 100
}
// ChatRequest 聊天请求(OpenAI 兼容)
type ChatRequest struct {
Model string `json:"model"`
@@ -1,5 +1,29 @@
package response
// AppQuotaRankingRow 应用配额排名查询单行(仅用于 service 层 GORM 查询扫描)
type AppQuotaRankingRow struct {
AppID string `gorm:"column:app_id"`
TotalCost float64 `gorm:"column:total_cost"`
MessageCost float64 `gorm:"column:message_cost"`
WorkflowCost float64 `gorm:"column:workflow_cost"`
RecordNum float64 `gorm:"column:record_num"`
}
// AppQuotaRankingCache 应用配额排名缓存结构(List + Total
type AppQuotaRankingCache struct {
List []GetAppQuotaRankingDataRes
Total int64
}
// AiImageQuotaRankingRow AI 图片使用量排名查询单行(仅用于 service 层 GORM 查询扫描)
type AiImageQuotaRankingRow struct {
Address string `gorm:"column:address"`
Path string `gorm:"column:path"`
TotalCost float64 `gorm:"column:total_cost"`
RecordNum int `gorm:"column:record_num"`
Model string `gorm:"column:model"`
}
// GetAccountQuotaRankingDataRes 获取账户配额排名数据的响应结构
type GetAccountQuotaRankingDataRes struct {
Ranking int `json:"ranking"` // 排名
+20 -33
View File
@@ -19,7 +19,8 @@ import (
type DashboardService struct{}
// GetAccountQuotaRankingData 分页获取【账号】额度排名列表
func (dashboardService *DashboardService) GetAccountQuotaRankingData(info gaiaReq.GetAccountQuotaRankingDataReq) (list []response.GetAccountQuotaRankingDataRes, total int64, err error) {
func (s *DashboardService) GetAccountQuotaRankingData(info gaiaReq.GetAccountQuotaRankingDataReq) (
list []response.GetAccountQuotaRankingDataRes, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
@@ -79,15 +80,13 @@ func (dashboardService *DashboardService) GetAccountQuotaRankingData(info gaiaRe
}
// GetAppQuotaRankingData 分页获取【应用】配额排名数据
func (dashboardService *DashboardService) GetAppQuotaRankingData(info gaiaReq.GetAppQuotaRankingDataReq) (list []response.GetAppQuotaRankingDataRes, total int64, err error) {
func (s *DashboardService) GetAppQuotaRankingData(info gaiaReq.GetAppQuotaRankingDataReq) (
list []response.GetAppQuotaRankingDataRes, total int64, err error) {
cacheKey := fmt.Sprintf("app_token_quota_ranking:%d:%d", info.Page, info.PageSize)
var cachedResult struct {
List []response.GetAppQuotaRankingDataRes
Total int64
}
var cachedResult response.AppQuotaRankingCache
if found, err := dashboardService.getCachedResult(cacheKey, &cachedResult); err == nil && found {
if found, err := s.getCachedResult(cacheKey, &cachedResult); err == nil && found {
return cachedResult.List, cachedResult.Total, nil
}
@@ -140,13 +139,7 @@ func (dashboardService *DashboardService) GetAppQuotaRankingData(info gaiaReq.Ge
}
// 执行查询
var results []struct {
AppID string `gorm:"column:app_id"`
TotalCost float64 `gorm:"column:total_cost"`
MessageCost float64 `gorm:"column:message_cost"`
WorkflowCost float64 `gorm:"column:workflow_cost"`
RecordNum float64 `gorm:"column:record_num"`
}
var results []response.AppQuotaRankingRow
err = query.Find(&results).Error
if err != nil {
@@ -269,12 +262,8 @@ func (dashboardService *DashboardService) GetAppQuotaRankingData(info gaiaReq.Ge
}
// 在返回结果之前,缓存结果
result := struct {
List []response.GetAppQuotaRankingDataRes
Total int64
}{list, total}
if err := dashboardService.cacheResult(cacheKey, result, 24*time.Hour); err != nil {
cachePayload := response.AppQuotaRankingCache{List: list, Total: total}
if err := s.cacheResult(cacheKey, cachePayload, 24*time.Hour); err != nil {
global.GVA_LOG.Error("Failed to cache result", zap.Error(err))
}
@@ -282,7 +271,8 @@ func (dashboardService *DashboardService) GetAppQuotaRankingData(info gaiaReq.Ge
}
// GetAppTokenQuotaRankingData 分页获取【应用密钥】配额排名数据列表
func (dashboardService *DashboardService) GetAppTokenQuotaRankingData(info gaiaReq.GetAppTokenQuotaRankingDataReq) (list []response.GetAppTokenQuotaRankingDataRes, total int64, err error) {
func (s *DashboardService) GetAppTokenQuotaRankingData(info gaiaReq.GetAppTokenQuotaRankingDataReq) (
list []response.GetAppTokenQuotaRankingDataRes, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
@@ -365,9 +355,11 @@ func (dashboardService *DashboardService) GetAppTokenQuotaRankingData(info gaiaR
}
// GetAppTokenDailyQuotaData 获取每天密钥花费数据列表
func (dashboardService *DashboardService) GetAppTokenDailyQuotaData(info gaiaReq.GetAppTokenDailyQuotaDataReq) (list []response.GetAppTokenDailyQuotaDataRes, err error) {
func (s *DashboardService) GetAppTokenDailyQuotaData(info gaiaReq.GetAppTokenDailyQuotaDataReq) (
list []response.GetAppTokenDailyQuotaDataRes, err error) {
db := global.GVA_DB.Select("DATE(stat_at) as stat_at, SUM(day_used_quota) as day_used_quota").Model(&gaia.ApiTokenMoneyDailyStatExtend{}).Order("stat_at desc").Group("DATE(stat_at)")
db := global.GVA_DB.Select("DATE(stat_at) as stat_at, SUM(day_used_quota) as day_used_quota").Model(
&gaia.ApiTokenMoneyDailyStatExtend{}).Order("stat_at desc").Group("DATE(stat_at)")
var apiTokenMoneyDailyStatExtends []gaia.ApiTokenMoneyDailyStatExtend
if info.AppId != "" {
@@ -397,7 +389,8 @@ func (dashboardService *DashboardService) GetAppTokenDailyQuotaData(info gaiaReq
}
// GetAiImageQuotaRankingData 获取【AI图片】使用量排名数据列表
func (dashboardService *DashboardService) GetAiImageQuotaRankingData(info gaiaReq.GetAiImageQuotaRankingDataReq) (list []response.GetAiImageQuotaRankingRes, err error) {
func (s *DashboardService) GetAiImageQuotaRankingData(info gaiaReq.GetAiImageQuotaRankingDataReq) (
list []response.GetAiImageQuotaRankingRes, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
@@ -427,13 +420,7 @@ func (dashboardService *DashboardService) GetAiImageQuotaRankingData(info gaiaRe
db = db.Limit(limit).Offset(offset)
}
var results []struct {
Address string
Path string
TotalCost float64
RecordNum int
Model string
}
var results []response.AiImageQuotaRankingRow
err = db.Find(&results).Error
if err != nil {
@@ -456,7 +443,7 @@ func (dashboardService *DashboardService) GetAiImageQuotaRankingData(info gaiaRe
return list, nil
}
func (dashboardService *DashboardService) cacheResult(key string, data interface{}, expiration time.Duration) error {
func (s *DashboardService) cacheResult(key string, data interface{}, expiration time.Duration) error {
jsonData, err := json.Marshal(data)
if err != nil {
@@ -465,7 +452,7 @@ func (dashboardService *DashboardService) cacheResult(key string, data interface
return global.GVA_REDIS.Set(context.Background(), key, jsonData, expiration).Err()
}
func (dashboardService *DashboardService) getCachedResult(key string, result interface{}) (bool, error) {
func (s *DashboardService) getCachedResult(key string, result interface{}) (bool, error) {
data, err := global.GVA_REDIS.Get(context.Background(), key).Bytes()
if err != nil {
if errors.Is(err, redis.Nil) {
@@ -1346,6 +1346,28 @@ func (s *ModelProviderService) ProxyRequest(
return err
}
// GetProxyLogs 分页查询代理日志(model_proxy_log 表)。
func (s *ModelProviderService) GetProxyLogs(info gaiaRequest.GetProxyLogsReq) (list []map[string]interface{}, total int64, err error) {
page, pageSize := info.Page, info.PageSize
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 20
}
db := global.GVA_DB.Table("model_proxy_log")
if err = db.Count(&total).Error; err != nil {
err = fmt.Errorf("查询日志总数失败:%w", err)
return
}
offset := (page - 1) * pageSize
if err = db.Order("created_at DESC").Limit(pageSize).Offset(offset).Find(&list).Error; err != nil {
err = fmt.Errorf("查询日志列表失败:%w", err)
return
}
return list, total, nil
}
// isProviderEnabled 检查该提供商是否已启用(未校验具体模型列表,用于通用代理)。
func (s *ModelProviderService) isProviderEnabled(providerName string) bool {
var config gaia.ModelProviderConfig