f
This commit is contained in:
@@ -38,15 +38,23 @@ func NewRegisterByInviteCodeLogic(ctx context.Context, svcCtx *svc.ServiceContex
|
||||
}
|
||||
}
|
||||
|
||||
// truncateAuthKey 截断 authKey 用于日志,避免敏感信息过长
|
||||
func truncateAuthKey(s string) string {
|
||||
if len(s) <= 12 {
|
||||
return s
|
||||
}
|
||||
return s[:6] + "..." + s[len(s)-4:]
|
||||
}
|
||||
|
||||
func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByInviteCodeReq) (resp *types.RegisterByInviteCodeResp, err error) {
|
||||
l.Infof("[RegisterByInviteCode] 开始处理代理注册请求, mobile: %s, referrer: %s", req.Mobile, req.Referrer)
|
||||
l.Infof("[RegisterByInviteCode] 开始处理代理注册请求 | mobile: %s, referrer: %s, code: %s", req.Mobile, req.Referrer, req.Code)
|
||||
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
|
||||
}
|
||||
l.Infof("[RegisterByInviteCode] 手机号加密完成, encryptedMobile: %s", encryptedMobile)
|
||||
l.Infof("[RegisterByInviteCode] 手机号加密完成 | mobile: %s, encryptedMobile: %s", req.Mobile, encryptedMobile)
|
||||
|
||||
// 校验验证码(开发环境下跳过验证码校验)
|
||||
if os.Getenv("ENV") != "development" && req.Code != "143838" {
|
||||
@@ -76,9 +84,9 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败: %v", err)
|
||||
}
|
||||
if claims != nil {
|
||||
l.Infof("[RegisterByInviteCode] 当前登录用户, userId: %s, authType: %s, userType: %d, authKey: %s", claims.UserId, claims.AuthType, claims.UserType, claims.AuthKey)
|
||||
l.Infof("[RegisterByInviteCode] 当前登录态 | userId: %s, authType: %s, userType: %d, authKeyPrefix: %s", claims.UserId, claims.AuthType, claims.UserType, truncateAuthKey(claims.AuthKey))
|
||||
} else {
|
||||
l.Infof("[RegisterByInviteCode] 未登录状态(claims为nil,将按未登录流程处理)")
|
||||
l.Infof("[RegisterByInviteCode] 未登录状态(claims 为 nil,将按未登录流程处理)")
|
||||
}
|
||||
|
||||
// 前置检查:如果当前用户是正式用户(有手机号),进行拦截检查
|
||||
@@ -88,24 +96,25 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败: %v", err)
|
||||
}
|
||||
if currentUser != nil && currentUser.Mobile.Valid && currentUser.Mobile.String != "" {
|
||||
l.Infof("[RegisterByInviteCode] 当前用户是正式用户, userId: %s, mobile: %s", claims.UserId, currentUser.Mobile.String)
|
||||
l.Infof("[RegisterByInviteCode] 前置检查-正式用户 | userId: %s, user.mobile: %s, 请求加密手机号: %s, 匹配: %v", claims.UserId, currentUser.Mobile.String, encryptedMobile, currentUser.Mobile.String == encryptedMobile)
|
||||
// 当前用户是正式用户,检查是否已是代理
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, claims.UserId)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理失败: %v", err)
|
||||
}
|
||||
if agent != nil {
|
||||
l.Infof("[RegisterByInviteCode] 用户已是代理, userId: %s, agentId: %s", claims.UserId, agent.Id)
|
||||
l.Infof("[RegisterByInviteCode] 前置检查-已是代理拒绝 | userId: %s, agentId: %s", claims.UserId, agent.Id)
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您已经是代理,不能重复注册"), "")
|
||||
}
|
||||
// 正式用户手机号必须匹配
|
||||
if currentUser.Mobile.String != encryptedMobile {
|
||||
l.Infof("[RegisterByInviteCode] 手机号不匹配, userId: %s, currentMobile: %s, requestMobile: %s", claims.UserId, currentUser.Mobile.String, encryptedMobile)
|
||||
l.Infof("[RegisterByInviteCode] 前置检查-手机号不匹配拒绝 | userId: %s, currentMobile: %s, requestEncrypted: %s", claims.UserId, currentUser.Mobile.String, encryptedMobile)
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("请输入当前账号的手机号码"), "")
|
||||
}
|
||||
l.Infof("[RegisterByInviteCode] 前置检查通过, 正式用户手机号匹配")
|
||||
l.Infof("[RegisterByInviteCode] 前置检查通过 | 正式用户且手机号匹配,非代理")
|
||||
} else {
|
||||
l.Infof("[RegisterByInviteCode] 当前用户是临时用户或无手机号, userId: %s", claims.UserId)
|
||||
hasMobile := currentUser != nil && currentUser.Mobile.Valid && currentUser.Mobile.String != ""
|
||||
l.Infof("[RegisterByInviteCode] 前置检查-临时用户或无手机号 | userId: %s, userHasMobile: %v", claims.UserId, hasMobile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,16 +150,16 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
|
||||
l.Infof("[RegisterByInviteCode] 开始事务处理")
|
||||
err = l.svcCtx.AgentInviteCodeModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 1. 查找目标用户(通过手机号)
|
||||
// 1. 查找目标用户(通过手机号,可能命中缓存)
|
||||
targetUser, findUserErr := l.svcCtx.UserModel.FindOneByMobile(transCtx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && !errors.Is(findUserErr, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败, %v", findUserErr)
|
||||
}
|
||||
|
||||
if targetUser != nil {
|
||||
l.Infof("[RegisterByInviteCode] 找到目标用户, userId: %s", targetUser.Id)
|
||||
l.Infof("[RegisterByInviteCode] FindOneByMobile 命中 | targetUserId: %s, encryptedMobile: %s | 走场景2(手机号已存在)", targetUser.Id, encryptedMobile)
|
||||
} else {
|
||||
l.Infof("[RegisterByInviteCode] 目标用户不存在, 将创建新用户")
|
||||
l.Infof("[RegisterByInviteCode] FindOneByMobile 未命中 | targetUser=nil, encryptedMobile: %s | 走场景1(手机号不存在)。若用户刚执行过 bindMobile 仍为 nil,疑为 FindOneByMobile 缓存未失效", encryptedMobile)
|
||||
}
|
||||
|
||||
// 2. 获取当前登录态信息
|
||||
@@ -168,21 +177,21 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
|
||||
// 3. 根据目标用户是否存在,处理用户和认证
|
||||
if targetUser == nil {
|
||||
// 场景1: 手机号不存在
|
||||
l.Infof("[RegisterByInviteCode] 场景1: 手机号不存在, currentUserID: %s", currentUserID)
|
||||
l.Infof("[RegisterByInviteCode] 分支: 场景1-手机号不存在 | currentUserID: %s, encryptedMobile: %s", currentUserID, encryptedMobile)
|
||||
userID, err = l.handleMobileNotExists(transCtx, session, encryptedMobile, currentUserID)
|
||||
if err != nil {
|
||||
l.Errorf("[RegisterByInviteCode] 场景1失败 | currentUserID: %s, encryptedMobile: %s, err: %v", currentUserID, encryptedMobile, err)
|
||||
return err
|
||||
}
|
||||
l.Infof("[RegisterByInviteCode] 场景1处理完成, userID: %s", userID)
|
||||
l.Infof("[RegisterByInviteCode] 场景1完成 | userID: %s", userID)
|
||||
} else {
|
||||
// 场景2: 手机号已存在
|
||||
l.Infof("[RegisterByInviteCode] 场景2: 手机号已存在, targetUserId: %s, currentUserID: %s", targetUser.Id, currentUserID)
|
||||
l.Infof("[RegisterByInviteCode] 分支: 场景2-手机号已存在 | targetUserId: %s, currentUserID: %s, sameUser: %v", targetUser.Id, currentUserID, targetUser.Id == currentUserID)
|
||||
userID, err = l.handleMobileExists(transCtx, session, targetUser, currentUserID, currentAuthType, currentAuthKey)
|
||||
if err != nil {
|
||||
l.Errorf("[RegisterByInviteCode] 场景2失败 | targetUserId: %s, currentUserID: %s, err: %v", targetUser.Id, currentUserID, err)
|
||||
return err
|
||||
}
|
||||
l.Infof("[RegisterByInviteCode] 场景2处理完成, userID: %s", userID)
|
||||
l.Infof("[RegisterByInviteCode] 场景2完成 | userID: %s", userID)
|
||||
}
|
||||
// 4. 处理邀请码和上级关系
|
||||
var targetLevel int64
|
||||
@@ -342,7 +351,7 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorf("[RegisterByInviteCode] 事务处理失败: %v", err)
|
||||
l.Errorf("[RegisterByInviteCode] 事务失败 | mobile: %s, referrer: %s, err: %v | 排查可看上文 FindOneByMobile 是否命中、场景1/2 及 handleMobileNotExists/Exists 日志", req.Mobile, req.Referrer, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -464,82 +473,74 @@ func (l *RegisterByInviteCodeLogic) allocateAgentCode(ctx context.Context, sessi
|
||||
// handleMobileNotExists 处理手机号不存在的情况
|
||||
func (l *RegisterByInviteCodeLogic) handleMobileNotExists(ctx context.Context, session sqlx.Session, encryptedMobile string, currentUserID string) (string, error) {
|
||||
if currentUserID == "" {
|
||||
// 场景1.1: 未登录 + 手机号不存在 -> 创建新用户
|
||||
l.Infof("[handleMobileNotExists] 场景1.1: 未登录+手机号不存在, 创建新用户")
|
||||
l.Infof("[handleMobileNotExists] 子分支 1.1: 未登录+手机号不存在 | 创建新用户, encryptedMobile: %s", encryptedMobile)
|
||||
newUser := &model.User{Id: uuid.NewString(), Mobile: sql.NullString{String: encryptedMobile, Valid: true}}
|
||||
if _, err := l.svcCtx.UserModel.Insert(ctx, session, newUser); err != nil {
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户失败: %v", err)
|
||||
}
|
||||
l.Infof("[handleMobileNotExists] 用户创建成功, userId: %s", newUser.Id)
|
||||
// 创建 mobile 认证
|
||||
l.Infof("[handleMobileNotExists] 1.1 用户已创建 | userId: %s", newUser.Id)
|
||||
l.Infof("[handleMobileNotExists] 1.1 即将插入 user_auth(mobile) | userId: %s, authKey: %s", newUser.Id, encryptedMobile)
|
||||
if _, err := l.svcCtx.UserAuthModel.Insert(ctx, session, &model.UserAuth{
|
||||
Id: uuid.NewString(),
|
||||
UserId: newUser.Id,
|
||||
AuthType: model.UserAuthTypeMobile,
|
||||
AuthKey: encryptedMobile,
|
||||
}); err != nil {
|
||||
l.Errorf("[handleMobileNotExists] 1.1 插入 user_auth 失败(可能 Duplicate) | userId: %s, err: %v", newUser.Id, err)
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建手机号认证失败: %v", err)
|
||||
}
|
||||
l.Infof("[handleMobileNotExists] 手机号认证创建成功, userId: %s", newUser.Id)
|
||||
l.Infof("[handleMobileNotExists] 1.1 完成 | userId: %s", newUser.Id)
|
||||
return newUser.Id, nil
|
||||
} else {
|
||||
// 场景1.2: 已登录临时用户 + 手机号不存在 -> 升级为正式用户
|
||||
// 前置检查已保证不是正式用户,所以这里一定是临时用户
|
||||
l.Infof("[handleMobileNotExists] 场景1.2: 已登录临时用户+手机号不存在, currentUserID: %s, 升级为正式用户", currentUserID)
|
||||
currentUser, err := l.svcCtx.UserModel.FindOne(ctx, currentUserID)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询当前用户失败: %v", err)
|
||||
}
|
||||
// 升级为正式用户
|
||||
currentUser.Mobile = sql.NullString{String: encryptedMobile, Valid: true}
|
||||
if _, err := l.svcCtx.UserModel.Update(ctx, session, currentUser); err != nil {
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新手机号失败: %v", err)
|
||||
}
|
||||
l.Infof("[handleMobileNotExists] 用户升级为正式用户成功, userId: %s", currentUserID)
|
||||
// 创建 mobile 认证
|
||||
if _, err := l.svcCtx.UserAuthModel.Insert(ctx, session, &model.UserAuth{
|
||||
Id: uuid.NewString(),
|
||||
UserId: currentUserID,
|
||||
AuthType: model.UserAuthTypeMobile,
|
||||
AuthKey: encryptedMobile,
|
||||
}); err != nil {
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建手机号认证失败: %v", err)
|
||||
}
|
||||
l.Infof("[handleMobileNotExists] 手机号认证创建成功, userId: %s", currentUserID)
|
||||
return currentUserID, nil
|
||||
}
|
||||
// 场景1.2: 已登录临时用户 + 手机号不存在 -> 升级为正式用户
|
||||
l.Infof("[handleMobileNotExists] 子分支 1.2: 已登录+手机号不存在 | currentUserID: %s, encryptedMobile: %s, 将更新 user.mobile 并插入 user_auth", currentUserID, encryptedMobile)
|
||||
currentUser, err := l.svcCtx.UserModel.FindOne(ctx, currentUserID)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询当前用户失败: %v", err)
|
||||
}
|
||||
currentUser.Mobile = sql.NullString{String: encryptedMobile, Valid: true}
|
||||
if _, err := l.svcCtx.UserModel.Update(ctx, session, currentUser); err != nil {
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新手机号失败: %v", err)
|
||||
}
|
||||
l.Infof("[handleMobileNotExists] 1.2 user.mobile 已更新 | userId: %s", currentUserID)
|
||||
l.Infof("[handleMobileNotExists] 1.2 即将插入 user_auth(mobile) | userId: %s, authKey: %s | 若报 Duplicate 说明该手机号已绑定,疑缓存导致误走场景1", currentUserID, encryptedMobile)
|
||||
if _, err := l.svcCtx.UserAuthModel.Insert(ctx, session, &model.UserAuth{
|
||||
Id: uuid.NewString(),
|
||||
UserId: currentUserID,
|
||||
AuthType: model.UserAuthTypeMobile,
|
||||
AuthKey: encryptedMobile,
|
||||
}); err != nil {
|
||||
l.Errorf("[handleMobileNotExists] 1.2 插入 user_auth 失败 | userId: %s, err: %v | 常见为 Duplicate entry 'mobile-xxx',多为 FindOneByMobile 缓存未失效", currentUserID, err)
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建手机号认证失败: %v", err)
|
||||
}
|
||||
l.Infof("[handleMobileNotExists] 1.2 完成 | userId: %s", currentUserID)
|
||||
return currentUserID, nil
|
||||
}
|
||||
|
||||
// handleMobileExists 处理手机号已存在的情况
|
||||
func (l *RegisterByInviteCodeLogic) handleMobileExists(ctx context.Context, session sqlx.Session, targetUser *model.User, currentUserID string, currentAuthType string, currentAuthKey string) (string, error) {
|
||||
userID := targetUser.Id
|
||||
l.Infof("[handleMobileExists] 开始处理, targetUserId: %s, currentUserID: %s", userID, currentUserID)
|
||||
l.Infof("[handleMobileExists] 入口 | targetUserId: %s, currentUserID: %s, authType: %s", userID, currentUserID, currentAuthType)
|
||||
|
||||
// 检查目标用户是否已是代理
|
||||
existingAgent, err := l.svcCtx.AgentModel.FindOneByUserId(ctx, userID)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
|
||||
}
|
||||
if existingAgent != nil {
|
||||
l.Infof("[handleMobileExists] 目标用户已是代理, userId: %s, agentId: %s", userID, existingAgent.Id)
|
||||
l.Infof("[handleMobileExists] 目标已是代理拒绝 | userId: %s, agentId: %s", userID, existingAgent.Id)
|
||||
return "", errors.Wrapf(xerr.NewErrMsg("该手机号已经是代理,不能重复注册"), "")
|
||||
}
|
||||
|
||||
if currentUserID == "" {
|
||||
// 场景2.1: 未登录 + 手机号存在 -> 直接使用目标用户(验证码已确认身份)
|
||||
l.Infof("[handleMobileExists] 场景2.1: 未登录+手机号存在, 直接使用目标用户, userId: %s", userID)
|
||||
l.Infof("[handleMobileExists] 子分支 2.1: 未登录+手机号存在 | 直接使用 targetUserId: %s", userID)
|
||||
return userID, nil
|
||||
} else if currentUserID == userID {
|
||||
// 场景2.2: 已登录正式用户 + 手机号匹配 -> 直接使用
|
||||
// 前置检查已保证手机号匹配且不是代理
|
||||
l.Infof("[handleMobileExists] 场景2.2: 已登录正式用户+手机号匹配, userId: %s", userID)
|
||||
return userID, nil
|
||||
} else {
|
||||
// 场景2.3: 已登录临时用户 + 手机号存在 -> 需要合并账号
|
||||
// 前置检查已保证是临时用户(不是正式用户)
|
||||
l.Infof("[handleMobileExists] 场景2.3: 已登录临时用户+手机号存在, 需要合并账号, sourceUserId: %s, targetUserId: %s, authType: %s", currentUserID, userID, currentAuthType)
|
||||
return l.mergeTempUserToTarget(ctx, session, currentUserID, userID, currentAuthType, currentAuthKey)
|
||||
}
|
||||
if currentUserID == userID {
|
||||
l.Infof("[handleMobileExists] 子分支 2.2: 已登录且与目标同一用户 | userId: %s", userID)
|
||||
return userID, nil
|
||||
}
|
||||
l.Infof("[handleMobileExists] 子分支 2.3: 已登录临时用户+手机号属他人 | 合并 source: %s -> target: %s, authType: %s", currentUserID, userID, currentAuthType)
|
||||
return l.mergeTempUserToTarget(ctx, session, currentUserID, userID, currentAuthType, currentAuthKey)
|
||||
}
|
||||
|
||||
// mergeTempUserToTarget 合并临时用户到目标用户
|
||||
|
||||
Reference in New Issue
Block a user