云
5 天以前 d0cf2cb2c352bdeb146aad2bb57ba7d39cef3bbd
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
@@ -40,6 +40,7 @@
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@@ -101,6 +102,10 @@
    private RoleService roleService;
    @Resource
    private PermissionService permissionService;
    @Autowired(required = false)
    private WxcpMembershipRegistration wxcpMembershipRegistration;
    @Resource
    private WxcpPasswordSetupGrant wxcpPasswordSetupGrant;
    /**
     * 验证码的开关,默认为 true
@@ -118,6 +123,7 @@
            createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
        }
        if (!userService.isPasswordMatch(password, user.getPassword())) {
            createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
@@ -141,6 +147,14 @@
        // 如果 socialType 非空,说明需要绑定社交用户
        if (reqVO.getSocialType() != null) {
            if (WECHAT_ENTERPRISE_TYPE.equals(reqVO.getSocialType())) {
                SocialUserRespDTO social = socialUserService.getSocialUserByCode(getUserType().getValue(),
                        reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState());
                if (wxcpMembershipRegistration == null || social == null
                        || wxcpMembershipRegistration.hasPendingApplication(social.getOpenid())) {
                    throw exception(AUTH_WXCP_MEMBERSHIP_PENDING);
                }
            }
            socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
                    reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
        }
@@ -219,27 +233,43 @@
        }
        // 2. 判断是否已有绑定的系统用户
        //
        // 注意:system_social_user_bind 里的 user_id 可能指向「已被删除」的用户 ——
        // AdminUserServiceImpl.deleteUser 只清理了角色和岗位,没有清理这张社交绑定表,
        // 删除后绑定关系会残留成孤儿。所以这里不能只看「有没有绑定」,
        // 还要看「绑定的人还在不在」。
        Long bindUserId = socialUser.getUserId();
        AdminUserDO user;
        AdminUserDO user = bindUserId != null ? userService.getUser(bindUserId) : null;
        if (bindUserId == null) {
            // 2.1 首次登录:自动注册新用户
        if (user == null) {
            if (WECHAT_ENTERPRISE_TYPE.equals(reqVO.getType())) {
                if (wxcpMembershipRegistration == null) throw new IllegalStateException("会员注册服务不可用");
                wxcpMembershipRegistration.startIfAbsent(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getMobile());
                return null; // 提交事务后由 Controller 返回待审核提示,不签发令牌
            }
            // 2.1 首次登录,或原绑定用户已被删除:自动注册新用户并(改)绑定
            //
            // 原绑定用户已删除时必须走这条路:否则该企业成员会永久卡在「用户不存在」——
            // 既登不进来,也不会被重新注册,是一个无法自愈的死锁。
            // bindSocialUser 内部会先删掉该社交用户的旧绑定,所以改绑不会产生重复记录。
            if (bindUserId != null) {
                log.warn("[socialLogin][社交用户({}) 原绑定的用户({}) 已不存在(可能已被删除),按首次登录重新注册]",
                        socialUser.getOpenid(), bindUserId);
            }
            user = autoRegisterUser(socialUser, reqVO.getType());
            // 绑定社交账号
            socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
                    reqVO.getType(), reqVO.getCode(), reqVO.getState()));
            log.info("[socialLogin][社交登录自动注册 userId={} username={} socialType={}]",
                    user.getId(), user.getUsername(), reqVO.getType());
        } else {
            // 2.2 已有绑定:直接获取用户
            user = userService.getUser(bindUserId);
            if (user == null) {
                throw exception(USER_NOT_EXISTS);
            }
        }
        if (CommonStatusEnum.isDisable(user.getStatus())) throw exception(AUTH_LOGIN_USER_DISABLED);
        // 3. 创建 Token 令牌,记录登录日志
        AuthLoginRespVO result = createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
        if (WECHAT_ENTERPRISE_TYPE.equals(reqVO.getType())) {
            wxcpPasswordSetupGrant.issue(result.getAccessToken(), user.getId());
        }
        // 4. 发布社交登录事件(供其他模块监听,如发送企业微信通知)
        eventPublisher.publishEvent(new SocialLoginEvent(
@@ -428,4 +458,4 @@
        userService.updateUserPassword(userByMobile.getId(), reqVO.getPassword());
    }
}
}