| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706 |
- package com.zsElectric.boot.system.service.impl;
- import cn.hutool.core.collection.CollectionUtil;
- import cn.hutool.core.lang.Assert;
- import cn.hutool.core.util.StrUtil;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
- import com.baomidou.mybatisplus.core.metadata.IPage;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.zsElectric.boot.common.constant.RedisConstants;
- import com.zsElectric.boot.common.constant.SystemConstants;
- import com.zsElectric.boot.core.exception.BusinessException;
- import com.zsElectric.boot.common.model.Option;
- import com.zsElectric.boot.platform.sms.enums.SmsTypeEnum;
- import com.zsElectric.boot.platform.sms.service.SmsService;
- import com.zsElectric.boot.security.model.UserAuthCredentials;
- import com.zsElectric.boot.security.service.PermissionService;
- import com.zsElectric.boot.security.token.TokenManager;
- import com.zsElectric.boot.security.util.SecurityUtils;
- import com.zsElectric.boot.platform.mail.service.MailService;
- import com.zsElectric.boot.system.converter.UserConverter;
- import com.zsElectric.boot.system.enums.DictCodeEnum;
- import com.zsElectric.boot.system.mapper.UserMapper;
- import com.zsElectric.boot.system.model.bo.UserBO;
- import com.zsElectric.boot.system.model.dto.CurrentUserDTO;
- import com.zsElectric.boot.system.model.dto.UserExportDTO;
- import com.zsElectric.boot.system.model.entity.DictItem;
- import com.zsElectric.boot.system.model.entity.User;
- import com.zsElectric.boot.system.model.entity.UserRole;
- import com.zsElectric.boot.system.model.form.*;
- import com.zsElectric.boot.system.model.form.*;
- import com.zsElectric.boot.system.model.query.UserPageQuery;
- import com.zsElectric.boot.system.model.vo.UserPageVO;
- import com.zsElectric.boot.system.model.vo.UserProfileVO;
- import com.zsElectric.boot.system.service.*;
- import com.zsElectric.boot.system.service.DictItemService;
- import com.zsElectric.boot.system.service.RoleService;
- import com.zsElectric.boot.system.service.UserRoleService;
- import com.zsElectric.boot.system.service.UserService;
- import lombok.RequiredArgsConstructor;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.security.crypto.password.PasswordEncoder;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import java.math.BigDecimal;
- import java.time.LocalDateTime;
- import java.util.*;
- import java.util.concurrent.TimeUnit;
- import java.util.stream.Collectors;
- /**
- * 用户业务实现类
- *
- * @author Ray.Hao
- * @since 2022/1/14
- */
- @Service
- @RequiredArgsConstructor
- @Slf4j
- public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
- private final PasswordEncoder passwordEncoder;
- private final UserRoleService userRoleService;
- private final RoleService roleService;
- private final PermissionService permissionService;
- private final SmsService smsService;
- private final MailService mailService;
- private final StringRedisTemplate redisTemplate;
- private final TokenManager tokenManager;
- private final DictItemService dictItemService;
- private final UserConverter userConverter;
-
- private final com.zsElectric.boot.business.service.UserInfoService userInfoService;
- /**
- * 获取用户分页列表
- *
- * @param queryParams 查询参数
- * @return {@link IPage<UserPageVO>} 用户分页列表
- */
- @Override
- public IPage<UserPageVO> getUserPage(UserPageQuery queryParams) {
- // 参数构建
- int pageNum = queryParams.getPageNum();
- int pageSize = queryParams.getPageSize();
- Page<UserBO> page = new Page<>(pageNum, pageSize);
- boolean isRoot = SecurityUtils.isRoot();
- queryParams.setIsRoot(isRoot);
- // 查询数据
- Page<UserBO> userPage = this.baseMapper.getUserPage(page, queryParams);
- // 实体转换
- return userConverter.toPageVo(userPage);
- }
- /**
- * 获取用户表单数据
- *
- * @param userId 用户ID
- * @return {@link UserForm} 用户表单数据
- */
- @Override
- public UserForm getUserFormData(Long userId) {
- return this.baseMapper.getUserFormData(userId);
- }
- /**
- * 新增用户
- *
- * @param userForm 用户表单对象
- * @return true|false
- */
- @Override
- public boolean saveUser(UserForm userForm) {
- String username = userForm.getUsername();
- long count = this.count(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
- Assert.isTrue(count == 0, "用户名已存在");
- // 实体转换 form->entity
- User entity = userConverter.toEntity(userForm);
- // 设置默认加密密码
- String defaultEncryptPwd = passwordEncoder.encode(SystemConstants.DEFAULT_PASSWORD);
- entity.setPassword(defaultEncryptPwd);
- entity.setCreateBy(SecurityUtils.getUserId());
- // 新增用户
- boolean result = this.save(entity);
- if (result) {
- // 保存用户角色
- userRoleService.saveUserRoles(entity.getId(), userForm.getRoleIds());
- }
- return result;
- }
- /**
- * 更新用户
- *
- * @param userId 用户ID
- * @param userForm 用户表单对象
- * @return true|false
- */
- @Override
- @Transactional
- public boolean updateUser(Long userId, UserForm userForm) {
- String username = userForm.getUsername();
- long count = this.count(new LambdaQueryWrapper<User>()
- .eq(User::getUsername, username)
- .ne(User::getId, userId)
- );
- Assert.isTrue(count == 0, "用户名已存在");
- // form -> entity
- User entity = userConverter.toEntity(userForm);
- entity.setUpdateBy(SecurityUtils.getUserId());
- // 修改用户
- boolean result = this.updateById(entity);
- if (result) {
- // 保存用户角色
- userRoleService.saveUserRoles(entity.getId(), userForm.getRoleIds());
- }
- return result;
- }
- /**
- * 删除用户
- *
- * @param idsStr 用户ID,多个以英文逗号(,)分割
- * @return true|false
- */
- @Override
- public boolean deleteUsers(String idsStr) {
- Assert.isTrue(StrUtil.isNotBlank(idsStr), "删除的用户数据为空");
- // 逻辑删除
- List<Long> ids = Arrays.stream(idsStr.split(","))
- .map(Long::parseLong)
- .collect(Collectors.toList());
- return this.removeByIds(ids);
- }
- /**
- * 根据用户名获取认证凭证信息
- *
- * @param username 用户名
- * @return 用户认证凭证信息 {@link UserAuthCredentials}
- */
- @Override
- public UserAuthCredentials getAuthCredentialsByUsername(String username) {
- UserAuthCredentials userAuthCredentials = this.baseMapper.getAuthCredentialsByUsername(username);
- if (userAuthCredentials != null) {
- Set<String> roles = userAuthCredentials.getRoles();
- // 获取最大范围的数据权限
- Integer dataScope = roleService.getMaximumDataScope(roles);
- userAuthCredentials.setDataScope(dataScope);
- }
- return userAuthCredentials;
- }
- /**
- * 根据OpenID获取用户认证信息
- *
- * @param openId 微信OpenID
- * @return 用户认证信息
- */
- @Override
- public UserAuthCredentials getAuthCredentialsByOpenId(String openId) {
- if (StrUtil.isBlank(openId)) {
- return null;
- }
- UserAuthCredentials userAuthCredentials = this.baseMapper.getAuthCredentialsByOpenId(openId);
- if (userAuthCredentials != null) {
- Set<String> roles = userAuthCredentials.getRoles();
- // 获取最大范围的数据权限
- Integer dataScope = roleService.getMaximumDataScope(roles);
- userAuthCredentials.setDataScope(dataScope);
- }
- return userAuthCredentials;
- }
- /**
- * 根据手机号获取用户认证信息
- *
- * @param mobile 手机号
- * @return 用户认证信息
- */
- @Override
- public UserAuthCredentials getAuthCredentialsByMobile(String mobile) {
- if (StrUtil.isBlank(mobile)) {
- return null;
- }
- UserAuthCredentials userAuthCredentials = this.baseMapper.getAuthCredentialsByMobile(mobile);
- if (userAuthCredentials != null) {
- Set<String> roles = userAuthCredentials.getRoles();
- // 获取最大范围的数据权限
- Integer dataScope = roleService.getMaximumDataScope(roles);
- userAuthCredentials.setDataScope(dataScope);
- }
- return userAuthCredentials;
- }
- /**
- * 注册或绑定微信用户
- *
- * @param openId 微信OpenID
- * @return 是否成功
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public boolean registerOrBindWechatUser(String openId) {
- if (StrUtil.isBlank(openId)) {
- return false;
- }
- // 查询是否已存在该openId的用户
- User existUser = this.getOne(
- new LambdaQueryWrapper<User>()
- .eq(User::getOpenid, openId)
- );
- if (existUser != null) {
- // 用户已存在,不需要注册
- return true;
- }
- // 创建新用户
- User newUser = new User();
- newUser.setNickname("微信用户"); // 默认昵称
- newUser.setUsername(openId); // TODO 后续替换为手机号
- newUser.setOpenid(openId);
- newUser.setGender(0); // 保密
- newUser.setUpdateBy(SecurityUtils.getUserId());
- newUser.setPassword(SystemConstants.DEFAULT_PASSWORD);
- newUser.setCreateTime(LocalDateTime.now());
- newUser.setUpdateTime(LocalDateTime.now());
- this.save(newUser);
- // 为了默认系统管理员角色,这里按需调整,实际情况绑定已存在的系统用户,另一种情况是给默认游客角色,然后由系统管理员设置用户的角色
- UserRole userRole = new UserRole();
- userRole.setUserId(newUser.getId());
- userRole.setRoleId(1L); // TODO 系统管理员
- userRoleService.save(userRole);
- return true;
- }
- /**
- * 根据手机号和OpenID注册用户
- *
- * @param mobile 手机号
- * @param openId 微信OpenID
- * @return 是否成功
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public boolean registerUserByMobileAndOpenId(String mobile, String openId) {
- // 修改验证逻辑:允许 openId 为 null,支持仅通过手机号注册
- if (StrUtil.isBlank(mobile)) {
- log.warn("注册用户失败:手机号为空");
- return false;
- }
- // 先查询 c_user_info 表中是否已存在手机号对应的用户
- com.zsElectric.boot.business.model.entity.UserInfo existingUserInfo = userInfoService.getOne(
- new LambdaQueryWrapper<com.zsElectric.boot.business.model.entity.UserInfo>()
- .eq(com.zsElectric.boot.business.model.entity.UserInfo::getPhone, mobile)
- );
- if (existingUserInfo != null) {
- log.info("手机号 {} 对应的UserInfo已存在,ID: {}", mobile, existingUserInfo.getId());
- // 如果存在用户但没绑定openId,且提供了openId,则绑定openId
- if (StrUtil.isNotBlank(openId) && StrUtil.isBlank(existingUserInfo.getOpenid())) {
- log.info("为UserInfo {} 绑定 OpenID", existingUserInfo.getId());
- existingUserInfo.setOpenid(openId);
- existingUserInfo.setUpdateTime(LocalDateTime.now());
- return userInfoService.updateById(existingUserInfo);
- }
- // 如果提供了不同的openId,则更新
- else if (StrUtil.isNotBlank(openId) && !openId.equals(existingUserInfo.getOpenid())) {
- log.info("更新UserInfo {} 的 OpenID", existingUserInfo.getId());
- existingUserInfo.setOpenid(openId);
- existingUserInfo.setUpdateTime(LocalDateTime.now());
- return userInfoService.updateById(existingUserInfo);
- }
- // 如果已经绑定了相同的openId或未提供openId,则不需要任何操作
- return true;
- }
- // 不存在用户,创建新的 UserInfo
- log.info("创建新UserInfo,手机号: {}, OpenID: {}", mobile, openId);
- com.zsElectric.boot.business.model.entity.UserInfo newUserInfo = new com.zsElectric.boot.business.model.entity.UserInfo();
- newUserInfo.setPhone(mobile);
- newUserInfo.setOpenid(openId); // openId 可以为 null
- newUserInfo.setNickName("微信用户_" + mobile.substring(mobile.length() - 4)); // 使用手机号后4位作为昵称
- newUserInfo.setCreateTime(LocalDateTime.now());
- newUserInfo.setUpdateTime(LocalDateTime.now());
-
- boolean saved = userInfoService.save(newUserInfo);
- if (!saved) {
- log.error("保存UserInfo失败,手机号: {}", mobile);
- return false;
- }
-
- log.info("UserInfo创建成功,ID: {}, 手机号: {}", newUserInfo.getId(), mobile);
-
- // 注意:UserInfo 不需要分配角色,因为它是业务用户表,不是系统用户表
- // 如果需要系统登录权限,应该额外创建 User 记录并分配角色
-
- return true;
- }
- /**
- * 绑定用户微信OpenID
- *
- * @param userId 用户ID
- * @param openId 微信OpenID
- * @return 是否成功
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public boolean bindUserOpenId(Long userId, String openId) {
- if (userId == null || StrUtil.isBlank(openId)) {
- return false;
- }
- // 检查是否已有其他用户绑定了此openId
- User existingUser = this.getOne(
- new LambdaQueryWrapper<User>()
- .eq(User::getOpenid, openId)
- .ne(User::getId, userId)
- );
- if (existingUser != null) {
- log.warn("OpenID {} 已被用户 {} 绑定,无法为用户 {} 绑定", openId, existingUser.getId(), userId);
- return false;
- }
- // 更新用户openId
- boolean updated = this.update(
- new LambdaUpdateWrapper<User>()
- .eq(User::getId, userId)
- .set(User::getOpenid, openId)
- .set(User::getUpdateTime, LocalDateTime.now())
- );
- return updated ;
- }
- /**
- * 获取导出用户列表
- *
- * @param queryParams 查询参数
- * @return {@link List<UserExportDTO>} 导出用户列表
- */
- @Override
- public List<UserExportDTO> listExportUsers(UserPageQuery queryParams) {
- boolean isRoot = SecurityUtils.isRoot();
- queryParams.setIsRoot(isRoot);
- List<UserExportDTO> exportUsers = this.baseMapper.listExportUsers(queryParams);
- if (CollectionUtil.isNotEmpty(exportUsers)) {
- //获取性别的字典项
- Map<String, String> genderMap = dictItemService.list(
- new LambdaQueryWrapper<DictItem>().eq(DictItem::getDictCode,
- DictCodeEnum.GENDER.getValue())
- ).stream()
- .collect(Collectors.toMap(DictItem::getValue, DictItem::getLabel)
- );
- exportUsers.forEach(item -> {
- String gender = item.getGender();
- if (StrUtil.isBlank(gender)) {
- return;
- }
- // 判断map是否为空
- if (genderMap.isEmpty()) {
- return;
- }
- item.setGender(genderMap.get(gender));
- });
- }
- return exportUsers;
- }
- /**
- * 获取登录用户信息
- *
- * @return {@link CurrentUserDTO} 用户信息
- */
- @Override
- public CurrentUserDTO getCurrentUserInfo() {
- String username = SecurityUtils.getUsername();
- // 获取登录用户基础信息
- User user = this.getOne(new LambdaQueryWrapper<User>()
- .eq(User::getUsername, username)
- .select(
- User::getId,
- User::getUsername,
- User::getNickname,
- User::getAvatar
- )
- );
- // entity->VO
- CurrentUserDTO userInfoVO = userConverter.toCurrentUserDto(user);
- // 用户角色集合
- Set<String> roles = SecurityUtils.getRoles();
- userInfoVO.setRoles(roles);
- // 用户权限集合
- if (CollectionUtil.isNotEmpty(roles)) {
- Set<String> perms = permissionService.getRolePermsFormCache(roles);
- userInfoVO.setPerms(perms);
- }
- return userInfoVO;
- }
- /**
- * 获取个人中心用户信息
- *
- * @param userId 用户ID
- * @return {@link UserProfileVO} 个人中心用户信息
- */
- @Override
- public UserProfileVO getUserProfile(Long userId) {
- UserBO entity = this.baseMapper.getUserProfile(userId);
- return userConverter.toProfileVo(entity);
- }
- /**
- * 修改个人中心用户信息
- *
- * @param formData 表单数据
- * @return true|false
- */
- @Override
- public boolean updateUserProfile(UserProfileForm formData) {
- Long userId = SecurityUtils.getUserId();
- User entity = userConverter.toEntity(formData);
- entity.setId(userId);
- return this.updateById(entity);
- }
- /**
- * 修改用户密码
- *
- * @param userId 用户ID
- * @param data 密码修改表单数据
- * @return true|false
- */
- @Override
- public boolean changePassword(Long userId, PasswordUpdateForm data) {
- User user = this.getById(userId);
- if (user == null) {
- throw new BusinessException("用户不存在");
- }
- String oldPassword = data.getOldPassword();
- // 校验原密码
- if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
- throw new BusinessException("原密码错误");
- }
- // 新旧密码不能相同
- if (passwordEncoder.matches(data.getNewPassword(), user.getPassword())) {
- throw new BusinessException("新密码不能与原密码相同");
- }
- // 判断新密码和确认密码是否一致
- if (passwordEncoder.matches(data.getNewPassword(), data.getConfirmPassword())) {
- throw new BusinessException("新密码和确认密码不一致");
- }
- String newPassword = data.getNewPassword();
- boolean result = this.update(new LambdaUpdateWrapper<User>()
- .eq(User::getId, userId)
- .set(User::getPassword, passwordEncoder.encode(newPassword))
- );
- if (result) {
- // 加入黑名单,重新登录
- String accessToken = SecurityUtils.getTokenFromRequest();
- tokenManager.invalidateToken(accessToken);
- }
- return result;
- }
- /**
- * 重置密码
- *
- * @param userId 用户ID
- * @param password 密码重置表单数据
- * @return true|false
- */
- @Override
- public boolean resetPassword(Long userId, String password) {
- return this.update(new LambdaUpdateWrapper<User>()
- .eq(User::getId, userId)
- .set(User::getPassword, passwordEncoder.encode(password))
- );
- }
- /**
- * 发送短信验证码(绑定或更换手机号)
- *
- * @param mobile 手机号
- * @return true|false
- */
- @Override
- public boolean sendMobileCode(String mobile) {
- // String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000));
- // TODO 为了方便测试,验证码固定为 1234,实际开发中在配置了厂商短信服务后,可以使用上面的随机验证码
- String code = "1234";
- Map<String, String> templateParams = new HashMap<>();
- templateParams.put("code", code);
- boolean result = smsService.sendSms(mobile, SmsTypeEnum.CHANGE_MOBILE, templateParams);
- if (result) {
- // 缓存验证码,5分钟有效,用于更换手机号校验
- String redisCacheKey = StrUtil.format(RedisConstants.Captcha.MOBILE_CODE, mobile);
- redisTemplate.opsForValue().set(redisCacheKey, code, 5, TimeUnit.MINUTES);
- }
- return result;
- }
- /**
- * 绑定或更换手机号
- *
- * @param form 表单数据
- * @return true|false
- */
- @Override
- public boolean bindOrChangeMobile(MobileUpdateForm form) {
- Long currentUserId = SecurityUtils.getUserId();
- User currentUser = this.getById(currentUserId);
- if (currentUser == null) {
- throw new BusinessException("用户不存在");
- }
- // 校验验证码
- String inputVerifyCode = form.getCode();
- String mobile = form.getMobile();
- String cacheKey = StrUtil.format(RedisConstants.Captcha.MOBILE_CODE, mobile);
- String cachedVerifyCode = redisTemplate.opsForValue().get(cacheKey);
- if (StrUtil.isBlank(cachedVerifyCode)) {
- throw new BusinessException("验证码已过期");
- }
- if (!inputVerifyCode.equals(cachedVerifyCode)) {
- throw new BusinessException("验证码错误");
- }
- // 验证完成删除验证码
- redisTemplate.delete(cacheKey);
- // 更新手机号码
- return this.update(
- new LambdaUpdateWrapper<User>()
- .eq(User::getId, currentUserId)
- .set(User::getMobile, mobile)
- );
- }
- /**
- * 发送邮箱验证码(绑定或更换邮箱)
- *
- * @param email 邮箱
- */
- @Override
- public void sendEmailCode(String email) {
- // String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000));
- // TODO 为了方便测试,验证码固定为 1234,实际开发中在配置了邮箱服务后,可以使用上面的随机验证码
- String code = "1234";
- mailService.sendMail(email, "邮箱验证码", "您的验证码为:" + code + ",请在5分钟内使用");
- // 缓存验证码,5分钟有效,用于更换邮箱校验
- String redisCacheKey = StrUtil.format(RedisConstants.Captcha.EMAIL_CODE, email);
- redisTemplate.opsForValue().set(redisCacheKey, code, 5, TimeUnit.MINUTES);
- }
- /**
- * 修改当前用户邮箱
- *
- * @param form 表单数据
- * @return true|false
- */
- @Override
- public boolean bindOrChangeEmail(EmailUpdateForm form) {
- Long currentUserId = SecurityUtils.getUserId();
- User currentUser = this.getById(currentUserId);
- if (currentUser == null) {
- throw new BusinessException("用户不存在");
- }
- // 获取前端输入的验证码
- String inputVerifyCode = form.getCode();
- // 获取缓存的验证码
- String email = form.getEmail();
- String redisCacheKey = StrUtil.format(RedisConstants.Captcha.EMAIL_CODE, email);
- String cachedVerifyCode = redisTemplate.opsForValue().get(redisCacheKey);
- if (StrUtil.isBlank(cachedVerifyCode)) {
- throw new BusinessException("验证码已过期");
- }
- if (!inputVerifyCode.equals(cachedVerifyCode)) {
- throw new BusinessException("验证码错误");
- }
- // 验证完成删除验证码
- redisTemplate.delete(redisCacheKey);
- // 更新邮箱地址
- return this.update(
- new LambdaUpdateWrapper<User>()
- .eq(User::getId, currentUserId)
- .set(User::getEmail, email)
- );
- }
- /**
- * 获取用户选项列表
- *
- * @return {@link List<Option<String>>} 用户选项列表
- */
- @Override
- public List<Option<String>> listUserOptions() {
- List<User> list = this.list(new LambdaQueryWrapper<User>()
- .eq(User::getStatus, 1)
- );
- return userConverter.toOptions(list);
- }
- }
|