| | |
| | | |
| | | import java.math.BigDecimal; |
| | | 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; |
| | | import org.springframework.stereotype.Service; |
| | | import com.ruoyi.system.domain.VehicleGps; |
| | | import com.ruoyi.system.domain.VehicleMileageStats; |
| | | import com.ruoyi.system.domain.VehicleGpsSegmentMileage; |
| | | import com.ruoyi.system.domain.TaskTimeInterval; |
| | | import com.ruoyi.system.domain.VehicleInfo; |
| | | import com.ruoyi.system.mapper.VehicleGpsMapper; |
| | | import com.ruoyi.system.mapper.VehicleMileageStatsMapper; |
| | | import com.ruoyi.system.mapper.VehicleGpsSegmentMileageMapper; |
| | | import com.ruoyi.system.mapper.VehicleInfoMapper; |
| | | import com.ruoyi.system.service.IVehicleMileageStatsService; |
| | | |
| | | /** |
| | |
| | | |
| | | @Autowired |
| | | private VehicleGpsMapper vehicleGpsMapper; |
| | | |
| | | @Autowired |
| | | private VehicleGpsSegmentMileageMapper segmentMileageMapper; |
| | | |
| | | @Autowired |
| | | private VehicleInfoMapper vehicleInfoMapper; |
| | | |
| | | /** |
| | | * 查询车辆里程统计 |
| | |
| | | |
| | | 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); |
| | |
| | | stats.setVehicleId(vehicleId); |
| | | stats.setStatDate(statDate); |
| | | |
| | | // 获取车牌号 |
| | | // 获取车牌号:优先从GPS数据,如果没有则从车辆表查询 |
| | | String vehicleNo = null; |
| | | if (!gpsList.isEmpty() && gpsList.get(0).getVehicleNo() != null) { |
| | | stats.setVehicleNo(gpsList.get(0).getVehicleNo()); |
| | | vehicleNo = gpsList.get(0).getVehicleNo(); |
| | | } |
| | | if (vehicleNo == null || vehicleNo.trim().isEmpty()) { |
| | | VehicleInfo vehicleInfo = vehicleInfoMapper.selectVehicleInfoById(vehicleId); |
| | | if (vehicleInfo != null) { |
| | | vehicleNo = vehicleInfo.getVehicleNo(); |
| | | } |
| | | } |
| | | stats.setVehicleNo(vehicleNo); |
| | | } |
| | | |
| | | // 6. 设置统计数据 |
| | |
| | | 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) { |
| | |
| | | } 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()); |
| | | } |
| | | } |
| | |
| | | @Override |
| | | public int batchCalculateMileageStats(Date statDate) { |
| | | try { |
| | | // 计算查询开始时间(7天前) |
| | | Calendar calendar = Calendar.getInstance(); |
| | | 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(); |
| | | List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds(startTimeStr); |
| | | |
| | | if (vehicleIds == null || vehicleIds.isEmpty()) { |
| | | logger.info("没有找到活跃车辆"); |
| | |
| | | } |
| | | } |
| | | |
| | | logger.info("批量里程统计完成 - 日期: {}, 总车辆数: {}, 成功: {}", statDate, vehicleIds.size(), successCount); |
| | | // logger.info("批量里程统计完成 - 日期: {}, 总车辆数: {}, 成功: {}", statDate, vehicleIds.size(), successCount); |
| | | return successCount; |
| | | |
| | | } catch (Exception e) { |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | 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点,计算相邻点之间的距离 |
| | |
| | | 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)); |
| | | } |
| | | |
| | | |
| | | 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); |
| | | |
| | | // 数据校验:占比应在0-1之间 |
| | | if (result.taskRatio.compareTo(BigDecimal.ONE) > 0) { |
| | | logger.warn("任务里程占比异常: {} (任务里程:{}, 总里程:{}), 强制设为1.0", |
| | | result.taskRatio, result.taskMileage, result.totalMileage); |
| | | result.taskRatio = BigDecimal.ONE; |
| | | } else if (result.taskRatio.compareTo(BigDecimal.ZERO) < 0) { |
| | | logger.warn("任务里程占比为负: {}, 强制设为0", result.taskRatio); |
| | | result.taskRatio = BigDecimal.ZERO; |
| | | } |
| | | } |
| | | |
| | | // 保留两位小数 |
| | |
| | | * 使用Haversine公式计算两个GPS坐标之间的距离(公里) |
| | | */ |
| | | private double calculateDistance(double lat1, double lon1, double lat2, double lon2) { |
| | | // 如果起点和终点经纬度相同,直接返回0,避免不必要的计算 |
| | | if (lat1 == lat2 && lon1 == lon2) { |
| | | return 0.0; |
| | | } |
| | | |
| | | // 将角度转换为弧度 |
| | | double dLat = Math.toRadians(lat2 - lat1); |
| | | double dLon = Math.toRadians(lon2 - lon1); |
| | |
| | | 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; |
| | | } |
| | | |
| | | /** |
| | |
| | | BigDecimal nonTaskMileage = BigDecimal.ZERO; |
| | | BigDecimal taskRatio = BigDecimal.ZERO; |
| | | } |
| | | |
| | | /** |
| | | * 解析日期时间字符串 |
| | | * |
| | | * @param dateTimeStr 日期时间字符串,格式:yyyy-MM-dd HH:mm:ss |
| | | * @return Date对象 |
| | | * @throws RuntimeException 如果解析失败 |
| | | */ |
| | | private Date parseDateTime(String dateTimeStr) { |
| | | if (dateTimeStr == null || dateTimeStr.trim().isEmpty()) { |
| | | throw new RuntimeException("日期时间字符串不能为空"); |
| | | } |
| | | |
| | | try { |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | sdf.setLenient(false); |
| | | return sdf.parse(dateTimeStr.trim()); |
| | | } catch (ParseException e) { |
| | | throw new RuntimeException("日期时间格式错误: " + dateTimeStr + ", 应为 yyyy-MM-dd HH:mm:ss", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 从分段里程数据汇总生成按日统计 |
| | | */ |
| | | @Override |
| | | public VehicleMileageStats aggregateFromSegmentMileage(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. 查询该日期范围内的所有分段里程数据 |
| | | List<VehicleGpsSegmentMileage> segments = segmentMileageMapper.selectSegmentsByDateRange(vehicleId, dayStart, dayEnd); |
| | | |
| | | if (segments == null || segments.isEmpty()) { |
| | | logger.info("车辆ID: {} 在日期: {} 无分段里程数据", vehicleId, statDate); |
| | | return null; |
| | | } |
| | | |
| | | 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 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); |
| | | |
| | | if (isNew) { |
| | | stats = new VehicleMileageStats(); |
| | | stats.setVehicleId(vehicleId); |
| | | stats.setStatDate(statDate); |
| | | |
| | | // 获取车牌号:优先从分段数据,如果没有则从车辆表查询 |
| | | String vehicleNo = null; |
| | | if (!segments.isEmpty() && segments.get(0).getVehicleNo() != null) { |
| | | vehicleNo = segments.get(0).getVehicleNo(); |
| | | } |
| | | if (vehicleNo == null || vehicleNo.trim().isEmpty()) { |
| | | VehicleInfo vehicleInfo = vehicleInfoMapper.selectVehicleInfoById(vehicleId); |
| | | if (vehicleInfo != null) { |
| | | vehicleNo = vehicleInfo.getVehicleNo(); |
| | | } |
| | | } |
| | | stats.setVehicleNo(vehicleNo); |
| | | } |
| | | |
| | | // 6. 设置统计数据 |
| | | 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(taskCount); |
| | | stats.setSegmentCount(segments.size()); |
| | | stats.setDataSource("segment"); // 标记数据来源为分段汇总 |
| | | |
| | | // 7. 保存到数据库 |
| | | if (isNew) { |
| | | vehicleMileageStatsMapper.insertVehicleMileageStats(stats); |
| | | } else { |
| | | vehicleMileageStatsMapper.updateVehicleMileageStats(stats); |
| | | } |
| | | |
| | | // logger.info("车辆ID: {} 日期: {} 从分段汇总完成 - 总里程: {}km, 任务里程: {}km, 非任务里程: {}km, 分段数: {}", |
| | | // vehicleId, statDate, totalMileage, taskMileage, nonTaskMileage, segments.size()); |
| | | |
| | | return stats; |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("从分段汇总里程统计失败 - 车辆ID: {}, 日期: {}", vehicleId, statDate, e); |
| | | throw new RuntimeException("汇总里程统计失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 批量从分段里程汇总生成按日统计 |
| | | */ |
| | | @Override |
| | | public int batchAggregateFromSegmentMileage(Date statDate) { |
| | | try { |
| | | // 计算查询开始时间(7天前) |
| | | Calendar calendar = Calendar.getInstance(); |
| | | 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(startTimeStr); |
| | | |
| | | if (vehicleIds == null || vehicleIds.isEmpty()) { |
| | | logger.info("没有找到活跃车辆"); |
| | | return 0; |
| | | } |
| | | |
| | | int successCount = 0; |
| | | for (Long vehicleId : vehicleIds) { |
| | | try { |
| | | aggregateFromSegmentMileage(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()); |
| | | } |
| | | } |
| | | } |