第四阶段开发完成

This commit is contained in:
2025-05-18 19:52:17 +08:00
parent 07391c3d69
commit ea60deaa8c
22 changed files with 2513 additions and 65 deletions

View File

@@ -0,0 +1,161 @@
package com.yupi.project.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.project.annotation.AuthCheck;
import com.yupi.project.common.BaseResponse;
import com.yupi.project.common.DeleteRequest;
import com.yupi.project.common.ErrorCode;
import com.yupi.project.common.ResultUtils;
import com.yupi.project.constant.UserConstant;
import com.yupi.project.exception.BusinessException;
import com.yupi.project.exception.ThrowUtils;
import com.yupi.project.model.dto.activationcode.ActivationCodeQueryRequest;
import com.yupi.project.model.dto.activationcode.GenerateCodesRequest;
import com.yupi.project.model.dto.activationcode.RedeemCodeRequest;
import com.yupi.project.model.dto.common.IdRequest;
import com.yupi.project.model.entity.ActivationCode;
import com.yupi.project.model.entity.User;
import com.yupi.project.model.vo.ActivationCodeVO;
import com.yupi.project.service.ActivationCodeService;
import com.yupi.project.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/activation-code")
@Slf4j
public class ActivationCodeController {
@Resource
private ActivationCodeService activationCodeService;
@Resource
private UserService userService;
// region 用户操作
/**
* 用户兑换激活码
*
* @param redeemCodeRequest 激活码请求
* @param request HTTP请求
* @return 是否成功
*/
@PostMapping("/redeem")
public BaseResponse<Boolean> redeemActivationCode(@RequestBody RedeemCodeRequest redeemCodeRequest, HttpServletRequest request) {
if (redeemCodeRequest == null || redeemCodeRequest.getCode() == null || redeemCodeRequest.getCode().trim().isEmpty()) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "激活码不能为空");
}
User loginUser = userService.getCurrentUser(request);
if (loginUser == null) {
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户未登录或登录已失效");
}
boolean result = activationCodeService.redeemCode(loginUser.getId(), redeemCodeRequest.getCode());
return ResultUtils.success(result);
}
// endregion
// region 管理员操作
/**
* 管理员批量生成激活码
*
* @param generateCodesRequest 生成请求
* @return 生成的激活码列表 (只返回code避免信息过多或根据需要返回VO)
*/
@PostMapping("/admin/generate")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<List<ActivationCodeVO>> generateActivationCodes(@RequestBody GenerateCodesRequest generateCodesRequest) {
if (generateCodesRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
if (generateCodesRequest.getCount() == null || generateCodesRequest.getCount() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "生成数量必须大于0");
}
if (generateCodesRequest.getValue() == null || generateCodesRequest.getValue().signum() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "面值必须大于0");
}
try {
List<ActivationCode> codes = activationCodeService.generateCodes(
generateCodesRequest.getCount(),
generateCodesRequest.getValue(),
generateCodesRequest.getExpireTime(),
generateCodesRequest.getBatchId()
);
List<ActivationCodeVO> voList = codes.stream()
.map(code -> {
ActivationCodeVO vo = new ActivationCodeVO();
BeanUtils.copyProperties(code, vo);
return vo;
})
.collect(Collectors.toList());
return ResultUtils.success(voList);
} catch (Exception e) {
log.error("Error generating activation codes", e);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, e.getMessage());
}
}
/**
* 管理员分页查询激活码
*
* @param activationCodeQueryRequest 查询条件
* @return 分页的激活码视图对象
*/
@PostMapping("/admin/list/page")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Page<ActivationCodeVO>> listActivationCodesByPage(@RequestBody ActivationCodeQueryRequest activationCodeQueryRequest) {
if (activationCodeQueryRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
long current = activationCodeQueryRequest.getCurrent();
long size = activationCodeQueryRequest.getPageSize();
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
Page<ActivationCode> activationCodePage = activationCodeService.page(new Page<>(current, size),
activationCodeService.getQueryWrapper(activationCodeQueryRequest));
Page<ActivationCodeVO> activationCodeVOPage = new Page<>(activationCodePage.getCurrent(), activationCodePage.getSize(), activationCodePage.getTotal());
List<ActivationCodeVO> activationCodeVOList = activationCodePage.getRecords().stream().map(activationCode -> {
ActivationCodeVO activationCodeVO = new ActivationCodeVO();
BeanUtils.copyProperties(activationCode, activationCodeVO);
if (activationCode.getIsUsed() == 1 && activationCode.getUserId() != null) {
User user = userService.getById(activationCode.getUserId());
if (user != null) {
activationCodeVO.setUserName(user.getUsername());
}
}
return activationCodeVO;
}).collect(Collectors.toList());
activationCodeVOPage.setRecords(activationCodeVOList);
return ResultUtils.success(activationCodeVOPage);
}
/**
* 管理员删除激活码
*
* @param idRequest 包含激活码ID的请求体
* @param request HTTP请求 (用于权限校验等)
* @return 是否成功
*/
@PostMapping("/admin/delete")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> deleteActivationCode(@RequestBody IdRequest idRequest, HttpServletRequest request) {
if (idRequest == null || idRequest.getId() == null || idRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "激活码ID不能为空");
}
boolean result = activationCodeService.deleteCode(idRequest.getId());
return ResultUtils.success(result);
}
// endregion
}

