AutoApi(自动化API接口)
产品宗旨:通过配置的方式自动化处理项目开发中简单重复的内容,解放生产力,使其更加专注产品业务逻辑;快速标准化输出,缩短项目研发周期
1. 产品简介
- 通过简单的接口规则配置,自动生成13个单表crud接口 + 3个主子表关联接口,基本覆盖单表crud的所有场景
- 支持各种场景自定义,在crud基础之上增加对复杂业务场景的自定义支持
- 仓库地址
2. 架构说明
2.1 系统架构

2.2 技术栈
| seq | 选型 | 版本 |
|---|---|---|
| 1 | spring boot | 2.7.3 |
| 2 | spring-web | 5.3.22 |
| 3 | javax.servlet-api | 4.0.1 |
| 4 | spring-boot-starter-log4j2 | 2.7.3 |
| 5 | springdoc-openapi-ui | 1.6.11 |
| 6 | spring-boot-starter-jdbc | 2.7.3 |
| 7 | groovy-all | 3.0.9 |
| 8 | velocity | 1.7 |
| 9 | jackson-datatype-jdk8 | 2.13.3 |
3. 功能说明
3.1 接口全景展示

3.2 部分接口示例
3.2.1 分页查询接口

3.2.2 单对象保存接口

3.3 自定义场景支持
| seq | 场景 | 状态 |
|---|---|---|
| 1 | 自定义排序字段配置 | 支持 |
| 2 | 查询操作自定义,比如like等 | 支持 |
| 3 | 自定义id生成(默认数据库自增) | 支持 |
| 4 | 公共字段赋值的支持,如create_time/create_user等 | 支持 |
| 5 | 自定义service实现的支持(复杂业务场景支持) | 支持 |
| 6 | 事务前事务后,非事务前非事务后自定义操作的支持 | 支持 |
| 7 | sql打印和级别定义(方便定位问题) | 支持 |
| 8 | 自定义基础实体类支持 | 支持 |
| 9 | 主子表关联,支持定义对应的主表字段和字表字段 | 支持 |
| 10 | mysql数据库支持 | 支持 |
| 11 | postgres数据库支持 | 研发中 |
| 12 | 接口定义配置支持数据库持久化(默认为配置文件) | 研发中 |
4. 开发指引
4.1 注意事项
本产品仅支持数值类型的id,比如int/long等,可以是多个字段的组合id,但要求至少有一个是数值类型
4.2 基础功能配置
基础的api功能,只需要执行以下几个简单的步骤,即可完成
4.2.1 配置步骤
(1) 创建数据库表,范例如下
注意事项:表字段说明会直接提现在接口上,所以表注解越详细易懂,接口说明则越清晰,沟通成本越低
-- 订单主表
CREATE TABLE `mall_order` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID编号',
`user_id` bigint NOT NULL COMMENT '用户表的用户ID',
`order_no` varchar(63) NOT NULL COMMENT '订单编码',
`order_status` smallint DEFAULT NULL COMMENT '订单状态',
`consignee` varchar(63) DEFAULT NULL COMMENT '收货人名称',
`mobile` varchar(63) DEFAULT NULL COMMENT '收货人手机号',
`address` varchar(127) DEFAULT NULL COMMENT '收货具体地址',
`message` varchar(512) DEFAULT '' COMMENT '用户订单留言',
`goods_price` decimal(12,4) DEFAULT NULL COMMENT '商品总金额',
`freight_price` decimal(12,4) DEFAULT NULL COMMENT '配送金额',
`coupon_price` decimal(12,4) DEFAULT NULL COMMENT '优惠券减免金额',
`integral_price` decimal(12,4) DEFAULT NULL COMMENT '用户积分减免金额',
`groupon_price` decimal(12,4) DEFAULT NULL COMMENT '团购优惠价减免金额',
`order_price` decimal(12,4) DEFAULT NULL COMMENT '订单金额',
`actual_price` decimal(12,4) DEFAULT NULL COMMENT '实付金额',
`pay_id` varchar(63) DEFAULT NULL COMMENT '微信付款编号',
`pay_time` datetime DEFAULT NULL COMMENT '微信付款时间',
`deleted` tinyint(1) DEFAULT '0' COMMENT '软删除',
`org_code` varchar(63) DEFAULT NULL COMMENT '组织机构编码',
`tenant` varchar(32) DEFAULT NULL COMMENT '租户标识',
`app_id` varchar(32) DEFAULT NULL COMMENT '应用标识 - 一般是创建者应用标识',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`create_user` varchar(32) DEFAULT NULL COMMENT '创建人标识',
`create_user_name` varchar(255) DEFAULT NULL COMMENT '创建人名称',
`update_time` timestamp NULL DEFAULT NULL COMMENT '最后更新时间',
`update_user` varchar(32) DEFAULT NULL COMMENT '最后更新人标识',
`update_user_name` varchar(255) DEFAULT NULL COMMENT '最后更新人名称',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1574 DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
-- 订单明细表
CREATE TABLE `mall_order_item` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID编号',
`order_id` bigint NOT NULL DEFAULT '0' COMMENT '订单表的订单ID',
`brand_id` bigint DEFAULT NULL COMMENT '店铺编码',
`goods_id` bigint NOT NULL DEFAULT '0' COMMENT '商品表的商品ID',
`goods_name` varchar(127) DEFAULT '' COMMENT '商品名称',
`goods_sn` varchar(63) DEFAULT '' COMMENT '商品编号',
`product_id` int DEFAULT '0' COMMENT '货品ID',
`number` smallint DEFAULT '0' COMMENT '购买数量',
`price` decimal(12,4) DEFAULT '0.0000' COMMENT '售价',
`specifications` text COMMENT '规格',
`pic_url` varchar(255) DEFAULT '' COMMENT '图片地址',
`comment` bigint DEFAULT '0' COMMENT '评价id',
`deleted` tinyint(1) DEFAULT '0' COMMENT '软删除',
`refund_id` bigint DEFAULT NULL COMMENT '退款跟踪ID',
`org_code` varchar(63) DEFAULT NULL COMMENT '组织机构编码',
`tenant` varchar(32) DEFAULT NULL COMMENT '租户标识',
`app_id` varchar(32) DEFAULT NULL COMMENT '应用标识 - 一般是创建者应用标识',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`create_user` varchar(32) DEFAULT NULL COMMENT '创建人标识',
`create_user_name` varchar(255) DEFAULT NULL COMMENT '创建人名称',
`update_time` timestamp NULL DEFAULT NULL COMMENT '最后更新时间',
`update_user` varchar(32) DEFAULT NULL COMMENT '最后更新人标识',
`update_user_name` varchar(255) DEFAULT NULL COMMENT '最后更新人名称',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_01_order_item` (`order_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='订单明细表';
(2) maven仓库地址配置:settings.xml
仓库认证
<servers>
<server>
<id>lockgate-releases</id>
<username>63704d6128b61c88e75b4600</username>
<password>7669WMzm=HrM</password>
</server>
</servers>
仓库profile
<profiles>
<profile>
<id>lockgate</id>
<repositories>
<repository>
<id>lockgate-releases</id>
<name>lockgate dependences</name>
<url>https://packages.aliyun.com/maven/repository/2139326-release-jtDdoi/</url>
</repository>
</repositories>
</profile>
</profiles>
激活profile
<activeProfiles>
<activeProfile>lockgate</activeProfile>
</activeProfiles>
完整范例
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>D:\repository\lockgate</localRepository>
<servers>
<server>
<id>lockgate-releases</id>
<username>63704d6128b61c88e75b4600</username>
<password>7669WMzm=HrM</password>
</server>
</servers>
<mirrors>
<mirror>
<id>AliRepo-aliyun</id>
<mirrorOf>*,!lockgate-releases</mirrorOf>
<name>Mirror Name for the Alirepo.</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>lockgate</id>
<repositories>
<repository>
<id>lockgate-releases</id>
<name>lockgate dependences</name>
<url>https://packages.aliyun.com/maven/repository/2139326-release-jtDdoi/</url>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles>
<!-- 可以激活多个 -->
<activeProfile>lockgate</activeProfile>
</activeProfiles>
</settings>
(3) 创建springboot工程,添加如下依赖
当前仅支持mysql数据库,所以依赖mysql支持包即可
<dependency>
<groupId>cn.lockgate</groupId>
<artifactId>lockgate-autoapi-support-mysql</artifactId>
<version>2.0.0</version>
</dependency>
(4) 配置application.yml或者bootstrap.yml
# 基础配置,略
# 数据源配置,略
# autoapi接口配置,如下
autoapi:
config:
# resource -> 配置文件, db -> 数据库
configReaderType: "resource"
# api的包路径
# autoPackage: "com.nolink.demo.codeless.auto.api"
# 是否强制为post
forcePosting: false
# 是否开启自动接口,default true
autoApiEnable: true
# 开启sql日志打印级别,默认为info
sqlLogLevel: info
# 自定义基础类
# baseDTOClass: cc.nolink.framework.dto.BaseResponseDTO
# 自定义查询基础类
# baseQueryDTOClass: cc.nolink.framework.dto.BaseRequestDTO
# 批处理批次大小
jdbc:
batchSize: 500
maxPageSize: 1000
api:
apis:
- domain: "retails" # 领域,构成接口path的组成部分,规则:/domain/service/entity/action
services:
- service: "mall" # 服务,构成接口path的组成部分
entities:
- entity: "order" # 实体,构成接口path的组成部分
table: "mall_order" # 实体对应的数据库表名
tags:
tagName: "订单头表API" # 接口归类名称,默认为:entity + "API"
tagDesc: "订单头表:mall_order增删改查接口" # 接口归类描述,默认为:table + "API"
actions: # 支持的action,all为全部,为空则表示一个也不支持,可以自定义选择需要的接口。支持的接口全集如下
- all
#- add # 新增一条记录
#- adds # 批量新增
#- updateById # 依据ID更新
#- updateByIds # 依据ID列表批量更新
#- updateByQueries # 依据自定义条件批量更新
#- save # 单个:id == null?新增:修改
#- saves # 批量:id == null?新增:修改
#- deleteById # 依据ID删除
#- deleteByIds # 依据ID列表批量删除
#- deleteByQueries # 依据自定义条件批量删除
#- findById # 依据ID查询
#- queryByIds # 依据ID列表查询列表,不带分页
#- query # 依据自定义条件查询列表,带分页
#- addWithItems # 主子表关联新增
#- deleteWithItemsById # 依据主表ID级联删除主子表
#- findWithItemsById # 依据主表ID级联查询主子表
orders: # map格式的排序规则配置,多个字段按定义的顺序进行排序
- order_no: desc # 数据库字段名:排序规则(asc/desc)
- create_time: asc
items: # 主子表关联配置,可以配置多个,如果没有,则不配置该items项
- tableName: "mall_order_item" # 子表名称
foreignKey: "order_id" # 子表关联字段名称
parentKey: "id" # 对应主表字段名称,默认是id
- entity: "order/item" # 实体,为了path显示更合理,entity支持定义成order/item这样的格式
table: "mall_order_item"
actions: # 自定义选择的action,最终接口仅仅展示选择的内容
- add
- adds
- updateById
- save
- saves
- deleteById
- service: "customer"
entities:
- entity: "customer"
table: "eg_customer"
actions:
- all
- domain: "marketing"
(5) 启动工程,打开swagger-ui接口列表,即可提交前端进行调试
例如范例工程打开:http://127.0.0.1:8080/swagger-ui.html

