# BadGuestServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.bo.black.BlackBO;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.*;
    import cloud.wconnect.frontoffice.cons.ApproveCons;
    import cloud.wconnect.frontoffice.cons.I18nConstant;
    import cloud.wconnect.frontoffice.cons.S3BucketFunctionCons;
    import cloud.wconnect.frontoffice.cons.badguest.*;
    import cloud.wconnect.frontoffice.entity.badguest.*;
    import cloud.wconnect.frontoffice.mapper.badguest.BadGuestCompanionMapper;
    import cloud.wconnect.frontoffice.mapper.badguest.BadGuestMapper;
    import cloud.wconnect.frontoffice.pojo.dto.badguest.ApproveDTO;
    import cloud.wconnect.frontoffice.pojo.dto.badguest.BadGuestDTO;
    import cloud.wconnect.frontoffice.pojo.dto.badguest.CancellationDTO;
    import cloud.wconnect.frontoffice.pojo.dto.badguest.GuestInHouseDTO;
    import cloud.wconnect.frontoffice.pojo.query.badguest.BadGuestQuery;
    import cloud.wconnect.frontoffice.pojo.query.badguest.ExportBadGuestQuery;
    import cloud.wconnect.frontoffice.pojo.vo.badguest.*;
    import cloud.wconnect.frontoffice.service.badguest.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Sets;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    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.function.Function;
    import java.util.stream.Collectors;

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

    /**
     * <p>
     * 黑名单用户 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2023-03-20
     */
    @Service
    @DS("logbook")
    public class BadGuestServiceImpl extends ServiceImpl<BadGuestMapper, BadGuest> implements IBadGuestService {

        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private IBadGuestCompanionService badGuestCompanionService;
        @Autowired
        private IBadGuestApproveLevelService approveLevelService;
        @Autowired
        private IBadGuestApproveConfigService approveConfigService;
        @Autowired
        private IBadGuestApproveLogService approveLogService;
        @Autowired
        private BadGuestCompanionMapper badGuestCompanionMapper;
        @Autowired
        private IBadGuestNotifyService badGuestNotifyService;
        @Autowired
        private Executor executor;
        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private IBgOperateLogService operateLogService;
        @Value("${aws-s3.region}")
        private String region;
        @Value("#{${aws-s3.function-bucket}}")
        private Map<String, Map<String, Object>> bucketMap;
        @Autowired
        private IBgNotifyRuleService notifyRuleService;
        @Autowired
        private IBgCommentService commentService;
        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;

        /**
         * 新增黑名单
         *
         * @param badGuestDTO 黑名单DTO
         * @param propertyDTO 资产DTO
         * @param loginUserId 当前登录用户
         * @return 主键id
         * @author lichen
         * @date 2024/6/4
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public Long save(BadGuestDTO badGuestDTO, PropertyDTO propertyDTO, int loginUserId) {
            long timeNow = DateTimeUtils.nowTimestamp();
            BadGuest newBadGuest = beanMapper.map(badGuestDTO, BadGuest.class);
            String attachments = newBadGuest.getAttachments();
            newBadGuest.setAttachments(Objects.isNull(attachments) ? "[]" : attachments);
            newBadGuest.setCreatedBy(loginUserId);
            newBadGuest.setCreatedTime(timeNow);
            newBadGuest.setApproveStatus(BlacklistApproveStatusEnum.PENDING.getStatus());
            newBadGuest.setApproveIndex(BaseConstant.ZERO);
            newBadGuest.setBatchId(BaseConstant.LONG_ZERO);
            newBadGuest.setLastUpdatedBy(BaseConstant.ZERO);
            newBadGuest.setLastUpdatedTime(BaseConstant.LONG_ZERO);
            //保存黑名单
            save(newBadGuest);
            //保存黑名单同住人员
            List<BadGuestCompanion> companions = beanMapper.mapList(badGuestDTO.getCompanions(), BadGuestCompanion.class, (dto, companion) -> {
                companion.setBadGuestId(newBadGuest.getId());
                companion.setCreatedTime(timeNow);
                companion.setCreatedBy(loginUserId);
                companion.setPropertyId(newBadGuest.getPropertyId());
            });
            //批量新增
            badGuestCompanionService.saveBatch(companions);
            //保存操作日志
            operateLogService.save(propertyDTO, newBadGuest.getId(), loginUserId, BgOperateTypeEnum.INSERT.getType(), "");

            return newBadGuest.getId();
        }

        /**
         * 修改黑名单
         *
         * @param oriBadGuest 修改前的黑名单数据
         * @param badGuestDTO 修改后的黑名单数据
         * @param propertyDTO 资产DTO
         * @param loginUserId 当前登录用户
         * @return void
         * @author lichen
         * @date 2024/6/4
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void update(BadGuest oriBadGuest, BadGuestDTO badGuestDTO, PropertyDTO propertyDTO, int loginUserId) {
            //更新黑名单
            BadGuest newBadGuest = beanMapper.map(badGuestDTO, BadGuest.class);
            newBadGuest.setApproveStatus(BlacklistApproveStatusEnum.PENDING.getStatus());
            newBadGuest.setApproveIndex(BaseConstant.ZERO);
            updateById(newBadGuest);
            //删除companion
            LambdaQueryWrapper<BadGuestCompanion> badGuestCompanionLambdaQueryWrapper = new LambdaQueryWrapper<>();
            badGuestCompanionLambdaQueryWrapper.eq(BadGuestCompanion::getPropertyId, badGuestDTO.getPropertyId());
            badGuestCompanionLambdaQueryWrapper.eq(BadGuestCompanion::getBadGuestId, badGuestDTO.getId());
            badGuestCompanionService.remove(badGuestCompanionLambdaQueryWrapper);
            //保存黑名单同住人员
            List<BadGuestCompanion> companions = beanMapper.mapList(badGuestDTO.getCompanions(), BadGuestCompanion.class, (dto, companion) -> {
                companion.setBadGuestId(newBadGuest.getId());
                companion.setCreatedTime(DateTimeUtils.nowTimestamp());
                companion.setCreatedBy(loginUserId);
                companion.setPropertyId(newBadGuest.getPropertyId());
            });
            //批量新增
            badGuestCompanionService.saveBatch(companions);
            //保存操作日志
            operateLogService.save(propertyDTO, oriBadGuest.getId(), loginUserId, BgOperateTypeEnum.UPDATE.getType(), "");

            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                //修改前的s3Key集合
                List<String> updateBeforeS3Keys = AttachmentUtil.getKeysByAbbrAttachmentArrayJson(oriBadGuest.getAttachments(), objectMapper);
                //修改后的s3Key集合
                List<String> updateAfterS3Keys = AttachmentUtil.getKeysByAbbrAttachmentArrayJson(badGuestDTO.getAttachments(), objectMapper);
                //删除的附件 = 修改前的附件集合 - 修改后的附件集合
                List<String> delS3Keys = Lists.newArrayList(updateBeforeS3Keys);
                delS3Keys.removeAll(updateAfterS3Keys);
                S3Utils.batchDeleteFile(region, bucketMap.get(S3BucketFunctionCons.GUESTS_BLACKLIST), delS3Keys);
            });
        }

        /**
         * 送审数据
         *
         * @param propertyDTO 资产DTO
         * @param oriBadGuest 黑名单数据
         * @param loginUserId 登录用户ID
         * @return void
         * @author lichen
         * @date 2024/6/4
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void toApprove(PropertyDTO propertyDTO, BadGuest oriBadGuest, int loginUserId) {
            //保存送审的操作日志
            operateLogService.save(propertyDTO, oriBadGuest.getId(), loginUserId, BgOperateTypeEnum.TO_APPROVE.getType(), "");

            //生成批次id规则：数据id + 当前时间戳
            Long timestamp = DateTimeUtils.nowTimestamp();
            long batchId = Long.parseLong(oriBadGuest.getId().toString() + timestamp);
            oriBadGuest.setBatchId(batchId);

            //查询当前操作人员是否审批人员
            LambdaQueryWrapper<BadGuestApproveConfig> approveConfigQueryWrapper = new LambdaQueryWrapper<>();
            approveConfigQueryWrapper.eq(BadGuestApproveConfig::getPropertyId, oriBadGuest.getPropertyId());
            approveConfigQueryWrapper.eq(BadGuestApproveConfig::getUserId, loginUserId);
            approveConfigQueryWrapper.orderByAsc(BadGuestApproveConfig::getApproveIndex);
            approveConfigQueryWrapper.last("limit 1");
            BadGuestApproveConfig approveConfig = approveConfigService.getOne(approveConfigQueryWrapper);
            if (Objects.isNull(approveConfig)) {
                /**
                 * 当前操作人员不是审批人员：查询当前资产的第一级审批数据，进行送审处理
                 * 送审处理逻辑：
                 *      1. 当前资产没有设置审批层级，数据更新为active
                 *      2. 当前资产设置了审批层级，数据更新为Inactive，审批状态为Approveing，并发邮件通知审批人
                 */
                LambdaQueryWrapper<BadGuestApproveLevel> approveLevelQueryWrapper = new LambdaQueryWrapper<>();
                approveLevelQueryWrapper.eq(BadGuestApproveLevel::getPropertyId, oriBadGuest.getPropertyId());
                approveLevelQueryWrapper.orderByAsc(BadGuestApproveLevel::getApproveIndex);
                approveLevelQueryWrapper.last("limit 1");
                BadGuestApproveLevel approveLevel = approveLevelService.getOne(approveLevelQueryWrapper);
                if (Objects.isNull(approveLevel)) {
                    //数据更新为审批通过
                    updateApproved(oriBadGuest);
                } else {
                    //数据更新为审核中
                    updateAproveing(approveLevel, oriBadGuest, 0);
                }
            } else {
                /**
                 * 当前操作人员是审批人员：先新增一条审批日志，再根据审批人员归属的审批层级，进行送审处理
                 * 送审处理逻辑：
                 *      1. 审批层级是任一人审批时：数据需要推送到下一个节点
                 *      2. 审批层级是全部人审批时：根据审批配置人员，进行送审处理
                 */
                //查询审批层级信息
                BadGuestApproveLevel byId = approveLevelService.getById(approveConfig.getLevelId());

                //新增一条审批日志
                approveLogService.save(new BadGuestApproveLog()
                        .setGuestId(oriBadGuest.getId()).setApproveBy(loginUserId)
                        .setApproveTime(DateTimeUtils.nowTimestamp()).setApproveIndex(byId.getApproveIndex())
                        .setBatchId(oriBadGuest.getBatchId()).setLevelId(byId.getId())
                        .setPropertyId(oriBadGuest.getPropertyId()).setIsApproved(true));

                //保存审批通过的操作日志
                operateLogService.save(propertyDTO, oriBadGuest.getId(), loginUserId, BgOperateTypeEnum.APPROVED.getType(), "");

                //根据审批层级规则进行送审处理
                if (byId.getRule() == ApproveCons.RULE_ANY) {
                    //推送数据至下一个节点
                    oriBadGuest.setApproveIndex(byId.getApproveIndex());
                    pushNextIndex(oriBadGuest);
                } else {
                    //查询当前节点所需的审批人员集合
                    LambdaQueryWrapper<BadGuestApproveConfig> configLambdaQueryWrapper = new LambdaQueryWrapper<>();
                    configLambdaQueryWrapper.eq(BadGuestApproveConfig::getPropertyId, oriBadGuest.getPropertyId());
                    configLambdaQueryWrapper.eq(BadGuestApproveConfig::getLevelId, byId.getId());
                    List<BadGuestApproveConfig> needApproveConfigs = approveConfigService.list(configLambdaQueryWrapper);
                    List<Integer> needApprovers = needApproveConfigs.stream().map(BadGuestApproveConfig::getUserId).toList();
                    if (needApprovers.size() == 1 && needApprovers.contains(oriBadGuest.getCreatedBy())) {
                        //当前节点所需的审批人员只包括创建人：推送数据至下一个节点
                        oriBadGuest.setApproveIndex(byId.getApproveIndex());
                        pushNextIndex(oriBadGuest);
                    } else {
                        //数据更新为审核中
                        updateAproveing(byId, oriBadGuest, loginUserId);
                    }
                }
            }
        }

        /**
         * 审核数据
         *
         * @param propertyDTO 资产DTO
         * @param oriBadGuest 黑名单数据
         * @param approveDTO  审批DTO
         * @return void
         * @author lichen
         * @date 2024/6/4
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void approve(PropertyDTO propertyDTO, BadGuest oriBadGuest, ApproveDTO approveDTO) {
            //查询当前审批层级数据
            BadGuestApproveLevel currentApproveLevel = approveLevelService.getByIndexAndPropertyId(oriBadGuest.getApproveIndex(), oriBadGuest.getPropertyId());
            Integer rule = currentApproveLevel.getRule();

            //查询当前节点的审批人集合
            Set<Integer> currentApproveUsers = approveConfigService.getApproveUsers(currentApproveLevel.getPropertyId(), currentApproveLevel.getId());
            if (!currentApproveUsers.contains(approveDTO.getApproveBy())) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "Have no authority to approve");
            }

            //审批日志对象
            BadGuestApproveLog badGuestApproveLog = new BadGuestApproveLog()
                    .setGuestId(oriBadGuest.getId()).setApproveBy(approveDTO.getApproveBy())
                    .setApproveOpinion(approveDTO.getApproveOpinion()).setApproveTime(DateTimeUtils.nowTimestamp())
                    .setApproveIndex(currentApproveLevel.getApproveIndex()).setBatchId(oriBadGuest.getBatchId())
                    .setLevelId(currentApproveLevel.getId()).setPropertyId(oriBadGuest.getPropertyId());

            //审核通过
            if (approveDTO.getApproveStatus() == BlacklistApproveStatusEnum.APPROVED.getStatus()) {
                //保存审批日志
                approveLogService.save(badGuestApproveLog.setIsApproved(true));
                //保存审批通过操作日志
                operateLogService.save(propertyDTO, oriBadGuest.getId(), approveDTO.getApproveBy(), BgOperateTypeEnum.APPROVED.getType(),
                        approveDTO.getApproveOpinion());

                if (rule == ApproveCons.RULE_ALL) {
                    //查询已经审批的人
                    Set<Integer> alreadyApprover = approveLogService.getCurrentLogUsers(oriBadGuest);
                    // 已经审批的人 包含 当前节点所需的审批人集合
                    if (alreadyApprover.containsAll(currentApproveUsers)) {
                        //推送数据至下一个节点
                        pushNextIndex(oriBadGuest);
                    }
                } else {
                    //推送数据至下一个节点
                    pushNextIndex(oriBadGuest);
                }
            } else {
                //保存审批日志
                approveLogService.save(badGuestApproveLog.setIsApproved(false));
                //保存审批拒绝操作日志
                operateLogService.save(propertyDTO, oriBadGuest.getId(), approveDTO.getApproveBy(), BgOperateTypeEnum.REJECTED.getType(),
                        approveDTO.getApproveOpinion());

                //更新数据为审核驳回，并发送邮件至数据创建人
                updateRejected(oriBadGuest);
            }
        }

        /**
         * 撤回数据
         *
         * @param propertyDTO 资产DTO
         * @param badGuest    待撤回的黑名单
         * @param opinion     意见
         * @param loginUserId 登录用户ID
         * @return void
         * @author lichen
         * @date 2024/6/4
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void withdrawn(PropertyDTO propertyDTO, BadGuest badGuest, String opinion, int loginUserId) {
            //保存操作日志
            operateLogService.save(propertyDTO, badGuest.getId(), loginUserId, BgOperateTypeEnum.WITHDRAW.getType(), opinion);
            //更新数据为pending
            badGuest.setApproveStatus(BlacklistApproveStatusEnum.PENDING.getStatus());
            badGuest.setApproveIndex(BaseConstant.ZERO);
            badGuest.setBatchId(BaseConstant.LONG_ZERO);
            updateById(badGuest);
        }

        /**
         * 筛选入住记录集合中的黑名单数据，筛选范围是租户下的黑名单
         *
         * @param propertyDTO         资产DTO
         * @param propertyIdsByTenant 租户下的资产ID集合
         * @param guestInHouseList    入住记录集合
         * @return java.util.List<cloud.wconnect.frontoffice.entity.badguest.BadGuest>
         * @author lichen
         * @date 2024/4/22
         */
        @Override
        public List<BadGuest> listByTenant(PropertyDTO propertyDTO, List<Integer> propertyIdsByTenant, List<GuestInHouseDTO> guestInHouseList) {
            //查询资产配置的通知规则
            BgNotifyRule notifyRule = notifyRuleService.getByPropertyIdAndType(propertyDTO.getId(), BlacklistNotifyConfigTypeEnum.BLACK_LIST_CHECKED_IN.getType());
            /**
             * 通知规则，默认通知非审批拒绝的数据
             */
            List<Integer> approveStatusList;
            if (Objects.nonNull(notifyRule)) {
                approveStatusList = JsonUtils.convertStringToList(objectMapper, notifyRule.getRule(), Integer.class);
            } else {
                approveStatusList = Lists.newArrayList(BlacklistApproveStatusEnum.PENDING.getStatus(),
                        BlacklistApproveStatusEnum.APPROVEING.getStatus(), BlacklistApproveStatusEnum.APPROVED.getStatus());
            }

            //查询黑名单id
            List<Long> badGuestIds = badGuestCompanionMapper.selectBadGuestIdsByNames(propertyIdsByTenant, guestInHouseList);
            if (CollectionUtils.isEmpty(badGuestIds)) return Collections.emptyList();

            //根据黑名单id查询黑名单集合
            LambdaQueryWrapper<BadGuest> badGuestLambdaQueryWrapper = new LambdaQueryWrapper<>();
            badGuestLambdaQueryWrapper.in(BadGuest::getId, badGuestIds);
            badGuestLambdaQueryWrapper.in(BadGuest::getPropertyId, propertyIdsByTenant);
            badGuestLambdaQueryWrapper.in(BadGuest::getApproveStatus, approveStatusList);
            return list(badGuestLambdaQueryWrapper);
        }

        /**
         * 保存黑名单信息，同时保存(主)同伴信息
         *
         * @param propertyDTO       资产DTO
         * @param badGuestList      黑名单集合
         * @param blackBOS          黑名单BO集合
         * @param firstApproveLevel 第一层审批层级
         * @return void
         * @author lichen
         * @date 2023/9/25
         */
        @Override
        public void saveBatchBadGuestAndPrimaryCompanion(PropertyDTO propertyDTO, List<BadGuest> badGuestList, List<BlackBO> blackBOS, BadGuestApproveLevel firstApproveLevel) {
            //保存黑名单信息
            saveBatch(badGuestList);

            //黑名单BO按照唯一键进行分组
            Map<String, BlackBO> uniqueKey2BlackBO = blackBOS.stream().collect(
                    Collectors.toMap(item -> BadGuest.generateKey(item.getFirstName(), item.getLastName(), item.getCheckInDate()), Function.identity()));

            //操作日志集合
            ArrayList<BgOperateLog> operateLogs = Lists.newArrayListWithCapacity(badGuestList.size());
            //保存黑名单同伴信息
            ArrayList<BadGuestCompanion> badGuestCompanionList = Lists.newArrayListWithCapacity(badGuestList.size());

            for (BadGuest badGuest : badGuestList) {
                //操作日志
                BgOperateLog operateLog = new BgOperateLog()
                        .setTenantId(propertyDTO.getTenantId())
                        .setPropertyId(propertyDTO.getId())
                        .setBadGuestId(badGuest.getId())
                        .setUserId(0)
                        .setOperateType(BgOperateTypeEnum.TO_APPROVE.getType())
                        .setOperateTime(DateTimeUtils.nowTimestamp())
                        .setOpinion("");
                operateLogs.add(operateLog);
                //当前数据的唯一key
                String uniqueKey = BadGuest.generateKey(badGuest.getFirstName(), badGuest.getLastName(), badGuest.getCheckInDate());
                //黑名单BO
                BlackBO blackBO = uniqueKey2BlackBO.get(uniqueKey);

                //构建黑名单同伴
                BadGuestCompanion badGuestCompanion = new BadGuestCompanion();
                badGuestCompanion.setPropertyId(badGuest.getPropertyId());
                badGuestCompanion.setBadGuestId(badGuest.getId());
                badGuestCompanion.setFirstName(badGuest.getFirstName());
                badGuestCompanion.setLastName(badGuest.getLastName());
                badGuestCompanion.setIsPrimary(true);
                badGuestCompanion.setCreatedBy(badGuest.getCreatedBy());
                badGuestCompanion.setCreatedTime(badGuest.getCreatedTime());
                //设置手机号
                if (StringUtils.isNotBlank(blackBO.getPhone())) {
                    badGuestCompanion.setPhone(JsonUtils.convertObjListToString(objectMapper, Lists.newArrayList(blackBO.getPhone())));
                } else {
                    badGuestCompanion.setPhone("[]");
                }
                //设置地址
                if (StringUtils.isNotBlank(blackBO.getAddress())) {
                    badGuestCompanion.setAddress(JsonUtils.convertObjListToString(objectMapper, Lists.newArrayList(blackBO.getAddress())));
                } else {
                    badGuestCompanion.setAddress("[]");
                }
                //设置会员卡号
                if (StringUtils.isNotBlank(blackBO.getMemberCardNum())) {
                    badGuestCompanion.setMemberCardNum(JsonUtils.convertObjListToString(objectMapper, Lists.newArrayList(blackBO.getMemberCardNum())));
                } else {
                    badGuestCompanion.setMemberCardNum("[]");
                }
                //设置邮箱
                if (StringUtils.isNotBlank(blackBO.getEmail())) {
                    badGuestCompanion.setEmail(JsonUtils.convertObjListToString(objectMapper, Lists.newArrayList(blackBO.getEmail())));
                } else {
                    badGuestCompanion.setEmail("[]");
                }
                badGuestCompanionList.add(badGuestCompanion);

            }
            //批量保存黑名单同伴信息
            badGuestCompanionService.saveBatch(badGuestCompanionList);
            //批量保存操作日志
            operateLogService.saveBatch(operateLogs);

            if (Objects.nonNull(firstApproveLevel)) {
                TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                    List<Integer> userIds = approveConfigService.listByLevelIds(firstApproveLevel.getPropertyId(), Lists.newArrayList(firstApproveLevel.getId()))
                            .stream().map(BadGuestApproveConfig::getUserId).toList();
                    //发送通知
                    badGuestNotifyService.notifyNextApproveUsers(firstApproveLevel.getPropertyId(), userIds, badGuestList, firstApproveLevel.getCc());
                });
            }
        }

        /**
         * 校验黑名单数据是否存在
         *
         * @param id 黑名单ID
         * @return cloud.wconnect.frontoffice.entity.badguest.BadGuest
         * @author lichen
         * @date 2024/6/4
         */
        @Override
        public BadGuest checkExist(long id) {
            BadGuest badGuest = getById(id);
            if (Objects.isNull(badGuest)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "id=" + id + " ,Guests Blacklist not found");
            }
            return badGuest;
        }

        /**
         * 删除黑名单的数据
         *
         * @param badGuest 黑名单
         * @return void
         * @author lichen
         * @date 2024/6/4
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void remove(BadGuest badGuest) {
            //删除黑名单
            removeById(badGuest.getId());
            //删除同伴信息
            badGuestCompanionService.remove(new LambdaQueryWrapper<BadGuestCompanion>()
                    .eq(BadGuestCompanion::getBadGuestId, badGuest.getId()));
            //删除操作日志
            operateLogService.remove(new LambdaQueryWrapper<BgOperateLog>()
                    .eq(BgOperateLog::getBadGuestId, badGuest.getId()));
            //删除审批日志
            approveLogService.remove(new LambdaQueryWrapper<BadGuestApproveLog>()
                    .eq(BadGuestApproveLog::getGuestId, badGuest.getId()));
        }

        /**
         * 分页查询黑名单数据
         *
         * @param tenantId        租户id
         * @param propertyDTOList 资产DTO集合
         * @param badGuestQuery   黑名单查询对象
         * @return cloud.wconnect.commonlib.pojo.dto.ListPagedDTO<cloud.wconnect.frontoffice.pojo.vo.badguest.BadGuestVO>
         * @author lichen
         * @date 2025/6/4
         */
        @Override
        public ListPagedDTO<BadGuestVO> pageQuery(Integer tenantId, List<PropertyDTO> propertyDTOList, BadGuestQuery badGuestQuery) {
            //根据客人信息模糊查询bad guest companion
            Set<Long> guestIds = Collections.emptySet();
            if (badGuestQuery.isQueryCompanion()) {
                List<BadGuestCompanion> companions = badGuestCompanionService.listByGuestInfo(badGuestQuery.getQueryPropertyIds(), badGuestQuery);
                if (CollectionUtils.isNotEmpty(companions)) {
                    guestIds = companions.stream().map(BadGuestCompanion::getBadGuestId).collect(Collectors.toSet());
                } else {
                    return new ListPagedDTO<>();
                }
            }

            //默认排序方式
            if (Objects.isNull(badGuestQuery.getIsAsc())) badGuestQuery.setIsAsc(false);
            //是否根据入住日期查询
            boolean isCheckInDate = Objects.nonNull(badGuestQuery.getStartCheckInDate()) && Objects.nonNull(badGuestQuery.getEndCheckInDate());
            //是否根据离店日期查询
            boolean isCheckOutDate = Objects.nonNull(badGuestQuery.getStartCheckOutDate()) && Objects.nonNull(badGuestQuery.getEndCheckOutDate());
            //构建查询
            LambdaQueryWrapper<BadGuest> queryWrapper = new LambdaQueryWrapper<BadGuest>()
                    .in(CollectionUtils.isNotEmpty(guestIds), BadGuest::getId, guestIds)
                    .in(BadGuest::getPropertyId, badGuestQuery.getQueryPropertyIds())
                    .between(isCheckInDate, BadGuest::getCheckInDate, badGuestQuery.getStartCheckInDate(), badGuestQuery.getEndCheckInDate())
                    .between(isCheckOutDate, BadGuest::getCheckOutDate, badGuestQuery.getStartCheckOutDate(), badGuestQuery.getEndCheckOutDate())
                    .eq(StringUtils.isNotBlank(badGuestQuery.getFolioNumber()), BadGuest::getFolioNumber, badGuestQuery.getFolioNumber())
                    .in(CollectionUtils.isNotEmpty(badGuestQuery.getStatusList()), BadGuest::getApproveStatus, badGuestQuery.getStatusList())
                    .eq(Objects.nonNull(badGuestQuery.getCreatedBy()), BadGuest::getCreatedBy, badGuestQuery.getCreatedBy())
                    .eq(Objects.nonNull(badGuestQuery.getType()), BadGuest::getType, badGuestQuery.getType());

            //排序
            if (badGuestQuery.getIsAsc()) {
                queryWrapper.orderByAsc(OrderByFieldEnum.getSFunction(badGuestQuery.getOrderBy()));
            } else {
                queryWrapper.orderByDesc(OrderByFieldEnum.getSFunction(badGuestQuery.getOrderBy()));
            }

            //分页查询
            Page<BadGuest> resultPage = page(new Page<>(badGuestQuery.getPage(), badGuestQuery.getSize()), queryWrapper);
            if (resultPage.getTotal() == BaseConstant.ZERO) {
                return new ListPagedDTO<>();
            }
            //循环处理数据
            List<BadGuest> records = resultPage.getRecords();
            List<Long> badGuestIds = Lists.newArrayListWithExpectedSize(records.size());
            Set<Integer> createBySet = Sets.newHashSetWithExpectedSize(records.size());
            for (BadGuest badGuest : records) {
                badGuestIds.add(badGuest.getId());
                createBySet.add(badGuest.getCreatedBy());

                long checkInDate = badGuest.getCheckInDate();
                long checkOutDate = badGuest.getCheckOutDate();
                long lastCheckInDate = badGuest.getLastCheckInDate();
                long lastCheckOutDate = badGuest.getLastCheckOutDate();
                if ((checkInDate == lastCheckInDate) && (checkOutDate == lastCheckOutDate)) {
                    badGuest.setLastCheckInDate(null);
                    badGuest.setLastCheckOutDate(null);
                }
            }

            //查询每个黑名单关联的同伴名称
            List<BadGuestCompanion> resultCompanions = badGuestCompanionService.listByBadGuestIds(badGuestIds);
            Map<Long, List<BadGuestCompanion>> companionsByBadGuestId = resultCompanions.stream().collect(groupingBy(BadGuestCompanion::getBadGuestId));

            //查询黑名单的备注
            Map<Long, Long> badGuestId2Count = commentService.listByBadGuestId(badGuestIds)
                    .stream().collect(groupingBy(BgComment::getBadGuestId, counting()));

            //查询用户姓名
            Map<Integer, String> userId2Name = userInternalService.getNameMapByIds(tenantId, createBySet);

            Map<Integer, String> propertyId2Name = propertyDTOList.stream().collect(Collectors.toMap(PropertyDTO::getId, PropertyDTO::getName));

            //records -> List<BadGuestVO>
            List<BadGuestVO> badGuestVOList = beanMapper.mapList(records, BadGuestVO.class, (record, vo) -> {
                //同住人员
                vo.setCompanions(beanMapper.mapList(companionsByBadGuestId.get(vo.getId()), CompanionVO.class));
                //设置附件
                String attachments = vo.getAttachments();
                if (StringUtils.isNotBlank(attachments) && !attachments.equals("[]")) {
                    vo.setAttachmentDtos(JsonUtils.convertStringToList(objectMapper, attachments, AttachmentVO.class));
                } else {
                    vo.setAttachmentDtos(Collections.emptyList());
                }
                //设置创建人名称
                vo.setCreatedByName(userId2Name.get(vo.getCreatedBy()));
                //设置资产名称
                vo.setPropertyName(propertyId2Name.get(vo.getPropertyId()));
                //设置备注数量
                vo.setCommentCount(badGuestId2Count.getOrDefault(vo.getId(), 0L));
                //设置状态
                vo.setStatus(record.getApproveStatus());
            });
            return new ListPagedDTO<>(resultPage.getTotal(), badGuestVOList);
        }

        /**
         * 查询导出的黑名单数据
         *
         * @param exportBadGuestQuery 查询对象
         * @param propertyDTO         当前资产DTO
         * @param propertyDTOList     资产DTO集合
         * @param locale              当前上下文语言
         * @return java.util.List<cloud.wconnect.frontoffice.pojo.vo.badguest.BadGuestExcelVO>
         * @author lichen
         * @date 2025/6/4
         */
        @Override
        public List<BadGuestExcelVO> listExport(ExportBadGuestQuery exportBadGuestQuery, PropertyDTO propertyDTO, List<PropertyDTO> propertyDTOList, Locale locale) {
            //资产名称集合
            Map<Integer, String> propertyId2Name = propertyDTOList.stream().collect(toMap(PropertyDTO::getId, PropertyDTO::getName));

            //查询黑名单数据
            List<BadGuest> badGuests = list(new LambdaQueryWrapper<BadGuest>()
                    .in(BadGuest::getPropertyId, exportBadGuestQuery.getQueryPropertyIds())
                    .between(BadGuest::getCheckInDate, exportBadGuestQuery.getStartCheckInDate(), exportBadGuestQuery.getEndCheckInDate())
                    .eq(Objects.nonNull(exportBadGuestQuery.getType()), BadGuest::getType, exportBadGuestQuery.getType())
                    .orderByDesc(BadGuest::getId));

            //查询每个黑名单关联的同伴名称
            Map<Long, List<BadGuestCompanion>> companionsByBadGuestId = Collections.emptyMap();
            if (CollectionUtils.isNotEmpty(badGuests)) {
                List<Long> badGuestIds = badGuests.stream().map(BadGuest::getId).collect(toList());
                companionsByBadGuestId = badGuestCompanionService.list(new LambdaQueryWrapper<BadGuestCompanion>()
                                .in(BadGuestCompanion::getBadGuestId, badGuestIds))
                        .stream().collect(groupingBy(BadGuestCompanion::getBadGuestId));
            }
            final Map<Long, List<BadGuestCompanion>> finalCompanionsByBadGuestId = companionsByBadGuestId;
            //时区
            String timezone = propertyDTO.getTimezone();
            //配置的日期格式
            String longDateFormat = propertyDTO.getInternationalization().getDateTime().getLongDate();
            //名称显示顺序
            String nameOrder = propertyDTO.getInternationalization().getNameOrder();
            //yes or no
            String i18nYes = i18nMessageUtils.message(I18nConstant.YES, locale);
            String i18nNo = i18nMessageUtils.message(I18nConstant.NO, locale);
            return beanMapper.mapList(badGuests, BadGuestExcelVO.class, (badGuest, excelVO) -> {
                //设置资产名称
                excelVO.setPropertyName(propertyId2Name.get(badGuest.getPropertyId()));
                //设置客人姓名
                excelVO.setGuestName(InternationalizationUtils.formatName(badGuest.getFirstName(), badGuest.getLastName(), nameOrder));
                //获取关联的同伴信息
                List<CompanionExcelVO> companionExcelVOS = beanMapper.mapList(finalCompanionsByBadGuestId.get(badGuest.getId()), CompanionExcelVO.class, (entity, vo) -> {
                    vo.setIsPrimaryDesc(entity.getIsPrimary() ? i18nYes : i18nNo);
                    vo.setGuestName(InternationalizationUtils.formatName(entity.getFirstName(), entity.getLastName(), nameOrder));

                    //格式化手机号
                    List<String> phoneList = JsonUtils.convertStringToList(objectMapper, entity.getPhone(), String.class);
                    if (CollectionUtils.isEmpty(phoneList)) {
                        vo.setFormatPhone("--");
                    } else {
                        vo.setFormatPhone(StringUtils.join(phoneList, "\r\n"));
                    }
                    //格式化地址
                    List<String> addressList = JsonUtils.convertStringToList(objectMapper, entity.getAddress(), String.class);
                    if (CollectionUtils.isEmpty(addressList)) {
                        vo.setFormatAddress("--");
                    } else {
                        vo.setFormatAddress(StringUtils.join(addressList, "\r\n"));
                    }
                    //格式化会员卡号
                    List<String> memberCardNumList = JsonUtils.convertStringToList(objectMapper, entity.getMemberCardNum(), String.class);
                    if (CollectionUtils.isEmpty(memberCardNumList)) {
                        vo.setFormatMemberCardNum("--");
                    } else {
                        vo.setFormatMemberCardNum(StringUtils.join(memberCardNumList, "\r\n"));
                    }
                    //格式化邮箱
                    List<String> emailList = JsonUtils.convertStringToList(objectMapper, entity.getEmail(), String.class);
                    if (CollectionUtils.isEmpty(emailList)) {
                        vo.setFormatEmail("--");
                    } else {
                        vo.setFormatEmail(StringUtils.join(emailList, "\r\n"));
                    }
                });
                excelVO.setCompanions(companionExcelVOS);
                //处理checkInDate
                Long checkInDate = badGuest.getCheckInDate();
                if (checkInDate == BaseConstant.ZERO) {
                    excelVO.setCheckInDateStr("");
                } else {
                    excelVO.setCheckInDateStr(InternationalizationUtils.formatDateTime(checkInDate, longDateFormat, StringUtils.EMPTY, timezone, locale));
                }
                //处理checkOutDate
                Long checkOutDate = badGuest.getCheckOutDate();
                if (checkOutDate == BaseConstant.ZERO) {
                    excelVO.setCheckOutDateStr("");
                } else {
                    excelVO.setCheckOutDateStr(InternationalizationUtils.formatDateTime(checkOutDate, longDateFormat, StringUtils.EMPTY, timezone, locale));
                }
                excelVO.setApproveStatusDesc(i18nMessageUtils.message(BlacklistApproveStatusEnum.getI18nCode(badGuest.getApproveStatus()), locale));
                //黑名单类型
                String typeDesc = i18nMessageUtils.message(BlacklistTypeEnum.getI18nCodeByType(badGuest.getType()), locale);
                excelVO.setTypeDesc(typeDesc);

                if (StringUtils.isNotBlank(badGuest.getCaseReason())) {
                    //黑名单理由
                    String caseReasonDesc = i18nMessageUtils.message(BlacklistReasonEnum.getI18nCodeByType(badGuest.getCaseReason()), locale);
                    excelVO.setCaseReasonDesc(caseReasonDesc);
                }
            });
        }

        /**
         * 校验是否归属于当前资产
         *
         * @param badGuest   黑名单数据
         * @param propertyId 资产id
         * @return void
         * @author lichen
         * @date 2025/6/4
         */
        @Override
        public void checkBelong(BadGuest badGuest, Integer propertyId) {
            if (badGuest.getPropertyId().compareTo(propertyId) != 0) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(), BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("BadGuest not belong currentProperty, badGuestId:{0},BadGuestBelongPropertyId:{1},currentPropertyId:{2}",
                                badGuest.getId(), badGuest.getPropertyId(), propertyId));
            }
        }

        /**
         * 作废黑名单
         *
         * @param propertyDTO     资产DTO
         * @param badGuest        黑名单
         * @param cancellationDTO 作废的DTO
         * @param loginUserId     登录用户id
         * @return void
         * @author lichen
         * @date 2025/6/4
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void cancellation(PropertyDTO propertyDTO, BadGuest badGuest, CancellationDTO cancellationDTO, int loginUserId) {
            //作废黑名单
            BadGuest updateBadGuest = new BadGuest().setId(badGuest.getId())
                    .setApproveStatus(BlacklistApproveStatusEnum.CANCELLATION.getStatus())
                    .setCancellationReason(cancellationDTO.getCancellationReason());
            updateById(updateBadGuest);
            //记录操作日志
            operateLogService.save(propertyDTO, badGuest.getId(), loginUserId, BgOperateTypeEnum.CANCELLATION.getType(),
                    cancellationDTO.getCancellationReason());
        }

        /**
         * 批量作废黑名单
         *
         * @param propertyDTO 资产DTO
         * @param badGuestIds 黑名单ID集合
         * @return void
         * @author lichen
         * @date 2025/6/4
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void batchCancellation(PropertyDTO propertyDTO, List<Long> badGuestIds) {
            if (CollectionUtils.isEmpty(badGuestIds)) return;

            long nowTimestamp = DateTimeUtils.nowTimestamp();

            ArrayList<BadGuest> badGuests = Lists.newArrayListWithCapacity(badGuestIds.size());
            ArrayList<BgOperateLog> bgOperateLogs = Lists.newArrayListWithCapacity(badGuestIds.size());
            for (Long badGuestId : badGuestIds) {
                badGuests.add(new BadGuest().setId(badGuestId)
                        .setApproveStatus(BlacklistApproveStatusEnum.CANCELLATION.getStatus()));

                BgOperateLog operateLog = new BgOperateLog().setTenantId(propertyDTO.getTenantId()).setPropertyId(propertyDTO.getId())
                        .setBadGuestId(badGuestId).setUserId(0).setOperateType(BgOperateTypeEnum.CANCELLATION.getType())
                        .setOperateTime(nowTimestamp)
                        .setOpinion("");
                bgOperateLogs.add(operateLog);
            }

            //批量作废黑名单
            updateBatchById(badGuests);
            //批量保存操作日志
            operateLogService.saveBatch(bgOperateLogs);
        }

        /**
         * 查询黑名单详情
         *
         * @param tenantId 租户id
         * @param badGuest 黑名单数据
         * @return cloud.wconnect.frontoffice.pojo.vo.badguest.BadGuestVO
         * @author lichen
         * @date 2025/6/5
         */
        @Override
        public BadGuestVO getDetail(Integer tenantId, BadGuest badGuest) {
            //获取黑名单数据的资产信息
            PropertyDTO propertyDTO = propertyI18nRedisService.get(badGuest.getPropertyId());

            //转换
            BadGuestVO badGuestVO = beanMapper.map(badGuest, BadGuestVO.class);
            badGuestVO.setPropertyName(propertyDTO.getName());

            //查询黑名单关联的同伴姓名集合
            List<BadGuestCompanion> companions = badGuestCompanionService.listByBadGuestIds(Lists.newArrayList(badGuest.getId()));
            badGuestVO.setCompanions(beanMapper.mapList(companions, CompanionVO.class));

            //查询用户信息
            Map<Integer, String> userId2Name = userInternalService.getNameMapByIds(tenantId,
                    Sets.newHashSet(badGuest.getCreatedBy(), badGuest.getLastUpdatedBy()));
            //设置用户信息
            badGuestVO.setLastUpdatedByName(userId2Name.get(badGuest.getLastUpdatedBy()));
            badGuestVO.setCreatedByName(userId2Name.get(badGuest.getCreatedBy()));

            //设置附件
            String attachments = badGuest.getAttachments();
            if (StringUtils.isNotBlank(attachments) && !attachments.equals("[]")) {
                badGuestVO.setAttachmentDtos(JsonUtils.convertStringToList(objectMapper, attachments, AttachmentVO.class));
            } else {
                badGuestVO.setAttachmentDtos(Collections.emptyList());
            }
            //设置状态
            badGuestVO.setStatus(badGuest.getApproveStatus());
            return badGuestVO;
        }

        /**
         * 根据caseNumber 查询黑名单数据
         *
         * @param propertyId  资产id
         * @param caseNumbers caseNumber集合
         * @return java.util.List<cloud.wconnect.frontoffice.entity.badguest.BadGuest>
         * @author lichen
         * @date 2025/6/7
         */
        @Override
        public List<BadGuest> listByCaseNumbers(Integer propertyId, List<String> caseNumbers) {
            if (CollectionUtils.isEmpty(caseNumbers)) return Collections.emptyList();
            return list(new LambdaQueryWrapper<BadGuest>().eq(BadGuest::getPropertyId, propertyId)
                    .in(BadGuest::getCaseNumber, caseNumbers));
        }

        /**
         * 保存cchargeback来源的黑名单数据
         *
         * @param propertyDTO 资产DTO
         * @param blackBOS    黑名单BO
         * @param badGuests   黑名单集合
         * @return void
         * @author lichen
         * @date 2025/6/7
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveBatchByChargeback(PropertyDTO propertyDTO, List<BlackBO> blackBOS, List<BadGuest> badGuests) {
            //根据caseNumber查询黑名单集合
            List<BadGuest> badGuestsByCaseNumbers = listByCaseNumbers(propertyDTO.getId(), badGuests.stream().map(BadGuest::getCaseNumber).toList());
            if (CollectionUtils.isNotEmpty(badGuestsByCaseNumbers)) {
                List<Long> badGuestIds = badGuestsByCaseNumbers.stream().map(BadGuest::getId).toList();
                //删除黑名单
                removeBatchByIds(badGuestIds);
                //删除同伴信息
                badGuestCompanionService.remove(new LambdaQueryWrapper<BadGuestCompanion>()
                        .in(BadGuestCompanion::getBadGuestId, badGuestIds));
                //删除操作日志
                operateLogService.remove(new LambdaQueryWrapper<BgOperateLog>()
                        .in(BgOperateLog::getBadGuestId, badGuestIds));
                //删除审批日志
                approveLogService.remove(new LambdaQueryWrapper<BadGuestApproveLog>()
                        .in(BadGuestApproveLog::getGuestId, badGuestIds));
            }

            //根据客人姓名和入住时间查询未作废的黑名单集合
            List<BadGuest> notVoidedBadGuests = baseMapper.listNotVoidedByGuestNameAndCheckInDate(propertyDTO.getId(), badGuests);
            Map<String, List<BadGuest>> key2BadGuests = notVoidedBadGuests.stream()
                    .collect(Collectors.groupingBy(item -> BadGuest.generateKey(item.getFirstName(), item.getLastName(), item.getCheckInDate())));

            //筛选出需要新增的黑名单集合
            List<BadGuest> batchList = badGuests.stream().filter(item -> {
                String key = BadGuest.generateKey(item.getFirstName(), item.getLastName(), item.getCheckInDate());
                return CollectionUtils.isEmpty(key2BadGuests.get(key));
            }).toList();
            if (CollectionUtils.isEmpty(batchList)) return;

            //获取资产的第一层审批级别
            BadGuestApproveLevel approveLevel = approveLevelService.getFirstApproveLevelByPropertyId(propertyDTO.getId());
            //填充非空字段
            paddingField(batchList, approveLevel);
            //保存黑名单
            saveBatchBadGuestAndPrimaryCompanion(propertyDTO, batchList, blackBOS, approveLevel);
        }

        /**
         * 填充非空字段
         *
         * @param badGuestList 黑名单集合
         * @param approveLevel 审批级别
         * @return void
         * @author lichen
         * @date 2025/6/7
         */
        private void paddingField(List<BadGuest> badGuestList, BadGuestApproveLevel approveLevel) {
            //根据审批层级判断状态
            Integer approveStatus = BlacklistApproveStatusEnum.APPROVED.getStatus();
            Integer approveIndex = 0;
            long batchId = 0L;
            if (Objects.nonNull(approveLevel)) {
                approveStatus = BlacklistApproveStatusEnum.APPROVEING.getStatus();
                approveIndex = approveLevel.getApproveIndex();
                //生成固定位数的字符串（如补零）
                String randomStr = String.format("%04d", new Random().nextInt(10000));
                batchId = Long.parseLong(DateTimeUtils.nowTimestamp() + randomStr);
            }

            for (BadGuest badGuest : badGuestList) {
                badGuest.setType(BlacklistTypeEnum.GUEST_ROOMS.getType());
                badGuest.setApproveStatus(approveStatus);
                badGuest.setApproveIndex(approveIndex);
                badGuest.setBatchId(batchId);
                badGuest.setWhatHappened("");
                badGuest.setReportedTo("");
                badGuest.setResults("");
                badGuest.setCreatedTime(DateTimeUtils.nowTimestamp());
                badGuest.setLastCheckInDate(0L);
                badGuest.setLastCheckOutDate(0L);
                badGuest.setAttachments("[]");
            }
        }

        /**
         * 更新为审批通过
         *
         * @param oriBadGuest 黑名单数据
         * @return void
         * @author lichen
         * @date 2024/6/4
         */
        private void updateApproved(BadGuest oriBadGuest) {
            oriBadGuest.setApproveStatus(BlacklistApproveStatusEnum.APPROVED.getStatus());
            updateById(oriBadGuest);
            //审批通过后发送邮件给数据创建人
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                badGuestNotifyService.notifyCreateBy(oriBadGuest, BlacklistApproveStatusEnum.APPROVED.getStatus());
            });
        }

        /**
         * 更新为approveing 并通知给待审批人
         *
         * @param approveLevel       审批级别
         * @param currentBadGuest    黑名单数据
         * @param ignoreNotifyUserId 忽略的通知人id
         * @return void
         * @author lichen
         * @date 2023/9/26
         */
        private void updateAproveing(BadGuestApproveLevel approveLevel, BadGuest currentBadGuest, Integer ignoreNotifyUserId) {
            //更新为审核中
            currentBadGuest.setApproveStatus(BlacklistApproveStatusEnum.APPROVEING.getStatus());
            currentBadGuest.setApproveIndex(approveLevel.getApproveIndex());
            updateById(currentBadGuest);

            //查询下一个审批层级的审批人
            List<Integer> nextApproveIndexUserIds = approveConfigService.listByLevelIds(approveLevel.getPropertyId(), Lists.newArrayList(approveLevel.getId()))
                    .stream().map(BadGuestApproveConfig::getUserId).toList();
            //删除需要过滤的通知人
            ArrayList<Integer> notifyUserIds = Lists.newArrayList(nextApproveIndexUserIds);
            notifyUserIds.remove(ignoreNotifyUserId);
            //发送给审批人
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                badGuestNotifyService.notifyNextApproveUsers(currentBadGuest.getPropertyId(), notifyUserIds, Lists.newArrayList(currentBadGuest), approveLevel.getCc());
            });
        }

        /**
         * @param oriBadGuest
         * @return void
         * @description 推送数据至下一个节点
         * @author lichen
         * @date 2023/3/23 8:56
         */
        private void pushNextIndex(BadGuest oriBadGuest) {
            //查询下一个节点
            LambdaQueryWrapper<BadGuestApproveLevel> badGuestApproveLevelQueryWrapper = new LambdaQueryWrapper<>();
            badGuestApproveLevelQueryWrapper.eq(BadGuestApproveLevel::getPropertyId, oriBadGuest.getPropertyId());
            badGuestApproveLevelQueryWrapper.gt(BadGuestApproveLevel::getApproveIndex, oriBadGuest.getApproveIndex());
            badGuestApproveLevelQueryWrapper.orderByAsc(BadGuestApproveLevel::getApproveIndex);
            badGuestApproveLevelQueryWrapper.last("limit 1");
            BadGuestApproveLevel nextIndexApprove = approveLevelService.getOne(badGuestApproveLevelQueryWrapper);
            if (Objects.isNull(nextIndexApprove)) {
                //更新数据为审核结束
                oriBadGuest.setApproveStatus(BlacklistApproveStatusEnum.APPROVED.getStatus());
                updateById(oriBadGuest);

                //审批通过后发送邮件给数据创建人
                TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                    badGuestNotifyService.notifyCreateBy(oriBadGuest, BlacklistApproveStatusEnum.APPROVED.getStatus());
                });
            } else {
                //更新数据，推送至下一个审核节点
                oriBadGuest.setApproveStatus(BlacklistApproveStatusEnum.APPROVEING.getStatus());
                oriBadGuest.setApproveIndex(nextIndexApprove.getApproveIndex());
                updateById(oriBadGuest);
                //查询下一个审批层级的审批人
                List<Integer> nextApproveIndexUserIds = approveConfigService.listByLevelIds(nextIndexApprove.getPropertyId(), Lists.newArrayList(nextIndexApprove.getId()))
                        .stream().map(BadGuestApproveConfig::getUserId).toList();
                //发送给审批人
                TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                    badGuestNotifyService.notifyNextApproveUsers(oriBadGuest.getPropertyId(), nextApproveIndexUserIds, Lists.newArrayList(oriBadGuest), nextIndexApprove.getCc());
                });

            }
        }

        /**
         * @param oriBadGuest
         * @return void
         * @description 更新数据为审核驳回
         * @author lichen
         * @date 2023/3/23 8:47
         */
        private void updateRejected(BadGuest oriBadGuest) {
            //更新为审核驳回
            oriBadGuest.setApproveStatus(BlacklistApproveStatusEnum.REJECTED.getStatus());
            updateById(oriBadGuest);
            //审批拒绝后发送邮件给数据创建人
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                badGuestNotifyService.notifyCreateBy(oriBadGuest, BlacklistApproveStatusEnum.REJECTED.getStatus());
            });
        }

    }