【架构师成长之路】5-零基础搭建单体项目-抽象CRUD公共方法

5.抽象CRUD公共方法,并自动生成接口文档

后台管理系统的CRUD方法都有公共的相同之处,那我们可以写一个抽象方法来继承实现自动CRUD

首先

创建一个BaseManageCrudController.java 公共接口

package com.yy.youbimanage.base;

import com.yy.youbimanage.pojo.PageDto;
import com.yy.youbimanage.pojo.Result;

import java.util.List;

/**
 * @author chase
 * @description
 * @date 2021-12-04 13:50:45
 */
public interface BaseManageCrudController<D> {

    Result<D> info(Long id);

    Result<List<D>> list(D d);

    Result<PageDto<D>> page(PageDto pageDto, D d);

    Result save(D d);

    Result update(D d);

    Result delete(Long[] ids);


}

实现公共接口

package com.yy.youbimanage.base;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yy.youbimanage.annotation.Add;
import com.yy.youbimanage.annotation.Update;
import com.yy.youbimanage.exception.GlobalException;
import com.yy.youbimanage.pojo.PageDto;
import com.yy.youbimanage.pojo.Result;
import com.yy.youbimanage.utils.ArgsUtil;
import com.yy.youbimanage.utils.ConvertUtil;
import com.yy.youbimanage.utils.GenericUtil;
import com.yy.youbimanage.utils.PageDtoUtil;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @author chase
 * @description
 * @date 2021-12-04 13:36:58
 */
public abstract class AbstractManageCrudController<E, D> implements BaseManageCrudController<D> {

    protected ConvertUtil convertUtil = ConvertUtil.SINGLETON;
    protected GenericUtil genericUtil = GenericUtil.SINGLETON;
    protected ArgsUtil argsUtil = ArgsUtil.SINGLETON;
    @Autowired
    protected RedissonClient redissonClient;

    protected abstract AbstractCrudService<E> getService();

    protected abstract List<String> notAccessMethod();

    protected final Result ok() {
        return new Result().ok();
    }

    protected final Result ok(Object data) {
        return new Result().ok(data);
    }

    protected final Result fail() {
        return new Result().fail();
    }

    protected final Result fail(String msg) {
        return new Result().fail(msg);
    }

    protected final Result fail(Result.Response response) {
        return new Result().fail(response);
    }

    @Override
    @GetMapping(value = "/{id}")
    @ApiOperation(value = "查看")
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功"),
            @ApiResponse(code = 400, message = "非法请求"),
            @ApiResponse(code = 401, message = "尚未登录"),
            @ApiResponse(code = 403, message = "权限不足"),
            @ApiResponse(code = 500, message = "请求失败"),
    })
    public Result<D> info(@PathVariable Long id) {
        if (notAccessMethod().contains("info")) {
            throw new GlobalException("该地址禁止访问");
        }
        E entity = getService().getById(id);
        D dto = (D) convertUtil.sourceToTarget(entity, genericUtil.getClass(this, 1));
        return ok(dto);
    }

    @Override
    @GetMapping(value = "/list")
    @ApiOperation(value = "列表")
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功"),
            @ApiResponse(code = 400, message = "非法请求"),
            @ApiResponse(code = 401, message = "尚未登录"),
            @ApiResponse(code = 403, message = "权限不足"),
            @ApiResponse(code = 500, message = "请求失败"),
    })
    public Result<List<D>> list(D d) {
        if (notAccessMethod().contains("list")) {
            throw new GlobalException("该地址禁止访问");
        }
        E e = (E) convertUtil.sourceToTarget(d, genericUtil.getClass(this, 0));
        List<E> entityList = getService().getByList(e);
        List<D> dtoList = convertUtil.sourceToTarget(entityList, genericUtil.getClass(this, 1));
        return ok(dtoList);
    }

    @Override
    @GetMapping(value = "/page")
    @ApiOperation(value = "分页")
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功"),
            @ApiResponse(code = 400, message = "非法请求"),
            @ApiResponse(code = 401, message = "尚未登录"),
            @ApiResponse(code = 403, message = "权限不足"),
            @ApiResponse(code = 500, message = "请求失败"),
    })
    public Result<PageDto<D>> page(PageDto pageDto, D d) {
        if (notAccessMethod().contains("page")) {
            throw new GlobalException("该地址禁止访问");
        }
        E entity = (E) convertUtil.sourceToTarget(d, genericUtil.getClass(this, 0));
        Page<E> entityPage = getService().getByPage(new Page<E>(pageDto.getCurrentPage(), pageDto.getPageSize()), entity);
        PageDto<D> resultPage = PageDtoUtil.pageToPageDto(entityPage);
        resultPage.setData(convertUtil.sourceToTarget(entityPage.getRecords(), genericUtil.getClass(this, 1)));
        return ok(resultPage);
    }

    @Override
    @PostMapping
    @ApiOperation(value = "新增")
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功"),
            @ApiResponse(code = 400, message = "非法请求"),
            @ApiResponse(code = 401, message = "尚未登录"),
            @ApiResponse(code = 403, message = "权限不足"),
            @ApiResponse(code = 500, message = "请求失败"),
    })
    public Result save(@Validated(value = Add.class) @RequestBody D d) {
        if (notAccessMethod().contains("save")) {
            throw new GlobalException("该地址禁止访问");
        }
        E entity = (E) convertUtil.sourceToTarget(d, genericUtil.getClass(this, 0));
        entity = getService().save(entity);
        D dto = (D) convertUtil.sourceToTarget(entity, genericUtil.getClass(this, 1));
        return ok(dto);
    }

    @Override
    @PutMapping
    @ApiOperation(value = "修改")
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功"),
            @ApiResponse(code = 400, message = "非法请求"),
            @ApiResponse(code = 401, message = "尚未登录"),
            @ApiResponse(code = 403, message = "权限不足"),
            @ApiResponse(code = 500, message = "请求失败"),
    })
    public Result update(@Validated(value = Update.class) @RequestBody D d) {
        if (notAccessMethod().contains("update")) {
            throw new GlobalException("该地址禁止访问");
        }
        E entity = (E) convertUtil.sourceToTarget(d, genericUtil.getClass(this, 0));
        int row = getService().update(entity);
        return ok(row);
    }

    @Override
    @DeleteMapping
    @ApiOperation(value = "删除")
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功"),
            @ApiResponse(code = 400, message = "非法请求"),
            @ApiResponse(code = 401, message = "尚未登录"),
            @ApiResponse(code = 403, message = "权限不足"),
            @ApiResponse(code = 500, message = "请求失败"),
    })
    public Result delete(@RequestBody Long[] ids) {
        if (notAccessMethod().contains("delete")) {
            throw new GlobalException("该地址禁止访问");
        }
        getService().delete(ids);
        return ok();
    }

}

