diff --git a/car-business-common/pom.xml b/car-business-common/pom.xml
index 8842d31..073eeee 100644
--- a/car-business-common/pom.xml
+++ b/car-business-common/pom.xml
@@ -44,6 +44,25 @@
god-common-swagger
+
+
+ com.god
+ car-base-remote
+ 3.6.3
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ 3.4.2
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ 3.4.2
+
+
diff --git a/car-business-common/src/main/java/com/god/business/common/domain/BreakDown.java b/car-business-common/src/main/java/com/god/business/common/domain/BreakDown.java
new file mode 100644
index 0000000..7576280
--- /dev/null
+++ b/car-business-common/src/main/java/com/god/business/common/domain/BreakDown.java
@@ -0,0 +1,100 @@
+package com.god.business.common.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.god.base.common.domain.Fence;
+import com.god.business.common.domain.request.ReqAddBreakDown;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+
+/**
+ * 故障记录
+ *
+ * @author LouZhiSHuo
+ * @Date 2023/11/29 14:40
+ **/
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class BreakDown {
+
+
+ /**
+ * 故障记录主建
+ */
+ private Integer id;
+
+ /**
+ * 车辆VIN
+ */
+ @NotEmpty(message = "车辆VIN不能为空")
+ private String carVin;
+
+ /**
+ * 故障码Id
+ */
+ @NotNull(message = "故障主建不能为空")
+ private Integer breakdownId;
+
+
+ /**
+ * 故障状态
+ */
+ private Integer status;
+
+
+ /**
+ * 故障开始时间
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8")
+ private Date createTime;
+
+ /**
+ * 故障结束时间
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8")
+ private Date endTime;
+
+
+ /**
+ * 创建人
+ */
+ private String createBy;
+
+ /**
+ * 修改人
+ */
+ private String updateBy;
+
+ /**
+ * 更新时间
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8")
+ private Date updateTime;
+
+
+
+ public static BreakDown insertBreakDown(ReqAddBreakDown reqAddBreakDown){
+ return BreakDown.builder()
+ .carVin(reqAddBreakDown.getCarVin())
+ .breakdownId(reqAddBreakDown.getBreakdownId())
+ .status(reqAddBreakDown.getStatus())
+ .createTime(reqAddBreakDown.getCreateTime())
+ .endTime(reqAddBreakDown.getEndTime())
+ .createBy(reqAddBreakDown.getCreateBy())
+ .updateBy(reqAddBreakDown.getUpdateBy())
+ .updateTime(reqAddBreakDown.getUpdateTime())
+ .build();
+ }
+
+}
diff --git a/car-business-common/src/main/java/com/god/business/common/domain/request/ReqAddBreakDown.java b/car-business-common/src/main/java/com/god/business/common/domain/request/ReqAddBreakDown.java
new file mode 100644
index 0000000..7a19ff3
--- /dev/null
+++ b/car-business-common/src/main/java/com/god/business/common/domain/request/ReqAddBreakDown.java
@@ -0,0 +1,81 @@
+package com.god.business.common.domain.request;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+/**
+ * 故障添加参数
+ *
+ * @author LouZhiSHuo
+ * @Date 2023/11/29 14:48
+ **/
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class ReqAddBreakDown {
+
+
+ /**
+ * 车辆VIN
+ */
+ private String carVin;
+
+ /**
+ * 故障码Id
+ */
+ private Integer breakdownId;
+
+ /**
+ * 故障状态
+ */
+ private Integer status;
+
+ /**
+ * 故障开始时间
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8")
+ private Date createTime;
+
+ /**
+ * 故障结束时间
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8")
+ private Date endTime;
+
+ /**
+ * 创建人
+ */
+ private String createBy;
+
+ /**
+ * 修改人
+ */
+ private String updateBy;
+
+ /**
+ * 修改时间
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8")
+ private Date updateTime;
+
+
+
+
+
+
+
+
+
+
+
+}
diff --git a/car-business-server/src/main/java/com/god/base/server/consumer/KafkaSub.java b/car-business-server/src/main/java/com/god/base/server/consumer/KafkaSub.java
new file mode 100644
index 0000000..9ef1b36
--- /dev/null
+++ b/car-business-server/src/main/java/com/god/base/server/consumer/KafkaSub.java
@@ -0,0 +1,16 @@
+package com.god.base.server.consumer;
+
+import com.god.common.redis.service.RedisService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.List;
+
+/**
+ * 拉取车辆信息
+ *
+ * @author LouZhiSHuo
+ * @Date 2023/11/27 13:36
+ **/
+public class KafkaSub {
+
+}
diff --git a/car-business-server/src/main/java/com/god/base/server/controller/AlarmController.java b/car-business-server/src/main/java/com/god/base/server/controller/AlarmController.java
new file mode 100644
index 0000000..2135c08
--- /dev/null
+++ b/car-business-server/src/main/java/com/god/base/server/controller/AlarmController.java
@@ -0,0 +1,64 @@
+package com.god.base.server.controller;
+
+import com.god.base.remote.RemoteCarService;
+import com.god.base.server.service.AlarmService;
+import com.god.business.common.domain.BreakDown;
+import com.god.business.common.domain.request.ReqAddBreakDown;
+import com.god.common.core.domain.Result;
+import com.god.common.log.annotation.Log;
+import com.god.common.log.enums.BusinessType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * 预警控制层
+ *
+ * @author LouZhiSHuo
+ * @Date 2023/11/27 10:50
+ **/
+@Controller
+public class AlarmController {
+
+ @Autowired
+ private AlarmService alarmService;
+
+
+ /**
+ * 围栏报警
+ * @param carVinId
+ * @return
+ */
+ @GetMapping("/startAlarm")
+ @Log(title = "围栏报警业务")
+ public Result rangeWarning(@RequestParam("carVinId") String carVinId) {
+ //围栏报警业务实现
+ alarmService.startAlarm(carVinId);
+ return Result.success();
+ }
+
+
+ /**
+ * 添加故障记录
+ * @param reqAddBreakDown
+ * @return
+ */
+ @PostMapping("/insertBreakDownLogs")
+ @Log(title = "添加车辆预警记录",businessType = BusinessType.INSERT)
+ public Result insertBreakDownLogs(@RequestBody ReqAddBreakDown reqAddBreakDown) {
+ //添加车辆预警记录
+ alarmService.save(BreakDown.insertBreakDown(reqAddBreakDown));
+
+ return Result.success();
+ }
+
+
+
+
+
+
+
+}
diff --git a/car-business-server/src/main/java/com/god/base/server/mapper/AlarmMapper.java b/car-business-server/src/main/java/com/god/base/server/mapper/AlarmMapper.java
new file mode 100644
index 0000000..a8c049f
--- /dev/null
+++ b/car-business-server/src/main/java/com/god/base/server/mapper/AlarmMapper.java
@@ -0,0 +1,14 @@
+package com.god.base.server.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.god.business.common.domain.BreakDown;
+
+/**
+ * 预计故障mapper
+ *
+ * @author LouZhiSHuo
+ * @Date 2023/11/29 15:02
+ **/
+public interface AlarmMapper extends BaseMapper {
+
+}
diff --git a/car-business-server/src/main/java/com/god/base/server/service/AlarmService.java b/car-business-server/src/main/java/com/god/base/server/service/AlarmService.java
new file mode 100644
index 0000000..d410612
--- /dev/null
+++ b/car-business-server/src/main/java/com/god/base/server/service/AlarmService.java
@@ -0,0 +1,17 @@
+package com.god.base.server.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.god.business.common.domain.BreakDown;
+
+/**
+ * @author LouZhiSHuo
+ * @Date 2023/11/27 20:40
+ **/
+public interface AlarmService extends IService{
+
+ /**
+ * 围栏报警业务实现
+ * @param carVinId
+ */
+ void startAlarm(String carVinId);
+}
diff --git a/car-business-server/src/main/java/com/god/base/server/service/impl/AlarmServiceImpl.java b/car-business-server/src/main/java/com/god/base/server/service/impl/AlarmServiceImpl.java
new file mode 100644
index 0000000..c0d07c0
--- /dev/null
+++ b/car-business-server/src/main/java/com/god/base/server/service/impl/AlarmServiceImpl.java
@@ -0,0 +1,134 @@
+package com.god.base.server.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.god.base.common.domain.Fence;
+import com.god.base.remote.RemoteCarService;
+import com.god.base.server.common.domain.Car;
+import com.god.base.server.mapper.AlarmMapper;
+import com.god.base.server.service.AlarmService;
+import com.god.base.server.util.FenceAlgorithm;
+import com.god.business.common.domain.BreakDown;
+
+import com.god.common.core.domain.Result;
+
+import lombok.extern.log4j.Log4j2;
+
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.awt.geom.Point2D;
+import java.util.*;
+
+/**
+ * @author LouZhiSHuo
+ * @Date 2023/11/27 20:40
+ **/
+@Service
+@Log4j2
+public class AlarmServiceImpl extends ServiceImpl implements AlarmService {
+
+ @Resource
+ private RemoteCarService remoteCarService;
+
+ @Resource
+ private FenceAlgorithm fenceAlgorithm;
+
+
+
+
+
+
+ /**
+ * 围栏报警业务实现
+ * @param carVinId
+ */
+ @Override
+ public void startAlarm(String carVinId) {
+ //调用 根据车辆VIN查询车辆信息 carController/list
+ Result carResult = remoteCarService.list(carVinId);
+ //车辆信息
+ Car car = carResult.getData();
+ //根据车辆信息查询该车辆绑定的围栏Id
+ long fenceId = car.getFenceId();
+ //获取到围栏Id,根据围栏Id查询围栏 信息 FenceController/fenceById
+ //Redis中的围栏坐标信息格式: " 经度,维度; "
+ Result fenceResult = remoteCarService.fenceById((int) fenceId);
+ //围栏信息
+ Fence fence = fenceResult.getData();
+ //获取该围栏行驶状态
+ Integer driveStatus = fence.getDriveStatus();// 1-驶入 2-驶出
+ //获取围栏坐标串
+ String fenceLocation = fence.getFenceLocation(); //116.357183,39.926512; 116.394348,39.909989; 116.34757,39.90617;
+ String[] split = fenceLocation.split(";");
+
+ List polygon = new ArrayList<>();
+
+ for (int i = 0; i < split.length; i++) {
+ String[] splits = split[i].split(",");
+
+ Point2D.Double aDouble = new Point2D.Double();
+
+ for (int j = 0; j < splits.length; j++) {
+ if (j == 0){
+ aDouble.x = Double.parseDouble(splits[j]);
+ } else {
+ aDouble.y = Double.parseDouble(splits[j]);
+ }
+ }
+ polygon.add(aDouble);
+ }
+
+ //消费消息的消息
+
+ //算法计算
+ //假设当前经纬度
+ Point2D.Double car_one = new Point2D.Double(39.9125, 116.3917);
+ boolean ptInPoly = fenceAlgorithm.computeFence(car_one, polygon);
+
+ if (driveStatus == 1){
+ //驶入
+ //围栏区域外 -正常 进入后报警
+ if (ptInPoly) {
+ //报警
+ //存储消息到redis
+
+ } else {
+ log.info("当前车辆:{},正常行驶中~~~~~~",carVinId);
+ }
+
+ } else {
+ //驶出
+ //围栏区域内 -正常 驶出报警
+
+ if (!ptInPoly) {
+ //报警
+ } else {
+ log.info("当前车辆:{},正常行驶中~~~~~~",carVinId);
+ }
+
+ }
+ //判断围栏状态
+ //1-驶入
+ //判断坐标是否在范围 外围:不做操作 , 坐标进入围栏内: 发送消息给围栏内的用户(使用RabbitMq)
+ //2-驶出
+ //判断坐标是否在范围 外围:发送消息给围栏内的用户(使用RabbitMq) , 坐标在围栏内:不做操作
+ }
+
+
+ /**
+ * 添加故障记录
+ * @param entity
+ * @return
+ */
+ @Override
+ public boolean save(BreakDown entity) {
+ entity.setCreateTime(new Date());
+
+ boolean save = super.save(entity);
+ if (!save) {
+ throw new RuntimeException("故障记录添加失败");
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/car-business-server/src/main/java/com/god/base/server/util/FenceAlgorithm.java b/car-business-server/src/main/java/com/god/base/server/util/FenceAlgorithm.java
new file mode 100644
index 0000000..e79e1ff
--- /dev/null
+++ b/car-business-server/src/main/java/com/god/base/server/util/FenceAlgorithm.java
@@ -0,0 +1,124 @@
+package com.god.base.server.util;
+
+import org.springframework.stereotype.Component;
+
+import java.awt.geom.Point2D;
+import java.util.List;
+
+/**
+ * 围栏算法
+ *
+ * @author LouZhiSHuo
+ * @Date 2023/11/28 19:12
+ **/
+@Component
+public class FenceAlgorithm {
+
+
+ /**
+ * 围栏区域内计算
+ * @param point
+ * @param pts
+ * @return
+ */
+ public boolean computeFence(Point2D.Double point, List pts){
+ //围栏顶点坐标数
+ int fenceSize = pts.size();
+
+ //如果点位在多边形的顶点或边上,也算在多边形内部,返回true
+ boolean boundDrVertex = true;
+
+ //射线与多边形相交次数
+ int intersectCount = 0;
+
+ ///浮点型计时候的容差
+ double precision = 2e-10;
+
+ //相邻两点位置
+ Point2D.Double fenceOne,fenceTwo;
+
+ //确认当前点
+ Point2D.Double p = point;
+
+ //起点
+ fenceOne = pts.get(0);
+
+ for (int i =0;i Math.max(fenceOne.x,fenceTwo.x)) {
+ //无交点
+ fenceOne = fenceTwo;
+ //继续判断
+ continue;
+ }
+
+ if (p.x > Math.min(fenceOne.x,fenceTwo.x) && p.x < Math.max(fenceOne.x,fenceTwo.x)) {
+ //当前点在线段于X轴上的投影内
+ if (p.y <= Math.max(fenceOne.y,fenceTwo.y)) {
+ //当前点的y坐标小于线段对y轴投影的最大值
+ if (fenceOne.x == fenceTwo.x && p.y >= Math.min(fenceOne.y,fenceTwo.y)) {
+ //当前点垂直在x轴边上
+ return boundDrVertex;
+ }
+
+ if (fenceOne.y == fenceTwo.y) {
+ //平行于X轴
+ if (fenceOne.y == p.y) {
+ //位于当前围栏的边上
+ return boundDrVertex;
+ } else {
+ //反之相交次数+1
+ ++intersectCount;
+ }
+
+ } else {
+ //非水平情况
+ double v = (p.x - fenceOne.x) * (fenceTwo.y - fenceOne.y) / (fenceTwo.x - fenceOne.x) + fenceOne.y;
+ //围栏某条边上
+ if (Math.abs(p.y - v) < precision) {
+ return boundDrVertex;
+ }
+
+ if (p.y < v) {
+ //相交次数+1
+ ++intersectCount;
+ }
+ }
+ }
+ } else {
+ //当前带你不在线段投影到x轴的区间中
+ if (p.x == fenceTwo.x && p.y <= fenceTwo.y) {
+ //位于x坐标对应的平行于y轴上的线上的低于终点y的坐标
+ Point2D.Double temp = pts.get((i + 1) % fenceSize);
+ if (p.x >= Math.min(fenceOne.x , temp.x) && p.x <= Math.max(fenceOne.x,temp.x)) {
+ //若当前点的x坐标位于fenceOne 和 temp 组成的线段关于x轴的投影中
+ //则记为该点的射线只穿过端点一次
+ //那么相交+1
+ ++intersectCount;
+ } else {
+ //若当前坐标的x不包含 fenceOne 和 temp 组成的线段关于x轴的投影中 , 则点射线通过的两条线段组成了一个弯折的部分,
+ //那么记为该点的射线穿过了端点两次
+ intersectCount += 2;
+ }
+ }
+ }
+ fenceOne = fenceTwo;
+ }
+ if (intersectCount % 2 == 0) {
+ //偶数在围栏外面
+ return false;
+ } else {
+ //奇数在围栏内
+ return true;
+ }
+ }
+}