<template>
  <div>
    <el-input
        v-model="searchTitle"
        placeholder="搜索帖子标题"
        style="width: 300px; margin-bottom: 20px"
        clearable
        @clear="fetchPosts"
        @keyup.enter.native="fetchPosts"
    />
    <el-table
        :data="posts"
        style="width: 100%"
        v-loading="loading"
        border
        :row-key="(row) => row.id"
    >
      <el-table-column prop="id" label="ID" width="70" />
      <el-table-column prop="title" label="标题" />
      <el-table-column prop="username" label="用户名" width="150" />
      <el-table-column label="点赞数" width="100" prop="likeCount" />
      <el-table-column label="举报数" width="100">
        <template #default="{ row }">
          <el-badge :value="reportsMap[row.id]?.length || 0" class="item">
            <el-button
                size="mini"
                type="danger"
                :disabled="!(reportsMap[row.id]?.length > 0)"
                @click="showReportDialog(row.id)"
            >
              查看举报
            </el-button>
          </el-badge>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="150">
        <template #default="{ row }">
          <el-button
              size="mini"
              type="danger"
              @click="confirmDeletePost(row.id)"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <el-pagination
        background
        layout="prev, pager, next"
        :page-size="pageSize"
        :total="total"
        :current-page="page"
        @current-change="onPageChange"
        style="margin-top: 20px; text-align: right;"
    />

    <!-- 举报弹窗 -->
    <el-dialog
        :visible.sync="reportDialogVisible"
        title="帖子举报详情"
        width="600px"
        :before-close="() => (reportDialogVisible = false)"
    >
      <el-table :data="currentReports" style="width: 100%" border>
        <el-table-column prop="reporterId" label="举报人ID" width="120" />
        <el-table-column prop="reason" label="举报原因" />
        <el-table-column
            prop="createdAt"
            label="举报时间"
            width="180"
            :formatter="formatDate"
        />
      </el-table>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import axios from 'axios'

const posts = ref([])
const reportsMap = reactive({})
const loading = ref(false)
const page = ref(1)
const pageSize = 10
const total = ref(0)
const searchTitle = ref('')
const reportDialogVisible = ref(false)
const currentReports = ref([])

const fetchPosts = async () => {
  loading.value = true
  try {
    // 后端没有分页接口，这里用前端模拟分页
    // 请求标题模糊查询接口
    const res = await axios.get('http://localhost:8080/forum/posts', {
      params: { title: searchTitle.value }
    })
    total.value = res.data.length
    // 简单分页模拟
    const start = (page.value - 1) * pageSize
    posts.value = res.data.slice(start, start + pageSize)

    // 同步查询这些帖子的举报数据
    await fetchReportsForPosts(posts.value.map(post => post.id))
  } catch (err) {
    console.error('获取帖子失败', err)
  } finally {
    loading.value = false
  }
}

// 查询多个帖子举报
const fetchReportsForPosts = async (postIds) => {
  for (const id of postIds) {
    try {
      const res = await axios.get(`http://localhost:8080/forum/post/reports/${id}`)
      reportsMap[id] = res.data
    } catch (err) {
      console.error(`查询帖子${id}举报失败`, err)
      reportsMap[id] = []
    }
  }
}

const showReportDialog = (postId) => {
  currentReports.value = reportsMap[postId] || []
  reportDialogVisible.value = true
}

const confirmDeletePost = (postId) => {
  window.$confirm = window.$confirm || ((msg, title, opts) => {
    // 弹窗兼容：这里改成element-plus的MessageBox.confirm
    return import('element-plus').then(({ElMessageBox}) =>
        ElMessageBox.confirm(msg, title, opts)
    )
  })
  window.$confirm('确定删除该帖子吗？', '提示', {
    type: 'warning'
  }).then(() => {
    deletePost(postId)
  }).catch(() => {})
}

const deletePost = async (postId) => {
  try {
    await axios.delete('http://localhost:8080/forum/post/' + postId)
    window.ElMessage?.success('帖子删除成功')
    fetchPosts()
  } catch (err) {
    console.error('删除帖子失败', err)
    window.ElMessage?.error('帖子删除失败')
  }
}

const onPageChange = (newPage) => {
  page.value = newPage
  fetchPosts()
}

onMounted(fetchPosts)

const formatDate = (row, column, cellValue) => {
  if (!cellValue) return ''
  return new Date(cellValue).toLocaleString()
}
</script>
package com.example.springboot.controller;


import com.example.springboot.entity.Forum.*;
import com.example.springboot.service.ForumService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/forum")
public class ForumController {

