【Springboot3+vue3】从零到一搭建Springboot3+vue3前后端分离项目之后端环境搭建

【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/

  1. 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>


  1. 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 配置代码生成器

  1. 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 整合

  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>
  1. 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

  1. 测试。配置完成后,访问路径: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

  1. pom.xml 中引入依赖
 <!-- jwt -->
 <dependency>
     <groupId>com.auth0</groupId>
     <artifactId>java-jwt</artifactId>
     <version>4.4.0</version>
 </dependency>

  1. 封装工具类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 登陆验证拦截

  1. 创建包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();
    }
}
  1. 在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 新增用户

逻辑为:

  1. 参数验证。对传入的user进行基本的非空校验,校验用户姓名,登录名以及密码是否为空或者null。为空则返回错误提示信息
  2. 用户名重复校验。通过userService中的findByLoginName去数据库中查询是否存在相同loginName的用户
  3. 密码非空判断,假如密码为空,设置为默认的123456
  4. 密码加密处理。使用1.11 中的MD5对数据库中的密码进行加密
  5. 保存用户信息到数据库,调用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 用户登录

  1. 接口定义:POST请求,路径为/login,
  2. 参数校验:对User对象判断,判断对象或则和loginName,password是否为空,为空则直接返回错误信息
  3. 用户名存在性校验调用userService.findByLoginName方法
  4. 密码正确性校验及登陆成功处理。

    /**
     * 用户登录
     * @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"
}

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/932172.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【书生大模型实战营】Linux 基础知识-L0G1000

前言&#xff1a;书生大模型实战营是上海人工智能实验室开展的大模型系列实践活动&#xff0c;提供免费算力平台&#xff0c;学员通过闯关式任务&#xff0c;可获得免费算力和存储&#xff0c;助力项目实践。本期是第4期&#xff0c;时间从十一月份开始&#xff0c;持续到十二月…

JS进阶DAY3|事件(二)事件流

目录 一、事件流说明 1.1 事件流概念 1.2 事件捕获阶段 1.3 事件冒泡阶段 二、事件传播的两个阶段说明 2.1 事件捕获 2.2 事件冒泡 3.3 示例代码 三、阻止冒泡 四、事件解绑 4.1 removeEventListener方法 4.2 使用 DOM0 级事件属性 4.3 使用一次性事件监听器 一、…

【AI工具】强大的AI编辑器Cursor详细使用教程

目录 一、下载安装与注册 二、内置模型与配置 三、常用快捷键 四、项目开发与问答 五、注意事项与技巧 参考资料 近日&#xff0c;由四名麻省理工学院&#xff08;MIT&#xff09;本科生共同创立的Anysphere公司宣布&#xff0c;其开发的AI代码编辑器Cursor在成立短短两年…

【AWR软件】AWR 软件添加电磁结构

文章目录 前言步骤 前言 微波虚拟 实验 步骤 project -> add em struture -> new em structure 输入名称&#xff0c;create. 添加端口&#xff1a;add edge port

uni-app登录界面样式

非常简洁的登录、注册界面模板&#xff0c;使用uni-app编写&#xff0c;直接复制粘贴即可&#xff0c;无任何引用&#xff0c;全部公开。 废话不多说&#xff0c;代码如下&#xff1a; login.vue文件 <template><view class"screen"><view class"…

普通算法——一维前缀和

一维前缀和 题目链接&#xff1a;https://www.acwing.com/problem/content/797/ 题目描述&#xff1a; 输入一个长度为 n 的整数序列。接下来再输入 m 个询问&#xff0c;每个询问输入一对 l,r。对于每个询问&#xff0c;输出原序列中从第 l 个数到第 r 个数的和。 **什么是…

小程序项目的基本组成结构

分类介绍 项目根目录下的文件及文件夹 pages文件夹 用来存放所有小程序的页面&#xff0c;其中每个页面都由4个基本文件组成&#xff0c;它们分别是&#xff1a; .js文件&#xff1a;页面的脚本文件&#xff0c;用于存放页面的数据、事件处理函数等 .json文件&#xff1a;…

【Go 基础】并发相关

并发相关 CAS CAS算法&#xff08;Compare And Swap&#xff09;&#xff0c;是原⼦操作的⼀种,&#xff0c;CAS 算法是⼀种有名的⽆锁算法。⽆锁编程&#xff0c;即不使⽤锁的情况下实现多线程之间的变量同步。可⽤于在多线程编程中实现不被打断的数据交换操作&#xff0c;从…

【H2O2|全栈】Node.js与MySQL连接

目录 前言 开篇语 准备工作 初始配置 创建连接池 操作数据库 封装方法 结束语 前言 开篇语 本节讲解如何使用Node.js实现与MySQL数据库的连接&#xff0c;并将该过程进行函数封装。 与基础部分的语法相比&#xff0c;ES6的语法进行了一些更加严谨的约束和优化&#…

基于人工智能的新中高考综合解决方案

1. 引言 近年来&#xff0c;随着人工智能技术的飞速发展&#xff0c;教育领域也迎来了深刻的变革。针对新中高考改革的需求&#xff0c;本解决方案集成了科大讯飞在人工智能领域的核心技术&#xff0c;旨在通过智能化手段提升教育教学效率与质量&#xff0c;助力学生全面发展。…

【Linux基础】yum 与 vim 的操作

目录 Linux 应用商店——yum yum和yum源是什么 关于镜像的简单理解 yum 的基本操作 yum的安装 yum install 命令 yum查看软件包 yum list 命令 yum的卸载 yum remove 命令 关于 rzsz 软件 安装 rzsz 软件&#xff1a; rz 命令 sz 命令 yum 源拓展 Linux 编辑器…

Elasticsearch数据迁移(快照)

1. 数据条件 一台原始es服务器&#xff08;192.168.xx.xx&#xff09;&#xff0c;数据迁移后的目标服务器&#xff08;10.2.xx.xx&#xff09;。 2台服务器所处环境&#xff1a; centos7操作系统&#xff0c; elasticsearch-7.3.0。 2. 为原始es服务器数据创建快照 修改elas…

【MySQL】数据类型的注意点和应用

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

首次打开韦东山提供的Ubuntu-18.04镜像后,该做哪些事?

目录 01-测试有无网络02-配置最基本的嵌入式开发环境(安装tftp-nfs等)03-缩短关机强制结束进行时间04-关闭软件的自动更新05-未完待续... 01-测试有无网络 ping www.baidu.com 02-配置最基本的嵌入式开发环境(安装tftp-nfs等) 需要安装 tftp&#xff0c;nfs&#xff0c;vim …

2030. gitLab A仓同步到B仓

文章目录 1 A 仓库备份 到 B 仓库2 B 仓库修改main分支的权限 1 A 仓库备份 到 B 仓库 #!/bin/bash# 定义变量 REPO_DIR"/home/xhome/opt/git_sync/zz_xx_xx" # 替换为你的本地库A的实际路径 REMOTE_ORIGIN"http://192.168.1.66:8181/zzkj_software/zz_xx_xx.…

Python与C++混合编程的优化策略与实践

在现代软件开发中&#xff0c;混合编程已成为一种普遍的开发模式。这种模式能够充分发挥不同编程语言的优势&#xff0c;实现性能与开发效率的最佳平衡。本文将深入探讨Python和C混合编程的策略与实践经验。 混合编程就像建造一座现代化的大厦&#xff0c;C就像大厦的钢筋混凝…

【kettle】mysql数据抽取至kafka/消费kafka数据存入mysql

目录 一、mysql数据抽取至kafka1、表输入2、json output3、kafka producer4、启动转换&#xff0c;查看是否可以消费 二、消费kafka数据存入mysql1、Kafka consumer2、Get records from stream3、字段选择4、JSON input5、表输出 一、mysql数据抽取至kafka 1、表输入 点击新建…

INS风格户外风光旅拍人像自拍摄影Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 户外风光旅拍人像自拍摄影结合 Lightroom 调色&#xff0c;可以打造出令人惊艳的视觉效果。这种风格将自然风光与人像完美融合&#xff0c;强调色彩的和谐与氛围感的营造。 预设信息 调色风格&#xff1a;INS风格预设适合类型&#xff1a;人像&#xff0c;户外&…

【burp】burpsuite基础(八)

Burp Suite基础&#xff08;八&#xff09; 声明&#xff1a;该笔记为up主 泷羽的课程笔记&#xff0c;本节链接指路。 警告&#xff1a;本教程仅作学习用途&#xff0c;若有用于非法行为的&#xff0c;概不负责。 ip伪装 安装组件jython 下载好后&#xff0c;在burp中打开扩展…

《船舶物资与市场》是什么级别的期刊?是正规期刊吗?能评职称吗?

问题解答 问&#xff1a;《船舶物资与市场》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊。 问&#xff1a;《船舶物资与市场》级别&#xff1f; 答&#xff1a;国家级。主管单位&#xff1a;中国船舶集团有限公司 主办单…