# ArrivalTask.java

    package cloud.wconnect.frontoffice.businesstask.clc;

    import cloud.wconnect.commonlib.businesstask.CheckReportMissedService;
    import cloud.wconnect.commonlib.businesstask.rule.MissedRuleByThreshold;
    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.FunctionCons;
    import cloud.wconnect.commonlib.cons.PmsReportEnum;
    import cloud.wconnect.commonlib.pojo.dto.*;
    import cloud.wconnect.commonlib.pojo.dto.line.PublicFlexMsgParm;
    import cloud.wconnect.commonlib.pojo.dto.line.ReportMissedMsgParm;
    import cloud.wconnect.commonlib.pojo.dto.scheduler.JobParameterDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.service.MailInternalService;
    import cloud.wconnect.commonlib.service.NotifyInternalService;
    import cloud.wconnect.commonlib.service.PropertyInternalService;
    import cloud.wconnect.commonlib.util.*;
    import cloud.wconnect.frontoffice.cons.BusinessEnum;
    import cloud.wconnect.frontoffice.cons.EventSubjectEnum;
    import cloud.wconnect.frontoffice.cons.FunctionPointEnum;
    import cloud.wconnect.frontoffice.cons.clc.CLCLineNotifyTitleEnum;
    import cloud.wconnect.frontoffice.cons.clc.CLCNotifyEnum;
    import cloud.wconnect.frontoffice.entity.clc.ClcArrivalDetailed;
    import cloud.wconnect.frontoffice.entity.clc.ClcEmailConfig;
    import cloud.wconnect.frontoffice.entity.clc.ClcEmailTarget;
    import cloud.wconnect.frontoffice.entity.clc.ClcSchedulerJob;
    import cloud.wconnect.frontoffice.pojo.dto.clc.ClcSchedulerDTO;
    import cloud.wconnect.frontoffice.pojo.vo.clc.MissingClcVo;
    import cloud.wconnect.frontoffice.service.clc.IClcArrivalDetailedService;
    import cloud.wconnect.frontoffice.service.clc.IClcEmailConfigService;
    import cloud.wconnect.frontoffice.service.clc.IClcEmailTargetService;
    import cloud.wconnect.frontoffice.service.clc.IClcSchedulerService;
    import com.alibaba.fastjson.JSONObject;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    import org.thymeleaf.TemplateEngine;
    import org.thymeleaf.context.Context;

    import java.time.ZonedDateTime;
    import java.util.Collections;
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Objects;
    import java.util.concurrent.TimeUnit;
    import java.util.stream.Collectors;

    /**
     * Task: arrival
     *
     * @author yyt
     * 2023/7/26
     */
    @Slf4j
    @Component
    public class ArrivalTask {

        @Autowired
        private IClcEmailConfigService clcEmailConfigService;
        @Autowired
        private IClcEmailTargetService clcEmailTargetService;
        @Autowired
        private PropertyInternalService propertyInternalService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private IClcArrivalDetailedService clcArrivalDetailedService;
        @Value("${default.timezone}")
        private String defaultTimezone;
        @Value("${here.link.clc-missing-list}")
        private String hereLink;
        @Autowired
        private TemplateEngine templateEngine;
        @Autowired
        private MailInternalService mailInternalService;
        @Autowired
        private CheckReportMissedService checkReportMissedService;
        @Autowired
        private LineMessageUtil lineMessageUtil;
        @Autowired
        private NotifyInternalService notifyInternalService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private IClcSchedulerService clcSchedulerService;
        @Autowired
        private ObjectMapper objectMapper;

        private static final String SUBJECT = EventSubjectEnum.ARRIVAL_YESTERDAY.getSubject();

        private static final String BUSINESS = BusinessEnum.CLC_ARRIVAL.getBusiness();

        /**
         * 通知 未匹配 clc 的入住信息
         *
         * @param propertyId 资产 id
         * @author yyt
         * @date 2023/7/26
         */
        @Async
        public void notifyMissingClc(Integer propertyId) {
            log.info("notifyMissingClc... start");
            String alertTitle = "[Warning] - Exceptions About Notify Missing CLC Arrival";
            StringBuilder warningEmail = new StringBuilder("<p>There is something wrong when notify missing clc arrival.</p>");

            try {
                // 查询邮件配置
                ClcEmailConfig config = clcEmailConfigService.getOne(new QueryWrapper<ClcEmailConfig>()
                        .lambda().eq(ClcEmailConfig::getType, ClcEmailConfig.TYPE_NOTIFY_MISSING_CLC)
                        .eq(ClcEmailConfig::getPropertyId, propertyId)
                        .eq(ClcEmailConfig::getStatus, true));
                if (Objects.isNull(config)) {
                    log.warn("notifyMissingClc... no enabled mail config was found");
                    log.info("notifyMissingClc... end");
                    return;
                }

                // 过滤有效酒店
                PropertyDTO propertyDTO = propertyInternalService.getOne(propertyId);
                if (!propertyDTO.getStatus()) {
                    log.warn("notifyMissingClc... the property is closed, no processing required");
                    log.info("notifyMissingClc... end");
                    return;
                }

                List<ClcEmailTarget> emailTargets = clcEmailTargetService.list(new QueryWrapper<ClcEmailTarget>().lambda()
                                        .eq(ClcEmailTarget::getConfigId, config.getId()));
                if (emailTargets.isEmpty()) {
                    log.warn("notifyMissingClc... email target is not set");
                    log.info("notifyMissingClc... end");
                    return;
                }

                // 查询未匹配 clc 入住信息，限制入住日期为近 7 天
                ZonedDateTime currentDateZoned = DateTimeUtils.dateNow(defaultTimezone);
                long fromDate = DateTimeUtils.toTimestamp(currentDateZoned.minusDays(7L));
                long toDate = DateTimeUtils.toTimestamp(currentDateZoned.minusDays(1L));
                List<ClcArrivalDetailed> missArrivals = clcArrivalDetailedService.list(propertyId, fromDate, toDate, ClcArrivalDetailed.MATCH_RESULT_MISSED);
                if (missArrivals.isEmpty()) {
                    log.warn("notifyMissingClc... not found missing records");
                    log.info("notifyMissingClc... end");
                    return;
                }

                String timezone = propertyDTO.getTimezone();
                String language = propertyDTO.getLanguage();
                List<MissingClcVo> missingClcVos = missArrivals.stream().map(item -> {
                    MissingClcVo missingClcVo = new MissingClcVo();
                    missingClcVo.setRoomNumber(item.getRoomNumber());
                    missingClcVo.setArrivalDateStr(InternationalizationUtils.formatDateTime(item.getArrivDate(),
                            propertyDTO.getInternationalization().getDateTime().getLongDate(),
                            StringUtils.EMPTY, timezone, language));
                    return missingClcVo;
                }).collect(Collectors.toList());

                //发送通知
                doNotifyMissingClc(propertyDTO, config, emailTargets, missingClcVos, fromDate, toDate);

                log.info("notifyMissingClc... send email,property:{}", propertyDTO.getAbbr());
            } catch (Exception e) {
                log.warn("notifyMissingClc...  exception occurs:", e);
                warningEmail.append("<p>Exception:").append(e).append("</p>");
                mailInternalService.sendAlertEmail(alertTitle, warningEmail.toString());
            }
            log.info("notifyMissingClc... end");
        }

        /**
         * 检查报告是否缺失
         *
         * @param schedulerJob 定时任务
         * @author yyt
         * @date 2023/7/27
         */
        @Async
        public void reportMissCheck(ClcSchedulerJob schedulerJob) {
            log.info("ArrivalTask.reportMissCheck start...");

            Integer propertyId = schedulerJob.getPropertyId();
            PropertyDTO property = propertyI18nRedisService.get(propertyId);
            if (!property.getStatus()) {
                log.warn("ArrivalTask.reportMissCheck end... the property is closed, no processing required");
                return;
            }

            // 查询邮件配置
            ClcEmailConfig emailConfig = clcEmailConfigService.getOne(new QueryWrapper<ClcEmailConfig>().lambda()
                    .eq(ClcEmailConfig::getType, ClcSchedulerDTO.MISSING_ARRIVAL_REPORT)
                    .eq(ClcEmailConfig::getPropertyId, propertyId)
                    .eq(ClcEmailConfig::getStatus, true));
            List<ClcEmailTarget> emailTargets = Collections.emptyList();
            if (Objects.nonNull(emailConfig)) {
                emailTargets = clcEmailTargetService.list(new QueryWrapper<ClcEmailTarget>()
                        .lambda().eq(ClcEmailTarget::getConfigId, emailConfig.getId())
                        .eq(ClcEmailTarget::getPropertyId, propertyId));
            }

            //获取通知配置DTO
            NotifyDTO notifyDTO = getNotifyDTO(property, emailConfig, emailTargets);

            // 资产配置的阈值
            JobParameterDTO jobParameterDTO = JsonUtils.convertStringToObject(objectMapper, schedulerJob.getParameters(), JobParameterDTO.class);
            ThresholdDTO thresholdDTO = new ThresholdDTO(jobParameterDTO.getThreshold(), TimeUnit.MINUTES, defaultTimezone);

            CheckReportMissedDTO check = CheckReportMissedDTO.builder()
                    .subject(SUBJECT)
                    .business(BUSINESS)
                    .propertyDTO(property)
                    .ruleClazzList(Collections.singletonList(MissedRuleByThreshold.class))
                    .notifyDTO(notifyDTO)
                    .thresholdDTO(thresholdDTO)
                    .build();
            checkReportMissedService.doCheck(check);

            log.info("ArrivalTask.reportMissCheck end...");
        }

        /**
         * 获取通知配置DTO
         *
         * @param propertyDTO 资产集合
         * @param emailConfig 邮件配置
         * @param targets     邮件通知人集合
         * @return 通知配置
         * @author lichen
         * @date 2023/12/14
         */
        private NotifyDTO getNotifyDTO(PropertyDTO propertyDTO, ClcEmailConfig emailConfig, List<ClcEmailTarget> targets) {
            if (Objects.isNull(emailConfig)) {
                return null;
            }

            Integer propertyId = propertyDTO.getId();
            String propertyName = propertyDTO.getName();
            String language = propertyDTO.getLanguage();
            String defaultSubject = clcEmailConfigService.getDefaultSubject(propertyId, CLCNotifyEnum.MISSING_REPORT_ARRIVAL.getCode());

            //获取邮件配置的通知人
            NotifyDTO notifyDTO = new NotifyDTO();
            notifyDTO.setPropertyId(propertyId);
            notifyDTO.setFunctionPoint(FunctionPointEnum.CLC_PMS_REPORT_MISSED.getFunctionPoint());
            notifyDTO.setSubject(StringUtils.isNotBlank(emailConfig.getSubject()) ? emailConfig.getSubject() : defaultSubject);
            notifyDTO.setIdentityList(beanMapper.mapList(targets, NotifyIdentityDTO.class));

            Long date = clcSchedulerService.getDateRange(propertyDTO, ClcSchedulerDTO.MISSING_ARRIVAL_REPORT);
            String missDate = InternationalizationUtils.formatDateTime(date,
                    propertyDTO.getInternationalization().getDateTime().getLongDate(),
                    StringUtils.EMPTY, propertyDTO.getTimezone(), language);

            String functionName = i18nMessageUtils.message(FunctionCons.CLC_LIST, language);

            // 获取资产配置的 PMS 报告名称
            String reportName = propertyInternalService.getPmsReportConfig(propertyId,
                    propertyDTO.getPmsType(), PmsReportEnum.ARRIVAL_DETAILED.getSymbol()).getReportName();

            Context context = new Context();
            context.setVariable("propertyName", propertyName);
            context.setVariable("hereLink", hereLink + "?property=" + propertyDTO.getAbbr() + "&tenant=" + propertyDTO.getTenantSymbol());
            context.setVariable("functionName", functionName);
            context.setVariable("reportName", reportName);
            context.setVariable("reportDate", missDate);
            String emailContent = templateEngine.process(EmailUtil.getMissReportTemplateName(language), context);
            notifyDTO.setEmailContent(EmailUtil.getFullEmailContent(defaultSubject, emailContent, language));

            //line通知内容
            ReportMissedMsgParm reportMissedMsgParm = new ReportMissedMsgParm(propertyName, propertyId, functionName, reportName, missDate);
            JSONObject lineMsg = lineMessageUtil.generateReportMissedMsg(reportMissedMsgParm);
            notifyDTO.setLineContent(Lists.newArrayList(lineMsg));

            return notifyDTO;
        }

        /**
         * 发送通知
         *
         * @param propertyDTO    资产
         * @param clcEmailConfig 邮件配置
         * @param emailTargets   邮件通知人
         * @param missingClcVos  未匹配的CLC集合
         * @param fromDate       开始日期
         * @param toDate         结束日期
         * @author lichen
         * @date 2023/12/14
         */
        private void doNotifyMissingClc(PropertyDTO propertyDTO, ClcEmailConfig clcEmailConfig, List<ClcEmailTarget> emailTargets,
                                        List<MissingClcVo> missingClcVos, long fromDate, long toDate) {
            String finalLink = hereLink + "?property=" + propertyDTO.getAbbr() + "&tenant=" + propertyDTO.getTenantSymbol()
                    + "&fromDate=" + fromDate + "&toDate=" + toDate + "&matchResults=1";

            Integer propertyId = propertyDTO.getId();
            String propertyName = propertyDTO.getName();
            String language = propertyDTO.getLanguage();

            String defaultSubject = clcEmailConfigService.getDefaultSubject(propertyId, CLCNotifyEnum.MISSING_CLC.getCode());

            //邮件内容
            Context context = new Context();
            context.setVariable("hereLink", finalLink);
            context.setVariable("propertyName", propertyName);
            context.setVariable("functionName", i18nMessageUtils.message(FunctionCons.CLC_LIST, language));
            context.setVariable("missingClcVos", missingClcVos.stream().limit(EmailUtil.LIMIT_COUNT).collect(Collectors.toList()));
            context.setVariable("exceedLimit", missingClcVos.size() > EmailUtil.LIMIT_COUNT);
            String mailContent = templateEngine.process("clc/missing-clc_" + language, context);
            mailContent = EmailUtil.getFullEmailContent(defaultSubject, mailContent, language);

            //line通知内容
            LinkedHashMap<String, String> businessMap = new LinkedHashMap<>();
            businessMap.put("Total", missingClcVos.size() + " pieces");
            //获取前5条数据的房间号，显示在line通知的消息中
            String roomNumbers = StringUtils.join(missingClcVos.stream()
                    .map(MissingClcVo::getRoomNumber).limit(5).toList(), ",");
            String suffix = missingClcVos.size() > 5 ? "..." : "";
            businessMap.put("Room Number", roomNumbers + suffix);

            PublicFlexMsgParm publicFlexMsgParm = PublicFlexMsgParm.builder()
                    .propertyId(propertyId)
                    .propertyName(propertyName)
                    .functionName(i18nMessageUtils.message(FunctionCons.CLC_LIST, language))
                    .title(CLCLineNotifyTitleEnum.MISSING_CLC.getI18nCode())
                    .link(finalLink)
                    .businessMap(businessMap)
                    .build();
            JSONObject lineContent = lineMessageUtil.generatePublicFlexMsg(publicFlexMsgParm);

            NotifyDTO notifyDTO = new NotifyDTO();
            notifyDTO.setPropertyId(propertyId);
            notifyDTO.setFunctionPoint(FunctionPointEnum.CLC_MISSING.getFunctionPoint());
            notifyDTO.setSubject(StringUtils.isNotBlank(clcEmailConfig.getSubject()) ? clcEmailConfig.getSubject() : defaultSubject);
            notifyDTO.setIdentityList(beanMapper.mapList(emailTargets, NotifyIdentityDTO.class));
            notifyDTO.setEmailContent(mailContent);
            notifyDTO.setLineContent(Lists.newArrayList(lineContent));

            //通知
            notifyInternalService.sendNotify(notifyDTO);
        }
    }