基于Spring前后端分离版本的论坛系统
- PP论坛地址
- 系统设计
- 逻辑交互图
- 数据库设计
- 工程结构概述
- 注册功能实现展示
- 注册交互图
- 参数要求
- 接口规范
- 后端具体实现
- 前端数据集成
- 接口拦截器实现
- mybatis生成类与映射文件
- 改造session存储到 redis
- 加盐算法实现
- 部分Bug调试记录
- 项目测试记录
- Postman 接口测试
- Jmeter 接口压测
- 项目配置问题记录
PP论坛地址
http://211.159.172.237:58080/sign-in.html
系统设计
- PP论坛 项目基于B/S架构
B/S架构全称是浏览器/服务器(Browser/Server)结构,分为Web浏览器、服务器程序、数据库服务三部分,可以理解为是对C/S架构一种改进。
由于所有的业务逻辑都由服务器程序处理,所以客户端仅使用浏览器就可以完成所有操作,大大降低了客户端的维护成本。
逻辑交互图
数据库设计
create database forum_db character set utf8mb4 collate utf8mb4_general_ci;
-- 选择数据库
use forum_db;
-- SET NAMES utf8mb4;
-- SET FOREIGN_KEY_CHECKS = 0;
-- 创建用户表
drop table if exists t_user;
create table t_user (
id bigint primary key auto_increment comment '编号,主键自增',
username varchar(20) not null unique comment '用户名,唯一的',
password varchar(32) not null comment '加密后的密码',
nickname varchar(50) not null comment '昵称',
phoneNum varchar(20) comment '手机号',
email varchar (50) comment '电子邮箱',
gender tinyint not null default 2 comment '性别:0女1男2保密',
salt varchar(32) not null comment '密码加盐',
avatarUrl varchar(255) comment '用户头像路径',
articleCount int not null default 0 comment '发帖数量',
isAdmin tinyint not null default 0 comment ' 管理员:0否1是',
remark varchar(1000) comment '备注:自我介绍',
state tinyint not null default 0 comment '状态:0正常1禁言',
deleteState tinyint not null default 0 comment '是否删除:0否1是',
createTime datetime not null comment '创建时间',
updateTime datetime not null comment '更新时间'
);
-- 创建板块表
drop table if exists t_board;
create table t_board (
id bigint primary key auto_increment comment '编号,主键自增',
name varchar(20) not null unique comment '板块名',
articleCount int not null default 0 comment '帖子数量',
sort int not null default 0 comment '升序排序',
state tinyint not null default 0 comment '状态:0正常1禁言',
deleteState tinyint not null default 0 comment '是否删除:0否1是',
createTime datetime not null comment '创建时间',
updateTime datetime not null comment '更新时间'
);
-- 创建帖子表
drop table if exists t_article;
create table t_article (
id bigint primary key auto_increment comment '编号,主键自增',
boardId bigint not null comment '关联板块编号',
userId bigint not null comment '关联用户编号',
title varchar(100) not null comment '帖子标题',
content text not null comment '帖子正文',
visitCount int not null default 0 comment '访问量',
likeCount int not null default 0 comment '点赞数',
replyCount int not null default 0 comment '回复数',
state tinyint not null default 0 comment '状态:0正常1禁言',
deleteState tinyint not null default 0 comment '是否删除:0否1是',
createTime datetime not null comment '创建时间',
updateTime datetime not null comment '更新时间'
);
-- 创建帖子回复表
drop table if exists t_article_reply;
create table t_article_reply (
id bigint primary key auto_increment comment '编号,主键自增',
articleId bigint not null comment '关联帖子编号',
postUserId bigint not null comment '关联楼主用户编号',
replyId bigint not null comment '关联回复编号,支持楼中楼',
replyUserId bigint not null comment '楼下回复用户编号,支持楼中楼',
content varchar(500) not null comment '回帖内容',
likeCount int not null comment '回帖点赞数',
state tinyint not null default 0 comment '状态:0正常1禁言',
deleteState tinyint not null default 0 comment '是否删除:0否1是',
createTime datetime not null comment '创建时间',
updateTime datetime not null comment '更新时间'
);
-- 创建私信表
drop table if exists t_message;
create table t_message (
id bigint primary key auto_increment comment '编号,主键自增',
postUserId bigint not null comment '发送者:关联用户编号',
receiveUserId bigint not null comment '接收者:关联用户编号',
content varchar(500) not null comment '私信内容',
state tinyint not null default 0 comment '状态:0正常1禁言',
deleteState tinyint not null default 0 comment '是否删除:0否1是',
createTime datetime not null comment '创建时间',
updateTime datetime not null comment '更新时间'
);
工程结构概述
- common:系统通用定义 (返回结果、状态码)
- config : 系统配置层 (session、Mybatis扫描路径)
- controller:接收用户请求 对参数做校验(调用service 返回执行结果)
- dao:数据库交互(调用mapper中定义的sql语句)
- exception : 异常处理 (自定义、全局)
- Interceptor : 拦截器使用
- model:实体对象 (根据数据库生成 )
- services:处理业务逻辑 ,定义接口(处理事务若遇异常向外抛)
- utill : 通用工具 (加密、非空校验)
- mapper:定义SQL语句(查数据库)
注册功能实现展示
注册交互图
参数要求
- 用户注册时需要提交的参数列表
确认密码:在Controller层对密码和确认密码进行校验,不通过直接返回错误
接口规范
后端具体实现
- 定义SQL,按用户名查询用户信息
- dao层中对应的类,添加SQL中定义的方法
- 创建接口,以及接口的实现类
- 进行service实现类中业务方法的编写
- 对所写的类进行单元测试
- 编写Controller层
- 接口测试
前端数据集成
使用JQuery完成AJAX请求,并处理HTML页面标签
// 构造数据
let postData = {
username : $('#username').val(),
nickname : $('#nickname').val(),
password : $('#password').val(),
passwordRepeat : $('#passwordRepeat').val()
};
// 发送AJAX请求
// contentType = application/x-www-form-urlencoded
// 成功后跳转到 sign-in.html
$.ajax ({
type : 'post',
url : 'user/register',
contentType : 'application/x-www-form-urlencoded',
data : postData,
// 回调方法
success: function(respData){
// 判断返回的状态码
if (respData.code == 0) {
// 跳转到登录页面
location.assign('/sign-in.html');
} else {
// 提示信息
$.toast({
heading: '警告',
text: respData.message,
icon: 'warning'
});
}
},
error : function() {
// 提示信息
$.toast({
heading: '错误',
text: '访问出现问题,请与管理员联系.',
icon: 'error'
});
}
});
接口拦截器实现
- 在application.yml 中引入项目自定义相关配置
# 项目自定义相关配置
project-forum:
login:
url: /sign-in.html # 未登录状况下强制跳转页面
- 确定要拦截的目标
mybatis生成类与映射文件
- 在pom.xml 中引入依赖
<mybatis-generator-plugin-version>1.4.1</mybatis-generator-plugin-version>
- 引入mybatis生成器插件:
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>${mybatis-generator-plugin-version}</version>
<executions>
<execution>
<id>Generate MyBatis Artifacts</id>
<phase>deploy</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<!-- 相关配置 -->
<configuration>
<!-- 打开⽇志 -->
<verbose>true</verbose>
<!-- 允许覆盖 -->
<overwrite>true</overwrite>
<!-- 配置⽂件路径 -->
<configurationFile>
src/main/resources/mybatis/generatorConfig.xml
</configurationFile>
</configuration>
</plugin>
- 配置文件(src/main/resources/mybatis/generatorConfig.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 驱动包路径,location中路径替换成自己本地路径 -->
<classPathEntry location="C:\Users\lenovo\.m2\repository\mysql\mysql-connector-java\5.1.49\mysql-connector-java-5.1.49.jar"/>
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 禁用自动生成的注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
<property name="suppressDate" value="true"/>
</commentGenerator>
<!-- 连接配置 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/forum_db?characterEncoding=utf8&useSSL=false"
userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver>
<!-- 小数统一转为BigDecimal -->
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 实体类生成位置 -->
<javaModelGenerator targetPackage="com.project.forum.model" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- mapper.xml生成位置 -->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- DAO类生成位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.project.forum.dao" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 配置生成表与实例, 只需要修改表名tableName, 与对应类名domainObjectName 即可-->
<table tableName="t_article" domainObjectName="Article" enableSelectByExample="false"
enableDeleteByExample="false" enableDeleteByPrimaryKey="false" enableCountByExample="false"
enableUpdateByExample="false">
<!-- 类的属性用数据库中的真实字段名做为属性名, 不指定这个属性会自动转换 _ 为驼峰命名规则-->
<property name="useActualColumnNames" value="true"/>
</table>
<table tableName="t_article_reply" domainObjectName="ArticleReply" enableSelectByExample="false"
enableDeleteByExample="false" enableDeleteByPrimaryKey="false" enableCountByExample="false"
enableUpdateByExample="false">
<property name="useActualColumnNames" value="true"/>
</table>
<table tableName="t_board" domainObjectName="Board" enableSelectByExample="false" enableDeleteByExample="false"
enableDeleteByPrimaryKey="false" enableCountByExample="false" enableUpdateByExample="false">
<property name="useActualColumnNames" value="true"/>
</table>
<table tableName="t_message" domainObjectName="Message" enableSelectByExample="false"
enableDeleteByExample="false" enableDeleteByPrimaryKey="false" enableCountByExample="false"
enableUpdateByExample="false">
<property name="useActualColumnNames" value="true"/>
</table>
<table tableName="t_user" domainObjectName="User" enableSelectByExample="false" enableDeleteByExample="false"
enableDeleteByPrimaryKey="false" enableCountByExample="false" enableUpdateByExample="false">
<property name="useActualColumnNames" value="true"/>
</table>
</context>
</generatorConfiguration>
-
创建好对应包,设置好数据表
-
在Insert 标签中添加获取主键值的选项
-
在dao层每个类中加入@Mapper注解;在model层下加入@Data,删掉get、set方法
-
配置Mybatis的扫描路径
# mybatis 相关配置,单独配置,顶格写
mybatis:
mapper-locations: classpath:mapper/**/*.xml # 指定 xxxMapper.xml的扫描路径
改造session存储到 redis
- 在 pom . xml 添加依赖框架
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
- 在 application.properties 中添加配置
# redis 配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.database=2
spring.session.store-type=redis
# session 过期时间
server.servlet.session.timeout=1800
spring.session.redis.flush-mode=on_save
spring.session.redis.namespace=spring:session
加盐算法实现
/**
* Description: 校验密码
*/
public class CheckUtil {
public static boolean check(String password , String DBSalt ,String DBPassword) {
String encryptPassword = MD5Util.md5Salt(password,DBSalt);
if (!encryptPassword.equalsIgnoreCase(DBPassword)) {
return false;
}
return true;
}
}
/**
* Description:密码加密方法
*/
public class MD5Util {
public static String md5 (String str) {
return DigestUtils.md5Hex(str);
}
public static String md5Salt (String str , String salt) {
return md5(md5(str)+salt);
}
}
/**
* Description: 密码的 salt
* 随机生成的uuid是36位(其中包含4个 “-”),去掉正好32位
*/
public class UUIDUtil {
public static String UUID_36() {
return UUID.randomUUID().toString();
}
public static String UUID_32() {
return UUID.randomUUID().toString().replace("-","");
}
}
部分Bug调试记录
- 编写完后端的获取用户信息接口后,又完成了前端的ajax编写,在测试成功时发现前端并未展示出用户信息,首先我抓包确定后端返回了需要的数据。
- 我认为是前端参数获取的问题,于是我调试起了前端代码,发现报错
TypeError: Cannot read properties of undefined (reading 'avatarUrl')
得出结果是:在尝试访问 respData.data 的属性之前,确定 respData.data 是否已经定义。 - 于是我重新检查后端代码,发现定义的返回结果data写成了date,导致undefined 报错
- 在修改了后端代码之后,数据正常显示出来
项目测试记录
Postman 接口测试
Jmeter 接口压测
项目配置问题记录
- 解决SpringBoot3整合Druid的兼容性问题
- java.lang.ClassNotFoundException: org.h2.Driver
- Spring Boot 3 集成 MyBatis
- VM warning