@@ -10,8 +10,12 @@ import com.gxwebsoft.common.core.utils.DomainUtil;
import com.gxwebsoft.common.core.utils.RedisUtil ;
import com.gxwebsoft.common.system.entity.* ;
import com.gxwebsoft.common.system.mapper.MenuMapper ;
import com.gxwebsoft.common.system.mapper.RoleMapper ;
import com.gxwebsoft.common.system.mapper.RoleMenuMapper ;
import com.gxwebsoft.common.system.mapper.UserRoleMapper ;
import com.gxwebsoft.common.system.mapper.TenantMapper ;
import com.gxwebsoft.common.system.param.MenuParam ;
import com.gxwebsoft.common.system.param.RoleParam ;
import com.gxwebsoft.common.system.service.* ;
import com.gxwebsoft.common.system.param.TenantParam ;
import com.gxwebsoft.common.core.web.ApiResult ;
@@ -26,6 +30,7 @@ import java.util.ArrayList;
import java.util.Comparator ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.LinkedHashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
@@ -37,10 +42,8 @@ import java.util.stream.Collectors;
* @author 科技小王子
* @since 2023-07-17 17:49:53
*/
@Service
public class TenantServiceImpl extends ServiceImpl < TenantMapper , Tenant > implements TenantService {
// cms建站权限标识前缀
private static final String CMS_AUTHORITY_PREFIX = " cms: " ;
@Service
public class TenantServiceImpl extends ServiceImpl < TenantMapper , Tenant > implements TenantService {
@Resource
private CompanyService companyService ;
@@ -49,6 +52,12 @@ public class TenantServiceImpl extends ServiceImpl<TenantMapper, Tenant> impleme
@Resource
private MenuMapper menuMapper ;
@Resource
private RoleMapper roleMapper ;
@Resource
private RoleMenuMapper roleMenuMapper ;
@Resource
private UserRoleMapper userRoleMapper ;
@Resource
private RoleMenuService roleMenuService ;
@Resource
private RoleService roleService ;
@@ -654,21 +663,70 @@ public class TenantServiceImpl extends ServiceImpl<TenantMapper, Tenant> impleme
@Override
@Transactional ( rollbackFor = { Exception . class } )
public ApiResult < ? > grantCmsPermission ( Integer refTenant , Integer curTenant ) {
// 1.当前租户的超级管理员角色, 必须带租户条件
final Role superAdmin = roleService . getOne ( new LambdaQueryWrapper < Role > ( )
. eq ( Role : : getRoleCode , " superAdmin " )
. eq ( Role : : getTenantId , curTenant )
. last ( " limit 1 " ) ) ;
if ( superAdmin = = null ) {
return new ApiResult < > ( Constants . RESULT_ERROR_CODE , " 当前租户没有superAdmin角色, 无法授权 " ) ;
if ( refTenant = = null | | curTenant = = null ) {
return new ApiResult < > ( Constants . RESULT_ERROR_CODE , " 参考租户或当前租户为空 " ) ;
}
final Integer roleId = superAdmin . getRoleId ( ) ;
if ( refTenant . equals ( curTenant ) ) {
return new ApiResult < > ( Constants . RESULT_ERROR_CODE , " 参考租户不能与当前租户相同 " ) ;
}
final Map < String , Object > data = new HashMap < > ( ) ;
data . put ( " refTenantId " , refTenant ) ;
data . put ( " tenantId " , curTenant ) ;
// 2.跨租户读取参考租户的全部未删除菜单
final Menu Param refParam = new Menu Param( ) ;
refParam . setTenantId ( refTenant ) ;
refParam . setDeleted ( 0 ) ;
final List < Menu > refMenus = menuMapper . getMenuByClone ( refParam ) ;
// ===== 1. 克隆角色 sys_role( 按 role_code 去重, superAdmin/admin/user 已存在则复用)=====
final Role Param refRole Param = new Role Param( ) ;
refRole Param . setTenantId ( refTenant ) ;
final List < Role > refRoles = roleMapper . selectListAll ( refRoleParam ) ;
if ( CollectionUtils . isEmpty ( refRoles ) ) {
return new ApiResult < > ( Constants . RESULT_ERROR_CODE , " 参考租户[ " + refTenant + " ]没有可复制的角色 " ) ;
}
final RoleParam curRoleParam = new RoleParam ( ) ;
curRoleParam . setTenantId ( curTenant ) ;
// role_code -> Role( 当前租户已存在的角色)
final Map < String , Role > curRoleByCode = roleMapper . selectListAll ( curRoleParam ) . stream ( )
. collect ( Collectors . toMap ( Role : : getRoleCode , r - > r , ( a , b ) - > a ) ) ;
// oldRoleId -> newRoleId 映射
final Map < Integer , Integer > roleIdMapping = new HashMap < > ( ) ;
// 待新增角色(按 role_code 去重)
final Map < String , Role > toCreateByCode = new LinkedHashMap < > ( ) ;
for ( Role r : refRoles ) {
final Role exist = curRoleByCode . get ( r . getRoleCode ( ) ) ;
if ( exist ! = null ) {
roleIdMapping . put ( r . getRoleId ( ) , exist . getRoleId ( ) ) ;
} else if ( ! toCreateByCode . containsKey ( r . getRoleCode ( ) ) ) {
final Role copy = new Role ( ) ;
copy . setRoleCode ( r . getRoleCode ( ) ) ;
copy . setRoleName ( r . getRoleName ( ) ) ;
copy . setComments ( r . getComments ( ) ) ;
copy . setSortNumber ( r . getSortNumber ( ) ) ;
copy . setTenantId ( curTenant ) ;
toCreateByCode . put ( r . getRoleCode ( ) , copy ) ;
}
}
int roleCreated = 0 ;
if ( ! toCreateByCode . isEmpty ( ) ) {
roleService . saveBatch ( new ArrayList < > ( toCreateByCode . values ( ) ) ) ;
// 重新读取当前租户角色,回填 oldRoleId -> newRoleId 映射
final Map < String , Role > refreshed = roleMapper . selectListAll ( curRoleParam ) . stream ( )
. collect ( Collectors . toMap ( Role : : getRoleCode , r - > r , ( a , b ) - > a ) ) ;
for ( Role r : refRoles ) {
final Role nr = refreshed . get ( r . getRoleCode ( ) ) ;
if ( nr ! = null ) {
roleIdMapping . put ( r . getRoleId ( ) , nr . getRoleId ( ) ) ;
curRoleByCode . putIfAbsent ( r . getRoleCode ( ) , nr ) ;
}
}
roleCreated = toCreateByCode . size ( ) ;
}
// 当前租户全部角色: roleId -> Role( 含新克隆的) , 供后续 role_code 对齐使用
final Map < Integer , Role > newRoleById = new HashMap < > ( ) ;
curRoleByCode . values ( ) . forEach ( r - > newRoleById . put ( r . getRoleId ( ) , r ) ) ;
// ===== 2. 克隆菜单 sys_menu( 按层级复制, 父先于子, 标题/权限去重)=====
final MenuParam refMenuParam = new MenuParam ( ) ;
refMenuParam . setTenantId ( refTenant ) ;
refMenuParam . setDeleted ( 0 ) ;
final List < Menu > refMenus = menuMapper . getMenuByClone ( refMenuParam ) ;
if ( CollectionUtils . isEmpty ( refMenus ) ) {
return new ApiResult < > ( Constants . RESULT_ERROR_CODE , " 参考租户[ " + refTenant + " ]没有可复制的菜单 " ) ;
}
@@ -676,42 +734,19 @@ public class TenantServiceImpl extends ServiceImpl<TenantMapper, Tenant> impleme
for ( Menu menu : refMenus ) {
refMenuMap . put ( menu . getMenuId ( ) , menu ) ;
}
// 3.筛选cms:*子树, 权限标识以cms:开头的节点 + 其全部祖先, 保证父级完整
final Set < Integer > cmsMenuIds = new HashSet < > ( ) ;
for ( Menu menu : refMenus ) {
if ( menu . getAuthority ( ) = = null | | ! menu . getAuthority ( ) . startsWith ( CMS_AUTHORITY_PREFIX ) ) {
continue ;
}
Menu current = menu ;
// add返回false说明该节点及其祖先已收录, 无需继续向上追溯
while ( current ! = null & & cmsMenuIds . add ( current . getMenuId ( ) ) ) {
final Integer parentId = current . getParentId ( ) ;
current = ( parentId = = null | | parentId = = 0 ) ? null : refMenuMap . get ( parentId ) ;
}
}
if ( cmsMenuIds . isEmpty ( ) ) {
return new ApiResult < > ( Constants . RESULT_ERROR_CODE , " 参考租户[ " + refTenant + " ]没有cms权限菜单 " ) ;
}
// 4.当前租户已有菜单, 用于去重, 保证接口幂等
final MenuParam curParam = new MenuParam ( ) ;
curParam . setTenantId ( curTenant ) ;
curParam . setDeleted ( 0 ) ;
final MenuParam curMenuParam = new MenuParam ( ) ;
curMenuParam . setTenantId ( curTenant ) ;
curMenuParam . setDeleted ( 0 ) ;
final Map < String , Integer > curMenuKeys = new HashMap < > ( ) ;
for ( Menu menu : menuMapper . getMenuByClone ( curParam ) ) {
for ( Menu menu : menuMapper . getMenuByClone ( curMenu Param ) ) {
curMenuKeys . putIfAbsent ( getMenuKey ( menu ) , menu . getMenuId ( ) ) ;
}
// 5.按层级复制菜单, 父菜单先于子菜单插入
final List < Menu > sources = refMenus . stream ( )
. filter ( d - > cmsMenuIds . contains ( d . getMenuId ( ) ) )
. sorted ( Comparator . comparingInt ( ( Menu d ) - > getMenuDepth ( d , refMenuMap ) ) )
. collect ( Collectors . toList ( ) ) ;
final Map < Integer , Integer > menuIdMapping = new HashMap < > ( ) ;
int menuCount = 0 ;
for ( Menu source : sources ) {
// 已存在的菜单不重复插入, 直接复用其菜单id作为子菜单的父级
final Integer existsMenuId = curMenuKeys . get ( getMenuKey ( source ) ) ;
if ( existsMenuId ! = null ) {
menuIdMapping . put ( source . getMenuId ( ) , existsMenuId ) ;
@@ -737,32 +772,88 @@ public class TenantServiceImpl extends ServiceImpl<TenantMapper, Tenant> impleme
menuCount + + ;
}
// 6.绑定cms菜单到超级管理员角色, 已绑定的跳过
final Set < Integer > boundMenuId s = roleMenuService . list ( new LambdaQueryWrapper < RoleMenu > ( )
. eq ( RoleMenu : : getRoleId , roleId ) )
. stream ( ) . map ( RoleMenu : : getMenuId ) . collect( Collectors . toSet ( ) ) ;
final List < RoleMenu > roleMenus = new ArrayList < > ( ) ;
for ( Integer menuId : m enuId Mapping . values ( ) ) {
if ( menuId = = null | | boundMenuIds . contains ( menuId ) ) {
// ===== 3. 克隆角色菜单 sys_role_menu( roleId/menuId 用映射转换,去重)=====
final Set < String > curRmKey s = roleMenuMapper . selectListAll ( curTenant ) . stream ( )
. map ( rm - > rm . getRoleId ( ) + " _ " + rm . getMenuId ( ) )
. collect ( Collectors . toSet ( ) ) ;
final List < RoleMenu > rmsToSave = new ArrayList < > ( ) ;
for ( RoleMenu rm : roleM enuMapper . selectListAll ( refTenant ) ) {
final Integer newRoleId = roleIdMapping . get ( rm . getRoleId ( ) ) ;
final Integer newMenuId = menuIdMapping . get ( rm . getMenuId ( ) ) ;
if ( newRoleId = = null | | newMenuId = = null ) {
continue ;
}
final RoleMenu roleMenu = new RoleMenu ( ) ;
roleMenu . setRoleId ( roleId ) ;
roleMenu . setMenuId ( menuId ) ;
roleMenu . setTenantId ( curTenant ) ;
roleMenus . add ( roleMenu ) ;
final String key = newRoleId + " _ " + newMenuId ;
if ( curRmKeys . contains ( key ) ) {
continue ;
}
if ( ! roleMenus . isEmpty ( ) ) {
roleMenuService . saveBatch ( roleMenus ) ;
final RoleMenu copy = new RoleMenu ( ) ;
copy . setRoleId ( newRoleId ) ;
copy . setMenuId ( newMenuId ) ;
copy . setTenantId ( curTenant ) ;
rmsToSave . add ( copy ) ;
curRmKeys . add ( key ) ;
}
int roleMenuCreated = 0 ;
if ( ! rmsToSave . isEmpty ( ) ) {
roleMenuService . saveBatch ( rmsToSave ) ;
roleMenuCreated = rmsToSave . size ( ) ;
}
final Map < String , Object > data = new HashMap < > ( ) ;
data . put ( " refTenantId " , ref Tenant) ;
data . put ( " tenantId " , curTenant ) ;
data . put ( " roleId " , r oleId ) ;
// ===== 4. 克隆用户角色 sys_user_role( 按 role_code 对齐当前租户用户,去重)=====
final List < UserRole > curUserRoles = userRoleMapper . selectListAll ( cur Tenant) ;
final Set < String > curUrKeys = curUserRoles . stream ( )
. map ( ur - > ur . getUserId ( ) + " _ " + ur . getR oleId ( ))
. collect ( Collectors . toSet ( ) ) ;
// 当前租户 userId -> 其 role_code 集合
final Map < Integer , Set < String > > userRoleCodes = new HashMap < > ( ) ;
for ( UserRole ur : curUserRoles ) {
final Role role = newRoleById . get ( ur . getRoleId ( ) ) ;
if ( role = = null ) {
continue ;
}
userRoleCodes . computeIfAbsent ( ur . getUserId ( ) , k - > new HashSet < > ( ) ) . add ( role . getRoleCode ( ) ) ;
}
final List < UserRole > ursToSave = new ArrayList < > ( ) ;
for ( UserRole ur : userRoleMapper . selectListAll ( refTenant ) ) {
final Integer newRoleId = roleIdMapping . get ( ur . getRoleId ( ) ) ;
if ( newRoleId = = null ) {
continue ;
}
final Role newRole = newRoleById . get ( newRoleId ) ;
if ( newRole = = null ) {
continue ;
}
final String roleCode = newRole . getRoleCode ( ) ;
// 找当前租户中 role_code 相同的用户,建立绑定
for ( Map . Entry < Integer , Set < String > > e : userRoleCodes . entrySet ( ) ) {
if ( ! e . getValue ( ) . contains ( roleCode ) ) {
continue ;
}
final String key = e . getKey ( ) + " _ " + newRoleId ;
if ( curUrKeys . contains ( key ) ) {
continue ;
}
final UserRole copy = new UserRole ( ) ;
copy . setUserId ( e . getKey ( ) ) ;
copy . setRoleId ( newRoleId ) ;
copy . setTenantId ( curTenant ) ;
ursToSave . add ( copy ) ;
curUrKeys . add ( key ) ;
}
}
int userRoleCreated = 0 ;
if ( ! ursToSave . isEmpty ( ) ) {
userRoleService . saveBatch ( ursToSave ) ;
userRoleCreated = ursToSave . size ( ) ;
}
data . put ( " roleTotal " , refRoles . size ( ) ) ;
data . put ( " roleCreated " , roleCreated ) ;
data . put ( " menuTotal " , menuIdMapping . size ( ) ) ;
data . put ( " menuCreated " , menuCount ) ;
data . put ( " roleMenuCreated " , roleMenus . size ( ) ) ;
data . put ( " roleMenuCreated " , roleMenuCreated ) ;
data . put ( " userRoleCreated " , userRoleCreated ) ;
return new ApiResult < > ( Constants . RESULT_OK_CODE , " 授权成功 " , data ) ;
}