- 添加 Redisson 依赖并配置自动装配 - 创建 application-common.yml 统一管理 Redis 配置 - 在 application.yml 中引入公共配置文件 - 修改生产环境配置以引用公共 Redis 配置 - 替换原生 RedisTemplate 为 Redisson 客户端 - 实现基于 Redisson 的分布式锁和缓存功能
823 lines
28 KiB
Java
823 lines
28 KiB
Java
package com.kexue.skills.service.impl;
|
||
|
||
import cn.hutool.core.bean.BeanUtil;
|
||
import com.alicp.jetcache.anno.CacheInvalidate;
|
||
import com.alicp.jetcache.anno.CachePenetrationProtect;
|
||
import com.alicp.jetcache.anno.Cached;
|
||
import com.github.pagehelper.PageHelper;
|
||
import com.github.pagehelper.PageInfo;
|
||
import com.kexue.skills.common.Assert;
|
||
import com.kexue.skills.common.CacheManager;
|
||
import com.kexue.skills.common.Const;
|
||
import com.kexue.skills.config.CaptchaConfig;
|
||
import com.kexue.skills.entity.*;
|
||
import com.kexue.skills.entity.dto.ContentPurchaseDto;
|
||
import com.kexue.skills.entity.dto.SysUserDto;
|
||
import com.kexue.skills.entity.request.*;
|
||
import com.kexue.skills.mapper.*;
|
||
import com.kexue.skills.service.SysUserService;
|
||
import com.kexue.skills.utils.MD5Util;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.dromara.sms4j.api.SmsBlend;
|
||
import org.dromara.sms4j.core.factory.SmsFactory;
|
||
import org.redisson.api.RBucket;
|
||
import org.redisson.api.RedissonClient;
|
||
import org.springframework.stereotype.Service;
|
||
|
||
import javax.annotation.Resource;
|
||
import java.math.BigDecimal;
|
||
import java.security.NoSuchAlgorithmException;
|
||
import java.util.LinkedHashMap;
|
||
import java.util.List;
|
||
import java.util.Objects;
|
||
import java.util.Random;
|
||
|
||
/**
|
||
* (SysUser)表服务实现类
|
||
*
|
||
* @author 王志维
|
||
* @since 2025-02-21 23:01:48
|
||
*/
|
||
@Service("sysUserService")
|
||
@Slf4j
|
||
public class SysUserServiceImpl implements SysUserService {
|
||
|
||
@Resource
|
||
private SysUserMapper sysUserMapper;
|
||
|
||
@Resource
|
||
private org.springframework.data.redis.core.RedisTemplate<String, String> redisTemplate;
|
||
|
||
@Resource
|
||
private RedissonClient redissonClient;
|
||
|
||
@Resource
|
||
private CaptchaConfig captchaConfig;
|
||
|
||
@Resource
|
||
private PointsAccountMapper pointsAccountMapper;
|
||
|
||
@Resource
|
||
private AccountMapper accountMapper;
|
||
|
||
@Resource
|
||
private SysUserRoleMapper sysUserRoleMapper;
|
||
|
||
@Resource
|
||
private ContentPurchaseMapper contentPurchaseMapper;
|
||
|
||
@Resource
|
||
private CmsContentLikeMapper cmsContentLikeMapper;
|
||
|
||
@Resource
|
||
private CmsContentViewMapper cmsContentViewMapper;
|
||
|
||
@Resource
|
||
private CmsContentMapper cmsContentMapper;
|
||
|
||
/**
|
||
* 分页查询数据
|
||
*
|
||
* @param queryDto
|
||
* @return 数据分页列表
|
||
*/
|
||
@Override
|
||
public PageInfo<SysUser> getPageList(SysUserDto queryDto) {
|
||
int pageNum = queryDto.getPageNum()==null?PAGENUM:queryDto.getPageNum();
|
||
int pageSize = queryDto.getPageSize()==null? PAGESIZE :queryDto.getPageSize();
|
||
PageHelper.startPage(pageNum,pageSize);
|
||
List<SysUser> dataList = sysUserMapper.getPageList(queryDto);
|
||
// 将所有返回的用户密码设置为null
|
||
dataList.forEach(user -> user.setPwd(null));
|
||
return new PageInfo<>(dataList);
|
||
}
|
||
|
||
/**
|
||
* 通过ID查询单条数据
|
||
*
|
||
* @param userId 主键
|
||
* @return 实例对象
|
||
*/
|
||
@Override
|
||
@Cached(name = "sysUser:", key = "#userId", expire = 3600, cacheType = com.alicp.jetcache.anno.CacheType.BOTH)
|
||
@CachePenetrationProtect
|
||
public SysUser queryById(Long userId) {
|
||
SysUser sysUser = sysUserMapper.queryById(userId);
|
||
sysUser.setPwd(null);
|
||
return sysUser;
|
||
}
|
||
|
||
/**
|
||
* 查询多条数据
|
||
*
|
||
* @param offset 查询起始位置
|
||
* @param limit 查询条数
|
||
* @return 对象列表
|
||
*/
|
||
@Override
|
||
public List<SysUser> queryAllByLimit(int offset, int limit) {
|
||
List<SysUser> sysUsers = sysUserMapper.queryAllByLimit(offset, limit);
|
||
// 将所有返回的用户密码设置为null
|
||
sysUsers.forEach(user -> user.setPwd(null));
|
||
return sysUsers;
|
||
}
|
||
|
||
/**
|
||
* 新增数据
|
||
*
|
||
* @param sysUser 实例对象
|
||
* @return 实例对象
|
||
*/
|
||
@Override
|
||
public SysUser insert(SysUser sysUser) {
|
||
SysUser byUsername = getByUsername(sysUser.getUserName());
|
||
Assert.isNull(byUsername, "用户名已存在");
|
||
sysUser.setEnable(Const.USER_STATUS_NORMAL);
|
||
sysUser.setDeleteFlag(Const.DELETE_FLAG_NO);
|
||
//写一个salt生成方法
|
||
sysUser.setSalt(System.currentTimeMillis()+"");
|
||
try {
|
||
String md5Pwd = MD5Util.encryptToHex(sysUser.getPwd()+sysUser.getSalt());
|
||
sysUser.setPwd(md5Pwd);
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
// 插入用户信息
|
||
sysUserMapper.insert(sysUser);
|
||
|
||
// 初始化账户(余额账户)
|
||
Account account = new Account();
|
||
account.setUserId(sysUser.getUserId());
|
||
account.setUserName(sysUser.getUserName());
|
||
account.setBalance(BigDecimal.ZERO); // 初始余额为0
|
||
account.setFrozenAmount(BigDecimal.ZERO); // 初始冻结金额为0
|
||
account.setDeleteFlag(Const.DELETE_FLAG_NO); // 初始未删除
|
||
accountMapper.insert(account);
|
||
|
||
// 初始化积分账户
|
||
PointsAccount pointsAccount = new PointsAccount();
|
||
pointsAccount.setUserId(sysUser.getUserId());
|
||
pointsAccount.setUserName(sysUser.getUserName());
|
||
pointsAccount.setTotalPoints(0); // 初始总积分为0
|
||
pointsAccount.setAvailablePoints(0); // 初始可用积分为0
|
||
pointsAccount.setFrozenPoints(0); // 初始冻结积分为0
|
||
pointsAccount.setDeleteFlag(Const.DELETE_FLAG_NO); // 初始未删除
|
||
pointsAccountMapper.insert(pointsAccount);
|
||
|
||
// 将返回的用户密码设置为null
|
||
sysUser.setPwd(null);
|
||
return sysUser;
|
||
}
|
||
|
||
/**
|
||
* 修改数据
|
||
*
|
||
* @param sysUser 实例对象
|
||
* @return 实例对象
|
||
*/
|
||
@Override
|
||
@CacheInvalidate(name = "sysUser:", key = "#sysUser.userId")
|
||
@CacheInvalidate(name = "sysUser:username:", key = "#sysUser.userName")
|
||
public SysUser update(SysUser sysUser) {
|
||
if (Objects.nonNull(sysUser.getUserId())){
|
||
// 校验用户名是否已经存在
|
||
if (sysUser.getUserName() != null && !sysUser.getUserName().isEmpty()) {
|
||
SysUser existingUser = sysUserMapper.getByUsername(sysUser.getUserName());
|
||
Assert.isTrue(existingUser == null || existingUser.getUserId().equals(sysUser.getUserId()), "用户名已存在");
|
||
}
|
||
sysUserMapper.update(sysUser);
|
||
}else {
|
||
insert(sysUser);
|
||
}
|
||
return queryById(sysUser.getUserId());
|
||
}
|
||
|
||
/**
|
||
* 通过主键删除数据
|
||
*
|
||
* @param userId 主键
|
||
* @return 是否成功
|
||
*/
|
||
@Override
|
||
@CacheInvalidate(name = "sysUser:", key = "#userId")
|
||
public boolean deleteById(Long userId) {
|
||
|
||
SysUser sysUser = queryById(userId);
|
||
//管理员不允许删除
|
||
Assert.isFalse(Const.ADMIN_USER_LIST.contains(sysUser.getUserName().toLowerCase()), "该用户是管理员,不能删除");
|
||
|
||
// 删除用户名缓存
|
||
redisTemplate.delete("sysUser:username:" + sysUser.getUserName());
|
||
|
||
return sysUserMapper.deleteById(userId) > 0;
|
||
}
|
||
|
||
@Override
|
||
public LoginUserDto login(LoginDto loginDto) {
|
||
// 参数验证
|
||
validateLoginParams(loginDto);
|
||
|
||
// 验证码验证
|
||
validateCaptcha(loginDto);
|
||
|
||
// 查询用户
|
||
SysUser sysUser = getUserByUsername(loginDto.getUsername());
|
||
|
||
// 密码验证
|
||
validatePassword(loginDto.getPassword(), sysUser);
|
||
|
||
// 生成token
|
||
String token = generateToken(sysUser.getUserId());
|
||
|
||
// 构建登录用户信息
|
||
LoginUser loginUser = buildLoginUser(sysUser, token);
|
||
|
||
// 存储登录信息到Redis
|
||
saveLoginUserToRedis(token, loginUser);
|
||
|
||
// 构建返回对象
|
||
LoginUserDto sysUserDto = buildLoginUserDto(loginUser);
|
||
|
||
log.info("用户:{}登录成功,token:{}", loginDto.getUsername(), token);
|
||
|
||
return sysUserDto;
|
||
}
|
||
|
||
/**
|
||
* 参数验证
|
||
*
|
||
* @param loginDto 登录请求参数
|
||
*/
|
||
private void validateLoginParams(LoginDto loginDto) {
|
||
Assert.notNull(loginDto.getUsername(), "用户名不能位空");
|
||
Assert.notNull(loginDto.getPassword(), "密码不能位空");
|
||
}
|
||
|
||
/**
|
||
* 验证码验证
|
||
*
|
||
* @param loginDto 登录请求参数
|
||
*/
|
||
private void validateCaptcha(LoginDto loginDto) {
|
||
if (captchaConfig.isEnabled()) {
|
||
Assert.notNull(loginDto.getCaptchaId(), "验证码不能为空");
|
||
Assert.notNull(loginDto.getCaptchaValue(), "验证码不能为空");
|
||
|
||
String captchaKey = "captcha:" + loginDto.getCaptchaId();
|
||
String captchaText = redisTemplate.opsForValue().get(captchaKey);
|
||
Assert.notNull(captchaText, "验证码已过期");
|
||
Assert.equals(captchaText, loginDto.getCaptchaValue().toLowerCase(), "验证码错误");
|
||
|
||
redisTemplate.delete(captchaKey);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据用户名查询用户
|
||
*
|
||
* @param username 用户名
|
||
* @return 用户对象
|
||
*/
|
||
private SysUser getUserByUsername(String username) {
|
||
SysUser sysUser = sysUserMapper.getByUsername(username);
|
||
Assert.notNull(sysUser, "用户名不存在");
|
||
return sysUser;
|
||
}
|
||
|
||
/**
|
||
* 密码验证
|
||
*
|
||
* @param password 密码
|
||
* @param sysUser 用户对象
|
||
*/
|
||
private void validatePassword(String password, SysUser sysUser) {
|
||
try {
|
||
// 假设客户端已经对密码进行了一次MD5加密,服务端使用双重加密验证
|
||
String encryptedPwd = MD5Util.doubleEncrypt(password, sysUser.getSalt());
|
||
Assert.equals(encryptedPwd, sysUser.getPwd(), "密码不正确");
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成token
|
||
*
|
||
* @param userId 用户ID
|
||
* @return token
|
||
*/
|
||
private String generateToken(Long userId) {
|
||
cn.dev33.satoken.stp.StpUtil.login(userId);
|
||
return cn.dev33.satoken.stp.StpUtil.getTokenValue();
|
||
}
|
||
|
||
/**
|
||
* 构建登录用户信息
|
||
*
|
||
* @param sysUser 用户对象
|
||
* @param token token
|
||
* @return 登录用户信息
|
||
*/
|
||
private LoginUser buildLoginUser(SysUser sysUser, String token) {
|
||
LoginUser loginUser = new LoginUser();
|
||
|
||
// 设置用户基本信息
|
||
sysUser.setPwd(null);
|
||
loginUser.setUserInfo(sysUser);
|
||
|
||
// 查询并设置用户角色列表
|
||
loginUser.setRoles(queryUserRoles(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户已购买的内容ID列表
|
||
loginUser.setPurchasedContentIds(queryPurchasedContentIds(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户账户信息
|
||
loginUser.setAccount(queryUserAccount(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户积分信息
|
||
loginUser.setPointsAccount(queryUserPointsAccount(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户最近点赞记录
|
||
loginUser.setFavorites(queryRecentFavorites(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户最近查看记录
|
||
loginUser.setHistory(queryRecentViews(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户最近创建记录
|
||
loginUser.setCreate(queryRecentCreatedContent(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户最近购买记录
|
||
loginUser.setHas(queryRecentPurchases(sysUser.getUserId()));
|
||
|
||
// 设置token
|
||
loginUser.setToken(token);
|
||
|
||
return loginUser;
|
||
}
|
||
|
||
/**
|
||
* 查询用户角色列表
|
||
*
|
||
* @param userId 用户ID
|
||
* @return 角色列表
|
||
*/
|
||
private List<String> queryUserRoles(Long userId) {
|
||
List<String> roles = new java.util.ArrayList<>();
|
||
try {
|
||
SysUserRole userRole = new SysUserRole();
|
||
userRole.setUserId(userId);
|
||
List<SysUserRole> userRoles = sysUserRoleMapper.queryAll(userRole);
|
||
|
||
for (SysUserRole ur : userRoles) {
|
||
roles.add("ROLE_" + ur.getRoleId());
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("查询用户角色列表失败:{}", e.getMessage());
|
||
roles = java.util.Collections.emptyList();
|
||
}
|
||
return roles;
|
||
}
|
||
|
||
/**
|
||
* 查询用户已购买的内容ID列表
|
||
*
|
||
* @param userId 用户ID
|
||
* @return 内容ID列表
|
||
*/
|
||
private List<Long> queryPurchasedContentIds(Long userId) {
|
||
List<Long> purchasedContentIds = new java.util.ArrayList<>();
|
||
try {
|
||
ContentPurchaseDto purchaseDto = new ContentPurchaseDto();
|
||
purchaseDto.setUserId(userId);
|
||
List<ContentPurchase> purchases = contentPurchaseMapper.getList(purchaseDto);
|
||
|
||
for (ContentPurchase purchase : purchases) {
|
||
purchasedContentIds.add(purchase.getContentId());
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("查询用户已购买内容列表失败:{}", e.getMessage());
|
||
purchasedContentIds = java.util.Collections.emptyList();
|
||
}
|
||
return purchasedContentIds;
|
||
}
|
||
|
||
/**
|
||
* 查询用户账户信息
|
||
*
|
||
* @param userId 用户ID
|
||
* @return 账户信息
|
||
*/
|
||
private Account queryUserAccount(Long userId) {
|
||
return accountMapper.queryByUserId(userId);
|
||
}
|
||
|
||
/**
|
||
* 查询用户积分信息
|
||
*
|
||
* @param userId 用户ID
|
||
* @return 积分信息
|
||
*/
|
||
private PointsAccount queryUserPointsAccount(Long userId) {
|
||
return pointsAccountMapper.queryByUserId(userId);
|
||
}
|
||
|
||
/**
|
||
* 查询用户最近点赞记录
|
||
*
|
||
* @param userId 用户ID
|
||
* @return 最近点赞记录
|
||
*/
|
||
private List<Long> queryRecentFavorites(Long userId) {
|
||
try {
|
||
return cmsContentLikeMapper.queryRecentLikesByUserId(userId, 20);
|
||
} catch (Exception e) {
|
||
log.error("查询用户最近点赞记录失败:{}", e.getMessage());
|
||
return java.util.Collections.emptyList();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询用户最近查看记录
|
||
*
|
||
* @param userId 用户ID
|
||
* @return 最近查看记录
|
||
*/
|
||
private List<Long> queryRecentViews(Long userId) {
|
||
try {
|
||
return cmsContentViewMapper.queryRecentViewsByUserId(userId, 20);
|
||
} catch (Exception e) {
|
||
log.error("查询用户最近查看记录失败:{}", e.getMessage());
|
||
return java.util.Collections.emptyList();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询用户最近创建记录
|
||
*
|
||
* @param userId 用户ID
|
||
* @return 最近创建记录
|
||
*/
|
||
private List<Long> queryRecentCreatedContent(Long userId) {
|
||
try {
|
||
return cmsContentMapper.queryRecentCreatedByUserId(userId, 20);
|
||
} catch (Exception e) {
|
||
log.error("查询用户最近创建记录失败:{}", e.getMessage());
|
||
return java.util.Collections.emptyList();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询用户最近购买记录
|
||
*
|
||
* @param userId 用户ID
|
||
* @return 最近购买记录
|
||
*/
|
||
private List<Long> queryRecentPurchases(Long userId) {
|
||
List<Long> has = new java.util.ArrayList<>();
|
||
try {
|
||
ContentPurchaseDto purchaseDto = new ContentPurchaseDto();
|
||
purchaseDto.setUserId(userId);
|
||
List<ContentPurchase> purchases = contentPurchaseMapper.getList(purchaseDto);
|
||
if (purchases != null && !purchases.isEmpty()) {
|
||
java.util.LinkedHashSet<Long> contentIdSet = new java.util.LinkedHashSet<>();
|
||
for (ContentPurchase purchase : purchases) {
|
||
if (contentIdSet.size() < 20) {
|
||
contentIdSet.add(purchase.getContentId());
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
has.addAll(contentIdSet);
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("查询用户最近购买记录失败:{}", e.getMessage());
|
||
has = java.util.Collections.emptyList();
|
||
}
|
||
return has;
|
||
}
|
||
|
||
/**
|
||
* 存储登录信息到Redis
|
||
*
|
||
* @param token token
|
||
* @param loginUser 登录用户信息
|
||
*/
|
||
private void saveLoginUserToRedis(String token, LoginUser loginUser) {
|
||
redisTemplate.opsForValue().set(
|
||
"loginUser:" + token,
|
||
cn.hutool.json.JSONUtil.toJsonStr(loginUser),
|
||
3600,
|
||
java.util.concurrent.TimeUnit.SECONDS
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 构建登录用户返回对象
|
||
*
|
||
* @param loginUser 登录用户信息
|
||
* @return 登录用户返回对象
|
||
*/
|
||
private LoginUserDto buildLoginUserDto(LoginUser loginUser) {
|
||
LoginUserDto sysUserDto = new LoginUserDto();
|
||
BeanUtil.copyProperties(loginUser.getUserInfo(), sysUserDto);
|
||
sysUserDto.setToken(loginUser.getToken());
|
||
sysUserDto.setFavorites(loginUser.getFavorites());
|
||
sysUserDto.setHistory(loginUser.getHistory());
|
||
sysUserDto.setCreate(loginUser.getCreate());
|
||
sysUserDto.setHas(loginUser.getHas());
|
||
return sysUserDto;
|
||
}
|
||
|
||
@Override
|
||
public boolean resetPassword(ResetPasswordDto resetPasswordDto) {
|
||
Assert.notNull(resetPasswordDto.getUserName(), "用户名不能位空");
|
||
Assert.notNull(resetPasswordDto.getOldPassword(), "旧密码不能位空");
|
||
Assert.notNull(resetPasswordDto.getNewPassword(), "新密码不能位空");
|
||
|
||
SysUser sysUser = sysUserMapper.getByUsername(resetPasswordDto.getUserName());
|
||
Assert.notNull(sysUser, "用户名不存在");
|
||
|
||
try {
|
||
String oldMd5Pwd = MD5Util.encryptToHex(resetPasswordDto.getOldPassword() + sysUser.getSalt());
|
||
Assert.equals(oldMd5Pwd, sysUser.getPwd(), "旧密码不正确");
|
||
|
||
String newMd5Pwd = MD5Util.encryptToHex(resetPasswordDto.getNewPassword() + sysUser.getSalt());
|
||
sysUser.setPwd(newMd5Pwd);
|
||
sysUserMapper.update(sysUser);
|
||
|
||
// 清除旧的token
|
||
CacheManager.removeTokenFromCache(resetPasswordDto.getUserName());
|
||
|
||
return true;
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public boolean resetPasswordByAdmin(ResetPasswordDto resetPasswordDto) {
|
||
Assert.notNull(resetPasswordDto.getUserName(), "用户名不能位空");
|
||
Assert.notNull(resetPasswordDto.getNewPassword(), "新密码不能位空");
|
||
|
||
SysUser sysUser = sysUserMapper.getByUsername(resetPasswordDto.getUserName());
|
||
Assert.notNull(sysUser, "用户名不存在");
|
||
|
||
try {
|
||
String newMd5Pwd = MD5Util.encryptToHex(resetPasswordDto.getNewPassword() + sysUser.getSalt());
|
||
sysUser.setPwd(newMd5Pwd);
|
||
sysUserMapper.update(sysUser);
|
||
|
||
// 清除旧的token
|
||
CacheManager.removeTokenFromCache(resetPasswordDto.getUserName());
|
||
|
||
return true;
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
@Cached(name = "sysUser:username:", key = "#username", expire = 3600, cacheType = com.alicp.jetcache.anno.CacheType.BOTH)
|
||
@CachePenetrationProtect
|
||
public SysUser getByUsername(String username) {
|
||
if (Objects.nonNull(username)){
|
||
SysUser sysUser = sysUserMapper.getByUsername(username);
|
||
if (sysUser != null) {
|
||
sysUser.setPwd(null);
|
||
}
|
||
return sysUser;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
@Override
|
||
public boolean resetPwd(Long userId, String newPassword, String operator) {
|
||
// 检查操作人是否为管理员
|
||
Assert.isTrue(Const.ADMIN_USER_LIST.contains(operator.toLowerCase()), "只有管理员才能执行此操作");
|
||
|
||
// 查询用户是否存在
|
||
SysUser sysUser = sysUserMapper.queryById(userId);
|
||
Assert.notNull(sysUser, "用户不存在");
|
||
|
||
try {
|
||
// 生成新的加密密码
|
||
String newMd5Pwd = MD5Util.encryptToHex(newPassword + sysUser.getSalt());
|
||
sysUser.setPwd(newMd5Pwd);
|
||
|
||
// 更新用户密码
|
||
sysUserMapper.update(sysUser);
|
||
|
||
// 清除旧的token
|
||
CacheManager.removeTokenFromCache(sysUser.getUserName());
|
||
|
||
return true;
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送手机验证码
|
||
*
|
||
* @param phone 手机号
|
||
* @return 是否发送成功
|
||
*/
|
||
@Override
|
||
public boolean sendSmsCode(String phone) {
|
||
try {
|
||
// 参数校验
|
||
Assert.notBlank(phone, "手机号不能为空");
|
||
|
||
// 生成6位随机验证码
|
||
String code = generateSmsCode();
|
||
|
||
// 存储验证码到Redis,有效期5分钟
|
||
redissonClient.getBucket("sms_code:" + phone).set(code, 300, java.util.concurrent.TimeUnit.SECONDS);
|
||
|
||
// 获取默认的短信发送器
|
||
SmsBlend sms = SmsFactory.getSmsBlend();
|
||
|
||
Assert.notNull(sms, "短信服务未正确配置");
|
||
log.info("获取短信发送器成功");
|
||
|
||
// 发送验证码 - 使用模板方式发送,避免直接文本发送可能导致的参数缺失
|
||
LinkedHashMap<String, String> params = new LinkedHashMap<>();
|
||
params.put("code", code); // 参数名需要与阿里云模板中的变量名匹配
|
||
|
||
// 发送短信,使用配置文件中定义的模板
|
||
// 根据API文档和用户需求,使用单个手机号发送的方法:sendMessage(String phone, Map<String, String> params)
|
||
// 系统会自动从配置中读取template-id
|
||
sms.sendMessage(phone, params);
|
||
|
||
log.info("向手机号 {} 发送验证码:{}", phone, code);
|
||
return true;
|
||
} catch (Exception e) {
|
||
log.error("向手机号 {} 发送验证码失败:{}", phone, e.getMessage());
|
||
log.error("详细错误信息:", e);
|
||
|
||
// 如果短信服务不可用,可以记录错误并返回false而不是抛出异常
|
||
// 或者根据业务需求决定是否继续
|
||
Assert.isTrue(false, "发送验证码失败:" + e.getMessage());
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 生成6位随机验证码
|
||
*
|
||
* @return 6位随机验证码
|
||
*/
|
||
private String generateSmsCode() {
|
||
Random random = new Random();
|
||
return String.format("%06d", random.nextInt(1000000));
|
||
}
|
||
|
||
/**
|
||
* 手机号登录
|
||
*
|
||
* @param phoneLoginDto 手机号登录信息
|
||
* @return 登录结果
|
||
*/
|
||
@Override
|
||
public LoginUserDto phoneLogin(PhoneLoginDto phoneLoginDto) {
|
||
String phone = phoneLoginDto.getPhone();
|
||
String code = phoneLoginDto.getCode();
|
||
|
||
Assert.notBlank(phone, "手机号不能为空");
|
||
Assert.notBlank(code, "验证码不能为空");
|
||
|
||
// 验证验证码
|
||
String cachedCode = redisTemplate.opsForValue().get("sms_code:" + phone);
|
||
Assert.notNull(cachedCode, "验证码已过期");
|
||
Assert.equals(cachedCode, code, "验证码错误");
|
||
|
||
// 验证成功后,删除验证码
|
||
redisTemplate.delete("sms_code:" + phone);
|
||
|
||
// 查询用户是否存在
|
||
SysUser sysUser = sysUserMapper.getByTel(phone);
|
||
|
||
// 如果用户不存在,自动创建账号
|
||
if (sysUser == null) {
|
||
sysUser = createUserByPhone(phone);
|
||
}
|
||
|
||
// 使用Sa-Token登录,生成token
|
||
cn.dev33.satoken.stp.StpUtil.login(sysUser.getUserId());
|
||
// 获取生成的token
|
||
String token = cn.dev33.satoken.stp.StpUtil.getTokenValue();
|
||
|
||
// 创建LoginUser对象
|
||
LoginUser loginUser = new LoginUser();
|
||
|
||
// 设置用户基本信息
|
||
sysUser.setPwd(null);
|
||
loginUser.setUserInfo(sysUser);
|
||
|
||
// 查询用户角色列表
|
||
List<String> roles = new java.util.ArrayList<>();
|
||
try {
|
||
// 创建查询条件,查询用户的角色关联记录
|
||
SysUserRole userRole = new SysUserRole();
|
||
userRole.setUserId(sysUser.getUserId());
|
||
List<SysUserRole> userRoles = sysUserRoleMapper.queryAll(userRole);
|
||
|
||
// 遍历角色关联记录,获取角色名称
|
||
for (SysUserRole ur : userRoles) {
|
||
roles.add("ROLE_" + ur.getRoleId());
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("查询用户角色列表失败:{}", e.getMessage());
|
||
roles = java.util.Collections.emptyList();
|
||
}
|
||
loginUser.setRoles(roles);
|
||
|
||
// 查询用户已购买的内容ID列表
|
||
List<Long> purchasedContentIds = new java.util.ArrayList<>();
|
||
try {
|
||
// 创建查询条件,查询用户的购买记录
|
||
ContentPurchaseDto purchaseDto = new ContentPurchaseDto();
|
||
purchaseDto.setUserId(sysUser.getUserId());
|
||
List<ContentPurchase> purchases = contentPurchaseMapper.getList(purchaseDto);
|
||
|
||
// 遍历购买记录,获取内容ID
|
||
for (ContentPurchase purchase : purchases) {
|
||
purchasedContentIds.add(purchase.getContentId());
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("查询用户已购买内容列表失败:{}", e.getMessage());
|
||
purchasedContentIds = java.util.Collections.emptyList();
|
||
}
|
||
loginUser.setPurchasedContentIds(purchasedContentIds);
|
||
|
||
// 查询用户账户信息
|
||
Account account = accountMapper.queryByUserId(sysUser.getUserId());
|
||
loginUser.setAccount(account);
|
||
|
||
// 查询用户积分信息
|
||
PointsAccount pointsAccount = pointsAccountMapper.queryByUserId(sysUser.getUserId());
|
||
loginUser.setPointsAccount(pointsAccount);
|
||
|
||
// 查询并设置用户最近点赞记录
|
||
loginUser.setFavorites(queryRecentFavorites(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户最近查看记录
|
||
loginUser.setHistory(queryRecentViews(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户最近创建记录
|
||
loginUser.setCreate(queryRecentCreatedContent(sysUser.getUserId()));
|
||
|
||
// 查询并设置用户最近购买记录
|
||
loginUser.setHas(queryRecentPurchases(sysUser.getUserId()));
|
||
|
||
// 设置token
|
||
loginUser.setToken(token);
|
||
|
||
// 将LoginUser对象存储到Redis缓存中,使用token作为key
|
||
redisTemplate.opsForValue().set("loginUser:" + token, cn.hutool.json.JSONUtil.toJsonStr(loginUser), 3600, java.util.concurrent.TimeUnit.SECONDS);
|
||
|
||
// 创建LoginUserDto返回对象
|
||
LoginUserDto sysUserDto = buildLoginUserDto(loginUser);
|
||
|
||
log.info("用户:{} 手机号登录成功,token:{}", phone, token);
|
||
|
||
return sysUserDto;
|
||
}
|
||
|
||
/**
|
||
* 根据手机号创建用户
|
||
*
|
||
* @param phone 手机号
|
||
* @return 创建的用户对象
|
||
*/
|
||
private SysUser createUserByPhone(String phone) {
|
||
SysUser sysUser = new SysUser();
|
||
|
||
// 设置用户名(使用手机号作为用户名)
|
||
sysUser.setUserName(phone);
|
||
sysUser.setTel(phone);
|
||
|
||
// 生成随机密码
|
||
String randomPassword = generateRandomPassword();
|
||
sysUser.setPwd(randomPassword);
|
||
|
||
// 调用insert方法创建用户
|
||
return insert(sysUser);
|
||
}
|
||
|
||
/**
|
||
* 生成随机密码
|
||
*
|
||
* @return 随机密码
|
||
*/
|
||
private String generateRandomPassword() {
|
||
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||
Random random = new Random();
|
||
StringBuilder sb = new StringBuilder(12);
|
||
for (int i = 0; i < 12; i++) {
|
||
sb.append(characters.charAt(random.nextInt(characters.length())));
|
||
}
|
||
return sb.toString();
|
||
}
|
||
}
|