合并

Change-Id: I7ef0ce3bd06a1a3a42b1176c3b1f22df3a1be6eb
diff --git a/src/main/java/com/ptp/ptplatform/config/WebMvcConfig.java b/src/main/java/com/ptp/ptplatform/config/WebMvcConfig.java
new file mode 100644
index 0000000..062e1cb
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/config/WebMvcConfig.java
@@ -0,0 +1,21 @@
+package com.ptp.ptplatform.config;

+

+import org.springframework.context.annotation.Configuration;

+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;

+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

+

+@Configuration

+public class WebMvcConfig implements WebMvcConfigurer {

+

+    @Override

+    public void addResourceHandlers(ResourceHandlerRegistry registry) {

+        // 获取项目运行目录的绝对路径

+        String projectPath = System.getProperty("user.dir");

+        // 定义本地文件存储路径(与代码中上传路径一致)

+        String uploadPath = projectPath + "/uploads/";

+

+        // 关键配置:将URL路径 "/uploads/**" 映射到本地文件系统

+        registry.addResourceHandler("/uploads/**")

+                .addResourceLocations("file:" + uploadPath);

+    }

+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/controller/HelpCommentController.java b/src/main/java/com/ptp/ptplatform/controller/HelpCommentController.java
index 6ee5d81..906b8f3 100644
--- a/src/main/java/com/ptp/ptplatform/controller/HelpCommentController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/HelpCommentController.java
@@ -3,6 +3,7 @@
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.ptp.ptplatform.entity.HelpComment;
 import com.ptp.ptplatform.service.HelpCommentService;
+import com.ptp.ptplatform.service.HelpPostService;
 import com.ptp.ptplatform.utils.Result;
 import lombok.AllArgsConstructor;
 import org.springframework.http.MediaType;
@@ -20,6 +21,7 @@
 @AllArgsConstructor
 public class HelpCommentController {
     private final HelpCommentService commentService;
+    private final HelpPostService postService;
 
     @PostMapping("/{commentId}/like")
     public Result like(@PathVariable int commentId) {
@@ -79,6 +81,9 @@
         // 保存到数据库
         commentService.save(reply);
 
+        // 更新帖子评论数
+        postService.incrementReplyCount(parentComment.getPostId());
+
         // 返回新回复及其父评论 ID
         return Result.ok()
                 .data("reply", reply)
@@ -86,13 +91,57 @@
     }
 
 
-        // 删除评论
-        @PostMapping("/delete/{commentId}")
-        public Result deletePost(@PathVariable int commentId) {
-            commentService.remove(new QueryWrapper<HelpComment>().eq("post_id", commentId));
-            boolean removed = commentService.removeById(commentId);
-            return removed ? Result.ok() : Result.error(404).setMessage("删除评论失败");
+    // 删除评论
+    @DeleteMapping("/{commentId}")
+    public Result deleteComment(
+            @PathVariable int commentId,
+            @RequestParam String authorId) {
+
+        // 1. 获取要删除的评论
+        HelpComment comment = commentService.getById(commentId);
+        if (comment == null) {
+            return Result.error(404).setMessage("评论不存在");
         }
 
+        // 2. 验证当前用户是否有权限删除(比对authorId)
+        if (!comment.getAuthorId().equals(authorId)) {
+            return Result.error(403).setMessage("无权删除此评论");
+        }
+
+        // 3. 递归删除所有子回复
+        deleteRepliesRecursively(commentId);
+
+        // 4. 删除主评论
+        boolean removed = commentService.removeById(commentId);
+
+//            // 5. 更新帖子回复数(如果需要)
+//            if (removed && comment.getParentId() == 0) {
+//                postService.decrementReplyCount(comment.getPostId());
+//            }
+
+        return removed ? Result.ok() : Result.error(500).setMessage("删除评论失败");
+    }
+    // 递归删除所有回复
+    private void deleteRepliesRecursively(int parentCommentId) {
+        // 添加保护措施防止无限递归
+        if (parentCommentId <= 0) return;
+
+        // 查找所有直接回复
+        List<HelpComment> replies = commentService.list(
+                new QueryWrapper<HelpComment>().eq("parent_id", parentCommentId)
+        );
+
+        // 递归删除每个回复及其子回复
+        for (HelpComment reply : replies) {
+            deleteRepliesRecursively(reply.getId());
+            commentService.removeById(reply.getId());
+
+//                // 如果是主评论(parent_id=0),更新帖子回复数
+//                if (reply.getParentId() == 0) {
+//                    postService.decrementReplyCount(reply.getPostId());
+//                }
+        }
+    }
+
 
 }
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/controller/HelpPostController.java b/src/main/java/com/ptp/ptplatform/controller/HelpPostController.java
index a40efb7..cb8a36b 100644
--- a/src/main/java/com/ptp/ptplatform/controller/HelpPostController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/HelpPostController.java
@@ -25,9 +25,49 @@
     private final HelpPostService postService;
     private final HelpCommentService commentService;
 
-    // 创建帖子
-    @PostMapping
-    public Result createPost(@RequestBody HelpPost post) {
+    // 修改创建帖子的方法,支持图片上传
+    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public Result createPost(
+
+            @RequestParam("title") String title,
+            @RequestParam("content") String content,
+            @RequestParam("authorId") String authorId,
+            @RequestParam(value = "image", required = false) MultipartFile image) {
+
+        HelpPost post = new HelpPost();
+        post.setTitle(title);
+        post.setContent(content);
+        post.setAuthorId(authorId);
+        post.setCreateTime(LocalDateTime.now());
+        post.setLikeCount(0);
+        post.setReplyCount(0);
+        post.setImageUrl(image.getOriginalFilename());
+//        post.setIsSolved(false);
+
+        // 处理图片上传
+        if (image != null && !image.isEmpty()) {
+            try {
+                String originalFilename = image.getOriginalFilename();
+                if(originalFilename != null) {
+                    String fileExt = originalFilename.substring(image.getOriginalFilename().lastIndexOf('.') + 1);
+                    String fileName = UUID.randomUUID() + "." + fileExt;
+
+                    String uploadDir = System.getProperty("user.dir") + File.separator + "uploads";
+                    File dir = new File(uploadDir);
+                    if (!dir.exists()) dir.mkdirs();
+
+                    File dest = new File(dir, fileName);
+                    image.transferTo(dest);
+
+                    // 保存相对访问路径
+                    post.setImageUrl("/uploads/" + fileName);
+                }
+            } catch (IOException e) {
+                return Result.error(404).setMessage("图片上传失败:" + e.getMessage());
+            }
+        } else {
+            post.setImageUrl(null);
+        }
         postService.save(post);
         return Result.ok().data("post", post);
     }
@@ -59,6 +99,10 @@
                         .orderByAsc("create_time")
         );
 
+        // 获取实时评论总数(包括所有层级)
+        long totalComments = allComments.size(); // 直接使用列表大小
+        post.setReplyCount(totalComments); // 更新计数
+
         // 构建评论树形结构
         List<HelpComment> rootComments = new ArrayList<>();
         Map<Integer, HelpComment> commentMap = new HashMap<>();
@@ -167,11 +211,25 @@
     }
 
     // 删除帖子
-    @PostMapping("/delete/{Id}")
-    public Result deletePost(@PathVariable int Id) {
-        commentService.remove(new QueryWrapper<HelpComment>().eq("post_id", Id));
-        boolean removed = postService.removeById(Id);
-        return removed ? Result.ok() : Result.error(404).setMessage("删除帖子失败");
-    }
+    @DeleteMapping("/{postId}")
+    public Result deletePost(@PathVariable int postId,
+                             @RequestParam String authorId) {
+        try {
+            // 1. 验证当前用户是否有权限删除(比对authorId)
+            HelpPost post = postService.getById(postId);
+            if (post == null) {
+                return Result.error(404).setMessage("帖子不存在");
+            }
+            if (!post.getAuthorId().equals(authorId)) {
+                return Result.error(403).setMessage("无权删除此帖子");
+            }
 
+            // 2. 删除帖子及关联评论
+            commentService.remove(new QueryWrapper<HelpComment>().eq("post_id", postId));
+            boolean removed = postService.removeById(postId);
+            return removed ? Result.ok() : Result.error(404).setMessage("删除评论失败");
+        } catch (Exception e) {
+            return Result.error(500).setMessage("服务器错误: " + e.getMessage());
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/entity/HelpPost.java b/src/main/java/com/ptp/ptplatform/entity/HelpPost.java
index be0bd02..90d0460 100644
--- a/src/main/java/com/ptp/ptplatform/entity/HelpPost.java
+++ b/src/main/java/com/ptp/ptplatform/entity/HelpPost.java
@@ -14,7 +14,8 @@
     private String authorId;
     private String title;
     private String content;
+    private String imageUrl;
     private Integer likeCount;
-    private Integer replyCount;
+    private long replyCount;
     private LocalDateTime createTime;
 }
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/HelpCommentService.java b/src/main/java/com/ptp/ptplatform/service/HelpCommentService.java
index 14622ab..8236fa7 100644
--- a/src/main/java/com/ptp/ptplatform/service/HelpCommentService.java
+++ b/src/main/java/com/ptp/ptplatform/service/HelpCommentService.java
@@ -7,5 +7,7 @@
 
 public interface HelpCommentService extends IService<HelpComment> {
     void incrementLike(int commentId);
-    List<HelpComment> getReplies(int parentId); // 新增方法
-}
\ No newline at end of file
+    List<HelpComment> getReplies(int parentId);
+    // 新增:获取帖子直接评论数
+    long countByPostId(Integer postId);// 新增方法
+}
diff --git a/src/main/java/com/ptp/ptplatform/service/impl/HelpCommentServiceImpl.java b/src/main/java/com/ptp/ptplatform/service/impl/HelpCommentServiceImpl.java
index 4f47e78..f15f618 100644
--- a/src/main/java/com/ptp/ptplatform/service/impl/HelpCommentServiceImpl.java
+++ b/src/main/java/com/ptp/ptplatform/service/impl/HelpCommentServiceImpl.java
@@ -33,4 +33,9 @@
                         .orderByAsc("create_time")
         );
     }
