编辑 | blame | 历史 | 原始文档

车辆GPS里程统计功能实现总结

一、功能实现清单

✅ 1. 数据库层

  • [x] vehicle_mileage_stats.sql - 创建里程统计表和明细表
  • [x] vehicle_mileage_stats_job.sql - 创建定时任务配置
  • [x] vehicle_mileage_stats_menu.sql - 创建菜单权限配置

✅ 2. 实体类(Domain)

  • [x] VehicleMileageStats.java - 里程统计实体
  • [x] TaskTimeInterval.java - 任务时间区间辅助类

✅ 3. 数据访问层(Mapper)

  • [x] VehicleMileageStatsMapper.java - 里程统计Mapper接口
  • [x] VehicleMileageStatsMapper.xml - MyBatis映射配置
  • [x] VehicleGpsMapper.java - 扩展GPS查询方法(新增2个方法)
  • [x] VehicleGpsMapper.xml - 扩展GPS查询SQL

✅ 4. 业务逻辑层(Service)

  • [x] IVehicleMileageStatsService.java - Service接口
  • [x] VehicleMileageStatsServiceImpl.java - Service实现(核心算法)

✅ 5. 控制层(Controller)

  • [x] VehicleMileageStatsController.java - REST API接口

✅ 6. 定时任务(Task)

  • [x] VehicleMileageStatsTask.java - 自动统计定时任务

✅ 7. 前端API

  • [x] mileageStats.js - 前端接口封装

✅ 8. 文档

  • [x] 车辆里程统计使用说明.md - 详细使用文档

二、核心技术实现

1. 里程计算算法

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);

    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) {
    long segmentDuration = segmentEnd.getTime() - segmentStart.getTime();
    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;
}

2. 任务时段定义