View File

@@ -0,0 +1,12 @@
package com.yupi.project.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yupi.project.model.entity.ActivationCode;
/**
* @description 针对表【activation_code(激活码表)】的数据库操作Mapper
* @createDate 2024-08-04 10:00:00
* @Entity com.yupi.project.model.entity.ActivationCode
*/
public interface ActivationCodeMapper extends BaseMapper<ActivationCode> {
}

View File

@@ -0,0 +1,70 @@
package com.yupi.project.model.dto.activationcode;
import com.yupi.project.common.PageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 激活码查询请求
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class ActivationCodeQueryRequest extends PageRequest implements Serializable {
/**
* 激活码 (模糊匹配)
*/
private String code;
/**
* 是否已使用 (0 - 未使用, 1 - 已使用)
*/
private Integer isUsed;
/**
* 批次号
*/
private String batchId;
/**
* 使用者用户ID
*/
private Long userId;
/**
* 面值下限 (用于范围查询)
*/
private BigDecimal valueMin;
/**
* 面值上限 (用于范围查询)
*/
private BigDecimal valueMax;
/**
* 过期时间开始 (用于范围查询)
*/
private LocalDateTime expireTimeStart;
/**
* 过期时间结束 (用于范围查询)
*/
private LocalDateTime expireTimeEnd;
/**
* 创建时间开始 (用于范围查询)
*/
private LocalDateTime createTimeStart;
/**
* 创建时间结束 (用于范围查询)
*/
private LocalDateTime createTimeEnd;
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,36 @@
package com.yupi.project.model.dto.activationcode;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 管理员生成激活码请求
*/
@Data
public class GenerateCodesRequest implements Serializable {
/**
* 生成数量
*/
private Integer count;
/**
* 激活码面值
*/
private BigDecimal value;
/**
* 过期时间 (可选, null 表示永不过期)
*/
private LocalDateTime expireTime;
/**
* 批次号 (可选, 用于追踪)
*/
private String batchId;
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,19 @@
package com.yupi.project.model.dto.activationcode;
import lombok.Data;
import java.io.Serializable;
/**
* 用户兑换激活码请求
*/
@Data
public class RedeemCodeRequest implements Serializable {
/**
* 激活码
*/
private String code;
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,13 @@
package com.yupi.project.model.dto.common;
import lombok.Data;
import java.io.Serializable;
/**
* 通用ID请求DTO
*/
@Data
public class IdRequest implements Serializable {
private Long id;
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,80 @@
package com.yupi.project.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 激活码表实体类
*/
@TableName(value = "activation_code")
@Data
public class ActivationCode implements Serializable {
/**
* 主键ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 激活码字符串,确保全局唯一
*/
private String code;
/**
* 激活码对应的面值(金额)
*/
private BigDecimal value;
/**
* 是否已使用0-未使用1-已使用
*/
private Integer isUsed;
/**
* 使用者用户ID (如果已使用)
*/
private Long userId;
/**
* 激活码使用时间 (如果已使用)
*/
private LocalDateTime useTime;
/**
* 激活码过期时间 (NULL表示永不过期)
*/
private LocalDateTime expireTime;
/**
* 生成批次号,方便管理员追踪管理
*/
private String batchId;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 逻辑删除标志0-未删除1-已删除
*/
@TableLogic
private Integer isDelete;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,76 @@
package com.yupi.project.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 激活码视图对象
*/
@Data
public class ActivationCodeVO implements Serializable {
/**
* 主键
*/
private Long id;
/**
* 激活码
*/
private String code;
/**
* 面值
*/
private BigDecimal value;
/**
* 是否已使用 (0 - 未使用, 1 - 已使用)
*/
private Integer isUsed;
/**
* 使用者用户ID (如果已使用)
*/
private Long userId;
/**
* 使用者用户名 (如果已使用, 需要额外查询填充)
*/
private String userName;
/**
* 使用时间 (如果已使用)
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime useTime;
/**
* 过期时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime expireTime;
/**
* 批次号
*/
private String batchId;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,58 @@
package com.yupi.project.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yupi.project.model.entity.ActivationCode;
import com.yupi.project.model.dto.activationcode.ActivationCodeQueryRequest;
// TODO: Import Page, ActivationCodeVO, ActivationCodeQueryRequest when created
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 激活码服务接口
*/
public interface ActivationCodeService extends IService<ActivationCode> {
/**
* (管理员) 批量生成激活码。
*
* @param count 生成数量
* @param value 单个激活码的面值
* @param expireTime 过期时间 (null 表示永不过期)
* @param batchId 批次ID (可由调用者传入或在Service中生成)
* @return 生成的激活码列表
* @throws Exception 如果生成过程中发生错误
*/
List<ActivationCode> generateCodes(int count, BigDecimal value, LocalDateTime expireTime, String batchId) throws Exception;
/**
* (用户) 兑换激活码。
*
* @param currentUserId 当前登录用户的ID
* @param code 激活码字符串
* @return true 如果兑换成功,并增加了用户余额
* @throws com.yupi.project.exception.BusinessException 如果激活码无效、已使用、已过期或兑换失败
*/
boolean redeemCode(Long currentUserId, String code);
/**
* 构建查询条件
*
* @param activationCodeQueryRequest 查询请求
* @return QueryWrapper
*/
QueryWrapper<ActivationCode> getQueryWrapper(ActivationCodeQueryRequest activationCodeQueryRequest);
/**
* 根据ID删除激活码 (逻辑删除)
*
* @param codeId 激活码ID
* @return 是否删除成功
*/
boolean deleteCode(Long codeId);
// TODO: Add method for listing codes (admin)
// Page<ActivationCodeVO> listCodes(ActivationCodeQueryRequest queryRequest);
}

View File

@@ -0,0 +1,215 @@
package com.yupi.project.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.project.common.ErrorCode;
import com.yupi.project.exception.BusinessException;
import com.yupi.project.mapper.ActivationCodeMapper;
import com.yupi.project.model.dto.activationcode.ActivationCodeQueryRequest;
import com.yupi.project.model.entity.ActivationCode;
import com.yupi.project.service.ActivationCodeService;
import com.yupi.project.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Service
@Slf4j
public class ActivationCodeServiceImpl extends ServiceImpl<ActivationCodeMapper, ActivationCode> implements ActivationCodeService {
@Resource
private UserService userService;
// 通常不直接注入Mapper而是通过继承的ServiceImpl的方法操作但如果需要复杂查询可以注入
// @Resource
// private ActivationCodeMapper activationCodeMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public List<ActivationCode> generateCodes(int count, BigDecimal value, LocalDateTime expireTime, String batchId) throws Exception {
if (count <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "生成数量必须大于0");
}
if (value == null || value.compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "激活码面值必须大于0");
}
List<ActivationCode> generatedCodes = new ArrayList<>();
String effectiveBatchId = (batchId == null || batchId.isEmpty()) ? UUID.randomUUID().toString() : batchId;
for (int i = 0; i < count; i++) {
ActivationCode activationCode = new ActivationCode();
// 生成唯一code的简单策略实际项目中可能需要更复杂的防碰撞机制或预生成库
String uniqueCode = UUID.randomUUID().toString().replace("-", "").substring(0, 16).toUpperCase();
// TODO: 考虑code冲突的可能性虽然UUID冲突概率极低但严格来说需要循环检查或数据库唯一约束处理
// 在高并发下,先生成再批量插入,并处理唯一约束异常可能是更好的方式
activationCode.setCode(uniqueCode);
activationCode.setValue(value);
activationCode.setExpireTime(expireTime);
activationCode.setBatchId(effectiveBatchId);
activationCode.setIsUsed(0); // 0 for not used
activationCode.setIsDelete(0); // 0 for not deleted
// createTime and updateTime will be handled by DB default or MyBatis Plus fill strategy
generatedCodes.add(activationCode);
}
boolean saveBatchResult = this.saveBatch(generatedCodes);
if (!saveBatchResult) {
log.error("Failed to batch save activation codes for batchId: {}", effectiveBatchId);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "批量生成激活码失败");
}
log.info("Successfully generated {} activation codes with value {}, batchId: {}.", count, value, effectiveBatchId);
return generatedCodes;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean redeemCode(Long currentUserId, String code) {
if (currentUserId == null || currentUserId <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "无效的用户ID");
}
if (code == null || code.trim().isEmpty()) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "激活码不能为空");
}
// 1. 查询激活码
QueryWrapper<ActivationCode> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("code", code.trim());
// is_delete = 0 会由 @TableLogic 自动处理
ActivationCode activationCode = this.getOne(queryWrapper);
if (activationCode == null) {
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "无效的激活码");
}
// 2. 检查是否已使用
if (activationCode.getIsUsed() == 1) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "此激活码已被使用");
}
// 3. 检查是否过期
if (activationCode.getExpireTime() != null && LocalDateTime.now().isAfter(activationCode.getExpireTime())) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "此激活码已过期");
}
// 4. 增加用户余额
boolean increaseSuccess = userService.increaseBalance(currentUserId, activationCode.getValue());
if (!increaseSuccess) {
// increaseBalance 内部应该会抛出异常如果失败如用户不存在或者返回false
log.error("Failed to increase balance for user {} while redeeming code {}", currentUserId, code);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "余额充值失败,请稍后再试");
}
// 5. 更新激活码状态
activationCode.setIsUsed(1);
activationCode.setUserId(currentUserId);
activationCode.setUseTime(LocalDateTime.now());
// updateTime 会自动更新
boolean updateResult = this.updateById(activationCode);
if (!updateResult) {
log.error("Failed to update activation code {} status after redeeming by user {}. Potential inconsistency!", code, currentUserId);
// 由于余额已增加,但激活码状态更新失败,这是一个危险状态。事务应回滚。
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新激活码状态失败,请联系客服");
}
log.info("User {} successfully redeemed activation code {}. Value: {}", currentUserId, code, activationCode.getValue());
return true;
}
@Override
public QueryWrapper<ActivationCode> getQueryWrapper(ActivationCodeQueryRequest activationCodeQueryRequest) {
QueryWrapper<ActivationCode> queryWrapper = new QueryWrapper<>();
if (activationCodeQueryRequest == null) {
return queryWrapper;
}
String code = activationCodeQueryRequest.getCode();
Integer isUsed = activationCodeQueryRequest.getIsUsed();
String batchId = activationCodeQueryRequest.getBatchId();
Long userId = activationCodeQueryRequest.getUserId();
BigDecimal valueMin = activationCodeQueryRequest.getValueMin();
BigDecimal valueMax = activationCodeQueryRequest.getValueMax();
LocalDateTime expireTimeStart = activationCodeQueryRequest.getExpireTimeStart();
LocalDateTime expireTimeEnd = activationCodeQueryRequest.getExpireTimeEnd();
LocalDateTime createTimeStart = activationCodeQueryRequest.getCreateTimeStart();
LocalDateTime createTimeEnd = activationCodeQueryRequest.getCreateTimeEnd();
String sortField = activationCodeQueryRequest.getSortField();
String sortOrder = activationCodeQueryRequest.getSortOrder();
// 拼接查询条件
if (StringUtils.isNotBlank(code)) {
queryWrapper.like("code", code);
}
if (isUsed != null) {
queryWrapper.eq("isUsed", isUsed);
}
if (StringUtils.isNotBlank(batchId)) {
queryWrapper.eq("batchId", batchId);
}
if (userId != null && userId > 0) {
queryWrapper.eq("userId", userId);
}
if (valueMin != null) {
queryWrapper.ge("value", valueMin);
}
if (valueMax != null) {
queryWrapper.le("value", valueMax);
}
if (expireTimeStart != null) {
queryWrapper.ge("expireTime", expireTimeStart);
}
if (expireTimeEnd != null) {
queryWrapper.le("expireTime", expireTimeEnd);
}
if (createTimeStart != null) {
queryWrapper.ge("createTime", createTimeStart);
}
if (createTimeEnd != null) {
queryWrapper.le("createTime", createTimeEnd);
}
// 默认按创建时间降序
if (StringUtils.isNotBlank(sortField)) {
queryWrapper.orderBy(true, "ascend".equalsIgnoreCase(sortOrder), sortField);
} else {
queryWrapper.orderByDesc("create_time");
}
return queryWrapper;
}
@Override
@Transactional
public boolean deleteCode(Long codeId) {
if (codeId == null || codeId <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "无效的激活码ID");
}
// 检查激活码是否存在这步可选因为removeById如果找不到也不会报错但返回false
ActivationCode existingCode = this.getById(codeId);
if (existingCode == null || existingCode.getIsDelete() == 1) { // 已经是逻辑删除状态
// 可以选择抛出未找到错误,或者认为已经是"已删除"状态返回true
// throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "激活码不存在或已被删除");
log.warn("Attempted to delete a non-existent or already deleted activation code with ID: {}", codeId);
return true; // 或者 false取决于业务定义这里认为已经是目标状态所以返回true
}
// 使用MyBatis Plus的逻辑删除
boolean result = this.removeById(codeId);
if (!result) {
// 这种情况理论上不应该发生,除非并发删除了或者 getById 和 removeById 之间状态变了
log.error("Failed to logically delete activation code with ID: {}. removeById returned false.", codeId);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除激活码失败");
}
log.info("Activation code with ID: {} logically deleted.", codeId);
return true;
}
}

