wlzboy
3 天以前 40a8157440e3b906da8f52e07d939d78c3f4c313
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsSegmentMileageServiceImpl.java
@@ -2,6 +2,7 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.format.DateTimeFormatter;
import java.util.*;
import com.ruoyi.common.utils.DateUtils;
@@ -40,6 +41,12 @@
    
    /** 天地图批量路径规划API */
    private static final String TIANDITU_ROUTE_API = "http://api.tianditu.gov.cn/drive";
    /** 线程安全的日期格式化器 */
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    /** 分批处理大小,避免一次性加载过多数据 */
    private static final int BATCH_SIZE = 10;
    
    @Autowired
    private VehicleGpsSegmentMileageMapper segmentMileageMapper;
@@ -111,47 +118,56 @@
                logger.warn("查询活跃车辆ID耗时过长: {}ms, 开始时间: {}, 建议检查 tb_vehicle_gps 表的索引(需要 vehicle_id 和 collect_time 组合索引)", 
                    queryTime, startTime);
            }
            logger.info("查询到 {} 辆活跃车辆,查询耗时: {}ms", vehicleIds != null ? vehicleIds.size() : 0, queryTime);
//            logger.info("查询到 {} 辆活跃车辆,查询耗时: {}ms", vehicleIds != null ? vehicleIds.size() : 0, queryTime);
            
            if (vehicleIds == null || vehicleIds.isEmpty()) {
                logger.info("没有找到活跃车辆");
//                logger.info("没有找到活跃车辆");
                return 0;
            }
            
            logger.info("找到 {} 辆活跃车辆,开始逐辆计算...", vehicleIds.size());
