package model import ( "fmt" "one-api/common" "one-api/constant" "one-api/dto" "time" "github.com/gin-gonic/gin" "github.com/bytedance/gopkg/util/gopool" ) // 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"` Username string `json:"username"` Setting string `json:"setting"` } func (user *UserBase) WriteContext(c *gin.Context) { common.SetContextKey(c, constant.ContextKeyUserGroup, user.Group) common.SetContextKey(c, constant.ContextKeyUserQuota, user.Quota) common.SetContextKey(c, constant.ContextKeyUserStatus, user.Status) common.SetContextKey(c, constant.ContextKeyUserEmail, user.Email) common.SetContextKey(c, constant.ContextKeyUserName, user.Username) common.SetContextKey(c, constant.ContextKeyUserSetting, user.GetSetting()) } func (user *UserBase) GetSetting() dto.UserSetting { setting := dto.UserSetting{} if user.Setting != "" { err := common.Unmarshal([]byte(user.Setting), &setting) if err != nil { common.SysError("failed to unmarshal setting: " + err.Error()) } } return setting } // getUserCacheKey returns the key for user cache func getUserCacheKey(userId int) string { return fmt.Sprintf("user:%d", userId) } // invalidateUserCache clears user cache func invalidateUserCache(userId int) error { if !common.RedisEnabled { return nil } return common.RedisDelKey(getUserCacheKey(userId)) } // updateUserCache updates all user cache fields using hash func updateUserCache(user User) error { if !common.RedisEnabled { return nil } return common.RedisHSetObj( getUserCacheKey(user.Id), user.ToBaseUser(), time.Duration(common.RedisKeyCacheSeconds())*time.Second, ) } // 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 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 } // 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, } 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 } // 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) } func cacheDecrUserQuota(userId int, delta int64) error { return cacheIncrUserQuota(userId, -delta) } // Helper functions to get individual fields if needed func getUserGroupCache(userId int) (string, error) { cache, err := GetUserCache(userId) if err != nil { return "", err } return cache.Group, nil } func getUserQuotaCache(userId int) (int, error) { cache, err := GetUserCache(userId) if err != nil { return 0, err } return cache.Quota, nil } func getUserStatusCache(userId int) (int, error) { cache, err := GetUserCache(userId) if err != nil { return 0, err } return cache.Status, nil } func getUserNameCache(userId int) (string, error) { cache, err := GetUserCache(userId) if err != nil { return "", err } return cache.Username, nil } func getUserSettingCache(userId int) (dto.UserSetting, error) { cache, err := GetUserCache(userId) if err != nil { return dto.UserSetting{}, err } return cache.GetSetting(), nil } // New functions for individual field updates func updateUserStatusCache(userId int, status bool) error { if !common.RedisEnabled { return nil } statusInt := common.UserStatusEnabled if !status { statusInt = common.UserStatusDisabled } return common.RedisHSetField(getUserCacheKey(userId), "Status", fmt.Sprintf("%d", statusInt)) } func updateUserQuotaCache(userId int, quota int) error { if !common.RedisEnabled { return nil } return common.RedisHSetField(getUserCacheKey(userId), "Quota", fmt.Sprintf("%d", quota)) } func updateUserGroupCache(userId int, group string) error { if !common.RedisEnabled { return nil } return common.RedisHSetField(getUserCacheKey(userId), "Group", group) } func updateUserNameCache(userId int, username string) error { if !common.RedisEnabled { return nil } return common.RedisHSetField(getUserCacheKey(userId), "Username", username) } func updateUserSettingCache(userId int, setting string) error { if !common.RedisEnabled { return nil } return common.RedisHSetField(getUserCacheKey(userId), "Setting", setting) }