创建一个分页的pageDto类

package com.chase.frame.base;

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

import java.io.Serializable;
import java.util.List;

/**
 * @auor chase
 * @date 2021-08-16 20:53:42
 * @apiNote
 */
@Data
@ApiModel(value = "分页Dto")
public class PageDto<T> implements Serializable {

    @ApiModelProperty(value = "页")
    private int currentPage;
    @ApiModelProperty(value = "行")
    private int pageSize;
    @ApiModelProperty(value = "总条数")
    private int total;
    @ApiModelProperty(value = "总页数")
    private int totalPage;
    @ApiModelProperty(value = "结果")
    private List<T> data;

    public PageDto() {
        this.currentPage = 1;
        this.pageSize = 10;
    }
}

对象转换工具类

package com.chase.frame.tools;

import cn.hutool.core.bean.BeanUtil;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author fengbiao
 * @apiNote entity转dto工具类
 * @date 2021/6/9 10:28
 */
public enum ConvertUtil {

    SINGLETON;

    /**
     * 将单个类转换成另外一种类
     *
     * @param source
     * @param target
     * @param <T>
     * @return
     */
    public <T> T sourceToTarget(Object source, Class<T> target) {
        if (source == null) {
            return null;
        }
        T targetObject = null;
        try {
            targetObject = target.newInstance();
            BeanUtil.copyProperties(source, targetObject);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return targetObject;
    }

    /**
     * 将多个类转换成另外一种类
     *
     * @param sourceList
     * @param target
     * @param <T>
     * @return
     */
    public <T> List<T> sourceToTarget(Collection<?> sourceList, Class<T> target) {
        if (sourceList == null) {
            return null;
        }
        List<T> targetList = new ArrayList<>(sourceList.size());
        try {
            for (Object source : sourceList) {
                T targetObject = target.newInstance();
                BeanUtil.copyProperties(source, targetObject);
                targetList.add(targetObject);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return targetList;
    }

}

package com.yy.youbimanage.utils;

import java.lang.reflect.ParameterizedType;

/**
 * @author liyang
 * @date 2021-08-18 19:23:03
 * @apiNote
 */
public enum GenericUtil {

    SINGLETON;

    public Class getClass(Object o, int index) {
        return (Class) (((ParameterizedType) o.getClass().getGenericSuperclass()).getActualTypeArguments()[index]);
    }

}

参数校验工具类

package com.chase.frame.tools;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author fengbiao
 * @apiNote 参数校验工具类
 * @date 2021/6/9 10:37
 */
public enum ArgsUtil {

    SINGLETON;

    /**
     * 检验参数是否为Null
     *
     * @param o
     * @return
     */
    @SuppressWarnings(value = "all")
    public boolean argsIsNull(Object... o) {
        for (Object index : o) {
            if (index == null) {
                return true;
            }
            if (index instanceof String) {
                if (((String) index).trim().isEmpty()) {
                    return true;
                }
            }
            if (index instanceof List) {
                if (((List) index).isEmpty()) {
                    return true;
                }
            }
            if (index instanceof Set) {
                if (((Set) index).isEmpty()) {
                    return true;
                }
            }
            if (index instanceof Map) {
                if (((Map) index).isEmpty()) {
                    return true;
                }

            }
        }
        return false;
    }

}

page工具类

package com.chase.frame.tools;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chase.frame.base.PageDto;

/**
 * @author chase
 * @description
 * @date 2021-12-10 14:41:42
 */
public class PageDtoUtil {

    public static PageDto pageToPageDto(Page page) {
        PageDto<?> pageDto = new PageDto<>();
        pageDto.setCurrentPage((int) page.getCurrent());
        pageDto.setPageSize((int) page.getSize());
        pageDto.setTotal((int) page.getTotal());
        pageDto.setTotalPage((int) page.getPages());
        return pageDto;
    }

}

全局异常处理

package com.chase.frame.exception;

import com.chase.frame.base.Result;

/**
 * @author chase
 * @description
 * @date 2021-12-04 10:56:12
 */
public class GlobalException extends RuntimeException {

    private String msg;
    private Integer code;

    public GlobalException(String message) {
        super(message);
    }

    public GlobalException(Result.Response response) {
        super(response.getMsg());
        this.code = response.getCode();
        this.msg = response.getMsg();
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

自定义注解类创建

package com.chase.frame.annotation;

/**
 * @author chase
 * @description
 * @date 2021-12-09 17:34:50
 */
public @interface Add {
}

package com.chase.frame.annotation;

/**
 * @author chase
 * @description
 * @date 2021-12-09 17:39:18
 */
public @interface Update {
}

创建service接口

package com.chase.frame.base;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import java.util.List;

/**
 * @author chase
 * @date 2021-08-17 19:23:30
 * @description CRUD的Service接口
 */
public interface BaseCrudService<E> {

    /**
     * 通过主键查询
     *
     * @param id
     * @return
     */
    E getById(Long id);

    /**
     * 通过构造器条件查询
     *
     * @param wrapper
     * @return
     */
    Integer count(Wrapper<E> wrapper);

    /**
     * 通过多个主键查询
     *
     * @param ids
     * @return
     */
    List<E> getByIds(Long[] ids);

    /**
     * 通过一个对象查询
     *
     * @param e
     * @param columns
     * @return
     */
    List<E> getByList(E e, String... columns);

    /**
     * 通过构造器条件查询
     *
     * @param wrapper
     * @return
     */
    List<E> getByList(Wrapper<E> wrapper);

    /**
     * 通过对象条件分页查询
     *
     * @param page
     * @param e
     * @param columns
     * @return
     */
    Page<E> getByPage(Page<E> page, E e, String... columns);

    /**
     * 通过构造器条件分页查询
     *
     * @param page
     * @param wrapper
     * @return
     */
    Page<E> getByPage(Page<E> page, Wrapper<E> wrapper);

    /**
     * 通过构造器查询
     *
     * @param wrapper
     * @return
     */
    E getByObj(Wrapper<E> wrapper);

    /**
     * 通过对象查询
     *
     * @param e
     * @return
     */
    E getByObj(E e);

    /**
     * 新增
     *
     * @param e
     * @return
     */
   E save(E e);

    /**
     * 修改
     *
     * @param e
     * @return
     */
    int update(E e);

    /**
     * 通过构造器修改
     *
     * @param e
     * @param uw
     * @return
     */
    int update(E e, Wrapper<E> uw);

    /**
     * 通过多个id删除
     *
     * @param ids
     * @return
     */
    int delete(Long[] ids);

    /**
     * 通过构造器删除
     *
     * @param wrapper
     * @return
     */
    int delete(Wrapper<E> wrapper);

}

package com.chase.frame.base;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;

/**
 * @author liyang
 * @apiNote
 * @date 2021-08-16 15:17:34
 */
public abstract class AbstractCrudService<E> implements BaseCrudService<E> {

    @Autowired
    protected RedissonClient redissonClient;

    /**
     * 获得具体实现BaseMapper
     *
     * @return
     */
    protected abstract BaseMapper<E> getMapper();

    @Override
    public E getById(Long id) {
        return getMapper().selectById(id);
    }

    @Override
    public Integer count(Wrapper<E> wrapper) {
        return getMapper().selectCount(wrapper);
    }

    @Override
    public List<E> getByIds(Long[] ids) {
        return getMapper().selectBatchIds(Arrays.asList(ids));
    }

    @Override
    public List<E> getByList(E e, String... columns) {
        QueryWrapper<E> queryWrapper = new QueryWrapper<>(e);
        if (columns != null && columns.length > 0) {
            queryWrapper.select(columns);
        }
        return getMapper().selectList(queryWrapper);
    }

    @Override
    public List<E> getByList(Wrapper<E> wrapper) {
        return getMapper().selectList(wrapper);
    }

    @Override
    public Page<E> getByPage(Page<E> page, E e, String... columns) {
        QueryWrapper<E> queryWrapper = new QueryWrapper<>(e);
        if (columns != null && columns.length > 0) {
            queryWrapper.select(columns);
        }
        return getMapper().selectPage(page, queryWrapper);
    }

    @Override
    public Page<E> getByPage(Page<E> page, Wrapper<E> wrapper) {
        return getMapper().selectPage(page, wrapper);
    }

    @Override
    public E getByObj(Wrapper<E> wrapper) {
        return getMapper().selectOne(wrapper);
    }

    @Override
    public E getByObj(E e) {
        QueryWrapper<E> queryWrapper = new QueryWrapper<>(e);
        return getMapper().selectOne(queryWrapper);
    }

    @Override
    @Transactional
    public E save(E e) {
        RLock lock = redissonClient.getLock(this.getClass().getSimpleName() + "Save");
        try {
            lock.lock();
            getMapper().insert(e);
        } catch (Exception ex) {
            throw ex;
        } finally {
            if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                lock.unlock();
            }
        }
        return e;
    }

    @Override
    @Transactional
    public int update(E e) {
        RLock lock = redissonClient.getLock(this.getClass().getSimpleName() + "Update");
        int row = 0;
        try {
            lock.lock();
            row = getMapper().updateById(e);
        } catch (Exception ex) {
            throw ex;
        } finally {
            if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                lock.unlock();
            }
        }
        return row;
    }

    @Override
    public int update(E e, Wrapper<E> uw) {
        RLock lock = redissonClient.getLock(this.getClass().getSimpleName() + "BatchUpdate");
        int row = 0;
        try {
            lock.lock();
            row = getMapper().update(e, uw);
        } catch (Exception ex) {
            throw ex;
        } finally {
            if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                lock.unlock();
            }
        }
        return row;
    }


    @Override
    @Transactional    public int delete(Long[] ids) {
        return getMapper().deleteBatchIds(Arrays.asList(ids));
    }

    @Override
    public int delete(Wrapper<E> wrapper) {
        return getMapper().delete(wrapper);
    }

}

都创建完成之后,测试一下

controller

package com.chase.frame.controller;

import com.chase.frame.base.AbstractCrudService;
import com.chase.frame.base.AbstractManageCrudController;
import com.chase.frame.model.dto.RuleDefineTypeDTO;
import com.chase.frame.model.entity.RuleDefineType;
import com.chase.frame.service.RuleDefineTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;

/**
 * @author chase
 * @Desc
 * @date 2022/8/18 16:26
 */
@RestController
@RequestMapping("/ruleDefineType")
public class RuleDefineTypeController extends AbstractManageCrudController<RuleDefineType, RuleDefineTypeDTO> {

    @Autowired
    private RuleDefineTypeService ruleDefineTypeService;

    @Override
    protected List<String> notAccessMethod() {
        return Arrays.asList();
    }

    @Override
    protected AbstractCrudService<RuleDefineType> getService() {
        return ruleDefineTypeService;
    }


}

service

package com.chase.frame.service;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chase.frame.base.AbstractCrudService;
import com.chase.frame.mapper.RuleDefineTypeMapper;
import com.chase.frame.model.entity.RuleDefineType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author chase
 * @Desc
 * @date 2022/8/29 16:37
 */
@Service
public class RuleDefineTypeService extends AbstractCrudService<RuleDefineType> {
    @Autowired
    private RuleDefineTypeMapper ruleDefineTypeMapper;

    @Override
    protected BaseMapper<RuleDefineType> getMapper() {
        return ruleDefineTypeMapper;
    }
}

对象类中写上接口文档的注解
在这里插入图片描述

自动生成对应的CRUD,分页接口
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值