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 的方法,以及获取服务和实体类型的方法。
具体方法:
- 获取服务
- 获取实体类型
- 成功返回
- 失败返回
- 参数校验失败返回
- 获取当前用户id
default修饰的方法不用实现:
在 Java 8 及以后的版本中,接口可以包含
default
修饰的方法。这些方法在接口中提供了一个默认实现,因此在实现接口时,子类可以选择是否覆盖这些default
方法。换句话说,子类可以直接使用接口中提供的默认实现,也可以根据需要覆盖它们。在
BaseController
接口中,许多方法都使用了default
修饰符。这意味着如果一个类实现了BaseController
接口,那么它可以选择是否要覆盖这些默认方法。如果不覆盖,类将使用接口中的默认实现。
Object... args
是可变参数,允许你传递零个或多个参数给一个方法,方法内部会将这些参数视为一个数组处理。
- 允许调用者传递不同数量的参数,而不需要重载方法。减少了编写多个重载方法的需要,简化了代码。
- 当你在方法中使用可变参数时,Java 会将这些参数打包成一个数组。在方法内部,你可以像处理普通数组一样处理这些参数。
interface SuperService
对于interface SuperService
,定义了一系列常用的业务层操作。这些操作包括对实体的增删改查、分页查询、批量操作等,提供了一个统一的接口来处理这些操作。
方法:
- 获取实体类类型:返回实体类的
Class
对象。通常用于反射操作或其他需要知道实体类类型的地方。 - 获取主键类型:返回主键的
Class
对象。通常用于反射操作或其他需要知道主键类型的地方。 - 获取管理器类型:返回管理器的
Class
对象。通常用于获取管理实体的逻辑。 - save:
SaveVO
是保存操作的视图对象。插入一条记录,选择性插入字段。返回插入后的实体对象。 - saveBatch:批量保存:
saveList
是要保存的实体集合。批量保存实体对象。返回是否保存成功的布尔值。 - copy:复制一条数据:
id
是要复制的实体的 ID。返回复制后的实体对象。如果存在唯一索引等限制条件,需要重写该方法进行处理。 - updateById:根据 ID 修改非空字段:参数
UpdateVO
是更新操作的视图对象。返回更新后的实体对象。 - updateAllById:根据 ID 修改所有字段:
UpdateVO
是更新操作的视图对象。返回更新后的实体对象。 - removeByIds:根据 ID 批量删除:
idList
是要删除的实体 ID 集合。返回是否删除成功的布尔值。 - getById:根据 ID 查询实体:
id
是要查询的实体 ID。返回查询到的实体对象或null
。 - list:根据查询条件查询实体列表(列表查询):
queryWrapper
是封装查询条件的对象。返回符合条件的实体列表或空列表。 - listByIds:批量查询:
ids
是要查询的实体 ID 列表。返回符合条件的实体列表或空列表。 - page:分页查询:
page
是分页对象,queryWrapper
是封装查询条件的对象。返回符合条件的实体分页对象。
这些方法提供了一个统一的接口,方便业务层对实体进行操作,提高了代码的可维护性和复用性。具体的实现类只需要实现这些方法即可完成常见的业务操作。
copy方法如果存在唯一索引等限制条件需要重写方法:
唯一索引是一种数据库约束,用于确保特定列中的数据是唯一的。例如,用户表中的
当你复制一条数据时,假设原数据如下:
ID Name 1 Alice alice@example.com 直接复制这条数据可能会产生如下数据:
ID Name 2 Alice alice@example.com 如果
为了避免违反唯一性约束,需要在复制数据时进行适当的处理,例如生成新的唯一字段值或进行其他逻辑处理。以下是一些重写
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); // 生成新的唯一字段值 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
接口,并且提供了一些基本的功能,主要用于获取注入的服务和实体类型。下面是对这个类的详细解释:
方法:
-
currentModelClass:通过反射获取当前类的实体类型
Class
对象。使用 MyBatis Plus 提供的
ReflectionKit.getSuperClassGenericType
方法,获取当前类 (this.getClass()
) 的第三个泛型参数的类型 (SuperSimpleController.class, 2
),也就是Entity
类型。 -
getEntityClass:返回当前类的实体类型
Class
对象。返回
entityClass
成员变量的值,该值在类初始化时通过currentModelClass
方法确定。 -
getSuperService:返回注入的服务对象。
返回
superService
成员变量的值,该值通过 Spring 的@Autowired
注解自动注入。
总结:
SuperSimpleController
提供了:
- 自动注入服务对象的能力。
- 获取当前类实体类型的能力。
- 统一的方法实现,减少代码重复。
abstract class SuperController
对于SuperController
,这个类继承了 SuperSimpleController
并实现了 SaveController
、UpdateController
、DeleteController
和 QueryController
接口,从而提供了基础的 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
,具体的业务控制器可以获得以下操作方法:
- 分页查询(page):支持子类扩展
handlerQueryParams
、query
、handlerWrapper
、handlerResult
方法。 - 保存(save):支持子类扩展
handlerSave
方法。 - 修改(update):支持子类扩展
handlerUpdate
方法。 - 删除(delete):支持子类扩展
handlerDelete
方法。 - 单体查询(get):根据 ID 直接查询数据库。
- 详情查询(detail):根据 ID 直接查询数据库。
- 列表查询(list):根据参数条件查询列表。
如果扩展方法无法满足需求,子类可以重写 page
、save
等方法,但应避免修改 @RequestMapping
参数,以确保路由配置的一致性。
主要代码:
resultVOClass
字段保存了ResultVO
的Class
对象,用于反射操作。currentResultVOClass()
:用于初始化resultVOClass
字段。通过反射获取ResultVO
的Class
对象。调用ReflectionKit.getSuperClassGenericType
方法,获取第 6 个泛型参数的类型。getResultVOClass()
:用于获取ResultVO
的Class
对象。返回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
:从请求体中获取保存参数,并进行验证。
- 调用
handlerSave(saveVO)
方法处理保存逻辑。 - 检查返回结果
result
的getDefExec()
方法是否返回true
,如果是,则调用getSuperService().save(saveVO)
保存实体,并返回成功结果。 - 如果
getDefExec()
返回false
,则直接返回result
。
copy()
:参数:@RequestParam("id") Id id
:从请求参数中获取 ID。
- 调用
getSuperService().copy(id)
方法复制实体,并返回成功结果。
handlerSave()
:提供一个自定义的保存处理方法。参数:SaveVO model
,保存对象。返回值默认返回 R.successDef()
,表示调用默认的保存方法。
- 子类可以重写该方法,以实现自定义的保存逻辑。如果返回值的
getDefExec()
方法返回false
,则不会调用默认的保存方法。
总结:SaveController
接口提供了一个标准的保存和复制操作,并允许子类通过重写 handlerSave
方法来自定义保存逻辑。通过使用 Spring MVC 注解和 Swagger 注解,可以方便地定义 RESTful API,并生成相应的 API 文档。WebLog
注解用于记录操作日志,以便进行操作追踪和调试。
abstract class SuperCacheController
对于abstract class SuperCacheController
,扩展了 SuperController
类,并在其基础上增加了缓存相关的方法。这些方法主要处理实体的缓存操作,如获取缓存数据、刷新缓存和清理缓存。
-
getSuperService()
重写了父类中的getSuperService
方法,返回SuperCacheService
实例。 -
get()
重写了父类中的get
方法。通过getSuperService().getByIdCache(id)
方法从缓存中获取实体对象,如果缓存中不存在,则查询数据库。使用
BeanPlusUtil.toBean(entity, getResultVOClass())
方法将实体对象转换为ResultVO
类型。返回封装好的响应结果
R<ResultVO>
。 -
refreshCache()
刷新缓存,通过getSuperService().refreshCache(ids)
方法刷新指定 ID 列表的缓存。 -
clearCache()
通过getSuperService().clearCache(ids)
方法清理指定 ID 列表的缓存。
总结:SuperCacheController
类在 SuperController
类的基础上扩展了缓存相关的方法,使得继承该类的子类不仅具备 SuperController
提供的基本 CRUD 功能,还能进行缓存操作,如获取缓存数据、刷新缓存和清理缓存。这使得开发人员在实现业务逻辑时,可以更加方便地管理实体缓存,提高系统的性能和响应速度。
----interface SuperCacheService
具体的增删改查在哪里实现的
class BaseOrgController
BaseOrgController
是一个前端控制器类,用于处理与组织(BaseOrg
)相关的请求。该类继承了 SuperCacheController
,因此它不仅具备 SuperController
提供的基本 CRUD 功能,还具备缓存操作的能力。该类通过一些额外的方法实现了特定于 BaseOrg
的业务逻辑。
代码:
-
echoService
:一个辅助服务,用于处理一些回显操作。 -
使用
@Parameters
注解定义多个参数,用于生成 Swagger 文档。使用
@Operation
注解描述 API 方法。 -
方法
check
调用superService.check(name, parentId, id)
检查名称是否可用,并返回结果。 -
方法
tree
调用superService.tree(pageQuery)
按树结构查询地区,并返回结果。 -
方法
saveOrgRole
调用superService.saveOrgRole(orgRoleSaveVO)
给机构分配角色,并返回结果。 -
方法
findOrgRoleByOrgId
调用superService.findOrgRoleByOrgId(orgId)
查询机构的角色,并返回结果。 -
方法
findCompanyByEmployeeId
调用superService.findCompanyByEmployeeId(employeeId)
查询员工的公司,并返回结果。 -
方法
findDeptByEmployeeId
调用superService.findDeptByEmployeeId(employeeId, companyId)
查询员工在指定公司下的所有部门,并返回结果。
总结:BaseOrgController
类通过继承 SuperCacheController
类,并在其基础上扩展了一些与 BaseOrg
相关的特定业务逻辑方法,如检查名称是否可用、按树结构查询地区、给机构分配角色等。这些方法结合 EchoService
和 BaseOrgService
实现了对组织数据的丰富操作,增强了控制器的功能和灵活性。
这个为什么不使用自动注入呢:private final EchoService echoService;
使用
final
字段和@RequiredArgsConstructor
注解来注入依赖项是一种更推荐的做法,因为它确保了这些依赖项在对象构造时被初始化,并且不会在之后被更改。这种方式也有助于保持代码的不可变性,减少潜在的并发问题和意外修改。详细解释:
自动注入的方式(
@Autowired
)@Autowired private EchoService echoService;
这种方式使用了 Spring 的依赖注入功能,将
EchoService
的实例注入到BaseOrgController
中。这种方式比较常见,但有几个缺点:
- 可变性:字段可以在运行时被修改,这可能会导致一些意外的错误。
- 依赖不透明:如果字段依赖项非常多,类的依赖关系可能不够明确。
- 测试复杂性:在单元测试中,需要使用反射或其他手段注入模拟对象,增加了测试的复杂性。
构造函数注入的方式(
final
字段和@RequiredArgsConstructor
注解)@RequiredArgsConstructor public class BaseOrgController { private final EchoService echoService; }
这种方式使用了 Lombok 的
@RequiredArgsConstructor
注解,自动生成一个包含所有final
字段的构造函数。构造函数注入有以下优点:
- 不可变性:
final
字段只能在构造函数中赋值,保证了依赖项的不可变性。- 清晰的依赖关系:通过构造函数参数,可以明确地看到类的所有依赖关系。
- 更易测试:在单元测试中,可以通过构造函数轻松注入模拟对象,不需要依赖反射或框架的特殊功能。
示例代码
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
类,并且在类的继承层次结构中正确地指定了泛型参数。