From 764ad2f6847dfdd095352f955d6665b3bf4945ca Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Sat, 28 Feb 2026 18:01:59 +0800 Subject: [PATCH] f --- .../logic/agent/applyforagentlogic.go | 95 +++++++++++-------- .../logic/user/mobilecodeloginlogic.go | 53 +++++++---- 2 files changed, 90 insertions(+), 58 deletions(-) diff --git a/app/main/api/internal/logic/agent/applyforagentlogic.go b/app/main/api/internal/logic/agent/applyforagentlogic.go index 7c23a9e..1cb64e9 100644 --- a/app/main/api/internal/logic/agent/applyforagentlogic.go +++ b/app/main/api/internal/logic/agent/applyforagentlogic.go @@ -69,58 +69,77 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type } } + // 先处理用户注册/绑定逻辑(在事务外) var userID string - transErr := l.svcCtx.AgentModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error { - // 1. 处理用户注册/绑定 + + // 最多重试3次,每次间隔50ms + maxRetries := 3 + for i := 0; i < maxRetries; i++ { user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true}) if findUserErr != nil && !errors.Is(findUserErr, model.ErrNotFound) { - return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败, %v", findUserErr) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败, %v", findUserErr) } - - if user == nil { - // 用户不存在,注册新用户 - if claims != nil && claims.UserType == model.UserTypeNormal { - return errors.Wrapf(xerr.NewErrMsg("当前用户已注册,请输入注册的手机号"), "") - } - userID, err = l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile) - if err != nil { - // 检查是否是并发注册导致的唯一键冲突 - // MySQL错误码: 1062 (23000) - Duplicate entry - errStr := err.Error() - if strings.Contains(errStr, "Duplicate entry") && strings.Contains(errStr, "user_auth.unique_type_key") { - // 并发冲突,重新查询用户 - l.Infof("申请代理, 检测到并发注册冲突,重新查询用户: %s", encryptedMobile) - user, retryErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true}) - if retryErr != nil { - return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "申请代理, 并发冲突后重新查询用户失败: %v", retryErr) - } - if user == nil { - return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "申请代理, 并发冲突后仍未找到用户") - } - userID = user.Id - l.Infof("申请代理, 并发冲突后获取到已注册用户, userId: %s, mobile: %s", userID, encryptedMobile) - } else { - return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "注册用户失败: %v", err) - } - } - } else { + + if user != nil { // 用户已存在 if claims != nil && claims.UserType == model.UserTypeTemp { // 临时用户,检查手机号是否已绑定其他微信号 userAuth, findUserAuthErr := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, user.Id, claims.AuthType) if findUserAuthErr != nil && !errors.Is(findUserAuthErr, model.ErrNotFound) { - return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户认证失败, %v", findUserAuthErr) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户认证失败, %v", findUserAuthErr) } if userAuth != nil && userAuth.AuthKey != claims.AuthKey { - return errors.Wrapf(xerr.NewErrMsg("该手机号已绑定其他微信号"), "") - } - // 临时用户,转为正式用户 - err = l.svcCtx.UserService.TempUserBindUser(l.ctx, session, user.Id) - if err != nil { - return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定用户失败: %v", err) + return nil, errors.Wrapf(xerr.NewErrMsg("该手机号已绑定其他微信号"), "") } } userID = user.Id + break + } + + // 用户不存在,注册新用户 + if claims != nil && claims.UserType == model.UserTypeNormal { + return nil, errors.Wrapf(xerr.NewErrMsg("当前用户已注册,请输入注册的手机号"), "") + } + l.Infof("申请代理, 用户不存在,尝试注册新用户 (尝试 %d/%d): %s", i+1, maxRetries, encryptedMobile) + registeredUserID, registerErr := l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile) + if registerErr != nil { + // 检查是否是并发注册导致的唯一键冲突 + errStr := registerErr.Error() + if strings.Contains(errStr, "Duplicate entry") && strings.Contains(errStr, "user_auth.unique_type_key") { + // 并发冲突,等待一小段时间后重试 + l.Infof("申请代理, 检测到并发注册冲突,等待后重试: %s", encryptedMobile) + if i < maxRetries-1 { + time.Sleep(50 * time.Millisecond) + continue + } + // 最后一次重试仍然失败,尝试最后一次查询 + user, lastRetryErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true}) + if lastRetryErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "申请代理, 并发冲突后重试查询用户失败: %v", lastRetryErr) + } + if user == nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "申请代理, 重试多次后仍未找到用户") + } + userID = user.Id + l.Infof("申请代理, 并发冲突重试后获取到已注册用户, userId: %s, mobile: %s", userID, encryptedMobile) + } else { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "注册用户失败: %v", registerErr) + } + } else { + userID = registeredUserID + l.Infof("申请代理, 注册新用户成功, userId: %s, mobile: %s", userID, encryptedMobile) + break + } + } + + // 处理临时用户绑定(在事务内) + transErr := l.svcCtx.AgentModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error { + // 如果是临时用户,需要绑定到正式用户 + if claims != nil && claims.UserType == model.UserTypeTemp { + err = l.svcCtx.UserService.TempUserBindUser(l.ctx, session, userID) + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定用户失败: %v", err) + } } // 3. 检查是否已是代理 diff --git a/app/main/api/internal/logic/user/mobilecodeloginlogic.go b/app/main/api/internal/logic/user/mobilecodeloginlogic.go index a8d3aab..3e7e9dd 100644 --- a/app/main/api/internal/logic/user/mobilecodeloginlogic.go +++ b/app/main/api/internal/logic/user/mobilecodeloginlogic.go @@ -56,42 +56,55 @@ func (l *MobileCodeLoginLogic) MobileCodeLogin(req *types.MobileCodeLoginReq) (r } } var userID string - user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true}) - if findUserErr != nil && findUserErr != model.ErrNotFound { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 读取数据库获取用户失败, mobile: %s, err: %+v", encryptedMobile, err) - } - if user == nil { - // 用户不存在,自动注册新用户 - l.Infof("手机登录, 用户不存在,自动注册新用户: %s", encryptedMobile) + + // 最多重试3次,每次间隔50ms + maxRetries := 3 + for i := 0; i < maxRetries; i++ { + user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true}) + if findUserErr != nil && findUserErr != model.ErrNotFound { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 读取数据库获取用户失败, mobile: %s, err: %+v", encryptedMobile, err) + } + + if user != nil { + // 用户存在 + if user.Disable == 1 { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_DISABLED), "用户已被封禁") + } + userID = user.Id + break + } + + // 用户不存在,尝试注册 + l.Infof("手机登录, 用户不存在,尝试注册新用户 (尝试 %d/%d): %s", i+1, maxRetries, encryptedMobile) registeredUserID, registerErr := l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile) if registerErr != nil { // 检查是否是并发注册导致的唯一键冲突 - // MySQL错误码: 1062 (23000) - Duplicate entry errStr := registerErr.Error() if strings.Contains(errStr, "Duplicate entry") && strings.Contains(errStr, "user_auth.unique_type_key") { - // 并发冲突,重新查询用户 - l.Infof("手机登录, 检测到并发注册冲突,重新查询用户: %s", encryptedMobile) - user, retryErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true}) - if retryErr != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 并发冲突后重新查询用户失败, mobile: %s, err: %+v", encryptedMobile, retryErr) + // 并发冲突,等待一小段时间后重试 + l.Infof("手机登录, 检测到并发注册冲突,等待后重试: %s", encryptedMobile) + if i < maxRetries-1 { + time.Sleep(50 * time.Millisecond) + continue + } + // 最后一次重试仍然失败,尝试最后一次查询 + user, lastRetryErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true}) + if lastRetryErr != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 并发冲突后重试查询用户失败, mobile: %s, err: %+v", encryptedMobile, lastRetryErr) } if user == nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 并发冲突后仍未找到用户, mobile: %s", encryptedMobile) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 重试多次后仍未找到用户, mobile: %s", encryptedMobile) } userID = user.Id - l.Infof("手机登录, 并发冲突后获取到已注册用户, userId: %s, mobile: %s", userID, encryptedMobile) + l.Infof("手机登录, 并发冲突重试后获取到已注册用户, userId: %s, mobile: %s", userID, encryptedMobile) } else { return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 自动注册用户失败, mobile: %s, err: %+v", encryptedMobile, registerErr) } } else { userID = registeredUserID l.Infof("手机登录, 自动注册用户成功, userId: %s, mobile: %s", userID, encryptedMobile) + break } - } else { - if user.Disable == 1 { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_DISABLED), "用户已被封禁") - } - userID = user.Id } token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID) if err != nil {