4.2.2 autoapi.config配置说明
| seq | 配置项 | 说明 |
|---|---|---|
| 1 | autoapi.config.configReaderType | 配置类型resource:配置文件配置db:数据库配置,暂不支持 |
| 2 | autoapi.config.autoPackage | 生成的api controller包路径,对于某些需要特定指定api扫描路径的场景,可以自定义配置该路径 |
| 3 | autoapi.config.forcePosting | 是否强制所有method为post,某些场景,或者具体项目组开发规范要求,需要接口全部用post的,则设置此项true:全postfalse(默认):按语义,有post/put/get/delete等 |
| 4 | autoapi.config.autoApiEnable | 是否启用自动接口true(默认):开启false:关闭,为false时,自动接口将失效(注意是接口失效,而不仅仅是swagger不显示) |
| 5 | autoapi.config.sqlLogLevel | sql打印控制,方便问题定位trace/debug/info(默认)/warn/error/fatal/off,为off时,不打印sql,一般生产环境设置为off |
| 6 | autoapi.config.baseDTOClass | 自定义维护类dto对象父类对于部分开发规范,可能会定义表结构规范,有固定的公共字段,并且有固定的model/dto父类与之对应 |
| 7 | autoapi.config.baseQueryDTOClass | 同上,不设置则默认为baseDTOClass,如果baseDTOClass也未设置,则为系统默认的BaseDTO |
4.3 自定义场景支持
4.3.1 自定义查询排序
对于列表查询,业务一般都会有要求按制定个的字段排序,系统支持自定义排序,如果有多个排序字段,将按配置的顺序进行排序
具体配置如下
- 受影响的接口
- query 按自定义条件分页查询
- queryByIds 依据id列表查询
- 配置方式
autoapi:
api:
apis:
- domain: "retails"
services:
- service: "mall"
entities:
- entity: "order"
table: "mall_order"
tags:
tagName: "订单头表API"
tagDesc: "订单头表:mall_order增删改查接口"
actions:
- all
orders: # map格式的排序规则配置,多个字段按定义的顺序进行排序
- order_no: desc # 数据库字段名: 排序规则(asc/desc)
- create_time: asc
-
实际执行效果
查询排序自定义sql
4.3.2 自定义查询操作
系统默认的查询操作为
- 时间字段,如:timestamp、date、time
默认范围查询,如create_time创建时间字段,默认会有如下3个查询字段以及对应的规则
(1) createTime有值,则:create_time = createTime,且此时下面两条规则失效
(2) createTimeStart(虚拟查询字段,数据库中不存在)有值,则:create_time >= createTimeStart
(3) createTimeEnd(虚拟查询字段,数据库中不存在)有值,则:create_time <= createTimeEnd - 除以上时间字段类型
默认精确查询:a = b
系统同时支持自定义查询操作,比如部分场景要求模糊查询,或者数值类型要求范围查询。
通过继承接口:IPostTableLoadHandler实现自定义查询配置,范例如下:
/**
* @auth lockgate
* @description
* @since 2022/10/18 22:37
*/
@Component
@AssignedTableName("mall_order")
@Slf4j
public class MallOrderOptionPostTableLoadHandler implements IPostTableLoadHandler {
/**
* 元数据加载完成,可以在这里按需求变更元数据,比如:范围查询、模糊查询定义等
*
* @param tableInfo
*/
@Override
public void doPostHandle(TableInfo tableInfo) {
if (Objects.nonNull(tableInfo) && !CollectionUtils.isEmpty(tableInfo.getColumns())) {
List<ColumnInfo> columns = tableInfo.getColumns();
// 转map
Map<String, ColumnInfo> columnInfoMap = columns
.stream()
.parallel()
.filter(col -> !col.isVirtual()) // 排除过滤虚拟字段:不能针对虚拟字段进行二次设置
.collect(Collectors.toConcurrentMap(ColumnInfo::getColumnName, Function.identity()));
// 自定义like查询
orderNo2Like(columnInfoMap.get("order_no"));
// 自定义范围查询
List<ColumnInfo> goodsPriceVirtualList = goodsPrice2Scope(columnInfoMap.get("goods_price"));
columns.addAll(goodsPriceVirtualList);
}
}
/**
* orderNo订单编码模糊查询范例
* @param orderNoColumn
*/
private void orderNo2Like(ColumnInfo orderNoColumn) {
if (Objects.nonNull(orderNoColumn)) {
// 支持的自定义操作详见:SqlOptEnum
orderNoColumn.setSqlOpt(SqlOptEnum.PRE_LIKE.getCode());
}
}
/**
* 商品价格范围查询范例
* @param goodsPriceColumn
*/
private List<ColumnInfo> goodsPrice2Scope(ColumnInfo goodsPriceColumn) {
List<ColumnInfo> virtualList = new ArrayList<>();
if (Objects.nonNull(goodsPriceColumn)) {
// 范围查询配置比较特殊,需要增加两个虚拟查询字段,起始值和结束值,ColumnExtendUtil.cloneAndRewrite方法为系统提供,不需要用户实现
virtualList.add(ColumnExtendUtil.cloneAndRewrite(goodsPriceColumn, IAutoApiConstant.VIRTUAL_START));
virtualList.add(ColumnExtendUtil.cloneAndRewrite(goodsPriceColumn, IAutoApiConstant.VIRTUAL_END));
}
return virtualList;
}
}
效果如下:
(1) 模糊查询和范围查询执行效果