任务时段 = 从任务创建时间(sys_task.create_time)到任务完成时间(sys_task.actual_end_time

SQL查询:
sql select tv.task_id, t.create_time as start_time, IFNULL(t.actual_end_time, NOW()) as end_time from sys_task_vehicle tv inner join sys_task t on tv.task_id = t.task_id where tv.vehicle_id = #{vehicleId} and t.del_flag = '0' and t.actual_end_time is not null and t.create_time < #{endTime} and t.actual_end_time > #{startTime}

3. 统计数据缓存

  • 每日定时任务自动统计前一天的数据
  • 统计结果存储在 tb_vehicle_mileage_stats 表中
  • 支持重复计算(更新已有记录)
  • 唯一索引:uk_vehicle_date (vehicle_id, stat_date)

三、API接口说明

1. 查询统计列表

GET /system/mileageStats/list
参数:
  - vehicleId: 车辆ID(可选)
  - vehicleNo: 车牌号(可选)
  - statDate: 统计日期(可选)
  - beginStatDate: 开始日期(可选)
  - endStatDate: 结束日期(可选)
  - pageNum: 页码
  - pageSize: 每页数量

2. 手动计算单车辆里程

POST /system/mileageStats/calculate
参数:
  - vehicleId: 车辆ID(必填)
  - statDate: 统计日期,格式 yyyy-MM-dd(必填)
返回:VehicleMileageStats对象

3. 批量计算所有车辆里程

POST /system/mileageStats/batchCalculate
参数:
  - statDate: 统计日期,格式 yyyy-MM-dd(必填)
返回:成功统计的车辆数量

4. 导出统计数据

POST /system/mileageStats/export
参数:同查询列表接口
返回:Excel文件

四、定时任务配置

默认配置

  • 任务名称:车辆里程统计任务
  • Bean名称:vehicleMileageStatsTask
  • 方法调用:calculateYesterdayMileage
  • Cron表达式0 30 1 * * ?(每天凌晨1:30执行)
  • 执行策略:立即执行
  • 并发执行:禁止
  • 状态:启用

手动触发方式

在系统管理 -> 定时任务中,可以手动执行:

  1. 统计昨日数据
    vehicleMileageStatsTask.calculateYesterdayMileage

  2. 统计指定日期
    vehicleMileageStatsTask.calculateMileageByDate('2025-11-09')

五、数据表结构

tb_vehicle_mileage_stats

字段名 类型 说明
stats_id bigint(20) 统计ID,主键
vehicle_id bigint(20) 车辆ID
vehicle_no varchar(20) 车牌号
stat_date date 统计日期
total_mileage decimal(10,2) 总里程(公里)
task_mileage decimal(10,2) 任务时段里程(公里)
non_task_mileage decimal(10,2) 非任务时段里程(公里)
task_ratio decimal(5,4) 任务里程占比(0-1)
gps_point_count int(11) GPS点数量
task_count int(11) 任务数量
create_time datetime 创建时间
update_time datetime 更新时间

索引:
- PRIMARY KEY: stats_id
- UNIQUE KEY: uk_vehicle_date (vehicle_id, stat_date)
- KEY: idx_vehicle_id (vehicle_id)
- KEY: idx_stat_date (stat_date)

tb_vehicle_mileage_detail(可选)

用于存储里程计算明细,方便调试和追溯。

六、部署步骤

1. 执行数据库脚本(按顺序)

1. sql/vehicle_mileage_stats.sql
2. sql/vehicle_mileage_stats_job.sql
3. sql/vehicle_mileage_stats_menu.sql

2. 重启应用

代码文件已自动创建,重启应用即可生效。

3. 验证部署

  1. 登录系统,检查菜单是否显示"车辆里程统计"
  2. 进入系统管理 -> 定时任务,检查是否有"车辆里程统计任务"
  3. 手动执行定时任务或调用API接口测试功能

七、使用示例

示例1:手动计算某车辆昨日里程

import { calculateMileage } from '@/api/mileageStats'

calculateMileage(1001, '2025-11-09').then(response => {
  console.log('统计结果:', response.data)
  // 输出示例:
  // {
  //   vehicleNo: '粤A12345',
  //   statDate: '2025-11-09',
  //   totalMileage: 285.67,
  //   taskMileage: 198.43,
  //   nonTaskMileage: 87.24,
  //   taskRatio: 0.6948,
  //   gpsPointCount: 1205,
  //   taskCount: 8
  // }
})

示例2:批量计算所有车辆指定日期里程

import { batchCalculateMileage } from '@/api/mileageStats'

batchCalculateMileage('2025-11-09').then(response => {
  console.log(response.msg) // 输出:批量里程统计完成,成功统计 45 辆车
})

示例3:查询车辆里程统计报表

import { listMileageStats } from '@/api/mileageStats'

const query = {
  vehicleNo: '粤A12345',
  beginStatDate: '2025-11-01',
  endStatDate: '2025-11-09',
  pageNum: 1,
  pageSize: 10
}

listMileageStats(query).then(response => {
  console.log('统计列表:', response.rows)
})

八、注意事项

1. GPS数据要求

  • GPS采集间隔:建议30-60秒
  • 数据字段必填:vehicle_id, longitude, latitude, collect_time
  • 坐标系统:支持WGS84、GCJ02等常用坐标系

2. 任务数据要求

  • 任务表:sys_task
  • 车辆任务关联表:sys_task_vehicle
  • 必填字段:task_id, vehicle_id, create_time, actual_end_time

3. 性能优化建议

  • 定时任务避开业务高峰期(建议凌晨执行)
  • GPS原始数据定期清理(建议保留7-30天)
  • 统计数据定期归档(建议保留3-6个月)

4. 数据准确性

  • 里程计算基于GPS轨迹,精度受GPS信号质量影响
  • Haversine公式计算的是直线距离,实际道路距离可能更长
  • 可结合天地图路径规划API获取更准确的道路距离

九、扩展功能建议

1. 集成天地图路径距离(更准确)

当前使用Haversine公式计算直线距离,可升级为:
- 将GPS轨迹点发送到天地图路径规划API
- 获取实际道路距离
- 提高里程统计精度

2. 实时里程统计

  • 在GPS数据入库时实时计算
  • 使用Redis缓存当日累计里程
  • 凌晨定时任务仅做数据固化

3. 里程异常告警

  • 单日里程超过阈值告警
  • 长时间无GPS数据告警
  • 里程突变异常告警

4. 数据可视化

  • 每日里程趋势图
  • 任务里程占比饼图
  • 车辆里程排名榜

十、故障排查

问题1:定时任务未执行

  • 检查定时任务状态是否为"启用"
  • 检查Cron表达式是否正确
  • 查看定时任务日志

问题2:统计结果为0

  • 检查GPS数据是否存在
  • 检查GPS数据的collect_time字段是否正确
  • 检查任务数据是否存在

问题3:里程数据异常

  • 检查GPS坐标是否合法(经纬度范围)
  • 检查是否存在GPS漂移点
  • 启用明细表分析每段距离

查看日志

# Service层日志
grep "VehicleMileageStatsServiceImpl" logs/ruoyi-*.log

# 定时任务日志
grep "VehicleMileageStatsTask" logs/ruoyi-*.log

十一、总结

✅ 本功能已完整实现车辆GPS里程统计的所有需求:
- ✅ 每日自动统计车辆行驶里程
- ✅ 区分任务时段和非任务时段里程
- ✅ 计算任务里程占比
- ✅ 统计数据缓存到数据库表
- ✅ 支持手动触发和批量计算
- ✅ 提供完整的查询和导出功能
- ✅ 集成定时任务自动化执行

核心算法采用Haversine公式计算GPS点间距离,按时间重叠比例分摊里程到任务和非任务时段,确保统计准确性。所有数据缓存在专用统计表中,支持高效查询和分析。