# WalkRoomApproveLevelServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    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.cons.walkroomrequest.WalkRoomApproveTypeEnum;
    import cloud.wconnect.frontoffice.entity.walkroomrequest.WalkRoomApproveConfig;
    import cloud.wconnect.frontoffice.entity.walkroomrequest.WalkRoomApproveLevel;
    import cloud.wconnect.frontoffice.entity.walkroomrequest.WalkRoomApproveLog;
    import cloud.wconnect.frontoffice.entity.walkroomrequest.WalkRoomDateinOther;
    import cloud.wconnect.frontoffice.mapper.walkroomrequest.WalkRoomApproveLevelMapper;
    import cloud.wconnect.frontoffice.pojo.dto.walkroomrequest.WalkRoomApproveCCDTO;
    import cloud.wconnect.frontoffice.pojo.dto.walkroomrequest.WalkRoomApproveLevelDTO;
    import cloud.wconnect.frontoffice.pojo.dto.walkroomrequest.WalkRoomApproveUserDTO;
    import cloud.wconnect.frontoffice.service.walkroomrequest.*;
    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 com.google.common.collect.ImmutableMap;
    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.util.*;
    import java.util.concurrent.Executor;
    import java.util.stream.Collectors;

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

    /**
     * <p>
     * walk room审批层级表 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2023-07-08
     */
    @Service
    @DS("logbook")
    public class WalkRoomApproveLevelServiceImpl extends ServiceImpl<WalkRoomApproveLevelMapper, WalkRoomApproveLevel> implements IWalkRoomApproveLevelService {

        @Autowired
        private IWalkRoomApproveConfigService approveConfigService;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private IWalkRoomApproveLogService approveLogService;
        @Autowired
        private WalkRoomApproveLevelMapper approveLevelMapper;
        @Autowired
        private Executor executor;
        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private IWalkRoomDateinOtherService walkRoomDateinOtherService;
        @Autowired
        @Lazy
        private IWalkRoomNotifyService walkRoomNotifyService;
        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private UsergroupInternalService usergroupInternalService;

        /**
         * 根据资产id和审批类型 查询审批级别
         *
         * @param propertyDTO 资产DTO
         * @param type        审批级别类型1:Sent 2:Receive
         * @return java.util.List<cloud.wconnect.frontoffice.pojo.dto.walkroom.WalkRoomApproveLevelDTO>
         * @author lichen
         * @date 2023/7/11
         */
        @Override
        public List<WalkRoomApproveLevelDTO> list(PropertyDTO propertyDTO, Integer type) {
            //查询approve level 信息
            List<WalkRoomApproveLevel> levelList = list(new LambdaQueryWrapper<WalkRoomApproveLevel>()
                    .eq(WalkRoomApproveLevel::getPropertyId, propertyDTO.getId())
                    .eq(WalkRoomApproveLevel::getType, type)
                    .orderByAsc(WalkRoomApproveLevel::getId));
            if (CollectionUtils.isEmpty(levelList)) {
                return Collections.emptyList();
            }

            //查询approve config信息
            List<Long> ids = levelList.stream().map(WalkRoomApproveLevel::getId).collect(Collectors.toList());
            List<WalkRoomApproveConfig> configList = approveConfigService.listByLevelIds(ids);

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

            //抄送人集合
            List<WalkRoomApproveCCDTO> approveCCList = levelList.stream()
                    .filter(approveLevel -> StringUtils.isNotBlank(approveLevel.getCc()) && !approveLevel.getCc().equals("[]"))
                    .map(item -> JsonUtils.convertStringToList(objectMapper, item.getCc(), WalkRoomApproveCCDTO.class))
                    .flatMap(Collection::stream)
                    .toList();
            //按照用户来源分组
            Map<Integer, List<Integer>> source2IdentityIdList = approveCCList.stream()
                    .collect(Collectors.groupingBy(WalkRoomApproveCCDTO::getSource, Collectors.mapping(WalkRoomApproveCCDTO::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, WalkRoomApproveLevelDTO.class, (entity, dto) -> {
                //审批人集合
                List<WalkRoomApproveUserDTO> approveUsers = levelId2UserIds.getOrDefault(dto.getId(), Collections.emptyList()).stream()
                        .map(userId -> beanMapper.map(userId2UserDTO.get(userId), WalkRoomApproveUserDTO.class))
                        .toList();
                dto.setUsers(approveUsers);

                //抄送人集合
                String cc = entity.getCc();
                if (StringUtils.isNotBlank(cc)) {
                    List<WalkRoomApproveCCDTO> approveCCDTOS = JsonUtils.convertStringToList(objectMapper, cc, WalkRoomApproveCCDTO.class);
                    for (WalkRoomApproveCCDTO 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 approveLevelDTO 审批级别dto
         * @return void
         * @author lichen
         * @date 2023/7/11
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public Map<String, Long> insert(WalkRoomApproveLevelDTO approveLevelDTO) {
            //查询最大的index
            Integer maxIndex = approveLevelMapper.getMaxIndexForUpdate(approveLevelDTO.getPropertyId(), approveLevelDTO.getType());
            if (Objects.isNull(maxIndex)) {
                approveLevelDTO.setApproveIndex(1);
            } else {
                //设置index = maxIndex + 1
                approveLevelDTO.setApproveIndex(++maxIndex);
            }

            WalkRoomApproveLevel walkRoomApproveLevel = beanMapper.map(approveLevelDTO, WalkRoomApproveLevel.class, entity -> {
                if (CollectionUtils.isNotEmpty(approveLevelDTO.getCcList())) {
                    entity.setCc(JsonUtils.convertObjListToString(objectMapper, approveLevelDTO.getCcList()));
                } else {
                    entity.setCc("[]");
                }
            });
            //新增approve level
            save(walkRoomApproveLevel);

            //批量新增approve config
            saveBatchApproveConfig(approveLevelDTO.getUsers(), walkRoomApproveLevel);

            return ImmutableMap.of("id", walkRoomApproveLevel.getId());
        }

        /**
         * 修改审批级别
         *
         * @param oriWalkRoomApproveLevel 修改前的审批级别数据
         * @param approveLevelDTO         审批级别dto
         * @return void
         * @author lichen
         * @date 2023/7/11
         */
        @Override
        public Map<String, Long> update(WalkRoomApproveLevel oriWalkRoomApproveLevel, WalkRoomApproveLevelDTO approveLevelDTO) {
            if (!oriWalkRoomApproveLevel.getRule().equals(approveLevelDTO.getRule()) && approveLevelDTO.getRule() == ApproveCons.RULE_ANY) {
                //审批规则由全部 改为 任一 时：当前节点已经有任一人审核过时，推送数据至下一个节点审核
                pushNextIndex(oriWalkRoomApproveLevel, ApproveCons.PUSH_TP_UPDATE_RULE, Collections.emptyList());
            }

            //更新后的审批配置人员集合
            List<WalkRoomApproveUserDTO> approveUsers = approveLevelDTO.getUsers();
            //审批规则是全部时，变更了审批人数量，需要判断是否推送至下一个节点审核
            if (oriWalkRoomApproveLevel.getRule().equals(approveLevelDTO.getRule()) && approveLevelDTO.getRule() == ApproveCons.RULE_ALL) {
                List<Integer> updApproveUserIds = approveUsers.stream().map(WalkRoomApproveUserDTO::getId).collect(toList());
                pushNextIndex(oriWalkRoomApproveLevel, ApproveCons.PUSH_TP_UPDATE_USER, updApproveUserIds);
            }

            //修改approve level
            WalkRoomApproveLevel walkRoomApproveLevel = beanMapper.map(approveLevelDTO, WalkRoomApproveLevel.class, entity -> {
                if (CollectionUtils.isNotEmpty(approveLevelDTO.getCcList())) {
                    entity.setCc(JsonUtils.convertObjListToString(objectMapper, approveLevelDTO.getCcList()));
                } else {
                    entity.setCc("[]");
                }
            });
            updateById(walkRoomApproveLevel);

            //批量删除当前节点下的审核人
            approveConfigService.remove(new LambdaQueryWrapper<WalkRoomApproveConfig>()
                    .eq(WalkRoomApproveConfig::getPropertyId, oriWalkRoomApproveLevel.getPropertyId())
                    .eq(WalkRoomApproveConfig::getLevelId, oriWalkRoomApproveLevel.getId()));

            //批量新增当前节点下的审核人
            saveBatchApproveConfig(approveUsers, walkRoomApproveLevel);

            return ImmutableMap.of("id", oriWalkRoomApproveLevel.getId());
        }

        /**
         * 删除审批级别
         *
         * @param walkRoomApproveLevel 审批级别数据
         * @return void
         * @author lichen
         * @date 2023/7/11
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void remove(WalkRoomApproveLevel walkRoomApproveLevel) {
            //推送待审核的数据至下一个节点
            pushNextIndex(walkRoomApproveLevel, ApproveCons.PUSH_TP_DELETE, Collections.emptyList());
            //删除approve level
            removeById(walkRoomApproveLevel.getId());
            //删除approve config
            approveConfigService.remove(new LambdaQueryWrapper<WalkRoomApproveConfig>()
                    .eq(WalkRoomApproveConfig::getPropertyId, walkRoomApproveLevel.getPropertyId())
                    .eq(WalkRoomApproveConfig::getLevelId, walkRoomApproveLevel.getId()));
        }

        /**
         * 查询审批级别集合
         *
         * @param propertyId 资产id
         * @param type       审批级别类型1:Sent 2:Receive
         * @return java.util.List<cloud.wconnect.frontoffice.entity.walkroom.WalkRoomApproveLevel>
         * @author lichen
         * @date 2023/7/14
         */
        @Override
        public List<WalkRoomApproveLevel> listByPropertyIdAndType(Integer propertyId, Integer type) {
            return list(new LambdaQueryWrapper<WalkRoomApproveLevel>()
                    .eq(WalkRoomApproveLevel::getPropertyId, propertyId)
                    .eq(WalkRoomApproveLevel::getType, type)
                    .orderByAsc(WalkRoomApproveLevel::getId));
        }

        /**
         * 根据资产id集合查询审批层级
         *
         * @param propertyIds 资产id集合
         * @return java.util.List<cloud.wconnect.frontoffice.entity.walkroom.WalkRoomApproveLevel>
         * @author lichen
         * @date 2023/7/19
         */
        @Override
        public List<WalkRoomApproveLevel> listByPropertyIds(Collection<Integer> propertyIds) {
            if (CollectionUtils.isEmpty(propertyIds)) return Collections.emptyList();
            return list(new LambdaQueryWrapper<WalkRoomApproveLevel>().in(WalkRoomApproveLevel::getPropertyId, propertyIds));
        }

        /**
         * 查询出资产配置的第一层审批层级
         *
         * @param propertyIds 资产Id集合
         * @return java.util.Map<java.lang.String, cloud.wconnect.frontoffice.entity.walkroom.WalkRoomApproveLevel>
         * @author lichen
         * @date 2024/3/15
         */
        @Override
        public Map<String, WalkRoomApproveLevel> listFirstApproveLevel(List<Integer> propertyIds) {
            return listByPropertyIds(propertyIds).stream().collect(groupingBy(
                    item -> WalkRoomApproveLevel.generateKey(item.getPropertyId(), item.getType()),
                    Collectors.collectingAndThen(Collectors.minBy(Comparator.comparingInt(WalkRoomApproveLevel::getApproveIndex)), Optional::get
                    )));
        }

        /**
         * 获取前一个审批层级
         *
         * @param walkRoomApproveLevel 审批层级
         * @return cloud.wconnect.frontoffice.entity.walkroomrequest.WalkRoomApproveLevel
         * @author lichen
         * @date 2025/6/11
         */
        @Override
        public WalkRoomApproveLevel getPrevApproveLevel(WalkRoomApproveLevel walkRoomApproveLevel) {
            return getOne(new LambdaQueryWrapper<WalkRoomApproveLevel>()
                    .eq(WalkRoomApproveLevel::getPropertyId, walkRoomApproveLevel.getPropertyId())
                    .eq(WalkRoomApproveLevel::getType, walkRoomApproveLevel.getType())
                    .lt(WalkRoomApproveLevel::getApproveIndex, walkRoomApproveLevel.getApproveIndex())
                    .orderByDesc(WalkRoomApproveLevel::getId)
                    .last("limit 1"));
        }

        /**
         * 审批级别数据发生变化后，推送数据至下一个审批节点
         *
         * @param oriWalkRoomApproveLevel 变化前的审批级别对象
         * @param type                    类型 1: 删除某一节点时推送 2.修改某一节点规则，由全部改为任一时推送 3.节点规则是全部时，删除某一节点上的人员时推送
         * @param updApproveUserIds       更新后的审批配置人员集合
         * @return void
         * @author lichen
         * @date 2023/7/11
         */
        private void pushNextIndex(WalkRoomApproveLevel oriWalkRoomApproveLevel, int type, List<Integer> updApproveUserIds) {
            //查询当前节点下待审核的数据
            List<WalkRoomDateinOther> walkRoomDateinOthers = walkRoomDateinOtherService.listApprovingByApproveLevel(oriWalkRoomApproveLevel);

            //数据为空时，直接return
            if (CollectionUtils.isEmpty(walkRoomDateinOthers)) return;

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

            //查询下一个节点
            LambdaQueryWrapper<WalkRoomApproveLevel> approveLevelQueryWrapper = new LambdaQueryWrapper<>();
            approveLevelQueryWrapper.eq(WalkRoomApproveLevel::getPropertyId, oriWalkRoomApproveLevel.getPropertyId());
            approveLevelQueryWrapper.eq(WalkRoomApproveLevel::getType, oriWalkRoomApproveLevel.getType());
            approveLevelQueryWrapper.gt(WalkRoomApproveLevel::getApproveIndex, oriWalkRoomApproveLevel.getApproveIndex());
            approveLevelQueryWrapper.orderByAsc(WalkRoomApproveLevel::getApproveIndex);
            approveLevelQueryWrapper.last("limit 1");
            WalkRoomApproveLevel nextIndexApprove = getOne(approveLevelQueryWrapper);
            if (Objects.isNull(nextIndexApprove)) {
                //根据审批级别类型，批量更新数据为审核结束
                if (oriWalkRoomApproveLevel.getType().equals(WalkRoomApproveTypeEnum.SENT.getCode())) {
                    pushList.forEach(item -> item.setSentApproveStatus(ApproveStatusEnum.APPROVED.getStatus()));
                } else {
                    pushList.forEach(item -> item.setReceiveApproveStatus(ApproveStatusEnum.APPROVED.getStatus()));
                }

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

                    //查询入住明细的审批日志信息
                    List<Long> dateinOtherIds = pushList.stream().map(WalkRoomDateinOther::getId).collect(toList());
                    Map<Long, List<WalkRoomApproveLog>> dateinOtherId2Logs = approveLogService.listDateinOtherApproveLog(dateinOtherIds, oriWalkRoomApproveLevel);

                    pushList.forEach(walkRoomDateinOther -> {
                        //如果没有审批记录，则回退到上一个审批层级
                        if (CollectionUtils.isEmpty(dateinOtherId2Logs.get(walkRoomDateinOther.getId()))) {
                            if (oriWalkRoomApproveLevel.getType().equals(WalkRoomApproveTypeEnum.SENT.getCode())) {
                                walkRoomDateinOther.setSentApproveIndex(prevApproveIndex);
                            } else {
                                walkRoomDateinOther.setReceiveApproveIndex(prevApproveIndex);
                            }
                        }
                    });
                }

                walkRoomDateinOtherService.saveOrUpdateBatch(pushList);
            } else {
                //批量推送数据至下一个审核节点
                if (oriWalkRoomApproveLevel.getType().equals(WalkRoomApproveTypeEnum.SENT.getCode())) {
                    pushList.forEach(item -> item.setSentApproveIndex(nextIndexApprove.getApproveIndex()));
                } else {
                    pushList.forEach(item -> item.setReceiveApproveIndex(nextIndexApprove.getApproveIndex()));
                }
                walkRoomDateinOtherService.saveOrUpdateBatch(pushList);
                //通知审核人
                TransactionUtils.afterCommitAsyncExecute(executor, () -> walkRoomNotifyService.pushNextApproveLevelSendApproveNotify(nextIndexApprove, pushList));
            }
        }

        /**
         * 过滤待推送至下一个审核节点的数据
         *
         * @param walkRoomDateinOthers    当前审批级别审核中的数据
         * @param oriWalkRoomApproveLevel 原审批级别数据
         * @param type                    变更类型1: 删除某一节点时推送 2.修改某一节点规则，由全部改为任一时推送 3.节点规则是全部时，删除某一节点上的人员时推送
         * @param updApproveUserIds       变更后的审批人员集合
         * @return java.util.List<cloud.wconnect.frontoffice.entity.walkroom.WalkRoomDateinOther>
         * @author lichen
         * @date 2023/7/11
         */
        private List<WalkRoomDateinOther> filter(List<WalkRoomDateinOther> walkRoomDateinOthers,
                                                 WalkRoomApproveLevel oriWalkRoomApproveLevel,
                                                 int type,
                                                 List<Integer> updApproveUserIds) {
            if (type == ApproveCons.PUSH_TP_DELETE) {
                return walkRoomDateinOthers;
            } else if (type == ApproveCons.PUSH_TP_UPDATE_RULE) {
                List<Long> dateinOtherIds = walkRoomDateinOthers.stream().map(WalkRoomDateinOther::getId).collect(toList());
                //查询入住明细的审批日志信息
                Map<Long, List<WalkRoomApproveLog>> logMap = approveLogService.listDateinOtherApproveLog(dateinOtherIds, oriWalkRoomApproveLevel);
                //过滤出当前节点已经有人审批的数据
                return walkRoomDateinOthers.stream()
                        .filter(item -> CollectionUtils.isNotEmpty(logMap.get(item.getId())))
                        .collect(toList());
            } else {
                List<Long> dateinOtherIds = walkRoomDateinOthers.stream().map(WalkRoomDateinOther::getId).collect(toList());
                //查询入住明细的审批日志信息
                Map<Long, List<WalkRoomApproveLog>> logMap = approveLogService.listDateinOtherApproveLog(dateinOtherIds, oriWalkRoomApproveLevel);
                //当已审批的集合 包含 修改的审批人集合 时推送至下一个节点
                return walkRoomDateinOthers.stream()
                        .filter(item -> {
                                    //获取入住明细已审批人集合
                                    List<Integer> alreadyApproverList = logMap.getOrDefault(item.getId(), Collections.emptyList())
                                            .stream().map(WalkRoomApproveLog::getApproveBy).toList();
                                    //过滤出《已审批人》包含《更新后的审批配置》的数据
                                    return new HashSet<>(alreadyApproverList).containsAll(updApproveUserIds);
                                }
                        ).collect(toList());
            }
        }

        /**
         * 批量保存walk room的approveConfig
         *
         * @param users        审批配置人员集合
         * @param approveLevel 审批级别对象
         * @return void
         * @author lichen
         * @date 2023/7/11
         */
        private void saveBatchApproveConfig(List<WalkRoomApproveUserDTO> users, WalkRoomApproveLevel approveLevel) {
            List<WalkRoomApproveConfig> approvedConfigs = users.stream().map(user -> WalkRoomApproveConfig.builder()
                    .levelId(approveLevel.getId())
                    .approveIndex(approveLevel.getApproveIndex())
                    .userId(user.getId())
                    .propertyId(approveLevel.getPropertyId())
                    .type(approveLevel.getType())
                    .build()
            ).collect(toList());
            approveConfigService.saveBatch(approvedConfigs);
        }
    }