一.项目背景
1.项目简介
智汇论坛是一个集高科技与高效交流于一体的在线社区平台,旨在为用户提供一个便捷、智能的讨论空间。通过集成先进的服务器端技术和丰富的浏览器端技术,智汇论坛不仅支持用户之间的实时互动与信息共享,还确保了平台的高可用性和良好的用户体验。项目采用Spring Boot作为核心框架,结合Spring MVC和MyBatis实现快速开发与高效数据访问,同时利用HTML、CSS、JavaScript等前端技术打造响应式界面,提升用户交互的流畅性和视觉美感。
2.项目技术
服务器端技术
- Spring
- Spring Boot
- Spring MVC
- MyBatis
浏览器端技术
- HTML, CSS, JavaScript
- jQuery
- Bootstrap
数据库
- MySQL
项目构建工具
- Maven
版本控制工具
- Git + GITEE
二.环境搭建
1.项目创建
2.热部署
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
3.pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.javastudy</groupId>
<artifactId>forum</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>forum</name>
<description>forum</description>
<properties>
<java.version>1.8</java.version>
<mybatis-starter.version>2.3.0</mybatis-starter.version>
<druid-starter.version>1.2.16</druid-starter.version>
<mysql-connector.version>5.1.49</mysql-connector.version>
</properties>
<dependencies>
<!-- spring web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-starter.version}</version>
</dependency>
<!-- 热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- spring boot test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mysql连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector.version}</version>
<scope>runtime</scope>
</dependency>
<!-- 阿里巴巴druid数据源,如果使用SpringBoot默认的数据源,删除或注释这个依赖即可 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid-starter.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.application.yml文件
#配置数据源
spring:
application:
name: "forum" # 项目名
output:
ansi:
enabled: always # 控制台输出彩色日志
datasource:
url: jdbc:mysql://127.0.0.1:13306/forum_db?characterEncoding=utf8&useSSL=false # 数据库连接串
username: root # 数据库用户名字\
password: woaini520 # 数据库密码
driver-class-name: com.mysql.jdbc.Driver # 数据库连接驱动
# 服务器配置
server:
port: 8082 # 指定端口号
# mybatis 相关配置,单独配置,顶格写
mybatis:
mapper-locations: classpath:mapper/**/*.xml # 指定 xxxMapper.xml的扫描路径
configuration: # 配置打印 MyBatis 执行的 SQL
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 日志信息
logging:
pattern:
dateformat: yyyy-MM-dd HH:mm:ss # 日期格式
file:
path: logs/
level:
root: info
三.数据库设计
1.创建数据库
-- ----------------------------
-- 创建数据库,并指定字符集
-- ----------------------------
drop database if exists forum_db;
create database forum_db character set utf8mb4 collate utf8mb4_general_ci;
2.创建数据表
-- 选择数据库
use forum_db;
set names utf8mb4;
set foreign_key_checks = 0;
(1)用户表
-- ----------------------------
-- 创建用户表 for t_user
-- ----------------------------
drop table if exists `t_user`;
create table `t_user` (
`id` bigint(20) not null auto_increment comment '用户编号,主键,自增',
`username` varchar(20) character set utf8mb4 collate utf8mb4_general_ci not null comment '用户名,非空,唯一',
`password` varchar(32) character set utf8mb4 collate utf8mb4_general_ci not null comment '加密后的密码',
`nickname` varchar(50) character set utf8mb4 collate utf8mb4_general_ci not null comment '昵称,非空',
`phonenum` varchar(20) character set utf8mb4 collate utf8mb4_general_ci null default null comment '手机号',
`email` varchar(50) character set utf8mb4 collate utf8mb4_general_ci null default null comment '邮箱地址',
`gender` tinyint(4) not null default 2 comment '0女 1男 2保密,非空,默认2',
`salt` varchar(32) character set utf8mb4 collate utf8mb4_general_ci not null comment '为密码加盐,非空',
`avatarurl` varchar(255) character set utf8mb4 collate utf8mb4_general_ci null default null comment '用户头像url,默认系统图片',
`articlecount` int(11) not null default 0 comment '发帖数量,非空,默认0',
`isadmin` tinyint(4) not null default 0 comment '是否管理员,0否 1是,默认0',
`remark` varchar(1000) character set utf8mb4 collate utf8mb4_general_ci null default null comment '备注,自我介绍',
`state` tinyint(4) not null default 0 comment '状态 0 正常,1 禁言,默认0',
`deletestate` tinyint(4) not null default 0 comment '是否删除 0否 1是,默认0',
`createtime` datetime not null comment '创建时间,精确到秒',
`updatetime` datetime not null comment '更新时间,精确到秒',
primary key (`id`) using btree,
unique index `user_username_uindex`(`username`) using btree
) engine = innodb auto_increment = 2 character set = utf8mb4 collate = utf8mb4_general_ci comment = '用户表' row_format = dynamic;
set foreign_key_checks = 1;
(2) 文章表
-- ----------------------------
-- 创建帖子表 t_article
-- ----------------------------
drop table if exists `t_article`;
create table `t_article` (
`id` bigint(20) not null auto_increment comment '帖子编号,主键,自增',
`boardid` bigint(20) not null comment '关联板块编号,非空',
`userid` bigint(20) not null comment '发帖人,非空,关联用户编号',
`title` varchar(100) character set utf8mb4 collate utf8mb4_general_ci not null comment '标题,非空,最大长度100个字符',
`content` text character set utf8mb4 collate utf8mb4_general_ci not null comment '帖子正文,非空',
`visitcount` int(11) not null default 0 comment '访问量,默认0',
`replycount` int(11) not null default 0 comment '回复数据,默认0',
`likecount` int(11) not null default 0 comment '点赞数,默认0',
`state` tinyint(4) not null default 0 comment '状态 0正常 1 禁用,默认0',
`deletestate` tinyint(4) not null default 0 comment '是否删除 0 否 1 是,默认0',
`createtime` datetime not null comment '创建时间,精确到秒,非空',
`updatetime` datetime not null comment '修改时间,精确到秒,非空',
primary key (`id`) using btree
) engine = innodb auto_increment = 1 character set = utf8mb4 collate = utf8mb4_general_ci comment = '帖子表' row_format = dynamic;
(3)文章回复表
-- ----------------------------
-- 创建帖子回复表 t_article_reply
-- ----------------------------
drop table if exists `t_article_reply`;
create table `t_article_reply` (
`id` bigint(20) not null auto_increment comment '编号,主键,自增',
`articleid` bigint(20) not null comment '关联帖子编号,非空',
`postuserid` bigint(20) not null comment '楼主用户,关联用户编号,非空',
`replyid` bigint(20) null default null comment '关联回复编号,支持楼中楼',
`replyuserid` bigint(20) null default null comment '楼主下的回复用户编号,支持楼中楼',
`content` varchar(500) character set utf8mb4 collate utf8mb4_general_ci not null comment '回贴内容,长度500个字符,非空',
`likecount` int(11) not null default 0 comment '点赞数,默认0',
`state` tinyint(4) not null default 0 comment '状态 0 正常,1禁用,默认0',
`deletestate` tinyint(4) not null default 0 comment '是否删除 0否 1是,默认0',
`createtime` datetime not null comment '创建时间,精确到秒,非空',
`updatetime` datetime not null comment '更新时间,精确到秒,非空',
primary key (`id`) using btree
) engine = innodb auto_increment = 1 character set = utf8mb4 collate = utf8mb4_general_ci comment = '帖子回复表' row_format = dynamic;
(4)版块表
-- ----------------------------
-- 创建版块表 t_board
-- ----------------------------
drop table if exists `t_board`;
create table `t_board` (
`id` bigint(20) not null auto_increment comment '版块编号,主键,自增',
`name` varchar(50) character set utf8mb4 collate utf8mb4_general_ci not null comment '版块名,非空',
`articlecount` int(11) not null default 0 comment '帖子数量,默认0',
`sort` int(11) not null default 0 comment '排序优先级,升序,默认0,',
`state` tinyint(4) not null default 0 comment '状态,0 正常,1禁用,默认0',
`deletestate` tinyint(4) not null default 0 comment '是否删除 0否,1是,默认0',
`createtime` datetime not null comment '创建时间,精确到秒,非空',
`updatetime` datetime not null comment '更新时间,精确到秒,非空',
primary key (`id`) using btree
) engine = innodb auto_increment = 1 character set = utf8mb4 collate = utf8mb4_general_ci comment = '版块表' row_format = dynamic;
(5)站内信表
-- ----------------------------
-- 创建站内信表 for t_message
-- ----------------------------
drop table if exists `t_message`;
create table `t_message` (
`id` bigint(20) not null auto_increment comment '站内信编号,主键,自增',
`postuserid` bigint(20) not null comment '发送者,并联用户编号',
`receiveuserid` bigint(20) not null comment '接收者,并联用户编号',
`content` varchar(255) character set utf8mb4 collate utf8mb4_general_ci not null comment '内容,非空,长度255个字符',
`state` tinyint(4) not null default 0 comment '状态 0未读 1已读,默认0',
`deletestate` tinyint(4) not null default 0 comment '是否删除 0否,1是,默认0',
`createtime` datetime not null comment '创建时间,精确到秒,非空',
`updatetime` datetime not null comment '更新时间,精确到秒,非空',
primary key (`id`) using btree
) engine = innodb auto_increment = 1 character set = utf8mb4 collate = utf8mb4_general_ci comment = '站内信表' row_format = dynamic;
四.工程配置
1.生成类的映射文件
版本统一管理
<mybatis-generator-plugin-version>1.4.1</mybatis-generator-plugin-version>
在 build --> plugins 标签中加入如下配置
<build>
<plugins>
<!-- 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>
</plugins>
</build>
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="D:\java cave\Maven\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:13306/forum_db?characterEncoding=utf8&useSSL=false"
userId="root"
password="woaini520">
</jdbcConnection>
<javaTypeResolver>
<!-- 小数统一转为BigDecimal -->
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 实体类生成位置 -->
<javaModelGenerator targetPackage="com.javastudy.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.javastudy.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>
2.配置扫描配置
@Configuration
//指定mybatis的扫描路径
@MapperScan("com.javastudy.forum.dao")
public class MybatisConfig {
}
3.测试
输入一数据
INSERT INTO `forum_db`.`t_user` (`id`, `username`, `password`, `nickname`,
`gender`, `salt`, `avatarurl`, `articlecount`, `isadmin`, `state`,
`deletestate`, `createtime`, `updatetime`) VALUES (1, 'joyboy', '123456', '路飞',
2, '123', 'avatar.png',
0, 1, 0, 0, '2022-12-13 22:30:10', '2022-12-13 22:30:13');
测试代码
@SpringBootTest
@Slf4j
class UserMapperTest {
@Resource
UserMapper userMapper;
@Resource
ObjectMapper objectMapper;
@Test
void insert() {
}
@Test
void insertSelective() {
}
@Test
void selectByPrimaryKey() throws JsonProcessingException {
User user = userMapper.selectByPrimaryKey(1L);
log.info(objectMapper.writeValueAsString(user));
}
@Test
void updateByPrimaryKeySelective() {
}
@Test
void updateByPrimaryKey() {
}
}
五.编写代码
1.公共代码
(1)定义状态码
(2)Swagger自动生成
引入版本号
<springfox-boot-starter.version>3.0.0</springfox-boot-starter.version>
引入相关依赖
<!-- API⽂档⽣成,基于swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${springfox-boot-starter.version}</version>
</dependency>
<!-- SpringBoot健康监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
编写配置类
package com.javastudy.forum.config;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.*;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author Chooker
* @create 2023-08-09 12:45
*/
// 配置类
@Configuration
// 开启Springfox-Swagger
@EnableOpenApi
public class SwaggerConfig {
/**
* Springfox-Swagger基本配置
*
* @return
*/
@Bean
public Docket createApi() {
Docket docket = new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.javastudy.forum.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
// 配置API基本信息
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("论坛系统API")
.description("论坛系统前后端分离API测试")
.contact(new Contact("Chooker", "https://blog.csdn.net/qq_64580912", "ak1474502128@gmail.com"))
.version("1.0")
.build();
return apiInfo;
}
/**
* 解决SpringBoot 6.0以上与Swagger 3.0.0 不兼容的问题
* 复制即可
**/
@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier,
ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties, Environment environment) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
String basePath = webEndpointProperties.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment,
basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath),
shouldRegisterLinksMapping, null);
}
private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment,
String basePath) {
return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath)
|| ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
}
application.yml 中添加配置
spring:
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER #Springfox-Swagger兼容性配置
具体API
API常用注解
@Api: 作用在Controller上,对控制器类的说明
tags="说明该类的作用,可以在前台界面上看到的注解
@ApiModel: 作用在响应的类上,对返回响应数据的说明
@ApiModelProerty:作用在类的属性上,对属性的说明
@ApiOperation: 作用在具体方法上,对API接口的说明
@ApiParam: 作用在方法中的每⼀个参数上,对参数的属性进行说明
六.具体功能实现
1.注册功能实现
(1)在Mapper.xml中编写SQL语句
写入操作
<insert id="insertSelective" parameterType="com.javastudy.forum.model.User">
insert into t_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="username != null">
username,
</if>
<if test="password != null">
password,
</if>
<if test="nickname != null">
nickname,
</if>
<if test="phonenum != null">
phonenum,
</if>
<if test="email != null">
email,
</if>
<if test="gender != null">
gender,
</if>
<if test="salt != null">
salt,
</if>
<if test="avatarurl != null">
avatarurl,
</if>
<if test="articlecount != null">
articlecount,
</if>
<if test="isadmin != null">
isadmin,
</if>
<if test="remark != null">
remark,
</if>
<if test="state != null">
state,
</if>
<if test="deletestate != null">
deletestate,
</if>
<if test="createtime != null">
createtime,
</if>
<if test="updatetime != null">
updatetime,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=BIGINT},
</if>
<if test="username != null">
#{username,jdbcType=VARCHAR},
</if>
<if test="password != null">
#{password,jdbcType=VARCHAR},
</if>
<if test="nickname != null">
#{nickname,jdbcType=VARCHAR},
</if>
<if test="phonenum != null">
#{phonenum,jdbcType=VARCHAR},
</if>
<if test="email != null">
#{email,jdbcType=VARCHAR},
</if>
<if test="gender != null">
#{gender,jdbcType=TINYINT},
</if>
<if test="salt != null">
#{salt,jdbcType=VARCHAR},
</if>
<if test="avatarurl != null">
#{avatarurl,jdbcType=VARCHAR},
</if>
<if test="articlecount != null">
#{articlecount,jdbcType=INTEGER},
</if>
<if test="isadmin != null">
#{isadmin,jdbcType=TINYINT},
</if>
<if test="remark != null">
#{remark,jdbcType=VARCHAR},
</if>
<if test="state != null">
#{state,jdbcType=TINYINT},
</if>
<if test="deletestate != null">
#{deletestate,jdbcType=TINYINT},
</if>
<if test="createtime != null">
#{createtime,jdbcType=TIMESTAMP},
</if>
<if test="updatetime != null">
#{updatetime,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>
根据用户名查询用户信息
创建如图的目录结构,并在extension目录下创建UserExtMapper.xml文件,里面编写自动生成以外的代码.
可以看出 UserExtMapper.xml与UserMapper.xml是共用resultMap和Base_Column_List的
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.javastudy.forum.dao.UserMapper">
<!--
1. 注意namespace表示命名空间,要与 UserMapper.xml中的namespace相同
2. 统一用com.javastudy.forum.dao.UserMapper, 也就是UserMapper的完全限定名(包名+类名)
3. 不同的映射文件指定了相同的namespace后,定义的所有用id或name标识的结果集映射都可以在不同的文件中共享
-->
<!-- 根据用户名查询用户信息-->
<select id="selectByUsername" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_user where username=#{username,jdbcType=VARCHAR};
</select>
</mapper>
(2)在Mapper.java中定义方法
@Mapper
public interface UserMapper {
int insert(User row);
int insertSelective(User row);
User selectByPrimaryKey(Long id);
int updateByPrimaryKeySelective(User row);
int updateByPrimaryKey(User row);
/**
* 根据用户名查询用户信息
* @param username
* @return
*/
User selectByUsername(String username);
}
(3)定义Service接口
public interface IUserService {
User selectByUsername(String username);
int createNormalUser(User user);
}
(4)实现Serivce接口
@Slf4j //日志
@Service
public class UserServiceImpl implements IUserService {
@Resource
UserMapper userMapper;
@Override
public User selectByUsername(String username) {
//非空校验
if (StringUtils.isEmpty(username)) {
//打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
//抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
//根据用户名查询用户信息
User user = userMapper.selectByUsername(username);
return user;
}
@Override
public int createNormalUser(User user) {
//非空校验
if (user == null || StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getNickname()) ||
StringUtils.isEmpty(user.getPassword()) || StringUtils.isEmpty(user.getSalt())) {
//打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
//抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
//校验用户名是否存在
User exitsUser = userMapper.selectByUsername(user.getUsername());
if (exitsUser != null) {
//打印日志
log.warn(ResultCode.FAILED_USER_EXISTS.toString());
//抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_EXISTS));
}
//为性別设置默认值
if (user.getGender() != null) {
if (user.getGender() < 0 || user.getGender() > 2) {
user.setGender((byte) 2);
}
} else {
user.setGender((byte) 2);
}
//为发帖数设置默认值
user.setArticlecount(0);
//设置是否管理员
user.setIsadmin((byte) 0);
//设置状态
user.setState((byte) 0);
//设置是否删除
user.setDeletestate((byte) 0);
//设置时间
Date date = new Date();
user.setCreatetime(date);
user.setUpdatetime(date);
//写入数据库
int row = userMapper.insertSelective(user);
if (row != 1) {
//打印日志
log.warn(ResultCode.FAILED_CREATE.toString() + " 注册用户失败,username:" + user.getUsername());
//抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));
}
return row;
}
}
(5)单元测试
(6)Controller实现方法外提供API接口
@RestController
@Slf4j
@RequestMapping("/user")
@Api(tags = "用户接口")
public class UserController {
@Resource
IUserService userService;
@ApiOperation("用户注册")
@PostMapping("/register")
public AppResult register(@ApiParam("用户名") @RequestParam("username") @NonNull String username,
@ApiParam("昵称") @RequestParam("nickname") @NonNull String nickname,
@ApiParam("密码") @RequestParam("password") @NonNull String password,
@ApiParam("确定密码") @RequestParam("passwordRepeat") @NonNull String passwordRepeat) {
if (!password.equals(passwordRepeat)) {
return AppResult.failed(ResultCode.FAILED_TWO_PWD_NOT_SAME);
}
User user = new User();
user.setUsername(username);
user.setNickname(nickname);
//对密码进行处理
String salt = UUIDUtils.UUID_32();
password = MD5Utils.md5Salt(password, salt);
user.setPassword(password);
user.setSalt(salt);
userService.createNormalUser(user);
return AppResult.success("注册成功");
}
}
(7)测试API接口
(8)实现前端逻辑,完成前后端交互
// 构造数据
var 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'
});
}
},
//http请求的失败
error: function () {
$.toast({
heading: '错误',
text: '访问出现问题,请联系管理员',
icon: 'error'
});
}
});
});
2.登录功能实现
与上述功能类似,这里不再演示
3.退出功能实现
退出功能直接在session中删除用户信息即可,因此不需要前面的五步
4.获取用户信息实现
(1)Controller实现方法外提供API接口
@ApiOperation("获取用户信息")
@GetMapping("/info")
public AppResult<User> getInfo(HttpServletRequest httpServletRequest) {
//获取Session对象
HttpSession session = httpServletRequest.getSession(false);
if (session == null || session.getAttribute(AppConfig.SESSION_USER_KEY) == null) {
return AppResult.failed("用户未登录");
}
User user = (User) session.getAttribute(AppConfig.SESSION_USER_KEY);
return AppResult.success(user);
}
(2)测试API接口
此时我们进行测试可以得到以上的数据,但是此时还是有一定的弊端的
- 密码和盐不能在网络上进行传输,会有安全隐患
- 有些为空的数据没有必要传输
- 日期信息的传输格式不是常规的
接下来一一来解决以上的问题.
1. 解决密码和盐传输的问题
在实体类属性上面加入@JsonIgnore注解
@ApiModelProperty("密码")
@JsonIgnore
private String password;
@ApiModelProperty("性别")
private Byte gender;
@ApiModelProperty("盐")
@JsonIgnore
private String salt;
@ApiModelProperty("删除状态")
@JsonIgnore
private Byte deletestate;
2.解决空数据传输的问题和解决日期格式的问题
# JSON序列化配置
jackson:
date-format: yyyy-MM-dd HH:mm:ss # 日期格式
default-property-inclusion: NON_NULL # 不为null时序列化
登录的时候,发现data信息不见了,因为data信息为null,所以没有进行序列化,但是实际上code,message,data信息就算是null也是要进行传输的.
加入以下注解可以解决这个问题.
@ApiModelProperty("状态码")
@JsonInclude(JsonInclude.Include.ALWAYS) // 任何情况下都参与JSON序列化
private Long code;
@ApiModelProperty("错误信息")
@JsonInclude(JsonInclude.Include.ALWAYS) // 任何情况下都参与JSON序列化
private String message;
@ApiModelProperty("返回的数据")
@JsonInclude(JsonInclude.Include.ALWAYS) // 任何情况下都参与JSON序列化
private T data;
头像同样也是需要的
@ApiModelProperty("头像地址")
@JsonInclude(JsonInclude.Include.ALWAYS)
private String avatarurl;
5.拦截器的实现
论坛中的大部分接口都需要在登录的情况下进行访问,因此我们需要一个拦截器对访问页面的时候对未登录进行校验
6.获取用户信息实现2
当我们进入一个帖子的时候,我们点击发帖的用户,可以看到用户的信息,此时前端给我们一个id,我们需要从数据库中根据id插叙到用户的信息.
7.获取版块信息实现
先向数据库中插入一些板块的信息.
-- 写入版块信息数据
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`, `deleteState`, `createTime`, `updateTime`) VALUES (1, 'Java', 0, 1, 0, 0, '2023-01-14 19:02:18', '2023-01-14 19:02:18');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`, `deleteState`, `createTime`, `updateTime`) VALUES (2, 'C++', 0, 2, 0, 0, '2023-01-14 19:02:41', '2023-01-14 19:02:41');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`, `deleteState`, `createTime`, `updateTime`) VALUES (3, '前端技术', 0, 3, 0, 0, '2023-01-14 19:02:52', '2023-01-14 19:02:52');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`, `deleteState`, `createTime`, `updateTime`) VALUES (4, 'MySQL', 0, 4, 0, 0, '2023-01-14 19:03:02', '2023-01-14 19:03:02');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`, `deleteState`, `createTime`, `updateTime`) VALUES (5, '面试宝典', 0, 5, 0, 0, '2023-01-14 19:03:24', '2023-01-14 19:03:24');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`, `deleteState`, `createTime`, `updateTime`) VALUES (6, '经验分享', 0, 6, 0, 0, '2023-01-14 19:03:48', '2023-01-14 19:03:48');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`, `deleteState`, `createTime`, `updateTime`) VALUES (7, '招聘信息', 0, 7, 0, 0, '2023-01-25 21:25:33', '2023-01-25 21:25:33');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`, `deleteState`, `createTime`, `updateTime`) VALUES (8, '福利待遇', 0, 8, 0, 0, '2023-01-25 21:25:58', '2023-01-25 21:25:58');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`, `deleteState`, `createTime`, `updateTime`) VALUES (9, '灌水区', 0, 9, 0, 0, '2023-01-25 21:26:12', '2023-01-25 21:26:12');
8.获得帖子信息实现
9.获得指定帖子信息实现
10.根据Id获取板块信息
11.发布新帖操作
发布帖子涉及到的是文章表(article),其次需要更新用户的文章数,涉及用户表(user),最后需要更新板块的帖子数,涉及到板块表(board),并且这三个操作要么都成功,要么都失败,因此我们需要给这三个操作加上事务.
12.获取文章详情
获取文章详情的时候也需要获取相关用户的信息,板块的信息
13.文章访问数量的增加
14..编辑文章
在编辑文章之前,我们需要在文章中增加一个属性,来表示这篇文章是不是属于作者的,前端进行判断来选择展示修改和删除文章的按钮
15.删除文章
删除文章的时候需要做以下三个操作
1.需要将删除文章的deleteState置为1(article表)
2.需要将相应板块的文章数减一(board表)
3.需要将相应用户的文章数减一(user表)
16.点赞文章
17.根据文章Id查询回复列表
进行插入数据
-- 写入回复表数据
INSERT INTO t_article_reply VALUES (NULL, 1, 1, NULL, NULL, '回复内容111', 0, 0, 0, '2023-08-14 16:52:00', '2023-08-14 16:52:00');
INSERT INTO t_article_reply VALUES (NULL, 1, 1, NULL, NULL, '回复内容222', 0, 0, 0, '2023-08-14 16:53:00', '2023-08-14 16:53:00');
INSERT INTO t_article_reply VALUES (NULL, 1, 1, NULL, NULL, '回复内容333', 0, 0, 0, '2023-08-14 16:54:00', '2023-08-14 16:54:00');
INSERT INTO t_article_reply VALUES (NULL, 1, 2, NULL, NULL, '回复内容444', 0, 0, 0, '2023-08-14 16:55:00', '2023-08-14 16:55:00');
INSERT INTO t_article_reply VALUES (NULL, 1, 2, NULL, NULL, '回复内容555', 0, 0, 0, '2023-08-14 16:56:00', '2023-08-14 16:56:00');
18.回复文章
回复文章需要进行如下两个操作
1.插入一个回复(ArticleReply表)
2.增加文章的回复数(Article表)
因为涉及到多次更新的操作,因此也需要事务操作
19.用户个人信息和文章展示
已经有根据id获取用户信息的方法,现在我们只需要根据userId获取所有文章信息的接口即可
20.修改用户信息
修改用户信息,首先需要获取用户的信息,前面的接口已经实现,我们只需要在前端根据接口获取信息展示到页面上即可
21.修改密码
22.站内信功能
这个功能涉及到的是message表
23.查看未读数
24.获取消息列表
在获取消息的时候,我们也需要发送者的用户信息,因此在message实体类中加上一个属性
25.站内信已读状态更新
26.站内信回复功能
七.项目的部署与发布
1.部署linux服务器
1.创建数据库
可以将sql语句变成一个文件,然后执行下面的代码
source /root/java78/table.sql
2.打包代码
打包完成之后,将jar包拖拽到linux服务器上
3.运行代码
后台启动项目
nohup java -jar Blog_Spring-0.0.1-SNAPSHOT.jar &
查看日志
cd logs/
tail -f spring.log
终止当前的服务
ps -ef | grep [ ]
kill -9 pid
注意:如果开启多个服务,需要开端口,给防火墙添加端口号
查看防火墙状态(如果没开启,建议开启,不开启可以直接访问,开启了需要进行已下的操作访问)
systemctl status firewalld
启动防火墙和关闭防火墙
systemctl start firewalld
systemctl stop firewalld
查看开放的端口号
firewall-cmd --list-ports
开启8080端口
firewall-cmd --permanent --add-port=8080/tcp
重启防火墙
firewall-cmd --reload
设置开机启动
systemctl enable firewalld
添加安全组
2.访问测试
八.项目描述
- 使用统一返回格式+全局错误信息定义处理前后端交互时的返回结果
- 使用@ControllerAdvice+@ExceptionHandler实现全局异常处理
- 使用拦截器实现用户登录校验
- 使用MybatisGeneratorConfig生成常的增删改查方法
- 集成Swagger实现自动生成API测试接口
- 使用jQuery完成AJAX请求,并处理HTML页面标签
- 对数据库中常用的查询字段建立索引,并使用查询计划分析索引是否生效
九.代码获取
代码链接:智汇论坛