创作中心模块包含首页展示、个人中心、帖子审核。

“首页展示”支持广告轮播展示、推广帖子优先展示、分页显示所有帖子、导航栏便捷标签筛选帖子、全局标题模糊搜索帖子、点击帖子“查看更多”进入帖子详情页。帖子详情页展示帖子封面图片、作者时间、详细内容(可以插入种子链接对种子进行介绍与推广)等基本信息、对帖子点赞收藏举报评论回复、查看相关推荐帖子。相关推荐会推荐当前帖子作者的其他帖子(最多推荐5篇),还会推荐具有相似标签的其他帖子,两者总共最多推荐9篇帖子。

“个人中心”包含“我的中心”和“我的收藏”。
“我的中心”中可以管理已经成功发布的帖子(编辑、删除帖子),还可以发布新帖子。发布新帖子时除了填写帖子基本信息以外,帖子标签支持下拉多项选择,用户还可以选择帖子推广项目并进行支付。设置了多种推广项目,包含广告轮播推广、帖子置顶展示、限时优先展示、分类页首条展示。系统后台执行自动定时任务,每小时对帖子的推广时效性进行检查,如超出推广时限,则取消帖子的推广显示特权。用户点击发布帖子后帖子处于待审核状态,需要管理员审核通过才能正常发布在首页展示页面。编辑帖子时用户可以追加帖子推广,但如果帖子处于推广状态,则禁止修改推广项目。
“我的收藏”中可以便捷查看所有已收藏的帖子。

“帖子审核”包含“帖子发布管理”和“帖子举报管理”。“帖子审核”板块具有权限管理,只有管理员界面能够进入。
“帖子发布管理”对所有待审核帖子进行处理,支持预览待审核帖子详细内容,批准通过和拒绝通过选项。
“帖子举报管理”对所有用户的举报请求进行人工审核,如果举报内容属实,则将帖子下架处理,如果举报内容不属实,驳回举报请求。所有举报请求的处理结果均留存显示,方便后续再次审查。+ bugfix

Change-Id: Iafa37f603aed3337484a3fc96d1cc70b83e8df0c
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 8053e2b..aa3f738 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -58,6 +58,12 @@
             <systemPath>${project.basedir}/torrent/ttorrent-client.jar</systemPath>
         </dependency>
 
+        <!-- 通用工具-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common</artifactId>
+        </dependency>
+
         <!-- 核心模块-->
         <dependency>
             <groupId>com.ruoyi</groupId>
@@ -76,6 +82,38 @@
             <artifactId>ruoyi-generator</artifactId>
         </dependency>
 
