package top.continew.admin.auth.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import me.zhyd.oauth.model.AuthUser;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.continew.admin.auth.model.req.EmailRegisterReq;
import top.continew.admin.auth.model.req.PhoneRegisterReq;
import top.continew.admin.auth.model.req.UserRegisterReq;
import top.continew.admin.auth.model.resp.RouteResp;
import top.continew.admin.auth.service.LoginService;
import top.continew.admin.common.constant.CacheConstants;
import top.continew.admin.common.constant.RegexConstants;
import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.common.util.SecureUtils;
import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.common.enums.GenderEnum;
import top.continew.admin.common.model.dto.LoginUser;
import top.continew.admin.common.model.dto.RoleDTO;
import top.continew.admin.common.util.helper.LoginHelper;
import top.continew.admin.system.enums.MenuTypeEnum;
import top.continew.admin.system.enums.MessageTemplateEnum;
import top.continew.admin.system.enums.MessageTypeEnum;
import top.continew.admin.system.enums.PasswordPolicyEnum;
import top.continew.admin.system.mapper.TaskPreDeductMapper;
import top.continew.admin.system.model.entity.DeptDO;
import top.continew.admin.system.model.entity.InviteCode;
import top.continew.admin.system.model.entity.RoleDO;
import top.continew.admin.system.model.entity.UserDO;
import top.continew.admin.system.model.entity.UserSocialDO;
import top.continew.admin.system.model.req.MessageReq;
import top.continew.admin.system.model.resp.MenuResp;
import top.continew.admin.system.service.*;
import top.continew.admin.system.mapper.InviteCodeMapper;
import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.validate.CheckUtils;
import top.continew.starter.core.util.validate.ValidationUtils;
import top.continew.starter.extension.crud.annotation.TreeField;
import top.continew.starter.extension.crud.util.TreeUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import top.continew.starter.messaging.websocket.util.WebSocketUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;

import static top.continew.admin.system.enums.PasswordPolicyEnum.PASSWORD_EXPIRATION_DAYS;

/**
 * 登录业务实现
 *
 * @author Charles7c
 * @since 2022/12/21 21:49
 */
