(Vue+SpringBoot+elementUi+WangEditer)仿论坛项目

项目使用到的技术与库

1.前端 Vue2 elementUi Cookie WangEditer

2.后端 SpringBoot Mybatis-Plus

3.数据库 MySql

一、效果展示

1.1主页效果:

1.2 文章编辑页面:

1.3 成功发布文章

1.4 文章关键字搜索提示

1.5 文章查询结果展示

1.6 文章内容及交互展示


二、表单设计的sql

用户:

sql 复制代码
create table paitool.user
(
    id                int auto_increment
        primary key,
    account           varchar(255)                                          not null,
    password          varchar(255)                                          not null,
    phone             varchar(20)                                           null,
    address           varchar(255)                                          null,
    isVip             tinyint(1)                  default 0                 null,
    email             varchar(255)                                          null,
    registration_date datetime                    default CURRENT_TIMESTAMP null,
    last_login        datetime                                              null,
    status            enum ('active', 'inactive') default 'active'          null,
    constraint account_UNIQUE
        unique (account),
    constraint email_UNIQUE
        unique (email),
    constraint phone_UNIQUE
        unique (phone)
);

文章:

sql 复制代码
create table paitool.forum_posts
(
    id         int auto_increment
        primary key,
    title      varchar(255)                            not null,
    content    text                                    not null,
    author_id  int                                     not null,
    created_at timestamp     default CURRENT_TIMESTAMP null,
    updated_at timestamp     default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,
    heat_value int           default 0                 null,
    rating     decimal(3, 2) default 0.00              null,
    tag        varchar(10)   default '其它'              null,
    constraint forum_posts_ibfk_1
        foreign key (author_id) references paitool.user (id)
);

文章交互表-点赞:

sql 复制代码
create table paitool.forum_post_likes
(
    user_id int not null,
    post_id int not null,
    primary key (user_id, post_id),
    constraint forum_post_likes_ibfk_1
        foreign key (user_id) references paitool.user (id),
    constraint forum_post_likes_ibfk_2
        foreign key (post_id) references paitool.forum_posts (id)
);

文章交互表-收藏:

sql 复制代码
create table paitool.forum_post_favorites
(
    user_id int not null,
    post_id int not null,
    primary key (user_id, post_id),
    constraint forum_post_favorites_ibfk_1
        foreign key (user_id) references paitool.user (id),
    constraint forum_post_favorites_ibfk_2
        foreign key (post_id) references paitool.forum_posts (id)
);

文章交互表-评论

sql 复制代码
create table paitool.forum_comments
(
    id           int auto_increment
        primary key,
    post_id      int                                 not null,
    user_id      int                                 not null,
    comment_text text                                not null,
    created_at   timestamp default CURRENT_TIMESTAMP null,
    constraint forum_comments_ibfk_1
        foreign key (user_id) references paitool.user (id),
    constraint forum_comments_ibfk_2
        foreign key (post_id) references paitool.forum_posts (id)
);

三、前端代码

3.1 论坛主页

Html:

