package service import ( "context" "database/sql" "ycc-server/app/main/api/internal/config" "ycc-server/app/main/model" "ycc-server/common/ctxdata" jwtx "ycc-server/common/jwt" "ycc-server/common/xerr" "github.com/google/uuid" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/stores/sqlx" ) type UserService struct { Config *config.Config userModel model.UserModel userAuthModel model.UserAuthModel agentModel model.AgentModel } // NewUserService 创建UserService实例 func NewUserService(config *config.Config, userModel model.UserModel, userAuthModel model.UserAuthModel, agentModel model.AgentModel) *UserService { return &UserService{ Config: config, userModel: userModel, userAuthModel: userAuthModel, agentModel: agentModel, } } // GenerateUUIDUserId 生成UUID用户ID func (s *UserService) GenerateUUIDUserId(ctx context.Context) (string, error) { id := uuid.NewString() return id, nil } // RegisterUUIDUser 注册UUID用户,返回用户ID func (s *UserService) RegisterUUIDUser(ctx context.Context) (string, error) { // 生成UUID uuidStr, err := s.GenerateUUIDUserId(ctx) if err != nil { return "", err } var userId string err = s.userModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { user := &model.User{Id: uuid.NewString()} if _, userInsertErr := s.userModel.Insert(ctx, session, user); userInsertErr != nil { return userInsertErr } userId = user.Id userAuth := &model.UserAuth{Id: uuid.NewString(), UserId: userId, AuthType: model.UserAuthTypeUUID, AuthKey: uuidStr} _, userAuthInsertErr := s.userAuthModel.Insert(ctx, session, userAuth) return userAuthInsertErr }) if err != nil { return "", err } return userId, nil } // GetUserType 根据user.Mobile字段动态计算用户类型 // 如果有mobile,则为正式用户(UserTypeNormal),否则为临时用户(UserTypeTemp) func (s *UserService) GetUserType(ctx context.Context, userID string) (int64, error) { user, err := s.userModel.FindOne(ctx, userID) if err != nil { return 0, err } if user.Mobile.Valid && user.Mobile.String != "" { return model.UserTypeNormal, nil } return model.UserTypeTemp, nil } // GeneralUserToken 生成用户token(动态计算userType) func (s *UserService) GeneralUserToken(ctx context.Context, userID string) (string, error) { platform, err := ctxdata.GetPlatformFromCtx(ctx) if err != nil { return "", err } var isAgent int64 var agentID string var authType string var authKey string // 获取用户信息,根据mobile字段动态计算userType user, err := s.userModel.FindOne(ctx, userID) if err != nil { return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取用户信息失败: %v", err) } // 根据mobile判断用户类型 var userType int64 if user.Mobile.Valid && user.Mobile.String != "" { userType = model.UserTypeNormal } else { userType = model.UserTypeTemp } agent, err := s.agentModel.FindOneByUserId(ctx, userID) if err != nil && !errors.Is(err, model.ErrNotFound) { return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新token, 获取用户代理信息失败: %v", err) } if agent != nil { agentID = agent.Id isAgent = model.AgentStatusYes } platAuthType := s.getAuthTypeByPlatform(platform) ua, err := s.userAuthModel.FindOneByUserIdAuthType(ctx, userID, platAuthType) if err == nil && ua != nil { authType = ua.AuthType authKey = ua.AuthKey } token, generaErr := jwtx.GenerateJwtToken(jwtx.JwtClaims{ UserId: userID, AgentId: agentID, Platform: platform, UserType: userType, IsAgent: isAgent, AuthType: authType, AuthKey: authKey, }, s.Config.JwtAuth.AccessSecret, s.Config.JwtAuth.AccessExpire) if generaErr != nil { return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "更新token, 生成token失败 : %s", userID) } return token, nil } func (s *UserService) getAuthTypeByPlatform(platform string) string { switch platform { case model.PlatformWxMini: return model.UserAuthTypeWxMiniOpenID case model.PlatformWxH5: return model.UserAuthTypeWxh5OpenID case model.PlatformH5, model.PlatformApp: return model.UserAuthTypeUUID default: return model.UserAuthTypeUUID } } // RegisterUser 注册用户,返回用户ID // 传入手机号,自动注册,如果ctx存在临时用户则临时用户转为正式用户 func (s *UserService) RegisterUser(ctx context.Context, mobile string) (string, error) { claims, err := ctxdata.GetClaimsFromCtx(ctx) if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) { return "", err } user, err := s.userModel.FindOneByMobile(ctx, sql.NullString{String: mobile, Valid: true}) if err != nil && !errors.Is(err, model.ErrNotFound) { return "", err } if user != nil { return "", errors.New("用户已注册") } // 普通注册 if claims == nil { var userId string err = s.userModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { user := &model.User{Id: uuid.NewString(), Mobile: sql.NullString{String: mobile, Valid: true}} if _, userInsertErr := s.userModel.Insert(ctx, session, user); userInsertErr != nil { return userInsertErr } userId = user.Id _, userAuthInsertErr := s.userAuthModel.Insert(ctx, session, &model.UserAuth{Id: uuid.NewString(), UserId: userId, AuthType: model.UserAuthTypeMobile, AuthKey: mobile}) if userAuthInsertErr != nil { return userAuthInsertErr } return nil }) if err != nil { return "", err } return userId, nil } // 双重判断是否已经注册(根据mobile判断,而不是userType) currentUser, err := s.userModel.FindOne(ctx, claims.UserId) if err != nil && !errors.Is(err, model.ErrNotFound) { return "", err } if currentUser != nil && currentUser.Mobile.Valid && currentUser.Mobile.String != "" { return "", errors.New("用户已注册") } var userId string // 临时用户绑定mobile转正式注册 err = s.userModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { user := &model.User{Id: uuid.NewString(), Mobile: sql.NullString{String: mobile, Valid: true}} if _, userInsertErr := s.userModel.Insert(ctx, session, user); userInsertErr != nil { return userInsertErr } userId = user.Id _, userAuthInsertErr := s.userAuthModel.Insert(ctx, session, &model.UserAuth{Id: uuid.NewString(), UserId: userId, AuthType: model.UserAuthTypeMobile, AuthKey: mobile}) if userAuthInsertErr != nil { return userAuthInsertErr } tempUserBindErr := s.TempUserBindUser(ctx, session, userId) if tempUserBindErr != nil { return tempUserBindErr } return nil }) if err != nil { return "", err } return userId, nil } // TempUserBindUser 临时用户绑定用户(添加mobile使其变为正式用户) func (s *UserService) TempUserBindUser(ctx context.Context, session sqlx.Session, normalUserID string) error { claims, err := ctxdata.GetClaimsFromCtx(ctx) if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) { return err } if claims == nil { return errors.New("无临时用户") } // 检查当前用户是否已经绑定了mobile(根据mobile判断,而不是userType) tempUser, err := s.userModel.FindOne(ctx, claims.UserId) if err != nil && !errors.Is(err, model.ErrNotFound) { return err } if tempUser != nil && tempUser.Mobile.Valid && tempUser.Mobile.String != "" { return errors.New("临时用户已注册") } existingAuth, err := s.userAuthModel.FindOneByAuthTypeAuthKey(ctx, claims.AuthType, claims.AuthKey) if err != nil && !errors.Is(err, model.ErrNotFound) { return err } if existingAuth != nil { return errors.New("临时用户已注册") } if session == nil { err := s.userAuthModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { _, err = s.userAuthModel.Insert(ctx, session, &model.UserAuth{Id: uuid.NewString(), UserId: normalUserID, AuthType: claims.AuthType, AuthKey: claims.AuthKey}) if err != nil { return err } return nil }) if err != nil { return err } return nil } else { _, err = s.userAuthModel.Insert(ctx, session, &model.UserAuth{Id: uuid.NewString(), UserId: normalUserID, AuthType: claims.AuthType, AuthKey: claims.AuthKey}) if err != nil { return err } return nil } }