doum
3 天以前 7ec3683c8e41460f4bb0bd3a6677198742313e2b
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwElectricalBizServiceImpl.java
@@ -64,6 +64,12 @@
    private static final int ELE_READ_TYPE_METER = 3;
    /** queryData 电表状态详情功能 ID */
    private static final String QUERY_DATA_FUNCTION_METER_STATUS = "253";
    /** DataRequest 单次最大条数(接口 limit 上限 1000) */
    private static final int QUERY_DATA_PAGE_SIZE = 500;
    /** 抄表/DataRequest 查询时间跨度上限(天) */
    private static final int MAX_METER_QUERY_DAYS = 7;
    /** 分页 total 合理上限,避免误把时间戳当作总条数 */
    private static final int QUERY_DATA_TOTAL_SANITY_MAX = 100_000;
    @Autowired
    private YwElectricalMapper ywElectricalMapper;
@@ -829,7 +835,7 @@
    @Override
    public void syncMeterDataScheduled() {
        try {
            syncMeterDataInternal();
            syncMeterDataInternal(null, null);
        } catch (Exception e) {
            log.warn("syncMeterDataScheduled failed", e);
        }
@@ -837,8 +843,36 @@
    @Override
    public String syncMeterDataFromPlatform() {
        MeterDataSyncStats stats = syncMeterDataInternal();
        MeterDataSyncStats stats = syncMeterDataInternal(null, null);
        return "抄表同步完成:新增【" + stats.addCount + "】条,跳过重复【" + stats.skipCount + "】条";
    }
    @Override
    public String syncMeterDataFromPlatform(String readTimeBegin, String readTimeEnd) {
        if (StringUtils.isBlank(readTimeBegin) || StringUtils.isBlank(readTimeEnd)) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "请选择抄表时间段");
        }
        String startTime = readTimeBegin.trim();
        String endTime = readTimeEnd.trim();
        validateManualSyncTimeRange(startTime, endTime);
        String expandedStart = expandStartByOneDay(startTime);
        MeterDataSyncStats stats = syncMeterDataInternal(expandedStart, endTime);
        return "抄表同步完成:新增【" + stats.addCount + "】条,跳过重复【" + stats.skipCount + "】条";
    }
    private String expandStartByOneDay(String startTime) {
        try {
            Date start = DateUtil.StringToDate(startTime, "yyyy-MM-dd HH:mm:ss");
            if (start == null) {
                return startTime;
            }
            Calendar cal = Calendar.getInstance();
            cal.setTime(start);
            cal.add(Calendar.DAY_OF_MONTH, -1);
            return DateUtil.formatDate(cal.getTime(), "yyyy-MM-dd HH:mm:ss");
        } catch (Exception e) {
            return startTime;
        }
    }
    private static class MeterDataSyncStats {
@@ -846,12 +880,14 @@
        private int skipCount;
    }
    private MeterDataSyncStats syncMeterDataInternal() {
    private MeterDataSyncStats syncMeterDataInternal(String startTime, String endTime) {
        MeterDataSyncStats stats = new MeterDataSyncStats();
        String startTime = resolveSyncStartTime();
        QueryDataRequest param = buildQueryDataRequest(startTime, DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"));
        log.info("sync meter data, start_time={}, end_time={}", startTime, param.getEnd_time());
        List<QueryDataInfoResponse> list = fetchQueryDataList(param);
        String resolvedStart = StringUtils.isNotBlank(startTime) ? startTime : resolveSyncStartTime();
        String resolvedEnd = StringUtils.isNotBlank(endTime) ? endTime : DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
        resolvedStart = capQueryStartTime(resolvedStart, resolvedEnd, MAX_METER_QUERY_DAYS);
        QueryDataRequest param = buildQueryDataRequest(resolvedStart, resolvedEnd);
        log.info("sync meter data, start_time={}, end_time={}", resolvedStart, param.getEnd_time());
        List<QueryDataInfoResponse> list = fetchAllQueryDataList(param);
        if (CollectionUtils.isEmpty(list)) {
            return stats;
        }
@@ -875,6 +911,51 @@
        return stats;
    }
    private void validateManualSyncTimeRange(String startTime, String endTime) {
        if (!startTime.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")
                || !endTime.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "抄表时间格式不正确");
        }
        Date start;
        Date end;
        try {
            start = DateUtil.StringToDate(startTime, "yyyy-MM-dd HH:mm:ss");
            end = DateUtil.StringToDate(endTime, "yyyy-MM-dd HH:mm:ss");
        } catch (Exception e) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "抄表时间格式不正确");
        }
        if (start == null || end == null || !start.before(end)) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "抄表开始时间必须早于结束时间");
        }
        long diffMs = end.getTime() - start.getTime();
        if (diffMs > (long) MAX_METER_QUERY_DAYS * 24 * 60 * 60 * 1000) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "抄表时间段不能超过" + MAX_METER_QUERY_DAYS + "天");
        }
    }
    private String capQueryStartTime(String startTime, String endTime, int maxDays) {
        if (StringUtils.isBlank(startTime) || StringUtils.isBlank(endTime)) {
            return startTime;
        }
        try {
            Date start = DateUtil.StringToDate(startTime, "yyyy-MM-dd HH:mm:ss");
            Date end = DateUtil.StringToDate(endTime, "yyyy-MM-dd HH:mm:ss");
            if (start == null || end == null || !start.before(end)) {
                return startTime;
            }
            long maxMs = (long) maxDays * 24 * 60 * 60 * 1000;
            if (end.getTime() - start.getTime() <= maxMs) {
                return startTime;
            }
            Calendar cal = Calendar.getInstance();
            cal.setTime(end);
            cal.add(Calendar.DAY_OF_MONTH, -maxDays);
            return DateUtil.formatDate(cal.getTime(), "yyyy-MM-dd HH:mm:ss");
        } catch (Exception e) {
            return startTime;
        }
    }
    /** 单表抄表后从第三方拉取最新数据入库,返回是否有新记录 */
    private boolean syncMeterDataForElectrical(YwElectrical e) {
        if (e == null || StringUtils.isBlank(e.getAddress())) {
@@ -887,7 +968,7 @@
                DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"));
        List<QueryDataInfoResponse> list;
        try {
            list = fetchQueryDataList(param);
            list = fetchAllQueryDataList(param);
        } catch (BusinessException ex) {
            log.warn("sync meter data for electricalId={} failed: {}", e.getId(), ex.getMessage());
            return false;
@@ -918,17 +999,66 @@
        param.setStart_time(startTime);
        param.setEnd_time(endTime);
        param.setOffset(0);
        param.setLimit(500);
        param.setLimit(QUERY_DATA_PAGE_SIZE);
        return param;
    }
    private List<QueryDataInfoResponse> fetchQueryDataList(QueryDataRequest param) {
        ElectronicDataResponse response = ElectronicToolUtil.queryDataRequest(param);
        if (!ElectronicToolUtil.isDataApiSuccess(response)) {
            throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(),
                    ElectronicToolUtil.dataApiErrorMessage(response, "抄表数据同步失败"));
    /** 按 DataRequest 分页 total 循环拉取全部抄表数据 */
    private List<QueryDataInfoResponse> fetchAllQueryDataList(QueryDataRequest param) {
        if (param == null) {
            return Collections.emptyList();
        }
        return parseQueryDataList(response);
        int limit = param.getLimit() > 0 ? param.getLimit() : QUERY_DATA_PAGE_SIZE;
        List<QueryDataInfoResponse> all = new ArrayList<>();
        int offset = 0;
        Integer total = null;
        int pageNo = 0;
        while (true) {
            pageNo++;
            param.setOffset(offset);
            param.setLimit(limit);
            ElectronicDataResponse response = ElectronicToolUtil.queryDataRequest(param);
            if (!ElectronicToolUtil.isDataApiSuccess(response)) {
                throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(),
                        ElectronicToolUtil.dataApiErrorMessage(response, "抄表数据同步失败"));
            }
            List<QueryDataInfoResponse> page = parseQueryDataList(response);
            if (!CollectionUtils.isEmpty(page)) {
                all.addAll(page);
            }
            if (total == null) {
                total = resolveQueryTotal(response);
            }
            log.info("sync meter data page={}, offset={}, pageSize={}, accumulated={}, total={}",
                    pageNo, offset, page.size(), all.size(), total);
            if (total != null && total > 0) {
                offset += limit;
                if (offset >= total) {
                    break;
                }
                continue;
            }
            if (CollectionUtils.isEmpty(page) || page.size() < limit) {
                break;
            }
            offset += limit;
            if (pageNo >= 200) {
                log.warn("sync meter data pagination exceeded safety page limit, accumulated={}", all.size());
                break;
            }
        }
        return all;
    }
    private Integer resolveQueryTotal(ElectronicDataResponse response) {
        if (response == null || response.getTotal() == null) {
            return null;
        }
        int total = response.getTotal();
        if (total <= 0 || total > QUERY_DATA_TOTAL_SANITY_MAX) {
            return null;
        }
        return total;
    }
    private List<QueryDataInfoResponse> parseQueryDataList(ElectronicDataResponse response) {