Files
mp-java/src/main/java/com/gxwebsoft/hjm/service/GpsMessageProcessor.java
赵忠林 7faf588314 feat(mqtt): 启用生产环境MQTT服务并优化GPS日志保存逻辑
- 在application-prod.yml中将MQTT服务enabled设置为true
- 引入StringRedisTemplate用于Redis分布式锁控制-重构GPS轨迹日志保存方法,增加Redis原子锁控制频率
-仅在GPS速度不为0时保存轨迹日志,避免无效数据- 使用Redis setIfAbsent实现2分钟内同一设备只保存一次日志
- 完善日志记录,增加设备、速度、经纬度等关键信息
-优化异常处理,记录具体设备号便于问题追踪
2025-11-10 12:37:01 +08:00

283 lines
9.2 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.gxwebsoft.hjm.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.support.geo.Point;
import com.gxwebsoft.common.core.utils.JSONUtil;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.hjm.entity.Gps;
import com.gxwebsoft.hjm.entity.HjmCar;
import com.gxwebsoft.hjm.entity.HjmFence;
import com.gxwebsoft.hjm.entity.HjmGpsLog;
import com.gxwebsoft.hjm.service.impl.HjmCarServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.concurrent.TimeUnit;
/**
* GPS消息处理器
*
* @author 科技小王子
* @since 2025-07-02
*/
@Service
public class GpsMessageProcessor {
private static final Logger logger = LoggerFactory.getLogger(GpsMessageProcessor.class);
@Resource
private HjmCarService hjmCarService;
@Resource
private HjmGpsLogService hjmGpsLogService;
@Resource
private HjmFenceService hjmFenceService;
@Resource
private RedisUtil redisUtil;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private HjmCarServiceImpl hjmCarServiceImpl;
/**
* 处理GPS消息
*
* @param payload 消息内容
*/
public void processGpsMessage(String payload) {
try {
logger.debug("开始处理GPS消息: {}", payload);
Gps gps = JSONUtil.parseObject(payload, Gps.class);
if (ObjectUtil.isEmpty(gps)) {
logger.warn("GPS数据为空跳过处理");
return;
}
processGpsData(gps);
} catch (Exception e) {
logger.error("处理GPS数据失败: {}", payload, e);
}
}
/**
* 处理GPS数据
*
* @param gps GPS数据
*/
private void processGpsData(Gps gps) {
try {
String gpsNo = gps.getImei();
if (StrUtil.isBlank(gpsNo)) {
logger.warn("GPS设备编号为空跳过处理");
return;
}
// 详细记录GPS数据处理过程
logger.info("处理GPS数据 - 设备编号: {}, Fixed: {}, 经度: {}, 纬度: {}, 速度: {}",
gpsNo, gps.getFixed(), gps.getLng(), gps.getLat(), gps.getSpeed());
HjmCar car = hjmCarService.getByGpsNo(gpsNo);
if (car == null) {
logger.warn("GPS设备编号: {} 在数据库中未找到对应车辆,请检查车辆配置", gpsNo);
return;
}
logger.info("GPS设备编号: {} 对应车辆: {} (ID: {})", gpsNo, car.getCode(), car.getId());
if (!gps.getFixed()) {
logger.warn("GPS设备编号: {} 定位未固定(Fixed=false),跳过处理", gpsNo);
return;
}
// 更新车辆GPS定位信息
updateCarLocation(car, gps);
// 保存GPS轨迹日志
saveGpsLog(car, gps);
// 检查电子围栏
checkFence(car, gps);
} catch (Exception e) {
logger.error("处理GPS数据时发生错误", e);
}
}
/**
* 更新车辆位置信息
*/
private void updateCarLocation(HjmCar car, Gps gps) {
try {
car.setLongitude(gps.getLng());
car.setLatitude(gps.getLat());
car.setSpeed(gps.getSpeed());
car.setUpdateTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(gps.getTime() * 1000), ZoneId.systemDefault()));
car.setGpsNo(gps.getImei());
if (hjmCarService.updateByGpsNo(car)) {
logger.debug("更新车辆GPS定位信息成功: {}", car.getCode());
// 保存GPS轨迹日志通过Redis控制频率
saveGpsLogRecord(car, gps);
} else {
logger.warn("更新车辆GPS定位信息失败: {}", car.getCode());
}
} catch (Exception e) {
logger.error("更新车辆位置信息失败", e);
}
}
/**
* 保存GPS轨迹日志记录
* 使用Redis控制保存频率同一GPS设备2分钟内只保存一次
*/
private void saveGpsLogRecord(HjmCar car, Gps gps) {
try {
// 只有速度不为0时才保存GPS轨迹
if (gps.getSpeed().equals("0.000")) {
logger.debug("GPS设备{}速度为0跳过保存轨迹日志", gps.getImei());
return;
}
// 构造Redis key使用GPS设备编号作为key
String redisKey = "gps_log:" + gps.getImei();
// 使用Redis的setIfAbsent原子操作实现分布式锁
// 只有key不存在时才设置成功返回truekey已存在时返回false
Boolean lockAcquired = stringRedisTemplate.opsForValue()
.setIfAbsent(redisKey, "locked", 2, TimeUnit.MINUTES);
if (Boolean.FALSE.equals(lockAcquired)) {
// 获取锁失败说明2分钟内已经保存过
logger.debug("GPS设备{}在2分钟内已保存过轨迹日志跳过本次保存", gps.getImei());
return;
}
// 获取锁成功,执行保存操作
HjmGpsLog log = new HjmGpsLog();
log.setTenantId(10519);
log.setCarId(car.getId());
log.setGpsNo(gps.getImei());
log.setLatitude(gps.getLat());
log.setLongitude(gps.getLng());
log.setDdmmyy(gps.getDdmmyy());
log.setHhmmss(gps.getHhmmss());
log.setSpeed(gps.getSpeed());
// 保存数据库
hjmGpsLogService.save(log);
logger.info("保存GPS轨迹日志成功: 设备={}, 速度={}, 经度={}, 纬度={}",
gps.getImei(), gps.getSpeed(), gps.getLng(), gps.getLat());
} catch (Exception e) {
logger.error("保存GPS轨迹日志失败: 设备={}", gps.getImei(), e);
}
}
/**
* 保存GPS日志兼容原有方法
*/
private void saveGpsLog(HjmCar car, Gps gps) {
// 这里可以添加其他GPS日志相关的处理逻辑
logger.debug("处理GPS日志: 车辆={}, GPS={}", car.getCode(), gps.getImei());
}
/**
* 检查电子围栏
*/
private void checkFence(HjmCar car, Gps gps) {
try {
String gpsNo = gps.getImei();
// 检查围栏缓存,避免频繁计算
String inFenceKey = redisUtil.get("inFence:" + gpsNo);
if (StrUtil.isNotBlank(inFenceKey)) {
logger.debug("围栏检查缓存命中,跳过计算: {}", gpsNo);
return;
}
// 设置围栏检查缓存1天
redisUtil.set("inFence:" + gpsNo, "1", 1L, TimeUnit.DAYS);
// 检查是否配置了围栏
if (car.getFenceId() == null) {
logger.debug("车辆未配置围栏: {}", car.getCode());
return;
}
HjmFence fence = hjmFenceService.getById(car.getFenceId());
if (fence == null) {
logger.warn("围栏不存在: {}", car.getFenceId());
return;
}
// 检查坐标有效性
if (!isValidCoordinate(car.getLongitude(), car.getLatitude())) {
logger.warn("车辆坐标无效: 经度={}, 纬度={}", car.getLongitude(), car.getLatitude());
return;
}
// 执行围栏判断
performFenceCheck(car, fence);
} catch (Exception e) {
logger.error("检查电子围栏时发生错误", e);
}
}
/**
* 检查坐标有效性
*/
private boolean isValidCoordinate(String longitude, String latitude) {
return longitude != null && latitude != null &&
!longitude.trim().isEmpty() && !latitude.trim().isEmpty();
}
/**
* 执行围栏判断
*/
private void performFenceCheck(HjmCar car, HjmFence fence) {
try {
double lng = Double.parseDouble(car.getLongitude());
double lat = Double.parseDouble(car.getLatitude());
Point carPoint = new Point();
carPoint.setLongitude(lng);
carPoint.setLatitude(lat);
// 使用多边形围栏判断
boolean isInFence = hjmCarServiceImpl.checkPolygonFence(carPoint, fence);
// 更新围栏状态
car.setInFence(isInFence);
car.setUpdateTime(LocalDateTime.now());
hjmCarService.updateById(car);
logger.info("车辆围栏检查完成: 车辆={}, 围栏={}, 是否在围栏内={}",
car.getCode(), fence.getId(), isInFence);
} catch (NumberFormatException e) {
logger.error("车辆坐标格式错误: 经度={}, 纬度={}", car.getLongitude(), car.getLatitude(), e);
} catch (Exception e) {
logger.error("执行围栏判断时发生错误", e);
}
}
}