wlzboy
9 小时以前 5f2ee03958a1a16dc27195c76ea7cffb422c95d1
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleMileageStatsServiceImpl.java
@@ -4,9 +4,15 @@
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.ruoyi.common.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -110,21 +116,34 @@
            
            calendar.add(Calendar.DAY_OF_MONTH, 1);
            Date dayEnd = calendar.getTime();
            String dayStartStr=DateUtils.formatDate(dayStart, DateUtils.YYYY_MM_DD_HH_MM_SS);
            String dayEndStr=DateUtils.formatDate(dayEnd, DateUtils.YYYY_MM_DD_HH_MM_SS);
            // 2. 查询车辆在该日期的GPS数据(按时间排序)
            List<VehicleGps> gpsList = vehicleGpsMapper.selectGpsDataByTimeRange(vehicleId, dayStart, dayEnd);
            List<VehicleGps> gpsList = vehicleGpsMapper.selectGpsDataByTimeRange(vehicleId, dayStartStr, dayEndStr);
            
            if (gpsList == null || gpsList.isEmpty()) {
                logger.info("车辆ID: {} 在日期: {} 无GPS数据", vehicleId, statDate);
                logger.info("---> 车辆ID:{} 在日期:{} 无GPS数据", vehicleId, statDate);
                return null;
            }
//            logger.info("---> 车辆ID:{} GPS数据条数:{}", vehicleId, gpsList.size());
            // 3. 查询车辆在该日期的任务时间区间
            List<TaskTimeInterval> taskIntervals = vehicleMileageStatsMapper.selectTaskTimeIntervals(vehicleId, dayStart, dayEnd);
            // 4. 计算里程
            MileageCalculation calculation = calculateMileage(gpsList, taskIntervals);
//            logger.info("---> 车辆ID:{} 任务时间区间数:{}", vehicleId, taskIntervals.size());
           List<VehicleGpsSegmentMileage> mileages = this.getTaskDistanceMileage(vehicleId, dayStart, dayEnd).stream().filter(e -> e.getSegmentDistance() != null && e.getSegmentDistance().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
           int totalGpsPoints = mileages.stream()
                .filter(segment -> segment.getGpsPointCount() != null)
                .mapToInt(VehicleGpsSegmentMileage::getGpsPointCount)
                .sum();
//           logger.info("---> 车辆ID:{} 任务时间:{} 里程时间:{}", vehicleId,
//                   taskIntervals.stream().map(e->"开始时间:"+DateUtils.formatDate(e.getStartTime())+",结束时间:"+DateUtils.formatDate(e.getEndTime())).collect(Collectors.joining()),
//                   mileages.stream().map(e->"开始时间:"+DateUtils.formatDate(e.getSegmentStartTime())+",结束时间:"+DateUtils.formatDate(e.getSegmentEndTime())).collect(Collectors.joining()));
            BigDecimal taskDistance = getTaskDistance(taskIntervals,mileages);
//            logger.info("---> 车辆ID:{} 任务总里程:{}", vehicleId, taskDistance);
            MileageCalculation calculation = calculateMileage(gpsList, taskDistance);
//            logger.info("---> 计算出车辆当天总里程,车辆ID:{},总里程:{},任务里程:{}",vehicleId,calculation.totalMileage,calculation.taskMileage);
            // 5. 查询或创建统计记录
            VehicleMileageStats stats = vehicleMileageStatsMapper.selectByVehicleIdAndDate(vehicleId, statDate);
            boolean isNew = (stats == null);
@@ -153,8 +172,11 @@
            stats.setTaskMileage(calculation.taskMileage);
            stats.setNonTaskMileage(calculation.nonTaskMileage);
            stats.setTaskRatio(calculation.taskRatio);
            stats.setGpsPointCount(gpsList.size());
            stats.setGpsPointCount(totalGpsPoints);
            stats.setTaskCount(taskIntervals == null ? 0 : taskIntervals.size());
//            logger.info("车辆ID: {} 日期: {} 里程统计完成 - 总里程: {}km, 任务里程: {}km, 非任务里程: {}km, 占比: {}",
//                    vehicleId, statDate, calculation.totalMileage, taskDistance, calculation.nonTaskMileage, calculation.taskRatio);
            
            // 7. 保存到数据库
            if (isNew) {
@@ -162,15 +184,17 @@
            } else {
                vehicleMileageStatsMapper.updateVehicleMileageStats(stats);
            }
            logger.info("车辆ID: {} 日期: {} 里程统计完成 - 总里程: {}km, 任务里程: {}km, 非任务里程: {}km, 占比: {}",
                       vehicleId, statDate, calculation.totalMileage, calculation.taskMileage,
                       calculation.nonTaskMileage, calculation.taskRatio);
//            if (taskIntervals != null) {
//                logger.info("---> 同步里程完成,车辆ID: {} 日期: {} 里程统计完成 - 总里程: {}km,任务数量:{} 任务里程: {}km, 非任务里程: {}km, 占比: {}",
//                           vehicleId, statDate, calculation.totalMileage,taskIntervals.size(), calculation.taskMileage,
//                           calculation.nonTaskMileage, calculation.taskRatio);
//            }
            return stats;
            
        } catch (Exception e) {
            logger.error("计算车辆里程统计失败 - 车辆ID: {}, 日期: {}", vehicleId, statDate, e);
            logger.error("---> 计算车辆里程统计失败 - 车辆ID: {}, 日期: {}", vehicleId, statDate, e);
            throw new RuntimeException("计算里程统计失败: " + e.getMessage());
        }
    }
