电子围栏,车辆实时位置接收

This commit is contained in:
19173159168
2025-10-14 23:52:00 +08:00
parent 02d139201c
commit 088860be89
9 changed files with 709 additions and 8 deletions

View File

@ -0,0 +1,30 @@
package com.ruoyi.api;
import com.ruoyi.operation.domain.DeviceLocation;
import com.ruoyi.operation.service.IZcElectronicFenceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/fuleStatusApi")
public class FuleStatusApi {
@Autowired
private IZcElectronicFenceService zcElectronicFenceService;
@PostMapping("/device-data")
public ResponseEntity<String> receiveDeviceData(@RequestBody DeviceLocation request) {
try {
zcElectronicFenceService.checkCarPositionInFence(request);
return ResponseEntity.ok("成功");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to process data: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,395 @@
package com.ruoyi.operation.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Date;
/**
* 设备位置信息实体类
*/
public class DeviceLocation {
// 经度方向
@JsonProperty("longitudeDirection")
private String longitudeDirection;
// 布防状态
@JsonProperty("armStatus")
private String armStatus;
// 海拔高度
@JsonProperty("altitude")
private Integer altitude;
// 北斗使用状态
@JsonProperty("beidouUsed")
private String beidouUsed;
// 速度
@JsonProperty("speed")
private Integer speed;
// 定位状态
@JsonProperty("locationStatus")
private String locationStatus;
// 供应商ID
@JsonProperty("providerId")
private Integer providerId;
// 充电状态
@JsonProperty("chargingStatus")
private String chargingStatus;
// 门磁状态
@JsonProperty("hallStatus")
private String hallStatus;
// 纬度
@JsonProperty("lat")
private Double latitude;
// 设备工作状态
@JsonProperty("deviceWorkStatus")
private String deviceWorkStatus;
// 方向
@JsonProperty("direction")
private Integer direction;
// 状态位
@JsonProperty("statusBit")
private Integer statusBit;
// 报警位
@JsonProperty("warnBit")
private Integer warnBit;
// 客户端ID/设备ID
@JsonProperty("clientId")
private String clientId;
// 经度
@JsonProperty("lng")
private Double longitude;
// 报警信息
@JsonProperty("alarms")
private String alarms;
// ACC状态
@JsonProperty("accStatus")
private String accStatus;
// 伽利略使用状态
@JsonProperty("galileoUsed")
private String galileoUsed;
// 运动状态
@JsonProperty("movingStatus")
private String movingStatus;
// 纬度方向
@JsonProperty("latitudeDirection")
private String latitudeDirection;
// 软ACC状态
@JsonProperty("softAccStatus")
private String softAccStatus;
// GLONASS使用状态
@JsonProperty("glonassUsed")
private String glonassUsed;
// GPS使用状态
@JsonProperty("gpsUsed")
private String gpsUsed;
// 油路状态
@JsonProperty("fuelStatus")
private String fuelStatus;
// 设备电源状态
@JsonProperty("fakePowerStatus")
private String fakePowerStatus;
// 时间戳
@JsonProperty("ts")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date timestamp;
// 无参构造函数
public DeviceLocation() {
}
// 全参构造函数
public DeviceLocation(String longitudeDirection, String armStatus, Integer altitude,
String beidouUsed, Integer speed, String locationStatus,
Integer providerId, String chargingStatus, String hallStatus,
Double latitude, String deviceWorkStatus, Integer direction,
Integer statusBit, Integer warnBit, String clientId,
Double longitude, String alarms, String accStatus,
String galileoUsed, String movingStatus, String latitudeDirection,
String softAccStatus, String glonassUsed, String gpsUsed,
String fuelStatus, String fakePowerStatus, Date timestamp) {
this.longitudeDirection = longitudeDirection;
this.armStatus = armStatus;
this.altitude = altitude;
this.beidouUsed = beidouUsed;
this.speed = speed;
this.locationStatus = locationStatus;
this.providerId = providerId;
this.chargingStatus = chargingStatus;
this.hallStatus = hallStatus;
this.latitude = latitude;
this.deviceWorkStatus = deviceWorkStatus;
this.direction = direction;
this.statusBit = statusBit;
this.warnBit = warnBit;
this.clientId = clientId;
this.longitude = longitude;
this.alarms = alarms;
this.accStatus = accStatus;
this.galileoUsed = galileoUsed;
this.movingStatus = movingStatus;
this.latitudeDirection = latitudeDirection;
this.softAccStatus = softAccStatus;
this.glonassUsed = glonassUsed;
this.gpsUsed = gpsUsed;
this.fuelStatus = fuelStatus;
this.fakePowerStatus = fakePowerStatus;
this.timestamp = timestamp;
}
// Getter和Setter方法
public String getLongitudeDirection() {
return longitudeDirection;
}
public void setLongitudeDirection(String longitudeDirection) {
this.longitudeDirection = longitudeDirection;
}
public String getArmStatus() {
return armStatus;
}
public void setArmStatus(String armStatus) {
this.armStatus = armStatus;
}
public Integer getAltitude() {
return altitude;
}
public void setAltitude(Integer altitude) {
this.altitude = altitude;
}
public String getBeidouUsed() {
return beidouUsed;
}
public void setBeidouUsed(String beidouUsed) {
this.beidouUsed = beidouUsed;
}
public Integer getSpeed() {
return speed;
}
public void setSpeed(Integer speed) {
this.speed = speed;
}
public String getLocationStatus() {
return locationStatus;
}
public void setLocationStatus(String locationStatus) {
this.locationStatus = locationStatus;
}
public Integer getProviderId() {
return providerId;
}
public void setProviderId(Integer providerId) {
this.providerId = providerId;
}
public String getChargingStatus() {
return chargingStatus;
}
public void setChargingStatus(String chargingStatus) {
this.chargingStatus = chargingStatus;
}
public String getHallStatus() {
return hallStatus;
}
public void setHallStatus(String hallStatus) {
this.hallStatus = hallStatus;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public String getDeviceWorkStatus() {
return deviceWorkStatus;
}
public void setDeviceWorkStatus(String deviceWorkStatus) {
this.deviceWorkStatus = deviceWorkStatus;
}
public Integer getDirection() {
return direction;
}
public void setDirection(Integer direction) {
this.direction = direction;
}
public Integer getStatusBit() {
return statusBit;
}
public void setStatusBit(Integer statusBit) {
this.statusBit = statusBit;
}
public Integer getWarnBit() {
return warnBit;
}
public void setWarnBit(Integer warnBit) {
this.warnBit = warnBit;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public String getAlarms() {
return alarms;
}
public void setAlarms(String alarms) {
this.alarms = alarms;
}
public String getAccStatus() {
return accStatus;
}
public void setAccStatus(String accStatus) {
this.accStatus = accStatus;
}
public String getGalileoUsed() {
return galileoUsed;
}
public void setGalileoUsed(String galileoUsed) {
this.galileoUsed = galileoUsed;
}
public String getMovingStatus() {
return movingStatus;
}
public void setMovingStatus(String movingStatus) {
this.movingStatus = movingStatus;
}
public String getLatitudeDirection() {
return latitudeDirection;
}
public void setLatitudeDirection(String latitudeDirection) {
this.latitudeDirection = latitudeDirection;
}
public String getSoftAccStatus() {
return softAccStatus;
}
public void setSoftAccStatus(String softAccStatus) {
this.softAccStatus = softAccStatus;
}
public String getGlonassUsed() {
return glonassUsed;
}
public void setGlonassUsed(String glonassUsed) {
this.glonassUsed = glonassUsed;
}
public String getGpsUsed() {
return gpsUsed;
}
public void setGpsUsed(String gpsUsed) {
this.gpsUsed = gpsUsed;
}
public String getFuelStatus() {
return fuelStatus;
}
public void setFuelStatus(String fuelStatus) {
this.fuelStatus = fuelStatus;
}
public String getFakePowerStatus() {
return fakePowerStatus;
}
public void setFakePowerStatus(String fakePowerStatus) {
this.fakePowerStatus = fakePowerStatus;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
@Override
public String toString() {
return "DeviceLocation{" +
"clientId='" + clientId + '\'' +
", longitude=" + longitude +
", latitude=" + latitude +
", timestamp=" + timestamp +
", speed=" + speed +
", direction=" + direction +
", locationStatus='" + locationStatus + '\'' +
", movingStatus='" + movingStatus + '\'' +
'}';
}
}

View File

@ -1,6 +1,10 @@
package com.ruoyi.operation.domain;
import java.util.List;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.operation.util.CoordinateTransformUtil;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
@ -172,4 +176,36 @@ public class ZcElectronicFence extends BaseEntity
.append("zcElectronicFenceRuleList", getZcElectronicFenceRuleList())
.toString();
}
/**
* 转换坐标为WGS84格式
*/
public void transformCoordinatesToWGS84() {
if (this.address != null && !this.address.isEmpty()) {
try {
// 解析原始坐标数据
JSONArray coordinates = JSONArray.parseArray(this.address);
for (int i = 0; i < coordinates.size(); i++) {
JSONObject point = coordinates.getJSONObject(i);
double lng = point.getDouble("lng");
double lat = point.getDouble("lat");
// 执行坐标转换
double[] wgs84 = CoordinateTransformUtil.gcj02towgs84(lng, lat);
// 更新坐标值
point.put("lng", wgs84[0]);
point.put("lat", wgs84[1]);
}
// 更新extend1字段
this.extend1 = coordinates.toJSONString();
} catch (Exception e) {
// 处理解析异常
e.printStackTrace();
}
}
}
}

View File

@ -1,6 +1,8 @@
package com.ruoyi.operation.service;
import java.util.List;
import com.ruoyi.operation.domain.DeviceLocation;
import com.ruoyi.operation.domain.ZcElectronicFence;
/**
@ -60,4 +62,12 @@ public interface IZcElectronicFenceService
public int deleteZcElectronicFenceById(Long id);
public int changeStatus(ZcElectronicFence zcElectronicFence);
/**
* 检查车辆位置是否在电子围栏内
*
* @param deviceLocation
*/
public void checkCarPositionInFence(DeviceLocation deviceLocation);
}

View File

@ -2,10 +2,17 @@ package com.ruoyi.operation.service.impl;
import java.util.Date;
import java.util.List;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.RedisKeyConstants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.RedisUtils;
import com.ruoyi.operation.domain.DeviceLocation;
import com.ruoyi.operation.domain.ZcCarModelPackage;
import com.ruoyi.operation.mapper.ZcElectronicFenceRuleMapper;
import com.ruoyi.operation.service.IZcRentCarRuleService;
import com.ruoyi.operation.util.Point;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@ -24,7 +31,7 @@ import com.ruoyi.common.core.text.Convert;
* @date 2025-10-13
*/
@Service
public class ZcElectronicFenceServiceImpl implements IZcElectronicFenceService
public class ZcElectronicFenceServiceImpl implements IZcElectronicFenceService
{
@Autowired
private ZcElectronicFenceMapper zcElectronicFenceMapper;
@ -32,6 +39,9 @@ public class ZcElectronicFenceServiceImpl implements IZcElectronicFenceService
private ZcElectronicFenceRuleMapper zcElectronicFenceRuleMapper;
@Autowired
private IZcRentCarRuleService zcRentCarRuleService;
@Autowired
private RedisUtils redisUtils;
/**
* 查询电子围栏
*
@ -66,6 +76,9 @@ public class ZcElectronicFenceServiceImpl implements IZcElectronicFenceService
@Override
public int insertZcElectronicFence(ZcElectronicFence zcElectronicFence)
{
// 坐标转换
zcElectronicFence.transformCoordinatesToWGS84();
zcElectronicFence.setCreateTime(DateUtils.getNowDate());
int rows = zcElectronicFenceMapper.insertZcElectronicFence(zcElectronicFence);
@ -96,6 +109,9 @@ public class ZcElectronicFenceServiceImpl implements IZcElectronicFenceService
@Override
public int updateZcElectronicFence(ZcElectronicFence zcElectronicFence)
{
// 坐标转换wgs84
zcElectronicFence.transformCoordinatesToWGS84();
zcElectronicFence.setUpdateTime(DateUtils.getNowDate());
zcElectronicFenceMapper.deleteZcElectronicFenceRuleByElectronicFenceId(zcElectronicFence.getId());
zcRentCarRuleService.updateZcRentCarRuleUnbindFence(zcElectronicFence.getId());
@ -145,15 +161,9 @@ public class ZcElectronicFenceServiceImpl implements IZcElectronicFenceService
return zcElectronicFenceMapper.deleteZcElectronicFenceById(id);
}
@Override
public int changeStatus(ZcElectronicFence zcElectronicFence) {
zcElectronicFence.setUpdateTime(DateUtils.getNowDate());
return zcElectronicFenceMapper.updateZcElectronicFence(zcElectronicFence);
}
/**
* 新增套餐规则与电子围栏关联信息
*
*
* @param zcElectronicFence 电子围栏对象
*/
public void insertZcElectronicFenceRule(ZcElectronicFence zcElectronicFence)
@ -174,4 +184,150 @@ public class ZcElectronicFenceServiceImpl implements IZcElectronicFenceService
}
}
}
@Override
public int changeStatus(ZcElectronicFence zcElectronicFence) {
zcElectronicFence.setUpdateTime(DateUtils.getNowDate());
return zcElectronicFenceMapper.updateZcElectronicFence(zcElectronicFence);
}
@Override
public void checkCarPositionInFence(DeviceLocation deviceLocation) {
String clientId = deviceLocation.getClientId();
// 获取电子围栏id
String redisEfenceKey = RedisKeyConstants.E_Fence_KEY + clientId;
if (redisUtils.get(redisEfenceKey) == null) {
return ;
}
String fenceId = redisUtils.get(redisEfenceKey);
// 查询电子围栏
ZcElectronicFence electronicFence = zcElectronicFenceMapper.selectZcElectronicFenceById(Long.parseLong(fenceId));
if (electronicFence == null || StringUtils.isEmpty(electronicFence.getAddress())) {
return ;
}
try {
// 解析电子围栏坐标点(转换后的wgs84坐标)
JSONArray fencePoints = JSONArray.parseArray(electronicFence.getExtend1());
List<Point> polygon = new ArrayList<>();
for (int i = 0; i < fencePoints.size(); i++) {
JSONObject pointObj = fencePoints.getJSONObject(i);
Point point = new Point(pointObj.getDouble("lng"), pointObj.getDouble("lat"));
polygon.add(point);
}
// 判断点是否在多边形内
Point testPoint = new Point(deviceLocation.getLongitude(), deviceLocation.getLatitude());
boolean currentlyInFence = isPointInPolygon(testPoint, polygon);
// 出圈时 fuelStatus=油路正常, 就下断电指令; 回到圈内时 fuelStatus=油路断开,就上电
String fuleStatus = deviceLocation.getFuelStatus();
// 状态发生变化时执行相应操作
if (currentlyInFence && "油路断开".equals(fuleStatus)) {
// 进入围栏 - 发出放电指令
sendPowerOnCommand(clientId);
} else if (!currentlyInFence && "油路正常".equals(fuleStatus)) {
// 超出围栏 - 发出断电指令
sendPowerOffCommand(clientId);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 判断点是否在多边形内(射线法)
*
* @param point 测试点
* @param polygon 多边形顶点列表
* @return true-在多边形内false-在多边形外
*/
private boolean isPointInPolygon(Point point, List<Point> polygon) {
int intersectCount = 0;
for (int i = 0; i < polygon.size(); i++) {
Point p1 = polygon.get(i);
Point p2 = polygon.get((i + 1) % polygon.size());
// 检查水平射线是否与边相交
if (rayIntersectsSegment(point, p1, p2)) {
intersectCount++;
}
}
// 奇数个交点表示在多边形内部
return (intersectCount % 2) == 1;
}
/**
* 判断射线是否与线段相交
*
* @param point 射线起点
* @param p1 线段端点1
* @param p2 线段端点2
* @return true-相交false-不相交
*/
private boolean rayIntersectsSegment(Point point, Point p1, Point p2) {
// 确保p1的y坐标小于等于p2的y坐标
if (p1.y > p2.y) {
Point temp = p1;
p1 = p2;
p2 = temp;
}
// 如果点在线段两端点y坐标范围之外则不相交
if (point.y < p1.y || point.y > p2.y) {
return false;
}
// 水平线段特殊处理
if (p1.y == p2.y) {
return (point.x >= Math.min(p1.x, p2.x) && point.x <= Math.max(p1.x, p2.x));
}
// 计算交点x坐标
double xIntersection = (point.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
// 判断交点是否在射线上
return point.x <= xIntersection;
}
/**
* 获取车辆上一次的指令状态
*
* @param clientId 车辆识别号
* @return 上次状态
*/
private Boolean getLastCarFuleStatus(String clientId) {
String status = redisUtils.get(RedisKeyConstants.CAR_FULE_STATUS_KEY + clientId);
if (StringUtils.isEmpty(status)) {
return null;
}
return Boolean.valueOf(status);
}
/**
* 发送通电指令
*
* @param clientId 车辆识别号
*/
private void sendPowerOnCommand(String clientId) {
// 实际实现发送通电指令到设备
System.out.println("发送放电指令给车辆: " + clientId);
}
/**
* 发送断电指令
*
* @param clientId 车辆识别号
*/
private void sendPowerOffCommand(String clientId) {
// 实际实现发送断电指令到设备
System.out.println("发送断电指令给车辆: " + clientId);
}
}

View File

@ -0,0 +1,38 @@
package com.ruoyi.operation.util;
public class CoordinateTransformUtil {
/**
* GCJ-02 to WGS-84
*/
public static double[] gcj02towgs84(double lng, double lat) {
double dlat = transformlat(lng - 105.0, lat - 35.0);
double dlng = transformlng(lng - 105.0, lat - 35.0);
double radlat = lat / 180.0 * Math.PI;
double magic = Math.sin(radlat);
magic = 1 - 0.006693421622965943 * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((6378137.0 * (1 - 0.006693421622965943)) / (magic * sqrtmagic) * Math.PI);
dlng = (dlng * 180.0) / (6378137.0 / sqrtmagic * Math.cos(radlat) * Math.PI);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[]{lng * 2 - mglng, lat * 2 - mglat};
}
private static double transformlat(double x, double y) {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
return ret;
}
private static double transformlng(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
return ret;
}
}

View File

@ -0,0 +1,12 @@
package com.ruoyi.operation.util;
public class Point {
public double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}

View File

@ -4,7 +4,9 @@ import java.math.BigDecimal;
import java.util.*;
import com.ruoyi.api.DataPushApi;
import com.ruoyi.common.constant.RedisKeyConstants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.RedisUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.operation.domain.ZcCar;
import com.ruoyi.operation.service.IZcCarService;
@ -57,6 +59,8 @@ public class ZcOrderMainServiceImpl implements IZcOrderMainService
private IZcCarService zcCarService;
@Autowired
private DataPushApi dataPushApi;
@Autowired
private RedisUtils redisUtils;
/**
@ -181,6 +185,16 @@ public class ZcOrderMainServiceImpl implements IZcOrderMainService
zcCarService.updateZcCar(zcCar);
logger.info("手动结束订单处理完成订单ID: {}", orderId);
//删除车辆电子围栏缓存
ZcCar queryCar = zcCarService.selectZcCarById(queryOrder.getVehicleId());
if(queryCar != null) {
String redisEfenceKey = RedisKeyConstants.E_Fence_KEY + queryCar.getLotNumber();
if (redisUtils.get(redisEfenceKey) != null) {
redisUtils.delete(redisEfenceKey);
}
}
return flag;
} catch (Exception e) {