@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {

    private final ProjectProperties projectProperties;
    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
    private final UserService userService;
    private final DeptService deptService;
    private final RoleService roleService;
    private final MenuService menuService;
    private final UserRoleService userRoleService;
    private final UserSocialService userSocialService;
    private final OptionService optionService;
    private final MessageService messageService;
    private final TaskPreDeductMapper taskPreDeductMapper;
    private final InviteCodeMapper inviteCodeMapper;

    @Override
    //@DS("#school")
    public LoginUser accountLogin(String username, String password, HttpServletRequest request) {
        //todo
        //CheckUtils.throwIf(!ContainerConstants.schools.contains(school), "学校不存在");
        UserDO user = userService.getByUsername(username);
        boolean isError = ObjectUtil.isNull(user) || !ObjectUtil.equal(password, user.getPassword());
        this.checkUserLocked(username, request, isError);
        CheckUtils.throwIf(isError, "用户名或密码错误");
        this.checkUserStatus(user);
        return this.login(user);
    }

    @Override
    public LoginUser phoneLogin(String phone) {
        UserDO user = userService.getByPhone(phone);
        CheckUtils.throwIfNull(user, "此手机号未绑定本系统账号");
        this.checkUserStatus(user);
        return this.login(user);
    }

    @Override
    public LoginUser emailLogin(String email) {
        UserDO user = userService.getByEmail(email);
        CheckUtils.throwIfNull(user, "此邮箱未绑定本系统账号");
        this.checkUserStatus(user);
        return this.login(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginUser socialLogin(AuthUser authUser) {
        String source = authUser.getSource();
        String openId = authUser.getUuid();
        UserSocialDO userSocial = userSocialService.getBySourceAndOpenId(source, openId);
        UserDO user;
        if (null == userSocial) {
            String username = authUser.getUsername();
            String nickname = authUser.getNickname();
            UserDO existsUser = userService.getByUsername(username);
            String randomStr = RandomUtil.randomString(RandomUtil.BASE_CHAR, 5);
            if (null != existsUser || !ReUtil.isMatch(RegexConstants.USERNAME, username)) {
                username = randomStr + IdUtil.fastSimpleUUID();
            }
            if (!ReUtil.isMatch(RegexConstants.GENERAL_NAME, nickname)) {
                nickname = source.toLowerCase() + randomStr;
            }
            user = new UserDO();
            user.setUsername(username);
            user.setNickname(nickname);
            user.setGender(GenderEnum.valueOf(authUser.getGender().name()));
            user.setAvatar(authUser.getAvatar());
            user.setDeptId(SysConstants.SUPER_DEPT_ID);
            Long userId = userService.add(user);
            RoleDO role = roleService.getByCode(SysConstants.ADMIN_ROLE_CODE);
            userRoleService.add(Collections.singletonList(role.getId()), userId);
            userSocial = new UserSocialDO();
            userSocial.setUserId(userId);
            userSocial.setSource(source);
            userSocial.setOpenId(openId);
            this.sendSecurityMsg(user);
        } else {
            user = BeanUtil.copyProperties(userService.getById(userSocial.getUserId()), UserDO.class);
        }
        this.checkUserStatus(user);
        userSocial.setMetaJson(JSONUtil.toJsonStr(authUser));
        userSocial.setLastLoginTime(LocalDateTime.now());
        userSocialService.saveOrUpdate(userSocial);
        return this.login(user);
    }

    @Override
    public List<RouteResp> buildRouteTree(Long userId) {
        Set<String> roleCodeSet = roleService.listCodeByUserId(userId);
        if (CollUtil.isEmpty(roleCodeSet)) {
            return new ArrayList<>(0);
        }
        // 查询菜单列表
        Set<MenuResp> menuSet = new LinkedHashSet<>();
        if (roleCodeSet.contains(SysConstants.ADMIN_ROLE_CODE)) {
            menuSet.addAll(menuService.listAll());
        } else {
            roleCodeSet.forEach(roleCode -> menuSet.addAll(menuService.listByRoleCode(roleCode)));
        }
        List<MenuResp> menuList = menuSet.stream().filter(m -> !MenuTypeEnum.BUTTON.equals(m.getType())).toList();
        // 构建路由树
        TreeField treeField = MenuResp.class.getDeclaredAnnotation(TreeField.class);
        TreeNodeConfig treeNodeConfig = TreeUtils.genTreeNodeConfig(treeField);
        List<Tree<Long>> treeList = TreeUtils.build(menuList, treeNodeConfig, (m, tree) -> {
            tree.setId(m.getId());
            tree.setParentId(m.getParentId());
            tree.setName(m.getTitle());
            tree.setWeight(m.getSort());
            tree.putExtra("type", m.getType().getValue());
            tree.putExtra("path", m.getPath());
            tree.putExtra("name", m.getName());
            tree.putExtra("component", m.getComponent());
            tree.putExtra("redirect", m.getRedirect());
            tree.putExtra("icon", m.getIcon());
            tree.putExtra("isExternal", m.getIsExternal());
            tree.putExtra("isCache", m.getIsCache());
            tree.putExtra("isHidden", m.getIsHidden());
            tree.putExtra("permission", m.getPermission());
        });
        return BeanUtil.copyToList(treeList, RouteResp.class);
    }

    /**
     * 登录并缓存用户信息
     *
     * @param user 用户信息
     * @return 令牌
     */
    private LoginUser login(UserDO user) {
        Long userId = user.getId();

        // 用户登录时重置预扣减余额为0
        try {
            taskPreDeductMapper.resetPreDeductByUserId(userId);
        } catch (Exception e) {
            // 忽略异常，确保登录流程正常
        }

        CompletableFuture<Set<String>> permissionFuture = CompletableFuture.supplyAsync(() -> roleService
            .listPermissionByUserId(userId), threadPoolTaskExecutor);
        CompletableFuture<Set<RoleDTO>> roleFuture = CompletableFuture.supplyAsync(() -> roleService
            .listByUserId(userId), threadPoolTaskExecutor);
        CompletableFuture<Integer> passwordExpirationDaysFuture = CompletableFuture.supplyAsync(() -> optionService
            .getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name()), threadPoolTaskExecutor);
        CompletableFuture.allOf(permissionFuture, roleFuture, passwordExpirationDaysFuture);
        LoginUser loginUser = new LoginUser(permissionFuture.join(), roleFuture.join(), passwordExpirationDaysFuture
            .join());

        if (loginUser.getRoleCodes() != null && !loginUser.getRoleCodes().isEmpty()) {
            for (String roleCode : loginUser.getRoleCodes()) {
                if ("teacher".equals(roleCode)) {
                    List<Long> deptIdS = deptService.listChildren(user.getDeptId())
                        .stream()
                        .map(DeptDO::getId)
                        .toList();
                    loginUser.setModelDeptIds(deptIdS);
                } else if ("student".equals(roleCode)) {
                    DeptDO dept = deptService.getById(user.getDeptId());
                    loginUser.setModelDeptIds(CollUtil.toList(dept.getParentId()));
                }
            }
        }
        BeanUtil.copyProperties(user, loginUser);
        if (user.getDeptId() != null) {
            DeptDO dept = deptService.getById(user.getDeptId());
            loginUser.setRouters(dept.getRouters());
        }
        return LoginHelper.login(loginUser);
    }

    /**
     * 检查用户状态
     *
     * @param user 用户信息
     */
    private void checkUserStatus(UserDO user) {
        CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, user.getStatus(), "此账号已被禁用，如有疑问，请联系管理员");
        DeptDO dept = deptService.getById(user.getDeptId());
        CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, dept.getStatus(), "此账号所属部门已被禁用，如有疑问，请联系管理员");
    }

    /**
     * 检测用户是否已被锁定
     *
     * @param username 用户名
     * @param request  请求对象
     * @param isError  是否登录错误
     */
    private void checkUserLocked(String username, HttpServletRequest request, boolean isError) {
        // 不锁定
        int maxErrorCount = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_COUNT.name());
        if (maxErrorCount <= SysConstants.NO) {
            return;
        }
        // 检测是否已被锁定
        String key = CacheConstants.USER_PASSWORD_ERROR_KEY_PREFIX + RedisUtils.formatKey(username, JakartaServletUtil
            .getClientIP(request));
        int lockMinutes = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_MINUTES.name());
        Integer currentErrorCount = ObjectUtil.defaultIfNull(RedisUtils.get(key), 0);
        CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "账号锁定 {} 分钟，请稍后再试", lockMinutes);
        // 登录成功清除计数
        if (!isError) {
            RedisUtils.delete(key);
            return;
        }
        // 登录失败递增计数
        currentErrorCount++;
        RedisUtils.set(key, currentErrorCount, Duration.ofMinutes(lockMinutes));
        CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "密码错误已达 {} 次，账号锁定 {} 分钟", maxErrorCount, lockMinutes);
    }

    /**
     * 发送安全消息
     *
     * @param user 用户信息
     */
    private void sendSecurityMsg(UserDO user) {
        MessageReq req = new MessageReq();
        MessageTemplateEnum socialRegister = MessageTemplateEnum.SOCIAL_REGISTER;
        req.setTitle(socialRegister.getTitle().formatted(projectProperties.getName()));
        req.setContent(socialRegister.getContent().formatted(user.getNickname()));
        req.setType(MessageTypeEnum.SECURITY);
        messageService.add(req, CollUtil.toList(user.getId()));
        List<String> tokenList = StpUtil.getTokenValueListByLoginId(user.getId());
        for (String token : tokenList) {
            WebSocketUtils.sendMessage(token, "1");
        }
    }

    /**
     * 简单登录
     *
     * @param userId 用户id
     */
    @Override
    public LoginUser simpleLogin(Long userId) {
        UserDO user = userService.getById(userId);
        this.checkUserStatus(user);
        return this.login(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginUser userRegister(UserRegisterReq registerReq, HttpServletRequest request) {
        // 验证验证码
        this.validateCaptcha(registerReq.getCaptcha(), registerReq.getUuid());

        // 解密密码
        String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq
            .getPassword()));
        ValidationUtils.throwIfBlank(rawPassword, "密码解密失败");
        String rawConfirmPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq
            .getConfirmPassword()));
        ValidationUtils.throwIfBlank(rawConfirmPassword, "确认密码解密失败");

        // 验证密码一致性
        ValidationUtils.throwIfNotEqual(rawPassword, rawConfirmPassword, "两次输入的密码不一致");

        // 验证密码强度
        ValidationUtils.throwIf(!ReUtil
            .isMatch(RegexConstants.PASSWORD, rawPassword), "密码长度为 8-32 个字符，支持大小写字母、数字、特殊字符，至少包含字母和数字");

        // 检查用户名唯一性
        CheckUtils.throwIfNotNull(userService.getByUsername(registerReq.getUsername()), "用户名 [{}] 已存在", registerReq
            .getUsername());

        // 检查邮箱唯一性（如果提供）
        if (StrUtil.isNotBlank(registerReq.getEmail())) {
            CheckUtils.throwIfNotNull(userService.getByEmail(registerReq.getEmail()), "邮箱 [{}] 已存在", registerReq
                .getEmail());
        }

        // 检查手机号唯一性（如果提供）
        if (StrUtil.isNotBlank(registerReq.getPhone())) {
            CheckUtils.throwIfNotNull(userService.getByPhone(registerReq.getPhone()), "手机号 [{}] 已存在", registerReq
                .getPhone());
        }

        // 创建用户
        UserDO user = this.createUser(registerReq.getUsername(), registerReq.getNickname(), rawPassword, registerReq
            .getGender(), registerReq.getEmail(), registerReq.getPhone(), registerReq.getDeptId());

        // 分配默认角色
        this.assignDefaultRole(user.getId());

        // 发送注册成功消息
        this.sendRegisterSuccessMsg(user);

        return this.login(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginUser phoneRegister(PhoneRegisterReq registerReq, HttpServletRequest request) {
        // 验证手机验证码
        this.validateSmsCaptcha(registerReq.getCaptcha(), registerReq.getPhone());

        // 验证邀请码
        this.validateInviteCode(registerReq.getInviteCode());

        // 解密密码
        //        String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq.getPassword()));
        //        ValidationUtils.throwIfBlank(rawPassword, "密码解密失败");
        //        String rawConfirmPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq.getConfirmPassword()));
        //        ValidationUtils.throwIfBlank(rawConfirmPassword, "确认密码解密失败");
        //
        //        // 验证密码一致性
        //        ValidationUtils.throwIfNotEqual(rawPassword, rawConfirmPassword, "两次输入的密码不一致");
        //
        //        // 验证密码强度
        //        ValidationUtils.throwIf(!ReUtil.isMatch(RegexConstants.PASSWORD, rawPassword),
        //            "密码长度为 8-32 个字符，支持大小写字母、数字、特殊字符，至少包含字母和数字");

        // 检查手机号唯一性
        CheckUtils.throwIfNotNull(userService.getByPhone(registerReq.getPhone()), "手机号 [{}] 已存在", registerReq
            .getPhone());

        // 创建用户
        UserDO user = new UserDO();
        user.setPhone(registerReq.getPhone());
        user.setUsername(registerReq.getPhone());
        user.setPassword("123456");
        user.setNickname(registerReq.getPhone());
        user.setDeptId(1L);
        user.setStatus(DisEnableStatusEnum.ENABLE);
        user.setIsSystem(false);
        user.setSysBeans(0);
        user.setPayBeans(0);
        user.setInviteBeans(0);
        user.setPreDeductBeans(0);
        Long userId = userService.add(user);
        user.setId(userId);

        // 分配默认角色
        this.assignTeacherRole(user.getId());

        // 发送注册成功消息
        this.sendRegisterSuccessMsg(user);

        // 更新邀请码状态
        if (StrUtil.isNotBlank(registerReq.getInviteCode())) {
            InviteCode existingInviteCode = inviteCodeMapper.selectOne(new LambdaQueryWrapper<InviteCode>()
                .eq(InviteCode::getInviteCode, registerReq.getInviteCode())
                .eq(InviteCode::getIsUsed, 0));
            if (existingInviteCode != null) {
                existingInviteCode.setIsUsed(1);
                existingInviteCode.setUserId(userId);
                existingInviteCode.setUsedTime(new Date());
                inviteCodeMapper.updateById(existingInviteCode);
            }
        }
        return this.login(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginUser emailRegister(EmailRegisterReq registerReq, HttpServletRequest request) {
        // 验证邮箱验证码
        this.validateEmailCaptcha(registerReq.getCaptcha(), registerReq.getEmail());

        // 解密密码
        String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq
            .getPassword()));
        ValidationUtils.throwIfBlank(rawPassword, "密码解密失败");
        String rawConfirmPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq
            .getConfirmPassword()));
        ValidationUtils.throwIfBlank(rawConfirmPassword, "确认密码解密失败");

        // 验证密码一致性
        ValidationUtils.throwIfNotEqual(rawPassword, rawConfirmPassword, "两次输入的密码不一致");

        // 验证密码强度
        ValidationUtils.throwIf(!ReUtil
            .isMatch(RegexConstants.PASSWORD, rawPassword), "密码长度为 8-32 个字符，支持大小写字母、数字、特殊字符，至少包含字母和数字");

        // 检查邮箱唯一性
        CheckUtils.throwIfNotNull(userService.getByEmail(registerReq.getEmail()), "邮箱 [{}] 已存在", registerReq
            .getEmail());

        // 生成用户名（邮箱前缀作为用户名）
        String username = StrUtil.subBefore(registerReq.getEmail(), "@", false);
        // 如果用户名已存在，添加随机后缀
        if (userService.getByUsername(username) != null) {
            username = username + RandomUtil.randomString(RandomUtil.BASE_CHAR, 5);
        }

        // 创建用户
        UserDO user = this.createUser(username, registerReq.getNickname(), rawPassword, registerReq
            .getGender(), registerReq.getEmail(), null, registerReq.getDeptId());

        // 分配默认角色
        this.assignDefaultRole(user.getId());

        // 发送注册成功消息
        this.sendRegisterSuccessMsg(user);

        return this.login(user);
    }

    /**
     * 验证邀请码
     *
     * @param inviteCode 邀请码
     */
    private void validateInviteCode(String inviteCode) {
        // 如果邀请码为空，直接返回（可选参数）
        if (StrUtil.isBlank(inviteCode)) {
            return;
        }

        // 查询邀请码是否存在
        InviteCode existingInviteCode = inviteCodeMapper.selectOne(new LambdaQueryWrapper<InviteCode>()
            .eq(InviteCode::getInviteCode, inviteCode)
            .eq(InviteCode::getIsUsed, 0));

        CheckUtils.throwIfNull(existingInviteCode, "邀请码 [{}] 不存在或已失效", inviteCode);
    }

    /**
     * 验证图片验证码
     *
     * @param captcha 验证码
     * @param uuid    验证码标识
     */
    private void validateCaptcha(String captcha, String uuid) {
        String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + uuid;
        String cachedCaptcha = RedisUtils.get(captchaKey);
        ValidationUtils.throwIfBlank(cachedCaptcha, "验证码已过期");
        RedisUtils.delete(captchaKey);
        ValidationUtils.throwIfNotEqualIgnoreCase(captcha, cachedCaptcha, "验证码错误");
    }

    /**
     * 验证短信验证码
     *
     * @param captcha 验证码
     * @param phone   手机号
     */
    private void validateSmsCaptcha(String captcha, String phone) {
        String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone;
        String cachedCaptcha = RedisUtils.get(captchaKey);
        ValidationUtils.throwIfBlank(cachedCaptcha, "验证码已过期");
        RedisUtils.delete(captchaKey);
        ValidationUtils.throwIfNotEqualIgnoreCase(captcha, cachedCaptcha, "验证码错误");
    }

    /**
     * 验证邮箱验证码
     *
     * @param captcha 验证码
     * @param email   邮箱
     */
    private void validateEmailCaptcha(String captcha, String email) {
        String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email;
        String cachedCaptcha = RedisUtils.get(captchaKey);
        ValidationUtils.throwIfBlank(cachedCaptcha, "验证码已过期");
        RedisUtils.delete(captchaKey);
        ValidationUtils.throwIfNotEqualIgnoreCase(captcha, cachedCaptcha, "验证码错误");
    }

    /**
     * 创建用户
     *
     * @param username 用户名
     * @param nickname 昵称
     * @param password 密码
     * @param gender   性别
     * @param email    邮箱
     * @param phone    手机号
     * @param deptId   部门ID
     * @return 用户信息
     */
    private UserDO createUser(String username,
                              String nickname,
                              String password,
                              GenderEnum gender,
                              String email,
                              String phone,
                              Long deptId) {
        UserDO user = new UserDO();
        user.setUsername(username);
        user.setNickname(nickname);
        user.setPassword(password);
        user.setGender(gender);
        user.setEmail(email);
        user.setPhone(phone);
        user.setDeptId(ObjectUtil.defaultIfNull(deptId, SysConstants.SUPER_DEPT_ID));
        user.setStatus(DisEnableStatusEnum.ENABLE);
        user.setIsSystem(false);
        user.setSysBeans(0);
        user.setPayBeans(0);
        user.setInviteBeans(0);
        user.setPreDeductBeans(0);

        Long userId = userService.add(user);
        user.setId(userId);
        return user;
    }

    /**
     * 分配teacher角色
     *
     * @param userId 用户ID
     */
    private void assignTeacherRole(Long userId) {
        // 查找默认角色（这里可以根据业务需求调整）
        RoleDO defaultRole = roleService.getByCode("teacher"); // 假设有一个默认的用户角色
        if (defaultRole == null) {
            // 如果没有默认角色，可以分配管理员角色或创建一个默认角色
            defaultRole = roleService.getByCode(SysConstants.ADMIN_ROLE_CODE);
        }

        if (defaultRole != null) {
            userRoleService.add(Collections.singletonList(defaultRole.getId()), userId);
        }
    }

    /**
     * 分配默认角色
     *
     * @param userId 用户ID
     */
    private void assignDefaultRole(Long userId) {
        // 查找默认角色（这里可以根据业务需求调整）
        RoleDO defaultRole = roleService.getByCode("student"); // 假设有一个默认的用户角色
        if (defaultRole == null) {
            // 如果没有默认角色，可以分配管理员角色或创建一个默认角色
            defaultRole = roleService.getByCode(SysConstants.ADMIN_ROLE_CODE);
        }

        if (defaultRole != null) {
            userRoleService.add(Collections.singletonList(defaultRole.getId()), userId);
        }
    }

    /**
     * 发送注册成功消息
     *
     * @param user 用户信息
     */
    private void sendRegisterSuccessMsg(UserDO user) {
        MessageReq req = new MessageReq();
        req.setTitle("注册成功通知");
        req.setContent("恭喜您成功注册 " + projectProperties.getName() + " 账号！用户名：" + user.getUsername());
        req.setType(MessageTypeEnum.SECURITY);
        messageService.add(req, CollUtil.toList(user.getId()));
    }
}
