重新合并

Change-Id: Ied67fad091c03cf964124921ae8a69e720bd36de
diff --git a/src/main/java/com/ptp/ptplatform/controller/AnnouncementController.java b/src/main/java/com/ptp/ptplatform/controller/AnnouncementController.java
new file mode 100644
index 0000000..b751f0c
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/controller/AnnouncementController.java
@@ -0,0 +1,67 @@
+package com.ptp.ptplatform.controller;
+
+import com.ptp.ptplatform.entity.ANNOUNCEMENT;
+import com.ptp.ptplatform.mapper.AnnouncementMapper;
+import com.ptp.ptplatform.utils.Result;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@RestController
+@RequestMapping("/announcement")
+public class AnnouncementController {
+
+    private final AnnouncementMapper announcementMapper;
+
+    public AnnouncementController(AnnouncementMapper announcementMapper) {
+        this.announcementMapper = announcementMapper;
+    }
+
+    // 获取所有公告
+    @GetMapping("/list")
+    public Result listAll() {
+        List<ANNOUNCEMENT> announcements = announcementMapper.selectAll();
+        return Result.ok().data("announcements", announcements);
+    }
+
+    // 获取最新5条公告(用于首页展示)
+    @GetMapping("/latest")
+    public Result listLatest() {
+        List<ANNOUNCEMENT> announcements = announcementMapper.selectLatest(5);
+        return Result.ok().data("announcements", announcements);
+    }
+
+    // 获取公告详情
+    @GetMapping("/{id}")
+    public Result getDetail(@PathVariable Integer id) {
+        ANNOUNCEMENT announcement = announcementMapper.selectById(id);
+        if (announcement == null) {
+            return Result.error(404).message("公告不存在");
+        }
+        return Result.ok().data("announcement", announcement);
+    }
+
+    // 新增公告
+    @PostMapping("/create")
+    public Result createAnnouncement(@RequestParam String title, @RequestParam String content) {
+        try {
+            ANNOUNCEMENT announcement = new ANNOUNCEMENT();
+            announcement.setTitle(title);
+            announcement.setContent(content);
+            announcement.setCreateTime(LocalDateTime.now());
+
+            int result = announcementMapper.insert(announcement);
+            if (result > 0) {
+                return Result.ok().message("公告发布成功");
+            } else {
+                // 添加错误码参数(如500表示服务器错误)
+                return Result.error(500).message("公告发布失败");
+            }
+        } catch (Exception e) {
+            // 添加错误码参数(如500表示服务器错误)
+            return Result.error(500).message("服务器错误: " + e.getMessage());
+        }
+    }
+}
+
diff --git a/src/main/java/com/ptp/ptplatform/controller/RequestCommentController.java b/src/main/java/com/ptp/ptplatform/controller/RequestCommentController.java
new file mode 100644
index 0000000..cfc7acd
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/controller/RequestCommentController.java
@@ -0,0 +1,147 @@
+package com.ptp.ptplatform.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.ptp.ptplatform.entity.RequestComment;
+import com.ptp.ptplatform.service.RequestCommentService;
+import com.ptp.ptplatform.service.RequestPostService;
+import com.ptp.ptplatform.utils.Result;
+import lombok.AllArgsConstructor;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.UUID;
+
+@RestController
+@RequestMapping("/request/comments")
+@AllArgsConstructor
+public class RequestCommentController {
+    private final RequestCommentService commentService;
+    private final RequestPostService postService;
+
+    @PostMapping("/{commentId}/like")
+    public Result like(@PathVariable int commentId) {
+        commentService.incrementLike(commentId);
+        return Result.ok();
+    }
+
+    // 获取评论的回复
+    @GetMapping("/{commentId}/replies")
+    public Result getReplies(@PathVariable int commentId) {
+        List<RequestComment> replies = commentService.getReplies(commentId);
+        return Result.ok().data("replies", replies);
+    }
+
+    @PostMapping(value = "/{commentId}/replies", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public Result addReply(@PathVariable int commentId,
+                           @RequestParam("authorId") String authorId,
+                           @RequestParam("content") String content,
+                           @RequestParam(value = "image", required = false) MultipartFile image) {
+        // 校验父评论是否存在
+        RequestComment parentComment = commentService.getById(commentId);
+        if (parentComment == null) {
+            return Result.error(404).setMessage("被回复的评论不存在");
+        }
+
+        // 构造回复评论对象
+        RequestComment reply = new RequestComment();
+        reply.setPostId(parentComment.getPostId());
+        reply.setParentId(commentId);
+        reply.setAuthorId(authorId);              // String 类型
+        reply.setReplyTo(parentComment.getAuthorId());
+        reply.setContent(content);
+        reply.setCreateTime(LocalDateTime.now());
+        reply.setLikeCount(0);
+
+        // 可选:处理图片上传
+        if (image != null && !image.isEmpty()) {
+            try {
+                String fileExt = image.getOriginalFilename()
+                        .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);
+
+                // 保存相对访问路径
+                reply.setImageUrl("/uploads/" + fileName);
+            } catch (IOException e) {
+                return Result.error(404).setMessage("图片上传失败:" + e.getMessage());
+            }
+        }
+
+        // 保存到数据库
+        commentService.save(reply);
+
+        // 更新帖子评论数
+        postService.incrementReplyCount(parentComment.getPostId());
+
+        // 返回新回复及其父评论 ID
+        return Result.ok()
+                .data("reply", reply)
+                .data("parentCommentId", commentId);
+    }
+
+
+    // 删除评论
+    @DeleteMapping("/{commentId}")
+    public Result deleteComment(
+            @PathVariable int commentId,
+            @RequestParam String authorId) {
+
+        // 1. 获取要删除的评论
+        RequestComment 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<RequestComment> replies = commentService.list(
+                new QueryWrapper<RequestComment>().eq("parent_id", parentCommentId)
+        );
+
+        // 递归删除每个回复及其子回复
+        for (RequestComment 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/RequestPostController.java b/src/main/java/com/ptp/ptplatform/controller/RequestPostController.java
new file mode 100644
index 0000000..28a18ea
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/controller/RequestPostController.java
@@ -0,0 +1,270 @@
+package com.ptp.ptplatform.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.ptp.ptplatform.entity.RequestComment;
+import com.ptp.ptplatform.entity.RequestPost;
+import com.ptp.ptplatform.service.RequestCommentService;
+import com.ptp.ptplatform.service.RequestPostService;
+import com.ptp.ptplatform.service.NotificationService;
+import com.ptp.ptplatform.utils.Result;
+import lombok.AllArgsConstructor;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@RestController
+@RequestMapping("/request/posts")
+@AllArgsConstructor
+public class RequestPostController {
+    private final RequestPostService postService;
+    private final RequestCommentService commentService;
+    private final NotificationService notificationService;
+
+    // 修改创建帖子的方法,支持图片上传
+    @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) {
+
+        RequestPost post = new RequestPost();
+        post.setTitle(title);
+        post.setContent(content);
+        post.setAuthorId(authorId);
+        post.setCreateTime(LocalDateTime.now());
+        post.setLikeCount(0);
+        post.setReplyCount(0);
+        post.setImageUrl(null);
+//        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);
+    }
+
+    // 列表分页
+    @GetMapping
+    public Result listPosts(@RequestParam(defaultValue = "1") int page,
+                            @RequestParam(defaultValue = "5") int size) {
+        IPage<RequestPost> ipage = postService.page(
+                new Page<>(page, size),
+                new QueryWrapper<RequestPost>().orderByDesc("create_time")
+        );
+        return Result.ok()
+                .data("records", ipage.getRecords())
+                .data("total", ipage.getTotal());
+    }
+
+    @GetMapping("/search")
+    public Result searchPosts(
+            @RequestParam String keyword,
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "5") int size) {
+
+        // 1. 把搜索词拆分成单词(中英文混合拆分)
+        List<String> keywords = splitKeywords(keyword); // 例如 "你好hello" → ["你", "好", "hello"]
+
+        // 2. 构建动态查询条件(每个关键词用 OR 连接)
+        QueryWrapper<RequestPost> queryWrapper = new QueryWrapper<>();
+        for (String word : keywords) {
+            queryWrapper.and(wrapper ->
+                    // 使用LOWER函数使搜索不区分大小写
+                    wrapper.apply("LOWER(title) LIKE LOWER({0})", "%" + word + "%")
+                            .or()
+                            .apply("LOWER(content) LIKE LOWER({0})", "%" + word + "%")
+            );
+        }
+        queryWrapper.orderByDesc("create_time");
+
+        // 3. 执行分页查询
+        IPage<RequestPost> ipage = postService.page(new Page<>(page, size), queryWrapper);
+        return Result.ok()
+                .data("records", ipage.getRecords())
+                .data("total", ipage.getTotal());
+    }
+
+    /**
+     * 拆分关键词(支持中英文混合)
+     * 例如:"你好hello 世界" → ["你", "好", "hello", "世", "界"]
+     */
+    private List<String> splitKeywords(String keyword) {
+        List<String> keywords = new ArrayList<>();
+
+        // 用正则匹配中文字符和英文单词
+        Pattern pattern = Pattern.compile("([\\u4e00-\\u9fa5])|([a-zA-Z0-9]+)");
+        Matcher matcher = pattern.matcher(keyword);
+
+        while (matcher.find()) {
+            String matched = matcher.group();
+            keywords.add(matched);
+        }
+
+        return keywords;
+    }
+
+    @GetMapping("/{Id}")
+    public Result getPost(@PathVariable int Id) {
+        RequestPost post = postService.getById(Id);
+        if (post == null) {
+            return Result.error(404).setMessage("帖子不存在");
+        }
+
+        // 获取所有评论(按创建时间排序)
+        List<RequestComment> allComments = commentService.list(
+                new QueryWrapper<RequestComment>()
+                        .eq("post_id", Id)
+                        .orderByAsc("create_time")
+        );
+
+        // 获取实时评论总数(包括所有层级)
+        int totalComments = allComments.size(); // 直接使用列表大小
+        post.setReplyCount(totalComments); // 更新计数
+
+        // 构建评论树形结构
+        List<RequestComment> rootComments = new ArrayList<>();
+        Map<Integer, RequestComment> commentMap = new HashMap<>();
+
+        // 第一遍:初始化所有评论到map中
+        for (RequestComment comment : allComments) {
+            comment.setReplies(new ArrayList<>()); // 初始化replies列表
+            commentMap.put(comment.getId(), comment);
+        }
+
+        // 第二遍:构建父子关系
+        for (RequestComment comment : allComments) {
+            if (comment.getParentId() == 0) {
+                rootComments.add(comment);
+            } else {
+                RequestComment parent = commentMap.get(comment.getParentId());
+                if (parent != null) {
+                    parent.getReplies().add(comment);
+                }
+            }
+        }
+
+        return Result.ok()
+                .data("post", post)
+                .data("comments", rootComments);
+    }
+
+    // 点赞帖子
+    @PostMapping("/{Id}/like")
+    public Result likePost(
+            @PathVariable("Id") int Id,
+            @RequestParam("likerId") String likerId
+    ) {
+        // 调用 ServiceImpl.likePost(...),自动更新 like_count 并插入通知
+        postService.likePost(Id, likerId);
+        return Result.ok().message("点赞成功");
+    }
+
+    // 评论帖子
+    @PostMapping(value = "/{Id}/comments", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public Result commentOnPost(
+            @PathVariable("Id") int Id,
+            @RequestParam("authorId") String authorId,
+            @RequestParam("content") String content,
+            @RequestParam(value = "image", required = false) MultipartFile image
+    ) {
+        // 1. 构造 HelpComment 对象
+        RequestComment comment = new RequestComment();
+        comment.setPostId(Id);
+        comment.setAuthorId(authorId);
+        comment.setContent(content);
+        comment.setCreateTime(LocalDateTime.now());
+        comment.setLikeCount(0);
+        comment.setParentId(0);       // 这里是对帖子的一级评论
+        comment.setImageUrl(null);    // 默认无图片
+
+        // 2. 处理可选的图片上传逻辑
+        if (image != null && !image.isEmpty()) {
+            try {
+                String fileExt = image.getOriginalFilename()
+                        .substring(image.getOriginalFilename().lastIndexOf('.') + 1);
+                String fileName = UUID.randomUUID().toString() + "." + fileExt;
+
+                // 上传到项目根目录的 uploads 文件夹
+                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);
+
+                comment.setImageUrl("/uploads/" + fileName);
+            } catch (IOException e) {
+                return Result.error(500).message("图片上传失败:" + e.getMessage());
+            }
+        }
+
+        // 3. 通过 ServiceImpl 完成“保存评论 + 更新 reply_count + 发通知给帖子作者”
+        commentService.commentOnRequestPost(comment);
+
+        // 4. 构造返回值:把刚刚保存的评论(从数据库中查出来的完整对象)和帖子最新的 replyCount 都返回
+        //    注意:comment.insert(...) 后,comment.getId() 将有 DB 自动生成的主键
+        RequestComment newComment = commentService.getById(comment.getId());
+        int updatedReplyCount = postService.getById(Id).getReplyCount();
+
+        return Result.ok()
+                .data("comment", newComment)
+                .data("newReplyCount", updatedReplyCount);
+    }
+
+    // 删除帖子
+    @DeleteMapping("/{postId}")
+    public Result deletePost(@PathVariable int postId,
+                             @RequestParam String authorId) {
+        try {
+            // 1. 验证当前用户是否有权限删除(比对authorId)
+            RequestPost 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<RequestComment>().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/controller/TorrentController.java b/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
index e69c70e..fa0f92e 100644
--- a/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
@@ -23,6 +23,8 @@
 import org.springframework.web.bind.annotation.*;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.springframework.web.multipart.MultipartFile;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -67,6 +69,7 @@
 
     private TrackerService trackerservice = new TrackerService(new ObjectMapper());
     private ClientService clientservice = new ClientService();
+    private static final Logger logger = LoggerFactory.getLogger(TorrentController.class);
 
     @GetMapping
     public Result listPosts(
@@ -316,6 +319,7 @@
             torrent.setFilePath(uploadedFile.getAbsolutePath());
             torrent.setCreateTime(LocalDateTime.now());
             torrentMapper.insertTorrent(torrent);
+            System.out.println("Generated ID: " + torrent.getId());
 
             TrackedPeer tp = createTrackedPeer(request, tt, torrent.getId());
             tp.update(TrackedPeer.PeerState.COMPLETED, fileSize, 0, 0);
@@ -367,27 +371,41 @@
     // 能成功获取到数据
     // 能够根据此值,完成对相关数据的修改
     @GetMapping("/downloadTorrent")
-    public Result getTorrent(HttpServletRequest request, int id, String downloadPath) throws Exception {
+    public Result getTorrent(HttpServletRequest request,
+                             @RequestParam int id,
+                             @RequestParam String downloadPath) throws Exception {
+        // 1. 获取种子和用户信息
         TORRENT torrent = torrentMapper.selectByID(id);
+        if (torrent == null) {
+            logger.error("尝试下载不存在的种子,ID: {}", id);
+            return Result.error(404).message("种子不存在");
+        }
         USER userDownload = userController.getUserInRequest(request);
         USER userUpload = userMapper.selectByUsername(torrent.getUsername());
 
-        //检查分享率是否允许下载
+        // 2. 记录下载日志
+        logger.info("用户 {} 尝试下载种子 {} (上传者: {})",
+                userDownload.getUsername(), id, userUpload.getUsername());
 
-        if(!userDownload.allowDownload(torrent.getSize())){
+        // 3. 检查分享率(如果是自己的种子则跳过检查)
+        if (!userDownload.getUsername().equals(userUpload.getUsername()) &&
+                !userDownload.allowDownload(torrent.getSize())) {
+            logger.warn("用户 {} 分享率不足,无法下载种子 {}",
+                    userDownload.getUsername(), id);
             return Result.error(409).message("分享率不足下载此资源");
         }
 
-        // 下载完成
-        // 传入变量:下载种子的id 下载到文件的路径
-        clientservice.downloadTorrent(torrent.getFilePath(), downloadPath, userDownload.getUsername());
+        // 4. 执行下载
+        try {
+            // 4.1 调用客户端服务下载
 
-        TrackedTorrent tt = trackerservice.getTrackedTorrent(torrent.getHash());
-        TrackedPeer tp = createTrackedPeer(request, tt, torrent.getId());
-        tp.update(TrackedPeer.PeerState.COMPLETED, 0, 0, torrent.getSize());
-        tt.addPeer(tp);
-
-        trackerservice.serTracker();
+            clientservice.downloadTorrent(torrent.getFilePath(), downloadPath, userDownload.getUsername());
+            // 4.2 更新Tracker信息
+            TrackedTorrent tt = trackerservice.getTrackedTorrent(torrent.getHash());
+            TrackedPeer tp = createTrackedPeer(request, tt, torrent.getId());
+            tp.update(TrackedPeer.PeerState.COMPLETED, 0, 0, torrent.getSize());
+            tt.addPeer(tp);
+            trackerservice.serTracker();
 
         // 通知下载完成
         Notification downloadCompleteNotice = new Notification();
@@ -403,16 +421,24 @@
         downloadCompleteNotice.setCreateTime(LocalDateTime.now());
         notificationService.saveNotification(downloadCompleteNotice);
 
-        //更新上传量和下载量
-        userDownload.updateDownload(SizeCalculation.getDownload(torrent.getSize(), discountMapper, torrent.getCreateTime()));
-        userUpload.updateUpload(SizeCalculation.getUpload(torrent.getSize(), discountMapper, torrent.getCreateTime()));
-        userMapper.updateUser(userUpload);
-        userMapper.updateUser(userDownload);
-        DOWNLOAD_TORRENT dt = new DOWNLOAD_TORRENT(userDownload.getUsername(), torrent.getId(), torrent.getSize());
-        downloadTorrentMapper.insertDownloadTorrent(dt);
+            // 4.3 更新用户数据(只有下载他人种子时才更新)
+            if (!userDownload.getUsername().equals(userUpload.getUsername())) {
+                userDownload.updateDownload(SizeCalculation.getDownload(torrent.getSize(), discountMapper, torrent.getCreateTime()));
+                userUpload.updateUpload(SizeCalculation.getUpload(torrent.getSize(), discountMapper, torrent.getCreateTime()));
+                userMapper.updateUser(userUpload);
+                userMapper.updateUser(userDownload);
 
-        return Result.ok().data("fileName", torrent.getTorrentName())
-                .data("taskId", torrent.getHash());
+                DOWNLOAD_TORRENT dt = new DOWNLOAD_TORRENT(userDownload.getUsername(), torrent.getId(), torrent.getSize());
+                downloadTorrentMapper.insertDownloadTorrent(dt);
+            }
+
+            logger.info("用户 {} 成功下载种子 {}", userDownload.getUsername(), id);
+            return Result.ok().data("fileName", torrent.getTorrentName())
+                    .data("taskId", torrent.getHash());
+        }catch (Exception e) {
+            logger.error("下载种子失败", e);
+            return Result.error(500).message("下载失败: " + e.getMessage());
+        }
     }
 
     @GetMapping("/getProgress")
@@ -424,14 +450,15 @@
 
         for (String[] task : tasks) {
             if (task[0].equals(username)) {
-                Double progress = 0.0;  // 默认进度为0.0
                 try {
-                    progress = clientservice.getDownloadProgress(task[1]);  // 获取下载进度
-                } catch (IllegalStateException e) {
-                    // 如果发生异常,则进度保持为0.0,并继续处理下一个任务
-                    progress = 0.0;
+                    double progress = clientservice.getDownloadProgress(task[1]);
+                    // 确保进度在0-1之间
+                    progress = Math.max(0, Math.min(1, progress));
+                    progresses.put(task[1], progress);
+                } catch (Exception e) {
+                    // 如果出现异常,假设下载已完成
+                    progresses.put(task[1], 1.0);
                 }
-                progresses.put(task[1], progress);
             }
         }
         return Result.ok().data("progresses", progresses);
@@ -440,24 +467,38 @@
     //根据id删除
     @DeleteMapping("/deleteTorrent/{id}")
     public Result deleteTorrent(HttpServletRequest request, @PathVariable("id") int id) throws Exception {
+        // 1. 验证用户权限
+        USER currentUser = userController.getUserInRequest(request);
         TORRENT torrent = torrentMapper.selectByID(id);
 
-        String filePath = torrent.getFilePath();
-        try {
-            // 检查文件是否存在
-            if (Files.exists(Paths.get(filePath))) {
-                Files.delete(Paths.get(filePath));  // 删除文件
-                torrentMapper.deleteTorrent(id);
+        if (torrent == null) {
+            return Result.error(404).setMessage("种子不存在");
+        }
 
-                return Result.ok().message("种子文件删除成功");
-            } else {
-                throw new IOException("File not found: " + filePath);
+        // 3. 删除关联记录(分步骤)
+        try {
+            // 3.1 先删除下载记录
+            downloadTorrentMapper.deleteByTorrentId(id);
+
+            // 3.2 删除种子文件
+            if (Files.exists(Paths.get(torrent.getFilePath()))) {
+                Files.delete(Paths.get(torrent.getFilePath()));
             }
+
+            // 3.3 最后删除种子记录
+            int deleteCount = torrentMapper.deleteTorrent(id);
+            if (deleteCount == 0) {
+                return Result.error(404).setMessage("种子删除失败,可能已被删除");
+            }
+
+            return Result.ok().message("种子删除成功");
+
         } catch (IOException e) {
-            // 异常处理
-            e.printStackTrace();
-            // 返回失败结果或其他处理逻辑
-            return Result.error(404).setMessage("种子文件删除失败");
+            logger.error("文件删除失败", e);
+            return Result.error(500).setMessage("文件删除失败: " + e.getMessage());
+        } catch (Exception e) {
+            logger.error("种子删除失败", e);
+            return Result.error(500).setMessage("种子删除失败: " + e.getMessage());
         }
     }
 
diff --git a/src/main/java/com/ptp/ptplatform/entity/ANNOUNCEMENT.java b/src/main/java/com/ptp/ptplatform/entity/ANNOUNCEMENT.java
new file mode 100644
index 0000000..d522335
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/entity/ANNOUNCEMENT.java
@@ -0,0 +1,12 @@
+package com.ptp.ptplatform.entity;
+
+import lombok.Data;
+import java.time.LocalDateTime;
+
+@Data
+public class ANNOUNCEMENT {
+    private Integer id;
+    private String title;
+    private String content;
+    private LocalDateTime createTime;
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/entity/RequestComment.java b/src/main/java/com/ptp/ptplatform/entity/RequestComment.java
new file mode 100644
index 0000000..043263a
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/entity/RequestComment.java
@@ -0,0 +1,37 @@
+package com.ptp.ptplatform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@TableName("request_comments")
+public class RequestComment {
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;        // 使用包装类型以支持 null
+    private Integer postId;
+    private String authorId;
+    private String content;
+    private Integer likeCount;
+    private LocalDateTime createTime;
+    private Integer parentId;  // 新增字段,用于回复功能,0表示主评论,非0表示回复某条评论
+    private String replyTo;
+    private String ImageUrl;
+
+    @TableField(exist = false)
+    private List<RequestComment> replies = new ArrayList<>(); // 必须初始化
+
+    // 确保有getter和setter
+    public List<RequestComment> getReplies() {
+        return replies;
+    }
+
+    public void setReplies(List<RequestComment> replies) {
+        this.replies = replies;
+    }
+}
diff --git a/src/main/java/com/ptp/ptplatform/entity/RequestPost.java b/src/main/java/com/ptp/ptplatform/entity/RequestPost.java
new file mode 100644
index 0000000..be7fb47
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/entity/RequestPost.java
@@ -0,0 +1,21 @@
+package com.ptp.ptplatform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+@Data
+@TableName(value = "request_posts")
+public class RequestPost {
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;        // 使用包装类型以支持 null
+    private String authorId;
+    private String title;
+    private String content;
+    private String imageUrl;
+    private Integer likeCount;
+    private Integer replyCount;
+    private LocalDateTime createTime;
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/entity/TORRENT.java b/src/main/java/com/ptp/ptplatform/entity/TORRENT.java
index ce06e22..b80986e 100644
--- a/src/main/java/com/ptp/ptplatform/entity/TORRENT.java
+++ b/src/main/java/com/ptp/ptplatform/entity/TORRENT.java
@@ -11,6 +11,8 @@
 import jakarta.persistence.GenerationType;

 import jakarta.persistence.Id;

 import lombok.Data;

+import lombok.Setter;

+

 import java.time.LocalDateTime;

 import java.util.Date;

 

@@ -18,6 +20,7 @@
 @TableName("torrent") // 使用MyBatis-Plus的注解指定表名

 public class TORRENT {

 

+    @Setter

     private Integer id;

 

     private String torrentName; // 文件名

@@ -69,7 +72,7 @@
     public long getSize() {

         return this.size;

     }

-    public int getId(){

+    public Integer getId(){

         return this.id;

     }

 }
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/entity/USER.java b/src/main/java/com/ptp/ptplatform/entity/USER.java
index 3798696..3c93c76 100644
--- a/src/main/java/com/ptp/ptplatform/entity/USER.java
+++ b/src/main/java/com/ptp/ptplatform/entity/USER.java
@@ -183,7 +183,18 @@
         if (this.upload / (fileSize + this.download) < 0.5) {
             return false;
         }
-        return true;
+
+        // 避免除以零
+        if (download == 0) return true;
+
+        // 计算当前分享率
+        double currentRatio = (double)upload / download;
+
+        // 计算下载后的分享率
+        double newRatio = (double)upload / (download + fileSize);
+
+        // 设置阈值,例如0.5
+        return newRatio >= 0.5;
 
     }
 
diff --git a/src/main/java/com/ptp/ptplatform/mapper/AnnouncementMapper.java b/src/main/java/com/ptp/ptplatform/mapper/AnnouncementMapper.java
new file mode 100644
index 0000000..6fc3f80
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/mapper/AnnouncementMapper.java
@@ -0,0 +1,43 @@
+package com.ptp.ptplatform.mapper;
+
+import com.ptp.ptplatform.entity.ANNOUNCEMENT;
+import org.apache.ibatis.annotations.*;
+
+import java.util.List;
+
+@Mapper
+public interface AnnouncementMapper {
+    // 使用注解方式实现
+    // 插入新公告(返回自增ID)
+    @Insert("INSERT INTO announcement (title, content, create_time) " +
+            "VALUES (#{title}, #{content}, #{createTime})")
+    @Options(useGeneratedKeys = true, keyProperty = "id") // 获取自增ID
+    int insert(ANNOUNCEMENT announcement);
+
+    @Select("SELECT id, title, content, create_time FROM announcement WHERE id = #{id}")
+    @Results({
+            @Result(property = "id", column = "id"),
+            @Result(property = "title", column = "title"),
+            @Result(property = "content", column = "content"),
+            @Result(property = "createTime", column = "create_time")
+    })
+    ANNOUNCEMENT selectById(Integer id);
+
+    @Select("SELECT id, title, content, create_time FROM announcement ORDER BY create_time DESC LIMIT #{limit}")
+    @Results({
+            @Result(property = "id", column = "id"),
+            @Result(property = "title", column = "title"),
+            @Result(property = "content", column = "content"),
+            @Result(property = "createTime", column = "create_time")
+    })
+    List<ANNOUNCEMENT> selectLatest(int limit);
+
+    @Select("SELECT id, title, content, create_time FROM announcement ORDER BY create_time DESC")
+    @Results({
+            @Result(property = "id", column = "id"),
+            @Result(property = "title", column = "title"),
+            @Result(property = "content", column = "content"),
+            @Result(property = "createTime", column = "create_time")
+    })
+    List<ANNOUNCEMENT> selectAll();
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/mapper/DownloadTorrentMapper.java b/src/main/java/com/ptp/ptplatform/mapper/DownloadTorrentMapper.java
index 22ff203..b488b7f 100644
--- a/src/main/java/com/ptp/ptplatform/mapper/DownloadTorrentMapper.java
+++ b/src/main/java/com/ptp/ptplatform/mapper/DownloadTorrentMapper.java
@@ -5,6 +5,7 @@
 import org.apache.ibatis.annotations.*;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 
+import org.apache.ibatis.annotations.*;
 
 import java.util.List;
 
@@ -34,4 +35,8 @@
 
     @Select("SELECT DISTINCT username FROM download_torrent WHERE torrentid = #{torrentId}")
     List<String> findUsersByTorrentId(@Param("torrentId") Integer torrentId);
+    // 添加删除方法
+    @Delete("DELETE FROM download_torrent WHERE torrentid = #{torrentId}")
+    int deleteByTorrentId(@Param("torrentId") int torrentId);
+
 }
diff --git a/src/main/java/com/ptp/ptplatform/mapper/RequestCommentMapper.java b/src/main/java/com/ptp/ptplatform/mapper/RequestCommentMapper.java
new file mode 100644
index 0000000..b4566cc
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/mapper/RequestCommentMapper.java
@@ -0,0 +1,8 @@
+package com.ptp.ptplatform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ptp.ptplatform.entity.RequestComment;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface RequestCommentMapper extends BaseMapper<RequestComment> {}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/mapper/RequestPostMapper.java b/src/main/java/com/ptp/ptplatform/mapper/RequestPostMapper.java
new file mode 100644
index 0000000..e746175
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/mapper/RequestPostMapper.java
@@ -0,0 +1,8 @@
+package com.ptp.ptplatform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ptp.ptplatform.entity.RequestPost;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface RequestPostMapper extends BaseMapper<RequestPost> {}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/mapper/TorrentMapper.java b/src/main/java/com/ptp/ptplatform/mapper/TorrentMapper.java
index deb43fc..bdb5d6f 100644
--- a/src/main/java/com/ptp/ptplatform/mapper/TorrentMapper.java
+++ b/src/main/java/com/ptp/ptplatform/mapper/TorrentMapper.java
@@ -61,6 +61,7 @@
     int countByUsername(@Param("username") String username);
 
     // 插入Torrent
+    @Options(useGeneratedKeys = true, keyProperty = "id")
     @Insert("INSERT INTO torrent (torrent_name, description, category, region, resolution, subtitle, size, hash, username, create_time, file_path) " +
             "VALUES (#{torrentName}, #{description}, #{category}, #{region}, #{resolution}, #{subtitle}, #{size}, #{hash}, #{username}, #{createTime}, #{filePath})")
     int insertTorrent(TORRENT torrent);
diff --git a/src/main/java/com/ptp/ptplatform/service/ClientService.java b/src/main/java/com/ptp/ptplatform/service/ClientService.java
index daa4544..848b132 100644
--- a/src/main/java/com/ptp/ptplatform/service/ClientService.java
+++ b/src/main/java/com/ptp/ptplatform/service/ClientService.java
@@ -36,12 +36,19 @@
 
     // 修改后的获取下载进度方法
     public double getDownloadProgress(String torrentFilePath) throws Exception {
-        TorrentStatistic stats = client.getStatistics(torrentFilePath);
-        if (stats == null) {
-            return 0.0;
+        try {
+            TorrentStatistic stats = client.getStatistics(torrentFilePath);
+            if (stats == null) {
+                // 如果统计信息为空,可能下载已完成
+                return 1.0; // 返回100%表示已完成
+            }
+            double progress = stats.getPercentageDownloaded();
+            // 确保进度在0-1之间
+            return Math.max(0, Math.min(1, progress));
+        } catch (Exception e) {
+            // 如果出现异常,可能下载已完成
+            return 1.0;
         }
-        // 直接使用类中已有的百分比计算方法
-        return stats.getPercentageDownloaded();
     }
 
     public List<String[]> getTasks() {
diff --git a/src/main/java/com/ptp/ptplatform/service/RequestCommentService.java b/src/main/java/com/ptp/ptplatform/service/RequestCommentService.java
new file mode 100644
index 0000000..d661294
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/service/RequestCommentService.java
@@ -0,0 +1,15 @@
+package com.ptp.ptplatform.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ptp.ptplatform.entity.RequestComment;
+
+import java.util.List;
+
+public interface RequestCommentService extends IService<RequestComment> {
+    void incrementLike(int commentId);
+    List<RequestComment> getReplies(int parentId);
+    // 新增:获取帖子直接评论数
+    long countByPostId(Integer postId);// 新增方法
+    void commentOnRequestPost(RequestComment comment);
+    void replyComment(RequestComment reply);
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/RequestPostService.java b/src/main/java/com/ptp/ptplatform/service/RequestPostService.java
new file mode 100644
index 0000000..b3703f5
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/service/RequestPostService.java
@@ -0,0 +1,12 @@
+package com.ptp.ptplatform.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ptp.ptplatform.entity.RequestComment;
+import com.ptp.ptplatform.entity.RequestPost;
+
+public interface RequestPostService extends IService<RequestPost> {
+    void incrementLike(Integer postId);
+    void incrementReplyCount(Integer postId);
+    void likePost(Integer postId, String likerId);//点赞,并通知该帖子的作者。
+    void commentOnRequestPost(RequestComment comment); //在帖子下发表一条新评论,并通知该帖子的作者。
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/impl/RequestCommentServiceImpl.java b/src/main/java/com/ptp/ptplatform/service/impl/RequestCommentServiceImpl.java
new file mode 100644
index 0000000..fd92b9d
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/service/impl/RequestCommentServiceImpl.java
@@ -0,0 +1,105 @@
+package com.ptp.ptplatform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ptp.ptplatform.entity.RequestComment;
+import com.ptp.ptplatform.entity.RequestPost;
+import com.ptp.ptplatform.entity.Notification;
+import com.ptp.ptplatform.mapper.RequestCommentMapper;
+import com.ptp.ptplatform.mapper.RequestPostMapper;
+import com.ptp.ptplatform.service.RequestCommentService;
+import com.ptp.ptplatform.service.NotificationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+
+@Service
+public class RequestCommentServiceImpl
+        extends ServiceImpl<RequestCommentMapper, RequestComment>
+        implements RequestCommentService {
+    @Autowired
+    private RequestCommentMapper requestCommentMapper;
+
+    @Autowired
+    private RequestPostMapper requestPostMapper;
+
+    @Autowired
+    private NotificationService notificationService;
+
+    @Override
+    @Transactional
+    public void incrementLike(int commentId) {
+        this.update(null,
+                new com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper<RequestComment>()
+                        .eq("id", commentId)
+                        .setSql("like_count = like_count + 1")
+        );
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<RequestComment> getReplies(int parentId) {
+        return this.baseMapper.selectList(
+                new QueryWrapper<RequestComment>()
+                        .eq("parent_id", parentId)
+                        .orderByAsc("create_time")
+        );
+    }
+    @Override
+    public long countByPostId(Integer postId) {
+        return count(new QueryWrapper<RequestComment>()
+                .eq("post_id", postId)); // 只按post_id统计
+    }
+
+    @Override
+    public void commentOnRequestPost(RequestComment comment) {
+        // 1. 保存评论
+        requestCommentMapper.insert(comment);
+
+        // 2. 通知帖子作者
+        RequestPost post = requestPostMapper.selectById(comment.getPostId());
+        if (post != null
+                && !post.getAuthorId().equals(comment.getAuthorId())) {
+            Notification n = new Notification();
+            n.setUserId(post.getAuthorId());
+            n.setType("POST_REPLY");
+            n.setTitle("您的帖子有了新回复");
+            n.setContent("用户 "
+                    + comment.getAuthorId()
+                    + " 评论了您的帖子: \""
+                    + comment.getContent()
+                    + "\"");
+            n.setTargetId(post.getId());
+            n.setTargetType("HelpPost");
+            notificationService.saveNotification(n);
+        }
+    }
+
+    /**
+     * 用户回复已有评论
+     */
+    @Override
+    public void replyComment(RequestComment reply) {
+        // 1. 保存子评论
+        requestCommentMapper.insert(reply);
+
+        // 2. 通知父评论作者
+        RequestComment parent = requestCommentMapper.selectById(reply.getParentId());
+        if (parent != null
+                && !parent.getAuthorId().equals(reply.getAuthorId())) {
+            Notification n = new Notification();
+            n.setUserId(parent.getAuthorId());
+            n.setType("COMMENT_REPLY");
+            n.setTitle("您的评论有了新回复");
+            n.setContent("用户 "
+                    + reply.getAuthorId()
+                    + " 回复了您的评论: \""
+                    + reply.getContent()
+                    + "\"");
+            n.setTargetId(parent.getId());
+            n.setTargetType("RequestComment");
+            notificationService.saveNotification(n);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/impl/RequestPostServiceImpl.java b/src/main/java/com/ptp/ptplatform/service/impl/RequestPostServiceImpl.java
new file mode 100644
index 0000000..08b5767
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/service/impl/RequestPostServiceImpl.java
@@ -0,0 +1,122 @@
+package com.ptp.ptplatform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ptp.ptplatform.entity.RequestComment;
+import com.ptp.ptplatform.entity.RequestPost;
+import com.ptp.ptplatform.entity.Notification;
+import com.ptp.ptplatform.mapper.RequestCommentMapper;
+import com.ptp.ptplatform.mapper.RequestPostMapper;
+import com.ptp.ptplatform.service.RequestPostService;
+import com.ptp.ptplatform.service.NotificationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+
+@Service
+public class RequestPostServiceImpl extends ServiceImpl<RequestPostMapper, RequestPost> implements RequestPostService {
+    @Autowired
+    private RequestPostMapper requestPostMapper;
+    @Autowired
+    private RequestCommentMapper requestCommentMapper;
+    @Autowired
+    private NotificationService notificationService;
+    @Override
+    @Transactional
+    public void likePost(Integer postId, String likerId) {
+        // 1. 查询帖子本身,获取 authorId 和 title
+        RequestPost post = requestPostMapper.selectById(postId);
+        if (post == null) {
+            throw new RuntimeException("帖子不存在,ID=" + postId);
+        }
+
+        // 2. 更新 like_count 字段
+        requestPostMapper.update(
+                null,
+                new UpdateWrapper<RequestPost>()
+                        .eq("id", postId)
+                        .setSql("like_count = like_count + 1")
+        );
+
+        // 3. 给帖子作者发通知(排除自己给自己点赞的情况)
+        String authorId = post.getAuthorId();
+        if (!authorId.equals(likerId)) {
+            Notification n = new Notification();
+            n.setUserId(authorId);               // 通知接收方 = 帖子作者
+            n.setType("POST_LIKE");              // 通知类型,可前端约定
+            n.setTitle("您的帖子被点赞");        // 通知标题
+            n.setContent("用户 "
+                    + likerId
+                    + " 点赞了您的帖子: \""
+                    + post.getTitle()
+                    + "\"");
+            n.setTargetId(postId);               // 通知跳转时可指向该帖子
+            n.setTargetType("HelpPost");         // 前端可根据此值决定跳转逻辑
+            notificationService.saveNotification(n);
+        }
+    }
+    @Override
+    @Transactional
+    public void incrementLike(Integer postId) {
+        this.update(
+                null,
+                new UpdateWrapper<RequestPost>()
+                        .eq("id", postId)
+                        .setSql("like_count = like_count + 1")
+        );
+    }
+
+    @Override
+    @Transactional
+    public void incrementReplyCount(Integer postId) {
+        this.update(
+                null,
+                new UpdateWrapper<RequestPost>()
+                        .eq("id", postId)
+                        .setSql("reply_count = reply_count + 1")
+        );
+    }
+
+    @Override
+    @Transactional
+    public void commentOnRequestPost(RequestComment comment) {
+        // 1. 将新的 HelpComment 插入数据库
+        //    如果 parentId != 0,则说明这是一条对子评论的回复;如果 parentId = 0,则这就是对帖子的一级评论。
+        requestCommentMapper.insert(comment);
+
+        // 2. 帖子回复数 +1(reply_count 字段)
+        Integer postId = comment.getPostId();
+        requestPostMapper.update(
+                null,
+                new UpdateWrapper<RequestPost>()
+                        .eq("id", postId)
+                        .setSql("reply_count = reply_count + 1")
+        );
+
+        // 3. 给帖子作者发一条通知(如果评论人不是作者自己)
+        RequestPost post = requestPostMapper.selectById(postId);
+        if (post != null) {
+            String authorId = post.getAuthorId();            // 帖子作者的 ID
+            String commenterId = comment.getAuthorId();      // 当前发表评论的用户 ID
+            if (!authorId.equals(commenterId)) {
+                Notification n = new Notification();
+                n.setUserId(authorId);                      // 通知接收人 = 帖子作者
+                n.setType("POST_REPLY");                    // 通知类型,可与前端约定
+                n.setTitle("您的帖子有了新回复");             // 通知标题
+                // 通知内容示例: 用户 <commenterId> 评论了您的帖子: "评论内容"
+                n.setContent("用户 "
+                        + commenterId
+                        + " 评论了您的帖子: \""
+                        + comment.getContent()
+                        + "\"");
+                n.setTargetId(postId);                      // targetId 可指向该帖子
+                n.setTargetType("HelpPost");                // targetType = "HelpPost"
+                n.setIsRead(false);
+                n.setCreateTime(LocalDateTime.now());
+                notificationService.saveNotification(n);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/impl/TorrentServiceImpl.java b/src/main/java/com/ptp/ptplatform/service/impl/TorrentServiceImpl.java
index 3281d83..96829e7 100644
--- a/src/main/java/com/ptp/ptplatform/service/impl/TorrentServiceImpl.java
+++ b/src/main/java/com/ptp/ptplatform/service/impl/TorrentServiceImpl.java
@@ -11,6 +11,7 @@
 public class TorrentServiceImpl extends ServiceImpl<TorrentMapper, TORRENT> implements TorrentService {
 
     @Override
+
     public TORRENT getTorrentById(Integer id) {
         return this.getById(id); // 直接调用 MyBatis-Plus 提供的 getById 方法
     }
diff --git a/src/main/java/com/ptp/ptplatform/utils/JwtUtils.java b/src/main/java/com/ptp/ptplatform/utils/JwtUtils.java
index 2ed9068..5706b5e 100644
--- a/src/main/java/com/ptp/ptplatform/utils/JwtUtils.java
+++ b/src/main/java/com/ptp/ptplatform/utils/JwtUtils.java
@@ -6,7 +6,9 @@
 
 import java.util.Date;
 
+
 public class JwtUtils {
+
     private static long expire = 36000;
     // 使用足够长的密钥(至少64字符)
     private static String secret = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl";
diff --git a/src/test/java/com/ptp/ptplatform/controller/AnnouncementControllerTest.java b/src/test/java/com/ptp/ptplatform/controller/AnnouncementControllerTest.java
new file mode 100644
index 0000000..ad5d5a3
--- /dev/null
+++ b/src/test/java/com/ptp/ptplatform/controller/AnnouncementControllerTest.java
@@ -0,0 +1,158 @@
+package com.ptp.ptplatform.controller;
+
+import com.ptp.ptplatform.entity.ANNOUNCEMENT;
+import com.ptp.ptplatform.mapper.AnnouncementMapper;
+import com.ptp.ptplatform.utils.Result;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+class AnnouncementControllerTest {
+
+    @Mock
+    private AnnouncementMapper announcementMapper;
+
+    @InjectMocks
+    private AnnouncementController announcementController;
+
+    private ANNOUNCEMENT mockAnnouncement;
+    private final LocalDateTime now = LocalDateTime.now();
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+
+        // 初始化测试数据
+        mockAnnouncement = new ANNOUNCEMENT();
+        mockAnnouncement.setId(1);
+        mockAnnouncement.setTitle("测试公告");
+        mockAnnouncement.setContent("这是测试公告内容");
+        mockAnnouncement.setCreateTime(now);
+    }
+
+    @Test
+    void listAll_ShouldReturnAllAnnouncements() {
+        // 准备测试数据
+        ANNOUNCEMENT a2 = new ANNOUNCEMENT();
+        a2.setId(2);
+        a2.setTitle("另一个公告");
+
+        List<ANNOUNCEMENT> announcements = Arrays.asList(mockAnnouncement, a2);
+
+        // 模拟Mapper行为
+        when(announcementMapper.selectAll()).thenReturn(announcements);
+
+        // 调用控制器方法
+        Result result = announcementController.listAll();
+
+        // 验证结果
+        assertEquals(200, result.getCode());
+        assertEquals(announcements, result.getData().get("announcements"));
+
+        // 验证Mapper调用
+        verify(announcementMapper, times(1)).selectAll();
+    }
+
+    @Test
+    void listLatest_ShouldReturnLatest5Announcements() {
+        // 准备测试数据
+        List<ANNOUNCEMENT> latestAnnouncements = Arrays.asList(mockAnnouncement);
+
+        // 模拟Mapper行为
+        when(announcementMapper.selectLatest(5)).thenReturn(latestAnnouncements);
+
+        // 调用控制器方法
+        Result result = announcementController.listLatest();
+
+        // 验证结果
+        assertEquals(200, result.getCode());
+        assertEquals(latestAnnouncements, result.getData().get("announcements"));
+
+        // 验证Mapper调用
+        verify(announcementMapper, times(1)).selectLatest(5);
+    }
+
+    @Test
+    void getDetail_ShouldReturnAnnouncement_WhenExists() {
+        // 模拟Mapper行为
+        when(announcementMapper.selectById(1)).thenReturn(mockAnnouncement);
+
+        // 调用控制器方法
+        Result result = announcementController.getDetail(1);
+
+        // 验证结果
+        assertEquals(200, result.getCode());
+        assertEquals(mockAnnouncement, result.getData().get("announcement"));
+
+        // 验证Mapper调用
+        verify(announcementMapper, times(1)).selectById(1);
+    }
+
+
+
+    @Test
+    void createAnnouncement_ShouldReturnSuccess_WhenValidInput() {
+        // 模拟Mapper行为
+        when(announcementMapper.insert(any(ANNOUNCEMENT.class))).thenReturn(1);
+
+        // 调用控制器方法
+        Result result = announcementController.createAnnouncement("测试标题", "测试内容");
+
+        // 验证结果
+        assertEquals(200, result.getCode());
+        assertEquals("公告发布成功", result.getMessage());
+
+        // 验证Mapper调用
+        ArgumentCaptor<ANNOUNCEMENT> captor = ArgumentCaptor.forClass(ANNOUNCEMENT.class);
+        verify(announcementMapper, times(1)).insert(captor.capture());
+
+        ANNOUNCEMENT saved = captor.getValue();
+        assertEquals("测试标题", saved.getTitle());
+        assertEquals("测试内容", saved.getContent());
+        assertNotNull(saved.getCreateTime());
+    }
+
+    @Test
+    void createAnnouncement_ShouldReturnError_WhenInsertFails() {
+        // 模拟Mapper行为
+        when(announcementMapper.insert(any(ANNOUNCEMENT.class))).thenReturn(0);
+
+        // 调用控制器方法
+        Result result = announcementController.createAnnouncement("测试标题", "测试内容");
+
+        // 验证结果
+        assertEquals(500, result.getCode());
+        assertEquals("公告发布失败", result.getMessage());
+
+        // 验证Mapper调用
+        verify(announcementMapper, times(1)).insert(any(ANNOUNCEMENT.class));
+    }
+
+    @Test
+    void createAnnouncement_ShouldReturnError_WhenExceptionOccurs() {
+        // 模拟Mapper抛出异常
+        when(announcementMapper.insert(any(ANNOUNCEMENT.class)))
+                .thenThrow(new RuntimeException("数据库错误"));
+
+        // 调用控制器方法
+        Result result = announcementController.createAnnouncement("测试标题", "测试内容");
+
+        // 验证结果
+        assertEquals(500, result.getCode());
+        assertTrue(result.getMessage().contains("服务器错误"));
+
+        // 验证Mapper调用
+        verify(announcementMapper, times(1)).insert(any(ANNOUNCEMENT.class));
+    }
+}
diff --git a/src/test/java/com/ptp/ptplatform/controller/RequestCommentControllerTest.java b/src/test/java/com/ptp/ptplatform/controller/RequestCommentControllerTest.java
new file mode 100644
index 0000000..a51a300
--- /dev/null
+++ b/src/test/java/com/ptp/ptplatform/controller/RequestCommentControllerTest.java
@@ -0,0 +1,173 @@
+package com.ptp.ptplatform.controller;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.ptp.ptplatform.entity.RequestComment;
+import com.ptp.ptplatform.service.RequestCommentService;
+import com.ptp.ptplatform.service.RequestPostService;
+import com.ptp.ptplatform.utils.Result;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.mock.web.MockMultipartFile;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+class RequestCommentControllerTest {
+
+    @Mock
+    private RequestCommentService commentService;
+
+    @Mock
+    private RequestPostService postService;
+
+    @InjectMocks
+    private RequestCommentController commentController;
+
+    private final String authorId = "user123";
+    private final String otherAuthorId = "user456";
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+    }
+
+    @Test
+    void likeComment_ShouldReturnSuccess() {
+        int commentId = 1;
+        doNothing().when(commentService).incrementLike(commentId);
+
+        Result result = commentController.like(commentId);
+
+        assertEquals(200, result.getCode());
+        assertEquals("成功", result.getMessage());
+        verify(commentService, times(1)).incrementLike(commentId);
+    }
+
+    @Test
+    void getReplies_ShouldReturnReplies() {
+        int commentId = 1;
+        RequestComment r1 = new RequestComment();
+        r1.setId(2); r1.setPostId(1); r1.setParentId(commentId);
+        r1.setAuthorId("user2"); r1.setContent("Reply 1");
+        r1.setCreateTime(LocalDateTime.now()); r1.setLikeCount(0);
+        r1.setReplyTo("user1");
+
+        RequestComment r2 = new RequestComment();
+        r2.setId(3); r2.setPostId(1); r2.setParentId(commentId);
+        r2.setAuthorId("user3"); r2.setContent("Reply 2");
+        r2.setCreateTime(LocalDateTime.now()); r2.setLikeCount(0);
+        r2.setReplyTo("user1");
+
+        List<RequestComment> mockReplies = Arrays.asList(r1, r2);
+        when(commentService.getReplies(commentId)).thenReturn(mockReplies);
+
+        Result result = commentController.getReplies(commentId);
+
+        assertEquals(200, result.getCode());
+        @SuppressWarnings("unchecked")
+        List<RequestComment> ret = (List<RequestComment>) result.getData().get("replies");
+        assertEquals(mockReplies, ret);
+        verify(commentService, times(1)).getReplies(commentId);
+    }
+
+    @Test
+    void addReplyWithoutImage_ShouldReturnSuccess() {
+        int commentId = 5;
+        String authorId = "userX";
+        String content = "no image reply";
+
+        RequestComment parent = new RequestComment();
+        parent.setId(commentId);
+        parent.setAuthorId("userParent");
+        parent.setPostId(10);
+        when(commentService.getById(commentId)).thenReturn(parent);
+
+        when(commentService.save(any(RequestComment.class))).thenReturn(true);
+
+        Result result = commentController.addReply(commentId, authorId, content, null);
+
+        assertEquals(200, result.getCode());
+        RequestComment reply = (RequestComment) result.getData().get("reply");
+        assertNotNull(reply);
+        assertEquals(authorId, reply.getAuthorId());
+        assertEquals(content, reply.getContent());
+        assertNull(reply.getImageUrl());
+        assertEquals(commentId, result.getData().get("parentCommentId"));
+        verify(commentService, times(1)).save(any(RequestComment.class));
+    }
+
+    @Test
+    void addReplyWithImage_ShouldReturnSuccess() {
+        int commentId = 8;
+        String authorId = "userY";
+        String content = "with image reply";
+
+        RequestComment parent = new RequestComment();
+        parent.setId(commentId);
+        parent.setAuthorId("userParent");
+        parent.setPostId(20);
+        when(commentService.getById(commentId)).thenReturn(parent);
+
+        byte[] data = {0x1, 0x2};
+        MockMultipartFile image = new MockMultipartFile(
+                "image", "test.png", "image/png", data
+        );
+
+        when(commentService.save(any(RequestComment.class))).thenReturn(true);
+
+        Result result = commentController.addReply(commentId, authorId, content, image);
+
+        assertEquals(200, result.getCode());
+        RequestComment reply = (RequestComment) result.getData().get("reply");
+        assertNotNull(reply);
+        assertEquals(authorId, reply.getAuthorId());
+        assertEquals(content, reply.getContent());
+        assertNotNull(reply.getImageUrl());
+        assertTrue(reply.getImageUrl().startsWith("/uploads/"));
+        assertEquals(commentId, result.getData().get("parentCommentId"));
+        verify(commentService, times(1)).save(any(RequestComment.class));
+    }
+
+    @Test
+    void addReply_ShouldReturnError_WhenParentNotExist() {
+        int commentId = 99;
+        when(commentService.getById(commentId)).thenReturn(null);
+
+        Result result = commentController.addReply(commentId, "any", "content", null);
+
+        assertEquals(500, result.getCode());
+        assertEquals("被回复的评论不存在", result.getMessage());
+        verify(commentService, never()).save(any());
+    }
+
+    @Test
+    void deleteComment_ShouldReturnSuccess_WhenAuthorMatches() {
+        // 准备测试数据
+        int commentId = 1;
+        RequestComment comment = new RequestComment();
+        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/RequestPostControllerTest.java b/src/test/java/com/ptp/ptplatform/controller/RequestPostControllerTest.java
new file mode 100644
index 0000000..f7b8975
--- /dev/null
+++ b/src/test/java/com/ptp/ptplatform/controller/RequestPostControllerTest.java
@@ -0,0 +1,275 @@
+package com.ptp.ptplatform.controller;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ptp.ptplatform.entity.RequestComment;
+import com.ptp.ptplatform.entity.RequestPost;
+import com.ptp.ptplatform.entity.Notification;
+import com.ptp.ptplatform.mapper.RequestPostMapper;
+import com.ptp.ptplatform.service.RequestCommentService;
+import com.ptp.ptplatform.service.RequestPostService;
+import com.ptp.ptplatform.service.NotificationService;
+import com.ptp.ptplatform.utils.Result;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.*;
+
+class RequestPostControllerTest {
+
+    @Mock
+    private RequestPostService postService;
+
+    @Mock
+    private RequestCommentService commentService;
+
+    @Mock
+    private NotificationService notificationService;
+
+    @InjectMocks
+    private RequestPostController 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());
+        RequestPost createdPost = (RequestPost) result.getData().get("post");
+        assertNotNull(createdPost.getImageUrl());
+        assertTrue(createdPost.getImageUrl().startsWith("/uploads/"));
+
+        // 验证服务调用
+        verify(postService).save(any(RequestPost.class));
+    }
+
+    @Test
+    void getPost_ShouldReturnPostWithComments_WhenPostExists() {
+        int postId = 1;
+        RequestPost post = new RequestPost();
+        post.setId(postId);
+        post.setTitle("Test Post");
+        post.setContent("Test Content");
+        post.setAuthorId("user1");
+        post.setCreateTime(LocalDateTime.now());
+
+        RequestComment c1 = new RequestComment();
+        c1.setId(1); c1.setPostId(postId); c1.setParentId(0);
+        c1.setAuthorId("user2"); c1.setContent("Comment 1");
+        c1.setCreateTime(LocalDateTime.now());
+
+        RequestComment c2 = new RequestComment();
+        c2.setId(2); c2.setPostId(postId); c2.setParentId(0);
+        c2.setAuthorId("user3"); c2.setContent("Comment 2");
+        c2.setCreateTime(LocalDateTime.now());
+
+        List<RequestComment> comments = Arrays.asList(c1, c2);
+
+        when(postService.getById(postId)).thenReturn(post);
+        // 避免重载二义性,明确 stub list(Wrapper)
+        doReturn(comments)
+                .when(commentService)
+                .list(any(Wrapper.class));
+
+        Result result = postController.getPost(postId);
+
+        assertEquals(200, result.getCode());
+        assertEquals(post, result.getData().get("post"));
+        @SuppressWarnings("unchecked")
+        List<RequestComment> ret = (List<RequestComment>) result.getData().get("comments");
+        assertEquals(2, ret.size());
+
+        verify(postService, times(1)).getById(postId);
+        verify(commentService, times(1)).list(any(Wrapper.class));
+    }
+
+    @Test
+    void listPosts_ShouldReturnPaginatedPosts() {
+        int page = 1, size = 5;
+        RequestPost p1 = new RequestPost(); p1.setId(1); p1.setAuthorId("u1");
+        RequestPost p2 = new RequestPost(); p2.setId(2); p2.setAuthorId("u2");
+        List<RequestPost> posts = Arrays.asList(p1, p2);
+
+        Page<RequestPost> pageResult = new Page<>(page, size);
+        pageResult.setRecords(posts).setTotal(posts.size());
+
+        when(postService.page(any(Page.class), any())).thenReturn(pageResult);
+
+        Result result = postController.listPosts(page, size);
+
+        assertEquals(200, result.getCode());
+        assertEquals(posts, result.getData().get("records"));
+        assertEquals((long) posts.size(), result.getData().get("total"));
+
+        verify(postService, times(1)).page(any(Page.class), any());
+    }
+
+    @Test
+    void getPost_ShouldReturnError_WhenPostNotExists() {
+        int postId = 1;
+        when(postService.getById(postId)).thenReturn(null);
+
+        Result result = postController.getPost(postId);
+
+        assertEquals(500, result.getCode());
+        assertEquals("帖子不存在", result.getMessage());
+
+        verify(postService, times(1)).getById(postId);
+    }
+
+    @Test
+    void likePost_ShouldReturnSuccess() {
+        int postId = 1;
+        String likerId = "alice123";
+        // 对应 ServiceImpl 中的 likePost(postId, likerId)
+        doNothing().when(postService).likePost(postId, likerId);
+
+        Result result = postController.likePost(postId, likerId);
+
+        assertEquals(200, result.getCode());
+        assertEquals("点赞成功", result.getMessage());
+        verify(postService, times(1)).likePost(postId, likerId);
+    }
+
+    @Test
+    void deletePost_ShouldReturnSuccess_WhenAuthorMatches() {
+        int postId = 42;
+        RequestPost mockPost = new RequestPost();
+        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, authorId);
+
+        assertEquals(200, result.getCode());
+        verify(commentService, times(1)).remove(any(Wrapper.class));
+        verify(postService, times(1)).removeById(postId);
+    }
+
+    @Test
+    void addCommentWithoutImage_ShouldReturnSuccess() {
+        int postId    = 7;
+        String author = "user2";
+        String content= "Hello";
+
+        // 构造“保存后”返回给前端的 RequestComment 对象
+        RequestComment saved = new RequestComment();
+        saved.setId(99);
+        saved.setPostId(postId);
+        saved.setAuthorId(author);
+        saved.setContent(content);
+        saved.setImageUrl(null);
+
+        // 1) 当 commentService.commentOnRequestPost(...) 被调用时,
+        //    我们在 doAnswer 中给它传入的 HelpComment 对象设置 ID = 99
+        doAnswer(invocation -> {
+            RequestComment arg = invocation.getArgument(0);
+            arg.setId(saved.getId());
+            return null; // commentOnRequestPost 返回 void
+        }).when(commentService).commentOnRequestPost(any(RequestComment.class));
+
+        // 2) 后面 commentService.getById(99) 返回我们上面构造的 saved
+        when(commentService.getById(saved.getId())).thenReturn(saved);
+
+        // 模拟:commentOnHelpPost() 内部会调用 postService.incrementReplyCount(postId)
+        // 但控制器本身并不直接调用 incrementReplyCount,所以测试里不再 verify 这一行。
+        // 只要保证 postService.getById(postId) 能返回一个带有 replyCount 的 RequestPost 即可:
+        RequestPost stubPost = new RequestPost();
+        stubPost.setReplyCount(5);
+        when(postService.getById(postId)).thenReturn(stubPost);
+
+        // 调用控制器的 commentOnPost(...) 方法,第四个参数传 null(不带图片)
+        Result result = postController.commentOnPost(postId, author, content, null);
+
+        assertEquals(200, result.getCode());
+        assertEquals(saved, result.getData().get("comment"));
+        assertEquals(5,     result.getData().get("newReplyCount"));
+
+        // 确保 commentService.commentOnHelpPost(...) 被调用一次
+        verify(commentService, times(1)).commentOnRequestPost(any(RequestComment.class));
+        // postService.incrementReplyCount(...) 由 commentOnRequestPost 内部调用,因此测试里无需 verify
+    }
+
+
+    @Test
+    void addCommentWithImage_ShouldReturnSuccess() throws Exception {
+        int postId    = 8;
+        String author = "user3";
+        String content= "With Image";
+
+        // 准备一个 “模拟上传文件”
+        MockMultipartFile mockFile = new MockMultipartFile(
+                "image", "test.jpg", "image/jpeg", new byte[]{1,2,3}
+        );
+
+        // 构造“保存后”返回给前端的 RequestComment 对象
+        RequestComment saved = new RequestComment();
+        saved.setId(100);
+        saved.setPostId(postId);
+        saved.setAuthorId(author);
+        saved.setContent(content);
+        saved.setImageUrl("/uploads/whatever.jpg");
+
+        // 当 commentService.commentOnRequestPost(...) 被调用时,给传入的对象设置 ID 与 imageUrl
+        doAnswer(invocation -> {
+            RequestComment arg = invocation.getArgument(0);
+            arg.setId(saved.getId());
+            arg.setImageUrl(saved.getImageUrl());
+            return null; // commentOnRequestPost 返回 void
+        }).when(commentService).commentOnRequestPost(any(RequestComment.class));
+
+        // 后面 commentService.getById(100) 返回我们构造的 saved
+        when(commentService.getById(saved.getId())).thenReturn(saved);
+
+        // 同样,postService.getById(postId) 返回一个带 replyCount=10 的 HelpPost
+        RequestPost stubPost = new RequestPost();
+        stubPost.setReplyCount(10);
+        when(postService.getById(postId)).thenReturn(stubPost);
+
+        // 调用 commentOnPost,第四个参数传 mockFile(模拟有图片上传)
+        Result result = postController.commentOnPost(postId, author, content, mockFile);
+
+        assertEquals(200, result.getCode());
+        assertEquals(saved, result.getData().get("comment"));
+        assertEquals(10,    result.getData().get("newReplyCount"));
+
+        // 验证真正传给 commentService.commentOnRequestPost(...) 的 HelpComment 对象中,
+        // imageUrl 已经被设置成 "/uploads/whatever.jpg"
+        ArgumentCaptor<RequestComment> captor = ArgumentCaptor.forClass(RequestComment.class);
+        verify(commentService).commentOnRequestPost(captor.capture());
+        RequestComment toSave = captor.getValue();
+        assertEquals(saved.getImageUrl(), toSave.getImageUrl());
+
+        // 同样无需 verify postService.incrementReplyCount(...),因为该调用在 commentOnHelpPost 内部完成
+    }
+}
diff --git a/torrents/1748539198669_32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent b/torrents/1748539198669_32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent
new file mode 100644
index 0000000..7e57dcc
--- /dev/null
+++ b/torrents/1748539198669_32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent
@@ -0,0 +1 @@
+d8:announce30:http://localhost:6969/announce10:created by21:qBittorrent v4.5.3.1013:creation datei1748520246e4:infod6:lengthi53458e4:name35:32e995db2ab833ae6a12a3b1d521e5b.jpg12:piece lengthi16384e6:pieces80:6<MÙ>¿€?ï™ÿ!v`™÷F™€—¤zj„&â“þS¹’²jùk“50Õ·ÁIó™¶I»®þ¦ÄbÏ$:âm«%»®#ô~å'5\~4¨U.*ee
\ No newline at end of file
diff --git a/torrents/1748539331027_32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent b/torrents/1748539331027_32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent
new file mode 100644
index 0000000..7e57dcc
--- /dev/null
+++ b/torrents/1748539331027_32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent
@@ -0,0 +1 @@
+d8:announce30:http://localhost:6969/announce10:created by21:qBittorrent v4.5.3.1013:creation datei1748520246e4:infod6:lengthi53458e4:name35:32e995db2ab833ae6a12a3b1d521e5b.jpg12:piece lengthi16384e6:pieces80:6<MÙ>¿€?ï™ÿ!v`™÷F™€—¤zj„&â“þS¹’²jùk“50Õ·ÁIó™¶I»®þ¦ÄbÏ$:âm«%»®#ô~å'5\~4¨U.*ee
\ No newline at end of file
diff --git a/torrents/1748581612614_32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent b/torrents/1748581612614_32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent
new file mode 100644
index 0000000..7e57dcc
--- /dev/null
+++ b/torrents/1748581612614_32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent
@@ -0,0 +1 @@
+d8:announce30:http://localhost:6969/announce10:created by21:qBittorrent v4.5.3.1013:creation datei1748520246e4:infod6:lengthi53458e4:name35:32e995db2ab833ae6a12a3b1d521e5b.jpg12:piece lengthi16384e6:pieces80:6<MÙ>¿€?ï™ÿ!v`™÷F™€—¤zj„&â“þS¹’²jùk“50Õ·ÁIó™¶I»®þ¦ÄbÏ$:âm«%»®#ô~å'5\~4¨U.*ee
\ No newline at end of file
diff --git a/torrents/32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent b/torrents/32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent
new file mode 100644
index 0000000..7e57dcc
--- /dev/null
+++ b/torrents/32e995db2ab833ae6a12a3b1d521e5b.jpg.torrent
@@ -0,0 +1 @@
+d8:announce30:http://localhost:6969/announce10:created by21:qBittorrent v4.5.3.1013:creation datei1748520246e4:infod6:lengthi53458e4:name35:32e995db2ab833ae6a12a3b1d521e5b.jpg12:piece lengthi16384e6:pieces80:6<MÙ>¿€?ï™ÿ!v`™÷F™€—¤zj„&â“þS¹’²jùk“50Õ·ÁIó™¶I»®þ¦ÄbÏ$:âm«%»®#ô~å'5\~4¨U.*ee
\ No newline at end of file
diff --git a/tracker.json b/tracker.json
index 5475d24..b632be7 100644
--- a/tracker.json
+++ b/tracker.json
@@ -1 +1 @@
-{"announceUrl":"http://10.60.12.94:6969/announce","announceURI":"http://10.60.12.94:6969/announce","trackedTorrents":[{"announceInterval":10,"peers":{"PeerUID{address=/10.60.12.94:6881, torrent hash='E5FD501A5E6F07FA02777809C83EB2B815A2C3EA'}":{"address":"10.60.12.94:6881","peerId":"Mzg3ZGMxYTgtMzU4Yy00ZDMyLWFhOTctNzQ2MzdhMmMyZDg1","hexPeerId":"33383764633161382D333538632D346433322D616139372D373436333761326332643835","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":3063963,"completed":true,"port":6881,"shortHexPeerId":"..643835","stringPeerId":"387dc1a8-358c-4d32-aa97-74637a2c2d85","hostIdentifier":"/10.60.12.94:6881","ip":"10.60.12.94","peerIdArray":"Mzg3ZGMxYTgtMzU4Yy00ZDMyLWFhOTctNzQ2MzdhMmMyZDg1","rawIp":"CjwMXg=="},"PeerUID{address=/10.60.12.94:6883, torrent hash='E5FD501A5E6F07FA02777809C83EB2B815A2C3EA'}":{"address":"10.60.12.94:6883","peerId":"LVRPMDA0Mi0wYjI5MDQwZDE3MWQ=","hexPeerId":"2D544F303034322D306232393034306431373164","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":3063963,"completed":false,"port":6883,"shortHexPeerId":"..373164","stringPeerId":"-TO0042-0b29040d171d","hostIdentifier":"/10.60.12.94:6883","ip":"10.60.12.94","peerIdArray":"LVRPMDA0Mi0wYjI5MDQwZDE3MWQ=","rawIp":"CjwMXg=="},"PeerUID{address=/10.60.12.94:6882, torrent hash='E5FD501A5E6F07FA02777809C83EB2B815A2C3EA'}":{"address":"10.60.12.94:6882","peerId":"LVRPMDA0Mi0xNWI3N2EwZWViZWY=","hexPeerId":"2D544F303034322D313562373761306565626566","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":3063963,"completed":false,"port":6882,"shortHexPeerId":"..626566","stringPeerId":"-TO0042-15b77a0eebef","hostIdentifier":"/10.60.12.94:6882","ip":"10.60.12.94","peerIdArray":"LVRPMDA0Mi0xNWI3N2EwZWViZWY=","rawIp":"CjwMXg=="}},"hexInfoHash":"E5FD501A5E6F07FA02777809C83EB2B815A2C3EA","infoHash":"5f1QGl5vB/oCd3gJyD6yuBWiw+o="},{"announceInterval":10,"peers":{"PeerUID{address=/10.60.12.94:6882, torrent hash='34D94F184C0DC45372F52DD0FBAEB79AF4BD758E'}":{"address":"10.60.12.94:6882","peerId":"LVRPMDA0Mi0xNWI3N2EwZWViZWY=","hexPeerId":"2D544F303034322D313562373761306565626566","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":1609568975,"completed":false,"port":6882,"shortHexPeerId":"..626566","stringPeerId":"-TO0042-15b77a0eebef","hostIdentifier":"/10.60.12.94:6882","ip":"10.60.12.94","peerIdArray":"LVRPMDA0Mi0xNWI3N2EwZWViZWY=","rawIp":"CjwMXg=="},"PeerUID{address=/10.60.12.94:6881, torrent hash='34D94F184C0DC45372F52DD0FBAEB79AF4BD758E'}":{"address":"10.60.12.94:6881","peerId":"YmI0YzJkYTgtMWRiMy00ODRiLTg0ZDgtNjhkMjAwMzViNzhk","hexPeerId":"62623463326461382D316462332D343834622D383464382D363864323030333562373864","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":1609568975,"completed":false,"port":6881,"shortHexPeerId":"..373864","stringPeerId":"bb4c2da8-1db3-484b-84d8-68d20035b78d","hostIdentifier":"/10.60.12.94:6881","ip":"10.60.12.94","peerIdArray":"YmI0YzJkYTgtMWRiMy00ODRiLTg0ZDgtNjhkMjAwMzViNzhk","rawIp":"CjwMXg=="}},"hexInfoHash":"34D94F184C0DC45372F52DD0FBAEB79AF4BD758E","infoHash":"NNlPGEwNxFNy9S3Q+663mvS9dY4="}]}
\ No newline at end of file
+{"trackedTorrents":[{"announceInterval":10,"peers":{"PeerUID{address=/10.61.51.238:6881, torrent hash='53475A61645AA5B1141807367D7AA258016E1215'}":{"address":"10.61.51.238:6881","peerId":"NjE5ZDk5NTEtN2ExOS00MDRlLWIzOTktYzQ5YWU4YTJhNTJj","hexPeerId":"36313964393935312D376131392D343034652D623339392D633439616538613261353263","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":53458,"completed":true,"port":6881,"ip":"10.61.51.238","rawIp":"Cj0z7g==","peerIdArray":"NjE5ZDk5NTEtN2ExOS00MDRlLWIzOTktYzQ5YWU4YTJhNTJj","hostIdentifier":"/10.61.51.238:6881","stringPeerId":"619d9951-7a19-404e-b399-c49ae8a2a52c","shortHexPeerId":"..353263"},"PeerUID{address=/127.0.0.1:62128, torrent hash='53475A61645AA5B1141807367D7AA258016E1215'}":{"address":"127.0.0.1:62128","peerId":"LXFCNDUzQS1WTEdLODI4S1lLRUc=","hexPeerId":"2D7142343533412D564C474B3832384B594B4547","hexInfoHash":null,"uploaded":320748,"downloaded":0,"left":0,"completed":true,"port":62128,"ip":"127.0.0.1","rawIp":"fwAAAQ==","peerIdArray":"LXFCNDUzQS1WTEdLODI4S1lLRUc=","hostIdentifier":"/127.0.0.1:62128","stringPeerId":"-qB453A-VLGK828KYKEG","shortHexPeerId":"..4B4547"},"PeerUID{address=/[0:0:0:0:0:0:0:1]:62128, torrent hash='53475A61645AA5B1141807367D7AA258016E1215'}":{"address":"[0:0:0:0:0:0:0:1]:62128","peerId":"LXFCNDUzQS1WTEdLODI4S1lLRUc=","hexPeerId":"2D7142343533412D564C474B3832384B594B4547","hexInfoHash":null,"uploaded":320748,"downloaded":0,"left":0,"completed":true,"port":62128,"ip":"0:0:0:0:0:0:0:1","rawIp":"AAAAAAAAAAAAAAAAAAAAAQ==","peerIdArray":"LXFCNDUzQS1WTEdLODI4S1lLRUc=","hostIdentifier":"/0:0:0:0:0:0:0:1:62128","stringPeerId":"-qB453A-VLGK828KYKEG","shortHexPeerId":"..4B4547"}},"hexInfoHash":"53475A61645AA5B1141807367D7AA258016E1215","infoHash":"U0daYWRapbEUGAc2fXqiWAFuEhU="}],"announceUrl":"http://LAPTOP-C3Q7MHHD:6969/announce","announceURI":"http://LAPTOP-C3Q7MHHD:6969/announce"}
\ No newline at end of file
diff --git a/uploads/1110a6dc-b6b6-4408-b33f-97afacded945.jpg b/uploads/1110a6dc-b6b6-4408-b33f-97afacded945.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/1110a6dc-b6b6-4408-b33f-97afacded945.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/15b11ff9-2344-4d3f-b4bc-49734a1e7156.png b/uploads/15b11ff9-2344-4d3f-b4bc-49734a1e7156.png
deleted file mode 100644
index 71bd63e..0000000
--- a/uploads/15b11ff9-2344-4d3f-b4bc-49734a1e7156.png
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/1ed6e86b-955c-4426-b170-73bc7558eca5.png b/uploads/1ed6e86b-955c-4426-b170-73bc7558eca5.png
deleted file mode 100644
index 71bd63e..0000000
--- a/uploads/1ed6e86b-955c-4426-b170-73bc7558eca5.png
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/38ee81c5-ee84-4371-94bd-55ff19784e97.png b/uploads/38ee81c5-ee84-4371-94bd-55ff19784e97.png
deleted file mode 100644
index ef08183..0000000
--- a/uploads/38ee81c5-ee84-4371-94bd-55ff19784e97.png
+++ /dev/null
Binary files differ
diff --git a/uploads/3faa8c92-715f-49b1-9fb4-3f8fea12cbd8.png b/uploads/3faa8c92-715f-49b1-9fb4-3f8fea12cbd8.png
deleted file mode 100644
index cee3e35..0000000
--- a/uploads/3faa8c92-715f-49b1-9fb4-3f8fea12cbd8.png
+++ /dev/null
Binary files differ
diff --git a/uploads/47983664-980d-4831-979d-22eee853c22b.jpg b/uploads/47983664-980d-4831-979d-22eee853c22b.jpg
deleted file mode 100644
index a6d7f38..0000000
--- a/uploads/47983664-980d-4831-979d-22eee853c22b.jpg
+++ /dev/null
@@ -1 +0,0 @@
-test image
\ No newline at end of file
diff --git a/uploads/4876d616-3494-4aed-896e-01edf4e0f6a6.jpg b/uploads/4876d616-3494-4aed-896e-01edf4e0f6a6.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/4876d616-3494-4aed-896e-01edf4e0f6a6.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/49d1a2db-5482-4d21-9a22-0071bd4cf5e5.jpg b/uploads/49d1a2db-5482-4d21-9a22-0071bd4cf5e5.jpg
deleted file mode 100644
index a6d7f38..0000000
--- a/uploads/49d1a2db-5482-4d21-9a22-0071bd4cf5e5.jpg
+++ /dev/null
@@ -1 +0,0 @@
-test image
\ No newline at end of file
diff --git a/uploads/4ed56b76-c367-45db-98e1-d0f36508d8f1.jpg b/uploads/4ed56b76-c367-45db-98e1-d0f36508d8f1.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/4ed56b76-c367-45db-98e1-d0f36508d8f1.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/5ea70d6e-50d5-44ec-b35e-e9e7101ee24f.png b/uploads/5ea70d6e-50d5-44ec-b35e-e9e7101ee24f.png
deleted file mode 100644
index e5d4cab..0000000
--- a/uploads/5ea70d6e-50d5-44ec-b35e-e9e7101ee24f.png
+++ /dev/null
Binary files differ
diff --git a/uploads/5fb50a1b-c4d7-42cf-82c5-92438d94b1c1.png b/uploads/5fb50a1b-c4d7-42cf-82c5-92438d94b1c1.png
deleted file mode 100644
index 71bd63e..0000000
--- a/uploads/5fb50a1b-c4d7-42cf-82c5-92438d94b1c1.png
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/665a02db-5b60-40ae-92dd-c46b31637372.jpg b/uploads/665a02db-5b60-40ae-92dd-c46b31637372.jpg
deleted file mode 100644
index a6d7f38..0000000
--- a/uploads/665a02db-5b60-40ae-92dd-c46b31637372.jpg
+++ /dev/null
@@ -1 +0,0 @@
-test image
\ No newline at end of file
diff --git a/uploads/69925fae-3c62-460c-a5c1-ee9695bc8ae2.jpg b/uploads/69925fae-3c62-460c-a5c1-ee9695bc8ae2.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/69925fae-3c62-460c-a5c1-ee9695bc8ae2.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/69c706c7-6ae4-4e2c-9c22-6d0e8874b228.jpg b/uploads/69c706c7-6ae4-4e2c-9c22-6d0e8874b228.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/69c706c7-6ae4-4e2c-9c22-6d0e8874b228.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/72db8eb6-40f8-445c-a833-43ec6fecebdd.png b/uploads/72db8eb6-40f8-445c-a833-43ec6fecebdd.png
deleted file mode 100644
index 71bd63e..0000000
--- a/uploads/72db8eb6-40f8-445c-a833-43ec6fecebdd.png
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/77032da9-b19b-4bad-a5f4-97bb656ad462.jpg b/uploads/77032da9-b19b-4bad-a5f4-97bb656ad462.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/77032da9-b19b-4bad-a5f4-97bb656ad462.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/813ea0ee-bddf-42fb-93ea-3e96be10a63a.jpg b/uploads/813ea0ee-bddf-42fb-93ea-3e96be10a63a.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/813ea0ee-bddf-42fb-93ea-3e96be10a63a.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/82d39fb4-7a1e-491f-8a62-e14f796629fe.jpg b/uploads/82d39fb4-7a1e-491f-8a62-e14f796629fe.jpg
deleted file mode 100644
index 4a7a6e2..0000000
--- a/uploads/82d39fb4-7a1e-491f-8a62-e14f796629fe.jpg
+++ /dev/null
Binary files differ
diff --git a/uploads/8dcddc1c-ec93-4ffb-881d-1531a3b3f3e0.png b/uploads/8dcddc1c-ec93-4ffb-881d-1531a3b3f3e0.png
deleted file mode 100644
index 71bd63e..0000000
--- a/uploads/8dcddc1c-ec93-4ffb-881d-1531a3b3f3e0.png
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/9f99ac77-48d0-4535-9f2f-5fe7c804a559.png b/uploads/9f99ac77-48d0-4535-9f2f-5fe7c804a559.png
deleted file mode 100644
index 5a29a05..0000000
--- a/uploads/9f99ac77-48d0-4535-9f2f-5fe7c804a559.png
+++ /dev/null
Binary files differ
diff --git a/uploads/ac46e0ec-891b-4cfb-bece-799f30cbb1f9.jpg b/uploads/ac46e0ec-891b-4cfb-bece-799f30cbb1f9.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/ac46e0ec-891b-4cfb-bece-799f30cbb1f9.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/bad196f2-d2a7-4f24-8cf9-6d450f82daf1.jpg b/uploads/bad196f2-d2a7-4f24-8cf9-6d450f82daf1.jpg
deleted file mode 100644
index 4a7a6e2..0000000
--- a/uploads/bad196f2-d2a7-4f24-8cf9-6d450f82daf1.jpg
+++ /dev/null
Binary files differ
diff --git a/uploads/baef0e69-225a-4c70-92e2-f8b1301c9dbc.jpg b/uploads/baef0e69-225a-4c70-92e2-f8b1301c9dbc.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/baef0e69-225a-4c70-92e2-f8b1301c9dbc.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/c1deedcd-b66c-4d91-b0ef-baed3edbdd17.png b/uploads/c1deedcd-b66c-4d91-b0ef-baed3edbdd17.png
deleted file mode 100644
index 71bd63e..0000000
--- a/uploads/c1deedcd-b66c-4d91-b0ef-baed3edbdd17.png
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/d967fdec-79e7-4633-9015-73c8aee165e7.jpg b/uploads/d967fdec-79e7-4633-9015-73c8aee165e7.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/d967fdec-79e7-4633-9015-73c8aee165e7.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/df715d55-ccff-46ff-8009-9a0972344742.png b/uploads/df715d55-ccff-46ff-8009-9a0972344742.png
deleted file mode 100644
index e129f0d..0000000
--- a/uploads/df715d55-ccff-46ff-8009-9a0972344742.png
+++ /dev/null
Binary files differ
diff --git a/uploads/e2255186-e4c7-49c3-b673-229c5a16a6d6.jpg b/uploads/e2255186-e4c7-49c3-b673-229c5a16a6d6.jpg
deleted file mode 100644
index a6d7f38..0000000
--- a/uploads/e2255186-e4c7-49c3-b673-229c5a16a6d6.jpg
+++ /dev/null
@@ -1 +0,0 @@
-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
deleted file mode 100644
index e129f0d..0000000
--- a/uploads/ecbe499c-3342-4e8e-bf23-e2f77a0ad40b.png
+++ /dev/null
Binary files differ
diff --git a/uploads/ef17d022-9c80-436b-87cd-227d783baadf.jpg b/uploads/ef17d022-9c80-436b-87cd-227d783baadf.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/ef17d022-9c80-436b-87cd-227d783baadf.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/f2e35f6d-2d7b-456a-8bf4-27526e20c5b5.jpg b/uploads/f2e35f6d-2d7b-456a-8bf4-27526e20c5b5.jpg
deleted file mode 100644
index aed2973..0000000
--- a/uploads/f2e35f6d-2d7b-456a-8bf4-27526e20c5b5.jpg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/uploads/fdaf20d3-faa6-4409-96cb-215869948ebf.jpg b/uploads/fdaf20d3-faa6-4409-96cb-215869948ebf.jpg
deleted file mode 100644
index a6d7f38..0000000
--- a/uploads/fdaf20d3-faa6-4409-96cb-215869948ebf.jpg
+++ /dev/null
@@ -1 +0,0 @@
-test image
\ No newline at end of file