# ClcGuestInhServiceImpl.java

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

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.exception.ParseReportException;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.GuestNameUtil;
    import cloud.wconnect.frontoffice.entity.clc.ClcGuestInh;
    import cloud.wconnect.frontoffice.entity.clc.ClcGuestInhFilterConfig;
    import cloud.wconnect.frontoffice.mapper.clc.ClcGuestInhMapper;
    import cloud.wconnect.frontoffice.pojo.bo.clc.GuestInHouseBO;
    import cloud.wconnect.frontoffice.service.clc.IClcGuestInhFilterConfigService;
    import cloud.wconnect.frontoffice.service.clc.IClcGuestInhService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    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.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.Node;
    import org.dom4j.io.SAXReader;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.io.InputStream;
    import java.text.DecimalFormat;
    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.time.format.DateTimeFormatter;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 服务实现类
     * </p>
     *
     * @author yyt
     * @since 2023-07-24
     */
    @Service
    @DS("logbook")
    public class ClcGuestInhServiceImpl extends ServiceImpl<ClcGuestInhMapper, ClcGuestInh> implements IClcGuestInhService {

        @Autowired
        private IClcGuestInhFilterConfigService clcGuestInhFilterConfigService;

        /**
         * 解析 guest in house 报告
         *
         * @param in          文件流
         * @param propertyDTO 资产信息
         * @return guest in house 数据
         * @author yyt
         * @date 2023/7/31
         */
        @Override
        public List<GuestInHouseBO> parseReport(InputStream in, PropertyDTO propertyDTO) {
            try {
                Document document = new SAXReader().read(in);
                List<Node> nodes = document.selectNodes("//G_ROOM");
                if (nodes.isEmpty()) {
                    return Collections.emptyList();
                }

                Integer propertyId = propertyDTO.getId();
                // 未配置过滤配置，则不处理后续逻辑
                List<String> rateCodes = clcGuestInhFilterConfigService.list(new QueryWrapper<ClcGuestInhFilterConfig>()
                                .lambda().eq(ClcGuestInhFilterConfig::getPropertyId, propertyId))
                        .stream().map(ClcGuestInhFilterConfig::getRateCode).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(rateCodes)) {
                    return Collections.emptyList();
                }

                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MM-dd-yy");
                DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
                DecimalFormat decimalFormat = new DecimalFormat("0000");
                List<GuestInHouseBO> guestInHouses = Lists.newArrayListWithExpectedSize(nodes.size());
                for (Node node : nodes) {
                    Element element = (Element) node;
                    // 验证 Rade Code 是否正确
                    String rateCode = element.elementText("RATE_CODE");
                    if (!rateCodes.contains(rateCode)) {
                        continue;
                    }
                    GuestInHouseBO guestInHouse = new GuestInHouseBO();
                    guestInHouse.setRateCode(rateCode);
                    guestInHouse.setCheckInDate(dateTimeFormatter2.format(LocalDate.parse(element.elementText("ARRIVAL"), dateTimeFormatter)));
                    guestInHouse.setCheckOutDate(dateTimeFormatter2.format(LocalDate.parse(element.elementText("DEPARTURE"), dateTimeFormatter)));
                    guestInHouse.setCompanyName(element.elementText("COMPANY_NAME"));

                    List<String> names = GuestNameUtil.handleGuestNameForGuestInHouseReport(element.elementText("GUEST_NAME"));
                    if (CollectionUtils.isEmpty(names)) {
                        continue;
                    }

                    guestInHouse.setFirstName(names.get(0));
                    guestInHouse.setLastName(names.get(1));

                    String room = element.elementText("ROOM");
                    // 房间号不足4位的补0
                    guestInHouse.setRoomNumber(StringUtils.isEmpty(room) ? "" : decimalFormat.format(Integer.parseInt(room)));
                    guestInHouses.add(guestInHouse);
                }
                if (CollectionUtils.isEmpty(guestInHouses)) {
                    return Collections.emptyList();
                }

                // 数据去重（姓名、房间号、入住日期）
                return guestInHouses.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                        new TreeSet<>(Comparator.comparing(GuestInHouseBO::getUniKey))), ArrayList::new));
            } catch (Exception e) {
                throw new ParseReportException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(), "parse guest in house report error." + e, e);
            }
        }

        /**
         * 处理插入 guest in house 数据
         *
         * @param propertyDTO   资产
         * @param operator      操作人
         * @param guestInHouses guest in house 数据
         * @author yyt
         * @date 2023/7/31
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleGuestInHouseData(PropertyDTO propertyDTO, int operator, List<GuestInHouseBO> guestInHouses) {
            // 先清空数据
            remove(new QueryWrapper<ClcGuestInh>().lambda().eq(ClcGuestInh::getPropertyId, propertyDTO.getId()));

            // 再保存
            if (CollectionUtils.isNotEmpty(guestInHouses)) {
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ENGLISH);
                Integer propertyId = propertyDTO.getId();
                ZoneId zoneId = ZoneId.of(propertyDTO.getTimezone());
                long now = DateTimeUtils.nowTimestamp();
                saveBatch(guestInHouses.stream().map(item -> {
                    ClcGuestInh guestInh = new ClcGuestInh();
                    guestInh.setPropertyId(propertyId);
                    guestInh.setRateCode(item.getRateCode());
                    guestInh.setCheckInDate(LocalDate.parse(item.getCheckInDate(), dateTimeFormatter).atStartOfDay(zoneId).toEpochSecond());
                    guestInh.setCheckOutDate(LocalDate.parse(item.getCheckOutDate(), dateTimeFormatter).atStartOfDay(zoneId).toEpochSecond());
                    guestInh.setRoomNumber(item.getRoomNumber());
                    guestInh.setFirstName(item.getFirstName());
                    guestInh.setLastName(item.getLastName());
                    guestInh.setCompanyName(item.getCompanyName());
                    guestInh.setCreatedBy(operator);
                    guestInh.setCreatedTime(now);
                    return guestInh;
                }).collect(Collectors.toList()));
            }
        }

    }