快速生成通用接口业务配置

简介: 快速生成通用接口业务配置

这个是干什么的?

简单介绍下:

作用:通过插件一键生成某个表对应的增删改查等基础或者说通用接口,这里面的代码是基础版的,后续改良后,很多业务的通用功能,比如流程的启动,自动下一步,通用导出,通用导入等等。

原理:核心就是BasicControllerPostProcessor,通过动态生成代理对象,完成逻辑实现。这里呢经过实践呢后续也有不少优化的,后续我总结后发一个新版的

一、核心处理类

package com.nari.core.handler;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;


/**
 * 动态加载baseMapper,baseDao,baseService
 *
 * @author <a href="mailto:njpkhuan@gmail.com">朱永胜</a>
 * @version 1.0.0
 * @since 2021/3/2
 */
@Slf4j
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class BasicControllerPostProcessor extends CommonAnnotationBeanPostProcessor {

    private static final long serialVersionUID = -945664767382485314L;

    @SuppressWarnings("NullableProblems")
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        String packageName = beanClass.getPackage().getName();
        log.trace("beanName---{},packageName:{}", beanName, packageName);
        List<String> per = ListUtil.of("com.nari.supervision.daily");
        List<String> tail = ListUtil.of("outbound","personal");
        if (per.stream().anyMatch(packageName::contains)) {
            if (tail.stream().map(v -> v + ".controller").anyMatch(packageName::contains)) {
                try {
                    updateResourceName(beanClass, beanName, "basicService", "Controller", "Service");
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }
            if (tail.stream().map(v -> v + ".service").anyMatch(packageName::contains)) {
                try {
                    updateResourceName(beanClass, beanName, "basicMapper", "Service", "TransformImpl");
                    updateResourceName(beanClass, beanName, "basicDao", "Service", "Mapper");
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }
            if (tail.stream().map(v -> v + "serviceImpl").anyMatch(packageName::contains)) {
                try {
                    updateResourceName(beanClass, beanName, "basicMapper", "Service", "TransformImpl");
                    updateResourceName(beanClass, beanName, "basicDao", "Service", "Mapper");
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }
        }
        return null;
    }

    private void updateResourceName(Class<?> beanClass, String beanName, String destField, String searchString, String replaceString)
            throws NoSuchFieldException, IllegalAccessException {
        Field field = beanClass.getSuperclass().getDeclaredField(destField);
        Resource basicMapper = field.getAnnotation(Resource.class);
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(basicMapper);
        Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
        memberValues.setAccessible(true);
        //noinspection unchecked
        Map<String, Object> map = (Map<String, Object>) memberValues.get(invocationHandler);
        map.put("name", StringUtils.replace(beanName, searchString, replaceString));
    }
}

二、基础类

BasicController

package com.nari.core.basic;

import com.nari.core.annotation.OperationLog;
import com.nari.core.groups.basic.InsertGroup;
import com.nari.core.groups.basic.UpdateGroup;
import com.nari.core.groups.query.PageQueryGroup;
import com.nari.core.web.ApiResult;
import com.nari.core.web.BaseParam;
import com.nari.core.web.PageResult;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiSort;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;


/**
 * 基础接口管理
 *
 * @author <a href="mailto:njpkhuan@gmail.com">朱永胜</a>
 * @version 1.0.0
 * @since 2021/3/1
 */
@Slf4j
@Valid
public class BasicController<T extends BasicModel, Q extends BaseParam, V, D, M extends BasicMapper<T, Q, D, V>> extends com.nari.core.web.BaseController {

    @Resource
    private BasicService<T, Q, V, D, M> basicService;

    /**
     * 添加
     *
     * @param query {@link Q}
     */
    @PostMapping
    @ApiOperation(value = "1.1 添加")
    @OperationLog(value = "添加")
    @Validated(value = InsertGroup.class)
    public ApiResult insert(@RequestBody Q query) {
        basicService.insert(query);
        return success("添加成功!");
    }


    /**
     * 根据id删除
     *
     * @param id 主键
     */
    @DeleteMapping("/{id}")
    @ApiOperation(value = "1.2 根据id删除")
    @OperationLog(value = "根据id删除")
    public ApiResult del(@PathVariable("id") String id) {
        basicService.del(id);
        return success("删除成功!");
    }

    /**
     * 根据id删除-软删除,实际修改标识符
     *
     * @param id 主键
     */
    @DeleteMapping("/{id}/weak")
    @ApiOperation(value = "1.3 根据id删除-软删除,实际修改标识符")
    @OperationLog(value = "根据id删除-软删除,实际修改标识符")
    public ApiResult delWeak(@PathVariable("id") String id) {
        basicService.delWeak(id);
        return success("删除成功!");
    }


    /**
     * 根据id更新信息
     *
     * @param query {@link Q}
     */
    @PutMapping("update")
    @ApiOperation(value = "1.4 根据id更新信息")
    @OperationLog(value = "根据id更新信息")
    @Validated(value = UpdateGroup.class)
    public ApiResult<V> update(@RequestBody Q query) {
        return success(basicService.update(query));
    }

    /**
     * 根据id更新信息-批量
     *
     * @param query {@link Q}
     */
    @PutMapping("updateBatch")
    @ApiOperation(value = "1.5 根据id更新信息-批量")
    @OperationLog(value = "根据id更新信息-批量")
    @Validated(value = UpdateGroup.class)
    public ApiResult updateBatch(@RequestBody List<Q> query) {
        basicService.updateBatch(query);
        return success();
    }


    /**
     * 查询单个数据
     *
     * @param id 主键
     */
    @GetMapping("/{id}")
    @ApiOperation(value = "1.6 查询单个数据")
    @OperationLog(value = "查询单个数据")
    public ApiResult<V> select(@PathVariable("id") String id) {
        return success(basicService.select(id));
    }

    /**
     * 分页
     *
     * @param query {@link Q}
     */
    @GetMapping("page")
    @ApiOperation(value = "1.7 分页")
    @OperationLog(value = "分页")
    @Validated(value = PageQueryGroup.class)
    public ApiResult<PageResult<V>> listUser(Q query) {
        return success(basicService.page(query));
    }
}

BasicDao

package com.nari.core.basic;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nari.core.web.BaseParam;
import com.nari.core.web.PageParam;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 基础接口
 *
 * @author <a href="mailto:njpkhuan@gmail.com">朱永胜</a>
 * @version 1.0.0
 * @since 2021/3/1
 */
public interface BasicDao<T extends BasicModel, D, Q extends BaseParam> extends BaseMapper<T> {
    List<D> selectPageRel(@Param("page") PageParam<D, Q> page, @Param("param") Q param);
}

BasicEnum

package com.nari.core.basic;


/**
 * 基础枚举
 * 主要用来配合apijson实现枚举展示
 *
 * @author <a href="mailto:17602556550@189.cn">朱永胜</a>
 * @version 1.0.0
 * @since 2023/5/25
 */
public interface BasicEnum {
    String getName();

    Integer getValue();
}

BasicMapper

package com.nari.core.basic;

import org.mapstruct.MappingTarget;

import java.util.List;


/**
 * 基础转换
 *
 * @author <a href="mailto:njpkhuan@gmail.com">朱永胜</a>
 * @version 1.0.0
 * @since 2021/3/1
 */
public interface BasicMapper<T, Q, D, V> {
    T query2do(Q query);

    V dto2View(D dto);

    D do2dto(T role);

    void update(Q query, @MappingTarget T t);

    List<V> dto2ViewPage(List<D> dto);
}

BasicModel

package com.nari.core.basic;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * 通用模型
 *
 * @author <a href="mailto:17602556550@189.cn">朱永胜</a>
 * @version 1.0.0
 * @since 2023/5/26
 */
@Data
public class BasicModel {
    /**
     * 主键
     */
    private String id;

    /**
     * 删除标识
     */
    @ApiModelProperty(value = "删除标识")
    private String deleted;
}

BasicService

package com.nari.core.basic;

import cn.hutool.core.util.IdUtil;
import com.nari.core.web.BaseParam;
import com.nari.core.web.PageParam;
import com.nari.core.web.PageResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;


/**
 * 基础服务
 *
 * @author <a href="mailto:njpkhuan@gmail.com">朱永胜</a>
 * @version 1.0.0
 * @since 2021/3/1
 */
@Slf4j
public class BasicService<T extends BasicModel, Q extends BaseParam, V, D, M extends BasicMapper<T, Q, D, V>> {
    @Resource
    private BasicDao<T, D, Q> basicDao;

    @Resource
    private BasicMapper<T, Q, D, V> basicMapper;

    /**
     * 添加
     *
     * @param query {@link Q}
     * @author <a href = "mailto:njpkhuan@gmail.com" >朱永胜</a >
     * @since 2021/3/1
     * @since 1.0.0
     */
    @Transactional(rollbackFor = Exception.class)
    public void insert(@Valid Q query) {
        T t = basicMapper.query2do(query);
        t.setId(IdUtil.simpleUUID());
        basicDao.insert(t);
    }

    /**
     * 删除
     *
     * @param id {@link Q}
     * @author <a href = "mailto:njpkhuan@gmail.com" >朱永胜</a >
     * @since 2021/3/1
     * @since 1.0.0
     */
    @Transactional(rollbackFor = Exception.class)
    public void del(@NotNull String id) {
        basicDao.deleteById(id);
    }

    /**
     * 根据用户id更新
     *
     * @param query @{@link Q}
     * @author <a href = "mailto:njpkhuan@gmail.com" >朱永胜</a >
     * @since 2021/3/1
     * @since 1.0.0
     */
    @Transactional(rollbackFor = Exception.class)
    public V update(@Valid Q query) {
        T data = basicMapper.query2do(query);
        basicDao.updateById(data);
        D d = basicMapper.do2dto(data);
        return basicMapper.dto2View(d);
    }

    /**
     * 查询
     *
     * @param id 主键
     * @author <a href = "mailto:njpkhuan@gmail.com" >朱永胜</a >
     * @since 2021/3/1
     * @since 1.0.0
     */
    public V select(@NotNull String id) {
        T data = basicDao.selectById(id);
        D d = basicMapper.do2dto(data);
        return basicMapper.dto2View(d);
    }


    /**
     * 查询多条记录
     *
     * @param query {@link Q}
     * @author <a href = "mailto:njpkhuan@gmail.com" >朱永胜</a >
     * @since 2021/2/23
     * @since 1.0.0
     */
    public PageResult<V> page(@Valid Q query) {
        PageParam<D, Q> page = new PageParam<>(query);
        page.setDefaultOrder("create_time desc");
        List<D> list = basicDao.selectPageRel(page, query);

        List<V> vList = basicMapper.dto2ViewPage(list);
        log.trace("{}", list);

        return new PageResult<>(vList, page.getTotal());
    }

    /**
     * 软删除
     *
     * @author <a href="mailto:17602556550@189.cn">朱永胜</a>
     * @since 2023/5/26
     */
    @Transactional(rollbackFor = Exception.class)
    public void delWeak(String id) {
        T t = basicDao.selectById(id);
        t.setDeleted("1");
        basicDao.updateById(t);
    }

    /**
     * 批量更新
     *
     * @author <a href="mailto:17602556550@189.cn">朱永胜</a>
     * @since 2023/6/6
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateBatch(List<Q> query) {
        query.forEach(this::update);
    }
}

GenericSuperclassUtil

package com.nari.core.basic;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class GenericSuperclassUtil {

    /*
     * 获取泛型类Class对象,不是泛型类则返回null
     */
    public static Class<?> getActualTypeArgument(Class<?> clazz) {
        Class<?> entitiClass = null;
        Type genericSuperclass = clazz.getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass)
                    .getActualTypeArguments();
            if (actualTypeArguments != null && actualTypeArguments.length > 0) {
                entitiClass = (Class<?>) actualTypeArguments[0];
            }
        }

        return entitiClass;
    }

    /*
     * 获取泛型类Class对象,不是泛型类则返回null
     */
    static Class<?> getActualTypeArgument(Class<?> clazz, Integer pos) {
        Class<?> entitiClass = null;
        Type genericSuperclass = clazz.getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass)
                    .getActualTypeArguments();
            if (actualTypeArguments != null && actualTypeArguments.length > pos) {
                entitiClass = (Class<?>) actualTypeArguments[pos];
            }
        }

        return entitiClass;
    }
}

