lamplamp的封装学习

学习lamplamp的封装方法

BaseOrgController

源代码

BaseOrgController

package top.tangyh.lamp.base.controller.user;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import top.tangyh.basic.annotation.log.WebLog;
import top.tangyh.basic.base.R;
import top.tangyh.basic.base.controller.SuperCacheController;
import top.tangyh.basic.interfaces.echo.EchoService;
import top.tangyh.lamp.base.entity.user.BaseOrg;
import top.tangyh.lamp.base.service.user.BaseOrgService;
import top.tangyh.lamp.base.vo.query.user.BaseOrgPageQuery;
import top.tangyh.lamp.base.vo.result.user.BaseOrgResultVO;
import top.tangyh.lamp.base.vo.save.user.BaseOrgRoleRelSaveVO;
import top.tangyh.lamp.base.vo.save.user.BaseOrgSaveVO;
import top.tangyh.lamp.base.vo.update.user.BaseOrgUpdateVO;

import java.util.List;

import static top.tangyh.lamp.common.constant.SwaggerConstants.DATA_TYPE_LONG;
import static top.tangyh.lamp.common.constant.SwaggerConstants.DATA_TYPE_STRING;


/**
 * <p>
 * 前端控制器
 * 组织
 * </p>
 *
 * @author zuihou
 * @date 2021-10-18
 */
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/baseOrg")
@Tag(name = "组织")
public class BaseOrgController extends SuperCacheController<BaseOrgService, Long, BaseOrg, BaseOrgSaveVO, BaseOrgUpdateVO, BaseOrgPageQuery, BaseOrgResultVO> {

    private final EchoService echoService;

    @Override
    public EchoService getEchoService() {
        return echoService;
    }


    @Parameters({
            @Parameter(name = "name", description = "name", required = true, schema = @Schema(type = DATA_TYPE_STRING), in = ParameterIn.QUERY),
            @Parameter(name = "parentId", description = "parentId", schema = @Schema(type = DATA_TYPE_LONG), in = ParameterIn.QUERY),
            @Parameter(name = "id", description = "ID", schema = @Schema(type = DATA_TYPE_LONG), in = ParameterIn.QUERY),
    })
    @Operation(summary = "检测名称是否可用")
    @GetMapping("/check")
    public R<Boolean> check(@RequestParam String name, @RequestParam Long parentId, @RequestParam(required = false) Long id) {
        return success(superService.check(name, parentId, id));
    }

    /**
     * 按树结构查询地区
     *
     * @param pageQuery 查询参数
     * @return 查询结果
     */
    @Operation(summary = "按树结构查询地区")
    @PostMapping("/tree")
    @WebLog("级联查询地区")
    public R<List<BaseOrgResultVO>> tree(@RequestBody BaseOrgPageQuery pageQuery) {
        return success(superService.tree(pageQuery));
    }

    /**
     * 给机构分配角色
     *
     * @param orgRoleSaveVO 参数
     * @return 新增结果
     */
    @Operation(summary = "给机构分配角色", description = "给机构分配角色")
    @PostMapping("/orgRole")
    @WebLog("给机构分配角色")
    public R<List<Long>> saveOrgRole(@RequestBody BaseOrgRoleRelSaveVO orgRoleSaveVO) {
        return success(superService.saveOrgRole(orgRoleSaveVO));
    }

    /**
     * 查询机构的角色
     *
     * @param orgId 员工id
     * @return 新增结果
     */
    @Operation(summary = "查询机构的角色")
    @GetMapping("/findOrgRoleByOrgId")
    @WebLog("查询机构的角色")
    public R<List<Long>> findOrgRoleByOrgId(@RequestParam Long orgId) {
        return success(superService.findOrgRoleByOrgId(orgId));
    }

    @Operation(summary = "查询员工的公司")
    @GetMapping("/findCompanyByEmployeeId")
    public R<List<BaseOrg>> findCompanyByEmployeeId(@RequestParam("employeeId") Long employeeId) {
        return success(superService.findCompanyByEmployeeId(employeeId));
    }

    @Operation(summary = "查询员工{employeeId}的在指定公司{companyId}下的所有部门")
    @GetMapping("/findDeptByEmployeeId")
    public R<List<BaseOrg>> findDeptByEmployeeId(@RequestParam("employeeId") Long employeeId,
                                                 @RequestParam("companyId") Long companyId) {
        return success(superService.findDeptByEmployeeId(employeeId, companyId));
    }
}

SuperCacheController

package top.tangyh.basic.base.controller;

import io.swagger.v3.oas.annotations.Operation;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import top.tangyh.basic.annotation.log.WebLog;
import top.tangyh.basic.base.R;
import top.tangyh.basic.base.entity.SuperEntity;
import top.tangyh.basic.base.service.SuperCacheService;
import top.tangyh.basic.utils.BeanPlusUtil;

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

/**
 * SuperCacheController
 * <p>
 * 继承该类,在SuperController类的基础上扩展了以下方法:
 * 1,get : 根据ID查询缓存,若缓存不存在,则查询DB
 *
 * @author zuihou
 * @date 2020年03月06日11:06:46
 */
public abstract class SuperCacheController<S extends SuperCacheService<Id, Entity>,
        Id extends Serializable, Entity extends SuperEntity<Id>, SaveVO, UpdateVO, PageQuery, ResultVO>
        extends SuperController<S, Id, Entity, SaveVO, UpdateVO, PageQuery, ResultVO> {
    @Override
    public SuperCacheService<Id, Entity> getSuperService() {
        return superService;
    }

    /**
     * 查询
     *
     * @param id 主键id
     * @return 查询结果
     */
    @Override
    @WebLog("'查询:' + #id")
    public R<ResultVO> get(@PathVariable Id id) {
        Entity entity = getSuperService().getByIdCache(id);
        return success(BeanPlusUtil.toBean(entity, getResultVOClass()));
    }

    /**
     * 刷新缓存
     *
     * @return 是否成功
     */
    @Operation(summary = "刷新缓存", description = "刷新缓存")
    @PostMapping("refreshCache")
    @WebLog("'刷新缓存'")
    public R<Boolean> refreshCache(@RequestBody List<Long> ids) {
        getSuperService().refreshCache(ids);
        return success(true);
    }

    /**
     * 清理缓存
     *
     * @return 是否成功
     */
    @Operation(summary = "清理缓存", description = "清理缓存")
    @PostMapping("clearCache")
    @WebLog("'清理缓存'")
    public R<Boolean> clearCache(@RequestBody List<Long> ids) {
        getSuperService().clearCache(ids);
        return success(true);
    }
}

SuperController

package top.tangyh.basic.base.controller;

import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import top.tangyh.basic.base.entity.SuperEntity;
import top.tangyh.basic.base.service.SuperService;

import java.io.Serializable;

/**
 * SuperNoPoiController
 * <p>
 * 继承该类,就拥有了如下方法:
 * 1,page 分页查询,并支持子类扩展4个方法:handlerQueryParams、query、handlerWrapper、handlerResult
 * 2,save 保存,并支持子类扩展方法:handlerSave
 * 3,update 修改,并支持子类扩展方法:handlerUpdate
 * 4,delete 删除,并支持子类扩展方法:handlerDelete
 * 5,get 单体查询, 根据ID直接查询DB
 * 6,detail 单体详情查询, 根据ID直接查询DB
 * 7,list 列表查询,根据参数条件,查询列表
 * <p>
 * 若重写扩展方法无法满足,则可以重写page、save等方法,但切记不要修改 @RequestMapping 参数
 *
 * @param <S>      Service
 * @param <Id>     主键
 * @param <Entity> 实体
 * @author zuihou
 * @date 2020年03月06日11:06:46
 */
