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

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

🤞目录🤞

💖一. 准备工作

💎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编辑

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

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

    🏓🏓🏓

    相关实践学习
    如何快速连接云数据库RDS MySQL
    本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
    全面了解阿里云能为你做什么
    阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
    相关文章
    |
    1月前
    |
    监控 前端开发 数据可视化
    3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
    @icraft/player-react 是 iCraft Editor 推出的 React 组件库,旨在简化3D数字孪生场景的前端集成。它支持零配置快速接入、自定义插件、丰富的事件和方法、动画控制及实时数据接入,帮助开发者轻松实现3D场景与React项目的无缝融合。
    107 8
    3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
    |
    2月前
    |
    JavaScript 前端开发 Docker
    前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
    在使用 Deno 构建项目时,生成的可执行文件体积较大,通常接近 100 MB,而 Node.js 构建的项目体积则要小得多。这是由于 Deno 包含了完整的 V8 引擎和运行时,使其能够在目标设备上独立运行,无需额外安装依赖。尽管体积较大,但 Deno 提供了更好的安全性和部署便利性。通过裁剪功能、使用压缩工具等方法,可以优化可执行文件的体积。
    151 3
    前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
    |
    1月前
    |
    前端开发 测试技术
    前端工程化的分支策略要如何与项目的具体情况相结合?
    前端工程化的分支策略要紧密结合项目的实际情况,以实现高效的开发、稳定的版本控制和顺利的发布流程。
    28 1
    |
    1月前
    |
    前端开发 Unix 测试技术
    揭秘!前端大牛们如何高效管理项目,确保按时交付高质量作品!
    【10月更文挑战第30天】前端开发项目涉及从需求分析到最终交付的多个环节。本文解答了如何制定合理项目计划、提高团队协作效率、确保代码质量和应对项目风险等问题,帮助你学习前端大牛们的项目管理技巧,确保按时交付高质量的作品。
    41 2
    |
    2月前
    |
    Java 关系型数据库 MySQL
    mysql5.7 jdbc驱动
    遵循上述步骤,即可在Java项目中高效地集成MySQL 5.7 JDBC驱动,实现数据库的访问与管理。
    478 1
    |
    2月前
    |
    缓存 前端开发 JavaScript
    前端架构思考:代码复用带来的隐形耦合,可能让大模型造轮子是更好的选择-从 CDN 依赖包被删导致个站打不开到数年前因11 行代码导致上千项目崩溃谈谈npm黑洞 - 统计下你的项目有多少个依赖吧!
    最近,我的个人网站因免费CDN上的Vue.js包路径变更导致无法访问,引发了我对前端依赖管理的深刻反思。文章探讨了NPM依赖陷阱、开源库所有权与维护压力、NPM生态问题,并提出减少不必要的依赖、重视模块设计等建议,以提升前端项目的稳定性和可控性。通过“left_pad”事件及个人经历,强调了依赖管理的重要性和让大模型代替人造轮子的潜在收益
    |
    2月前
    |
    前端开发 JavaScript 开发工具
    前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(三)
    前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(三)
    46 0
    |
    2月前
    |
    Web App开发 前端开发 JavaScript
    前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(二)
    前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(二)
    59 0
    |
    2月前
    |
    Web App开发 移动开发 前端开发
    前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(一)
    前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(一)
    63 0
    |
    2月前
    |
    前端开发 API 开发者
    🥇前端宝藏:多项目掌握技能的冒险之旅🏆
    在前端开发的学习旅程中,实践是提升技能的关键。本文介绍了多个前端项目,包括计算器、天气应用、经典游戏等,涵盖了从React到Svelte的各种技术栈。每个项目都附有在线演示和源代码,旨在帮助读者深入理解实现细节,激励更多人参与实际项目开发。通过这些项目,读者可以将理论知识转化为实践,拓展职业机会。
    24 0