html 复制代码
<template>
    <div id="forumLayOut">

        <div id="Top" style="background-color: rgb(250, 250, 250); padding-top: 20px">

            <div id="serchBorder" style="padding-bottom: 13px;">
                <!-- 搜索框 -->
                <el-autocomplete v-model="searchKeyWord" :fetch-suggestions="querySearchAsync" :trigger-on-focus="false"
                    placeholder="请输入关键字" style="width: 300px;" @select="handleSelect">
                </el-autocomplete>
                <el-button type="primary" @click="onSubmit">查询</el-button>
            </div>

            <!-- 分类查询 -->
            <div>

                <div style="margin-bottom: 15px;">
                    <el-checkbox-group v-model="checkboxGroup1" :max="1">
                        <el-checkbox-button v-for="city in cities" :label="city" :key="city">{{ city
                            }}</el-checkbox-button>
                    </el-checkbox-group>
                </div>

            </div>


            <div style="height: 380px; width: 100%;">
                <!-- 轮播图 -->
                <div class="block" style="width: 30%; float: left; margin-left: 5%; height: 400px;">
                    <el-carousel height="350px" style="width: 100%;  ">
                        <el-carousel-item v-for="item in 4" :key="item">
                            <img src="https://img95.699pic.com/photo/50035/3211.jpg_wh860.jpg" alt="风景测试">
                            <h3 class="small">{{ item }}</h3>
                        </el-carousel-item>
                    </el-carousel>
                </div>


                <div style=" height: 350px; background-color: rgb(250, 250, 250); width: 25%; float: left;
                border: 1px solid rgb(240, 240, 242);  margin-left: 3%;">


                    <div style="height: 50px; width: 100%; background-color: rgb(245, 245, 245); ">
                        <i class="el-icon-share"></i>
                        <div><b>热门</b></div>
                        <hr>
                    </div>

                    <div class="link-container">
                        <a href="#" class="link" id="TurnLink">杀死谷歌,成为AI时代的搜索皇帝!</a>
                        <p style="color: gray;">Perplexity CEO 最新四万字访谈</p>
                    </div>

                    <div class="link-container">
                        <a href="#" class="link" id="TurnLink">重写系统后痛批:这门语言烂透了!</a>
                        <p style="color: gray;">耗时18个月,开发者弃TypeScript投Rust</p>
                    </div>
                    <div class="link-container">
                        <a href="#" class="link" id="TurnLink">Shire 编码智能体语言</a>
                        <p style="color: gray;">打造你的专属 AI 编程助手</p>
                    </div>


                </div>


                <div style="float: left;  margin-left: 3%; height: 350px; background-color: rgb(250, 250, 250); width: 25%; float: left;
                     border: 1px solid rgb(240, 240, 242); ">


                    <div style="height: 50px; width: 100%; background-color: rgb(245, 245, 245); ">
                        <i class="el-icon-message-solid"></i>
                        <div><b>头条</b></div>
                        <hr>
                    </div>

                    <div class="link-container">
                        <a href="#" class="link" id="TurnLink">史上开发最久的游戏!</a>
                        <p style="color: gray;">耗时 22 年,5 名打工人凑了几百欧就开工,只剩 1 人坚守到发布...</p>
                    </div>

                    <div class="link-container">
                        <a href="#" class="link" id="TurnLink">实习期间创下 Transformer</a>
                        <p style="color: gray;">他说:当年整个 AI 圈都无法预见我们今天的高度</p>
                    </div>

                    <div class="link-container">
                        <a href="#" class="link" id="TurnLink">杀死谷歌,成为AI时代的搜索皇帝!</a>
                        <p style="color: gray;">Perplexity CEO 最新四万字访谈</p>
                    </div>

                </div>
            </div>


        </div>

        <el-divider></el-divider>
        <div id="bottom">

            <!-- Tabs 标签页  -->
            <el-tabs v-model="activeName" @tab-click="handleClick" style="padding-left: 2em; ">

                <el-tab-pane label="我的文章" name="first">
                    <div class="parent-div" style="min-height: 500px">

                        <div v-if="posts.length === 0">
                            <el-empty :image-size="200"></el-empty>
                        </div>

                        <div class="custom-card" v-for="(post, index) in posts" :key="index"
                            @click="getForumPostDetail(post.id)">
                            <div class="card-content">
                                <h1 class="card-title">标题: {{ post.title }}</h1>
                                <div class="card-meta">
                                    <span>作者: {{ post.account }}</span>
                                    <span>标签: {{ post.tag }}</span>
                                    <span><i class="el-icon-view">{{ post.heatValue }}</i></span>
                                </div>
                                <div class="card-rating">
                                    <span>文章评分:</span>
                                    <el-rate v-model="post.rating" disabled show-score text-color="#ff9900"
                                        score-template="{value}" style="display: inline-block;"></el-rate>
                                </div>
                            </div>
                        </div>
                    </div>


                </el-tab-pane>


                <el-tab-pane label="推荐文章" name="second">

                    <div v-if="posts.length === 0">
                        <el-empty :image-size="200"></el-empty>
                    </div>

                    <div class="custom-card" v-for="(post, index) in posts" :key="index"
                        @click="getForumPostDetail(post.postId)">
                        <div class="card-content">
                            <h1 class="card-title">标题: {{ post.title }}</h1>
                            <div class="card-meta">
                                <span>作者: {{ post.account }}</span>
                                <span>标签: {{ post.tag }}</span>
                                <span><i class="el-icon-view">{{ post.heat_value }}</i></span>
                            </div>
                            <div class="card-rating">
                                <span>文章评分:</span>
                                <el-rate v-model="post.rating" disabled show-score text-color="#ff9900"
                                    score-template="{value}" style="display: inline-block;"></el-rate>
                            </div>
                        </div>
                    </div>

                </el-tab-pane>
                <el-tab-pane label="热门文章" name="third">



                    <div class="parent-div" style="min-height: 500px">
                        <div v-if="posts.length === 0">
                            <el-empty :image-size="200"></el-empty>
                        </div>
                        <div class="custom-card" v-for="(post, index) in posts" :key="index"
                            @click="getForumPostDetail(post.id)">
                            <div class="card-content">
                                <h1 class="card-title">标题: {{ post.title }}</h1>
                                <div class="card-meta">
                                    <span>作者: {{ post.account }}</span>
                                    <span>标签: {{ post.tag }}</span>
                                    <span><i class="el-icon-view">{{ post.heatValue }}</i></span>
                                </div>
                                <div class="card-rating">
                                    <span>文章评分:</span>
                                    <el-rate v-model="post.rating" disabled show-score text-color="#ff9900"
                                        score-template="{value}" style="display: inline-block;"></el-rate>
                                </div>
                            </div>
                        </div>
                    </div>




                </el-tab-pane>

                <el-tab-pane label="优质文章" name="fourth">


                    <div class="parent-div" style="min-height: 500px">
                        <div v-if="posts.length === 0">
                            <el-empty :image-size="200"></el-empty>
                        </div>
                        <div class="custom-card" v-for="(post, index) in posts" :key="index"
                            @click="getForumPostDetail(post.id)">
                            <div class="card-content">
                                <h1 class="card-title">标题: {{ post.title }}</h1>
                                <div class="card-meta">
                                    <span>作者: {{ post.account }}</span>
                                    <span>标签: {{ post.tag }}</span>
                                    <span><i class="el-icon-view">{{ post.heatValue }}</i></span>
                                </div>
                                <div class="card-rating">
                                    <span>文章评分:</span>
                                    <el-rate v-model="post.rating" disabled show-score text-color="#ff9900"
                                        score-template="{value}" style="display: inline-block;"></el-rate>
                                </div>
                            </div>
                        </div>
                    </div>



                </el-tab-pane>

                <el-tab-pane label="我的收藏" name="fifth">
                    <div v-if="posts.length === 0">
                        <el-empty :image-size="200"></el-empty>
                    </div>



                    <div class="parent-div" style="min-height: 500px">
                        <div v-if="posts.length === 0">
                            <el-empty :image-size="200"></el-empty>
                        </div>
                        <div class="custom-card" v-for="(post, index) in posts" :key="index"
                            @click="getForumPostDetail(post.id)">
                            <div class="card-content">
                                <h1 class="card-title">标题: {{ post.title }}</h1>
                                <div class="card-meta">
                                    <span>作者: {{ post.account }}</span>
                                    <span>标签: {{ post.tag }}</span>
                                    <span><i class="el-icon-view">{{ post.heatValue }}</i></span>
                                </div>
                                <div class="card-rating">
                                    <span>文章评分:</span>
                                    <el-rate v-model="post.rating" disabled show-score text-color="#ff9900"
                                        score-template="{value}" style="display: inline-block;"></el-rate>
                                </div>
                            </div>
                        </div>
                    </div>


                </el-tab-pane>
            </el-tabs>

            <el-button type="warning" round id="iWantPost" @click="navigateToPostEdit">我要发布文章</el-button>

        </div>




    </div>