    @Resource
    private ForumService forumService;

    // 发布帖子
    @PostMapping("/post")
    public String createPost(@RequestBody Post post) {
        int result = forumService.createPost(post);
        return result > 0 ? "Post created successfully" : "Post creation failed";
    }

    // 删除帖子（普通用户需要在业务层验证是否本人）
    @DeleteMapping("/post/{id}")
    public String deletePost(@PathVariable Integer id) {
        int result = forumService.deletePost(id);
        return result > 0 ? "Post deleted successfully" : "Post deletion failed";
    }

    // 修改帖子
    @PutMapping("/post")
    public String updatePost(@RequestBody Post post) {
        int result = forumService.updatePost(post);
        return result > 0 ? "Post updated successfully" : "Post update failed";
    }

    // 根据帖子ID获取帖子详情
    @GetMapping("/post/{id}")
    public Post getPostById(@PathVariable Integer id) {
        return forumService.getPostById(id);
    }

    // 模糊查询帖子标题
    @GetMapping("/posts")
    public List<Post> searchPostsByTitle(@RequestParam String title) {
        return forumService.searchPostsByTitle(title);
    }

    // 新增评论
    @PostMapping("/comment")
    public String createComment(@RequestBody Comment comment) {
        int result = forumService.createComment(comment);
        return result > 0 ? "Comment added successfully" : "Comment adding failed";
    }

    // 删除评论
    @DeleteMapping("/comment/{id}")
    public String deleteComment(@PathVariable Integer id) {
        int result = forumService.deleteComment(id);
        return result > 0 ? "Comment deleted successfully" : "Comment deletion failed";
    }

    // 修改评论
    @PutMapping("/comment")
    public String updateComment(@RequestBody Comment comment) {
        int result = forumService.updateComment(comment);
        return result > 0 ? "Comment updated successfully" : "Comment update failed";
    }

    // 获取某帖子的评论列表（包含回复）
    @GetMapping("/comments/{postId}")
    public List<Comment> getCommentsByPostId(@PathVariable Integer postId) {
        return forumService.getCommentsByPostId(postId);
    }

    // 点赞帖子
    @PostMapping("/post/like")
    public String likePost(@RequestBody LikePost likePost) {
        int result = forumService.createLikePost(likePost);
        return result > 0 ? "点赞成功" : "已经点赞过或点赞失败";
    }

    // 取消点赞
    @DeleteMapping("/post/like")
    public String unlikePost(@RequestParam Integer userId, @RequestParam Integer postId) {
        int result = forumService.deleteLikePost(userId, postId);
        return result > 0 ? "取消点赞成功" : "取消点赞失败";
    }

    // 收藏帖子
    @PostMapping("/post/favorite")
    public String favoritePost(@RequestBody FavoritePost favoritePost) {
        int result = forumService.createFavoritePost(favoritePost);
        return result > 0 ? "收藏成功" : "收藏失败或已收藏";
    }

    // 取消收藏
    @DeleteMapping("/post/favorite")
    public String unfavoritePost(@RequestParam Integer userId, @RequestParam Integer postId) {
        int result = forumService.deleteFavoritePost(userId, postId);
        return result > 0 ? "取消收藏成功" : "取消收藏失败";
    }

    // 举报帖子
    @PostMapping("/post/report")
    public String reportPost(@RequestBody ReportPost reportPost) {
        int result = forumService.createReportPost(reportPost);
        return result > 0 ? "举报成功" : "举报失败";
    }

    // 管理员查看举报
    @GetMapping("/post/reports/{postId}")
    public List<ReportPost> getReportsByPostId(@PathVariable Integer postId) {
        return forumService.getReportPostsByPostId(postId);
    }

    @GetMapping("/user/{userId}/posts")
    public List<Post> getPostsByUserId(@PathVariable Integer userId) {
        List<Post> posts = forumService.getPostsByUserId(userId);

        // 这里可以查询并设置每个帖子的点赞数，点赞和收藏状态，比如（简单示例，效率不错的话可DAO一次搞定）：
        posts.forEach(post -> {
            int likeCount = forumService.getLikeCountByPostId(post.getId());


            // 查询当前用户是否点赞/收藏，改成传参可改
            // 这里暂时userId是查询者，请改成当前登录用户id
            LikePost likePost = forumService.getLikePost(userId, post.getId());
            post.setLiked(likePost != null);

            FavoritePost favPost = forumService.getFavoritePost(userId, post.getId());
            post.setFavorited(favPost != null);
        });
        return posts;
    }
    /**
     * 获取用户点赞过的帖子列表
     */
    @GetMapping("/user/{userId}/liked-posts")
    public List<Post> getLikedPostsByUserId(@PathVariable Integer userId) {
        return forumService.getLikedPostsByUserId(userId);
    }

