package com.ruoyi.system.service.impl;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.util.*;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSONArray;
|
import com.alibaba.fastjson2.JSONObject;
|
import com.ruoyi.common.config.TiandituMapConfig;
|
import com.ruoyi.common.core.redis.RedisCache;
|
import com.ruoyi.common.utils.http.HttpUtils;
|
import com.ruoyi.system.domain.VehicleGps;
|
import com.ruoyi.system.domain.VehicleGpsSegmentMileage;
|
import com.ruoyi.system.domain.SysTask;
|
import com.ruoyi.system.domain.VehicleInfo;
|
import com.ruoyi.system.mapper.VehicleGpsMapper;
|
import com.ruoyi.system.mapper.VehicleGpsSegmentMileageMapper;
|
import com.ruoyi.system.mapper.SysTaskMapper;
|
import com.ruoyi.system.mapper.VehicleInfoMapper;
|
import com.ruoyi.system.service.IVehicleGpsSegmentMileageService;
|
import com.ruoyi.system.service.IVehicleMileageStatsService;
|
import com.ruoyi.system.service.ISysConfigService;
|
|
/**
|
* 车辆GPS分段里程Service业务层处理
|
*/
|
@Service
|
public class VehicleGpsSegmentMileageServiceImpl implements IVehicleGpsSegmentMileageService {
|
|
private static final Logger logger = LoggerFactory.getLogger(VehicleGpsSegmentMileageServiceImpl.class);
|
|
/** 地球半径(公里) */
|
private static final double EARTH_RADIUS_KM = 6371.0;
|
|
/** 天地图批量路径规划API */
|
private static final String TIANDITU_ROUTE_API = "http://api.tianditu.gov.cn/drive";
|
|
@Autowired
|
private VehicleGpsSegmentMileageMapper segmentMileageMapper;
|
|
@Autowired
|
private VehicleGpsMapper vehicleGpsMapper;
|
|
@Autowired
|
private SysTaskMapper sysTaskMapper;
|
|
@Autowired
|
private TiandituMapConfig tiandituMapConfig;
|
|
@Autowired
|
private ISysConfigService configService;
|
|
@Autowired
|
private RedisCache redisCache;
|
|
@Autowired
|
private IVehicleMileageStatsService mileageStatsService;
|
|
@Autowired
|
private VehicleInfoMapper vehicleInfoMapper;
|
|
@Override
|
public VehicleGpsSegmentMileage selectVehicleGpsSegmentMileageById(Long segmentId) {
|
return segmentMileageMapper.selectVehicleGpsSegmentMileageById(segmentId);
|
}
|
|
@Override
|
public List<VehicleGpsSegmentMileage> selectVehicleGpsSegmentMileageList(VehicleGpsSegmentMileage vehicleGpsSegmentMileage) {
|
return segmentMileageMapper.selectVehicleGpsSegmentMileageList(vehicleGpsSegmentMileage);
|
}
|
|
@Override
|
public int insertVehicleGpsSegmentMileage(VehicleGpsSegmentMileage vehicleGpsSegmentMileage) {
|
return segmentMileageMapper.insertVehicleGpsSegmentMileage(vehicleGpsSegmentMileage);
|
}
|
|
@Override
|
public int updateVehicleGpsSegmentMileage(VehicleGpsSegmentMileage vehicleGpsSegmentMileage) {
|
return segmentMileageMapper.updateVehicleGpsSegmentMileage(vehicleGpsSegmentMileage);
|
}
|
|
@Override
|
public int deleteVehicleGpsSegmentMileageByIds(Long[] segmentIds) {
|
return segmentMileageMapper.deleteVehicleGpsSegmentMileageByIds(segmentIds);
|
}
|
|
@Override
|
public int deleteVehicleGpsSegmentMileageById(Long segmentId) {
|
return segmentMileageMapper.deleteVehicleGpsSegmentMileageById(segmentId);
|
}
|
|
@Override
|
public int batchCalculateSegmentMileage(Date startTime, Date endTime) {
|
try {
|
// 查询所有活跃车辆
|
List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds();
|
|
if (vehicleIds == null || vehicleIds.isEmpty()) {
|
logger.info("没有找到活跃车辆");
|
return 0;
|
}
|
|
int successCount = 0;
|
for (Long vehicleId : vehicleIds) {
|
try {
|
int segmentCount = calculateVehicleSegmentMileage(vehicleId, startTime, endTime);
|
if (segmentCount > 0) {
|
successCount++;
|
}
|
} catch (Exception e) {
|
logger.error("计算车辆 {} 的分段里程失败", vehicleId, e);
|
}
|
}
|
|
logger.info("批量分段里程计算完成 - 时间范围: {} 到 {}, 总车辆数: {}, 成功: {}",
|
startTime, endTime, vehicleIds.size(), successCount);
|
return successCount;
|
|
} catch (Exception e) {
|
logger.error("批量计算分段里程失败", e);
|
throw new RuntimeException("批量计算失败: " + e.getMessage());
|
}
|
}
|
|
@Override
|
public int compensateCalculation(int lookbackDays) {
|
try {
|
// 计算时间范围
|
Calendar cal = Calendar.getInstance();
|
Date endTime = cal.getTime();
|
cal.add(Calendar.DAY_OF_MONTH, -lookbackDays);
|
Date startTime = cal.getTime();
|
|
logger.info("开始补偿计算 - 回溯天数: {}, 时间范围: {} 到 {}", lookbackDays, startTime, endTime);
|
|
// 查询所有活跃车辆
|
List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds();
|
|
if (vehicleIds == null || vehicleIds.isEmpty()) {
|
logger.info("没有找到活跃车辆");
|
return 0;
|
}
|
|
int successCount = 0;
|
int totalUncalculated = 0;
|
|
for (Long vehicleId : vehicleIds) {
|
try {
|
// 查询该车辆未被计算的GPS数据
|
List<VehicleGps> uncalculatedGps = vehicleGpsMapper.selectUncalculatedGps(vehicleId, startTime, endTime);
|
|
if (uncalculatedGps == null || uncalculatedGps.isEmpty()) {
|
logger.debug("车辆 {} 没有未计算的GPS数据", vehicleId);
|
continue;
|
}
|
|
totalUncalculated += uncalculatedGps.size();
|
logger.info("车辆 {} 发现 {} 个未计算的GPS点,开始补偿计算...",
|
vehicleId, uncalculatedGps.size());
|
|
// 重新计算该车辆在该时间范围的分段里程
|
// 注意:这里会重新计算整个时间范围,确保边缘节点被正确处理
|
int segmentCount = calculateVehicleSegmentMileage(vehicleId, startTime, endTime);
|
|
if (segmentCount > 0) {
|
successCount++;
|
logger.info("车辆 {} 补偿计算完成,生成 {} 个分段记录", vehicleId, segmentCount);
|
}
|
} catch (Exception e) {
|
logger.error("车辆 {} 补偿计算失败", vehicleId, e);
|
}
|
}
|
|
logger.info("补偿计算完成 - 总车辆数: {}, 未计算GPS点数: {}, 成功车辆数: {}",
|
vehicleIds.size(), totalUncalculated, successCount);
|
return successCount;
|
|
} catch (Exception e) {
|
logger.error("补偿计算失败", e);
|
throw new RuntimeException("补偿计算失败: " + e.getMessage());
|
}
|
}
|
|
@Override
|
public int calculateVehicleSegmentMileage(Long vehicleId, Date startTime, Date endTime) {
|
try {
|
// 获取配置的时间间隔(分钟)
|
int segmentMinutes = configService.selectConfigByKey("gps.mileage.segment.minutes") != null
|
? Integer.parseInt(configService.selectConfigByKey("gps.mileage.segment.minutes"))
|
: 5;
|
|
// 获取计算方式配置
|
String calculateMethod = configService.selectConfigByKey("gps.mileage.calculate.method");
|
if (calculateMethod == null || calculateMethod.isEmpty()) {
|
calculateMethod = "tianditu";
|
}
|
|
// 获取是否跳过已计算GPS点的配置
|
String skipCalculatedConfig = configService.selectConfigByKey("gps.mileage.skip.calculated");
|
boolean skipCalculated = skipCalculatedConfig == null || "true".equalsIgnoreCase(skipCalculatedConfig);
|
|
// 查询车辆在时间范围内的GPS数据
|
List<VehicleGps> gpsList = vehicleGpsMapper.selectGpsDataByTimeRange(vehicleId, startTime, endTime);
|
|
if (gpsList == null || gpsList.isEmpty()) {
|
logger.debug("车辆ID: {} 在时间范围 {} 到 {} 内无GPS数据", vehicleId, startTime, endTime);
|
return 0;
|
}
|
|
logger.info("车辆ID: {} 查询到 {} 条GPS数据", vehicleId, gpsList.size());
|
|
// 按时间段分组GPS数据
|
Map<Date, List<VehicleGps>> segmentedData = segmentGpsDataByTime(gpsList, segmentMinutes);
|
|
int savedCount = 0;
|
VehicleGps previousSegmentLastPoint = null; // 记录上一个时间段的最后一个点
|
|
// 遍历每个时间段,计算里程
|
for (Map.Entry<Date, List<VehicleGps>> entry : segmentedData.entrySet()) {
|
Date segmentStartTime = entry.getKey();
|
List<VehicleGps> segmentGpsList = entry.getValue();
|
|
if (segmentGpsList.size() < 2) {
|
// 如果本段只有1个点,但有上一段的最后一个点,仍可计算跨段距离
|
if (segmentGpsList.size() == 1 && previousSegmentLastPoint != null) {
|
// 保留当前点作为下一段的前置点,但不创建记录
|
previousSegmentLastPoint = segmentGpsList.get(0);
|
}
|
continue; // 至少需要2个点才能计算距离
|
}
|
|
// 检查是否已存在该时间段的记录
|
VehicleGpsSegmentMileage existing = segmentMileageMapper.selectByVehicleIdAndTime(vehicleId, segmentStartTime);
|
if (existing != null) {
|
logger.debug("车辆 {} 时间段 {} 的分段里程已存在,跳过", vehicleId, segmentStartTime);
|
// 更新上一段最后一个点
|
previousSegmentLastPoint = segmentGpsList.get(segmentGpsList.size() - 1);
|
continue;
|
}
|
|
// 计算时间段的结束时间
|
Calendar cal = Calendar.getInstance();
|
cal.setTime(segmentStartTime);
|
cal.add(Calendar.MINUTE, segmentMinutes);
|
Date segmentEndTime = cal.getTime();
|
|
// 计算该时间段的里程(包括跨段距离)
|
BigDecimal distance = calculateSegmentDistanceWithGap(segmentGpsList, calculateMethod, previousSegmentLastPoint);
|
|
// 收集GPS ID列表(包括上一段的最后一个点,因为跨段间隙距离也用到了它)
|
List<Long> gpsIdList = new ArrayList<>();
|
// 如果有上一段的最后一个点,先添加它的ID
|
if (previousSegmentLastPoint != null && previousSegmentLastPoint.getGpsId() != null) {
|
gpsIdList.add(previousSegmentLastPoint.getGpsId());
|
}
|
// 再添加当前段的所有GPS点ID
|
for (VehicleGps gps : segmentGpsList) {
|
if (gps.getGpsId() != null) {
|
gpsIdList.add(gps.getGpsId());
|
}
|
}
|
String gpsIds = gpsIdList.stream()
|
.map(String::valueOf)
|
.collect(java.util.stream.Collectors.joining(","));
|
|
// 创建分段里程记录
|
VehicleGpsSegmentMileage segment = new VehicleGpsSegmentMileage();
|
segment.setVehicleId(vehicleId);
|
|
// 从GPS数据或车辆表获取车牌号
|
String vehicleNo = segmentGpsList.get(0).getVehicleNo();
|
if (vehicleNo == null || vehicleNo.trim().isEmpty()) {
|
// GPS数据中没有车牌号,从车辆表查询
|
VehicleInfo vehicleInfo = vehicleInfoMapper.selectVehicleInfoById(vehicleId);
|
if (vehicleInfo != null) {
|
vehicleNo = vehicleInfo.getVehicleNo();
|
}
|
}
|
segment.setVehicleNo(vehicleNo);
|
|
segment.setSegmentStartTime(segmentStartTime);
|
segment.setSegmentEndTime(segmentEndTime);
|
|
// 起点坐标
|
VehicleGps firstPoint = segmentGpsList.get(0);
|
segment.setStartLongitude(BigDecimal.valueOf(firstPoint.getLongitude()));
|
segment.setStartLatitude(BigDecimal.valueOf(firstPoint.getLatitude()));
|
|
// 终点坐标
|
VehicleGps lastPoint = segmentGpsList.get(segmentGpsList.size() - 1);
|
segment.setEndLongitude(BigDecimal.valueOf(lastPoint.getLongitude()));
|
segment.setEndLatitude(BigDecimal.valueOf(lastPoint.getLatitude()));
|
|
segment.setSegmentDistance(distance);
|
segment.setGpsPointCount(gpsIdList.size()); // GPS点数:包括边缘点 + 当前段的点
|
segment.setGpsIds(gpsIds); // 设置GPS ID列表
|
segment.setCalculateMethod(calculateMethod);
|
|
// 查询并关联正在执行的任务
|
associateActiveTask(segment, vehicleId, segmentStartTime, segmentEndTime);
|
|
// 保存到数据库
|
segmentMileageMapper.insertVehicleGpsSegmentMileage(segment);
|
|
// 更新上一段最后一个点,供下一段使用
|
previousSegmentLastPoint = segmentGpsList.get(segmentGpsList.size() - 1);
|
|
// 记录已计算的GPS点到状态表(如果开启了重复计算控制)
|
if (skipCalculated && segment.getSegmentId() != null) {
|
for (Long gpsId : gpsIdList) {
|
try {
|
segmentMileageMapper.insertGpsCalculated(gpsId, segment.getSegmentId(), vehicleId);
|
} catch (Exception e) {
|
// 忽略重复键异常,继续处理
|
logger.debug("记录GPS计算状态失败,可能已存在: gpsId={}", gpsId);
|
}
|
}
|
}
|
|
savedCount++;
|
|
logger.debug("车辆 {} 时间段 {} 到 {} 里程: {}km, GPS点数: {}, GPS IDs: {}",
|
vehicleId, segmentStartTime, segmentEndTime, distance, segmentGpsList.size(),
|
gpsIds.length() > 50 ? gpsIds.substring(0, 50) + "..." : gpsIds);
|
}
|
|
logger.info("车辆 {} 计算完成,保存了 {} 个时间段的里程数据", vehicleId, savedCount);
|
|
// 自动触发汇总生成每日统计(如果有数据被保存)
|
if (savedCount > 0) {
|
try {
|
// 获取涉及的日期范围,触发汇总
|
Set<Date> affectedDates = new HashSet<>();
|
Calendar cal = Calendar.getInstance();
|
|
for (Map.Entry<Date, List<VehicleGps>> entry : segmentedData.entrySet()) {
|
cal.setTime(entry.getKey());
|
cal.set(Calendar.HOUR_OF_DAY, 0);
|
cal.set(Calendar.MINUTE, 0);
|
cal.set(Calendar.SECOND, 0);
|
cal.set(Calendar.MILLISECOND, 0);
|
affectedDates.add(cal.getTime());
|
}
|
|
// 对每个涉及的日期,触发汇总
|
for (Date statDate : affectedDates) {
|
try {
|
mileageStatsService.aggregateFromSegmentMileage(vehicleId, statDate);
|
logger.info("车辆 {} 日期 {} 的统计数据已自动汇总生成", vehicleId, statDate);
|
} catch (Exception e) {
|
logger.error("车辆 {} 日期 {} 自动汇总统计失败", vehicleId, statDate, e);
|
}
|
}
|
} catch (Exception e) {
|
logger.error("触发自动汇总失败", e);
|
}
|
}
|
|
return savedCount;
|
|
} catch (Exception e) {
|
logger.error("计算车辆 {} 分段里程失败", vehicleId, e);
|
throw new RuntimeException("计算分段里程失败: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 将GPS数据按时间段分组
|
*/
|
private Map<Date, List<VehicleGps>> segmentGpsDataByTime(List<VehicleGps> gpsList, int segmentMinutes) {
|
Map<Date, List<VehicleGps>> segmentedData = new LinkedHashMap<>();
|
|
for (VehicleGps gps : gpsList) {
|
// 解析GPS采集时间
|
Date collectTime = parseDateTime(gps.getCollectTime());
|
|
// 计算该GPS点所属的时间段起始时间(向下取整到最近的时间段)
|
Date segmentStart = getSegmentStartTime(collectTime, segmentMinutes);
|
|
// 添加到对应时间段
|
segmentedData.computeIfAbsent(segmentStart, k -> new ArrayList<>()).add(gps);
|
}
|
|
return segmentedData;
|
}
|
|
/**
|
* 获取时间段的起始时间(向下取整)
|
*/
|
private Date getSegmentStartTime(Date time, int segmentMinutes) {
|
Calendar cal = Calendar.getInstance();
|
cal.setTime(time);
|
|
// 将分钟数向下取整到最近的分段
|
int minute = cal.get(Calendar.MINUTE);
|
int segmentIndex = minute / segmentMinutes;
|
int alignedMinute = segmentIndex * segmentMinutes;
|
|
cal.set(Calendar.MINUTE, alignedMinute);
|
cal.set(Calendar.SECOND, 0);
|
cal.set(Calendar.MILLISECOND, 0);
|
|
return cal.getTime();
|
}
|
|
/**
|
* 计算一个时间段内的总里程(包括与上一段的间隙距离)
|
* @param gpsList 当前时间段的GPS点列表
|
* @param calculateMethod 计算方式
|
* @param previousLastPoint 上一个时间段的最后一个点(可为null)
|
*/
|
private BigDecimal calculateSegmentDistanceWithGap(List<VehicleGps> gpsList, String calculateMethod, VehicleGps previousLastPoint) {
|
if (gpsList == null || gpsList.size() < 2) {
|
return BigDecimal.ZERO;
|
}
|
|
BigDecimal totalDistance = BigDecimal.ZERO;
|
|
// 1. 先计算跨段间隙距离(上一段最后一个点 -> 当前段第一个点)
|
if (previousLastPoint != null) {
|
VehicleGps currentFirstPoint = gpsList.get(0);
|
double gapDistance = calculateHaversineDistance(
|
previousLastPoint.getLatitude().doubleValue(),
|
previousLastPoint.getLongitude().doubleValue(),
|
currentFirstPoint.getLatitude().doubleValue(),
|
currentFirstPoint.getLongitude().doubleValue()
|
);
|
totalDistance = totalDistance.add(BigDecimal.valueOf(gapDistance));
|
|
logger.debug("跨段间隙距离: {}km (上一段末点 -> 当前段首点)",
|
String.format("%.3f", gapDistance));
|
}
|
|
// 2. 再计算当前段内部的距离
|
BigDecimal segmentInternalDistance;
|
if ("tianditu".equalsIgnoreCase(calculateMethod)) {
|
segmentInternalDistance = calculateDistanceByTianditu(gpsList);
|
} else {
|
segmentInternalDistance = calculateDistanceByHaversine(gpsList);
|
}
|
totalDistance = totalDistance.add(segmentInternalDistance);
|
|
return totalDistance.setScale(3, RoundingMode.HALF_UP);
|
}
|
|
/**
|
* 计算一个时间段内的总里程(仅段内距离)
|
*/
|
private BigDecimal calculateSegmentDistance(List<VehicleGps> gpsList, String calculateMethod) {
|
if (gpsList == null || gpsList.size() < 2) {
|
return BigDecimal.ZERO;
|
}
|
|
BigDecimal totalDistance = BigDecimal.ZERO;
|
|
if ("tianditu".equalsIgnoreCase(calculateMethod)) {
|
// 使用天地图API计算(批量计算更精确)
|
totalDistance = calculateDistanceByTianditu(gpsList);
|
} else {
|
// 使用Haversine公式计算(直线距离,更快但不够精确)
|
totalDistance = calculateDistanceByHaversine(gpsList);
|
}
|
|
return totalDistance.setScale(3, RoundingMode.HALF_UP);
|
}
|
|
/**
|
* 使用天地图API计算距离
|
*/
|
private BigDecimal calculateDistanceByTianditu(List<VehicleGps> gpsList) {
|
try {
|
// 天地图路径规划API有点数限制,如果点太多需要分批处理
|
int maxPointsPerRequest = 50; // 天地图API建议不超过50个点
|
BigDecimal totalDistance = BigDecimal.ZERO;
|
|
// 如果GPS点数较少,直接使用Haversine公式(避免频繁调用API)
|
if (gpsList.size() <= 3) {
|
return calculateDistanceByHaversine(gpsList);
|
}
|
|
// 分批处理
|
for (int i = 0; i < gpsList.size() - 1; i += maxPointsPerRequest) {
|
int endIndex = Math.min(i + maxPointsPerRequest, gpsList.size());
|
List<VehicleGps> batchList = gpsList.subList(i, endIndex);
|
|
BigDecimal batchDistance = calculateBatchDistanceByTianditu(batchList);
|
totalDistance = totalDistance.add(batchDistance);
|
}
|
|
return totalDistance;
|
|
} catch (Exception e) {
|
logger.warn("天地图API计算距离失败,降级使用Haversine公式: {}", e.getMessage());
|
return calculateDistanceByHaversine(gpsList);
|
}
|
}
|
|
/**
|
* 使用天地图API计算一批GPS点的距离
|
*/
|
private BigDecimal calculateBatchDistanceByTianditu(List<VehicleGps> gpsList) {
|
try {
|
// 简化处理:计算相邻点之间的直线距离总和
|
// 注:天地图的路径规划API主要用于导航,这里用简化的距离计算
|
BigDecimal totalDistance = BigDecimal.ZERO;
|
|
for (int i = 0; i < gpsList.size() - 1; i++) {
|
VehicleGps p1 = gpsList.get(i);
|
VehicleGps p2 = gpsList.get(i + 1);
|
|
double distance = calculateHaversineDistance(
|
p1.getLatitude().doubleValue(),
|
p1.getLongitude().doubleValue(),
|
p2.getLatitude().doubleValue(),
|
p2.getLongitude().doubleValue()
|
);
|
|
totalDistance = totalDistance.add(BigDecimal.valueOf(distance));
|
}
|
|
return totalDistance;
|
|
} catch (Exception e) {
|
logger.error("天地图批量距离计算失败", e);
|
throw e;
|
}
|
}
|
|
/**
|
* 使用Haversine公式计算距离
|
*/
|
private BigDecimal calculateDistanceByHaversine(List<VehicleGps> gpsList) {
|
BigDecimal totalDistance = BigDecimal.ZERO;
|
|
for (int i = 0; i < gpsList.size() - 1; i++) {
|
VehicleGps p1 = gpsList.get(i);
|
VehicleGps p2 = gpsList.get(i + 1);
|
|
double distance = calculateHaversineDistance(
|
p1.getLatitude().doubleValue(),
|
p1.getLongitude().doubleValue(),
|
p2.getLatitude().doubleValue(),
|
p2.getLongitude().doubleValue()
|
);
|
|
totalDistance = totalDistance.add(BigDecimal.valueOf(distance));
|
}
|
|
return totalDistance;
|
}
|
|
/**
|
* 使用Haversine公式计算两点之间的距离(公里)
|
*/
|
private double calculateHaversineDistance(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);
|
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 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());
|
} catch (Exception e) {
|
throw new RuntimeException("日期时间格式错误: " + dateTimeStr + ", 应为 yyyy-MM-dd HH:mm:ss", e);
|
}
|
}
|
|
/**
|
* 查询并关联车辆正在执行的任务
|
* @param segment 分段里程记录
|
* @param vehicleId 车辆ID
|
* @param segmentStartTime 时间段开始时间
|
* @param segmentEndTime 时间段结束时间
|
*/
|
private void associateActiveTask(VehicleGpsSegmentMileage segment, Long vehicleId,
|
Date segmentStartTime, Date segmentEndTime) {
|
try {
|
// 查询该车辆正在执行的任务列表
|
List<SysTask> activeTasks = sysTaskMapper.selectActiveTasksByVehicleId(vehicleId);
|
|
if (activeTasks == null || activeTasks.isEmpty()) {
|
logger.debug("车辆 {} 在时间段 {} - {} 没有正在执行的任务", vehicleId, segmentStartTime, segmentEndTime);
|
return;
|
}
|
|
// 遍历任务,查找与当前时间段有重叠的任务
|
for (SysTask task : activeTasks) {
|
// 获取任务的实际执行时间,如果没有实际时间则使用计划时间
|
Date taskStart = task.getActualStartTime() != null ? task.getActualStartTime() : task.getPlannedStartTime();
|
Date taskEnd = task.getActualEndTime() != null ? task.getActualEndTime() : task.getPlannedEndTime();
|
|
// 判断时间段是否有重叠
|
if (isTimeOverlap(segmentStartTime, segmentEndTime, taskStart, taskEnd)) {
|
// 关联任务ID和任务编号
|
segment.setTaskId(task.getTaskId());
|
segment.setTaskCode(task.getTaskCode());
|
|
logger.debug("车辆 {} 时间段 {} - {} 关联任务: taskId={}, taskCode={}",
|
vehicleId, segmentStartTime, segmentEndTime, task.getTaskId(), task.getTaskCode());
|
break; // 找到一个匹配的任务即可
|
}
|
}
|
|
} catch (Exception e) {
|
// 关联任务失败不影响主流程,只记录日志
|
logger.warn("关联车辆 {} 的任务信息失败", vehicleId, e);
|
}
|
}
|
|
/**
|
* 判断两个时间段是否有重叠
|
* @param start1 时间段1开始
|
* @param end1 时间段1结束
|
* @param start2 时间段2开始
|
* @param end2 时间段2结束
|
* @return true-有重叠, false-无重叠
|
*/
|
private boolean isTimeOverlap(Date start1, Date end1, Date start2, Date end2) {
|
// 任何时间为null,返回false
|
if (start1 == null || end1 == null || start2 == null || end2 == null) {
|
return false;
|
}
|
|
// 两个时间段有重叠的条件:
|
// start1 < end2 && end1 > start2
|
return start1.before(end2) && end1.after(start2);
|
}
|
}
|