From fc5a120d48805730412e5ab7457fddcdcfd3da16 Mon Sep 17 00:00:00 2001
From: eric <465889110@qq.com>
Date: Thu, 16 Oct 2025 01:30:02 +0800
Subject: [PATCH] =?UTF-8?q?=E5=9C=A8=E9=A1=B9=E7=9B=AE=E5=86=85=E5=A4=84?=
=?UTF-8?q?=E7=90=86=E7=94=B5=E5=AD=90=E5=9B=B4=E6=A0=8F=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 5 +
.../com/sczx/sync/config/KafkaConsumer.java | 8 +-
.../sync/mapper/ElectronicFenceMapper.java | 14 ++
.../java/com/sczx/sync/po/EfenceInfo.java | 37 +++++
.../sync/service/ElectronicFenceService.java | 8 +
.../impl/ElectronicFenceServiceImpl.java | 155 ++++++++++++++++++
src/main/java/com/sczx/sync/utils/Point.java | 12 ++
.../java/com/sczx/sync/utils/RedisUtil.java | 74 +++++++++
.../mapper/ElectronicFenceMapper.xml | 13 ++
9 files changed, 325 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/com/sczx/sync/mapper/ElectronicFenceMapper.java
create mode 100644 src/main/java/com/sczx/sync/po/EfenceInfo.java
create mode 100644 src/main/java/com/sczx/sync/service/ElectronicFenceService.java
create mode 100644 src/main/java/com/sczx/sync/service/impl/ElectronicFenceServiceImpl.java
create mode 100644 src/main/java/com/sczx/sync/utils/Point.java
create mode 100644 src/main/java/com/sczx/sync/utils/RedisUtil.java
create mode 100644 src/main/resources/mapper/ElectronicFenceMapper.xml
diff --git a/pom.xml b/pom.xml
index 8944a80..a8b5bf2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -180,6 +180,11 @@
1.2.83
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
diff --git a/src/main/java/com/sczx/sync/config/KafkaConsumer.java b/src/main/java/com/sczx/sync/config/KafkaConsumer.java
index 196527e..aaa338f 100644
--- a/src/main/java/com/sczx/sync/config/KafkaConsumer.java
+++ b/src/main/java/com/sczx/sync/config/KafkaConsumer.java
@@ -1,5 +1,6 @@
package com.sczx.sync.config;
+import com.sczx.sync.service.ElectronicFenceService;
import lombok.extern.slf4j.Slf4j;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sczx.sync.dto.DeviceData;
@@ -19,6 +20,10 @@ public class KafkaConsumer {
@Autowired
private ThreadPoolConfig threadPoolConfig;
+
+ @Autowired
+ private ElectronicFenceService electronicFenceService;
+
@Value("${dataPush.deviceDataUrl}")
private String deviceDataUrl;
@@ -54,7 +59,8 @@ public class KafkaConsumer {
log.info("处理1分钟以内的消息: " + data.getClientId() + " at " + data.getTs());
// 调用接口处理逻辑
- callExternalApi(data);
+ //callExternalApi(data);
+ electronicFenceService.checkCarPositionInFence( data);
} catch (Exception e) {
log.error("Failed to process message: " + message);
e.printStackTrace();
diff --git a/src/main/java/com/sczx/sync/mapper/ElectronicFenceMapper.java b/src/main/java/com/sczx/sync/mapper/ElectronicFenceMapper.java
new file mode 100644
index 0000000..9465eba
--- /dev/null
+++ b/src/main/java/com/sczx/sync/mapper/ElectronicFenceMapper.java
@@ -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 {
+
+ String selectZcElectronicFenceById(Long id);
+
+
+}
diff --git a/src/main/java/com/sczx/sync/po/EfenceInfo.java b/src/main/java/com/sczx/sync/po/EfenceInfo.java
new file mode 100644
index 0000000..619e81e
--- /dev/null
+++ b/src/main/java/com/sczx/sync/po/EfenceInfo.java
@@ -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;
+}
diff --git a/src/main/java/com/sczx/sync/service/ElectronicFenceService.java b/src/main/java/com/sczx/sync/service/ElectronicFenceService.java
new file mode 100644
index 0000000..49e9c91
--- /dev/null
+++ b/src/main/java/com/sczx/sync/service/ElectronicFenceService.java
@@ -0,0 +1,8 @@
+package com.sczx.sync.service;
+
+import com.sczx.sync.dto.DeviceData;
+
+public interface ElectronicFenceService {
+
+ void checkCarPositionInFence(DeviceData data);
+}
diff --git a/src/main/java/com/sczx/sync/service/impl/ElectronicFenceServiceImpl.java b/src/main/java/com/sczx/sync/service/impl/ElectronicFenceServiceImpl.java
new file mode 100644
index 0000000..c5b972d
--- /dev/null
+++ b/src/main/java/com/sczx/sync/service/impl/ElectronicFenceServiceImpl.java
@@ -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 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 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);
+ }
+}
diff --git a/src/main/java/com/sczx/sync/utils/Point.java b/src/main/java/com/sczx/sync/utils/Point.java
new file mode 100644
index 0000000..e240d6d
--- /dev/null
+++ b/src/main/java/com/sczx/sync/utils/Point.java
@@ -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;
+ }
+
+}
diff --git a/src/main/java/com/sczx/sync/utils/RedisUtil.java b/src/main/java/com/sczx/sync/utils/RedisUtil.java
new file mode 100644
index 0000000..1b64543
--- /dev/null
+++ b/src/main/java/com/sczx/sync/utils/RedisUtil.java
@@ -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);
+ }
+
+}
diff --git a/src/main/resources/mapper/ElectronicFenceMapper.xml b/src/main/resources/mapper/ElectronicFenceMapper.xml
new file mode 100644
index 0000000..6b8c945
--- /dev/null
+++ b/src/main/resources/mapper/ElectronicFenceMapper.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+