UserCancellationService.java 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. package com.ruoyi.app.user;
  2. import com.ruoyi.common.constant.CacheConstants;
  3. import com.ruoyi.common.core.redis.RedisCache;
  4. import com.ruoyi.common.exception.ServiceException;
  5. import com.ruoyi.common.utils.MessageUtils;
  6. import com.ruoyi.system.domain.InfoUser;
  7. import com.ruoyi.system.mapper.InfoUserMapper;
  8. import org.springframework.beans.factory.annotation.Value;
  9. import org.springframework.stereotype.Service;
  10. import org.springframework.transaction.annotation.Transactional;
  11. import java.time.Instant;
  12. import java.time.temporal.ChronoUnit;
  13. import java.util.Date;
  14. /** 普通用户主动注销;调用登录恢复前必须已经完成短信或第三方身份验证。 */
  15. @Service
  16. public class UserCancellationService {
  17. private final InfoUserMapper users;
  18. private final RedisCache redis;
  19. private final int graceDays;
  20. public UserCancellationService(InfoUserMapper users, RedisCache redis,
  21. @Value("${user.cancellation.grace-days:7}") int graceDays) {
  22. this.users = users; this.redis = redis;
  23. this.graceDays = graceDays > 0 && graceDays <= 365 ? graceDays : 7;
  24. }
  25. public record CancellationView(
  26. @com.fasterxml.jackson.annotation.JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
  27. Date cancellationDeadlineAt, int graceDays) { }
  28. @Transactional(rollbackFor = Exception.class)
  29. public CancellationView request(Long userId) {
  30. // 与认证恢复共用数据库行锁;重复申请不能延长原截止时间。
  31. InfoUser user = users.selectCancellationUserForUpdate(userId);
  32. requireUser(user);
  33. if (!"0".equals(user.getDelFlag())) throw unavailable();
  34. Date deadline = Date.from(Instant.now().plus(graceDays, ChronoUnit.DAYS));
  35. if (users.requestUserCancellation(userId, deadline) != 1) throw unavailable();
  36. redis.deleteKeys(CacheConstants.USER_TOKEN_KEY + userId + ":*");
  37. return new CancellationView(deadline, graceDays);
  38. }
  39. @Transactional(rollbackFor = Exception.class)
  40. public InfoUser authenticatedLogin(Long userId) {
  41. // 登录入口的外层事务持有此锁至签发新会话完成,防止并发注销覆盖恢复。
  42. InfoUser user = users.selectCancellationUserForUpdate(userId);
  43. requireUser(user);
  44. if ("0".equals(user.getDelFlag())) return user;
  45. Date now = new Date();
  46. if (!canRecover(user, now) || users.restoreCancelledUser(userId, now) != 1) throw unavailable();
  47. user.setDelFlag("0"); user.setCancellationDeadlineAt(null);
  48. return user;
  49. }
  50. public static boolean canRecover(InfoUser user, Date now) {
  51. return user != null && "0".equals(user.getUserType()) && "0".equals(user.getStatus())
  52. && "1".equals(user.getDelFlag()) && user.getCancellationDeadlineAt() != null
  53. && user.getCancellationDeadlineAt().after(now);
  54. }
  55. private void requireUser(InfoUser user) {
  56. if (user == null || !"0".equals(user.getUserType()) || !"0".equals(user.getStatus())) throw unavailable();
  57. }
  58. private ServiceException unavailable() { return new ServiceException(MessageUtils.message("no.user.stop")); }
  59. }