</template>

js:

javascript 复制代码
<script>
import axios from 'axios';
import Cookies from 'js-cookie';

const cityOptions = ['新闻报道', '科技动态', '生活时尚', '教育学习', '健康养生'];
export default {
    components: {

    },

    data() {
        return {
            searchKeyWord: '',
            suggestions: [], // 添加这个属性
            checkboxGroup1: [],
            cities: cityOptions,
            activeName: 'first',
            currentPage1: 5,
            currentPage2: 5,
            currentPage3: 5,
            currentPage4: 4,
            posts: [
            ],
            
        }
    },
    methods: {
        onSubmit() {
            this.$router.push({
                name: 'ArticalSearchView',
                params: {
                    searchKeyWord: this.searchKeyWord
                }
            })


        },
        handleClick(tab) {

            // 我的文章
            if (tab.name === 'first') {
                this.posts = []
                this.getMyArticle();

            }

            // 推荐文章
            if (tab.name === 'second') {
                this.posts = [];

                axios.get('/api/forum/getAllForumPost', {
                    params: {
                        pageSize: 1,
                        pageNumber: 10
                    }
                }).then((response) => {
                    console.log(response.data.data);
                    
                    this.posts = response.data.data;
                });


            }
            // 热门文章
            if (tab.name === 'third') {
                this.posts = [];

                axios.get('/api/forum/getHotPosts').then((response) => {
                    console.log(response.data.data);
                    this.posts = response.data.data;
                });


            }

            // 优质文章
            if (tab.name === 'fourth') {
                this.posts = [];

                axios.get('/api/forum/getOutStandPosts').then((response) => {
                    console.log(response.data.data);
                    this.posts = response.data.data;
                });

            }

            // 我的收藏
            if (tab.name === 'fifth') {
                this.posts = [];
                const id = Cookies.get("userId");

                if (id === null) {
                    this.$message({
                        message: '请先登录',
                        type: 'warning'
                    });
                    return;
                }

                axios.get('/api/forum/getMyFavorite', {
                    params: {
                        id: id
                    }
                }).then((response) => {
                    console.log(response.data.data);
                    this.posts = response.data.data;
                });

            }



        },

        // 处理分页功能
        handleSizeChange(val) {
            console.log(`每页 ${val} 条`);
        },
        handleCurrentChange(val) {
            console.log(`当前页: ${val}`);
        },

        navigateToPostEdit() {
            this.$router.push({ name: 'ForumPostEditView' });
        },


        // 跳转到文章详情
        getForumPostDetail(postId) {
            console.log("getForumPostDetail");
            console.log(postId);
            this.$router.push(`/post/${postId}`);
        },

        getMyArticle() {
            this.posts = [];
            const id = Cookies.get("userId");

            if (id === null) {
                this.$message({
                    message: '请先登录',
                    type: 'warning'
                });
                return;
            } else {
                axios.get('/api/forum/MyArticle', {
                    params: {
                        id: id
                    }
                }).then((response) => {
                    console.log(response.data.data);
                    this.posts = response.data.data;
                })

            }

        },
        // 异步获取建议列表
        querySearchAsync(queryString, cb) {
            if (queryString.length === 0) {
                return cb([]); // 当查询字符串为空时,直接返回空数组
            }
            axios.get('/api/forum/getLikeSearch', { params: { keyword: queryString } })
                .then(response => {
                    // 确保从后端返回的数据中提取出正确的数组
                    const results = response.data.data || [];
                    // 调用callback函数,传入搜索结果
                    cb(results);
                })
                .catch(error => {
                    console.error('Error fetching search suggestions:', error);
                   
                    cb([]);
                });
        },
        // 处理选择事件
        handleSelect(item) {
            this.searchKeyWord = item.value; 
            this.onSubmit();
        }


    },

    mounted() {
        this.getMyArticle();

    }


}

</script>

css:

css 复制代码
<style scoped>
#forumLayOut {

    background-color: white;
    height: auto;
    width: 100%;
    line-height: normal;
}

#serchBorder {

    line-height: normal;
}

.el-carousel__item h3 {
    color: #475669;
    font-size: 14px;
    opacity: 0.75;
    line-height: 150px;
    margin: 0;
}

.el-carousel__item:nth-child(2n) {
    background-color: #99a9bf;
}

.el-carousel__item:nth-child(2n+1) {
    background-color: #d3dce6;
}

#Pagination {
    align-self: center;
    /* 居中对齐 */
    margin-bottom: 1rem;
    /* 可选,增加底部边距 */
    margin-top: 10%;
}


#iWantPost {
    position: fixed;
    /* 设置为固定定位 */
    bottom: 60px;
    /* 距离底部的距离,可根据需要调整 */
    right: 40px;
    /* 距离右侧的距离,可根据需要调整 */
}


.el-tabs__content {
    overflow: hidden;
    position: relative;
    height: auto;
}

.custom-card {
    background-color: #ffffff;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    margin-bottom: 16px;
    transition: box-shadow 0.3s ease-in-out;
}

.custom-card:hover {
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    cursor: pointer;
    background-color: rgb(245, 245, 245);
}

.card-content {
    padding: 16px;
}

.card-title {
    font-size: 1.2em;
    margin-bottom: 8px;
    color: #333;
}

.card-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
    color: #666;
}

.card-rating {
    color: #666;
}

.link-container {
    line-height: normal;
    float: left;
    width: 100%;
    text-align: left;
    padding-left: 40px;
    padding-top: 10px;
}

.link {
    text-decoration: none;
    font-size: large;
    color: black;
}

.link:hover {
    text-decoration: underline;
}
</style>

3.2 发布文章页面