+        <!-- 测试依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Spring Security Test Support -->
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
 
     <build>
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCenterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCenterController.java
new file mode 100644
index 0000000..e05e55e
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCenterController.java
@@ -0,0 +1,1133 @@
+package com.ruoyi.web.controller.post.controller;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.math.BigDecimal;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.github.pagehelper.PageHelper;
+import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.web.controller.post.domain.Post;
+import com.ruoyi.web.controller.post.domain.PostComment;
+import com.ruoyi.web.controller.post.domain.PostFavorite;
+import com.ruoyi.web.controller.post.domain.PostTag;
+import com.ruoyi.web.controller.post.domain.PostPayment;
+import com.ruoyi.web.controller.post.domain.PostReport;
+import com.ruoyi.web.controller.post.service.IPostCommentService;
+import com.ruoyi.web.controller.post.service.IPostFavoriteService;
+import com.ruoyi.web.controller.post.service.IPostService;
+import com.ruoyi.web.controller.post.service.IPostTagService;
+import com.ruoyi.web.controller.post.mapper.PostPaymentMapper;
+import com.ruoyi.web.controller.post.mapper.PostReportMapper;
+
+/**
+ * 帖子中心控制器,面向前端用户
+ * 
+ * @author thunderhub
+ */
+@RestController
+@RequestMapping("/post-center")
+public class PostCenterController extends BaseController
+{
+    @Autowired
+    private IPostService postService;
+    
+    @Autowired
+    private IPostTagService postTagService;
+    
+    @Autowired
+    private IPostCommentService postCommentService;
+    
+    @Autowired
+    private IPostFavoriteService postFavoriteService;
+    
+    @Autowired
+    private PostPaymentMapper postPaymentMapper;
+    
+    @Autowired
+    private PostReportMapper postReportMapper;
+
+    /**
+     * 获取帖子列表
+     */
+    @Anonymous
+    @GetMapping("/list")
+    public TableDataInfo list(Post post, 
+                             @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+                             @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
+                             @RequestParam(value = "tags", required = false) String tags) 
+    {
+        // 手动设置分页参数 - 确保分页参数正确传递
+        PageHelper.startPage(pageNum, pageSize);
+        
+        // 只查询状态为正常的帖子
+        if (post.getStatus() == null) {
+            post.setStatus("1");
+        }
+        
+        // 如果有标签筛选参数,设置到查询条件中
+        if (tags != null && !tags.trim().isEmpty() && !"all".equals(tags)) {
+            post.setTags(tags);
+        }
+        
+        List<Post> list = postService.selectPostList(post);
+        return getDataTable(list);
+    }
+
+    /**
+     * 获取推广帖子列表(用于轮播展示)
+     */
+    @Anonymous
+    @GetMapping("/promotion")
+    public AjaxResult getPromotionPosts()
+    {
+        Post query = new Post();
+        query.setStatus("1"); // 只查询已发布的帖子
+        
+        List<Post> allPosts = postService.selectPostList(query);
+        
+        // 筛选出购买了最高等级推广(promotionPlanId = 1)的帖子用于轮播
+        List<Post> carouselPosts = allPosts.stream()
+                .filter(post -> post.getPromotionPlanId() != null && post.getPromotionPlanId().equals(1L))
+                .limit(5) // 限制轮播数量
+                .collect(java.util.stream.Collectors.toList());
+        
+        return success(carouselPosts);
+    }
+
+    /**
+     * 获取帖子详情
+     */
+    @Anonymous
+    @GetMapping("/{postId}")
+    public AjaxResult getDetail(@PathVariable Long postId)
+    {
+        Map<String, Object> result = new HashMap<>();
+        
+        // 更新浏览量
+        postService.updatePostViews(postId);
+        
+        // 获取帖子信息
+        Post post = postService.selectPostById(postId);
+        if (post == null || !"1".equals(post.getStatus())) {
+            return error("帖子不存在或已下架");
+        }
+        result.put("post", post);
+        
+        // 获取帖子标签
+        List<PostTag> tags = postTagService.selectPostTagsByPostId(postId);
+        result.put("tags", tags);
+        
+        // 获取评论,只返回顶级评论,限制10条
+        List<PostComment> comments = postCommentService.selectCommentsByPostId(postId, 10);
+        
+        // 获取每个评论的回复
+        List<Map<String, Object>> commentList = new ArrayList<>();
+        for (PostComment comment : comments) {
+            if ("1".equals(comment.getStatus())) { // 只返回正常状态的评论
+                Map<String, Object> commentMap = new HashMap<>();
+                commentMap.put("comment", comment);
+                
+                List<PostComment> replies = postCommentService.selectCommentsByParentId(comment.getCommentId());
+                // 过滤回复,只返回正常状态的回复
+                List<PostComment> validReplies = new ArrayList<>();
+                for (PostComment reply : replies) {
+                    if ("1".equals(reply.getStatus())) {
+                        validReplies.add(reply);
+                    }
+                }
+                commentMap.put("replies", validReplies);
+                commentList.add(commentMap);
+            }
+        }
+        result.put("comments", commentList);
+        
+        // 获取推荐帖子(最多9个)
+        List<Post> recommendedPosts = postService.getRecommendedPosts(post.getAuthorId(), post.getTags(), postId, 9);
+        result.put("recommendedPosts", recommendedPosts);
+        
+        // 判断当前用户是否已收藏
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            // 用户未登录,忽略异常
+        }
+        
+        if (userId != null) {
+            PostFavorite favorite = postFavoriteService.selectPostFavoriteByPostIdAndUserId(postId, userId);
+            result.put("favorited", favorite != null && "0".equals(favorite.getStatus()));
+        } else {
+            result.put("favorited", false);
+        }
+        
+        return success(result);
+    }
+
+    /**
+     * 添加评论
+     */
+    @PostMapping("/comment")
+    public AjaxResult addComment(@RequestBody PostComment postComment)
+    {
+        if (postComment.getPostId() == null) {
+            return error("帖子ID不能为空");
+        }
+        
+        if (postComment.getContent() == null || postComment.getContent().trim().isEmpty()) {
+            return error("评论内容不能为空");
+        }
+        
+        // 检查用户是否登录
+        Long userId = null;
+        String username = null;
+        try {
+            userId = getUserId();
+            username = getUsername();
+        } catch (Exception e) {
+            return error("请先登录后再评论");
+        }
+        
+        if (userId == null) {
+            return error("请先登录后再评论");
+        }
+        
+        postComment.setCreateBy(username);
+        postComment.setUserId(userId);
+        postComment.setUserName(username);
+        postComment.setLikes(0L);
+        postComment.setStatus("1"); // 默认正常状态
+        
+        // 如果父评论ID为空,设置为0(顶级评论)
+        if (postComment.getParentId() == null) {
+            postComment.setParentId(0L);
+        }
+        
+        int result = postCommentService.insertPostComment(postComment);
+        
+        if (result > 0) {
+            // 更新帖子评论数
+            postService.updatePostComments(postComment.getPostId(), 1);
+            
+            // 返回新添加的评论,包括评论ID
+            return success(postCommentService.selectPostCommentById(postComment.getCommentId()));
+        } else {
+            return error("添加评论失败");
+        }
+    }
+    
+    /**
+     * 收藏/取消收藏帖子
+     */
+    @PostMapping("/favorite/{postId}")
+    public AjaxResult toggleFavorite(@PathVariable Long postId, @RequestParam(required = false, defaultValue = "true") boolean favorite)
+    {
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            // 用户未登录
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        // 检查帖子是否存在
+        Post post = postService.selectPostById(postId);
+        if (post == null || !"1".equals(post.getStatus())) {
+            return error("帖子不存在或已下架");
+        }
+        
+        // 检查是否已收藏
+        PostFavorite exist = postFavoriteService.selectPostFavoriteByPostIdAndUserId(postId, userId);
+        
+        Map<String, Object> resultData = new HashMap<>();
+        resultData.put("favorited", favorite);
+        
+        if (favorite) {
+            // 收藏操作
+            if (exist != null) {
+                // 已存在记录,检查状态
+                if ("0".equals(exist.getStatus())) {
+                    return AjaxResult.success("已收藏该帖子");
+                } else {
+                    // 取消收藏状态,改为收藏状态
+                    exist.setStatus("0");
+                    exist.setUpdateBy(getUsername());
+                    postFavoriteService.updatePostFavorite(exist);
+                    
+                    // 更新帖子收藏数量
+                    postService.updatePostFavorites(postId, 1);
+                    
+                    return AjaxResult.success("收藏成功");
+                }
+            } else {
+                // 创建收藏记录
+                PostFavorite favoriteRecord = new PostFavorite();
+                favoriteRecord.setPostId(postId);
+                favoriteRecord.setUserId(userId);
+                favoriteRecord.setPostTitle(post.getTitle());
+                favoriteRecord.setPostCover(post.getCoverImage());
+                favoriteRecord.setStatus("0");
+                favoriteRecord.setCreateBy(getUsername());
+                postFavoriteService.insertPostFavorite(favoriteRecord);
+                
+                // 更新帖子收藏数量
+                postService.updatePostFavorites(postId, 1);
+                
+                return AjaxResult.success("收藏成功");
+            }
+        } else {
+            // 取消收藏操作
+            if (exist != null && "0".equals(exist.getStatus())) {
+                // 更新为取消收藏状态
+                exist.setStatus("1");
+                exist.setUpdateBy(getUsername());
+                postFavoriteService.updatePostFavorite(exist);
+                
+                // 更新帖子收藏数量
+                postService.updatePostFavorites(postId, -1);
+                
+                return AjaxResult.success("取消收藏成功");
+            } else {
+                return AjaxResult.success("未收藏该帖子");
+            }
+        }
+    }
+    
+    /**
+     * 获取热门标签
+     */
+    @Anonymous
+    @GetMapping("/tags/hot")
+    public AjaxResult getHotTags(@RequestParam(required = false, defaultValue = "10") int limit)
+    {
+        // 获取帖子数量最多的标签
+        PostTag query = new PostTag();
+        query.setStatus("0"); // 正常状态
+        
+        // 使用startPage方法
+        startPage();
+        
+        // 手动限制结果数量
+        List<PostTag> allTags = postTagService.selectPostTagList(query);
+        List<PostTag> sortedTags = allTags.stream()
+                .sorted((a, b) -> b.getPostCount().compareTo(a.getPostCount()))
+                .limit(limit)
+                .collect(java.util.stream.Collectors.toList());
+        
+        return AjaxResult.success(sortedTags);
+    }
+    
+    /**
+     * 根据标签获取帖子
+     */
+    @Anonymous
+    @GetMapping("/bytag/{tagId}")
+    public TableDataInfo getPostsByTag(@PathVariable Long tagId)
+    {
+        startPage();
+        // 查询该标签的所有帖子
+        // 假设已经实现了这个方法
+        // 实际情况下,可能需要通过关联表查询
+        PostTag tag = postTagService.selectPostTagById(tagId);
+        
+        if (tag == null) {
+            return getDataTable(new ArrayList<>());
+        }
+        
+        Post query = new Post();
+        query.setTags(tag.getTagName());
+        query.setStatus("1"); // 只查询正常状态的帖子
+        List<Post> posts = postService.selectPostList(query);
+        
+        return getDataTable(posts);
+    }
+
+    /**
+     * 发布新帖子
+     */
+    @PostMapping("/publish")
+    public AjaxResult publishPost(@RequestBody Post post)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        String username = null;
+        try {
+            userId = getUserId();
+            username = getUsername();
+        } catch (Exception e) {
+            return error("请先登录后再发布帖子");
+        }
+        
+        if (userId == null) {
+            return error("请先登录后再发布帖子");
+        }
+        
+        // 验证必填字段
+        if (post.getTitle() == null || post.getTitle().trim().isEmpty()) {
+            return error("帖子标题不能为空");
+        }
+        
+        if (post.getContent() == null || post.getContent().trim().isEmpty()) {
+            return error("帖子内容不能为空");
+        }
+        
+        if (post.getSummary() == null || post.getSummary().trim().isEmpty()) {
+            return error("帖子摘要不能为空");
+        }
+        
+        // 设置帖子基本信息
+        post.setAuthorId(userId);
+        post.setAuthor(username);
+        post.setViews(0L);
+        post.setComments(0L);
+        post.setFavorites(0L);
+        post.setLikes(0L);
+        post.setStatus("0"); // 默认待审核状态
+        post.setCreateBy(username);
+        
+        // 自动设置发布时间
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        post.setPublishTime(sdf.format(new Date()));
+        
+        // 如果没有设置封面图,使用默认图片
+        if (post.getCoverImage() == null || post.getCoverImage().trim().isEmpty()) {
+            post.setCoverImage("/images/default-cover.jpg");
+        }
+        
+        // 处理标签
+        if (post.getTags() != null && !post.getTags().trim().isEmpty()) {
+            // 这里可以处理标签逻辑,比如创建标签关联关系
+            // 暂时直接保存标签字符串
+        }
+        
+        int result = postService.insertPost(post);
+        
+        if (result > 0) {
+            return success("帖子发布成功,等待管理员审核");
+        } else {
+            return error("帖子发布失败");
+        }
+    }
+    
+    /**
+     * 获取当前用户的帖子列表
+     */
+    @GetMapping("/my-posts")
+    public TableDataInfo getMyPosts(Post post,
+                                   @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+                                   @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            return getDataTable(new ArrayList<>());
+        }
+        
+        if (userId == null) {
+            return getDataTable(new ArrayList<>());
+        }
+        
+        // 手动设置分页参数
+        PageHelper.startPage(pageNum, pageSize);
+        
+        // 只查询当前用户的帖子
+        post.setAuthorId(userId);
+        List<Post> list = postService.selectPostList(post);
+        return getDataTable(list);
+    }
+    
+    /**
+     * 获取当前用户的收藏列表
+     */
+    @GetMapping("/my-favorites")
+    public TableDataInfo getMyFavorites(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+                                       @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            return getDataTable(new ArrayList<>());
+        }
+        
+        if (userId == null) {
+            return getDataTable(new ArrayList<>());
+        }
+        
+        // 手动设置分页参数
+        PageHelper.startPage(pageNum, pageSize);
+        
+        // 查询用户收藏的帖子
+        PostFavorite favoriteQuery = new PostFavorite();
+        favoriteQuery.setUserId(userId);
+        favoriteQuery.setStatus("0"); // 只查询有效收藏
+        List<PostFavorite> favorites = postFavoriteService.selectPostFavoriteList(favoriteQuery);
+        
+        // 获取收藏的帖子详情
+        List<Post> favoritePosts = new ArrayList<>();
+        for (PostFavorite favorite : favorites) {
+            Post post = postService.selectPostById(favorite.getPostId());
+            if (post != null && "1".equals(post.getStatus())) {
+                favoritePosts.add(post);
+            }
+        }
+        
+        return getDataTable(favoritePosts);
+    }
+    
+    /**
+     * 更新帖子
+     */
+    @PutMapping("/update")
+    public AjaxResult updatePost(@RequestBody Post post)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        String username = null;
+        try {
+            userId = getUserId();
+            username = getUsername();
+        } catch (Exception e) {
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        if (post.getPostId() == null) {
+            return error("帖子ID不能为空");
+        }
+        
+        // 检查帖子是否存在且属于当前用户
+        Post existingPost = postService.selectPostById(post.getPostId());
+        if (existingPost == null) {
+            return error("帖子不存在");
+        }
+        
+        if (!userId.equals(existingPost.getAuthorId())) {
+            return error("只能编辑自己的帖子");
+        }
+        
+        // 验证必填字段
+        if (post.getTitle() == null || post.getTitle().trim().isEmpty()) {
+            return error("帖子标题不能为空");
+        }
+        
+        if (post.getContent() == null || post.getContent().trim().isEmpty()) {
+            return error("帖子内容不能为空");
+        }
+        
+        if (post.getSummary() == null || post.getSummary().trim().isEmpty()) {
+            return error("帖子摘要不能为空");
+        }
+        
+        // 设置更新信息
+        post.setUpdateBy(username);
+        
+        int result = postService.updatePost(post);
+        
+        if (result > 0) {
+            return success("帖子更新成功");
+        } else {
+            return error("帖子更新失败");
+        }
+    }
+    
+    /**
+     * 删除帖子
+     */
+    @DeleteMapping("/delete/{postId}")
+    public AjaxResult deletePost(@PathVariable Long postId)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        // 检查帖子是否存在且属于当前用户
+        Post existingPost = postService.selectPostById(postId);
+        if (existingPost == null) {
+            return error("帖子不存在");
+        }
+        
+        if (!userId.equals(existingPost.getAuthorId())) {
+            return error("只能删除自己的帖子");
+        }
+        
+        try {
+            // 级联删除相关数据
+            // 1. 删除帖子评论
+            postCommentService.deletePostCommentByPostId(postId);
+            
+            // 2. 删除帖子收藏记录
+            postFavoriteService.deletePostFavoriteByPostId(postId);
+            
+            // 3. 删除帖子本身
+        int result = postService.deletePostById(postId);
+        
+        if (result > 0) {
+            return success("帖子删除成功");
+        } else {
+            return error("帖子删除失败");
+            }
+        } catch (Exception e) {
+            return error("删除失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 获取可用标签列表(用于下拉选择)
+     */
+    @Anonymous
+    @GetMapping("/tags/available")
+    public AjaxResult getAvailableTags()
+    {
+        PostTag query = new PostTag();
+        query.setStatus("0"); // 正常状态
+        List<PostTag> tags = postTagService.selectPostTagList(query);
+        return success(tags);
+    }
+    
+    /**
+     * 点赞/取消点赞帖子
+     */
+    @PostMapping("/like/{postId}")
+    public AjaxResult toggleLike(@PathVariable Long postId, @RequestParam(required = false, defaultValue = "true") boolean like)
+    {
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        // 检查帖子是否存在
+        Post post = postService.selectPostById(postId);
+        if (post == null || !"1".equals(post.getStatus())) {
+            return error("帖子不存在或已下架");
+        }
+        
+        // 这里简化处理,直接更新点赞数
+        // 实际应用中应该记录用户点赞状态,防止重复点赞
+        if (like) {
+            postService.updatePostLikes(postId, 1);
+            return success("点赞成功");
+        } else {
+            postService.updatePostLikes(postId, -1);
+            return success("取消点赞成功");
+        }
+    }
+    
+    /**
+     * 点赞/取消点赞评论
+     */
+    @PostMapping("/comment/like/{commentId}")
+    public AjaxResult toggleCommentLike(@PathVariable Long commentId, @RequestParam(required = false, defaultValue = "true") boolean like)
+    {
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        // 检查评论是否存在
+        PostComment comment = postCommentService.selectPostCommentById(commentId);
+        if (comment == null || !"1".equals(comment.getStatus())) {
+            return error("评论不存在或已删除");
+        }
+        
+        // 这里简化处理,直接更新点赞数
+        // 实际应用中应该记录用户点赞状态,防止重复点赞
+        if (like) {
+            postCommentService.updateCommentLikes(commentId, 1);
+            return success("点赞成功");
+        } else {
+            postCommentService.updateCommentLikes(commentId, -1);
+            return success("取消点赞成功");
+        }
+    }
+    
+    /**
+     * 上传图片
+     */
+    @PostMapping("/upload")
+    public AjaxResult uploadImage(@RequestParam("file") MultipartFile file)
+    {
+        try {
+            if (file.isEmpty()) {
+                return error("上传文件不能为空");
+            }
+            
+            // 检查文件类型
+            String originalFilename = file.getOriginalFilename();
+            if (originalFilename == null) {
+                return error("文件名不能为空");
+            }
+            
+            String extension = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
+            if (!extension.matches("(?i)\\.(jpg|jpeg|png|gif|bmp|webp)$")) {
+                return error("只支持jpg、jpeg、png、gif、bmp、webp格式的图片文件");
+            }
+            
+            // 检查文件大小(限制为5MB)
+            if (file.getSize() > 5 * 1024 * 1024) {
+                return error("文件大小不能超过5MB");
+            }
+            
+            // 生成唯一文件名
+            String fileName = System.currentTimeMillis() + "_" + 
+                             (int)(Math.random() * 1000) + extension;
+            
+            // 使用配置文件中的相对路径
+            String frontendImagePath = com.ruoyi.common.config.RuoYiConfig.getFrontendImagePath();
+            if (frontendImagePath == null) {
+                return error("前端图片路径未配置");
+            }
+            
+            // 获取项目根目录的绝对路径
+            String projectRoot = System.getProperty("user.dir");
+            String uploadDir = Paths.get(projectRoot, frontendImagePath).toString();
+            
+            File uploadDirFile = new File(uploadDir);
+            if (!uploadDirFile.exists()) {
+                uploadDirFile.mkdirs();
+            }
+            
+            // 保存文件
+            Path filePath = Paths.get(uploadDir, fileName);
+            Files.copy(file.getInputStream(), filePath);
+            
+            // 返回访问URL - 前端可以直接通过/images/访问
+            String imageUrl = "/images/" + fileName;
+            
+            Map<String, String> result = new HashMap<>();
+            result.put("url", imageUrl);
+            result.put("filename", fileName);
+            result.put("originalName", originalFilename);
+            result.put("size", String.valueOf(file.getSize()));
+            
+            return success(result);
+        } catch (IOException e) {
+            return error("上传失败:" + e.getMessage());
+        } catch (Exception e) {
+            return error("上传失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 删除图片
+     */
+    @DeleteMapping("/upload")
+    public AjaxResult deleteImage(@RequestParam("filename") String filename)
+    {
+        try {
+            if (filename == null || filename.trim().isEmpty()) {
+                return error("文件名不能为空");
+            }
+            
+            // 使用配置文件中的相对路径
+            String frontendImagePath = com.ruoyi.common.config.RuoYiConfig.getFrontendImagePath();
+            if (frontendImagePath == null) {
+                return error("前端图片路径未配置");
+            }
+            
+            // 获取项目根目录的绝对路径
+            String projectRoot = System.getProperty("user.dir");
+            String uploadDir = Paths.get(projectRoot, frontendImagePath).toString();
+            Path filePath = Paths.get(uploadDir, filename);
+            
+            // 检查文件是否存在
+            if (Files.exists(filePath)) {
+                Files.delete(filePath);
+                return success("图片删除成功");
+            } else {
+                return error("文件不存在");
+            }
+        } catch (IOException e) {
+            return error("删除失败:" + e.getMessage());
+        } catch (Exception e) {
+            return error("删除失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取推广计划列表
+     */
+    @Anonymous
+    @GetMapping("/promotion-plans")
+    public AjaxResult getPromotionPlans()
+    {
+        // 这里应该从数据库查询推广计划,暂时返回硬编码数据
+        List<Map<String, Object>> plans = new ArrayList<>();
+        
+        Map<String, Object> plan1 = new HashMap<>();
+        plan1.put("id", 1);
+        plan1.put("name", "首页推荐");
+        plan1.put("description", "帖子显示在首页推荐位置");
+        plan1.put("price", 50.00);
+        plan1.put("duration", 7);
+        plans.add(plan1);
+        
+        Map<String, Object> plan2 = new HashMap<>();
+        plan2.put("id", 2);
+        plan2.put("name", "置顶推广");
+        plan2.put("description", "帖子在分类页面置顶显示");
+        plan2.put("price", 30.00);
+        plan2.put("duration", 3);
+        plans.add(plan2);
+        
+        Map<String, Object> plan3 = new HashMap<>();
+        plan3.put("id", 3);
+        plan3.put("name", "热门推荐");
+        plan3.put("description", "帖子显示在热门推荐区域");
+        plan3.put("price", 80.00);
+        plan3.put("duration", 14);
+        plans.add(plan3);
+        
+        Map<String, Object> plan4 = new HashMap<>();
+        plan4.put("id", 4);
+        plan4.put("name", "限时闪推");
+        plan4.put("description", "特定时间段内高优显示");
+        plan4.put("price", 20.00);
+        plan4.put("duration", 1);
+        plans.add(plan4);
+        
+        Map<String, Object> plan5 = new HashMap<>();
+        plan5.put("id", 5);
+        plan5.put("name", "分类页首条");
+        plan5.put("description", "在所属分类列表第一条显示(非置顶样式)");
+        plan5.put("price", 40.00);
+        plan5.put("duration", 5);
+        plans.add(plan5);
+        
+        return success(plans);
+    }
+
+    /**
+     * 创建支付记录
+     */
+    @PostMapping("/payment")
+    public AjaxResult createPayment(@RequestBody PaymentRequest request)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        // 验证参数
+        if (request.getPostId() == null || request.getPlanId() == null || request.getAmount() == null) {
+            return error("参数不完整");
+        }
+        
+        // 检查帖子是否存在且属于当前用户
+        Post post = postService.selectPostById(request.getPostId());
+        if (post == null) {
+            return error("帖子不存在");
+        }
+        
+        if (!userId.equals(post.getAuthorId())) {
+            return error("只能为自己的帖子购买推广");
+        }
+        
+        // 创建支付记录
+        PostPayment payment = new PostPayment();
+        payment.setPostId(request.getPostId());
+        payment.setPlanId(request.getPlanId());
+        payment.setUserId(userId);
+        payment.setAmount(BigDecimal.valueOf(request.getAmount()));
+        payment.setPaymentStatus("pending"); // 待支付状态
+        payment.setPaymentTime(new Date());
+        
+        int result = postPaymentMapper.insertPostPayment(payment);
+        
+        if (result > 0) {
+            return success(payment);
+        } else {
+            return error("创建支付记录失败");
+        }
+    }
+
+    /**
+     * 检查帖子推广状态
+     */
+    @GetMapping("/promotion-status/{postId}")
+    public AjaxResult getPromotionStatus(@PathVariable Long postId)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        // 检查帖子是否存在且属于当前用户
+        Post post = postService.selectPostById(postId);
+        if (post == null) {
+            return error("帖子不存在");
+        }
+        
+        if (!userId.equals(post.getAuthorId())) {
+            return error("只能查看自己的帖子推广状态");
+        }
+        
+        Map<String, Object> result = new HashMap<>();
+        result.put("hasPromotion", post.getPromotionPlanId() != null);
+        result.put("promotionPlanId", post.getPromotionPlanId());
+        
+        // 查询最新的支付记录
+        if (post.getPromotionPlanId() != null) {
+            PostPayment latestPayment = postPaymentMapper.selectLatestPaymentByPostId(postId);
+            result.put("latestPayment", latestPayment);
+        }
+        
+        return success(result);
+    }
+
+    /**
+     * 确认支付成功
+     */
+    @PostMapping("/payment/confirm/{paymentId}")
+    public AjaxResult confirmPayment(@PathVariable Long paymentId)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        // 查询支付记录
+        PostPayment payment = postPaymentMapper.selectPostPaymentById(paymentId);
+        if (payment == null) {
+            return error("支付记录不存在");
+        }
+        
+        if (!userId.equals(payment.getUserId())) {
+            return error("只能确认自己的支付记录");
+        }
+        
+        if (!"pending".equals(payment.getPaymentStatus())) {
+            return error("支付记录状态异常");
+        }
+        
+        // 更新支付状态为成功
+        payment.setPaymentStatus("paid");
+        payment.setPaymentTime(new Date());
+        postPaymentMapper.updatePostPayment(payment);
+        
+        // 更新帖子的推广计划ID
+        Post updatePost = new Post();
+        updatePost.setPostId(payment.getPostId());
+        updatePost.setPromotionPlanId(payment.getPlanId());
+        updatePost.setUpdateBy(getUsername());
+        
+        int result = postService.updatePost(updatePost);
+        
+        if (result > 0) {
+            return success("支付成功,推广已生效");
+        } else {
+            return error("支付确认失败");
+        }
+    }
+
+    /**
+     * 取消支付
+     */
+    @PostMapping("/payment/cancel/{paymentId}")
+    public AjaxResult cancelPayment(@PathVariable Long paymentId)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        try {
+            userId = getUserId();
+        } catch (Exception e) {
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        // 查询支付记录
+        PostPayment payment = postPaymentMapper.selectPostPaymentById(paymentId);
+        if (payment == null) {
+            return error("支付记录不存在");
+        }
+        
+        if (!userId.equals(payment.getUserId())) {
+            return error("只能取消自己的支付记录");
+        }
+        
+        if (!"pending".equals(payment.getPaymentStatus())) {
+            return error("支付记录状态异常");
+        }
+        
+        // 更新支付状态为失败
+        payment.setPaymentStatus("failed");
+        postPaymentMapper.updatePostPayment(payment);
+        
+        return success("支付已取消");
+    }
+
+    /**
+     * 支付请求类
+     */
+    public static class PaymentRequest {
+        private Long postId;
+        private Long planId;
+        private Double amount;
+
+        public Long getPostId() {
+            return postId;
+        }
+
+        public void setPostId(Long postId) {
+            this.postId = postId;
+        }
+
+        public Long getPlanId() {
+            return planId;
+        }
+
+        public void setPlanId(Long planId) {
+            this.planId = planId;
+        }
+
+        public Double getAmount() {
+            return amount;
+        }
+
+        public void setAmount(Double amount) {
+            this.amount = amount;
+        }
+    }
+
+    /**
+     * 举报帖子
+     */
+    @PostMapping("/report/{postId}")
+    public AjaxResult reportPost(@PathVariable Long postId, @RequestBody ReportRequest request)
+    {
+        // 检查用户是否登录
+        Long userId = null;
+        String username = null;
+        try {
+            userId = getUserId();
+            username = getUsername();
+        } catch (Exception e) {
+            return error("请先登录");
+        }
+        
+        if (userId == null) {
+            return error("请先登录");
+        }
+        
+        // 检查帖子是否存在
+        Post post = postService.selectPostById(postId);
+        if (post == null || !"1".equals(post.getStatus())) {
+            return error("帖子不存在或已下架");
+        }
+        
+        // 检查举报理由
+        if (request.getReason() == null || request.getReason().trim().isEmpty()) {
+            return error("举报理由不能为空");
+        }
+        
+        // 创建举报记录
+        PostReport report = new PostReport();
+        report.setPostId(postId);
+        report.setPostTitle(post.getTitle());
+        report.setReportUserId(userId);
+        report.setReportUserName(username);
+        report.setReportReason(request.getReason());
+        report.setStatus("0"); // 待处理状态
+        report.setCreateBy(username);
+        
+        int result = postReportMapper.insertPostReport(report);
+        
+        if (result > 0) {
+            return success("举报提交成功,我们会尽快处理");
+        } else {
+            return error("举报提交失败");
+        }
+    }
+
+    /**
+     * 举报请求类
+     */
+    public static class ReportRequest {
+        private String reason;
+
+        public String getReason() {
+            return reason;
+        }
+
+        public void setReason(String reason) {
+            this.reason = reason;
+        }
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCommentController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCommentController.java
new file mode 100644
index 0000000..a3859fa
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCommentController.java
@@ -0,0 +1,167 @@
+package com.ruoyi.web.controller.post.controller;
+
+import java.util.List;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.web.controller.post.domain.PostComment;
+import com.ruoyi.web.controller.post.service.IPostCommentService;
+import com.ruoyi.web.controller.post.service.IPostService;
+
+/**
+ * 帖子评论信息操作处理
+ * 
+ * @author thunderhub
+ */
+@RestController
+@RequestMapping("/post/comment")
+public class PostCommentController extends BaseController
+{
+    @Autowired
+    private IPostCommentService postCommentService;
+    
+    @Autowired
+    private IPostService postService;
+
+    /**
+     * 获取帖子评论列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:comment:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(PostComment postComment)
+    {
+        startPage();
+        List<PostComment> list = postCommentService.selectPostCommentList(postComment);
+        return getDataTable(list);
+    }
+    
+    /**
+     * 导出帖子评论列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:comment:export')")
+    @Log(title = "帖子评论", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, PostComment postComment)
+    {
+        List<PostComment> list = postCommentService.selectPostCommentList(postComment);
+        ExcelUtil<PostComment> util = new ExcelUtil<PostComment>(PostComment.class);
+        util.exportExcel(response, list, "评论数据");
+    }
+
+    /**
+     * 获取帖子评论详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('post:comment:query')")
+    @GetMapping(value = "/{commentId}")
+    public AjaxResult getInfo(@PathVariable Long commentId)
+    {
+        return success(postCommentService.selectPostCommentById(commentId));
+    }
+
+    /**
+     * 新增帖子评论
+     */
+    @PreAuthorize("@ss.hasPermi('post:comment:add')")
+    @Log(title = "帖子评论", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody PostComment postComment)
+    {
+        postComment.setCreateBy(getUsername());
+        postComment.setUserId(getUserId());
+        postComment.setUserName(getUsername());
+        postComment.setLikes(0L);
+        
+        // 如果父评论ID为空,设置为0(顶级评论)
+        if (postComment.getParentId() == null) {
+            postComment.setParentId(0L);
+        }
+        
+        int result = postCommentService.insertPostComment(postComment);
+        
+        if (result > 0) {
+            // 更新帖子评论数
+            postService.updatePostComments(postComment.getPostId(), 1);
+        }
+        
+        return toAjax(result);
+    }
+
+    /**
+     * 修改帖子评论
+     */
+    @PreAuthorize("@ss.hasPermi('post:comment:edit')")
+    @Log(title = "帖子评论", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody PostComment postComment)
+    {
+        postComment.setUpdateBy(getUsername());
+        return toAjax(postCommentService.updatePostComment(postComment));
+    }
+
+    /**
+     * 删除帖子评论
+     */
+    @PreAuthorize("@ss.hasPermi('post:comment:remove')")
+    @Log(title = "帖子评论", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{commentIds}")
+    public AjaxResult remove(@PathVariable Long[] commentIds)
+    {
+        // 查询评论信息,用于后续更新帖子评论数
+        for (Long commentId : commentIds) {
+            PostComment comment = postCommentService.selectPostCommentById(commentId);
+            if (comment != null) {
+                // 计算删除的评论和回复总数
+                int count = 1; // 至少有一条评论
+                
+                // 如果是顶级评论,还需要计算回复数
+                if (comment.getParentId() == 0) {
+                    List<PostComment> replies = postCommentService.selectCommentsByParentId(commentId);
+                    count += replies.size();
+                }
+                
+                // 更新帖子评论数
+                postService.updatePostComments(comment.getPostId(), -count);
+            }
+        }
+        
+        return toAjax(postCommentService.deletePostCommentByIds(commentIds));
+    }
+    
+    /**
+     * 获取帖子的评论列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:comment:list')")
+    @GetMapping("/list/{postId}")
+    public AjaxResult listByPostId(@PathVariable Long postId)
+    {
+        List<PostComment> comments = postCommentService.selectCommentsByPostId(postId, 0);
+        return success(comments);
+    }
+    
+    /**
+     * 点赞评论
+     */
+    @PreAuthorize("@ss.hasPermi('post:comment:like')")
+    @Log(title = "评论点赞", businessType = BusinessType.UPDATE)
+    @PostMapping("/like/{commentId}")
+    public AjaxResult like(@PathVariable Long commentId)
+    {
+        int result = postCommentService.updateCommentLikes(commentId, 1);
+        return toAjax(result);
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostController.java
new file mode 100644
index 0000000..e885966
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostController.java
@@ -0,0 +1,590 @@
+package com.ruoyi.web.controller.post.controller;
+
+import java.util.ArrayList;
+import java.util.List;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.web.controller.post.domain.Post;
+import com.ruoyi.web.controller.post.domain.PostComment;
+import com.ruoyi.web.controller.post.domain.PostFavorite;
+import com.ruoyi.web.controller.post.domain.PostTag;
+import com.ruoyi.web.controller.post.domain.PostReport;
+import com.ruoyi.web.controller.post.service.IPostCommentService;
+import com.ruoyi.web.controller.post.service.IPostFavoriteService;
+import com.ruoyi.web.controller.post.service.IPostService;
+import com.ruoyi.web.controller.post.service.IPostTagService;
+import com.ruoyi.web.controller.post.mapper.PostReportMapper;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Date;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 帖子信息操作处理
+ * 
+ * @author thunderhub
+ */
+@RestController
+@RequestMapping("/post")
+public class PostController extends BaseController
+{
+    @Autowired
+    private IPostService postService;
+    
+    @Autowired
+    private IPostTagService postTagService;
+    
+    @Autowired
+    private IPostCommentService postCommentService;
+    
+    @Autowired
+    private IPostFavoriteService postFavoriteService;
+    
+    @Autowired
+    private PostReportMapper postReportMapper;
+
+    /**
+     * 获取帖子列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(Post post)
+    {
+        startPage();
+        List<Post> list = postService.selectPostList(post);
+        return getDataTable(list);
+    }
+    
+    /**
+     * 导出帖子列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:export')")
+    @Log(title = "帖子管理", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, Post post)
+    {
+        List<Post> list = postService.selectPostList(post);
+        ExcelUtil<Post> util = new ExcelUtil<Post>(Post.class);
+        util.exportExcel(response, list, "帖子数据");
+    }
+
+    /**
+     * 获取帖子详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:query')")
+    @GetMapping(value = "/{postId}")
+    public AjaxResult getInfo(@PathVariable Long postId)
+    {
+        Map<String, Object> result = new HashMap<>();
+        
+        // 更新浏览量
+        postService.updatePostViews(postId);
+        
+        // 获取帖子信息
+        Post post = postService.selectPostById(postId);
+        if (post == null) {
+            return error("帖子不存在");
+        }
+        result.put("post", post);
+        
+        // 获取帖子标签
+        List<PostTag> tags = postTagService.selectPostTagsByPostId(postId);
+        result.put("tags", tags);
+        
+        // 获取评论,只返回顶级评论,不包含回复
+        PostComment commentQuery = new PostComment();
+        commentQuery.setPostId(postId);
+        commentQuery.setParentId(0L);
+        List<PostComment> comments = postCommentService.selectPostCommentList(commentQuery);
+        
+        // 获取每个评论的回复
+        List<Map<String, Object>> commentList = new ArrayList<>();
+        for (PostComment comment : comments) {
+            Map<String, Object> commentMap = new HashMap<>();
+            commentMap.put("comment", comment);
+            List<PostComment> replies = postCommentService.selectCommentsByParentId(comment.getCommentId());
+            commentMap.put("replies", replies);
+            commentList.add(commentMap);
+        }
+        result.put("comments", commentList);
+        
+        // 获取相关帖子
+        // 1. 同一作者的其他帖子
+        List<Post> authorPosts = postService.selectAuthorOtherPosts(post.getAuthorId(), postId, 3);
+        result.put("authorPosts", authorPosts);
+        
+        // 2. 相似标签的帖子
+        List<Post> similarPosts = postService.selectSimilarTagsPosts(post.getTags(), postId, 3);
+        result.put("similarPosts", similarPosts);
+        
+        // 判断当前用户是否已收藏
+        Long userId = getUserId();
+        if (userId != null) {
+            PostFavorite favorite = postFavoriteService.selectPostFavoriteByPostIdAndUserId(postId, userId);
+            result.put("favorited", favorite != null && "0".equals(favorite.getStatus()));
+        } else {
+            result.put("favorited", false);
+        }
+        
+        return success(result);
+    }
+
+    /**
+     * 新增帖子
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:add')")
+    @Log(title = "帖子管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody Post post)
+    {
+        post.setCreateBy(getUsername());
+        post.setAuthorId(getUserId());
+        post.setAuthor(getUsername());
+        post.setViews(0L);
+        post.setComments(0L);
+        post.setFavorites(0L);
+        
+        // 处理标签,如果有传入标签数组
+        String tags = post.getTags();
+        int result = postService.insertPost(post);
+        
+        if (result > 0 && StringUtils.isNotEmpty(tags)) {
+            // 处理标签关联
+            String[] tagArray = tags.split(",");
+            for (String tagName : tagArray) {
+                // 查询标签是否存在
+                PostTag tagQuery = new PostTag();
+                tagQuery.setTagName(tagName);
+                List<PostTag> existTags = postTagService.selectPostTagList(tagQuery);
+                
+                Long tagId;
+                if (existTags.isEmpty()) {
+                    // 创建新标签
+                    PostTag newTag = new PostTag();
+                    newTag.setTagName(tagName);
+                    newTag.setTagColor("blue"); // 默认颜色
+                    newTag.setPostCount(1L);
+                    newTag.setStatus("0"); // 正常状态
+                    newTag.setCreateBy(getUsername());
+                    postTagService.insertPostTag(newTag);
+                    tagId = newTag.getTagId();
+                } else {
+                    // 更新标签帖子数量
+                    PostTag existTag = existTags.get(0);
+                    tagId = existTag.getTagId();
+                    postTagService.updatePostTagCount(tagId, 1);
+                }
+                
+                // 创建帖子标签关联
+                Long[] tagIds = new Long[]{tagId};
+                postTagService.batchInsertPostTagRelation(post.getPostId(), tagIds);
+            }
+        }
+        
+        return toAjax(result);
+    }
+
+    /**
+     * 修改帖子
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:edit')")
+    @Log(title = "帖子管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody Post post)
+    {
+        post.setUpdateBy(getUsername());
+        
+        // 处理标签
+        String tags = post.getTags();
+        
+        // 先删除旧的标签关联
+        postTagService.deletePostTagRelation(post.getPostId());
+        
+        int result = postService.updatePost(post);
+        
+        if (result > 0 && StringUtils.isNotEmpty(tags)) {
+            // 处理标签关联
+            String[] tagArray = tags.split(",");
+            for (String tagName : tagArray) {
+                // 查询标签是否存在
+                PostTag tagQuery = new PostTag();
+                tagQuery.setTagName(tagName);
+                List<PostTag> existTags = postTagService.selectPostTagList(tagQuery);
+                
+                Long tagId;
+                if (existTags.isEmpty()) {
+                    // 创建新标签
+                    PostTag newTag = new PostTag();
+                    newTag.setTagName(tagName);
+                    newTag.setTagColor("blue"); // 默认颜色
+                    newTag.setPostCount(1L);
+                    newTag.setStatus("0"); // 正常状态
+                    newTag.setCreateBy(getUsername());
+                    postTagService.insertPostTag(newTag);
+                    tagId = newTag.getTagId();
+                } else {
+                    // 更新标签帖子数量
+                    PostTag existTag = existTags.get(0);
+                    tagId = existTag.getTagId();
+                    postTagService.updatePostTagCount(tagId, 1);
+                }
+                
+                // 创建帖子标签关联
+                Long[] tagIds = new Long[]{tagId};
+                postTagService.batchInsertPostTagRelation(post.getPostId(), tagIds);
+            }
+        }
+        
+        return toAjax(result);
+    }
+
+    /**
+     * 删除帖子
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:remove')")
+    @Log(title = "帖子管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{postIds}")
+    public AjaxResult remove(@PathVariable Long[] postIds)
+    {
+        // 删除帖子相关的评论和收藏
+        for (Long postId : postIds) {
+            postCommentService.deletePostCommentByPostId(postId);
+            postFavoriteService.deletePostFavoriteByPostId(postId);
+            // 删除帖子标签关联
+            postTagService.deletePostTagRelation(postId);
+        }
+        
+        return toAjax(postService.deletePostByIds(postIds));
+    }
+    
+    /**
+     * 收藏帖子
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:favorite')")
+    @Log(title = "帖子收藏", businessType = BusinessType.INSERT)
+    @PostMapping("/favorite/{postId}")
+    public AjaxResult favorite(@PathVariable Long postId)
+    {
+        Long userId = getUserId();
+        
+        // 检查是否已收藏
+        PostFavorite exist = postFavoriteService.selectPostFavoriteByPostIdAndUserId(postId, userId);
+        
+        if (exist != null) {
+            // 已存在记录,检查状态
+            if ("0".equals(exist.getStatus())) {
+                return error("已收藏该帖子");
+            } else {
+                // 取消收藏状态,改为收藏状态
+                exist.setStatus("0");
+                exist.setUpdateBy(getUsername());
+                postFavoriteService.updatePostFavorite(exist);
+                
+                // 更新帖子收藏数量
+                postService.updatePostFavorites(postId, 1);
+                
+                return success("收藏成功");
+            }
+        } else {
+            // 创建收藏记录
+            Post post = postService.selectPostById(postId);
+            if (post == null) {
+                return error("帖子不存在");
+            }
+            
+            PostFavorite favorite = new PostFavorite();
+            favorite.setPostId(postId);
+            favorite.setUserId(userId);
+            favorite.setPostTitle(post.getTitle());
+            favorite.setPostCover(post.getCoverImage());
+            favorite.setStatus("0");
+            favorite.setCreateBy(getUsername());
+            postFavoriteService.insertPostFavorite(favorite);
+            
+            // 更新帖子收藏数量
+            postService.updatePostFavorites(postId, 1);
+            
+            return success("收藏成功");
+        }
+    }
+    
+    /**
+     * 取消收藏帖子
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:unfavorite')")
+    @Log(title = "取消收藏", businessType = BusinessType.UPDATE)
+    @PostMapping("/unfavorite/{postId}")
+    public AjaxResult unfavorite(@PathVariable Long postId)
+    {
+        Long userId = getUserId();
+        
+        // 检查是否已收藏
+        PostFavorite exist = postFavoriteService.selectPostFavoriteByPostIdAndUserId(postId, userId);
+        
+        if (exist != null && "0".equals(exist.getStatus())) {
+            // 更新为取消收藏状态
+            exist.setStatus("1");
+            exist.setUpdateBy(getUsername());
+            postFavoriteService.updatePostFavorite(exist);
+            
+            // 更新帖子收藏数量
+            postService.updatePostFavorites(postId, -1);
+            
+            return success("取消收藏成功");
+        } else {
+            return error("未收藏该帖子");
+        }
+    }
+    
+    /**
+     * 获取用户收藏的帖子列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:list')")
+    @GetMapping("/favorite/list")
+    public TableDataInfo favoriteList()
+    {
+        startPage();
+        PostFavorite query = new PostFavorite();
+        query.setUserId(getUserId());
+        query.setStatus("0"); // 只查询收藏状态的记录
+        List<PostFavorite> list = postFavoriteService.selectPostFavoriteList(query);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询待审核帖子列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:review')")
+    @GetMapping("/review/list")
+    public TableDataInfo reviewList(Post post)
+    {
+        startPage();
+        // 只查询待审核状态的帖子
+        post.setStatus("0");
+        List<Post> list = postService.selectPostList(post);
+        return getDataTable(list);
+    }
+
+    /**
+     * 审核帖子(通过/拒绝)
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:review')")
+    @Log(title = "帖子审核", businessType = BusinessType.UPDATE)
+    @PutMapping("/review/{postId}")
+    public AjaxResult reviewPost(@PathVariable Long postId, @RequestBody ReviewRequest request)
+    {
+        Post post = postService.selectPostById(postId);
+        if (post == null) {
+            return error("帖子不存在");
+        }
+        
+        Post updatePost = new Post();
+        updatePost.setPostId(postId);
+        updatePost.setUpdateBy(getUsername());
+        
+        if ("approve".equals(request.getAction())) {
+            updatePost.setStatus("1"); // 审核通过,状态改为已发布
+            updatePost.setRemark(request.getReason());
+            
+            int result = postService.updatePost(updatePost);
+            if (result > 0) {
+                return success("帖子审核通过");
+            } else {
+                return error("审核操作失败");
+            }
+        } else if ("reject".equals(request.getAction())) {
+            updatePost.setStatus("2"); // 审核拒绝
+            updatePost.setRemark(request.getReason());
+            
+            int result = postService.updatePost(updatePost);
+            if (result > 0) {
+                return success("帖子审核拒绝");
+            } else {
+                return error("审核操作失败");
+            }
+        } else {
+            return error("无效的审核操作");
+        }
+    }
+
+    /**
+     * 强制下架帖子
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:takedown')")
+    @Log(title = "帖子下架", businessType = BusinessType.UPDATE)
+    @PutMapping("/takedown/{postId}")
+    public AjaxResult takeDownPost(@PathVariable Long postId, @RequestBody TakeDownRequest request)
+    {
+        Post post = postService.selectPostById(postId);
+        if (post == null) {
+            return error("帖子不存在");
+        }
+        
+        Post updatePost = new Post();
+        updatePost.setPostId(postId);
+        updatePost.setStatus("3"); // 状态改为已下架
+        updatePost.setRemark(request.getReason());
+        updatePost.setUpdateBy(getUsername());
+        
+        int result = postService.updatePost(updatePost);
+        if (result > 0) {
+            return success("帖子已下架");
+        } else {
+            return error("下架操作失败");
+        }
+    }
+
+    /**
+     * 查询举报列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:report')")
+    @GetMapping("/report/list")
+    public TableDataInfo reportList(PostReport postReport)
+    {
+        startPage();
+        List<PostReport> list = postReportMapper.selectPostReportList(postReport);
+        return getDataTable(list);
+    }
+
+    /**
+     * 处理举报(通过/驳回)
+     */
+    @PreAuthorize("@ss.hasPermi('post:post:report')")
+    @Log(title = "举报处理", businessType = BusinessType.UPDATE)
+    @PutMapping("/report/handle/{reportId}")
+    public AjaxResult handleReport(@PathVariable Long reportId, @RequestBody ReportHandleRequest request)
+    {
+        PostReport report = postReportMapper.selectPostReportById(reportId);
+        if (report == null) {
+            return error("举报记录不存在");
+        }
+        
+        if ("approve".equals(request.getAction())) {
+            // 举报属实,下架帖子
+            Post post = postService.selectPostById(request.getPostId());
+            if (post != null) {
+                Post updatePost = new Post();
+                updatePost.setPostId(request.getPostId());
+                updatePost.setStatus("3"); // 状态改为已下架
+                updatePost.setRemark("因举报下架:" + (request.getReason() != null ? request.getReason() : ""));
+                updatePost.setUpdateBy(getUsername());
+                
+                int result = postService.updatePost(updatePost);
+                if (result > 0) {
+                    // 更新举报记录状态
+                    report.setStatus("1"); // 已处理
+                    report.setHandleResult("举报属实,帖子已下架");
+                    report.setHandleTime(new Date());
+                    report.setHandleBy(getUsername());
+                    postReportMapper.updatePostReport(report);
+                    
+                    return success("举报处理成功,帖子已下架");
+                } else {
+                    return error("处理失败");
+                }
+            } else {
+                return error("帖子不存在");
+            }
+        } else if ("reject".equals(request.getAction())) {
+            // 举报不属实,驳回举报
+            report.setStatus("2"); // 已驳回
+            report.setHandleResult("举报不属实" + (request.getReason() != null ? ":" + request.getReason() : ""));
+            report.setHandleTime(new Date());
+            report.setHandleBy(getUsername());
+            postReportMapper.updatePostReport(report);
+            
+            return success("举报已驳回");
+        } else {
+            return error("无效的处理操作");
+        }
+    }
+
+    /**
+     * 审核请求类
+     */
+    public static class ReviewRequest {
+        private String action;
+        private String reason;
+
+        public String getAction() {
+            return action;
+        }
+
+        public void setAction(String action) {
+            this.action = action;
+        }
+
+        public String getReason() {
+            return reason;
+        }
+
+        public void setReason(String reason) {
+            this.reason = reason;
+        }
+    }
+
+    /**
+     * 下架请求类
+     */
+    public static class TakeDownRequest {
+        private String reason;
+
+        public String getReason() {
+            return reason;
+        }
+
+        public void setReason(String reason) {
+            this.reason = reason;
+        }
+    }
+
+    /**
+     * 举报处理请求类
+     */
+    public static class ReportHandleRequest {
+        private String action;
+        private String reason;
+        private Long postId;
+
+        public String getAction() {
+            return action;
+        }
+
+        public void setAction(String action) {
+            this.action = action;
+        }
+
+        public String getReason() {
+            return reason;
+        }
+
+        public void setReason(String reason) {
+            this.reason = reason;
+        }
+
+        public Long getPostId() {
+            return postId;
+        }
+
+        public void setPostId(Long postId) {
+            this.postId = postId;
+        }
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostTagController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostTagController.java
new file mode 100644
index 0000000..7b4b496
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostTagController.java
@@ -0,0 +1,138 @@
+package com.ruoyi.web.controller.post.controller;
+
+import java.util.List;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.web.controller.post.domain.PostTag;
+import com.ruoyi.web.controller.post.service.IPostTagService;
+
+/**
+ * 帖子标签管理
+ * 
+ * @author thunderhub
+ */
+@RestController
+@RequestMapping("/post/tag")
+public class PostTagController extends BaseController
+{
+    @Autowired
+    private IPostTagService postTagService;
+
+    /**
+     * 获取帖子标签列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:tag:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(PostTag postTag)
+    {
+        startPage();
+        List<PostTag> list = postTagService.selectPostTagList(postTag);
+        return getDataTable(list);
+    }
+    
+    /**
+     * 导出帖子标签列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:tag:export')")
+    @Log(title = "帖子标签", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, PostTag postTag)
+    {
+        List<PostTag> list = postTagService.selectPostTagList(postTag);
+        ExcelUtil<PostTag> util = new ExcelUtil<PostTag>(PostTag.class);
+        util.exportExcel(response, list, "标签数据");
+    }
+
+    /**
+     * 获取帖子标签详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('post:tag:query')")
+    @GetMapping(value = "/{tagId}")
+    public AjaxResult getInfo(@PathVariable Long tagId)
+    {
+        return success(postTagService.selectPostTagById(tagId));
+    }
+
+    /**
+     * 新增帖子标签
+     */
+    @PreAuthorize("@ss.hasPermi('post:tag:add')")
+    @Log(title = "帖子标签", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody PostTag postTag)
+    {
+        if (!postTagService.checkTagNameUnique(postTag))
+        {
+            return error("新增标签'" + postTag.getTagName() + "'失败,标签名称已存在");
+        }
+        postTag.setCreateBy(getUsername());
+        postTag.setPostCount(0L);
+        return toAjax(postTagService.insertPostTag(postTag));
+    }
+
+    /**
+     * 修改帖子标签
+     */
+    @PreAuthorize("@ss.hasPermi('post:tag:edit')")
+    @Log(title = "帖子标签", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody PostTag postTag)
+    {
+        if (!postTagService.checkTagNameUnique(postTag))
+        {
+            return error("修改标签'" + postTag.getTagName() + "'失败,标签名称已存在");
+        }
+        postTag.setUpdateBy(getUsername());
+        return toAjax(postTagService.updatePostTag(postTag));
+    }
+
+    /**
+     * 删除帖子标签
+     */
+    @PreAuthorize("@ss.hasPermi('post:tag:remove')")
+    @Log(title = "帖子标签", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{tagIds}")
+    public AjaxResult remove(@PathVariable Long[] tagIds)
+    {
+        return toAjax(postTagService.deletePostTagByIds(tagIds));
+    }
+    
+    /**
+     * 获取帖子的标签列表
+     */
+    @PreAuthorize("@ss.hasPermi('post:tag:list')")
+    @GetMapping("/list/{postId}")
+    public AjaxResult listByPostId(@PathVariable Long postId)
+    {
+        List<PostTag> tags = postTagService.selectPostTagsByPostId(postId);
+        return success(tags);
+    }
+    
+    /**
+     * 获取所有可用标签(用于下拉选择)
+     */
+    @GetMapping("/optionselect")
+    public AjaxResult optionselect()
+    {
+        PostTag query = new PostTag();
+        query.setStatus("0"); // 正常状态
+        List<PostTag> tags = postTagService.selectPostTagList(query);
+        return success(tags);
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/Post.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/Post.java
new file mode 100644
index 0000000..40f18bf
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/Post.java
@@ -0,0 +1,256 @@
+package com.ruoyi.web.controller.post.domain;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 帖子表 sys_post
+ * 
+ * @author thunderhub
+ */
+public class Post extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 帖子ID */
+    @Excel(name = "帖子ID", cellType = ColumnType.NUMERIC)
+    private Long postId;
+
+    /** 帖子标题 */
+    @Excel(name = "帖子标题")
+    private String title;
+
+    /** 帖子内容(富文本) */
+    private String content;
+
+    /** 帖子摘要 */
+    @Excel(name = "帖子摘要")
+    private String summary;
+
+    /** 封面图片URL */
+    @Excel(name = "封面图片")
+    private String coverImage;
+
+    /** 作者ID */
+    @Excel(name = "作者ID")
+    private Long authorId;
+
+    /** 作者名称 */
+    @Excel(name = "作者名称")
+    private String author;
+
+    /** 浏览量 */
+    @Excel(name = "浏览量")
+    private Long views;
+
+    /** 评论数 */
+    @Excel(name = "评论数")
+    private Long comments;
+
+    /** 收藏数 */
+    @Excel(name = "收藏数")
+    private Long favorites;
+
+    /** 点赞数 */
+    @Excel(name = "点赞数")
+    private Long likes;
+
+    /** 状态(0待审核 1正常 2拒绝 3下架) */
+    @Excel(name = "状态", readConverterExp = "0=待审核,1=正常,2=拒绝,3=下架")
+    private String status;
+
+    /** 发布时间 */
+    @Excel(name = "发布时间")
+    private String publishTime;
+    
+    /** 标签列表,用逗号分隔 */
+    private String tags;
+
+    /** 推广计划ID */
+    @Excel(name = "推广计划ID")
+    private Long promotionPlanId;
+
+    public Long getPostId()
+    {
+        return postId;
+    }
+
+    public void setPostId(Long postId)
+    {
+        this.postId = postId;
+    }
+
+    @NotBlank(message = "帖子标题不能为空")
+    @Size(min = 0, max = 100, message = "帖子标题长度不能超过100个字符")
+    public String getTitle()
+    {
+        return title;
+    }
+
+    public void setTitle(String title)
+    {
+        this.title = title;
+    }
+
+    public String getContent()
+    {
+        return content;
+    }
+
+    public void setContent(String content)
+    {
+        this.content = content;
+    }
+
+    @Size(min = 0, max = 500, message = "帖子摘要长度不能超过500个字符")
+    public String getSummary()
+    {
+        return summary;
+    }
+
+    public void setSummary(String summary)
+    {
+        this.summary = summary;
+    }
+
+    public String getCoverImage()
+    {
+        return coverImage;
+    }
+
+    public void setCoverImage(String coverImage)
+    {
+        this.coverImage = coverImage;
+    }
+
+    public Long getAuthorId()
+    {
+        return authorId;
+    }
+
+    public void setAuthorId(Long authorId)
+    {
+        this.authorId = authorId;
+    }
+
+    public String getAuthor()
+    {
+        return author;
+    }
+
+    public void setAuthor(String author)
+    {
+        this.author = author;
+    }
+
+    public Long getViews()
+    {
+        return views;
+    }
+
+    public void setViews(Long views)
+    {
+        this.views = views;
+    }
+
+    public Long getComments()
+    {
+        return comments;
+    }
+
+    public void setComments(Long comments)
+    {
+        this.comments = comments;
+    }
+
+    public Long getFavorites()
+    {
+        return favorites;
+    }
+
+    public void setFavorites(Long favorites)
+    {
+        this.favorites = favorites;
+    }
+
+    public Long getLikes()
+    {
+        return likes;
+    }
+
+    public void setLikes(Long likes)
+    {
+        this.likes = likes;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public String getPublishTime()
+    {
+        return publishTime;
+    }
+
+    public void setPublishTime(String publishTime)
+    {
+        this.publishTime = publishTime;
+    }
+    
+    public String getTags()
+    {
+        return tags;
+    }
+
+    public void setTags(String tags)
+    {
+        this.tags = tags;
+    }
+
+    public Long getPromotionPlanId()
+    {
+        return promotionPlanId;
+    }
+
+    public void setPromotionPlanId(Long promotionPlanId)
+    {
+        this.promotionPlanId = promotionPlanId;
+    }
+    
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("postId", getPostId())
+            .append("title", getTitle())
+            .append("content", getContent())
+            .append("summary", getSummary())
+            .append("coverImage", getCoverImage())
+            .append("authorId", getAuthorId())
+            .append("author", getAuthor())
+            .append("views", getViews())
+            .append("comments", getComments())
+            .append("favorites", getFavorites())
+            .append("likes", getLikes())
+            .append("status", getStatus())
+            .append("publishTime", getPublishTime())
+            .append("tags", getTags())
+            .append("promotionPlanId", getPromotionPlanId())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostComment.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostComment.java
new file mode 100644
index 0000000..5041087
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostComment.java
@@ -0,0 +1,193 @@
+package com.ruoyi.web.controller.post.domain;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 帖子评论表 sys_post_comment
+ * 
+ * @author thunderhub
+ */
+public class PostComment extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 评论ID */
+    @Excel(name = "评论ID", cellType = ColumnType.NUMERIC)
+    private Long commentId;
+
+    /** 帖子ID */
+    @Excel(name = "帖子ID")
+    private Long postId;
+
+    /** 评论内容 */
+    @Excel(name = "评论内容")
+    private String content;
+
+    /** 评论人ID */
+    @Excel(name = "评论人ID")
+    private Long userId;
+
+    /** 评论人名称 */
+    @Excel(name = "评论人名称")
+    private String userName;
+
+    /** 评论人头像 */
+    private String userAvatar;
+
+    /** 父评论ID,如果为0则为顶级评论 */
+    private Long parentId;
+
+    /** 回复对象的用户ID */
+    private Long replyUserId;
+
+    /** 回复对象的用户名称 */
+    private String replyUserName;
+
+    /** 状态(0待审核 1正常 2拒绝) */
+    @Excel(name = "状态", readConverterExp = "0=待审核,1=正常,2=拒绝")
+    private String status;
+
+    /** 点赞数 */
+    @Excel(name = "点赞数")
+    private Long likes;
+
+    public Long getCommentId()
+    {
+        return commentId;
+    }
+
+    public void setCommentId(Long commentId)
+    {
+        this.commentId = commentId;
+    }
+
+    public Long getPostId()
+    {
+        return postId;
+    }
+
+    public void setPostId(Long postId)
+    {
+        this.postId = postId;
+    }
+
+    @NotBlank(message = "评论内容不能为空")
+    @Size(min = 0, max = 500, message = "评论内容长度不能超过500个字符")
+    public String getContent()
+    {
+        return content;
+    }
+
+    public void setContent(String content)
+    {
+        this.content = content;
+    }
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public String getUserName()
+    {
+        return userName;
+    }
+
+    public void setUserName(String userName)
+    {
+        this.userName = userName;
+    }
+
+    public String getUserAvatar()
+    {
+        return userAvatar;
+    }
+
+    public void setUserAvatar(String userAvatar)
+    {
+        this.userAvatar = userAvatar;
+    }
+
+    public Long getParentId()
+    {
+        return parentId;
+    }
+
+    public void setParentId(Long parentId)
+    {
+        this.parentId = parentId;
+    }
+
+    public Long getReplyUserId()
+    {
+        return replyUserId;
+    }
+
+    public void setReplyUserId(Long replyUserId)
+    {
+        this.replyUserId = replyUserId;
+    }
+
+    public String getReplyUserName()
+    {
+        return replyUserName;
+    }
+
+    public void setReplyUserName(String replyUserName)
+    {
+        this.replyUserName = replyUserName;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public Long getLikes()
+    {
+        return likes;
+    }
+
+    public void setLikes(Long likes)
+    {
+        this.likes = likes;
+    }
+    
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("commentId", getCommentId())
+            .append("postId", getPostId())
+            .append("content", getContent())
+            .append("userId", getUserId())
+            .append("userName", getUserName())
+            .append("userAvatar", getUserAvatar())
+            .append("parentId", getParentId())
+            .append("replyUserId", getReplyUserId())
+            .append("replyUserName", getReplyUserName())
+            .append("status", getStatus())
+            .append("likes", getLikes())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostFavorite.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostFavorite.java
new file mode 100644
index 0000000..1a6c0f0
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostFavorite.java
@@ -0,0 +1,117 @@
+package com.ruoyi.web.controller.post.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 帖子收藏表 sys_post_favorite
+ * 
+ * @author thunderhub
+ */
+public class PostFavorite extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 收藏ID */
+    @Excel(name = "收藏ID", cellType = ColumnType.NUMERIC)
+    private Long favoriteId;
+
+    /** 帖子ID */
+    @Excel(name = "帖子ID")
+    private Long postId;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    /** 帖子标题 */
+    @Excel(name = "帖子标题")
+    private String postTitle;
+
+    /** 帖子封面 */
+    private String postCover;
+
+    /** 收藏状态(0收藏 1取消) */
+    @Excel(name = "收藏状态", readConverterExp = "0=收藏,1=取消")
+    private String status;
+
+    public Long getFavoriteId()
+    {
+        return favoriteId;
+    }
+
+    public void setFavoriteId(Long favoriteId)
+    {
+        this.favoriteId = favoriteId;
+    }
+
+    public Long getPostId()
+    {
+        return postId;
+    }
+
+    public void setPostId(Long postId)
+    {
+        this.postId = postId;
+    }
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public String getPostTitle()
+    {
+        return postTitle;
+    }
+
+    public void setPostTitle(String postTitle)
+    {
+        this.postTitle = postTitle;
+    }
+
+    public String getPostCover()
+    {
+        return postCover;
+    }
+
+    public void setPostCover(String postCover)
+    {
+        this.postCover = postCover;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+    
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("favoriteId", getFavoriteId())
+            .append("postId", getPostId())
+            .append("userId", getUserId())
+            .append("postTitle", getPostTitle())
+            .append("postCover", getPostCover())
+            .append("status", getStatus())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostPayment.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostPayment.java
new file mode 100644
index 0000000..091fd94
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostPayment.java
@@ -0,0 +1,92 @@
+package com.ruoyi.web.controller.post.domain;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 帖子支付记录对象 post_payment
+ * 
+ * @author thunderhub
+ */
+public class PostPayment extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 支付ID */
+    private Long paymentId;
+
+    /** 帖子ID */
+    private Long postId;
+
+    /** 推广计划ID */
+    private Long planId;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 支付金额 */
+    private BigDecimal amount;
+
+    /** 支付状态 */
+    private String paymentStatus;
+
+    /** 支付时间 */
+    private Date paymentTime;
+
+    public Long getPaymentId() {
+        return paymentId;
+    }
+
+    public void setPaymentId(Long paymentId) {
+        this.paymentId = paymentId;
+    }
+
+    public Long getPostId() {
+        return postId;
+    }
+
+    public void setPostId(Long postId) {
+        this.postId = postId;
+    }
+
+    public Long getPlanId() {
+        return planId;
+    }
+
+    public void setPlanId(Long planId) {
+        this.planId = planId;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public void setAmount(BigDecimal amount) {
+        this.amount = amount;
+    }
+
+    public String getPaymentStatus() {
+        return paymentStatus;
+    }
+
+    public void setPaymentStatus(String paymentStatus) {
+        this.paymentStatus = paymentStatus;
+    }
+
+    public Date getPaymentTime() {
+        return paymentTime;
+    }
+
+    public void setPaymentTime(Date paymentTime) {
+        this.paymentTime = paymentTime;
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostPromotionPlan.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostPromotionPlan.java
new file mode 100644
index 0000000..422e156
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostPromotionPlan.java
@@ -0,0 +1,118 @@
+package com.ruoyi.web.controller.post.domain;
+
+import java.math.BigDecimal;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 帖子推广计划对象 post_promotion_plan
+ * 
+ * @author thunderhub
+ */
+public class PostPromotionPlan extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 推广计划ID */
+    private Long planId;
+
+    /** 推广计划名称 */
+    private String planName;
+
+    /** 推广计划描述 */
+    private String planDescription;
+
+    /** 推广价格 */
+    private BigDecimal price;
+
+    /** 推广时长(天) */
+    private Integer duration;
+
+    /** 推广等级(数字越小等级越高) */
+    private Integer level;
+
+    /** 状态(0正常 1停用) */
+    private String status;
+
+    public void setPlanId(Long planId) 
+    {
+        this.planId = planId;
+    }
+
+    public Long getPlanId() 
+    {
+        return planId;
+    }
+
+    public void setPlanName(String planName) 
+    {
+        this.planName = planName;
+    }
+
+    public String getPlanName() 
+    {
+        return planName;
+    }
+
+    public void setPlanDescription(String planDescription) 
+    {
+        this.planDescription = planDescription;
+    }
+
+    public String getPlanDescription() 
+    {
+        return planDescription;
+    }
+
+    public void setPrice(BigDecimal price) 
+    {
+        this.price = price;
+    }
+
+    public BigDecimal getPrice() 
+    {
+        return price;
+    }
+
+    public void setDuration(Integer duration) 
+    {
+        this.duration = duration;
+    }
+
+    public Integer getDuration() 
+    {
+        return duration;
+    }
+
+    public void setLevel(Integer level) 
+    {
+        this.level = level;
+    }
+
+    public Integer getLevel() 
+    {
+        return level;
+    }
+
+    public void setStatus(String status) 
+    {
+        this.status = status;
+    }
+
+    public String getStatus() 
+    {
+        return status;
+    }
+
+    @Override
+    public String toString() {
+        return "PostPromotionPlan{" +
+                "planId=" + planId +
+                ", planName='" + planName + '\'' +
+                ", planDescription='" + planDescription + '\'' +
+                ", price=" + price +
+                ", duration=" + duration +
+                ", level=" + level +
+                ", status='" + status + '\'' +
+                '}';
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostReport.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostReport.java
new file mode 100644
index 0000000..d8f8f7f
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostReport.java
@@ -0,0 +1,124 @@
+package com.ruoyi.web.controller.post.domain;
+
+import java.util.Date;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 帖子举报对象 post_report
+ * 
+ * @author thunderhub
+ */
+public class PostReport extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 举报ID */
+    private Long reportId;
+
+    /** 帖子ID */
+    private Long postId;
+
+    /** 帖子标题 */
+    private String postTitle;
+
+    /** 举报用户ID */
+    private Long reportUserId;
+
+    /** 举报用户名 */
+    private String reportUserName;
+
+    /** 举报理由 */
+    private String reportReason;
+
+    /** 举报状态 0-待处理 1-已处理 2-已驳回 */
+    private String status;
+
+    /** 处理结果 */
+    private String handleResult;
+
+    /** 处理时间 */
+    private Date handleTime;
+
+    /** 处理人 */
+    private String handleBy;
+
+    public Long getReportId() {
+        return reportId;
+    }
+
+    public void setReportId(Long reportId) {
+        this.reportId = reportId;
+    }
+
+    public Long getPostId() {
+        return postId;
+    }
+
+    public void setPostId(Long postId) {
+        this.postId = postId;
+    }
+
+    public String getPostTitle() {
+        return postTitle;
+    }
+
+    public void setPostTitle(String postTitle) {
+        this.postTitle = postTitle;
+    }
+
+    public Long getReportUserId() {
+        return reportUserId;
+    }
+
+    public void setReportUserId(Long reportUserId) {
+        this.reportUserId = reportUserId;
+    }
+
+    public String getReportUserName() {
+        return reportUserName;
+    }
+
+    public void setReportUserName(String reportUserName) {
+        this.reportUserName = reportUserName;
+    }
+
+    public String getReportReason() {
+        return reportReason;
+    }
+
+    public void setReportReason(String reportReason) {
+        this.reportReason = reportReason;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getHandleResult() {
+        return handleResult;
+    }
+
+    public void setHandleResult(String handleResult) {
+        this.handleResult = handleResult;
+    }
+
+    public Date getHandleTime() {
+        return handleTime;
+    }
+
+    public void setHandleTime(Date handleTime) {
+        this.handleTime = handleTime;
+    }
+
+    public String getHandleBy() {
+        return handleBy;
+    }
+
+    public void setHandleBy(String handleBy) {
+        this.handleBy = handleBy;
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostTag.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostTag.java
new file mode 100644
index 0000000..e4a4b55
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostTag.java
@@ -0,0 +1,107 @@
+package com.ruoyi.web.controller.post.domain;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 帖子标签表 sys_post_tag
+ * 
+ * @author thunderhub
+ */
+public class PostTag extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 标签ID */
+    @Excel(name = "标签ID", cellType = ColumnType.NUMERIC)
+    private Long tagId;
+
+    /** 标签名称 */
+    @Excel(name = "标签名称")
+    private String tagName;
+
+    /** 标签颜色 */
+    @Excel(name = "标签颜色")
+    private String tagColor;
+
+    /** 帖子数量 */
+    @Excel(name = "帖子数量")
+    private Long postCount;
+
+    /** 状态(0正常 1停用) */
+    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    public Long getTagId()
+    {
+        return tagId;
+    }
+
+    public void setTagId(Long tagId)
+    {
+        this.tagId = tagId;
+    }
+
+    @NotBlank(message = "标签名称不能为空")
+    @Size(min = 0, max = 50, message = "标签名称长度不能超过50个字符")
+    public String getTagName()
+    {
+        return tagName;
+    }
+
+    public void setTagName(String tagName)
+    {
+        this.tagName = tagName;
+    }
+
+    public String getTagColor()
+    {
+        return tagColor;
+    }
+
+    public void setTagColor(String tagColor)
+    {
+        this.tagColor = tagColor;
+    }
+
+    public Long getPostCount()
+    {
+        return postCount;
+    }
+
+    public void setPostCount(Long postCount)
+    {
+        this.postCount = postCount;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+    
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("tagId", getTagId())
+            .append("tagName", getTagName())
+            .append("tagColor", getTagColor())
+            .append("postCount", getPostCount())
+            .append("status", getStatus())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostTagRelation.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostTagRelation.java
new file mode 100644
index 0000000..78f4443
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/domain/PostTagRelation.java
@@ -0,0 +1,70 @@
+package com.ruoyi.web.controller.post.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 帖子标签关联表 sys_post_tag_relation
+ * 
+ * @author thunderhub
+ */
+public class PostTagRelation extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 关联ID */
+    private Long id;
+
+    /** 帖子ID */
+    @Excel(name = "帖子ID")
+    private Long postId;
+
+    /** 标签ID */
+    @Excel(name = "标签ID")
+    private Long tagId;
+
+    public Long getId()
+    {
+        return id;
+    }
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getPostId()
+    {
+        return postId;
+    }
+
+    public void setPostId(Long postId)
+    {
+        this.postId = postId;
+    }
+
+    public Long getTagId()
+    {
+        return tagId;
+    }
+
+    public void setTagId(Long tagId)
+    {
+        this.tagId = tagId;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("postId", getPostId())
+            .append("tagId", getTagId())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .toString();
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostCommentMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostCommentMapper.java
new file mode 100644
index 0000000..c5103a1
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostCommentMapper.java
@@ -0,0 +1,94 @@
+package com.ruoyi.web.controller.post.mapper;
+
+import java.util.List;
+
+import com.ruoyi.web.controller.post.domain.PostComment;
+
+/**
+ * 帖子评论Mapper接口
+ * 
+ * @author thunderhub
+ */
+public interface PostCommentMapper 
+{
+    /**
+     * 查询帖子评论信息
+     * 
+     * @param commentId 帖子评论ID
+     * @return 帖子评论信息
+     */
+    public PostComment selectPostCommentById(Long commentId);
+
+    /**
+     * 查询帖子评论列表
+     * 
+     * @param postComment 帖子评论信息
+     * @return 帖子评论集合
+     */
+    public List<PostComment> selectPostCommentList(PostComment postComment);
+
+    /**
+     * 根据帖子ID查询评论
+     * 
+     * @param postId 帖子ID
+     * @param limit 查询数量,0表示查询全部
+     * @return 帖子评论集合
+     */
+    public List<PostComment> selectCommentsByPostId(Long postId, int limit);
+
+    /**
+     * 根据父评论ID查询回复
+     * 
+     * @param parentId 父评论ID
+     * @return 帖子评论集合
+     */
+    public List<PostComment> selectCommentsByParentId(Long parentId);
+
+    /**
+     * 新增帖子评论
+     * 
+     * @param postComment 帖子评论信息
+     * @return 结果
+     */
+    public int insertPostComment(PostComment postComment);
+
+    /**
+     * 修改帖子评论
+     * 
+     * @param postComment 帖子评论信息
+     * @return 结果
+     */
+    public int updatePostComment(PostComment postComment);
+
+    /**
+     * 删除帖子评论
+     * 
+     * @param commentId 帖子评论ID
+     * @return 结果
+     */
+    public int deletePostCommentById(Long commentId);
+
+    /**
+     * 批量删除帖子评论
+     * 
+     * @param commentIds 需要删除的帖子评论ID
+     * @return 结果
+     */
+    public int deletePostCommentByIds(Long[] commentIds);
+
+    /**
+     * 根据帖子ID删除评论
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int deletePostCommentByPostId(Long postId);
+
+    /**
+     * 更新评论点赞数
+     * 
+     * @param postComment 评论信息
+     * @return 结果
+     */
+    public int updateCommentLikes(PostComment postComment);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostContentMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostContentMapper.java
new file mode 100644
index 0000000..6929295
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostContentMapper.java
@@ -0,0 +1,114 @@
+package com.ruoyi.web.controller.post.mapper;
+
+import java.util.List;
+
+import com.ruoyi.web.controller.post.domain.Post;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 帖子内容Mapper接口
+ * 
+ * @author thunderhub
+ */
+public interface PostContentMapper 
+{
+    /**
+     * 查询帖子信息
+     * 
+     * @param postId 帖子ID
+     * @return 帖子信息
+     */
+    public Post selectPostById(Long postId);
+
+    /**
+     * 查询帖子列表
+     * 
+     * @param post 帖子信息
+     * @return 帖子集合
+     */
+    public List<Post> selectPostList(Post post);
+
+    /**
+     * 查询作者的其他帖子
+     * 
+     * @param authorId 作者ID
+     * @param postId 当前帖子ID(排除)
+     * @param limit 查询数量
+     * @return 帖子集合
+     */
+    public List<Post> selectAuthorOtherPosts(@Param("authorId") Long authorId, @Param("postId") Long postId, @Param("limit") int limit);
+
+    /**
+     * 查询相似标签的帖子
+     * 
+     * @param tags 标签字符串
+     * @param postId 当前帖子ID(排除)
+     * @param limit 查询数量
+     * @return 帖子集合
+     */
+    public List<Post> selectSimilarTagsPosts(@Param("tags") String tags, @Param("postId") Long postId, @Param("limit") int limit);
+
+    /**
+     * 新增帖子
+     * 
+     * @param post 帖子信息
+     * @return 结果
+     */
+    public int insertPost(Post post);
+
+    /**
+     * 修改帖子
+     * 
+     * @param post 帖子信息
+     * @return 结果
+     */
+    public int updatePost(Post post);
+
+    /**
+     * 删除帖子
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int deletePostById(Long postId);
+
+    /**
+     * 批量删除帖子
+     * 
+     * @param postIds 需要删除的帖子ID
+     * @return 结果
+     */
+    public int deletePostByIds(Long[] postIds);
+
+    /**
+     * 更新帖子浏览量
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int updatePostViews(Long postId);
+
+    /**
+     * 更新帖子评论数
+     * 
+     * @param post 帖子信息
+     * @return 结果
+     */
+    public int updatePostComments(Post post);
+
+    /**
+     * 更新帖子收藏数
+     * 
+     * @param post 帖子信息
+     * @return 结果
+     */
+    public int updatePostFavorites(Post post);
+
+    /**
+     * 更新帖子点赞数
+     * 
+     * @param post 帖子信息
+     * @return 结果
+     */
+    public int updatePostLikes(Post post);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostContentTagMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostContentTagMapper.java
new file mode 100644
index 0000000..5229c59
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostContentTagMapper.java
@@ -0,0 +1,112 @@
+package com.ruoyi.web.controller.post.mapper;
+
+import java.util.List;
+
+import com.ruoyi.web.controller.post.domain.PostTag;
+import com.ruoyi.web.controller.post.domain.PostTagRelation;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 帖子标签Mapper接口
+ * 
+ * @author thunderhub
+ */
+@Mapper
+public interface PostContentTagMapper 
+{
+    /**
+     * 查询帖子标签信息
+     * 
+     * @param tagId 帖子标签ID
+     * @return 帖子标签信息
+     */
+    public PostTag selectPostTagById(Long tagId);
+
+    /**
+     * 查询帖子标签列表
+     * 
+     * @param postTag 帖子标签信息
+     * @return 帖子标签集合
+     */
+    public List<PostTag> selectPostTagList(PostTag postTag);
+
+    /**
+     * 根据帖子ID查询标签
+     * 
+     * @param postId 帖子ID
+     * @return 帖子标签集合
+     */
+    public List<PostTag> selectPostTagsByPostId(Long postId);
+
+    /**
+     * 新增帖子标签
+     * 
+     * @param postTag 帖子标签信息
+     * @return 结果
+     */
+    public int insertPostTag(PostTag postTag);
+
+    /**
+     * 修改帖子标签
+     * 
+     * @param postTag 帖子标签信息
+     * @return 结果
+     */
+    public int updatePostTag(PostTag postTag);
+
+    /**
+     * 删除帖子标签
+     * 
+     * @param tagId 帖子标签ID
+     * @return 结果
+     */
+    public int deletePostTagById(Long tagId);
+
+    /**
+     * 批量删除帖子标签
+     * 
+     * @param tagIds 需要删除的帖子标签ID
+     * @return 结果
+     */
+    public int deletePostTagByIds(Long[] tagIds);
+
+    /**
+     * 插入帖子标签关联
+     * 
+     * @param relation 关联信息
+     * @return 结果
+     */
+    public int insertPostTagRelation(PostTagRelation relation);
+
+    /**
+     * 批量插入帖子标签关联
+     * 
+     * @param list 关联列表
+     * @return 结果
+     */
+    public int batchInsertPostTagRelation(List<PostTagRelation> list);
+
+    /**
+     * 删除帖子标签关联
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int deletePostTagRelation(Long postId);
+
+    /**
+     * 更新标签帖子数量
+     * 
+     * @param postTag 帖子标签信息
+     * @return 结果
+     */
+    public int updatePostTagCount(PostTag postTag);
+
+    /**
+     * 校验标签名称是否唯一
+     * 
+     * @param tagName 标签名称
+     * @return 结果
+     */
+    public PostTag checkTagNameUnique(String tagName);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostFavoriteMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostFavoriteMapper.java
new file mode 100644
index 0000000..96885e8
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostFavoriteMapper.java
@@ -0,0 +1,88 @@
+package com.ruoyi.web.controller.post.mapper;
+
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+
+import com.ruoyi.web.controller.post.domain.PostFavorite;
+
+/**
+ * 帖子收藏Mapper接口
+ * 
+ * @author thunderhub
+ */
+public interface PostFavoriteMapper 
+{
+    /**
+     * 查询帖子收藏信息
+     * 
+     * @param favoriteId 帖子收藏ID
+     * @return 帖子收藏信息
+     */
+    public PostFavorite selectPostFavoriteById(Long favoriteId);
+
+    /**
+     * 查询帖子收藏列表
+     * 
+     * @param postFavorite 帖子收藏信息
+     * @return 帖子收藏集合
+     */
+    public List<PostFavorite> selectPostFavoriteList(PostFavorite postFavorite);
+
+    /**
+     * 查询用户是否收藏帖子
+     * 
+     * @param postId 帖子ID
+     * @param userId 用户ID
+     * @return 收藏信息
+     */
+    public PostFavorite selectPostFavoriteByPostIdAndUserId(@Param("postId") Long postId, @Param("userId") Long userId);
+
+    /**
+     * 新增帖子收藏
+     * 
+     * @param postFavorite 帖子收藏信息
+     * @return 结果
+     */
+    public int insertPostFavorite(PostFavorite postFavorite);
+
+    /**
+     * 修改帖子收藏
+     * 
+     * @param postFavorite 帖子收藏信息
+     * @return 结果
+     */
+    public int updatePostFavorite(PostFavorite postFavorite);
+
+    /**
+     * 删除帖子收藏
+     * 
+     * @param favoriteId 帖子收藏ID
+     * @return 结果
+     */
+    public int deletePostFavoriteById(Long favoriteId);
+
+    /**
+     * 批量删除帖子收藏
+     * 
+     * @param favoriteIds 需要删除的帖子收藏ID
+     * @return 结果
+     */
+    public int deletePostFavoriteByIds(Long[] favoriteIds);
+
+    /**
+     * 取消收藏帖子
+     * 
+     * @param postId 帖子ID
+     * @param userId 用户ID
+     * @return 结果
+     */
+    public int cancelPostFavorite(@Param("postId") Long postId, @Param("userId") Long userId);
+
+    /**
+     * 根据帖子ID删除收藏
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int deletePostFavoriteByPostId(Long postId);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPaymentMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPaymentMapper.java
new file mode 100644
index 0000000..538338c
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPaymentMapper.java
@@ -0,0 +1,76 @@
+package com.ruoyi.web.controller.post.mapper;
+
+import java.util.List;
+import com.ruoyi.web.controller.post.domain.PostPayment;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 帖子支付记录Mapper接口
+ * 
+ * @author thunderhub
+ */
+public interface PostPaymentMapper 
+{
+    /**
+     * 查询帖子支付记录
+     * 
+     * @param paymentId 帖子支付记录ID
+     * @return 帖子支付记录
+     */
+    public PostPayment selectPostPaymentById(Long paymentId);
+
+    /**
+     * 查询帖子支付记录列表
+     * 
+     * @param postPayment 帖子支付记录
+     * @return 帖子支付记录集合
+     */
+    public List<PostPayment> selectPostPaymentList(PostPayment postPayment);
+
+    /**
+     * 根据帖子ID查询最新的支付记录
+     * 
+     * @param postId 帖子ID
+     * @return 支付记录
+     */
+    public PostPayment selectLatestPaymentByPostId(Long postId);
+
+    /**
+     * 查询过期的推广支付记录
+     * 
+     * @return 过期的支付记录列表
+     */
+    public List<PostPayment> selectExpiredPromotionPayments();
+
+    /**
+     * 新增帖子支付记录
+     * 
+     * @param postPayment 帖子支付记录
+     * @return 结果
+     */
+    public int insertPostPayment(PostPayment postPayment);
+
+    /**
+     * 修改帖子支付记录
+     * 
+     * @param postPayment 帖子支付记录
+     * @return 结果
+     */
+    public int updatePostPayment(PostPayment postPayment);
+
+    /**
+     * 删除帖子支付记录
+     * 
+     * @param paymentId 帖子支付记录ID
+     * @return 结果
+     */
+    public int deletePostPaymentById(Long paymentId);
+
+    /**
+     * 批量删除帖子支付记录
+     * 
+     * @param paymentIds 需要删除的数据ID
+     * @return 结果
+     */
+    public int deletePostPaymentByIds(Long[] paymentIds);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPromotionPlanMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPromotionPlanMapper.java
new file mode 100644
index 0000000..03f0138
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPromotionPlanMapper.java
@@ -0,0 +1,60 @@
+package com.ruoyi.web.controller.post.mapper;
+
+import java.util.List;
+import com.ruoyi.web.controller.post.domain.PostPromotionPlan;
+
+/**
+ * 帖子推广计划Mapper接口
+ * 
+ * @author thunderhub
+ */
+public interface PostPromotionPlanMapper 
+{
+    /**
+     * 查询帖子推广计划
+     * 
+     * @param planId 帖子推广计划ID
+     * @return 帖子推广计划
+     */
+    public PostPromotionPlan selectPostPromotionPlanById(Long planId);
+
+    /**
+     * 查询帖子推广计划列表
+     * 
+     * @param postPromotionPlan 帖子推广计划
+     * @return 帖子推广计划集合
+     */
+    public List<PostPromotionPlan> selectPostPromotionPlanList(PostPromotionPlan postPromotionPlan);
+
+    /**
+     * 新增帖子推广计划
+     * 
+     * @param postPromotionPlan 帖子推广计划
+     * @return 结果
+     */
+    public int insertPostPromotionPlan(PostPromotionPlan postPromotionPlan);
+
+    /**
+     * 修改帖子推广计划
+     * 
+     * @param postPromotionPlan 帖子推广计划
+     * @return 结果
+     */
+    public int updatePostPromotionPlan(PostPromotionPlan postPromotionPlan);
+
+    /**
+     * 删除帖子推广计划
+     * 
+     * @param planId 帖子推广计划ID
+     * @return 结果
+     */
+    public int deletePostPromotionPlanById(Long planId);
+
+    /**
+     * 批量删除帖子推广计划
+     * 
+     * @param planIds 需要删除的数据ID
+     * @return 结果
+     */
+    public int deletePostPromotionPlanByIds(Long[] planIds);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostReportMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostReportMapper.java
new file mode 100644
index 0000000..0941836
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostReportMapper.java
@@ -0,0 +1,60 @@
+package com.ruoyi.web.controller.post.mapper;
+
+import java.util.List;
+import com.ruoyi.web.controller.post.domain.PostReport;
+
+/**
+ * 帖子举报Mapper接口
+ * 
+ * @author thunderhub
+ */
+public interface PostReportMapper 
+{
+    /**
+     * 查询帖子举报
+     * 
+     * @param reportId 帖子举报ID
+     * @return 帖子举报
+     */
+    public PostReport selectPostReportById(Long reportId);
+
+    /**
+     * 查询帖子举报列表
+     * 
+     * @param postReport 帖子举报
+     * @return 帖子举报集合
+     */
+    public List<PostReport> selectPostReportList(PostReport postReport);
+
+    /**
+     * 新增帖子举报
+     * 
+     * @param postReport 帖子举报
+     * @return 结果
+     */
+    public int insertPostReport(PostReport postReport);
+
+    /**
+     * 修改帖子举报
+     * 
+     * @param postReport 帖子举报
+     * @return 结果
+     */
+    public int updatePostReport(PostReport postReport);
+
+    /**
+     * 删除帖子举报
+     * 
+     * @param reportId 帖子举报ID
+     * @return 结果
+     */
+    public int deletePostReportById(Long reportId);
+
+    /**
+     * 批量删除帖子举报
+     * 
+     * @param reportIds 需要删除的数据ID
+     * @return 结果
+     */
+    public int deletePostReportByIds(Long[] reportIds);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostCommentService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostCommentService.java
new file mode 100644
index 0000000..3ab45cf
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostCommentService.java
@@ -0,0 +1,95 @@
+package com.ruoyi.web.controller.post.service;
+
+import java.util.List;
+
+import com.ruoyi.web.controller.post.domain.PostComment;
+
+/**
+ * 帖子评论 服务层
+ * 
+ * @author thunderhub
+ */
+public interface IPostCommentService 
+{
+    /**
+     * 查询帖子评论信息
+     * 
+     * @param commentId 帖子评论ID
+     * @return 帖子评论信息
+     */
+    public PostComment selectPostCommentById(Long commentId);
+
+    /**
+     * 查询帖子评论列表
+     * 
+     * @param postComment 帖子评论信息
+     * @return 帖子评论集合
+     */
+    public List<PostComment> selectPostCommentList(PostComment postComment);
+
+    /**
+     * 根据帖子ID查询评论
+     * 
+     * @param postId 帖子ID
+     * @param limit 查询数量,0表示查询全部
+     * @return 帖子评论集合
+     */
+    public List<PostComment> selectCommentsByPostId(Long postId, int limit);
+
+    /**
+     * 根据父评论ID查询回复
+     * 
+     * @param parentId 父评论ID
+     * @return 帖子评论集合
+     */
+    public List<PostComment> selectCommentsByParentId(Long parentId);
+
+    /**
+     * 新增帖子评论
+     * 
+     * @param postComment 帖子评论信息
+     * @return 结果
+     */
+    public int insertPostComment(PostComment postComment);
+
+    /**
+     * 修改帖子评论
+     * 
+     * @param postComment 帖子评论信息
+     * @return 结果
+     */
+    public int updatePostComment(PostComment postComment);
+
+    /**
+     * 删除帖子评论信息
+     * 
+     * @param commentId 帖子评论ID
+     * @return 结果
+     */
+    public int deletePostCommentById(Long commentId);
+
+    /**
+     * 批量删除帖子评论信息
+     * 
+     * @param commentIds 需要删除的帖子评论ID
+     * @return 结果
+     */
+    public int deletePostCommentByIds(Long[] commentIds);
+
+    /**
+     * 根据帖子ID删除评论
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int deletePostCommentByPostId(Long postId);
+
+    /**
+     * 更新评论点赞数
+     * 
+     * @param commentId 评论ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    public int updateCommentLikes(Long commentId, int count);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostFavoriteService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostFavoriteService.java
new file mode 100644
index 0000000..50418fb
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostFavoriteService.java
@@ -0,0 +1,95 @@
+package com.ruoyi.web.controller.post.service;
+
+import java.util.List;
+
+import com.ruoyi.web.controller.post.domain.PostFavorite;
+
+/**
+ * 帖子收藏 服务层
+ * 
+ * @author thunderhub
+ */
+public interface IPostFavoriteService 
+{
+    /**
+     * 查询帖子收藏信息
+     * 
+     * @param favoriteId 帖子收藏ID
+     * @return 帖子收藏信息
+     */
+    public PostFavorite selectPostFavoriteById(Long favoriteId);
+
+    /**
+     * 查询帖子收藏列表
+     * 
+     * @param postFavorite 帖子收藏信息
+     * @return 帖子收藏集合
+     */
+    public List<PostFavorite> selectPostFavoriteList(PostFavorite postFavorite);
+
+    /**
+     * 查询用户是否收藏帖子
+     * 
+     * @param postId 帖子ID
+     * @param userId 用户ID
+     * @return 收藏信息
+     */
+    public PostFavorite selectPostFavoriteByPostIdAndUserId(Long postId, Long userId);
+
+    /**
+     * 新增帖子收藏
+     * 
+     * @param postFavorite 帖子收藏信息
+     * @return 结果
+     */
+    public int insertPostFavorite(PostFavorite postFavorite);
+
+    /**
+     * 修改帖子收藏
+     * 
+     * @param postFavorite 帖子收藏信息
+     * @return 结果
+     */
+    public int updatePostFavorite(PostFavorite postFavorite);
+
+    /**
+     * 删除帖子收藏信息
+     * 
+     * @param favoriteId 帖子收藏ID
+     * @return 结果
+     */
+    public int deletePostFavoriteById(Long favoriteId);
+
+    /**
+     * 批量删除帖子收藏信息
+     * 
+     * @param favoriteIds 需要删除的帖子收藏ID
+     * @return 结果
+     */
+    public int deletePostFavoriteByIds(Long[] favoriteIds);
+
+    /**
+     * 取消收藏帖子
+     * 
+     * @param postId 帖子ID
+     * @param userId 用户ID
+     * @return 结果
+     */
+    public int cancelPostFavorite(Long postId, Long userId);
+
+    /**
+     * 根据帖子ID删除收藏
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int deletePostFavoriteByPostId(Long postId);
+
+    /**
+     * 根据用户ID查询收藏列表
+     * 
+     * @param userId 用户ID
+     * @return 收藏列表
+     */
+    public List<PostFavorite> selectPostFavoritesByUserId(Long userId);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostService.java
new file mode 100644
index 0000000..55ce02f
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostService.java
@@ -0,0 +1,142 @@
+package com.ruoyi.web.controller.post.service;
+
+import java.util.List;
+
+import com.ruoyi.web.controller.post.domain.Post;
+
+/**
+ * 帖子 服务层
+ * 
+ * @author thunderhub
+ */
+public interface IPostService 
+{
+    /**
+     * 查询帖子信息
+     * 
+     * @param postId 帖子ID
+     * @return 帖子信息
+     */
+    public Post selectPostById(Long postId);
+
+    /**
+     * 查询帖子列表
+     * 
+     * @param post 帖子信息
+     * @return 帖子集合
+     */
+    public List<Post> selectPostList(Post post);
+
+    /**
+     * 查询作者的其他帖子
+     * 
+     * @param authorId 作者ID
+     * @param postId 当前帖子ID(排除)
+     * @param limit 查询数量
+     * @return 帖子集合
+     */
+    public List<Post> selectAuthorOtherPosts(Long authorId, Long postId, int limit);
+
+    /**
+     * 查询相似标签的帖子
+     * 
+     * @param tags 标签列表
+     * @param postId 当前帖子ID(排除)
+     * @param limit 查询数量
+     * @return 帖子集合
+     */
+    public List<Post> selectSimilarTagsPosts(String tags, Long postId, int limit);
+
+    /**
+     * 获取帖子推荐列表(包含作者其他帖子和相似标签帖子)
+     * 
+     * @param authorId 作者ID
+     * @param tags 标签列表
+     * @param postId 当前帖子ID(排除)
+     * @param maxCount 最大推荐数量
+     * @return 推荐帖子集合
+     */
+    public List<Post> getRecommendedPosts(Long authorId, String tags, Long postId, int maxCount);
+
+    /**
+     * 新增帖子
+     * 
+     * @param post 帖子信息
+     * @return 结果
+     */
+    public int insertPost(Post post);
+
+    /**
+     * 修改帖子
+     * 
+     * @param post 帖子信息
+     * @return 结果
+     */
+    public int updatePost(Post post);
+
+    /**
+     * 删除帖子信息
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int deletePostById(Long postId);
+
+    /**
+     * 批量删除帖子信息
+     * 
+     * @param postIds 需要删除的帖子ID
+     * @return 结果
+     */
+    public int deletePostByIds(Long[] postIds);
+
+    /**
+     * 更新帖子浏览量
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int updatePostViews(Long postId);
+
+    /**
+     * 更新帖子评论数
+     * 
+     * @param postId 帖子ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    public int updatePostComments(Long postId, int count);
+
+    /**
+     * 更新帖子收藏数
+     * 
+     * @param postId 帖子ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    public int updatePostFavorites(Long postId, int count);
+
+    /**
+     * 更新帖子点赞数
+     * 
+     * @param postId 帖子ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    public int updatePostLikes(Long postId, int count);
+
+    /**
+     * 检查并处理过期的推广帖子
+     * 
+     * @return 处理的帖子数量
+     */
+    public int checkAndHandleExpiredPromotions();
+
+    /**
+     * 取消帖子推广
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int cancelPostPromotion(Long postId);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostTagService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostTagService.java
new file mode 100644
index 0000000..1ec82eb
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/IPostTagService.java
@@ -0,0 +1,103 @@
+package com.ruoyi.web.controller.post.service;
+
+import java.util.List;
+
+import com.ruoyi.web.controller.post.domain.PostTag;
+
+/**
+ * 帖子标签 服务层
+ * 
+ * @author thunderhub
+ */
+public interface IPostTagService 
+{
+    /**
+     * 查询帖子标签信息
+     * 
+     * @param tagId 帖子标签ID
+     * @return 帖子标签信息
+     */
+    public PostTag selectPostTagById(Long tagId);
+
+    /**
+     * 查询帖子标签列表
+     * 
+     * @param postTag 帖子标签信息
+     * @return 帖子标签集合
+     */
+    public List<PostTag> selectPostTagList(PostTag postTag);
+
+    /**
+     * 根据帖子ID查询标签
+     * 
+     * @param postId 帖子ID
+     * @return 帖子标签集合
+     */
+    public List<PostTag> selectPostTagsByPostId(Long postId);
+
+    /**
+     * 新增帖子标签
+     * 
+     * @param postTag 帖子标签信息
+     * @return 结果
+     */
+    public int insertPostTag(PostTag postTag);
+
+    /**
+     * 修改帖子标签
+     * 
+     * @param postTag 帖子标签信息
+     * @return 结果
+     */
+    public int updatePostTag(PostTag postTag);
+
+    /**
+     * 删除帖子标签信息
+     * 
+     * @param tagId 帖子标签ID
+     * @return 结果
+     */
+    public int deletePostTagById(Long tagId);
+
+    /**
+     * 批量删除帖子标签信息
+     * 
+     * @param tagIds 需要删除的帖子标签ID
+     * @return 结果
+     */
+    public int deletePostTagByIds(Long[] tagIds);
+
+    /**
+     * 批量插入帖子标签关联
+     * 
+     * @param postId 帖子ID
+     * @param tagIds 标签ID数组
+     * @return 结果
+     */
+    public int batchInsertPostTagRelation(Long postId, Long[] tagIds);
+
+    /**
+     * 删除帖子标签关联
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int deletePostTagRelation(Long postId);
+
+    /**
+     * 更新标签帖子数量
+     * 
+     * @param tagId 标签ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    public int updatePostTagCount(Long tagId, int count);
+
+    /**
+     * 检查标签名称是否唯一
+     * 
+     * @param postTag 帖子标签信息
+     * @return 结果
+     */
+    public boolean checkTagNameUnique(PostTag postTag);
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostCommentServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostCommentServiceImpl.java
new file mode 100644
index 0000000..425d61f
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostCommentServiceImpl.java
@@ -0,0 +1,146 @@
+package com.ruoyi.web.controller.post.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.ruoyi.web.controller.post.domain.PostComment;
+import com.ruoyi.web.controller.post.mapper.PostCommentMapper;
+import com.ruoyi.web.controller.post.service.IPostCommentService;
+
+/**
+ * 帖子评论 服务层实现
+ * 
+ * @author thunderhub
+ */
+@Service
+public class PostCommentServiceImpl implements IPostCommentService
+{
+    @Autowired
+    private PostCommentMapper postCommentMapper;
+
+    /**
+     * 查询帖子评论信息
+     * 
+     * @param commentId 帖子评论ID
+     * @return 帖子评论信息
+     */
+    @Override
+    public PostComment selectPostCommentById(Long commentId)
+    {
+        return postCommentMapper.selectPostCommentById(commentId);
+    }
+
+    /**
+     * 查询帖子评论列表
+     * 
+     * @param postComment 帖子评论信息
+     * @return 帖子评论集合
+     */
+    @Override
+    public List<PostComment> selectPostCommentList(PostComment postComment)
+    {
+        return postCommentMapper.selectPostCommentList(postComment);
+    }
+
+    /**
+     * 根据帖子ID查询评论
+     * 
+     * @param postId 帖子ID
+     * @param limit 查询数量,0表示查询全部
+     * @return 帖子评论集合
+     */
+    @Override
+    public List<PostComment> selectCommentsByPostId(Long postId, int limit)
+    {
+        return postCommentMapper.selectCommentsByPostId(postId, limit);
+    }
+
+    /**
+     * 根据父评论ID查询回复
+     * 
+     * @param parentId 父评论ID
+     * @return 帖子评论集合
+     */
+    @Override
+    public List<PostComment> selectCommentsByParentId(Long parentId)
+    {
+        return postCommentMapper.selectCommentsByParentId(parentId);
+    }
+
+    /**
+     * 新增帖子评论
+     * 
+     * @param postComment 帖子评论信息
+     * @return 结果
+     */
+    @Override
+    public int insertPostComment(PostComment postComment)
+    {
+        return postCommentMapper.insertPostComment(postComment);
+    }
+
+    /**
+     * 修改帖子评论
+     * 
+     * @param postComment 帖子评论信息
+     * @return 结果
+     */
+    @Override
+    public int updatePostComment(PostComment postComment)
+    {
+        return postCommentMapper.updatePostComment(postComment);
+    }
+
+    /**
+     * 删除帖子评论信息
+     * 
+     * @param commentId 帖子评论ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostCommentById(Long commentId)
+    {
+        return postCommentMapper.deletePostCommentById(commentId);
+    }
+
+    /**
+     * 批量删除帖子评论信息
+     * 
+     * @param commentIds 需要删除的帖子评论ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostCommentByIds(Long[] commentIds)
+    {
+        return postCommentMapper.deletePostCommentByIds(commentIds);
+    }
+
+    /**
+     * 根据帖子ID删除评论
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostCommentByPostId(Long postId)
+    {
+        return postCommentMapper.deletePostCommentByPostId(postId);
+    }
+
+    /**
+     * 更新评论点赞数
+     * 
+     * @param commentId 评论ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    @Override
+    public int updateCommentLikes(Long commentId, int count)
+    {
+        PostComment comment = new PostComment();
+        comment.setCommentId(commentId);
+        comment.setLikes((long) count);
+        return postCommentMapper.updateCommentLikes(comment);
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostFavoriteServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostFavoriteServiceImpl.java
new file mode 100644
index 0000000..9249a94
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostFavoriteServiceImpl.java
@@ -0,0 +1,145 @@
+package com.ruoyi.web.controller.post.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.ruoyi.web.controller.post.domain.PostFavorite;
+import com.ruoyi.web.controller.post.mapper.PostFavoriteMapper;
+import com.ruoyi.web.controller.post.service.IPostFavoriteService;
+
+/**
+ * 帖子收藏 服务层实现
+ * 
+ * @author thunderhub
+ */
+@Service
+public class PostFavoriteServiceImpl implements IPostFavoriteService
+{
+    @Autowired
+    private PostFavoriteMapper postFavoriteMapper;
+
+    /**
+     * 查询帖子收藏信息
+     * 
+     * @param favoriteId 帖子收藏ID
+     * @return 帖子收藏信息
+     */
+    @Override
+    public PostFavorite selectPostFavoriteById(Long favoriteId)
+    {
+        return postFavoriteMapper.selectPostFavoriteById(favoriteId);
+    }
+
+    /**
+     * 查询帖子收藏列表
+     * 
+     * @param postFavorite 帖子收藏信息
+     * @return 帖子收藏集合
+     */
+    @Override
+    public List<PostFavorite> selectPostFavoriteList(PostFavorite postFavorite)
+    {
+        return postFavoriteMapper.selectPostFavoriteList(postFavorite);
+    }
+
+    /**
+     * 查询用户是否收藏帖子
+     * 
+     * @param postId 帖子ID
+     * @param userId 用户ID
+     * @return 收藏信息
+     */
+    @Override
+    public PostFavorite selectPostFavoriteByPostIdAndUserId(Long postId, Long userId)
+    {
+        return postFavoriteMapper.selectPostFavoriteByPostIdAndUserId(postId, userId);
+    }
+
+    /**
+     * 新增帖子收藏
+     * 
+     * @param postFavorite 帖子收藏信息
+     * @return 结果
+     */
+    @Override
+    public int insertPostFavorite(PostFavorite postFavorite)
+    {
+        return postFavoriteMapper.insertPostFavorite(postFavorite);
+    }
+
+    /**
+     * 修改帖子收藏
+     * 
+     * @param postFavorite 帖子收藏信息
+     * @return 结果
+     */
+    @Override
+    public int updatePostFavorite(PostFavorite postFavorite)
+    {
+        return postFavoriteMapper.updatePostFavorite(postFavorite);
+    }
+
+    /**
+     * 删除帖子收藏信息
+     * 
+     * @param favoriteId 帖子收藏ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostFavoriteById(Long favoriteId)
+    {
+        return postFavoriteMapper.deletePostFavoriteById(favoriteId);
+    }
+
+    /**
+     * 批量删除帖子收藏信息
+     * 
+     * @param favoriteIds 需要删除的帖子收藏ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostFavoriteByIds(Long[] favoriteIds)
+    {
+        return postFavoriteMapper.deletePostFavoriteByIds(favoriteIds);
+    }
+
+    /**
+     * 取消收藏帖子
+     * 
+     * @param postId 帖子ID
+     * @param userId 用户ID
+     * @return 结果
+     */
+    @Override
+    public int cancelPostFavorite(Long postId, Long userId)
+    {
+        return postFavoriteMapper.cancelPostFavorite(postId, userId);
+    }
+
+    /**
+     * 根据帖子ID删除收藏
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostFavoriteByPostId(Long postId)
+    {
+        return postFavoriteMapper.deletePostFavoriteByPostId(postId);
+    }
+
+    /**
+     * 根据用户ID查询收藏列表
+     * 
+     * @param userId 用户ID
+     * @return 收藏列表
+     */
+    @Override
+    public List<PostFavorite> selectPostFavoritesByUserId(Long userId)
+    {
+        PostFavorite postFavorite = new PostFavorite();
+        postFavorite.setUserId(userId);
+        return postFavoriteMapper.selectPostFavoriteList(postFavorite);
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostServiceImpl.java
new file mode 100644
index 0000000..c619f96
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostServiceImpl.java
@@ -0,0 +1,311 @@
+package com.ruoyi.web.controller.post.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.web.controller.post.mapper.PostContentMapper;
+import com.ruoyi.web.controller.post.mapper.PostPaymentMapper;
+import com.ruoyi.web.controller.post.mapper.PostPromotionPlanMapper;
+import com.ruoyi.web.controller.post.domain.Post;
+import com.ruoyi.web.controller.post.domain.PostPayment;
+import com.ruoyi.web.controller.post.domain.PostPromotionPlan;
+import com.ruoyi.web.controller.post.service.IPostService;
+
+import java.util.Date;
+import java.util.Calendar;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+
+/**
+ * 帖子Service业务层处理
+ * 
+ * @author thunderhub
+ */
+@Service
+public class PostServiceImpl implements IPostService
+{
+    @Autowired
+    private PostContentMapper postMapper;
+
+    @Autowired
+    private PostPaymentMapper postPaymentMapper;
+
+    @Autowired
+    private PostPromotionPlanMapper postPromotionPlanMapper;
+
+    /**
+     * 查询帖子信息
+     * 
+     * @param postId 帖子ID
+     * @return 帖子信息
+     */
+    @Override
+    public Post selectPostById(Long postId)
+    {
+        return postMapper.selectPostById(postId);
+    }
+
+    /**
+     * 查询帖子列表
+     * 
+     * @param post 帖子信息
+     * @return 帖子集合
+     */
+    @Override
+    public List<Post> selectPostList(Post post)
+    {
+        return postMapper.selectPostList(post);
+    }
+
+    /**
+     * 查询作者的其他帖子
+     * 
+     * @param authorId 作者ID
+     * @param postId 当前帖子ID(排除)
+     * @param limit 查询数量
+     * @return 帖子集合
+     */
+    @Override
+    public List<Post> selectAuthorOtherPosts(Long authorId, Long postId, int limit)
+    {
+        return postMapper.selectAuthorOtherPosts(authorId, postId, limit);
+    }
+
+    /**
+     * 查询相似标签的帖子
+     * 
+     * @param tags 标签列表
+     * @param postId 当前帖子ID(排除)
+     * @param limit 查询数量
+     * @return 帖子集合
+     */
+    @Override
+    public List<Post> selectSimilarTagsPosts(String tags, Long postId, int limit)
+    {
+        if (tags == null || tags.trim().isEmpty()) {
+            return List.of();
+        }
+        
+        return postMapper.selectSimilarTagsPosts(tags, postId, limit);
+    }
+
+    /**
+     * 新增帖子
+     * 
+     * @param post 帖子信息
+     * @return 结果
+     */
+    @Override
+    public int insertPost(Post post)
+    {
+        return postMapper.insertPost(post);
+    }
+
+    /**
+     * 修改帖子
+     * 
+     * @param post 帖子信息
+     * @return 结果
+     */
+    @Override
+    public int updatePost(Post post)
+    {
+        return postMapper.updatePost(post);
+    }
+
+    /**
+     * 删除帖子信息
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostById(Long postId)
+    {
+        return postMapper.deletePostById(postId);
+    }
+
+    /**
+     * 批量删除帖子信息
+     * 
+     * @param postIds 需要删除的帖子ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostByIds(Long[] postIds)
+    {
+        return postMapper.deletePostByIds(postIds);
+    }
+
+    /**
+     * 更新帖子浏览量
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    @Override
+    public int updatePostViews(Long postId)
+    {
+        return postMapper.updatePostViews(postId);
+    }
+
+    /**
+     * 更新帖子评论数
+     * 
+     * @param postId 帖子ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    @Override
+    public int updatePostComments(Long postId, int count)
+    {
+        Post post = new Post();
+        post.setPostId(postId);
+        post.setComments((long) count);
+        return postMapper.updatePostComments(post);
+    }
+
+    /**
+     * 更新帖子收藏数
+     * 
+     * @param postId 帖子ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    @Override
+    public int updatePostFavorites(Long postId, int count)
+    {
+        Post post = new Post();
+        post.setPostId(postId);
+        post.setFavorites((long) count);
+        return postMapper.updatePostFavorites(post);
+    }
+
+    /**
+     * 更新帖子点赞数
+     * 
+     * @param postId 帖子ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    @Override
+    public int updatePostLikes(Long postId, int count)
+    {
+        Post post = new Post();
+        post.setPostId(postId);
+        post.setLikes((long) count);
+        return postMapper.updatePostLikes(post);
+    }
+
+    /**
+     * 检查并处理过期的推广帖子
+     * 
+     * @return 处理的帖子数量
+     */
+    @Override
+    public int checkAndHandleExpiredPromotions()
+    {
+        Logger logger = LoggerFactory.getLogger(PostServiceImpl.class);
+        int processedCount = 0;
+        
+        try {
+            // 查询所有过期的推广支付记录
+            List<PostPayment> expiredPayments = postPaymentMapper.selectExpiredPromotionPayments();
+            
+            for (PostPayment payment : expiredPayments) {
+                // 获取推广计划信息
+                PostPromotionPlan plan = postPromotionPlanMapper.selectPostPromotionPlanById(payment.getPlanId());
+                if (plan == null) {
+                    continue;
+                }
+                
+                // 计算推广到期时间
+                Calendar cal = Calendar.getInstance();
+                cal.setTime(payment.getPaymentTime());
+                cal.add(Calendar.DAY_OF_MONTH, plan.getDuration());
+                Date expireTime = cal.getTime();
+                
+                // 检查是否已过期
+                if (new Date().after(expireTime)) {
+                    // 取消帖子推广
+                    int result = cancelPostPromotion(payment.getPostId());
+                    if (result > 0) {
+                        processedCount++;
+                        logger.info("帖子ID: {} 的推广已过期,已自动取消", payment.getPostId());
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logger.error("检查推广时效性时发生错误", e);
+        }
+        
+        return processedCount;
+    }
+
+    /**
+     * 取消帖子推广
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    @Override
+    public int cancelPostPromotion(Long postId)
+    {
+        Post post = new Post();
+        post.setPostId(postId);
+        post.setPromotionPlanId(null);
+        return postMapper.updatePost(post);
+    }
+
+    /**
+     * 获取帖子推荐列表(包含作者其他帖子和相似标签帖子)
+     * 
+     * @param authorId 作者ID
+     * @param tags 标签列表
+     * @param postId 当前帖子ID(排除)
+     * @param maxCount 最大推荐数量
+     * @return 推荐帖子集合
+     */
+    @Override
+    public List<Post> getRecommendedPosts(Long authorId, String tags, Long postId, int maxCount)
+    {
+        List<Post> recommendedPosts = new ArrayList<>();
+        
+        // 1. 获取作者的其他帖子(最多5个)
+        List<Post> authorPosts = selectAuthorOtherPosts(authorId, postId, 5);
+        recommendedPosts.addAll(authorPosts);
+        
+        // 2. 如果作者帖子不足maxCount个,则补充相似标签的帖子
+        if (recommendedPosts.size() < maxCount && tags != null && !tags.trim().isEmpty()) {
+            int remainingCount = maxCount - recommendedPosts.size();
+            List<Post> similarPosts = selectSimilarTagsPosts(tags, postId, remainingCount + 5); // 多查询一些以便去重
+            
+            // 去重:排除已经在推荐列表中的帖子
+            for (Post similarPost : similarPosts) {
+                if (recommendedPosts.size() >= maxCount) {
+                    break;
+                }
+                
+                boolean isDuplicate = false;
+                for (Post existingPost : recommendedPosts) {
+                    if (existingPost.getPostId().equals(similarPost.getPostId())) {
+                        isDuplicate = true;
+                        break;
+                    }
+                }
+                
+                if (!isDuplicate) {
+                    recommendedPosts.add(similarPost);
+                }
+            }
+        }
+        
+        // 3. 限制最大数量
+        if (recommendedPosts.size() > maxCount) {
+            recommendedPosts = recommendedPosts.subList(0, maxCount);
+        }
+        
+        return recommendedPosts;
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostTagServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostTagServiceImpl.java
new file mode 100644
index 0000000..3be6dcc
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/service/impl/PostTagServiceImpl.java
@@ -0,0 +1,176 @@
+package com.ruoyi.web.controller.post.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.ruoyi.web.controller.post.domain.PostTag;
+import com.ruoyi.web.controller.post.domain.PostTagRelation;
+import com.ruoyi.web.controller.post.mapper.PostContentTagMapper;
+import com.ruoyi.web.controller.post.service.IPostTagService;
+
+/**
+ * 帖子标签 服务层实现
+ * 
+ * @author thunderhub
+ */
+@Service
+public class PostTagServiceImpl implements IPostTagService
+{
+    @Autowired
+    private PostContentTagMapper postContentTagMapper;
+
+    /**
+     * 查询帖子标签信息
+     * 
+     * @param tagId 帖子标签ID
+     * @return 帖子标签信息
+     */
+    @Override
+    public PostTag selectPostTagById(Long tagId)
+    {
+        return postContentTagMapper.selectPostTagById(tagId);
+    }
+
+    /**
+     * 查询帖子标签列表
+     * 
+     * @param postTag 帖子标签信息
+     * @return 帖子标签集合
+     */
+    @Override
+    public List<PostTag> selectPostTagList(PostTag postTag)
+    {
+        return postContentTagMapper.selectPostTagList(postTag);
+    }
+
+    /**
+     * 根据帖子ID查询标签
+     * 
+     * @param postId 帖子ID
+     * @return 帖子标签集合
+     */
+    @Override
+    public List<PostTag> selectPostTagsByPostId(Long postId)
+    {
+        return postContentTagMapper.selectPostTagsByPostId(postId);
+    }
+
+    /**
+     * 新增帖子标签
+     * 
+     * @param postTag 帖子标签信息
+     * @return 结果
+     */
+    @Override
+    public int insertPostTag(PostTag postTag)
+    {
+        return postContentTagMapper.insertPostTag(postTag);
+    }
+
+    /**
+     * 修改帖子标签
+     * 
+     * @param postTag 帖子标签信息
+     * @return 结果
+     */
+    @Override
+    public int updatePostTag(PostTag postTag)
+    {
+        return postContentTagMapper.updatePostTag(postTag);
+    }
+
+    /**
+     * 删除帖子标签信息
+     * 
+     * @param tagId 帖子标签ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostTagById(Long tagId)
+    {
+        return postContentTagMapper.deletePostTagById(tagId);
+    }
+
+    /**
+     * 批量删除帖子标签信息
+     * 
+     * @param tagIds 需要删除的帖子标签ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostTagByIds(Long[] tagIds)
+    {
+        return postContentTagMapper.deletePostTagByIds(tagIds);
+    }
+
+    /**
+     * 批量插入帖子标签关联
+     * 
+     * @param postId 帖子ID
+     * @param tagIds 标签ID数组
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int batchInsertPostTagRelation(Long postId, Long[] tagIds)
+    {
+        List<PostTagRelation> list = new ArrayList<PostTagRelation>();
+        for (Long tagId : tagIds)
+        {
+            PostTagRelation relation = new PostTagRelation();
+            relation.setPostId(postId);
+            relation.setTagId(tagId);
+            list.add(relation);
+        }
+        return postContentTagMapper.batchInsertPostTagRelation(list);
+    }
+
+    /**
+     * 删除帖子标签关联
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostTagRelation(Long postId)
+    {
+        return postContentTagMapper.deletePostTagRelation(postId);
+    }
+
+    /**
+     * 更新标签帖子数量
+     * 
+     * @param tagId 标签ID
+     * @param count 增加/减少的数量
+     * @return 结果
+     */
+    @Override
+    public int updatePostTagCount(Long tagId, int count)
+    {
+        PostTag postTag = new PostTag();
+        postTag.setTagId(tagId);
+        postTag.setPostCount((long) count);
+        return postContentTagMapper.updatePostTagCount(postTag);
+    }
+
+    /**
+     * 检查标签名称是否唯一
+     * 
+     * @param postTag 帖子标签信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkTagNameUnique(PostTag postTag)
+    {
+        Long tagId = postTag.getTagId() == null ? -1L : postTag.getTagId();
+        PostTag info = postContentTagMapper.checkTagNameUnique(postTag.getTagName());
+        if (info != null && info.getTagId().longValue() != tagId.longValue())
+        {
+            return false;
+        }
+        return true;
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/task/PromotionExpiryTask.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/task/PromotionExpiryTask.java
new file mode 100644
index 0000000..39855e7
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/task/PromotionExpiryTask.java
@@ -0,0 +1,49 @@
+package com.ruoyi.web.controller.post.task;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import com.ruoyi.web.controller.post.service.IPostService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 推广时效性检查定时任务
+ * 
+ * @author thunderhub
+ */
+@Component
+public class PromotionExpiryTask
+{
+    private static final Logger logger = LoggerFactory.getLogger(PromotionExpiryTask.class);
+
+    @Autowired
+    private IPostService postService;
+
+    /**
+     * 检查推广时效性
+     * 每小时执行一次
+     */
+    @Scheduled(cron = "0 0 * * * ?")
+    public void checkPromotionExpiry()
+    {
+        logger.info("开始检查推广时效性...");
+        
+        try {
+            int processedCount = postService.checkAndHandleExpiredPromotions();
+            logger.info("推广时效性检查完成,处理了 {} 个过期推广", processedCount);
+        } catch (Exception e) {
+            logger.error("推广时效性检查失败", e);
+        }
+    }
+
+    /**
+     * 手动触发推广时效性检查
+     * 用于测试或手动执行
+     */
+    public void manualCheckPromotionExpiry()
+    {
+        logger.info("手动触发推广时效性检查...");
+        checkPromotionExpiry();
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml
index ffe92e5..1eb2c2f 100644
--- a/ruoyi-admin/src/main/resources/application-druid.yml
+++ b/ruoyi-admin/src/main/resources/application-druid.yml
@@ -6,9 +6,9 @@
         druid:
             # 主库数据源
             master:
-                url: jdbc:mysql://rm-bp1v264vgxpa95ferbo.mysql.rds.aliyuncs.com:3306/jrx?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                username: root
-                password: Jrx5201314
+                url: jdbc:mysql://202.205.102.121:3306/ThunderHub?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                username: team4
+                password: woaizhaoyutao
             # 从库数据源
             slave:
                 # 从数据源开关/默认关闭
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 0333eba..f80b3fd 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -1,15 +1,18 @@
 # 项目相关配置
 ruoyi:
   # 名称
-  name: TdHub
+  name: ThunderHub
   # 版本
-  version: 3.8.8
+  version: 1.0.0
   # 版权年份
   copyrightYear: 2024
-  profile: ./download
+  # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
+  profile: ./upload
+  # 前端项目图片路径(相对于后端项目根目录)
+  frontendImagePath: ../ThunderHubWeb/public/images
   # 获取ip地址开关
   addressEnabled: false
-  # 验证码类型 math 数字计算 char 字符验证
+  # 验证码类型 math 数组计算 char 字符验证
   captchaType: math
 
 # 开发环境配置
@@ -52,6 +55,12 @@
     basename: i18n/messages
   profiles:
     active: druid
+  # 静态资源映射
+  mvc:
+    static-path-pattern: /static/**
+  web:
+    resources:
+      static-locations: classpath:/static/
   # 文件上传
   servlet:
     multipart:
diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml
index a360583..a8c526e 100644
--- a/ruoyi-admin/src/main/resources/logback.xml
+++ b/ruoyi-admin/src/main/resources/logback.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <configuration>
     <!-- 日志存放路径 -->
-	<property name="log.path" value="/home/ruoyi/logs" />
+	<property name="log.path" value="./logs" />
     <!-- 日志输出格式 -->
 	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
 
diff --git a/ruoyi-admin/src/main/resources/mapper/post/PostCommentMapper.xml b/ruoyi-admin/src/main/resources/mapper/post/PostCommentMapper.xml
new file mode 100644
index 0000000..537dd4c
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/mapper/post/PostCommentMapper.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.web.controller.post.mapper.PostCommentMapper">
+    
+    <resultMap type="PostComment" id="PostCommentResult">
+        <id     property="commentId"     column="comment_id"     />
+        <result property="postId"        column="post_id"        />
+        <result property="content"       column="content"        />
+        <result property="userId"        column="user_id"        />
+        <result property="userName"      column="user_name"      />
+        <result property="userAvatar"    column="user_avatar"    />
+        <result property="parentId"      column="parent_id"      />
+        <result property="replyUserId"   column="reply_user_id"  />
+        <result property="replyUserName" column="reply_user_name"/>
+        <result property="status"        column="status"         />
+        <result property="likes"         column="likes"          />
+        <result property="createBy"      column="create_by"      />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"      column="update_by"      />
+        <result property="updateTime"    column="update_time"    />
+        <result property="remark"        column="remark"         />
+    </resultMap>
+    
+    <sql id="selectPostCommentVo">
+        select comment_id, post_id, content, user_id, user_name, user_avatar, parent_id, reply_user_id, reply_user_name, status, likes,
+            create_by, create_time, update_by, update_time, remark
+        from post_comment
+    </sql>
+    
+    <select id="selectPostCommentList" parameterType="PostComment" resultMap="PostCommentResult">
+        <include refid="selectPostCommentVo"/>
+        <where>
+            <if test="postId != null">
+                AND post_id = #{postId}
+            </if>
+            <if test="userId != null">
+                AND user_id = #{userId}
+            </if>
+            <if test="userName != null  and userName != ''">
+                AND user_name like concat('%', #{userName}, '%')
+            </if>
+            <if test="parentId != null">
+                AND parent_id = #{parentId}
+            </if>
+            <if test="status != null  and status != ''">
+                AND status = #{status}
+            </if>
+        </where>
+        order by create_time desc
+    </select>
+    
+    <select id="selectPostCommentById" parameterType="Long" resultMap="PostCommentResult">
+        <include refid="selectPostCommentVo"/>
+        where comment_id = #{commentId}
+    </select>
+    
+    <select id="selectCommentsByPostId" resultMap="PostCommentResult">
+        <include refid="selectPostCommentVo"/>
+        where post_id = #{postId} and parent_id = 0 and status = '1'
+        order by create_time desc
+        <if test="limit > 0">
+            limit #{limit}
+        </if>
+    </select>
+    
+    <select id="selectCommentsByParentId" parameterType="Long" resultMap="PostCommentResult">
+        <include refid="selectPostCommentVo"/>
+        where parent_id = #{parentId} and status = '1'
+        order by create_time asc
+    </select>
+    
+    <insert id="insertPostComment" parameterType="PostComment" useGeneratedKeys="true" keyProperty="commentId">
+        insert into post_comment (
+            post_id,
+            content,
+            user_id,
+            user_name,
+            user_avatar,
+            parent_id,
+            reply_user_id,
+            reply_user_name,
+            status,
+            likes,
+            create_by,
+            create_time,
+            update_by,
+            update_time,
+            remark
+        ) values (
+            #{postId},
+            #{content},
+            #{userId},
+            #{userName},
+            #{userAvatar},
+            #{parentId},
+            #{replyUserId},
+            #{replyUserName},
+            #{status},
+            #{likes},
+            #{createBy},
+            now(),
+            #{updateBy},
+            now(),
+            #{remark}
+        )
+    </insert>
+    
+    <update id="updatePostComment" parameterType="PostComment">
+        update post_comment
+        <set>
+            <if test="content != null and content != ''">content = #{content},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="likes != null">likes = #{likes},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            update_time = now(),
+            <if test="remark != null">remark = #{remark},</if>
+        </set>
+        where comment_id = #{commentId}
+    </update>
+    
+    <delete id="deletePostCommentById" parameterType="Long">
+        delete from post_comment where comment_id = #{commentId}
+    </delete>
+    
+    <delete id="deletePostCommentByIds" parameterType="Long">
+        delete from post_comment where comment_id in 
+        <foreach collection="array" item="commentId" open="(" separator="," close=")">
+            #{commentId}
+        </foreach>
+    </delete>
+    
+    <delete id="deletePostCommentByPostId" parameterType="Long">
+        delete from post_comment where post_id = #{postId}
+    </delete>
+    
+    <update id="updateCommentLikes" parameterType="PostComment">
+        update post_comment set likes = likes + #{likes} where comment_id = #{commentId}
+    </update>
+    
+</mapper> 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/mapper/post/PostFavoriteMapper.xml b/ruoyi-admin/src/main/resources/mapper/post/PostFavoriteMapper.xml
new file mode 100644
index 0000000..a79ee9b
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/mapper/post/PostFavoriteMapper.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.web.controller.post.mapper.PostFavoriteMapper">
+    
+    <resultMap type="PostFavorite" id="PostFavoriteResult">
+        <id     property="favoriteId"   column="favorite_id"   />
+        <result property="postId"       column="post_id"       />
+        <result property="userId"       column="user_id"       />
+        <result property="postTitle"    column="post_title"    />
+        <result property="postCover"    column="post_cover"    />
+        <result property="status"       column="status"        />
+        <result property="createBy"     column="create_by"     />
+        <result property="createTime"   column="create_time"   />
+        <result property="updateBy"     column="update_by"     />
+        <result property="updateTime"   column="update_time"   />
+        <result property="remark"       column="remark"        />
+    </resultMap>
+    
+    <sql id="selectPostFavoriteVo">
+        select favorite_id, post_id, user_id, post_title, post_cover, status,
+            create_by, create_time, update_by, update_time, remark
+        from post_favorite
+    </sql>
+    
+    <select id="selectPostFavoriteList" parameterType="PostFavorite" resultMap="PostFavoriteResult">
+        <include refid="selectPostFavoriteVo"/>
+        <where>
+            <if test="postId != null">
+                AND post_id = #{postId}
+            </if>
+            <if test="userId != null">
+                AND user_id = #{userId}
+            </if>
+            <if test="status != null and status != ''">
+                AND status = #{status}
+            </if>
+        </where>
+        order by create_time desc
+    </select>
+    
+    <select id="selectPostFavoriteById" parameterType="Long" resultMap="PostFavoriteResult">
+        <include refid="selectPostFavoriteVo"/>
+        where favorite_id = #{favoriteId}
+    </select>
+    
+    <select id="selectPostFavoriteByPostIdAndUserId" resultMap="PostFavoriteResult">
+        <include refid="selectPostFavoriteVo"/>
+        where post_id = #{postId} and user_id = #{userId}
+    </select>
+    
+    <insert id="insertPostFavorite" parameterType="PostFavorite" useGeneratedKeys="true" keyProperty="favoriteId">
+        insert into post_favorite (
+            post_id,
+            user_id,
+            post_title,
+            post_cover,
+            status,
+            create_by,
+            create_time,
+            update_by,
+            update_time,
+            remark
+        ) values (
+            #{postId},
+            #{userId},
+            #{postTitle},
+            #{postCover},
+            #{status},
+            #{createBy},
+            now(),
+            #{updateBy},
+            now(),
+            #{remark}
+        )
+    </insert>
+    
+    <update id="updatePostFavorite" parameterType="PostFavorite">
+        update post_favorite
+        <set>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            update_time = now(),
+            <if test="remark != null">remark = #{remark},</if>
+        </set>
+        where favorite_id = #{favoriteId}
+    </update>
+    
+    <delete id="deletePostFavoriteById" parameterType="Long">
+        delete from post_favorite where favorite_id = #{favoriteId}
+    </delete>
+    
+    <delete id="deletePostFavoriteByIds" parameterType="Long">
+        delete from post_favorite where favorite_id in 
+        <foreach collection="array" item="favoriteId" open="(" separator="," close=")">
+            #{favoriteId}
+        </foreach>
+    </delete>
+    
+    <delete id="deletePostFavoriteByPostId" parameterType="Long">
+        delete from post_favorite where post_id = #{postId}
+    </delete>
+    
+    <update id="cancelPostFavorite">
+        update post_favorite set status = '1', update_time = now() 
+        where post_id = #{postId} and user_id = #{userId}
+    </update>
+    
+</mapper> 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/mapper/post/PostMapper.xml b/ruoyi-admin/src/main/resources/mapper/post/PostMapper.xml
new file mode 100644
index 0000000..5254e6f
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/mapper/post/PostMapper.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.web.controller.post.mapper.PostContentMapper">
+    
+    <resultMap type="Post" id="PostResult">
+        <id     property="postId"          column="post_id"          />
+        <result property="title"           column="title"            />
+        <result property="content"         column="content"          />
+        <result property="summary"         column="summary"          />
+        <result property="coverImage"      column="cover_image"      />
+        <result property="authorId"        column="author_id"        />
+        <result property="author"          column="author"           />
+        <result property="views"           column="views"            />
+        <result property="comments"        column="comments"         />
+        <result property="favorites"       column="favorites"        />
+        <result property="likes"           column="likes"            />
+        <result property="status"          column="status"           />
+        <result property="publishTime"     column="publish_time"     />
+        <result property="tags"            column="tags"             />
+        <result property="promotionPlanId" column="promotion_plan_id"/>
+        <result property="createBy"        column="create_by"        />
+        <result property="createTime"      column="create_time"      />
+        <result property="updateBy"        column="update_by"        />
+        <result property="updateTime"      column="update_time"      />
+        <result property="remark"          column="remark"           />
+    </resultMap>
+    
+    <sql id="selectPostVo">
+        select post_id, title, content, summary, cover_image, author_id, author, views, comments, favorites, likes, 
+               status, publish_time, tags, promotion_plan_id, create_by, create_time, update_by, update_time, remark
+        from post
+    </sql>
+    
+    <select id="selectPostList" parameterType="Post" resultMap="PostResult">
+        <include refid="selectPostVo"/>
+        <where>
+            <if test="title != null and title != ''">
+                AND title like concat('%', #{title}, '%')
+            </if>
+            <if test="author != null and author != ''">
+                AND author like concat('%', #{author}, '%')
+            </if>
+            <if test="status != null and status != ''">
+                AND status = #{status}
+            </if>
+            <if test="tags != null and tags != ''">
+                AND tags like concat('%', #{tags}, '%')
+            </if>
+            <if test="authorId != null">
+                AND author_id = #{authorId}
+            </if>
+        </where>
+        order by 
+            case when promotion_plan_id is not null then 0 else 1 end,
+            create_time desc
+    </select>
+    
+    <select id="selectPostById" parameterType="Long" resultMap="PostResult">
+        <include refid="selectPostVo"/>
+        where post_id = #{postId}
+    </select>
+    
+    <select id="selectAuthorOtherPosts" resultMap="PostResult">
+        <include refid="selectPostVo"/>
+        where author_id = #{authorId} and post_id != #{postId} and status = '1'
+        order by create_time desc
+        limit #{limit}
+    </select>
+    
+    <select id="selectSimilarTagsPosts" resultMap="PostResult">
+        <include refid="selectPostVo"/>
+        <where>
+            post_id != #{postId} and status = '1'
+            <if test="tags != null and tags != ''">
+                AND tags like concat('%', #{tags}, '%')
+            </if>
+        </where>
+        order by create_time desc
+        limit #{limit}
+    </select>
+    
+    <insert id="insertPost" parameterType="Post" useGeneratedKeys="true" keyProperty="postId">
+        insert into post (
+            title,
+            content,
+            summary,
+            cover_image,
+            author_id,
+            author,
+            views,
+            comments,
+            favorites,
+            likes,
+            status,
+            publish_time,
+            tags,
+            promotion_plan_id,
+            create_by,
+            create_time,
+            update_by,
+            update_time,
+            remark
+        ) values (
+            #{title},
+            #{content},
+            #{summary},
+            #{coverImage},
+            #{authorId},
+            #{author},
+            #{views},
+            #{comments},
+            #{favorites},
+            #{likes},
+            #{status},
+            #{publishTime},
+            #{tags},
+            #{promotionPlanId},
+            #{createBy},
+            now(),
+            #{updateBy},
+            now(),
+            #{remark}
+        )
+    </insert>
+    
+    <update id="updatePost" parameterType="Post">
+        update post
+        <set>
+            <if test="title != null and title != ''">title = #{title},</if>
+            <if test="content != null">content = #{content},</if>
+            <if test="summary != null">summary = #{summary},</if>
+            <if test="coverImage != null">cover_image = #{coverImage},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="tags != null">tags = #{tags},</if>
+            <if test="promotionPlanId != null">promotion_plan_id = #{promotionPlanId},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            update_time = now(),
+            <if test="remark != null">remark = #{remark},</if>
+        </set>
+        where post_id = #{postId}
+    </update>
+    
+    <delete id="deletePostById" parameterType="Long">
+        delete from post where post_id = #{postId}
+    </delete>
+    
+    <delete id="deletePostByIds" parameterType="Long">
+        delete from post where post_id in 
+        <foreach collection="array" item="postId" open="(" separator="," close=")">
+            #{postId}
+        </foreach>
+    </delete>
+    
+    <update id="updatePostViews" parameterType="Long">
+        update post set views = views + 1 where post_id = #{postId}
+    </update>
+    
+    <update id="updatePostComments" parameterType="Post">
+        update post set comments = comments + #{comments} where post_id = #{postId}
+    </update>
+    
+    <update id="updatePostFavorites" parameterType="Post">
+        update post set favorites = favorites + #{favorites} where post_id = #{postId}
+    </update>
+    
+    <update id="updatePostLikes" parameterType="Post">
+        update post set likes = likes + #{likes} where post_id = #{postId}
+    </update>
+    
+</mapper> 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/mapper/post/PostPaymentMapper.xml b/ruoyi-admin/src/main/resources/mapper/post/PostPaymentMapper.xml
new file mode 100644
index 0000000..c12dc2e
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/mapper/post/PostPaymentMapper.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.web.controller.post.mapper.PostPaymentMapper">
+    
+    <resultMap type="com.ruoyi.web.controller.post.domain.PostPayment" id="PostPaymentResult">
+        <id     property="paymentId"       column="payment_id"       />
+        <result property="postId"          column="post_id"          />
+        <result property="planId"          column="plan_id"          />
+        <result property="userId"          column="user_id"          />
+        <result property="amount"          column="amount"           />
+        <result property="paymentStatus"   column="payment_status"   />
+        <result property="paymentTime"     column="payment_time"     />
+    </resultMap>
+    
+    <sql id="selectPostPaymentVo">
+        select payment_id, post_id, plan_id, user_id, amount, payment_status, payment_time
+        from post_payment
+    </sql>
+    
+    <select id="selectPostPaymentList" parameterType="com.ruoyi.web.controller.post.domain.PostPayment" resultMap="PostPaymentResult">
+        <include refid="selectPostPaymentVo"/>
+        <where>
+            <if test="postId != null">
+                AND post_id = #{postId}
+            </if>
+            <if test="planId != null">
+                AND plan_id = #{planId}
+            </if>
+            <if test="userId != null">
+                AND user_id = #{userId}
+            </if>
+            <if test="paymentStatus != null and paymentStatus != ''">
+                AND payment_status = #{paymentStatus}
+            </if>
+        </where>
+        order by payment_time desc
+    </select>
+    
+    <select id="selectPostPaymentById" parameterType="Long" resultMap="PostPaymentResult">
+        <include refid="selectPostPaymentVo"/>
+        where payment_id = #{paymentId}
+    </select>
+    
+    <select id="selectLatestPaymentByPostId" parameterType="Long" resultMap="PostPaymentResult">
+        <include refid="selectPostPaymentVo"/>
+        where post_id = #{postId} and payment_status = 'paid'
+        order by payment_time desc
+        limit 1
+    </select>
+    
+    <select id="selectExpiredPromotionPayments" resultMap="PostPaymentResult">
+        <include refid="selectPostPaymentVo"/>
+        where payment_status = 'paid' 
+        and post_id in (
+            select post_id from post where promotion_plan_id is not null
+        )
+        order by payment_time desc
+    </select>
+    
+    <insert id="insertPostPayment" parameterType="com.ruoyi.web.controller.post.domain.PostPayment" useGeneratedKeys="true" keyProperty="paymentId">
+        insert into post_payment (
+            post_id,
+            plan_id,
+            user_id,
+            amount,
+            payment_status,
+            payment_time
+        ) values (
+            #{postId},
+            #{planId},
+            #{userId},
+            #{amount},
+            #{paymentStatus},
+            #{paymentTime}
+        )
+    </insert>
+    
+    <update id="updatePostPayment" parameterType="com.ruoyi.web.controller.post.domain.PostPayment">
+        update post_payment
+        <set>
+            <if test="postId != null">post_id = #{postId},</if>
+            <if test="planId != null">plan_id = #{planId},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="amount != null">amount = #{amount},</if>
+            <if test="paymentStatus != null and paymentStatus != ''">payment_status = #{paymentStatus},</if>
+            <if test="paymentTime != null">payment_time = #{paymentTime},</if>
+        </set>
+        where payment_id = #{paymentId}
+    </update>
+    
+    <delete id="deletePostPaymentById" parameterType="Long">
+        delete from post_payment where payment_id = #{paymentId}
+    </delete>
+    
+    <delete id="deletePostPaymentByIds" parameterType="Long">
+        delete from post_payment where payment_id in 
+        <foreach collection="array" item="paymentId" open="(" separator="," close=")">
+            #{paymentId}
+        </foreach>
+    </delete>
+    
+</mapper> 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/mapper/post/PostPromotionPlanMapper.xml b/ruoyi-admin/src/main/resources/mapper/post/PostPromotionPlanMapper.xml
new file mode 100644
index 0000000..0d22833
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/mapper/post/PostPromotionPlanMapper.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.web.controller.post.mapper.PostPromotionPlanMapper">
+    
+    <resultMap type="com.ruoyi.web.controller.post.domain.PostPromotionPlan" id="PostPromotionPlanResult">
+        <id     property="planId"           column="plan_id"           />
+        <result property="planName"         column="plan_name"         />
+        <result property="planDescription"  column="plan_description"  />
+        <result property="price"            column="price"             />
+        <result property="duration"         column="duration"          />
+        <result property="level"            column="level"             />
+        <result property="status"           column="status"            />
+        <result property="createBy"         column="create_by"         />
+        <result property="createTime"       column="create_time"       />
+        <result property="updateBy"         column="update_by"         />
+        <result property="updateTime"       column="update_time"       />
+        <result property="remark"           column="remark"            />
+    </resultMap>
+    
+    <sql id="selectPostPromotionPlanVo">
+        select plan_id, plan_name, plan_description, price, duration, level, status, 
+               create_by, create_time, update_by, update_time, remark
+        from post_promotion_plan
+    </sql>
+    
+    <select id="selectPostPromotionPlanList" parameterType="com.ruoyi.web.controller.post.domain.PostPromotionPlan" resultMap="PostPromotionPlanResult">
+        <include refid="selectPostPromotionPlanVo"/>
+        <where>
+            <if test="planName != null and planName != ''">
+                AND plan_name like concat('%', #{planName}, '%')
+            </if>
+            <if test="status != null and status != ''">
+                AND status = #{status}
+            </if>
+        </where>
+        order by level asc, create_time desc
+    </select>
+    
+    <select id="selectPostPromotionPlanById" parameterType="Long" resultMap="PostPromotionPlanResult">
+        <include refid="selectPostPromotionPlanVo"/>
+        where plan_id = #{planId}
+    </select>
+    
+    <insert id="insertPostPromotionPlan" parameterType="com.ruoyi.web.controller.post.domain.PostPromotionPlan" useGeneratedKeys="true" keyProperty="planId">
+        insert into post_promotion_plan (
+            plan_name,
+            plan_description,
+            price,
+            duration,
+            level,
+            status,
+            create_by,
+            create_time,
+            remark
+        ) values (
+            #{planName},
+            #{planDescription},
+            #{price},
+            #{duration},
+            #{level},
+            #{status},
+            #{createBy},
+            sysdate(),
+            #{remark}
+        )
+    </insert>
+    
+    <update id="updatePostPromotionPlan" parameterType="com.ruoyi.web.controller.post.domain.PostPromotionPlan">
+        update post_promotion_plan
+        <set>
+            <if test="planName != null and planName != ''">plan_name = #{planName},</if>
+            <if test="planDescription != null">plan_description = #{planDescription},</if>
+            <if test="price != null">price = #{price},</if>
+            <if test="duration != null">duration = #{duration},</if>
+            <if test="level != null">level = #{level},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            update_time = sysdate()
+        </set>
+        where plan_id = #{planId}
+    </update>
+    
+    <delete id="deletePostPromotionPlanById" parameterType="Long">
+        delete from post_promotion_plan where plan_id = #{planId}
+    </delete>
+    
+    <delete id="deletePostPromotionPlanByIds" parameterType="Long">
+        delete from post_promotion_plan where plan_id in 
+        <foreach collection="array" item="planId" open="(" separator="," close=")">
+            #{planId}
+        </foreach>
+    </delete>
+    
+</mapper> 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/mapper/post/PostReportMapper.xml b/ruoyi-admin/src/main/resources/mapper/post/PostReportMapper.xml
new file mode 100644
index 0000000..fbbe021
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/mapper/post/PostReportMapper.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.web.controller.post.mapper.PostReportMapper">
+    
+    <resultMap type="com.ruoyi.web.controller.post.domain.PostReport" id="PostReportResult">
+        <id     property="reportId"         column="report_id"         />
+        <result property="postId"           column="post_id"           />
+        <result property="postTitle"        column="post_title"        />
+        <result property="reportUserId"     column="report_user_id"    />
+        <result property="reportUserName"   column="report_user_name"  />
+        <result property="reportReason"     column="report_reason"     />
+        <result property="status"           column="status"            />
+        <result property="handleResult"     column="handle_result"     />
+        <result property="handleTime"       column="handle_time"       />
+        <result property="handleBy"         column="handle_by"         />
+        <result property="createBy"         column="create_by"         />
+        <result property="createTime"       column="create_time"       />
+        <result property="updateBy"         column="update_by"         />
+        <result property="updateTime"       column="update_time"       />
+        <result property="remark"           column="remark"            />
+    </resultMap>
+    
+    <sql id="selectPostReportVo">
+        select report_id, post_id, post_title, report_user_id, report_user_name, report_reason, 
+               status, handle_result, handle_time, handle_by, create_by, create_time, 
+               update_by, update_time, remark
+        from post_report
+    </sql>
+    
+    <select id="selectPostReportList" parameterType="com.ruoyi.web.controller.post.domain.PostReport" resultMap="PostReportResult">
+        <include refid="selectPostReportVo"/>
+        <where>
+            <if test="postId != null">
+                AND post_id = #{postId}
+            </if>
+            <if test="reportUserId != null">
+                AND report_user_id = #{reportUserId}
+            </if>
+            <if test="status != null and status != ''">
+                AND status = #{status}
+            </if>
+            <if test="postTitle != null and postTitle != ''">
+                AND post_title like concat('%', #{postTitle}, '%')
+            </if>
+            <if test="reportUserName != null and reportUserName != ''">
+                AND report_user_name like concat('%', #{reportUserName}, '%')
+            </if>
+        </where>
+        order by create_time desc
+    </select>
+    
+    <select id="selectPostReportById" parameterType="Long" resultMap="PostReportResult">
+        <include refid="selectPostReportVo"/>
+        where report_id = #{reportId}
+    </select>
+    
+    <insert id="insertPostReport" parameterType="com.ruoyi.web.controller.post.domain.PostReport" useGeneratedKeys="true" keyProperty="reportId">
+        insert into post_report (
+            post_id,
+            post_title,
+            report_user_id,
+            report_user_name,
+            report_reason,
+            status,
+            create_by,
+            create_time
+        ) values (
+            #{postId},
+            #{postTitle},
+            #{reportUserId},
+            #{reportUserName},
+            #{reportReason},
+            #{status},
+            #{createBy},
+            sysdate()
+        )
+    </insert>
+    
+    <update id="updatePostReport" parameterType="com.ruoyi.web.controller.post.domain.PostReport">
+        update post_report
+        <set>
+            <if test="postId != null">post_id = #{postId},</if>
+            <if test="postTitle != null and postTitle != ''">post_title = #{postTitle},</if>
+            <if test="reportUserId != null">report_user_id = #{reportUserId},</if>
+            <if test="reportUserName != null and reportUserName != ''">report_user_name = #{reportUserName},</if>
+            <if test="reportReason != null and reportReason != ''">report_reason = #{reportReason},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="handleResult != null">handle_result = #{handleResult},</if>
+            <if test="handleTime != null">handle_time = #{handleTime},</if>
+            <if test="handleBy != null and handleBy != ''">handle_by = #{handleBy},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            update_time = sysdate()
+        </set>
+        where report_id = #{reportId}
+    </update>
+    
+    <delete id="deletePostReportById" parameterType="Long">
+        delete from post_report where report_id = #{reportId}
+    </delete>
+    
+    <delete id="deletePostReportByIds" parameterType="Long">
+        delete from post_report where report_id in 
+        <foreach collection="array" item="reportId" open="(" separator="," close=")">
+            #{reportId}
+        </foreach>
+    </delete>
+    
+</mapper> 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/mapper/post/PostTagMapper.xml b/ruoyi-admin/src/main/resources/mapper/post/PostTagMapper.xml
new file mode 100644
index 0000000..1060c0a
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/mapper/post/PostTagMapper.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.web.controller.post.mapper.PostContentTagMapper">
+    
+    <resultMap type="PostTag" id="PostTagResult">
+        <id     property="tagId"      column="tag_id"      />
+        <result property="tagName"    column="tag_name"    />
+        <result property="tagColor"   column="tag_color"   />
+        <result property="postCount"  column="post_count"  />
+        <result property="status"     column="status"      />
+        <result property="createBy"   column="create_by"   />
+        <result property="createTime" column="create_time" />
+        <result property="updateBy"   column="update_by"   />
+        <result property="updateTime" column="update_time" />
+        <result property="remark"     column="remark"      />
+    </resultMap>
+    
+    <resultMap type="PostTagRelation" id="PostTagRelationResult">
+        <id     property="id"         column="id"          />
+        <result property="postId"     column="post_id"     />
+        <result property="tagId"      column="tag_id"      />
+        <result property="createBy"   column="create_by"   />
+        <result property="createTime" column="create_time" />
+        <result property="updateBy"   column="update_by"   />
+        <result property="updateTime" column="update_time" />
+    </resultMap>
+    
+    <sql id="selectPostTagVo">
+        select tag_id, tag_name, tag_color, post_count, status,
+            create_by, create_time, update_by, update_time, remark
+        from post_tag
+    </sql>
+    
+    <select id="selectPostTagList" parameterType="PostTag" resultMap="PostTagResult">
+        <include refid="selectPostTagVo"/>
+        <where>
+            <if test="tagName != null and tagName != ''">
+                AND tag_name like concat('%', #{tagName}, '%')
+            </if>
+            <if test="status != null and status != ''">
+                AND status = #{status}
+            </if>
+        </where>
+        order by post_count desc, create_time desc
+    </select>
+    
+    <select id="selectPostTagById" parameterType="Long" resultMap="PostTagResult">
+        <include refid="selectPostTagVo"/>
+        where tag_id = #{tagId}
+    </select>
+    
+    <select id="selectPostTagsByPostId" parameterType="Long" resultMap="PostTagResult">
+        select t.tag_id, t.tag_name, t.tag_color, t.post_count, t.status,
+            t.create_by, t.create_time, t.update_by, t.update_time, t.remark
+        from post_tag t
+        inner join post_tag_relation r on t.tag_id = r.tag_id
+        where r.post_id = #{postId} and t.status = '0'
+    </select>
+    
+    <select id="checkTagNameUnique" parameterType="String" resultMap="PostTagResult">
+        <include refid="selectPostTagVo"/>
+        where tag_name = #{tagName}
+    </select>
+    
+    <insert id="insertPostTag" parameterType="PostTag" useGeneratedKeys="true" keyProperty="tagId">
+        insert into post_tag (
+            tag_name,
+            tag_color,
+            post_count,
+            status,
+            create_by,
+            create_time,
+            update_by,
+            update_time,
+            remark
+        ) values (
+            #{tagName},
+            #{tagColor},
+            #{postCount},
+            #{status},
+            #{createBy},
+            now(),
+            #{updateBy},
+            now(),
+            #{remark}
+        )
+    </insert>
+    
+    <update id="updatePostTag" parameterType="PostTag">
+        update post_tag
+        <set>
+            <if test="tagName != null and tagName != ''">tag_name = #{tagName},</if>
+            <if test="tagColor != null">tag_color = #{tagColor},</if>
+            <if test="postCount != null">post_count = #{postCount},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            update_time = now(),
+            <if test="remark != null">remark = #{remark},</if>
+        </set>
+        where tag_id = #{tagId}
+    </update>
+    
+    <delete id="deletePostTagById" parameterType="Long">
+        delete from post_tag where tag_id = #{tagId}
+    </delete>
+    
+    <delete id="deletePostTagByIds" parameterType="Long">
+        delete from post_tag where tag_id in 
+        <foreach collection="array" item="tagId" open="(" separator="," close=")">
+            #{tagId}
+        </foreach>
+    </delete>
+    
+    <update id="updatePostTagCount" parameterType="PostTag">
+        update post_tag set post_count = post_count + #{postCount} where tag_id = #{tagId}
+    </update>
+    
+    <!-- 帖子标签关联表操作 -->
+    <insert id="insertPostTagRelation" parameterType="com.ruoyi.web.controller.post.domain.PostTagRelation">
+        insert into post_tag_relation (post_id, tag_id) values (#{postId}, #{tagId})
+    </insert>
+    
+    <insert id="batchInsertPostTagRelation" parameterType="java.util.List">
+        insert into post_tag_relation (post_id, tag_id) values
+        <foreach collection="list" item="item" separator=",">
+            (#{item.postId}, #{item.tagId})
+        </foreach>
+    </insert>
+    
+    <delete id="deletePostTagRelation" parameterType="Long">
+        delete from post_tag_relation where post_id = #{postId}
+    </delete>
+    
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCenterControllerTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCenterControllerTest.java
new file mode 100644
index 0000000..880b0fc
--- /dev/null
+++ b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCenterControllerTest.java
@@ -0,0 +1,537 @@
+package com.ruoyi.web.controller.post;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.web.controller.post.controller.PostCenterController;
+import com.ruoyi.web.controller.post.domain.*;
+import com.ruoyi.web.controller.post.service.*;
+import com.ruoyi.web.controller.post.mapper.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+/**
+ * PostCenterController 单元测试
+ * 
+ * 测试覆盖所有帖子中心相关的API接口
+ * 包括:帖子列表、详情、评论、收藏、点赞、发布、上传等功能
+ */
+@ExtendWith(MockitoExtension.class)
+@WithMockUser(username = "testUser", authorities = {"admin"})
+class PostCenterControllerTest {
+
+    @Mock
+    private IPostService postService;
+
+    @Mock
+    private IPostTagService postTagService;
+
+    @Mock
+    private IPostCommentService postCommentService;
+
+    @Mock
+    private IPostFavoriteService postFavoriteService;
+
+    @Mock
+    private PostReportMapper postReportMapper;
+
+    @Mock
+    private PostPaymentMapper postPaymentMapper;
+
+    @InjectMocks
+    private PostCenterController postCenterController;
+
+    private MockMvc mockMvc;
+    private ObjectMapper objectMapper;
+
+    @BeforeEach
+    void setUp() {
+        // 创建模拟的安全上下文
+        SysUser sysUser = new SysUser();
+        sysUser.setUserId(1L);
+        sysUser.setUserName("testUser");
+        
+        LoginUser loginUser = new LoginUser(1L, 1L, sysUser, new HashSet<>());
+        
+        UsernamePasswordAuthenticationToken authToken = 
+            new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
+        SecurityContextHolder.getContext().setAuthentication(authToken);
+        
+        mockMvc = MockMvcBuilders.standaloneSetup(postCenterController).build();
+        objectMapper = new ObjectMapper();
+    }
+
+    /**
+     * 测试获取帖子列表
+     */
+    @Test
+    void testGetPostList() throws Exception {
+        List<Post> posts = new ArrayList<>();
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setTitle("测试帖子");
+        posts.add(post);
+
+        when(postService.selectPostList(any(Post.class))).thenReturn(posts);
+
+        mockMvc.perform(get("/post-center/list"))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostList(any(Post.class));
+    }
+
+    /**
+     * 测试获取帖子详情
+     */
+    @Test
+    void testGetPostDetail() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setTitle("测试帖子");
+        post.setStatus("1");
+
+        when(postService.updatePostViews(1L)).thenReturn(1);
+        when(postService.selectPostById(1L)).thenReturn(post);
+        when(postTagService.selectPostTagsByPostId(1L)).thenReturn(new ArrayList<>());
+        when(postCommentService.selectCommentsByPostId(eq(1L), eq(10))).thenReturn(new ArrayList<>());
+        when(postService.getRecommendedPosts(any(), any(), eq(1L), eq(9))).thenReturn(new ArrayList<>());
+
+        mockMvc.perform(get("/post-center/{postId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(1L);
+        verify(postService, times(1)).updatePostViews(1L);
+    }
+
+    /**
+     * 测试发布帖子
+     */
+    @Test
+    void testPublishPost() throws Exception {
+        when(postService.insertPost(any(Post.class))).thenReturn(1);
+
+        String postData = "{\"title\":\"新帖子\",\"content\":\"帖子内容\",\"summary\":\"帖子摘要\"}";
+
+        mockMvc.perform(post("/post-center/publish")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(postData))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).insertPost(any(Post.class));
+    }
+
+    /**
+     * 测试更新帖子
+     */
+    @Test
+    void testUpdatePost() throws Exception {
+        Post existingPost = new Post();
+        existingPost.setPostId(1L);
+        existingPost.setAuthorId(1L);
+
+        when(postService.selectPostById(1L)).thenReturn(existingPost);
+        when(postService.updatePost(any(Post.class))).thenReturn(1);
+
+        String postData = "{\"postId\":1,\"title\":\"更新帖子\",\"content\":\"更新内容\",\"summary\":\"更新摘要\"}";
+
+        mockMvc.perform(put("/post-center/update")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(postData))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).updatePost(any(Post.class));
+    }
+
+    /**
+     * 测试删除帖子
+     */
+    @Test
+    void testDeletePost() throws Exception {
+        Post existingPost = new Post();
+        existingPost.setPostId(1L);
+        existingPost.setAuthorId(1L);
+
+        when(postService.selectPostById(1L)).thenReturn(existingPost);
+        when(postCommentService.deletePostCommentByPostId(1L)).thenReturn(1);
+        when(postFavoriteService.deletePostFavoriteByPostId(1L)).thenReturn(1);
+        when(postService.deletePostById(1L)).thenReturn(1);
+
+        mockMvc.perform(delete("/post-center/delete/{postId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).deletePostById(1L);
+    }
+
+    /**
+     * 测试添加评论
+     */
+    @Test
+    void testAddComment() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setPostId(1L);
+        comment.setContent("测试评论");
+        comment.setParentId(0L);
+
+        when(postCommentService.selectPostCommentById(any())).thenReturn(null);
+        when(postCommentService.insertPostComment(any(PostComment.class))).thenReturn(1);
+        when(postService.updatePostComments(eq(1L), anyInt())).thenReturn(1);
+
+        String commentData = objectMapper.writeValueAsString(comment);
+
+        mockMvc.perform(post("/post-center/comment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(commentData))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).insertPostComment(any(PostComment.class));
+    }
+
+    /**
+     * 测试获取评论列表(实际是测试帖子列表)
+     */
+    @Test
+    void testGetComments() throws Exception {
+        List<Post> posts = new ArrayList<>();
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setTitle("测试帖子");
+        posts.add(post);
+
+        when(postService.selectPostList(any(Post.class))).thenReturn(posts);
+
+        // 测试帖子列表接口
+        mockMvc.perform(get("/post-center/list")
+                .param("postId", "1")
+                .param("pageSize", "10"))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostList(any(Post.class));
+    }
+
+    /**
+     * 测试收藏帖子
+     */
+    @Test
+    void testToggleFavorite() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setStatus("1");
+        post.setTitle("测试帖子");
+
+        when(postService.selectPostById(1L)).thenReturn(post);
+        when(postFavoriteService.selectPostFavoriteByPostIdAndUserId(anyLong(), anyLong())).thenReturn(null);
+        when(postFavoriteService.insertPostFavorite(any())).thenReturn(1);
+        when(postService.updatePostFavorites(anyLong(), anyInt())).thenReturn(1);
+
+        mockMvc.perform(post("/post-center/favorite/{postId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postFavoriteService, times(1)).insertPostFavorite(any());
+    }
+
+    /**
+     * 测试点赞帖子
+     */
+    @Test
+    void testToggleLike() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setStatus("1");
+
+        when(postService.selectPostById(1L)).thenReturn(post);
+        when(postService.updatePostLikes(anyLong(), anyInt())).thenReturn(1);
+
+        mockMvc.perform(post("/post-center/like/{postId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).updatePostLikes(eq(1L), anyInt());
+    }
+
+    /**
+     * 测试上传图片
+     */
+    @Test
+    void testUploadImage() throws Exception {
+        mockMvc.perform(multipart("/post-center/upload")
+                .file("file", "test image content".getBytes())
+                .param("filename", "test.jpg"))
+                .andExpect(status().isOk());
+    }
+
+    /**
+     * 测试获取我的帖子
+     */
+    @Test
+    void testGetMyPosts() throws Exception {
+        List<Post> posts = new ArrayList<>();
+        when(postService.selectPostList(any())).thenReturn(posts);
+
+        mockMvc.perform(get("/post-center/my-posts"))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostList(any());
+    }
+
+    /**
+     * 测试获取我的收藏
+     */
+    @Test
+    void testGetMyFavorites() throws Exception {
+        List<PostFavorite> favorites = new ArrayList<>();
+        when(postFavoriteService.selectPostFavoriteList(any())).thenReturn(favorites);
+
+        mockMvc.perform(get("/post-center/my-favorites"))
+                .andExpect(status().isOk());
+
+        verify(postFavoriteService, times(1)).selectPostFavoriteList(any());
+    }
+
+    /**
+     * 测试举报帖子
+     */
+    @Test
+    void testReportPost() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setStatus("1");
+        post.setTitle("测试帖子");
+
+        when(postService.selectPostById(1L)).thenReturn(post);
+        when(postReportMapper.insertPostReport(any())).thenReturn(1);
+
+        String reportData = "{\"reason\":\"垃圾内容\"}";
+
+        mockMvc.perform(post("/post-center/report/{postId}", 1L)
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(reportData))
+                .andExpect(status().isOk());
+
+        verify(postReportMapper, times(1)).insertPostReport(any());
+    }
+
+    /**
+     * 测试获取热门标签
+     */
+    @Test
+    void testGetHotTags() throws Exception {
+        List<PostTag> tags = new ArrayList<>();
+        when(postTagService.selectPostTagList(any())).thenReturn(tags);
+
+        mockMvc.perform(get("/post-center/tags/hot"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagList(any());
+    }
+
+    /**
+     * 测试获取可用标签
+     */
+    @Test
+    void testGetAvailableTags() throws Exception {
+        List<PostTag> tags = new ArrayList<>();
+        when(postTagService.selectPostTagList(any())).thenReturn(tags);
+
+        mockMvc.perform(get("/post-center/tags/available"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagList(any());
+    }
+
+    /**
+     * 测试点赞评论
+     */
+    @Test
+    void testToggleCommentLike() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setCommentId(1L);
+        comment.setStatus("1");
+
+        when(postCommentService.selectPostCommentById(1L)).thenReturn(comment);
+        when(postCommentService.updateCommentLikes(eq(1L), anyInt())).thenReturn(1);
+
+        mockMvc.perform(post("/post-center/comment/like/{commentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).updateCommentLikes(eq(1L), anyInt());
+    }
+
+    /**
+     * 测试获取帖子详情(帖子不存在)
+     */
+    @Test
+    void testGetPostDetailNotFound() throws Exception {
+        when(postService.updatePostViews(999L)).thenReturn(0);
+        when(postService.selectPostById(999L)).thenReturn(null);
+
+        mockMvc.perform(get("/post-center/{postId}", 999L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(999L);
+    }
+
+    /**
+     * 测试发布空内容帖子
+     */
+    @Test
+    void testPublishEmptyPost() throws Exception {
+        String emptyPostData = "{\"title\":\"\",\"content\":\"\"}";
+
+        mockMvc.perform(post("/post-center/publish")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(emptyPostData))
+                .andExpect(status().isOk());
+    }
+
+    /**
+     * 测试添加空评论
+     */
+    @Test
+    void testAddEmptyComment() throws Exception {
+        String emptyCommentData = "{\"postId\":1,\"content\":\"\"}";
+
+        mockMvc.perform(post("/post-center/comment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(emptyCommentData))
+                .andExpect(status().isOk());
+    }
+
+    /**
+     * 测试上传空文件
+     */
+    @Test
+    void testUploadEmptyFile() throws Exception {
+        mockMvc.perform(multipart("/post-center/upload")
+                .file("file", new byte[0])
+                .param("filename", ""))
+                .andExpect(status().isOk());
+    }
+
+    /**
+     * 测试获取推广计划
+     */
+    @Test
+    void testGetPromotionPlans() throws Exception {
+        mockMvc.perform(get("/post-center/promotion-plans"))
+                .andExpect(status().isOk());
+    }
+
+    /**
+     * 测试创建支付
+     */
+    @Test
+    void testCreatePayment() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setAuthorId(1L);
+
+        when(postService.selectPostById(1L)).thenReturn(post);
+        when(postPaymentMapper.insertPostPayment(any())).thenReturn(1);
+
+        String paymentData = "{\"postId\":1,\"planId\":1,\"amount\":100}";
+
+        mockMvc.perform(post("/post-center/payment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(paymentData))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).insertPostPayment(any());
+    }
+
+    /**
+     * 测试取消支付
+     */
+    @Test
+    void testCancelPayment() throws Exception {
+        PostPayment payment = new PostPayment();
+        payment.setPaymentId(1L);
+        payment.setUserId(1L);
+        payment.setPaymentStatus("pending");
+
+        when(postPaymentMapper.selectPostPaymentById(1L)).thenReturn(payment);
+        when(postPaymentMapper.updatePostPayment(any())).thenReturn(1);
+
+        mockMvc.perform(post("/post-center/payment/cancel/{paymentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).updatePostPayment(any());
+    }
+
+    /**
+     * 测试获取推广状态
+     */
+    @Test
+    void testGetPromotionStatus() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setAuthorId(1L);
+
+        when(postService.selectPostById(1L)).thenReturn(post);
+
+        mockMvc.perform(get("/post-center/promotion-status/{postId}", 1L))
+                .andExpect(status().isOk());
+    }
+
+    /**
+     * 测试获取推广帖子
+     */
+    @Test
+    void testGetPromotionPosts() throws Exception {
+        List<Post> posts = new ArrayList<>();
+        when(postService.selectPostList(any())).thenReturn(posts);
+
+        mockMvc.perform(get("/post-center/promotion"))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostList(any());
+    }
+
+    /**
+     * 测试根据标签获取帖子
+     */
+    @Test
+    void testGetPostsByTag() throws Exception {
+        PostTag tag = new PostTag();
+        tag.setTagId(1L);
+        tag.setTagName("测试标签");
+        
+        List<Post> posts = new ArrayList<>();
+        when(postTagService.selectPostTagById(1L)).thenReturn(tag);
+        when(postService.selectPostList(any())).thenReturn(posts);
+
+        mockMvc.perform(get("/post-center/bytag/{tagId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostList(any());
+    }
+
+    /**
+     * 测试删除图片
+     */
+    @Test
+    void testDeleteImage() throws Exception {
+        mockMvc.perform(delete("/post-center/upload")
+                .param("filename", "test.jpg"))
+                .andExpect(status().isOk());
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCommentControllerTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCommentControllerTest.java
new file mode 100644
index 0000000..8e81d5a
--- /dev/null
+++ b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCommentControllerTest.java
@@ -0,0 +1,378 @@
+package com.ruoyi.web.controller.post;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.web.controller.post.controller.PostCommentController;
+import com.ruoyi.web.controller.post.domain.PostComment;
+import com.ruoyi.web.controller.post.service.IPostCommentService;
+import com.ruoyi.web.controller.post.service.IPostService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+/**
+ * PostCommentController 单元测试
+ * 
+ * 测试覆盖所有评论管理相关的API接口
+ * 包括:评论列表、详情、增删改查、点赞、导出等功能
+ */
+@ExtendWith(MockitoExtension.class)
+class PostCommentControllerTest {
+
+    @Mock
+    private IPostCommentService postCommentService;
+
+    @Mock
+    private IPostService postService;
+
+    @InjectMocks
+    private PostCommentController postCommentController;
+
+    private MockMvc mockMvc;
+    private ObjectMapper objectMapper;
+
+    @BeforeEach
+    void setUp() {
+        // 创建模拟的安全上下文
+        SysUser sysUser = new SysUser();
+        sysUser.setUserId(1L);
+        sysUser.setUserName("testUser");
+        
+        LoginUser loginUser = new LoginUser(1L, 1L, sysUser, new HashSet<>());
+        
+        UsernamePasswordAuthenticationToken authToken = 
+            new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
+        SecurityContextHolder.getContext().setAuthentication(authToken);
+        
+        mockMvc = MockMvcBuilders.standaloneSetup(postCommentController).build();
+        objectMapper = new ObjectMapper();
+    }
+
+    /**
+     * 测试获取评论列表
+     */
+    @Test
+    void testGetCommentList() throws Exception {
+        List<PostComment> comments = new ArrayList<>();
+        PostComment comment = new PostComment();
+        comment.setCommentId(1L);
+        comment.setContent("测试评论");
+        comments.add(comment);
+
+        when(postCommentService.selectPostCommentList(any(PostComment.class))).thenReturn(comments);
+
+        mockMvc.perform(get("/post/comment/list"))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).selectPostCommentList(any(PostComment.class));
+    }
+
+    /**
+     * 测试获取评论详情
+     */
+    @Test
+    void testGetCommentInfo() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setCommentId(1L);
+        comment.setContent("测试评论");
+
+        when(postCommentService.selectPostCommentById(1L)).thenReturn(comment);
+
+        mockMvc.perform(get("/post/comment/{commentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).selectPostCommentById(1L);
+    }
+
+    /**
+     * 测试添加评论
+     */
+    @Test
+    void testAddComment() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setPostId(1L);
+        comment.setContent("新评论");
+
+        when(postCommentService.insertPostComment(any(PostComment.class))).thenReturn(1);
+        when(postService.updatePostComments(eq(1L), eq(1))).thenReturn(1);
+
+        mockMvc.perform(post("/post/comment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(comment)))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).insertPostComment(any(PostComment.class));
+        verify(postService, times(1)).updatePostComments(eq(1L), eq(1));
+    }
+
+    /**
+     * 测试编辑评论
+     */
+    @Test
+    void testEditComment() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setCommentId(1L);
+        comment.setContent("编辑后的评论");
+
+        when(postCommentService.updatePostComment(any(PostComment.class))).thenReturn(1);
+
+        mockMvc.perform(put("/post/comment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(comment)))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).updatePostComment(any(PostComment.class));
+    }
+
+    /**
+     * 测试删除评论
+     */
+    @Test
+    void testDeleteComment() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setCommentId(1L);
+        comment.setPostId(1L);
+        comment.setParentId(0L);
+
+        when(postCommentService.selectPostCommentById(1L)).thenReturn(comment);
+        when(postCommentService.selectCommentsByParentId(1L)).thenReturn(new ArrayList<>());
+        when(postCommentService.deletePostCommentByIds(any())).thenReturn(1);
+        when(postService.updatePostComments(eq(1L), eq(-1))).thenReturn(1);
+
+        mockMvc.perform(delete("/post/comment/{commentIds}", "1"))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).selectPostCommentById(1L);
+        verify(postCommentService, times(1)).deletePostCommentByIds(any());
+        verify(postService, times(1)).updatePostComments(eq(1L), eq(-1));
+    }
+
+    /**
+     * 测试删除包含回复的顶级评论
+     */
+    @Test
+    void testDeleteTopCommentWithReplies() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setCommentId(1L);
+        comment.setPostId(1L);
+        comment.setParentId(0L);
+
+        List<PostComment> replies = new ArrayList<>();
+        PostComment reply1 = new PostComment();
+        reply1.setCommentId(2L);
+        replies.add(reply1);
+        PostComment reply2 = new PostComment();
+        reply2.setCommentId(3L);
+        replies.add(reply2);
+
+        when(postCommentService.selectPostCommentById(1L)).thenReturn(comment);
+        when(postCommentService.selectCommentsByParentId(1L)).thenReturn(replies);
+        when(postCommentService.deletePostCommentByIds(any())).thenReturn(1);
+        when(postService.updatePostComments(eq(1L), eq(-3))).thenReturn(1); // 1个评论 + 2个回复
+
+        mockMvc.perform(delete("/post/comment/{commentIds}", "1"))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).selectPostCommentById(1L);
+        verify(postCommentService, times(1)).selectCommentsByParentId(1L);
+        verify(postCommentService, times(1)).deletePostCommentByIds(any());
+        verify(postService, times(1)).updatePostComments(eq(1L), eq(-3));
+    }
+
+    /**
+     * 测试根据帖子ID获取评论列表
+     */
+    @Test
+    void testListByPostId() throws Exception {
+        List<PostComment> comments = new ArrayList<>();
+        when(postCommentService.selectCommentsByPostId(eq(1L), eq(0))).thenReturn(comments);
+
+        mockMvc.perform(get("/post/comment/list/{postId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).selectCommentsByPostId(eq(1L), eq(0));
+    }
+
+    /**
+     * 测试点赞评论
+     */
+    @Test
+    void testLikeComment() throws Exception {
+        when(postCommentService.updateCommentLikes(eq(1L), eq(1))).thenReturn(1);
+
+        mockMvc.perform(post("/post/comment/like/{commentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).updateCommentLikes(eq(1L), eq(1));
+    }
+
+    /**
+     * 测试导出评论
+     */
+    @Test
+    void testExportComments() throws Exception {
+        List<PostComment> comments = new ArrayList<>();
+        when(postCommentService.selectPostCommentList(any(PostComment.class))).thenReturn(comments);
+
+        mockMvc.perform(post("/post/comment/export"))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).selectPostCommentList(any(PostComment.class));
+    }
+
+    /**
+     * 测试添加回复评论(非顶级评论)
+     */
+    @Test
+    void testAddReplyComment() throws Exception {
+        PostComment replyComment = new PostComment();
+        replyComment.setPostId(1L);
+        replyComment.setContent("回复评论");
+        replyComment.setParentId(1L); // 回复给评论ID为1的评论
+
+        when(postCommentService.insertPostComment(any(PostComment.class))).thenReturn(1);
+        when(postService.updatePostComments(eq(1L), eq(1))).thenReturn(1);
+
+        mockMvc.perform(post("/post/comment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(replyComment)))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).insertPostComment(any(PostComment.class));
+        verify(postService, times(1)).updatePostComments(eq(1L), eq(1));
+    }
+
+    /**
+     * 测试删除不存在的评论
+     */
+    @Test
+    void testDeleteNonExistentComment() throws Exception {
+        when(postCommentService.selectPostCommentById(999L)).thenReturn(null);
+        when(postCommentService.deletePostCommentByIds(any())).thenReturn(0);
+
+        mockMvc.perform(delete("/post/comment/{commentIds}", "999"))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).selectPostCommentById(999L);
+        verify(postCommentService, times(1)).deletePostCommentByIds(any());
+        verify(postService, never()).updatePostComments(any(), anyInt());
+    }
+
+    /**
+     * 测试批量删除评论
+     */
+    @Test
+    void testBatchDeleteComments() throws Exception {
+        PostComment comment1 = new PostComment();
+        comment1.setCommentId(1L);
+        comment1.setPostId(1L);
+        comment1.setParentId(0L);
+
+        PostComment comment2 = new PostComment();
+        comment2.setCommentId(2L);
+        comment2.setPostId(1L);
+        comment2.setParentId(0L);
+
+        when(postCommentService.selectPostCommentById(1L)).thenReturn(comment1);
+        when(postCommentService.selectPostCommentById(2L)).thenReturn(comment2);
+        when(postCommentService.selectCommentsByParentId(1L)).thenReturn(new ArrayList<>());
+        when(postCommentService.selectCommentsByParentId(2L)).thenReturn(new ArrayList<>());
+        when(postCommentService.deletePostCommentByIds(any())).thenReturn(2);
+        when(postService.updatePostComments(eq(1L), eq(-1))).thenReturn(1);
+
+        mockMvc.perform(delete("/post/comment/{commentIds}", "1,2"))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).selectPostCommentById(1L);
+        verify(postCommentService, times(1)).selectPostCommentById(2L);
+        verify(postCommentService, times(1)).deletePostCommentByIds(any());
+        verify(postService, times(2)).updatePostComments(eq(1L), eq(-1));
+    }
+
+    /**
+     * 测试添加空内容评论(应该返回验证错误)
+     */
+    @Test
+    void testAddEmptyContentComment() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setPostId(1L);
+        comment.setContent(""); // 空内容
+
+        mockMvc.perform(post("/post/comment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(comment)))
+                .andExpect(status().isBadRequest());
+
+        verify(postCommentService, never()).insertPostComment(any(PostComment.class));
+    }
+
+    /**
+     * 测试更新评论状态
+     */
+    @Test
+    void testUpdateCommentStatus() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setCommentId(1L);
+        comment.setStatus("0");
+        comment.setContent("有效内容");
+
+        when(postCommentService.updatePostComment(any(PostComment.class))).thenReturn(1);
+
+        mockMvc.perform(put("/post/comment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(comment)))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).updatePostComment(any(PostComment.class));
+    }
+
+    /**
+     * 测试获取不存在评论的详情
+     */
+    @Test
+    void testGetNonExistentCommentInfo() throws Exception {
+        when(postCommentService.selectPostCommentById(999L)).thenReturn(null);
+
+        mockMvc.perform(get("/post/comment/{commentId}", 999L))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).selectPostCommentById(999L);
+    }
+
+    /**
+     * 测试编辑不存在的评论
+     */
+    @Test
+    void testEditNonExistentComment() throws Exception {
+        PostComment comment = new PostComment();
+        comment.setCommentId(999L);
+        comment.setContent("不存在的评论");
+
+        when(postCommentService.updatePostComment(any(PostComment.class))).thenReturn(0);
+
+        mockMvc.perform(put("/post/comment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(comment)))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).updatePostComment(any(PostComment.class));
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostControllerTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostControllerTest.java
new file mode 100644
index 0000000..34b2992
--- /dev/null
+++ b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostControllerTest.java
@@ -0,0 +1,406 @@
+package com.ruoyi.web.controller.post;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.web.controller.post.controller.PostController;
+import com.ruoyi.web.controller.post.domain.Post;
+import com.ruoyi.web.controller.post.domain.PostComment;
+import com.ruoyi.web.controller.post.domain.PostFavorite;
+import com.ruoyi.web.controller.post.domain.PostReport;
+import com.ruoyi.web.controller.post.domain.PostTag;
+import com.ruoyi.web.controller.post.service.IPostService;
+import com.ruoyi.web.controller.post.service.IPostTagService;
+import com.ruoyi.web.controller.post.service.IPostCommentService;
+import com.ruoyi.web.controller.post.service.IPostFavoriteService;
+import com.ruoyi.web.controller.post.mapper.PostReportMapper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+/**
+ * PostController 单元测试
+ * 
+ * 测试覆盖所有帖子管理相关的API接口
+ * 包括:帖子列表、详情、增删改查、审核、导出等功能
+ */
+@ExtendWith(MockitoExtension.class)
+class PostControllerTest {
+
+    @Mock
+    private IPostService postService;
+
+    @Mock
+    private IPostTagService postTagService;
+
+    @Mock
+    private IPostCommentService postCommentService;
+
+    @Mock
+    private IPostFavoriteService postFavoriteService;
+
+    @Mock
+    private PostReportMapper postReportMapper;
+
+    @InjectMocks
+    private PostController postController;
+
+    private MockMvc mockMvc;
+    private ObjectMapper objectMapper;
+
+    @BeforeEach
+    void setUp() {
+        // 创建模拟的安全上下文
+        SysUser sysUser = new SysUser();
+        sysUser.setUserId(1L);
+        sysUser.setUserName("testUser");
+        
+        LoginUser loginUser = new LoginUser(1L, 1L, sysUser, new HashSet<>());
+        
+        UsernamePasswordAuthenticationToken authToken = 
+            new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
+        SecurityContextHolder.getContext().setAuthentication(authToken);
+        
+        mockMvc = MockMvcBuilders.standaloneSetup(postController).build();
+        objectMapper = new ObjectMapper();
+    }
+
+    /**
+     * 测试获取帖子列表
+     */
+    @Test
+    void testGetPostList() throws Exception {
+        List<Post> posts = new ArrayList<>();
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setTitle("测试帖子");
+        posts.add(post);
+
+        when(postService.selectPostList(any(Post.class))).thenReturn(posts);
+
+        mockMvc.perform(get("/post/list"))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostList(any(Post.class));
+    }
+
+    /**
+     * 测试获取帖子详情
+     */
+    @Test
+    void testGetPostInfo() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setTitle("测试帖子");
+        post.setAuthorId(1L);
+        post.setTags("测试标签");
+
+        when(postService.updatePostViews(1L)).thenReturn(1);
+        when(postService.selectPostById(1L)).thenReturn(post);
+        when(postTagService.selectPostTagsByPostId(1L)).thenReturn(new ArrayList<>());
+        when(postCommentService.selectPostCommentList(any())).thenReturn(new ArrayList<>());
+        when(postService.selectAuthorOtherPosts(eq(1L), eq(1L), eq(3))).thenReturn(new ArrayList<>());
+        when(postService.selectSimilarTagsPosts(eq("测试标签"), eq(1L), eq(3))).thenReturn(new ArrayList<>());
+        when(postFavoriteService.selectPostFavoriteByPostIdAndUserId(eq(1L), eq(1L))).thenReturn(null);
+
+        mockMvc.perform(get("/post/{postId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(1L);
+        verify(postService, times(1)).updatePostViews(1L);
+    }
+
+    /**
+     * 测试添加帖子
+     */
+    @Test
+    void testAddPost() throws Exception {
+        Post post = new Post();
+        post.setTitle("新帖子");
+        post.setContent("帖子内容");
+
+        when(postService.insertPost(any(Post.class))).thenReturn(1);
+
+        mockMvc.perform(post("/post")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(post)))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).insertPost(any(Post.class));
+    }
+
+    /**
+     * 测试编辑帖子
+     */
+    @Test
+    void testEditPost() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setTitle("编辑后的帖子");
+
+        when(postService.updatePost(any(Post.class))).thenReturn(1);
+        when(postTagService.deletePostTagRelation(1L)).thenReturn(1);
+
+        mockMvc.perform(put("/post")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(post)))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).updatePost(any(Post.class));
+    }
+
+    /**
+     * 测试删除帖子
+     */
+    @Test
+    void testDeletePost() throws Exception {
+        when(postCommentService.deletePostCommentByPostId(1L)).thenReturn(1);
+        when(postFavoriteService.deletePostFavoriteByPostId(1L)).thenReturn(1);
+        when(postTagService.deletePostTagRelation(1L)).thenReturn(1);
+        when(postService.deletePostByIds(any())).thenReturn(1);
+
+        mockMvc.perform(delete("/post/{postIds}", "1"))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).deletePostCommentByPostId(1L);
+        verify(postFavoriteService, times(1)).deletePostFavoriteByPostId(1L);
+        verify(postTagService, times(1)).deletePostTagRelation(1L);
+        verify(postService, times(1)).deletePostByIds(any());
+    }
+
+    /**
+     * 测试导出帖子
+     */
+    @Test
+    void testExportPosts() throws Exception {
+        List<Post> posts = new ArrayList<>();
+        when(postService.selectPostList(any(Post.class))).thenReturn(posts);
+
+        mockMvc.perform(post("/post/export"))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostList(any(Post.class));
+    }
+
+    /**
+     * 测试收藏帖子
+     */
+    @Test
+    void testFavoritePost() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setTitle("测试帖子");
+
+        when(postService.selectPostById(1L)).thenReturn(post);
+        when(postFavoriteService.selectPostFavoriteByPostIdAndUserId(eq(1L), eq(1L))).thenReturn(null);
+        when(postFavoriteService.insertPostFavorite(any())).thenReturn(1);
+        when(postService.updatePostFavorites(eq(1L), eq(1))).thenReturn(1);
+
+        mockMvc.perform(post("/post/favorite/{postId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(1L);
+        verify(postFavoriteService, times(1)).insertPostFavorite(any());
+    }
+
+    /**
+     * 测试获取收藏列表
+     */
+    @Test
+    void testGetFavoriteList() throws Exception {
+        List<PostFavorite> favorites = new ArrayList<>();
+        when(postFavoriteService.selectPostFavoriteList(any())).thenReturn(favorites);
+
+        mockMvc.perform(get("/post/favorite/list"))
+                .andExpect(status().isOk());
+
+        verify(postFavoriteService, times(1)).selectPostFavoriteList(any());
+    }
+
+    /**
+     * 测试获取待审核帖子列表
+     */
+    @Test
+    void testGetReviewList() throws Exception {
+        List<Post> posts = new ArrayList<>();
+        when(postService.selectPostList(any())).thenReturn(posts);
+
+        mockMvc.perform(get("/post/review/list"))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostList(any());
+    }
+
+    /**
+     * 测试审核通过帖子
+     */
+    @Test
+    void testReviewPostApprove() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setStatus("0");
+
+        when(postService.selectPostById(1L)).thenReturn(post);
+        when(postService.updatePost(any())).thenReturn(1);
+
+        String requestBody = "{\"action\":\"approve\",\"reason\":\"审核通过\"}";
+
+        mockMvc.perform(put("/post/review/{postId}", 1L)
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(requestBody))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(1L);
+        verify(postService, times(1)).updatePost(any());
+    }
+
+    /**
+     * 测试获取举报列表
+     */
+    @Test
+    void testGetReportList() throws Exception {
+        List<PostReport> reports = new ArrayList<>();
+        when(postReportMapper.selectPostReportList(any())).thenReturn(reports);
+
+        mockMvc.perform(get("/post/report/list"))
+                .andExpect(status().isOk());
+
+        verify(postReportMapper, times(1)).selectPostReportList(any());
+    }
+
+    /**
+     * 测试获取帖子详情(帖子不存在)
+     */
+    @Test 
+    void testGetNonExistentPostInfo() throws Exception {
+        when(postService.selectPostById(999L)).thenReturn(null);
+        when(postService.updatePostViews(999L)).thenReturn(0);
+
+        // 控制器现在会检查post是否为null并返回错误
+        mockMvc.perform(get("/post/{postId}", 999L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(999L);
+        verify(postService, times(1)).updatePostViews(999L);
+    }
+
+    /**
+     * 测试编辑不存在的帖子
+     */
+    @Test
+    void testEditNonExistentPost() throws Exception {
+        Post post = new Post();
+        post.setPostId(999L);
+        post.setTitle("不存在的帖子");
+
+        when(postService.updatePost(any(Post.class))).thenReturn(0);
+        when(postTagService.deletePostTagRelation(999L)).thenReturn(0);
+
+        mockMvc.perform(put("/post")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(post)))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).updatePost(any(Post.class));
+    }
+
+    /**
+     * 测试删除不存在的帖子
+     */
+    @Test
+    void testDeleteNonExistentPost() throws Exception {
+        when(postCommentService.deletePostCommentByPostId(999L)).thenReturn(0);
+        when(postFavoriteService.deletePostFavoriteByPostId(999L)).thenReturn(0);
+        when(postTagService.deletePostTagRelation(999L)).thenReturn(0);
+        when(postService.deletePostByIds(any())).thenReturn(0);
+
+        mockMvc.perform(delete("/post/{postIds}", "999"))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).deletePostCommentByPostId(999L);
+        verify(postFavoriteService, times(1)).deletePostFavoriteByPostId(999L);
+        verify(postService, times(1)).deletePostByIds(any());
+    }
+
+    /**
+     * 测试审核不存在的帖子
+     */
+    @Test
+    void testReviewNonExistentPost() throws Exception {
+        when(postService.selectPostById(999L)).thenReturn(null);
+
+        String requestBody = "{\"action\":\"approve\",\"reason\":\"审核通过\"}";
+
+        mockMvc.perform(put("/post/review/{postId}", 999L)
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(requestBody))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(999L);
+        verify(postService, never()).updatePost(any());
+    }
+
+    /**
+     * 测试添加带标签的帖子
+     */
+    @Test
+    void testAddPostWithTags() throws Exception {
+        Post post = new Post();
+        post.setTitle("带标签的帖子");
+        post.setContent("内容");
+        post.setTags("Java,Spring");
+        post.setPostId(1L); // 模拟插入后的ID
+
+        when(postService.insertPost(any(Post.class))).thenReturn(1);
+        when(postTagService.selectPostTagList(any(PostTag.class))).thenReturn(new ArrayList<>());
+        when(postTagService.insertPostTag(any(PostTag.class))).thenReturn(1);
+        when(postTagService.batchInsertPostTagRelation(eq(1L), any())).thenReturn(1);
+
+        mockMvc.perform(post("/post")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(post)))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).insertPost(any(Post.class));
+    }
+
+    /**
+     * 测试批量删除帖子
+     */
+    @Test
+    void testBatchDeletePosts() throws Exception {
+        when(postCommentService.deletePostCommentByPostId(1L)).thenReturn(1);
+        when(postCommentService.deletePostCommentByPostId(2L)).thenReturn(1);
+        when(postFavoriteService.deletePostFavoriteByPostId(1L)).thenReturn(1);
+        when(postFavoriteService.deletePostFavoriteByPostId(2L)).thenReturn(1);
+        when(postTagService.deletePostTagRelation(1L)).thenReturn(1);
+        when(postTagService.deletePostTagRelation(2L)).thenReturn(1);
+        when(postService.deletePostByIds(any())).thenReturn(2);
+
+        mockMvc.perform(delete("/post/{postIds}", "1,2"))
+                .andExpect(status().isOk());
+
+        verify(postCommentService, times(1)).deletePostCommentByPostId(1L);
+        verify(postCommentService, times(1)).deletePostCommentByPostId(2L);
+        verify(postFavoriteService, times(1)).deletePostFavoriteByPostId(1L);
+        verify(postFavoriteService, times(1)).deletePostFavoriteByPostId(2L);
+        verify(postService, times(1)).deletePostByIds(any());
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostTagControllerTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostTagControllerTest.java
new file mode 100644
index 0000000..a2f3fbb
--- /dev/null
+++ b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostTagControllerTest.java
@@ -0,0 +1,383 @@
+package com.ruoyi.web.controller.post;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.web.controller.post.controller.PostTagController;
+import com.ruoyi.web.controller.post.domain.PostTag;
+import com.ruoyi.web.controller.post.service.IPostTagService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static org.mockito.Mockito.lenient;
+
+/**
+ * PostTagController 单元测试
+ * 
+ * 测试覆盖所有标签管理相关的API接口
+ * 包括:标签列表、详情、增删改查、导出等功能
+ */
+@ExtendWith(MockitoExtension.class)
+class PostTagControllerTest {
+
+    @Mock
+    private IPostTagService postTagService;
+
+    @InjectMocks
+    private PostTagController postTagController;
+
+    private MockMvc mockMvc;
+    private ObjectMapper objectMapper;
+
+    @BeforeEach
+    void setUp() {
+        // 创建模拟的安全上下文
+        SysUser sysUser = new SysUser();
+        sysUser.setUserId(1L);
+        sysUser.setUserName("testUser");
+        
+        LoginUser loginUser = new LoginUser(1L, 1L, sysUser, new HashSet<>());
+        
+        UsernamePasswordAuthenticationToken authToken = 
+            new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
+        SecurityContextHolder.getContext().setAuthentication(authToken);
+        
+        mockMvc = MockMvcBuilders.standaloneSetup(postTagController).build();
+        objectMapper = new ObjectMapper();
+    }
+
+    /**
+     * 测试获取标签列表
+     */
+    @Test
+    void testGetTagList() throws Exception {
+        List<PostTag> tags = new ArrayList<>();
+        PostTag tag = new PostTag();
+        tag.setTagId(1L);
+        tag.setTagName("测试标签");
+        tags.add(tag);
+
+        when(postTagService.selectPostTagList(any(PostTag.class))).thenReturn(tags);
+
+        mockMvc.perform(get("/post/tag/list"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagList(any(PostTag.class));
+    }
+
+    /**
+     * 测试获取标签详情
+     */
+    @Test
+    void testGetTagInfo() throws Exception {
+        PostTag tag = new PostTag();
+        tag.setTagId(1L);
+        tag.setTagName("测试标签");
+
+        when(postTagService.selectPostTagById(1L)).thenReturn(tag);
+
+        mockMvc.perform(get("/post/tag/{tagId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagById(1L);
+    }
+
+    /**
+     * 测试添加标签
+     */
+    @Test
+    void testAddTag() throws Exception {
+        PostTag tag = new PostTag();
+        tag.setTagName("新标签");
+
+        when(postTagService.checkTagNameUnique(any())).thenReturn(true);
+        when(postTagService.insertPostTag(any(PostTag.class))).thenReturn(1);
+
+        mockMvc.perform(post("/post/tag")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(tag)))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).checkTagNameUnique(any());
+        verify(postTagService, times(1)).insertPostTag(any(PostTag.class));
+    }
+
+    /**
+     * 测试编辑标签
+     */
+    @Test
+    void testEditTag() throws Exception {
+        PostTag tag = new PostTag();
+        tag.setTagId(1L);
+        tag.setTagName("编辑后的标签");
+
+        when(postTagService.checkTagNameUnique(any())).thenReturn(true);
+        when(postTagService.updatePostTag(any(PostTag.class))).thenReturn(1);
+
+        mockMvc.perform(put("/post/tag")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(tag)))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).checkTagNameUnique(any());
+        verify(postTagService, times(1)).updatePostTag(any(PostTag.class));
+    }
+
+    /**
+     * 测试删除标签
+     */
+    @Test
+    void testDeleteTag() throws Exception {
+        when(postTagService.deletePostTagByIds(any())).thenReturn(1);
+
+        mockMvc.perform(delete("/post/tag/{tagIds}", "1"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).deletePostTagByIds(any());
+    }
+
+    /**
+     * 测试批量删除标签
+     */
+    @Test
+    void testBatchDeleteTags() throws Exception {
+        when(postTagService.deletePostTagByIds(any())).thenReturn(2);
+
+        mockMvc.perform(delete("/post/tag/{tagIds}", "1,2"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).deletePostTagByIds(any());
+    }
+
+    /**
+     * 测试导出标签
+     */
+    @Test
+    void testExportTags() throws Exception {
+        List<PostTag> tags = new ArrayList<>();
+        when(postTagService.selectPostTagList(any(PostTag.class))).thenReturn(tags);
+
+        mockMvc.perform(post("/post/tag/export"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagList(any(PostTag.class));
+    }
+
+    /**
+     * 测试根据帖子ID获取标签列表
+     */
+    @Test
+    void testListByPostId() throws Exception {
+        List<PostTag> tags = new ArrayList<>();
+        when(postTagService.selectPostTagsByPostId(1L)).thenReturn(tags);
+
+        mockMvc.perform(get("/post/tag/list/{postId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagsByPostId(1L);
+    }
+
+    /**
+     * 测试获取可用标签选项
+     */
+    @Test
+    void testOptionSelect() throws Exception {
+        List<PostTag> tags = new ArrayList<>();
+        PostTag tag = new PostTag();
+        tag.setTagId(1L);
+        tag.setTagName("可用标签");
+        tag.setStatus("0");
+        tags.add(tag);
+
+        when(postTagService.selectPostTagList(any(PostTag.class))).thenReturn(tags);
+
+        mockMvc.perform(get("/post/tag/optionselect"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagList(any(PostTag.class));
+    }
+
+    /**
+     * 测试获取不存在的标签详情
+     */
+    @Test
+    void testGetNonExistentTagInfo() throws Exception {
+        when(postTagService.selectPostTagById(999L)).thenReturn(null);
+
+        mockMvc.perform(get("/post/tag/{tagId}", 999L))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagById(999L);
+    }
+
+    /**
+     * 测试编辑不存在的标签
+     */
+    @Test
+    void testEditNonExistentTag() throws Exception {
+        PostTag tag = new PostTag();
+        tag.setTagId(999L);
+        tag.setTagName("不存在的标签");
+
+        when(postTagService.checkTagNameUnique(any())).thenReturn(true);
+        when(postTagService.updatePostTag(any(PostTag.class))).thenReturn(0);
+
+        mockMvc.perform(put("/post/tag")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(tag)))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).updatePostTag(any(PostTag.class));
+    }
+
+    /**
+     * 测试删除不存在的标签
+     */
+    @Test
+    void testDeleteNonExistentTag() throws Exception {
+        when(postTagService.deletePostTagByIds(any())).thenReturn(0);
+
+        mockMvc.perform(delete("/post/tag/{tagIds}", "999"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).deletePostTagByIds(any());
+    }
+
+    /**
+     * 测试添加重复标签名
+     */
+    @Test
+    void testAddDuplicateTagName() throws Exception {
+        PostTag tag = new PostTag();
+        tag.setTagName("重复标签");
+
+        when(postTagService.checkTagNameUnique(any())).thenReturn(false);
+
+        mockMvc.perform(post("/post/tag")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(tag)))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).checkTagNameUnique(any());
+        verify(postTagService, never()).insertPostTag(any());
+    }
+
+    /**
+     * 测试编辑为重复标签名
+     */
+    @Test
+    void testEditToDuplicateTagName() throws Exception {
+        PostTag tag = new PostTag();
+        tag.setTagId(1L);
+        tag.setTagName("重复标签");
+
+        when(postTagService.checkTagNameUnique(any())).thenReturn(false);
+
+        mockMvc.perform(put("/post/tag")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(tag)))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).checkTagNameUnique(any());
+        verify(postTagService, never()).updatePostTag(any());
+    }
+
+    /**
+     * 测试添加标签失败
+     */
+    @Test
+    void testAddTagFailed() throws Exception {
+        PostTag tag = new PostTag();
+        tag.setTagName("新标签");
+
+        when(postTagService.checkTagNameUnique(any())).thenReturn(true);
+        when(postTagService.insertPostTag(any(PostTag.class))).thenReturn(0);
+
+        mockMvc.perform(post("/post/tag")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(tag)))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).checkTagNameUnique(any());
+        verify(postTagService, times(1)).insertPostTag(any(PostTag.class));
+    }
+
+    /**
+     * 测试编辑标签失败
+     */
+    @Test
+    void testEditTagFailed() throws Exception {
+        PostTag tag = new PostTag();
+        tag.setTagId(1L);
+        tag.setTagName("编辑后的标签");
+
+        when(postTagService.checkTagNameUnique(any())).thenReturn(true);
+        when(postTagService.updatePostTag(any(PostTag.class))).thenReturn(0);
+
+        mockMvc.perform(put("/post/tag")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(objectMapper.writeValueAsString(tag)))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).checkTagNameUnique(any());
+        verify(postTagService, times(1)).updatePostTag(any(PostTag.class));
+    }
+
+    /**
+     * 测试删除标签失败
+     */
+    @Test
+    void testDeleteTagFailed() throws Exception {
+        when(postTagService.deletePostTagByIds(any())).thenReturn(0);
+
+        mockMvc.perform(delete("/post/tag/{tagIds}", "1"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).deletePostTagByIds(any());
+    }
+
+    /**
+     * 测试获取空的标签列表
+     */
+    @Test
+    void testGetEmptyTagList() throws Exception {
+        List<PostTag> tags = new ArrayList<>();
+        when(postTagService.selectPostTagList(any(PostTag.class))).thenReturn(tags);
+
+        mockMvc.perform(get("/post/tag/list"))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagList(any(PostTag.class));
+    }
+
+    /**
+     * 测试根据不存在的帖子ID获取标签列表
+     */
+    @Test
+    void testListByNonExistentPostId() throws Exception {
+        List<PostTag> tags = new ArrayList<>();
+        when(postTagService.selectPostTagsByPostId(999L)).thenReturn(tags);
+
+        mockMvc.perform(get("/post/tag/list/{postId}", 999L))
+                .andExpect(status().isOk());
+
+        verify(postTagService, times(1)).selectPostTagsByPostId(999L);
+    }
+} 
\ No newline at end of file
diff --git a/ruoyi-admin/upload/images/.gitkeep b/ruoyi-admin/upload/images/.gitkeep
new file mode 100644
index 0000000..f3f3088
--- /dev/null
+++ b/ruoyi-admin/upload/images/.gitkeep
@@ -0,0 +1,2 @@
+# This file ensures the images directory is tracked by git
+# Directory for storing uploaded post images 
\ No newline at end of file