基于Java的仓库管理系统设计与实现(二)

1. 创建SpringBoot项目

IDEA创建SpringBoot项目,springBoot版本选择3.3.5,类型选择Maven项目,组和工件构成包名,这里分别写cn.xy21lin和wms_lin,java版本选择17,打包默认jar方式。

1.1 导入POM依赖

pom文件如下,注意依赖版本要对应,这里注释了SpringSecurity相关依赖,因为导入了SpringDoc,security可能会影响访问SpringDoc,后面做了相关配置再开启

<?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.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.xy21lin</groupId>
    <artifactId>wms_lin</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>wms_lin</name>
    <description>wms_lin</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
        <velocity.version>2.3</velocity.version>
        <springdoc.version>2.2.0</springdoc.version>
        <mybatis-plus.version>3.5.5</mybatis-plus.version>
        <mysql-version>8.0.30</mysql-version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>${velocity.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!--         <dependency> -->
        <!--             <groupId>org.springframework.security</groupId> -->
        <!--             <artifactId>spring-security-test</artifactId> -->
        <!--             <scope>test</scope> -->
        <!--         </dependency> -->
        <!--         <dependency> -->
        <!--             <groupId>org.springframework.boot</groupId> -->
        <!--             <artifactId>spring-boot-starter-security</artifactId> -->
        <!--         </dependency> -->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

1.2 生成代码-使用代码生成器

依赖导入了mybatis-plus相关依赖,所以直接使用mybatis-plus提供的代码生成器进行生成代码,这里在测试目录,也就是test/java/cn.xy21lin.wms_lin 下创建一个类进行代码生成。

  1. 创建代码生成器类:GenretorCode.java
package cn.xy21lin.wms_lin;


import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.sql.DataSource;
import java.util.Collections;

@SpringBootTest
public class GenreatorCode {
    // 作者
    private static final String AUTHOR = "十一月的早晨";
    /**
    *  这里因为直接在yml文件里配置了数据源,所以直接依赖注入DataSource使用,也可以自己在这里再次进行配置
        // JDBC配置
        private static final String JDBC_URL = "jdbc:mysql://localhost:3306/wms_lin?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
        // 账号
        private static final String JDBC_USERNAME = "root";
        // 密码
        private static final String JDBC_PASSWORD = "";
    */
    // 导出目录
    private static final String OUT_FIR = ".\\src\\main\\java";
    // 包名
    private static final String PACKAGE_NAME = "cn.xy21lin.wms_lin";

    // 数据库表
    private static final String[] TABLES = {
            "biz_category", "biz_client", "biz_inventory", "biz_product", "biz_stock_in",
            "biz_stock_in_item", "biz_stock_out", "biz_stock_out_item",
            "biz_supplier", "biz_warehouse", "sys_menu", "sys_role", "sys_user", "sys_role_menu"
    };

    // 过滤前缀
    private static final String[] PREFIX = {"biz_","sys_"};

    @Resource
    DataSource dataSource;

    @Test
    void generator() {
        // 创建代码生成器
        FastAutoGenerator.create(new DataSourceConfig.Builder(dataSource))
                .globalConfig(builder -> builder
                        // 设置作者
                        .author(AUTHOR)
                        // 启用Swagger注解,这里使用的SpringDoc,所以是这个命令,命令选不对,生成的注解不对应依赖,会报错
                        .enableSpringdoc()
                        // 设置输出目录
                        .outputDir(OUT_FIR)
                )
                .packageConfig(builder -> builder
                        // 设置父包名
                        .parent(PACKAGE_NAME)
                        // 设置Mapper XML文件输出路径
                        .pathInfo(Collections.singletonMap(OutputFile.xml, ".\\src\\main\\resources\\mapper"))
                )
                .strategyConfig(builder -> builder
                                // 添加需要生成的表
                                .addInclude(TABLES)
                                // 添加表前缀过滤
                                .addTablePrefix(PREFIX)
                                // 实体类配置
                                .entityBuilder()
//                        // 启用Lombok
                                .enableLombok()
                                // 启用链式模型
                                .enableChainModel()
                                // 控制器配置
                                .controllerBuilder()
                                // 启用REST风格
                                .enableRestStyle()
                )
                // 执行生成
                .execute();
    }
}
  1. 直接运行
    运行结果
    生成结果目录.jpg

编译通过,运行成功,这里我已经运行过了,所以提示文件已存在,还可以注意到生成的表biz_inventorysys_role_menu存在多主键情况,所以需要对其进行修改。直接将表中@TableId注解修改为@TableField即可。

2. 配置yml文件

注意mysql依赖使用的是8.0版本,所以驱动名称为:com.mysql.cj.jdbc.Driver

spring:
  application:
    name: wms
  datasource:
    url: jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: 帐号
    password: 密码
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
  configuration:
  # 开启sql日志记录
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 声明mapper.xml文件位置
  mapper-locations: classpath*:mapper/**/*.xml
  # 为实体类起别名,不需要去写全限定类名,直接使用category 这种别名
  type-aliases-package: cn.xy21lin.wms_lin.entity
  global-config:
    db-config:
  # 启用逻辑删除列
      logic-delete-field: deleted

3. 配置Swagger3-openAPI

新建一个config包,创建OpenAPIConfig类,用来编写Swagger3配置

@Configuration
public class OpenAPIConfig  {

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("接口文档标题")
                        .description("SpringBoot3 集成 Swagger3接口文档")
                        .version("v1"))
                .externalDocs(new ExternalDocumentation()
                        .description("项目API文档")
                        .url("/"));
    }
}

4. 配置Mybatis-plus 分页插件

