# CompRoomApproveLevelServiceImpl.java

    package cloud.wconnect.frontoffice.service.comproom.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.EmailConfigCons;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.UserDTO;
    import cloud.wconnect.commonlib.pojo.dto.UsergroupDTO;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.service.UsergroupInternalService;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.commonlib.util.TransactionUtils;
    import cloud.wconnect.frontoffice.cons.ApproveCons;
    import cloud.wconnect.frontoffice.cons.ApproveStatusEnum;
    import cloud.wconnect.frontoffice.entity.comproom.CompRoomApproveConfig;
    import cloud.wconnect.frontoffice.entity.comproom.CompRoomApproveLevel;
    import cloud.wconnect.frontoffice.entity.comproom.CompRoomApproveLog;
    import cloud.wconnect.frontoffice.entity.comproom.RqCompRoomForm;
    import cloud.wconnect.frontoffice.mapper.comproom.CompRoomApproveLevelMapper;
    import cloud.wconnect.frontoffice.pojo.dto.comproom.CompRoomApproveCCDTO;
    import cloud.wconnect.frontoffice.pojo.dto.comproom.CompRoomApproveLevelDTO;
    import cloud.wconnect.frontoffice.pojo.dto.comproom.CompRoomApproveUserDTO;
    import cloud.wconnect.frontoffice.service.comproom.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

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

    import static java.util.stream.Collectors.*;

    /**
     * <p>
     * comp room审批层级表 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2023-08-21
     */
    @Service
    @DS("logbook")
    public class CompRoomApproveLevelServiceImpl extends ServiceImpl<CompRoomApproveLevelMapper, CompRoomApproveLevel> implements ICompRoomApproveLevelService {

        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private CompRoomApproveLevelMapper approveLevelMapper;
        @Autowired
        @Lazy
        private ICompRoomApproveConfigService approveConfigService;
        @Autowired
        @Lazy
        private IRqCompRoomFormService compRoomService;
        @Autowired
        @Lazy
        private ICompRoomNotifyService compRoomNotifyService;
        @Autowired
        private Executor executor;
        @Autowired
        @Lazy
        private ICompRoomApproveLogService approveLogService;
        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private UsergroupInternalService usergroupInternalService;

        /**
         * 查询下一个审批层级
         *
         * @param propertyId   资产id
         * @param approveIndex 审批节点
         * @return cloud.wconnect.frontoffice.entity.comproom.CompRoomApproveLevel
         * @author lichen
         * @date 2023/8/21
         */
        @Override
        public CompRoomApproveLevel getNextByApproveIndex(Integer propertyId, Integer approveIndex) {
            return getOne(new LambdaQueryWrapper<CompRoomApproveLevel>()
                    .eq(CompRoomApproveLevel::getPropertyId, propertyId)
                    .gt(CompRoomApproveLevel::getApproveIndex, approveIndex)
                    .orderByAsc(CompRoomApproveLevel::getApproveIndex)
                    .last("limit 1"));
        }

        /**
         * 获取第一个审批层级
         *
         * @param propertyId 资产id
         * @return cloud.wconnect.frontoffice.entity.comproom.CompRoomApproveLevel
         * @author lichen
         * @date 2023/8/21
         */
        @Override
        public CompRoomApproveLevel getFirstApproveLevel(Integer propertyId) {
            return getOne(new LambdaQueryWrapper<CompRoomApproveLevel>()
                    .eq(CompRoomApproveLevel::getPropertyId, propertyId)
                    .orderByAsc(CompRoomApproveLevel::getApproveIndex)
                    .last("limit 1"));
        }

        /**
         * 根据审批层级索引，查询审批级别
         *
         * @param propertyId   资产id
         * @param approveIndex 审批层级索引
         * @return cloud.wconnect.frontoffice.entity.badguest.BadGuestApproveLevel
         * @author lichen
         * @date 2023/8/22
         */
        @Override
        public CompRoomApproveLevel getByApproveIndex(Integer propertyId, Integer approveIndex) {
            return getOne(new LambdaQueryWrapper<CompRoomApproveLevel>()
                    .eq(CompRoomApproveLevel::getPropertyId, propertyId)
                    .eq(CompRoomApproveLevel::getApproveIndex, approveIndex));
        }

        /**
         * 查询审批层级
         *
         * @param propertyId 资产id
         * @return java.util.List<cloud.wconnect.frontoffice.entity.comproom.CompRoomApproveLevel>
         * @author lichen
         * @date 2023/8/22
         */
        @Override
        public List<CompRoomApproveLevel> listByPropertyId(Integer propertyId) {
            return list(new LambdaQueryWrapper<CompRoomApproveLevel>()
                    .eq(CompRoomApproveLevel::getPropertyId, propertyId)
                    .orderByAsc(CompRoomApproveLevel::getApproveIndex));
        }

        /**
         * 校验审批层级是否存在
         *
         * @param id 审批层级主键id
         * @return cloud.wconnect.frontoffice.entity.comproom.CompRoomApproveLevel
         * @author lichen
         * @date 2023/8/22
         */
        @Override
        public CompRoomApproveLevel checkIsExist(Integer id) {
            CompRoomApproveLevel compRoomApproveLevel = getById(id);
            if (Objects.isNull(compRoomApproveLevel)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND, BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        MessageFormat.format("The comp room approveLevel not found, approveLevelId:{0}", id));
            }
            return compRoomApproveLevel;
        }

        /**
         * 保存审批层级
         *
         * @param approveLevelDTO 审批层级dto
         * @return void
         * @author lichen
         * @date 2023/8/22
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void save(CompRoomApproveLevelDTO approveLevelDTO) {
            List<Integer> userIds = approveLevelDTO.getUsers().stream().map(CompRoomApproveUserDTO::getId).toList();
            //查询最大的index
            Integer maxIndex = approveLevelMapper.getMaxIndexForUpdate(approveLevelDTO.getPropertyId());
            if (Objects.isNull(maxIndex)) {
                approveLevelDTO.setApproveIndex(1);
            } else {
                //设置index = maxIndex + 1
                approveLevelDTO.setApproveIndex(++maxIndex);
            }
            //新增approve level
            CompRoomApproveLevel compRoomApproveLevel = beanMapper.map(approveLevelDTO, CompRoomApproveLevel.class, entity -> {
                if (CollectionUtils.isNotEmpty(approveLevelDTO.getCcList())) {
                    entity.setCc(JsonUtils.convertObjListToString(objectMapper, approveLevelDTO.getCcList()));
                } else {
                    entity.setCc("[]");
                }
            });
            save(compRoomApproveLevel);
            //批量新增approve config
            saveBatchApproveConfig(userIds, compRoomApproveLevel);
        }

        /**
         * 修改审批层级
         *
         * @param oriApproveLevel 修改前的审批层级
         * @param approveLevelDTO 审批层级dto
         * @return void
         * @author lichen
         * @date 2023/8/22
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void update(CompRoomApproveLevel oriApproveLevel, CompRoomApproveLevelDTO approveLevelDTO) {
            //审批人集合
            List<Integer> userIds = approveLevelDTO.getUsers().stream().map(CompRoomApproveUserDTO::getId).toList();
            if (!oriApproveLevel.getRule().equals(approveLevelDTO.getRule()) && approveLevelDTO.getRule() == ApproveCons.RULE_ANY) {
                //审批规则由全部 改为 任一 时：当前节点已经有任一人审核过时，推送数据至下一个节点审核
                pushNextIndex(oriApproveLevel, ApproveCons.PUSH_TP_UPDATE_RULE, Collections.emptyList());
            }
            //审批规则是全部时，变更了审批人数量，需要判断是否推送至下一个节点审核
            if (oriApproveLevel.getRule().equals(approveLevelDTO.getRule()) && approveLevelDTO.getRule() == ApproveCons.RULE_ALL) {
                pushNextIndex(oriApproveLevel, ApproveCons.PUSH_TP_UPDATE_USER, userIds);
            }
            //修改approveLevel
            CompRoomApproveLevel approveLevel = beanMapper.map(approveLevelDTO, CompRoomApproveLevel.class, entity -> {
                if (CollectionUtils.isNotEmpty(approveLevelDTO.getCcList())) {
                    entity.setCc(JsonUtils.convertObjListToString(objectMapper, approveLevelDTO.getCcList()));
                } else {
                    entity.setCc("[]");
                }
            });
            updateById(approveLevel);
            //删除当前节点下的审核人
            approveConfigService.remove(new LambdaQueryWrapper<CompRoomApproveConfig>()
                    .eq(CompRoomApproveConfig::getPropertyId, oriApproveLevel.getPropertyId())
                    .eq(CompRoomApproveConfig::getLevelId, oriApproveLevel.getId()));
            //批量新增当前节点下的审核人
            saveBatchApproveConfig(userIds, approveLevel);
        }

        /**
         * 删除审批层级
         *
         * @param approveLevel 审批层级
         * @return void
         * @author lichen
         * @date 2023/8/22
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void remove(CompRoomApproveLevel approveLevel) {
            //推送待审核的数据至下一个节点
            pushNextIndex(approveLevel, ApproveCons.PUSH_TP_DELETE, Collections.emptyList());
            //删除approveLevel
            removeById(approveLevel.getId());
            //删除approveConfig
            approveConfigService.remove(new LambdaQueryWrapper<CompRoomApproveConfig>()
                    .eq(CompRoomApproveConfig::getPropertyId, approveLevel.getPropertyId())
                    .eq(CompRoomApproveConfig::getLevelId, approveLevel.getId()));
        }

        /**
         * 查询审批层级
         *
         * @param propertyDTO 资产DTO
         * @return java.util.List<cloud.wconnect.frontoffice.pojo.dto.comproom.CompRoomApproveLevelDTO>
         * @author lichen
         * @date 2024/3/13
         */
        @Override
        public List<CompRoomApproveLevelDTO> list(PropertyDTO propertyDTO) {
            //查询approve level 信息
            List<CompRoomApproveLevel> levelList = listByPropertyId(propertyDTO.getId());
            if (CollectionUtils.isEmpty(levelList)) return Collections.emptyList();

            //查询approve config信息
            List<CompRoomApproveConfig> configList = approveConfigService.listByLevelIds(propertyDTO.getId(),
                    levelList.stream().map(CompRoomApproveLevel::getId).toList());

            //用户信息
            List<Integer> userIdsByApproveConfig = configList.stream().map(CompRoomApproveConfig::getUserId).toList();
            Map<Integer, List<Integer>> levelId2UserIds = configList.stream()
                    .collect(groupingBy(CompRoomApproveConfig::getLevelId, mapping(CompRoomApproveConfig::getUserId, toList())));

            //抄送人集合
            List<CompRoomApproveCCDTO> approveCCList = levelList.stream()
                    .filter(approveLevel -> StringUtils.isNotBlank(approveLevel.getCc()) && !approveLevel.getCc().equals("[]"))
                    .map(item -> JsonUtils.convertStringToList(objectMapper, item.getCc(), CompRoomApproveCCDTO.class))
                    .flatMap(Collection::stream)
                    .toList();
            //按照用户来源分组
            Map<Integer, List<Integer>> source2IdentityIdList = approveCCList.stream()
                    .collect(Collectors.groupingBy(CompRoomApproveCCDTO::getSource, Collectors.mapping(CompRoomApproveCCDTO::getIdentityId, Collectors.toList())));

            //用户集合
            List<Integer> allUserIds = new ArrayList<>();
            allUserIds.addAll(source2IdentityIdList.getOrDefault(EmailConfigCons.SOURCE_USER, Collections.emptyList()));
            allUserIds.addAll(userIdsByApproveConfig);
            Map<Integer, UserDTO> userId2UserDTO = userInternalService.getUserMapByIds(propertyDTO.getTenantId(), allUserIds);

            //用户组集合
            Map<Integer, String> usergroupId2Name = usergroupInternalService.getUsergroups(propertyDTO.getTenantId(), propertyDTO.getId(),
                            source2IdentityIdList.getOrDefault(EmailConfigCons.SOURCE_USERGROUP, Collections.emptyList()))
                    .stream().collect(Collectors.toMap(UsergroupDTO::getId, UsergroupDTO::getName));
            return beanMapper.mapList(levelList, CompRoomApproveLevelDTO.class, (entity, dto) -> {
                //审批人集合
                List<CompRoomApproveUserDTO> approveUsers = levelId2UserIds.getOrDefault(dto.getId(), Collections.emptyList()).stream()
                        .map(userId -> beanMapper.map(userId2UserDTO.get(userId), CompRoomApproveUserDTO.class))
                        .toList();
                dto.setUsers(approveUsers);

                //抄送人集合
                String cc = entity.getCc();
                if (StringUtils.isNotBlank(cc)) {
                    List<CompRoomApproveCCDTO> approveCCDTOS = JsonUtils.convertStringToList(objectMapper, cc, CompRoomApproveCCDTO.class);
                    for (CompRoomApproveCCDTO approveCCDTO : approveCCDTOS) {
                        Integer source = approveCCDTO.getSource();
                        Integer identityId = approveCCDTO.getIdentityId();
                        switch (source) {
                            case EmailConfigCons.SOURCE_USER:
                                UserDTO userDTO = userId2UserDTO.get(identityId);
                                if (Objects.nonNull(userDTO)) {
                                    approveCCDTO.setIdentityName(userDTO.getName());
                                    approveCCDTO.setEmail(userDTO.getEmail());
                                }
                                break;
                            case EmailConfigCons.SOURCE_USERGROUP:
                                approveCCDTO.setIdentityName(usergroupId2Name.get(identityId));
                                break;
                            default:
                                break;
                        }
                    }
                    dto.setCcList(approveCCDTOS);
                } else {
                    dto.setCcList(Collections.emptyList());
                }
            });
        }

        /**
         * 查询上一个审批层级
         *
         * @param approveLevel 审批层级
         * @return cloud.wconnect.frontoffice.entity.comproom.CompRoomApproveLevel
         * @author lichen
         * @date 2025/6/11
         */
        @Override
        public CompRoomApproveLevel getPrevApproveLevel(CompRoomApproveLevel approveLevel) {
            return getOne(new LambdaQueryWrapper<CompRoomApproveLevel>()
                    .eq(CompRoomApproveLevel::getPropertyId, approveLevel.getPropertyId())
                    .lt(CompRoomApproveLevel::getApproveIndex, approveLevel.getApproveIndex())
                    .orderByDesc(CompRoomApproveLevel::getApproveIndex)
                    .last("limit 1"));
        }

        /**
         * 保存审批配置
         *
         * @param userIds      用户ID集合
         * @param approveLevel 审批层级
         * @return void
         * @author lichen
         * @date 2023/8/22
         */
        private void saveBatchApproveConfig(List<Integer> userIds, CompRoomApproveLevel approveLevel) {
            List<CompRoomApproveConfig> approvedConfigs = userIds.stream().map(userId -> CompRoomApproveConfig.builder()
                    .levelId(approveLevel.getId())
                    .approveIndex(approveLevel.getApproveIndex())
                    .userId(userId)
                    .propertyId(approveLevel.getPropertyId())
                    .build()
            ).collect(toList());
            approveConfigService.saveBatch(approvedConfigs);
        }

        /**
         * 推送至下一个节点
         *
         * @param oriApproveLevel   更新前的审核层级
         * @param type              1: 删除某一节点时推送 2.修改某一节点规则，由全部改为任一时推送 3.节点规则是全部时，删除某一节点上的人员时推送
         * @param updApproveUserIds 更新后的审批人
         * @return void
         * @author lichen
         * @date 2023/8/22
         */
        private void pushNextIndex(CompRoomApproveLevel oriApproveLevel, int type, List<Integer> updApproveUserIds) {
            //查询当前节点下待审核的数据
            List<RqCompRoomForm> compRoomList = compRoomService.listApprovingByApproveIndex(oriApproveLevel.getPropertyId(), oriApproveLevel.getApproveIndex());
            //数据为空时，直接return
            if (CollectionUtils.isEmpty(compRoomList)) return;

            //过滤待推送的数据
            List<RqCompRoomForm> pushList = this.filter(compRoomList, oriApproveLevel, type, updApproveUserIds);
            if (CollectionUtils.isEmpty(pushList)) return;

            //查询下一个节点
            CompRoomApproveLevel nextApproveIndex = getNextByApproveIndex(oriApproveLevel.getPropertyId(), oriApproveLevel.getApproveIndex());
            if (Objects.isNull(nextApproveIndex)) {
                //批量更新数据为审核结束
                pushList.forEach(item -> {
                    item.setApproveStatus(ApproveStatusEnum.APPROVED.getStatus());
                });

                /*
                 * 删除审批层级时，如果黑名单数据在被删除的层级上没有人审批时，则回退到上一个审批节点
                 * 当没有上一个审批节点时，approveIndex置为0
                 */
                if (type == ApproveCons.PUSH_TP_DELETE) {
                    //查询上一个层级
                    CompRoomApproveLevel prevApproveLevel = getPrevApproveLevel(oriApproveLevel);
                    int prevApproveIndex = Objects.isNull(prevApproveLevel) ? 0 : prevApproveLevel.getApproveIndex();

                    //查询免费房数据在当前审批层级下的审批记录
                    List<Long> batchIds = pushList.stream().map(RqCompRoomForm::getBatchId).collect(toList());
                    Map<Long, List<CompRoomApproveLog>> compRoomId2ApprovedLogs = approveLogService.listApprovedLogByBatchIdsAndApproveIndex(batchIds, oriApproveLevel);

                    pushList.forEach(compRoom -> {
                        //如果没有审批记录，则回退到上一个审批层级
                        if (CollectionUtils.isEmpty(compRoomId2ApprovedLogs.get(compRoom.getId()))) {
                            compRoom.setApproveIndex(prevApproveIndex);
                        }
                    });
                }

                compRoomService.saveOrUpdateBatch(pushList);
                //审批通过后发邮件给创建人
                TransactionUtils.afterCommitAsyncExecute(executor, () -> compRoomNotifyService.batchNotifyCreateUserByApproved(pushList));
            } else {
                //批量推送数据至下一个审核节点
                pushList.forEach(item -> item.setApproveIndex(nextApproveIndex.getApproveIndex()));
                compRoomService.saveOrUpdateBatch(pushList);

                //发邮件给下一个审批节点的审核人
                TransactionUtils.afterCommitAsyncExecute(executor, () -> compRoomNotifyService.notifyNextApprovalNodeApprover(pushList, nextApproveIndex));
            }
        }

        /**
         * 过滤待推送的数据
         *
         * @param compRoomList      待推送的数据
         * @param oriApproveLevel   更新前的审核层级
         * @param type              推送类型
         * @param updApproveUserIds 更新后的审批人
         * @return java.util.List<cloud.wconnect.frontoffice.entity.comproom.RqCompRoomForm>
         * @author lichen
         * @date 2023/8/22
         */
        private List<RqCompRoomForm> filter(List<RqCompRoomForm> compRoomList, CompRoomApproveLevel oriApproveLevel, int type, List<Integer> updApproveUserIds) {
            if (type == ApproveCons.PUSH_TP_DELETE) {
                return compRoomList;
            } else if (type == ApproveCons.PUSH_TP_UPDATE_RULE) {
                List<Long> batchIds = compRoomList.stream().map(RqCompRoomForm::getBatchId).collect(toList());
                //查询在当前节点下comp room数据审批通过的日志
                Map<Long, List<CompRoomApproveLog>> logMap = approveLogService.listApprovedLogByBatchIdsAndApproveIndex(batchIds, oriApproveLevel);
                //过滤出当前节点已经有人审批的数据
                return compRoomList.stream()
                        .filter(item -> CollectionUtils.isNotEmpty(logMap.get(item.getId())))
                        .collect(toList());
            } else {
                List<Long> batchIds = compRoomList.stream().map(RqCompRoomForm::getBatchId).collect(toList());
                //查询在当前节点下comp room数据审批通过的日志
                Map<Long, List<CompRoomApproveLog>> logMap = approveLogService.listApprovedLogByBatchIdsAndApproveIndex(batchIds, oriApproveLevel);
                //当已审批的集合 包含 修改的审批人集合 时推送至下一个节点
                return compRoomList.stream()
                        .filter(item -> {
                                    //获取数据对应的已审批人集合
                                    List<CompRoomApproveLog> approveLogList = logMap.get(item.getId());
                                    List<Integer> approverList = new ArrayList<>();
                                    if (CollectionUtils.isNotEmpty(approveLogList)) {
                                        approverList = approveLogList.stream().map(CompRoomApproveLog::getApproveBy).toList();
                                    }
                                    //过滤出《已审批人》包含《更新后的审批配置》的数据
                                    return new HashSet<>(approverList).containsAll(updApproveUserIds);
                                }
                        ).collect(toList());
            }
        }
    }