# 车辆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点间距离) ```java 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; } ``` #### 时间重叠比例计算(任务里程分摊) ```java private double calculateTaskOverlapRatio(Date segmentStart, Date segmentEnd, List 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. 执行数据库脚本(按顺序) ```bash 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:手动计算某车辆昨日里程 ```javascript 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:批量计算所有车辆指定日期里程 ```javascript import { batchCalculateMileage } from '@/api/mileageStats' batchCalculateMileage('2025-11-09').then(response => { console.log(response.msg) // 输出:批量里程统计完成,成功统计 45 辆车 }) ``` ### 示例3:查询车辆里程统计报表 ```javascript 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漂移点 - 启用明细表分析每段距离 ### 查看日志 ```bash # Service层日志 grep "VehicleMileageStatsServiceImpl" logs/ruoyi-*.log # 定时任务日志 grep "VehicleMileageStatsTask" logs/ruoyi-*.log ``` ## 十一、总结 ✅ 本功能已完整实现车辆GPS里程统计的所有需求: - ✅ 每日自动统计车辆行驶里程 - ✅ 区分任务时段和非任务时段里程 - ✅ 计算任务里程占比 - ✅ 统计数据缓存到数据库表 - ✅ 支持手动触发和批量计算 - ✅ 提供完整的查询和导出功能 - ✅ 集成定时任务自动化执行 核心算法采用Haversine公式计算GPS点间距离,按时间重叠比例分摊里程到任务和非任务时段,确保统计准确性。所有数据缓存在专用统计表中,支持高效查询和分析。