- 在AccountFrozen实体和DTO中添加question字段用于记录用户问题或需求 - 在AccountTransaction实体中添加callId字段用于关联冻结单调用 - 更新数据库映射文件中的查询和插入语句以支持新增字段 - 在账户冻结服务中实现question字段的赋值逻辑 - 在冻结单释放时将question和callId传递到交易记录中 - 添加数据库表结构变更SQL脚本为account_frozen表增加question字段 - 添加数据库表结构变更SQL脚本为account_transaction表增加call_id字段及索引 - 在用户注册流程中实现邀请奖励机制为被邀请用户赠送积分
1195 lines
44 KiB
Java
1195 lines
44 KiB
Java
package com.kexue.skills.service.impl;
|
||
|
||
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.common.LoginUserCacheUtil;
|
||
import com.kexue.skills.config.CaptchaConfig;
|
||
import com.kexue.skills.entity.Account;
|
||
import com.kexue.skills.entity.ContentPurchase;
|
||
import com.kexue.skills.entity.SysUser;
|
||
import com.kexue.skills.entity.SysUserRole;
|
||
import com.kexue.skills.entity.dto.ContentPurchaseDto;
|
||
import com.kexue.skills.entity.dto.SessionDto;
|
||
import com.kexue.skills.entity.dto.SysUserDto;
|
||
import com.kexue.skills.entity.request.*;
|
||
import com.kexue.skills.exception.BizException;
|
||
import com.kexue.skills.mapper.AccountMapper;
|
||
import com.kexue.skills.mapper.ContentPurchaseMapper;
|
||
import com.kexue.skills.mapper.CmsContentLikeMapper;
|
||
import com.kexue.skills.mapper.CmsContentViewMapper;
|
||
import com.kexue.skills.mapper.CmsContentMapper;
|
||
import com.kexue.skills.mapper.SysUserMapper;
|
||
import com.kexue.skills.mapper.SysUserRoleMapper;
|
||
import com.kexue.skills.service.SysUserService;
|
||
import com.kexue.skills.service.AccountService;
|
||
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.RedissonClient;
|
||
import org.springframework.stereotype.Service;
|
||
|
||
import javax.annotation.Resource;
|
||
import java.math.BigDecimal;
|
||
import java.security.NoSuchAlgorithmException;
|
||
import java.util.*;
|
||
|
||
/**
|
||
* (SysUser)表服务实现类
|
||
*
|
||
* @author 王志维
|
||
* @since 2025-02-21 23:01:48
|
||
*/
|
||
@Service("sysUserService")
|
||
@Slf4j
|
||
public class SysUserServiceImpl implements SysUserService {
|
||
|
||
@Resource
|
||
private SysUserMapper sysUserMapper;
|
||
|
||
@Resource
|
||
private RedissonClient redissonClient;
|
||
|
||
@Resource
|
||
private CaptchaConfig captchaConfig;
|
||
|
||
|
||
@Resource
|
||
private AccountMapper accountMapper;
|
||
|
||
@Resource
|
||
private SysUserRoleMapper sysUserRoleMapper;
|
||
|
||
@Resource
|
||
private ContentPurchaseMapper contentPurchaseMapper;
|
||
|
||
@Resource
|
||
private CmsContentLikeMapper cmsContentLikeMapper;
|
||
|
||
@Resource
|
||
private CmsContentViewMapper cmsContentViewMapper;
|
||
|
||
@Resource
|
||
private CmsContentMapper cmsContentMapper;
|
||
|
||
@Resource
|
||
private AccountService accountService;
|
||
|
||
/**
|
||
* 分页查询数据
|
||
*
|
||
* @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()+"");
|
||
|
||
// 生成随机邀请码(8位字母数字组合)
|
||
if (sysUser.getInviteCode() == null || sysUser.getInviteCode().isEmpty()) {
|
||
sysUser.setInviteCode(generateInviteCode());
|
||
}
|
||
|
||
// 处理邀请码
|
||
if (sysUser.getInvitedCode() != null && !sysUser.getInvitedCode().isEmpty()) {
|
||
// 验证邀请码是否有效
|
||
SysUser inviter = sysUserMapper.getByInviteCode(sysUser.getInvitedCode());
|
||
if (inviter == null) {
|
||
throw new BizException("邀请码无效");
|
||
}
|
||
|
||
// 设置邀请人用户ID
|
||
sysUser.setInvitedBy(inviter.getUserId());
|
||
|
||
// 给邀请人赠送100积分
|
||
accountService.addGiftBalance(
|
||
inviter.getUserId(),
|
||
BigDecimal.valueOf(100),
|
||
"gift_" + System.currentTimeMillis(),
|
||
null,
|
||
"invite",
|
||
"邀请用户注册赠送"
|
||
);
|
||
}
|
||
|
||
try {
|
||
// 假设客户端已经对密码进行了一次MD5加密,服务端使用双重加密验证
|
||
String encryptedPwd = MD5Util.doubleEncrypt(sysUser.getPwd(), sysUser.getSalt());
|
||
sysUser.setPwd(encryptedPwd);
|
||
} 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);
|
||
|
||
// 注册赠送100积分
|
||
accountService.addGiftBalance(
|
||
sysUser.getUserId(),
|
||
BigDecimal.valueOf(100),
|
||
"gift_" + System.currentTimeMillis(),
|
||
null,
|
||
"register",
|
||
"注册赠送"
|
||
);
|
||
|
||
// 将返回的用户密码设置为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 sysUserUpdateDto 用户更新请求参数
|
||
* @return 实例对象
|
||
*/
|
||
@Override
|
||
@CacheInvalidate(name = "sysUser:", key = "#sysUserUpdateDto.userId")
|
||
@CacheInvalidate(name = "sysUser:username:", key = "#sysUserUpdateDto.userName")
|
||
public SysUser update(SysUserUpdateDto sysUserUpdateDto) {
|
||
if (Objects.nonNull(sysUserUpdateDto.getUserId())){
|
||
// 查询用户信息
|
||
SysUser sysUser = sysUserMapper.queryById(sysUserUpdateDto.getUserId());
|
||
Assert.notNull(sysUser, "用户不存在");
|
||
|
||
// 校验用户名是否已经存在
|
||
if (sysUserUpdateDto.getUserName() != null && !sysUserUpdateDto.getUserName().isEmpty()) {
|
||
SysUser existingUser = sysUserMapper.getByUsername(sysUserUpdateDto.getUserName());
|
||
Assert.isTrue(existingUser == null || existingUser.getUserId().equals(sysUserUpdateDto.getUserId()), "用户名已存在");
|
||
sysUser.setUserName(sysUserUpdateDto.getUserName());
|
||
}
|
||
|
||
// 更新邮箱
|
||
if (sysUserUpdateDto.getEmail() != null) {
|
||
sysUser.setEmail(sysUserUpdateDto.getEmail());
|
||
}
|
||
|
||
// 更新手机号
|
||
if (sysUserUpdateDto.getTel() != null) {
|
||
sysUser.setTel(sysUserUpdateDto.getTel());
|
||
}
|
||
|
||
// 更新状态
|
||
if (sysUserUpdateDto.getEnable() != null) {
|
||
sysUser.setEnable(sysUserUpdateDto.getEnable());
|
||
}
|
||
|
||
// 更新密码(如果有)
|
||
if (sysUserUpdateDto.getPassword() != null && !sysUserUpdateDto.getPassword().isEmpty()) {
|
||
try {
|
||
// 假设客户端已经对密码进行了一次MD5加密,服务端使用双重加密验证
|
||
String encryptedPwd = MD5Util.doubleEncrypt(sysUserUpdateDto.getPassword(), sysUser.getSalt());
|
||
sysUser.setPwd(encryptedPwd);
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
// 执行更新
|
||
sysUserMapper.update(sysUser);
|
||
|
||
// 判断更新的用户是否是当前登录用户,如果是则更新用户缓存
|
||
Long currentUserId = loginUserCacheUtil.getCurrentUserId();
|
||
if (currentUserId != null && currentUserId.equals(sysUserUpdateDto.getUserId())) {
|
||
updateUserCache(currentUserId, sysUser);
|
||
}
|
||
}else {
|
||
// 如果没有用户ID,创建新用户
|
||
SysUser sysUser = new SysUser();
|
||
sysUser.setUserName(sysUserUpdateDto.getUserName());
|
||
sysUser.setEmail(sysUserUpdateDto.getEmail());
|
||
sysUser.setTel(sysUserUpdateDto.getTel());
|
||
sysUser.setEnable(sysUserUpdateDto.getEnable());
|
||
sysUser.setPwd(sysUserUpdateDto.getPassword());
|
||
insert(sysUser);
|
||
}
|
||
return queryById(sysUserUpdateDto.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()), "该用户是管理员,不能删除");
|
||
|
||
// 删除用户名缓存
|
||
redissonClient.getBucket("sysUser:username:" + sysUser.getUserName()).delete();
|
||
|
||
return sysUserMapper.deleteById(userId) > 0;
|
||
}
|
||
|
||
@Override
|
||
public LoginUserDto login(LoginDto loginDto) {
|
||
// 参数验证
|
||
validateLoginParams(loginDto);
|
||
|
||
// 验证码验证
|
||
validateCaptcha(loginDto);
|
||
|
||
// 查询用户(支持用户名或手机号)
|
||
SysUser sysUser = getUserByUsernameOrPhone(loginDto.getUsername());
|
||
|
||
// 密码验证
|
||
validatePassword(loginDto.getPassword(), sysUser);
|
||
|
||
// 检查用户是否已有 token,如果有则使旧 token 失效
|
||
String oldToken = CacheManager.getTokenFromCache(sysUser.getUserName());
|
||
if (oldToken != null && !oldToken.isEmpty()) {
|
||
log.info("检测到用户 {} 已有旧 token: {}", sysUser.getUserName(), oldToken);
|
||
// 从 Redis 中删除旧 token 对应的登录信息
|
||
try {
|
||
redissonClient.getBucket("loginUser:" + oldToken).delete();
|
||
log.info("已删除 Redis 中的旧 token 数据:{}", oldToken);
|
||
} catch (Exception e) {
|
||
log.error("删除 Redis 中的旧 token 失败:{}", e.getMessage());
|
||
}
|
||
// 使旧 token 失效(Sa-Token 内部操作)
|
||
try {
|
||
cn.dev33.satoken.stp.StpUtil.logoutByTokenValue(oldToken);
|
||
log.info("已通过 Sa-Token 使旧 token 失效:{}", oldToken);
|
||
} catch (Exception e) {
|
||
log.error("Sa-Token 使旧 token 失效失败:{}", e.getMessage());
|
||
}
|
||
// 从缓存中移除旧 token
|
||
CacheManager.removeTokenFromCache(sysUser.getUserName());
|
||
log.info("用户:{}的旧 token 已完全失效", sysUser.getUserName());
|
||
}
|
||
|
||
// 查询用户角色列表
|
||
List<String> roles = queryUserRoles(sysUser.getUserId());
|
||
log.info("用户{}的角色列表:{}", sysUser.getUserName(), String.join(",", roles));
|
||
|
||
// 生成token并设置角色
|
||
String token = generateToken(sysUser.getUserId(), roles);
|
||
log.info("设置后Sa-Token中的角色列表:{}", String.join(",", cn.dev33.satoken.stp.StpUtil.getRoleList()));
|
||
|
||
// 构建登录用户信息
|
||
LoginUser loginUser = buildLoginUser(sysUser, token);
|
||
|
||
// 存储登录信息到Redis
|
||
saveLoginUserToRedis(token, loginUser);
|
||
|
||
// 存储token与用户名的映射关系到CacheManager
|
||
CacheManager.putTokenToCache(sysUser.getUserName(), token);
|
||
|
||
// 构建返回对象
|
||
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 = (String) redissonClient.getBucket(captchaKey).get();
|
||
Assert.notNull(captchaText, "验证码已过期");
|
||
Assert.equals(captchaText, loginDto.getCaptchaValue().toLowerCase(), "验证码错误");
|
||
|
||
redissonClient.getBucket(captchaKey).delete();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据用户名查询用户
|
||
*
|
||
* @param username 用户名
|
||
* @return 用户对象
|
||
*/
|
||
private SysUser getUserByUsername(String username) {
|
||
SysUser sysUser = sysUserMapper.getByUsername(username);
|
||
Assert.notNull(sysUser, "用户名不存在");
|
||
return sysUser;
|
||
}
|
||
|
||
/**
|
||
* 根据用户名或手机号查询用户
|
||
*
|
||
* @param usernameOrPhone 用户名或手机号
|
||
* @return 用户对象
|
||
*/
|
||
public SysUser getUserByUsernameOrPhone(String usernameOrPhone) {
|
||
// 先尝试通过用户名查询
|
||
SysUser sysUser = sysUserMapper.getByUsername(usernameOrPhone);
|
||
if (sysUser == null) {
|
||
// 如果用户名查询不到,尝试通过手机号查询
|
||
sysUser = sysUserMapper.getByTel(usernameOrPhone);
|
||
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
|
||
* @param roles 角色列表
|
||
* @return token
|
||
*/
|
||
private String generateToken(Long userId, List<String> roles) {
|
||
// 登录用户
|
||
cn.dev33.satoken.stp.StpUtil.login(userId);
|
||
// 添加角色
|
||
if (!roles.isEmpty()) {
|
||
cn.dev33.satoken.stp.StpUtil.getRoleList().addAll(roles);
|
||
}
|
||
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.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 角色编码列表
|
||
*/
|
||
@Override
|
||
public List<String> queryUserRoles(Long userId) {
|
||
try {
|
||
// 直接通过关联查询获取角色编码列表
|
||
return sysUserRoleMapper.queryRoleCodesByUserId(userId);
|
||
} catch (Exception e) {
|
||
log.error("查询用户角色列表失败:{}", e.getMessage());
|
||
return java.util.Collections.emptyList();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询用户已购买的内容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 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) {
|
||
redissonClient.getBucket("loginUser:" + token)
|
||
.set(cn.hutool.json.JSONUtil.toJsonStr(loginUser), 86400, java.util.concurrent.TimeUnit.SECONDS);
|
||
}
|
||
|
||
/**
|
||
* 构建登录用户返回对象
|
||
*
|
||
* @param loginUser 登录用户信息
|
||
* @return 登录用户返回对象
|
||
*/
|
||
private LoginUserDto buildLoginUserDto(LoginUser loginUser) {
|
||
LoginUserDto sysUserDto = new LoginUserDto();
|
||
sysUserDto.setUserInfo(loginUser.getUserInfo());
|
||
sysUserDto.setToken(loginUser.getToken());
|
||
sysUserDto.setFavorites(loginUser.getFavorites());
|
||
sysUserDto.setHistory(loginUser.getHistory());
|
||
sysUserDto.setCreate(loginUser.getCreate());
|
||
sysUserDto.setHas(loginUser.getHas());
|
||
return sysUserDto;
|
||
}
|
||
|
||
@Resource
|
||
private LoginUserCacheUtil loginUserCacheUtil;
|
||
|
||
@Override
|
||
public boolean resetPassword(ResetPwdDto resetPasswordDto) {
|
||
// 1. 检查登录状态
|
||
Long currentUserId = loginUserCacheUtil.getCurrentUserId();
|
||
Assert.notNull(currentUserId, "请先登录");
|
||
|
||
// 2. 检查参数
|
||
Assert.notNull(resetPasswordDto.getUserId(), "用户ID不能为空");
|
||
Assert.notNull(resetPasswordDto.getNewPassword(), "新密码不能为空");
|
||
|
||
// 3. 检查是否是管理员
|
||
boolean isAdmin = isAdminUser(currentUserId);
|
||
if (!isAdmin) {
|
||
throw new BizException("只有管理员才能重置密码");
|
||
}
|
||
|
||
// 4. 查询用户(支持用户名或手机号)
|
||
SysUser sysUser = sysUserMapper.queryById(resetPasswordDto.getUserId());
|
||
|
||
try {
|
||
// 5. 参照密码创建时候的逻辑,对新密码进行双重加密
|
||
String newEncryptedPwd = MD5Util.doubleEncrypt(resetPasswordDto.getNewPassword(), sysUser.getSalt());
|
||
sysUser.setPwd(newEncryptedPwd);
|
||
sysUserMapper.update(sysUser);
|
||
|
||
// 6. 清除旧的token
|
||
CacheManager.removeTokenFromCache(sysUser.getUserName());
|
||
|
||
return true;
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查用户是否是管理员
|
||
*
|
||
* @param userId 用户 ID
|
||
* @return 是否是管理员
|
||
*/
|
||
private boolean isAdminUser(Long userId) {
|
||
// 先检查是否在管理员用户列表中
|
||
SysUser currentUser = sysUserMapper.queryById(userId);
|
||
if (currentUser != null && Const.ADMIN_USER_LIST.contains(currentUser.getUserName().toLowerCase())) {
|
||
return true;
|
||
}
|
||
|
||
// 检查用户是否有管理员角色(通过角色编码判断)
|
||
List<String> roles = queryUserRoles(userId);
|
||
// 只要有一个角色编码是 "admin",就是管理员
|
||
for (String role : roles) {
|
||
if ("admin".equalsIgnoreCase(role)) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
@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) {
|
||
// 获取当前登录用户ID
|
||
Long currentUserId = loginUserCacheUtil.getCurrentUserId();
|
||
Assert.notNull(currentUserId, "请先登录");
|
||
|
||
// 查询用户是否存在
|
||
SysUser sysUser = sysUserMapper.queryById(userId);
|
||
Assert.notNull(sysUser, "用户不存在");
|
||
|
||
// 检查是否是用户自己修改密码,或者是管理员
|
||
boolean isAdmin = Const.ADMIN_USER_LIST.contains(operator.toLowerCase());
|
||
boolean isSelf = currentUserId.equals(sysUser.getUserId());
|
||
Assert.isTrue(isAdmin || isSelf, "只能修改自己的密码");
|
||
|
||
try {
|
||
// 假设客户端已经对密码进行了一次MD5加密,服务端使用双重加密验证
|
||
String newEncryptedPwd = MD5Util.doubleEncrypt(newPassword, sysUser.getSalt());
|
||
sysUser.setPwd(newEncryptedPwd);
|
||
|
||
// 更新用户密码
|
||
sysUserMapper.update(sysUser);
|
||
|
||
// 清除旧的token
|
||
CacheManager.removeTokenFromCache(sysUser.getUserName());
|
||
|
||
return true;
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public boolean resetPasswordByUsernameOrPhone(String usernameOrPhone, String newPassword, String operator) {
|
||
// 获取当前登录用户ID
|
||
Long currentUserId = loginUserCacheUtil.getCurrentUserId();
|
||
Assert.notNull(currentUserId, "请先登录");
|
||
|
||
// 查询用户是否存在(先尝试通过用户名查询,再尝试通过手机号查询)
|
||
SysUser sysUser = sysUserMapper.getByUsername(usernameOrPhone);
|
||
if (sysUser == null) {
|
||
sysUser = sysUserMapper.getByTel(usernameOrPhone);
|
||
}
|
||
Assert.notNull(sysUser, "用户不存在");
|
||
|
||
// 检查是否是用户自己修改密码,或者是管理员
|
||
boolean isAdmin = Const.ADMIN_USER_LIST.contains(operator.toLowerCase());
|
||
boolean isSelf = currentUserId.equals(sysUser.getUserId());
|
||
Assert.isTrue(isAdmin || isSelf, "只能修改自己的密码");
|
||
|
||
try {
|
||
// 假设客户端已经对密码进行了一次MD5加密,服务端使用双重加密验证
|
||
String newEncryptedPwd = MD5Util.doubleEncrypt(newPassword, sysUser.getSalt());
|
||
sysUser.setPwd(newEncryptedPwd);
|
||
|
||
// 更新用户密码
|
||
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, "手机号不能为空");
|
||
|
||
// 验证手机号格式
|
||
String phoneRegex = "^1[3-9]\\d{9}$";
|
||
if (!phone.matches(phoneRegex)) {
|
||
Assert.isTrue(false, "手机号格式不正确");
|
||
}
|
||
|
||
// 生成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 = (String) redissonClient.getBucket("sms_code:" + phone).get();
|
||
Assert.notNull(cachedCode, "验证码已过期");
|
||
Assert.equals(cachedCode, code, "验证码错误");
|
||
|
||
// 验证成功后,删除验证码
|
||
redissonClient.getBucket("sms_code:" + phone).delete();
|
||
|
||
// 查询用户是否存在
|
||
SysUser sysUser = sysUserMapper.getByTel(phone);
|
||
|
||
// 如果用户不存在,自动创建账号
|
||
if (sysUser == null) {
|
||
sysUser = createUserByPhone(phone, phoneLoginDto.getInviteCode());
|
||
}
|
||
|
||
// 检查用户是否已有 token,如果有则使旧 token 失效
|
||
String oldToken = CacheManager.getTokenFromCache(sysUser.getUserName());
|
||
if (oldToken != null && !oldToken.isEmpty()) {
|
||
log.info("检测到用户 {} 已有旧 token: {}", sysUser.getUserName(), oldToken);
|
||
// 从 Redis 中删除旧 token 对应的登录信息
|
||
try {
|
||
redissonClient.getBucket("loginUser:" + oldToken).delete();
|
||
log.info("已删除 Redis 中的旧 token 数据:{}", oldToken);
|
||
} catch (Exception e) {
|
||
log.error("删除 Redis 中的旧 token 失败:{}", e.getMessage());
|
||
}
|
||
// 使旧 token 失效(Sa-Token 内部操作)
|
||
try {
|
||
cn.dev33.satoken.stp.StpUtil.logoutByTokenValue(oldToken);
|
||
log.info("已通过 Sa-Token 使旧 token 失效:{}", oldToken);
|
||
} catch (Exception e) {
|
||
log.error("Sa-Token 使旧 token 失效失败:{}", e.getMessage());
|
||
}
|
||
// 从缓存中移除旧 token
|
||
CacheManager.removeTokenFromCache(sysUser.getUserName());
|
||
log.info("用户:{}的旧 token 已完全失效", sysUser.getUserName());
|
||
}
|
||
|
||
// 查询用户角色列表
|
||
List<String> roles = queryUserRoles(sysUser.getUserId());
|
||
log.info("用户{}的角色列表:{}", sysUser.getUserName(), String.join(",", roles));
|
||
|
||
// 生成token并设置角色
|
||
String token = generateToken(sysUser.getUserId(), roles);
|
||
log.info("设置后Sa-Token中的角色列表:{}", String.join(",", cn.dev33.satoken.stp.StpUtil.getRoleList()));
|
||
|
||
// 创建LoginUser对象
|
||
LoginUser loginUser = new LoginUser();
|
||
|
||
// 设置用户基本信息
|
||
sysUser.setPwd(null);
|
||
loginUser.setUserInfo(sysUser);
|
||
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);
|
||
|
||
// 查询并设置用户最近点赞记录
|
||
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
|
||
redissonClient.getBucket("loginUser:" + token).set(cn.hutool.json.JSONUtil.toJsonStr(loginUser), 86400, java.util.concurrent.TimeUnit.SECONDS);
|
||
|
||
// 存储token与用户名的映射关系到CacheManager
|
||
CacheManager.putTokenToCache(sysUser.getUserName(), token);
|
||
|
||
// 创建LoginUserDto返回对象
|
||
LoginUserDto sysUserDto = buildLoginUserDto(loginUser);
|
||
|
||
log.info("用户:{} 手机号登录成功,token:{}", phone, token);
|
||
|
||
return sysUserDto;
|
||
}
|
||
|
||
/**
|
||
* 根据手机号创建用户
|
||
*
|
||
* @param phone 手机号
|
||
* @param inviteCode 邀请码
|
||
* @return 创建的用户对象
|
||
*/
|
||
private SysUser createUserByPhone(String phone, String inviteCode) {
|
||
SysUser sysUser = new SysUser();
|
||
|
||
// 设置用户名(使用手机号作为用户名)
|
||
sysUser.setUserName(phone);
|
||
sysUser.setTel(phone);
|
||
|
||
// 生成随机邀请码(8位字母数字组合)
|
||
String generatedInviteCode = generateInviteCode();
|
||
sysUser.setInviteCode(generatedInviteCode);
|
||
|
||
// 处理邀请码
|
||
if (inviteCode != null && !inviteCode.isEmpty()) {
|
||
// 验证邀请码是否有效
|
||
SysUser inviter = sysUserMapper.getByInviteCode(inviteCode);
|
||
if (inviter == null) {
|
||
throw new BizException("邀请码无效");
|
||
}
|
||
// 设置被邀请码和邀请人用户ID
|
||
sysUser.setInvitedCode(inviteCode);
|
||
sysUser.setInvitedBy(inviter.getUserId());
|
||
|
||
// 给邀请人赠送100积分
|
||
accountService.addGiftBalance(
|
||
inviter.getUserId(),
|
||
BigDecimal.valueOf(100),
|
||
"gift_" + System.currentTimeMillis(),
|
||
null,
|
||
"invite",
|
||
"邀请用户注册赠送"
|
||
);
|
||
|
||
// 给被邀请人(新用户)也赠送100积分作为邀请奖励
|
||
// 注意:这里先不执行,等账户创建后再执行
|
||
}
|
||
|
||
// 设置固定salt为666666
|
||
String salt = "666666";
|
||
sysUser.setSalt(salt);
|
||
|
||
// 设置默认密码为000000,按照验证逻辑计算存储值
|
||
// 验证逻辑:MD5Util.doubleEncrypt(password, salt) = MD5Util.encryptToHex(clientEncryptedPassword + salt)
|
||
// 客户端会发送MD5("000000"),服务端存储MD5(MD5("000000") + salt)
|
||
try {
|
||
// 计算客户端加密后的密码:MD5("000000")
|
||
String clientEncryptedPassword = MD5Util.encryptToHex("000000");
|
||
// 计算数据库存储的密码:MD5(clientEncryptedPassword + salt)
|
||
String storedPassword = MD5Util.encryptToHex(clientEncryptedPassword + salt);
|
||
sysUser.setPwd(storedPassword);
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
|
||
// 直接调用mapper插入,跳过insert方法的加密逻辑
|
||
sysUser.setEnable(Const.USER_STATUS_NORMAL);
|
||
sysUser.setDeleteFlag(Const.DELETE_FLAG_NO);
|
||
sysUserMapper.insert(sysUser);
|
||
|
||
// 初始化账户(余额账户)
|
||
Account account = new Account();
|
||
account.setUserId(sysUser.getUserId());
|
||
account.setUserName(sysUser.getUserName());
|
||
account.setBalance(BigDecimal.ZERO);
|
||
account.setFrozenAmount(BigDecimal.ZERO);
|
||
account.setDeleteFlag(Const.DELETE_FLAG_NO);
|
||
accountMapper.insert(account);
|
||
|
||
// 注册赠送100积分
|
||
accountService.addGiftBalance(
|
||
sysUser.getUserId(),
|
||
BigDecimal.valueOf(100),
|
||
"gift_" + System.currentTimeMillis(),
|
||
null,
|
||
"register",
|
||
"注册赠送"
|
||
);
|
||
|
||
// 如果有邀请码,额外赠送100积分作为邀请奖励
|
||
if (inviteCode != null && !inviteCode.isEmpty()) {
|
||
accountService.addGiftBalance(
|
||
sysUser.getUserId(),
|
||
BigDecimal.valueOf(100),
|
||
"gift_" + System.currentTimeMillis(),
|
||
null,
|
||
"invite_reward",
|
||
"通过邀请码注册赠送"
|
||
);
|
||
}
|
||
|
||
return sysUser;
|
||
}
|
||
|
||
/**
|
||
* 生成8位邀请码
|
||
*
|
||
* @return 邀请码
|
||
*/
|
||
private String generateInviteCode() {
|
||
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||
Random random = new Random();
|
||
StringBuilder sb = new StringBuilder(8);
|
||
for (int i = 0; i < 8; i++) {
|
||
sb.append(characters.charAt(random.nextInt(characters.length())));
|
||
}
|
||
return sb.toString();
|
||
}
|
||
|
||
/**
|
||
* 生成随机密码
|
||
*
|
||
* @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();
|
||
}
|
||
|
||
@Override
|
||
public SessionDto createSession(Long userId) {
|
||
SessionDto sessionDto = new SessionDto();
|
||
|
||
// 查询用户信息
|
||
SysUser sysUser = sysUserMapper.queryById(userId);
|
||
if (sysUser == null) {
|
||
throw new RuntimeException("用户不存在");
|
||
}
|
||
|
||
String sessionId = sysUser.getSessionId();
|
||
if (sessionId != null && !sessionId.isEmpty()) {
|
||
// 已有会话,直接返回
|
||
sessionDto.setSessionId(sessionId);
|
||
sessionDto.setNew(false);
|
||
} else {
|
||
// 没有会话,创建新的会话ID
|
||
sessionId = java.util.UUID.randomUUID().toString();
|
||
sysUser.setSessionId(sessionId);
|
||
sysUserMapper.update(sysUser);
|
||
sessionDto.setSessionId(sessionId);
|
||
sessionDto.setNew(true);
|
||
}
|
||
|
||
return sessionDto;
|
||
}
|
||
|
||
/**
|
||
* 更新用户缓存(参照 currentUser 方法的逻辑)
|
||
*
|
||
* @param userId 用户ID
|
||
* @param updatedUser 更新后的用户信息
|
||
*/
|
||
private void updateUserCache(Long userId, SysUser updatedUser) {
|
||
try {
|
||
// 获取当前登录用户的 token
|
||
String token = loginUserCacheUtil.getTokenFromRequest();
|
||
if (token == null || token.isEmpty()) {
|
||
log.warn("无法获取当前用户的 token,跳过缓存更新");
|
||
return;
|
||
}
|
||
|
||
// 从 Redis 中获取 LoginUser 对象
|
||
String loginUserJson = (String) redissonClient.getBucket("loginUser:" + token).get();
|
||
if (loginUserJson == null || loginUserJson.isEmpty()) {
|
||
log.warn("Redis 中未找到 LoginUser 缓存,token: {}", token);
|
||
return;
|
||
}
|
||
|
||
// 解析 JSON 字符串为 LoginUser 对象
|
||
LoginUser loginUser = cn.hutool.json.JSONUtil.toBean(loginUserJson, LoginUser.class);
|
||
|
||
// 验证是否是当前用户
|
||
if (loginUser != null && loginUser.getUserInfo() != null &&
|
||
loginUser.getUserInfo().getUserId().equals(userId)) {
|
||
|
||
// 更新用户信息(密码设置为 null,不暴露给前端)
|
||
updatedUser.setPwd(null);
|
||
loginUser.setUserInfo(updatedUser);
|
||
|
||
// 将更新后的 LoginUser 写回 Redis
|
||
redissonClient.getBucket("loginUser:" + token)
|
||
.set(cn.hutool.json.JSONUtil.toJsonStr(loginUser));
|
||
|
||
log.info("已更新用户 {} 的缓存信息", userId);
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("更新用户缓存失败:{}", e.getMessage(), e);
|
||
}
|
||
}
|
||
}
|