(2) 范围查询参数显示效果

4.3.3 自定义id生成
系统默认的id规则是数据库自增,同时也支持应用自定义id,只要继承数据库操作listener,实现beforeInsert方法,在插入对象前对id进行赋值即可,范例代码如下
(1) 方法一,直接对按数据库字段,对对象进行赋值
/**
* @auth lockgate
* @description
* @since 2022/10/24 21:53
*/
@Component
@AssignedTableName("mall_order")
public class CustomIdGenerator implements ITableOperationHandler {
/**
* 插入之前设置值
* 常见的需要设置的字段:id、创建人、创建时间戳、更新人、更新时间戳
*
* @param entity
*/
@Override
public void doBeforeInsert(Object entity) {
// 方法 1:直接按数据库字段名进行赋值
setFieldValue(entity, "id", 1500l);
}
}
效果演示如下
入参:

保存后的效果:

数据库实际效果:

(2) 方式二,配置基础父对象

@Component
@AssignedTableName("mall_order")
public class CustomIdGenerator implements ITableOperationHandler {
/**
* 插入之前设置值
* 常见的需要设置的字段:id、创建人、创建时间戳、更新人、更新时间戳
*
* @param entity
*/
@Override
public void doBeforeInsert(Object entity) {
// 方法 1:直接按数据库字段名进行赋值
// setFieldValue(entity, "id", 1500l);
// 方法 2:配置中定义了对象的父基础对象CustomBaseDTO,可以直接对该对象赋值
if (Objects.nonNull(entity)) {
((CustomBaseDTO)entity).setId(1550l);
}
}
}
效果同方法1,这里不重复演示。
4.3.4 自定义字段赋值
自定义字段赋值多用于公共基础字段赋值的场景,比如部分项目数据库开发规范定义了数据表都必须有创建人、创建时间、最后更新人、最后更新时间等字段,此时可以针对这些非业务字段进行独立的赋值。
例如表基础字段如下
CREATE TABLE `mall_order` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID编号',
`org_code` varchar(64) DEFAULT NULL COMMENT '组织机构编码',
`tenant` varchar(32) DEFAULT NULL COMMENT '租户标识',
`app_id` varchar(32) DEFAULT NULL COMMENT '应用标识 - 一般是创建者应用标识',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`create_user` varchar(32) DEFAULT NULL COMMENT '创建人标识',
`create_user_name` varchar(256) DEFAULT NULL COMMENT '创建人名称',
`update_time` timestamp NULL DEFAULT NULL COMMENT '最后更新时间',
`update_user` varchar(32) DEFAULT NULL COMMENT '最后更新人标识',
`update_user_name` varchar(256) DEFAULT NULL COMMENT '最后更新人名称',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='订单表';
对应的方法同自定义id一样,也有两种:
(1) 直接依据数据库字段进行赋值
// 方法 1:直接按数据库字段名进行赋值
setFieldValue(entity, "create_time", Date.from(Instant.now()));
setFieldValue(entity, "create_user", "102");
setFieldValue(entity, "create_user_name", "阀门02");
(2) 强转成基础对象进行赋值
/**
* @auth lockgate
* @description
* @since 2022/10/24 21:53
*/
@Component
@AssignedTableName("mall_order")
public class CustomIdGenerator implements ITableOperationHandler {
/**
* 插入之前设置值
* 常见的需要设置的字段:id、创建人、创建时间戳、更新人、更新时间戳
*
* @param entity
*/
@Override
public void doBeforeInsert(Object entity) {
// 方法 1:直接按数据库字段名进行赋值
// setFieldValue(entity, "id", 1500l);
// 方法 2:配置中定义了对象的父基础对象CustomBaseDTO,可以直接对该对象赋值
if (Objects.nonNull(entity)) {
((CustomBaseDTO)entity).setId(1550l);
}
// 除了赋值id,同时也可以赋值基础字段,如下
// 方法 1:直接按数据库字段名进行赋值
/*
setFieldValue(entity, "create_time", Date.from(Instant.now()));
setFieldValue(entity, "create_user", "102");
setFieldValue(entity, "create_user_name", "阀门02");
*/
// 方法 2:配置中定义了对象的父基础对象CustomBaseDTO,可以直接对该对象赋值
if (Objects.nonNull(entity)) {
((CustomBaseDTO)entity).setOrgCode("C002");
((CustomBaseDTO)entity).setTenant("T002");
((CustomBaseDTO)entity).setAppId("swagger002");
((CustomBaseDTO)entity).setCreateTime(Date.from(Instant.now()));
((CustomBaseDTO)entity).setCreateUser("103");
((CustomBaseDTO)entity).setCreateUserName("阀门03");
}
}
/**
* 更新之前设置值
* 常见的需要设置的字段:更新人、更新时间戳
*
* @param entity
*/
@Override
public void doBeforeUpdate(Object entity) {
if (Objects.nonNull(entity)) {
((CustomBaseDTO)entity).setUpdateTime(Date.from(Instant.now()));
((CustomBaseDTO)entity).setUpdateUser("103");
((CustomBaseDTO)entity).setUpdateUserName("阀门03");
}
}
}
4.3.5 自定义事务前后操作支持
框架支持事务内的切面操作和事务外的切面操作,实现对应的接口即可,对应接口如下
(1) 事务内:IInsideTransactionOperationAspectHandler
/**
* @auth lockgate
* @description 事务内的前后处理,需要定义table(@AppointTableName)和具体的action(@ActionType)
* @since 2022/9/7 0:42
*/
public interface IInsideTransactionOperationAspectHandler {
/**
* try块:逻辑执行前
* 具体参数类型:cn.lockgate.autoapi.runtime.IApiAutoOperation对应方法的参数和返回类型
* @param tableName
* @param args
*/
default void doBefore(String tableName, Object[] args) {}
/**
* try块:逻辑执行后
* 具体参数类型:cn.lockgate.autoapi.runtime.IApiAutoOperation对应方法的参数和返回类型
* @param tableName
* @param args
* @param result
*/
default void doAfter(String tableName, Object[] args, Object result) {}
}
(2) 事务外:IOutsideTransactionOperationAspectHandler
/**
* @auth lockgate
* @description
* @since 2022/9/7 0:42
*/
public interface IOutsideTransactionOperationAspectHandler extends IInsideTransactionOperationAspectHandler {
/**
* catch块中调用
* result:
* true -> 继续抛出异常
* false -> 只打印异常信息,不抛出异常
* 具体参数类型:cn.lockgate.autoapi.runtime.IApiAutoOperation对应方法的参数和返回类型
* @param tableName
* @param args
*/
default boolean doException(String tableName, Object[] args, Throwable ex) {
return true;
}
/**
* finally块中调用
* 具体参数类型:cn.lockgate.autoapi.runtime.IApiAutoOperation对应方法的参数和返回类型
* @param tableName
* @param args
* @param result
*/
default void doFinally(String tableName, Object[] args, Object result) {}
}
实现原理说明:

