# ClcMissingReportServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.ReportImportResultCons;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.frontoffice.entity.clc.ClcMissingReport;
    import cloud.wconnect.frontoffice.mapper.clc.ClcMissingReportMapper;
    import cloud.wconnect.frontoffice.pojo.bo.clc.CrawLogBO;
    import cloud.wconnect.frontoffice.pojo.dto.clc.ImportArrivalLogDTO;
    import cloud.wconnect.frontoffice.pojo.query.clc.ArrivalImportLogQuery;
    import cloud.wconnect.frontoffice.service.clc.IClcMissingReportService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Sets;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.time.format.DateTimeFormatter;
    import java.util.*;

    /**
     * <p>
     *  服务实现类
     * </p>
     *
     * @author yyt
     * @since 2023-07-24
     */
    @Service
    @DS("logbook")
    public class ClcMissingReportServiceImpl extends ServiceImpl<ClcMissingReportMapper, ClcMissingReport> implements IClcMissingReportService {

        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private BeanMapper beanMapper;

        /**
         * 校验异常数据存在
         *
         * @param id 异常记录 id
         * @return 异常数据
         * @author yyt
         * @date 2023/7/25
         */
        @Override
        public ClcMissingReport checkExist(long id) {
            ClcMissingReport missingReport = getById(id);
            if (Objects.isNull(missingReport)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the clcMissingReport is not found;id:" + id);
            }
            return missingReport;
        }

        /**
         * 保存日志
         *
         * @param propertyDTO 资产
         * @param crawLogBO   采集日志
         * @author yyt
         * @date 2023/9/5
         */
        @Override
        public void saveLog(PropertyDTO propertyDTO, CrawLogBO crawLogBO) {
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
            long now = DateTimeUtils.nowTimestamp();
            baseMapper.saveOrUpdate(new ClcMissingReport().setPropertyId(propertyDTO.getId())
                    .setTheDate(LocalDate.parse(crawLogBO.getTheDate(), dateTimeFormatter)
                            .atStartOfDay(ZoneId.of(propertyDTO.getTimezone())).toEpochSecond())
                    .setReportType(crawLogBO.getReportType()).setCreatedBy(BaseConstant.ZERO).setCreatedTime(now)
                    .setStatus(crawLogBO.getIsSuccess() ? ReportImportResultCons.IMPORT_RESULT_SUCCESS : ReportImportResultCons.IMPORT_RESULT_FAIL)
                    .setResolvedBy(0).setResolvedTime(crawLogBO.getIsSuccess() ? now : 0L));
        }

        /**
         * 插入进行中导入记录
         *
         * @param propertyId 资产 id
         * @param theDate    指定日期
         * @param url        文件路径
         * @param userId     操作人
         * @return 导入记录
         * @author yyt
         * @date 2023/10/31
         */
        @Override
        public ClcMissingReport saveInProgressRecord(Integer propertyId, Long theDate, String url, int userId) {
            ClcMissingReport importLog = new ClcMissingReport()
                    .setPropertyId(propertyId)
                    .setTheDate(theDate)
                    .setReportType(ClcMissingReport.REPORT_TYPE_ARRIVALS)
                    .setStatus(ReportImportResultCons.IMPORT_IN_PROGRESS)
                    .setUrl(url)
                    .setCreatedBy(userId)
                    .setCreatedTime(DateTimeUtils.nowTimestamp()).setResolvedBy(BaseConstant.ZERO).setResolvedTime(BaseConstant.LONG_ZERO);
            baseMapper.saveOrUpdate(importLog);
            return importLog;
        }

        /**
         * 新增 / 修改
         *
         * @param clcMissingReport clcMissingReport
         * @author yyt
         * @date 2024/6/6
         **/
        @Override
        public void saveOrUpdateRewrite(ClcMissingReport clcMissingReport) {
            baseMapper.saveOrUpdate(clcMissingReport);
        }

        /**
         * 分页查询数据
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return 导入记录
         * @author yyt
         * @date 2024/11/19
         **/
        @Override
        public Page<ClcMissingReport> listPage(Integer propertyId, ArrivalImportLogQuery query) {
            return page(new Page<>(query.getPage(), query.getSize()), packageSql(propertyId, query).orderByDesc(ClcMissingReport::getId));
        }

        /**
         * 查询数据
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return 导入记录
         * @author yyt
         * @date 2024/11/19
         **/
        @Override
        public List<ClcMissingReport> list(Integer propertyId, ArrivalImportLogQuery query) {
            return list(packageSql(propertyId, query));
        }

        /**
         * 封装查询 sql
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return sql
         * @author yyt
         * @date 2024/11/19
         **/
        private LambdaQueryWrapper<ClcMissingReport> packageSql(Integer propertyId, ArrivalImportLogQuery query) {
            return new QueryWrapper<ClcMissingReport>().lambda().eq(ClcMissingReport::getPropertyId, propertyId)
                    .eq(ClcMissingReport::getReportType, ClcMissingReport.REPORT_TYPE_ARRIVALS)
                    .in(CollectionUtils.isNotEmpty(query.getStatus()), ClcMissingReport::getStatus, query.getStatus())
                    .between(Objects.nonNull(query.getStartDataDate()) && Objects.nonNull(query.getEndDataDate()),
                            ClcMissingReport::getTheDate, query.getStartDataDate(), query.getEndDataDate())
                    .nested(Objects.nonNull(query.getStartOperationDate()) && Objects.nonNull(query.getEndOperationDate()),
                            sql -> {
                                sql.between(ClcMissingReport::getCreatedTime, query.getStartOperationDate(), query.getEndOperationDate())
                                        .or()
                                        .between(ClcMissingReport::getResolvedTime, query.getStartOperationDate(), query.getEndOperationDate());
                            });
        }

        /**
         * 获取导入 arrival 报告记录
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return arrival 报告记录
         * @author yyt
         * @date 2026/1/19
         **/
        @Override
        public ListPagedDTO<ImportArrivalLogDTO> listImportArrivalLogDTO(Integer tenantId, Integer propertyId, ArrivalImportLogQuery query) {
            long total;
            List<ClcMissingReport> records;
            if (Objects.nonNull(query.getPage()) && Objects.nonNull(query.getSize())) {
                Page<ClcMissingReport> importLogPage = listPage(propertyId, query);
                records = importLogPage.getRecords();
                total = importLogPage.getTotal();
            } else {
                records = list(propertyId, query);
                total = records.size();
            }

            Map<Integer, String> userNameById = summaryUserName(tenantId, records);

            return new ListPagedDTO<>(total, beanMapper.mapList(records, ImportArrivalLogDTO.class,
                    (importLog, importLogDTO) -> {
                        importLogDTO.setCreatedName(userNameById.get(importLog.getCreatedBy()));
                        importLogDTO.setResolvedName(userNameById.get(importLog.getResolvedBy()));
                    }));
        }

        /**
         * 获取操作用户名称
         *
         * @param tenantId 租户 id
         * @param logs     日志
         * @return 用户名称
         * @author yyt
         * @date 2026/1/19
         **/
        private Map<Integer, String> summaryUserName(Integer tenantId, List<ClcMissingReport> logs) {
            if (CollectionUtils.isNotEmpty(logs)) {
                Set<Integer> userIds = Sets.newHashSetWithExpectedSize(logs.size());
                for (ClcMissingReport item : logs) {
                    if (item.getCreatedBy() > 0) {
                        userIds.add(item.getCreatedBy());
                    }
                    if (item.getResolvedBy() > 0) {
                        userIds.add(item.getResolvedBy());
                    }
                }
                if (CollectionUtils.isNotEmpty(userIds)) {
                    return userInternalService.getNameMapByIds(tenantId, userIds);
                }
            }
            return Collections.emptyMap();
        }

        /**
         * 更新状态
         *
         * @param id     记录 id
         * @param status 状态
         * @param userId 操作人
         * @author yyt
         * @date 2026/1/27
         **/
        @Override
        public void updateStatus(long id, int status, int userId) {
            updateById(new ClcMissingReport()
                    .setId(id)
                    .setStatus(status)
                    .setResolvedBy(userId)
                    .setResolvedTime(DateTimeUtils.nowTimestamp()));
        }

    }