config包下,创建MybatisConfig类,编写分页插件,直接复制即可

package cn.xy21lin.wms_lin.config;


import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisConfig {
    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
        // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
        return interceptor;
    }
}

5. 统一返回数据

新建util包,创建ResultCode类,封装请求响应码,再创建Result类,统一返回数据格式。

  1. ResultCode
package cn.xy21lin.wms_lin.util;

public class ResultCode {
    public static final int SUCCESS = 200; // 请求成功
    public static final int ERROR = 400; // 请求错误,服务器无法理解
    public static final int UNAUTHORIZED = 401; // 未授权,需要身份验证
    public static final int FORBIDDEN = 403; // 禁止访问,权限不足
    public static final int NOT_FOUND = 404; // 请求的资源不存在
    public static final int METHOD_NOT_ALLOWED = 405; // 请求方法不被允许
    public static final int REQUEST_TIMEOUT = 408; // 请求超时
    public static final int INTERNAL_SERVER_ERROR = 500; // 服务器内部错误
}

  1. Result
package cn.xy21lin.wms_lin.util;

import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class Result<T> {
    private Integer code;
    private String message;
    private T data;
    private boolean success;

    private Result(){}

    // 只返回成功,适用于只关注操作结果,不关心返回数据
    public static<T> Result<T> success(){
        return new Result<T>().setCode(ResultCode.SUCCESS).setMessage("成功").setSuccess(true);
    }
    // 返回操作数据,关注操作结果和数据
    public static<T> Result<T> success(T data){
        return new Result<T>().setCode(ResultCode.SUCCESS).setMessage("成功").setSuccess(true).setData(data);
    }

    // 返回失败,失败消息可以自己调用后再进行填写
    public static<T> Result<T> fail(){
        return new Result<T>().setCode(ResultCode.ERROR).setMessage("失败").setSuccess(false);
    }
}

6. 封装分页类

之前分页信息一直是在url显示,例如:/list?page=1&size=10,使用@PostMapping("/{start}/{size}")配合 @PathVariable int start, @PathVariable int size 获取分页参数,这里不想采用这种方式,选择使用一个分页类,使用泛型,便于各实体类查询返回分页数据。

package cn.xy21lin.wms_lin.util;

import jakarta.validation.constraints.Min;
import lombok.Data;

@Data
public class PageUtil<T> {
    // 确保合法输入
    @Min(value = 1, message = "pageNum必须大于等于1")
    private Integer pageNum;
    // 确保合法输入
    @Min(value = 1, message = "pageSize必须大于等于1")
    private Integer pageSize;
    // 使用泛型,增强代码复用性
    private T data;
}

7. 测试分页

这里使用Category类,编写获取商品分类列表方法

  1. mapper
    //获取商品分类列表
    Page<Category> getCategoryList(Page<Category> page, Category category);
  1. service
    //service接口
    //获取商品分类列表
    Page<Category> getCategoryList(Page<Category> page, Category category);

    //service实现类
    @Resource
    CategoryMapper categoryMapper;

    //返回商品分类列表
    @Override
    public Page<Category> getCategoryList(Page<Category> page, Category category) {
        return categoryMapper.getCategoryList(page, category);
    }
  1. controller
/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author 十一月的早晨
 * @since 2025-02-26
 */
@RestController
@RequestMapping("/category")
// 用于swagger文档显示
@Tag(description = "CategoryController", name = "类别管理")
public class CategoryController {

    @Resource
    ICategoryService categoryService;

    // 使用post请求后,控制器接收并处理请求
    @PostMapping("/list")
    public Result getCategoryList(@Validated @RequestBody PageUtil<Category> category){
        Page<Category> page = categoryService.getCategoryList(new Page<>(category.getPageNum(), category.getPageSize()), category.getData());
        return Result.success().setMessage("获取成功").setData(page);
    }
}
  1. 模拟请求发送

    url : http://localhost:8080/category/list
    post :
    {
    "pageNum": 1,
    "pageSize": 2,
    "data": {}
    }
    ua : content-type:application/json

  2. 获取响应结果
    {"code":200,"message":"获取成功","data":{"records":[{"id":1,"name":"食品","parentId":0,"level":1,"isDeleted":null},{"id":2,"name":"饮料","parentId":1,"level":2,"isDeleted":null}],"total":4,"size":2,"current":1,"pages":2},"success":true}
    

如果不想使用xml编写sql语句,简单的条件查询可以使用mybatis提供的QueryWrapper进行编写,例如:

    public Page<Client> getClientList(Page<Client> page, Client client) {
        QueryWrapper<Client> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id","name","contact","phone","address","coop_start_date","coop_end_date")
                .like(!StringUtils.isNullOrEmpty(client.getName()), "name", client.getName())
                .like(!StringUtils.isNullOrEmpty(client.getContact()), "contact", client.getContact())
                .like(!StringUtils.isNullOrEmpty(client.getPhone()), "phone", client.getPhone())
                .like(!StringUtils.isNullOrEmpty(client.getAddress()), "address", client.getAddress());
        return clientMapper.selectPage(page,queryWrapper);
    }

注意:启动前,需要在启动类添加@MapperScan("cn.xy21lin.wms_lin")注解

这里再记录一个小问题,配置全部完成后,启动项目报错Invalid bound statement (not found),很经典的错误,就是mapper映射问题,这里经过检查,发现是对应mapper.xml文件命名空间错误,因为之前生成包名为wms,重新修改后为wms_lin,但是由于文件已存在所以没有覆盖,导致xml文件命名空间中的包名和现存包名不一致,修改后即可成功运行。