    /**
     * 获取用户收藏过的帖子列表
     */
    @GetMapping("/user/{userId}/favorited-posts")
    public List<Post> getFavoritedPostsByUserId(@PathVariable Integer userId) {
        return forumService.getFavoritedPostsByUserId(userId);
    }

    @GetMapping("/post/like/status")
    public Map<String, Boolean> getLikeStatus(@RequestParam Integer userId, @RequestParam Integer postId) {
        boolean liked = forumService.getLikePost(userId, postId) != null;
        return Collections.singletonMap("liked", liked);
    }

    @GetMapping("/post/favorite/status")
    public Map<String, Boolean> getFavoriteStatus(@RequestParam Integer userId, @RequestParam Integer postId) {
        boolean favorited = forumService.getFavoritePost(userId, postId) != null;
        return Collections.singletonMap("favorited", favorited);
    }
}
<?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.example.springboot.mapper.ForumMapper">

    <!-- 插入帖子 -->
    <insert id="insertPost" parameterType="com.example.springboot.entity.Forum.Post" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO post(user_id,title,content,created_at,updated_at)
        VALUES(#{userId}, #{title}, #{content}, NOW(), NOW())
    </insert>

    <!-- 删除帖子 -->
    <delete id="deletePostById" parameterType="int">
        DELETE FROM post WHERE id = #{id}
    </delete>

    <!-- 更新帖子 -->
    <update id="updatePost" parameterType="com.example.springboot.entity.Forum.Post">
        UPDATE post
        SET title = #{title}, content = #{content}, updated_at = NOW()
        WHERE id = #{id}
    </update>

    <!-- 根据id查询帖子（带用户名） -->
    <select id="selectPostById" parameterType="int" resultMap="postResultMap">
        SELECT
            p.id,
            p.user_id AS userId,
            u.username,
            p.title,
            p.content,
            p.created_at AS createdAt,
            p.updated_at AS updatedAt,
            IFNULL(like_counts.count, 0) AS likeCount
        FROM post p
                 LEFT JOIN user u ON p.user_id = u.id
                 LEFT JOIN (
            SELECT post_id, COUNT(*) AS count FROM like_post GROUP BY post_id
        ) like_counts ON like_counts.post_id = p.id
        WHERE p.id = #{id}
    </select>

    <resultMap id="postResultMap" type="com.example.springboot.entity.Forum.Post">
        <id property="id" column="id"/>
        <result property="userId" column="userId"/>
        <result property="username" column="username"/>
        <result property="title" column="title"/>
        <result property="content" column="content"/>
        <result property="createdAt" column="createdAt"/>
        <result property="updatedAt" column="updatedAt"/>
        <result property="likeCount" column="likeCount"/>
        <result property="liked" column="liked"/>
        <result property="favorited" column="favorited"/>
    </resultMap>

    <!-- 配置resultMap对应属性映射到Post实体 -->

    <!-- 模糊搜索帖子的标题 -->
    <select id="selectPostsByTitleLike" parameterType="string" resultType="com.example.springboot.entity.Forum.Post">
        SELECT
            p.id,
            p.user_id AS userId,
            u.username,
            p.title,
            p.content,
            p.created_at AS createdAt,
            p.updated_at AS updatedAt
        FROM post p
                 LEFT JOIN user u ON p.user_id = u.id
        WHERE p.title LIKE CONCAT('%', #{title}, '%')
        ORDER BY p.created_at DESC
    </select>

    <!-- 新增评论 -->
    <insert id="insertComment" parameterType="com.example.springboot.entity.Forum.Comment" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO comment(post_id, user_id, parent_comment_id, content, created_at, updated_at)
        VALUES(#{postId}, #{userId}, #{parentCommentId}, #{content}, NOW(), NOW())
    </insert>

    <!-- 删除评论 -->
    <delete id="deleteCommentById" parameterType="int">
        DELETE FROM comment WHERE id = #{id}
    </delete>

    <!-- 修改评论内容 -->
    <update id="updateComment" parameterType="com.example.springboot.entity.Forum.Comment">
        UPDATE comment
        SET content = #{content}, updated_at = NOW()
        WHERE id = #{id}
    </update>

    <!-- 查询某帖子下所有评论（带用户名） -->
    <select id="selectCommentsByPostId" parameterType="int" resultType="com.example.springboot.entity.Forum.Comment">
        SELECT
            c.id,
            c.post_id AS postId,
            c.user_id AS userId,
            u.username,
            c.parent_comment_id AS parentCommentId,
            c.content,
            c.created_at AS createdAt,
            c.updated_at AS updatedAt
        FROM comment c
                 LEFT JOIN user u ON c.user_id = u.id
        WHERE c.post_id = #{postId}
        ORDER BY c.created_at ASC
    </select>

    <!-- 点赞 -->
    <insert id="insertLikePost" parameterType="com.example.springboot.entity.Forum.LikePost" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO like_post(user_id, post_id, created_at) VALUES(#{userId}, #{postId}, NOW())
    </insert>

    <delete id="deleteLikePost" parameterType="map">
        DELETE FROM like_post WHERE user_id = #{userId} AND post_id = #{postId}
    </delete>

    <select id="selectLikePost" parameterType="map" resultType="com.example.springboot.entity.Forum.LikePost">
        SELECT * FROM like_post WHERE user_id = #{userId} AND post_id = #{postId} LIMIT 1
    </select>

    <!-- 收藏 -->
    <insert id="insertFavoritePost" parameterType="com.example.springboot.entity.Forum.FavoritePost" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO favorite_post(user_id, post_id, created_at) VALUES(#{userId}, #{postId}, NOW())
    </insert>

    <delete id="deleteFavoritePost" parameterType="map">
        DELETE FROM favorite_post WHERE user_id = #{userId} AND post_id = #{postId}
    </delete>

    <select id="selectFavoritePost" parameterType="map" resultType="com.example.springboot.entity.Forum.FavoritePost">
        SELECT * FROM favorite_post WHERE user_id = #{userId} AND post_id = #{postId} LIMIT 1
    </select>

    <!-- 举报 -->
    <insert id="insertReportPost" parameterType="com.example.springboot.entity.Forum.ReportPost" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO report_post(reporter_id, post_id, reason, created_at) VALUES(#{reporterId}, #{postId}, #{reason}, NOW())
    </insert>

    <select id="selectReportPostsByPostId" parameterType="int" resultType="com.example.springboot.entity.Forum.ReportPost">
        SELECT * FROM report_post WHERE post_id = #{postId} ORDER BY created_at DESC
    </select>

    <!-- 查询某用户的所有帖子 -->
    <select id="selectPostsByUserId" parameterType="int" resultType="com.example.springboot.entity.Forum.Post">
        SELECT
            p.id,
            p.user_id AS userId,
            u.username,
            p.title,
            p.content,
            p.created_at AS createdAt,
            p.updated_at AS updatedAt,
            IFNULL(like_counts.count, 0) AS likeCount
        FROM post p
                 LEFT JOIN user u ON p.user_id = u.id
                 LEFT JOIN (
            SELECT post_id, COUNT(*) AS count
            FROM like_post
            GROUP BY post_id
        ) like_counts ON like_counts.post_id = p.id
        WHERE p.user_id = #{userId}
        ORDER BY p.created_at DESC
    </select>

    <!-- 查询某帖子的点赞数量 -->
    <select id="selectLikeCountByPostId" parameterType="int" resultType="int">
        SELECT COUNT(*) FROM like_post WHERE post_id = #{postId}
    </select>

    <!-- 查询用户点赞过的帖子 -->
    <select id="selectLikedPostsByUserId" parameterType="int" resultType="com.example.springboot.entity.Forum.Post">
        SELECT
            p.id,
            p.user_id AS userId,
            u.username,
            p.title,
            p.content,
            p.created_at AS createdAt,
            p.updated_at AS updatedAt
        FROM post p
                 INNER JOIN like_post lp ON p.id = lp.post_id
                 LEFT JOIN user u ON p.user_id = u.id
        WHERE lp.user_id = #{userId}
        ORDER BY lp.created_at DESC
    </select>

    <!-- 查询用户收藏过的帖子 -->
    <select id="selectFavoritedPostsByUserId" parameterType="int" resultType="com.example.springboot.entity.Forum.Post">
        SELECT
            p.id,
            p.user_id AS userId,
            u.username,
            p.title,
            p.content,
            p.created_at AS createdAt,
            p.updated_at AS updatedAt
        FROM post p
                 INNER JOIN favorite_post fp ON p.id = fp.post_id
                 LEFT JOIN user u ON p.user_id = u.id
        WHERE fp.user_id = #{userId}
        ORDER BY fp.created_at DESC
    </select>

</mapper>
点赞数获取不到，查看不了举报