diff --git a/pom.xml b/pom.xml
index 2079d64..c7629ea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,6 +26,11 @@
3.5.10
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
org.springframework.boot
spring-boot-starter-web
@@ -56,6 +61,12 @@
spring-boot-starter-test
test
+
+
+ org.mockito
+ mockito-inline
+ test
+
diff --git a/src/main/java/com/bao/dating/anno/Log.java b/src/main/java/com/bao/dating/anno/Log.java
new file mode 100644
index 0000000..d867395
--- /dev/null
+++ b/src/main/java/com/bao/dating/anno/Log.java
@@ -0,0 +1,11 @@
+package com.bao.dating.anno;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Log {
+}
diff --git a/src/main/java/com/bao/dating/aspect/LoggingAspect.java b/src/main/java/com/bao/dating/aspect/LoggingAspect.java
new file mode 100644
index 0000000..e104d61
--- /dev/null
+++ b/src/main/java/com/bao/dating/aspect/LoggingAspect.java
@@ -0,0 +1,48 @@
+package com.bao.dating.aspect;
+
+
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.*;
+import org.springframework.stereotype.Component;
+
+/**
+ * 日志切面
+ * @author KilLze
+ */
+@Aspect
+@Component
+@Slf4j
+public class LoggingAspect {
+ @Pointcut("execution(* com.bao.dating.service.impl.*.*(..))")
+ private void pt(){}
+
+ /**
+ * 方法执行前执行
+ * @param joinPoint 方法参数
+ */
+ @Before("pt()")
+ public void logBeforeMethod(JoinPoint joinPoint){
+ // 获取方法名
+ String methodName = joinPoint.getSignature().getName();
+ // 获取参数
+ Object[] args = joinPoint.getArgs();
+ log.info("方法 {} 开始执行,参数: {}", methodName, args);
+ }
+
+ /**
+ * 方法执行成功后执行
+ * @param joinPoint 方法参数
+ */
+ @AfterReturning(pointcut = "pt()", returning = "result")
+ public void logAfterMethod(JoinPoint joinPoint, Object result){
+ String methodName = joinPoint.getSignature().getName();
+ log.info("方法 {} 执行成功,返回值: {}", methodName, result);
+ }
+
+ @AfterThrowing(pointcut = "pt()", throwing = "exception")
+ public void logAfterThrowing(JoinPoint joinPoint, Exception exception){
+ String methodName = joinPoint.getSignature().getName();
+ log.error("方法 {} 执行异常", methodName, exception);
+ }
+}
diff --git a/src/main/java/com/bao/dating/aspect/OperateLogAspect.java b/src/main/java/com/bao/dating/aspect/OperateLogAspect.java
new file mode 100644
index 0000000..440f141
--- /dev/null
+++ b/src/main/java/com/bao/dating/aspect/OperateLogAspect.java
@@ -0,0 +1,64 @@
+package com.bao.dating.aspect;
+
+
+import com.bao.dating.context.UserContext;
+import com.bao.dating.mapper.OperateLogMapper;
+import com.bao.dating.pojo.entity.OperateLog;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+
+/**
+ * 记录操作日志
+ * @author KilLze
+ */
+@Slf4j
+@Aspect
+@Component
+public class OperateLogAspect {
+
+ @Autowired
+ private OperateLogMapper operateLogMapper;
+
+ @Around("@annotation(com.bao.dating.anno.Log)")
+ public Object logOperate(ProceedingJoinPoint pjp) throws Throwable{
+
+ // 记录方法开始的时间
+ long startTime = System.currentTimeMillis();
+
+ // 执行目标方法
+ Object result = pjp.proceed();
+
+ long endTime = System.currentTimeMillis();
+ long costTime = endTime - startTime;
+
+
+ // 构建日志对象
+ OperateLog operatelog = new OperateLog();
+ operatelog.setOperateUserId(getUserId());
+ operatelog.setOperateTime(LocalDateTime.now());
+ operatelog.setClassName(pjp.getTarget().getClass().getName());
+ operatelog.setMethodName(pjp.getSignature().getName());
+ operatelog.setMethodParams(Arrays.toString(pjp.getArgs()));
+ operatelog.setReturnValue(result != null ? result.toString() : "void");
+ operatelog.setCostTime(costTime);
+
+ log.info("记录操作日志: {}", operatelog);
+
+ operateLogMapper.insert(operatelog);
+
+ return result;
+ }
+
+ private Long getUserId() {
+ return UserContext.getUserId();
+ }
+
+}
diff --git a/src/main/java/com/bao/dating/config/RedisConfig.java b/src/main/java/com/bao/dating/config/RedisConfig.java
new file mode 100644
index 0000000..93dcbbd
--- /dev/null
+++ b/src/main/java/com/bao/dating/config/RedisConfig.java
@@ -0,0 +1,33 @@
+package com.bao.dating.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * Redis 配置类
+ * @author KilLze
+ */
+@Configuration
+public class RedisConfig {
+ @Bean
+ public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+ // 创建RedisTemplate对象
+ RedisTemplate redisTemplate = new RedisTemplate<>();
+ // 设置redis的连接工厂对象
+ redisTemplate.setConnectionFactory(redisConnectionFactory);
+
+ // 设置redis key的序列化器
+ redisTemplate.setKeySerializer(new StringRedisSerializer());
+ // 设置value的序列化器
+ redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
+ // 设置hash类型的key和value的序列化器
+ redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+ redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
+
+ return redisTemplate;
+ }
+}
diff --git a/src/main/java/com/bao/dating/controller/PostController.java b/src/main/java/com/bao/dating/controller/PostController.java
index 648e32f..b4fb20b 100644
--- a/src/main/java/com/bao/dating/controller/PostController.java
+++ b/src/main/java/com/bao/dating/controller/PostController.java
@@ -1,6 +1,7 @@
package com.bao.dating.controller;
+import com.bao.dating.anno.Log;
import com.bao.dating.common.Result;
import com.bao.dating.common.ResultCode;
import com.bao.dating.pojo.dto.PostRequestDTO;
@@ -30,6 +31,7 @@ public class PostController {
* @param files 媒体文件数组
* @return 上传后的文件URL列表
*/
+ @Log
@PostMapping(value = "/upload", consumes = "multipart/form-data")
public Result> uploadMedia(@RequestParam("files") MultipartFile[] files) {
List fileUrls = postService.uploadMedia(files);
@@ -41,6 +43,7 @@ public class PostController {
* @param postDTO 动态信息
* @return 发布的动态对象
*/
+ @Log
@PostMapping( "/createPost")
public Result createPostJson(@RequestBody PostRequestDTO postDTO) {
// 调用 Service 层处理发布动态业务逻辑
@@ -54,10 +57,11 @@ public class PostController {
* @param postIds 动态ID
* @return 删除结果
*/
+ @Log
@PostMapping("/deletePost")
public Result deleteById(@RequestBody List postIds){
int deletedCount = postService.deletePostById(postIds);
- return Result.success(ResultCode.SUCCESS_DELETE, deletedCount > 0 ? "成功删除" : "删除失败,该动态不存在", null);
+ return Result.success(ResultCode.SUCCESS_DELETE, "成功删除" + deletedCount + "条动态", null);
}
/**
@@ -65,7 +69,7 @@ public class PostController {
* @param postId 动态ID
* @return 动态对象
*/
- @PostMapping("/{postId}")
+ @GetMapping("/{postId}")
public Result getPostById(@PathVariable Long postId) {
PostEditVO postEditVO = postService.getPostForEdit(postId);
return Result.success(ResultCode.SUCCESS,"查询成功", postEditVO);
@@ -77,6 +81,7 @@ public class PostController {
* @param postRequestDTO 动态信息
* @return 更新后的动态对象
*/
+ @Log
@PostMapping("/{postId}/updatePost")
public Result updatePost(@PathVariable Long postId, @RequestBody PostRequestDTO postRequestDTO) {
PostEditVO result = postService.updatePost(postId, postRequestDTO);
diff --git a/src/main/java/com/bao/dating/controller/PostFavoriteController.java b/src/main/java/com/bao/dating/controller/PostFavoriteController.java
index fd44e56..4f60435 100644
--- a/src/main/java/com/bao/dating/controller/PostFavoriteController.java
+++ b/src/main/java/com/bao/dating/controller/PostFavoriteController.java
@@ -4,7 +4,6 @@ import com.bao.dating.common.Result;
import com.bao.dating.common.ResultCode;
import com.bao.dating.pojo.entity.User;
import com.bao.dating.service.PostFavoriteService;
-import com.bao.dating.service.PostService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
diff --git a/src/main/java/com/bao/dating/controller/PostLikeController.java b/src/main/java/com/bao/dating/controller/PostLikeController.java
index ef246e7..df0cdc6 100644
--- a/src/main/java/com/bao/dating/controller/PostLikeController.java
+++ b/src/main/java/com/bao/dating/controller/PostLikeController.java
@@ -2,13 +2,11 @@ package com.bao.dating.controller;
import com.bao.dating.common.Result;
import com.bao.dating.common.ResultCode;
-import com.bao.dating.mapper.PostLikeMapper;
import com.bao.dating.service.PostLikeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
-import java.util.Objects;
@RestController
@RequestMapping("/posts")
diff --git a/src/main/java/com/bao/dating/controller/UserController.java b/src/main/java/com/bao/dating/controller/UserController.java
index b62cdfd..aad6328 100644
--- a/src/main/java/com/bao/dating/controller/UserController.java
+++ b/src/main/java/com/bao/dating/controller/UserController.java
@@ -1,5 +1,6 @@
package com.bao.dating.controller;
+import com.bao.dating.anno.Log;
import com.bao.dating.common.Result;
import com.bao.dating.common.ResultCode;
import com.bao.dating.context.UserContext;
@@ -8,10 +9,13 @@ import com.bao.dating.pojo.dto.UserLoginDTO;
import com.bao.dating.pojo.vo.UserInfoVO;
import com.bao.dating.pojo.vo.UserLoginVO;
import com.bao.dating.service.UserService;
+import io.jsonwebtoken.Jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.http.HttpServletRequest;
+
/**
* 用户接口
*
@@ -34,6 +38,17 @@ public class UserController {
return Result.success(ResultCode.SUCCESS, "登录成功", userloginVO);
}
+ /**
+ * 退出登录
+ * 从请求头中获取token并将其加入黑名单
+ */
+ @PostMapping("/logout")
+ public Result logout(HttpServletRequest request) {
+ String token = request.getHeader("token");
+ userService.logout(token);
+ return Result.success(ResultCode.SUCCESS,"退出登录成功",null);
+ }
+
/**
* 获取用户信息
* @return 用户信息
@@ -50,6 +65,7 @@ public class UserController {
* @param file 头像文件
* @return 上传后的文件URL列表
*/
+ @Log
@PostMapping(value = "/info/uploadAvatar", consumes = "multipart/form-data")
public Result uploadAvatar(@RequestParam("file") MultipartFile file) {
String fileUrl = userService.uploadAvatar(file);
@@ -61,6 +77,7 @@ public class UserController {
* @param file 背景文件
* @return 上传后的文件URL列表
*/
+ @Log
@PostMapping(value = "/info/uploadBackground", consumes = "multipart/form-data")
public Result uploadBackground(@RequestParam("file") MultipartFile file) {
String fileUrl = userService.uploadBackground(file);
@@ -72,6 +89,7 @@ public class UserController {
* @param userInfoUpdateDTO 用户信息更新参数
* @return 更新后的用户信息
*/
+ @Log
@PostMapping("/info/update")
public Result userInfoUpdate(@RequestBody UserInfoUpdateDTO userInfoUpdateDTO) {
Long userId = UserContext.getUserId();
diff --git a/src/main/java/com/bao/dating/handler/GlobalExceptionHandler.java b/src/main/java/com/bao/dating/handler/GlobalExceptionHandler.java
index 4e1993d..25eb6c3 100644
--- a/src/main/java/com/bao/dating/handler/GlobalExceptionHandler.java
+++ b/src/main/java/com/bao/dating/handler/GlobalExceptionHandler.java
@@ -4,7 +4,6 @@ import com.bao.dating.common.Result;
import com.bao.dating.common.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
-import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -15,7 +14,7 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.util.stream.Collectors;
/**
* 全局异常处理器
@@ -32,7 +31,12 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("参数验证失败: {}", e.getMessage());
- return Result.error(ResultCode.PARAM_ERROR, e.getBindingResult().getFieldError().getDefaultMessage());
+ String msg = e.getBindingResult()
+ .getFieldErrors()
+ .stream()
+ .map(error -> error.getField() + ":" + error.getDefaultMessage())
+ .collect(Collectors.joining("; "));
+ return Result.error(ResultCode.PARAM_ERROR, msg);
}
/**
diff --git a/src/main/java/com/bao/dating/interceptor/TokenInterceptor.java b/src/main/java/com/bao/dating/interceptor/TokenInterceptor.java
index e520ce8..8e11f59 100644
--- a/src/main/java/com/bao/dating/interceptor/TokenInterceptor.java
+++ b/src/main/java/com/bao/dating/interceptor/TokenInterceptor.java
@@ -5,7 +5,10 @@ import javax.servlet.http.HttpServletResponse;
import com.bao.dating.context.UserContext;
import com.bao.dating.util.JwtUtil;
+import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
@@ -18,6 +21,10 @@ import org.springframework.web.servlet.HandlerInterceptor;
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
+
+ @Autowired
+ private RedisTemplate redisTemplate;
+
/**
* 在请求处理之前进行拦截
* 从请求头或URL参数中获取token,验证其有效性,并将用户ID保存到ThreadLocal中
@@ -45,13 +52,38 @@ public class TokenInterceptor implements HandlerInterceptor {
if (!JwtUtil.validateToken(token)) {
log.error("Token无效或已过期");
response.setStatus(401);
+ response.setContentType("application/json;charset=UTF-8");
+ response.getWriter().write("Token无效或已过期");
+ return false;
+ }
+
+ // 检查 token 是否在黑名单中
+ Object blacklistToken = redisTemplate.opsForValue().get("jwt:blacklist:" + token);
+ if (blacklistToken != null) {
+ log.error("Token已在黑名单中");
+ response.setStatus(401);
+ response.setContentType("application/json;charset=UTF-8");
+ response.getWriter().write("登录已失效, 请重新登录");
return false;
}
// 解析 token
String userId = JwtUtil.getSubjectFromToken(token);
+
+ // 从Redis获取存储的token进行比对
+ Object redisTokenObj = redisTemplate.opsForValue().get("login:token:" + userId);
+ String redisToken = redisTokenObj != null ? redisTokenObj.toString() : null;
+
+ // 验证Redis中的token是否存在且匹配
+ if (redisToken == null || !redisToken.equals(token)) {
+ log.error("登录已失效");
+ response.setStatus(401);
+ response.setContentType("application/json;charset=UTF-8");
+ response.getWriter().write("登录已失效");
+ return false;
+ }
+
log.info("用户: {}", userId);
-
// 保存 userId 到 ThreadLocal
UserContext.setUserId(Long.valueOf(userId));
return true;
diff --git a/src/main/java/com/bao/dating/mapper/CommentsMapper.java b/src/main/java/com/bao/dating/mapper/CommentsMapper.java
index 1de2c6d..78ca350 100644
--- a/src/main/java/com/bao/dating/mapper/CommentsMapper.java
+++ b/src/main/java/com/bao/dating/mapper/CommentsMapper.java
@@ -18,4 +18,11 @@ public interface CommentsMapper {
// 根据动态ID查询评论列表
@Select("SELECT * FROM comments WHERE post_id = #{post_id} ORDER BY created_at DESC")
List getCommentsByPostId(@Param("post_id") Long post_id);
+
+ /**
+ * 根据动态ID批量删除评论
+ * @param postIds
+ * @return
+ */
+ int deleteCommentsByPostIds(@Param("postIds") List postIds);
}
diff --git a/src/main/java/com/bao/dating/mapper/OperateLogMapper.java b/src/main/java/com/bao/dating/mapper/OperateLogMapper.java
new file mode 100644
index 0000000..e97c421
--- /dev/null
+++ b/src/main/java/com/bao/dating/mapper/OperateLogMapper.java
@@ -0,0 +1,19 @@
+package com.bao.dating.mapper;
+
+
+import com.bao.dating.pojo.entity.OperateLog;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 操作日志Mapper
+ * @author KilLze
+ */
+@Mapper
+public interface OperateLogMapper {
+
+ @Insert("insert into operate_log (operate_user_id, operate_time, class_name, method_name, method_params, return_value, cost_time) " +
+ "values (#{operateUserId}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")
+ public void insert(OperateLog log);
+
+}
diff --git a/src/main/java/com/bao/dating/mapper/PostFavoriteMapper.java b/src/main/java/com/bao/dating/mapper/PostFavoriteMapper.java
index a0aa443..88f05e9 100644
--- a/src/main/java/com/bao/dating/mapper/PostFavoriteMapper.java
+++ b/src/main/java/com/bao/dating/mapper/PostFavoriteMapper.java
@@ -12,4 +12,12 @@ public interface PostFavoriteMapper {
List selectUserIDByPostID(@Param("postId") Long postId);
int addPostFavorite(PostFavorite postFavorite);
int deletePostFavorite(@Param("postId") Long postId);
+
+ /**
+ * 批量删除收藏
+ * @param postIds
+ * @return
+ */
+ int deleteFavoritesByPostIds(@Param("postIds") List postIds);
+
}
diff --git a/src/main/java/com/bao/dating/mapper/PostLikeMapper.java b/src/main/java/com/bao/dating/mapper/PostLikeMapper.java
index 41783e4..50e28be 100644
--- a/src/main/java/com/bao/dating/mapper/PostLikeMapper.java
+++ b/src/main/java/com/bao/dating/mapper/PostLikeMapper.java
@@ -4,6 +4,8 @@ import com.bao.dating.pojo.entity.PostLike;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
+import java.util.List;
+
@Mapper
public interface PostLikeMapper {
/**
@@ -31,4 +33,12 @@ public interface PostLikeMapper {
* @return
*/
int deleteByPostIdAndUserId(@Param("postId") Long postId, @Param("userId") Long userId);
+
+ /**
+ * 批量删除点赞记录
+ *
+ * @param postIds
+ * @return
+ */
+ int deleteLikesByPostIds(@Param("postIds") List postIds);
}
diff --git a/src/main/java/com/bao/dating/mapper/PostMapper.java b/src/main/java/com/bao/dating/mapper/PostMapper.java
index 8380a2f..5a3ac72 100644
--- a/src/main/java/com/bao/dating/mapper/PostMapper.java
+++ b/src/main/java/com/bao/dating/mapper/PostMapper.java
@@ -21,11 +21,11 @@ public interface PostMapper {
void insert(Post post);
/**
- * 根据ID删除动态
+ * 根据ID修改动态状态
*
* @param postIds 动态ID
*/
- int deletePostByIds(List postIds);
+ int updatePublicById(@Param("postIds") List postIds, @Param("userId") Long userId);
/**
* 根据ID查询动态
diff --git a/src/main/java/com/bao/dating/pojo/dto/UserInfoUpdateDTO.java b/src/main/java/com/bao/dating/pojo/dto/UserInfoUpdateDTO.java
index 97dd6e9..a3bc0ab 100644
--- a/src/main/java/com/bao/dating/pojo/dto/UserInfoUpdateDTO.java
+++ b/src/main/java/com/bao/dating/pojo/dto/UserInfoUpdateDTO.java
@@ -2,6 +2,7 @@ package com.bao.dating.pojo.dto;
import lombok.Data;
+import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@@ -11,7 +12,7 @@ import java.util.List;
* @author KilLze
*/
@Data
-public class UserInfoUpdateDTO {
+public class UserInfoUpdateDTO implements Serializable {
private Long userId;
private String userName;
private String nickname;
diff --git a/src/main/java/com/bao/dating/pojo/entity/OperateLog.java b/src/main/java/com/bao/dating/pojo/entity/OperateLog.java
new file mode 100644
index 0000000..c17f0c1
--- /dev/null
+++ b/src/main/java/com/bao/dating/pojo/entity/OperateLog.java
@@ -0,0 +1,30 @@
+package com.bao.dating.pojo.entity;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 操作日志
+ * @author KilLze
+ */
+@Data
+public class OperateLog implements Serializable {
+ /** ID */
+ private Long id;
+ /** 操作人ID */
+ private Long operateUserId;
+ /** 操作时间 */
+ private LocalDateTime operateTime;
+ /** 操作类名 */
+ private String className;
+ /** 操作方法名 */
+ private String methodName;
+ /** 操作方法参数 */
+ private String methodParams;
+ /** 操作方法返回值 */
+ private String returnValue;
+ /** 操作耗时 */
+ private Long costTime;
+}
diff --git a/src/main/java/com/bao/dating/service/PostService.java b/src/main/java/com/bao/dating/service/PostService.java
index 627fed6..b598daa 100644
--- a/src/main/java/com/bao/dating/service/PostService.java
+++ b/src/main/java/com/bao/dating/service/PostService.java
@@ -27,7 +27,7 @@ public interface PostService {
Post createPost(PostRequestDTO postRequestDTO);
/**
- * 批量删除动态
+ * 批量删除动态(将动态状态改为已删除)
* @param postIds 动态ID
* @return 删除的动态对象
*/
diff --git a/src/main/java/com/bao/dating/service/UserService.java b/src/main/java/com/bao/dating/service/UserService.java
index 5705ceb..8dd074c 100644
--- a/src/main/java/com/bao/dating/service/UserService.java
+++ b/src/main/java/com/bao/dating/service/UserService.java
@@ -18,6 +18,13 @@ public interface UserService {
*/
UserLoginVO userLogin(UserLoginDTO userLoginDTO);
+ /**
+ * 退出登录
+ * @param token 登录凭证
+ * @return 注册结果
+ */
+ void logout(String token);
+
/**
* 查询个人信息
* @param userId 动态ID
diff --git a/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java b/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java
index 5a430cd..2b47c9d 100644
--- a/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java
+++ b/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java
@@ -4,17 +4,22 @@ import com.bao.dating.common.aliyun.GreenImageScan;
import com.bao.dating.common.aliyun.GreenTextScan;
import com.bao.dating.common.result.GreenAuditResult;
import com.bao.dating.context.UserContext;
+import com.bao.dating.mapper.CommentsMapper;
+import com.bao.dating.mapper.PostFavoriteMapper;
+import com.bao.dating.mapper.PostLikeMapper;
import com.bao.dating.mapper.PostMapper;
import com.bao.dating.pojo.dto.PostRequestDTO;
import com.bao.dating.pojo.entity.Post;
import com.bao.dating.pojo.vo.PostEditVO;
import com.bao.dating.service.PostService;
import com.bao.dating.common.aliyun.AliOssUtil;
+import com.bao.dating.service.UserService;
import com.bao.dating.util.FileUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@@ -43,6 +48,15 @@ public class PostServiceImpl implements PostService {
@Autowired
private PostMapper postMapper;
+ @Autowired
+ private PostLikeMapper postLikeMapper;
+
+ @Autowired
+ private PostFavoriteMapper postFavoriteMapper;
+
+ @Autowired
+ private CommentsMapper commentsMapper;
+
/**
* 上传媒体文件
* @param files 媒体文件数组
@@ -177,7 +191,7 @@ public class PostServiceImpl implements PostService {
}
/**
- * 批量删除动态
+ * 批量删除动态(将动态状态改为已删除)
*
* @param postIds 动态ID
* @return 删除的动态对象
@@ -188,19 +202,21 @@ public class PostServiceImpl implements PostService {
// 判断用户权限
Long userId = UserContext.getUserId();
- // 遍历所有要删除的帖子ID,验证权限
- for (Long postId : postIds) {
- Post post = postMapper.selectById(postId);
- if (post == null) {
- throw new RuntimeException("动态不存在");
- }
- // 验证用户权限
- if (post.getUserId() == null || !post.getUserId().equals(userId)) {
- throw new RuntimeException("无权限删除此动态");
- }
+ if (CollectionUtils.isEmpty(postIds)) {
+ return 0;
}
- // 批量删除动态
- return postMapper.deletePostByIds(postIds);
+ int affected = postMapper.updatePublicById(postIds, userId);
+
+ if (affected == 0) {
+ throw new RuntimeException("未删除任何动态,可能无权限或动态不存在");
+ }
+
+ // 删除动态下的评论、点赞、收藏
+ commentsMapper.deleteCommentsByPostIds(postIds);
+ postLikeMapper.deleteLikesByPostIds(postIds);
+ postFavoriteMapper.deleteFavoritesByPostIds(postIds);
+
+ return affected;
}
/**
diff --git a/src/main/java/com/bao/dating/service/impl/UserServiceImpl.java b/src/main/java/com/bao/dating/service/impl/UserServiceImpl.java
index d6b48dd..189b7e3 100644
--- a/src/main/java/com/bao/dating/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/bao/dating/service/impl/UserServiceImpl.java
@@ -5,6 +5,7 @@ import com.bao.dating.common.aliyun.GreenImageScan;
import com.bao.dating.common.aliyun.GreenTextScan;
import com.bao.dating.common.result.AliOssResult;
import com.bao.dating.common.result.GreenAuditResult;
+import com.bao.dating.config.RedisConfig;
import com.bao.dating.context.UserContext;
import com.bao.dating.mapper.UserMapper;
import com.bao.dating.pojo.dto.UserInfoUpdateDTO;
@@ -16,17 +17,17 @@ import com.bao.dating.service.UserService;
import com.bao.dating.util.FileUtil;
import com.bao.dating.util.JwtUtil;
import com.bao.dating.util.MD5Util;
+import io.jsonwebtoken.Claims;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
/**
* 用户服务实现类
@@ -45,6 +46,9 @@ public class UserServiceImpl implements UserService {
@Autowired
private GreenImageScan greenImageScan;
+ @Autowired
+ private RedisTemplate redisTemplate;
+
@Autowired
private UserMapper userMapper;
@@ -76,6 +80,15 @@ public class UserServiceImpl implements UserService {
}
// 生成token
String token = JwtUtil.generateToken(String.valueOf(user.getUserId()));
+
+ String redisKey = "login:token:" + user.getUserId();
+ redisTemplate.opsForValue().set(
+ redisKey,
+ token,
+ 7,
+ TimeUnit.DAYS
+ );
+
// 封装返回
UserLoginVO userLoginVO = new UserLoginVO();
userLoginVO.setUserId(user.getUserId());
@@ -84,6 +97,29 @@ public class UserServiceImpl implements UserService {
return userLoginVO;
}
+ /**
+ * 退出登录
+ * @param token 登录凭证
+ */
+ @Override
+ public void logout(String token) {
+ Claims claims = JwtUtil.getClaimsFromToken(token);
+ Date expiration = claims.getExpiration();
+ // 判断 token 是否已过期
+ long ttl = expiration.getTime() - System.currentTimeMillis();
+ // 如果 token 已过期,则不用处理
+ if (ttl <= 0) {
+ return;
+ }
+
+ String logoutKey = "jwt:blacklist:" + token;
+ redisTemplate.opsForValue().set(
+ logoutKey,
+ "logout",
+ ttl,
+ TimeUnit.MILLISECONDS);
+ }
+
/**
* 获取用户信息
*
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 82aafc7..5cc67fa 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -2,6 +2,11 @@ server:
port: 8080
spring:
+ mvc:
+ throw-exception-if-no-handler-found: true
+ web:
+ resources:
+ add-mappings: false
datasource:
url: jdbc:mysql://110.42.41.177:3306/dating?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: root
diff --git a/src/main/resources/com/bao/dating/mapper/CommentsMapper.xml b/src/main/resources/com/bao/dating/mapper/CommentsMapper.xml
new file mode 100644
index 0000000..55f46c5
--- /dev/null
+++ b/src/main/resources/com/bao/dating/mapper/CommentsMapper.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/com/bao/dating/mapper/PostFavoriteMapper.xml b/src/main/resources/com/bao/dating/mapper/PostFavoriteMapper.xml
index 41b5195..9467b9c 100644
--- a/src/main/resources/com/bao/dating/mapper/PostFavoriteMapper.xml
+++ b/src/main/resources/com/bao/dating/mapper/PostFavoriteMapper.xml
@@ -14,4 +14,14 @@
+
+
+
+ DELETE FROM post_favorite
+ WHERE post_id IN
+
+ #{postId}
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/com/bao/dating/mapper/PostLikeMapper.xml b/src/main/resources/com/bao/dating/mapper/PostLikeMapper.xml
index fe82536..9e26cd2 100644
--- a/src/main/resources/com/bao/dating/mapper/PostLikeMapper.xml
+++ b/src/main/resources/com/bao/dating/mapper/PostLikeMapper.xml
@@ -14,4 +14,14 @@
delete from dating.post_like where post_id = #{postId} and user_id = #{userId}
+
+
+
+ DELETE FROM post_like
+ WHERE post_id IN
+
+ #{postId}
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/com/bao/dating/mapper/PostMapper.xml b/src/main/resources/com/bao/dating/mapper/PostMapper.xml
index b1924e4..d3c96bd 100644
--- a/src/main/resources/com/bao/dating/mapper/PostMapper.xml
+++ b/src/main/resources/com/bao/dating/mapper/PostMapper.xml
@@ -28,25 +28,19 @@
#{isPublic}, 0, 0, #{createdAt}, #{updatedAt})
-
-
- DELETE FROM post WHERE post_id IN
+
+
+ UPDATE post
+
+ is_public = 3,
+ updated_at = NOW()
+
+ WHERE post_id IN
#{postId}
-
-
-
- DELETE FROM post_favorite WHERE post_id = #{postId}
-
-
-
- DELETE FROM post_like WHERE post_id = #{postId}
-
-
-
- DELETE FROM comments WHERE post_id = #{postId}
-
+ AND user_id = #{userId}
+