html 复制代码
<template>

    <div style="border: 1px solid #ccc; line-height: normal; height: 100%;">
        <div>

            <el-form :inline="true" :model="formInline" class="demo-form-inline">
                <el-form-item label="文章标题">
                    <el-input v-model="formInline.title" placeholder="请输入文章标题" maxlength="20"></el-input>
                </el-form-item>


                <el-form-item label="类别">
                    <el-select v-model="formInline.category" placeholder="请选择文章类别">
                        <el-option label="新闻报道" value="news"></el-option>
                        <el-option label="科技动态" value="technology"></el-option>
                        <el-option label="生活时尚" value="lifestyle"></el-option>
                        <el-option label="教育学习" value="education"></el-option>
                        <el-option label="健康养生" value="health"></el-option>
                    </el-select>
                </el-form-item>


                <el-form-item>
                    <el-button type="primary" @click="onSubmit"
                        v-loading.fullscreen.lock="fullscreenLoading">提交</el-button>
                </el-form-item>
            </el-form>

        </div>
        <Toolbar style="border-bottom: 1px solid #ccc" :editor="editor" :defaultConfig="toolbarConfig" :mode="mode" />
        <Editor style="height: 500px; overflow-y: hidden; height: 100%;" v-model="html" :defaultConfig="editorConfig"
            :mode="mode" @onCreated="onCreated" />
    </div>

</template>


<script>
import Vue from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import axios from 'axios'
import Cookies from 'js-cookie'


export default Vue.extend({
    components: { Editor, Toolbar },
    data() {
        return {
            editor: null,
            html: ' ',
            toolbarConfig: {},
            editorConfig: { placeholder: '请输入内容...' },
            mode: 'default', // or 'simple'
            formInline: {
                title: '',
                category: ''
            },
            fullscreenLoading: false
        }
    },
    methods: {
        onCreated(editor) {
            this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错

        },
        onSubmit() {

            this.fullscreenLoading = true;

            const userId = Cookies.get('userId'); // 获取并转换userId

            axios.post('/api/forum/add', {

                "title": this.formInline.title,

                "content": this.editor.getHtml(), 

                "authorId": userId,

                "tag": this.formInline.category,

            }).then((response) => {

                console.log(response.data);

                this.fullscreenLoading = false;

                this.$router.push({ name: 'ForumSucessPostView' });

            }).catch(error => {

                console.error(error);

                this.fullscreenLoading = false;

            });

        },

    },
    mounted() {

    },
    beforeDestroy() {
        const editor = this.editor
        if (editor == null) return
        editor.destroy() // 组件销毁时,及时销毁编辑器


    },

})
</script>


<style src="@wangeditor/editor/dist/css/style.css"></style>

3.3 文章发布成功页面


3.4 查看文章页面

html:

html 复制代码
<template>
    <div style="line-height: normal; background-color: rgb(246, 247, 249); height: auto; min-height: 80%;">

        <!-- 文章信息 -->
        <div style="padding-top: 10px; width: auto; min-width: 40%;">
            <!-- 实现文字垂直居中 -->
            <div id="Infor" style="background-color: white;">
                <h1 style="font-size: 28px; text-align: center">文章标题:{{title}}</h1>
                <span>创作者:{{author}}</span>
                <span style="margin-left: 20px;">创作日期:{{createAt}}</span>
                <span style="margin-left: 20px;"><i class="el-icon-view">{{heatValue}}</i></span>
            </div>

            <el-divider><i class="el-icon-mobile-phone"></i></el-divider>


            <!-- 文章内容展示区 -->
            <div id="contentDisplay">
                <div v-html="content"
                    style="padding-left: 2em; padding-top: 15px; padding-right: 2em; padding-bottom: 30px;"></div>
            </div>
        </div>

        <el-divider><i class="el-icon-edit"></i></el-divider>

        <!-- 交互按键 -->
        <div id="buttom">
            <el-button type="warning" round @click="getBackToForum">返回到论坛</el-button>
            <el-button type="warning" icon="el-icon-star-off" circle @click="PostFavorite"></el-button>
            <el-button type="danger" icon="el-icon-thumb" circle @click="PostLike"></el-button>

        </div>

        <!-- 评论区 -->
        <div id="commentListShow">
            <el-card class="box-card">
                <div slot="header" class="clearfix">
                    <span>评论详情</span>
                </div>
                <div id="commentInputArea">

                    <el-input type="textarea" placeholder="请您输入友善的评论吧" v-model="textarea" maxlength="300"
                        show-word-limit id="inputFrame" :clearable="clearAble" resize="none">
                    </el-input>
                    <div style="margin-top: 10px; padding-bottom: 50px;">
                        <el-button type="primary" @click="SubmitComment">发表评论</el-button>
                        <el-button type="primary" @click="CancelComment">取消评论</el-button>
                    </div>
                </div>

                <div id="commentList">
                    <div class="comment-card" v-for="comment in comments" :key="comment.id">
                        <div class="comment-head">
                            <h1 class="username">{{ comment.account }}</h1>
                            <p class="created-at">发表于:{{ comment.createdAt }}</p>
                        </div>
                        <el-divider></el-divider>
                        <p class="comment-text">{{ comment.commentText }}</p>
                    </div>
                </div>

            </el-card>
        </div>


    </div>
</template>

script:

html 复制代码
<script>
import axios from 'axios';
import Cookies from 'js-cookie';


