# WalkRoomDateinOtherServiceImpl.java

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

    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.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    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.cons.walkroomrequest.WalkRoomErrorI18nConstant;
    import cloud.wconnect.frontoffice.cons.walkroomrequest.WalkRoomFieldI18nConstant;
    import cloud.wconnect.frontoffice.cons.walkroomrequest.WalkRoomStatusEnum;
    import cloud.wconnect.frontoffice.entity.walkroomrequest.*;
    import cloud.wconnect.frontoffice.mapper.walkroomrequest.WalkRoomDateinOtherMapper;
    import cloud.wconnect.frontoffice.pojo.dto.walkroomrequest.ConfirmDTO;
    import cloud.wconnect.frontoffice.pojo.dto.walkroomrequest.WalkRoomApproveDTO;
    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.core.conditions.update.LambdaUpdateWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Lists;
    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 jakarta.annotation.Nullable;

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

    /**
     * <p>
     * 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2023-07-08
     */
    @Service
    @DS("logbook")
    public class WalkRoomDateinOtherServiceImpl extends ServiceImpl<WalkRoomDateinOtherMapper, WalkRoomDateinOther> implements IWalkRoomDateinOtherService {

        @Autowired
        @Lazy
        private IWalkRoomApproveLogService approveLogService;
        @Autowired
        @Lazy
        private IWalkRoomApproveLevelService approveLevelService;
        @Autowired
        @Lazy
        private IWalkRoomApproveConfigService approveConfigService;
        @Autowired
        @Lazy
        private IWalkRoomRequestService walkRoomRequestService;
        @Autowired
        private Executor executor;
        @Autowired
        @Lazy
        private IWalkRoomNotifyService walkRoomNotifyService;
        @Autowired
        @Lazy
        private IWalkRoomRequestLogService walkRoomRequestLogService;
        @Autowired
        @Lazy
        private IWalkRoomDateinRateService walkRoomDateinRateService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 查询某审批级别下待审核的数据
         *
         * @param walkRoomApproveLevel 审批级别
         * @return java.util.List<cloud.wconnect.frontoffice.entity.walkroom.WalkRoomDateinOther>
         * @author lichen
         * @date 2023/7/11
         */
        @Override
        public List<WalkRoomDateinOther> listApprovingByApproveLevel(WalkRoomApproveLevel walkRoomApproveLevel) {
            LambdaQueryWrapper<WalkRoomDateinOther> queryWrapper = new LambdaQueryWrapper<>();
            //根据审批级别类型拼装查询条件
            if (walkRoomApproveLevel.getType().equals(WalkRoomApproveTypeEnum.SENT.getCode())) {
                queryWrapper.eq(WalkRoomDateinOther::getFromPropertyId, walkRoomApproveLevel.getPropertyId());
                queryWrapper.eq(WalkRoomDateinOther::getSentApproveStatus, ApproveStatusEnum.APPROVEING.getStatus());
                queryWrapper.eq(WalkRoomDateinOther::getSentApproveIndex, walkRoomApproveLevel.getApproveIndex());
            } else {
                queryWrapper.eq(WalkRoomDateinOther::getToPropertyId, walkRoomApproveLevel.getPropertyId());
                queryWrapper.eq(WalkRoomDateinOther::getReceiveApproveStatus, ApproveStatusEnum.APPROVEING.getStatus());
                queryWrapper.eq(WalkRoomDateinOther::getReceiveApproveIndex, walkRoomApproveLevel.getApproveIndex());
            }
            return list(queryWrapper);
        }

        /**
         * 查询入住明细记录
         *
         * @param reqIds walkRoom主键id
         * @return java.util.List<cloud.wconnect.frontoffice.entity.walkroom.WalkRoomDateinOther>
         * @author lichen
         * @date 2023/7/12
         */
        @Override
        public List<WalkRoomDateinOther> listByReqIds(List<Long> reqIds) {
            if (CollectionUtils.isEmpty(reqIds)) return Collections.emptyList();
            return list(new LambdaQueryWrapper<WalkRoomDateinOther>().in(WalkRoomDateinOther::getReqId, reqIds));
        }

        /**
         * 据指定日期，获取入住记录集合中指定日期之前 crs number 不为空的 crs number
         *
         * @param walkRoomDateinOthers 入住记录集合
         * @param date                 指定日期(yyyyMMdd)
         * @return java.lang.String
         * @author lichen
         * @date 2023/7/13
         */
        @Override
        public String getEarlyCrsNumber(List<WalkRoomDateinOther> walkRoomDateinOthers, Integer date) {
            if (CollectionUtils.isEmpty(walkRoomDateinOthers)) return "";

            //过滤出指定日期之前crsNumber不为空的入住记录，按照日期倒序排序
            List<WalkRoomDateinOther> earlyDateinOthers = walkRoomDateinOthers.stream()
                    .filter(item -> StringUtils.isNotBlank(item.getCrsNumber()) && item.getDate().compareTo(date) < 0)
                    .sorted(Comparator.comparingLong(WalkRoomDateinOther::getDate).reversed())
                    .collect(Collectors.toList());

            if (CollectionUtils.isEmpty(earlyDateinOthers)) return "";
            //获取指定日期之前，离指定日期最近日期的crsNumber
            return earlyDateinOthers.get(0).getCrsNumber();
        }

        /**
         * 当入住日没有入住记录时，生成一条入住记录
         *
         * @param walkRoomRequest          walk room数据
         * @param date                     入住日(yyyyMMdd)
         * @param propertyIdAndType2Levels 发送方和接收方的审批层级集合
         * @return cloud.wconnect.frontoffice.entity.walkroom.WalkRoomDateinOther 入住日对应的入住记录
         * @author lichen
         * @date 2023/7/19
         */
        @Override
        public WalkRoomDateinOther saveDateinOther(WalkRoomRequest walkRoomRequest, Integer date, Map<String, List<WalkRoomApproveLevel>> propertyIdAndType2Levels) {
            //查询入住日是否已经有入住记录
            WalkRoomDateinOther oriWalkRoomDateinOther = getOne(new LambdaQueryWrapper<WalkRoomDateinOther>()
                    .eq(WalkRoomDateinOther::getReqId, walkRoomRequest.getId()).eq(WalkRoomDateinOther::getDate, date));
            if (Objects.nonNull(oriWalkRoomDateinOther)) return oriWalkRoomDateinOther;
            //构建入住记录对象
            WalkRoomDateinOther dateinOther = buildDateinOther(walkRoomRequest, date, propertyIdAndType2Levels);
            //保存
            save(dateinOther);

            //更新finalOtherId、finalCrsNumber字段（注：有新增的入住记录时，需要更新finalOtherId、finalCrsNumber）
            walkRoomRequestService.updateFinalOtherIdAndFinalCrsNumber(walkRoomRequest);
            return dateinOther;
        }

        /**
         * 重置walk room所有入住记录的审批信息和接收方确认信息
         *
         * @param walkRoomRequest          walk room数据
         * @param propertyIdAndType2Levels 发送方和接收方的审批层级集合
         * @param isResetConfirm           是否重置确认信息
         * @return void
         * @author lichen
         * @date 2023/7/21
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void resetAllDateinApproveInfoAndConfirm(WalkRoomRequest walkRoomRequest, Map<String, List<WalkRoomApproveLevel>> propertyIdAndType2Levels, Boolean isResetConfirm) {
            List<WalkRoomDateinOther> dateinOthers = listByReqIds(Collections.singletonList(walkRoomRequest.getId()));
            if (CollectionUtils.isEmpty(dateinOthers)) return;

            //构建更新对象
            WalkRoomDateinOther dateinOther = new WalkRoomDateinOther();
            dateinOther.setFromPropertyId(walkRoomRequest.getFromPropertyId());
            dateinOther.setToPropertyId(walkRoomRequest.getToPropertyId());
            //重置walk room的审批信息
            clearApproveInfo(dateinOther, propertyIdAndType2Levels);
            //是否更新接收方为true时
            if (isResetConfirm) {
                //入住记录的接收方确认信息设置为未确认
                dateinOther.setCrsNumber("");
                dateinOther.setStatus(0);
                dateinOther.setComment("");
                dateinOther.setUpdateBy(0);
                dateinOther.setUpdateTime(0L);
            }

            //执行更新
            update(dateinOther, new LambdaUpdateWrapper<WalkRoomDateinOther>().eq(WalkRoomDateinOther::getReqId, walkRoomRequest.getId()));

            //删除审批日志
            List<Long> otherIds = dateinOthers.stream().map(WalkRoomDateinOther::getId).collect(Collectors.toList());
            approveLogService.remove(new LambdaQueryWrapper<WalkRoomApproveLog>()
                    .in(WalkRoomApproveLog::getDateinOtherId, otherIds)
                    .eq(WalkRoomApproveLog::getReqId, walkRoomRequest.getId()));
        }

        /**
         * 校验接收方在入住期间是否 未确认过 入住记录
         *
         * @param id walk room 主键id
         */
        @Override
        public void checkReceiverIsUnConfirmed(Long id) {
            long count = count(new LambdaQueryWrapper<WalkRoomDateinOther>()
                    .eq(WalkRoomDateinOther::getReqId, id)
                    .gt(WalkRoomDateinOther::getUpdateBy, 0));
            if (count > 0) {
                throw new BadRequestException(WalkRoomErrorI18nConstant.RECEIVER_ALREADY_CONFIRMED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The data cannot be modified because it has been confirmed by the receiver, walkRoomId=" + id);
            }
        }

        /**
         * 审批入住记录
         *
         * @param walkRoomRequest walk room数据
         * @param date            被审批的入住记录对应的入住日(yyyyMMdd)
         * @param approveDTO      审批dto
         * @return void
         * @author lichen
         * @date 2023/7/19
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void approve(WalkRoomRequest walkRoomRequest, Integer date, WalkRoomApproveDTO approveDTO) {
            Integer approveType = approveDTO.getApproveType();
            Integer fromPropertyId = walkRoomRequest.getFromPropertyId();
            Integer toPropertyId = walkRoomRequest.getToPropertyId();
            //查询发送方和接收方的审批层级
            Map<String, List<WalkRoomApproveLevel>> propertyIdAndType2Levels = approveLevelService.listByPropertyIds(Arrays.asList(fromPropertyId, toPropertyId)).stream()
                    .collect(Collectors.groupingBy(item -> WalkRoomApproveLevel.generateKey(item.getPropertyId(), item.getType())));

            //查询入住记录
            WalkRoomDateinOther dateinOther = getByReqIdAndDate(walkRoomRequest.getId(), date);
            if (Objects.isNull(dateinOther)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND, BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        MessageFormat.format("Walk room request Date in record not found, walkRoomRequestId:{0}, date:{1}",
                                walkRoomRequest.getId(), date));
            }

            Integer approveStatus = isSent(approveType) ? dateinOther.getSentApproveStatus() : dateinOther.getReceiveApproveStatus();
            //校验是否审批中
            if (approveStatus != ApproveStatusEnum.APPROVEING.getStatus()) {
                throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_STATE_CAN_OPERATE,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(ApproveStatusEnum.APPROVEING.getI18nCode(), WalkRoomFieldI18nConstant.OPERATE_APPROVAL),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        MessageFormat.format("Only the data in the approval can be approved. walkRoomId:{0}, date:{1}, approveStatus:{2}",
                                walkRoomRequest.getId(), date, approveStatus));
            }

            //获取审批层级集合
            List<WalkRoomApproveLevel> approveLevels;
            if (isSent(approveType)) {
                String sentKey = WalkRoomApproveLevel.generateKey(fromPropertyId, WalkRoomApproveTypeEnum.SENT.getCode());
                approveLevels = propertyIdAndType2Levels.get(sentKey);
            } else {
                String receiveKey = WalkRoomApproveLevel.generateKey(toPropertyId, WalkRoomApproveTypeEnum.RECEIVE.getCode());
                approveLevels = propertyIdAndType2Levels.get(receiveKey);
            }

            //审批
            doApprove(dateinOther, approveLevels, approveDTO);
        }

        /**
         * 处理目标日期的入住记录和审批信息
         *
         * @param walkRoomRequest          walk room数据
         * @param date                     入住日（yyyyMMdd）
         * @param propertyIdAndType2Levels 发送方和接收方的审批层级集合
         * @param confirmDTO               接收方确认信息
         * @return WalkRoomDateinOther 保存的入住记录信息
         * @author lichen
         * @date 2023/7/20
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public WalkRoomDateinOther saveDateinOtherAndResetApproveInfo(WalkRoomRequest walkRoomRequest, Integer date, Map<String, List<WalkRoomApproveLevel>> propertyIdAndType2Levels, ConfirmDTO confirmDTO) {
            //查询入住日入住记录
            WalkRoomDateinOther oriWalkRoomDateinOther = getOne(new LambdaQueryWrapper<WalkRoomDateinOther>()
                    .eq(WalkRoomDateinOther::getReqId, walkRoomRequest.getId()).eq(WalkRoomDateinOther::getDate, date));

            if (Objects.nonNull(oriWalkRoomDateinOther)) {
                //清空审批信息
                clearApproveInfo(oriWalkRoomDateinOther, propertyIdAndType2Levels);
                if (Objects.nonNull(confirmDTO)) {
                    //确认信息不为空时，填充确认信息
                    oriWalkRoomDateinOther.setCrsNumber(confirmDTO.getCrsNumber());
                    oriWalkRoomDateinOther.setComment(confirmDTO.getComment());
                    oriWalkRoomDateinOther.setStatus(confirmDTO.getStatus());
                    oriWalkRoomDateinOther.setUpdateBy(confirmDTO.getConfirmUserId());
                    oriWalkRoomDateinOther.setUpdateTime(DateTimeUtils.nowTimestamp());
                }
                //更新入住记录
                updateById(oriWalkRoomDateinOther);
                //删除审批日志
                approveLogService.remove(new LambdaQueryWrapper<WalkRoomApproveLog>()
                        .eq(WalkRoomApproveLog::getDateinOtherId, oriWalkRoomDateinOther.getId())
                        .eq(WalkRoomApproveLog::getReqId, walkRoomRequest.getId()));
                return oriWalkRoomDateinOther;
            } else {
                //构建入住记录对象
                WalkRoomDateinOther dateinOther = buildDateinOther(walkRoomRequest, date, propertyIdAndType2Levels);
                if (Objects.nonNull(confirmDTO)) {
                    //确认信息不为空时，填充确认信息
                    dateinOther.setCrsNumber(confirmDTO.getCrsNumber());
                    dateinOther.setComment(confirmDTO.getComment());
                    dateinOther.setStatus(confirmDTO.getStatus());
                    dateinOther.setUpdateBy(confirmDTO.getConfirmUserId());
                    dateinOther.setUpdateTime(DateTimeUtils.nowTimestamp());
                }
                //保存
                save(dateinOther);
                return dateinOther;
            }
        }

        /**
         * 根据 walk room主键id集合 和 入住日集合 查询入住记录
         *
         * @param reqIds walk room主键id集合
         * @param dates  入住日集合(yyyyMMdd)
         * @return java.util.List<cloud.wconnect.frontoffice.entity.walkroom.WalkRoomDateinOther>
         * @author lichen
         * @date 2023/7/19
         */
        @Override
        public List<WalkRoomDateinOther> listByReqIdsAndDates(List<Long> reqIds, @Nullable List<Integer> dates) {
            return list(new LambdaQueryWrapper<WalkRoomDateinOther>()
                    .in(WalkRoomDateinOther::getReqId, reqIds)
                    .in(CollectionUtils.isNotEmpty(dates), WalkRoomDateinOther::getDate, dates));
        }

        /**
         * 发送方审批
         *
         * @param dateinOther   待审批的入住记录
         * @param approveLevels 审批层级集合
         * @param approveDTO    审批dto
         * @return void
         * @author lichen
         * @date 2023/7/19
         */
        private void doApprove(WalkRoomDateinOther dateinOther, List<WalkRoomApproveLevel> approveLevels, WalkRoomApproveDTO approveDTO) {
            //审批类型
            Integer approveType = approveDTO.getApproveType();
            //获取入住记录在审批审批类型下对应的审批层级索引
            Integer appproveIndex = isSent(approveType) ? dateinOther.getSentApproveIndex() : dateinOther.getReceiveApproveIndex();
            //根据审批层级索引获取审批层级
            Optional<WalkRoomApproveLevel> optionalByApproveLevel = approveLevels.stream()
                    .filter(item -> item.getApproveIndex().equals(appproveIndex)).findAny();
            if (optionalByApproveLevel.isEmpty()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Walk room request approve level not exist");
            }
            WalkRoomApproveLevel currentApproveLevel = optionalByApproveLevel.get();

            //查询审批层级所需要的审批人集合
            List<WalkRoomApproveConfig> walkRoomApproveConfigs = approveConfigService.listByLevelIds(Collections.singletonList(currentApproveLevel.getId()));
            List<Integer> needApproveUsers = walkRoomApproveConfigs.stream().map(WalkRoomApproveConfig::getUserId).toList();
            //校验用户是否审批人员
            if (!needApproveUsers.contains(approveDTO.getApproveBy())) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("User are not approver, user:{0}", approveDTO.getApproveBy()));
            }

            //构建审批日志
            WalkRoomApproveLog approveLog = buildApproveLog(dateinOther, approveDTO, currentApproveLevel);

            //审批通过
            if (approveDTO.getApproveStatus() == ApproveStatusEnum.APPROVED.getStatus()) {
                approveLog.setIsApproved(true);
                //保存审批日志
                approveLogService.save(approveLog);

                Integer rule = currentApproveLevel.getRule();
                if (rule == ApproveCons.RULE_ALL) {
                    //查询入住记录在当前审批层级索引上已经审批的人
                    List<Integer> alreadyApprover = approveLogService.listApproveIndexLogByDateinOther(dateinOther, approveDTO.getApproveType())
                            .stream().map(WalkRoomApproveLog::getApproveBy).toList();
                    //已经审批的人 包含 当前节点所需的审批人集合
                    if (alreadyApprover.containsAll(needApproveUsers)) {
                        //推送数据至下一个节点
                        pushNextIndex(dateinOther, approveLevels, approveDTO.getApproveType());
                    }
                } else {
                    //推送数据至下一个节点
                    pushNextIndex(dateinOther, approveLevels, approveDTO.getApproveType());
                }
            } else {//审批拒绝
                approveLog.setIsApproved(false);
                //保存审批日志
                approveLogService.save(approveLog);
                if (isSent(approveType)) {
                    //更新发送方审批状态为审核驳回
                    dateinOther.setSentApproveStatus(ApproveStatusEnum.REJECTED.getStatus());
                } else {
                    //更新接收方审批状态为审核驳回
                    dateinOther.setReceiveApproveStatus(ApproveStatusEnum.REJECTED.getStatus());
                }
                updateById(dateinOther);
            }
        }

        /**
         * 接收方确认客人入住信息
         *
         * @param walkRoomRequest walk room数据
         * @param confirmDTO      确认信息
         * @param loginUserId     登录用户id
         * @return void
         * @author lichen
         * @date 2023/7/20
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void confirm(WalkRoomRequest walkRoomRequest, ConfirmDTO confirmDTO, int loginUserId) {
            //查询发送方和接收方的审批层级
            Integer fromPropertyId = walkRoomRequest.getFromPropertyId();
            Integer toPropertyId = walkRoomRequest.getToPropertyId();

            //查询发送方和接收方的审批层级
            Map<String, List<WalkRoomApproveLevel>> propertyIdAndType2Levels = approveLevelService
                    .listByPropertyIds(Arrays.asList(fromPropertyId, toPropertyId))
                    .stream()
                    .collect(Collectors.groupingBy(item -> WalkRoomApproveLevel.generateKey(item.getPropertyId(), item.getType())));

            /**
             * 处理目标日期的入住记录和审批信息
             * 1.如果目标日期的入住记录存在则清空审批信息
             * 2.如果目标日期的入住记录不存在则生成入住记录
             */
            WalkRoomDateinOther dateinOther = saveDateinOtherAndResetApproveInfo(walkRoomRequest, confirmDTO.getDate(), propertyIdAndType2Levels, confirmDTO);

            //提前退房
            if (confirmDTO.getStatus() != WalkRoomStatusEnum.CHECKED_IN.getCode()) {
                //更新finalOtherId、finalCrsNumber、invalidate(失效日)
                WalkRoomRequest updateRequest = new WalkRoomRequest();
                updateRequest.setFinalOtherId(dateinOther.getId());
                updateRequest.setInvalidDate(confirmDTO.getDate());
                updateRequest.setFinalCrsNumber(confirmDTO.getCrsNumber());
                updateRequest.setId(walkRoomRequest.getId());
                walkRoomRequestService.updateById(updateRequest);
                /**
                 * 删除掉大于提前退房日的入住记录和walk rate
                 */
                removeByReqIdAndDateRange(walkRoomRequest.getId(), null, confirmDTO.getDate(), true);
                walkRoomDateinRateService.removeByReqIdAndDateRange(walkRoomRequest.getId(), null, confirmDTO.getDate(), true);
            } else {
                //查询所有的入住记录
                List<WalkRoomDateinOther> walkRoomDateinOthers = listByReqIds(Collections.singletonList(walkRoomRequest.getId()));
                //按照入住日倒序
                walkRoomDateinOthers.sort(Comparator.comparing(WalkRoomDateinOther::getDate).reversed());

                //筛选出提前退房的入住记录
                Optional<WalkRoomDateinOther> checkOutDateinOtherOptional = walkRoomDateinOthers.stream()
                        .filter(item -> item.getStatus() == WalkRoomStatusEnum.EARLY_CHECK_OUT.getCode())
                        .findFirst();

                if (checkOutDateinOtherOptional.isPresent()) { //有提前退房的入住记录
                    //获取提前退房的入住记录
                    WalkRoomDateinOther checkOutDateinOther = checkOutDateinOtherOptional.get();
                    WalkRoomRequest updateRequest = new WalkRoomRequest();
                    updateRequest.setFinalOtherId(checkOutDateinOther.getId());
                    updateRequest.setInvalidDate(checkOutDateinOther.getDate());
                    updateRequest.setFinalCrsNumber(checkOutDateinOther.getCrsNumber());
                    updateRequest.setId(walkRoomRequest.getId());
                    walkRoomRequestService.updateById(updateRequest);
                } else {
                    //获取入住日最大的一条入住记录
                    WalkRoomDateinOther maxDateOther = walkRoomDateinOthers.get(0);
                    WalkRoomRequest updateRequest = new WalkRoomRequest();
                    updateRequest.setFinalOtherId(maxDateOther.getId());
                    updateRequest.setInvalidDate(0);
                    updateRequest.setFinalCrsNumber(maxDateOther.getCrsNumber());
                    updateRequest.setId(walkRoomRequest.getId());
                    walkRoomRequestService.updateById(updateRequest);
                }
            }

            /**
             * 记录日志
             */
            walkRoomRequestLogService.saveConfirmLog(walkRoomRequest, dateinOther, loginUserId);

            //发送审批通知
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                walkRoomNotifyService.dateinOtherApproveResetSendApproveNotify(Lists.newArrayList(dateinOther));
            });
        }

        /**
         * 查询目标日期之前的入住记录集合
         *
         * @param reqIds     walk room主键id
         * @param targetDate 目标日期(yyyyMMdd)
         * @return java.util.Map<java.lang.Long, java.util.List < cloud.wconnect.frontoffice.entity.walkroom.WalkRoomDateinOther>>
         * @author lichen
         * @date 2023/7/20
         */
        @Override
        public Map<Long, List<WalkRoomDateinOther>> listEarlyByReqidsAndDate(List<Long> reqIds, Integer targetDate) {
            return list(new LambdaQueryWrapper<WalkRoomDateinOther>()
                    .in(WalkRoomDateinOther::getReqId, reqIds)
                    .lt(WalkRoomDateinOther::getDate, targetDate)
                    .ne(WalkRoomDateinOther::getCrsNumber, ""))
                    .stream().collect(Collectors.groupingBy(WalkRoomDateinOther::getReqId));
        }

        /**
         * 构建入住记录
         *
         * @param walkRoomRequest          walk room数据
         * @param checkInDate              入住日期(yyyyMMdd)
         * @param propertyIdAndType2Levels 接收方和发送方的审批层级集合
         * @return cloud.wconnect.frontoffice.entity.walkroom.WalkRoomDateinOther
         * @author lichen
         * @date 2023/7/21
         */
        @Override
        public WalkRoomDateinOther buildDateinOther(WalkRoomRequest walkRoomRequest, Integer checkInDate, Map<String, List<WalkRoomApproveLevel>> propertyIdAndType2Levels) {
            WalkRoomDateinOther dateinOther = new WalkRoomDateinOther();
            dateinOther.setReqId(walkRoomRequest.getId());
            dateinOther.setFromPropertyId(walkRoomRequest.getFromPropertyId());
            dateinOther.setToPropertyId(walkRoomRequest.getToPropertyId());
            dateinOther.setDate(checkInDate);
            dateinOther.setCrsNumber("");
            dateinOther.setStatus(0);
            dateinOther.setUpdateBy(0);
            dateinOther.setUpdateTime(0L);

            //查询发送方是否有审批层级
            String sentKey = WalkRoomApproveLevel.generateKey(walkRoomRequest.getFromPropertyId(), WalkRoomApproveTypeEnum.SENT.getCode());
            if (propertyIdAndType2Levels.containsKey(sentKey)) {
                //获取第一个层级的索引
                WalkRoomApproveLevel firstApproveLevel = propertyIdAndType2Levels.get(sentKey)
                        .stream().min(Comparator.comparing(WalkRoomApproveLevel::getApproveIndex)).get();
                dateinOther.setSentApproveIndex(firstApproveLevel.getApproveIndex());
                //发送方设置审批层级时，发送方审批状态设置为审批中
                dateinOther.setSentApproveStatus(ApproveStatusEnum.APPROVEING.getStatus());
            } else {
                //发送方未设置审批层级时，发送方审批状态设置为审批通过
                dateinOther.setSentApproveIndex(0);
                dateinOther.setSentApproveStatus(ApproveStatusEnum.APPROVED.getStatus());
            }
            //查询接收方是否有审批层级
            String receiveKey = WalkRoomApproveLevel.generateKey(walkRoomRequest.getToPropertyId(), WalkRoomApproveTypeEnum.RECEIVE.getCode());
            if (propertyIdAndType2Levels.containsKey(receiveKey)) {
                //获取第一个层级的索引
                WalkRoomApproveLevel firstApproveLevel = propertyIdAndType2Levels.get(receiveKey)
                        .stream().min(Comparator.comparing(WalkRoomApproveLevel::getApproveIndex)).get();
                dateinOther.setReceiveApproveIndex(firstApproveLevel.getApproveIndex());
                //接收方设置审批层级时，发送方审批状态设置为审批中
                dateinOther.setReceiveApproveStatus(ApproveStatusEnum.APPROVEING.getStatus());
            } else {
                //接收方未设置审批层级时，发送方审批状态设置为审批通过
                dateinOther.setReceiveApproveIndex(0);
                dateinOther.setReceiveApproveStatus(ApproveStatusEnum.APPROVED.getStatus());
            }
            return dateinOther;
        }

        /**
         * 处理入住记录并更新finalOtherId
         *
         * @param insertBatch                     批量新增的入住记录
         * @param updateBatchFinalOtherIdRequests 需要批量更新FinalOtherId的walk room数据集合
         * @return void
         * @author lichen
         * @date 2023/7/21
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void processDateinOther(List<WalkRoomDateinOther> insertBatch, List<WalkRoomRequest> updateBatchFinalOtherIdRequests) {
            //批量新增
            saveBatch(insertBatch);

            //批量更新finalOtherId
            if (CollectionUtils.isNotEmpty(updateBatchFinalOtherIdRequests)) {
                List<Long> reqIds = updateBatchFinalOtherIdRequests.stream().map(WalkRoomRequest::getId).collect(Collectors.toList());
                //查询入住记录集合
                Map<Long, List<WalkRoomDateinOther>> reqId2DateinOther = listByReqIds(reqIds).stream().collect(Collectors.groupingBy(WalkRoomDateinOther::getReqId));
                //批量更新的walk room
                List<WalkRoomRequest> updateRequests = Lists.newArrayListWithExpectedSize(reqIds.size());

                for (WalkRoomRequest walkRoomRequest : updateBatchFinalOtherIdRequests) {
                    Long reqId = walkRoomRequest.getId();
                    List<WalkRoomDateinOther> others = reqId2DateinOther.get(reqId);
                    if (CollectionUtils.isNotEmpty(others)) {
                        //倒序排序
                        others.sort(Comparator.comparing(WalkRoomDateinOther::getDate).reversed());
                        //取最新数据的id
                        WalkRoomDateinOther other = others.get(0);
                        WalkRoomRequest updateRequest = new WalkRoomRequest();
                        updateRequest.setFinalOtherId(other.getId());
                        updateRequest.setFinalCrsNumber(other.getCrsNumber());
                        updateRequest.setId(reqId);
                        updateRequests.add(updateRequest);
                    }
                }
                //批量更新
                walkRoomRequestService.updateBatchById(updateRequests);
            }

            //初始化入住记录后发送审批通知
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                walkRoomNotifyService.dateinOtherApproveResetSendApproveNotify(insertBatch);
            });
        }

        /**
         * 清空审批信息
         *
         * @param walkRoomDateinOther             入住记录
         * @param propertyIdAndType2ApproveLevels 发送方和接收方的审批层级集合
         * @return void
         * @author lichen
         * @date 2023/7/21
         */
        @Override
        public void clearApproveInfo(WalkRoomDateinOther walkRoomDateinOther, Map<String, List<WalkRoomApproveLevel>> propertyIdAndType2ApproveLevels) {
            String sentKey = WalkRoomApproveLevel.generateKey(walkRoomDateinOther.getFromPropertyId(), WalkRoomApproveTypeEnum.SENT.getCode());
            String receiveKey = WalkRoomApproveLevel.generateKey(walkRoomDateinOther.getToPropertyId(), WalkRoomApproveTypeEnum.RECEIVE.getCode());

            if (propertyIdAndType2ApproveLevels.containsKey(sentKey)) {
                WalkRoomApproveLevel firstApproveLevel = propertyIdAndType2ApproveLevels.get(sentKey)
                        .stream().min(Comparator.comparing(WalkRoomApproveLevel::getApproveIndex)).get();
                walkRoomDateinOther.setSentApproveIndex(firstApproveLevel.getApproveIndex());
                //发送方审批层级不为空时，设置为审批中
                walkRoomDateinOther.setSentApproveStatus(ApproveStatusEnum.APPROVEING.getStatus());
            } else {
                walkRoomDateinOther.setSentApproveIndex(0);
                //发送方审批层级为空时，设置为审批通过
                walkRoomDateinOther.setSentApproveStatus(ApproveStatusEnum.APPROVED.getStatus());
            }

            if (propertyIdAndType2ApproveLevels.containsKey(receiveKey)) {
                WalkRoomApproveLevel firstApproveLevel = propertyIdAndType2ApproveLevels.get(receiveKey)
                        .stream().min(Comparator.comparing(WalkRoomApproveLevel::getApproveIndex)).get();
                walkRoomDateinOther.setReceiveApproveIndex(firstApproveLevel.getApproveIndex());
                //接受方审批层级不为空时，设置为审批中
                walkRoomDateinOther.setReceiveApproveStatus(ApproveStatusEnum.APPROVEING.getStatus());
            } else {
                //接受方审批层级为空时，设置为审批通过
                walkRoomDateinOther.setReceiveApproveIndex(0);
                walkRoomDateinOther.setReceiveApproveStatus(ApproveStatusEnum.APPROVED.getStatus());
            }
        }

        /**
         * 删除入住记录
         *
         * @param reqId walk room主键id
         * @return void
         * @author lichen
         * @date 2023/7/25
         */
        @Override
        public void removeByReqId(Long reqId) {
            remove(new LambdaQueryWrapper<WalkRoomDateinOther>().eq(WalkRoomDateinOther::getReqId, reqId));
        }

        /**
         * 根据walk room主键id和日期范围 删除日期范围以外的入住记录
         *
         * @param reqId           walk room主键id
         * @param checkInDate     入住日期(yyyyMMdd)
         * @param checkOutDate    离店日期(yyyyMMdd)
         * @param isEarlyCheckOut 是否提前离店
         * @return void
         * @author lichen
         * @date 2023/7/25
         */
        @Override
        public void removeByReqIdAndDateRange(Long reqId, Integer checkInDate, Integer checkOutDate, boolean isEarlyCheckOut) {
            LambdaQueryWrapper<WalkRoomDateinOther> removeWrapper = new LambdaQueryWrapper<WalkRoomDateinOther>()
                    .eq(WalkRoomDateinOther::getReqId, reqId);
            if (isEarlyCheckOut) {
                //提前退房时，不删除checkOutDate当日的数据
                removeWrapper.and(queryWrapper -> queryWrapper.lt(Objects.nonNull(checkInDate), WalkRoomDateinOther::getDate, checkInDate)
                        .or().gt(Objects.nonNull(checkOutDate), WalkRoomDateinOther::getDate, checkOutDate));
            } else {
                //非提前退房时，删除checkOutDate当日的数据
                removeWrapper.and(queryWrapper -> queryWrapper.lt(Objects.nonNull(checkInDate), WalkRoomDateinOther::getDate, checkInDate)
                        .or().ge(Objects.nonNull(checkOutDate), WalkRoomDateinOther::getDate, checkOutDate));
            }
            remove(removeWrapper);
        }

        /**
         * 查询入住记录
         *
         * @param reqId walk room主键id
         * @param date  入住日(yyyyMMdd)
         * @return cloud.wconnect.frontoffice.entity.walkroom.WalkRoomDateinOther
         * @author lichen
         * @date 2023/8/15
         */
        @Override
        public WalkRoomDateinOther getByReqIdAndDate(Long reqId, Integer date) {
            return getOne(new LambdaQueryWrapper<WalkRoomDateinOther>()
                    .eq(WalkRoomDateinOther::getReqId, reqId)
                    .eq(WalkRoomDateinOther::getDate, date));
        }

        /**
         * 构建审批日志对象
         *
         * @param dateinOther  入住记录
         * @param approveDTO   审批dto
         * @param approveLevel 审批层级
         * @return cloud.wconnect.frontoffice.entity.walkroom.WalkRoomApproveLog
         * @author lichen
         * @date 2023/7/19
         */
        private WalkRoomApproveLog buildApproveLog(WalkRoomDateinOther dateinOther, WalkRoomApproveDTO approveDTO, WalkRoomApproveLevel approveLevel) {
            WalkRoomApproveLog approveLog = new WalkRoomApproveLog();
            //根据审批类型设置资产id
            if (isSent(approveDTO.getApproveType())) {
                approveLog.setPropertyId(dateinOther.getFromPropertyId());
            } else {
                approveLog.setPropertyId(dateinOther.getToPropertyId());
            }
            approveLog.setDateinOtherId(dateinOther.getId());
            approveLog.setReqId(dateinOther.getReqId());
            approveLog.setDate(dateinOther.getDate());
            approveLog.setLevelId(approveLevel.getId());
            approveLog.setApproveIndex(approveLevel.getApproveIndex());
            approveLog.setApproveBy(approveDTO.getApproveBy());
            approveLog.setApproveTime(DateTimeUtils.nowTimestamp());
            approveLog.setApproveOpinion(approveDTO.getApproveOpinion());
            return approveLog;
        }

        /**
         * 推送至下一个节点
         *
         * @param dateinOther   入住记录
         * @param approveLevels 审批层级集合
         * @param approveType   审批类型1：sent 2:receive
         * @return void
         * @author lichen
         * @date 2023/7/19
         */
        private void pushNextIndex(WalkRoomDateinOther dateinOther, List<WalkRoomApproveLevel> approveLevels, Integer approveType) {
            //筛选出大于当前审批层级索引的审批层级集合
            List<WalkRoomApproveLevel> gtLevels = approveLevels.stream().filter(item -> {
                if (isSent(approveType)) {
                    return item.getApproveIndex() > dateinOther.getSentApproveIndex();
                } else {
                    return item.getApproveIndex() > dateinOther.getReceiveApproveIndex();
                }
            }).sorted(Comparator.comparing(WalkRoomApproveLevel::getApproveIndex)).collect(Collectors.toList());

            //gtLevels为空，表示没有下一个审批层级
            if (CollectionUtils.isEmpty(gtLevels)) {
                if (isSent(approveType)) {
                    dateinOther.setSentApproveStatus(ApproveStatusEnum.APPROVED.getStatus());
                } else {
                    dateinOther.setReceiveApproveStatus(ApproveStatusEnum.APPROVED.getStatus());
                }
                //更新数据为审核结束
                updateById(dateinOther);
            } else {
                //集合的第一条表示下一个审批层级
                WalkRoomApproveLevel nextApproveLevel = gtLevels.get(0);
                //下一个审批层级的审批层级索引
                Integer nextApproveIndex = nextApproveLevel.getApproveIndex();
                if (isSent(approveType)) {
                    dateinOther.setSentApproveStatus(ApproveStatusEnum.APPROVEING.getStatus());
                    dateinOther.setSentApproveIndex(nextApproveIndex);
                } else {
                    dateinOther.setReceiveApproveStatus(ApproveStatusEnum.APPROVEING.getStatus());
                    dateinOther.setReceiveApproveIndex(nextApproveIndex);
                }
                //更新数据，推送至下一个审核节点
                updateById(dateinOther);
                //发送给审批人
                TransactionUtils.afterCommitAsyncExecute(executor, () -> walkRoomNotifyService.pushNextApproveLevelSendApproveNotify(
                        nextApproveLevel, Arrays.asList(dateinOther)));
            }
        }

        /**
         * 判断是否sent类型
         *
         * @param approveType
         * @return true:是 false:不是
         */
        private boolean isSent(Integer approveType) {
            return approveType == WalkRoomApproveTypeEnum.SENT.getCode();
        }

    }