# SvaDataPermissionConfigServiceImpl.java

    package cloud.wconnect.workperformance.service.sva.scheduleactual.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.DataPermissionConfigConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.RoleDTO;
    import cloud.wconnect.commonlib.pojo.dto.UserDTO;
    import cloud.wconnect.commonlib.pojo.dto.UsergroupDTO;
    import cloud.wconnect.commonlib.service.DataPermissionConfigInternalService;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.TransactionUtils;
    import cloud.wconnect.commonlib.util.UpdateLogUtil;
    import cloud.wconnect.workperformance.entity.sva.SvaDataPermissionConfig;
    import cloud.wconnect.workperformance.mapper.sva.SvaDataPermissionConfigMapper;
    import cloud.wconnect.workperformance.pojo.dto.sva.datapermission.SvaDataPermissionConfigDTO;
    import cloud.wconnect.workperformance.pojo.query.sva.datapermission.SvaDataPermissionConfigQuery;
    import cloud.wconnect.workperformance.service.sva.scheduleactual.ISvaDataPermissionConfigService;
    import cloud.wconnect.workperformance.service.sva.scheduleactual.ISvaSendNotifyService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Maps;
    import com.google.common.collect.Sets;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import jakarta.annotation.Resource;

    import java.text.MessageFormat;
    import java.util.*;
    import java.util.concurrent.Executor;
    import java.util.stream.Collectors;

    /**
     * <p>
     * sva 数据权限配置 服务实现类
     * </p>
     *
     * @author zc
     * @since 2023-06-21
     */
    @Service
    @DS("work_performance")
    public class SvaDataPermissionConfigServiceImpl extends ServiceImpl<SvaDataPermissionConfigMapper, SvaDataPermissionConfig> implements ISvaDataPermissionConfigService {
        @Resource
        private DataPermissionConfigInternalService dataPermissionConfigInternalService;
        @Resource
        private ISvaSendNotifyService svaSendNotifyService;
        @Resource
        private UserInternalService userInternalService;
        @Resource
        private BeanMapper beanMapper;
        @Resource
        private UpdateLogUtil updateLogUtil;
        @Resource
        private Executor executor;

        private final List<Integer> checkApiIds = DataPermissionConfigConstant.SVA_API_ID;

        /**
         * 数据权限集合
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return 数据权限集合
         * @author zc
         * @date 2023/6/21
         */
        @Override
        public ListPagedDTO<SvaDataPermissionConfigDTO> list(int tenantId, int propertyId, SvaDataPermissionConfigQuery query) {
            LambdaQueryWrapper<SvaDataPermissionConfig> queryWrapper = new LambdaQueryWrapper<SvaDataPermissionConfig>()
                    .eq(SvaDataPermissionConfig::getTenantId, tenantId)
                    .eq(SvaDataPermissionConfig::getPropertyId, propertyId)
                    .eq(Objects.nonNull(query.getIdentityType()),
                            SvaDataPermissionConfig::getIdentityType, query.getIdentityType())
                    .eq(Objects.nonNull(query.getIdentityId()),
                            SvaDataPermissionConfig::getIdentityId, query.getIdentityId())
                    .eq(Objects.nonNull(query.getStatus()),
                            SvaDataPermissionConfig::getStatus, query.getStatus());

            long total;
            List<SvaDataPermissionConfig> dataPermissionConfigList;
            if (Objects.nonNull(query.getPage()) && Objects.nonNull(query.getSize())) {
                queryWrapper.orderByDesc(SvaDataPermissionConfig::getId);

                Page<SvaDataPermissionConfig> dataPermissionConfigPage = page(new Page<>(query.getPage(),
                        query.getSize()), queryWrapper);

                total = dataPermissionConfigPage.getTotal();
                dataPermissionConfigList = dataPermissionConfigPage.getRecords();
            } else {
                dataPermissionConfigList = list(queryWrapper);
                total = dataPermissionConfigList.size();
            }

            if (CollectionUtils.isEmpty(dataPermissionConfigList)) {
                return new ListPagedDTO<>();
            }

            List<SvaDataPermissionConfigDTO> configDTOList = beanMapper.mapList(dataPermissionConfigList,
                    SvaDataPermissionConfigDTO.class);

            // 组装分类、身份名称
            setIdentityName(tenantId, propertyId, configDTOList);

            return new ListPagedDTO<>(total, configDTOList);
        }

        /**
         * 异步刷新数据权限状态
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @author zc
         * @date 2023/6/21
         */
        @Async
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void dataPermissionFlushStatus(int tenantId, int propertyId) {
            List<SvaDataPermissionConfig> dataPermissionConfigList = list(new QueryWrapper<SvaDataPermissionConfig>().lambda()
                    .eq(SvaDataPermissionConfig::getTenantId, tenantId)
                    .eq(SvaDataPermissionConfig::getPropertyId, propertyId)
                    .ne(SvaDataPermissionConfig::getStatus, DataPermissionConfigConstant.STATUS_WAIT_PASS));
            if (CollectionUtils.isEmpty(dataPermissionConfigList)) {
                return;
            }

            Map<Integer, Set<Integer>> type2IdentityIds = dataPermissionConfigList.stream()
                    .collect(Collectors.groupingBy(SvaDataPermissionConfig::getIdentityType,
                            Collectors.mapping(SvaDataPermissionConfig::getIdentityId, Collectors.toSet())));

            Map<Integer, Map<Integer, Boolean>> identityId2HasApiByType = dataPermissionConfigInternalService
                    .checkContainsApi(tenantId, propertyId, type2IdentityIds, checkApiIds);

            List<SvaDataPermissionConfig> updateList = Lists.newArrayList();
            dataPermissionConfigList.forEach(config -> {
                Map<Integer, Boolean> identityId2HasApi = identityId2HasApiByType.get(config.getIdentityType());
                if (Objects.nonNull(identityId2HasApi)) {
                    int status = identityId2HasApi.getOrDefault(config.getIdentityId(), Boolean.FALSE) ?
                            DataPermissionConfigConstant.STATUS_NORMAL : DataPermissionConfigConstant.STATUS_INVALID;
                    if (!config.getStatus().equals(status)) {
                        updateList.add(SvaDataPermissionConfig
                                .builder()
                                .id(config.getId())
                                .status(status)
                                .build());
                    }
                }
            });

            if (CollectionUtils.isNotEmpty(updateList)) {
                updateBatchById(updateList);
            }
        }

        /**
         * 同步校验数据权限状态
         *
         * @param tenantId     租户 id
         * @param propertyId   资产 id
         * @param identityType 身份类别（0：角色(Role)；1：用户组(UserGroup)；2：用户(User)）
         * @param identityId   identityId
         * @author zc
         * @date 2023/6/21
         */
        @Override
        public void verifyDataPermissionStatus(int tenantId, int propertyId, int identityType, int identityId) {
            boolean valid = dataPermissionConfigInternalService.verifyDataPermissionStatus(tenantId, propertyId, checkApiIds,
                    identityId, identityType);
            if (!valid) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_PERMISSION_INVALID,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The data permission is invalid");
            }
        }

        /**
         * 校验数据权限数据是否重复
         *
         * @param tenantId     租户 id
         * @param propertyId   资产 id
         * @param id           数据权限数据 id
         * @param identityType 身份类别（0：角色(Role)；1：用户组(UserGroup)；2：用户(User)）
         * @param identityId   身份数据 id
         * @author zc
         * @date 2023/6/3
         */
        @Override
        public void checkAlreadyExist(int tenantId, int propertyId, Integer id, int identityType, int identityId) {
            long count = count(new QueryWrapper<SvaDataPermissionConfig>().lambda()
                    .eq(SvaDataPermissionConfig::getTenantId, tenantId)
                    .eq(SvaDataPermissionConfig::getPropertyId, propertyId)
                    .ne(Objects.nonNull(id), SvaDataPermissionConfig::getId, id)
                    .eq(SvaDataPermissionConfig::getIdentityType, identityType)
                    .eq(SvaDataPermissionConfig::getIdentityId, identityId));
            if (count > 0) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_EXISTED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The dataPermissionConfig is already exist.");
            }
        }

        /**
         * 保存数据权限
         *
         * @param tenantId                租户 id
         * @param propertyId              资产 id
         * @param dataPermissionConfigDTO 数据权限内容
         * @param loginUserId             登录用户id
         * @return id
         * @author zc
         * @date 2023/6/21
         */
        @Override
        public int save(int tenantId, int propertyId, SvaDataPermissionConfigDTO dataPermissionConfigDTO, int loginUserId) {
            SvaDataPermissionConfig dataPermissionConfig = beanMapper.map(dataPermissionConfigDTO,
                    SvaDataPermissionConfig.class, item -> {
                        item.setTenantId(tenantId);
                        item.setPropertyId(propertyId);
                        item.setStatus(DataPermissionConfigConstant.STATUS_NORMAL);
                        item.setUpdateLog(updateLogUtil.get(loginUserId));
                    });

            save(dataPermissionConfig);

            return dataPermissionConfig.getId();
        }

        /**
         * 校验数据是否存在并返回数据权限数据
         *
         * @param id id
         * @return SvaDataPermissionConfig
         * @author zc
         * @date 2023/6/21
         */
        @Override
        public SvaDataPermissionConfig checkExist(int id) {
            SvaDataPermissionConfig config = getById(id);
            if (Objects.isNull(config)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The dataPermissionConfig is not found; id:" + id);
            }
            return config;
        }

        /**
         * 校验数据归属
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param config     数据权限数据
         * @author zc
         * @date 2023/6/21
         */
        @Override
        public void checkBelong(int tenantId, int propertyId, SvaDataPermissionConfig config) {
            if (!config.getTenantId().equals(tenantId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("The dataPermissionConfig does not belong to this tenant; tenantId:{0}; dataPermissionId:{1}",
                                tenantId, config.getId()));
            }

            if (!config.getPropertyId().equals(propertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("The dataPermissionConfig does not belong to this property; propertyId:{0}; dataPermissionId:{1}",
                                propertyId, config.getId()));
            }
        }

        /**
         * 更新数据权限
         *
         * @param originConfig            原数据权限
         * @param dataPermissionConfigDTO 数据权限内容
         * @param loginUserId             登录用户id
         * @author zc
         * @date 2023/6/21
         */
        @Override
        public void update(SvaDataPermissionConfig originConfig, SvaDataPermissionConfigDTO dataPermissionConfigDTO, int loginUserId) {
            SvaDataPermissionConfig dataPermissionConfig = beanMapper.map(dataPermissionConfigDTO, SvaDataPermissionConfig.class,
                    item -> {
                        item.setId(originConfig.getId());
                        item.setStatus(DataPermissionConfigConstant.STATUS_NORMAL);
                        item.setUpdateLog(updateLogUtil.append(originConfig.getUpdateLog(), loginUserId));
                    });

            updateById(dataPermissionConfig);
        }

        /**
         * 数据权限审批
         *
         * @param tenantId     租户 id
         * @param propertyId   资产 id
         * @param originConfig 原数据权限
         * @author zc
         * @date 2023/6/22
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void approval(int tenantId, int propertyId, SvaDataPermissionConfig originConfig) {
            Map<Integer, Set<Integer>> type2IdentityIds = Maps.newHashMap();
            type2IdentityIds.put(originConfig.getIdentityType(), Sets.newHashSet(originConfig.getIdentityId()));

            Map<Integer, Map<Integer, Boolean>> id2HasApiByType = dataPermissionConfigInternalService
                    .checkContainsApi(tenantId, propertyId, type2IdentityIds, checkApiIds);

            int status = id2HasApiByType.getOrDefault(originConfig.getIdentityType(), Collections.emptyMap())
                    .getOrDefault(originConfig.getIdentityId(), Boolean.FALSE) ? DataPermissionConfigConstant.STATUS_NORMAL :
                    DataPermissionConfigConstant.STATUS_INVALID;

            originConfig.setStatus(status);
            updateById(originConfig);

            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                // 数据权限审批通知
                svaSendNotifyService.dataPermissionApproval(originConfig);
            });
        }

        /**
         * 校验用户是否拥有数据权限
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param userId     用户 id
         * @return boolean true：有权限；false：无权限
         * @author zc
         * @date 2023/6/26
         */
        @Override
        public boolean verifyDataPermissionAccess(int tenantId, int propertyId, int userId) {
            UserDTO userDTO = userInternalService.getOneByProperty(tenantId, propertyId, userId);

            // 用户拥有的角色 id
            List<Integer> roleIds = userDTO.getRoles().stream()
                    .map(RoleDTO::getId).collect(Collectors.toList());

            // 用户拥有的用户组 id
            List<Integer> userGroupIds = userDTO.getUserGroups().stream()
                    .map(UsergroupDTO::getId).collect(Collectors.toList());

            LambdaQueryWrapper<SvaDataPermissionConfig> queryWrapper = new QueryWrapper<SvaDataPermissionConfig>().lambda()
                    .eq(SvaDataPermissionConfig::getTenantId, tenantId)
                    .eq(SvaDataPermissionConfig::getPropertyId, propertyId)
                    .eq(SvaDataPermissionConfig::getStatus, DataPermissionConfigConstant.STATUS_NORMAL)
                    .and(query -> query
                            .nested(q -> q
                                    .eq(SvaDataPermissionConfig::getIdentityType, DataPermissionConfigConstant.IDENTITY_TYPE_USER)
                                    .in(SvaDataPermissionConfig::getIdentityId, userId))
                            .or(CollectionUtils.isNotEmpty(roleIds), q -> q
                                    .eq(SvaDataPermissionConfig::getIdentityType, DataPermissionConfigConstant.IDENTITY_TYPE_ROLE)
                                    .in(SvaDataPermissionConfig::getIdentityId, roleIds))
                            .or(CollectionUtils.isNotEmpty(userGroupIds), q -> q
                                    .eq(SvaDataPermissionConfig::getIdentityType, DataPermissionConfigConstant.IDENTITY_TYPE_USER_GROUP)
                                    .in(SvaDataPermissionConfig::getIdentityId, userGroupIds))
                    );

            return count(queryWrapper) > 0;
        }

        /**
         * 申请数据权限
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param identityId 身份数据 id
         * @author zc
         * @date 2023/6/27
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void apply(int tenantId, int propertyId, int identityId) {
            // 创建等待通过的数据
            SvaDataPermissionConfig dataPermissionConfig = SvaDataPermissionConfig
                    .builder()
                    .tenantId(tenantId)
                    .propertyId(propertyId)
                    .identityType(DataPermissionConfigConstant.IDENTITY_TYPE_USER)
                    .identityId(identityId)
                    .status(DataPermissionConfigConstant.STATUS_WAIT_PASS)
                    .updateLog(updateLogUtil.get(identityId))
                    .build();
            save(dataPermissionConfig);

            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                // 数据权限申请通知
                svaSendNotifyService.dataPermissionApply(dataPermissionConfig);
            });
        }

        /**
         * 组装分类、身份名称
         *
         * @param tenantId                 租户 id
         * @param propertyId               资产 id
         * @param dataPermissionConfigList 数据权限数据
         * @author zc
         * @date 2023/6/2
         */
        private void setIdentityName(int tenantId, int propertyId, List<SvaDataPermissionConfigDTO> dataPermissionConfigList) {
            Map<Integer, Set<Integer>> type2ConfigSet = dataPermissionConfigList.stream()
                    .collect(Collectors.groupingBy(SvaDataPermissionConfigDTO::getIdentityType,
                            Collectors.mapping(SvaDataPermissionConfigDTO::getIdentityId, Collectors.toSet())));

            // 获取角色名称
            Map<Integer, String> roleId2Name = dataPermissionConfigInternalService.getIdentityName(tenantId, propertyId,
                    DataPermissionConfigConstant.IDENTITY_TYPE_ROLE,
                    type2ConfigSet.getOrDefault(DataPermissionConfigConstant.IDENTITY_TYPE_ROLE, Collections.emptySet()));

            // 获取用户组名称
            Map<Integer, String> userGroupId2Name = dataPermissionConfigInternalService.getIdentityName(tenantId, propertyId,
                    DataPermissionConfigConstant.IDENTITY_TYPE_USER_GROUP,
                    type2ConfigSet.getOrDefault(DataPermissionConfigConstant.IDENTITY_TYPE_USER_GROUP, Collections.emptySet()));

            // 获取用户名称
            Map<Integer, String> userId2Name = dataPermissionConfigInternalService.getIdentityName(tenantId, propertyId,
                    DataPermissionConfigConstant.IDENTITY_TYPE_USER,
                    type2ConfigSet.getOrDefault(DataPermissionConfigConstant.IDENTITY_TYPE_USER, Collections.emptySet()));

            for (SvaDataPermissionConfigDTO config : dataPermissionConfigList) {
                // 设置身份名称
                switch (config.getIdentityType()) {
                    case DataPermissionConfigConstant.IDENTITY_TYPE_ROLE:
                        config.setIdentity(roleId2Name.get(config.getIdentityId()));
                        break;
                    case DataPermissionConfigConstant.IDENTITY_TYPE_USER_GROUP:
                        config.setIdentity(userGroupId2Name.get(config.getIdentityId()));
                        break;
                    case DataPermissionConfigConstant.IDENTITY_TYPE_USER:
                        config.setIdentity(userId2Name.get(config.getIdentityId()));
                        break;
                    default:
                }
            }
        }
    }