//            logger.info("找到 {} 辆活跃车辆,开始分批逐辆计算...", vehicleIds.size());
            
            int successCount = 0;
            int failedCount = 0;
            
            // 逐辆计算,包含错误处理和重试机制
            for (int i = 0; i < vehicleIds.size(); i++) {
                Long vehicleId = vehicleIds.get(i);
                try {
//                    logger.info("正在处理车辆 {} ({}/{})", vehicleId, i + 1, vehicleIds.size());
                    int segmentCount = calculateVehicleSegmentMileage(vehicleId, startTime, endTime);
                    if (segmentCount > 0) {
                        successCount++;
//                        logger.info("车辆 {} 计算成功,生成 {} 个分段记录", vehicleId, segmentCount);
                    } else {
//                        logger.debug("车辆 {} 无有新的GPS分段数据", vehicleId);
            // 分批处理,避免一次性处理过多数据导致内存溢出
            for (int batchStart = 0; batchStart < vehicleIds.size(); batchStart += BATCH_SIZE) {
                int batchEnd = Math.min(batchStart + BATCH_SIZE, vehicleIds.size());
                List<Long> batchVehicleIds = vehicleIds.subList(batchStart, batchEnd);
//                logger.info("处理批次 {}-{}/{}", batchStart + 1, batchEnd, vehicleIds.size());
                // 逐辆计算,包含错误处理和重试机制
                for (int i = 0; i < batchVehicleIds.size(); i++) {
                    Long vehicleId = batchVehicleIds.get(i);
                    int overallIndex = batchStart + i;
                    try {
                        int segmentCount = calculateVehicleSegmentMileage(vehicleId, startTime, endTime);
                        if (segmentCount > 0) {
                            successCount++;
                        }
                    } catch (Exception e) {
                        failedCount++;
                        logger.error("计算车辆 {} 的分段里程失败 ({}/{})", vehicleId, overallIndex + 1, vehicleIds.size(), e);
                        // 不中断整个批处理,继续处理下一辆车
                    }
                } catch (Exception e) {
                    failedCount++;
                    logger.error("计算车辆 {} 的分段里程失败 ({}/{})", vehicleId, i + 1, vehicleIds.size(), e);
                    
                    // 不中断整个批处理,继续处理下一辆车
                    // 每处理10辆车输出一次进度
                    if ((overallIndex + 1) % 10 == 0) {
//                        logger.info("批量计算进度: {}/{}, 成功: {}, 失败: {}",
//                                   overallIndex + 1, vehicleIds.size(), successCount, failedCount);
                    }
                }
                
                // 每处理10辆车输出一次进度
                if ((i + 1) % 10 == 0) {
                    logger.info("批量计算进度: {}/{}, 成功: {}, 失败: {}",
                               i + 1, vehicleIds.size(), successCount, failedCount);
                // 批次结束后,主动触发GC建议(不强制)
                if (batchEnd < vehicleIds.size()) {
                    System.gc();
//                    logger.debug("批次 {}-{} 处理完成,已建议JVM回收内存", batchStart + 1, batchEnd);
                }
            }
            
            logger.info("批量分段里程计算完成 - 时间范围: {} 到 {}, 总车辆数: {}, 成功: {}, 失败: {}",
                       startTime, endTime, vehicleIds.size(), successCount, failedCount);
//            logger.info("批量分段里程计算完成 - 时间范围: {} 到 {}, 总车辆数: {}, 成功: {}, 失败: {}",
//                       startTime, endTime, vehicleIds.size(), successCount, failedCount);
            return successCount;
            
        } catch (Exception e) {
@@ -169,14 +185,14 @@
            cal.add(Calendar.DAY_OF_MONTH, -lookbackDays);
            Date startTime = cal.getTime();
            
            logger.info("开始补偿计算 - 回溯天数: {}, 时间范围: {} 到 {}", lookbackDays, startTime, endTime);
//            logger.info("开始补偿计算 - 回溯天数: {}, 时间范围: {} 到 {}", lookbackDays, startTime, endTime);
            String startTimeStr=DateUtils.formatDate(startTime, DateUtils.YYYY_MM_DD_HH_MM_SS);
            // 查询所有活跃车辆
            List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds(startTimeStr);
            
            if (vehicleIds == null || vehicleIds.isEmpty()) {
                logger.info("没有找到活跃车辆");
//                logger.info("没有找到活跃车辆");
                return 0;
            }
            
@@ -185,19 +201,24 @@
            String endTimeStr=DateUtils.formatDate(endTime, DateUtils.YYYY_MM_DD_HH_MM_SS);
            for (Long vehicleId : vehicleIds) {
                try {
                    // 查询该车辆未被计算的GPS数据
                    List<VehicleGps> uncalculatedGps = vehicleGpsMapper.selectUncalculatedGps(vehicleId, startTimeStr, endTimeStr);
            // 分批处理车辆,避免内存溢出
            for (int batchStart = 0; batchStart < vehicleIds.size(); batchStart += BATCH_SIZE) {
                int batchEnd = Math.min(batchStart + BATCH_SIZE, vehicleIds.size());
                List<Long> batchVehicleIds = vehicleIds.subList(batchStart, batchEnd);
                for (Long vehicleId : batchVehicleIds) {
                    try {
                        // 查询该车辆未被计算的GPS数据
                        List<VehicleGps> uncalculatedGps = vehicleGpsMapper.selectUncalculatedGps(vehicleId, startTimeStr, endTimeStr);
                    
                    if (uncalculatedGps == null || uncalculatedGps.isEmpty()) {
                        logger.debug("车辆 {} 没有未计算的GPS数据", vehicleId);
//                        logger.debug("车辆 {} 没有未计算的GPS数据", vehicleId);
                        continue;
                    }
                    
                    totalUncalculated += uncalculatedGps.size();
                    logger.info("车辆 {} 发现 {} 个未计算的GPS点,开始补偿计算...",
                               vehicleId, uncalculatedGps.size());
//                    logger.info("车辆 {} 发现 {} 个未计算的GPS点,开始补偿计算...",
//                               vehicleId, uncalculatedGps.size());
                    
                    // 获取未计算GPS数据的时间范围
                    Date uncalculatedStartTime = parseDateTime(uncalculatedGps.get(0).getCollectTime());
@@ -207,16 +228,16 @@
                    Long lastCalculatedGpsId = segmentMileageMapper.selectLastCalculatedGpsId(vehicleId, uncalculatedStartTime);
                    
                    if (lastCalculatedGpsId != null) {
                        logger.info("车辆 {} 找到最后一个已处理的GPS点ID: {},将与未处理数据一起计算", vehicleId, lastCalculatedGpsId);
//                        logger.info("车辆 {} 找到最后一个已处理的GPS点ID: {},将与未处理数据一起计算", vehicleId, lastCalculatedGpsId);
                        
                        // 将最后一个已处理的GPS点加入列表前面,作为前置点
                        VehicleGps lastCalculatedGps = vehicleGpsMapper.selectVehicleGpsById(lastCalculatedGpsId);
                        if (lastCalculatedGps != null) {
                            uncalculatedGps.add(0, lastCalculatedGps); // 插入到列表最前面
                            logger.info("已将GPS点 {} 作为前置点加入计算列表", lastCalculatedGpsId);
//                            logger.info("已将GPS点 {} 作为前置点加入计算列表", lastCalculatedGpsId);
                        }
                    } else {
                        logger.info("车辆 {} 没有找到已处理的前置 GPS点,从第一个未处理点开始计算", vehicleId);
//                        logger.info("车辆 {} 没有找到已处理的前置 GPS点,从第一个未处理点开始计算", vehicleId);
                    }
                    
                    // 重新计算该车辆在该时间范围的分段里程
@@ -224,17 +245,24 @@
                    int segmentCount = calculateVehicleSegmentMileageWithGpsList(
                        vehicleId, uncalculatedGps, uncalculatedStartTime, uncalculatedEndTime);
                    
                    if (segmentCount > 0) {
                        successCount++;
                        logger.info("车辆 {} 补偿计算完成,生成 {} 个分段记录", vehicleId, segmentCount);
                        if (segmentCount > 0) {
                            successCount++;
//                            logger.info("车辆 {} 补偿计算完成,生成 {} 个分段记录", vehicleId, segmentCount);
                        }
                    } catch (Exception e) {
                        logger.error("车辆 {} 补偿计算失败", vehicleId, e);
                    }
                } catch (Exception e) {
                    logger.error("车辆 {} 补偿计算失败", vehicleId, e);
                }
                // 每批次结束后,主动建议GC(不需要显式清空引用,局部变量会自动释放)
                if (batchEnd < vehicleIds.size()) {
                    System.gc();
//                    logger.debug("补偿计算批次 {}-{} 完成,已建议JVM回收内存", batchStart + 1, batchEnd);
                }
            }
            
            logger.info("补偿计算完成 - 总车辆数: {}, 未计算GPS点数: {}, 成功车辆数: {}",
                       vehicleIds.size(), totalUncalculated, successCount);
//            logger.info("补偿计算完成 - 总车辆数: {}, 未计算GPS点数: {}, 成功车辆数: {}",
//                       vehicleIds.size(), totalUncalculated, successCount);
            return successCount;
            
        } catch (Exception e) {
@@ -252,7 +280,7 @@
            List<VehicleGps> gpsList = vehicleGpsMapper.selectGpsDataByTimeRange(vehicleId, startTimeStr, endTimeStr);
            
            if (gpsList == null || gpsList.isEmpty()) {
                logger.debug("车辆ID: {} 在时间范围 {} 到 {} 内无GPS数据", vehicleId, startTime, endTime);
//                logger.debug("车辆ID: {} 在时间范围 {} 到 {} 内无GPS数据", vehicleId, startTime, endTime);
                return 0;
            }
            
@@ -279,7 +307,7 @@
        try {
            // 验证输入数据
            if (gpsList == null || gpsList.isEmpty()) {
                logger.debug("车辆ID: {} 在时间范围 {} 到 {} 内无GPS数据", vehicleId, startTime, endTime);
//                logger.debug("车辆ID: {} 在时间范围 {} 到 {} 内无GPS数据", vehicleId, startTime, endTime);
                return 0;
            }
            
@@ -404,7 +432,7 @@
    private boolean isSegmentAlreadyCalculated(Long vehicleId, Date segmentStartTime, List<VehicleGps> segmentGpsList) {
        VehicleGpsSegmentMileage existing = segmentMileageMapper.selectByVehicleIdAndTime(vehicleId, segmentStartTime);
        if (existing != null) {
            logger.debug("车辆 {} 时间段 {} 的分段里程已存在,跳过", vehicleId, segmentStartTime);
//            logger.debug("车辆 {} 时间段 {} 的分段里程已存在,跳过", vehicleId, segmentStartTime);
            return true;
        }
        return false;
@@ -470,7 +498,8 @@
     * 收集GPS ID列表(包括前置点)
     */
    private List<Long> collectGpsIds(List<VehicleGps> segmentGpsList, VehicleGps previousSegmentLastPoint) {
        List<Long> gpsIdList = new ArrayList<>();
        // 预分配合理容量,减少扩容开销
        List<Long> gpsIdList = new ArrayList<>(segmentGpsList.size() + 1);
        
        // 如果有上一段的最后一个点,先添加它的ID(用于计算跨段距离)
        if (previousSegmentLastPoint != null && previousSegmentLastPoint.getGpsId() != null) {
@@ -822,16 +851,22 @@
    /**
     * 解析日期时间字符串
     * 使用ThreadLocal的SimpleDateFormat,避免每次创建新对象
     */
    private static final ThreadLocal<java.text.SimpleDateFormat> DATE_FORMAT_THREAD_LOCAL =
        ThreadLocal.withInitial(() -> {
            java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            sdf.setLenient(false);
            return sdf;
        });
    private Date parseDateTime(String dateTimeStr) {
        if (dateTimeStr == null || dateTimeStr.trim().isEmpty()) {
            throw new RuntimeException("日期时间字符串不能为空");
        }
        
        try {
            java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            sdf.setLenient(false);
            return sdf.parse(dateTimeStr.trim());
            return DATE_FORMAT_THREAD_LOCAL.get().parse(dateTimeStr.trim());
        } catch (Exception e) {
            throw new RuntimeException("日期时间格式错误: " + dateTimeStr + ", 应为 yyyy-MM-dd HH:mm:ss", e);
        }
@@ -853,7 +888,7 @@
            List<SysTask> activeTasks = sysTaskMapper.selectTaskByVehicleIdAndDate(vehicleId,segmentStartTimeStr,segmentEndTimeStr);
            
            if (activeTasks == null || activeTasks.isEmpty()) {
                logger.info("车辆 {} 在时间段 {} - {} 没有正在执行的任务", vehicleId, segmentStartTime, segmentEndTime);
//                logger.info("车辆 {} 在时间段 {} - {} 没有正在执行的任务", vehicleId, segmentStartTime, segmentEndTime);
                return;
            }