+    @Override
+    public long countByPostId(Integer postId) {
+        return count(new QueryWrapper<HelpComment>()
+                .eq("post_id", postId)); // 只按post_id统计
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/com/ptp/ptplatform/controller/HelpCommentControllerTest.java b/src/test/java/com/ptp/ptplatform/controller/HelpCommentControllerTest.java
index f637e39..dca6c2f 100644
--- a/src/test/java/com/ptp/ptplatform/controller/HelpCommentControllerTest.java
+++ b/src/test/java/com/ptp/ptplatform/controller/HelpCommentControllerTest.java
@@ -3,6 +3,7 @@
 import com.baomidou.mybatisplus.core.conditions.Wrapper;

 import com.ptp.ptplatform.entity.HelpComment;

 import com.ptp.ptplatform.service.HelpCommentService;

+import com.ptp.ptplatform.service.HelpPostService;

 import com.ptp.ptplatform.utils.Result;

 import org.junit.jupiter.api.BeforeEach;

 import org.junit.jupiter.api.Test;

@@ -24,9 +25,15 @@
     @Mock

     private HelpCommentService commentService;

 

+    @Mock

+    private HelpPostService postService;

+

     @InjectMocks

     private HelpCommentController commentController;

 

+    private final String authorId = "user123";

+    private final String otherAuthorId = "user456";

+

     @BeforeEach

     void setUp() {

         MockitoAnnotations.openMocks(this);

@@ -141,4 +148,26 @@
         verify(commentService, never()).save(any());

     }

 

+    @Test

+    void deleteComment_ShouldReturnSuccess_WhenAuthorMatches() {

+        // 准备测试数据

+        int commentId = 1;

+        HelpComment comment = new HelpComment();

+        comment.setId(commentId);

+        comment.setAuthorId(authorId);

+        comment.setPostId(10);

+

+        when(commentService.getById(commentId)).thenReturn(comment);

+        when(commentService.removeById(commentId)).thenReturn(true);

+

+        // 调用方法

+        Result result = commentController.deleteComment(commentId, authorId);

+

+        // 验证结果

+        assertEquals(200, result.getCode());

+

+        // 验证服务调用

+        verify(commentService).removeById(commentId);

+    }

+

 }

