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

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

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

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

Change-Id: Iafa37f603aed3337484a3fc96d1cc70b83e8df0c
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