public abstract class SuperController<S extends SuperService<Id, Entity>, Id extends Serializable, Entity extends SuperEntity<Id>, SaveVO, UpdateVO, PageQuery, ResultVO>
        extends SuperSimpleController<S, Id, Entity>
        implements SaveController<Id, Entity, SaveVO>,
        UpdateController<Id, Entity, UpdateVO>,
        DeleteController<Id, Entity>,
        QueryController<Id, Entity, PageQuery, ResultVO> {
    protected Class<ResultVO> resultVOClass = currentResultVOClass();

    protected Class<ResultVO> currentResultVOClass() {
        return (Class<ResultVO>) ReflectionKit.getSuperClassGenericType(this.getClass(), SuperController.class, 6);
    }

    @Override
    public Class<ResultVO> getResultVOClass() {
        return this.resultVOClass;
    }
}

SuperSimpleController

package top.tangyh.basic.base.controller;

import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import org.springframework.beans.factory.annotation.Autowired;
import top.tangyh.basic.base.entity.SuperEntity;
import top.tangyh.basic.base.service.SuperService;

import java.io.Serializable;

/**
 * 简单的实现了BaseController,为了获取注入 Service 和 实体类型
 * <p>
 * 基类该类后,没有任何方法。
 * 可以让业务Controller继承 SuperSimpleController 后,按需实现 *Controller 接口
 *
 * @param <S>      Service
 * @param <Entity> 实体
 * @author zuihou
 * @date 2020年03月07日22:08:27
 */
public abstract class SuperSimpleController<S extends SuperService<Id, Entity>, Id extends Serializable, Entity extends SuperEntity<Id>>
        implements BaseController<Id, Entity> {
    @Autowired
    protected S superService;
    protected Class<Entity> entityClass = currentModelClass();

    protected Class<Entity> currentModelClass() {
        return (Class<Entity>) ReflectionKit.getSuperClassGenericType(this.getClass(), SuperSimpleController.class, 2);
    }

    @Override
    public Class<Entity> getEntityClass() {
        return this.entityClass;
    }

    @Override
    public SuperService<Id, Entity> getSuperService() {
        return superService;
    }


}

BaseController

package top.tangyh.basic.base.controller;

import top.tangyh.basic.base.R;
import top.tangyh.basic.base.entity.SuperEntity;
import top.tangyh.basic.base.service.SuperService;
import top.tangyh.basic.context.ContextUtil;
import top.tangyh.basic.exception.BizException;
import top.tangyh.basic.exception.code.BaseExceptionCode;

import java.io.Serializable;

/**
 * 基础接口
 *
 * @param <Entity> 实体
 * @author zuihou
 * @date 2020年03月07日21:56:32
 */
public interface BaseController<Id extends Serializable, Entity extends SuperEntity<Id>> {

    /**
     * 获取Service
     *
     * @return Service
     */
    SuperService<Id, Entity> getSuperService();

    /**
     * 获取实体的类型
     *
     * @return 实体的类型
     */
    Class<Entity> getEntityClass();

    /**
     * 成功返回
     *
     * @param data 返回内容
     * @param <T>  返回类型
     * @return R 成功
     */
    default <T> R<T> success(T data) {
        return R.success(data);
    }

    /**
     * 成功返回
     *
     * @return R.true
     */
    default R<Boolean> success() {
        return R.success();
    }

    /**
     * 失败返回
     *
     * @param msg 失败消息
     * @param <T> 返回类型
     * @return 失败
     */
    default <T> R<T> fail(String msg) {
        return R.fail(msg);
    }

    /**
     * 失败返回
     *
     * @param msg  失败消息
     * @param args 动态参数
     * @param <T>  返回类型
     * @return 失败
     */
    default <T> R<T> fail(String msg, Object... args) {
        return R.fail(msg, args);
    }

    /**
     * 失败返回
     *
     * @param code 失败编码
     * @param msg  失败消息
     * @param <T>  返回类型
     * @return 失败
     */
    default <T> R<T> fail(int code, String msg) {
        return R.fail(code, msg);
    }

    /**
     * 失败返回
     *
     * @param exceptionCode 失败异常码
     * @return 失败
     */
    default <T> R<T> fail(BaseExceptionCode exceptionCode) {
        return R.fail(exceptionCode);
    }

    /**
     * 失败返回
     *
     * @param exception 异常
     * @return 失败
     */
    default <T> R<T> fail(BizException exception) {
        return R.fail(exception);
    }

    /**
     * 失败返回
     *
     * @param throwable 异常
     * @return 失败
     */
    default <T> R<T> fail(Throwable throwable) {
        return R.fail(throwable);
    }

    /**
     * 参数校验失败返回
     *
     * @param msg 错误消息
     * @return 失败
     */
    default <T> R<T> validFail(String msg) {
        return R.validFail(msg);
    }

    /**
     * 参数校验失败返回
     *
     * @param msg  错误消息
     * @param args 错误参数
     * @return 失败
     */
    default <T> R<T> validFail(String msg, Object... args) {
        return R.validFail(msg, args);
    }

    /**
     * 参数校验失败返回
     *
     * @param exceptionCode 错误编码
     * @return 失败
     */
    default <T> R<T> validFail(BaseExceptionCode exceptionCode) {
        return R.validFail(exceptionCode);
    }

    /**
     * 获取当前id
     *
     * @return userId
     */
    default Long getUserId() {
        return ContextUtil.getUserId();
    }


}

SuperService

package top.tangyh.basic.base.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import top.tangyh.basic.base.entity.SuperEntity;
import top.tangyh.basic.base.manager.SuperManager;

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

/**
 * 业务层
 *
 * @param <Id>     ID
 * @param <Entity> 实体
 * @author zuihou
 * @date 2020年03月03日20:49:03
 */
public interface SuperService<Id extends Serializable, Entity extends SuperEntity<?>> {

    /**
     * 获取实体的类型
     *
     * @return 实体类class类型
     */
    Class<Entity> getEntityClass();

    /**
     * 获取主键的类型
     *
     * @return 主键class类型
     */
    Class<Id> getIdClass();

    /**
     * 获取Manager的类型
     *
     * @return Manager的class类型
     */
    SuperManager<Entity> getSuperManager();

    /**
     * 插入一条记录(选择字段,策略插入)
     *
     * @param entity 实体对象
     * @return 是否插入成功
     */
    <SaveVO> Entity save(SaveVO entity);

    /**
     * 批量保存
     *
     * @param saveList 实体集合
     * @return 是否执行成功
     */
    boolean saveBatch(List<Entity> saveList);

    /**
     * 复制一条数据
     * <p>
     * 注意:若该数据存在唯一索引等限制条件,需要重写该方法进行判断或处理。
     *
     * @param id ID
     * @return 复制后的实体
     */
    Entity copy(Id id);

    /**
     * 根据 ID 修改实体中非空的字段
     *
     * @param entity 实体对象
     * @return 是否修改成功
     */
    <UpdateVO> Entity updateById(UpdateVO entity);

