【Springboot3+vue3】从零到一搭建Springboot3+vue3前后端分离项目,整合knef4j和mybaits实现基础用户信息管理
- 后端环境搭建
- 1.1 环境准备
- 1.2 数据库表准备
- 1.3 SpringBoot3项目创建
- 1.4 MySql环境整合,使用druid连接池
- 1.5 整合mybatis-plus
- 1.5.1 引入mybaties-plus
- 1.5.2 配置代码生成器
- 1.5.3 配置分页插件
- 1.6 整合swagger3(knife4j)
- 1.6.1 整合
- 1.6.2 使用
- 1.7 数据交互处理
- 1.7.1响应数据封装(公共返回数据类)
- 1.7.2 分页查询返回数据封装
- 1.8 全局异常处理
- 1.9 整合JWT,生成token
- 1.10 封装ThreadLocal工具类
- 1.11 MD5封装
- 1.12 登陆验证拦截
- 1.13 新增,登录和查询当前用户
- 1.13.1 新增用户
- 1.13.2 用户登录
- 1.13.3 获取当前用户信息
- 1.13.4 查询所有用户信息
- 1.14 删除和修改用户信息
- 1.14.1 删除用户信息
- 1.14.2 修改用户信息
跟着黑马敲了黑马点评和苍穹外卖,但是感觉黑马教程封装的太好了。于是想自己从零到一搭建一个前后端分离的简单项目。
主要参考的博客为:
从零搭建SpringBoot3+Vue3前后端分离项目基座,中小项目可用_springboot+vue3-CSDN博客
记录一下自己的实现过程。
最终实现效果如下:
后端环境搭建
1.1 环境准备
-spring-boot3 最低支持jdk17,所以需要准备jdk17的环境
-Idea版本 2024.1.4
-Mysql8
1.2 数据库表准备
可以随便使用一个数据库可视化软件,然后创建数据库data_demo,并运行以下查询,创建用户表
我用的是Navicat Premium
建好后,选中数据库,右键选择新建查询
运行如下命令,创建数据库表
CREATE TABLE user(
id INT NOT NULL AUTO_INCREMENT COMMENT '主键',
login_name VARCHAR(255) NOT NULL COMMENT '登录名(账号)',
password VARCHAR(255) NOT NULL COMMENT '密码',
name varchar(50) NOT NULL COMMENT '姓名',
sex char(1) NOT NULL COMMENT '性别',
phone VARCHAR(20) COMMENT '联系电话',
PRIMARY KEY(id)
)COMMENT '用户信息表';
1.3 SpringBoot3项目创建
之后等待项目构建完成。构建完成的pom.xml文件如下,注意我在这里修改了spring的version,修改为3.3.3 ,因为在后面集成knef4j的时候发现3.4.0版本的spring无法显示接口。
参考博客如下:
尚硅谷新SSM教程中关于Knife4j 接口文档无法显示接口的问题_knife4j 识别不到controller-CSDN博客
即,正确的启动控制台没问题,但是不显示接口信息,后面将spring的版本降为3.3.3,成功解决
<?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>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.myProject</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>demo</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.4 MySql环境整合,使用druid连接池
在pom.xml中添加jdbc依赖,防止项目启动时找不到Bean报错
<!--添加JDBC依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
添加druid依赖
<!--添加Druid依赖-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-3-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.20</version>
</dependency>
项目使用yml格式配置文件,修改resources下的application.properties为application.yml
,删除文件原本内容,并将application.yml文件内容加上如下
配置项目端口号
server:
port: 9999
在application.yml
中配置数据源和druid连接池 ,注意将database的name修改为自己数据库的名称,以及mysql的username和password
# ???????
database:
name: data_demo
spring:
datasource: #????
url: jdbc:mysql://localhost:3306/${database.name}?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
启动项目, 查看日志,端口和druid 初始化, 整合成功
1.5 整合mybatis-plus
1.5.1 引入mybaties-plus
官网地址: https://baomidou.com/
- pom.xml中添加如下依赖
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-spring-boot3-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
- application.yml中加入如下配置
# mybatis ??????
mybatis-plus:
configuration:
#????????
map-underscore-to-camel-case: true
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.demo
启动项目,出现mybatis-plus日志
1.5.2 配置代码生成器
- pom.xml中添加如下依赖
<!-- mybatis-plus 代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.5</version>
</dependency>
<!-- mybatis-plus代码生成器模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
2.新建utils包,并在下边创建MybatisPlusGenerator.java代码生成器配置类
package com.example.demo.utils;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.io.File;
import java.sql.Types;
import java.util.Collections;
public class MybatisPlusGenerator {
static final String url = "jdbc:mysql://127.0.0.1:3306/data_demo?serverTimezone=UTC"; // 数据库地址
static final String username = "root"; // 数据库用户名
static final String password = "123456"; // 数据库密码
static final String authorName = "HuaXiang"; // 作者名
static final String parentPackageNameResource = "com/example/demo"; // mapper.xml路径
static final String parentPackageNameJava = "com.example.demo"; // java 文件父包名
// 要生成代码对应的数据表名
static final String tableName = "user";
public static void main(String[] args) {
FastAutoGenerator.create(url, username, password)
// 1.全局配置
.globalConfig(builder -> {
builder.author(authorName) // 设置作者
.enableSpringdoc() // 开启 swagger 模式
// 获取当前工程路径并定位到项目java目录下
.outputDir(System.getProperty("user.dir") + "/src/main/java"); // 指定输出目录
})
// 2.数据源配置
.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
}))
// 3.包名策略配置
.packageConfig(builder -> {
builder.parent(parentPackageNameJava) // 设置父包名
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
//.moduleName("system") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/" + "/mapper")); // 设置mapperXml生成路径
})
// 策略配置
.strategyConfig(builder -> {
builder.addInclude(tableName) // 设置需要生成的表名
// 覆盖已生成文件
.entityBuilder().enableFileOverride()
.mapperBuilder().enableFileOverride()
.serviceBuilder().enableFileOverride().formatServiceFileName("%sService");
//.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
// 配置模板
.templateConfig(builder -> {
//builder.controller(""); // 不生成controller
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
- 修改数据库相关信息
- 修改authorName
- 修改parentPackageNameResource, parentPackageNameJava
- 修改tableName, 要生成代码的数据表名称,多个表使用,分割
- .enableSpringdoc() 可以选择,生成swagger3文档注释
修改完成后运行main函数,生成相应代码,mapper.xml在resource下与java同路径下
运行后目录如下:
1.5.3 配置分页插件
新建config包,并在下边配置类MybatiesPlusConfig.java
package com.example.demo.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
return interceptor;
}
}
在启动类配置扫描mapper路径@MapperScan(“com.example.demo.mapper”)
1.6 整合swagger3(knife4j)
1.6.1 整合
- pom.xml引入knife4j的依赖
<!-- API 文档 knife4j -->
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
- application.yml中添加配置
# springdoc-openapi 配置
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs
group-configs:
- group: 'default'
paths-to-match: '/**'
packages-to-scan: com.example.demo.controller
# knife4j 配置
knife4j:
# 是否启用增强
enable: true
# 开启生产环境屏蔽
production: false
# 是否认证登录
basic:
# basic是否开启,默认为false
enable: true
username: root
password: 123456
setting:
language: zh_cn
enable-version: true
enable-swagger-models: true
- 测试。配置完成后,访问路径:http://localhost:9999/doc.html ,用户名密码为配置文件中的用户名和密码,如图
1.6.2 使用
实体类使用@Schema注解,在mybatis-plus选择的话会自动生成
Controller层使用
/**
* <p>
* 用户信息表 前端控制器
* </p>
* @since 2024-12-06
*/
@Tag(name = "用户接口")
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 用户列表分页
* @param pageSize 每页显示的条数
* @param currentPage 要查询的页
* @param name 用户姓名
* @return Result<PageResultBean<List<User>>>
*/
@GetMapping("/pageList")
@Operation(summary = "用户列表分页查询")
@Parameters({
@Parameter(name = "Authorization", in = ParameterIn.HEADER, required = true, description = "token"),
@Parameter(name = "pageSize", required = true, description = "每页显示的条数"),
@Parameter(name = "currentPage", required = true, description = "要查询的页"),
@Parameter(name = "name", description = "用户姓名", required = false)
})
public Result<PageResultBean<User>> pageList(@RequestParam int pageSize, @RequestParam int currentPage, @Nullable @RequestParam String name) {
IPage<User> page = userService.pageList(pageSize, currentPage, name);
if (page == null) {
return Result.error("查询失败");
}
//PageResultBean<User> pageResultBean = new PageResultBean<User>(page.getTotal(), page.getRecords());
return Result.success(PageResultBean.getInstance(page.getTotal(), page.getRecords()));
}
}
1.7 数据交互处理
1.7.1响应数据封装(公共返回数据类)
封装返回数据封装类,放到common包下:
package com.example.demo.common;
import io.swagger.v3.oas.annotations.media.Schema;
public class Result<T> {
@Schema(description = "业务状态码 1:成功 0: 失败")
private int code; // 业务状态码 0:成功 1: 失败
@Schema(description = "提示信息")
private String message; // 提示信息
@Schema(description = "返回数据")
private T data; // 响应数据
public Result(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
// 操作成功返回响应结果(带响应数据)
public static <E> Result<E> success(E data) {
return new Result<>(1, "操作成功", data);
}
public static <E> Result<E> success() {
return new Result<>(1, "操作成功", null);
}
public static <E> Result<E> error(String message) {
return new Result<>(0, message, null);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
1.7.2 分页查询返回数据封装
封装分页查询的数据,放到common包下
package com.example.demo.common;
import io.swagger.v3.oas.annotations.media.Schema;
public class Result<T> {
@Schema(description = "业务状态码 0:成功 1: 失败")
private int code; // 业务状态码 0:成功 1: 失败
@Schema(description = "提示信息")
private String message; // 提示信息
@Schema(description = "返回数据")
private T data; // 响应数据
public Result(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
// 操作成功返回响应结果(带响应数据)
public static <E> Result<E> success(E data) {
return new Result<>(0, "操作成功", data);
}
public static <E> Result<E> success() {
return new Result<>(0, "操作成功", null);
}
public static <E> Result<E> error(String message) {
return new Result<>(1, message, null);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
1.8 全局异常处理
创建exception包,在包下放全局异常处理类GlobalExceptionHandler.java,
使用springboot @RestControllerAdvice注解配置
package com.example.demo.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.example.demo.common.Result;
// 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(Exception.class)
public Result<String> handlerException(Exception e) {
logger.warn(e.getMessage());
return Result.error(StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "操作失败");
}
}
1.9 整合JWT,生成token
- pom.xml 中引入依赖
<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
- 封装工具类JwtUtil,放到utils包下
package com.example.demo.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
private static final String KEY = "Demo"; // 密钥
// 接收数据,生成token并返回
public static String getToken(Map<String, Object> claims) {
return JWT.create()
.withClaim("claims", claims)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 失效时间1小时
.sign(Algorithm.HMAC256(KEY));
}
// 接收token,验证并返回数据
public static Map<String, Object> parseToken(String token) {
return JWT.require(Algorithm.HMAC256(KEY))
.build()
.verify(token)
.getClaim("claims")
.asMap();
}
}
1.10 封装ThreadLocal工具类
因为前后端分离,使用ThreadLocal线程变量存储用户登录信息,替代session
package com.example.demo.utils;
public class ThreadLocalUtil {
// 提供ThreadLocal 对象
private static final ThreadLocal<Object> THREAD_LOCAL = new ThreadLocal<>();
// 获取存储值
public static <T> T get() {
return (T) THREAD_LOCAL.get();
}
// 存储值
public static void set(Object value) {
THREAD_LOCAL.set(value);
}
// 清除THREAD_LOCAL 防止内存泄漏
public static void remove() {
THREAD_LOCAL.remove();
}
}
1.11 MD5封装
package com.example.demo.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Md5Util {
/**
* 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合
*/
protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
protected static MessageDigest messagedigest = null;
static {
try {
messagedigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsaex) {
System.err.println(Md5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");
nsaex.printStackTrace();
}
}
/**
* 生成字符串的md5校验值
*
* @param s
* @return
*/
public static String getMD5String(String s) {
return getMD5String(s.getBytes());
}
/**
* 判断字符串的md5校验码是否与一个已知的md5码相匹配
*
* @param password 要校验的字符串
* @param md5PwdStr 已知的md5校验码
* @return
*/
public static boolean checkPassword(String password, String md5PwdStr) {
String s = getMD5String(password);
return s.equals(md5PwdStr);
}
public static String getMD5String(byte[] bytes) {
messagedigest.update(bytes);
return bufferToHex(messagedigest.digest());
}
private static String bufferToHex(byte bytes[]) {
return bufferToHex(bytes, 0, bytes.length);
}
private static String bufferToHex(byte bytes[], int m, int n) {
StringBuffer stringbuffer = new StringBuffer(2 * n);
int k = m + n;
for (int l = m; l < k; l++) {
appendHexPair(bytes[l], stringbuffer);
}
return stringbuffer.toString();
}
private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>>
// 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换
stringbuffer.append(c0);
stringbuffer.append(c1);
}
// 测试
public static void main(String[] args) {
System.out.println(Md5Util.getMD5String("admin"));
}
}
1.12 登陆验证拦截
- 创建包interceptors,在包下配置登录拦截器
package com.example.demo.interceptors;
import com.example.demo.utils.JwtUtil;
import com.example.demo.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Map;
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取token
String token = request.getHeader("Authorization");
// 验证token
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
// 存储业务信息到线程变量
ThreadLocalUtil.set(claims);
// 放行拦截
return true;
} catch (Exception e) {
// 登录信息异常或未登录,http响应状态码为401
response.setStatus(401);
// 拦截请求
return false;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清除ThreadLocal业务数据
ThreadLocalUtil.remove();
}
}
- 在config包下创建WebConfig配置类,注册登录拦截器
package com.example.demo.config;
import com.example.demo.interceptors.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 配置放行资源
// 无需拦截的接口集合
List<String> ignorePath = new ArrayList<>();
// knife4j(swagger)
ignorePath.add("/swagger-resources/**");
ignorePath.add("/doc.html");
ignorePath.add("/v3/**");
ignorePath.add("/webjars/**");
ignorePath.add("/static/**");
ignorePath.add("/templates/**");
ignorePath.add("/error");
// 登录页面
ignorePath.add("/user/login");
ignorePath.add("/user/add");
registry.addInterceptor(loginInterceptor).excludePathPatterns(ignorePath);
}
}
1.13 新增,登录和查询当前用户
1.13.1 新增用户
逻辑为:
- 参数验证。对传入的user进行基本的非空校验,校验用户姓名,登录名以及密码是否为空或者null。为空则返回错误提示信息
- 用户名重复校验。通过userService中的
findByLoginName
去数据库中查询是否存在相同loginName的用户 - 密码非空判断,假如密码为空,设置为默认的123456
- 密码加密处理。使用1.11 中的MD5对数据库中的密码进行加密
- 保存用户信息到数据库,调用userService中的
add
方法。
在UserController.java中加入如下方法
/**
* 添加用户
* @param User
* @return Result<String>
*/
@PostMapping("/add")
@Operation(summary = "添加用户")
@Parameter(name = "Authorization", in = ParameterIn.HEADER, required = true, description = "token")
public Result<String> addUser(@RequestBody User user) {
return userService.add(user);
}
在UserService.java中加入如下方法
Result<String> add(User user);
UserServiceImpl.java中
package com.example.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.utils.Md5Util;
import org.springframework.stereotype.Service;
/**
* <p>
* 用户信息表 服务实现类
* </p>
*
* @author HuaXiang
* @since 2024-12-06
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
private final UserMapper userMapper;
public UserServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}
public IPage<User> pageList(int pageSize, int currentPage, String name) {
// 创建Page对象,传入当前页码和每页显示的记录数
Page<User> page = new Page<>(currentPage, pageSize);
// 可以在这里添加条件构造器来添加更复杂的查询条件,比如根据用户名模糊查询等
// 以下是示例,如果name不为空进行模糊查询,实际根据需求调整
if (name!= null &&!name.isEmpty()) {
// 假设User实体类中有对应的username字段用于查询,此处只是示例,需根据实际调整
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(User::getName, name);
return baseMapper.selectPage(page, queryWrapper);
}
// 如果没有额外条件,直接进行简单分页查询
return baseMapper.selectPage(page, null);
}
@Override
public User findByLoginName(String loginName) {
return userMapper.findByLoginName(loginName);
}
@Override
public Result<String> add(User user) {
// 登录名校验
if (user.getLoginName() == null || "".equals(user.getLoginName())) {
return Result.error("登录名不能为空");
}
// 登录名重复校验
if (findByLoginName(user.getLoginName()) != null) {
return Result.error("登录名已存在,请重新输入");
}
if (user.getName() == null || "".equals(user.getName())) {
return Result.error("姓名不能为空");
}
if (user.getPassword() == null || "".equals(user.getPassword())) {
user.setPassword(Md5Util.getMD5String("123456"));
}else{
user.setPassword(Md5Util.getMD5String(user.getPassword()));
}
if (userMapper.insert(user) > 0) {
return Result.success("添加成功");
}
return Result.error("添加失败");
}
}
UserMapper.java中
package com.example.demo.mapper;
import com.example.demo.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/**
* <p>
* 用户信息表 Mapper 接口
* </p>
*
* @author HuaXiang
* @since 2024-12-06
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT * FROM user WHERE login_name = #{loginName}")
User findByLoginName(String loginName);
}
使用swagger测试新增用户功能,在浏览器中输入http://localhost:9999/doc.html
请求参数如下:
{
"loginName": "user1",
"password": "user1",
"name": "user1",
"sex": "男",
"phone": "15612341234"
}
数据库中查看,已经成功插入用户user1,密码也为user1
1.13.2 用户登录
- 接口定义:POST请求,路径为/login,
- 参数校验:对User对象判断,判断对象或则和loginName,password是否为空,为空则直接返回错误信息
- 用户名存在性校验调用userService.findByLoginName方法
- 密码正确性校验及登陆成功处理。
/**
* 用户登录
* @param user
* @return
*/
@PostMapping("/login")
@Operation(summary = "用户登录")
@Parameters({
@Parameter(name = "loginName", description = "登录名", required = true, schema = @Schema(type = "sting")),
@Parameter(name = "password", description = "密码", required = true, schema = @Schema(type = "sting"))
})
public Result<String> login(@RequestBody User user) {
if (user == null || "".equals(user.getLoginName()) || user.getLoginName() == null || "".equals(user.getPassword()) || user.getPassword() == null) {
return Result.error("用户名密码不能为空");
}
// 检验用户名是否存在
User eruser = userService.findByLoginName(user.getLoginName());
if (eruser == null) {
return Result.error("用户名不存在");
}
// 检验用户密码是否正确
if (Md5Util.getMD5String(user.getPassword()).equals(eruser.getPassword())) {
Map<String, Object> claims = new HashMap<>();
claims.put("id", eruser.getId());
claims.put("name", eruser.getName());
claims.put("loginName", eruser.getLoginName());
String token = JwtUtil.getToken(claims);
return Result.success(token);
}
return Result.error("密码错误");
}
测试
用刚刚新创建的用户测试用户登录功能。
1.13.3 获取当前用户信息
UserController.java
/**
* 获取当前登录用户信息
* @return User
*/
@GetMapping("/currentUser")
@Operation(summary = "获取当前登录用户信息")
@Parameter(name = "Authorization", in = ParameterIn.HEADER, required = true, description = "token")
public Result<User> getCurrentUser() {
Map<String, Object> userSession = ThreadLocalUtil.get();
int id = (int) userSession.get("id");
User user = userService.getUserById(id);
if (user != null) {
return Result.success(user);
}
return Result.error("用户状态异常");
}
UserService.java
User getUserById(int id);
UserServiceImpl.java
@Override
public User getUserById(int id) {
return userMapper.getUserById(id);
}
UserMapper.java
@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(int id);
测试,复制1.13.2中用户登录成功返回的token值作为请求头
1.13.4 查询所有用户信息
UserController.javas
/**
* 查询所有用户信息
* @return List<User>
*/
@GetMapping("/userList")
@Operation(summary = "查询所有用户信息")
@Parameter(name = "Authorization", in = ParameterIn.HEADER, required = true, description = "token")
public Result<List<User>> userList() {
List<User> userList = userService.getUserList();
if (userList == null) {
return Result.error("查询失败");
}
return Result.success(userList);
}
UserService.java
List<User> getUserList();
UserServiceImpl.java
/**
* 查询所有用户信息
* @return List<User>
*/
@Override
public List<User> getUserList() {
return userMapper.selectList(null);
}
1.14 删除和修改用户信息
1.14.1 删除用户信息
UserController.java
/**
* 删除用户
*/
@DeleteMapping("/delete/{id}")
@Operation(summary = "删除用户信息")
@Parameters({
@Parameter(name = "Authorization", in = ParameterIn.HEADER, required = true, description = "token"),
@Parameter(name = "id", required = true, description = "用户id")
})
public Result<String> delete(@PathVariable int id) {
boolean result = userService.deleteById(id); // 调用service层的方法根据loginname删除用户信息,这里假设UserService中有removeById方法
if (result) {
return Result.success("用户删除成功");
} else {
return Result.error("用户删除失败");
}
}
UserService.java
boolean deleteById(int id);
UserServiceImpl.java
@Override
public boolean deleteById(int id) {
return userMapper.deleteById(id) > 0;
}
测试
1.14.2 修改用户信息
UserController.java
@PutMapping("/update")
@Operation(summary = "修改用户信息")
@Parameter(name = "Authorization", in = ParameterIn.HEADER, required = true, description = "token")
public Result<String> updateUser(@RequestBody User user) {
Map<String, Object> userSession = ThreadLocalUtil.get();
int id = (int) userSession.get("id");
user.setId(id);
user.setPassword(Md5Util.getMD5String(user.getPassword()));
if (userService.update(user) > 0) {
return Result.success("修改成功");
}else {
return Result.error("修改失败");
}
}
UserService.java
int update(User user);
UserServiceImpl.java
@Override
public int update(User user) {
return userMapper.updateById(user);
}
测试,先在请求头部中加上Authorization。(1.13.2中用户登录的返回的data)
请求参数如下:(将性别修改为女)
{
"loginName": "user1",
"password": "user1",
"name": "user1",
"sex": "女",
"phone": "15612341234"
}