# CategoryRecomHourSumServiceImpl.java

    package cloud.wconnect.workperformance.service.sva.scheduleactual.impl;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.MoudleEventSubjectEnum;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.commonlib.util.TransactionUtils;
    import cloud.wconnect.sns.http.processor.Sender;
    import cloud.wconnect.workperformance.entity.sva.CategoryRecomHourSum;
    import cloud.wconnect.workperformance.mapper.sva.CategoryRecomHourSumMapper;
    import cloud.wconnect.workperformance.pojo.bo.modelevent.dephourstatistics.SvaDepHourStatisticsEventBO;
    import cloud.wconnect.workperformance.pojo.dto.sva.hoursstatistics.SvaDepCategoryRecommendHourTotalDTO;
    import cloud.wconnect.workperformance.service.sva.scheduleactual.ICategoryRecomHourSumService;
    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.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import jakarta.annotation.Resource;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.lang.Nullable;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.util.List;
    import java.util.Objects;
    import java.util.concurrent.Executor;

    /**
     * <p>
     * 部门分类推荐时数总和表 服务实现类
     * </p>
     *
     * @author zc
     * @since 2023-06-27
     */
    @Service
    @DS("work_performance")
    public class CategoryRecomHourSumServiceImpl extends ServiceImpl<CategoryRecomHourSumMapper, CategoryRecomHourSum> implements ICategoryRecomHourSumService {
        @Resource
        private ObjectMapper objectMapper;
        @Resource
        private Executor executor;
        @Resource
        @Qualifier("moduleEventSender")
        private Sender moduleEventSender;

        /**
         * 获取指定日期范围内的推荐时数总和
         *
         * @param propertyId           资产 id
         * @param fromDate             开始日期
         * @param toDate               结束日期
         * @param departmentCategoryId 部门分类 id
         * @return CategoryRecomHourSum 集合
         * @author zc
         * @date 2023/6/27
         */
        @Override
        public List<CategoryRecomHourSum> listByDate(int propertyId, long fromDate, long toDate, @Nullable Integer departmentCategoryId) {
            return list(new QueryWrapper<CategoryRecomHourSum>().lambda()
                    .eq(CategoryRecomHourSum::getPropertyId, propertyId)
                    .eq(CategoryRecomHourSum::getFromDate, fromDate)
                    .eq(CategoryRecomHourSum::getToDate, toDate)
                    .eq(Objects.nonNull(departmentCategoryId), CategoryRecomHourSum::getDepCategoryId, departmentCategoryId));
        }

        /**
         * 获取指定日期的推荐时数总和
         *
         * @param propertyId            资产 id
         * @param fromDate              开始日期
         * @param toDate                结束日期
         * @param departmentCategoryIds 部门分类 id
         * @return CategoryRecomHourSum 集合
         * @author zc
         * @date 2023/6/27
         */
        @Override
        public List<CategoryRecomHourSum> listByDate(int propertyId, long fromDate, long toDate, List<Integer> departmentCategoryIds) {
            return list(new QueryWrapper<CategoryRecomHourSum>().lambda()
                    .eq(CategoryRecomHourSum::getPropertyId, propertyId)
                    .eq(CategoryRecomHourSum::getFromDate, fromDate)
                    .eq(CategoryRecomHourSum::getToDate, toDate)
                    .in(CollectionUtils.isNotEmpty(departmentCategoryIds), CategoryRecomHourSum::getDepCategoryId, departmentCategoryIds));
        }

        /**
         * 获取指定日期范围内的推荐时数总和
         *
         * @param propertyId            资产 id
         * @param startDate             开始日期
         * @param endDate               结束日期
         * @param departmentCategoryIds 部门分类 id
         * @return CategoryRecomHourSum 集合
         * @author zc
         * @date 2025/4/4
         */
        @Override
        public List<CategoryRecomHourSum> listByDateRange(int propertyId, long startDate, long endDate, List<Integer> departmentCategoryIds) {
            return list(new QueryWrapper<CategoryRecomHourSum>().lambda()
                    .eq(CategoryRecomHourSum::getPropertyId, propertyId)
                    .ge(CategoryRecomHourSum::getFromDate, startDate)
                    .le(CategoryRecomHourSum::getToDate, endDate)
                    .in(CollectionUtils.isNotEmpty(departmentCategoryIds), CategoryRecomHourSum::getDepCategoryId, departmentCategoryIds));
        }

        /**
         * 编辑指定部门分类一周内的 Recommend Hours 总时数
         *
         * @param propertyDTO 资产
         * @param saveDTO     编辑内容
         * @author zc
         * @date 2025/2/7
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveRecommendHours(PropertyDTO propertyDTO, SvaDepCategoryRecommendHourTotalDTO saveDTO, int operatorId) {
            long nowTimestamp = DateTimeUtils.nowTimestamp();
            baseMapper.saveOrUpdate(CategoryRecomHourSum.builder()
                    .propertyId(propertyDTO.getId())
                    .depCategoryId(saveDTO.getDepartmentCategoryId())
                    .fromDate(saveDTO.getFromDate())
                    .toDate(saveDTO.getToDate())
                    .recomHourSum(saveDTO.getRecommendHoursTotal())
                    .createdBy(operatorId)
                    .createdTime(nowTimestamp)
                    .lastUpdatedBy(operatorId)
                    .lastUpdatedTime(nowTimestamp)
                    .build());

            // 指定日期范围内数据发生变化时，发送 SNS 事件
            TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                long startDate = DateTimeUtils.parseToLong(DateTimeUtils.toZonedDateTime(saveDTO.getFromDate(),
                        propertyDTO.getTimezone()), BaseConstant.DATE_FORMAT_YYYYMMDD);
                long endDate = DateTimeUtils.parseToLong(DateTimeUtils.toZonedDateTime(saveDTO.getToDate(),
                        propertyDTO.getTimezone()), BaseConstant.DATE_FORMAT_YYYYMMDD);
                sendModuleEvent(propertyDTO.getId(), Lists.newArrayList(startDate, endDate));
            });
        }

        /**
         * 发送数据变更事件
         *
         * @param propertyId 资产 id
         * @param dateRange  日期范围(yyyyMMdd)
         * @author zc
         * @date 2025/4/4
         */
        private void sendModuleEvent(int propertyId, List<Long> dateRange) {
            SvaDepHourStatisticsEventBO eventBO = SvaDepHourStatisticsEventBO.builder()
                    .propertyId(propertyId)
                    .startDate(dateRange.get(0))
                    .endDate(dateRange.get(1))
                    .build();
            String messages = JsonUtils.convertObjListToString(objectMapper, Lists.newArrayList(eventBO));
            moduleEventSender.publish(MoudleEventSubjectEnum.SVA_DEPARTMENT_HOUR_STATISTICS.getSubject(), messages);
        }
    }