package logic import ( "context" "strconv" "time" "lone-services/pkg/redis" "lone-services/pkg/utils" user "lone-services/rpc/user/pb" "lone-services/services/user/internal/config" "lone-services/services/user/internal/dao" jsoniter "github.com/json-iterator/go" ) func okResponse(data any) *user.Response { buf, _ := jsoniter.Marshal(data) return &user.Response{ Code: utils.Ok.Code, Msg: utils.Ok.Msg, Data: string(buf), } } func failResponse(status utils.Status) *user.Response { return &user.Response{ Code: status.Code, Msg: status.Msg, } } func outResponse(status utils.Status, msg string) *user.Response { return &user.Response{ Code: status.Code, Msg: msg, } } func toSession(info dao.UserRow, mobilePlain string, client *dao.Client) dao.UserLoginSession { return dao.UserLoginSession{ Id: info.Id, Name: info.Name, Avatar: info.Avatar, Mobile: mobilePlain, Gender: info.Gender, Birthday: info.Birthday.DateString(), Type: client.Type, ClientCode: client.Code, } } func sessionClaims(session dao.UserLoginSession, tokenType string) utils.Claims { return utils.Claims{ UserID: session.Id, Name: session.Name, Avatar: session.Avatar, Mobile: session.Mobile, Gender: session.Gender, Birthday: session.Birthday, Type: session.Type, ClientCode: session.ClientCode, BizId: session.BizId, TokenType: tokenType, } } func buildJwtToken(jwtAuth *config.JWTAuth, session dao.UserLoginSession) (dao.Token, error) { accessToken, err := jwtAuth.Access.Sign(sessionClaims(session, utils.TokenTypeAccess)) if err != nil { return dao.Token{}, err } refreshToken, err := jwtAuth.Refresh.Sign(sessionClaims(session, utils.TokenTypeRefresh)) if err != nil { return dao.Token{}, err } return dao.Token{ AccessToken: accessToken, RefreshToken: refreshToken, Info: session, }, nil } func loginCacheType(client *dao.Client) string { if client == nil { return utils.LoginTypeUser } switch client.SubjectType { case dao.SubjectTypeSale: return utils.LoginTypeSale case dao.SubjectTypeStore: return utils.LoginTypeStore case dao.SubjectTypeUser: return utils.LoginTypeUser } switch client.Type { case dao.TypeSale: return utils.LoginTypeSale case dao.TypeStore: return utils.LoginTypeStore default: return utils.LoginTypeUser } } func setLogin(jwtAuth *config.JWTAuth, accessToken, refreshToken string, session dao.UserLoginSession, client *dao.Client) utils.Status { accessExpire := time.Duration(jwtAuth.Access.TTL) * time.Second refreshExpire := time.Duration(jwtAuth.Refresh.TTL) * time.Second loginType := loginCacheType(client) ctx := context.Background() userStr, _ := jsoniter.Marshal(session) redisKey := utils.GetLoginKey(loginType, accessToken) if err := redis.Client.Set(ctx, redisKey, userStr, accessExpire).Err(); err != nil { return utils.Fail } redisKeysKey := utils.GetLoginKeysKey(loginType, strconv.FormatInt(session.Id, utils.NumberTen)) var loginInfo utils.LoginRedis if oldAuth, err := redis.Client.Get(ctx, redisKeysKey).Result(); err == nil { _ = jsoniter.Unmarshal([]byte(oldAuth), &loginInfo) if len(loginInfo.Token) > utils.NumberOne { redis.Client.Del(ctx, utils.GetLoginKey(loginType, loginInfo.Token)) } if len(loginInfo.Refresh) > utils.NumberOne { redis.Client.Del(ctx, utils.GetLoginRefreshKey(loginType, loginInfo.Refresh)) } } loginInfo.Token = accessToken loginInfo.Refresh = refreshToken loginInfo.Info = userStr newStr, _ := jsoniter.Marshal(loginInfo) if err := redis.Client.Set(ctx, redisKeysKey, newStr, refreshExpire).Err(); err != nil { return utils.Fail } refreshKey := utils.GetLoginRefreshKey(loginType, refreshToken) if err := redis.Client.Set(ctx, refreshKey, strconv.FormatInt(session.Id, utils.NumberTen), refreshExpire).Err(); err != nil { return utils.Fail } return utils.Ok } // tryRenewUserLogin 仅对 C 端(subject_type=user)复用已有 token 并续期; // 门店/销售不走此逻辑。成功时更新会话中的 client_code/biz_id 等字段。 func tryRenewUserLogin(jwtAuth *config.JWTAuth, session dao.UserLoginSession, client *dao.Client) (*dao.Token, bool) { if jwtAuth == nil || jwtAuth.Access == nil || jwtAuth.Refresh == nil { return nil, false } if loginCacheType(client) != utils.LoginTypeUser { return nil, false } if redis.Client == nil || session.Id < utils.NumberOne { return nil, false } ctx := context.Background() loginType := utils.LoginTypeUser infoKey := utils.GetLoginKeysKey(loginType, strconv.FormatInt(session.Id, utils.NumberTen)) oldAuth, err := redis.Client.Get(ctx, infoKey).Result() if err != nil { return nil, false } var loginInfo utils.LoginRedis if err := jsoniter.Unmarshal([]byte(oldAuth), &loginInfo); err != nil { return nil, false } if len(loginInfo.Token) <= utils.NumberOne || len(loginInfo.Refresh) <= utils.NumberOne { return nil, false } tokenKey := utils.GetLoginKey(loginType, loginInfo.Token) if _, err := redis.Client.Get(ctx, tokenKey).Result(); err != nil { return nil, false } accessExpire := time.Duration(jwtAuth.Access.TTL) * time.Second refreshExpire := time.Duration(jwtAuth.Refresh.TTL) * time.Second userStr, _ := jsoniter.Marshal(session) if err := redis.Client.Set(ctx, tokenKey, userStr, accessExpire).Err(); err != nil { return nil, false } loginInfo.Info = userStr newStr, _ := jsoniter.Marshal(loginInfo) if err := redis.Client.Set(ctx, infoKey, newStr, refreshExpire).Err(); err != nil { return nil, false } refreshKey := utils.GetLoginRefreshKey(loginType, loginInfo.Refresh) if err := redis.Client.Set(ctx, refreshKey, strconv.FormatInt(session.Id, utils.NumberTen), refreshExpire).Err(); err != nil { return nil, false } return &dao.Token{ AccessToken: loginInfo.Token, RefreshToken: loginInfo.Refresh, Info: session, }, true } func remaponUserLoginAfterMerge(jwtAuth *config.JWTAuth, fromUserId, toUserId int64, session dao.UserLoginSession) error { if jwtAuth == nil || jwtAuth.Access == nil || jwtAuth.Refresh == nil { return nil } if redis.Client == nil || fromUserId < utils.NumberOne || toUserId < utils.NumberOne { return nil } if session.Id != toUserId { session.Id = toUserId } ctx := context.Background() loginType := utils.LoginTypeUser fromInfoKey := utils.GetLoginKeysKey(loginType, strconv.FormatInt(fromUserId, utils.NumberTen)) oldAuth, err := redis.Client.Get(ctx, fromInfoKey).Result() if err != nil { return nil } var loginInfo utils.LoginRedis if err := jsoniter.Unmarshal([]byte(oldAuth), &loginInfo); err != nil { return err } if len(loginInfo.Token) <= utils.NumberOne { return nil } accessExpire := time.Duration(jwtAuth.Access.TTL) * time.Second refreshExpire := time.Duration(jwtAuth.Refresh.TTL) * time.Second userStr, _ := jsoniter.Marshal(session) tokenKey := utils.GetLoginKey(loginType, loginInfo.Token) if err := redis.Client.Set(ctx, tokenKey, userStr, accessExpire).Err(); err != nil { return err } toInfoKey := utils.GetLoginKeysKey(loginType, strconv.FormatInt(toUserId, utils.NumberTen)) if oldTo, err := redis.Client.Get(ctx, toInfoKey).Result(); err == nil { var toLogin utils.LoginRedis if uErr := jsoniter.Unmarshal([]byte(oldTo), &toLogin); uErr == nil { if len(toLogin.Token) > utils.NumberOne && toLogin.Token != loginInfo.Token { _ = redis.Client.Del(ctx, utils.GetLoginKey(loginType, toLogin.Token)).Err() } if len(toLogin.Refresh) > utils.NumberOne && toLogin.Refresh != loginInfo.Refresh { _ = redis.Client.Del(ctx, utils.GetLoginRefreshKey(loginType, toLogin.Refresh)).Err() } } } loginInfo.Info = userStr newStr, _ := jsoniter.Marshal(loginInfo) if err := redis.Client.Set(ctx, toInfoKey, newStr, refreshExpire).Err(); err != nil { return err } if fromUserId != toUserId { _ = redis.Client.Del(ctx, fromInfoKey).Err() } if len(loginInfo.Refresh) > utils.NumberOne { refreshKey := utils.GetLoginRefreshKey(loginType, loginInfo.Refresh) if err := redis.Client.Set(ctx, refreshKey, strconv.FormatInt(toUserId, utils.NumberTen), refreshExpire).Err(); err != nil { return err } } return nil }