上一篇中,我们介绍了项目的基本架构和相关知识。这一篇,我们将在上一篇已搭建好的项目基础架构上进行整合 MyBatis-Plus 、Knife4j。
1. 整合 MyBatis-Plus
1.1. 关于 MyBatis-Plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。关于 MyBatis-Plus 的具体介绍,请参照官方文档( 链接地址)。Spring Cloud Alibaba
1.2. 版本选择
由于我们项目使用的是 Spring Boot 3.0.2,需要 MyBatis-Plus3.5.3 及以上版本才支持,我们选择
MyBatis-Plus 的版本为 3.5.3.1。
1.3. 项目整合
1.3.1. 创建组件
我们在使用 MyBatis-Plus 的时候,也经常会对它的一些特性进行封装。因此,我们将在 mall-component 工程下创建一个 MyBatis-Plus 的工程,作为组件工程,供各个模块使用。
在 mall-component 工程下创建名称为 mall-component-mybatis 的子工程。
mall-component-mybatis 工程的 pom.xml 文件内容如下。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example.component</groupId>
<artifactId>mall-component</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>mall-component-mybatis</artifactId>
<name>mall-component-mybatis</name>
<description>mybatis组件</description>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- mybatis-plus 3.5.3及以上版本 才支持 spring boot 3-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
</dependencies>
</project>
在 mall-component-mybatis 工程中创建 org.example.component.mybatis.config 包目录,在该包目录下创建 MyBatis-Plus 配置类,取名为:MybatisPlusConfig。
MybatisPlusConfig 内容如下。
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//添加乐观锁拦截器
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
mall-component-mybatis 组件工程结构如下。
1.3.2. 使用组件
在上面,我们已经创建好了 mall-component-mybatis 组件工程,下面就要被账户模块、商品模块、订单模块依赖使用了。
由于我们的 model 是放在 domain 层工程的,model 中实体类的字段会涉及到 MyBatis-Plus 的东西,加上 infra 层工程需要依赖 domain 层工程,我们就将 mall-component-mybatis 组件依赖添加在domain 层工程中。
分别在账户模块、商品模块、订单模块的 domain 层工程的 pom.xml 添加如下依赖。
<!--mybatis-->
<dependency>
<groupId>org.example.component</groupId>
<artifactId>mall-component-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
账户模块 domain 层工程的 pom.xml 添加依赖后,内容如下。
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example.account</groupId>
<artifactId>mall-account-center</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>mall-account-center-domain</artifactId>
<name>mall-account-center-domain</name>
<description>领域层</description>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--mall-account-center-dto-->
<dependency>
<groupId>org.example.account</groupId>
<artifactId>mall-account-center-dto</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.example.component</groupId>
<artifactId>mall-component-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
商品模块 domain 层工程的 pom.xml 添加依赖后,内容如下。
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example.product</groupId>
<artifactId>mall-product-center</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>mall-product-center-domain</artifactId>
<name>mall-product-center-domain</name>
<description>领域层</description>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--mall-product-center-dto-->
<dependency>
<groupId>org.example.product</groupId>
<artifactId>mall-product-center-dto</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.example.component</groupId>
<artifactId>mall-component-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
订单模块 domain 层工程的 pom.xml 添加依赖后,内容如下。
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example.order</groupId>
<artifactId>mall-order-center</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>mall-order-center-domain</artifactId>
<name>mall-order-center-domain</name>
<description>领域层</description>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--mall-order-center-dto-->
<dependency>
<groupId>org.example.order</groupId>
<artifactId>mall-order-center-dto</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.example.component</groupId>
<artifactId>mall-component-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
1.3.3. 实体类创建
创建数据库表 mall_account、mall_product、mall_order 对应的实体类,类名分别为:AccountEntity、ProductEntity、OrderEntity。MyBatis-Plus也有代码生成器,可以利用代码生成器生成。
AccountEntity 内容如下。
@Data
@Accessors(chain = true)
@TableName("mall_account")
public class AccountEntity extends Model<AccountEntity> {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**
* 用户id
*/
@TableField("user_id")
private Long userId;
/**
* 账户code
*/
@TableField("account_code")
private String accountCode;
/**
* 账户名称
*/
@TableField("account_name")
private String accountName;
/**
* 金额
*/
@TableField("amount")
private BigDecimal amount;
/**
* 是否被删除
*/
@TableField("is_deleted")
public Integer isDeleted;
/**
* 创建人
*/
@TableField("created_by")
public Long createdBy;
/**
* 创建时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@TableField("created_time")
public Date createdTime;
/**
* 修改人
*/
@TableField("updated_by")
public Long updatedBy;
/**
* 修改时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@TableField("updated_time")
public Date updatedTime;
/**
* 版本号
*/
@Version
@TableField("reversion")
private Integer reversion;
@Override
public Serializable pkVal() {
return this.id;
}
}
ProductEntity 内容如下。
@Data
@Accessors(chain = true)
@TableName("mall_product")
public class ProductEntity extends Model<ProductEntity> {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**
* 商品code
*/
@TableField("product_code")
private String productCode;
/**
* 商品名称
*/
@TableField("product_name")
private String productName;
/**
* 商品数量
*/
@TableField("count")
private Integer count;
/**
* 商品价格
*/
@TableField("price")
private BigDecimal price;
/**
* 是否被删除
*/
@TableField("is_deleted")
public Integer isDeleted;
/**
* 创建人
*/
@TableField("created_by")
public Long createdBy;
/**
* 创建时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@TableField("created_time")
public Date createdTime;
/**
* 修改人
*/
@TableField("updated_by")
public Long updatedBy;
/**
* 修改时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@TableField("updated_time")
public Date updatedTime;
/**
* 版本号
*/
@Version
@TableField("reversion")
private Integer reversion;
@Override
public Serializable pkVal() {
return this.id;
}
}
OrderEntity 内容如下。
@Data
@Accessors(chain = true)
@TableName("mall_order")
public class OrderEntity extends Model<OrderEntity> {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**
* 订单编号
*/
@TableField("order_no")
private String orderNo;
/**
* 账户code
*/
@TableField("account_code")
private String accountCode;
/**
* 商品编号
*/
@TableField("product_code")
private String productCode;
/**
* 商品数量
*/
@TableField("count")
private Integer count;
/**
* 金额
*/
@TableField("amount")
private BigDecimal amount;
/**
* 是否被删除
*/
@TableField("is_deleted")
public Integer isDeleted;
/**
* 创建人
*/
@TableField("created_by")
public Long createdBy;
/**
* 创建时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@TableField("created_time")
public Date createdTime;
/**
* 修改人
*/
@TableField("updated_by")
public Long updatedBy;
/**
* 修改时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@TableField("updated_time")
public Date updatedTime;
/**
* 版本号
*/
@Version
@TableField("reversion")
private Integer reversion;
@Override
public Serializable pkVal() {
return this.id;
}
}
1.3.4. Mapper 接口创建
创建实体类 AccountEntity、ProductEntity、OrderEntity 对应的 Mapper 接口,名称分别为:AccountMapper、ProductMapper、OrderMapper。
AccountMapper 内容如下。
@Mapper
public interface AccountMapper extends BaseMapper<AccountEntity> {
}
ProductMapper 内容如下。
@Mapper
public interface ProductMapper extends BaseMapper<ProductEntity> {
}
OrderMapper 内容如下。
@Mapper
public interface OrderMapper extends BaseMapper<OrderEntity> {
}
1.3.5. 领域接口创建
创建实体类 AccountEntity、ProductEntity、OrderEntity 对应的领域接口,名称分别为:AccountService、ProductService、OrderService。
AccountService 内容如下。
public interface AccountService extends IService<AccountEntity> {
/**
* 新增账户
*/
void add(AccountReqDTO accountReqDTO,Long id) throws BaseException;
/**
* 编辑账户
*/
void edit(AccountReqDTO accountReqDTO) throws BaseException;
/**
* 扣减金额
*/
void reduceAmount(Long id, BigDecimal amount) throws BizException;
/**
* 分页查询账户列表
*/
PageDTO<AccountRspDTO> pageList(PageQuery<AccountQuery> pageQuery);
/**
*查询满足条件的记录
*/
List<AccountRspDTO> queryList(AccountQuery accountQuery);
}
ProductService 内容如下。
public interface ProductService extends IService<ProductEntity> {
/**
* 新增商品
*/
void add(ProductReqDTO productReqDTO,Long id) throws BaseException;
/**
* 编辑商品
*/
void edit(ProductReqDTO productReqDTO) throws BaseException;
/**
* 扣减数量
*/
void reduceCount(Long id, Integer count) throws BizException;
/**
*分页查询账户列表
*/
PageDTO<ProductRspDTO> pageList(PageQuery<ProductQuery> pageQuery);
/**
*查查满足条件的记录
*/
List<ProductRspDTO> queryList(ProductQuery productQuery);
}
OrderService 内容如下。
public interface OrderService extends IService<OrderEntity> {
/**
* 新增订单
*/
void add(OrderReqDTO orderReqDTO, Long id) throws BaseException;
/**
* 编辑订单
*/
void edit(OrderReqDTO orderReqDTO) throws BaseException;
/**
*分页查询账户列表
*/
PageDTO<OrderRspDTO> pageList(PageQuery<OrderQuery> pageQuery);
/**
*查询满足条件的记录
*/
List<OrderRspDTO> queryList(OrderQuery productQuery);
}
上面的 PageQuery,从 COLA 官方项目中拷贝过来稍加修改作为公共组件使用。AccountQuery、ProductQuery、OrderQuery 分别创建在各模块的 dto 组件工程下作为每个模块的列表查询入参。
1.3.6. 领域接口实现类创建
创建 AccountService、ProductService、OrderService 对应的实现类,名称分别为:AccountServiceImpl、ProductServiceImpl、OrderServiceImpl。
AccountServiceImpl 内容如下。
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, AccountEntity> implements AccountService {
/**
*构建查询信息
*/
private LambdaQueryWrapper<AccountEntity> buildAccountLambdaQueryWrapper(AccountQuery accountQuery){
LambdaQueryWrapper<AccountEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
if(Objects.nonNull(accountQuery)){
lambdaQueryWrapper.eq(Objects.nonNull(accountQuery.getId()),AccountEntity::getId,accountQuery.getId());
lambdaQueryWrapper.like(Objects.nonNull(accountQuery.getAccountCode()),AccountEntity::getAccountCode,accountQuery.getAccountCode());
lambdaQueryWrapper.like(Objects.nonNull(accountQuery.getAccountName()),AccountEntity::getAccountName,accountQuery.getAccountName());
}
return lambdaQueryWrapper;
}
@Override
public void add(AccountReqDTO accountReqDTO, Long id) throws BaseException {
AccountEntity accountEntity = new AccountEntity();
BeanUtils.copyProperties(accountReqDTO,accountEntity);
accountEntity.setId(id);
accountEntity.setCreatedBy(1L);
accountEntity.setCreatedTime(new Date());
try {
this.save(accountEntity);
} catch (Exception e) {
e.printStackTrace();
throw ExceptionFactory.sysException("新增账户,保存数据异常");
}
}
@Override
public void edit(AccountReqDTO accountReqDTO) throws BaseException {
AccountEntity accountEntity = this.getById(accountReqDTO.getId());
if(Objects.isNull(accountEntity)){
throw ExceptionFactory.bizException("账户信息不存在!");
}
BeanUtils.copyProperties(accountReqDTO,accountEntity);
accountEntity.setUpdatedBy(1L);
accountEntity.setUpdatedTime(new Date());
try {
this.updateById(accountEntity);
} catch (Exception e) {
e.printStackTrace();
throw ExceptionFactory.sysException("编辑账户,保存数据异常");
}
}
@Override
public void reduceAmount(Long id, BigDecimal amount) throws BizException {
AccountEntity accountEntity = this.getById(id);
if(Objects.isNull(accountEntity)){
throw ExceptionFactory.bizException("账户信息不存在!");
}
accountEntity.setAmount(accountEntity.getAmount().subtract(amount));
accountEntity.setUpdatedBy(1L);
accountEntity.setUpdatedTime(new Date());
this.updateById(accountEntity);
}
@Override
public PageDTO<AccountRspDTO> pageList(PageQuery<AccountQuery> pageQuery) {
PageDTO<AccountEntity> page = new PageDTO<>();
AccountQuery accountQuery = pageQuery.getQuery();
LambdaQueryWrapper<AccountEntity> lambdaQueryWrapper = this.buildAccountLambdaQueryWrapper(accountQuery);
page.setSize(pageQuery.getSize());
page.setCurrent(pageQuery.getCurrent());
page = this.page(page,lambdaQueryWrapper);
PageDTO<AccountRspDTO> pageAccountRspDTO = (PageDTO<AccountRspDTO>)page.convert(accountEntity->{
AccountRspDTO accountRspDTO = new AccountRspDTO();
BeanUtils.copyProperties(accountEntity,accountRspDTO);
return accountRspDTO;
});
return pageAccountRspDTO;
}
@Override
public List<AccountRspDTO> queryList(AccountQuery accountQuery) {
LambdaQueryWrapper<AccountEntity> lambdaQueryWrapper = this.buildAccountLambdaQueryWrapper(accountQuery);
List<AccountEntity> accountEntityList = this.list(lambdaQueryWrapper);
if(!CollectionUtils.isEmpty(accountEntityList)){
List<AccountRspDTO> accountRspDTOList = accountEntityList.stream().map(accountEntity->{
AccountRspDTO accountRspDTO = new AccountRspDTO();
BeanUtils.copyProperties(accountEntity,accountRspDTO);
return accountRspDTO;
}).collect(Collectors.toList());
return accountRspDTOList;
}
return null;
}
}
ProductServiceImpl 内容如下。
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductEntity> implements ProductService {
/**
*构建查询信息
*/
private LambdaQueryWrapper<ProductEntity> buildProductLambdaQueryWrapper(ProductQuery productQuery){
LambdaQueryWrapper<ProductEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Objects.nonNull(productQuery.getId()), ProductEntity::getId,productQuery.getId());
lambdaQueryWrapper.like(Objects.nonNull(productQuery.getProductCode()), ProductEntity::getProductCode,productQuery.getProductCode());
lambdaQueryWrapper.like(Objects.nonNull(productQuery.getProductName()), ProductEntity::getProductName,productQuery.getProductName());
return lambdaQueryWrapper;
}
@Override
public void add(ProductReqDTO productReqDTO, Long id) throws BaseException {
ProductEntity productEntity = new ProductEntity();
BeanUtils.copyProperties(productReqDTO,productEntity);
productEntity.setId(id);
productEntity.setCreatedBy(1L);
productEntity.setCreatedTime(new Date());
try {
this.save(productEntity);
} catch (Exception e) {
e.printStackTrace();
throw ExceptionFactory.sysException("新增商品,保存数据异常");
}
}
@Override
public void edit(ProductReqDTO productReqDTO) throws BaseException {
ProductEntity productEntity = this.getById(productReqDTO.getId());
if(Objects.isNull(productEntity)){
throw ExceptionFactory.bizException("商品信息不存在!");
}
BeanUtils.copyProperties(productReqDTO,productEntity);
productEntity.setUpdatedBy(1L);
productEntity.setUpdatedTime(new Date());
try {
this.updateById(productEntity);
} catch (Exception e) {
e.printStackTrace();
throw ExceptionFactory.sysException("编辑商品,保存数据异常");
}
}
@Override
public void reduceCount(Long id, Integer count) throws BizException {
ProductEntity productEntity = this.getById(id);
if(Objects.isNull(productEntity)){
throw ExceptionFactory.bizException("商品信息不存在!");
}
productEntity.setCount(productEntity.getCount()-count);
productEntity.setUpdatedBy(1L);
productEntity.setUpdatedTime(new Date());
this.updateById(productEntity);
}
@Override
public PageDTO<ProductRspDTO> pageList(PageQuery<ProductQuery> pageQuery) {
PageDTO<ProductEntity> page = new PageDTO<>();
ProductQuery productQuery = pageQuery.getQuery();
LambdaQueryWrapper<ProductEntity> lambdaQueryWrapper = this.buildProductLambdaQueryWrapper(productQuery);
page.setSize(pageQuery.getSize());
page.setCurrent(pageQuery.getCurrent());
page = this.page(page,lambdaQueryWrapper);
PageDTO<ProductRspDTO> pageProductRspDTO = (PageDTO<ProductRspDTO>)page.convert(productEntity->{
ProductRspDTO productRspDTO = new ProductRspDTO();
BeanUtils.copyProperties(productEntity,productRspDTO);
return productRspDTO;
});
return pageProductRspDTO;
}
@Override
public List<ProductRspDTO> queryList(ProductQuery ProductQuery) {
LambdaQueryWrapper<ProductEntity> lambdaQueryWrapper = this.buildProductLambdaQueryWrapper(ProductQuery);
List<ProductEntity> ProductEntityList = this.list(lambdaQueryWrapper);
if(!CollectionUtils.isEmpty(ProductEntityList)){
List<ProductRspDTO> ProductRspDTOList = ProductEntityList.stream().map(productEntity->{
ProductRspDTO ProductRspDTO = new ProductRspDTO();
BeanUtils.copyProperties(productEntity,ProductRspDTO);
return ProductRspDTO;
}).collect(Collectors.toList());
return ProductRspDTOList;
}
return null;
}
}
OrderServiceImpl 内容如下。
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderEntity> implements OrderService {
/**
*构建查询信息
*/
private LambdaQueryWrapper<OrderEntity> buildOrderLambdaQueryWrapper(OrderQuery orderQuery){
LambdaQueryWrapper<OrderEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Objects.nonNull(orderQuery.getId()), OrderEntity::getId,orderQuery.getId());
lambdaQueryWrapper.like(Objects.nonNull(orderQuery.getOrderNo()), OrderEntity::getOrderNo,orderQuery.getOrderNo());
lambdaQueryWrapper.like(Objects.nonNull(orderQuery.getAccountCode()), OrderEntity::getAccountCode,orderQuery.getAccountCode());
lambdaQueryWrapper.like(Objects.nonNull(orderQuery.getProductCode()), OrderEntity::getProductCode,orderQuery.getProductCode());
return lambdaQueryWrapper;
}
@Override
public void add(OrderReqDTO orderReqDTO, Long id) throws BaseException {
OrderEntity orderEntity = new OrderEntity();
BeanUtils.copyProperties(orderReqDTO,orderEntity);
String orderNo = "DD"+System.currentTimeMillis();
orderEntity.setId(id);
orderEntity.setOrderNo(orderNo);
orderEntity.setCreatedBy(1L);
orderEntity.setCreatedTime(new Date());
try {
this.save(orderEntity);
} catch (Exception e) {
e.printStackTrace();
throw ExceptionFactory.sysException("新增订单,保存数据异常");
}
}
@Override
public void edit(OrderReqDTO orderReqDTO) throws BaseException {
OrderEntity orderEntity = this.getById(orderReqDTO.getId());
if(Objects.isNull(orderEntity)){
throw ExceptionFactory.bizException("订单信息不存在!");
}
BeanUtils.copyProperties(orderReqDTO,orderEntity);
orderEntity.setUpdatedBy(1L);
orderEntity.setUpdatedTime(new Date());
try {
this.updateById(orderEntity);
} catch (Exception e) {
e.printStackTrace();
throw ExceptionFactory.sysException("编辑订单,保存数据异常");
}
}
@Override
public PageDTO<OrderRspDTO> pageList(PageQuery<OrderQuery> pageQuery) {
PageDTO<OrderEntity> page = new PageDTO<>();
OrderQuery orderQuery = pageQuery.getQuery();
LambdaQueryWrapper<OrderEntity> lambdaQueryWrapper = this.buildOrderLambdaQueryWrapper(orderQuery);
page.setSize(pageQuery.getSize());
page.setCurrent(pageQuery.getCurrent());
page = this.page(page,lambdaQueryWrapper);
PageDTO<OrderRspDTO> pageOrderRspDTO = (PageDTO<OrderRspDTO>)page.convert(orderEntity->{
OrderRspDTO orderRspDTO = new OrderRspDTO();
BeanUtils.copyProperties(orderEntity,orderRspDTO);
return orderRspDTO;
});
return pageOrderRspDTO;
}
@Override
public List<OrderRspDTO> queryList(OrderQuery OrderQuery) {
LambdaQueryWrapper<OrderEntity> lambdaQueryWrapper = this.buildOrderLambdaQueryWrapper(OrderQuery);
List<OrderEntity> OrderEntityList = this.list(lambdaQueryWrapper);
if(!CollectionUtils.isEmpty(OrderEntityList)){
List<OrderRspDTO> OrderRspDTOList = OrderEntityList.stream().map(orderEntity->{
OrderRspDTO OrderRspDTO = new OrderRspDTO();
BeanUtils.copyProperties(orderEntity,OrderRspDTO);
return OrderRspDTO;
}).collect(Collectors.toList());
return OrderRspDTOList;
}
return null;
}
}
2. 整合 Knife4j
2.1. 关于 Knife4j
Knife4j 是为 Java MVC 框架集成 Swagger 生成 Api 文档的增强解决方案,前身是 swagger-bootstrap-ui。关于 Knife4j 的具体介绍,请参照官方文档(链接地址)。
2.2. 版本选择
由于我们项目使用的是 Spring Boot 3.0.2,Knife4j 需要4.0及以上版本才适配。目前最新版本为4.3.0,那我们选择 Knife4j 的版本为4.3.0。
2.3. 项目整合
2.3.1. 添加 Knife4j
2.3.1.1. 添加依赖
Knife4j 的注解一般都是添加在 DTO 或 Controller 上的,在 Controller 的方法中,DTO 又往往被作为入参或出参。因此本系列将 knife4j 的依赖添加在 DTO 文件所在的地方。本系列将 COLA 官方项目的部分 DTO 拷贝到 mall-component-common 工程中作为公共组件,为使 mall-component-common 工程中的 DTO 也能使用 knife4j 的注解,于是将 knife4j 的依赖添加到 mall-component-common 工程中。
mall-component-common 工程中的 pom.xml 文件内容如下。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example.component</groupId>
<artifactId>mall-component</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>mall-component-common</artifactId>
<name>mall-component-common</name>
<description>组件工具</description>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<!-- https://www.pangugle.com/lib/maven/view/cn.hutool/hutool-all.html -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.19</version>
</dependency>
<!--knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
</dependencies>
</project>
2.3.1.2. 添加注解
以账户模块为例,给 AccountReqDTO 添加注解后如下。
@Schema(name = "AccountReqDTO",description = "account入参DTO")
@Data
public class AccountReqDTO extends DTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键id,新增时,不必传,编辑时,必传。")
private Long id;
@Schema(description = "用户id")
private Long userId;
@Schema(description = "账户code")
private String accountCode;
@Schema(description = "账户名称")
private String accountName;
@Schema(description = "金额")
private BigDecimal amount;
}
2.3.1.3. 添加分组
由于我们的适配层(Adapter Layer)接口根据 web、wireless、wap 等类型的适配做了归类,我们也的 knife4j 文档也可以按此进行分组。以账户模块为例,在 mall-account-center-dto 工程中创建org.example.account.config 包目录,在该目录下创建 knife4j 配置类,名称为:Knife4jConfig。
Knife4jConfig 内容如下。
@Configuration
@EnableKnife4j
public class Knife4jConfig {
private static final String GLOBAL_HEADER = "header";
private static final String GLOBAL_TOKEN = "token";
@Bean
public GroupedOpenApi webApi() {
return GroupedOpenApi.builder()
.group("adapter-"+BaseConstant.AdapterType.WEB)
.pathsToMatch("/"+ BaseConstant.AdapterType.WEB+"/**")
.build();
}
@Bean
public GroupedOpenApi wapApi() {
return GroupedOpenApi.builder()
.group("adapter-"+BaseConstant.AdapterType.WAP)
.pathsToMatch("/"+ BaseConstant.AdapterType.WAP+"/**")
.build();
}
@Bean
public GroupedOpenApi wirelessApi() {
return GroupedOpenApi.builder()
.group("adapter-"+BaseConstant.AdapterType.WIRELESS)
.pathsToMatch("/"+ BaseConstant.AdapterType.WIRELESS+"/**")
.build();
}
@Bean
public OpenAPI openAPI() {
return new OpenAPI().info(info()).components(new Components()
.addSecuritySchemes(GLOBAL_TOKEN,
new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")));
}
private Info info() {
return new Info()
.title("账户服务-API文档")
.version("v1.0")
.description("本文档描述了后端微服务接口定义")
.termsOfService("https://www.xxx.com")
.license(new License().name("Apache 2.0").url("https://springdoc.org"));
}
}
对应 Knife4j 配置,也支持在 yml 文件中进行配置,例如官方示例。
2.3.2. 添加前端控制器
分别在账户模块、商品模块、订单模块的适配层 web 目录下,创建前端控制器,添加对应的 CRUD 接口,前端控制器名称分别为:AccountController、ProductController、OrderController。
AccountController 内容如下。
@Slf4j
@Tag(name = "account-wep端api")
@RestController
@RequestMapping(BaseConstant.AdapterType.WEB+"/v1/account")
public class AccountController {
@Resource
private AccountExecutor accountExecutor;
@Operation(summary = "新增账户")
@PostMapping("/add")
public ResponseResult<String> add(@RequestBody AccountReqDTO accountReqDTO){
log.info("add account, accountReqDTO:{}",accountReqDTO);
this.accountExecutor.addAccount(accountReqDTO);
return ResponseResult.ok("add account succeed");
}
@Operation(summary = "编辑账户")
@PostMapping("/edit")
public ResponseResult<String> edit(@RequestBody AccountReqDTO accountReqDTO){
log.info("edit account, accountReqDTO:{}",accountReqDTO);
this.accountExecutor.editAccount(accountReqDTO);
return ResponseResult.ok("update account succeed");
}
@Operation(summary = "根据id删除账户")
@PostMapping("/removeById")
@Parameter(name = "id",description = "主键id",required = true)
public ResponseResult<String> removeById(@RequestParam Long id){
log.info("delete account,id is {}",id);
this.accountExecutor.removeAccountById(id);
return ResponseResult.ok("delete account succeed");
}
@Operation(summary = "根据id查询账户")
@GetMapping("/queryById/{id}")
@Parameter(name = "id",description = "主键id",required = true)
public ResponseResult<AccountRspDTO> queryById(@PathVariable(value = "id") Long id){
log.info("query account detail,id is :{}",id);
AccountRspDTO accountRspDTO = this.accountExecutor.queryAccountById(id);
return ResponseResult.ok(accountRspDTO);
}
@Operation(summary = "分页查询账户列表")
@PostMapping("/pageList")
public ResponseResult<PageDTO<AccountRspDTO> > pageList(@RequestBody PageQuery<AccountQuery> pageQuery){
log.info("page account list, pageQuery:{}",pageQuery);
PageDTO<AccountRspDTO> pageDTO = this.accountExecutor.pageAccountList(pageQuery);
return ResponseResult.ok(pageDTO);
}
@Operation(summary = "查查满足条件的记录")
@PostMapping("/queryList")
public ResponseResult<List<AccountRspDTO>> queryList(@RequestBody AccountQuery accountQuery){
log.info("query account list, accountQuery:{}",accountQuery);
List<AccountRspDTO> dtoList = this.accountExecutor.queryAccountList(accountQuery);
return ResponseResult.ok(dtoList);
}
@Operation(summary = "扣减金额")
@PostMapping("/reduceAmount")
@Parameters ({
@Parameter(name = "id",description = "主键id",required = true),
@Parameter(name = "amount",description = "金额",required = true)
})
public ResponseResult<String> reduceAmount(@RequestParam("id") Long id,@RequestParam("amount") BigDecimal amount){
log.info("reduce account amount, id:{},amount:{}",id,amount);
this.accountExecutor.reduceAccountAmount(id,amount);
return ResponseResult.ok("reduce amount succeed");
}
}
ProductController 内容如下。
@Slf4j
@Tag(name = "product-wep端api")
@RestController
@RequestMapping(BaseConstant.AdapterType.WEB+"/v1/product")
public class ProductController {
@Resource
private ProductExecutor productExecutor;
@Operation(summary = "新增商品")
@PostMapping("/add")
public ResponseResult<String> add(@RequestBody ProductReqDTO productReqDTO){
log.info("add product, productReqDTO:{}",productReqDTO);
this.productExecutor.addProduct(productReqDTO);
return ResponseResult.ok("add Product succeed");
}
@Operation(summary = "编辑商品")
@PostMapping("/edit")
public ResponseResult<String> edit(@RequestBody ProductReqDTO productReqDTO){
log.info("edit product, productReqDTO:{}",productReqDTO);
this.productExecutor.editProduct(productReqDTO);
return ResponseResult.ok("update Product succeed");
}
@Operation(summary = "根据id删除商品")
@PostMapping("/removeById")
@Parameter(name = "id",description = "主键id",required = true)
public ResponseResult<String> removeById(@RequestParam Long id){
log.info("delete product,id is {}",id);
this.productExecutor.removeProductById(id);
return ResponseResult.ok("delete Product succeed");
}
@Operation(summary = "根据id查询商品")
@GetMapping("/queryById/{id}")
@Parameter(name = "id",description = "主键id",required = true)
public ResponseResult<ProductRspDTO> queryById(@PathVariable(value = "id") Long id){
log.info("query product detail,id is :{}",id);
ProductRspDTO productRspDTO = this.productExecutor.queryProductById(id);
return ResponseResult.ok(productRspDTO);
}
@Operation(summary = "扣减数量")
@PostMapping("/reduceAmount")
@Parameters({
@Parameter(name = "id",description = "主键id",required = true),
@Parameter(name = "count",description = "数量",required = true)
})
public ResponseResult<String> reduceCount(@RequestParam("id") Long id,@RequestParam("count") Integer count){
log.info("reduce product count, id:{},count:{}",id,count);
this.productExecutor.reduceProductCount(id,count);
return ResponseResult.ok("reduce account succeed");
}
@Operation(summary = "分页查询商品列表")
@PostMapping("/pageList")
public ResponseResult<PageDTO<ProductRspDTO> > pageList(@RequestBody PageQuery<ProductQuery> pageQuery){
log.info("page product list, pageQuery:{}",pageQuery);
PageDTO<ProductRspDTO> pageDTO = this.productExecutor.pageProductList(pageQuery);
return ResponseResult.ok(pageDTO);
}
@Operation(summary = "查查满足条件的记录")
@PostMapping("/queryList")
public ResponseResult<List<ProductRspDTO>> queryList(@RequestBody ProductQuery productQuery){
log.info("query product list, productQuery:{}",productQuery);
List<ProductRspDTO> dtoList = this.productExecutor.queryProductList(productQuery);
return ResponseResult.ok(dtoList);
}
}
OrderController 内容如下。
@Slf4j
@Tag(name = "order-wep端api")
@RestController
@RequestMapping(BaseConstant.AdapterType.WEB+"/v1/order")
public class OrderController {
@Resource
private OrderExecutor orderExecutor;
@Operation(summary = "新增订单")
@PostMapping("/add")
public ResponseResult<String> add(@RequestBody OrderReqDTO orderReqDTO){
log.info("add order, orderReqDTO:{}",orderReqDTO);
this.orderExecutor.addOrder(orderReqDTO);
return ResponseResult.ok("add Order succeed");
}
@Operation(summary = "编辑订单")
@PostMapping("/edit")
public ResponseResult<String> edit(@RequestBody OrderReqDTO orderReqDTO){
log.info("edit order, orderReqDTO:{}",orderReqDTO);
this.orderExecutor.editOrder(orderReqDTO);
return ResponseResult.ok("update Order succeed");
}
@Operation(summary = "根据id删除订单")
@PostMapping("/removeById")
@Parameter(name = "id",description = "主键id",required = true)
public ResponseResult<String> removeById(@RequestParam Long id){
log.info("delete order,id is {}",id);
this.orderExecutor.removeOrderById(id);
return ResponseResult.ok("delete Order succeed");
}
@Operation(summary = "根据id查询订单")
@GetMapping("/queryById/{id}")
@Parameter(name = "id",description = "主键id",required = true)
public ResponseResult<OrderRspDTO> queryById(@PathVariable(value = "id") Long id){
log.info("query order detail,id is :{}",id);
OrderRspDTO orderRspDTO = this.orderExecutor.queryOrderById(id);
return ResponseResult.ok(orderRspDTO);
}
@Operation(summary = "分页查询订单列表")
@PostMapping("/pageList")
public ResponseResult<PageDTO<OrderRspDTO> > pageList(@RequestBody PageQuery<OrderQuery> pageQuery){
log.info("page order pageQuery:{}",pageQuery);
PageDTO<OrderRspDTO> pageDTO = this.orderExecutor.pageOrderList(pageQuery);
return ResponseResult.ok(pageDTO);
}
@Operation(summary = "查查满足条件的记录")
@PostMapping("/queryList")
public ResponseResult<List<OrderRspDTO>> queryList(@RequestBody OrderQuery orderQuery){
log.info("query order list, orderQuery:{}",orderQuery);
List<OrderRspDTO> dtoList = this.orderExecutor.queryOrderList(orderQuery);
return ResponseResult.ok(dtoList);
}
}
2.3.3. 添加应用层业务类
上面的前端控制器都需要调用应用层的业务方法。分别在账户模块、商品模块、订单模块的应用层executor 目录下,创建业务类,添加对应的方法。业务类名称分别为:AccountExecutor、ProductExecutor、OrderExecutor。
AccountExecutor 内容如下。
@Component
public class AccountExecutor {
@Resource
private AccountService accountService;
/**
*新增账户信息
*/
public void addAccount(AccountReqDTO accountReqDTO) throws BaseException {
Long id = IdWorker.getId();
this.accountService.add(accountReqDTO,id);
}
/**
*编辑账户
*/
public void editAccount(AccountReqDTO accountReqDTO) throws BaseException {
this.accountService.edit(accountReqDTO);
}
/**
*根据id删除账户
*/
public void removeAccountById(Long id) throws BizException {
this.accountService.removeById(id);
}
/**
*根据id查询账户
*/
public AccountRspDTO queryAccountById(Long id) {
AccountEntity accountEntity = this.accountService.getById(id);
AccountRspDTO accountRspDTO = new AccountRspDTO();
BeanUtils.copyProperties(accountEntity,accountRspDTO);
return accountRspDTO;
}
/**
*扣减金额
*/
public void reduceAccountAmount(Long id, BigDecimal amount) throws BizException {
this.accountService.reduceAmount(id,amount);
}
/**
*分页查询
*/
public PageDTO<AccountRspDTO> pageAccountList(PageQuery<AccountQuery> pageQuery) {
return this.accountService.pageList(pageQuery);
}
/**
*查查满足条件的记录
*/
public List<AccountRspDTO> queryAccountList(AccountQuery accountQuery) {
return this.accountService.queryList(accountQuery);
}
}
ProductExecutor 内容如下。
@Component
public class ProductExecutor {
@Resource
private ProductService productService;
/**
*新增商品信息
*/
public void addProduct(ProductReqDTO productReqDTO) throws BaseException {
Long id = IdWorker.getId();
this.productService.add(productReqDTO,id);
}
/**
*编辑商品
*/
public void editProduct(ProductReqDTO productReqDTO) throws BaseException {
this.productService.edit(productReqDTO);
}
/**
*根据id删除商品
*/
public void removeProductById(Long id) throws BizException {
this.productService.removeById(id);
}
/**
*根据id查询商品
*/
public ProductRspDTO queryProductById(Long id) {
ProductEntity productEntity = this.productService.getById(id);
ProductRspDTO productRspDTO = new ProductRspDTO();
BeanUtils.copyProperties(productEntity,productRspDTO);
return productRspDTO;
}
/**
*扣减数量
*/
public void reduceProductCount(Long id, Integer count) throws BizException {
this.productService.reduceCount(id,count);
}
/**
*分页查询
*/
public PageDTO<ProductRspDTO> pageProductList(PageQuery<ProductQuery> pageQuery) {
return this.productService.pageList(pageQuery);
}
/**
*查查满足条件的记录
*/
public List<ProductRspDTO> queryProductList(ProductQuery productQuery) {
return this.productService.queryList(productQuery);
}
}
OrderExecutor 内容如下。
@Component
public class OrderExecutor {
@Resource
private OrderService orderService;
/**
*新增订单信息
*/
public void addOrder(OrderReqDTO orderReqDTO) throws BaseException {
Long id = IdWorker.getId();
this.orderService.add(orderReqDTO,id);
}
/**
*编辑订单
*/
public void editOrder(OrderReqDTO orderReqDTO) throws BaseException {
this.orderService.edit(orderReqDTO);
}
/**
*根据id删除订单
*/
public void removeOrderById(Long id) throws BizException {
this.orderService.removeById(id);
}
/**
*根据id查询订单
*/
public OrderRspDTO queryOrderById(Long id) {
OrderEntity orderEntity = this.orderService.getById(id);
OrderRspDTO orderRspDTO = new OrderRspDTO();
BeanUtils.copyProperties(orderEntity,orderRspDTO);
return orderRspDTO;
}
/**
*分页查询
*/
public PageDTO<OrderRspDTO> pageOrderList(PageQuery<OrderQuery> pageQuery) {
return this.orderService.pageList(pageQuery);
}
/**
*查查满足条件的记录
*/
public List<OrderRspDTO> queryOrderList(OrderQuery orderQuery) {
return this.orderService.queryList(orderQuery);
}
}
3. MyBatis-Plus+Knife4j 测试
分别启动账户模块、商品模块、订单模块服务。
3.1. 测试文档效果
3.1.1. 账户模块
3.1.1.1. 文档首页
打开浏览器,输入 http://localhost:7030/doc.html# 地址,效果如下。
3.1.1.2. 文档分组
分组下拉效果如下所示。
选择 adapter-web 组,效果如下。
3.1.2. 商品模块
3.1.2.1. 文档首页
打开浏览器,输入 http://localhost:7040/doc.html# 地址,效果如下。
3.1.2.2. 文档分组
分组下拉效果如下所示。
选择 adapter-web 组,效果如下。
3.1.3. 订单模块
3.1.3.1. 文档首页
打开浏览器,输入 http://localhost:7050/doc.html# 地址,效果如下。
3.1.3.2. 文档分组
分组下拉效果如下所示。
选择 adapter-web 组,效果如下。
3.2. 测试接口效果
以账户模块为例进行测试。
3.2.1. 新增账户
打开新增账户接口文档。
3.2.1.1. 入参信息
3.2.1.2. 出参信息
3.2.1.3. 执行效果
3.2.2. 分页查询
打开分页查询接口文档。
3.2.2.1. 入参信息
3.2.2.2. 出参信息
3.2.2.3. 执行效果
3.2.3. 详情接口
打开根据 id 查询账户接口文档。
3.2.3.1. 入参信息
3.2.3.2. 出参信息
3.2.3.3. 执行效果
4. 总结
本篇根据上一篇搭建好的项目基础架构,介绍了 MyBatis-Plus 、Knife4j 的整合,并分别给账户模块、商品模块、订单模块下的各层工程,添加了 CRUD 接口。同时对 MyBatis-Plus 、Knife4j 整合后的效果进行了相关测试。