|
|
@@ -0,0 +1,329 @@
|
|
|
+package com.sckw.system.service;
|
|
|
+
|
|
|
+import com.sckw.core.exception.SystemException;
|
|
|
+import com.sckw.core.model.constant.Global;
|
|
|
+import com.sckw.core.utils.CollectionUtils;
|
|
|
+import com.sckw.core.utils.IdWorker;
|
|
|
+import com.sckw.core.web.constant.HttpStatus;
|
|
|
+import com.sckw.core.web.context.LoginUserHolder;
|
|
|
+import com.sckw.redis.utils.RedissonUtils;
|
|
|
+import com.sckw.system.dao.KwsDataPermissionDao;
|
|
|
+import com.sckw.system.dao.KwsEnterpriseDao;
|
|
|
+import com.sckw.system.dao.KwsRoleDao;
|
|
|
+import com.sckw.system.model.KwsDataPermission;
|
|
|
+import com.sckw.system.model.KwsEnterprise;
|
|
|
+import com.sckw.system.model.KwsRole;
|
|
|
+import com.sckw.system.model.vo.req.DataPermissionReqVo;
|
|
|
+import com.sckw.system.model.vo.res.DataPermissionEntTreeResVo;
|
|
|
+import com.sckw.system.model.vo.res.DataPermissionResVo;
|
|
|
+import com.sckw.system.model.vo.res.EntBaseInfo;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 数据权限Service
|
|
|
+ *
|
|
|
+ * @author cxf
|
|
|
+ * @date 2026-03-26
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class KwsDataPermissionService {
|
|
|
+
|
|
|
+ /** Redis缓存key前缀:角色数据权限企业ID列表 */
|
|
|
+ private static final String REDIS_DATA_PERM_PREFIX = "dataPermission:role:";
|
|
|
+ /** Redis缓存key前缀:角色个人数据权限标识 */
|
|
|
+ private static final String REDIS_PERSONAL_FLAG_PREFIX = "dataPermission:personal:";
|
|
|
+ /** 缓存过期时间:2小时 */
|
|
|
+ private static final long CACHE_EXPIRE = 2 * 60 * 60L;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private KwsDataPermissionDao kwsDataPermissionDao;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private KwsRoleDao kwsRoleDao;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private KwsEnterpriseDao kwsEnterpriseDao;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存/更新角色数据权限(先删后插)
|
|
|
+ *
|
|
|
+ * @param reqVo 数据权限请求
|
|
|
+ */
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void saveDataPermission(DataPermissionReqVo reqVo) {
|
|
|
+ Long roleId = reqVo.getRoleId();
|
|
|
+ KwsRole kwsRole = kwsRoleDao.selectByKey(roleId);
|
|
|
+ if (Objects.isNull(kwsRole)) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "角色不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 更新角色的个人数据权限标识
|
|
|
+ KwsRole updateRole = new KwsRole();
|
|
|
+ updateRole.setId(roleId);
|
|
|
+ if (Objects.nonNull(reqVo.getPersonalDataFlag())) {
|
|
|
+ kwsRole.setPersonalDataFlag(reqVo.getPersonalDataFlag());
|
|
|
+ updateRole.setPersonalDataFlag(reqVo.getPersonalDataFlag());
|
|
|
+ kwsRoleDao.updateById(updateRole);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 逻辑删除旧的数据权限
|
|
|
+ kwsDataPermissionDao.deleteByRoleId(roleId);
|
|
|
+
|
|
|
+ // 3. 批量插入新的数据权限
|
|
|
+ List<Long> entIds = reqVo.getEntIds();
|
|
|
+ if (CollectionUtils.isNotEmpty(entIds)) {
|
|
|
+ Long userId = LoginUserHolder.getUserId();
|
|
|
+ Date now = new Date();
|
|
|
+ List<KwsDataPermission> permList = new ArrayList<>();
|
|
|
+ for (Long entId : entIds) {
|
|
|
+ KwsDataPermission perm = new KwsDataPermission();
|
|
|
+ perm.setId(new IdWorker(1L).nextId());
|
|
|
+ perm.setRoleId(roleId);
|
|
|
+ perm.setEntId(entId);
|
|
|
+ perm.setStatus(Global.NO);
|
|
|
+ perm.setDelFlag(Global.NO);
|
|
|
+ perm.setCreateBy(userId);
|
|
|
+ perm.setCreateTime(now);
|
|
|
+ perm.setUpdateBy(userId);
|
|
|
+ perm.setUpdateTime(now);
|
|
|
+ permList.add(perm);
|
|
|
+ }
|
|
|
+ if (kwsDataPermissionDao.saveBatch(permList) < permList.size()) {
|
|
|
+ throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 清除该角色的数据权限缓存,使权限立即生效
|
|
|
+ clearCache(roleId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询角色的数据权限配置
|
|
|
+ *
|
|
|
+ * @param roleId 角色ID
|
|
|
+ * @return 数据权限响应
|
|
|
+ */
|
|
|
+ public DataPermissionResVo getDataPermission(Long roleId) {
|
|
|
+ KwsRole kwsRole = kwsRoleDao.selectByKey(roleId);
|
|
|
+ if (Objects.isNull(kwsRole)) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "角色不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ DataPermissionResVo resVo = new DataPermissionResVo();
|
|
|
+ resVo.setRoleId(roleId);
|
|
|
+ resVo.setRoleName(kwsRole.getName());
|
|
|
+ resVo.setPersonalDataFlag(kwsRole.getPersonalDataFlag() != null ? kwsRole.getPersonalDataFlag() : Global.NO);
|
|
|
+
|
|
|
+ List<KwsDataPermission> permList = kwsDataPermissionDao.selectByRoleId(roleId);
|
|
|
+ if (CollectionUtils.isNotEmpty(permList)) {
|
|
|
+ List<Long> entIds = permList.stream().map(KwsDataPermission::getEntId).toList();
|
|
|
+ resVo.setEntIds(entIds);
|
|
|
+ // 查询企业名称
|
|
|
+ List<KwsEnterprise> entList = kwsEnterpriseDao.selectAllByKeys(entIds);
|
|
|
+ if (CollectionUtils.isNotEmpty(entList)) {
|
|
|
+ resVo.setEntNames(entList.stream().map(KwsEnterprise::getFirmName).toList());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ resVo.setEntIds(new ArrayList<>());
|
|
|
+ resVo.setEntNames(new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ return resVo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询数据权限企业树
|
|
|
+ * 客户端:展示当前账号所属企业及下级企业
|
|
|
+ * 平台端:展示所有入驻企业
|
|
|
+ *
|
|
|
+ * @return 企业树
|
|
|
+ */
|
|
|
+ public List<DataPermissionEntTreeResVo> getEntTree() {
|
|
|
+ List<DataPermissionEntTreeResVo> treeList;
|
|
|
+
|
|
|
+ if (LoginUserHolder.isManager()) {
|
|
|
+ // 平台端:展示所有入驻企业
|
|
|
+ List<KwsEnterprise> allEnts = kwsEnterpriseDao.findAllEnterprise();
|
|
|
+ treeList = buildEntTree(allEnts, 0L);
|
|
|
+ } else {
|
|
|
+ // 客户端:展示当前企业及下级企业(递归CTE查询)
|
|
|
+ Long entId = LoginUserHolder.getEntId();
|
|
|
+ List<EntBaseInfo> entBaseInfos = kwsEnterpriseDao.queryCte(entId);
|
|
|
+ if (CollectionUtils.isEmpty(entBaseInfos)) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ treeList = buildEntTreeFromCte(entBaseInfos, entId);
|
|
|
+ }
|
|
|
+
|
|
|
+ return treeList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据角色ID列表级联删除数据权限(角色删除时调用)
|
|
|
+ *
|
|
|
+ * @param roleIds 角色ID列表
|
|
|
+ */
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void deleteByRoleIds(List<Long> roleIds) {
|
|
|
+ if (CollectionUtils.isEmpty(roleIds)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ kwsDataPermissionDao.deleteByRoleIds(roleIds);
|
|
|
+ // 清除所有相关角色的缓存
|
|
|
+ for (Long roleId : roleIds) {
|
|
|
+ clearCache(roleId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 缓存相关 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从缓存获取角色可访问的企业ID列表,缓存未命中则查库并回填
|
|
|
+ *
|
|
|
+ * @param roleId 角色ID
|
|
|
+ * @return 可访问的企业ID集合
|
|
|
+ */
|
|
|
+ public Set<Long> getCachedEntIds(Long roleId) {
|
|
|
+ String cacheKey = REDIS_DATA_PERM_PREFIX + roleId;
|
|
|
+ try {
|
|
|
+ Object cached = RedissonUtils.get(cacheKey);
|
|
|
+ if (Objects.nonNull(cached) && cached instanceof Set) {
|
|
|
+ return (Set<Long>) cached;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("读取数据权限缓存异常, roleId={}", roleId, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 缓存未命中,查库
|
|
|
+ List<KwsDataPermission> permList = kwsDataPermissionDao.selectByRoleId(roleId);
|
|
|
+ Set<Long> entIdSet = CollectionUtils.isEmpty(permList)
|
|
|
+ ? Collections.emptySet()
|
|
|
+ : permList.stream().map(KwsDataPermission::getEntId).collect(Collectors.toSet());
|
|
|
+
|
|
|
+ try {
|
|
|
+ RedissonUtils.put(cacheKey, entIdSet, CACHE_EXPIRE);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("写入数据权限缓存异常, roleId={}", roleId, e);
|
|
|
+ }
|
|
|
+ return entIdSet;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从缓存获取角色的个人数据权限标识
|
|
|
+ *
|
|
|
+ * @param roleId 角色ID
|
|
|
+ * @return true=开启个人数据权限
|
|
|
+ */
|
|
|
+ public boolean getCachedPersonalFlag(Long roleId) {
|
|
|
+ String cacheKey = REDIS_PERSONAL_FLAG_PREFIX + roleId;
|
|
|
+ try {
|
|
|
+ Object cached = RedissonUtils.get(cacheKey);
|
|
|
+ if (Objects.nonNull(cached)) {
|
|
|
+ return Objects.equals(cached, Global.YES);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("读取个人数据权限缓存异常, roleId={}", roleId, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ KwsRole kwsRole = kwsRoleDao.selectByKey(roleId);
|
|
|
+ int flag = (kwsRole != null && kwsRole.getPersonalDataFlag() != null) ? kwsRole.getPersonalDataFlag() : Global.NO;
|
|
|
+ try {
|
|
|
+ RedissonUtils.put(cacheKey, flag, CACHE_EXPIRE);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("写入个人数据权限缓存异常, roleId={}", roleId, e);
|
|
|
+ }
|
|
|
+ return Objects.equals(flag, Global.YES);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清除角色数据权限缓存
|
|
|
+ */
|
|
|
+ public void clearCache(Long roleId) {
|
|
|
+ try {
|
|
|
+ RedissonUtils.delete(REDIS_DATA_PERM_PREFIX + roleId);
|
|
|
+ RedissonUtils.delete(REDIS_PERSONAL_FLAG_PREFIX + roleId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("清除数据权限缓存异常, roleId={}", roleId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 私有方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建企业树(平台端,从全量企业列表构建)
|
|
|
+ */
|
|
|
+ private List<DataPermissionEntTreeResVo> buildEntTree(List<KwsEnterprise> entList, Long rootPid) {
|
|
|
+ if (CollectionUtils.isEmpty(entList)) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ Map<Long, List<KwsEnterprise>> pidMap = entList.stream()
|
|
|
+ .collect(Collectors.groupingBy(e -> e.getPid() != null ? e.getPid() : 0L));
|
|
|
+
|
|
|
+ return buildChildren(pidMap, rootPid);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<DataPermissionEntTreeResVo> buildChildren(Map<Long, List<KwsEnterprise>> pidMap, Long parentId) {
|
|
|
+ List<KwsEnterprise> children = pidMap.get(parentId);
|
|
|
+ if (CollectionUtils.isEmpty(children)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ List<DataPermissionEntTreeResVo> result = new ArrayList<>();
|
|
|
+ for (KwsEnterprise ent : children) {
|
|
|
+ DataPermissionEntTreeResVo node = new DataPermissionEntTreeResVo();
|
|
|
+ node.setId(ent.getId());
|
|
|
+ node.setPid(ent.getPid());
|
|
|
+ node.setFirmName(ent.getFirmName());
|
|
|
+ node.setChildren(buildChildren(pidMap, ent.getId()));
|
|
|
+ result.add(node);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建企业树(客户端,从CTE递归结果构建)
|
|
|
+ */
|
|
|
+ private List<DataPermissionEntTreeResVo> buildEntTreeFromCte(List<EntBaseInfo> entBaseInfos, Long rootEntId) {
|
|
|
+ Map<Long, List<EntBaseInfo>> pidMap = entBaseInfos.stream()
|
|
|
+ .collect(Collectors.groupingBy(e -> e.getPid() != null ? e.getPid() : 0L));
|
|
|
+
|
|
|
+ // 根节点是当前企业
|
|
|
+ List<DataPermissionEntTreeResVo> result = new ArrayList<>();
|
|
|
+ for (EntBaseInfo info : entBaseInfos) {
|
|
|
+ if (Objects.equals(info.getId(), rootEntId)) {
|
|
|
+ DataPermissionEntTreeResVo root = new DataPermissionEntTreeResVo();
|
|
|
+ root.setId(info.getId());
|
|
|
+ root.setPid(info.getPid());
|
|
|
+ root.setFirmName(info.getFirmName());
|
|
|
+ root.setChildren(buildCteChildren(pidMap, info.getId()));
|
|
|
+ result.add(root);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<DataPermissionEntTreeResVo> buildCteChildren(Map<Long, List<EntBaseInfo>> pidMap, Long parentId) {
|
|
|
+ List<EntBaseInfo> children = pidMap.get(parentId);
|
|
|
+ if (CollectionUtils.isEmpty(children)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ List<DataPermissionEntTreeResVo> result = new ArrayList<>();
|
|
|
+ for (EntBaseInfo info : children) {
|
|
|
+ DataPermissionEntTreeResVo node = new DataPermissionEntTreeResVo();
|
|
|
+ node.setId(info.getId());
|
|
|
+ node.setPid(info.getPid());
|
|
|
+ node.setFirmName(info.getFirmName());
|
|
|
+ node.setChildren(buildCteChildren(pidMap, info.getId()));
|
|
|
+ result.add(node);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|