企业级通用WEB脚手架应该这么写,让CRUD极致简化
发现共性
(台账页面)清一色的新增、编辑、查询、删除、导入导出功能
几乎清一色的台账都是查询、新增、编辑修改、删除、批量删除、批量新增、导入Excel、导入Excel、导出json、导入json等;如果是复杂关系的多表共同操作的台账就不能简化,还是得根据业务需求去老老实实一个接口一个接口的搬砖。。。。
如果你发现这类台账仅仅就一个表的数据,那么你还会写一堆增删改查,导入导出的接口花费一天至好几天吗?你是初级程序员这样做有必要很锻炼你的基本功,当你工作几年之后发现大量重复的操作会让你莫名其妙的失落和迷茫,然后全是体力活让你没劲和浪费时间,不如写个通用教授叫,几分钟搞定然后摸鱼。。。。
(后端接口)大多数都是三层架构(Controller/Service/Mapper)
不重复写三层架构不等于什么都不写,知识适当抽取共性,通过JAVA的多态机制和泛型思想去通用性自动适配和转化。
所以这三层每一个表每一个ORM映射实体都还是会去创建,只是核心业务抽取不会写里面的代码,只需要简单配置就自适应拥有各种CRUDE的功能
Mybatis-plus是一个很优秀的ORM框架,集成它你在项目里可以简化单表的Service层和Mapper层的CRUD开发,但是接口层他是没有提供的。所以我们的重点就是编写接口层的共性,只需要你的具体Controller去继承共性的通用接口,就自动拥有一系列功能,然后扩展Mybatisplus的Service接口,让其有更加丰富的功能自适应条件匹配,导入导入EXCEL或则JSON等。
实际项目中仍然是需要架构这些层的,但是里面东西重复写的就不需要重复写;
接口层例如:
看似什么都没写接口:其实打开Swagger会发现,它已经有很多通用接口了
具体实现
首先实体封装共性(逻辑删除,创建时间、修改时间、创建人、修改人、创建部门、租户)等清一色几乎所有表都有的数据,抽取到一个Base实体,其他业务表实体只填写业务字段,继承拥有
/**
* @author makeJava
*/
@Data
public class CommonTableInfo implements Serializable {
/**
* Description: 租户编号
*/
@Schema(title = "tenant_id",description = "租户编号")
@TableField(value = "tenant_id", fill = FieldFill.INSERT)
protected String tenantId;
/**
* Description: 创建部门
*/
@Schema(title = "create_dept",description = "创建部门")
@TableField(value = "create_dept", fill = FieldFill.INSERT)
protected Long createDept;
/**
* Description: 创建者
*/
@Schema(title = "create_by",description = "创建者")
@TableField(value = "create_by", fill = FieldFill.INSERT)
protected Long createBy;
/**
* Description: 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
@Schema(title = "create_time",description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
protected LocalDateTime createTime;
/**
* Description: 更新者
*/
@Schema(title = "update_by",description = "更新者")
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
protected Long updateBy;
/**
* Description: 更新时间
*/
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
@Schema(title = "update_time",description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
protected LocalDateTime updateTime;
@TableField(value = "del_flag")
@Schema(title = "del_flag",description = "删除标志(1代表存在 0代表删除)")
@TableLogic(delval = "0",value = "1")
protected boolean delFlag;
}
然后具体的台账表,只需要继承通用字段
通用Controller需要自动切换对应的Service实现去调用共性功能,根据泛型锁定实际使用的ORM实体
使用JAVA的抽象类作为通用基类,然后通过抽象方法调用子实现类的getService;获取对应的Service
/**
* @author makeJava
*/
@Slf4j
@SuppressWarnings("unused")
public abstract class BaseCommonController<T extends Serializable> {
public static final String XLSX = ".xlsx";
public abstract BaseCommonService<T> getThisService();
/**
* 分页查询数据
*
* @param limitRequest 查询实体
* @return 所有数据
*/
@PostMapping("/queryLimit")
@Operation(summary = "分页查询请求")
public Resp<Object> queryPage(@RequestBody BaseLimitRequest<T> limitRequest) {
// 先---执行扩展
extendBeforeByQueryLimit(limitRequest);
// 分页查询
IPage<T> page = getThisService().queryLimitPage(limitRequest);
// 封装返回结果集
BaseLimitResponse<T> data = BaseLimitResponse.getInstance(page.getRecords(), page.getTotal(), page.getPages(), limitRequest.getPageIndex(), limitRequest.getPageSize());
// 后---执行扩展||可以执行类型转化
Object object = extendAfterByQueryLimit(data);
return Resp.ok(object);
}
/**
* 分页查询扩展点--前置
*/
protected void extendBeforeByQueryLimit(BaseLimitRequest<T> limitRequest) {
}
/**
* 分页查询扩展点---后置
*/
protected Object extendAfterByQueryLimit(BaseLimitResponse<T> data) {
return data;
}
/**
* 通过主键查询单条数据
*
* @param id 主键
* @return 单条数据
*/
@GetMapping("/queryOne/{id}")
@Operation(summary = "查询ById")
@Parameter(name = "id", description = "id", in = ParameterIn.PATH)
public Resp<Object> selectOne(@PathVariable("id") Serializable id) {
T oneBody = getThisService().getById(id);
// 后置扩展--可能会有类型转化为前端view对象
Object viewOjb = extendAfterByOne(oneBody);
return Resp.ok(viewOjb);
}
/**
* Id查询扩展点---后置
*/
protected Object extendAfterByOne(T one) {
return one;
}
/**
* 新增数据
*
* @param obj 实体对象
* @return 新增结果
*/
@PostMapping("/save")
@Log(title = "租户", businessType = BusinessType.INSERT)
@Operation(summary = "新增数据")
public Resp<String> insert(@RequestBody T obj) {
// 新增--前置扩展
extendBeforeBySave(obj);
// 执行新增
getThisService().save(obj);
// 新增--后置扩展
extendAfterBySave(obj);
return Resp.ok("新增数据--成功");
}
// 新增前置扩展
protected void extendBeforeBySave(T obj) {
}
// 新增后置扩展
protected void extendAfterBySave(T obj) {
}
/**
* 批量新增数据
*
* @param list 实体对象
* @return 新增结果
*/
@PostMapping("/saveBatch")
@Log(title = "租户", businessType = BusinessType.INSERT)
@Operation(summary = "批量新增数据")
public Resp<String> insertBatch(@RequestBody List<T> list) {
// 批量新增前置--扩展
extendBeforeBySaveBatch(list);
getThisService().saveBatchByEasyBaseMapper(list);
return Resp.ok("批量新增数据--成功");
}
// 新增批量前置扩展
protected void extendBeforeBySaveBatch(List<T> list) {
}
/**
* 修改数据
*
* @param obj 实体对象
* @return 修改结果
*/
@PutMapping("/update")
@Log(title = "租户", businessType = BusinessType.UPDATE)
@Operation(summary = "修改数据")
public Resp<String> update(@RequestBody T obj) {
// 修改--前置扩展
extendBeforeByUpdate(obj);
// 执行修改
getThisService().updateById(obj);
// 修改--后置扩展
extendAfterByUpdate(obj);
return Resp.ok("修改数据--成功");
}
// 修改--前置扩展
protected void extendBeforeByUpdate(T obj) {
}
// 修改--后置扩展
protected void extendAfterByUpdate(T obj) {
}
/**
* 删除数据
*
* @param idList 主键结合
* @return 删除结果
*/
@DeleteMapping("/dels")
@Log(title = "租户", businessType = BusinessType.DELETE)
@Operation(summary = "删除数据")
public Resp<String> delete(@RequestParam("idList") List<Long> idList) {
// 删除--前置扩展
extendBeforeByDelete(idList);
// 删除
boolean delNumber = getThisService().removeByIds(idList);
// 删除--后置扩展
extendAfterByDelete(idList);
String executeMsg = "idList 是:" + idList;
return delNumber ? Resp.ok(executeMsg) : Resp.error(executeMsg);
}
// 删除--前置扩展
protected void extendBeforeByDelete(List<Long> idList) {
}
// 删除--后置扩展
protected void extendAfterByDelete(List<Long> idList) {
}
/**
* 是否支持Excel导入
*/
protected abstract boolean isSuperExcel();
/**
* Excel导入
*
* @return 导入结果
*/
@PostMapping("/excelInput")
@Log(title = "Excel导入通用", businessType = BusinessType.INSERT)
@Operation(summary = "Excel导入")
public Resp<SysFileInfo> excelInput(@RequestParam("file") MultipartFile multipartFile) {
if (isSuperExcel()) {
return getThisService().excelInput(multipartFile);
}
return Resp.error("不支持Excel导入");
}
/**
* 导出JSON模板
*/
@GetMapping("/export/json")
@Operation(summary = "JSON导出通用")
public void exportJson(HttpServletResponse response, T entity, BasePage pageQuery) throws IOException {
// 原始表数据--完整版|不要替换为vo|dto|bo等非原始对象
QueryWrapper<T> queryWrapper = getThisService().forQueryWhere(entity);
IPage<T> page = new Page<>(pageQuery.getPageIndex(), pageQuery.getPageSize());
List<T> dataList = getThisService().list(page, queryWrapper);
// 使用ObjectMapper将对象转换成JSON格式的字节数组
ObjectMapper objectMapper = new ObjectMapper();
byte[] data = objectMapper.writeValueAsBytes(dataList);
// 设置响应头信息
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment; filename=data.json");
response.setContentLength(data.length);
// 写出数据到响应体
response.getOutputStream().write(data);
response.getOutputStream().flush();
}
/**
* 导入JSON模板
*/
@PostMapping("/import/json")
@Operation(summary = "JSON导入通用")
public Resp<Boolean> importJson(@RequestParam("file") MultipartFile multipartFile) {
// 检查上传的文件是否为空
if (multipartFile.isEmpty()) {
throw new BusinessSelfException("上传的文件不能为空");
}
if (!ContentType.JSON.getValue().equals(multipartFile.getContentType())) {
throw new BusinessSelfException("上传的文件不是JSON格式的");
}
// 读取JSON文件里的内容到List
try (InputStream inputStream = multipartFile.getInputStream()) {
// 使用ObjectMapper解析JSON文件内容
ObjectMapper objectMapper = new ObjectMapper();
List<T> dataModels = objectMapper.readValue(inputStream,
objectMapper.getTypeFactory().constructCollectionType(List.class, getThisService().getEntityClass()));
getThisService().saveBatch(dataModels);
} catch (Exception e) {
// 记录错误日志,并抛出自定义异常,指示用户导入失败
log.error(e.getMessage(), e);
throw new BusinessSelfException("导入失败,请联系管理员");
}
return Resp.ok(Boolean.TRUE);
}
/**
* 文件下载并且失败的时候返回json(默认失败了会返回一个有部分数据的Excel)
*
* @since 2.1.1
*/
@PostMapping("/excelOutput")
@Operation(summary = "Excel导出通用")
public void downloadFailedUsingJson(@RequestBody BaseLimitRequest<T> limitRequest, HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
try {
if (!isSuperExcel()) {
throw new BusinessSelfException("不支持Excel导入");
}
// 分页查询
IPage<T> page = getThisService().queryLimitPage(limitRequest);
response.setHeader("Content-Disposition", "attachment; filename=example.xlsx");
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode(DateUtil.format(new Date(), DatePattern.CHINESE_DATE_PATTERN) + "导出Excel", StandardCharsets.UTF_8).replace("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + XLSX);
// 这里需要设置不关闭流
EasyExcelFactory.write(response.getOutputStream(), limitRequest.getRequestBody().getClass()).autoCloseStream(Boolean.FALSE).sheet("数据")
.doWrite(page.getRecords());
} catch (Exception e) {
log.error(e.getMessage(), e);
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Resp<Object> error = Resp.error("数据导出失败:" + e.getMessage());
response.getWriter().println(JSON.toJSONString(error));
}
}
}
Service既然都是通用式,那就加一层集成了Mybatisplus的Iservice自动单表CURD功能,然后再多一层导入导出等扩展
/**
* @author Administrator
*/
public interface BaseCommonService<T> extends GTBaseService<T> {
/**
* Description: 分页查询
*
* @param limitRequest 分页查询参数
* @author: makeJava
* @date: 2024-11-25 16:30:08
* @return:com.baomidou.mybatisplus.core.metadata.IPage
*/
IPage<T> queryLimitPage(BaseLimitRequest<T> limitRequest);
/**
* Description: 批量新增
*
* @param entityList entityList
* @author: makeJava
* @date: 2024-11-25 16:30:08
* @return:Integer
*/
Integer saveBatchByEasyBaseMapper(List<T> entityList);
/**
* Excel导入
*
* @param multipartFile 文件
* @return Resp
*/
default Resp<SysFileInfo> excelInput(MultipartFile multipartFile) {
// 检查上传的文件是否为空
if (multipartFile.isEmpty()) {
throw new IllegalArgumentException("上传的文件不能为空");
}
// EasyExcel 读取Excel文件里的内容到List
try (InputStream inputStream = multipartFile.getInputStream()) {
BaseMapper<T> mapper = getBaseMapper();
BaseExcelInputListener<T> listener = new BaseExcelInputListener<>(mapper);
// 使用EasyExcelFactory读取输入流中的数据,映射到SysUser类,并使用SysUserExcelInputListener监听并处理读取到的数据
EasyExcelFactory.read(inputStream, getEntityClass(), listener).sheet().doRead();
} catch (Exception e) {
// 记录错误日志,并抛出自定义异常,指示用户导入失败
throw new BusinessSelfException("用户导入失败,请联系管理员");
}
SysFileInfoService bean = SpringUtil.getBean(SysFileInfoService.class);
// 保存文件信息
return Resp.ok(bean.saveFileInfo(multipartFile));
}
}
自适应查询再加一层,可以加也可以合并
/**
* @author Administrator
*/
@SuppressWarnings("unused")
public interface GTBaseService<T> extends IService<T> {
String DEFAULT_TIME_KEY = "create_time";
String DEFAULT_LAMBDA_TIME_KEY = "createTime";
String DEFAULT_LOGIC_KEY = "delFlag";
int ASC = 1;
int DESC = 2;
/**
* Description: 分页对象构建
*
* @author: makeJava
*/
default IPage<T> getByPageParam(BaseLimitRequest<T> param) {
long pageIndex = param.getPageIndex();
long pageSize = param.getPageSize();
return new Page<>(pageIndex, pageSize);
}
/**
* Description: 分页对象构建
*
* @author: makeJava
*/
default IPage<T> getByPageParam(long pageIndex, long pageSize) {
return new Page<>(pageIndex, pageSize);
}
/**
* 构建整张表的全属性自适应的搜索条件-V1
*
* @param thisObj thisObj
* @param limitRequest limitRequest
* @return QueryWrapper
*/
default QueryWrapper<T> queryWrapperBuilder(JSONObject thisObj, BaseLimitRequest<T> limitRequest) {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
// 构建字段级条件构造器--自适应
buildQueryConditions(queryWrapper, thisObj, null);
// 构建时间级条件构造器--自适应
addTimeFilters(queryWrapper, limitRequest);
// 构建排序条件构造器--自适应
addOrderByClause(queryWrapper, limitRequest);
return queryWrapper;
}
/**
* 构建整张表的全属性自适应的搜索条件-V2 支持字段过滤
*
* @param thisObj thisObj
* @param limitRequest limitRequest
* @return QueryWrapper
*/
default QueryWrapper<T> queryWrapperBuilder(JSONObject thisObj, BaseLimitRequest<T> limitRequest, List<String> excludeCol) {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
// 构建字段级条件构造器--自适应
buildQueryConditions(queryWrapper, thisObj, excludeCol);
// 构建时间级条件构造器--自适应
addTimeFilters(queryWrapper, limitRequest);
// 构建排序条件构造器--自适应
addOrderByClause(queryWrapper, limitRequest);
return queryWrapper;
}
private void buildQueryConditions(QueryWrapper<T> queryWrapper, JSONObject thisObj, List<String> excludeCol) {
Set<Map.Entry<String, Object>> entries = thisObj.entrySet();
for (Map.Entry<String, Object> entry : entries) {
String key = entry.getKey();
if (key.equals(DEFAULT_LOGIC_KEY)) {
continue;
}
if (CollUtil.isNotEmpty(excludeCol) && excludeCol.contains(key)) {
continue;
}
String column = convertToSnakeCase(key);
Object value = entry.getValue();
if (value instanceof String) {
queryWrapper.like(getValueIsBoolean(value), column, value);
} else {
queryWrapper.eq(getValueIsBoolean(value), column, value);
}
}
}
private void addTimeFilters(QueryWrapper<T> queryWrapper, BaseLimitRequest<T> limitRequest) {
LocalDateTime beginTime = limitRequest.getBeginTime();
LocalDateTime endTime = limitRequest.getEndTime();
if (beginTime != null) {
queryWrapper.ge(getValueIsBoolean(beginTime), DEFAULT_TIME_KEY, beginTime);
}
if (endTime != null) {
queryWrapper.le(getValueIsBoolean(endTime), DEFAULT_TIME_KEY, endTime);
}
}
private void addOrderByClause(QueryWrapper<T> queryWrapper, BaseLimitRequest<T> limitRequest) {
if (limitRequest.isNeedOrderBy()) {
switch (limitRequest.getOrderByMethod()) {
case ASC:
queryWrapper.orderByAsc(limitRequest.getOrderByKeyName());
break;
case DESC:
queryWrapper.orderByDesc(limitRequest.getOrderByKeyName());
break;
default:
queryWrapper.orderByDesc(DEFAULT_TIME_KEY);
}
}
}
private String convertToSnakeCase(String key) {
StringBuilder column = new StringBuilder();
char[] chars = key.toCharArray();
for (char aChar : chars) {
if (Character.isUpperCase(aChar)) {
column.append("_");
}
column.append(Character.toLowerCase(aChar));
}
return column.toString();
}
default boolean getValueIsBoolean(Object object) {
if (object instanceof String) {
return StringUtils.isNotEmpty((CharSequence) object);
}
return !ObjectUtils.isEmpty(object);
}
/**
* 构建整张表的全属性自适应的搜索条件-V2
*
* @param thisObj thisObj
* @param limitRequest limitRequest
* @return QueryWrapper
*/
default LambdaQueryWrapper<T> lambdaQueryWrapperBuilder(T thisObj, BaseLimitRequest<T> limitRequest) {
LambdaQueryWrapper<T> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.setEntity(thisObj);
return queryWrapper;
}
/**
* 获取ORM的Session工厂
*
* @return SqlSessionFactory
*/
SqlSessionFactory getSqlSessionFactory();
/**
* 获取ORM的最终实例Mapper
*
* @return Mapper
*/
Class<?> getThisMapperClass();
/**
* 获取Spring的事务管理器
*
* @return TransactionTemplate
*/
TransactionTemplate getSpringTx();
/**
* 批量新增V2 支持版本:3.5.4 +
*
* @param list list
*/
default void enhancedBatchInsertion(List<T> list) {
MybatisBatch<T> mybatisBatch = new MybatisBatch<>(getSqlSessionFactory(), list);
MybatisBatch.Method<T> method = new MybatisBatch.Method<>(getThisMapperClass());
mybatisBatch.execute(method.insert());
}
/**
* 批量新增V3 支持版本:3.5.4 + 事务支持
*
* @param list list
*/
@SuppressWarnings("unused")
default void transactionBatchInsertion(List<T> list) {
TransactionTemplate springTx = getSpringTx();
springTx.execute((TransactionCallback<List<BatchResult>>) status -> {
MybatisBatch.Method<T> mapperMethod = new MybatisBatch.Method<>(getThisMapperClass());
// 执行批量插入
MybatisBatchUtils.execute(getSqlSessionFactory(), list, mapperMethod.insert());
throw new BusinessSelfException("出错了");
});
}
default QueryWrapper<T> forQueryWhere(T entity) {
JSONObject thisObj = JSONObject.from(entity);
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
// 构建字段级条件构造器--自适应
buildQueryConditions(queryWrapper, thisObj,null);
return queryWrapper;
}
}
Mapper层增强 引入批量操作的接口
/**
* @author Administrator
*/
public interface EasyBaseMapper <T> extends BaseMapper<T> {
/**
* 批量插入--推荐
* @param entityList entityList
* @return int
*/
int insertBatchSomeColumn(List<T> entityList);
}
配置增强扩展
/**
* @description: MyBatis-Plus提供的InsertBatchSomeColumn方法
* @author: makeJava
* @since JDk17
**/
public class EasySqlInjector extends DefaultSqlInjector {
/**
* 注意:此SQL注入器继承了DefaultSqlInjector(默认注入器),调用了DefaultSqlInjector的getMethodList方法,保留了mybatis-plus的自带方法
* @param configuration 配置对象
* @param mapperClass 当前mapper
* @param tableInfo 表信息
* @return List
*/
@Override
public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
return methodList;
}
}
属性填充
/**
* @description: Mybatis -plus 自动填充
* @author: makeJava
* @since JDk17
**/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 注意填充的字段必须要加注解 参考@TableField(value = "create_dept", fill = FieldFill.INSERT)
*
* @param metaObject 元对象
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ...." );
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof CommonTableInfo baseEntity) {
// 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间
LocalDateTime current = ObjectUtil.isNotNull(baseEntity.getCreateTime())
? baseEntity.getCreateTime() : LocalDateTime.now();
baseEntity.setCreateTime(current);
baseEntity.setUpdateTime(current);
baseEntity.setDelFlag(true);
// 如果创建人为空,则填充当前登录用户的信息
if (ObjectUtil.isNull(baseEntity.getCreateBy())) {
LoginUser loginUser = getLoginUser();
if (ObjectUtil.isNotNull(loginUser)) {
Long userId = loginUser.getUserId();
// 填充创建人、更新人和创建部门信息
baseEntity.setCreateBy(userId);
baseEntity.setUpdateBy(userId);
baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept())
? baseEntity.getCreateDept() : loginUser.getDeptId());
}
}
this.strictInsertFill(metaObject, "createBy", Long.class, baseEntity.getCreateBy());
this.strictInsertFill(metaObject, "updateBy", Long.class, baseEntity.getUpdateBy());
this.strictInsertFill(metaObject, "createDept", Long.class, baseEntity.getCreateDept());
}
// 或者
this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
this.strictInsertFill(metaObject, "tenant_id", String.class, "000000" );
}
/**
* 更新时候填充的字段 也必须加注解
*
* @param metaObject 元对象
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ...." );
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof CommonTableInfo baseEntity) {
// 获取当前时间作为更新时间,无论原始对象中的更新时间是否为空都填充
LocalDateTime current = LocalDateTime.now();
baseEntity.setUpdateTime(current);
// 获取当前登录用户的ID,并填充更新人信息
Long userId = LoginHelper.getUserId();
if (ObjectUtil.isNotNull(userId)) {
baseEntity.setUpdateBy(userId);
}
this.strictInsertFill(metaObject, "updateBy", Long.class, baseEntity.getUpdateBy());
}
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
/**
* 获取当前登录用户信息
*
* @return 当前登录用户的信息,如果用户未登录则返回 null
*/
private LoginUser getLoginUser() {
LoginUser loginUser;
try {
loginUser = LoginHelper.getLoginUser();
} catch (Exception e) {
log.warn("自动注入警告 => 用户未登录" );
return null;
}
return loginUser;
}
}
已经够简化了把!!!只需要继承一下,就单表的接口就完事了
NO NO NO,还需要写代码就是不行!!!!
进一步代码都不需要写了,只需要建立一个表,全自动代码生成,EasyCode来了
配置好代码生成模板
点一下就完事了
代码都不需要你写了,具体EasyCode的模板怎么配置可参考:
https://blog.youkuaiyun.com/user_admin_god/article/details/128209998?spm=1001.2014.3001.5502