    /**
     * 根据id修改 entity 的所有字段
     *
     * @param entity 实体对象
     * @return 是否修改成功
     */
    <UpdateVO> Entity updateAllById(UpdateVO entity);

    /**
     * 删除(根据ID 批量删除)
     *
     * @param idList 主键ID列表
     * @return 是否删除成功
     */
    boolean removeByIds(Collection<Id> idList);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     * @return 实体对象或null
     */
    Entity getById(Id id);

    /**
     * 查询列表
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @return 实体对象集合或空集合
     */
    List<Entity> list(Wrapper<Entity> queryWrapper);

    /**
     * 批量查询
     *
     * @param ids 主键
     * @return 实体对象集合或空集合
     */
    List<Entity> listByIds(List<Id> ids);


    /**
     * 翻页查询
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @return 实体分页对象
     */
    <E extends IPage<Entity>> E page(E page, Wrapper<Entity> queryWrapper);
}

R

package top.tangyh.basic.base;

import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import top.tangyh.basic.exception.BizException;
import top.tangyh.basic.exception.code.BaseExceptionCode;
import top.tangyh.basic.jackson.JsonUtil;

import java.util.HashMap;
import java.util.Map;


/**
 * @author zuihou
 * @date 2017-12-13 10:55
 */
@Getter
@Setter
@Accessors(chain = true)
@SuppressWarnings("ALL")
public class R<T> {
    public static final String DEF_ERROR_MESSAGE = "系统繁忙,请稍候再试";
    public static final String HYSTRIX_ERROR_MESSAGE = "请求超时,请稍候再试";
    public static final int SUCCESS_CODE = 0;
    public static final int FAIL_CODE = -1;
    public static final int TIMEOUT_CODE = -2;
    /**
     * 统一参数验证异常
     */
    public static final int VALID_EX_CODE = -9;
    public static final int OPERATION_EX_CODE = -10;
    /**
     * 调用是否成功标识,0:成功,-1:系统繁忙,此时请开发者稍候再试 详情见[ExceptionCode]
     */
    @Schema(description = "响应编码:0/200-请求处理成功")
    private int code;

    /**
     * 是否执行默认操作
     */
    @JsonIgnore
    private Boolean defExec = true;

    /**
     * 调用结果
     */
    @Schema(description = "响应数据")
    private T data;

    /**
     * 结果消息,如果调用成功,消息通常为空T
     */
    @Schema(description = "提示消息")
    private String msg = "ok";

    @Schema(description = "请求路径")
    private String path;
    /**
     * 附加数据
     */
    @Schema(description = "附加数据")
    private Map<Object, Object> extra;

    /**
     * 响应时间
     */
    @Schema(description = "响应时间戳")
    private long timestamp = System.currentTimeMillis();

    /**
     * 系统报错时,抛出的原生信息
     */
    @Schema(description = "异常消息")
    private String errorMsg = "";

    private R() {
        this.defExec = false;
        this.timestamp = System.currentTimeMillis();
    }

    public R(int code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
        this.defExec = false;
        this.timestamp = System.currentTimeMillis();
    }

    public R(int code, T data, String msg, String errorMsg) {
        this(code, data, msg);
        this.errorMsg = errorMsg;
    }

    public R(int code, T data, String msg, boolean defExec) {
        this(code, data, msg);
        this.defExec = defExec;
    }

    public static <E> R<E> result(int code, E data, String msg) {
        return new R<>(code, data, msg);
    }

    public static <E> R<E> result(int code, E data, String msg, String errorMsg) {
        return new R<>(code, data, msg, errorMsg);
    }

    /**
     * 请求成功消息
     *
     * @param data 结果
     * @return RPC调用结果
     */
    public static <E> R<E> success(E data) {
        return new R<>(SUCCESS_CODE, data, "ok");
    }

    public static R<Boolean> success() {
        return new R<>(SUCCESS_CODE, true, "ok");
    }


    public static <E> R<E> successDef(E data) {
        return new R<>(SUCCESS_CODE, data, "ok", true);
    }

    public static <E> R<E> successDef() {
        return new R<>(SUCCESS_CODE, null, "ok", true);
    }

    public static <E> R<E> successDef(E data, String msg) {
        return new R<>(SUCCESS_CODE, data, msg, true);
    }

    /**
     * 请求成功方法 ,data返回值,msg提示信息
     *
     * @param data 结果
     * @param msg  消息
     * @return RPC调用结果
     */
    public static <E> R<E> success(E data, String msg) {
        return new R<>(SUCCESS_CODE, data, msg);
    }

    /**
     * 请求失败消息
     *
     * @param msg
     * @return
     */
    public static <E> R<E> fail(int code, String msg) {
        return new R<>(code, null, (msg == null || msg.isEmpty()) ? DEF_ERROR_MESSAGE : msg);
    }

    public static <E> R<E> fail(int code, String msg, String errorMsg) {
        return new R<>(code, null, (msg == null || msg.isEmpty()) ? DEF_ERROR_MESSAGE : msg, errorMsg);
    }

    public static <E> R<E> fail(String msg) {
        return fail(OPERATION_EX_CODE, msg);
    }

    public static <E> R<E> fail(String msg, Object... args) {
        String message = (msg == null || msg.isEmpty()) ? DEF_ERROR_MESSAGE : msg;
        return new R<>(OPERATION_EX_CODE, null, String.format(message, args));
    }

    public static <E> R<E> fail(BaseExceptionCode exceptionCode) {
        return validFail(exceptionCode);
    }

    public static <E> R<E> fail(BizException exception) {
        if (exception == null) {
            return fail(DEF_ERROR_MESSAGE);
        }
        return new R<>(exception.getCode(), null, exception.getMessage(), exception.getMessage());
    }

    /**
     * 请求失败消息,根据异常类型,获取不同的提供消息
     *
     * @param throwable 异常
     * @return RPC调用结果
     */
    public static <E> R<E> fail(Throwable throwable) {
        String msg = throwable != null ? throwable.getMessage() : DEF_ERROR_MESSAGE;
        return fail(FAIL_CODE, msg, msg);
    }

    public static <E> R<E> validFail(String msg) {
        return new R<>(VALID_EX_CODE, null, (msg == null || msg.isEmpty()) ? DEF_ERROR_MESSAGE : msg);
    }

    public static <E> R<E> validFail(String msg, Object... args) {
        String message = (msg == null || msg.isEmpty()) ? DEF_ERROR_MESSAGE : msg;
        return new R<>(VALID_EX_CODE, null, String.format(message, args));
    }

    public static <E> R<E> validFail(BaseExceptionCode exceptionCode) {
        return new R<>(exceptionCode.getCode(), null,
                (exceptionCode.getMsg() == null || exceptionCode.getMsg().isEmpty()) ? DEF_ERROR_MESSAGE : exceptionCode.getMsg());
    }

    public static <E> R<E> timeout() {
        return fail(TIMEOUT_CODE, HYSTRIX_ERROR_MESSAGE);
    }


    public R<T> put(String key, Object value) {
        if (this.extra == null) {
            this.extra = new HashMap<>(16);
        }
        this.extra.put(key, value);
        return this;
    }

