From 1c2a4edae9190bd37221a7edc971292f2efeef0e Mon Sep 17 00:00:00 2001 From: KilLze Date: Tue, 13 Jan 2026 01:29:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=94=A8=E6=88=B7=E5=B0=81?= =?UTF-8?q?=E7=A6=81=EF=BC=8C=E8=A7=A3=E5=B0=81=EF=BC=8C=E5=B0=81=E7=A6=81?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=EF=BC=88=E7=94=B1=E4=BA=8E=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=91=98=EF=BC=8C=E6=89=80=E4=BB=A5=E7=8E=B0?= =?UTF-8?q?=E5=9C=A8=E5=85=88=E7=94=A8=E6=99=AE=E9=80=9A=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=B0=81=E7=A6=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dating/controller/AdminController.java | 47 ++++++++++++ .../dating/interceptor/TokenInterceptor.java | 2 +- .../com/bao/dating/mapper/UserBanMapper.java | 43 +++++++++++ .../com/bao/dating/pojo/dto/UserBanDTO.java | 13 ++++ .../bao/dating/service/UserBanService.java | 31 ++++++++ .../service/impl/UserBanServiceImpl.java | 74 +++++++++++++++++++ .../java/com/bao/dating/util/UserBanUtil.java | 24 +++++- .../com/bao/dating/mapper/UserBanMapper.xml | 50 +++++++++++++ 8 files changed, 282 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/bao/dating/mapper/UserBanMapper.java create mode 100644 src/main/java/com/bao/dating/pojo/dto/UserBanDTO.java create mode 100644 src/main/java/com/bao/dating/service/UserBanService.java create mode 100644 src/main/java/com/bao/dating/service/impl/UserBanServiceImpl.java create mode 100644 src/main/resources/com/bao/dating/mapper/UserBanMapper.xml diff --git a/src/main/java/com/bao/dating/controller/AdminController.java b/src/main/java/com/bao/dating/controller/AdminController.java index d449500..e7cf97f 100644 --- a/src/main/java/com/bao/dating/controller/AdminController.java +++ b/src/main/java/com/bao/dating/controller/AdminController.java @@ -1,4 +1,51 @@ package com.bao.dating.controller; +import com.bao.dating.common.Result; +import com.bao.dating.common.ResultCode; +import com.bao.dating.pojo.dto.UserBanDTO; +import com.bao.dating.pojo.entity.UserBan; +import com.bao.dating.service.UserBanService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 管理员控制器 + * @author lenovo + */ +@RestController +@RequestMapping("/admin") public class AdminController { + @Autowired + private UserBanService userBanService; + + /** + * 封禁用户 + */ + @PostMapping("/{userId}/ban") + public Result banUser(@PathVariable Long userId, + @RequestBody UserBanDTO userBanDTO) { + userBanDTO.setUserId(userId); + userBanService.banUser(userBanDTO); + return Result.success(ResultCode.SUCCESS, "封禁成功"); + } + + /** + * 解封用户 + */ + @PostMapping("/{userId}/unban") + public Result unbanUser(@PathVariable Long userId) { + + userBanService.unbanUser(userId); + return Result.success(ResultCode.SUCCESS, "解封成功"); + } + + /** + * 查询封禁状态 + */ + @GetMapping("/{userId}/banInfo") + public Result banInfo(@PathVariable Long userId) { + + UserBan ban = userBanService.getActiveBan(userId); + return Result.success(ResultCode.SUCCESS, "查询成功", ban); + } } diff --git a/src/main/java/com/bao/dating/interceptor/TokenInterceptor.java b/src/main/java/com/bao/dating/interceptor/TokenInterceptor.java index e2357f9..2d976ec 100644 --- a/src/main/java/com/bao/dating/interceptor/TokenInterceptor.java +++ b/src/main/java/com/bao/dating/interceptor/TokenInterceptor.java @@ -74,7 +74,7 @@ public class TokenInterceptor implements HandlerInterceptor { String banKey = "user:ban:" + userId; if (Boolean.TRUE.equals(redisTemplate.hasKey(banKey))) { String reason = String.valueOf(redisTemplate.opsForValue().get(banKey)); - log.error("用户 {} 已被封禁:{}", userId, reason); + log.error("用户 {} 已被封禁,原因:{}", userId, reason); response.setStatus(403); response.setContentType("application/json;charset=UTF-8"); diff --git a/src/main/java/com/bao/dating/mapper/UserBanMapper.java b/src/main/java/com/bao/dating/mapper/UserBanMapper.java new file mode 100644 index 0000000..a7d586c --- /dev/null +++ b/src/main/java/com/bao/dating/mapper/UserBanMapper.java @@ -0,0 +1,43 @@ +package com.bao.dating.mapper; + +import com.bao.dating.pojo.entity.UserBan; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface UserBanMapper { + + /** + * 新增封禁记录 + * @param userBan 封禁记录 + * @return 影响行数 + */ + int insertBan(UserBan userBan); + + /** + * 查询是否存在生效中的封禁 + * @param userId 用户ID + * @return 存在返回1,不存在返回0 + */ + int existsActiveBan(@Param("userId") Long userId); + + /** + * 查询生效中的封禁记录 + * @param userId 用户ID + * @return 封禁记录 + */ + UserBan selectActiveBan(@Param("userId") Long userId); + + /** + * 解封用户 + * @param userId 用户ID + * @return 影响行数 + */ + int unbanUser(@Param("userId") Long userId); + + /** + * 定时任务:过期自动解封 + * @return 影响行数 + */ + int updateExpiredBans(); +} diff --git a/src/main/java/com/bao/dating/pojo/dto/UserBanDTO.java b/src/main/java/com/bao/dating/pojo/dto/UserBanDTO.java new file mode 100644 index 0000000..0f54079 --- /dev/null +++ b/src/main/java/com/bao/dating/pojo/dto/UserBanDTO.java @@ -0,0 +1,13 @@ +package com.bao.dating.pojo.dto; +import lombok.Data; + +/** + * 用户封禁数据传输对象 + * @author KilLze + */ +@Data +public class UserBanDTO { + private Long userId; + private String reason; + private Integer banDays; +} diff --git a/src/main/java/com/bao/dating/service/UserBanService.java b/src/main/java/com/bao/dating/service/UserBanService.java new file mode 100644 index 0000000..37a38de --- /dev/null +++ b/src/main/java/com/bao/dating/service/UserBanService.java @@ -0,0 +1,31 @@ +package com.bao.dating.service; + +import com.bao.dating.pojo.dto.UserBanDTO; +import com.bao.dating.pojo.entity.UserBan; + +/** + * 用户封禁服务接口 + * @author KilLze + */ +public interface UserBanService { + + /** + * 封禁用户 + * @param userBanDTO 用户封禁信息 + * + */ + void banUser(UserBanDTO userBanDTO); + + /** + * 解封用户 + * @param userId 用户ID + */ + void unbanUser(Long userId); + + /** + * 查询封禁信息 + * @param userId 用户ID + * @return 封禁信息 + */ + UserBan getActiveBan(Long userId); +} diff --git a/src/main/java/com/bao/dating/service/impl/UserBanServiceImpl.java b/src/main/java/com/bao/dating/service/impl/UserBanServiceImpl.java new file mode 100644 index 0000000..917e4dc --- /dev/null +++ b/src/main/java/com/bao/dating/service/impl/UserBanServiceImpl.java @@ -0,0 +1,74 @@ +package com.bao.dating.service.impl; + +import com.bao.dating.mapper.UserBanMapper; +import com.bao.dating.pojo.dto.UserBanDTO; +import com.bao.dating.pojo.entity.UserBan; +import com.bao.dating.service.UserBanService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; + +@Service +public class UserBanServiceImpl implements UserBanService { + + @Autowired + private UserBanMapper userBanMapper; + + @Autowired + private RedisTemplate redisTemplate; + + @Override + public void banUser(UserBanDTO userBanDTO) { + + // 已被封禁,直接拒绝 + if (userBanMapper.existsActiveBan(userBanDTO.getUserId()) > 0) { + throw new RuntimeException("用户已处于封禁状态"); + } + + LocalDateTime now = LocalDateTime.now(); + LocalDateTime endTime = userBanDTO.getBanDays() == null ? null : now.plusDays(userBanDTO.getBanDays()); + + // 1. 写数据库 + UserBan ban = new UserBan(); + ban.setUserId(userBanDTO.getUserId()); + ban.setReason(userBanDTO.getReason()); + ban.setBanStartTime(now); + ban.setBanEndTime(endTime); + ban.setStatus(1); + userBanMapper.insertBan(ban); + + // 2. 写 Redis + String key = "user:ban:" + userBanDTO.getUserId(); + if (userBanDTO.getBanDays() == null) { + redisTemplate.opsForValue().set(key, userBanDTO.getReason()); + } else { + redisTemplate.opsForValue().set(key, userBanDTO.getReason(), userBanDTO.getBanDays(), TimeUnit.DAYS); + } + + // 3. 踢下线 + redisTemplate.delete("login:token:" + userBanDTO.getUserId()); + } + + /** + * 解封用户 + */ + @Override + public void unbanUser(Long userId) { + // 更新数据库 + userBanMapper.unbanUser(userId); + + // 删除 Redis + redisTemplate.delete("user:ban:" + userId); + } + + /** + * 获取用户封禁信息 + */ + @Override + public UserBan getActiveBan(Long userId) { + return userBanMapper.selectActiveBan(userId); + } +} diff --git a/src/main/java/com/bao/dating/util/UserBanUtil.java b/src/main/java/com/bao/dating/util/UserBanUtil.java index 13a0745..b53236a 100644 --- a/src/main/java/com/bao/dating/util/UserBanUtil.java +++ b/src/main/java/com/bao/dating/util/UserBanUtil.java @@ -5,6 +5,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; +import java.util.concurrent.TimeUnit; + /** * 用户封禁验证工具类 * 提供统一的用户封禁状态检查功能 @@ -27,7 +29,27 @@ public class UserBanUtil { String banKey = "user:ban:" + userId; if (Boolean.TRUE.equals(redisTemplate.hasKey(banKey))) { String reason = (String) redisTemplate.opsForValue().get(banKey); - throw new RuntimeException("账号已被封禁:" + reason); + + // 获取剩余过期时间(秒) + Long ttlSeconds = redisTemplate.getExpire(banKey, TimeUnit.SECONDS); + String remainingTime = ""; + if (ttlSeconds != null && ttlSeconds > 0) { + long days = ttlSeconds / (24 * 3600); + long hours = (ttlSeconds % (24 * 3600)) / 3600; + long minutes = (ttlSeconds % 3600) / 60; + + if (days > 0) { + remainingTime = ",剩余时间:" + days + "天" + hours + "小时"; + } else if (hours > 0) { + remainingTime = ",剩余时间:" + hours + "小时" + minutes + "分钟"; + } else { + remainingTime = ",剩余时间:" + minutes + "分钟"; + } + } else { + remainingTime = ",永久封禁"; + } + + throw new RuntimeException("账号已被封禁,原因:" + reason + remainingTime); } } diff --git a/src/main/resources/com/bao/dating/mapper/UserBanMapper.xml b/src/main/resources/com/bao/dating/mapper/UserBanMapper.xml new file mode 100644 index 0000000..915d251 --- /dev/null +++ b/src/main/resources/com/bao/dating/mapper/UserBanMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + INSERT INTO user_ban + (user_id, reason, ban_start_time, ban_end_time, status) + VALUES + (#{userId}, #{reason}, #{banStartTime}, #{banEndTime}, #{status}) + + + + + + + + + + + UPDATE user_ban + SET status = 0 + WHERE user_id = #{userId} + AND status = 1 + + + + + UPDATE user_ban + SET status = 0 + WHERE status = 1 + AND ban_end_time IS NOT NULL + AND ban_end_time < NOW() + + + \ No newline at end of file