低代码之自动生成API

AutoApi(自动化API接口)

产品宗旨:通过配置的方式自动化处理项目开发中简单重复的内容,解放生产力,使其更加专注产品业务逻辑;快速标准化输出,缩短项目研发周期


1. 产品简介

2. 架构说明

2.1 系统架构

architecture.png

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

swagger效果示例

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) 模糊查询和范围查询执行效果

模糊和范围查询sql

(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);
    }
}

效果演示如下
入参:

id自定义入参演示

保存后的效果:
id自定义出参演示

数据库实际效果:
id自定义数据库保存效果

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

@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生成(如果自定义实现了对应的保存方法)
  • 自定义字段赋值(如果自定义实现了对应的保存更新方法)
  • 自定义事务前后操作(如果自定义实现了对应的保存更新方法)
    • 非事务的切面操作不受影响(原因可参考切面实现原理图)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容