# OvertimeDataPermissionConfigServiceImpl.java

    package cloud.wconnect.workperformance.service.mor.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.mor.OvertimeDataPermissionConfig;
    import cloud.wconnect.workperformance.mapper.mor.OvertimeDataPermissionConfigMapper;
    import cloud.wconnect.workperformance.pojo.dto.mor.MorDataPermissionConfigDTO;
    import cloud.wconnect.workperformance.pojo.query.mor.MorDataPermissionConfigQuery;
    import cloud.wconnect.workperformance.service.mor.IMorSendNotifyService;
    import cloud.wconnect.workperformance.service.mor.IOvertimeDataPermissionConfigService;
    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>
     * 数据权限配置 服务实现类
     * </p>
     *
     * @author zc
     * @since 2023-11-22
     */
    @Service
    @DS("work_performance")
    public class OvertimeDataPermissionConfigServiceImpl extends ServiceImpl<OvertimeDataPermissionConfigMapper, OvertimeDataPermissionConfig> implements IOvertimeDataPermissionConfigService {
        @Resource
        private DataPermissionConfigInternalService dataPermissionConfigInternalService;
        @Resource
        private IMorSendNotifyService sendNotifyService;
        @Resource
        private Executor executor;
        @Resource
        private UserInternalService userInternalService;
        @Resource
        private BeanMapper beanMapper;
        @Resource
        private UpdateLogUtil updateLogUtil;

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

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

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

            List<OvertimeDataPermissionConfig> 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(OvertimeDataPermissionConfig
                                .builder()
                                .id(config.getId())
                                .status(status)
                                .build());
                    }
                }
            });

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

        /**
         * 数据权限集合
         *
         * @param tenantId    租户 id
         * @param propertyId  资产 id
         * @param configQuery 查询条件
         * @return 数据权限集合
         * @author zc
         * @date 2023/11/22
         */
        @Override
        public ListPagedDTO<MorDataPermissionConfigDTO> list(int tenantId, int propertyId, MorDataPermissionConfigQuery configQuery) {
            LambdaQueryWrapper<OvertimeDataPermissionConfig> queryWrapper =
                    new QueryWrapper<OvertimeDataPermissionConfig>().lambda()
                            .eq(OvertimeDataPermissionConfig::getTenantId, tenantId)
                            .eq(OvertimeDataPermissionConfig::getPropertyId, propertyId)
                            .eq(Objects.nonNull(configQuery.getIdentityType()),
                                    OvertimeDataPermissionConfig::getIdentityType, configQuery.getIdentityType())
                            .eq(Objects.nonNull(configQuery.getIdentityId()),
                                    OvertimeDataPermissionConfig::getIdentityId, configQuery.getIdentityId())
                            .eq(Objects.nonNull(configQuery.getStatus()),
                                    OvertimeDataPermissionConfig::getStatus, configQuery.getStatus());

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

                Page<OvertimeDataPermissionConfig> dataPermissionConfigPage = page(new Page<>(configQuery.getPage(),
                        configQuery.getSize()), queryWrapper);

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

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

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

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

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

        /**
         * 组装分类、身份名称
         *
         * @param tenantId                 租户 id
         * @param propertyId               资产 id
         * @param dataPermissionConfigList 数据权限数据
         * @author zc
         * @date 2023/11/22
         */
        private void setIdentityName(int tenantId, int propertyId, List<MorDataPermissionConfigDTO> dataPermissionConfigList) {
            Map<Integer, Set<Integer>> type2ConfigSet = dataPermissionConfigList.stream()
                    .collect(Collectors.groupingBy(MorDataPermissionConfigDTO::getIdentityType,
                            Collectors.mapping(MorDataPermissionConfigDTO::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 (MorDataPermissionConfigDTO config : dataPermissionConfigList) {
                // 设置身份名称
                switch (config.getIdentityType()) {
                    case DataPermissionConfigConstant.IDENTITY_TYPE_ROLE -> {
                        config.setIdentity(roleId2Name.get(config.getIdentityId()));
                    }
                    case DataPermissionConfigConstant.IDENTITY_TYPE_USER_GROUP -> {
                        config.setIdentity(userGroupId2Name.get(config.getIdentityId()));
                    }
                    case DataPermissionConfigConstant.IDENTITY_TYPE_USER -> {
                        config.setIdentity(userId2Name.get(config.getIdentityId()));
                    }
                    default -> {
                    }
                }
            }
        }

        /**
         * 校验数据权限数据是否重复
         *
         * @param tenantId     租户 id
         * @param propertyId   资产 id
         * @param id           数据权限数据 id
         * @param identityType 身份类别（0：角色(Role)；1：用户组(UserGroup)；2：用户(User)）
         * @param identityId   身份数据 id
         * @author zc
         * @date 2023/11/22
         */
        @Override
        public void checkAlreadyExist(int tenantId, int propertyId, Integer id, int identityType, int identityId) {
            long count = count(new QueryWrapper<OvertimeDataPermissionConfig>().lambda()
                    .eq(OvertimeDataPermissionConfig::getTenantId, tenantId)
                    .eq(OvertimeDataPermissionConfig::getPropertyId, propertyId)
                    .ne(Objects.nonNull(id), OvertimeDataPermissionConfig::getId, id)
                    .eq(OvertimeDataPermissionConfig::getIdentityType, identityType)
                    .eq(OvertimeDataPermissionConfig::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 identityType 身份类别（0：角色(Role)；1：用户组(UserGroup)；2：用户(User)）
         * @param identityId   identityId
         * @author zc
         * @date 2023/11/22
         */
        @Override
        public void verifyDataPermissionStatus(int tenantId, int propertyId, int identityType, int identityId) {
            boolean valid = dataPermissionConfigInternalService.verifyDataPermissionStatus(tenantId,
                    propertyId, DataPermissionConfigConstant.MOR_OVERTIME_API_ID, 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 dataPermissionConfigDTO 数据权限内容
         * @param loginUserId             登录用户id
         * @return id
         * @author zc
         * @date 2023/11/22
         */
        @Override
        public int save(int tenantId, int propertyId, MorDataPermissionConfigDTO dataPermissionConfigDTO, int loginUserId) {
            OvertimeDataPermissionConfig dataPermissionConfig = beanMapper.map(dataPermissionConfigDTO,
                    OvertimeDataPermissionConfig.class, item -> {
                        item.setTenantId(tenantId);
                        item.setPropertyId(propertyId);
                        item.setStatus(DataPermissionConfigConstant.STATUS_NORMAL);
                        item.setUpdateLog(updateLogUtil.get(loginUserId));
                    });

            save(dataPermissionConfig);

            return dataPermissionConfig.getId();
        }

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

            updateById(dataPermissionConfig);
        }

        /**
         * 校验数据是否存在并返回数据权限数据
         *
         * @param id id
         * @return OvertimeDataPermissionConfig
         * @author zc
         * @date 2023/11/22
         */
        @Override
        public OvertimeDataPermissionConfig checkExist(int id) {
            OvertimeDataPermissionConfig 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/11/22
         */
        @Override
        public void checkBelong(int tenantId, int propertyId, OvertimeDataPermissionConfig 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 tenantId     租户 id
         * @param propertyId   资产 id
         * @param originConfig 原数据权限
         * @author zc
         * @date 2023/11/22
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void approval(int tenantId, int propertyId, OvertimeDataPermissionConfig 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, DataPermissionConfigConstant.MOR_OVERTIME_API_ID);

            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, () -> {
                // 数据权限审批通知
                sendNotifyService.dataPermissionApproval(originConfig);
            });
        }

        /**
         * 校验用户是否拥有数据权限
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param userId     用户 id
         * @return boolean true：有权限；false：无权限
         * @author zc
         * @date 2023/11/22
         */
        @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<OvertimeDataPermissionConfig> queryWrapper = new QueryWrapper<OvertimeDataPermissionConfig>().lambda()
                    .eq(OvertimeDataPermissionConfig::getTenantId, tenantId)
                    .eq(OvertimeDataPermissionConfig::getPropertyId, propertyId)
                    .eq(OvertimeDataPermissionConfig::getStatus, DataPermissionConfigConstant.STATUS_NORMAL)
                    .and(query -> query
                            .nested(q -> q
                                    .eq(OvertimeDataPermissionConfig::getIdentityType, DataPermissionConfigConstant.IDENTITY_TYPE_USER)
                                    .in(OvertimeDataPermissionConfig::getIdentityId, userId))
                            .or(CollectionUtils.isNotEmpty(roleIds), q -> q
                                    .eq(OvertimeDataPermissionConfig::getIdentityType, DataPermissionConfigConstant.IDENTITY_TYPE_ROLE)
                                    .in(OvertimeDataPermissionConfig::getIdentityId, roleIds))
                            .or(CollectionUtils.isNotEmpty(userGroupIds), q -> q
                                    .eq(OvertimeDataPermissionConfig::getIdentityType, DataPermissionConfigConstant.IDENTITY_TYPE_USER_GROUP)
                                    .in(OvertimeDataPermissionConfig::getIdentityId, userGroupIds))
                    );

            return count(queryWrapper) > 0;
        }

        /**
         * 申请数据权限
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param identityId 身份数据 id
         * @author zc
         * @date 2023/11/22
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void apply(int tenantId, int propertyId, int identityId) {
            // 创建等待通过的数据
            OvertimeDataPermissionConfig dataPermissionConfig = OvertimeDataPermissionConfig
                    .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, () -> {
                // 数据权限申请通知
                sendNotifyService.dataPermissionApply(dataPermissionConfig);
            });
        }
    }