【前端+后端项目】 - 论坛信息管理系统(Web+servlet+MySQL+JDBC)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 现在我们可以基于模板的方式,通过服务器把数据渲染到页面中,然后直接返回完整的页面给浏览器。

🤞目录🤞

💖一. 准备工作

💎1) 创建 web 项目

💎2) 创建目录结构

💎3) 配置 pom.xml

💖二. 数据库操作代码

💎1. 数据库设计

1.1 ER图

1.2 ER图转化为关系模型并优化

1.3 创建数据库/表结构

💎2. 封装数据库

1.  DBUtil 类(JDBC连接数据库)

2. 创建实体类

       2.1 User

       2.2 Section

       2.3 Topic

       2.4 Reply

       2.5 Key

3. 封装对数据库的增删查改类

       3.1 UserDao

       3.2 SectionDao

       3.3 TopicDao

       3.4 ReplyDao

       3.5 KeyDao

💖三. 约定前后端交互接口

💎0. 前端页面

💎1. 实现论坛列表页

1.1约定前后端交互接口

1.2 实现服务器代码

1.3 编写客户端代码

💎2. 实现论坛主贴页

2.1约定前后端交互接口

2.2 实现服务器代码

2.3 编写客户端代码

💎3. 实现论坛回复页

3.1约定前后端交互接口

3.2 实现服务器代码

3.3 编写客户端代码

💎4. 实现论坛个人中心页

4.1约定前后端交互接口

4.2 实现服务器代码

4.3 编写客户端代码

💎5. 实现论坛发布帖子页

5.1约定前后端交互接口

5.2 实现服务器代码

5.3 编写客户端代码

💎6. 实现论坛登录页

6.1约定前后端交互接口

6.2 实现服务器代码

6.3 编写客户端代码

💎7. 实现论坛注册页

7.1约定前后端交互接口

7.2 实现服务器代码

7.3 编写客户端代码

💎8. 实现论坛注销

8.1实现服务器代码


【大家好,我是爱干饭的猿,如果喜欢这篇文章,点个赞👍,关注一下吧,后续会持续分享其他项目的相关操作和Linux相关知识

上一篇文章:《前端项目 - 论坛信息管理系统(纯页面)》

本篇文章介绍的《论坛信息管理系统》源码链接放在文章末尾了,感兴趣的朋友可以看看。


上一篇文章中我们完成了论坛信息管理系统的前端页面,现在我们可以基于模板的方式,通过服务器把数据渲染到页面中,然后直接返回完整的页面给浏览器。

目前比较主流的开发方式是“前后端分离”的方式,在这种方式下服务端不关注页面的内容,而是网页提供的数据。

网页可以通过ajax的方式和服务器之间交互数据,网页拿到数据之后再根据数据的内容渲染到页面上。

image.gif编辑

此篇文章仅展示重要代码思路过程,项目源码在 gitee 中

链接:《论坛信息管理系统》

效果展示

论坛列表页:

image.gif编辑

论坛主贴页:

image.gif编辑

论坛回帖页:

image.gif编辑

用户写帖子页:

image.gif编辑

用户个人信息页:

image.gif编辑

论坛登录页:image.gif编辑

论坛注册页:

image.gif编辑


一. 准备工作

1) 创建 web 项目

2) 创建目录结构

image.gif编辑

3) 配置 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>forum-inforation-management-system</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- 指定最终 war 包的名称 -->
    <packaging>war</packaging>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <!-- 加入 servlet 依赖 -->
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

image.gif


二. 数据库操作代码

1. 数据库设计

1.1 ER图

image.gif编辑