@@ -186,9 +210,9 @@
            calendar.setTime(statDate);
            calendar.add(Calendar.DAY_OF_MONTH, -7);
            Date startTime = calendar.getTime();
            String startTimeStr = DateUtils.formatDate(startTime,DateUtils.YYYY_MM_DD_HH_MM_SS);
            // 查询所有活跃车辆
            List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds(startTime);
            List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds(startTimeStr);
            
            if (vehicleIds == null || vehicleIds.isEmpty()) {
                logger.info("没有找到活跃车辆");
@@ -205,7 +229,7 @@
                }
            }
            
            logger.info("批量里程统计完成 - 日期: {}, 总车辆数: {}, 成功: {}", statDate, vehicleIds.size(), successCount);
//            logger.info("批量里程统计完成 - 日期: {}, 总车辆数: {}, 成功: {}", statDate, vehicleIds.size(), successCount);
            return successCount;
            
        } catch (Exception e) {
@@ -214,10 +238,23 @@
        }
    }
    private BigDecimal calculateTotalMileage(List<VehicleGpsSegmentMileage> mileages) {
        if (mileages == null || mileages.isEmpty()) {
            return BigDecimal.ZERO;
        }
        return mileages.stream()
            .filter(mileage -> mileage.getSegmentDistance() != null) // 过滤掉距离为null的分段
            .map(mileage -> mileage.getSegmentDistance())
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    /**
     * 计算里程的内部方法
     */
    private MileageCalculation calculateMileage(List<VehicleGps> gpsList, List<TaskTimeInterval> taskIntervals) {
    private MileageCalculation calculateMileage(List<VehicleGps> gpsList, BigDecimal taskDistance) {
        MileageCalculation result = new MileageCalculation();
        
        // 遍历GPS点,计算相邻点之间的距离
@@ -232,23 +269,12 @@
                p2.getLatitude().doubleValue(), 
                p2.getLongitude().doubleValue()
            );
            // 获取这段距离的时间区间
            Date segmentStart = parseDateTime(p1.getCollectTime());
            Date segmentEnd = parseDateTime(p2.getCollectTime());
            // 计算这段距离在任务时段的占比
            double taskRatio = calculateTaskOverlapRatio(segmentStart, segmentEnd, taskIntervals);
            // 分摊里程
            double taskDistance = distance * taskRatio;
            double nonTaskDistance = distance * (1 - taskRatio);
            result.totalMileage = result.totalMileage.add(BigDecimal.valueOf(distance));
            result.taskMileage = result.taskMileage.add(BigDecimal.valueOf(taskDistance));
            result.nonTaskMileage = result.nonTaskMileage.add(BigDecimal.valueOf(nonTaskDistance));
        }
        result.taskMileage=taskDistance;
        result.nonTaskMileage=result.totalMileage.subtract(result.taskMileage);
        // 计算任务里程占比
        if (result.totalMileage.compareTo(BigDecimal.ZERO) > 0) {
            result.taskRatio = result.taskMileage.divide(result.totalMileage, 4, RoundingMode.HALF_UP);
@@ -297,32 +323,95 @@
        return EARTH_RADIUS_KM * c;
    }
    //计算任务时间段内的里程,应该拿到该任务在工作时间段里的分段距离然后相加
    private List<VehicleGpsSegmentMileage> getTaskDistanceMileage(Long vehicleId, Date segmentStart, Date segmentEnd) {
        List<VehicleGpsSegmentMileage> mileages = segmentMileageMapper.selectSegmentsByDateRange(vehicleId, segmentStart, segmentEnd);
        return mileages != null ? mileages : new ArrayList<>();
    }
    /**
     * 计算时间段与任务时段的重叠比例
     * 计算在任务时间段内的实际任务里程
     * 通过检查分段里程数据是否与任务时间段重叠,累加这些重叠分段的实际里程
     *
     * @param taskTimeIntervals 任务时间段列表
     * @param segmentMileages 分段里程数据列表
     * @return 在任务时间段内的总里程
     */
    private double calculateTaskOverlapRatio(Date segmentStart, Date segmentEnd, List<TaskTimeInterval> taskIntervals) {
        if (taskIntervals == null || taskIntervals.isEmpty()) {
            return 0.0;
    private BigDecimal getTaskDistance(List<TaskTimeInterval> taskTimeIntervals, List<VehicleGpsSegmentMileage> segmentMileages) {
        if (taskTimeIntervals == null || taskTimeIntervals.isEmpty() ||
            segmentMileages == null || segmentMileages.isEmpty()) {
            return BigDecimal.ZERO;
        }
        
        long segmentDuration = segmentEnd.getTime() - segmentStart.getTime();
        if (segmentDuration <= 0) {
            return 0.0;
        }
        BigDecimal totalTaskDistance = BigDecimal.ZERO;
        
        long totalOverlap = 0;
        for (TaskTimeInterval task : taskIntervals) {
            // 计算重叠时间
            long overlapStart = Math.max(segmentStart.getTime(), task.getStartTime().getTime());
            long overlapEnd = Math.min(segmentEnd.getTime(), task.getEndTime().getTime());
        // 遍历所有分段里程数据
        for (VehicleGpsSegmentMileage segment : segmentMileages) {
            // 只处理有关联任务ID且有距离数据的分段
//            if (segment.getTaskId() == null || segment.getSegmentDistance() == null) {
//                continue;
//            }
            // 检查该分段是否与任何任务时间段重叠
            Date segmentStart = segment.getSegmentStartTime();
            Date segmentEnd = segment.getSegmentEndTime();
            
            if (overlapEnd > overlapStart) {
                totalOverlap += (overlapEnd - overlapStart);
            boolean isInTaskPeriod = false;
            for (TaskTimeInterval taskInterval : taskTimeIntervals) {
                // 计算时间重叠 --任务时间段
                long overlapStart = Math.max(segmentStart.getTime(), taskInterval.getStartTime().getTime());
                long overlapEnd = Math.min(segmentEnd.getTime(), taskInterval.getEndTime().getTime());
                // 如果有时间重叠,则该分段属于任务里程
                if (overlapEnd > overlapStart) {
                    isInTaskPeriod = true;
                    break;
                }
            }
            // 如果分段在任务时间段内,则累加其里程
            if (isInTaskPeriod) {
                totalTaskDistance = totalTaskDistance.add(segment.getSegmentDistance());
            }
        }
        
        return (double) totalOverlap / segmentDuration;
        return totalTaskDistance;
    }
    /**
     * 计算指定时间段内的实际任务里程
     * 通过查找与该时间段重叠的任务,并累加这些任务在该时间段内的实际里程
     */
    private double calculateActualTaskMileage(Date segmentStart, Date segmentEnd, List<VehicleGpsSegmentMileage> segmentMileages) {
        if (segmentMileages == null || segmentMileages.isEmpty()) {
            return 0.0;
        }
        double totalTaskMileage = 0.0;
        // 遍历所有分段里程数据,找出与指定时间段重叠且有关联任务的分段
        for (VehicleGpsSegmentMileage segment : segmentMileages) {
            // 只处理有关联任务的分段
            if (segment.getTaskId() == null) {
                continue;
            }
            // 检查分段时间与指定时间段是否有重叠
            Date segStart = segment.getSegmentStartTime();
            Date segEnd = segment.getSegmentEndTime();
            // 计算重叠时间
            long overlapStart = Math.max(segmentStart.getTime(), segStart.getTime());
            long overlapEnd = Math.min(segmentEnd.getTime(), segEnd.getTime());
            // 如果有时间重叠,则将该分段的距离加入任务里程
            if (overlapEnd > overlapStart) {
                if (segment.getSegmentDistance() != null) {
                    totalTaskMileage += segment.getSegmentDistance().doubleValue();
                }
            }
        }
        return totalTaskMileage;
    }
    /**
@@ -381,59 +470,28 @@
                logger.info("车辆ID: {} 在日期: {} 无分段里程数据", vehicleId, statDate);
                return null;
            }
            // 3. 汇总里程数据
            BigDecimal totalMileage = BigDecimal.ZERO;
            int totalGpsPoints = 0;
            for (VehicleGpsSegmentMileage segment : segments) {
                if (segment.getSegmentDistance() != null) {
                    totalMileage = totalMileage.add(segment.getSegmentDistance());
                }
                if (segment.getGpsPointCount() != null) {
                    totalGpsPoints += segment.getGpsPointCount();
                }
            }
            // 4. 查询该日期的任务时间区间,计算任务里程和非任务里程
            List<TaskTimeInterval> taskIntervals = vehicleMileageStatsMapper.selectTaskTimeIntervals(vehicleId, dayStart, dayEnd);
            Integer taskCount = taskIntervals != null ? taskIntervals.size() : 0;
            logger.info("车辆ID: {} 在日期: {} 有 {} 个任务", vehicleId, statDate, taskCount);
            List<VehicleGpsSegmentMileage> mileages = this.getTaskDistanceMileage(vehicleId, dayStart, dayEnd).stream().filter(e -> e.getSegmentDistance() != null && e.getSegmentDistance().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
            logger.info("车辆ID: {} 在日期: {} 有 {} 个分段里程数据", vehicleId, statDate, mileages.size());
            Integer totalGpsPoints = mileages != null ? mileages.stream()
                .filter(segment -> segment.getGpsPointCount() != null)
                .mapToInt(VehicleGpsSegmentMileage::getGpsPointCount)
                .sum() : 0;
            BigDecimal taskDistance = getTaskDistance(taskIntervals, mileages);
            BigDecimal totalDistance = calculateTotalMileage(segments);
            BigDecimal nonTaskDistance = totalDistance.subtract(taskDistance);
            
            BigDecimal taskMileage = BigDecimal.ZERO;
            BigDecimal nonTaskMileage = BigDecimal.ZERO;
            for (VehicleGpsSegmentMileage segment : segments) {
                Date segStart = segment.getSegmentStartTime();
                Date segEnd = segment.getSegmentEndTime();
                BigDecimal segDistance = segment.getSegmentDistance() != null ? segment.getSegmentDistance() : BigDecimal.ZERO;
                // 计算该分段与任务时段的重叠比例
                double taskRatio = calculateTaskOverlapRatio(segStart, segEnd, taskIntervals);
                // 分摇里程
                BigDecimal taskDist = segDistance.multiply(BigDecimal.valueOf(taskRatio));
                BigDecimal nonTaskDist = segDistance.multiply(BigDecimal.valueOf(1 - taskRatio));
                taskMileage = taskMileage.add(taskDist);
                nonTaskMileage = nonTaskMileage.add(nonTaskDist);
            }
            // 计算任务里程占比
            // 防止除零错误
            BigDecimal taskRatio = BigDecimal.ZERO;
            if (totalMileage.compareTo(BigDecimal.ZERO) > 0) {
                taskRatio = taskMileage.divide(totalMileage, 4, RoundingMode.HALF_UP);
                // 数据校验:占比应在0-1之间,如果超出说明数据异常
                if (taskRatio.compareTo(BigDecimal.ONE) > 0) {
                    logger.warn("车辆ID: {} 日期: {} 任务里程占比异常: {} (任务里程:{}, 总里程:{}), 强制设为1.0",
                               vehicleId, statDate, taskRatio, taskMileage, totalMileage);
                    taskRatio = BigDecimal.ONE;
                } else if (taskRatio.compareTo(BigDecimal.ZERO) < 0) {
                    logger.warn("车辆ID: {} 日期: {} 任务里程占比为负: {}, 强制设为0",
                               vehicleId, statDate, taskRatio);
                    taskRatio = BigDecimal.ZERO;
                }
            if (totalDistance != null && totalDistance.compareTo(BigDecimal.ZERO) > 0) {
                taskRatio = taskDistance.divide(totalDistance, 4, RoundingMode.HALF_UP);
            }
            // 3. 汇总里程数据
            // 5. 查询或创建统计记录
            VehicleMileageStats stats = vehicleMileageStatsMapper.selectByVehicleIdAndDate(vehicleId, statDate);
            boolean isNew = (stats == null);
@@ -458,12 +516,12 @@
            }
            
            // 6. 设置统计数据
            stats.setTotalMileage(totalMileage.setScale(2, RoundingMode.HALF_UP));
            stats.setTaskMileage(taskMileage.setScale(2, RoundingMode.HALF_UP));
            stats.setNonTaskMileage(nonTaskMileage.setScale(2, RoundingMode.HALF_UP));
            stats.setTotalMileage(totalDistance.setScale(2, RoundingMode.HALF_UP));
            stats.setTaskMileage(taskDistance.setScale(2, RoundingMode.HALF_UP));
            stats.setNonTaskMileage(nonTaskDistance.setScale(2, RoundingMode.HALF_UP));
            stats.setTaskRatio(taskRatio);
            stats.setGpsPointCount(totalGpsPoints);
            stats.setTaskCount(taskIntervals == null ? 0 : taskIntervals.size());
            stats.setTaskCount(taskCount);
            stats.setSegmentCount(segments.size());
            stats.setDataSource("segment"); // 标记数据来源为分段汇总
            
@@ -474,8 +532,8 @@
                vehicleMileageStatsMapper.updateVehicleMileageStats(stats);
            }
            
            logger.info("车辆ID: {} 日期: {} 从分段汇总完成 - 总里程: {}km, 任务里程: {}km, 非任务里程: {}km, 分段数: {}",
                       vehicleId, statDate, totalMileage, taskMileage, nonTaskMileage, segments.size());
//            logger.info("车辆ID: {} 日期: {} 从分段汇总完成 - 总里程: {}km, 任务里程: {}km, 非任务里程: {}km, 分段数: {}",
//                       vehicleId, statDate, totalMileage, taskMileage, nonTaskMileage, segments.size());
            
            return stats;
            
@@ -496,9 +554,11 @@
            calendar.setTime(statDate);
            calendar.add(Calendar.DAY_OF_MONTH, -7);
            Date startTime = calendar.getTime();
            String startTimeStr = DateUtils.formatDate(startTime, DateUtils.YYYY_MM_DD_HH_MM_SS);
            
            // 查询所有活跃车辆
            List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds(startTime);
            List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds(startTimeStr);
            
            if (vehicleIds == null || vehicleIds.isEmpty()) {
                logger.info("没有找到活跃车辆");