diff --git a/src/test/java/com/ptp/ptplatform/controller/HelpPostControllerTest.java b/src/test/java/com/ptp/ptplatform/controller/HelpPostControllerTest.java
index 0098abb..2176e5b 100644
--- a/src/test/java/com/ptp/ptplatform/controller/HelpPostControllerTest.java
+++ b/src/test/java/com/ptp/ptplatform/controller/HelpPostControllerTest.java
@@ -36,12 +36,36 @@
     @InjectMocks

     private HelpPostController postController;

 

+    private final String authorId = "user123";

+    private final String otherAuthorId = "user456";

+

     @BeforeEach

     void setUp() {

         MockitoAnnotations.openMocks(this);

     }

 

     @Test

+    void createPost_ShouldReturnSuccess() throws Exception {

+        // 准备测试数据

+        String title = "Test Title";

+        String content = "Test Content";

+        MockMultipartFile image = new MockMultipartFile(

+                "image", "test.jpg", "image/jpeg", "test image".getBytes());

+

+        // 调用方法

+        Result result = postController.createPost(title, content, authorId, image);

+

+        // 验证结果

+        assertEquals(200, result.getCode());

+        HelpPost createdPost = (HelpPost) result.getData().get("post");

+        assertNotNull(createdPost.getImageUrl());

+        assertTrue(createdPost.getImageUrl().startsWith("/uploads/"));

+

+        // 验证服务调用

+        verify(postService).save(any(HelpPost.class));

+    }