export default{
    data() {
        return {
            postId: '',
            title: '',
            content: '',
            value1: null,
            textarea: '',
            userId:'',
            clearAble: true,
            comments:{},
            author:'',
            createAt:'',
            heatValue:'',
        }
    },
    created() {

        this.postId = this.$route.params.postId;
        this.fetchPostDetail(this.$route.params.postId);
        this.userId = Cookies.get('userId');
    },
    mounted() {
        this.readComment();
        
    },
    methods: {
        // 前端实现路径传参
        async fetchPostDetail(postId) {
            try {
                this.fullscreenLoading = true;
                const url = `/api/forum/post/${postId}`;
                // 发起GET请求
                const response = await axios.get(url);

                if (response.status === 200) {
                    // 请求成功,处理响应数据
                    const postData = response.data;
                    console.log('文章详情:', postData);
                    // 更新组件状态或执行其他操作
                    this.title = response.data.data.title;
                    this.content = response.data.data.content;
                    this.author = response.data.data.account;
                    this.createAt = response.data.data.createdAt;
                    this.heatValue = response.data.data.heatValue;
                    this.fullscreenLoading = false;
                } else {
                    console.error('请求失败,状态码:', response.status);
                }
            } catch (error) {
                console.error('请求错误:', error);
            }
        },

        getBackToForum() {
            this.$router.push({ name: 'forum' });
        },


        // 取消评论
        CancelComment(){
            this.textarea = '';

        },

        // 执行点赞按钮
        PostLike(){
            this.isLogin();
            axios.get('/api/forum/like',{
                params:{
                    postId : this.postId,
                    userId : this.userId
                }
            }).then((response)=>{
               this.MessageNotify(response);
            })
        },

        // 执行收藏按钮
        PostFavorite(){
            this.isLogin();
            axios.get('/api/forum/favorite',{
                params:{
                    postId : this.postId,
                    userId : this.userId
                }
            }).then((response)=>{
               this.MessageNotify(response);
            })


        },
        SubmitComment(){

            this.isLogin();

            axios.post('/api/forum/writeComment',{
                postId : this.postId,
                userId : this.userId,
                commentText : this.textarea
            }).then((response)=>{
                this.MessageNotify(response);
                this.textarea = '';
                this.readComment();
            })

            console.log("submit");
        },

        isLogin(){
            if(Cookies.get('userId') == null){
                this.$message.error('请先登录');
                return;
            }
        },

        // 消息提醒
        MessageNotify(response){
            if(response.data.code == 200){
                this.$message.success(response.data.data);
            }else{
                console.log(response.data);
                this.$message.error(response.data.message);
            }
        },

        readComment(){
            axios.get('/api/forum/getComment',{
                params:{
                    postId : this.postId
                }
            }).then((response)=>{
                this.comments = response.data.data;
            })
        },


    }

}


</script>

css:


3.5 文章搜索页面

html:

html 复制代码
<template>
    <div id="layout">

        <div id="searchFrame">
            <div id="InputFrame">
                <el-input type="textarea" placeholder="请输入内容" v-model="textarea" rows="1" resize="none"
                    style="font-size: larger; width: 80%;">
                </el-input>
                <el-button type="warning" @click="SearchSubmit"  icon="el-icon-search">查询</el-button>
            </div>
        </div>

        <div id="excess">

            <div id="Interate">
                <i class="el-icon-search"> 搜索结果</i>
            </div>

        </div>

        
        <div id="SearchContent">

            <div v-if="posts.length === 0">
                <el-empty :image-size="200"></el-empty>
            </div>



            <div class="custom-card" v-for="(post, index) in posts" :key="index" @click="getForumPostDetail(post.id)">
                <div class="card-content">
                    <h1 class="card-title">标题: {{ post.title }}</h1>
                    <div class="card-meta">
                        <span>作者: {{ post.account }}</span>
                        <span>标签: {{ post.tag }}</span>
                        <span><i class="el-icon-view">{{ post.heatValue }}</i></span>
                    </div>
                    <div class="card-rating">
                        <span>文章评分:</span>
                        <el-rate v-model="post.rating" disabled show-score text-color="#ff9900" score-template="{value}"
                            style="display: inline-block;"></el-rate>
                    </div>
                </div>
            </div>
        </div>


    </div>
</template>

script:

html 复制代码
<script>
import axios from 'axios';


export default {
    data() {
        return {
            textarea: '',
            searchKeyWord: '',
            posts: [],
        }
    },

    methods: {
        SearchSubmit() {
            console.log(this.textarea);
            axios.get('/api/forum/search', {
                params: {
                    searchKeyWord: this.textarea
                }
            }).then((response) => {

                if (response.data.code !== 200) {
                    this.$notify({
                        title: '警告',
                        message: '搜索失败',
                        type: 'warning'
                    });
                }

                console.log(response.data.data);
                this.posts = response.data.data;
            });
        },


        Search() {

            axios.get('/api/forum/search', {
                params: {
                    searchKeyWord: this.searchKeyWord
                }
            }).then((response) => {

                if (response.data.code !== 200) {
                    this.$notify({
                        title: '警告',
                        message: '搜索失败',
                        type: 'warning'
                    });
                }

                console.log(response.data.data);
                this.posts = response.data.data;
            });
        },

         // 跳转到文章详情
         getForumPostDetail(postId) {
            console.log("getForumPostDetail");
            console.log(postId);
            this.$router.push(`/post/${postId}`);
        },



    },

    mounted() {
        this.searchKeyWord = this.$route.params.searchKeyWord;
        this.textarea = this.searchKeyWord;
        this.Search();
    }

}
</script>

css:

html 复制代码
<style scoped>
#layout {
    width: 100%;
    min-height: 90%;
    background-color: rgb(245, 246, 247);
    line-height: normal;
}

#SearchContent{
    min-height: 800px;
    

}

#searchFrame {
    height: 70px;
    width: 100%;
    background-color: white;
    box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
    position: -webkit-sticky;
    /* Safari */
    position: sticky;
    top: 0;
    z-index: 1000;
    line-height: normal;
}

#InputFrame {
    width: 40%;
    margin: 0 auto;
    height: 60%;
    padding-top: 15px;
}

#Interate {

    float: left;
    margin-top: 20px;
    margin-left: 20px;
}

#excess {

    height: 61px;
    width: 80%;
    background-color: white;
    margin: 0 auto;
    margin-top: 25px;
    border: 1px solid rgb(245, 245, 245);
    border-radius: 4px;
}



