Jeecg-boot 2.4.6+ 多租户改造方案(涉及菜单部门角色等基础模块)
場景:
jeecg boot提供了多租戶的配置,但是并沒有完整實現該功能,此文就原系統表的菜單、部門、角色、用戶為例實現多租戶功能實現方案。
配置基礎菜單json, 做多租戶的時候,需要將部分系統管理的菜單復制多分分配給不同租戶角色,所以需要配置一個json文件,詳細見補丁文件
修改代碼:全項目搜索注解@RequiresRoles @RequiresPermissions,刪除,沒找到忽略
修改SysDepartMapper.xml增加代碼
<!-- 根據父ID查詢同級部門 --> <select id="querySameLevelDepart" parameterType="String" resultType="org.jeecg.modules.system.entity.SysDepart">select * from sys_depart<choose><when test="pid != null and pid != ''">where parent_id = #{pid,jdbcType=VARCHAR}</when><otherwise>where parent_id is null or parent_id=''</otherwise></choose>order by org_code desc </select>修改SysDepartMapper增加代碼:
@InterceptorIgnore(tenantLine = "true")List<SysDepart> querySameLevelDepart(@Param("pid")String pid);接口ISysDepartService新增代碼如下:
List<SysDepart> querySameLevelDepart(String pid);實現類SysDepartServiceImpl新增:
@Overridepublic List<SysDepart> querySameLevelDepart(String pid) {return baseMapper.querySameLevelDepart(pid);}還是修改SysDepartServiceImpl: 將注解@Cacheable(value = CacheConstant.SYS_DEPARTS_CACHE) @Cacheable(value = CacheConstant.SYS_DEPART_IDS_CACHE)去掉
修改SysPermissionTree代碼:
/*** 租戶ID*/ private java.lang.Integer tenantId;/*** 是否是基礎菜單 1是0否 如果是1 新增租戶的時候需要復制*/ private java.lang.Boolean baseFlag;修改OrgCodeRule代碼:
修改租戶表:sys_tenant 增加一個字段 pre_code(角色編碼前綴)
修改新增租戶的邏輯 租戶接口:
實現類SysTenantServiceImpl:
package org.jeecg.modules.system.service.impl;import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.util.oConvertUtils; import org.jeecg.modules.system.entity.*; import org.jeecg.modules.system.mapper.SysPermissionMapper; import org.jeecg.modules.system.mapper.SysTenantMapper; import org.jeecg.modules.system.service.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils;import javax.annotation.Resource; import java.io.IOException; import java.util.*;@Service @Slf4j public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant> implements ISysTenantService {@AutowiredISysPermissionService sysPermissionService;@AutowiredISysUserService sysUserService;@AutowiredISysRoleService sysRoleService;@AutowiredISysUserRoleService sysUserRoleService;@AutowiredISysRolePermissionService sysRolePermissionService;private List<SysPermission> getPermissionList(){// 如果設置了BaseFlag字段配置 可以讀取數據庫 /* LambdaQueryWrapper<SysPermission> query = new LambdaQueryWrapper<SysPermission>();query.eq(SysPermission::getBaseFlag, true);query.eq(SysPermission::getTenantId, 1);List<SysPermission> ls = sysPermissionService.list(query);*/// 讀取json 需要自己提前在baseRoute.json文件里配置菜單信息String jsonPath = "static/system/baseRoute.json";ClassPathResource classPathResource = new ClassPathResource(jsonPath);byte[] bytes = new byte[0];try {bytes = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());} catch (IOException e) {e.printStackTrace();}String json = new String(bytes);JSONArray array = JSON.parseArray(json);List<SysPermission> ls = array.toJavaList(SysPermission.class);return ls;}@Override@Transactionalpublic void saveSysTenant(SysTenant sysTenant) {this.save(sysTenant);int tenantId = sysTenant.getId();List<SysPermission> ls = getPermissionList();Collection<String> menuIds = setPermissionTenant(ls, tenantId);sysPermissionService.saveBatch(ls);// 修改admin用戶的租戶SysUser user = sysUserService.getUserByName("admin");String refTenantIds = user.getRelTenantIds();if(oConvertUtils.isEmpty(refTenantIds)){user.setRelTenantIds(String.valueOf(tenantId));}else{user.setRelTenantIds(refTenantIds+","+tenantId);}sysUserService.updateById(user);// 添加admin角色SysRole role = new SysRole();role.setRoleCode(sysTenant.getPreCode()+"_admin");role.setRoleName("管理員");role.setTenantId(tenantId);sysRoleService.save(role);// 添加角色 用戶關系SysUserRole sysUserRole = new SysUserRole();// TODO is ok?sysUserRole.setRoleId(role.getId());sysUserRole.setUserId(user.getId());sysUserRoleService.save(sysUserRole);// 添加角色 菜單關系List<SysRolePermission> list = new ArrayList<>();for(String menuId: menuIds){SysRolePermission sp = new SysRolePermission();sp.setPermissionId(menuId);sp.setRoleId(role.getId());list.add(sp);}sysRolePermissionService.saveBatch(list);}private String randomId(){long id = IdWorker.getId();return String.valueOf(id);}private Collection<String> setPermissionTenant(List<SysPermission> ls, int tenantId){// 循環兩次 第一次設置ID和tenantId 第二次設置pidMap<String, String> map = new HashMap<>();for(SysPermission p: ls){String oldId = p.getId();String newId = randomId();map.put(oldId, newId);p.setId(newId);p.setTenantId(tenantId);p.setCreateBy(null);p.setCreateTime(null);p.setUpdateBy(null);p.setUpdateTime(null);}for(SysPermission p: ls){String oldPid = p.getParentId();if(oConvertUtils.isNotEmpty(oldPid)){String newPid = map.get(oldPid);if(oConvertUtils.isNotEmpty(newPid)){p.setParentId(newPid);}else{// TODO 一般情況下這個newPid是肯定有值的 如果沒有值 說明當前節點的父節點 沒有設置為基礎路由 那么 需要遞歸獲取 所有父級節點 挨個設置一下即可}}}return map.values();} }系統做成多租戶后,新增租戶的時候菜單會復制多份,如果這個時候想再切回來,那么多余的數據需要被清除,可以執行下面的sql:
delete from sys_role_permission where permission_id in (select id from sys_permission where tenant_id <> 1); delete from sys_role_permission where role_id in (select id from sys_role where tenant_id <> 1); delete from sys_permission where tenant_id <> 1;delete from sys_user_role where role_id in (select id from sys_role where tenant_id <> 1); delete from sys_role where tenant_id <> 1;delete from sys_user_depart where dep_id in (select id from sys_depart where tenant_id <> 1); delete from sys_depart where tenant_id <> 1;代碼升級文件:鏈接:https://pan.baidu.com/s/1uwj2khoE6t_IYqfxa57ssQ 提取碼:6v7h
===================================
上文多租戶要求配置一個基礎路由的json文件,里面的數據如果不想手動編寫可以在菜單表添加一個特殊標識 ,然后查詢需要的數據,將數據以json格式輸出,拿到json后配置到baseRoute.json文件即可:
// 這里的baseFlag就是一個特殊標識,說明該菜單需要被多租戶復制分配
query.eq(SysPermission::getBaseFlag, true);query.eq(SysPermission::getTenantId, 1);List<SysPermission> ls = sysPermissionService.list(query); JSONArray array = new JSONArray();for(SysPermission p: ls){Map<String, Object> map = new LinkedHashMap<>();map.put("name", p.getName());map.put("id", p.getId());map.put("parentId", p.getParentId());map.put("component", p.getComponent());map.put("componentName", p.getComponentName());map.put("url", p.getUrl());map.put("route", p.isRoute());map.put("delFlag", p.getDelFlag());map.put("sortNo", p.getSortNo());map.put("icon", p.getIcon());map.put("alwaysShow", p.isAlwaysShow());map.put("hidden", p.isHidden());map.put("internalOrExternal", p.isInternalOrExternal());map.put("keepAlive", p.isKeepAlive());map.put("leaf", p.isLeaf());map.put("menuType", p.getMenuType());map.put("ruleFlag", p.getRuleFlag());JSONObject json = new JSONObject(map);array.add(json);}System.out.println("======================");System.out.println(array.toJSONString());System.out.println("======================");其他:用戶列表的saas實現
不建議直接把用戶表加到配置中,建議單獨做一個列表,針對租戶ID配置通過配置數據權限的方式實現。
因為用戶邏輯代碼太多了,改動成本太高,單獨做一個菜單給只自己看自己租戶的數據即可。
總結
以上是生活随笔為你收集整理的Jeecg-boot 2.4.6+ 多租户改造方案(涉及菜单部门角色等基础模块)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新炬首架梁铭图:从70万字SRE神作提炼
- 下一篇: JSP 标准标签库(JSTL)