View File

@@ -140,25 +140,51 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
public User getCurrentUser(HttpServletRequest request) {
// 优先从 SecurityContextHolder 获取认证信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated() && !(authentication.getPrincipal() instanceof String && authentication.getPrincipal().equals("anonymousUser"))) {
if (authentication != null && authentication.isAuthenticated() && !(authentication.getPrincipal() instanceof String && "anonymousUser".equals(authentication.getPrincipal()))) {
Object principal = authentication.getPrincipal();
if (principal instanceof User) {
return (User) principal; // principal 已经是 safetyUser
User userFromContext = (User) principal;
// 根据 Context 中的用户ID从数据库重新获取最新的用户信息
User latestUser = this.getById(userFromContext.getId());
if (latestUser != null && latestUser.getIsDeleted() == 0) { // 确保用户未被删除
// 返回脱敏后的最新用户信息
return getSafetyUser(latestUser);
} else {
// 如果根据ID查不到用户了例如被删除了则认为未登录或异常
// 清除可能无效的认证信息
SecurityContextHolder.clearContext();
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户状态异常或已失效,请重新登录");
}
} else if (principal instanceof org.springframework.security.core.userdetails.User) {
// 如果 principal 是 Spring SecurityUser (不太可能在这里,因为我们设置的是 safetyUser)
// 需要转换或重新查询
// For now, assume it's our User object based on login logic
// 如果Spring SecurityUser对象通常包含username
org.springframework.security.core.userdetails.User springUser = (org.springframework.security.core.userdetails.User) principal;
User userByUsername = this.getOne(new QueryWrapper<User>().eq("username", springUser.getUsername()));
if (userByUsername != null && userByUsername.getIsDeleted() == 0) {
return getSafetyUser(userByUsername);
} else {
SecurityContextHolder.clearContext();
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户状态异常或已失效,请重新登录");
}
}
}
// 如果 SecurityContextHolder 中没有,尝试从 session (旧逻辑,作为后备或移除)
// 如果 SecurityContextHolder 中没有有效信息,尝试从 session (旧逻辑,作为后备)
// 注意:如果完全依赖 Spring Security这部分可以考虑移除或调整
Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
if (userObj instanceof User) {
// 最好在这里也验证一下数据库中的用户状态或者确保session中的信息足够可信
return (User) userObj;
User userFromSession = (User) userObj;
User latestUser = this.getById(userFromSession.getId());
if (latestUser != null && latestUser.getIsDeleted() == 0) {
// 如果session中的用户有效也最好将其信息同步到SecurityContext以保持一致性 (可选)
// recreateAuthenticationInSecurityContext(latestUser, request); // 辅助方法,如果需要
return getSafetyUser(latestUser);
}
// Session 中的用户信息无效
request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "会话已过期或无效,请重新登录");
}
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
}
@Override