.custom-card {
    background-color: #ffffff;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    margin-bottom: 16px;
    transition: box-shadow 0.3s ease-in-out;
    width: 80%;
    margin: 0 auto;

    border: 1px solid rgb(245, 245, 245);
}

.custom-card:hover {
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    cursor: pointer;
    background-color: rgb(245, 245, 245);
}

.card-content {
    padding: 16px;
}

.card-title {
    font-size: 1.2em;
    margin-bottom: 8px;
    color: #333;
}

.card-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
    color: #666;
}

.card-rating {
    color: #666;
}

.card-rating {
    color: #666;
}
</style>

四、后端代码

4.1项目后端依赖库

XML 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.11</version>
        </dependency>

        <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
             <version>3.5.3.1</version>
        </dependency>

        <!--swagger-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.2</version>
        </dependency>

        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.15</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-spring-web</artifactId>
            <version>3.0.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dashscope-sdk-java</artifactId>
            <version>2.8.2</version>
        </dependency>
        <!--okhttp3 依赖-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>

        <!-- Lombok dependency -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--        验证码模块-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>

4.2工具类Result类与实体类

java 复制代码
public class Result<T> {
    // 状态码常量
    public static final int SUCCESS = 200;
    public static final int ERROR = 500;
    
    private int code; // 状态码
    private String message; // 消息
    private T data; // 数据

    // 构造函数,用于创建成功的结果对象
    private Result(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    // 成功结果的静态方法
    public static <T> Result<T> success(T data) {
        return new Result<>(SUCCESS, "Success", data);
    }


    // 错误结果的静态方法
    public static <T> Result<T> error(String message) {
        return new Result<>(ERROR, message, null);
    }

    // 错误结果的静态方法,可以传入自定义的状态码
    public static <T> Result<T> error(int code, String message) {
        return new Result<>(code, message, null);
    }

    // 获取状态码
    public int getCode() {
        return code;
    }

    // 设置状态码
    public void setCode(int code) {
        this.code = code;
    }

    // 获取消息
    public String getMessage() {
        return message;
    }

    // 设置消息
    public void setMessage(String message) {
        this.message = message;
    }

    // 获取数据
    public T getData() {
        return data;
    }

    // 设置数据
    public void setData(T data) {
        this.data = data;
    }

    // 用于转换为Map类型的方法,方便序列化为JSON
    public Map<String, Object> toMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("code", code);
        map.put("message", message);
        map.put("data", data);
        return map;
    }
}

Entity:

Forumpost:

java 复制代码
@TableName(value ="forum_posts")
@Data
public class ForumPosts implements Serializable {
    /**
     * 
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 
     */
    @TableField(value = "title")
    private String title;
    /**
     * 
     */
    @TableField(value = "content")
    private String content;
    /**
     * 
     */
    @TableField(value = "author_id")
    private Integer authorId;
    /**
     * 
     */
    @TableField(value = "created_at")
    private LocalDateTime createdAt;
    /**
     * 
     */
    @TableField(value = "updated_at")
    private LocalDateTime updatedAt;
    /**
     * 
     */
    @TableField(value = "heat_value")
    private Integer heatValue;
    /**
     * 
     */
    @TableField(value = "rating")
    private BigDecimal rating;
    /**
     * 
     */
    @TableField(value = "tag")
    private String tag;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;

    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (that == null) {
            return false;
        }
        if (getClass() != that.getClass()) {
            return false;
        }
        ForumPosts other = (ForumPosts) that;
        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
            && (this.getTitle() == null ? other.getTitle() == null : this.getTitle().equals(other.getTitle()))
            && (this.getContent() == null ? other.getContent() == null : this.getContent().equals(other.getContent()))
            && (this.getAuthorId() == null ? other.getAuthorId() == null : this.getAuthorId().equals(other.getAuthorId()))
            && (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt()))
            && (this.getUpdatedAt() == null ? other.getUpdatedAt() == null : this.getUpdatedAt().equals(other.getUpdatedAt()))
            && (this.getHeatValue() == null ? other.getHeatValue() == null : this.getHeatValue().equals(other.getHeatValue()))
            && (this.getRating() == null ? other.getRating() == null : this.getRating().equals(other.getRating()))
            && (this.getTag() == null ? other.getTag() == null : this.getTag().equals(other.getTag()));
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
        result = prime * result + ((getTitle() == null) ? 0 : getTitle().hashCode());
        result = prime * result + ((getContent() == null) ? 0 : getContent().hashCode());
        result = prime * result + ((getAuthorId() == null) ? 0 : getAuthorId().hashCode());
        result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
        result = prime * result + ((getUpdatedAt() == null) ? 0 : getUpdatedAt().hashCode());
        result = prime * result + ((getHeatValue() == null) ? 0 : getHeatValue().hashCode());
        result = prime * result + ((getRating() == null) ? 0 : getRating().hashCode());
        result = prime * result + ((getTag() == null) ? 0 : getTag().hashCode());
        return result;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(" [");
        sb.append("Hash = ").append(hashCode());
        sb.append(", id=").append(id);
        sb.append(", title=").append(title);
        sb.append(", content=").append(content);
        sb.append(", authorId=").append(authorId);
        sb.append(", createdAt=").append(createdAt);
        sb.append(", updatedAt=").append(updatedAt);
        sb.append(", heatValue=").append(heatValue);
        sb.append(", rating=").append(rating);
        sb.append(", tag=").append(tag);
        sb.append(", serialVersionUID=").append(serialVersionUID);
        sb.append("]");
        return sb.toString();
    }
}
复制代码
ForumPostLike:
java 复制代码
@Data

@TableName(value ="forum_post_likes")

public class ForumPostLike {

    private int userId;

    private int postId;


}
复制代码
ForumPostFavorites:
java 复制代码
@Data
@TableName(value ="forum_post_favorites")
public class ForumPostFavorites {

