# WalkRoomRateServiceImpl.java

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

    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.TransactionUtils;
    import cloud.wconnect.frontoffice.entity.walkroomrequest.*;
    import cloud.wconnect.frontoffice.mapper.walkroomrequest.WalkRoomRateMapper;
    import cloud.wconnect.frontoffice.pojo.dto.walkroomrequest.WalkRateDTO;
    import cloud.wconnect.frontoffice.service.walkroomrequest.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Lists;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

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

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

    /**
     * <p>
     * walk room 某天固定 rate值 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2023-07-08
     */
    @Service
    @DS("logbook")
    public class WalkRoomRateServiceImpl extends ServiceImpl<WalkRoomRateMapper, WalkRoomRate> implements IWalkRoomRateService {

        @Autowired
        @Lazy
        private IWalkRoomRequestService walkRoomRequestService;
        @Autowired
        @Lazy
        private IWalkRoomDateinRateService walkRoomDateinRateService;
        @Autowired
        @Lazy
        private IWalkRoomDateinOtherService walkRoomDateinOtherService;
        @Autowired
        @Lazy
        private IWalkRoomApproveLevelService approveLevelService;
        @Autowired
        @Lazy
        private IWalkRoomApproveLogService approveLogService;
        @Autowired
        @Lazy
        private IWalkRoomNotifyService walkRoomNotifyService;
        @Autowired
        private Executor executor;

        /**
         * 查询资产目标日期设置的rate值
         *
         * @param propertyId 资产id
         * @param date       目标日期(yyyyMMdd)
         * @return cloud.wconnect.frontoffice.entity.walkroom.WalkRoomRate
         * @author lichen
         * @date 2023/7/13
         */
        @Override
        public WalkRoomRate getByPropertyIdAndDate(Integer propertyId, Integer date) {
            return getOne(new LambdaQueryWrapper<WalkRoomRate>()
                    .eq(WalkRoomRate::getPropertyId, propertyId).eq(WalkRoomRate::getTheDate, date));
        }

        /**
         * 查询资产在多个日期设置的rate值
         *
         * @param propertyId 资产id
         * @param dates      目标日期集合(yyyyMMdd)
         * @return java.util.List<cloud.wconnect.frontoffice.entity.walkroom.WalkRoomRate>
         * @author lichen
         * @date 2023/7/14
         */
        @Override
        public List<WalkRoomRate> listByPropertyIdAndDates(Integer propertyId, List<Integer> dates) {
            if (CollectionUtils.isEmpty(dates)) return Collections.emptyList();

            return list(new LambdaQueryWrapper<WalkRoomRate>()
                    .eq(WalkRoomRate::getPropertyId, propertyId).in(WalkRoomRate::getTheDate, dates));
        }

        /**
         * 批量设置接收方某日的walk rate
         *
         * @param walkRateDTO walk rate dto
         * @param loginUserId 登录用户id
         * @return void
         * @author lichen
         * @date 2023/7/19
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void batchSetReceiverWalkRate(WalkRateDTO walkRateDTO, int loginUserId) {
            /**
             * 1. 设置资产目标日期的walk rate
             */
            setPropertyRate(walkRateDTO, loginUserId);
            /**
             * 2. 批量更新资产作为接收方的walk room的 目标日期的 入住记录 的walk rate值
             * 注意：如果目标日期已经有单独设置的walk rate则不需要更新
             */
            List<WalkRoomRequest> resetApproves = batchUpdateDateinWalkRate(walkRateDTO, loginUserId);
            /**
             * 3.批量更新过目标日期的入住记录的walk rate数据，需要重置审批信息
             */
            List<WalkRoomDateinOther> needApproveNotifyDateinOthers = resetDateinOtherApproveInfo(resetApproves, walkRateDTO.getDate());
            /**
             * 4.审批信息重置后发送审批通知
             */
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                walkRoomNotifyService.dateinOtherApproveResetSendApproveNotify(needApproveNotifyDateinOthers);
            });
        }

        /**
         * 重置目标日期的入住记录审批信息
         *
         * @param resetApproves 需要重置审批信息的walk room数据集合
         * @param date          目标日期(yyyyMMdd)
         * @return 需要发送审批通知的入住记录集合
         * @author lichen
         * @date 2023/7/21
         */
        private List<WalkRoomDateinOther> resetDateinOtherApproveInfo(List<WalkRoomRequest> resetApproves, Integer date) {
            if (CollectionUtils.isEmpty(resetApproves)) return Collections.emptyList();

            Map<Long, WalkRoomRequest> reqId2WalkRoomRequest = resetApproves.stream().collect(Collectors.toMap(WalkRoomRequest::getId, Function.identity()));
            List<Long> resetApproveReqIds = resetApproves.stream().map(WalkRoomRequest::getId).collect(Collectors.toList());

            //收集资产id
            Set<Integer> propertyIds = resetApproves.stream().flatMap(item -> Stream.of(item.getFromPropertyId(), item.getToPropertyId())).collect(Collectors.toSet());
            List<WalkRoomApproveLevel> approveLevelList = approveLevelService.listByPropertyIds(propertyIds);
            Map<String, List<WalkRoomApproveLevel>> propertyIdAndType2ApproveLevels = approveLevelList.stream()
                    .collect(groupingBy(item -> WalkRoomApproveLevel.generateKey(item.getPropertyId(), item.getType())));

            //查询入住记录
            List<WalkRoomDateinOther> walkRoomDateinOthers = walkRoomDateinOtherService.listByReqIdsAndDates(resetApproveReqIds, Collections.singletonList(date));
            Map<Long, WalkRoomDateinOther> reqId2DateinOther = walkRoomDateinOthers.stream().collect(Collectors.toMap(WalkRoomDateinOther::getReqId, Function.identity()));

            List<WalkRoomDateinOther> batchInsertDateinOthers = Lists.newArrayListWithExpectedSize(resetApproves.size());
            List<WalkRoomDateinOther> batchUpdateDateinOthers = Lists.newArrayListWithExpectedSize(resetApproves.size());

            for (WalkRoomRequest walkRoomRequest : resetApproves) {
                Long id = walkRoomRequest.getId();
                //获取入住记录
                WalkRoomDateinOther walkRoomDateinOther = reqId2DateinOther.get(id);
                if (Objects.isNull(walkRoomDateinOther)) {
                    //构建入住记录
                    walkRoomDateinOther = walkRoomDateinOtherService.buildDateinOther(walkRoomRequest, date, propertyIdAndType2ApproveLevels);
                    batchInsertDateinOthers.add(walkRoomDateinOther);
                } else {
                    //清空审批信息
                    walkRoomDateinOtherService.clearApproveInfo(walkRoomDateinOther, propertyIdAndType2ApproveLevels);
                    batchUpdateDateinOthers.add(walkRoomDateinOther);
                }
            }

            //批量新增入住记录，批量新增入住记录后需要更新对应的walk room数据的finalOtherId、finalCrsNumber
            if (CollectionUtils.isNotEmpty(batchInsertDateinOthers)) {
                List<Long> batchInsertReqIds = batchInsertDateinOthers.stream().map(WalkRoomDateinOther::getReqId).collect(Collectors.toList());
                //批量新增
                walkRoomDateinOtherService.saveBatch(batchInsertDateinOthers);

                //根据walk room主键id查询所有的入住记录集合
                Map<Long, List<WalkRoomDateinOther>> othersByReqId = walkRoomDateinOtherService.listByReqIds(batchInsertReqIds)
                        .stream().collect(groupingBy(WalkRoomDateinOther::getReqId));

                if (!othersByReqId.isEmpty()) {
                    //需要更新finalOtherId、finalCrsNumber的walk room数据集合
                    List<WalkRoomRequest> updateRequestsForFinalOtherId = Lists.newArrayListWithExpectedSize(resetApproves.size());

                    othersByReqId.forEach((reqId, others) -> {
                        WalkRoomRequest walkRoomRequest = reqId2WalkRoomRequest.get(reqId);
                        if (walkRoomRequest.getInvalidDate() != 0) {
                            //已退房的不处理
                            return;
                        }
                        //倒序
                        others.sort(Comparator.comparing(WalkRoomDateinOther::getDate).reversed());
                        //获取date最大的一条数据
                        WalkRoomDateinOther maxDateOther = others.get(0);
                        //构建walk room数据对象
                        WalkRoomRequest updateFinalOtherEntity = new WalkRoomRequest();
                        updateFinalOtherEntity.setFinalOtherId(maxDateOther.getId());
                        updateFinalOtherEntity.setFinalCrsNumber(maxDateOther.getCrsNumber());
                        updateFinalOtherEntity.setId(walkRoomRequest.getId());
                        updateRequestsForFinalOtherId.add(updateFinalOtherEntity);
                    });

                    //批量更新walk room的finalOtherId、finalCrsNumber
                    if (CollectionUtils.isNotEmpty(updateRequestsForFinalOtherId)) {
                        walkRoomRequestService.updateBatchById(updateRequestsForFinalOtherId);
                    }
                }
            }

            //批量更新入住记录
            if (CollectionUtils.isNotEmpty(batchUpdateDateinOthers)) {
                walkRoomDateinOtherService.updateBatchById(batchUpdateDateinOthers);
                //修改的入住记录，需要删除审批日志
                List<Long> otherIds = batchUpdateDateinOthers.stream().map(WalkRoomDateinOther::getId).collect(Collectors.toList());
                List<Long> reqIds = batchUpdateDateinOthers.stream().map(WalkRoomDateinOther::getReqId).collect(Collectors.toList());
                approveLogService.remove(new LambdaQueryWrapper<WalkRoomApproveLog>()
                        .in(WalkRoomApproveLog::getDateinOtherId, otherIds)
                        .in(WalkRoomApproveLog::getReqId, reqIds));
            }

            //返回需要发送审批通知的入住记录
            List<WalkRoomDateinOther> needApproveNotifyDateinOthers = new ArrayList<>();
            needApproveNotifyDateinOthers.addAll(batchInsertDateinOthers);
            needApproveNotifyDateinOthers.addAll(batchUpdateDateinOthers);
            return needApproveNotifyDateinOthers;
        }

        /**
         * 批量更新资产作为接收方的walk room的 目标日期的 入住记录 的walk rate值
         *
         * @param walkRateDTO walk rate dto
         * @param loginUserId 登录用户id
         * @return java.util.List<cloud.wconnect.frontoffice.entity.walkroom.WalkRoomRequest> 需要重置审批信息的walk room数据
         * @author lichen
         * @date 2023/7/21
         */
        private List<WalkRoomRequest> batchUpdateDateinWalkRate(WalkRateDTO walkRateDTO, int loginUserId) {
            Integer date = walkRateDTO.getDate();
            BigDecimal rate = walkRateDTO.getRate();

            //查询 当前资产是接收方 且 check in <= 入住日 check out 大于 入住日 的数据
            List<WalkRoomRequest> walkRoomRequests = walkRoomRequestService.list(new LambdaQueryWrapper<WalkRoomRequest>()
                    .eq(WalkRoomRequest::getToPropertyId, walkRateDTO.getPropertyId())
                    .le(WalkRoomRequest::getCheckInDate, date)
                    .gt(WalkRoomRequest::getCheckOutDate, date));
            if (CollectionUtils.isEmpty(walkRoomRequests)) return Collections.emptyList();
            List<Long> reqIds = walkRoomRequests.stream().map(WalkRoomRequest::getId).collect(Collectors.toList());

            //查询目标日期的walk rate
            List<WalkRoomDateinRate> dateinRateList = walkRoomDateinRateService.listByReqIdAndDate(reqIds, Collections.singletonList(date));
            Map<Long, WalkRoomDateinRate> reqId2DateinRate = dateinRateList.stream().collect(Collectors.toMap(WalkRoomDateinRate::getReqId, Function.identity()));

            //批量新增的walk rate的集合
            List<WalkRoomDateinRate> batchInsertDateinRates = Lists.newArrayListWithExpectedSize(walkRoomRequests.size());
            //批量设置rate的数据id
            List<Long> updateRateIds = Lists.newArrayListWithExpectedSize(walkRoomRequests.size());
            //需要重置审批信息的walk room数据集合
            List<WalkRoomRequest> resetApproves = Lists.newArrayListWithExpectedSize(walkRoomRequests.size());

            for (WalkRoomRequest walkRoomRequest : walkRoomRequests) {
                Long id = walkRoomRequest.getId();
                WalkRoomDateinRate dateinRate = reqId2DateinRate.get(id);
                if (dateinRate == null) {
                    dateinRate = new WalkRoomDateinRate();
                    dateinRate.setReqId(id);
                    dateinRate.setFromPropertyId(walkRoomRequest.getFromPropertyId());
                    dateinRate.setToPropertyId(walkRoomRequest.getToPropertyId());
                    dateinRate.setDate(date);
                    dateinRate.setRate(rate);
                    dateinRate.setUpdateBy(loginUserId);
                    dateinRate.setUpdateTime(DateTimeUtils.nowTimestamp());

                    batchInsertDateinRates.add(dateinRate);
                    resetApproves.add(walkRoomRequest);
                } else {
                    //没有单独设置过walk rate的才需要批量设置
                    if (Objects.isNull(dateinRate.getRateByHand())) {
                        updateRateIds.add(dateinRate.getId());
                        resetApproves.add(walkRoomRequest);
                    }
                }
            }

            //批量新增
            if (CollectionUtils.isNotEmpty(batchInsertDateinRates)) {
                walkRoomDateinRateService.saveBatch(batchInsertDateinRates);
            }

            //批量修改
            if (CollectionUtils.isNotEmpty(updateRateIds)) {
                List<WalkRoomDateinRate> batchUpdateDateinRates = updateRateIds.stream().map(id -> {
                    WalkRoomDateinRate updateWalkRoomDateinRate = new WalkRoomDateinRate();
                    updateWalkRoomDateinRate.setId(id);
                    updateWalkRoomDateinRate.setRate(rate);
                    updateWalkRoomDateinRate.setUpdateBy(loginUserId);
                    updateWalkRoomDateinRate.setUpdateTime(DateTimeUtils.nowTimestamp());
                    return updateWalkRoomDateinRate;
                }).collect(Collectors.toList());
                walkRoomDateinRateService.updateBatchById(batchUpdateDateinRates);
            }
            return resetApproves;
        }

        /**
         * 设置资产目标日期的walk rate
         *
         * @param walkRateDTO walk rate dto
         * @param loginUserId 登录用户id
         * @return void
         * @author lichen
         * @date 2023/7/21
         */
        private void setPropertyRate(WalkRateDTO walkRateDTO, int loginUserId) {
            //根据资产id 和 目标日期 查询walk rate
            WalkRoomRate oriWalkRoomRate = getByPropertyIdAndDate(walkRateDTO.getPropertyId(), walkRateDTO.getDate());
            if (Objects.nonNull(oriWalkRoomRate)) {
                oriWalkRoomRate.setRoomRate(walkRateDTO.getRate());
                oriWalkRoomRate.setUpdatedBy(loginUserId);
                oriWalkRoomRate.setUpdatedTime(DateTimeUtils.nowTimestamp());
                //修改
                updateById(oriWalkRoomRate);
            } else {
                WalkRoomRate walkRoomRate = new WalkRoomRate();
                walkRoomRate.setPropertyId(walkRateDTO.getPropertyId());
                walkRoomRate.setTheDate(walkRateDTO.getDate());
                walkRoomRate.setRoomRate(walkRateDTO.getRate());
                walkRoomRate.setCreatedBy(loginUserId);
                walkRoomRate.setCreatedTime(DateTimeUtils.nowTimestamp());
                //新增
                save(walkRoomRate);
            }
        }
    }