+

+    @Test

     void getPost_ShouldReturnPostWithComments_WhenPostExists() {

         int postId = 1;

         HelpPost post = new HelpPost();

@@ -127,31 +151,21 @@
     }

 

     @Test

-    void deletePost_ShouldReturnSuccess_WhenPostRemoved() {

+    void deletePost_ShouldReturnSuccess_WhenAuthorMatches() {

         int postId = 42;

+        HelpPost mockPost = new HelpPost();

+        mockPost.setAuthorId(authorId);

         // remove 返回 boolean,而非 void

+        when(postService.getById(postId)).thenReturn(mockPost);

         when(commentService.remove(any(Wrapper.class))).thenReturn(true);

         when(postService.removeById(postId)).thenReturn(true);

 

-        Result result = postController.deletePost(postId);

+        Result result = postController.deletePost(postId, authorId);

 

         assertEquals(200, result.getCode());

         verify(commentService, times(1)).remove(any(Wrapper.class));

         verify(postService, times(1)).removeById(postId);

     }

-    @Test

-    void deletePost_ShouldReturnError_WhenPostRemoveFails() {

-        int postId = 42;

-        when(commentService.remove(any(Wrapper.class))).thenReturn(true);

-        when(postService.removeById(postId)).thenReturn(false);

-

-        Result result = postController.deletePost(postId);

-

-        assertEquals(500, result.getCode());

-        assertEquals("删除帖子失败", result.getMessage());

-        verify(commentService, times(1)).remove(any(Wrapper.class));

-        verify(postService, times(1)).removeById(postId);

-    }

 

     @Test

     void addCommentWithoutImage_ShouldReturnSuccess() {

@@ -189,7 +203,7 @@
         // 断言

         assertEquals(200, result.getCode());

         assertEquals(saved, result.getData().get("comment"));

-        assertEquals(5,     result.getData().get("newReplyCount"));

+        assertEquals(5L,     result.getData().get("newReplyCount"));

 

         verify(commentService, times(1)).save(any(HelpComment.class));

         verify(postService,   times(1)).incrementReplyCount(postId);

@@ -237,7 +251,7 @@
 

         assertEquals(200, result.getCode());

         assertEquals(saved, result.getData().get("comment"));

-        assertEquals(10,    result.getData().get("newReplyCount"));

+        assertEquals(10L,    result.getData().get("newReplyCount"));

 

         // 并且真正保存的对象也带上了 imageUrl

         ArgumentCaptor<HelpComment> captor = ArgumentCaptor.forClass(HelpComment.class);

diff --git a/uploads/813ea0ee-bddf-42fb-93ea-3e96be10a63a.jpg b/uploads/813ea0ee-bddf-42fb-93ea-3e96be10a63a.jpg
new file mode 100644
index 0000000..aed2973
--- /dev/null
+++ b/uploads/813ea0ee-bddf-42fb-93ea-3e96be10a63a.jpg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/uploads/9f99ac77-48d0-4535-9f2f-5fe7c804a559.png b/uploads/9f99ac77-48d0-4535-9f2f-5fe7c804a559.png
new file mode 100644
index 0000000..5a29a05
--- /dev/null
+++ b/uploads/9f99ac77-48d0-4535-9f2f-5fe7c804a559.png
Binary files differ
diff --git a/uploads/baef0e69-225a-4c70-92e2-f8b1301c9dbc.jpg b/uploads/baef0e69-225a-4c70-92e2-f8b1301c9dbc.jpg
new file mode 100644
index 0000000..aed2973
--- /dev/null
+++ b/uploads/baef0e69-225a-4c70-92e2-f8b1301c9dbc.jpg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/uploads/c1deedcd-b66c-4d91-b0ef-baed3edbdd17.png b/uploads/c1deedcd-b66c-4d91-b0ef-baed3edbdd17.png
new file mode 100644
index 0000000..71bd63e
--- /dev/null
+++ b/uploads/c1deedcd-b66c-4d91-b0ef-baed3edbdd17.png
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/uploads/df715d55-ccff-46ff-8009-9a0972344742.png b/uploads/df715d55-ccff-46ff-8009-9a0972344742.png
new file mode 100644
index 0000000..e129f0d
--- /dev/null
+++ b/uploads/df715d55-ccff-46ff-8009-9a0972344742.png
Binary files differ
diff --git a/uploads/e2255186-e4c7-49c3-b673-229c5a16a6d6.jpg b/uploads/e2255186-e4c7-49c3-b673-229c5a16a6d6.jpg
new file mode 100644
index 0000000..a6d7f38
--- /dev/null
+++ b/uploads/e2255186-e4c7-49c3-b673-229c5a16a6d6.jpg
@@ -0,0 +1 @@
+test image
\ No newline at end of file
diff --git a/uploads/ecbe499c-3342-4e8e-bf23-e2f77a0ad40b.png b/uploads/ecbe499c-3342-4e8e-bf23-e2f77a0ad40b.png
new file mode 100644
index 0000000..e129f0d
--- /dev/null
+++ b/uploads/ecbe499c-3342-4e8e-bf23-e2f77a0ad40b.png
Binary files differ
diff --git a/uploads/fdaf20d3-faa6-4409-96cb-215869948ebf.jpg b/uploads/fdaf20d3-faa6-4409-96cb-215869948ebf.jpg
new file mode 100644
index 0000000..a6d7f38
--- /dev/null
+++ b/uploads/fdaf20d3-faa6-4409-96cb-215869948ebf.jpg
@@ -0,0 +1 @@
+test image
\ No newline at end of file