    private int userId;

    private int postId;

}
复制代码
ForumComments
java 复制代码
@TableName(value ="forum_comments")
@Data
public class ForumComments implements Serializable {
    /**
     * 
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 
     */
    @TableField(value = "post_id")
    private Integer postId;

    /**
     * 
     */
    @TableField(value = "user_id")
    private Integer userId;

    /**
     * 
     */
    @TableField(value = "comment_text")
    private String commentText;

    /**
     * 
     */
    @TableField(value = "created_at")
    private LocalDateTime createdAt;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;

    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (that == null) {
            return false;
        }
        if (getClass() != that.getClass()) {
            return false;
        }
        ForumComments other = (ForumComments) that;
        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
            && (this.getPostId() == null ? other.getPostId() == null : this.getPostId().equals(other.getPostId()))
            && (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
            && (this.getCommentText() == null ? other.getCommentText() == null : this.getCommentText().equals(other.getCommentText()))
            && (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt()));
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
        result = prime * result + ((getPostId() == null) ? 0 : getPostId().hashCode());
        result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
        result = prime * result + ((getCommentText() == null) ? 0 : getCommentText().hashCode());
        result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
        return result;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(" [");
        sb.append("Hash = ").append(hashCode());
        sb.append(", id=").append(id);
        sb.append(", postId=").append(postId);
        sb.append(", userId=").append(userId);
        sb.append(", commentText=").append(commentText);
        sb.append(", createdAt=").append(createdAt);
        sb.append(", serialVersionUID=").append(serialVersionUID);
        sb.append("]");
        return sb.toString();
    }
}

DTO:

java 复制代码
@Data
public class CommentDTO {


    private int  userId;

    private int postId;

    private String commentText;


}
java 复制代码
@Data
public class ForumAddPostDTO {

    @JsonProperty("title")
    private String title;

    @JsonProperty("content")
    private String content;

    @JsonProperty("authorId")
    private Integer authorId;

    @JsonProperty("tag")
    private String tag;
}

VO:

java 复制代码
@Data
public class ArticleVO {

    private String title;

    private String content;

    private String account;

    private Integer heatValue;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createdAt;

}
java 复制代码
@Data
public class CommentVo {

    private int id;

    private String commentText;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createdAt;

    private String account;
}
java 复制代码
@Data
public class LikeSearchVo {
    private String value;
}

4.3 自定义异常与全局异常

java 复制代码
public class BaseException extends RuntimeException{

    public BaseException(){
    }
    public BaseException(String msg){
        super(msg);
    }
    
}
java 复制代码
public class NotFoundArticleException extends BaseException{
    public NotFoundArticleException(String msg){
        super(msg);
    }

}
java 复制代码
public class AlreadyLikeException extends BaseException{

    public AlreadyLikeException(String msg){
        super(msg);
    }


}

全局异常处理类:

java 复制代码
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler
    public Result exceptionHandler(BaseException ex){
        log.error("异常信息:{}", ex.getMessage());
        return Result.error(ex.getMessage());
    }

}

4.4Controller层

1.ForumPostController

java 复制代码
@RequestMapping("/forum")
@RestController
@Api(tags = "文章管理")
@Slf4j
public class ForumPostController {

    @Autowired
    ForumPostsService forumPostsService;

    @Autowired
    UserService userService;

    @Autowired
    ForumCommentsService forumCommentsService;




    @ApiOperation("新增文章")
    @PostMapping("/add")
    public Result addForumPost(@RequestBody ForumAddPostDTO forumAddPostDTO) throws ParseException {
        ForumPosts forumPosts = new ForumPosts();
        BeanUtils.copyProperties(forumAddPostDTO, forumPosts);
        forumPostsService.save(forumPosts);
        return Result.success("新增成功");
    }


    @GetMapping("/getAllForumPost")
    @ApiOperation("推荐文章查询")
    public Result getAllForumPost(@RequestParam(value="pageSize", defaultValue = "10") int pageSize,
                                  @RequestParam(value="pageNumber", defaultValue = "1") int pageNumber){


        Page<ForumPosts> page = new Page<>(pageNumber, pageSize);
        // 创建查询包装器并指定排序规则
        QueryWrapper<ForumPosts> queryWrapper = new QueryWrapper<>();
        // 假设你想按照创建时间降序排序
        queryWrapper.orderByDesc("created_at");
        Page<ForumPosts> paged = forumPostsService.page(page,queryWrapper);
        List<ForumPosts> postsList = paged.getRecords();
        List<ForumPageVO> posts = new ArrayList<>();


        for (ForumPosts post : postsList) {
            ForumPageVO vo = new ForumPageVO();
            // 获取账户信息
            User user = userService.getById(post.getAuthorId());
            vo.setAccount(user.getAccount());

            // 直接从ForumPosts对象复制其他字段
            vo.setTag(post.getTag());
            vo.setRating(post.getRating()); // 如果需要字符串形式
            vo.setTitle(post.getTitle());
            vo.setPostId(post.getId());
            vo.setHeat_value(post.getHeatValue());
            // 添加到列表
            posts.add(vo);
        }


        return Result.success(posts);

    }


//    文章阅读
    @ApiOperation("读取文章")
    @GetMapping("post/{id}")
    public Result<ArticleVO> readArtical(@PathVariable int id){
        ArticleVO articleVO =  forumPostsService.readArticle(id);
        return Result.success(articleVO);
    }


//   我的文章功能
    @ApiOperation("我的文章")
    @GetMapping("/MyArticle")
    public Result getMyArticle(@Param("id") int id){
        List<ForumPosts> forumPostsList = forumPostsService.getByAuthorId(id);
        return Result.success(forumPostsList);
    }

//    热门文章功能
    @ApiOperation("热门文章")
    @GetMapping("/getHotPosts")
    public Result getHotPosts(){
        QueryWrapper<ForumPosts> queryWrapper = new QueryWrapper();
        queryWrapper.orderByDesc("heat_value");
        List<ForumPosts> forumPosts = forumPostsService.list(queryWrapper);
        return Result.success(forumPosts);
    }

