同步用户(synced)的实际余额存储在 synced_quota 字段, 但用户列表/详情接口返回的是 quota 字段(值为 0)。 新增 ApplySyncedQuota 方法,在 API 返回前将 synced_quota 赋值给 quota,使前端能正确显示余额。 同时优化 GetUserModels:map 去重替代 O(n) 线性扫描, 禁用模型查询移至 model 层,新增 StringsSubtract 工具函数。 Co-Authored-By: Claude <noreply@anthropic.com>master
| @@ -87,6 +87,24 @@ func StringsContains(strs []string, str string) bool { | |||||
| return false | return false | ||||
| } | } | ||||
| // StringsSubtract returns elements from source that are not in exclude. | |||||
| func StringsSubtract(source, exclude []string) []string { | |||||
| if len(exclude) == 0 { | |||||
| return source | |||||
| } | |||||
| excludeSet := make(map[string]struct{}, len(exclude)) | |||||
| for _, s := range exclude { | |||||
| excludeSet[s] = struct{}{} | |||||
| } | |||||
| result := make([]string, 0, len(source)) | |||||
| for _, s := range source { | |||||
| if _, ok := excludeSet[s]; !ok { | |||||
| result = append(result, s) | |||||
| } | |||||
| } | |||||
| return result | |||||
| } | |||||
| // StringToByteSlice []byte only read, panic on append | // StringToByteSlice []byte only read, panic on append | ||||
| func StringToByteSlice(s string) []byte { | func StringToByteSlice(s string) []byte { | ||||
| tmp1 := (*[2]uintptr)(unsafe.Pointer(&s)) | tmp1 := (*[2]uintptr)(unsafe.Pointer(&s)) | ||||
| @@ -270,6 +270,7 @@ func GetUser(c *gin.Context) { | |||||
| common.ApiErrorI18n(c, i18n.MsgUserNoPermissionSameLevel) | common.ApiErrorI18n(c, i18n.MsgUserNoPermissionSameLevel) | ||||
| return | return | ||||
| } | } | ||||
| user.ApplySyncedQuota() | |||||
| c.JSON(http.StatusOK, gin.H{ | c.JSON(http.StatusOK, gin.H{ | ||||
| "success": true, | "success": true, | ||||
| "message": "", | "message": "", | ||||
| @@ -523,13 +524,16 @@ func GetUserModels(c *gin.Context) { | |||||
| } | } | ||||
| groups := service.GetUserUsableGroups(user.Group) | groups := service.GetUserUsableGroups(user.Group) | ||||
| var models []string | var models []string | ||||
| seen := make(map[string]struct{}) | |||||
| for group := range groups { | for group := range groups { | ||||
| for _, g := range model.GetGroupEnabledModels(group) { | for _, g := range model.GetGroupEnabledModels(group) { | ||||
| if !common.StringsContains(models, g) { | |||||
| if _, ok := seen[g]; !ok { | |||||
| seen[g] = struct{}{} | |||||
| models = append(models, g) | models = append(models, g) | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| models = common.StringsSubtract(models, model.GetDisabledModelNames(models)) | |||||
| c.JSON(http.StatusOK, gin.H{ | c.JSON(http.StatusOK, gin.H{ | ||||
| "success": true, | "success": true, | ||||
| "message": "", | "message": "", | ||||
| @@ -98,6 +98,16 @@ func (mi *Model) Delete() error { | |||||
| return DB.Delete(mi).Error | return DB.Delete(mi).Error | ||||
| } | } | ||||
| // GetDisabledModelNames returns model names that are disabled (status = 0) from the given list. | |||||
| func GetDisabledModelNames(names []string) []string { | |||||
| if len(names) == 0 { | |||||
| return nil | |||||
| } | |||||
| var disabled []string | |||||
| DB.Table("models").Where("model_name IN ? AND status = 0", names).Pluck("model_name", &disabled) | |||||
| return disabled | |||||
| } | |||||
| func GetVendorModelCounts() (map[int64]int64, error) { | func GetVendorModelCounts() (map[int64]int64, error) { | ||||
| var stats []struct { | var stats []struct { | ||||
| VendorID int64 | VendorID int64 | ||||
| @@ -214,6 +214,20 @@ func GetMaxUserId() int { | |||||
| return user.Id | return user.Id | ||||
| } | } | ||||
| // ApplySyncedQuota 将同步用户的 quota 替换为 synced_quota, | |||||
| // 使 API 返回的 quota 对所有用户类型都表示实际可用余额。 | |||||
| func (u *User) ApplySyncedQuota() { | |||||
| if u.IsSyncedUser() { | |||||
| u.Quota = u.SyncedQuota | |||||
| } | |||||
| } | |||||
| func applySyncedUserQuota(users []*User) { | |||||
| for _, u := range users { | |||||
| u.ApplySyncedQuota() | |||||
| } | |||||
| } | |||||
| func GetAllUsers(pageInfo *common.PageInfo) (users []*User, total int64, err error) { | func GetAllUsers(pageInfo *common.PageInfo) (users []*User, total int64, err error) { | ||||
| // Start transaction | // Start transaction | ||||
| tx := DB.Begin() | tx := DB.Begin() | ||||
| @@ -245,6 +259,7 @@ func GetAllUsers(pageInfo *common.PageInfo) (users []*User, total int64, err err | |||||
| return nil, 0, err | return nil, 0, err | ||||
| } | } | ||||
| applySyncedUserQuota(users) | |||||
| return users, total, nil | return users, total, nil | ||||
| } | } | ||||
| @@ -312,6 +327,7 @@ func SearchUsers(keyword string, group string, startIdx int, num int) ([]*User, | |||||
| return nil, 0, err | return nil, 0, err | ||||
| } | } | ||||
| applySyncedUserQuota(users) | |||||
| return users, total, nil | return users, total, nil | ||||
| } | } | ||||