代码示例:
@AssignedTableName("mall_order") // 指定表,必选
@ActionType(ActionEnum.SAVE) // 指定方法,必选
@Component
public class MallOrderSaveOutsideTransactionServiceAspectHandler implements IOutsideTransactionOperationAspectHandler {
/**
* try块:逻辑执行前
* 具体参数类型:cn.lockgate.autoapi.runtime.IApiAutoOperation对应方法的参数和返回类型
* @param tableName
* @param args
*/
@Override
public void doBefore(String tableName, Object[] args) {
// business code here ...
}
/**
* try块:逻辑执行后
* 具体参数类型:cn.lockgate.autoapi.runtime.IApiAutoOperation对应方法的参数和返回类型
* @param tableName
* @param args
* @param result
*/
@Override
public void doAfter(String tableName, Object[] args, Object result) {
// business code here ...
}
/**
* catch块中调用
* result:
* true -> 继续抛出异常
* false -> 只打印异常信息,不抛出异常
* 具体参数类型:cn.lockgate.autoapi.runtime.IApiAutoOperation对应方法的参数和返回类型
* @param tableName
* @param args
*/
@Override
public boolean doException(String tableName, Object[] args, Throwable ex) {
// business code here ...
return true;
}
/**
* finally块中调用
* 具体参数类型:cn.lockgate.autoapi.runtime.IApiAutoOperation对应方法的参数和返回类型
* @param tableName
* @param args
* @param result
*/
@Override
public void doFinally(String tableName, Object[] args, Object result) {
// business code here ...
}
}
4.3.6 自定义DTO父类
autoapi:
config:
# 自定义基础类
baseDTOClass: cc.nolink.example.custom.dto.CustomBaseDTO
# 自定义查询基础类
baseQueryDTOClass: cc.nolink.example.custom.dto.CustomQueryDTO
规则说明:
- baseDTOClass 指定新增、保存、返回等类的父类,父类中已经存在的属性(驼峰命名匹配),子类中将不会再重复生成,若不指定,则默认BaseDTO(框架提供的一个空对象,不含任何属性)
- baseQueryDTOClass 指定查询类的父类,如果不指定,则默认用baseDTOClass
4.3.7 自定义主子表关联字段
api:
apis:
- domain: "retails"
services:
- service: "mall"
entities:
- entity: "order"
table: "mall_order"
tags:
tagName: "订单头表API"
tagDesc: "订单头表:mall_order增删改查接口"
actions:
- all
orders: # map格式的排序规则配置
- order_no: desc
- create_time: asc
items:
- tableName: "mall_order_item" # 关联字表表名
foreignKey: "order_id" # 对应字表字段
parentKey: "id" # 关联主表字段,默认是id
- tableName: "mall_order_goods" # 关联字表表名
foreignKey: "order_id" # 对应字表字段
parentKey: "id" # 关联主表字段,默认是id
规则说明:
- foreignKey 指定子表关联字段
- parentKey 指定主表关联字段
主子表当前只允许配置单字段的映射关系,不支持多组合字段的映射关系;
同一主表可以支持配置多个子表;
4.3.8 自定义service业务实现逻辑
默认情况下,接口只提供简单的增删改查逻辑,无法满足复杂业务需求,所以框架提供了自定义service实现的支持,以此通过自定义业务逻辑处理,满足各种场景的业务需求。
4.3.8.1 实现说明
代码范例:
@Service
@AssignedTableName("mall_order") // 必须:需要指定哪张表对应的service
@Slf4j
public class CustomMallOrderService extends AbstractApiAppointedOperation {
private MallOrderService mallOrderService;
@Autowired
public CustomMallOrderService(MallOrderService mallOrderService) {
this.mallOrderService = mallOrderService;
}
/**
* 场景1:通过自定义的方式实现接口后面的业务逻辑,比如这里选择mybatis-plus接管后续的处理
*
* @param tableName
* @param arg
* @return
*/
@SneakyThrows
@Override
public Object add(String tableName, Object arg) {
if (Objects.nonNull(arg)) {
MallOrderModel order = PojoCopier.get().copy(arg, MallOrderModel.class);
boolean r = mallOrderService.save(order);
if (r) {
return order;
}
}
return null;
}
/**
* 场景2:仅仅是想插入业务处理逻辑,数据保存继续使用框架提供的能力
* 注意:这里ApiAutoService的获取必须通过如下方式,以ApiAutoOperationFactory的方式获取,不能通过spring bean注入的方式,因为ApiAutoService的初始化一定在自定义类之后
*
* @param tableName
* @param arg
* @return
*/
@Override
public int updateById(String tableName, Object arg) {
// 业务逻辑处理 1 ...
int r = ApiAutoOperationFactory.get().getApiAutoService().updateById(tableName, arg);
// 业务逻辑处理 2 ...
return r;
}
/**
* 自定义实现对应的方法
* @param tableName
* @param id
* @return
*/
@Override
public Object findById(String tableName, long id) {
log.info("CustomMallOrderService.findById params: tableName={}, id={}", tableName, id);
MallOrderModel model = mallOrderService.getById(id);
log.info("CustomMallOrderService.findById result: {}", JacksonUtil.format(JacksonUtil.toJsonString(model)));
return model;
}
}
说明:
- 继承抽象类:AbstractApiAppointedOperation
- 抽象类AbstractApiAppointedOperation本身已经包含默认逻辑,所以实现类只需要选择需要实现的方法即可
- 比如范例只实现了add/findById方法,其他未实现的方法,会继续走框架的默认逻辑
- 实现后的逻辑看项目需求,可以选择mybatis,jdbc,jpa,沿用框架的能力等可行的方式,无任何限制
- 如果后续处理的逻辑选择沿用框架的能力,即调用框架ApiAutoService方法执行数据保存,则只要插入需要的业务逻辑处理即可,不需要其他操作
- 否则,使用其他方式的话,需要开发者自行实现对应如Model、Service、Mapper/Dao等配套对象实现,此时相当于框架仅仅提供了接口入口controller层
4.3.8.2 使用限制
特别说明:如果自定义service实现后续不继续使用框架ApiAutoService方法,则对应框架逻辑将失效(因为自定义service接管了框架功能)如下:
- 自定义查询排序(如果自定义实现了对应的查询方法)
- 自定义查询操作(如果自定义实现了对应的查询方法)
- 自定义id生成(如果自定义实现了对应的保存方法)
- 自定义字段赋值(如果自定义实现了对应的保存更新方法)
- 自定义事务前后操作(如果自定义实现了对应的保存更新方法)
- 非事务的切面操作不受影响(原因可参考切面实现原理图)
