wlzboy
2025-11-15 caf56217dc2bf898b63b0e1f31a7098202c32825
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleMileageStatsServiceImpl.java
@@ -0,0 +1,296 @@
package com.ruoyi.system.service.impl;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.system.domain.VehicleGps;
import com.ruoyi.system.domain.VehicleMileageStats;
import com.ruoyi.system.domain.TaskTimeInterval;
import com.ruoyi.system.mapper.VehicleGpsMapper;
import com.ruoyi.system.mapper.VehicleMileageStatsMapper;
import com.ruoyi.system.service.IVehicleMileageStatsService;
/**
 * 车辆里程统计Service业务层处理
 */
@Service
public class VehicleMileageStatsServiceImpl implements IVehicleMileageStatsService {
    private static final Logger logger = LoggerFactory.getLogger(VehicleMileageStatsServiceImpl.class);
    /** 地球半径(公里) */
    private static final double EARTH_RADIUS_KM = 6371.0;
    @Autowired
    private VehicleMileageStatsMapper vehicleMileageStatsMapper;
    @Autowired
    private VehicleGpsMapper vehicleGpsMapper;
    /**
     * 查询车辆里程统计
     */
    @Override
    public VehicleMileageStats selectVehicleMileageStatsById(Long statsId) {
        return vehicleMileageStatsMapper.selectVehicleMileageStatsById(statsId);
    }
    /**
     * 查询车辆里程统计列表
     */
    @Override
    public List<VehicleMileageStats> selectVehicleMileageStatsList(VehicleMileageStats vehicleMileageStats) {
        return vehicleMileageStatsMapper.selectVehicleMileageStatsList(vehicleMileageStats);
    }
    /**
     * 新增车辆里程统计
     */
    @Override
    public int insertVehicleMileageStats(VehicleMileageStats vehicleMileageStats) {
        return vehicleMileageStatsMapper.insertVehicleMileageStats(vehicleMileageStats);
    }
    /**
     * 修改车辆里程统计
     */
    @Override
    public int updateVehicleMileageStats(VehicleMileageStats vehicleMileageStats) {
        return vehicleMileageStatsMapper.updateVehicleMileageStats(vehicleMileageStats);
    }
    /**
     * 批量删除车辆里程统计
     */
    @Override
    public int deleteVehicleMileageStatsByIds(Long[] statsIds) {
        return vehicleMileageStatsMapper.deleteVehicleMileageStatsByIds(statsIds);
    }
    /**
     * 删除车辆里程统计信息
     */
    @Override
    public int deleteVehicleMileageStatsById(Long statsId) {
        return vehicleMileageStatsMapper.deleteVehicleMileageStatsById(statsId);
    }
    /**
     * 计算并保存指定车辆指定日期的里程统计
     */
    @Override
    public VehicleMileageStats calculateAndSaveMileageStats(Long vehicleId, Date statDate) {
        try {
            // 1. 获取统计日期的开始和结束时间
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(statDate);
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            Date dayStart = calendar.getTime();
            calendar.add(Calendar.DAY_OF_MONTH, 1);
            Date dayEnd = calendar.getTime();
            // 2. 查询车辆在该日期的GPS数据(按时间排序)
            List<VehicleGps> gpsList = vehicleGpsMapper.selectGpsDataByTimeRange(vehicleId, dayStart, dayEnd);
            if (gpsList == null || gpsList.isEmpty()) {
                logger.info("车辆ID: {} 在日期: {} 无GPS数据", vehicleId, statDate);
                return null;
            }
            // 3. 查询车辆在该日期的任务时间区间
            List<TaskTimeInterval> taskIntervals = vehicleMileageStatsMapper.selectTaskTimeIntervals(vehicleId, dayStart, dayEnd);
            // 4. 计算里程
            MileageCalculation calculation = calculateMileage(gpsList, taskIntervals);
            // 5. 查询或创建统计记录
            VehicleMileageStats stats = vehicleMileageStatsMapper.selectByVehicleIdAndDate(vehicleId, statDate);
            boolean isNew = (stats == null);
            if (isNew) {
                stats = new VehicleMileageStats();
                stats.setVehicleId(vehicleId);
                stats.setStatDate(statDate);
                // 获取车牌号
                if (!gpsList.isEmpty() && gpsList.get(0).getVehicleNo() != null) {
                    stats.setVehicleNo(gpsList.get(0).getVehicleNo());
                }
            }
            // 6. 设置统计数据
            stats.setTotalMileage(calculation.totalMileage);
            stats.setTaskMileage(calculation.taskMileage);
            stats.setNonTaskMileage(calculation.nonTaskMileage);
            stats.setTaskRatio(calculation.taskRatio);
            stats.setGpsPointCount(gpsList.size());
            stats.setTaskCount(taskIntervals == null ? 0 : taskIntervals.size());
            // 7. 保存到数据库
            if (isNew) {
                vehicleMileageStatsMapper.insertVehicleMileageStats(stats);
            } else {
                vehicleMileageStatsMapper.updateVehicleMileageStats(stats);
            }
            logger.info("车辆ID: {} 日期: {} 里程统计完成 - 总里程: {}km, 任务里程: {}km, 非任务里程: {}km, 占比: {}",
                       vehicleId, statDate, calculation.totalMileage, calculation.taskMileage,
                       calculation.nonTaskMileage, calculation.taskRatio);
            return stats;
        } catch (Exception e) {
            logger.error("计算车辆里程统计失败 - 车辆ID: {}, 日期: {}", vehicleId, statDate, e);
            throw new RuntimeException("计算里程统计失败: " + e.getMessage());
        }
    }
    /**
     * 批量计算所有车辆指定日期的里程统计
     */
    @Override
    public int batchCalculateMileageStats(Date statDate) {
        try {
            // 查询所有活跃车辆
            List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds();
            if (vehicleIds == null || vehicleIds.isEmpty()) {
                logger.info("没有找到活跃车辆");
                return 0;
            }
            int successCount = 0;
            for (Long vehicleId : vehicleIds) {
                try {
                    calculateAndSaveMileageStats(vehicleId, statDate);
                    successCount++;
                } catch (Exception e) {
                    logger.error("计算车辆 {} 的里程统计失败", vehicleId, e);
                }
            }
            logger.info("批量里程统计完成 - 日期: {}, 总车辆数: {}, 成功: {}", statDate, vehicleIds.size(), successCount);
            return successCount;
        } catch (Exception e) {
            logger.error("批量计算里程统计失败 - 日期: {}", statDate, e);
            throw new RuntimeException("批量计算失败: " + e.getMessage());
        }
    }
    /**
     * 计算里程的内部方法
     */
    private MileageCalculation calculateMileage(List<VehicleGps> gpsList, List<TaskTimeInterval> taskIntervals) {
        MileageCalculation result = new MileageCalculation();
        // 遍历GPS点,计算相邻点之间的距离
        for (int i = 0; i < gpsList.size() - 1; i++) {
            VehicleGps p1 = gpsList.get(i);
            VehicleGps p2 = gpsList.get(i + 1);
            // 计算两点间距离(使用Haversine公式)
            double distance = calculateDistance(
                p1.getLatitude().doubleValue(),
                p1.getLongitude().doubleValue(),
                p2.getLatitude().doubleValue(),
                p2.getLongitude().doubleValue()
            );
            // 获取这段距离的时间区间
            Date segmentStart = p1.getCollectTime();
            Date segmentEnd = 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));
        }
        // 计算任务里程占比
        if (result.totalMileage.compareTo(BigDecimal.ZERO) > 0) {
            result.taskRatio = result.taskMileage.divide(result.totalMileage, 4, RoundingMode.HALF_UP);
        }
        // 保留两位小数
        result.totalMileage = result.totalMileage.setScale(2, RoundingMode.HALF_UP);
        result.taskMileage = result.taskMileage.setScale(2, RoundingMode.HALF_UP);
        result.nonTaskMileage = result.nonTaskMileage.setScale(2, RoundingMode.HALF_UP);
        return result;
    }
    /**
     * 使用Haversine公式计算两个GPS坐标之间的距离(公里)
     */
    private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        // 将角度转换为弧度
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);
        double rLat1 = Math.toRadians(lat1);
        double rLat2 = Math.toRadians(lat2);
        // Haversine公式
        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                   Math.cos(rLat1) * Math.cos(rLat2) *
                   Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return EARTH_RADIUS_KM * c;
    }
    /**
     * 计算时间段与任务时段的重叠比例
     */
    private double calculateTaskOverlapRatio(Date segmentStart, Date segmentEnd, List<TaskTimeInterval> taskIntervals) {
        if (taskIntervals == null || taskIntervals.isEmpty()) {
            return 0.0;
        }
        long segmentDuration = segmentEnd.getTime() - segmentStart.getTime();
        if (segmentDuration <= 0) {
            return 0.0;
        }
        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());
            if (overlapEnd > overlapStart) {
                totalOverlap += (overlapEnd - overlapStart);
            }
        }
        return (double) totalOverlap / segmentDuration;
    }
    /**
     * 里程计算结果内部类
     */
    private static class MileageCalculation {
        BigDecimal totalMileage = BigDecimal.ZERO;
        BigDecimal taskMileage = BigDecimal.ZERO;
        BigDecimal nonTaskMileage = BigDecimal.ZERO;
        BigDecimal taskRatio = BigDecimal.ZERO;
    }
}