三、POM

  <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>

  <dependency>
      <groupId>org.mapstruct</groupId>
      <artifactId>mapstruct</artifactId>
      <version>${org.mapstruct.version}</version>
  </dependency>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <annotationProcessorPaths>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </path>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok-mapstruct-binding</artifactId>
                <version>0.2.0</version>
            </path>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${org.mapstruct.version}</version>
            </path>
        </annotationProcessorPaths>

    </configuration>
</plugin>
相关文章
|
6月前
|
安全 C# 开发者
C#中的默认接口方法:接口演化的新篇章
【1月更文挑战第11天】本文探讨了C# 8.0中引入的默认接口方法,这一特性允许在接口中定义具有默认实现的方法。文章介绍了默认接口方法的语法、使用场景,以及它们如何影响接口的设计和实现,同时讨论了默认接口方法带来的好处和潜在的陷阱。
|
23天前
|
搜索推荐 数据挖掘 API
1688 详情 API 接口的优势体现在哪些方面?
1688详情API接口提供高效自动化数据获取、高质量全面数据、助力业务决策与营销、提升客户服务体验、广泛灵活的应用场景及安全可靠的数据保护,全方位支持商家发展。
|
3月前
|
开发框架 前端开发 JavaScript
在Winform应用中增加通用的业务编码规则生成
在Winform应用中增加通用的业务编码规则生成
|
6月前
|
安全 前端开发 NoSQL
如果让你设计一个接口,你会考虑哪些问题?
接口设计需关注参数校验、扩展性、幂等性、日志、线程池隔离、异常重试、异步处理、查询优化、限流、安全性、锁粒度和避免长事务。入参与返回值校验确保数据正确性;考虑接口扩展性以适应不同业务需求;幂等设计防止重复操作;关键接口打印日志辅助问题排查;核心接口使用线程池隔离确保稳定性;异常处理中可采用重试机制,注意超时控制;适合异步的场景如用户注册后的通知;并行查询提升性能;限流保护接口,防止过载;配置黑白名单保障安全;适当控制锁粒度提高并发性能;避免长事务影响系统响应。
|
6月前
|
SQL XML JSON
Hasor【部署 04】Dataway接口配置服务扩展能力实例分享
Hasor【部署 04】Dataway接口配置服务扩展能力实例分享
105 0
|
程序员 C++
论接口的封装能力
论接口的封装能力
49 0
|
SQL 消息中间件 缓存
12种接口优化的通用方案
12种接口优化的通用方案
233 0
|
算法 安全 网络协议
如何设计一个安全的对外接口 ?
最近有个项目需要对外提供一个接口,提供公网域名进行访问,而且接口和交易订单有关,所以安全性很重要;这里整理了一下常用的一些安全措施以及具体如何去实现。
111 0
|
SQL 存储 缓存
18种接口实用优化方案总结
18种接口实用优化方案总结
207 0