package com.backendsys.modules.system.service.impl; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; import com.backendsys.exception.CustException; import com.backendsys.modules.common.config.redis.utils.RedisUtil; import com.backendsys.modules.common.config.security.utils.SecurityUtil; import com.backendsys.modules.common.config.security.utils.TokenUtil; import com.backendsys.modules.common.utils.MybatisUtil; import com.backendsys.modules.sse.entity.SseResponse; import com.backendsys.modules.sse.entity.SseResponseEnum; import com.backendsys.modules.sse.utils.SseUtil; import com.backendsys.modules.system.dao.*; import com.backendsys.modules.system.entity.*; import com.backendsys.modules.system.service.SysUserIntegralService; import com.backendsys.modules.system.service.SysUserRoleMenuService; import com.backendsys.modules.system.service.SysUserService; import com.backendsys.utils.MD5Util; import com.backendsys.utils.response.PageEntity; import com.backendsys.utils.response.PageInfoResult; import com.backendsys.utils.v2.PageUtils; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Service public class SysUserServiceImpl extends ServiceImpl implements SysUserService { @Value("${REDIS_LOGIN_TOKEN_PREFIX}") private String REDIS_LOGIN_TOKEN_PREFIX; @Value("${spring.application.name}") private String APPLICATION_NAME; @Autowired private SseUtil sseUtil; @Lazy @Autowired RedissonClient redissonClient; @Autowired private RedisUtil redisUtil; @Autowired private TokenUtil tokenUtil; @Autowired private SecurityUtil securityUtil; @Autowired private SysUserDao sysUserDao; @Autowired private SysUserInfoDao sysUserInfoDao; @Autowired private SysUserRoleDao sysUserRoleDao; @Autowired private SysUserRoleMenuService sysUserRoleMenuService; @Autowired private SysUserRoleRelationDao sysUserRoleRelationDao; @Autowired private SysUserRolePermissionRelationDao sysUserRolePermissionRelationDao; @Autowired private SysUserIntegralService sysUserIntegralService; /** * 获取系统用户列表 */ @Override public PageEntity selectUserList(SysUserDTO sysUserDTO) { PageUtils.startPage(); // 分页 List list = sysUserInfoDao.selectUserList(sysUserDTO); return new PageInfoResult(list).toEntity(); } /** * 获取系统用户列表 (在线的) */ @Override public PageEntity selectUserOnlineList(SysUserDTO sysUserDTO) { PageUtils.startPage(); // 分页 // 获得 用户最后登录的 tokenUUID Collection redisKeys = redisUtil.keys(REDIS_LOGIN_TOKEN_PREFIX + "*"); List last_login_uuids = redisKeys.stream().map(e -> String.valueOf(e).replace(REDIS_LOGIN_TOKEN_PREFIX, "")).collect(Collectors.toList()); List list = sysUserInfoDao.selectUserByLastLoginUuids(last_login_uuids); return new PageInfoResult(list).toEntity(); } /** * 获取系统用户详情 */ @Override public SysUserInfo selectUserInfo(SysUserInfo entity) { Long user_id = entity.getUser_id(); // 获得 用户账号信息 SysUser sysUser = sysUserDao.selectOne(new QueryWrapper().eq("id", user_id)); if (sysUser == null) throw new CustException("用户不存在"); // 获得 用户基本信息 LambdaQueryWrapper wrapperUserInfo = new LambdaQueryWrapper<>(); // 条件:用户ID (必选) wrapperUserInfo.eq(SysUserInfo::getUser_id, user_id); // 条件:邀请码 if (StrUtil.isNotEmpty(entity.getInvite_code())) { wrapperUserInfo.eq(SysUserInfo::getInvite_code, entity.getInvite_code()); } SysUserInfo sysUserInfo = sysUserInfoDao.selectOne(wrapperUserInfo); if (sysUserInfo == null) throw new CustException("用户不存在"); // 用户账号信息 赋值到 用户基本信息 BeanUtils.copyProperties(sysUser, sysUserInfo); // 获得 用户角色 List> roles = sysUserRoleDao.selectRoleByUserId(user_id); sysUserInfo.setRoles(roles); // 获得 用户角色Id List role_ids = roles.stream().map(m -> Convert.toLong(m.get("role_id"))).collect(Collectors.toList()); sysUserInfo.setRole_id(role_ids); // 获得 用户角色权限Id (根据角色) List permission_ids = sysUserRolePermissionRelationDao.selectUserRolePermissionIdsByRoleIds(role_ids); sysUserInfo.setPermission_ids(permission_ids); // 获得用户角色菜单 (根据权限) List> sysUserRoleMenuList = sysUserRoleMenuService.selectUserRoleMenuList(permission_ids, 1); sysUserInfo.setMenus(sysUserRoleMenuList); // 获得用户积分 (需要权限) Integer integral = sysUserIntegralService.selectIntegralByUserId(user_id); sysUserInfo.setIntegral(integral); return sysUserInfo; } /** * 获取系统用户详情(简约) */ @Override public SysUserInfoSimple selectUserInfoSimple(Long user_id) { // 获得 用户账号信息 SysUser sysUser = sysUserDao.selectOne(new QueryWrapper().eq("id", user_id)); if (sysUser == null) throw new CustException("用户不存在"); // 获得 用户基本信息 SysUserInfo sysUserInfo = sysUserInfoDao.selectOne(new QueryWrapper().eq("user_id", user_id)); if (sysUserInfo == null) throw new CustException("用户不存在"); // 获得 用户角色 List> roles = sysUserRoleDao.selectRoleByUserId(user_id); sysUserInfo.setRoles(roles); // 获得 用户角色Id List role_ids = roles.stream().map(m -> Convert.toLong(m.get("role_id"))).collect(Collectors.toList()); sysUserInfo.setRole_id(role_ids); // 获得用户积分 (需要权限) Integer integral = sysUserIntegralService.selectIntegralByUserId(user_id); sysUserInfo.setIntegral(integral); // 用户账号信息 赋值到 用户基本信息 BeanUtils.copyProperties(sysUser, sysUserInfo); // 用户基本信息 赋值到 用户基本信息(简约) SysUserInfoSimple sysUserInfoSimple = new SysUserInfoSimple(); BeanUtils.copyProperties(sysUserInfo, sysUserInfoSimple); return sysUserInfoSimple; } /** * 获取系统用户权限 */ @Override public Map selectUserPermission(Long user_id) { // 获得当前角色关系(集合) List userRoleIds = sysUserRoleRelationDao.selectUserRoleIds(user_id); // 获得当前角色关系(集合) 所对应的权限(集合) List rolePermissionIds = new ArrayList<>(); if (userRoleIds.size() > 0) { rolePermissionIds = sysUserRolePermissionRelationDao.selectUserRolePermissionIdsByRoleIds(userRoleIds); rolePermissionIds = rolePermissionIds.stream().distinct().collect(Collectors.toList()); } Map resp = new LinkedHashMap<>(); resp.put("user_id", user_id); resp.put("permission_ids", rolePermissionIds); return resp; } /** * 获取系统用户菜单 */ @Override public Map selectUserMenu(Long user_id) { // 获取系统用户权限 Map permissionResp = selectUserPermission(user_id); List permission_ids = (List) permissionResp.get("permission_ids"); // 获取系统用户菜单 (根据权限) List> menuList = sysUserRoleMenuService.selectUserRoleMenuList(permission_ids, 1); Map resp = new LinkedHashMap<>(); resp.put("user_id", user_id); resp.put("menus", menuList); return resp; } /** * 创建系统用户 */ @Override @Transactional(rollbackFor = Exception.class) public Map insertUser(SysUserDTO sysUserDTO) { RLock lock = redissonClient.getLock("insertUser"); try { lock.tryLock(3, TimeUnit.SECONDS); String phone = sysUserDTO.getPhone(); Integer phoneAreaCode = sysUserDTO.getPhone_area_code(); String username = sysUserDTO.getUsername(); // [查询] 判断用户名是否存在 if (username != null) { SysUser sysUser1 = sysUserDao.selectOne(new LambdaQueryWrapper().eq(SysUser::getUsername, username)); if (sysUser1 != null) throw new CustException("用户名 (" + username + ") 已存在"); } // [查询] 判断手机号是否存在 if (phone != null) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysUser::getPhone, phone); queryWrapper.eq(SysUser::getPhone_area_code, phoneAreaCode); SysUser sysUser2 = sysUserDao.selectOne(queryWrapper); if (sysUser2 != null) throw new CustException("手机号码 (+" + phoneAreaCode + " " + phone + ") 已存在"); } // 密码二次加密 String password = sysUserDTO.getPassword(); BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String encodedPassword = encoder.encode(password); sysUserDTO.setPassword(encodedPassword); // 创建用户 sysUserDao.insertUser(sysUserDTO); // 初始化用户积分 sysUserIntegralService.init(sysUserDTO.getId()); return Map.of("user_id", sysUserDTO.getId()); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } /** * 编辑系统用户信息 */ @Override @Transactional(rollbackFor = Exception.class) public Map updateUserInfo(SysUserDTO sysUserDTO) { RLock lock = redissonClient.getLock("updateUserInfo"); try { lock.tryLock(3, TimeUnit.SECONDS); // 判断记录是否存在 MybatisUtil.checkExists(sysUserDao, "id", sysUserDTO.getUser_id(), "用户不存在"); // 判断手机号码是否存在 if (sysUserDTO.getPhone() != null) { SysUser sysUser = sysUserDao.selectOne(new LambdaQueryWrapper().eq(SysUser::getPhone, sysUserDTO.getPhone())); if (sysUser != null) throw new CustException("手机号码已存在"); } // 当 status 状态为 -1(禁用) 时,同时清除登录状态 Integer status = sysUserDTO.getStatus(); if (status != null && status == -1) { // 删除旧的登录缓存 tokenUtil.deleteRedisLoginToken(sysUserDTO.getLast_login_uuid()); sysUserDTO.setLast_login_uuid(""); } System.out.println("sysUserDTO = " + sysUserDTO); sysUserDao.updateUserInfo(sysUserDTO); return Map.of("user_id", sysUserDTO.getUser_id()); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } /** * 编辑系统用户角色绑定 */ @Override @Transactional(rollbackFor = Exception.class) public Map updateUserRoleRelation(SysUserRoleRelation sysUserRoleRelation) { RLock lock = redissonClient.getLock("updateUserRoleRelation"); try { lock.tryLock(3, TimeUnit.SECONDS); List role_ids = sysUserRoleRelation.getRole_ids(); Long user_id = sysUserRoleRelation.getUser_id(); // 1.删除全部用户与角色的关系 (sys_user_role_relation) LambdaQueryWrapper wrapperRoleRelation = new LambdaQueryWrapper<>(); wrapperRoleRelation.eq(SysUserRoleRelation::getUser_id, user_id); sysUserRoleRelationDao.delete(wrapperRoleRelation); // 2.重新添加参数中的角色与权限的关系 (sys_user_role_relation) sysUserRoleRelationDao.insertBatch(user_id, role_ids); return Map.of("user_id", sysUserRoleRelation.getUser_id()); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } /** * 编辑系统用户密码 */ @Override @Transactional(rollbackFor = Exception.class) public Map updateUserPassword(SysUserDTO sysUserDTO) { RLock lock = redissonClient.getLock("updateUserPassword"); try { lock.tryLock(3, TimeUnit.SECONDS); // 查询用户 SysUser sysUser = sysUserDao.selectOne(new QueryWrapper().eq("id", sysUserDTO.getUser_id())); if (sysUser == null) throw new CustException("原密码不正确"); BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); // [判断] 原密码是否正确 String old_password_request = sysUserDTO.getOld_password(); String old_password = sysUser.getPassword(); if (!encoder.matches(old_password_request, old_password)) { throw new CustException("原密码不正确"); } SysUser entity = new SysUser(); entity.setId(sysUserDTO.getUser_id()); // // MD5加密 // String md5Password = MD5Util.encrypt(sysUserDTO.getPassword()); // 加盐加密 (前端会传已加密过的密码) String encodedPassword = encoder.encode(sysUserDTO.getPassword()); entity.setPassword(encodedPassword); System.out.println(entity); // [编辑] 用户密码 sysUserDao.updateById(entity); // [SSE] 发送退出登录的消息 String dataStr = (new SseResponse(SseResponseEnum.LOGOUT)).toJsonStr(); sseUtil.send(sysUserDTO.getUser_id(), dataStr); return Map.of("user_id", sysUserDTO.getUser_id()); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } /** * 重置系统用户密码 */ @Override @Transactional(rollbackFor = Exception.class) public Map resetUserPassword(SysUserDTO sysUserDTO) { RLock lock = redissonClient.getLock("resetUserPassword"); try { lock.tryLock(3, TimeUnit.SECONDS); // 生成一个六位的随机数密码 String uuid = UUID.randomUUID().toString().replace("-", ""); String password = uuid.substring(0, 6); // MD5加密 String md5Password = MD5Util.encrypt(password); // 加盐加密 BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String encodedPassword = encoder.encode(md5Password); sysUserDTO.setPassword(encodedPassword); // SysUser entity = new SysUser(); entity.setId(sysUserDTO.getUser_id()); entity.setPassword(encodedPassword); System.out.println(entity); // [编辑] 用户密码 sysUserDao.updateById(entity); // 要先判断连接存不存在? // [SSE] 发送退出登录的消息 try { String dataStr = (new SseResponse(SseResponseEnum.LOGOUT)).toJsonStr(); sseUtil.send(sysUserDTO.getUser_id(), dataStr); } catch (Exception e) { System.out.println(e.getMessage()); } Map response = new LinkedHashMap<>(); response.put("user_id", sysUserDTO.getUser_id()); response.put("plain_password", password); return response; } catch (InterruptedException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } /** * 删除系统用户 (以及部分关联表) */ @Override @Transactional(rollbackFor = Exception.class) public Map deleteUser(SysUserDTO sysUserDTO) { RLock lock = redissonClient.getLock("deleteUser"); try { lock.tryLock(3, TimeUnit.SECONDS); // 单个删除 Long user_id = sysUserDTO.getUser_id(); if (user_id != null) { sysUserDao.deleteById(user_id); sysUserInfoDao.delete(new QueryWrapper().eq("user_id", user_id)); sysUserRoleRelationDao.delete(new QueryWrapper().eq("user_id", user_id)); return Map.of("user_id", user_id); } // 批量删除 List user_ids = sysUserDTO.getUser_ids(); if (user_ids != null && user_ids.size() > 0) { sysUserDao.delete(new QueryWrapper().in("id", user_ids)); sysUserInfoDao.delete(new QueryWrapper().in("user_id", user_ids)); sysUserRoleRelationDao.delete(new QueryWrapper().in("user_id", user_ids)); return Map.of("user_ids", user_ids); } return null; } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } /** * 踢出系统用户 */ @Override public Map kickUser(Long user_id) { RLock lock = redissonClient.getLock("kickUser"); try { lock.tryLock(3, TimeUnit.SECONDS); // 查询用户信息 Wrapper queryWrapper = new QueryWrapper().lambda().eq(SysUserInfo::getUser_id, user_id); SysUserInfo sysUserInfo = sysUserInfoDao.selectOne(queryWrapper); if (sysUserInfo != null) { // 删除旧的登录缓存 tokenUtil.deleteRedisLoginToken(sysUserInfo.getLast_login_uuid()); // 更新用户信息 (查询最后登录uuid,并清除) Wrapper updateWrapper = new UpdateWrapper().lambda().set(SysUserInfo::getLast_login_uuid, "").eq(SysUserInfo::getUser_id, user_id); sysUserInfoDao.update(null, updateWrapper); // [SSE] 发送退出登录的消息 String dataStr = (new SseResponse(SseResponseEnum.LOGOUT)).toJsonStr(); sseUtil.send(sysUserInfo.getUser_id(), dataStr); } return Map.of("user_id", user_id); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } /** * 审核用户 */ @Override public Map auditUser(SysUserDTO sysUserDTO) { RLock lock = redissonClient.getLock("auditUser"); try { lock.tryLock(3, TimeUnit.SECONDS); Long user_id = sysUserDTO.getUser_id(); SysUserInfo entity = new SysUserInfo(); entity.setAudit_status(sysUserDTO.getAudit_status()); entity.setAudit_note(sysUserDTO.getAudit_note()); sysUserInfoDao.update(entity, new LambdaQueryWrapper().eq(SysUserInfo::getUser_id, user_id)); return Map.of("user_id", user_id); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } }