新增: list和page支持关联查询,支持自定义返回结果类型,并可以在init方法中初始化返回字段类型及数据组装函数定义

调整:优化角色修改循环单个处理为批量处理,并异步进行刷新用户权限
This commit is contained in:
ruying408
2024-08-25 01:37:10 +08:00
parent 8a6ea0e436
commit 6296f328ae
7 changed files with 202 additions and 34 deletions

View File

@@ -1,11 +1,16 @@
package com.cool.core.base;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.lang.Editor;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.cool.core.enums.QueryModeEnum;
import com.cool.core.exception.CoolPreconditions;
import com.cool.core.request.CrudOption;
import com.cool.core.request.R;
@@ -20,6 +25,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.Getter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
@@ -27,6 +33,8 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 控制层基类
@@ -60,6 +68,12 @@ public abstract class BaseController<S extends BaseService<T>, T extends BaseEnt
@ModelAttribute
protected void preHandle(HttpServletRequest request,
@RequestAttribute JSONObject requestParams) {
String requestPath = ((ServletRequestAttributes) Objects.requireNonNull(
RequestContextHolder.getRequestAttributes())).getRequest().getRequestURI();
if (!requestPath.endsWith("/page") && !requestPath.endsWith("/list")) {
// 非page或list不执行
return;
}
this.pageOption.set(new CrudOption<>(requestParams));
this.listOption.set(new CrudOption<>(requestParams));
this.requestParams.set(requestParams);
@@ -133,7 +147,7 @@ public abstract class BaseController<S extends BaseService<T>, T extends BaseEnt
@PostMapping("/update")
protected R update(@RequestBody T t, @RequestAttribute() JSONObject requestParams) {
Long id = t.getId();
JSONObject info = JSONUtil.parseObj(JSONUtil.toJsonStr(service.info(id)));
JSONObject info = JSONUtil.parseObj(JSONUtil.toJsonStr(service.getById(id)));
requestParams.forEach(info::set);
info.set("updateTime", new Date());
service.update(requestParams, JSONUtil.toBean(info, currentEntityClass()));
@@ -161,7 +175,14 @@ public abstract class BaseController<S extends BaseService<T>, T extends BaseEnt
@PostMapping("/list")
protected R list(@RequestAttribute() JSONObject requestParams,
@RequestAttribute(COOL_LIST_OP) CrudOption<T> option) {
return R.ok(service.list(requestParams, option.getQueryWrapper(entityClass)));
QueryModeEnum queryModeEnum = option.getQueryModeEnum();
List list = (List) switch (queryModeEnum) {
case ENTITY_WITH_RELATIONS -> service.listWithRelations(requestParams, option.getQueryWrapper(entityClass));
case CUSTOM -> transformList(service.list(requestParams, option.getQueryWrapper(entityClass), option.getAsType()), option.getAsType());
default -> service.list(requestParams, option.getQueryWrapper(entityClass));
};
invokerTransform(option, list);
return R.ok(list);
}
/**
@@ -175,9 +196,24 @@ public abstract class BaseController<S extends BaseService<T>, T extends BaseEnt
@RequestAttribute(COOL_PAGE_OP) CrudOption<T> option) {
Integer page = requestParams.getInt("page", 1);
Integer size = requestParams.getInt("size", 20);
return R.ok(
pageResult((Page<T>) service.page(requestParams, new Page<>(page, size),
option.getQueryWrapper(entityClass))));
QueryModeEnum queryModeEnum = option.getQueryModeEnum();
Object obj = switch (queryModeEnum) {
case ENTITY_WITH_RELATIONS -> service.pageWithRelations(requestParams, new Page<>(page, size), option.getQueryWrapper(entityClass));
case CUSTOM -> transformPage(service.page(requestParams, new Page<>(page, size), option.getQueryWrapper(entityClass), option.getAsType()), option.getAsType());
default -> service.page(requestParams, new Page<>(page, size), option.getQueryWrapper(entityClass));
};
Page pageResult = (Page) obj;
invokerTransform(option, pageResult.getRecords());
return R.ok(pageResult(pageResult));
}
/**
* 转换参数,组装数据
*/
private void invokerTransform(CrudOption<T> option, List list) {
if (ObjUtil.isNotEmpty(option.getTransform())) {
option.getTransform().apply(list);
}
}
/**
@@ -185,7 +221,7 @@ public abstract class BaseController<S extends BaseService<T>, T extends BaseEnt
*
* @param page 分页返回数据
*/
protected Map<String, Object> pageResult(Page<T> page) {
protected Map<String, Object> pageResult(Page page) {
Map<String, Object> result = new HashMap<>();
Map<String, Object> pagination = new HashMap<>();
pagination.put("size", page.getPageSize());
@@ -223,4 +259,21 @@ public abstract class BaseController<S extends BaseService<T>, T extends BaseEnt
return Convert.toList(Long.class, ids);
}
/**
* 适用于自定义返回值为 mapmap 的key为数据库字段转驼峰命名
*/
protected List transformList(List records, Class<?> asType) {
if (ObjUtil.isEmpty(asType) || !Map.class.isAssignableFrom(asType)) {
return records;
}
List<Map> list = new ArrayList<>();
Editor<String> keyEditor = property -> StrUtil.toCamelCase(property);
records.forEach(o ->
list.add(BeanUtil.beanToMap(o, new HashMap(), false, keyEditor)));
return list;
}
protected Page transformPage(Page page, Class<?> asType) {
page.setRecords(transformList(page.getRecords(), asType));
return page;
}
}

View File

@@ -76,6 +76,15 @@ public interface BaseService<T> extends IService<T> {
*/
Object list(JSONObject requestParams, QueryWrapper queryWrapper);
/**
* 查询所有
*
* @param requestParams 请求参数
* @param queryWrapper 查询条件
* @return 列表信息
*/
<R> List<R> list(JSONObject requestParams, QueryWrapper queryWrapper, Class<R> asType);
/**
* 查询所有
* 带关联查询
@@ -95,6 +104,16 @@ public interface BaseService<T> extends IService<T> {
*/
Object page(JSONObject requestParams, Page<T> page, QueryWrapper queryWrapper);
/**
* 分页查询
*
* @param requestParams 请求参数
* @param page 分页信息
* @param queryWrapper 查询条件
* @return 分页信息
*/
<R> Page<R> page(JSONObject requestParams, Page page, QueryWrapper queryWrapper, Class<R> asType);
/**
* 分页查询
* 带关联查询
@@ -108,7 +127,7 @@ public interface BaseService<T> extends IService<T> {
/**
* 查询信息
*
* @param id ID
* @param id ID
*/
Object info(Long id);

View File

@@ -78,6 +78,11 @@ public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity<T>> e
return this.list(queryWrapper);
}
@Override
public <R> List<R> list(JSONObject requestParams, QueryWrapper queryWrapper, Class<R> asType) {
return mapper.selectListByQueryAs(queryWrapper, asType);
}
@Override
public Object listWithRelations(JSONObject requestParams, QueryWrapper queryWrapper) {
return mapper.selectListWithRelationsByQuery(queryWrapper);
@@ -88,6 +93,12 @@ public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity<T>> e
return this.page(page, queryWrapper);
}
@Override
public <R> Page<R> page(JSONObject requestParams, Page page, QueryWrapper queryWrapper,
Class<R> asType) {
return mapper.paginateAs(page, queryWrapper, asType);
}
@Override
public Object pageWithRelations(JSONObject requestParams, Page<T> page,
QueryWrapper queryWrapper) {

View File

@@ -1,6 +1,8 @@
package com.cool.core.config;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -30,4 +32,10 @@ public class ThreadPoolConfig {
executor.initialize();
return executor;
}
@Bean(name = "cachedThreadPool")
public ExecutorService cachedThreadPool() {
// 创建一个虚拟线程池,每个任务使用一个虚拟线程执行
return Executors.newCachedThreadPool();
}
}

View File

@@ -0,0 +1,10 @@
package com.cool.core.enums;
/**
* 查询模式决定返回值
*/
public enum QueryModeEnum {
ENTITY, // 实体(默认)
ENTITY_WITH_RELATIONS, // 实体关联查询(如实体字段上加 @RelationOneToMany 等注解)
CUSTOM , // 自定义默认为Map
}

View File

@@ -2,11 +2,13 @@ package com.cool.core.request;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import com.cool.core.enums.QueryModeEnum;
import com.mybatisflex.annotation.Table;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryCondition;
@@ -14,6 +16,7 @@ import com.mybatisflex.core.query.QueryTable;
import com.mybatisflex.core.query.QueryWrapper;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import lombok.Data;
import org.springframework.core.env.Environment;
@@ -31,17 +34,26 @@ public class CrudOption<T> {
private QueryColumn[] select;
private JSONObject requestParams;
private QueryModeEnum queryModeEnum;
private Transform<List> transform;
public interface Transform<List> {
void apply(List list);
}
/**
* queryModeEnum 为 CUSTOM,可设置 默认为Map
*/
private Class<?> asType;
private Environment evn;
public CrudOption(JSONObject requestParams) {
this.requestParams = requestParams;
this.queryWrapper = QueryWrapper.create();
this.evn = SpringUtil.getBean(Environment.class);
}
public CrudOption<T> fieldEq(QueryColumn... fields) {
this.fieldEq = fields;
return this;
queryModeEnum = QueryModeEnum.ENTITY;
}
public QueryWrapper getQueryWrapper(Class<T> entityClass) {
@@ -53,16 +65,58 @@ public class CrudOption<T> {
return this;
}
/**
* 按前端传上来的字段值做eq
*/
public CrudOption<T> fieldEq(QueryColumn... fields) {
this.fieldEq = fields;
return this;
}
/**
* 按前端传上来的字段值做like
*/
public CrudOption<T> keyWordLikeFields(QueryColumn... fields) {
this.keyWordLikeFields = fields;
return this;
}
/**
* 需要返回给前端的字段
*/
public CrudOption<T> select(QueryColumn... selects) {
this.select = selects;
return this;
}
/**
* 查询模式决定返回值
* 目前有三种模式,按实体查询返回、关联查询返回(实体字段上加 @RelationOneToMany 等注解)、自定义返回结果
*/
public CrudOption<T> queryModeEnum(QueryModeEnum queryModeEnum) {
this.queryModeEnum = queryModeEnum;
if (ObjUtil.equal(queryModeEnum, QueryModeEnum.CUSTOM)
&& ObjUtil.isEmpty(asType)) {
asType = Map.class;
}
return this;
}
/**
* 自定义返回结果对象类型
*/
public CrudOption<T> asType(Class<?> asType) {
this.asType = asType;
return this;
}
/**
* 转换参数,组装数据
*/
public CrudOption<T> transform(Transform<List> transform) {
this.transform = transform;
return this;
}
/**
* 构建查询条件

View File

@@ -19,6 +19,7 @@ import com.cool.modules.base.service.sys.BaseSysPermsService;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Row;
import java.util.*;
import java.util.concurrent.ExecutorService;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -41,18 +42,20 @@ public class BaseSysPermsServiceImpl implements BaseSysPermsService {
final private BaseSysDepartmentMapper baseSysDepartmentMapper;
final private ExecutorService cachedThreadPool;
@Override
public Long[] loginDepartmentIds() {
String username = CoolSecurityUtil.getAdminUsername();
if (username.equals("admin")) {
return baseSysDepartmentMapper.selectAll().stream().map(BaseSysDepartmentEntity::getId)
.toArray(Long[]::new);
.toArray(Long[]::new);
} else {
Long[] roleIds = getRoles(username);
return baseSysRoleDepartmentMapper
.selectListByQuery(
QueryWrapper.create().in(BaseSysRoleDepartmentEntity::getRoleId, (Object) roleIds))
.stream().map(BaseSysRoleDepartmentEntity::getDepartmentId).toArray(Long[]::new);
.selectListByQuery(
QueryWrapper.create().in(BaseSysRoleDepartmentEntity::getRoleId, (Object) roleIds))
.stream().map(BaseSysRoleDepartmentEntity::getDepartmentId).toArray(Long[]::new);
}
}
@@ -70,8 +73,8 @@ public class BaseSysPermsServiceImpl implements BaseSysPermsService {
queryWrapper.in(BaseSysRoleDepartmentEntity::getRoleId, (Object) roleIds);
}
return baseSysRoleDepartmentMapper
.selectListByQuery(queryWrapper)
.stream().map(BaseSysRoleDepartmentEntity::getDepartmentId).toArray(Long[]::new);
.selectListByQuery(queryWrapper)
.stream().map(BaseSysRoleDepartmentEntity::getDepartmentId).toArray(Long[]::new);
}
@Override
@@ -97,7 +100,7 @@ public class BaseSysPermsServiceImpl implements BaseSysPermsService {
@Override
public Long[] getRoles(String username) {
return getRoles(
baseSysUserMapper.selectOneByQuery(QueryWrapper.create().eq(BaseSysUserEntity::getUsername, username)));
baseSysUserMapper.selectOneByQuery(QueryWrapper.create().eq(BaseSysUserEntity::getUsername, username)));
}
@Override
@@ -105,7 +108,7 @@ public class BaseSysPermsServiceImpl implements BaseSysPermsService {
Long[] roleIds = null;
if (!userEntity.getUsername().equals("admin")) {
List<BaseSysUserRoleEntity> list = baseSysUserRoleMapper
.selectListByQuery(QueryWrapper.create().eq(BaseSysUserRoleEntity::getUserId, userEntity.getId()));
.selectListByQuery(QueryWrapper.create().eq(BaseSysUserRoleEntity::getUserId, userEntity.getId()));
roleIds = list.stream().map(BaseSysUserRoleEntity::getRoleId).toArray(Long[]::new);
if (Arrays.asList(roleIds).contains(1L)) {
roleIds = null;
@@ -124,7 +127,7 @@ public class BaseSysPermsServiceImpl implements BaseSysPermsService {
List<BaseSysMenuEntity> menus = getMenus(roleIds);
Set<String> perms = new HashSet<>();
String[] permsData = menus.stream().map(BaseSysMenuEntity::getPerms)
.filter(itemPerms -> !StrUtil.isEmpty(itemPerms)).toArray(String[]::new);
.filter(itemPerms -> !StrUtil.isEmpty(itemPerms)).toArray(String[]::new);
for (String permData : permsData) {
perms.addAll(Arrays.asList(permData.split(",")));
}
@@ -155,7 +158,7 @@ public class BaseSysPermsServiceImpl implements BaseSysPermsService {
@Override
public List<BaseSysMenuEntity> getMenus(String username) {
BaseSysUserEntity sysUserEntity = baseSysUserMapper
.selectOneByQuery(QueryWrapper.create().eq(BaseSysUserEntity::getUsername, username));
.selectOneByQuery(QueryWrapper.create().eq(BaseSysUserEntity::getUsername, username));
return getMenus(sysUserEntity.getId());
}
@@ -173,27 +176,37 @@ public class BaseSysPermsServiceImpl implements BaseSysPermsService {
public void updatePerms(Long roleId, Long[] menuIdList, Long[] departmentIds) {
// 更新菜单权限
baseSysRoleMenuMapper.deleteByQuery(QueryWrapper.create().eq(BaseSysRoleMenuEntity::getRoleId, roleId));
List<BaseSysRoleMenuEntity> batchRoleMenuList = new ArrayList<>();
for (Long menuId : menuIdList) {
BaseSysRoleMenuEntity roleMenuEntity = new BaseSysRoleMenuEntity();
roleMenuEntity.setRoleId(roleId);
roleMenuEntity.setMenuId(menuId);
baseSysRoleMenuMapper.insert(roleMenuEntity);
batchRoleMenuList.add(roleMenuEntity);
}
if (ObjectUtil.isNotEmpty(batchRoleMenuList)) {
baseSysRoleMenuMapper.insertBatch(batchRoleMenuList);
}
// 更新部门权限
baseSysRoleDepartmentMapper
.deleteByQuery(QueryWrapper.create().eq(BaseSysRoleDepartmentEntity::getRoleId, roleId));
.deleteByQuery(QueryWrapper.create().eq(BaseSysRoleDepartmentEntity::getRoleId, roleId));
List<BaseSysRoleDepartmentEntity> batchRoleDepartmentList = new ArrayList<>();
for (Long departmentId : departmentIds) {
BaseSysRoleDepartmentEntity roleDepartmentEntity = new BaseSysRoleDepartmentEntity();
roleDepartmentEntity.setRoleId(roleId);
roleDepartmentEntity.setDepartmentId(departmentId);
baseSysRoleDepartmentMapper.insert(roleDepartmentEntity);
batchRoleDepartmentList.add(roleDepartmentEntity);
}
// 刷新对应角色用户的权限
List<BaseSysUserRoleEntity> userRoles = baseSysUserRoleMapper
if (ObjectUtil.isNotEmpty(batchRoleDepartmentList)) {
baseSysRoleDepartmentMapper.insertBatch(batchRoleDepartmentList);
}
cachedThreadPool.submit(() -> {
// 刷新对应角色用户的权限
List<BaseSysUserRoleEntity> userRoles = baseSysUserRoleMapper
.selectListByQuery(QueryWrapper.create().eq(BaseSysUserRoleEntity::getRoleId, roleId));
for (BaseSysUserRoleEntity userRole : userRoles) {
refreshPerms(userRole.getUserId());
}
for (BaseSysUserRoleEntity userRole : userRoles) {
refreshPerms(userRole.getUserId());
}
});
}
@Override
@@ -227,11 +240,11 @@ public class BaseSysPermsServiceImpl implements BaseSysPermsService {
public void refreshPermsByMenuId(Long menuId) {
// 刷新超管权限、 找出这个菜单的所有用户、 刷新用户权限
BaseSysUserEntity admin = baseSysUserMapper
.selectOneByQuery(QueryWrapper.create().eq(BaseSysUserEntity::getUsername, "admin"));
.selectOneByQuery(QueryWrapper.create().eq(BaseSysUserEntity::getUsername, "admin"));
refreshPerms(admin.getId());
List<Row> list = baseSysRoleMenuMapper.selectRowsByQuery(QueryWrapper.create().select(BASE_SYS_USER_ROLE_ENTITY.USER_ID)
.from(BASE_SYS_ROLE_MENU_ENTITY).leftJoin(BASE_SYS_USER_ROLE_ENTITY)
.on(BASE_SYS_ROLE_MENU_ENTITY.ROLE_ID.eq(BASE_SYS_USER_ROLE_ENTITY.ROLE_ID)).and(BASE_SYS_ROLE_MENU_ENTITY.MENU_ID.eq(menuId, ObjectUtil.isNotEmpty(menuId))).groupBy(BASE_SYS_USER_ROLE_ENTITY.USER_ID));
.from(BASE_SYS_ROLE_MENU_ENTITY).leftJoin(BASE_SYS_USER_ROLE_ENTITY)
.on(BASE_SYS_ROLE_MENU_ENTITY.ROLE_ID.eq(BASE_SYS_USER_ROLE_ENTITY.ROLE_ID)).and(BASE_SYS_ROLE_MENU_ENTITY.MENU_ID.eq(menuId, ObjectUtil.isNotEmpty(menuId))).groupBy(BASE_SYS_USER_ROLE_ENTITY.USER_ID));
for (Row row : list) {
refreshPerms(row.getLong("userId"));
}
@@ -241,7 +254,7 @@ public class BaseSysPermsServiceImpl implements BaseSysPermsService {
public void refreshPermsByRoleId(Long roleId) {
// 找出角色对应的所有用户
List<BaseSysUserRoleEntity> list = baseSysUserRoleMapper
.selectListByQuery(QueryWrapper.create().eq(BaseSysUserRoleEntity::getRoleId, roleId));
.selectListByQuery(QueryWrapper.create().eq(BaseSysUserRoleEntity::getRoleId, roleId));
list.forEach(e -> {
refreshPerms(e.getUserId());
});