|
|
@@ -1,206 +1,213 @@
|
|
|
package model
|
|
|
|
|
|
import (
|
|
|
+ "encoding/json"
|
|
|
"fmt"
|
|
|
"one-api/common"
|
|
|
"one-api/constant"
|
|
|
- "strconv"
|
|
|
"time"
|
|
|
+
|
|
|
+ "github.com/bytedance/gopkg/util/gopool"
|
|
|
)
|
|
|
|
|
|
-// Change UserCache struct to userCache
|
|
|
-type userCache struct {
|
|
|
+// UserBase struct remains the same as it represents the cached data structure
|
|
|
+type UserBase struct {
|
|
|
Id int `json:"id"`
|
|
|
Group string `json:"group"`
|
|
|
+ Email string `json:"email"`
|
|
|
Quota int `json:"quota"`
|
|
|
Status int `json:"status"`
|
|
|
- Role int `json:"role"`
|
|
|
Username string `json:"username"`
|
|
|
+ Setting string `json:"setting"`
|
|
|
}
|
|
|
|
|
|
-// Rename all exported functions to private ones
|
|
|
-// invalidateUserCache clears all user related cache
|
|
|
-func invalidateUserCache(userId int) error {
|
|
|
- if !common.RedisEnabled {
|
|
|
+func (user *UserBase) GetSetting() map[string]interface{} {
|
|
|
+ if user.Setting == "" {
|
|
|
return nil
|
|
|
}
|
|
|
-
|
|
|
- keys := []string{
|
|
|
- fmt.Sprintf(constant.UserGroupKeyFmt, userId),
|
|
|
- fmt.Sprintf(constant.UserQuotaKeyFmt, userId),
|
|
|
- fmt.Sprintf(constant.UserEnabledKeyFmt, userId),
|
|
|
- fmt.Sprintf(constant.UserUsernameKeyFmt, userId),
|
|
|
- }
|
|
|
-
|
|
|
- for _, key := range keys {
|
|
|
- if err := common.RedisDel(key); err != nil {
|
|
|
- return fmt.Errorf("failed to delete cache key %s: %w", key, err)
|
|
|
- }
|
|
|
- }
|
|
|
- return nil
|
|
|
+ return common.StrToMap(user.Setting)
|
|
|
}
|
|
|
|
|
|
-// updateUserGroupCache updates user group cache
|
|
|
-func updateUserGroupCache(userId int, group string) error {
|
|
|
- if !common.RedisEnabled {
|
|
|
- return nil
|
|
|
+func (user *UserBase) SetSetting(setting map[string]interface{}) {
|
|
|
+ settingBytes, err := json.Marshal(setting)
|
|
|
+ if err != nil {
|
|
|
+ common.SysError("failed to marshal setting: " + err.Error())
|
|
|
+ return
|
|
|
}
|
|
|
- return common.RedisSet(
|
|
|
- fmt.Sprintf(constant.UserGroupKeyFmt, userId),
|
|
|
- group,
|
|
|
- time.Duration(constant.UserId2QuotaCacheSeconds)*time.Second,
|
|
|
- )
|
|
|
+ user.Setting = string(settingBytes)
|
|
|
}
|
|
|
|
|
|
-// updateUserQuotaCache updates user quota cache
|
|
|
-func updateUserQuotaCache(userId int, quota int) error {
|
|
|
- if !common.RedisEnabled {
|
|
|
- return nil
|
|
|
- }
|
|
|
- return common.RedisSet(
|
|
|
- fmt.Sprintf(constant.UserQuotaKeyFmt, userId),
|
|
|
- fmt.Sprintf("%d", quota),
|
|
|
- time.Duration(constant.UserId2QuotaCacheSeconds)*time.Second,
|
|
|
- )
|
|
|
+// getUserCacheKey returns the key for user cache
|
|
|
+func getUserCacheKey(userId int) string {
|
|
|
+ return fmt.Sprintf("user:%d", userId)
|
|
|
}
|
|
|
|
|
|
-// updateUserStatusCache updates user status cache
|
|
|
-func updateUserStatusCache(userId int, userEnabled bool) error {
|
|
|
+// invalidateUserCache clears user cache
|
|
|
+func invalidateUserCache(userId int) error {
|
|
|
if !common.RedisEnabled {
|
|
|
return nil
|
|
|
}
|
|
|
- enabled := "0"
|
|
|
- if userEnabled {
|
|
|
- enabled = "1"
|
|
|
- }
|
|
|
- return common.RedisSet(
|
|
|
- fmt.Sprintf(constant.UserEnabledKeyFmt, userId),
|
|
|
- enabled,
|
|
|
- time.Duration(constant.UserId2StatusCacheSeconds)*time.Second,
|
|
|
- )
|
|
|
+ return common.RedisHDelObj(getUserCacheKey(userId))
|
|
|
}
|
|
|
|
|
|
-// updateUserNameCache updates username cache
|
|
|
-func updateUserNameCache(userId int, username string) error {
|
|
|
+// updateUserCache updates all user cache fields using hash
|
|
|
+func updateUserCache(user User) error {
|
|
|
if !common.RedisEnabled {
|
|
|
return nil
|
|
|
}
|
|
|
- return common.RedisSet(
|
|
|
- fmt.Sprintf(constant.UserUsernameKeyFmt, userId),
|
|
|
- username,
|
|
|
+
|
|
|
+ return common.RedisHSetObj(
|
|
|
+ getUserCacheKey(user.Id),
|
|
|
+ user.ToBaseUser(),
|
|
|
time.Duration(constant.UserId2QuotaCacheSeconds)*time.Second,
|
|
|
)
|
|
|
}
|
|
|
|
|
|
-// updateUserCache updates all user cache fields
|
|
|
-func updateUserCache(userId int, username string, userGroup string, quota int, status int) error {
|
|
|
- if !common.RedisEnabled {
|
|
|
- return nil
|
|
|
+// GetUserCache gets complete user cache from hash
|
|
|
+func GetUserCache(userId int) (userCache *UserBase, err error) {
|
|
|
+ var user *User
|
|
|
+ var fromDB bool
|
|
|
+ defer func() {
|
|
|
+ // Update Redis cache asynchronously on successful DB read
|
|
|
+ if shouldUpdateRedis(fromDB, err) && user != nil {
|
|
|
+ gopool.Go(func() {
|
|
|
+ if err := updateUserCache(*user); err != nil {
|
|
|
+ common.SysError("failed to update user status cache: " + err.Error())
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ // Try getting from Redis first
|
|
|
+ userCache, err = cacheGetUserBase(userId)
|
|
|
+ if err == nil {
|
|
|
+ return userCache, nil
|
|
|
}
|
|
|
|
|
|
- if err := updateUserGroupCache(userId, userGroup); err != nil {
|
|
|
- return fmt.Errorf("update group cache: %w", err)
|
|
|
+ // If Redis fails, get from DB
|
|
|
+ fromDB = true
|
|
|
+ user, err = GetUserById(userId, false)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err // Return nil and error if DB lookup fails
|
|
|
}
|
|
|
|
|
|
- if err := updateUserQuotaCache(userId, quota); err != nil {
|
|
|
- return fmt.Errorf("update quota cache: %w", err)
|
|
|
+ // Create cache object from user data
|
|
|
+ userCache = &UserBase{
|
|
|
+ Id: user.Id,
|
|
|
+ Group: user.Group,
|
|
|
+ Quota: user.Quota,
|
|
|
+ Status: user.Status,
|
|
|
+ Username: user.Username,
|
|
|
+ Setting: user.Setting,
|
|
|
+ Email: user.Email,
|
|
|
}
|
|
|
|
|
|
- if err := updateUserStatusCache(userId, status == common.UserStatusEnabled); err != nil {
|
|
|
- return fmt.Errorf("update status cache: %w", err)
|
|
|
+ return userCache, nil
|
|
|
+}
|
|
|
+
|
|
|
+func cacheGetUserBase(userId int) (*UserBase, error) {
|
|
|
+ if !common.RedisEnabled {
|
|
|
+ return nil, fmt.Errorf("redis is not enabled")
|
|
|
}
|
|
|
+ var userCache UserBase
|
|
|
+ // Try getting from Redis first
|
|
|
+ err := common.RedisHGetObj(getUserCacheKey(userId), &userCache)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return &userCache, nil
|
|
|
+}
|
|
|
|
|
|
- if err := updateUserNameCache(userId, username); err != nil {
|
|
|
- return fmt.Errorf("update username cache: %w", err)
|
|
|
+// Add atomic quota operations using hash fields
|
|
|
+func cacheIncrUserQuota(userId int, delta int64) error {
|
|
|
+ if !common.RedisEnabled {
|
|
|
+ return nil
|
|
|
}
|
|
|
+ return common.RedisHIncrBy(getUserCacheKey(userId), "Quota", delta)
|
|
|
+}
|
|
|
|
|
|
- return nil
|
|
|
+func cacheDecrUserQuota(userId int, delta int64) error {
|
|
|
+ return cacheIncrUserQuota(userId, -delta)
|
|
|
}
|
|
|
|
|
|
-// getUserGroupCache gets user group from cache
|
|
|
+// Helper functions to get individual fields if needed
|
|
|
func getUserGroupCache(userId int) (string, error) {
|
|
|
- if !common.RedisEnabled {
|
|
|
- return "", nil
|
|
|
+ cache, err := GetUserCache(userId)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
}
|
|
|
- return common.RedisGet(fmt.Sprintf(constant.UserGroupKeyFmt, userId))
|
|
|
+ return cache.Group, nil
|
|
|
}
|
|
|
|
|
|
-// getUserQuotaCache gets user quota from cache
|
|
|
func getUserQuotaCache(userId int) (int, error) {
|
|
|
- if !common.RedisEnabled {
|
|
|
- return 0, nil
|
|
|
- }
|
|
|
- quotaStr, err := common.RedisGet(fmt.Sprintf(constant.UserQuotaKeyFmt, userId))
|
|
|
+ cache, err := GetUserCache(userId)
|
|
|
if err != nil {
|
|
|
return 0, err
|
|
|
}
|
|
|
- return strconv.Atoi(quotaStr)
|
|
|
+ return cache.Quota, nil
|
|
|
}
|
|
|
|
|
|
-// getUserStatusCache gets user status from cache
|
|
|
func getUserStatusCache(userId int) (int, error) {
|
|
|
- if !common.RedisEnabled {
|
|
|
- return 0, nil
|
|
|
- }
|
|
|
- statusStr, err := common.RedisGet(fmt.Sprintf(constant.UserEnabledKeyFmt, userId))
|
|
|
+ cache, err := GetUserCache(userId)
|
|
|
if err != nil {
|
|
|
return 0, err
|
|
|
}
|
|
|
- return strconv.Atoi(statusStr)
|
|
|
+ return cache.Status, nil
|
|
|
}
|
|
|
|
|
|
-// getUserNameCache gets username from cache
|
|
|
func getUserNameCache(userId int) (string, error) {
|
|
|
- if !common.RedisEnabled {
|
|
|
- return "", nil
|
|
|
+ cache, err := GetUserCache(userId)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
}
|
|
|
- return common.RedisGet(fmt.Sprintf(constant.UserUsernameKeyFmt, userId))
|
|
|
+ return cache.Username, nil
|
|
|
}
|
|
|
|
|
|
-// getUserCache gets complete user cache
|
|
|
-func getUserCache(userId int) (*userCache, error) {
|
|
|
- if !common.RedisEnabled {
|
|
|
- return nil, nil
|
|
|
- }
|
|
|
-
|
|
|
- group, err := getUserGroupCache(userId)
|
|
|
+func getUserSettingCache(userId int) (map[string]interface{}, error) {
|
|
|
+ setting := make(map[string]interface{})
|
|
|
+ cache, err := GetUserCache(userId)
|
|
|
if err != nil {
|
|
|
- return nil, fmt.Errorf("get group cache: %w", err)
|
|
|
+ return setting, err
|
|
|
}
|
|
|
+ return cache.GetSetting(), nil
|
|
|
+}
|
|
|
|
|
|
- quota, err := getUserQuotaCache(userId)
|
|
|
- if err != nil {
|
|
|
- return nil, fmt.Errorf("get quota cache: %w", err)
|
|
|
+// New functions for individual field updates
|
|
|
+func updateUserStatusCache(userId int, status bool) error {
|
|
|
+ if !common.RedisEnabled {
|
|
|
+ return nil
|
|
|
}
|
|
|
-
|
|
|
- status, err := getUserStatusCache(userId)
|
|
|
- if err != nil {
|
|
|
- return nil, fmt.Errorf("get status cache: %w", err)
|
|
|
+ statusInt := common.UserStatusEnabled
|
|
|
+ if !status {
|
|
|
+ statusInt = common.UserStatusDisabled
|
|
|
}
|
|
|
+ return common.RedisHSetField(getUserCacheKey(userId), "Status", fmt.Sprintf("%d", statusInt))
|
|
|
+}
|
|
|
|
|
|
- username, err := getUserNameCache(userId)
|
|
|
- if err != nil {
|
|
|
- return nil, fmt.Errorf("get username cache: %w", err)
|
|
|
+func updateUserQuotaCache(userId int, quota int) error {
|
|
|
+ if !common.RedisEnabled {
|
|
|
+ return nil
|
|
|
}
|
|
|
+ return common.RedisHSetField(getUserCacheKey(userId), "Quota", fmt.Sprintf("%d", quota))
|
|
|
+}
|
|
|
|
|
|
- return &userCache{
|
|
|
- Id: userId,
|
|
|
- Group: group,
|
|
|
- Quota: quota,
|
|
|
- Status: status,
|
|
|
- Username: username,
|
|
|
- }, nil
|
|
|
+func updateUserGroupCache(userId int, group string) error {
|
|
|
+ if !common.RedisEnabled {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return common.RedisHSetField(getUserCacheKey(userId), "Group", group)
|
|
|
}
|
|
|
|
|
|
-// Add atomic quota operations
|
|
|
-func cacheIncrUserQuota(userId int, delta int64) error {
|
|
|
+func updateUserNameCache(userId int, username string) error {
|
|
|
if !common.RedisEnabled {
|
|
|
return nil
|
|
|
}
|
|
|
- key := fmt.Sprintf(constant.UserQuotaKeyFmt, userId)
|
|
|
- return common.RedisIncr(key, delta)
|
|
|
+ return common.RedisHSetField(getUserCacheKey(userId), "Username", username)
|
|
|
}
|
|
|
|
|
|
-func cacheDecrUserQuota(userId int, delta int64) error {
|
|
|
- return cacheIncrUserQuota(userId, -delta)
|
|
|
+func updateUserSettingCache(userId int, setting string) error {
|
|
|
+ if !common.RedisEnabled {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return common.RedisHSetField(getUserCacheKey(userId), "Setting", setting)
|
|
|
}
|