    //    热门文章功能
    @ApiOperation("优质文章")
    @GetMapping("/getOutStandPosts")
    public Result getOutStandPosts(){

        QueryWrapper<ForumPosts> queryWrapper = new QueryWrapper();
        queryWrapper.orderByDesc("rating");
        List<ForumPosts> forumPosts = forumPostsService.list(queryWrapper);
        return Result.success(forumPosts);
    }


    //  我的收藏
    @ApiOperation("我的收藏")
    @GetMapping("/getMyFavorite")
    public Result getMyFavorite(@Param("id") int id){

       List<ForumPosts> forumPostsList =  forumPostsService.getMyFavorite(id);

        return Result.success(forumPostsList);
    }


//    文章查询
    @ApiOperation("文章查询")
    @GetMapping("/search")
    public Result postSearch(@RequestParam("searchKeyWord") String searchText){

        if (searchText == null){
            return Result.error("不能输入为空噢");
        }
        LambdaQueryWrapper<ForumPosts> lambdaQueryWrapper = new LambdaQueryWrapper<>();

//        TODO:通过LamdaQueryWrapper 模糊查询
        // 模糊查询title字段,%searchText%会被自动添加
        lambdaQueryWrapper.like(ForumPosts::getTitle, searchText);

        // 假设这里有一个service接口用于操作ForumPosts表
        List<ForumPosts> postsList = forumPostsService.list(lambdaQueryWrapper);

        // 根据你的Result类的具体实现,返回查询结果
        return Result.success(postsList);
    }

    /**
     * 标题模糊查询
     * @param keyword 关键词
     * @return 匹配的标题列表
     */
    @GetMapping("/getLikeSearch")
    @ApiOperation("标题模糊查询")
    public Result getLikeSearch(@RequestParam String keyword){
        List<LikeSearchVo> titles = forumPostsService.findTitlesByKeyword(keyword);
        return Result.success(titles);
    }


//    用户交互
    //    点赞
    @ApiOperation("点赞")
    @GetMapping("/like")
    public Result PostLike(@RequestParam("postId") int postId,@RequestParam("userId") int userId){
        String result =  forumPostsService.PostLike(postId,userId);
        return Result.success(result);
    }

//    收藏
    @ApiOperation("收藏")
    @GetMapping("/favorite")
    public Result PostFavorite(@RequestParam("postId") int postId,@RequestParam("userId") int userId){

        String result = forumPostsService.PostFavorite(postId,userId);

        return Result.success(result);
    }

//    写评论
  @ApiOperation("写评论")
  @PostMapping("/writeComment")
   public Result WriteComment(@RequestBody CommentDTO commentdto){

      ForumComments forumComments = new ForumComments();
      BeanUtils.copyProperties(commentdto,forumComments);
      forumCommentsService.save(forumComments);
      return Result.success("评论成功");
  }


//    读评论
    @ApiOperation("读取评论")
    @GetMapping("/getComment")
    public Result<List<CommentVo>> GetComment(@RequestParam("postId") int postId){
        List<CommentVo> comment = forumCommentsService.getComment(postId);
        return Result.success(comment);
    }


}

4.5 Service层

java 复制代码
public interface ForumPostsService extends IService<ForumPosts> {

    List<ForumPosts> getByAuthorId(Integer id);

    String PostLike(int postId, int userId);

    String PostFavorite(int postId, int userId);

    ArticleVO readArticle(int id);

    List<ForumPosts> getMyFavorite(int id);

    List<LikeSearchVo> findTitlesByKeyword(String keyword);
}
java 复制代码
public interface ForumCommentsService extends IService<ForumComments> {

    List<CommentVo> getComment(int postId);
}

4.6 Mapper层

java 复制代码
public interface ForumCommentsMapper extends BaseMapper<ForumComments> {

}
java 复制代码
@Mapper
public interface ForumPostFavoritesMapper extends BaseMapper<ForumPostFavorites> {
}
java 复制代码
@Mapper
public interface ForumPostLikeMapper extends BaseMapper<ForumPostLike> {
}
java 复制代码
@Mapper
public interface ForumPostsMapper extends BaseMapper<ForumPosts> {


    @Select("select  * from paitool.user as a,paitool.forum_posts as b where  a.id=b.author_id and a.id = #{id}")
    List<ForumPosts> getByAuthorId(Integer id);
    

}

五、进阶思路

1.通过ElasticSearch优化搜索引擎

2.使用Redis存储热门文章,以减少数据库压力

3.通过若依框架+AI 完善管理系统


相关推荐
freewlt7 小时前
前端性能优化实战:从 Lighthouse 分数到用户体验的全面升级
前端·性能优化·ux
小小亮017 小时前
Next.js基础
开发语言·前端·javascript
华洛7 小时前
我用AI做了一个48秒的真人精品漫剧,不难也不贵
前端·javascript·后端
WZTTMoon8 小时前
Spring Boot 中Servlet、Filter、Listener 四种注册方式全解析
spring boot·后端·servlet
standovon8 小时前
Spring Boot整合Redisson的两种方式
java·spring boot·后端
Novlan18 小时前
我把 Claude Code 里的隐藏彩蛋提取出来了——零依赖的 ASCII 虚拟宠物系统
前端
IAUTOMOBILE8 小时前
Python 流程控制与函数定义:从调试现场到工程实践
java·前端·python
好大哥呀9 小时前
C++ Web 编程
开发语言·前端·c++
zs宝来了9 小时前
Spring Boot 自动配置原理:@EnableAutoConfiguration 的魔法
spring boot·自动配置·源码解析·enableautoconfiguration
爱学习的小仙女!10 小时前
面试题 前端(一)DOCTYPE作用 标准模式与混杂模式区分
前端·前端面试题