diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysMenu.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysMenu.java index 090e2e6d6..ec6963a25 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysMenu.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysMenu.java @@ -23,11 +23,11 @@ import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.extension.activerecord.Model; import com.pig4cloud.pigx.common.core.util.TenantTable; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.FieldNameConstants; import java.time.LocalDateTime; @@ -42,6 +42,7 @@ import java.time.LocalDateTime; @Data @TenantTable @Schema(description = "菜单") +@FieldNameConstants @EqualsAndHashCode(callSuper = true) public class SysMenu extends Model { diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java index e70725940..f53c7c496 100755 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java @@ -16,6 +16,7 @@ */ package com.pig4cloud.pigx.admin.service.impl; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; @@ -33,6 +34,7 @@ import com.pig4cloud.pigx.common.data.datascope.DataScopeTypeEnum; import com.pig4cloud.pigx.common.data.resolver.ParamResolver; import com.pig4cloud.pigx.common.data.tenant.TenantBroker; import lombok.AllArgsConstructor; +import org.springframework.beans.BeanUtils; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @@ -42,7 +44,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -57,255 +61,266 @@ import java.util.stream.Collectors; @AllArgsConstructor public class SysTenantServiceImpl extends ServiceImpl implements SysTenantService { - private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder(); + private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder(); - private final SysOauthClientDetailsService clientServices; + private final SysOauthClientDetailsService clientServices; - private final SysUserRoleService userRoleService; + private final SysUserRoleService userRoleService; - private final SysRoleMenuMapper roleMenuMapper; + private final SysRoleMenuMapper roleMenuMapper; - private final SysDictItemService dictItemService; + private final SysDictItemService dictItemService; - private final SysPublicParamService paramService; + private final SysPublicParamService paramService; - private final SysUserService userService; + private final SysUserService userService; - private final SysRoleService roleService; + private final SysRoleService roleService; - private final SysMenuService menuService; + private final SysMenuService menuService; - private final SysDeptService deptService; + private final SysDeptService deptService; - private final SysDictService dictService; + private final SysDictService dictService; - private final CacheManager cacheManager; + private final CacheManager cacheManager; - /** - * 获取正常状态租户 - *

- * 1. 状态正常 2. 开始时间小于等于当前时间 3. 结束时间大于等于当前时间 - * @return - */ - @Override - @Cacheable(value = CacheConstants.TENANT_DETAILS) - public List getNormalTenant() { - return baseMapper - .selectList(Wrappers.lambdaQuery().eq(SysTenant::getStatus, TenantStateEnum.NORMAL.getCode())); - } + /** + * 获取正常状态租户 + *

+ * 1. 状态正常 2. 开始时间小于等于当前时间 3. 结束时间大于等于当前时间 + * + * @return + */ + @Override + @Cacheable(value = CacheConstants.TENANT_DETAILS) + public List getNormalTenant() { + return baseMapper + .selectList(Wrappers.lambdaQuery().eq(SysTenant::getStatus, TenantStateEnum.NORMAL.getCode())); + } - /** - * 保存租户 - *

- * 1. 保存租户 2. 初始化权限相关表 - sys_user - sys_role - sys_menu - sys_user_role - - * sys_role_menu - sys_dict - sys_dict_item - sys_client_details - sys_public_params - * @param sysTenant 租户实体 - * @return - */ - @Override - @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = CacheConstants.TENANT_DETAILS) - public Boolean saveTenant(SysTenant sysTenant) { - this.save(sysTenant); - // 查询系统默认租户配置参数 - Long defaultId = ParamResolver.getLong("TENANT_DEFAULT_ID", 1L); - String defaultDeptName = ParamResolver.getStr("TENANT_DEFAULT_DEPTNAME", "租户默认部门"); - String defaultUsername = ParamResolver.getStr("TENANT_DEFAULT_USERNAME", "admin"); - String defaultPassword = ParamResolver.getStr("TENANT_DEFAULT_PASSWORD", "123456"); - String defaultRoleCode = ParamResolver.getStr("TENANT_DEFAULT_ROLECODE", "ROLE_ADMIN"); - String defaultRoleName = ParamResolver.getStr("TENANT_DEFAULT_ROLENAME", "租户默认角色"); - String userDefaultRoleCode = ParamResolver.getStr("USER_DEFAULT_ROLECODE", "GENERAL_USER"); - String userDefaultRoleName = ParamResolver.getStr("USER_DEFAULT_ROLENAME", "普通用户"); + /** + * 保存租户 + *

+ * 1. 保存租户 2. 初始化权限相关表 - sys_user - sys_role - sys_menu - sys_user_role - + * sys_role_menu - sys_dict - sys_dict_item - sys_client_details - sys_public_params + * + * @param sysTenant 租户实体 + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = CacheConstants.TENANT_DETAILS) + public Boolean saveTenant(SysTenant sysTenant) { + this.save(sysTenant); + // 查询系统默认租户配置参数 + Long defaultId = ParamResolver.getLong("TENANT_DEFAULT_ID", 1L); + String defaultDeptName = ParamResolver.getStr("TENANT_DEFAULT_DEPTNAME", "租户默认部门"); + String defaultUsername = ParamResolver.getStr("TENANT_DEFAULT_USERNAME", "admin"); + String defaultPassword = ParamResolver.getStr("TENANT_DEFAULT_PASSWORD", "123456"); + String defaultRoleCode = ParamResolver.getStr("TENANT_DEFAULT_ROLECODE", "ROLE_ADMIN"); + String defaultRoleName = ParamResolver.getStr("TENANT_DEFAULT_ROLENAME", "租户默认角色"); + String userDefaultRoleCode = ParamResolver.getStr("USER_DEFAULT_ROLECODE", "GENERAL_USER"); + String userDefaultRoleName = ParamResolver.getStr("USER_DEFAULT_ROLENAME", "普通用户"); - List dictList = new ArrayList<>(32); - List dictIdList = new ArrayList<>(32); - List dictItemList = new ArrayList<>(64); - List menuList = new ArrayList<>(128); - List clientDetailsList = new ArrayList<>(16); - List publicParamList = new ArrayList<>(64); + List dictList = new ArrayList<>(32); + List dictIdList = new ArrayList<>(32); + List dictItemList = new ArrayList<>(64); + List menuList = new ArrayList<>(128); + List clientDetailsList = new ArrayList<>(16); + List publicParamList = new ArrayList<>(64); - TenantBroker.runAs(defaultId, (id) -> { - // 查询系统内置字典 - dictList.addAll(dictService.list()); - // 查询系统内置字典项目 - dictIdList.addAll(dictList.stream().map(SysDict::getId).toList()); - dictItemList.addAll( - dictItemService.list(Wrappers.lambdaQuery().in(SysDictItem::getDictId, dictIdList))); - List newMenuList = menuService.list(Wrappers.lambdaQuery() - .in(SysMenu::getMenuId, StrUtil.split(sysTenant.getMenuId(), CharUtil.COMMA))); - // 查询当前租户菜单 - menuList.addAll(newMenuList); - // 查询客户端配置 - clientDetailsList.addAll(clientServices.list()); - // 查询系统参数配置 - publicParamList.addAll(paramService.list()); - }); + TenantBroker.runAs(defaultId, (id) -> { + // 查询系统内置字典 + dictList.addAll(dictService.list()); + // 查询系统内置字典项目 + dictIdList.addAll(dictList.stream().map(SysDict::getId).toList()); + dictItemList.addAll( + dictItemService.list(Wrappers.lambdaQuery().in(SysDictItem::getDictId, dictIdList))); + List newMenuList = menuService.list(Wrappers.lambdaQuery() + .in(SysMenu::getMenuId, StrUtil.split(sysTenant.getMenuId(), CharUtil.COMMA))); + // 查询当前租户菜单 + menuList.addAll(newMenuList); + // 查询客户端配置 + clientDetailsList.addAll(clientServices.list()); + // 查询系统参数配置 + publicParamList.addAll(paramService.list()); + }); - // 保证插入租户为新的租户 - TenantBroker.applyAs(sysTenant.getId(), (id -> { - // 插入部门 - SysDept dept = new SysDept(); - dept.setName(defaultDeptName); - dept.setParentId(0L); - deptService.save(dept); - // 构造初始化用户 - SysUser user = new SysUser(); - user.setUsername(defaultUsername); - user.setPassword(ENCODER.encode(defaultPassword)); - user.setDeptId(dept.getDeptId()); - userService.save(user); + // 保证插入租户为新的租户 + TenantBroker.applyAs(sysTenant.getId(), (id -> { + // 插入部门 + SysDept dept = new SysDept(); + dept.setName(defaultDeptName); + dept.setParentId(0L); + deptService.save(dept); + // 构造初始化用户 + SysUser user = new SysUser(); + user.setUsername(defaultUsername); + user.setPassword(ENCODER.encode(defaultPassword)); + user.setDeptId(dept.getDeptId()); + userService.save(user); - // 构造普通用户角色 - SysRole roleDefault = new SysRole(); - roleDefault.setRoleCode(userDefaultRoleCode); - roleDefault.setRoleName(userDefaultRoleName); - roleDefault.setDsType(DataScopeTypeEnum.SELF_LEVEL.getType()); - roleService.save(roleDefault); + // 构造普通用户角色 + SysRole roleDefault = new SysRole(); + roleDefault.setRoleCode(userDefaultRoleCode); + roleDefault.setRoleName(userDefaultRoleName); + roleDefault.setDsType(DataScopeTypeEnum.SELF_LEVEL.getType()); + roleService.save(roleDefault); - // 构造新角色 管理员角色 - SysRole role = new SysRole(); - role.setRoleCode(defaultRoleCode); - role.setRoleName(defaultRoleName); - role.setDsType(DataScopeTypeEnum.ALL.getType()); - roleService.save(role); - // 用户角色关系 - SysUserRole userRole = new SysUserRole(); - userRole.setUserId(user.getUserId()); - userRole.setRoleId(role.getRoleId()); - userRoleService.save(userRole); - // 插入新的菜单 - saveTenantMenu(menuList, CommonConstants.MENU_TREE_ROOT_ID, CommonConstants.MENU_TREE_ROOT_ID); + // 构造新角色 管理员角色 + SysRole role = new SysRole(); + role.setRoleCode(defaultRoleCode); + role.setRoleName(defaultRoleName); + role.setDsType(DataScopeTypeEnum.ALL.getType()); + roleService.save(role); + // 用户角色关系 + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(user.getUserId()); + userRole.setRoleId(role.getRoleId()); + userRoleService.save(userRole); + // 插入新的菜单 + saveTenantMenu(menuList, CommonConstants.MENU_TREE_ROOT_ID, CommonConstants.MENU_TREE_ROOT_ID); - // 重新查询出所有的菜单关联角色 - List list = menuService.list(); + // 重新查询出所有的菜单关联角色 + List list = menuService.list(); - // 查询全部菜单,构造角色菜单关系 - List roleMenuList = list.stream().map(menu -> { - SysRoleMenu roleMenu = new SysRoleMenu(); - roleMenu.setRoleId(role.getRoleId()); - roleMenu.setMenuId(menu.getMenuId()); - return roleMenu; - }).toList(); - roleMenuList.forEach(roleMenuMapper::insert); - // 插入系统字典 - dictService.saveBatch(dictList.stream().peek(d -> d.setId(null)).collect(Collectors.toList())); - // 处理字典项最新关联的字典ID - List itemList = dictList.stream() - .flatMap(dict -> dictItemList.stream() - .filter(item -> item.getDictType().equals(dict.getDictType())) - .peek(item -> { - item.setDictId(dict.getId()); - item.setId(null); - })) - .collect(Collectors.toList()); + // 查询全部菜单,构造角色菜单关系 + List roleMenuList = list.stream().map(menu -> { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(role.getRoleId()); + roleMenu.setMenuId(menu.getMenuId()); + return roleMenu; + }).toList(); + roleMenuList.forEach(roleMenuMapper::insert); + // 插入系统字典 + dictService.saveBatch(dictList.stream().peek(d -> d.setId(null)).collect(Collectors.toList())); + // 处理字典项最新关联的字典ID + List itemList = dictList.stream() + .flatMap(dict -> dictItemList.stream() + .filter(item -> item.getDictType().equals(dict.getDictType())) + .peek(item -> { + item.setDictId(dict.getId()); + item.setId(null); + })) + .collect(Collectors.toList()); - // 插入客户端 - clientServices.saveBatch(clientDetailsList.stream().peek(d -> d.setId(null)).collect(Collectors.toList())); - // 插入系统配置 - paramService - .saveBatch(publicParamList.stream().peek(d -> d.setPublicId(null)).collect(Collectors.toList())); - return dictItemService.saveBatch(itemList); - })); + // 插入客户端 + clientServices.saveBatch(clientDetailsList.stream().peek(d -> d.setId(null)).collect(Collectors.toList())); + // 插入系统配置 + paramService + .saveBatch(publicParamList.stream().peek(d -> d.setPublicId(null)).collect(Collectors.toList())); + return dictItemService.saveBatch(itemList); + })); - SpringContextHolder.publishEvent(new ClientDetailsInitRunner.ClientDetailsInitEvent(sysTenant)); + SpringContextHolder.publishEvent(new ClientDetailsInitRunner.ClientDetailsInitEvent(sysTenant)); - return Boolean.TRUE; - } + return Boolean.TRUE; + } - /** - * 修改租户 - * @param tenantDTO - * @return - */ - @Override - @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) - public Boolean updateTenant(SysTenant tenantDTO) { - SysTenant tenant = baseMapper.selectById(tenantDTO.getId()); - // 更新租户数据 - updateById(tenantDTO); + /** + * 修改租户 + * + * @param tenantDTO 新旧套餐数据 + * @return true/false + */ + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) + public Boolean updateTenant(SysTenant tenantDTO) { + SysTenant oldTenant = baseMapper.selectById(tenantDTO.getId()); + List newMenuIdList = StrUtil.split(tenantDTO.getMenuId(), CharUtil.COMMA); + List oldMenuIdList = StrUtil.split(oldTenant.getMenuId(), CharUtil.COMMA); - // 如果没有修改租户套餐 - Long defaultId = ParamResolver.getLong("TENANT_DEFAULT_ID", 1L); - if (defaultId.equals(tenantDTO.getId())) { - return Boolean.TRUE; - } - if (tenant.getMenuId().equals(tenantDTO.getMenuId())) { - return Boolean.TRUE; - } + // 更新租户信息 + updateById(tenantDTO); - List sysMenuList = TenantBroker.applyAs(defaultId, - id -> menuService.list(Wrappers.lambdaQuery() - .in(SysMenu::getMenuId, StrUtil.split(tenantDTO.getMenuId(), CharUtil.COMMA)))); + // 如果没有修改租户套餐 + Long defaultId = ParamResolver.getLong("TENANT_DEFAULT_ID", 1L); + List sysMenuList = TenantBroker.applyAs(defaultId, id -> menuService.list()); + if (defaultId.equals(tenantDTO.getId())) { + return Boolean.TRUE; + } - TenantBroker.runAs(tenantDTO.getId(), (tenantId -> { - // 查询当前租户的所有菜单 - List menuList = menuService.list(Wrappers.emptyWrapper()); + // 比较新旧套餐是否一致 + if (CollUtil.isEqualList(newMenuIdList, oldMenuIdList)) { + return Boolean.TRUE; + } - // 套餐功能对比和已有菜单对比 (新增) - List addMenuList = new ArrayList<>(); - List delMenuList = new ArrayList<>(); + // 0. 计算两个差集,差集表示变化的功能项 + Collection disjunctionList = CollUtil.disjunction(newMenuIdList, oldMenuIdList); + // 1. 如果旧套餐包含差集元素则删除 + CollUtil.intersection(oldMenuIdList, disjunctionList).forEach(menuId -> TenantBroker.runAs(tenantDTO.getId(), (tenantId -> { + //查询租户菜单元信息 + SysMenu menu = CollUtil.findOneByField(sysMenuList, SysMenu.Fields.menuId, Long.parseLong(menuId)); - // 判断新增的菜单列表 - menuExist(sysMenuList, menuList, addMenuList); + // 根据path 或者 permisson 删除目标租户菜单 + menuService.remove(Wrappers.lambdaQuery() + .eq(StrUtil.isNotBlank(menu.getPath()), SysMenu::getPath, menu.getPath()) + .or() + .eq(StrUtil.isNotBlank(menu.getPermission()), SysMenu::getPermission, menu.getPermission()) + ); + }))); - // 判断删除的菜单列表 - menuExist(menuList, sysMenuList, delMenuList); + // 2. 如果旧套餐不包含差集元素则新增 + List newTenantMenuIdList = CollUtil.subtract(disjunctionList, oldMenuIdList).stream().map(menuId -> TenantBroker.applyAs(tenantDTO.getId(), (tenantId -> { + //查询租户菜单元信息 + SysMenu menu = CollUtil.findOneByField(sysMenuList, SysMenu.Fields.menuId, Long.parseLong(menuId)); - // 新增的菜单 - this.saveTenantMenu(addMenuList, CommonConstants.MENU_TREE_ROOT_ID, CommonConstants.MENU_TREE_ROOT_ID); - // 套餐删除的菜单 - List menuIdList = delMenuList.stream().map(SysMenu::getMenuId).toList(); - menuService.removeBatchByIds(menuIdList); + // 新增租户菜单,但未维护上下级关系 + SysMenu newMenu = new SysMenu(); + BeanUtils.copyProperties(menu, newMenu, SysMenu.Fields.menuId); + menuService.save(newMenu); + return newMenu; + }))).collect(Collectors.toList()); - // 清空菜单权限 - cacheManager.getCache(CacheConstants.MENU_DETAILS).clear(); - })); - return Boolean.TRUE; - } + // 3. 更新新增菜单上下级关系 + for (SysMenu tenantMenu : newTenantMenuIdList) { + // 查询租户菜单父元信息 + SysMenu parentMenu = CollUtil.findOneByField(sysMenuList, SysMenu.Fields.menuId, tenantMenu.getParentId()); + // 查询目标租户父菜单元信息 + TenantBroker.runAs(tenantDTO.getId(), tenantId -> { + // 如何菜单父节点是根节点则直接插入,不要查询父节点判断 + if (Objects.equals(tenantMenu.getParentId(), CommonConstants.MENU_TREE_ROOT_ID)) { + menuService.updateById(tenantMenu); + return; + } - /** - * 判断菜单是否存在 - * @param sysMenuList 要判断的菜单列表 - * @param menuList 已有菜单列表 - * @param addMenuList 待添加的菜单列表 - */ - private void menuExist(List sysMenuList, List menuList, List addMenuList) { - for (SysMenu sysMenu : sysMenuList) { + // 根据path 或者 permisson 查询目标父菜单 + SysMenu tenantParentMenu = menuService.getOne(Wrappers.lambdaQuery() + .eq(StrUtil.isNotBlank(parentMenu.getPath()), SysMenu::getPath, parentMenu.getPath()) + .or() + .eq(StrUtil.isNotBlank(parentMenu.getPermission()), SysMenu::getPermission, parentMenu.getPermission()), false); - if (StrUtil.isNotBlank(sysMenu.getPath())) { - // 根据菜单path名称,查询套餐菜单是否存在 - if (menuList.stream().noneMatch(menu -> sysMenu.getPath().equals(menu.getPath()))) { - addMenuList.add(sysMenu); - } - } + tenantMenu.setParentId(tenantParentMenu.getMenuId()); + menuService.updateById(tenantMenu); + }); + } - if (StrUtil.isNotBlank(sysMenu.getPermission())) { - // 根据菜单permission名称,查询套餐菜单是否存在 - if (menuList.stream().noneMatch(menu -> sysMenu.getPermission().equals(menu.getPermission()))) { - addMenuList.add(sysMenu); - } - } + // 清空菜单权限缓存 + cacheManager.getCache(CacheConstants.MENU_DETAILS).clear(); + return Boolean.TRUE; + } - } - } - /** - * 保存新的租户菜单,维护成新的菜单 - * @param menuList 菜单列表 - * @param originParentId 原始上级 - * @param targetParentId 目标上级 - */ - private void saveTenantMenu(List menuList, Long originParentId, Long targetParentId) { - menuList.stream().filter(menu -> menu.getParentId().equals(originParentId)).forEach(menu -> { - // 保存菜单原始menuId, 方便查询子节点使用 - Long originMenuId = menu.getMenuId(); - menu.setMenuId(null); - menu.setParentId(targetParentId); - menuService.save(menu); - // 查找此节点的子节点,然后子节点的重新插入父节点更改为新的menuId - saveTenantMenu(menuList, originMenuId, menu.getMenuId()); - }); - } + /** + * 保存新的租户菜单,维护成新的菜单 + * + * @param menuList 菜单列表 + * @param originParentId 原始上级 + * @param targetParentId 目标上级 + */ + private void saveTenantMenu(List menuList, Long originParentId, Long targetParentId) { + menuList.stream().filter(menu -> menu.getParentId().equals(originParentId)).forEach(menu -> { + // 保存菜单原始menuId, 方便查询子节点使用 + Long originMenuId = menu.getMenuId(); + menu.setMenuId(null); + menu.setParentId(targetParentId); + menuService.save(menu); + // 查找此节点的子节点,然后子节点的重新插入父节点更改为新的menuId + saveTenantMenu(menuList, originMenuId, menu.getMenuId()); + }); + } }