Files
ycc-proxy-server/app/main/api/internal/service/userService.go
2025-12-10 13:56:45 +08:00

259 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}