# AlertMessageServiceImpl.java

    package cloud.wconnect.rateshop.service.rateshop.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.rateshop.cons.rateshop.AlertRuleEnum;
    import cloud.wconnect.rateshop.cons.rateshop.MarketDemandTrendEnum;
    import cloud.wconnect.rateshop.entity.rateshop.*;
    import cloud.wconnect.rateshop.mapper.rateshop.AlertMessageMapper;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.PriceCacheKeyDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.RateConfigDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.alertmessage.alertrule.AlertRuleDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.alertmessage.alertrule.RateRuleDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.alertmessage.AlertMsgContentDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.sender.*;
    import cloud.wconnect.rateshop.pojo.query.rateshop.AlertMsgQuery;
    import cloud.wconnect.rateshop.pojo.query.rateshop.DailyAlertMsgQuery;
    import cloud.wconnect.rateshop.pojo.vo.rateshop.AlertMsgVO;
    import cloud.wconnect.rateshop.pojo.vo.rateshop.DailyAlertMsgVO;
    import cloud.wconnect.rateshop.service.AmazonSqsService;
    import cloud.wconnect.rateshop.service.rateshop.IAlertMessageService;
    import cloud.wconnect.rateshop.service.rateshop.IHotelService;
    import cloud.wconnect.rateshop.service.rateshop.ISettingService;
    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.fasterxml.jackson.databind.ObjectMapper;
    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.beans.factory.annotation.Value;
    import org.springframework.data.redis.core.RedisCallback;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.time.LocalDate;
    import java.time.temporal.ChronoUnit;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 警报 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2023-06-06
     */
    @DS("rate_shop")
    @Service
    public class AlertMessageServiceImpl extends ServiceImpl<AlertMessageMapper, AlertMessage> implements IAlertMessageService {

        @Autowired
        private RedisTemplate<String, String> redisTemplate;
        @Autowired
        private AmazonSqsService amazonSqsService;
        @Autowired
        private ObjectMapper objectMapper;
        @Value("${sqs.queue-url.alert_event_queue}")
        private String alertEventUrl;
        @Autowired
        private ISettingService settingService;
        @Autowired
        private IHotelService hotelService;
        @Autowired
        private BeanMapper beanMapper;

        /**
         * 发送价格变化的警报消息
         *
         * @param msgMinPrices 最低房价集合
         * @return void
         * @author lichen
         * @date 2025/4/4
         */
        @Override
        public void sendPriceChangeAlert(List<MsgMinPrice> msgMinPrices) {
            //获取当前时间
            LocalDate nowLocalDate = LocalDate.now();
            //过滤出未来14天以内的数据
            List<MsgMinPrice> msgMinPrices14Days = msgMinPrices.stream().filter(msgMinPrice -> {
                Long checkInDate = msgMinPrice.getCheckInDate();

                //把checkInDate转化为localDate
                LocalDate checkInDateLocalDate = DateTimeUtils.strToLocalDate(checkInDate.toString(), BaseConstant.DATE_FORMAT_YYYYMMDD);

                // 比较两个日期之间的天数差
                long diffDays = ChronoUnit.DAYS.between(checkInDateLocalDate, nowLocalDate);
                //判断checkInDate是否在未来14天之内
                return Math.abs(diffDays) <= 14;
            }).toList();

            if (CollectionUtils.isEmpty(msgMinPrices14Days)) {
                return;
            }

            Map<String, MsgMinPrice> msgKey2MinPrice = msgMinPrices14Days.stream().collect(
                    Collectors.toMap(item -> beanMapper.map(item, PriceCacheKeyDTO.class).generateLowestRateCacheKey(), Function.identity()));

            //价格对应的缓存key集合
            ArrayList<String> redisKeyList = Lists.newArrayList(msgKey2MinPrice.keySet());

            //缓存中的value集合
            List<String> cacheValueList = redisTemplate.opsForValue().multiGet(redisKeyList);
            //根据比对规则，生成alertMessage
            for (int i = 0; i < redisKeyList.size(); i++) {
                String key = redisKeyList.get(i);
                //缓存中的最低价格
                String cacheMinPriceStr = cacheValueList.get(i);
                //消息中的最低价格对象
                MsgMinPrice msgMinPriceObj = msgKey2MinPrice.get(key);
                if (StringUtils.isEmpty(cacheMinPriceStr) || Objects.isNull(msgMinPriceObj.getMinPrice())) continue;
                //生成alertMessage，并发送消息
                doGeneratePriceMessage(cacheMinPriceStr, msgMinPriceObj);
            }
        }

        /**
         * 处理iwc级别变化的消息
         *
         * @param iwcChangeMessage iwc级别变化的消息对象
         * @return void
         * @author lichen
         * @date 2025/4/5
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void processIwcLevelChangeMessage(IwcChangeMessage iwcChangeMessage) {
            //查询property setting 集合
            Setting setting = settingService.getByPropertyId(iwcChangeMessage.getPropertyId());

            //酒店配置alert规则
            AlertRuleDTO alertRuleDTO = JsonUtils.convertStringToObject(objectMapper, setting.getAlertRules(), AlertRuleDTO.class);
            if (Objects.nonNull(alertRuleDTO.getIwcRule()) && alertRuleDTO.getIwcRule().getStatus() == AlertRuleDTO.ENABLE) {
                //当前时间戳
                long nowTimestamp = DateTimeUtils.nowTimestamp();

                List<IwcChangeContent> iwcChangeContents = iwcChangeMessage.getIwcChangeContents();

                List<AlertMessage> alertMessages = iwcChangeContents.stream().map(changeContent -> {
                            //构建alertMessage对象
                            return AlertMessage.builder()
                                    .propertyId(setting.getPropertyId())
                                    .rule(AlertRuleEnum.IWC_LEVEL_CHANGE.getCode())
                                    .message(JsonUtils.convertObjToString(objectMapper, changeContent))
                                    .isSent(false)
                                    .triggerTime(nowTimestamp)
                                    .checkInDate(changeContent.getCheckInDate())
                                    .build();
                        }
                ).toList();

                saveBatch(alertMessages);
            }
        }

        /**
         * 查询每日的警报信息
         *
         * @param propertyDTO        资产DTO
         * @param dailyAlertMsgQuery 查询参数
         * @return java.util.List<cloud.wconnect.rateshop.pojo.vo.rateshop.DailyAlertMsgVO>
         * @author lichen
         * @date 2025/9/5
         */
        @Override
        public List<DailyAlertMsgVO> listDailyAlert(PropertyDTO propertyDTO, DailyAlertMsgQuery dailyAlertMsgQuery) {
            long startDate = Long.parseLong(dailyAlertMsgQuery.getFromDate().replace("-",""));
            long endDate = Long.parseLong(dailyAlertMsgQuery.getToDate().replace("-",""));
            //查询警报数据
            Map<Long, List<AlertMessage>> checkInDate2AlertMessages = list(new LambdaQueryWrapper<AlertMessage>()
                    .eq(AlertMessage::getPropertyId, propertyDTO.getId())
                    .between(AlertMessage::getCheckInDate, startDate, endDate)
                    .orderByDesc(AlertMessage::getId))
                    .stream().collect(Collectors.groupingBy(AlertMessage::getCheckInDate));

            //时间转换为localDate
            LocalDate fromLocalDate = DateTimeUtils.strToLocalDate(dailyAlertMsgQuery.getFromDate(), BaseConstant.DATE_FORMAT_YYYY_MM_DD);
            LocalDate toLocalDate = DateTimeUtils.strToLocalDate(dailyAlertMsgQuery.getToDate(), BaseConstant.DATE_FORMAT_YYYY_MM_DD);
            List<DailyAlertMsgVO> resultList = new ArrayList<>();
            //生成日历板
            for (LocalDate localDate = fromLocalDate; !localDate.isAfter(toLocalDate); localDate = localDate.plusDays(1)) {
                //把localDate转成Integer类型的时间（yyyyMMdd）
                long dailyLong = Long.parseLong(DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYYMMDD));

                List<AlertMessage> alertMessages = checkInDate2AlertMessages.getOrDefault(dailyLong, Collections.emptyList());

                //List<AlertMessage> -> List<AlertMsgVO>
                List<AlertMsgVO> alertMsgVOS = alertMessages.stream().map(alertMessage ->
                        AlertMsgVO.builder()
                                .id(alertMessage.getId())
                                .rule(alertMessage.getRule())
                                .isSent(alertMessage.getIsSent())
                                .message(alertMessage.getMessage())
                                .triggerTime(alertMessage.getTriggerTime())
                                .checkInDate(alertMessage.getCheckInDate())
                                .build()).toList();

                DailyAlertMsgVO dailyAlertMsgVO = new DailyAlertMsgVO();
                dailyAlertMsgVO.setDate(DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYY_MM_DD));
                dailyAlertMsgVO.setAlerts(alertMsgVOS);
                resultList.add(dailyAlertMsgVO);
            }
            return resultList;
        }

        /**
         * 查询警报信息
         *
         * @param propertyDTO   资产DTO
         * @param alertMsgQuery 筛选条件
         * @return java.util.List<cloud.wconnect.rateshop.pojo.vo.rateshop.AlertMsgVO>
         * @author lichen
         * @date 2025/12/30
         */
        @Override
        public List<AlertMsgVO> listAlertMsg(PropertyDTO propertyDTO, AlertMsgQuery alertMsgQuery) {
            //根据资产id查询rateShop功能设置信息
            Setting setting = settingService.getByPropertyId(propertyDTO.getId());
            //rateShop功能设置为空时，返回空的数据
            if (Objects.isNull(setting)) return Collections.emptyList();

            //当前日期yyyyMMdd
            String nowDateStr = DateTimeUtils.dateNowStr(propertyDTO.getTimezone(), BaseConstant.DATE_FORMAT_YYYYMMDD);
            long nowDate = Long.parseLong(nowDateStr);

            //查询条件
            LambdaQueryWrapper<AlertMessage> queryWrapper = new LambdaQueryWrapper<AlertMessage>()
                    .gt(Objects.nonNull(alertMsgQuery.getFromId()), AlertMessage::getId, alertMsgQuery.getFromId())
                    .eq(AlertMessage::getPropertyId, propertyDTO.getId())
                    .ge(AlertMessage::getCheckInDate, nowDate)
                    .eq(Objects.nonNull(alertMsgQuery.getRule()), AlertMessage::getRule, alertMsgQuery.getRule())
                    .apply(Objects.nonNull(alertMsgQuery.getDatasourceId()), "JSON_EXTRACT(message,  '$.datasourceId') = {0}", alertMsgQuery.getDatasourceId())
                    .orderByDesc(AlertMessage::getId);
            //查询警报数据
            List<AlertMessage> alertMessageList = list(queryWrapper);
            if (CollectionUtils.isEmpty(alertMessageList)) return Collections.emptyList();
            //List<AlertMessage> -> List<AlertMsgVO>
            return alertMessageList.stream().map(alertMessage ->
                    AlertMsgVO.builder()
                            .id(alertMessage.getId())
                            .rule(alertMessage.getRule())
                            .isSent(alertMessage.getIsSent())
                            .message(alertMessage.getMessage())
                            .triggerTime(alertMessage.getTriggerTime())
                            .checkInDate(alertMessage.getCheckInDate())
                            .build()
            ).collect(Collectors.toList());
        }

        /**
         * 处理价格变化或sold out的消息
         *
         * @param priceChangeMessage 价格变化消息对象
         * @author lichen
         * @date 2023/6/7
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void processPriceChangeMessage(PriceChangeMessage priceChangeMessage) {
            //酒店id
            Integer hotelId = priceChangeMessage.getHotelId();
            //币种id
            Integer currencyId = priceChangeMessage.getCurrencyId();
            //含税标志
            Integer containTaxFlag = priceChangeMessage.getContainTaxFlag();
            //房价类型
            Integer rateType = priceChangeMessage.getRateType();
            //数据源ID
            Integer datasourceId = priceChangeMessage.getDatasourceId();
            //销售地区
            String pos = priceChangeMessage.getPos();
            //查询酒店信息
            Hotel hotel = hotelService.getById(hotelId);
            //根据hotelId、币种、销售地区查询property setting集合
            List<Setting> settingList = settingService.listByHotelIdAndCurrencyIdAndPos(hotelId, currencyId, pos);
            if (CollectionUtils.isEmpty(settingList)) return;

            List<AlertMessage> alertMessages = new ArrayList<>();

            settingList.forEach(setting -> {
                //房价参数配置
                List<RateConfigDTO> rateConfigDTOS = JsonUtils.convertStringToList(objectMapper, setting.getRateConfig(), RateConfigDTO.class);

                //按照房价类型分组
                Map<Integer, List<RateConfigDTO>> rateType2RateConfigDTOS = rateConfigDTOS.stream().collect(Collectors.groupingBy(RateConfigDTO::getRateType));

                //判断房价类型是否匹配
                Set<Integer> rateTypeList = rateType2RateConfigDTOS.keySet();
                if (!rateTypeList.contains(rateType)) return;

                //数据源ID对应的平台ID集合
                Map<Integer, List<Integer>> datasourceId2PlatformIds = rateType2RateConfigDTOS.get(rateType)
                        .stream().collect(Collectors.toMap(RateConfigDTO::getDatasourceId, RateConfigDTO::getPlatformIds));
                Set<Integer> datasourceIds = datasourceId2PlatformIds.keySet();

                //判断数据源是否匹配
                if (!datasourceIds.contains(datasourceId)) return;

                //判断平台ID是否匹配
                List<Integer> platformIds = datasourceId2PlatformIds.get(datasourceId);
                if (!platformIds.contains(priceChangeMessage.getPlatformId())) return;

                //判断含税标识是否匹配
                if (!setting.getContainTaxFlag().equals(containTaxFlag)) return;

                //酒店配置的alert rule
                AlertRuleDTO alertRuleDTO = JsonUtils.convertStringToObject(objectMapper, setting.getAlertRules(), AlertRuleDTO.class);

                //变化的价格
                BigDecimal changePrice = priceChangeMessage.getPrice();

                //是否满足条件，满足则会创建alertMessage
                boolean match = false;
                //创建price change的警报消息
                AlertMsgContentDTO alertMsgContentDTO = beanMapper.map(priceChangeMessage, AlertMsgContentDTO.class);
                alertMsgContentDTO.setSymbol(hotel.getSymbol());

                //规则
                int ruleCode;

                if (changePrice.compareTo(BigDecimal.ZERO) != 0) {//price change消息
                    ruleCode = AlertRuleEnum.PRICE_CHANGE.getCode();
                    //获取价格变动幅度的alert rule
                    Optional<RateRuleDTO> optionalRule = alertRuleDTO.getRateRule().stream().filter(rule -> rule.getRateType().equals(rateType)).findFirst();
                    if (optionalRule.isPresent() && optionalRule.get().getStatus() == AlertRuleDTO.ENABLE) {
                        BigDecimal configPriceChangeRange = optionalRule.get().getPriceChangeRange();
                        if (Objects.nonNull(configPriceChangeRange) && changePrice.compareTo(configPriceChangeRange) >= 0) {
                            //设置变化的幅度
                            alertMsgContentDTO.setRate(configPriceChangeRange.toString());
                            match = true;
                        }
                    }

                } else {//sold out消息
                    ruleCode = AlertRuleEnum.SOLD_OUT.getCode();
                    //获取售罄的alert rule
                    if (alertRuleDTO.getSoldOutRule().getStatus() == AlertRuleDTO.ENABLE) {
                        match = true;
                    }
                }

                if (match) {
                    alertMessages.add(AlertMessage.builder()
                            .propertyId(setting.getPropertyId())
                            .rule(ruleCode)
                            .message(JsonUtils.convertObjToString(objectMapper, alertMsgContentDTO))
                            .isSent(false)
                            .triggerTime(DateTimeUtils.nowTimestamp())
                            .checkInDate(priceChangeMessage.getCheckInDate())
                            .build());
                }
            });

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

        /**
         * 处理趋势级别变化的消息
         *
         * @param marketDemandMessage 趋势级别变化消息对象
         * @author lichen
         * @date 2023/6/7
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void processMarketDemandMessage(MarketDemandMessage marketDemandMessage) {
            //根据地区ID查询property setting 集合
            List<Setting> settings = settingService.listByTrendSourceLocationId(marketDemandMessage.getLocationId());

            ArrayList<AlertMessage> batchList = new ArrayList<>();

            settings.forEach(setting -> {
                //酒店配置的alert rule
                AlertRuleDTO alertRuleDTO = JsonUtils.convertStringToObject(objectMapper, setting.getAlertRules(), AlertRuleDTO.class);
                if (alertRuleDTO.getMarketDemandRule().getStatus() == AlertRuleDTO.ENABLE) {
                    List<MarketDemandChangeContent> changeContents = marketDemandMessage.getChangeContents();
                    //checkInDates -> alertMessages
                    List<AlertMessage> alertMessages = changeContents.stream().map(changeContent -> {
                                Map<String, Object> msgMap = new HashMap<>();
                                msgMap.put("changeType", changeContent.getChangeType());
                                msgMap.put("changeAfterLevel", changeContent.getChangeAfterLevel());

                                //构建alertMessage对象
                                return AlertMessage.builder()
                                        .propertyId(setting.getPropertyId())
                                        .rule(AlertRuleEnum.MARKET_DEMAND.getCode())
                                        .message(JsonUtils.convertObjToString(objectMapper, msgMap))
                                        .isSent(false)
                                        .triggerTime(DateTimeUtils.nowTimestamp())
                                        .checkInDate(changeContent.getCheckInDate())
                                        .build();
                            }
                    ).toList();
                    batchList.addAll(alertMessages);
                }
            });

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

        /**
         * 生成并发送趋势级别变化的消息
         *
         * @param trendList  市场需求消息
         * @param locationId 地区ID
         * @author lichen
         * @date 2023/6/8
         */
        @Override
        public void generateMarketDemandMessage(List<Trend> trendList, Integer locationId) {

            HashMap<String, TrendLevel> msgKey2TrendLevel = filterWithin14Days1(trendList, locationId);
            if (CollectionUtils.isEmpty(msgKey2TrendLevel.keySet())) {
                return;
            }

            handleTrendAlert(locationId, msgKey2TrendLevel);

            //批量刷新缓存key
            HashMap<String, String> refreshMap = msgKey2TrendLevel.entrySet().stream()
                    .collect(HashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue().getLevel()), HashMap::putAll);
            redisTemplate.opsForValue().multiSet(refreshMap);
            //用一次连接，设置过期时间
            redisTemplate.executePipelined((RedisCallback<Void>) connection -> {
                for (String key : msgKey2TrendLevel.keySet()) {
                    //过期时间15天, 1小时 * 24（1天） * 15（天）
                    connection.expire(key.getBytes(), 3600L * 24L * 15L);
                }
                return null;
            });
        }

        /**
         * 处理趋势级别变化的消息
         *
         * @param locationId        地址ID
         * @param msgKey2TrendLevel 消息中的需求趋势集合
         * @return void
         * @author lichen
         * @date 2025/4/3
         */
        private void handleTrendAlert(Integer locationId, HashMap<String, TrendLevel> msgKey2TrendLevel) {
            ArrayList<String> cacheKeyList = new ArrayList<>(msgKey2TrendLevel.keySet());
            List<MarketDemandChangeContent> changeContentList = new ArrayList<>();
            List<String> cacheValues = redisTemplate.opsForValue().multiGet(cacheKeyList);
            if (cacheValues == null) {
                throw new IllegalArgumentException("redisTemplate -> multiGet is running in pipeline / transaction mode, please check the redisTemplate configuration.");
            }

            // 判断是否发生趋势级别变化
            for (int i = 0; i < cacheKeyList.size(); i++) {
                TrendLevel trendLevel = msgKey2TrendLevel.get(cacheKeyList.get(i));

                // 缓存中的当前级别不为空 且 和消息中最新级别不一致时
                if (StringUtils.isNotEmpty(cacheValues.get(i)) && !cacheValues.get(i).equals(trendLevel.getLevel())) {
                    //市场趋势级别变化类型
                    int changeType = Integer.parseInt(trendLevel.getLevel()) > Integer.parseInt(cacheValues.get(i)) ?
                            MarketDemandTrendEnum.RISEN.getTrend() : MarketDemandTrendEnum.DROPPED.getTrend();

                    changeContentList.add(new MarketDemandChangeContent(trendLevel.getCheckInDate(),
                            changeType, Integer.parseInt(trendLevel.getLevel())));
                }
            }

            if (CollectionUtils.isNotEmpty(changeContentList)) {
                MarketDemandMessage marketDemandMessage = new MarketDemandMessage();
                marketDemandMessage.setLocationId(locationId);
                marketDemandMessage.setChangeContents(changeContentList);

                AlertEventMessage alertEventMessage = new AlertEventMessage();
                alertEventMessage.setType(AlertEventMessage.TYPE_MARKET_DEMAND);
                alertEventMessage.setContent(JsonUtils.convertObjToString(objectMapper, marketDemandMessage));

                amazonSqsService.sendMessage(alertEventUrl, JsonUtils.convertObjToString(objectMapper, alertEventMessage));
            }
        }

        /**
         * 过滤未来14天的数据并存储到缓存中
         *
         * @param trendList  需求趋势集合
         * @param locationId 地址ID
         * @return java.util.HashMap<java.lang.String, cloud.wconnect.rateshop.pojo.dto.rateshop.sender.TrendLevel>
         * @author lichen
         * @date 2025/4/3
         */
        private static HashMap<String, TrendLevel> filterWithin14Days1(List<Trend> trendList, Integer locationId) {
            HashMap<String, TrendLevel> msgKey2TrendLevel = new HashMap<>();

            trendList.forEach(trend -> {
                Long checkInDate = trend.getCheckInDate();
                //判断checkInDate是否在未来14天之内
                if (Math.abs(
                        ChronoUnit.DAYS.between(
                                DateTimeUtils.strToLocalDate(checkInDate.toString(), BaseConstant.DATE_FORMAT_YYYYMMDD),
                                LocalDate.now())
                ) > 14) {
                    return;
                }

                msgKey2TrendLevel.put(StringUtils.join(Arrays.asList("trend", locationId.toString(), checkInDate.toString()), ":"),
                        new TrendLevel(trend.getLevel().toString(), checkInDate));
            });

            return msgKey2TrendLevel;
        }

        /**
         * 生成alertMessage，并发送消息
         * 包括（sold out 和 priceChange类型的消息）
         *
         * @param cacheMinPriceStr 缓存中的最低价格字符串
         * @param msgMinPriceObj   消息中的最低价格对象
         * @author lichen
         * @date 2023/6/7
         */
        private void doGeneratePriceMessage(String cacheMinPriceStr, MsgMinPrice msgMinPriceObj) {
            //消息中的最低价格
            BigDecimal msgMinPrice = msgMinPriceObj.getMinPrice();

            //缓存中的最低价格不为空时，把字符串转换成BigDecimal类型
            BigDecimal cacheMinPrice = new BigDecimal(cacheMinPriceStr).setScale(2, RoundingMode.HALF_UP);

            //构建PriceChangeMessage
            PriceChangeMessage priceChangeMessage = beanMapper.map(msgMinPriceObj, PriceChangeMessage.class);

            //缓存中的价格等于0
            if (cacheMinPrice.compareTo(BigDecimal.ZERO) == BaseConstant.ZERO) {
                if (msgMinPrice.compareTo(BigDecimal.ZERO) != BaseConstant.ZERO) {
                    //由sold out 变为了 有最低价格
                    //发送priceChange消息，价格为消息中的最低价格
                    priceChangeMessage.setPrice(msgMinPrice);
                    this.sendPriceChangeMessage(priceChangeMessage);
                }
                //else 为连续sold out，不用发送消息
            } else {
                if (msgMinPrice.compareTo(BigDecimal.ZERO) == BaseConstant.ZERO) {
                    //缓存不等于0，消息中价格等于0时，发送sold out消息
                    priceChangeMessage.setPrice(BigDecimal.ZERO);
                    this.sendPriceChangeMessage(priceChangeMessage);
                } else {
                    if (msgMinPrice.compareTo(cacheMinPrice) != BaseConstant.ZERO) {
                        //计算缓存中的价格 和 消息中的价格 相差的绝对值
                        BigDecimal priceChange = msgMinPrice.subtract(cacheMinPrice).abs();
                        //发送priceChange消息，price的值为 价格差的绝对值
                        priceChangeMessage.setPrice(priceChange);
                        this.sendPriceChangeMessage(priceChangeMessage);
                    }
                    //else 缓存中的价格 和消息中的价格一样，不用处理
                }
            }
        }

        /**
         * 发送priceChange消息
         *
         * @param priceChangeMessage 价格变化消息
         * @author lichen
         * @date 2023/6/6
         */
        private void sendPriceChangeMessage(PriceChangeMessage priceChangeMessage) {
            String content = JsonUtils.convertObjToString(objectMapper, priceChangeMessage);

            //构建AlertEventMessage
            AlertEventMessage alertEventMessage = new AlertEventMessage();
            alertEventMessage.setType(AlertEventMessage.TYPE_PRICE_CHANGE);
            alertEventMessage.setContent(content);

            //发送消息
            String message = JsonUtils.convertObjToString(objectMapper, alertEventMessage);
            amazonSqsService.sendMessage(alertEventUrl, message);
        }
    }