1.2 ER图转化为关系模型并优化

    1. 管理员信息表管理员编号,管理员名称,管理员密码
    2. 用户信息表用户编号,用户名称,用户密码,用户生日,用户性别,用户性别,用户生日,用户职业,用户爱好,用户帖子数,用户等级,用户注册日期
    3. 块信息表版块编号,版主编号,版块名称,版块说明,版块点击次数,版块主题数
    4. 主贴信息表主贴编号主贴板块编号主贴用户编号主贴回复次数主贴标题主贴内容主贴时间主贴回复次数,主贴点击次数
    5. 回复贴信息表回帖编号回帖主题帖子编号回帖的用户编号回帖标题回帖内容回帖时间回帖点击次数

    1.3 创建数据库/表结构

    根据在ER图中的实体,创建库表结构

    db.sql

    -- 创建数据库
    if exit forum_management_information_system1 drop database `forum_management_information_system1`;
    CREATE SCHEMA `forum_management_information_system1` DEFAULT CHARACTER SET utf8 ;
    -- 创建管理员信息表
    CREATE TABLE `admin` (
      `adminID` int NOT NULL AUTO_INCREMENT COMMENT '管理员编号',
      `adminName` varchar(20) NOT NULL COMMENT '管理员名称',
      `adminPassword` varchar(20) NOT NULL COMMENT '管理员密码',
      PRIMARY KEY (`adminID`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='管理员信息表';
    -- 创建用户信息表
    CREATE TABLE `user` (
      `uID` int NOT NULL AUTO_INCREMENT COMMENT '用户编号',
      `userName` varchar(20) NOT NULL COMMENT '用户昵称',
      `userPassword` varchar(20) NOT NULL COMMENT '用户密码',
      `userSex` varchar(20) NOT NULL COMMENT '用户性别',
      `userAge` int NOT NULL COMMENT '用户生日',
      `userOccupation` varchar(20) NOT NULL COMMENT '用户职业',
      `userHobby` varchar(20) NOT NULL COMMENT '用户爱好',
      `userPoint` int NOT NULL DEFAULT '0' COMMENT '用户积分',
      `userClass` int NOT NULL DEFAULT '0' COMMENT '用户等级',
      `userRegister` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '用户注册时间',
      PRIMARY KEY (`uID`)
    ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3 COMMENT='用户信息表';
    -- 板块信息表
    CREATE TABLE `section` (
      `sID` int NOT NULL AUTO_INCREMENT COMMENT '板块编号',
      `uID` int NOT NULL COMMENT '版主编号(用户编号)',
      `sName` varchar(50) NOT NULL COMMENT '板块名称',
      `sStatement` text NOT NULL COMMENT '板块说明',
      `sClickCount` int NOT NULL DEFAULT '0' COMMENT '板块点击次数',
      `sTopicCount` int NOT NULL DEFAULT '0' COMMENT '板块主题数',
      PRIMARY KEY (`sID`),
      KEY `s_uID_idx` (`uID`)
    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COMMENT='板块信息表';
    -- 主贴信息表
    CREATE TABLE `topic` (
      `tID` int NOT NULL AUTO_INCREMENT COMMENT '主贴编号',
      `sID` int NOT NULL COMMENT '主贴板块编号',
      `uID` int NOT NULL COMMENT '主贴用户编号',
      `tTopic` varchar(20) NOT NULL COMMENT '主贴标题',
      `tContent` text NOT NULL COMMENT '主贴内容',
      `tTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '主贴时间',
      `tReplyCount` int NOT NULL DEFAULT '0' COMMENT '主贴回复次数',
      `tClickCount` int NOT NULL DEFAULT '0' COMMENT '主贴点击次数',
      PRIMARY KEY (`tID`),
      KEY `t_uID_idx` (`uID`),
      KEY `t_sID_idx` (`sID`),
      CONSTRAINT `t_sID` FOREIGN KEY (`sID`) REFERENCES `section` (`sID`),
      CONSTRAINT `t_uID` FOREIGN KEY (`uID`) REFERENCES `user` (`uID`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb3 COMMENT='主贴信息表';
    -- 回复贴信息表
    CREATE TABLE `reply` (
      `rID` int NOT NULL AUTO_INCREMENT COMMENT '回帖编号',
      `tID` int NOT NULL COMMENT '回帖主题帖子编号(主贴信息表)',
      `uID` int NOT NULL COMMENT '回帖的用户编号',
      `rTopic` varchar(20) NOT NULL DEFAULT '回帖' COMMENT '回帖标题',
      `rContent` text NOT NULL COMMENT '回帖内容',
      `rTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '回帖时间',
      `rClickCount` int NOT NULL DEFAULT '0' COMMENT '回帖点击次数',
      PRIMARY KEY (`rID`)
    ) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb3 COMMENT='回复贴信息表';
    -- 初始数据 (增加一个用户)
    INSERT INTO `forum_management_information_system1`.`user` (`userName`, `userPassword`, `userSex`, `userAge`, `userOccupation`, `userHobby`) VALUES ('管理员', '123456', '男', '18', '管理员', '你猜');
    -- 初始化板块数据
    INSERT INTO `forum_management_information_system1`.`section` (`uID`, `sName`, `sStatement`) VALUES ('1', '网易云', '小时候枕头上全是口水,长大后枕头上全是泪水;小时候微笑是一个心情,长大后微笑是一种表情;小时候哭着哭着就笑了,长大后笑着笑着就哭了。我们终于到了小时候最羡慕的年龄,但却没有成为小时候最想成为的人。——网易云音乐热评《当我已不再那么年轻》');
    INSERT INTO `forum_management_information_system1`.`section` (`uID`, `sName`, `sStatement`) VALUES ('1', '码农', '码农Coding Peasant(s):可以指在程序设计某个专业领域中的专业人士,或是从事软体撰写,程序开发、维护的专业人员。但一般Coder特指进行编写代码的编码员。');
    INSERT INTO `forum_management_information_system1`.`section` (`uID`, `sName`, `sStatement`) VALUES ('1', '电影', '电影,也被称为运动画面或动态画面,即“映画”,是作品视觉艺术形式,通过使用移动图像来表达沟通思想,故事,认知,情感,价值观,或各类大气模拟体验。这些图像通常伴随着声音,很少有其他感官刺激。 “电影”一词是电影摄影的缩写,通常用于指代电影制作和电影业,以及由此产生的艺术形式。');
    INSERT INTO `forum_management_information_system1`.`section` (`uID`, `sName`, `sStatement`) VALUES ('1', '体育', '( physical education ,缩写 PE 或 P.E. ),是一种复杂的社会文化现象,它是一种以身体与智力活动为基本手段,根据人体生长发育、技能形成和机能提高等规律,达到促进全面发育、提高身体素质与全面教育水平、增强体质与提高运动能力、改善生活方式与提高生活质量的一种有意识、有目的、有组织的社会活动。');
    INSERT INTO `forum_management_information_system1`.`section` (`uID`, `sName`, `sStatement`) VALUES ('1', '游戏', '游戏(英文: Game)是所有哺乳类动物,特别是灵长类动物学习生存的第一步。它是一种基于物质需求满足之上的,在一些特定时间、空间范围内遵循某种特定规则的,追求精神世界需求满足的社会行为方式,但同时这种行为方式也是哺乳类动物或者灵长类动物所需的一种降压减排的方式,不管是在出生幼年期,或者发育期,成熟期都会需要的一种行为方式。');
    INSERT INTO `forum_management_information_system1`.`section` (`uID`, `sName`, `sStatement`) VALUES ('1', '旅行', '“旅”是旅行,外出,即为了实现某一目的而在空间上从甲地到乙地的行进过程;“游”是外出游览、观光、娱乐,即为达到这些目的所作的旅行。二者合起来即旅游。所以,旅行偏重于行,旅游不但有“行”,且有观光、娱乐含义。');
    INSERT INTO `forum_management_information_system1`.`section` (`uID`, `sName`, `sStatement`) VALUES ('1', '明星', ' (拼音míng xīng,注音ㄇㄧㄥˊ ㄒㄧㄥ)汉语 词语 ,古书上指金星;旧时指 交际 场中有名的女子;现指在某个领域内有一定影响力的人物。 泛指有名的 演员 、 歌手 、 艺人 、 运动员 等。');

    image.gif

    2. 封装数据库

    1.  DBUtil 类(JDBC连接数据库)

    image.gif编辑

    package forum.util;
    import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
    import lombok.SneakyThrows;
    import javax.sql.DataSource;
    import java.sql.Connection;
    /**
     * @author haomin
     * @date 2022/06/29 17:23
     **/
    public class DBUtil {
        private static final DataSource dataSource;
        static {
            MysqlDataSource mysqlDataSource = new MysqlDataSource();
            // 数据库路径要正确
            mysqlDataSource.setUrl("jdbc:mysql:///forum_management_information_system1?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai");
            mysqlDataSource.setUser("root");
            mysqlDataSource.setPassword("123456");
            dataSource = mysqlDataSource;
        }
        @SneakyThrows
        public static Connection connection() {
            return dataSource.getConnection();
        }
    }

    image.gif

    2. 创建实体类

    image.gif编辑

    一般放对象的目录命名为model,根据model目录,其中存放的都是各种对象,例如 User 类、Topic 类、Reply 类。

    使用@Data注解在类上时,不用再手动添加get/set等方法了,简化代码

    2.1 User

    package forum.model;
    import lombok.Data;
    // 使用@Data注解在类上时,不用再手动添加get/set等方法了,简化代码
    @Data
    public class User {
        public Integer uID;
        public String username;
        public String password;
        public String userSex;
        public Integer userAge;
        public String userOccupation;
        public String userHobby;
        public Integer userPoint;
        public Integer userClass;
        public String userRegister;
    }

    image.gif

    2.2 Section

    package forum.model;
    import lombok.Data;
    @Data
    public class Section {
        public Integer sID;
        public Integer uID;
        public String sName;
        public String sStatement;
        public Integer sClickCount;
        public Integer sTopicCount;
    }

    image.gif

    2.3 Topic

    package forum.model;
    import com.fasterxml.jackson.annotation.JsonFormat;
    import lombok.Data;
    import java.util.Date;
    @Data
    public class Topic {
        public Integer tID;
        public Integer sID;
        public Integer uID;
        public String username;
        public String tTopic;
        public String tContent;
        //@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        public String tTime;
        public Integer tReplyCount;
        public Integer tClickCount;
    }

    image.gif

    2.4 Reply

    package forum.model;
    import com.fasterxml.jackson.annotation.JsonFormat;
    import java.util.Date;
    public class Reply {
        public Integer rID;
        public Integer tID;
        public Integer uID;
        public String username;
        public String rTopic;
        public String rContent;
        public String rTime;
        public Integer rClickCount;
    }

    image.gif

    2.5 Key

    为实现一个搜索功能,新建了一个Keyword实体

    package forum.model;
    import lombok.Data;
    @Data
    public class Keyword {
        public String keyword;
    }

    image.gif

    3. 封装对数据库的增删查改类

    image.gif编辑

    一般放数据库增删查改的目录命名为dao,根据dao目录,其中存放的都是各种数据库增删查改方法类,例如 UserDao 类, SectionDao 类。

    3.1 UserDao

    package forum.dao;
    import forum.model.User;
    import forum.util.DBUtil;
    import lombok.SneakyThrows;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    /**
     * @author haomin
     * @date 2022/06/29 17:22
     **/
    // 从数据库查找user数据的方法类
    public class UserDao {
        // 用户帖子数
        @SneakyThrows
        public int selectPointCountByUid(int uid){
            String sql = "select count(*) from topic where uID = ?";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setInt(1,uid);
                    try (ResultSet rs = ps.executeQuery()){
                        rs.next();
                        int count = rs.getInt("count(*)");
                        updatePointCountByUid(uid,count);
                        return count;
                    }
                }
            }
        }
        // 更新主贴回复数
        @SneakyThrows
        public void updatePointCountByUid(int uID, int count) {
            String sql = "update user set userPoint = ? where uID = ?";
            try (Connection c = DBUtil.connection()) {
                try (PreparedStatement ps = c.prepareStatement(sql)) {
                    ps.setInt(1,count);
                    ps.setInt(2,uID);
                    ps.executeUpdate();
                }
            }
        }
        // 用户等级
        public int selectClassCountByUid(int uid){
            int point = new UserDao().selectPointCountByUid(uid);
            int classCount = 0;
            if(point == 0){
                classCount = 0;
            }else if(point < 5){
                classCount = 1;
            }else if(point < 15){
                classCount = 2;
            }else if(point < 30){
                classCount = 3;
            }else {
                classCount = 4;
            }
            updateClassCountByUid(uid,classCount);
            return classCount;
        }
        @SneakyThrows
        private void updateClassCountByUid(int uID, int classCount) {
            String sql = "update user set userClass = ? where uID = ?";
            try (Connection c = DBUtil.connection()) {
                try (PreparedStatement ps = c.prepareStatement(sql)) {
                    ps.setInt(1,classCount);
                    ps.setInt(2,uID);
                    ps.executeUpdate();
                }
            }
        }
        // 查询用户ByUsernameAndPassword
        @SneakyThrows
        public User selectOneByUsernameAndPassword(String username,String password){
            String sql = "select uID, userSex, userAge,userOccupation, userHobby, userPoint, userClass, userRegister from user where userName = ? and userPassword = ?";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setString(1,username);
                    ps.setString(2,password);
                    System.out.println("DEBUG: " + ps);
                    try (ResultSet rs = ps.executeQuery()){
                        if(!rs.next()){
                            return null;
                        }
                        User user = new User();
                        user.uID = rs.getInt("uID");
                        user.username = username;
                        user.password = password;
                        user.userSex = rs.getString("userSex");
                        user.userAge = rs.getInt("userAge");
                        user.userOccupation = rs.getString("userOccupation");
                        user.userHobby = rs.getString("userHobby");
                        user.userPoint = rs.getInt("userPoint");
                        user.userClass = rs.getInt("userClass");
                        String Time = rs.getString("userRegister");
                        user.userRegister = Time.substring(0,Time.length()-5);
                        return user;
                    }
                }
            }
        }
        // 查询用户名ByUid
        @SneakyThrows
        public String selectUserNameByUid(int uid){
            String sql = "select username from user where uID = ?";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setInt(1,uid);
                    try (ResultSet rs = ps.executeQuery()){
                        if(!rs.next()){
                            return null;
                        }
                        String username = rs.getString("username");
                        return username;
                    }
                }
            }
        }
        // 新建用户
        @SneakyThrows
        public User insert(String username, String password, String sex, int age, String occupation, String hobby) {
            String sql = "insert into user (username,userPassword,userSex,userAge,userOccupation,userHobby) values (?, ?, ?, ?, ?, ?)";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setString(1,username);
                    ps.setString(2,password);
                    ps.setString(3,sex);
                    ps.setInt(4,age);
                    ps.setString(5,occupation);
                    ps.setString(6,hobby);
                    ps.executeUpdate();
                    System.out.println("新建用户完成");
                    return new UserDao().selectOneByUsernameAndPassword(username,password);
                }
            }
        }
    }

    image.gif

    3.2 SectionDao

    package forum.dao;
    import forum.model.Section;
    import forum.util.DBUtil;
    import lombok.SneakyThrows;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.List;
    /**
     * @author haomin
     * @date 2022/06/30 11:32
     **/
    public class SectionDao {
        // 得到板块集合ByUid
        @SneakyThrows
        public List<Section> selectListByUid(){
            List<Section> list = new ArrayList<>();
            String sql = "select sID, uID, sName, sStatement, sClickCount, sTopicCount from section;";
            try (Connection c = DBUtil.connection()) {
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    System.out.println("DEBUG: " + ps);
                    try (ResultSet rs = ps.executeQuery()){
                        while (rs.next()){
                            Section section = new Section();
                            section.sID = rs.getInt("sID");
                            section.uID = rs.getInt("uID");
                            section.sName = rs.getString("sName");
                            section.sStatement = rs.getString("sStatement");
                            section.sClickCount = rs.getInt("sClickCount");
                            section.sTopicCount = new TopicDao().sTopicCount(section.sID);
                            list.add(section);
                        }
                    }
                }
            }
            return list;
        }
        // 得到板块集合ByKeyWord
        @SneakyThrows
        public List<Section> selectSectionListByKeyword(String keyword) {
            List<Section> list = new ArrayList<>();
            String sql = "select * from section where sName like ?;";
            System.out.println(sql);
            try (Connection c = DBUtil.connection()) {
                System.out.println(sql);
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    System.out.println(sql);
                    String key = "%"+keyword+"%";
                    ps.setString(1,key);
                    System.out.println(sql);
                    try (ResultSet rs = ps.executeQuery()){
                        while (rs.next()){
                            Section section = new Section();
                            section.sID = rs.getInt("sID");
                            section.uID = rs.getInt("uID");
                            section.sName = rs.getString("sName");
                            section.sStatement = rs.getString("sStatement");
                            section.sClickCount = rs.getInt("sClickCount");
                            section.sTopicCount = new TopicDao().sTopicCount(section.sID);
                            list.add(section);
                        }
                    }
                }
            }
            return list;
        }
        // 更新浏览次数
        @SneakyThrows
        public void update(int sid) {
            String sql = "update section set sClickCount = sClickCount+1 where sid = ?";
            try (Connection c = DBUtil.connection()) {
                try (PreparedStatement ps = c.prepareStatement(sql)) {
                    ps.setInt(1,sid);
                    ps.executeUpdate();
                }
            }
        }
    }

    image.gif

    3.3 TopicDao

    package forum.dao;
    import forum.model.Section;
    import forum.model.Topic;
    import forum.model.User;
    import forum.util.DBUtil;
    import lombok.SneakyThrows;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.List;
    /**
     * @author haomin
     * @date 2022/06/30 16:38
     **/
    public class TopicDao {
        // 查询板块帖子数
        @SneakyThrows
        public int sTopicCount(Integer sID) {
            String sql = "select count(*) from topic where sID = ?";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setInt(1,sID);
                    try (ResultSet rs = ps.executeQuery()){
                        rs.next();
                        int count = rs.getInt("count(*)");
                        updateSTopicCount(sID, count);
                        return count;
                    }
                }
            }
        }
        // 更新板块帖子数
        @SneakyThrows
        public void updateSTopicCount(int sID, int count) {
            String sql = "update section set sTopicCount = ? where sID = ?";
            try (Connection c = DBUtil.connection()) {
                try (PreparedStatement ps = c.prepareStatement(sql)) {
                    ps.setInt(1,count);
                    ps.setInt(2,sID);
                    ps.executeUpdate();
                }
            }
        }
        // 得到主贴集合BySid
        @SneakyThrows
        public List<Topic> selectListByUid(int sid){
            List<Topic> list = new ArrayList<>();
            String sql = "select tID, uID, tTopic, tContent, tTime, tReplyCount, tClickCount from topic where sID = ? order by tTime desc";
            try(Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setInt(1,sid);
                    System.out.println("DEBUG: " + ps);
                    try (ResultSet rs = ps.executeQuery()){
                        while (rs.next()){
                            Topic topic = new Topic();
                            topic.tID = rs.getInt("tID");
                            topic.sID = sid;
                            topic.uID = rs.getInt("uID");
                            topic.username = new UserDao().selectUserNameByUid(topic.uID);
                            topic.tTopic = rs.getString("tTopic");
                            topic.tContent = rs.getString("tContent");
                            String Time = rs.getString("tTime");
                            topic.tTime = Time.substring(0,Time.length()-5);
                            topic.tReplyCount = new ReplyDao().tReplyCount(topic.tID);
                            topic.tClickCount = rs.getInt("tClickCount");
                            list.add(topic);
                        }
                    }
                }
            }
            return list;
        }
        // 得到主贴集合ByKeyword
        @SneakyThrows
        public List<Topic> selectTopicListByKeyword(String keyword) {
            List<Topic> list = new ArrayList<>();
            String sql = "select * from topic where tTopic like ?;";
            System.out.println(sql);
            try (Connection c = DBUtil.connection()) {
                System.out.println(sql);
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    System.out.println(sql);
                    String key = "%"+keyword+"%";
                    ps.setString(1,key);
                    System.out.println(sql);
                    try (ResultSet rs = ps.executeQuery()){
                        while (rs.next()){
                            Topic topic = new Topic();
                            topic.tID = rs.getInt("tID");
                            topic.sID = rs.getInt("sId");
                            topic.uID = rs.getInt("uID");
                            topic.username = new UserDao().selectUserNameByUid(topic.uID);
                            topic.tTopic = rs.getString("tTopic");
                            topic.tContent = rs.getString("tContent");
                            String Time = rs.getString("tTime");
                            topic.tTime = Time.substring(0,Time.length()-5);
                            topic.tReplyCount = new ReplyDao().tReplyCount(topic.tID);
                            topic.tClickCount = rs.getInt("tClickCount");
                            list.add(topic);
                        }
                    }
                }
            }
            return list;
        }
        // 查询一个主贴ByTid
        @SneakyThrows
        public Topic selectTopicByTid(int tid) {
            String sql = "select sID, uID, tTopic, tContent, tTime, tReplyCount, tClickCount from topic where tID = ?";
            try(Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setInt(1,tid);
                    System.out.println("DEBUG: " + ps);
                    try (ResultSet rs = ps.executeQuery()){
                        rs.next();
                        Topic topic = new Topic();
                        topic.tID = tid;
                        topic.sID = rs.getInt("sID");
                        topic.uID = rs.getInt("uID");
                        topic.username = new UserDao().selectUserNameByUid(topic.uID);
                        topic.tTopic = rs.getString("tTopic");
                        topic.tContent = rs.getString("tContent");
                        String Time = rs.getString("tTime");
                        topic.tTime = Time.substring(0,Time.length()-5);
                        topic.tReplyCount = new ReplyDao().tReplyCount(topic.tID);
                        topic.tClickCount = rs.getInt("tClickCount");
                        return topic;
                    }
                }
            }
        }
        // 插入主贴
        @SneakyThrows
        public void insert(int sid, Integer uID, String title, String content) {
            String sql = "insert into topic (sID, uID, tTopic, tContent) values (?, ?, ?, ?)";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setInt(1,sid);
                    ps.setInt(2,uID);
                    ps.setString(3,title);
                    ps.setString(4,content);
                    ps.executeUpdate();
                    System.out.println("插入完成");
                }
            }
        }
        // 更新主贴浏览次数
        @SneakyThrows
        public void update(int tid) {
            String sql = "update topic set tClickCount = tClickCount+1 where tid = ?";
            try (Connection c = DBUtil.connection()) {
                try (PreparedStatement ps = c.prepareStatement(sql)) {
                    ps.setInt(1,tid);
                    ps.executeUpdate();
                }
            }
        }
    }

    image.gif

    3.4 ReplyDao

    package forum.dao;
    import forum.model.Reply;
    import forum.util.DBUtil;
    import lombok.SneakyThrows;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.List;
    /**
     * @author haomin
     * @date 2022/06/30 16:38
     **/
    public class ReplyDao {
        // 查询主贴回复数
        @SneakyThrows
        public int tReplyCount(int tid){
            try (Connection c = DBUtil.connection()){
                String sql = "select count(*) from reply where tID = ?;";
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setInt(1,tid);
                    try (ResultSet rs = ps.executeQuery()){
                        rs.next();
                        int count = rs.getInt("count(*)");
                        updateTReplyCount(tid,count);
                        return count;
                    }
                }
            }
        }
        // 更新主贴回复数
        @SneakyThrows
        public void updateTReplyCount(int tID, int count) {
            String sql = "update topic set tReplyCount = ? where tID = ?";
            try (Connection c = DBUtil.connection()) {
                try (PreparedStatement ps = c.prepareStatement(sql)) {
                    ps.setInt(1,count);
                    ps.setInt(2,tID);
                    ps.executeUpdate();
                }
            }
        }
        // 根据tid查询所有回复
        @SneakyThrows
        public List<Reply> selectListByTid(int tid){
            List<Reply> list = new ArrayList<>();
            String sql = "select rID, uID, rTopic, rContent, rTime, rClickCount from reply where tid = ? order by rTime desc";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setInt(1,tid);
                    System.out.println("DEBUG: " + ps);
                    try (ResultSet rs = ps.executeQuery()){
                        while (rs.next()){
                            Reply reply = new Reply();
                            reply.rID = rs.getInt("rID");
                            reply.tID = tid;
                            reply.uID = rs.getInt("uID");
                            reply.username = new UserDao().selectUserNameByUid(reply.uID);
                            reply.rTopic = rs.getString("rTopic");
                            reply.rContent = rs.getString("rContent");
                            String Time = rs.getString("rTime");
                            reply.rTime = Time.substring(0,Time.length()-5);
                            reply.rClickCount = rs.getInt("rClickCount");
                            list.add(reply);
                        }
                    }
                }
            }
            return list;
        }
        // 插入主贴
        @SneakyThrows
        public void insert(int tid, Integer uID, String content) {
            try (Connection c = DBUtil.connection()){
                String sql = "insert into reply (tID,uID,rContent) values (?,?,?)";
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setInt(1,tid);
                    ps.setInt(2,uID);
                    ps.setString(3,content);
                    ps.executeUpdate();
                    System.out.println("插入完成");
                }
            }
        }
    }

    image.gif

    3.5 KeyDao

    package forum.dao;
    import forum.model.Keyword;
    import forum.util.DBUtil;
    import lombok.SneakyThrows;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    /**
     * @author haomin
     * @date 2022/07/04 10:11
     **/
    public class KeyDao {
        // 插入查询的关键字
        @SneakyThrows
        public void insertKey (String keyword){
            String sql = "INSERT INTO `forum_management_information_system`.`key` (`keyword`) VALUES (?);";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.setString(1,keyword);
                    ps.executeUpdate();
                    System.out.println("插入完成");
                }
            }
        }
        // 更新主贴回复数
        @SneakyThrows
        public void updateTReplyCount(int tID, int count) {
            String sql = "update topic set tReplyCount = ? where tID = ?";
            try (Connection c = DBUtil.connection()) {
                try (PreparedStatement ps = c.prepareStatement(sql)) {
                    ps.setInt(1,count);
                    ps.setInt(2,tID);
                    ps.executeUpdate();
                }
            }
        }
        // 查询是否存在关键字
        @SneakyThrows
        public String selectKey () {
            String sql = "select keyword from forum_management_information_system.key;";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    try (ResultSet rs = ps.executeQuery()){
                        if(rs.next()){
                            String keyword = rs.getString("keyword");
                            return keyword;
                        }else {
                            return null;
                        }
                    }
                }
            }
        }
        // 删除关键字
        @SneakyThrows
        public void deleteKey () {
            String sql = "DELETE FROM `forum_management_information_system`.`key` WHERE (1 = 1);";
            try (Connection c = DBUtil.connection()){
                try (PreparedStatement ps = c.prepareStatement(sql)){
                    ps.executeUpdate();
                }
            }
        }
    }

    image.gif


    三. 约定前后端交互接口

    0. 前端页面

    先把前端页面拷贝到当前项目

    image.gif编辑

    前端页面可见于上一篇博客:《前端项目 - 论坛信息管理系统(纯页面)》

    1. 实现论坛列表页

    1.1约定前后端交互接口

    {
      "currentUser" : {
        "uID" : 17,
        "username" : "张三",
        "password" : "123",
        "userSex" : "男",
        "userAge" : 18,
        "userOccupation" : "大数据开发工程师",
        "userHobby" : "敲代码",
        "userPoint" : 1,
        "userClass" : 1,
        "userRegister" : "2022-07-08 15:01",
        "uid" : 17
      },
      "pointCount" : 1,
      "classCount" : 1,
      "sectionList" : [ {
        "sID" : 12,
        "uID" : 1,
        "sName" : "网易云",
        "sStatement" : "小时候枕头上全是口水,长大后枕头上全是泪水;小时候微笑是一个心情,长大后微笑是一种表情;小时候哭着哭着就笑了,长大后笑着笑着就哭了。我们终于到了小时候最羡慕的年龄,但却没有成为小时候最想成为的人。——网易云音乐热评《当我已不再那么年轻》",
        "sClickCount" : 2,
        "sTopicCount" : 3,
        "uid" : 1,
        "sname" : "网易云",
        "sclickCount" : 2,
        "sstatement" : "小时候枕头上全是口水,长大后枕头上全是泪水;小时候微笑是一个心情,长大后微笑是一种表情;小时候哭着哭着就笑了,长大后笑着笑着就哭了。我们终于到了小时候最羡慕的年龄,但却没有成为小时候最想成为的人。——网易云音乐热评《当我已不再那么年轻》"
        "sid" : 12,
        "stopicCount" : 3
      }, ...]
      "topicList" : null
    }

    image.gif

    1.2 实现服务器代码

    package forum.servlet;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import forum.dao.KeyDao;
    import forum.model.Keyword;
    import forum.model.SectionListResult;
    import forum.model.User;
    import forum.service.SectionService;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    /**
     * @author haomin
     * @date 2022/06/30 11:21
     **/
    @WebServlet("/section-list.json")
    public class SectionListJsonServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            User currentUser = null;
            Keyword key = null;
            System.out.println("开始");
            HttpSession session = req.getSession(false);
            if(session != null){
                currentUser = (User) session.getAttribute("currentUser");
            }
            String keyword = new KeyDao().selectKey();
            SectionService sectionService = new SectionService();
            SectionListResult result = null;
            if(keyword == null) {
                result = sectionService.sectionListResult(currentUser);
                System.out.println("执行第一个");
            }else {
                System.out.println(keyword);
                result = sectionService.sectionListResult(currentUser,keyword);
                System.out.println("执行第二个");
            }
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonString = objectMapper.writeValueAsString(result);
            resp.setCharacterEncoding("utf-8");
            resp.setContentType("application/json");
            resp.getWriter().printf(jsonString);
        }
    }

    image.gif

    1.3 编写客户端代码

    // 1. request /json/article_list.json
    // 2. update dom tree
    function renderAuthor(currentUser) {
        document.querySelector('.author-username').textContent = currentUser.username
        document.querySelector('.author-userSex').textContent = currentUser.userSex
        document.querySelector('.author-userAge').textContent = currentUser.userAge
        document.querySelector('.author-userOccupation').textContent = currentUser.userOccupation
        document.querySelector('.author-userHobby').textContent = "爱好:" + currentUser.userHobby
    }
    function renderCount(pointCount, classCount) {
        document.querySelector('.author-userPoint').textContent = pointCount
        document.querySelector('.author-userClass').textContent = classCount
    }
    function renderSectionList(sectionList, topicList) {
        console.log(sectionList)
        var container = document.querySelector('.section')
        console.log(container)
        for (var i in sectionList) {
            var section = sectionList[i]
            console.log(section)
            var html = `<li>` +
                 `<div class="板块信息">` +
                    ` <h3 class="板块名称">${section.sName}</h3>` +
                     `<div class="版主">版块ID: <span class="版主名称">${section.sID}</span></div>` +
                    `<p class="板块说明">${section.sStatement}</p>` +
                    ` <div class="点击">` +
                         `<div class="数量">` +
                            ` <div>浏览量</div>` +
                            ` <div>${section.sClickCount}</div>` +
                         `</div>` +
                         `<div>` +
                             `<div>主贴数</div>` +
                             `<div>${section.sTopicCount}</div>` +
                         `</div>` +
                     `</div>` +
                     `<div class="查看详情">` +
                         `<a href="detail.html?sid=${section.sID}" target="_self">查看详情>></a>` +
                     `</div>` +
                 `</div></li>`
            container.innerHTML += html
        }
    }
    function renderTopicList(topicList) {
        console.log(topicList)
        var container = document.querySelector('.topic')
        console.log(container)
        for (var i in topicList) {
            var topic = topicList[i]
            console.log(topic)
            var html = `<li>` +
                 `<div class="板块信息">` +
                     `<h3 class="板块名称">${topic.tTopic}</h3>` +
                    ` <p class="主贴时间">${topic.tTime}</p>` +
                    ` <div class="版主">贴主: <span class="版主名称">${topic.username}</span></div>` +
                    ` <p class="板块说明">${topic.tContent}</p>` +
                    ` <div class="点击">` +
                       `  <div class="数量">` +
                             `<div>浏览量</div>` +
                            ` <div>${topic.tClickCount}</div>` +
                         `</div>` +
                         `<div>` +
                             `<div>回复</div>` +
                             `<div>${topic.tReplyCount}</div>` +
                         `</div>` +
                    ` </div>` +
                    `<div class="查看回复" style="display: block; margin: 10px auto 0 auto; align-items: center;
                    text-align: center;color: black;user-select: none;cursor: pointer;">` +
                      `<a href="reply.html?tid=${topic.tID}" target="_self" style="color: black;text-decoration:
                      none;font-size: 13px;">查看回复>></a>` +
                    `</div>` +
                 `</div></li>`
            container.innerHTML += html
        }
    }
    var xhr = new XMLHttpRequest()
    xhr.open('get', '/section-list.json')
    xhr.onload = function() {
        //alert(this.responseText);
        var data = JSON.parse(this.responseText)
        if(!data.currentUser){
            // 重定向
            // location = '/login.html'
            location.assign('/login.html')
            return
        }
        renderAuthor(data.currentUser)
        renderCount(data.pointCount, data.classCount)
        renderSectionList(data.sectionList)
        renderTopicList(data.topicList)
    }
    xhr.send()

    image.gif

    实现效果:

    image.gif编辑

    2. 实现论坛主贴页

    2.1约定前后端交互接口

    {
      "currentUser" : {
        "uID" : 17,
        "username" : "张三",
        "password" : "123",
        "userSex" : "男",
        "userAge" : 18,
        "userOccupation" : "大数据开发工程师",
        "userHobby" : "敲代码",
        "userPoint" : 1,
        "userClass" : 1,
        "userRegister" : "2022-07-08 15:01",
        "uid" : 17
      },
      "pointCount" : 1,
      "classCount" : 1,
      "topicList" : [ {
        "tID" : 16,
        "sID" : 12,
        "uID" : 11,
        "username" : "管理员",
        "tTopic" : "窃取天上三分景",
        "tContent" : "小狐狸想去人间很久了,临行之前,她的姥姥赠她了一件宝物芙蓉面。 小狐狸得了这芙蓉面,欢喜极了,她摸了摸芙蓉面道:“姥姥,我得了芙蓉面也能行走人世吗?” “是啊,只要你戴上芙蓉面,这世上任何人的脸,都可以幻化,可你是妖,就算有了芙蓉面,也得学世人的姿态。” 小狐狸拿着芙蓉面思索半天,",
        "tTime" : "2022-07-08 13:50",
        "tReplyCount" : 4,
        "tClickCount" : 4,
        "uid" : 11,
        "treplyCount" : 4,
        "tclickCount" : 4,
        "ttime" : "2022-07-08 13:50",
        "tid" : 16,
        "tcontent" : "小狐狸想去人间很久了,临行之前,她的姥姥赠她了一件宝物芙蓉面。 小狐狸得了这芙蓉面,欢喜极了,她摸了摸芙蓉面道:“姥姥,我得了芙蓉面也能行走人世吗?” “是啊,只要你戴上芙蓉面,这世上任何人的脸,都可以幻化,可你是妖,就算有了芙蓉面,也得学世人的姿态。” 小狐狸拿着芙蓉面思索半天,",
        "ttopic" : "窃取天上三分景",
        "sid" : 12
      },...]
    }

    image.gif

    2.2 实现服务器代码

    package forum.servlet;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import forum.dao.KeyDao;
    import forum.model.Keyword;
    import forum.model.SectionListResult;
    import forum.model.User;
    import forum.service.SectionService;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    /**
     * @author haomin
     * @date 2022/06/30 11:21
     **/
    @WebServlet("/section-list.json")
    public class SectionListJsonServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            User currentUser = null;
            Keyword key = null;
            System.out.println("开始");
            HttpSession session = req.getSession(false);
            if(session != null){
                currentUser = (User) session.getAttribute("currentUser");
            }
            String keyword = new KeyDao().selectKey();
            SectionService sectionService = new SectionService();
            SectionListResult result = null;
            if(keyword == null) {
                result = sectionService.sectionListResult(currentUser);
                System.out.println("执行第一个");
            }else {
                System.out.println(keyword);
                result = sectionService.sectionListResult(currentUser,keyword);
                System.out.println("执行第二个");
            }
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(result);
            resp.setCharacterEncoding("utf-8");
            resp.setContentType("application/json");
            resp.getWriter().printf(jsonString);
        }
    }

    image.gif

    2.3 编写客户端代码

    // 1. request /json/article_list.json
    // 2. update dom tree
    function renderAuthor(currentUser) {
        document.querySelector('.author-username').textContent = currentUser.username
        document.querySelector('.author-userSex').textContent = currentUser.userSex
        document.querySelector('.author-userAge').textContent = currentUser.userAge
        document.querySelector('.author-userOccupation').textContent = currentUser.userOccupation
        document.querySelector('.author-userHobby').textContent = "爱好:" + currentUser.userHobby
    }
    function renderCount(pointCount, classCount) {
        document.querySelector('.author-userPoint').textContent = pointCount
        document.querySelector('.author-userClass').textContent = classCount
    }
    function renderTopicList(topicList) {
        console.log(topicList)
        var container = document.querySelector('.topic')
        console.log(container)
        for (var i in topicList) {
            var topic = topicList[i]
            console.log(topic)
            var html = `<li>` +
                 `<div class="板块信息">` +
                     `<h3 class="板块名称">${topic.tTopic}</h3>` +
                    ` <p class="主贴时间">${topic.tTime}</p>` +
                    ` <div class="版主">贴主: <span class="版主名称">${topic.username}</span></div>` +
                    ` <p class="板块说明">${topic.tContent}</p>` +
                    ` <div class="点击">` +
                       `  <div class="数量">` +
                             `<div>浏览量</div>` +
                            ` <div>${topic.tClickCount}</div>` +
                         `</div>` +
                         `<div>` +
                             `<div>回复</div>` +
                             `<div>${topic.tReplyCount}</div>` +
                         `</div>` +
                    ` </div>` +
                    `<div class="查看回复" style="display: block; margin: 10px auto 0 auto; align-items: center;
                    text-align: center;color: black;user-select: none;cursor: pointer;">` +
                      `<a href="reply.html?tid=${topic.tID}" target="_self" style="color: black;text-decoration:
                      none;font-size: 13px;">查看回复>></a>` +
                    `</div>` +
                 `</div></li>`
            container.innerHTML += html
        }
    }
    var xhr = new XMLHttpRequest()
    xhr.open('get', '/topic-list.json' + location.search)
    xhr.onload = function() {
        // 打印 json内容
        alert(this.responseText);
        console.log(this.responseText)
        var data = JSON.parse(this.responseText)
        if(!data.currentUser){
            // 重定向
            // location = '/login.html'
            location.assign('/login.html')
            return
        }
        renderAuthor(data.currentUser)
        renderCount(data.pointCount, data.classCount)
        renderTopicList(data.topicList)
    }
    xhr.send()

    image.gif

    实现效果:

    image.gif编辑

    3. 实现论坛回复页

    3.1约定前后端交互接口

    {
      "currentUser" : {
        "uID" : 17,
        "username" : "张三",
        "password" : "123",
        "userSex" : "男",
        "userAge" : 18,
        "userOccupation" : "大数据开发工程师",
        "userHobby" : "敲代码",
        "userPoint" : 1,
        "userClass" : 1,
        "userRegister" : "2022-07-08 15:01",
        "uid" : 17
      },
      "pointCount" : 1,
      "classCount" : 1,
      "replyList" : [ {
        "rID" : 39,
        "tID" : 16,
        "uID" : 17,
        "username" : "张三",
        "rTopic" : "回帖",
        "rContent" : "hello word",
        "rTime" : "2022-07-08 15:04",
        "rClickCount" : 0
      }, {
        "rID" : 32,
        "tID" : 16,
        "uID" : 11,
        "username" : "管理员",
        "rTopic" : "回帖",
        "rContent" : "有些人为了上前排连尊严都不要了,您怎么看呢?父亲?",
        "rTime" : "2022-07-08 13:54",
        "rClickCount" : 0
      }],
      "tid" : 16,
      "topic" : {
        "tID" : 16,
        "sID" : 12,
        "uID" : 11,
        "username" : "管理员",
        "tTopic" : "窃取天上三分景",
        "tContent" : "小狐狸想去人间很久了,临行之前,她的姥姥赠她了一件宝物芙蓉面。 小狐狸得了这芙蓉面,欢喜极了,她摸了摸芙蓉面道:“姥姥,我得了芙蓉面也能行走人世吗?” “是啊,只要你戴上芙蓉面,这世上任何人的脸,都可以幻化,可你是妖,就算有了芙蓉面,也得学世人的姿态。” 小狐狸拿着芙蓉面思索半天,",
        "tTime" : "2022-07-08 13:50",
        "tReplyCount" : 4,
        "tClickCount" : 6,
        "uid" : 11,
        "treplyCount" : 4,
        "tclickCount" : 6,
        "ttime" : "2022-07-08 13:50",
        "tid" : 16,
        "tcontent" : "小狐狸想去人间很久了,临行之前,她的姥姥赠她了一件宝物芙蓉面。 小狐狸得了这芙蓉面,欢喜极了,她摸了摸芙蓉面道:“姥姥,我得了芙蓉面也能行走人世吗?” “是啊,只要你戴上芙蓉面,这世上任何人的脸,都可以幻化,可你是妖,就算有了芙蓉面,也得学世人的姿态。” 小狐狸拿着芙蓉面思索半天,",
        "ttopic" : "窃取天上三分景",
        "sid" : 12
      }
    }

    image.gif

    3.2 实现服务器代码

    package forum.servlet;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import forum.dao.TopicDao;
    import forum.model.ReplyListResult;
    import forum.model.User;
    import forum.service.ReplyService;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    /**
     * @author haomin
     * @date 2022/07/01 17:05
     **/
    @WebServlet("/reply-list.json")
    public class ReplyListServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.setCharacterEncoding("utf-8");
            String tidString = req.getParameter("tid");
            int tid = Integer.parseInt(tidString);
            User currentUser = null;
            HttpSession session = req.getSession(false);
            if(session != null){
                currentUser = (User)session.getAttribute("currentUser");
            }
            // 浏览一次回帖页面,浏览次数加1
            new TopicDao().update(tid);
            ReplyService replyService = new ReplyService();
            ReplyListResult result = replyService.replyListResult(currentUser,tid);
            result.tid = tid;
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(result);
            resp.setCharacterEncoding("utf-8");
            resp.setContentType("application/json");
            resp.getWriter().println(jsonString);
        }
    }

    image.gif

    3.3 编写客户端代码

    function renderAuthor(currentUser) {
        document.querySelector('.author-username').textContent = currentUser.username
        document.querySelector('.author-userSex').textContent = currentUser.userSex
        document.querySelector('.author-userAge').textContent = currentUser.userAge
        document.querySelector('.author-userOccupation').textContent = currentUser.userOccupation
        document.querySelector('.author-userHobby').textContent = "爱好:" + currentUser.userHobby
    }
    function renderCount(pointCount, classCount) {
        document.querySelector('.author-userPoint').textContent = pointCount
        document.querySelector('.author-userClass').textContent = classCount
    }
    function renderDefineTid(tid){
        document.querySelector('.tid').value = tid;
    }
    function renderReplyList(topic,replyList){
        console.log(replyList)
        var container = document.querySelector('.reply')
        console.log(container)
        // 主贴
        var html = `<li>` +
             `<div class="板块信息1">` +
                 `<h3 class="板块名称">${topic.tTopic}</h3>` +
                ` <p class="主贴时间">${topic.tTime}</p>` +
                ` <div class="版主">贴主: <span class="版主名称">${topic.username}</span></div>` +
                ` <p class="板块说明">${topic.tContent}</p>` +
                    ` <div class="点击">` +
                       `  <div class="数量">` +
                             `<div>浏览量</div>` +
                            ` <div>${topic.tClickCount}</div>` +
                         `</div>` +
                    ` </div>` +
             `</div></li>`
        container.innerHTML += html
        //回帖
        for(var i in replyList){
            var reply = replyList[i]
            console.log(reply)
            var html = `<li>` +
                         `<div class="板块信息">` +
                             `<h3 class="板块名称">${reply.rTopic}</h3>` +
                            ` <p class="主贴时间">${reply.rTime}</p>` +
                            ` <div class="版主"><span class="版主名称">${reply.username}</span></div>` +
                            ` <p class="板块说明">${reply.rContent}</p>` +
                            /*` <div class="点击">` +
                               `  <div class="数量">` +
                                     `<div>点击次数</div>` +
                                    ` <div>100</div>` +
                                 `</div>` +
                            ` </div>` +*/
                         `</div></li>`
                    container.innerHTML += html
        }
    }
    var xhr = new XMLHttpRequest()
    xhr.open('get','/reply-list.json' + location.search)
    xhr.onload = function() {
        //alert(this.responseText);
        console.log(this.responseText)
        var data = JSON.parse(this.responseText)
        if(!data.currentUser){
            location.assign('/login.html')
            return
        }
        renderAuthor(data.currentUser)
        renderCount(data.pointCount, data.classCount)
        renderReplyList(data.topic,data.replyList)
        renderDefineTid(data.tid)
    }
    xhr.send()

    image.gif

    实现效果:

    image.gif编辑

    4. 实现论坛个人中心页

    4.1约定前后端交互接口

    个人中心的json数据来自于/section-list.json

    {
      "currentUser" : {
        "uID" : 17,
        "username" : "张三",
        "password" : "123",
        "userSex" : "男",
        "userAge" : 18,
        "userOccupation" : "大数据开发工程师",
        "userHobby" : "敲代码",
        "userPoint" : 1,
        "userClass" : 1,
        "userRegister" : "2022-07-08 15:01",
        "uid" : 17
      }
    }

    image.gif

    4.2 实现服务器代码

    个人中心的服务器代码来自于/section-list.json,和列表页服务器代码相同,再次不在赘述。

    4.3 编写客户端代码

    function renderAuthor(currentUser) {
        document.querySelector('.author-username').value = currentUser.username
        document.querySelector('.author-userSex').value = currentUser.userSex
        document.querySelector('.author-userAge').value = currentUser.userAge
        document.querySelector('.author-userOccupation').value = currentUser.userOccupation
        document.querySelector('.author-userHobby').value = currentUser.userHobby
        document.querySelector('.author-userPoint').value = currentUser.userPoint
        document.querySelector('.author-userClass').value = currentUser.userClass
        document.querySelector('.author-userRegister').value = currentUser.userRegister
    }
    var xhr = new XMLHttpRequest()
    xhr.open('get', '/section-list.json')
    xhr.onload = function() {
        //alert(this.responseText);
        var data = JSON.parse(this.responseText)
        if(!data.currentUser){
            // 重定向
            // location = '/login.html'
            location.assign('/login.html')
            return
        }
        renderAuthor(data.currentUser)
    }
    xhr.send()

    image.gif

    实现效果:

    image.gif编辑

    5. 实现论坛发布帖子页

    5.1约定前后端交互接口

    [请求] post /editor.do

    image.gif

    5.2 实现服务器代码

    package forum.servlet;
    import forum.dao.TopicDao;
    import forum.model.User;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    /**
     * @author haomin
     * @date 2022/07/01 19:30
     **/
    @WebServlet("/editor.do")
    public class EditorDoServlet extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.setCharacterEncoding("utf-8");
            String title = req.getParameter("title");
            String sidString = req.getParameter("type");
            int sid = Integer.parseInt(sidString);
            String content = req.getParameter("editor-markdown-doc");
            User currentUser = null;
            HttpSession session = req.getSession(false);
            if (session != null) {
                currentUser = (User) session.getAttribute("currentUser");
            }
            if (currentUser == null) {
                // 说明没有登录
                resp.sendRedirect("/login.html");
                return;
            }
            TopicDao topicDao = new TopicDao();
            topicDao.insert(sid,currentUser.uID,title,content);
            // 重定向到文章详情页
            resp.sendRedirect("/detail.html?sid=" + sid);
        }
    }

    image.gif

    5.3 编写客户端代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>写帖子</title>
        <link rel="stylesheet" href="css/navigation bar.css">
        <link rel="stylesheet" href="css/editor.css">
    </head>
    <body>
        <div class="导航栏">
            <div class="left-logo">
                <div class="logo"><img src="img/系统图标.png" width="45"></div>
                <p>论坛管理信息系统</p>
            </div>
            <div class="占位"></div>
            <div class="right-function">
                <a href="center.html">个人中心</a>
                <a href="list.html">主页</a>
                <a href="editor.html">写帖子</a>
                <a href="logout" target="_self">注销</a>
            </div>
        </div>
        <div class="blog-edit-container">
            <form method="post" action="/editor.do">
                <div class="title">
                    <input type="text" placeholder="在这里写下主贴标题" id="title" name="title">
                    <input type="text" placeholder="板块编号" id="type" name="type" style="    height: 40px;
        width: 80px;
        text-indent: 10px;
        border-radius: 10px;
        outline: none;
        border: none;
        background-color:rgba(255, 255, 255, 0.8);">
                    <button id="submit">发布帖子</button>
                </div>
            <div id="editor" class="编辑器"></div>
            <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
            <script src="js/jquery.min.js"></script>
            <script src="editor.md/lib/marked.min.js"></script>
            <script src="editor.md/lib/prettify.min.js"></script>
            <script src="editor.md/editormd.js"></script>
            </form>
        </div>
        <script>
            var content = '# 在这里写下一篇帖子 ##';
            var config = {
                // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.
                width: "100%",
                // 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度
                height: "100vh",
                // 编辑器中的初始内容
                markdown: content,
                // 指定 editor.md 依赖的插件路径
                path: "editor.md/lib/"
            };
            editormd("editor", config);
        </script>
    </body>
    </html>

    image.gif

    实现效果:

    image.gif编辑

    6. 实现论坛登录页

    6.1约定前后端交互接口

    [请求] post /login.do

    image.gif

    6.2 实现服务器代码

    package forum.servlet;
    import forum.dao.UserDao;
    import forum.model.User;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    /**
     * @author haomin
     * @date 2022/06/29 17:18
     **/
    @WebServlet("/login.do")
    public class LoginDoServlet extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.setCharacterEncoding("utf-8");
            String username = req.getParameter("username");
            String password = req.getParameter("password");
            System.out.println("DEBUG: username = " + username);
            System.out.println("DEBUG: password = " + password);
            UserDao userDao = new UserDao();
            User user = userDao.selectOneByUsernameAndPassword(username,password);
            if(user == null){
                resp.sendRedirect("/login.html");
                return;
            }
            System.out.println(user);
            HttpSession session = req.getSession();
            session.setAttribute("currentUser",user);
            resp.sendRedirect("/");
        }
    }

    image.gif

    6.3 编写客户端代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
        <link rel="stylesheet" href="css/navigation bar.css">
        <link rel="stylesheet" href="css/login.css">
    </head>
    <body>
        <div class="导航栏">
            <div class="left-logo">
                <div class="logo"><img src="img/系统图标.png" width="45"></div>
                <p>论坛管理信息系统</p>
            </div>
            <div class="占位"></div>
            <div class="right-function">
                <span class="center"></span>
                <a href="list.html">主页</a>
                <a href="editor.html" target="_blank">写帖子</a>
                <a href="logon.html">注册</a>
            </div>
        </div>
        <div class="主体区">
            <div class="登录框">
                <form action="/login.do" method="post">
                    <h2>登录</h2>
                    <div>
                        <span>用户名</span>
                        <input type="text" class="text" name="username" placeholder="请输入用户名">
                    </div>
                    <div>
                        <span>密码</span>
                        <input type="password" class="text" name="password" placeholder="请输入密码">
                    </div>
                    <div>
                        <button class="提交">登录</button>
                    </div>
                </form>
            </div>
        </div>
    </body>
    </html>

    image.gif

    实现效果:

    image.gif编辑

    7. 实现论坛注册页

    7.1约定前后端交互接口

    [请求] post /logon.do

    image.gif

    7.2 实现服务器代码

    package forum.servlet;
    import forum.dao.UserDao;
    import forum.model.User;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    /**
     * @author haomin
     * @date 2022/07/03 08:34
     **/
    @WebServlet("/logon.do")
    public class LogonDoServlet extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.setCharacterEncoding("utf-8");
            String username = req.getParameter("username");
            String sex = req.getParameter("sex");
            String age = req.getParameter("age");
            String occupation = req.getParameter("occupation");
            String hobby = req.getParameter("hobby");
            String password1 = req.getParameter("password1");
            String password2 = req.getParameter("password2");
            UserDao userDao = new UserDao();
            User user = userDao.insert(username,password1,sex,Integer.parseInt(password1),occupation,hobby);
            HttpSession session = req.getSession();
            session.setAttribute("currentUser",user);
            resp.sendRedirect("/");
        }
    }

    image.gif

    7.3 编写客户端代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册</title>
        <link rel="stylesheet" href="css/navigation bar.css">
        <link rel="stylesheet" href="css/logon.css">
    </head>
    <body>
        <div class="导航栏">
            <div class="left-logo">
                <div class="logo"><img src="img/系统图标.png" width="45"></div>
                <p>论坛管理信息系统</p>
            </div>
            <div class="占位"></div>
            <div class="right-function">
                <span class="center"></span>
                <a href="list.html">主页</a>
                <a href="editor.html" target="_blank">写帖子</a>
                <a href="login.html">登录</a>
            </div>
        </div>
        <div class="主体区">
            <div class="注册框">
                <form action="/logon.do" method="post">
                    <h2>注册</h2>
                    <div>
                        <span>昵称</span>
                        <input type="text" class="text" placeholder="请输入昵称" name="username">
                    </div>
                    <div>
                        <span>年龄</span>
                        <input type="text" class="text" placeholder="请输入年龄" name="age">
                    </div>
                    <div>
                        <span>性别</span>
                        <input type="text" class="text" placeholder="请输入性别  男|女" name="sex">
                    </div>
                    <div>
                        <span>爱好</span>
                        <input type="text" class="text" placeholder="请输入爱好" name="hobby">
                    </div>
                    <div>
                        <span>职业</span>
                        <input type="text" class="text" placeholder="请输入职业" name="occupation">
                    </div>
    <!--                <div>-->
    <!--                    <span>Email</span>-->
    <!--                    <input type="text" class="text" placeholder="请输入Email" name="email">-->
    <!--                </div>-->
                    <div>
                        <span>密码</span>
                        <input type="password" class="text" placeholder="请输入密码" name="password1">
                    </div>
                    <div>
                        <span>再次确认密码</span>
                        <input type="password" class="text" placeholder="请再次确认密码" name="password2">
                    </div>
                    <div>
                        <button class="提交">注册</button>
                    </div>
                </form>
            </div>
        </div>
    </body>
    </html>

    image.gif

    实现效果:

    image.gif编辑

    8. 实现论坛注销

    8.1实现服务器代码

    package forum.servlet;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    @WebServlet("/logout")
    public class LogoutServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            HttpSession session = req.getSession(false);
            if (session != null) {
                session.removeAttribute("currentUser");
            }
            resp.sendRedirect("/");
        }
    }

    image.gif

    package forum.servlet;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    /**
     * @author haomin
     * @date 2022/06/30 08:43
     **/
    @WebServlet("")
    public class IndexServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.sendRedirect("/list.html");
        }
    }

    image.gif

    重定向到论坛主页,但是服务器发现没有用户登录,继而重定向到用户登录页。

    实现效果:

    image.gif编辑

    论坛信息管理系统主要重点就介绍到这里了,如果想看看项目源码,可以点击这里:《论坛信息管理系统》

    如果你喜欢这篇文章,请点赞加关注吧,或者如果你对与文章有什么困惑,可以私信我。

    🏓🏓🏓

    相关实践学习
    如何在云端创建MySQL数据库
    开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
    全面了解阿里云能为你做什么
    阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
    相关文章
    |
    1月前
    |
    前端开发 JavaScript 安全
    前端性能调优:HTTP/2与HTTPS在Web加速中的应用
    【10月更文挑战第27天】本文介绍了HTTP/2和HTTPS在前端性能调优中的应用。通过多路复用、服务器推送和头部压缩等特性,HTTP/2显著提升了Web性能。同时,HTTPS确保了数据传输的安全性。文章提供了示例代码,展示了如何使用Node.js创建一个HTTP/2服务器。
    58 3
    |
    1月前
    |
    前端开发 JavaScript
    探索现代Web应用的微前端架构
    【10月更文挑战第40天】在数字时代的浪潮中,Web应用的发展日益复杂多变。微前端架构作为一种新兴的设计理念,正逐步改变着传统的单一前端开发模式。本文将深入探讨微前端的核心概念、实现原理及其在实际项目中的应用,同时通过一个简单的代码示例,揭示如何将一个庞大的前端工程拆分成小而美的模块,进而提升项目的可维护性、可扩展性和开发效率。
    |
    22天前
    |
    关系型数据库 MySQL Java
    Servlet+MySQL增删改查 原文出自[易百教程] 转载请保留原文链接: https://www.yiibai.com/geek/1391
    对于任何项目开发,创建,读取,更新和删除(CRUD)记录操作是应用程序的一个最重要部分。
    61 20
    |
    24天前
    |
    前端开发 JavaScript 搜索推荐
    HTML与CSS在Web组件化中的核心作用及前端技术趋势
    本文探讨了HTML与CSS在Web组件化中的核心作用及前端技术趋势。从结构定义、语义化到样式封装与布局控制,两者不仅提升了代码复用率和可维护性,还通过响应式设计、动态样式等技术增强了用户体验。面对兼容性、代码复杂度等挑战,文章提出了相应的解决策略,强调了持续创新的重要性,旨在构建高效、灵活的Web应用。
    32 6
    |
    1月前
    |
    消息中间件 前端开发 JavaScript
    探索微前端架构:构建现代Web应用的新策略
    本文探讨了微前端架构的概念、优势及实施策略,旨在解决传统单体应用难以快速迭代和团队协作的问题。微前端允许不同团队独立开发、部署应用的各部分,提升灵活性与可维护性。文中还讨论了技术栈灵活性、独立部署、团队自治等优势,并提出了定义清晰接口、使用Web组件、状态管理和样式隔离等实施策略。
    |
    1月前
    |
    监控 前端开发 JavaScript
    探索微前端架构:构建可扩展的现代Web应用
    【10月更文挑战第29天】本文探讨了微前端架构的核心概念、优势及实施策略,通过将大型前端应用拆分为多个独立的微应用,提高开发效率、增强可维护性,并支持灵活的技术选型。实际案例包括Spotify和Zalando的成功应用。
    |
    1月前
    |
    前端开发 安全 应用服务中间件
    前端性能调优:HTTP/2与HTTPS在Web加速中的应用
    【10月更文挑战第26天】随着互联网的快速发展,前端性能调优成为开发者的重要任务。本文探讨了HTTP/2与HTTPS在前端性能优化中的应用,介绍了二进制分帧、多路复用和服务器推送等特性,并通过Nginx配置示例展示了如何启用HTTP/2和HTTPS,以提升Web应用的性能和安全性。
    37 3
    |
    1月前
    |
    前端开发 JavaScript API
    前端框架新探索:Svelte在构建高性能Web应用中的优势
    【10月更文挑战第26天】近年来,前端技术飞速发展,Svelte凭借独特的编译时优化和简洁的API设计,成为构建高性能Web应用的优选。本文介绍Svelte的特点和优势,包括编译而非虚拟DOM、组件化开发、状态管理及响应式更新机制,并通过示例代码展示其使用方法。
    48 2
    |
    2月前
    |
    存储 人工智能 前端开发
    前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
    本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
    178 2
    |
    2月前
    |
    JavaScript 前端开发 程序员
    前端学习笔记——node.js
    前端学习笔记——node.js
    49 0

    推荐镜像

    更多
    下一篇
    DataWorks