# AdjEventReceiveHandleServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.ApproveStatusEnum;
    import cloud.wconnect.commonlib.pojo.bo.adj.chargeback.ChargebackAdjBO;
    import cloud.wconnect.commonlib.pojo.bo.adj.chargeback.ChargebackBatchAdjBO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.UserDTO;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.GuestNameUtil;
    import cloud.wconnect.commonlib.util.RedissonLockUtil;
    import cloud.wconnect.commonlib.util.TransactionUtils;
    import cloud.wconnect.frontoffice.cons.adjustment.AdjConstant;
    import cloud.wconnect.frontoffice.cons.adjustment.AdjustmentStatusEnum;
    import cloud.wconnect.frontoffice.cons.adjustment.FlowStatusEnum;
    import cloud.wconnect.frontoffice.entity.adjustment.*;
    import cloud.wconnect.frontoffice.service.adjustment.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.google.common.collect.Lists;
    import jakarta.annotation.Resource;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.collections.CollectionUtils;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.util.*;
    import java.util.concurrent.Executor;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    import static java.util.stream.Collectors.toMap;
    import static java.util.stream.Collectors.toSet;

    /**
     * AdjustmentListener 监听器调用的业务层
     *
     * @author lichen
     * @date 2023/9/26
     */
    @Service
    @DS("logbook")
    @Slf4j
    public class AdjEventReceiveHandleServiceImpl implements IAdjEventReceiveHandleService {

        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private IAdRequestService adRequestService;
        @Autowired
        private IAdRequestCommentService adRequestCommentService;
        @Autowired
        private IAdjSenderService adjSenderService;
        @Autowired
        private Executor executor;
        @Autowired
        private IAdRequestMirrorService adRequestMirrorService;
        @Autowired
        private IAdRequestCommentMirrorService adRequestCommentMirrorService;
        @Autowired
        private IAdjOperateLogService operateLogService;
        @Autowired
        private IAdjApproveLogService approveLogService;
        @Autowired
        private IAdjPmsConfigService adjPmsConfigService;
        @Autowired
        private IAdjApproveConfigService adjApproveConfigService;
        @Autowired
        private IAdjApproveLevelService adjApproveLevelService;
        @Autowired
        private IAdjApproveLogService adjApproveLogService;
        @Autowired
        private IAdjSendNotifyService adjSendNotifyService;
        @Autowired
        private IAdjOutOfOfficeConfigService adjOutOfOfficeConfigService;
        @Autowired
        private UserInternalService userInternalService;
        @Resource
        private RedissonClient redissonClient;

        /**
         * 保存adj
         *
         * @param chargebackBatchAdjBO adj BO类
         * @param propertyDTO          资产dto
         * @author lichen
         * @date 2023/9/26
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void save(ChargebackBatchAdjBO chargebackBatchAdjBO, PropertyDTO propertyDTO) {
            // 调账编号集合
            List<String> adjNumbers = chargebackBatchAdjBO.getAdjs().stream().map(ChargebackAdjBO::getAdjNumber).collect(Collectors.toList());
            // 查询已经存在的数据
            Map<String, AdRequest> adjNumber2Adj = adRequestService.listByAdjNumbers(adjNumbers).stream().collect(Collectors.toMap(AdRequest::getAdjNumber, Function.identity()));

            ArrayList<AdRequest> updateList = Lists.newArrayListWithCapacity(adjNumbers.size());
            ArrayList<AdRequest> insertList = Lists.newArrayListWithCapacity(adjNumbers.size());

            long nowTimestamp = DateTimeUtils.nowTimestamp();
            for (ChargebackAdjBO adjBO : chargebackBatchAdjBO.getAdjs()) {
                AdRequest oriAdRequest = adjNumber2Adj.get(adjBO.getAdjNumber());

                AdRequest entity = beanMapper.map(adjBO, AdRequest.class);

                if (Objects.nonNull(oriAdRequest)) {
                    entity.setId(oriAdRequest.getId());
                    // 格式化客人姓名
                    entity.setGuestName(GuestNameUtil.removeSpaces(adjBO.getGuestName()));
                    updateList.add(entity);
                } else {
                    entity.setPropertyId(propertyDTO.getId());
                    // 格式化客人姓名
                    entity.setGuestName(GuestNameUtil.removeSpaces(adjBO.getGuestName()));
                    entity.setAutoCreated(false);
                    entity.setApproveIndex(0);
                    entity.setApproveLevelId(0L);
                    entity.setBatchId(0L);
                    entity.setStatus(AdjustmentStatusEnum.UNDETERMINED.getStatus());
                    entity.setFlowStatus(FlowStatusEnum.APPROVAING.getFlowStatus());
                    entity.setIsArchive(false);
                    // 初始化成未配对
                    entity.setBusinessDate(0);
                    entity.setMatchTime(0L);
                    // 设置关联时间
                    entity.setCaseRelTime(nowTimestamp);
                    // 设置为非作废
                    entity.setIsCancel(false);
                    // 设置默认值
                    entity.setPmsAccount("");
                    insertList.add(entity);
                }
            }
            adRequestService.updateBatchById(updateList);
            adRequestService.saveBatch(insertList);

            //发送adj的变更消息
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                List<Long> insertIds = insertList.stream().map(AdRequest::getId).collect(Collectors.toList());
                List<Long> updateIds = updateList.stream().map(AdRequest::getId).collect(Collectors.toList());
                ArrayList<Long> allIds = Lists.newArrayList(insertIds);
                allIds.addAll(updateIds);
                adjSenderService.batchPublishSave(propertyDTO.getId(), allIds);
            });
        }

        /**
         * 审批完成
         *
         * @param chargebackBatchAdjBO adj BO类
         * @param propertyDTO          资产dto
         * @author lichen
         * @date 2023/9/26
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void approved(ChargebackBatchAdjBO chargebackBatchAdjBO, PropertyDTO propertyDTO) {
            List<String> adjNumberList = chargebackBatchAdjBO.getAdjs().stream().map(ChargebackAdjBO::getAdjNumber).toList();

            //根据adjNumber查询表中的数据
            List<AdRequest> adjList = adRequestService.listByAdjNumbers(adjNumberList);
            if (CollectionUtils.isEmpty(adjList)) return;

            //设置为审批通过
            adjList.forEach(adRequest -> {
                adRequest.setStatus(AdjustmentStatusEnum.VALID.getStatus());
                adRequest.setFlowStatus(FlowStatusEnum.APPROVAL_COMPLETED.getFlowStatus());
            });
            //更新为审批通过
            adRequestService.updateBatchById(adjList);

            //发送adj的变更消息
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                adjSenderService.batchPublishSave(propertyDTO.getId(),
                        adjList.stream().map(AdRequest::getId).toList());
            });
        }

        /**
         * 删除adj
         *
         * @param chargebackBatchAdjBO adj BO类
         * @param propertyDTO          资产dto
         * @author lichen
         * @date 2023/9/26
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void delete(ChargebackBatchAdjBO chargebackBatchAdjBO, PropertyDTO propertyDTO) {
            List<String> adjNumberList = chargebackBatchAdjBO.getAdjs().stream().map(ChargebackAdjBO::getAdjNumber).toList();

            // 根据adjNumber查询表中的数据
            List<AdRequest> existAdjs = adRequestService.listByAdjNumbers(adjNumberList);
            if (CollectionUtils.isEmpty(existAdjs)) return;

            RLock rLock = redissonClient.getLock(AdjConstant.GET_MAX_ADJ_NUMBER_LOCK_PREFIX + propertyDTO.getId().toString());
            if (RedissonLockUtil.getLock(rLock, 3, 5)) {
                try {
                    // 执行删除逻辑
                    doDelete(existAdjs, propertyDTO);
                } finally {
                    if (rLock.isHeldByCurrentThread()) {
                        // 释放锁
                        rLock.unlock();
                    }
                }
            } else {
                throw new RuntimeException("delete error, The number of execution failures reached the upper limit");
            }
        }

        /**
         * 删除adj
         *
         * @param delAdjs     删除的调账数据集合
         * @param propertyDTO 资产dto
         * @author lichen
         * @date 2023/9/26
         */
        private void doDelete(List<AdRequest> delAdjs, PropertyDTO propertyDTO) {
            // 删除所有调账数据、备注、操作日志、审批日志
            List<Long> adjIds = delAdjs.stream().map(AdRequest::getId).sorted().toList();
            adRequestService.removeByIds(adjIds);
            adRequestCommentService.removeByAdjIds(adjIds);
            operateLogService.remove(new LambdaQueryWrapper<AdjOperateLog>()
                    .eq(AdjOperateLog::getPropertyId, propertyDTO.getId())
                    .in(AdjOperateLog::getAdjId, adjIds));
            approveLogService.remove(new LambdaQueryWrapper<AdjApproveLog>()
                    .eq(AdjApproveLog::getPropertyId, propertyDTO.getId())
                    .in(AdjApproveLog::getAdjId, adjIds));

            // 查询PMS配置
            List<AdjPmsConfig> adjPmsConfigs = adjPmsConfigService.listByPropertyIdAndDataType(propertyDTO.getId(), AdjPmsConfig.DATA_TYPE_DRAFT);
            Map<String, AdjPmsConfig> pmsAccount2Entity = adjPmsConfigs.stream().collect(toMap(AdjPmsConfig::getPmsAccount, Function.identity()));

            // 恢复已配对的数据（恢复至PMS报告下发时刻）
            List<AdRequest> matchedList = delAdjs.stream().filter(adj -> adj.getBusinessDate() > 0).collect(Collectors.toList());
            ArrayList<AdRequest> insertBatch = Lists.newArrayListWithExpectedSize(matchedList.size());
            for (AdRequest source : matchedList) {
                AdRequest adj = beanMapper.map(source, AdRequest.class);
                adj.setId(null);
                // 设置adjNumber为空，后续重新按照Adjustment功能的规则生成编号
                adj.setAdjNumber("");
                // 清空审批层级
                adj.setStatus(AdjustmentStatusEnum.UNDETERMINED.getStatus());
                adj.setFlowStatus(FlowStatusEnum.DRAFT.getFlowStatus());
                adj.setApproveIndex(0);
                adj.setApproveLevelId(0L);
                adj.setBatchId(0L);
                // 设置为未绑定Case
                adj.setCbAdjId(0L);
                adj.setCaseNumber("");
                adj.setCaseRelTime(0L);
                // 设置为非作废数据
                adj.setIsCancel(false);
                // 设置为自动创建数据
                adj.setAutoCreated(true);
                adj.setCreateBy(0);
                // 创建时间设置为配对时间（即报告下发时间）
                adj.setCreateTime(adj.getMatchTime());
                adj.setUpdateBy(0);
                adj.setUpdateTime(0L);
                // 设置为未归档
                adj.setIsArchive(false);
                // 附件设置为空
                adj.setAttachments("[]");
                // 备注设置为空
                adj.setComment("");
                adj.setMistakeBy("");
                adj.setChoosedReasonDetails("");
                // 根据pmsAccount重新分配责任人和类型
                AdjPmsConfig adjPmsConfig = pmsAccount2Entity.get(adj.getPmsAccount());
                if (Objects.nonNull(adjPmsConfig)) {
                    adj.setRequestType(adjPmsConfig.getRequestType());
                    adj.setDutyUser(adjPmsConfig.getUserId());
                } else {
                    adj.setRequestType(0);
                    adj.setDutyUser(0);
                }

                insertBatch.add(adj);
            }
            if (CollectionUtils.isNotEmpty(insertBatch)) {
                adRequestService.setAdjNumBatch(insertBatch, propertyDTO);
                adRequestService.saveBatch(insertBatch);
            }

            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                // 恢复的数据发送adj变更消息
                List<Long> insertAdjIds = insertBatch.stream().map(AdRequest::getId).collect(Collectors.toList());
                adjSenderService.batchPublishSave(propertyDTO.getId(), insertAdjIds);

                // 删除的数据发送adj删除消息
                adjSenderService.publishDelete(propertyDTO.getId(), delAdjs);
            });
        }

        /**
         * 更新备注
         *
         * @param chargebackBatchAdjBO adj BO类
         * @param propertyDTO          资产dto
         * @author lichen
         * @date 2023/9/26
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void updateComment(ChargebackBatchAdjBO chargebackBatchAdjBO, PropertyDTO propertyDTO) {
            List<String> adjNumberList = chargebackBatchAdjBO.getAdjs().stream().map(ChargebackAdjBO::getAdjNumber).toList();

            // 根据adjNumber查询表中的数据
            List<AdRequest> adjList = adRequestService.listByAdjNumbers(adjNumberList);
            if (CollectionUtils.isEmpty(adjList)) return;
            List<Long> adjIds = adjList.stream().map(AdRequest::getId).toList();
            Map<String, Long> adjNumber2AdjId = adjList.stream().collect(Collectors.toMap(AdRequest::getAdjNumber, AdRequest::getId));

            // 删除备注
            adRequestCommentService.removeByAdjIds(adjIds);

            ArrayList<AdRequestComment> allCommentList = Lists.newArrayList();
            for (ChargebackAdjBO adjBO : chargebackBatchAdjBO.getAdjs()) {
                Long adjId = adjNumber2AdjId.get(adjBO.getAdjNumber());
                if (Objects.isNull(adjId)) continue;

                List<AdRequestComment> commentList = beanMapper.mapList(adjBO.getComments(), AdRequestComment.class, (bo, entity) -> {
                    entity.setPropertyId(propertyDTO.getId());
                    entity.setArId(adjId);
                });
                allCommentList.addAll(commentList);
            }
            // 新增评论
            adRequestCommentService.saveBatch(allCommentList);

            //发送adj的变更消息
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                for (Long adjId : adjIds) {
                    adjSenderService.publishSaveComment(propertyDTO.getId(), adjId);
                }
            });
        }

        /**
         * 绑定adj
         *
         * @param chargebackBatchAdjBO adj BO类
         * @param propertyDTO          资产DTO
         * @author lichen
         * @date 2026/2/11
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void bind(ChargebackBatchAdjBO chargebackBatchAdjBO, PropertyDTO propertyDTO) {
            // 调账编号集合
            List<String> adjNumbers = chargebackBatchAdjBO.getAdjs().stream().map(ChargebackAdjBO::getAdjNumber).collect(Collectors.toList());
            // 查询已经存在的数据
            Map<String, AdRequest> adjNumber2Adj = adRequestService.listByAdjNumbers(adjNumbers).stream().collect(Collectors.toMap(AdRequest::getAdjNumber, Function.identity()));

            ArrayList<AdRequest> updateList = Lists.newArrayListWithCapacity(adjNumbers.size());
            ArrayList<AdRequestMirror> needMirrorList = Lists.newArrayListWithCapacity(adjNumbers.size());

            long nowTimestamp = DateTimeUtils.nowTimestamp();
            for (ChargebackAdjBO adjBO : chargebackBatchAdjBO.getAdjs()) {
                AdRequest entity = beanMapper.map(adjBO, AdRequest.class);

                AdRequest oriAdRequest = adjNumber2Adj.get(adjBO.getAdjNumber());
                if (oriAdRequest.getBusinessDate() > 0) {
                    // 绑定已配对的数据时，需要备份
                    needMirrorList.add(beanMapper.map(oriAdRequest, AdRequestMirror.class));
                }

                entity.setId(oriAdRequest.getId());
                // 格式化客人姓名
                entity.setGuestName(GuestNameUtil.removeSpaces(adjBO.getGuestName()));
                // 设置关联Chargeback的时间
                entity.setCaseRelTime(nowTimestamp);
                // 清空审批层级
                entity.setApproveIndex(0);
                entity.setApproveLevelId(0L);
                entity.setBatchId(0L);
                entity.setStatus(AdjustmentStatusEnum.UNDETERMINED.getStatus());
                entity.setFlowStatus(FlowStatusEnum.APPROVAING.getFlowStatus());
                // 设置为未归档
                entity.setIsArchive(false);
                updateList.add(entity);
            }
            adRequestService.updateBatchById(updateList);

            // 删除绑定数据的操作日志、审批日志
            List<Long> updateIds = updateList.stream().map(AdRequest::getId).sorted().toList();
            operateLogService.remove(new LambdaQueryWrapper<AdjOperateLog>()
                    .eq(AdjOperateLog::getPropertyId, propertyDTO.getId())
                    .in(AdjOperateLog::getAdjId, updateIds));
            approveLogService.remove(new LambdaQueryWrapper<AdjApproveLog>()
                    .eq(AdjApproveLog::getPropertyId, propertyDTO.getId())
                    .in(AdjApproveLog::getAdjId, updateIds));

            if(CollectionUtils.isNotEmpty(needMirrorList)){
                needMirrorList.forEach(mirror -> {
                    // 清空审批层级
                    mirror.setApproveIndex(0);
                    mirror.setApproveLevelId(0L);
                    mirror.setBatchId(0L);
                    mirror.setStatus(AdjustmentStatusEnum.UNDETERMINED.getStatus());
                    mirror.setFlowStatus(FlowStatusEnum.DRAFT.getFlowStatus());
                });
                // 备份调账数据
                adRequestMirrorService.saveBatch(needMirrorList);

                // 备份调账备注
                List<AdRequestComment> adRequestComments = adRequestCommentService.listByAdjustmentId(needMirrorList.stream().map(AdRequestMirror::getId).collect(Collectors.toList()));
                adRequestCommentMirrorService.saveBatch(beanMapper.mapList(adRequestComments, AdRequestCommentMirror.class));
            }

            //发送adj的变更消息
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                adjSenderService.batchPublishSave(propertyDTO.getId(), updateList.stream().map(AdRequest::getId).collect(Collectors.toList()));
            });
        }

        /**
         * 解绑adj
         *
         * @param chargebackBatchAdjBO adj BO类
         * @param propertyDTO          资产DTO
         * @author lichen
         * @date 2026/2/11
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void unbind(ChargebackBatchAdjBO chargebackBatchAdjBO, PropertyDTO propertyDTO) {
            // 调账编号集合
            List<String> adjNumbers = chargebackBatchAdjBO.getAdjs().stream().map(ChargebackAdjBO::getAdjNumber).collect(Collectors.toList());

            // 查询已经存在的数据
            List<AdRequest> existAdjs = adRequestService.listByAdjNumbers(adjNumbers);
            List<Long> existAdjIds = existAdjs.stream().map(AdRequest::getId).collect(Collectors.toList());
            // 先删除
            adRequestService.removeByIds(existAdjIds);
            adRequestCommentService.removeByAdjIds(existAdjIds);

            // 恢复调账数据
            List<AdRequestMirror> adRequestMirrors = adRequestMirrorService.listByAdjNumbers(adjNumbers);
            adRequestService.saveBatch(beanMapper.mapList(adRequestMirrors, AdRequest.class));

            // 恢复调账备注
            List<Long> adRequestMirrorIds = adRequestMirrors.stream().map(AdRequestMirror::getId).collect(Collectors.toList());
            List<AdRequestCommentMirror> adRequestCommentMirrors = adRequestCommentMirrorService.listByAdjustmentId(adRequestMirrorIds);
            adRequestCommentService.saveBatch(beanMapper.mapList(adRequestCommentMirrors, AdRequestComment.class));

            // 删除备份的调账数据
            if(CollectionUtils.isNotEmpty(adRequestMirrorIds)){
                adRequestMirrorService.removeByIds(adRequestMirrorIds);
                // 删除备份的调账备注
                adRequestCommentMirrorService.removeBatchByAdjustmentIds(adRequestMirrorIds);
            }

            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                // 恢复的数据发送adj变更消息
                adjSenderService.batchPublishSave(propertyDTO.getId(), adRequestMirrorIds);
                adRequestMirrorIds.forEach(adjId -> adjSenderService.publishSaveComment(propertyDTO.getId(), adjId));

                // 删除的数据发送adj删除消息
                List<AdRequest> delAdjs = existAdjs.stream().filter(existAdj -> !adRequestMirrorIds.contains(existAdj.getId())).collect(Collectors.toList());
                adjSenderService.publishDelete(propertyDTO.getId(), delAdjs);
            });
        }

        /**
         * 用户关闭
         *
         * @param propertyDTO 资产
         * @param userId      用户 id
         * @author yyt
         * @date 2026/3/20
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void userLose(PropertyDTO propertyDTO, Integer userId) {
            Integer tenantId = propertyDTO.getTenantId();
            Integer propertyId = propertyDTO.getId();
            // 检查用户是否是审批人，若用户不是审批人，则 return
            List<AdjApproveConfig> adjApproveConfigs = adjApproveConfigService.listByUserId(propertyId, userId);
            if(CollectionUtils.isEmpty(adjApproveConfigs)) return;

            // 不同类型下的审批层级
            List<AdjApproveLevel> adjApproveLevels = adjApproveLevelService.listByPropertyId(propertyId);
            Map<Integer, List<AdjApproveLevel>> approveLevelsByType = adjApproveLevels
                    .stream().collect(Collectors.groupingBy(AdjApproveLevel::getType));
            Map<Integer, Map<Long, AdjApproveLevel>> approveLevelByIdAndType = adjApproveLevels
                    .stream().collect(Collectors.groupingBy(AdjApproveLevel::getType,
                            Collectors.toMap(AdjApproveLevel::getId, v -> v)));

            // 所有审批人+补位审批人
            List<Long> levelIds = adjApproveConfigs.stream().map(AdjApproveConfig::getLevelId).collect(Collectors.toList());
            List<AdjApproveConfig> allApprover = adjApproveConfigService.listByLevelIds(propertyId, levelIds);
            Map<Long, List<Integer>> approverByLevelId = allApprover.stream().collect(Collectors.groupingBy(AdjApproveConfig::getLevelId,
                    Collectors.mapping(AdjApproveConfig::getUserId, Collectors.toList())));
            List<Integer> allApproverIds = allApprover.stream().map(AdjApproveConfig::getUserId).toList();
            Map<Integer, List<AdjOutOfOfficeConfig>> adjOutOfOfficeConfigsByType = adjOutOfOfficeConfigService.listEffectiveConfigForApprover(propertyId, null, allApproverIds)
                    .stream().collect(Collectors.groupingBy(AdjOutOfOfficeConfig::getType));
            // 状态是有效的审批人
            List<Integer> effectiveUserIds = userInternalService.getUsers(tenantId, allApproverIds)
                    .stream().filter(UserDTO::getStatus).map(UserDTO::getId).toList();

            // 查询审批人所在层级对应的审批中的 adj
            List<AdRequest> adRequests = adRequestService.listApprovingByLevels(propertyId, levelIds);
            // adj 对应的已经审批过的审批人
            Map<Long, List<AdRequest>> adjByLevelId = Collections.emptyMap();
            Map<Long, Map<Long, Set<Integer>>> alreadyApprovedUserIdsByLevelId = Collections.emptyMap();
            if (CollectionUtils.isNotEmpty(adRequests)) {
                adjByLevelId = adRequests.stream().collect(Collectors.groupingBy(AdRequest::getApproveLevelId));
                alreadyApprovedUserIdsByLevelId = adjApproveLogService.listApproveRecord(propertyId, adRequests)
                        .stream().collect(Collectors.groupingBy(AdjApproveLog::getLevelId,
                                Collectors.groupingBy(AdjApproveLog::getAdjId,
                                        Collectors.mapping(AdjApproveLog::getApproveBy, toSet()))));
            }

            for (AdjApproveConfig approveConfig : adjApproveConfigs) {
                // 循环处理当前审批人
                // 若审批人所属最后一个审批层级，则不处理 adj
                // 检查审批层级下是否有未禁用人员，若没有未禁用人员，则 adj 直接进入下个审批层级
                // 若有未禁用人员，看看未禁用人员是否已经审批过，若都已审批，则 adj 直接进入下个审批层级
                // 否则，adj 继续留在当前层级
                if (approveLevelsByType.getOrDefault(approveConfig.getType(), Collections.emptyList())
                        .stream().noneMatch(item -> item.getApproveIndex() > approveConfig.getApproveIndex())) {
                    continue;
                }

                Long levelId = approveConfig.getLevelId();
                // 当前层级下的审批人
                List<Integer> approvers = approverByLevelId.getOrDefault(levelId, Collections.emptyList());
                if (CollectionUtils.isEmpty(approvers)) continue;

                // 当前层级下待审批的 adj
                List<AdRequest> adjs = adjByLevelId.getOrDefault(levelId, Collections.emptyList());
                if (CollectionUtils.isEmpty(adjs)) continue;
                // adj 对应的已审批过的审批人
                Map<Long, Set<Integer>> alreadyApprovedUserIdsByAdj = alreadyApprovedUserIdsByLevelId.getOrDefault(levelId, Collections.emptyMap());

                // 检查当前层级下的其他人是否都已禁用，若已禁用，则 adj 直接进入下个审批层级
                List<AdRequest> publishAdjs = Lists.newArrayListWithExpectedSize(adjs.size());
                approvers.remove(userId);
                List<Integer> currentLevelEffectiveUserIds = approvers.stream().filter(effectiveUserIds::contains).toList();
                if (CollectionUtils.isEmpty(currentLevelEffectiveUserIds)) {
                    publishAdjs = adjs;
                } else {
                    // 若有未禁用人员，看看未禁用人员和补位人员是否已经审批过，若都已审批，则 adj 直接进入下个审批层级
                    for (AdRequest adj : adjs) {
                        Set<Integer> alreadyApprovedUserIds = alreadyApprovedUserIdsByAdj.getOrDefault(adj.getId(), Collections.emptySet());
                        List<Integer> notApprovers = currentLevelEffectiveUserIds.stream().filter(approveUserId -> !alreadyApprovedUserIds.contains(approveUserId)).toList();
                        if (CollectionUtils.isNotEmpty(notApprovers)) {
                            // 看看补位人员是否审批过
                            if (!adRequestService.checkSubstitutePersonnelApprove(notApprovers, alreadyApprovedUserIds,
                                    adjOutOfOfficeConfigsByType.getOrDefault(approveConfig.getType(), Collections.emptyList()))) {
                                continue;
                            }
                        }
                        publishAdjs.add(adj);
                    }
                }
                if (CollectionUtils.isNotEmpty(publishAdjs)) {
                    // 生成当前层级当前用户的已跳过审批记录
                    long now = DateTimeUtils.nowTimestamp();
                    adjApproveLogService.saveBatch(publishAdjs.stream()
                            .map(adj -> new AdjApproveLog()
                                    .setPropertyId(propertyId)
                                    .setBatchId(adj.getBatchId())
                                    .setAdjId(adj.getId())
                                    .setLevelId(approveConfig.getLevelId())
                                    .setApproveIndex(approveConfig.getApproveIndex())
                                    .setApproveBy(userId)
                                    .setApproveStatus(ApproveStatusEnum.SKIPPED.getStatus())
                                    .setApproveTime(now))
                            .toList());

                    // 进入下一级
                    adjApproveLevelService.handlePushNextIndex(approveLevelByIdAndType.get(approveConfig.getType()).get(levelId), publishAdjs, null);
                }
            }

            // 发送审批人员禁用通知
            adjSendNotifyService.notifyApprovalPersonnelDisabled(propertyDTO, userId, adjApproveConfigs);
        }

    }