    public R<T> putAll(Map<Object, Object> extra) {
        if (this.extra == null) {
            this.extra = new HashMap<>(16);
        }
        this.extra.putAll(extra);
        return this;
    }

    /**
     * 逻辑处理是否成功
     *
     * @return 是否成功
     */
    public Boolean getIsSuccess() {
        return this.code == SUCCESS_CODE || this.code == 200;
    }

    @Override
    public String toString() {
        return JsonUtil.toJson(this);
    }
}

SuperCacheService

package top.tangyh.basic.base.service;

import org.springframework.lang.NonNull;
import top.tangyh.basic.base.entity.SuperEntity;
import top.tangyh.basic.model.cache.CacheKey;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;

/**
 * 基于MP的 IService 新增了3个方法: getByIdCache
 * 其中:
 * 1,getByIdCache 方法 会先从缓存查询,后从DB查询 (取决于实现类)
 * 2、SuperService 上的方法
 *
 * @param <Id>     ID
 * @param <Entity> 实体
 * @author zuihou
 * @date 2020年03月03日20:49:03
 */
public interface SuperCacheService<Id extends Serializable, Entity extends SuperEntity<?>>
        extends SuperService<Id, Entity> {

    /**
     * 根据id 先查缓存,再查db
     *
     * @param id 主键
     * @return 对象
     */
    Entity getByIdCache(Id id);

    /**
     * 根据 key 查询缓存中存放的id,缓存不存在根据loader加载并写入数据,然后根据查询出来的id查询 实体
     *
     * @param key    缓存key
     * @param loader 加载器
     * @return 对象
     */
    Entity getByKey(CacheKey key, Function<CacheKey, Object> loader);

    /**
     * 可能会缓存穿透
     *
     * @param ids    主键id
     * @param loader 回调
     * @return 对象集合
     */
    List<Entity> findByIds(@NonNull Collection<? extends Serializable> ids, Function<Collection<? extends Serializable>, Collection<Entity>> loader);

    /**
     * 刷新缓存
     *
     * @param ids 主键
     */
    void refreshCache(List<Long> ids);

    /**
     * 清理缓存
     *
     * @param ids 主键
     */
    void clearCache(List<Long> ids);
}

EchoService

package top.tangyh.basic.interfaces.echo;

/**
 * @author zuihou
 * @date 2021/9/12 12:03
 */
public interface EchoService {
    /**
     * 回显数据的3个步骤:(出现回显失败时,认真debug该方法)
     * <p>
     * 1. parse: 通过反射将obj的字段上标记了 @Echo 注解的字段解析出来, 封装到typeMap中
     * 2. load: 依次查询待回显的数据
     * 3. write: 将查询出来的结果 反射或put 到obj的 字段或echoMap 中
     * <p>
     * 注意:若对象中需要回显的字段之间出现循环引用,很可能发生异常,所以请保证不要出现循环引用!!!
     *
     * @param obj          需要回显的参数,支持 自定义对象(User)、集合(List<User>、Set<User>)、IPage
     * @param isUseCache   是否使用内存缓存
     * @param ignoreFields 忽略字段
     */
    void action(Object obj, boolean isUseCache, String... ignoreFields);

    /**
     * 回显数据的3个步骤:(出现回显失败时,认真debug该方法)
     * <p>
     * 1. parse: 通过反射将obj的字段上标记了 @Echo 注解的字段解析出来, 封装到typeMap中
     * 2. load: 依次查询待回显的数据
     * 3. write: 将查询出来的结果 反射或put 到obj的 字段或echoMap 中
     * <p>
     * 注意:若对象中需要回显的字段之间出现循环引用,很可能发生异常,所以请保证不要出现循环引用!!!
     *
     * @param obj          需要回显的参数,支持 自定义对象(User)、集合(List<User>、Set<User>)、IPage
     * @param ignoreFields 忽略字段
     */
    default void action(Object obj, String... ignoreFields) {
        this.action(obj, false, ignoreFields);
    }
}

---

BaseOrgService

package top.tangyh.lamp.base.service.user;

import top.tangyh.basic.base.service.SuperCacheService;
import top.tangyh.lamp.base.entity.user.BaseOrg;
import top.tangyh.lamp.base.vo.query.user.BaseOrgPageQuery;
import top.tangyh.lamp.base.vo.result.user.BaseOrgResultVO;
import top.tangyh.lamp.base.vo.save.user.BaseOrgRoleRelSaveVO;

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

/**
 * <p>
 * 业务接口
 * 组织
 * </p>
 *
 * @author zuihou
 * @date 2021-10-18
 */
public interface BaseOrgService extends SuperCacheService<Long, BaseOrg> {
    /**
     * 根据id查询待回显参数
     *
     * @param ids 唯一键(可能不是主键ID)
     * @return
     */
    Map<Serializable, Object> findByIds(Set<Serializable> ids);

    /**
     * 检测机构名称是否存在
     *
     * @param name     机构名称
     * @param parentId 父ID
     * @param id       机构id
     * @return
     */
    boolean check(String name, Long parentId, Long id);

    /**
     * 查询机构树
     *
     * @param query 参数
     * @return 机构树
     */
    List<BaseOrgResultVO> tree(BaseOrgPageQuery query);

    /**
     * 给机构分配角色
     *
     * @param orgRoleSaveVO 参数
     * @return 新增结果
     */
    List<Long> saveOrgRole(BaseOrgRoleRelSaveVO orgRoleSaveVO);

    /**
     * 查询机构的角色
     *
     * @param orgId 员工id
     * @return 新增结果
     */
    List<Long> findOrgRoleByOrgId(Long orgId);

    /**
     * 查询员工{employeeId}的在指定公司{companyId}下的所有部门
     *
     * @param employeeId 员工ID
     * @param companyId  公司ID
     * @return java.util.List<top.tangyh.lamp.base.entity.user.BaseOrg>
     * @author tangyh
     * @date 2022/10/26 10:59 PM
     * @create [2022/10/26 10:59 PM ] [tangyh] [初始创建]
     */
    List<BaseOrg> findDeptByEmployeeId(Long employeeId, Long companyId);


    /**
     * 查询员工的公司
     *
     * @param employeeId 员工ID
     * @return java.util.List<top.tangyh.lamp.model.entity.base.SysOrg>
     * @author tangyh
     * @date 2022/10/26 10:29 PM
     * @create [2022/10/26 10:29 PM ] [tangyh] [初始创建]
     */
    List<BaseOrg> findCompanyByEmployeeId(Long employeeId);

    /**
     * 查询 {companyList} 中id等于 {lastCompanyId} 的公司 或 部门
     *
     * @param orgList   公司 或 部门列表
     * @param lastOrgId 最后一次登录的公司ID 或 部门Id
     * @return top.tangyh.lamp.base.entity.user.BaseOrg
     * @author tangyh
     * @date 2022/10/26 10:27 PM
     * @create [2022/10/26 10:27 PM ] [tangyh] [初始创建]
     */
    BaseOrg getDefaultOrg(List<BaseOrg> orgList, Long lastOrgId);

    /**
     * 查询当前员工的所有部门或单位
     *
     * @param employeeId 员工id
     * @return
     */
    List<BaseOrg> findOrgByEmployeeId(Long employeeId);

    /**
     * 根据部门id,递归查询部门的上级公司id
     * @param deptId 部门id
     * @return
     */
    BaseOrg getCompanyByDeptId(Long deptId);
}

