在项目内处理电子围栏逻辑
This commit is contained in:
5
pom.xml
5
pom.xml
@ -180,6 +180,11 @@
|
|||||||
<version>1.2.83</version>
|
<version>1.2.83</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Redis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- kafka -->
|
<!-- kafka -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.sczx.sync.config;
|
package com.sczx.sync.config;
|
||||||
|
|
||||||
|
import com.sczx.sync.service.ElectronicFenceService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.sczx.sync.dto.DeviceData;
|
import com.sczx.sync.dto.DeviceData;
|
||||||
@ -19,6 +20,10 @@ public class KafkaConsumer {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ThreadPoolConfig threadPoolConfig;
|
private ThreadPoolConfig threadPoolConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ElectronicFenceService electronicFenceService;
|
||||||
|
|
||||||
@Value("${dataPush.deviceDataUrl}")
|
@Value("${dataPush.deviceDataUrl}")
|
||||||
private String deviceDataUrl;
|
private String deviceDataUrl;
|
||||||
|
|
||||||
@ -54,7 +59,8 @@ public class KafkaConsumer {
|
|||||||
log.info("处理1分钟以内的消息: " + data.getClientId() + " at " + data.getTs());
|
log.info("处理1分钟以内的消息: " + data.getClientId() + " at " + data.getTs());
|
||||||
|
|
||||||
// 调用接口处理逻辑
|
// 调用接口处理逻辑
|
||||||
callExternalApi(data);
|
//callExternalApi(data);
|
||||||
|
electronicFenceService.checkCarPositionInFence( data);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to process message: " + message);
|
log.error("Failed to process message: " + message);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.sczx.sync.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.sczx.sync.po.BaseUser;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ElectronicFenceMapper extends BaseMapper<BaseUser> {
|
||||||
|
|
||||||
|
String selectZcElectronicFenceById(Long id);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
37
src/main/java/com/sczx/sync/po/EfenceInfo.java
Normal file
37
src/main/java/com/sczx/sync/po/EfenceInfo.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package com.sczx.sync.po;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电池订单信息实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class EfenceInfo {
|
||||||
|
|
||||||
|
/** ID */
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 名称 */
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** 围栏坐标地址 */
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
/** 关联套餐 */
|
||||||
|
private String ruleName;
|
||||||
|
|
||||||
|
/** 部门状态(0正常 1停用) */
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/** 删除标志(0代表存在 2代表删除) */
|
||||||
|
private String delFlag;
|
||||||
|
|
||||||
|
|
||||||
|
private String extend1;
|
||||||
|
|
||||||
|
|
||||||
|
private String extend2;
|
||||||
|
|
||||||
|
|
||||||
|
private String extend3;
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.sczx.sync.service;
|
||||||
|
|
||||||
|
import com.sczx.sync.dto.DeviceData;
|
||||||
|
|
||||||
|
public interface ElectronicFenceService {
|
||||||
|
|
||||||
|
void checkCarPositionInFence(DeviceData data);
|
||||||
|
}
|
||||||
@ -0,0 +1,155 @@
|
|||||||
|
package com.sczx.sync.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.sczx.sync.dto.DeviceData;
|
||||||
|
import com.sczx.sync.mapper.ElectronicFenceMapper;
|
||||||
|
import com.sczx.sync.service.ElectronicFenceService;
|
||||||
|
import com.sczx.sync.utils.Point;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.sczx.sync.utils.RedisUtil;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@Component
|
||||||
|
public class ElectronicFenceServiceImpl implements ElectronicFenceService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisUtil redisUtils;
|
||||||
|
@Autowired
|
||||||
|
private ElectronicFenceMapper ElectronicFenceMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCarPositionInFence(DeviceData deviceLocation) {
|
||||||
|
String RedisKeyConstants = "sczxOrder:"+"eFence:";
|
||||||
|
|
||||||
|
String clientId = deviceLocation.getClientId();
|
||||||
|
// 获取电子围栏id
|
||||||
|
String redisEfenceKey = RedisKeyConstants + clientId;
|
||||||
|
if (redisUtils.get(redisEfenceKey) == null) {
|
||||||
|
log.info("未找到车辆对应的电子围栏ID:" + clientId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String fenceId = redisUtils.get(redisEfenceKey);
|
||||||
|
// 查询电子围栏
|
||||||
|
String electronicFenceString = ElectronicFenceMapper.selectZcElectronicFenceById(Long.parseLong(fenceId));
|
||||||
|
|
||||||
|
if (electronicFenceString == null) {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 解析电子围栏坐标点(转换后的wgs84坐标)
|
||||||
|
JSONArray fencePoints = JSONArray.parseArray(electronicFenceString);
|
||||||
|
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.getLng(), deviceLocation.getLat());
|
||||||
|
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 车辆识别号
|
||||||
|
*/
|
||||||
|
private void sendPowerOnCommand(String clientId) {
|
||||||
|
// 实际实现发送通电指令到设备
|
||||||
|
|
||||||
|
log.info("发送通电指令给车辆: " + clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送断电指令
|
||||||
|
*
|
||||||
|
* @param clientId 车辆识别号
|
||||||
|
*/
|
||||||
|
private void sendPowerOffCommand(String clientId) {
|
||||||
|
// 实际实现发送断电指令到设备
|
||||||
|
|
||||||
|
log.info("发送断电指令给车辆: " + clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/main/java/com/sczx/sync/utils/Point.java
Normal file
12
src/main/java/com/sczx/sync/utils/Point.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.sczx.sync.utils;
|
||||||
|
|
||||||
|
public class Point {
|
||||||
|
|
||||||
|
public double x, y;
|
||||||
|
|
||||||
|
public Point(double x, double y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
74
src/main/java/com/sczx/sync/utils/RedisUtil.java
Normal file
74
src/main/java/com/sczx/sync/utils/RedisUtil.java
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package com.sczx.sync.utils;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 张黎
|
||||||
|
* @Date: 2025/07/06/14:21
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class RedisUtil {
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StringRedisTemplate redisTemplate;
|
||||||
|
|
||||||
|
public void set(String key, String value, long timeout, TimeUnit unit) {
|
||||||
|
redisTemplate.opsForValue().set(key, value, timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String key, String value) {
|
||||||
|
redisTemplate.opsForValue().set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(String key) {
|
||||||
|
return redisTemplate.opsForValue().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(String key) {
|
||||||
|
redisTemplate.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Long DEFAULT_EXPIRE = 120L;
|
||||||
|
|
||||||
|
public boolean getRedisLock(String key, String lockName) {
|
||||||
|
log.info("获取锁 - {}", key);
|
||||||
|
return getRedisLockWithTimeout(key, lockName, 120L);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取redislock
|
||||||
|
* 加锁并设置过期时间
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param lockName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean getRedisLockWithTimeout(String key, String lockName, Long lockExpire) {
|
||||||
|
boolean lock = false;
|
||||||
|
if (Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, lockName))) {
|
||||||
|
redisTemplate.expire(key, lockExpire, TimeUnit.SECONDS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (redisTemplate.getExpire(key) == -1) {
|
||||||
|
//保证锁一定设置过期时间
|
||||||
|
redisTemplate.expire(key, DEFAULT_EXPIRE, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
log.info("未获取到锁:" + lock + ", lockName={},key={}", lockName, key);
|
||||||
|
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteRedisLock(String key) {
|
||||||
|
log.info("释放锁 - {}", key);
|
||||||
|
redisTemplate.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
13
src/main/resources/mapper/ElectronicFenceMapper.xml
Normal file
13
src/main/resources/mapper/ElectronicFenceMapper.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.sczx.sync.mapper.ElectronicFenceMapper">
|
||||||
|
|
||||||
|
|
||||||
|
<select id="selectZcElectronicFenceById" parameterType="Long" resultType="java.lang.String">
|
||||||
|
select a.extend1
|
||||||
|
from zc_electronic_fence a
|
||||||
|
left join zc_electronic_fence_rule b on b.electronic_fence_id = a.id
|
||||||
|
where a.id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
Reference in New Issue
Block a user