# GmwrStrDataServiceImpl.java

    package cloud.wconnect.workperformance.service.gmwr.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.MoudleEventSubjectEnum;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.commonlib.util.NumberUtil;
    import cloud.wconnect.commonlib.util.TransactionUtils;
    import cloud.wconnect.sns.http.processor.Sender;
    import cloud.wconnect.workperformance.cons.HandleReportDataStatusCons;
    import cloud.wconnect.workperformance.cons.gmwr.GmwrStatusEnum;
    import cloud.wconnect.workperformance.cons.gmwr.strdata.GmwrStrDataDateEnum;
    import cloud.wconnect.workperformance.cons.gmwr.strdata.GmwrStrDataEnum;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrStr;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrStrData;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrStrDataNote;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrWeeks;
    import cloud.wconnect.workperformance.entity.hotelkpi.HotelKpiStrWeek;
    import cloud.wconnect.workperformance.mapper.gmwr.GmwrStrDataMapper;
    import cloud.wconnect.workperformance.pojo.bo.gmwr.event.GmwrStrWeekEventBO;
    import cloud.wconnect.workperformance.pojo.bo.gmwr.str.GmwrStrDataBO;
    import cloud.wconnect.workperformance.pojo.bo.gmwr.str.GmwrStrDataCategoryBO;
    import cloud.wconnect.workperformance.pojo.bo.gmwr.str.GmwrStrDataDetailBO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.note.GmwrNoteDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.strdata.GmwrStrDataDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.strdata.GmwrStrDataDateDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.strdata.GmwrStrDataValueDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.strdata.basic.GmwrStrDataBasicDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.strdata.basic.GmwrStrDataBasicValueDetailDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.strdata.save.GmwrStrDataCategorySaveDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.strdata.save.GmwrStrDataDetailSaveDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.strdata.save.GmwrStrDataSaveDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.strdata.save.GmwrStrDataValueSaveDTO;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrStrDataNoteService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrStrDataService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrStrService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrWeeksService;
    import cloud.wconnect.workperformance.service.hotelkpi.internal.IHotelKpiInternalService;
    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 jakarta.annotation.Nullable;
    import jakarta.annotation.Resource;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.time.DayOfWeek;
    import java.time.LocalDate;
    import java.time.temporal.TemporalAdjusters;
    import java.util.*;
    import java.util.concurrent.Executor;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     * GM Weekly Report STR Data 服务实现类
     * </p>
     *
     * @author zc
     * @since 2024-10-12
     */
    @Slf4j
    @Service
    @DS("work_performance")
    public class GmwrStrDataServiceImpl extends ServiceImpl<GmwrStrDataMapper, GmwrStrData> implements IGmwrStrDataService {
        @Resource
        private IGmwrStrDataNoteService strDataNoteService;
        @Resource
        private IGmwrStrService strService;
        @Resource
        private IGmwrWeeksService weeksService;
        @Lazy
        @Resource
        private IHotelKpiInternalService hotelKpiInternalService;
        @Resource
        private UserInternalService userInternalService;
        @Resource
        private ObjectMapper objectMapper;
        @Resource
        private BeanMapper beanMapper;
        @Resource
        @Qualifier("moduleEventSender")
        private Sender moduleEventSender;
        @Resource
        private Executor executor;

        /**
         * 更新 STR Data 数据状态
         *
         * @param id     Str Data id
         * @param status 状态
         * @author zc
         * @date 2025/9/4
         */
        @Override
        public void updateStatus(int id, String status) {
            updateById(new GmwrStrData().setId(id).setStatus(status));
        }

        /**
         * 保存 STR Data 数据为处理中
         *
         * @param propertyId 资产 id
         * @param gmwrWeeks  周报
         * @return Str Data id
         * @author zc
         * @date 2025/9/4
         */
        @Override
        public int saveInProgress(int propertyId, GmwrWeeks gmwrWeeks) {
            GmwrStrData strData = getByReportId(gmwrWeeks.getId());

            // 如果数据不存在则创建新对象
            if (Objects.isNull(strData)) {
                strData = new GmwrStrData();
                strData.setPropertyId(propertyId);
                strData.setReportId(gmwrWeeks.getId());
                strData.setDetail(JsonUtils.convertObjListToString(objectMapper,
                        GmwrStrDataBasicDTO.generateInitBasicDTOList()));
            }
            strData.setStatus(HandleReportDataStatusCons.PROCESSING);

            baseMapper.insertOrUpdateByUnique(strData);

            return strData.getId();
        }

        /**
         * 生成 Str Data 数据
         *
         * @param propertyDTO 资产
         * @param gmwrWeeks   周报
         * @author zc
         * @date 2024/10/18
         */
        @Override
        public void generate(PropertyDTO propertyDTO, GmwrWeeks gmwrWeeks) {
            GmwrStrData strData = getByReportId(gmwrWeeks.getId());
            List<GmwrStrDataBasicDTO> basicDTOList;
            if (Objects.isNull(strData)) {
                strData = new GmwrStrData();
                basicDTOList = GmwrStrDataBasicDTO.generateInitBasicDTOList();
            } else {
                basicDTOList = JsonUtils.convertStringToList(objectMapper, strData.getDetail(), GmwrStrDataBasicDTO.class);
            }

            GmwrStr str = strService.getNewOne(propertyDTO.getId());
            if (Objects.nonNull(str)) {
                strData.setDataDate(str.getDataDate());
                strData.setFileUrl(str.getFileUrl());

                List<GmwrStrDataCategoryBO> strList = JsonUtils.convertStringToList(objectMapper,
                        str.getDetail(), GmwrStrDataCategoryBO.class);

                updateBySystem(basicDTOList, strList);
            }
            strData.setPropertyId(propertyDTO.getId());
            strData.setReportId(gmwrWeeks.getId());

            String detail = JsonUtils.convertObjListToString(objectMapper, basicDTOList);
            strData.setDetail(detail);
            strData.setStatus(HandleReportDataStatusCons.SUCCESS);

            baseMapper.insertOrUpdateByUnique(strData);
        }

        /**
         * 获取指定周报的 Str 数据
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param reportId   周报 id
         * @return GmwrStrDataDTO
         * @author zc
         * @date 2024/10/18
         */
        @Override
        public GmwrStrDataDTO get(int tenantId, int propertyId, int reportId) {
            GmwrStrDataDTO strDataDTO = new GmwrStrDataDTO();
            GmwrStrData strData = getByReportId(reportId);
            if (Objects.isNull(strData)) {
                return strDataDTO;
            }
            List<GmwrStrDataBasicDTO> basicDTOList = JsonUtils.convertStringToList(objectMapper,
                    strData.getDetail(), GmwrStrDataBasicDTO.class);

            List<GmwrStrDataNote> strDataNoteList = strDataNoteService.listByReportId(reportId);
            Map<Integer, Map<Integer, GmwrStrDataNote>> category2NoteByDateCategory = strDataNoteList.stream()
                    .collect(Collectors.groupingBy(GmwrStrDataNote::getDateCategory,
                            Collectors.toMap(GmwrStrDataNote::getCategory, Function.identity())));

            // 获取用户名
            Set<Integer> userIds = strDataNoteList.stream().map(GmwrStrDataNote::getUpdatedBy)
                    .filter(item -> item > 0).collect(Collectors.toSet());
            userIds.addAll(basicDTOList.stream().map(GmwrStrDataBasicDTO::extractAllUserId)
                    .flatMap(Collection::stream).toList());
            Map<Integer, String> userId2Name = userInternalService.getNameMapByIds(tenantId, userIds);

            // Str 报告数据日期
            Long reportWeekStartDate;
            Long reportEndDate;
            Long reportR28DStartDate;
            if (Objects.nonNull(strData.getDataDate())) {
                LocalDate reportStartLocalDate = DateTimeUtils.strToLocalDate(strData.getDataDate().toString(),
                        BaseConstant.DATE_FORMAT_YYYYMMDD);
                reportWeekStartDate = Long.parseLong(DateTimeUtils.localDateToStr(reportStartLocalDate,
                        BaseConstant.DATE_FORMAT_YYYYMMDD));
                reportEndDate = Long.parseLong(DateTimeUtils.localDateToStr(reportStartLocalDate.plusDays(6),
                        BaseConstant.DATE_FORMAT_YYYYMMDD));
                reportR28DStartDate = Long.parseLong(DateTimeUtils.localDateToStr(reportStartLocalDate.minusDays(21),
                        BaseConstant.DATE_FORMAT_YYYYMMDD));
            } else {
                reportWeekStartDate = null;
                reportEndDate = null;
                reportR28DStartDate = null;
            }

            List<GmwrStrDataDateDTO> strDataDateDTOList = basicDTOList.stream().map(basicDTO -> {
                Map<Integer, GmwrStrDataNote> category2Note = category2NoteByDateCategory.getOrDefault(basicDTO.getCategory(), Collections.emptyMap());
                List<GmwrStrDataValueDTO> valueDTOList = basicDTO.getList().stream().map(basicValueDTO ->
                        beanMapper.map(basicValueDTO, GmwrStrDataValueDTO.class, dto -> {
                            dto.setCategoryName(GmwrStrDataEnum.getCategoryName(basicValueDTO.getCategory()));
                            dto.getProperty().setUpdatedName(userId2Name.get(basicValueDTO.getProperty().getUpdatedBy()));
                            dto.getPropertyChg().setUpdatedName(userId2Name.get(basicValueDTO.getPropertyChg().getUpdatedBy()));
                            dto.getCompSet().setUpdatedName(userId2Name.get(basicValueDTO.getCompSet().getUpdatedBy()));
                            dto.getCompSetChg().setUpdatedName(userId2Name.get(basicValueDTO.getCompSetChg().getUpdatedBy()));
                            dto.getIndex().setUpdatedName(userId2Name.get(basicValueDTO.getIndex().getUpdatedBy()));
                            dto.getIndexChg().setUpdatedName(userId2Name.get(basicValueDTO.getIndexChg().getUpdatedBy()));

                            GmwrStrDataNote note = category2Note.get(basicValueDTO.getCategory());
                            GmwrNoteDTO noteDTO = new GmwrNoteDTO();
                            if (Objects.nonNull(note)) {
                                noteDTO.setContent(note.getContent());
                                noteDTO.setUpdatedName(userId2Name.get(note.getUpdatedBy()));
                                noteDTO.setUpdatedTime(note.getUpdatedTime());
                            }
                            dto.setNote(noteDTO);
                        })
                ).toList();

                GmwrStrDataDateDTO dataDateDTO = new GmwrStrDataDateDTO();
                dataDateDTO.setCategory(basicDTO.getCategory());
                dataDateDTO.setCategoryName(GmwrStrDataDateEnum.getCategoryName(basicDTO.getCategory()));
                dataDateDTO.setList(valueDTOList);
                if (basicDTO.getCategory() == GmwrStrDataDateEnum.WEEK.getCategory()) {
                    dataDateDTO.setStartDate(reportWeekStartDate);
                    dataDateDTO.setEndDate(reportEndDate);
                } else {
                    dataDateDTO.setStartDate(reportR28DStartDate);
                    dataDateDTO.setEndDate(reportEndDate);
                }
                return dataDateDTO;
            }).toList();
            strDataDTO.setList(strDataDateDTOList);
            strDataDTO.setReportUrl(strData.getFileUrl());
            strDataDTO.setStatus(strData.getStatus());

            return strDataDTO;
        }

        /**
         * 根据周报 id 获取 str 数据
         *
         * @param reportId 周报 id
         * @return GmwrStrData
         * @author zc
         * @date 2024/11/5
         */
        @Override
        public GmwrStrData getByReportId(int reportId) {
            return getOne(new LambdaQueryWrapper<GmwrStrData>()
                    .eq(GmwrStrData::getReportId, reportId));
        }

        /**
         * 保存数据
         *
         * @param propertyId 资产 id
         * @param gmwrWeeks  周报
         * @param operatorId 操作人 id
         * @param saveDTO    保存数据
         * @author zc
         * @date 2025/1/21
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void save(int propertyId, GmwrWeeks gmwrWeeks, int operatorId, GmwrStrDataSaveDTO saveDTO) {
            GmwrStrData strData = getByReportId(gmwrWeeks.getId());
            List<GmwrStrDataBasicDTO> basicDTOList = Objects.isNull(strData) ?
                    GmwrStrDataBasicDTO.generateInitBasicDTOList() :
                    JsonUtils.convertStringToList(objectMapper, strData.getDetail(), GmwrStrDataBasicDTO.class);

            // 更新数据
            updateByManual(basicDTOList, saveDTO.getList(), operatorId);

            // 如果数据不存在则创建新对象
            if (Objects.isNull(strData)) {
                strData = new GmwrStrData();
                strData.setPropertyId(propertyId);
                strData.setReportId(gmwrWeeks.getId());
            }
            strData.setDataDate(saveDTO.getDataDate());
            strData.setDetail(JsonUtils.convertObjListToString(objectMapper, basicDTOList));
            strData.setStatus(HandleReportDataStatusCons.SUCCESS);

            baseMapper.insertOrUpdateByUnique(strData);

            // 指定日期的 Str week 数据发生变化时，发送 SNS 事件
            TransactionUtils.afterCommitAsyncExecute(executor, () ->
                    sendModuleEvent(propertyId, gmwrWeeks.getWeek())
            );
        }

        /**
         * 更新 BasicDTO 列表数据 by System
         *
         * @param basicDTOList 基础数据列表
         * @param boList       数据
         * @author zc
         * @date 2025/8/22
         */
        private void updateBySystem(List<GmwrStrDataBasicDTO> basicDTOList, List<GmwrStrDataCategoryBO> boList) {
            // 构建日期分类到保存数据的映射
            Map<Integer, GmwrStrDataCategoryBO> category2BO = boList.stream()
                    .collect(Collectors.toMap(GmwrStrDataCategoryBO::getCategory, Function.identity()));

            basicDTOList.forEach(basicDTO -> {
                GmwrStrDataCategoryBO categoryBO = category2BO.get(basicDTO.getCategory());
                if (Objects.isNull(categoryBO)) {
                    return;
                }

                // 构建值分类到保存数据的映射
                Map<Integer, GmwrStrDataDetailBO> category2DetailBO = categoryBO.getList().stream()
                        .collect(Collectors.toMap(GmwrStrDataDetailBO::getCategory, Function.identity()));

                // 更新每个值
                basicDTO.getList().forEach(basicValueDTO -> {
                    GmwrStrDataDetailBO detailBO = category2DetailBO.get(basicValueDTO.getCategory());
                    if (Objects.isNull(detailBO)) {
                        return;
                    }
                    // 更新各个字段的值
                    basicValueDTO.getProperty().setCrawl(detailBO.getProperty());
                    basicValueDTO.getPropertyChg().setCrawl(detailBO.getPropertyChg());
                    basicValueDTO.getCompSet().setCrawl(detailBO.getCompSet());
                    basicValueDTO.getCompSetChg().setCrawl(detailBO.getCompSetChg());
                    basicValueDTO.getIndex().setCrawl(detailBO.getIndex());
                    basicValueDTO.getIndexChg().setCrawl(detailBO.getIndexChg());
                });
            });
        }

        /**
         * 更新 BasicDTO 列表数据 by Manual
         *
         * @param basicDTOList 基础数据列表
         * @param saveDTOList  保存数据
         * @param operatorId   操作人 id
         * @author zc
         * @date 2025/8/22
         */
        private void updateByManual(List<GmwrStrDataBasicDTO> basicDTOList, List<GmwrStrDataCategorySaveDTO> saveDTOList, int operatorId) {
            Map<Integer, GmwrStrDataCategorySaveDTO> category2SaveDTO = saveDTOList.stream()
                    .collect(Collectors.toMap(GmwrStrDataCategorySaveDTO::getCategory, Function.identity()));

            long nowTimestamp = DateTimeUtils.nowTimestamp();
            basicDTOList.forEach(basicDTO -> {
                GmwrStrDataCategorySaveDTO dateSaveDTO = category2SaveDTO.get(basicDTO.getCategory());
                if (Objects.isNull(dateSaveDTO)) {
                    return;
                }

                // 构建值分类到保存数据的映射
                Map<Integer, GmwrStrDataDetailSaveDTO> category2ValueSaveDTO = dateSaveDTO.getList().stream()
                        .collect(Collectors.toMap(GmwrStrDataDetailSaveDTO::getCategory, Function.identity()));

                // 更新每个值
                basicDTO.getList().forEach(basicValueDTO -> {
                    GmwrStrDataDetailSaveDTO dataValueSaveDTO = category2ValueSaveDTO.get(basicValueDTO.getCategory());
                    if (Objects.isNull(dataValueSaveDTO)) {
                        return;
                    }

                    // 更新各个字段的值
                    setManualValue(basicValueDTO.getProperty(), dataValueSaveDTO.getProperty(),
                            operatorId, nowTimestamp);
                    setManualValue(basicValueDTO.getPropertyChg(), dataValueSaveDTO.getPropertyChg(),
                            operatorId, nowTimestamp);
                    setManualValue(basicValueDTO.getCompSet(), dataValueSaveDTO.getCompSet(),
                            operatorId, nowTimestamp);
                    setManualValue(basicValueDTO.getCompSetChg(), dataValueSaveDTO.getCompSetChg(),
                            operatorId, nowTimestamp);
                    setManualValue(basicValueDTO.getIndex(), dataValueSaveDTO.getIndex(),
                            operatorId, nowTimestamp);
                    setManualValue(basicValueDTO.getIndexChg(), dataValueSaveDTO.getIndexChg(),
                            operatorId, nowTimestamp);
                });
            });
        }

        /**
         * 设置人工录入值
         *
         * @param valueDetailDTO 值详情DTO
         * @param saveDTO        数值
         * @param operatorId     操作人 id
         * @param nowTimestamp   当前时间戳
         * @author zc
         * @date 2025/8/21
         */
        private void setManualValue(GmwrStrDataBasicValueDetailDTO valueDetailDTO, GmwrStrDataValueSaveDTO saveDTO,
                                    int operatorId, long nowTimestamp) {
            if (saveDTO.getUseSystem()) {
                valueDetailDTO.setManual(null);
                valueDetailDTO.setUpdatedBy(0);
                valueDetailDTO.setUpdatedTime(0L);
            } else {
                if (!NumberUtil.equals(valueDetailDTO.getManual(), saveDTO.getValue())) {
                    valueDetailDTO.setManual(saveDTO.getValue());
                    valueDetailDTO.setUpdatedBy(operatorId);
                    valueDetailDTO.setUpdatedTime(nowTimestamp);
                }
            }
        }

        /**
         * 处理导入报告
         *
         * @param propertyDTO 资产
         * @param strDataId   Str Data id
         * @param bo          数据
         * @param path        报告文件地址
         * @author zc
         * @date 2025/8/23
         */
        @Override
        public void handleStrWeekReport(PropertyDTO propertyDTO, @Nullable Integer strDataId, GmwrStrDataBO bo, String path) {
            // 周报 id
            int reportId;
            GmwrStrData strData = null;

            // 查询报告所属周报
            if (Objects.isNull(strDataId)) {
                DayOfWeek dayOfWeek = hotelKpiInternalService.getWeekStartDay(propertyDTO.getId());
                LocalDate localDate = DateTimeUtils.strToLocalDate(bo.getDataDate().toString(),
                        BaseConstant.DATE_FORMAT_YYYYMMDD).with(TemporalAdjusters.nextOrSame(dayOfWeek));
                // 计算报告归属的周期日期
                long date = Long.parseLong(DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYYMMDD));

                // 根据周期日期获取周报数据
                GmwrWeeks gmwrWeeks = weeksService.getByDate(propertyDTO.getId(), date);
                if (Objects.isNull(gmwrWeeks)) {
                    return;
                }
                // 只处理未完成周期
                if (gmwrWeeks.getStatus() == GmwrStatusEnum.COMPLETE.getStatus()) {
                    return;
                }
                reportId = gmwrWeeks.getId();
            } else {
                strData = getById(strDataId);
                reportId = strData.getReportId();
            }

            List<GmwrStrDataBasicDTO> basicDTOList = Objects.isNull(strData) ?
                    GmwrStrDataBasicDTO.generateInitBasicDTOList() :
                    JsonUtils.convertStringToList(objectMapper, strData.getDetail(), GmwrStrDataBasicDTO.class);

            // 更新数据
            updateBySystem(basicDTOList, bo.getList());

            // 如果数据不存在则创建新对象
            if (Objects.isNull(strData)) {
                strData = new GmwrStrData();
                strData.setPropertyId(propertyDTO.getId());
                strData.setReportId(reportId);
                strData.setStatus(HandleReportDataStatusCons.SUCCESS);
            }
            strData.setDataDate(bo.getDataDate());
            strData.setFileUrl(path);
            strData.setDetail(JsonUtils.convertObjListToString(objectMapper, basicDTOList));

            baseMapper.insertOrUpdateByUnique(strData);
        }

        /**
         * 处理模块事件数据
         *
         * @param propertyDTO 资产
         * @param date        周期日期(yyyyMMdd)
         * @author zc
         * @date 2025/8/23
         */
        @Override
        public void handleModuleEvent(PropertyDTO propertyDTO, long date) {
            GmwrWeeks gmwrWeeks = weeksService.getByDate(propertyDTO.getId(), date);
            if (Objects.isNull(gmwrWeeks)) {
                return;
            }

            // 只处理未完成周期
            if (gmwrWeeks.getStatus() == GmwrStatusEnum.COMPLETE.getStatus()) {
                return;
            }
            // 获取 Hotel KPI STR 周数据
            HotelKpiStrWeek strWeek = hotelKpiInternalService.getStrWeek(propertyDTO.getId(), date);
            if (Objects.isNull(strWeek)) {
                return;
            }
            GmwrStrData strData = new GmwrStrData()
                    .setPropertyId(propertyDTO.getId())
                    .setReportId(gmwrWeeks.getId())
                    .setDataDate(strWeek.getDataDate())
                    .setFileUrl(strWeek.getFileUrl())
                    .setDetail(strWeek.getDetail())
                    .setStatus(HandleReportDataStatusCons.SUCCESS);
            baseMapper.insertOrUpdateByUnique(strData);
        }

        /**
         * 发送模块事件
         *
         * @param propertyId 资产 id
         * @param date       日期(yyyyMMdd)
         * @author zc
         * @date 2025/8/23
         */
        private void sendModuleEvent(int propertyId, long date) {
            GmwrStrWeekEventBO strWeekEventBO = new GmwrStrWeekEventBO();
            strWeekEventBO.setPropertyId(propertyId);
            strWeekEventBO.setDate(date);
            moduleEventSender.publish(MoudleEventSubjectEnum.WEEKLY_GM_REPORT_STR_WEEK.getSubject(),
                    JsonUtils.convertObjToString(objectMapper, strWeekEventBO));
        }
    }