BaseOrgServiceImpl

package top.tangyh.lamp.base.service.user.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.tangyh.basic.base.service.impl.SuperCacheServiceImpl;
import top.tangyh.basic.database.mybatis.conditions.Wraps;
import top.tangyh.basic.database.mybatis.conditions.query.LbQueryWrap;
import top.tangyh.basic.interfaces.echo.EchoService;
import top.tangyh.basic.utils.ArgumentAssert;
import top.tangyh.basic.utils.TreeUtil;
import top.tangyh.lamp.base.entity.user.BaseEmployeeOrgRel;
import top.tangyh.lamp.base.entity.user.BaseOrg;
import top.tangyh.lamp.base.entity.user.BaseOrgRoleRel;
import top.tangyh.lamp.base.manager.user.BaseEmployeeOrgRelManager;
import top.tangyh.lamp.base.manager.user.BaseOrgManager;
import top.tangyh.lamp.base.manager.user.BaseOrgRoleRelManager;
import top.tangyh.lamp.base.service.user.BaseOrgService;
import top.tangyh.lamp.base.vo.query.user.BaseOrgPageQuery;
import top.tangyh.lamp.base.vo.result.user.BaseOrgResultVO;
import top.tangyh.lamp.base.vo.save.user.BaseOrgRoleRelSaveVO;
import top.tangyh.lamp.base.vo.save.user.BaseOrgSaveVO;
import top.tangyh.lamp.base.vo.update.user.BaseOrgUpdateVO;
import top.tangyh.lamp.common.cache.base.user.OrgRoleCacheKeyBuilder;
import top.tangyh.lamp.common.constant.DefValConstants;
import top.tangyh.lamp.model.enumeration.base.OrgTypeEnum;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 组织
 * </p>
 *
 * @author zuihou
 * @date 2021-10-18
 */
@Slf4j
@Service
@RequiredArgsConstructor

