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

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

🤞目录🤞

💖一. 准备工作

💎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
    相关文章
    |
    2天前
    |
    JSON 搜索推荐 API
    Python的web框架有哪些?小项目比较推荐哪个?
    【10月更文挑战第15天】Python的web框架有哪些?小项目比较推荐哪个?
    10 1
    |
    2天前
    |
    人工智能 前端开发
    2024 川渝 Web 前端开发技术交流会「互联」:等你来报名!
    2024 川渝 Web 前端开发技术交流会「互联」:等你来报名!
    2024 川渝 Web 前端开发技术交流会「互联」:等你来报名!
    |
    5天前
    |
    存储 前端开发 JavaScript
    从 Web 2.0 到 Web 3.0:前端开发的历史与未来
    【10月更文挑战第4天】本文探讨了从 Web 2.0 到 Web 3.0 的前端开发演变过程。Web 2.0 时代,前端开发者从静态网页设计走向复杂交互,技术框架如 jQuery、React 和 Vue 带来了巨大的变革。而 Web 3.0 以区块链技术为核心,带来了去中心化的互联网体验,前端开发者面临与区块链交互、去中心化身份验证、分布式存储等新挑战。文章总结了 Web 2.0 和 Web 3.0 的核心区别,并为开发者提供了如何应对新技术的建议,帮助他们在新时代中掌握技能、设计更安全的用户体验。
    22 0
    从 Web 2.0 到 Web 3.0:前端开发的历史与未来
    |
    6天前
    |
    前端开发 JavaScript 开发者
    web前端需要学什么
    web前端需要学什么
    |
    5天前
    |
    移动开发 前端开发 JavaScript
    前端开发实战:利用Web Speech API之speechSynthesis实现文字转语音功能
    前端开发实战:利用Web Speech API之speechSynthesis实现文字转语音功能
    30 0
    |
    5天前
    |
    存储 安全 前端开发
    在前端开发中需要考虑的常见web安全问题和攻击原理以及防范措施
    在前端开发中需要考虑的常见web安全问题和攻击原理以及防范措施
    27 0
    |
    5天前
    |
    存储 前端开发 API
    前端开发中,Web Storage的存储数据的方法localstorage和sessionStorage的使用及区别
    前端开发中,Web Storage的存储数据的方法localstorage和sessionStorage的使用及区别
    33 0
    |
    6天前
    |
    编解码 前端开发 JavaScript
    Web前端开发需要掌握哪些知识?
    Web前端在最近几年发展的十分迅速,报名进行Web前端培训的同学也越来越多。移动互联网的背景之下,每一个人每天会跟各式各样的手机端应用打交道,殊不知这些App都跟Web前端有着密不可分的关系。
    |
    2月前
    |
    缓存 安全 Java
    Java服务器端技术:Servlet与JSP的集成与扩展
    Java服务器端技术:Servlet与JSP的集成与扩展
    27 3
    |
    2月前
    |
    存储 缓存 前端开发
    Servlet与JSP在Java Web应用中的性能调优策略
    Servlet与JSP在Java Web应用中的性能调优策略
    29 1