Browse Source

fix(user): 修复从节点同步用户余额显示为 0 的问题

同步用户(synced)的实际余额存储在 synced_quota 字段,
但用户列表/详情接口返回的是 quota 字段(值为 0)。
新增 ApplySyncedQuota 方法,在 API 返回前将 synced_quota
赋值给 quota,使前端能正确显示余额。

同时优化 GetUserModels:map 去重替代 O(n) 线性扫描,
禁用模型查询移至 model 层,新增 StringsSubtract 工具函数。

Co-Authored-By: Claude <noreply@anthropic.com>
master
fengsilin 1 week ago
parent
commit
a34d44a824
4 changed files with 49 additions and 1 deletions
  1. +18
    -0
      common/str.go
  2. +5
    -1
      controller/user.go
  3. +10
    -0
      model/model_meta.go
  4. +16
    -0
      model/user.go

+ 18
- 0
common/str.go View File

@@ -87,6 +87,24 @@ func StringsContains(strs []string, str string) bool {
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
func StringToByteSlice(s string) []byte {
tmp1 := (*[2]uintptr)(unsafe.Pointer(&s))


+ 5
- 1
controller/user.go View File

@@ -270,6 +270,7 @@ func GetUser(c *gin.Context) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionSameLevel)
return
}
user.ApplySyncedQuota()
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
@@ -523,13 +524,16 @@ func GetUserModels(c *gin.Context) {
}
groups := service.GetUserUsableGroups(user.Group)
var models []string
seen := make(map[string]struct{})
for group := range groups {
for _, g := range model.GetGroupEnabledModels(group) {
if !common.StringsContains(models, g) {
if _, ok := seen[g]; !ok {
seen[g] = struct{}{}
models = append(models, g)
}
}
}
models = common.StringsSubtract(models, model.GetDisabledModelNames(models))
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",


+ 10
- 0
model/model_meta.go View File

@@ -98,6 +98,16 @@ func (mi *Model) Delete() 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) {
var stats []struct {
VendorID int64


+ 16
- 0
model/user.go View File

@@ -214,6 +214,20 @@ func GetMaxUserId() int {
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) {
// Start transaction
tx := DB.Begin()
@@ -245,6 +259,7 @@ func GetAllUsers(pageInfo *common.PageInfo) (users []*User, total int64, err err
return nil, 0, err
}

applySyncedUserQuota(users)
return users, total, nil
}

@@ -312,6 +327,7 @@ func SearchUsers(keyword string, group string, startIdx int, num int) ([]*User,
return nil, 0, err
}

applySyncedUserQuota(users)
return users, total, nil
}



Loading…
Cancel
Save