public class BaseOrgServiceImpl extends SuperCacheServiceImpl<BaseOrgManager, Long, BaseOrg>
        implements BaseOrgService {
    private final BaseEmployeeOrgRelManager baseEmployeeOrgRelManager;
    private final BaseOrgRoleRelManager baseOrgRoleRelManager;
    private final EchoService echoService;

    @Override
    public Map<Serializable, Object> findByIds(Set<Serializable> ids) {
        return superManager.findByIds(ids.stream().map(Convert::toLong).collect(Collectors.toSet()));
    }

    private void fillOrg(BaseOrg org) {
        if (org.getParentId() == null || org.getParentId() <= 0) {
            org.setParentId(DefValConstants.PARENT_ID);
            org.setTreePath(DefValConstants.TREE_PATH_SPLIT);
            org.setTreeGrade(DefValConstants.TREE_GRADE);
        } else {
            BaseOrg parent = this.superManager.getByIdCache(org.getParentId());
            ArgumentAssert.notNull(parent, "请正确填写父级组织");

            org.setTreeGrade(parent.getTreeGrade() + 1);
            org.setTreePath(TreeUtil.getTreePath(parent.getTreePath(), parent.getId()));
        }
    }

    @Override
    @Transactional(readOnly = true)
    public boolean check(String name, Long parentId, Long id) {
        ArgumentAssert.notEmpty(name, "请填写名称");
        LbQueryWrap<BaseOrg> wrap = Wraps.<BaseOrg>lbQ().eq(BaseOrg::getName, name).eq(BaseOrg::getParentId, parentId).ne(BaseOrg::getId, id);
        return superManager.count(wrap) > 0;
    }

    @Override
    protected <SaveVO> BaseOrg saveBefore(SaveVO saveVO) {
        BaseOrgSaveVO baseOrgSaveVO = (BaseOrgSaveVO) saveVO;
        ArgumentAssert.isFalse(check(baseOrgSaveVO.getName(), baseOrgSaveVO.getParentId(), null), StrUtil.format("组织[{}]已经存在", baseOrgSaveVO.getName()));
        BaseOrg baseOrg = super.saveBefore(baseOrgSaveVO);
        fillOrg(baseOrg);
        return baseOrg;
    }

    @Override
    protected <UpdateVO> BaseOrg updateBefore(UpdateVO updateVO) {
        BaseOrgUpdateVO baseOrgUpdateVO = (BaseOrgUpdateVO) updateVO;
        ArgumentAssert.isFalse(check(baseOrgUpdateVO.getName(), baseOrgUpdateVO.getParentId(), baseOrgUpdateVO.getId()), StrUtil.format("组织[{}]已经存在", baseOrgUpdateVO.getName()));
        BaseOrg baseOrg = super.updateBefore(baseOrgUpdateVO);
        fillOrg(baseOrg);
        return baseOrg;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByIds(Collection<Long> idList) {
        if (idList.isEmpty()) {
            return false;
        }
        long userCount = baseEmployeeOrgRelManager.count(Wraps.<BaseEmployeeOrgRel>lbQ().in(BaseEmployeeOrgRel::getOrgId, idList));
        ArgumentAssert.isFalse(userCount > 0, "您选择的组织下还存在用户,禁止删除!请先移除该组织下所有用户后在进行删除!");
        long childrenCount = superManager.count(Wraps.<BaseOrg>lbQ().in(BaseOrg::getParentId, idList));
        ArgumentAssert.isFalse(childrenCount > 0, "您选择的组织下还存在子组织,禁止删除!请先移除该组织下所有子组织后在进行删除!");

        boolean flag = superManager.removeByIds(idList);

        baseOrgRoleRelManager.deleteByOrg(idList);
        baseEmployeeOrgRelManager.deleteByOrg(idList);
        return flag;
    }

    @Override
    public List<BaseOrgResultVO> tree(BaseOrgPageQuery query) {
        List<BaseOrg> list = superManager.list(Wraps.<BaseOrg>lbQ()
                .like(BaseOrg::getName, query.getName()).eq(BaseOrg::getState, query.getState()).orderByAsc(BaseOrg::getSortValue));
        List<BaseOrgResultVO> treeList = BeanUtil.copyToList(list, BaseOrgResultVO.class);
        echoService.action(treeList);
        return TreeUtil.buildTree(treeList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> saveOrgRole(BaseOrgRoleRelSaveVO saveVO) {
        if (saveVO.getFlag() == null) {
            saveVO.setFlag(true);
        }

        baseOrgRoleRelManager.remove(Wraps.<BaseOrgRoleRel>lbQ().eq(BaseOrgRoleRel::getOrgId, saveVO.getOrgId())
                .in(BaseOrgRoleRel::getRoleId, saveVO.getRoleIdList()));

        if (saveVO.getFlag() && CollUtil.isNotEmpty(saveVO.getRoleIdList())) {
            List<BaseOrgRoleRel> list = saveVO.getRoleIdList().stream()
                    .map(roleId -> BaseOrgRoleRel.builder()
                            .roleId(roleId).orgId(saveVO.getOrgId())
                            .build()).toList();
            baseOrgRoleRelManager.saveBatch(list);
        }

        cacheOps.del(OrgRoleCacheKeyBuilder.build(saveVO.getOrgId()));
        return findOrgRoleByOrgId(saveVO.getOrgId());
    }

    @Override
    @Transactional(readOnly = true)
    public List<Long> findOrgRoleByOrgId(Long orgId) {
        return baseOrgRoleRelManager.listObjs(Wrappers.<BaseOrgRoleRel>lambdaQuery()
                        .select(BaseOrgRoleRel::getRoleId)
                        .eq(BaseOrgRoleRel::getOrgId, orgId),
                Convert::toLong
        );
    }


    @Override
    @Transactional(readOnly = true)
    public List<BaseOrg> findDeptByEmployeeId(Long employeeId, Long companyId) {
        // 员工所属的机构 ID (可能含有单位或部门)
        List<Long> orgIdList = baseEmployeeOrgRelManager.findOrgIdByEmployeeId(employeeId);
        // 员工所属的机构 实体类
        List<BaseOrg> orgList = findByIds(orgIdList, null);

        /*
         * 有可能 companyId 为空,但 orgIdList 不为空
         * 原因: 在维护机构数据时, 没有将 部门 挂在 单位 下,而是直接将 部门 作为根节点,并挂载 子部门。
         */

        return orgList.stream()
                // 只查找部门
                .filter(item -> OrgTypeEnum.DEPT.eq(item.getType()))
                // 限定查找 companyId 的下级部门
                .filter(item -> companyId == null || StrUtil.contains(item.getTreePath(), TreeUtil.buildTreePath(companyId)))
                .toList();
    }


    @Override
    public BaseOrg getDefaultOrg(List<BaseOrg> orgList, Long lastOrgId) {
        if (CollUtil.isEmpty(orgList)) {
            return null;
        }
        BaseOrg sysOrg = null;
        if (lastOrgId != null) {
            sysOrg = orgList.stream().filter(item -> lastOrgId.equals(item.getId())).findFirst().orElse(null);
        }
        if (sysOrg == null && !orgList.isEmpty()) {
            sysOrg = orgList.get(0);
        }
        return sysOrg;
    }


    @Override
    @Transactional(readOnly = true)
    public List<BaseOrg> findCompanyByEmployeeId(Long employeeId) {
        // 下文中提到的机构:指 base_org 中的数据,无论它的 type 为单位或部门

        // 员工所属的机构 ID
        List<Long> orgIdList = baseEmployeeOrgRelManager.findOrgIdByEmployeeId(employeeId);
        // 员工所属的机构 实体类
        List<BaseOrg> orgList = findByIds(orgIdList, null);

        // 员工所属的机构的所有上级ID
        List<Long> parentIdList = orgList.stream()
                .map(item -> StrUtil.splitToArray(item.getTreePath(), DefValConstants.TREE_PATH_SPLIT))
                // 数组流 转 字符串流
                .flatMap(Arrays::stream)
                .distinct()
                // 去除空数据
                .filter(ObjectUtil::isNotEmpty)
                .map(Convert::toLong)
                // 类型转换
                .toList();

        // 员工所属的机构 以及 上级机构

        List<BaseOrg> sysOrgList = new ArrayList<>(orgList);
        if (CollUtil.isNotEmpty(parentIdList)) {
            List<BaseOrg> parentList = superManager.findByIds(parentIdList, null);
            sysOrgList.addAll(parentList);
        }

        // 员工所属的 单位或上级单位
        List<BaseOrg> companyList = new ArrayList<>();
        Set<Long> companyIdSet = new HashSet<>();
        for (BaseOrg sysOrg : sysOrgList) {
            if (OrgTypeEnum.COMPANY.eq(sysOrg.getType()) && !companyIdSet.contains(sysOrg.getId())) {
                companyList.add(sysOrg);
                companyIdSet.add(sysOrg.getId());
            }
        }
        return companyList;
    }

    @Override
    @Transactional(readOnly = true)
    public List<BaseOrg> findOrgByEmployeeId(Long employeeId) {
        // 员工所属的机构 ID
        List<Long> orgIdList = baseEmployeeOrgRelManager.findOrgIdByEmployeeId(employeeId);
        // 员工所属的机构 实体类
        return findByIds(orgIdList, null);
    }

    @Override
    @Transactional(readOnly = true)
    public BaseOrg getCompanyByDeptId(Long deptId) {
        if (deptId == null) {
            return null;
        }
        BaseOrg org = superManager.getByIdCache(deptId);
        if (org == null) {
            return null;
        }
        if (OrgTypeEnum.COMPANY.eq(org.getType())) {
            return org;
        }
        return getCompanyByDeptId(org.getParentId());
    }
}

ChatGPT

interface BaseController

对于interface BaseController,其提供了一些常见的控制器方法。这些方法包括成功和失败的返回方法,获取当前用户 ID 的方法,以及获取服务和实体类型的方法。

具体方法:

  1. 获取服务
  2. 获取实体类型
  3. 成功返回
  4. 失败返回
  5. 参数校验失败返回
  6. 获取当前用户id

default修饰的方法不用实现:

在 Java 8 及以后的版本中,接口可以包含 default 修饰的方法。这些方法在接口中提供了一个默认实现,因此在实现接口时,子类可以选择是否覆盖这些 default 方法。换句话说,子类可以直接使用接口中提供的默认实现,也可以根据需要覆盖它们。

BaseController 接口中,许多方法都使用了 default 修饰符。这意味着如果一个类实现了 BaseController 接口,那么它可以选择是否要覆盖这些默认方法。如果不覆盖,类将使用接口中的默认实现。

Object... args是可变参数,允许你传递零个或多个参数给一个方法,方法内部会将这些参数视为一个数组处理。

  1. 允许调用者传递不同数量的参数,而不需要重载方法。减少了编写多个重载方法的需要,简化了代码。
  2. 当你在方法中使用可变参数时,Java 会将这些参数打包成一个数组。在方法内部,你可以像处理普通数组一样处理这些参数。

interface SuperService

对于interface SuperService,定义了一系列常用的业务层操作。这些操作包括对实体的增删改查、分页查询、批量操作等,提供了一个统一的接口来处理这些操作。

方法:

  1. 获取实体类类型:返回实体类的 Class 对象。通常用于反射操作或其他需要知道实体类类型的地方。
  2. 获取主键类型:返回主键的 Class 对象。通常用于反射操作或其他需要知道主键类型的地方。
  3. 获取管理器类型:返回管理器的 Class 对象。通常用于获取管理实体的逻辑。
  4. save:SaveVO 是保存操作的视图对象。插入一条记录,选择性插入字段。返回插入后的实体对象。
  5. saveBatch:批量保存:saveList 是要保存的实体集合。批量保存实体对象。返回是否保存成功的布尔值。
  6. copy:复制一条数据:id 是要复制的实体的 ID。返回复制后的实体对象。如果存在唯一索引等限制条件,需要重写该方法进行处理。
  7. updateById:根据 ID 修改非空字段:参数UpdateVO 是更新操作的视图对象。返回更新后的实体对象。
  8. updateAllById:根据 ID 修改所有字段:UpdateVO 是更新操作的视图对象。返回更新后的实体对象。
  9. removeByIds:根据 ID 批量删除:idList 是要删除的实体 ID 集合。返回是否删除成功的布尔值。
  10. getById:根据 ID 查询实体:id 是要查询的实体 ID。返回查询到的实体对象或 null
  11. list:根据查询条件查询实体列表(列表查询):queryWrapper 是封装查询条件的对象。返回符合条件的实体列表或空列表。
  12. listByIds:批量查询:ids 是要查询的实体 ID 列表。返回符合条件的实体列表或空列表。
  13. page:分页查询:page 是分页对象,queryWrapper 是封装查询条件的对象。返回符合条件的实体分页对象。

这些方法提供了一个统一的接口,方便业务层对实体进行操作,提高了代码的可维护性和复用性。具体的实现类只需要实现这些方法即可完成常见的业务操作。

copy方法如果存在唯一索引等限制条件需要重写方法:

唯一索引是一种数据库约束,用于确保特定列中的数据是唯一的。例如,用户表中的 email 字段通常会有唯一索引,以确保每个用户的邮箱地址是唯一的。

当你复制一条数据时,假设原数据如下:

ID Name Email
1 Alice alice@example.com

直接复制这条数据可能会产生如下数据:

ID Name Email
2 Alice alice@example.com

如果 Email 字段有唯一索引,那么插入这条复制的数据时会违反唯一性约束,因为两个记录的 Email 字段是相同的。

为了避免违反唯一性约束,需要在复制数据时进行适当的处理,例如生成新的唯一字段值或进行其他逻辑处理。以下是一些重写 copy 方法的示例:

  • 可以在复制数据时生成一个新的唯一字段值,例如生成一个新的 Email 地址:

    @Override
    public Entity copy(Id id) {
        // 获取原数据
        Entity original = getById(id);
        if (original == null) {
            throw new BizException("Data not found");
        }
    
        // 创建新的实体对象
        Entity copy = new Entity();
    
        // 复制属性
        BeanUtils.copyProperties(original, copy);
    
        // 生成新的唯一字段值
        copy.setEmail(original.getEmail() + ".copy");
    
        // 保存复制后的实体对象
        save(copy);
    
        return copy;
    }
    
  • 进行其他逻辑处理:

    可以在复制数据时进行其他逻辑处理,例如检查并调整其他约束条件:

    @Override
    public Entity copy(Id id) {
        Entity original = getById(id);
        if (original == null) {
            throw new BizException("Data not found");
        }
    
        Entity copy = new Entity();
        BeanUtils.copyProperties(original, copy);
    
        // 调整其他约束条件
        if (isDuplicate(copy)) {
            handleDuplicate(copy);
        }
    
        save(copy);
    
        return copy;
    }
    

abstract class SuperSimpleController

对于abstract class SuperSimpleController,它实现了 BaseController 接口,并且提供了一些基本的功能,主要用于获取注入的服务和实体类型。下面是对这个类的详细解释:

方法:

  1. currentModelClass:通过反射获取当前类的实体类型 Class 对象。

    使用 MyBatis Plus 提供的 ReflectionKit.getSuperClassGenericType 方法,获取当前类 (this.getClass()) 的第三个泛型参数的类型 (SuperSimpleController.class, 2),也就是 Entity 类型。

  2. getEntityClass:返回当前类的实体类型 Class 对象。

    返回 entityClass 成员变量的值,该值在类初始化时通过 currentModelClass 方法确定。

  3. getSuperService:返回注入的服务对象。

    返回 superService 成员变量的值,该值通过 Spring 的 @Autowired 注解自动注入。

总结:

SuperSimpleController 提供了:

  • 自动注入服务对象的能力。
  • 获取当前类实体类型的能力。
  • 统一的方法实现,减少代码重复。

abstract class SuperController

对于SuperController,这个类继承了 SuperSimpleController 并实现了 SaveControllerUpdateControllerDeleteControllerQueryController 接口,从而提供了基础的 CRUD 操作和分页查询功能。

首先,SuperController 类定义了多个泛型参数:

  • S:服务接口类型,必须继承 SuperService
  • Id:实体的 ID 类型,必须实现 Serializable 接口。
  • Entity:实体类型,必须继承 SuperEntity
  • SaveVO:保存操作的视图对象类型。
  • UpdateVO:更新操作的视图对象类型。
  • PageQuery:分页查询的视图对象类型。
  • ResultVO:查询结果的视图对象类型。

SuperController 类继承了 SuperSimpleController 并实现了以下接口:

  • SaveController<Id, Entity, SaveVO>:保存操作的接口。
  • UpdateController<Id, Entity, UpdateVO>:更新操作的接口。
  • DeleteController<Id, Entity>:删除操作的接口。
  • QueryController<Id, Entity, PageQuery, ResultVO>:查询操作的接口。

这些接口定义了相应的 CRUD 操作方法,而 SuperController 提供了默认实现,并支持扩展方法。

通过继承 SuperController,具体的业务控制器可以获得以下操作方法:

  1. 分页查询(page):支持子类扩展 handlerQueryParamsqueryhandlerWrapperhandlerResult 方法。
  2. 保存(save):支持子类扩展 handlerSave 方法。
  3. 修改(update):支持子类扩展 handlerUpdate 方法。
  4. 删除(delete):支持子类扩展 handlerDelete 方法。
  5. 单体查询(get):根据 ID 直接查询数据库。
  6. 详情查询(detail):根据 ID 直接查询数据库。
  7. 列表查询(list):根据参数条件查询列表。

如果扩展方法无法满足需求,子类可以重写 pagesave 等方法,但应避免修改 @RequestMapping 参数,以确保路由配置的一致性。

主要代码:

  1. resultVOClass 字段保存了 ResultVOClass 对象,用于反射操作。
  2. currentResultVOClass():用于初始化 resultVOClass 字段。通过反射获取 ResultVOClass 对象。调用 ReflectionKit.getSuperClassGenericType 方法,获取第 6 个泛型参数的类型。
  3. getResultVOClass():用于获取 ResultVOClass 对象。返回 resultVOClass 字段。

总的来说,SuperController 类提供了一个基础的控制器实现,封装了常见的 CRUD 操作和分页查询功能,并支持通过扩展方法进行定制。具体业务控制器可以通过继承这个类,快速实现常见操作,同时保留扩展和定制的灵活性。

interface SaveController

对于interface SaveController,定义了两个主要的操作:新增(save)和复制(copy),并提供了一个自定义的保存处理方法(handlerSave)。这个接口通过使用 Spring MVC 注解来定义 HTTP 请求处理方法,集成 Swagger 注解生成 API 文档,并使用自定义注解记录 Web 日志。

SaveController 接口定义了三个泛型参数:

  • Id:实体的 ID 类型,必须实现 Serializable 接口。
  • Entity:实体类型,必须继承 SuperEntity
  • SaveVO:保存操作的视图对象类型。

该接口继承了 BaseController 接口,继承了一些基础方法。

save():参数:@RequestBody @Validated SaveVO saveVO:从请求体中获取保存参数,并进行验证。

  1. 调用 handlerSave(saveVO) 方法处理保存逻辑。
  2. 检查返回结果 resultgetDefExec() 方法是否返回 true,如果是,则调用 getSuperService().save(saveVO) 保存实体,并返回成功结果。
  3. 如果 getDefExec() 返回 false,则直接返回 result

copy():参数:@RequestParam("id") Id id:从请求参数中获取 ID。

  1. 调用 getSuperService().copy(id) 方法复制实体,并返回成功结果。

handlerSave():提供一个自定义的保存处理方法。参数:SaveVO model,保存对象。返回值默认返回 R.successDef(),表示调用默认的保存方法。

  1. 子类可以重写该方法,以实现自定义的保存逻辑。如果返回值的 getDefExec() 方法返回 false,则不会调用默认的保存方法。

总结:SaveController 接口提供了一个标准的保存和复制操作,并允许子类通过重写 handlerSave 方法来自定义保存逻辑。通过使用 Spring MVC 注解和 Swagger 注解,可以方便地定义 RESTful API,并生成相应的 API 文档。WebLog 注解用于记录操作日志,以便进行操作追踪和调试。

abstract class SuperCacheController

对于abstract class SuperCacheController,扩展了 SuperController 类,并在其基础上增加了缓存相关的方法。这些方法主要处理实体的缓存操作,如获取缓存数据、刷新缓存和清理缓存。

  1. getSuperService()重写了父类中的 getSuperService 方法,返回 SuperCacheService 实例。

  2. get()重写了父类中的 get 方法。通过 getSuperService().getByIdCache(id) 方法从缓存中获取实体对象,如果缓存中不存在,则查询数据库。

    使用 BeanPlusUtil.toBean(entity, getResultVOClass()) 方法将实体对象转换为 ResultVO 类型。

    返回封装好的响应结果 R<ResultVO>

  3. refreshCache()刷新缓存,通过 getSuperService().refreshCache(ids) 方法刷新指定 ID 列表的缓存。

  4. clearCache()通过 getSuperService().clearCache(ids) 方法清理指定 ID 列表的缓存。

总结:SuperCacheController 类在 SuperController 类的基础上扩展了缓存相关的方法,使得继承该类的子类不仅具备 SuperController 提供的基本 CRUD 功能,还能进行缓存操作,如获取缓存数据、刷新缓存和清理缓存。这使得开发人员在实现业务逻辑时,可以更加方便地管理实体缓存,提高系统的性能和响应速度。

----interface SuperCacheService

具体的增删改查在哪里实现的

class BaseOrgController

BaseOrgController 是一个前端控制器类,用于处理与组织(BaseOrg)相关的请求。该类继承了 SuperCacheController,因此它不仅具备 SuperController 提供的基本 CRUD 功能,还具备缓存操作的能力。该类通过一些额外的方法实现了特定于 BaseOrg 的业务逻辑。

代码:

  1. echoService:一个辅助服务,用于处理一些回显操作。

  2. 使用 @Parameters 注解定义多个参数,用于生成 Swagger 文档。

    使用 @Operation 注解描述 API 方法。

  3. 方法 check 调用 superService.check(name, parentId, id) 检查名称是否可用,并返回结果。

  4. 方法 tree 调用 superService.tree(pageQuery) 按树结构查询地区,并返回结果。

  5. 方法 saveOrgRole 调用 superService.saveOrgRole(orgRoleSaveVO) 给机构分配角色,并返回结果。

  6. 方法 findOrgRoleByOrgId 调用 superService.findOrgRoleByOrgId(orgId) 查询机构的角色,并返回结果。

  7. 方法 findCompanyByEmployeeId 调用 superService.findCompanyByEmployeeId(employeeId) 查询员工的公司,并返回结果。

  8. 方法 findDeptByEmployeeId 调用 superService.findDeptByEmployeeId(employeeId, companyId) 查询员工在指定公司下的所有部门,并返回结果。

总结:BaseOrgController 类通过继承 SuperCacheController 类,并在其基础上扩展了一些与 BaseOrg 相关的特定业务逻辑方法,如检查名称是否可用、按树结构查询地区、给机构分配角色等。这些方法结合 EchoServiceBaseOrgService 实现了对组织数据的丰富操作,增强了控制器的功能和灵活性。

这个为什么不使用自动注入呢:private final EchoService echoService;

使用 final 字段和 @RequiredArgsConstructor 注解来注入依赖项是一种更推荐的做法,因为它确保了这些依赖项在对象构造时被初始化,并且不会在之后被更改。这种方式也有助于保持代码的不可变性,减少潜在的并发问题和意外修改。

详细解释:

自动注入的方式(@Autowired

@Autowired
private EchoService echoService;

这种方式使用了 Spring 的依赖注入功能,将 EchoService 的实例注入到 BaseOrgController 中。这种方式比较常见,但有几个缺点:

  1. 可变性:字段可以在运行时被修改,这可能会导致一些意外的错误。
  2. 依赖不透明:如果字段依赖项非常多,类的依赖关系可能不够明确。
  3. 测试复杂性:在单元测试中,需要使用反射或其他手段注入模拟对象,增加了测试的复杂性。

构造函数注入的方式(final 字段和 @RequiredArgsConstructor 注解)

@RequiredArgsConstructor
public class BaseOrgController {
    private final EchoService echoService;
}

这种方式使用了 Lombok 的 @RequiredArgsConstructor 注解,自动生成一个包含所有 final 字段的构造函数。构造函数注入有以下优点:

  1. 不可变性:final 字段只能在构造函数中赋值,保证了依赖项的不可变性。
  2. 清晰的依赖关系:通过构造函数参数,可以明确地看到类的所有依赖关系。
  3. 更易测试:在单元测试中,可以通过构造函数轻松注入模拟对象,不需要依赖反射或框架的特殊功能。

示例代码

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/baseOrg")
public class BaseOrgController extends SuperCacheController<BaseOrgService, Long, BaseOrg, BaseOrgSaveVO, BaseOrgUpdateVO, BaseOrgPageQuery, BaseOrgResultVO> {

    private final EchoService echoService;

    @Override
    public EchoService getEchoService() {
        return echoService;
    }

    // 其他方法...
}

在上面的代码中,EchoService 被声明为 final 字段,并且 BaseOrgController 类使用了 @RequiredArgsConstructor 注解。这确保了 EchoService 在对象创建时被正确初始化,并且之后不会被修改。这种方式既保证了依赖项的安全性,又提高了代码的可维护性和测试性。

结论

使用 final 字段和 @RequiredArgsConstructor 进行依赖注入是一种更优雅和安全的方式,相对于传统的 @Autowired 字段注入方法,它具有不可变性、依赖关系清晰和更易测试等优点。

BaseOrgService是在哪里注入的:

BaseOrgController 中,BaseOrgService 实际上是通过继承自 SuperCacheController 类来注入的。具体注入的过程通常是在 Spring 容器启动时由 Spring 自动完成的。

继承结构:

BaseOrgController` extends `SuperCacheController
SuperCacheController` extends `SuperController
SuperController` extends `SuperSimpleController

注入过程:

  • superService 字段是在 SuperSimpleController 类中通过 @Autowired 注解进行依赖注入的。
  • BaseOrgService 类型通过继承链(BaseOrgController -> SuperCacheController -> SuperController -> SuperSimpleController)传递到 superService 字段中。
  • Spring 容器在启动时会扫描这些类,并根据注解和配置自动注入 BaseOrgService 实现。

因此,BaseOrgService 被自动注入到 BaseOrgController 中是因为它继承了包含 @Autowired 注解的 SuperSimpleController 类,并且在类的继承层次结构中正确地指定了泛型参数。

posted @ 2024-07-15 08:15  燕子去了  阅读(57)  评论(0)    收藏  举报

Powered by .NET 8.0 on Kubernetes

我会翻山越岭,到每一个我想去的地方

...