From 04a270ff2b5919127524913ffc45c59c6c6d9201 Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Wed, 22 Apr 2026 14:22:30 +0800
Subject: [PATCH] 增加部门区域管理业务和无人机实时位置所属区域推送

---
 ard-common/src/main/java/com/ard/common/constant/RegionConstants.java                   |   15 
 ard-work/src/main/java/com/ard/work/service/impl/UavRegionDetectService.java            |   43 +
 ard-system/src/main/resources/mapper/data/ArdDeptRegionMapper.xml                       |  210 +++++++++
 ard-admin/src/main/java/com/ard/web/controller/data/ArdDeptRegionController.java        |  145 ++++++
 ard-system/src/main/java/com/ard/data/region/domain/ConflictRegion.java                 |   17 
 ard-system/src/main/java/com/ard/data/region/domain/Coordinate.java                     |   40 +
 ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java      |   10 
 ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java                     |   16 
 ard-system/src/main/java/com/ard/data/region/service/IArdDeptRegionService.java         |   64 ++
 ard-system/src/main/java/com/ard/data/region/service/impl/ArdDeptRegionServiceImpl.java |  298 +++++++++++++
 ard-system/src/main/java/com/ard/data/region/domain/ArdDeptRegion.java                  |   62 ++
 ard-system/src/main/java/com/ard/data/region/domain/ConflictCheckResult.java            |   18 
 ard-system/src/main/java/com/ard/data/region/domain/PointQueryDTO.java                  |   18 
 ard-system/src/main/java/com/ard/data/region/utils/RegionCoordinateUtils.java           |  154 +++++++
 ard-work/src/main/java/com/ard/work/service/impl/UavPositionChangeService.java          |   89 ++++
 ard-work/src/main/java/com/ard/work/domain/cache/UavRegionCache.java                    |   30 +
 ard-system/src/main/java/com/ard/data/region/mapper/ArdDeptRegionMapper.java            |   89 ++++
 17 files changed, 1,311 insertions(+), 7 deletions(-)

diff --git a/ard-admin/src/main/java/com/ard/web/controller/data/ArdDeptRegionController.java b/ard-admin/src/main/java/com/ard/web/controller/data/ArdDeptRegionController.java
new file mode 100644
index 0000000..424a872
--- /dev/null
+++ b/ard-admin/src/main/java/com/ard/web/controller/data/ArdDeptRegionController.java
@@ -0,0 +1,145 @@
+package com.ard.web.controller.data;
+
+import com.ard.common.annotation.Log;
+import com.ard.common.core.controller.BaseController;
+import com.ard.common.core.domain.AjaxResult;
+import com.ard.common.core.page.TableDataInfo;
+import com.ard.common.enums.BusinessType;
+import com.ard.common.utils.poi.ExcelUtil;
+import com.ard.data.region.domain.ArdDeptRegion;
+import com.ard.data.region.domain.ConflictCheckResult;
+import com.ard.data.region.domain.PointQueryDTO;
+import com.ard.data.region.service.IArdDeptRegionService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 部门区域Controller
+ *
+ * @author ruoyi
+ */
+@Api(tags = "部门区域接口")
+@RestController
+@RequestMapping("/data/region")
+public class ArdDeptRegionController extends BaseController {
+    @Autowired
+    private IArdDeptRegionService ardDeptRegionService;
+
+    /**
+     * 查询部门区域列表
+     */
+    @ApiOperation("查询部门区域列表")
+    @PreAuthorize("@ss.hasPermi('data:region:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(ArdDeptRegion ardDeptRegion) {
+        startPage();
+        List<ArdDeptRegion> list = ardDeptRegionService.selectArdDeptRegionList(ardDeptRegion);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出部门区域列表
+     */
+    @ApiOperation("导出部门区域列表")
+    @PreAuthorize("@ss.hasPermi('data:region:export')")
+    @Log(title = "部门区域", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(ArdDeptRegion ardDeptRegion) {
+        List<ArdDeptRegion> list = ardDeptRegionService.selectArdDeptRegionList(ardDeptRegion);
+        ExcelUtil<ArdDeptRegion> util = new ExcelUtil<ArdDeptRegion>(ArdDeptRegion.class);
+        return util.exportExcel(list, "部门区域数据");
+    }
+
+    /**
+     * 获取部门区域详细信息
+     */
+    @ApiOperation("获取部门区域详细信息")
+    @PreAuthorize("@ss.hasPermi('data:region:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return success(ardDeptRegionService.selectArdDeptRegionById(id));
+    }
+
+    /**
+     * 检查区域冲突(画区域时调用)
+     */
+    @ApiOperation("检查区域冲突")
+    @PostMapping("/check")
+    public AjaxResult checkConflict(@Valid @RequestBody ArdDeptRegion ardDeptRegion) {
+        ConflictCheckResult result = ardDeptRegionService.checkConflict(ardDeptRegion);
+        if (result.getHasConflict()) {
+            return AjaxResult.error(result.getMessage(), result);
+        }
+        return AjaxResult.success(result.getMessage(), result);
+    }
+
+    /**
+     * 新增部门区域
+     */
+    @ApiOperation("新增部门区域")
+    @PreAuthorize("@ss.hasPermi('data:region:add')")
+    @Log(title = "部门区域", businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    public AjaxResult add(@Valid @RequestBody ArdDeptRegion ardDeptRegion) {
+        if (!ardDeptRegionService.checkRegionNameUnique(ardDeptRegion)) {
+            return error("新增区域'" + ardDeptRegion.getRegionName() + "'失败,区域名称已存在");
+        }
+        return toAjax(ardDeptRegionService.insertArdDeptRegion(ardDeptRegion));
+    }
+
+    /**
+     * 修改部门区域
+     */
+    @ApiOperation("修改部门区域")
+    @PreAuthorize("@ss.hasPermi('data:region:edit')")
+    @Log(title = "部门区域", businessType = BusinessType.UPDATE)
+    @PutMapping("/edit")
+    public AjaxResult edit(@RequestBody ArdDeptRegion ardDeptRegion) {
+        if (!ardDeptRegionService.checkRegionNameUnique(ardDeptRegion)) {
+            return error("修改区域'" + ardDeptRegion.getRegionName() + "'失败,区域名称已存在");
+        }
+        return toAjax(ardDeptRegionService.updateArdDeptRegion(ardDeptRegion));
+    }
+
+    /**
+     * 修改状态(启用/禁用)
+     */
+    @ApiOperation("修改状态")
+    @PreAuthorize("@ss.hasPermi('data:region:edit')")
+    @Log(title = "部门区域", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public AjaxResult changeStatus(@RequestBody ArdDeptRegion ardDeptRegion)
+    {
+        return toAjax(ardDeptRegionService.updateStatus(ardDeptRegion.getId(), ardDeptRegion.getStatus()));
+    }
+
+    /**
+     * 删除部门区域
+     */
+    @ApiOperation("删除部门区域")
+    @PreAuthorize("@ss.hasPermi('data:region:remove')")
+    @Log(title = "部门区域", businessType = BusinessType.DELETE)
+    @DeleteMapping("/remove/{ids}")
+    public AjaxResult remove(@PathVariable String[] ids) {
+        return toAjax(ardDeptRegionService.deleteArdDeptRegionByIds(ids));
+    }
+
+    /**
+     * 根据坐标查询所属区域
+     */
+    @ApiOperation("根据坐标查询所属区域")
+    @PostMapping("/getByPoint")
+    public AjaxResult getRegionByPoint(@RequestBody PointQueryDTO query) {
+        ArdDeptRegion region = ardDeptRegionService.selectRegionByPoint(query.getLat(), query.getLng());
+        if (region == null) {
+            return error("该坐标不在任何区域内");
+        }
+        return success(region);
+    }
+}
\ No newline at end of file
diff --git a/ard-common/src/main/java/com/ard/common/constant/RegionConstants.java b/ard-common/src/main/java/com/ard/common/constant/RegionConstants.java
new file mode 100644
index 0000000..da50de8
--- /dev/null
+++ b/ard-common/src/main/java/com/ard/common/constant/RegionConstants.java
@@ -0,0 +1,15 @@
+package com.ard.common.constant;
+
+/**
+ * 区域常量
+ * 
+ * @author ruoyi
+ */
+public class RegionConstants
+{
+    /** 区域名称唯一 */
+    public static final boolean UNIQUE = true;
+    
+    /** 区域名称不唯一 */
+    public static final boolean NOT_UNIQUE = false;
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/region/domain/ArdDeptRegion.java b/ard-system/src/main/java/com/ard/data/region/domain/ArdDeptRegion.java
new file mode 100644
index 0000000..11e5a3d
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/region/domain/ArdDeptRegion.java
@@ -0,0 +1,62 @@
+package com.ard.data.region.domain;
+
+import com.ard.common.annotation.Excel;
+import com.ard.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.List;
+
+/**
+ * 部门区域对象 ard_dept_region
+ * 
+ * @author ruoyi
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ArdDeptRegion extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 区域ID */
+    private Long id;
+
+    /** 部门ID */
+    @Excel(name = "部门ID")
+    private Long deptId;
+
+    /** 区域名称 */
+    @Excel(name = "区域名称")
+    private String regionName;
+
+    /** 区域颜色(十六进制颜色码) */
+    @Excel(name = "区域颜色")
+    private String regionColor;
+
+    /** 区域类型(POINT/LINE/POLYGON,非数据库字段,自动判断) */
+    private String regionType;
+
+    /** 区域边界WKT(非数据库字段,用于传输) */
+    private String boundaryWkt;
+
+    /** 中心纬度 */
+    @Excel(name = "中心纬度")
+    private Double centerLat;
+
+    /** 中心经度 */
+    @Excel(name = "中心经度")
+    private Double centerLng;
+
+    /** 面积(平方米) */
+    @Excel(name = "面积")
+    private Double areaSize;
+
+    /** 坐标集合(非数据库字段,用于接收前端坐标) */
+    private List<Coordinate> coordinates;
+
+    /** 状态(0正常 1停用) */
+    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
+    private String status;
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/region/domain/ConflictCheckResult.java b/ard-system/src/main/java/com/ard/data/region/domain/ConflictCheckResult.java
new file mode 100644
index 0000000..26303ad
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/region/domain/ConflictCheckResult.java
@@ -0,0 +1,18 @@
+package com.ard.data.region.domain;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 冲突检查结果
+ * 
+ * @author ruoyi
+ */
+@Data
+public class ConflictCheckResult
+{
+    private Boolean hasConflict;
+    private String message;
+    private List<ConflictRegion> conflictRegions;
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/region/domain/ConflictRegion.java b/ard-system/src/main/java/com/ard/data/region/domain/ConflictRegion.java
new file mode 100644
index 0000000..34e7837
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/region/domain/ConflictRegion.java
@@ -0,0 +1,17 @@
+package com.ard.data.region.domain;
+
+import lombok.Data;
+
+/**
+ * 冲突区域信息
+ * 
+ * @author ruoyi
+ */
+@Data
+public class ConflictRegion
+{
+    private Long id;
+    private String regionName;
+    private String regionColor;
+    private String relationship;
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/region/domain/Coordinate.java b/ard-system/src/main/java/com/ard/data/region/domain/Coordinate.java
new file mode 100644
index 0000000..36e6b43
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/region/domain/Coordinate.java
@@ -0,0 +1,40 @@
+package com.ard.data.region.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 坐标实体类
+ * 
+ * @author ruoyi
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Coordinate
+{
+    /** 经度 */
+    private Double lng;
+    
+    /** 纬度 */
+    private Double lat;
+    
+    /**
+     * 转换为 MySQL 需要的格式 (纬度 经度)
+     */
+    public String toLatLngString()
+    {
+        return String.format("%.8f %.8f", lat, lng);
+    }
+    
+    /**
+     * 验证坐标是否有效
+     */
+    public boolean isValid()
+    {
+        return lng != null && lat != null 
+            && lng >= -180 && lng <= 180 
+            && lat >= -90 && lat <= 90;
+    }
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/region/domain/PointQueryDTO.java b/ard-system/src/main/java/com/ard/data/region/domain/PointQueryDTO.java
new file mode 100644
index 0000000..4606d52
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/region/domain/PointQueryDTO.java
@@ -0,0 +1,18 @@
+package com.ard.data.region.domain;
+
+import lombok.Data;
+
+/**
+ * 坐标查询参数
+ * 
+ * @author ruoyi
+ */
+@Data
+public class PointQueryDTO
+{
+    /** 纬度 */
+    private Double lat;
+    
+    /** 经度 */
+    private Double lng;
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/region/mapper/ArdDeptRegionMapper.java b/ard-system/src/main/java/com/ard/data/region/mapper/ArdDeptRegionMapper.java
new file mode 100644
index 0000000..207ab75
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/region/mapper/ArdDeptRegionMapper.java
@@ -0,0 +1,89 @@
+package com.ard.data.region.mapper;
+
+import com.ard.data.region.domain.ArdDeptRegion;
+import com.ard.data.region.domain.ConflictRegion;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 部门区域Mapper接口
+ * 
+ * @author ruoyi
+ */
+public interface ArdDeptRegionMapper
+{
+    /**
+     * 查询部门区域
+     */
+    public ArdDeptRegion selectArdDeptRegionById(Long id);
+    
+    /**
+     * 查询部门区域列表
+     */
+    public List<ArdDeptRegion> selectArdDeptRegionList(ArdDeptRegion ardDeptRegion);
+    
+    /**
+     * 新增部门区域
+     */
+    public int insertArdDeptRegion(ArdDeptRegion ardDeptRegion);
+    
+    /**
+     * 修改部门区域
+     */
+    public int updateArdDeptRegion(ArdDeptRegion ardDeptRegion);
+
+    /**
+     * 启用/禁用区域
+     */
+    public int updateStatus(@Param("id") Long id, @Param("status") String status);
+
+    /**
+     * 删除部门区域
+     */
+    public int deleteArdDeptRegionById(Long id);
+    
+    /**
+     * 批量删除部门区域
+     */
+    public int deleteArdDeptRegionByIds(String[] ids);
+
+    /**
+     * 根据区域名称查询区域(用于唯一性校验)
+     */
+    public ArdDeptRegion checkRegionNameUnique(@Param("regionName") String regionName);
+
+
+    /**
+     * 检查点冲突(1个坐标)
+     */
+    public List<ConflictRegion> checkPointConflict(@Param("deptId") Long deptId,
+                                                   @Param("wkt") String wkt,
+                                                   @Param("excludeId") Long excludeId);
+
+    /**
+     * 检查线冲突(2个坐标)
+     */
+    public List<ConflictRegion> checkLineConflict(@Param("deptId") Long deptId,
+                                                  @Param("wkt") String wkt,
+                                                  @Param("excludeId") Long excludeId);
+
+    /**
+     * 检查面冲突(3个以上坐标)
+     */
+    public List<ConflictRegion> checkPolygonConflict(@Param("deptId") Long deptId,
+                                                     @Param("wkt") String wkt,
+                                                     @Param("excludeId") Long excludeId);
+    
+    /**
+     * 获取边界WKT
+     */
+    public String selectBoundaryWkt(@Param("id") Long id);
+    
+    /**
+     * 根据坐标查询所属区域
+     */
+    public ArdDeptRegion selectRegionByPoint(@Param("pointWkt") String pointWkt);
+    
+
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/region/service/IArdDeptRegionService.java b/ard-system/src/main/java/com/ard/data/region/service/IArdDeptRegionService.java
new file mode 100644
index 0000000..31ba676
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/region/service/IArdDeptRegionService.java
@@ -0,0 +1,64 @@
+package com.ard.data.region.service;
+
+import com.ard.data.region.domain.ArdDeptRegion;
+import com.ard.data.region.domain.ConflictCheckResult;
+
+import java.util.List;
+
+/**
+ * 部门区域Service接口
+ * 
+ * @author ruoyi
+ */
+public interface IArdDeptRegionService
+{
+    /**
+     * 查询部门区域
+     */
+    public ArdDeptRegion selectArdDeptRegionById(Long id);
+    
+    /**
+     * 查询部门区域列表
+     */
+    public List<ArdDeptRegion> selectArdDeptRegionList(ArdDeptRegion ardDeptRegion);
+    
+    /**
+     * 新增部门区域
+     */
+    public int insertArdDeptRegion(ArdDeptRegion ardDeptRegion);
+    
+    /**
+     * 修改部门区域
+     */
+    public int updateArdDeptRegion(ArdDeptRegion ardDeptRegion);
+
+    /**
+     * 修改状态(启用/禁用)
+     */
+    public int updateStatus(Long id, String status);
+
+    /**
+     * 批量删除部门区域
+     */
+    public int deleteArdDeptRegionByIds(String[] ids);
+    
+    /**
+     * 删除部门区域信息
+     */
+    public int deleteArdDeptRegionById(Long id);
+
+    /**
+     * 校验区域名称是否唯一
+     */
+    public boolean checkRegionNameUnique(ArdDeptRegion ardDeptRegion);
+
+    /**
+     * 检查区域冲突
+     */
+    public ConflictCheckResult checkConflict(ArdDeptRegion ardDeptRegion);
+    
+    /**
+     * 根据坐标查询所属区域
+     */
+    public ArdDeptRegion selectRegionByPoint(Double lat, Double lng);
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/region/service/impl/ArdDeptRegionServiceImpl.java b/ard-system/src/main/java/com/ard/data/region/service/impl/ArdDeptRegionServiceImpl.java
new file mode 100644
index 0000000..43fc008
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/region/service/impl/ArdDeptRegionServiceImpl.java
@@ -0,0 +1,298 @@
+package com.ard.data.region.service.impl;
+
+import com.ard.common.constant.RegionConstants;
+import com.ard.common.utils.DateUtils;
+import com.ard.common.utils.SecurityUtils;
+import com.ard.common.utils.StringUtils;
+import com.ard.data.region.domain.ArdDeptRegion;
+import com.ard.data.region.domain.ConflictCheckResult;
+import com.ard.data.region.domain.ConflictRegion;
+import com.ard.data.region.domain.Coordinate;
+import com.ard.data.region.mapper.ArdDeptRegionMapper;
+import com.ard.data.region.service.IArdDeptRegionService;
+import com.ard.data.region.utils.RegionCoordinateUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 部门区域Service业务层处理
+ *
+ * @author ruoyi
+ */
+@Slf4j
+@Service
+public class ArdDeptRegionServiceImpl implements IArdDeptRegionService
+{
+    @Autowired
+    private ArdDeptRegionMapper ardDeptRegionMapper;
+
+    @Override
+    public ArdDeptRegion selectArdDeptRegionById(Long id)
+    {
+        ArdDeptRegion region = ardDeptRegionMapper.selectArdDeptRegionById(id);
+        if (region != null)
+        {
+            region.setCoordinates(RegionCoordinateUtils.toCoordinates(region.getBoundaryWkt()));
+        }
+        return region;
+    }
+
+    @Override
+    public List<ArdDeptRegion> selectArdDeptRegionList(ArdDeptRegion ardDeptRegion)
+    {
+        List<ArdDeptRegion> list = ardDeptRegionMapper.selectArdDeptRegionList(ardDeptRegion);
+        list.forEach(region -> {
+            region.setCoordinates(RegionCoordinateUtils.toCoordinates(region.getBoundaryWkt()));
+        });
+        return list;
+    }
+
+    /**
+     * 校验区域名称是否唯一
+     */
+    @Override
+    public boolean checkRegionNameUnique(ArdDeptRegion ardDeptRegion)
+    {
+        Long id = StringUtils.isNull(ardDeptRegion.getId()) ? -1L : ardDeptRegion.getId();
+        ArdDeptRegion info = ardDeptRegionMapper.checkRegionNameUnique(ardDeptRegion.getRegionName());
+        if (StringUtils.isNotNull(info) && info.getId().longValue() != id.longValue())
+        {
+            return RegionConstants.NOT_UNIQUE;
+        }
+        return RegionConstants.UNIQUE;
+    }
+
+    /**
+     * 检查区域冲突
+     */
+    @Override
+    public ConflictCheckResult checkConflict(ArdDeptRegion ardDeptRegion)
+    {
+        ConflictCheckResult result = new ConflictCheckResult();
+
+        try
+        {
+            // 1. 验证坐标
+            List<Coordinate> coordinates = ardDeptRegion.getCoordinates();
+            RegionCoordinateUtils.validateCoordinates(coordinates);
+
+            // 2. 根据坐标数量自动判断类型
+            String regionType = RegionCoordinateUtils.getRegionTypeByCoordinates(coordinates);
+            ardDeptRegion.setRegionType(regionType);
+
+            // 3. 转换为WKT
+            String wkt = RegionCoordinateUtils.toWkt(coordinates);
+            ardDeptRegion.setBoundaryWkt(wkt);
+
+            // 4. 根据类型检查冲突
+            List<ConflictRegion> conflicts = null;
+            Long excludeId = ardDeptRegion.getId();
+
+            switch (regionType)
+            {
+                case "POINT":
+                    conflicts = ardDeptRegionMapper.checkPointConflict(
+                            ardDeptRegion.getDeptId(), wkt, excludeId);
+                    break;
+                case "LINE":
+                    conflicts = ardDeptRegionMapper.checkLineConflict(
+                            ardDeptRegion.getDeptId(), wkt, excludeId);
+                    break;
+                case "POLYGON":
+                    conflicts = ardDeptRegionMapper.checkPolygonConflict(
+                            ardDeptRegion.getDeptId(), wkt, excludeId);
+                    break;
+                default:
+                    result.setHasConflict(true);
+                    result.setMessage("不支持的几何类型");
+                    return result;
+            }
+
+            if (!CollectionUtils.isEmpty(conflicts))
+            {
+                result.setHasConflict(true);
+                result.setMessage(String.format("该%s与%d个已有区域存在冲突",
+                        getRegionTypeName(regionType), conflicts.size()));
+                result.setConflictRegions(conflicts);
+            }
+            else
+            {
+                result.setHasConflict(false);
+                result.setMessage("无冲突,可以添加");
+            }
+        }
+        catch (Exception e)
+        {
+            log.error("检查冲突失败", e);
+            result.setHasConflict(true);
+            result.setMessage("检查失败: " + e.getMessage());
+        }
+
+        return result;
+    }
+
+    @Override
+    @Transactional
+    public int insertArdDeptRegion(ArdDeptRegion ardDeptRegion)
+    {
+        // 1. 检查冲突
+        ConflictCheckResult checkResult = checkConflict(ardDeptRegion);
+        if (checkResult.getHasConflict())
+        {
+            throw new RuntimeException(checkResult.getMessage());
+        }
+
+        // 2. 计算中心点和面积
+        List<Coordinate> coordinates = ardDeptRegion.getCoordinates();
+        Coordinate center = RegionCoordinateUtils.calculateCenter(coordinates);
+        ardDeptRegion.setCenterLat(center != null ? center.getLat() : null);
+        ardDeptRegion.setCenterLng(center != null ? center.getLng() : null);
+
+        // 只有面类型计算面积
+        if ("POLYGON".equals(ardDeptRegion.getRegionType()))
+        {
+            ardDeptRegion.setAreaSize(RegionCoordinateUtils.calculateArea(coordinates));
+        }
+        else
+        {
+            ardDeptRegion.setAreaSize(0.0);
+        }
+
+        // 3. 设置默认颜色
+        if (ardDeptRegion.getRegionColor() == null || ardDeptRegion.getRegionColor().isEmpty())
+        {
+            ardDeptRegion.setRegionColor("#3c8dbc");
+        }
+
+        // 4. 插入数据
+        ardDeptRegion.setCreateBy(SecurityUtils.getUsername());
+        ardDeptRegion.setCreateTime(DateUtils.getNowDate());
+        ardDeptRegion.setStatus("0");
+
+        return ardDeptRegionMapper.insertArdDeptRegion(ardDeptRegion);
+    }
+
+    @Override
+    @Transactional
+    public int updateArdDeptRegion(ArdDeptRegion ardDeptRegion)
+    {
+        // 1. 获取原区域
+        ArdDeptRegion oldRegion = selectArdDeptRegionById(ardDeptRegion.getId());
+        if (oldRegion == null)
+        {
+            throw new RuntimeException("区域不存在");
+        }
+
+        // 2. 如果坐标有变化,检查冲突
+        if (ardDeptRegion.getCoordinates() != null && !ardDeptRegion.getCoordinates().isEmpty())
+        {
+            List<Coordinate> coordinates = ardDeptRegion.getCoordinates();
+            RegionCoordinateUtils.validateCoordinates(coordinates);
+
+            String regionType = RegionCoordinateUtils.getRegionTypeByCoordinates(coordinates);
+            ardDeptRegion.setRegionType(regionType);
+
+            String wkt = RegionCoordinateUtils.toWkt(coordinates);
+            ardDeptRegion.setBoundaryWkt(wkt);
+
+            // 根据类型检查冲突
+            List<ConflictRegion> conflicts = null;
+
+            switch (regionType)
+            {
+                case "POINT":
+                    conflicts = ardDeptRegionMapper.checkPointConflict(
+                            ardDeptRegion.getDeptId(), wkt, ardDeptRegion.getId());
+                    break;
+                case "LINE":
+                    conflicts = ardDeptRegionMapper.checkLineConflict(
+                            ardDeptRegion.getDeptId(), wkt, ardDeptRegion.getId());
+                    break;
+                case "POLYGON":
+                    conflicts = ardDeptRegionMapper.checkPolygonConflict(
+                            ardDeptRegion.getDeptId(), wkt, ardDeptRegion.getId());
+                    break;
+            }
+
+            if (!CollectionUtils.isEmpty(conflicts))
+            {
+                throw new RuntimeException("更新后的区域与已有区域存在冲突");
+            }
+
+            // 重新计算中心点和面积
+            Coordinate center = RegionCoordinateUtils.calculateCenter(coordinates);
+            ardDeptRegion.setCenterLat(center != null ? center.getLat() : null);
+            ardDeptRegion.setCenterLng(center != null ? center.getLng() : null);
+
+            if ("POLYGON".equals(regionType))
+            {
+                ardDeptRegion.setAreaSize(RegionCoordinateUtils.calculateArea(coordinates));
+            }
+            else
+            {
+                ardDeptRegion.setAreaSize(0.0);
+            }
+        }
+
+        ardDeptRegion.setUpdateBy(SecurityUtils.getUsername());
+        ardDeptRegion.setUpdateTime(DateUtils.getNowDate());
+
+        return ardDeptRegionMapper.updateArdDeptRegion(ardDeptRegion);
+    }
+
+    /**
+     * 修改状态(启用/禁用)
+     */
+    @Override
+    public int updateStatus(Long id, String status)
+    {
+        // 检查区域是否存在
+        ArdDeptRegion region = selectArdDeptRegionById(id);
+        if (region == null)
+        {
+            throw new RuntimeException("区域不存在");
+        }
+
+        // 更新状态
+        return ardDeptRegionMapper.updateStatus(id, status);
+    }
+
+    @Override
+    public ArdDeptRegion selectRegionByPoint(Double lat, Double lng)
+    {
+        String pointWkt = String.format("POINT(%f %f)", lat, lng);
+        return ardDeptRegionMapper.selectRegionByPoint(pointWkt);
+    }
+
+    @Override
+    public int deleteArdDeptRegionByIds(String[] ids)
+    {
+        return ardDeptRegionMapper.deleteArdDeptRegionByIds(ids);
+    }
+
+    @Override
+    public int deleteArdDeptRegionById(Long id)
+    {
+        return ardDeptRegionMapper.deleteArdDeptRegionById(id);
+    }
+
+    /**
+     * 获取区域类型中文名
+     */
+    private String getRegionTypeName(String type)
+    {
+        switch (type)
+        {
+            case "POINT": return "点";
+            case "LINE": return "线";
+            case "POLYGON": return "面";
+            default: return type;
+        }
+    }
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/region/utils/RegionCoordinateUtils.java b/ard-system/src/main/java/com/ard/data/region/utils/RegionCoordinateUtils.java
new file mode 100644
index 0000000..602ad4d
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/region/utils/RegionCoordinateUtils.java
@@ -0,0 +1,154 @@
+package com.ard.data.region.utils;
+
+import com.ard.data.region.domain.Coordinate;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 区域坐标工具类
+ *
+ * @author ruoyi
+ */
+public class RegionCoordinateUtils {
+    /**
+     * 根据坐标数量自动判断区域类型
+     *
+     * @param coordinates 坐标集合
+     * @return POINT/LINE/POLYGON
+     */
+    public static String getRegionTypeByCoordinates(List<Coordinate> coordinates) {
+        if (CollectionUtils.isEmpty(coordinates)) {
+            throw new RuntimeException("坐标集合不能为空");
+        }
+
+        int size = coordinates.size();
+        if (size == 1) {
+            return "POINT";
+        } else if (size == 2) {
+            return "LINE";
+        } else {
+            return "POLYGON";
+        }
+    }
+
+    /**
+     * 将坐标集合转换为 WKT 格式(自动判断类型)
+     */
+    public static String toWkt(List<Coordinate> coordinates) {
+        if (CollectionUtils.isEmpty(coordinates)) {
+            throw new RuntimeException("坐标集合不能为空");
+        }
+
+        int size = coordinates.size();
+
+        if (size == 1) {
+            // 点
+            Coordinate coord = coordinates.get(0);
+            return String.format("POINT(%s)", coord.toLatLngString());
+        } else if (size == 2) {
+            // 线
+            List<String> points = coordinates.stream()
+                    .map(Coordinate::toLatLngString)
+                    .collect(Collectors.toList());
+            return String.format("LINESTRING(%s, %s)", points.get(0), points.get(1));
+        } else {
+            // 面(多边形)
+            List<String> points = coordinates.stream()
+                    .map(Coordinate::toLatLngString)
+                    .collect(Collectors.toList());
+            String allPoints = String.join(", ", points);
+            String firstPoint = points.get(0);
+            return String.format("POLYGON((%s, %s))", allPoints, firstPoint);
+        }
+    }
+
+    /**
+     * 将 WKT 字符串转换为坐标集合
+     *
+     * @param wkt 如:POLYGON((45.718839 126.670721, 45.708756 126.670151, ...))
+     * @return 坐标集合
+     */
+    public static List<Coordinate> toCoordinates(String wkt) {
+        List<Coordinate> coordinates = new ArrayList<>();
+        if (wkt == null || wkt.isEmpty()) {
+            return coordinates;
+        }
+        // 提取括号内的坐标字符串
+        String coordStr = wkt;
+        if (wkt.startsWith("POLYGON")) {
+            coordStr = wkt.substring(wkt.indexOf("((") + 2, wkt.lastIndexOf("))"));
+        } else if (wkt.startsWith("LINESTRING")) {
+            coordStr = wkt.substring(wkt.indexOf("(") + 1, wkt.lastIndexOf(")"));
+        } else if (wkt.startsWith("POINT")) {
+            coordStr = wkt.substring(wkt.indexOf("(") + 1, wkt.lastIndexOf(")"));
+        }
+
+        // 分割坐标点
+        String[] points = coordStr.split(",");
+        for (String point : points) {
+            String[] lngLat = point.trim().split(" ");
+            if (lngLat.length >= 2) {
+                Double lat = Double.parseDouble(lngLat[0]);
+                Double lng = Double.parseDouble(lngLat[1]);
+                coordinates.add(new Coordinate(lng, lat));
+            }
+        }
+        return coordinates;
+    }
+
+    /**
+     * 计算中心点
+     */
+    public static Coordinate calculateCenter(List<Coordinate> coordinates) {
+        if (CollectionUtils.isEmpty(coordinates)) {
+            return null;
+        }
+        double sumLng = 0;
+        double sumLat = 0;
+        for (Coordinate coord : coordinates) {
+            sumLng += coord.getLng();
+            sumLat += coord.getLat();
+        }
+        return new Coordinate(sumLng / coordinates.size(), sumLat / coordinates.size());
+    }
+
+    /**
+     * 计算面积(仅面类型有效)
+     */
+    public static double calculateArea(List<Coordinate> coordinates) {
+        if (coordinates == null || coordinates.size() < 3) {
+            return 0;
+        }
+
+        double area = 0;
+        int n = coordinates.size();
+        for (int i = 0; i < n; i++) {
+            Coordinate current = coordinates.get(i);
+            Coordinate next = coordinates.get((i + 1) % n);
+            area += current.getLng() * next.getLat() - next.getLng() * current.getLat();
+        }
+        area = Math.abs(area) / 2;
+
+        double lat = coordinates.get(0).getLat();
+        double meterPerDegree = 111000 * Math.cos(Math.toRadians(lat));
+        return area * meterPerDegree * 111000;
+    }
+
+    /**
+     * 验证坐标集合
+     */
+    public static void validateCoordinates(List<Coordinate> coordinates) {
+        if (CollectionUtils.isEmpty(coordinates)) {
+            throw new RuntimeException("坐标集合不能为空");
+        }
+
+        for (Coordinate coord : coordinates) {
+            if (!coord.isValid()) {
+                throw new RuntimeException("无效坐标");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ard-system/src/main/resources/mapper/data/ArdDeptRegionMapper.xml b/ard-system/src/main/resources/mapper/data/ArdDeptRegionMapper.xml
new file mode 100644
index 0000000..d9bb961
--- /dev/null
+++ b/ard-system/src/main/resources/mapper/data/ArdDeptRegionMapper.xml
@@ -0,0 +1,210 @@
+<?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.ard.data.region.mapper.ArdDeptRegionMapper">
+
+    <resultMap type="ArdDeptRegion" id="ArdDeptRegionResult">
+        <id     property="id"          column="id"          />
+        <result property="deptId"      column="dept_id"     />
+        <result property="regionName"  column="region_name" />
+        <result property="regionColor" column="region_color" />
+        <result property="centerLat"   column="center_lat"  />
+        <result property="centerLng"   column="center_lng"  />
+        <result property="areaSize"    column="area_size"   />
+        <result property="status"      column="status"      />
+        <result property="createBy"    column="create_by"   />
+        <result property="createTime"  column="create_time" />
+        <result property="updateBy"    column="update_by"   />
+        <result property="updateTime"  column="update_time" />
+        <result property="remark"      column="remark"      />
+    </resultMap>
+
+    <sql id="selectArdDeptRegionVo">
+        SELECT r.id, r.dept_id, r.region_name, r.region_color,
+               ST_AsText(r.region_boundary) as boundary_wkt,
+               r.center_lat, r.center_lng, r.area_size,
+               r.status, r.create_by, r.create_time, r.update_by, r.update_time, r.remark,
+               d.dept_name
+        FROM ard_dept_region r
+                 LEFT JOIN sys_dept d ON r.dept_id = d.dept_id
+    </sql>
+
+    <!-- 查询部门区域列表(查询所有,不过滤状态) -->
+    <select id="selectArdDeptRegionList" parameterType="ArdDeptRegion" resultMap="ArdDeptRegionResult">
+        <include refid="selectArdDeptRegionVo"/>
+        <where>
+            <if test="deptId != null"> AND r.dept_id = #{deptId}</if>
+            <if test="regionName != null and regionName != ''"> AND r.region_name like concat('%', #{regionName}, '%')</if>
+            <if test="status != null and status != ''"> AND r.status = #{status}</if>
+        </where>
+        ORDER BY r.create_time DESC
+    </select>
+
+    <!-- 查询部门区域(根据ID,查询所有) -->
+    <select id="selectArdDeptRegionById" parameterType="Long" resultMap="ArdDeptRegionResult">
+        <include refid="selectArdDeptRegionVo"/>
+        WHERE r.id = #{id}
+    </select>
+
+    <!-- 获取边界WKT -->
+    <select id="selectBoundaryWkt" parameterType="Long" resultType="String">
+        SELECT ST_AsText(region_boundary)
+        FROM ard_dept_region
+        WHERE id = #{id}
+    </select>
+
+    <!-- 根据区域名称查询区域(用于唯一性校验,查询所有) -->
+    <select id="checkRegionNameUnique" parameterType="String" resultMap="ArdDeptRegionResult">
+        SELECT id, dept_id, region_name, region_color,
+               center_lat, center_lng, area_size,
+               status, create_by, create_time, update_by, update_time, remark
+        FROM ard_dept_region
+        WHERE region_name = #{regionName}
+            LIMIT 1
+    </select>
+
+    <!-- 检查点冲突(只查启用的区域) -->
+    <select id="checkPointConflict" resultType="ConflictRegion">
+        SELECT
+        id,
+        region_name as regionName,
+        region_color as regionColor,
+        CASE
+        WHEN ST_Contains(region_boundary, ST_GeomFromText(#{wkt}, 4326)) THEN '包含该点'
+        WHEN ST_Intersects(region_boundary, ST_GeomFromText(#{wkt}, 4326)) THEN '相交'
+        ELSE '无冲突'
+        END as relationship
+        FROM ard_dept_region
+        WHERE status = '0'
+        <if test="deptId != null">
+            AND dept_id = #{deptId}
+        </if>
+        <if test="excludeId != null">
+            AND id != #{excludeId}
+        </if>
+        AND ST_Intersects(region_boundary, ST_GeomFromText(#{wkt}, 4326))
+    </select>
+
+    <!-- 检查线冲突(只查启用的区域) -->
+    <select id="checkLineConflict" resultType="ConflictRegion">
+        SELECT
+        id,
+        region_name as regionName,
+        region_color as regionColor,
+        CASE
+        WHEN ST_Intersects(region_boundary, ST_GeomFromText(#{wkt}, 4326)) THEN '相交'
+        WHEN ST_Contains(region_boundary, ST_GeomFromText(#{wkt}, 4326)) THEN '包含该线'
+        ELSE '无冲突'
+        END as relationship
+        FROM ard_dept_region
+        WHERE status = '0'
+        <if test="deptId != null">
+            AND dept_id = #{deptId}
+        </if>
+        <if test="excludeId != null">
+            AND id != #{excludeId}
+        </if>
+        AND ST_Intersects(region_boundary, ST_GeomFromText(#{wkt}, 4326))
+    </select>
+
+    <!-- 检查面冲突(只查启用的区域) -->
+    <select id="checkPolygonConflict" resultType="ConflictRegion">
+        SELECT
+        id,
+        region_name as regionName,
+        region_color as regionColor,
+        CASE
+        WHEN ST_Contains(region_boundary, ST_GeomFromText(#{wkt}, 4326)) THEN '包含该面'
+        WHEN ST_Within(ST_GeomFromText(#{wkt}, 4326), region_boundary) THEN '在该面内部'
+        WHEN ST_Overlaps(region_boundary, ST_GeomFromText(#{wkt}, 4326)) THEN '重叠'
+        WHEN ST_Intersects(region_boundary, ST_GeomFromText(#{wkt}, 4326)) THEN '相交'
+        ELSE '无冲突'
+        END as relationship
+        FROM ard_dept_region
+        WHERE status = '0'
+        <if test="deptId != null">
+            AND dept_id = #{deptId}
+        </if>
+        <if test="excludeId != null">
+            AND id != #{excludeId}
+        </if>
+        AND ST_Intersects(region_boundary, ST_GeomFromText(#{wkt}, 4326))
+    </select>
+
+    <!-- 根据坐标查询所属区域(查询所有区域,包括禁用的) -->
+    <select id="selectRegionByPoint" resultMap="ArdDeptRegionResult">
+        <include refid="selectArdDeptRegionVo"/>
+        WHERE ST_Contains(r.region_boundary, ST_GeomFromText(#{pointWkt}, 4326))
+        LIMIT 1
+    </select>
+
+    <!-- 新增部门区域(默认状态为启用0) -->
+    <insert id="insertArdDeptRegion" parameterType="ArdDeptRegion" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO ard_dept_region
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="deptId != null">dept_id,</if>
+            <if test="regionName != null">region_name,</if>
+            <if test="regionColor != null">region_color,</if>
+            <if test="boundaryWkt != null">region_boundary,</if>
+            <if test="centerLat != null">center_lat,</if>
+            <if test="centerLng != null">center_lng,</if>
+            <if test="areaSize != null">area_size,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="remark != null">remark,</if>
+            status, create_time
+        </trim>
+        VALUES
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="deptId != null">#{deptId},</if>
+            <if test="regionName != null">#{regionName},</if>
+            <if test="regionColor != null">#{regionColor},</if>
+            <if test="boundaryWkt != null">ST_GeomFromText(#{boundaryWkt}, 4326),</if>
+            <if test="centerLat != null">#{centerLat},</if>
+            <if test="centerLng != null">#{centerLng},</if>
+            <if test="areaSize != null">#{areaSize},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="remark != null">#{remark},</if>
+            '0', NOW()
+        </trim>
+    </insert>
+
+    <!-- 修改部门区域 -->
+    <update id="updateArdDeptRegion" parameterType="ArdDeptRegion">
+        UPDATE ard_dept_region
+        <set>
+            <if test="regionName != null">region_name = #{regionName},</if>
+            <if test="regionColor != null">region_color = #{regionColor},</if>
+            <if test="boundaryWkt != null">region_boundary = ST_GeomFromText(#{boundaryWkt}, 4326),</if>
+            <if test="centerLat != null">center_lat = #{centerLat},</if>
+            <if test="centerLng != null">center_lng = #{centerLng},</if>
+            <if test="areaSize != null">area_size = #{areaSize},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            update_time = NOW()
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <!-- 启用/禁用区域 -->
+    <update id="updateStatus">
+        UPDATE ard_dept_region
+        SET status = #{status}, update_time = NOW()
+        WHERE id = #{id}
+    </update>
+
+    <!-- 物理删除区域 -->
+    <delete id="deleteArdDeptRegionById" parameterType="Long">
+        DELETE FROM ard_dept_region WHERE id = #{id}
+    </delete>
+
+    <!-- 批量物理删除区域 -->
+    <delete id="deleteArdDeptRegionByIds" parameterType="String">
+        DELETE FROM ard_dept_region WHERE id IN
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java b/ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java
index 9e60142..103d511 100644
--- a/ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java
+++ b/ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java
@@ -3,11 +3,10 @@
 import com.ard.common.constant.CacheConstants;
 import com.ard.common.core.domain.entity.SysUser;
 import com.ard.common.core.redis.RedisCache;
+import com.ard.data.region.domain.ArdDeptRegion;
+import com.ard.data.region.service.IArdDeptRegionService;
 import com.ard.work.config.RouteStateMachineService;
-import com.ard.work.domain.cache.UavSignalInfoCache;
-import com.ard.work.domain.cache.UavStateCache;
-import com.ard.work.domain.cache.UavTelemetryDataCache;
-import com.ard.work.domain.cache.UavWaylineCache;
+import com.ard.work.domain.cache.*;
 import com.ard.work.domain.dto.signal.SignalInfo;
 import com.ard.work.domain.dto.state.UavState;
 import com.ard.work.domain.dto.telemetry.TelemetryData;
@@ -69,6 +68,9 @@
             // 将载荷当前控制者用户ID添加到映射中
             map.put("payloadController", payloadUser);
         }
+        // 获取当前无人机所属区域
+        ArdDeptRegion ardDeptRegion = UavRegionCache.get(boxSn);
+        map.put("region", ardDeptRegion);
         return map;
     }
 
diff --git a/ard-work/src/main/java/com/ard/work/domain/cache/UavRegionCache.java b/ard-work/src/main/java/com/ard/work/domain/cache/UavRegionCache.java
new file mode 100644
index 0000000..cc96b99
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/domain/cache/UavRegionCache.java
@@ -0,0 +1,30 @@
+package com.ard.work.domain.cache;
+
+import com.ard.data.region.domain.ArdDeptRegion;
+import lombok.extern.slf4j.Slf4j;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 无人机区域缓存(简化版)
+ *
+ * @author 刘苏义
+ */
+@Slf4j
+public class UavRegionCache {
+
+    // 无人机当前区域名称缓存 Map<无人机ID, 区域>
+    private static final ConcurrentHashMap<String, ArdDeptRegion> cache = new ConcurrentHashMap<>();
+
+    // 存储数据到缓存
+    public static void put(String boxSn, ArdDeptRegion region) {cache.put(boxSn, region);}
+
+    // 从缓存中获取数据
+    public static ArdDeptRegion get(String boxSn) {
+        return cache.get(boxSn);
+    }
+
+    // 删除缓存中的数据
+    public static void remove(String boxSn) {
+        cache.remove(boxSn);
+    }
+}
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java
index 83e24dd..2fc7464 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java
@@ -86,10 +86,10 @@
     private TyClient tyClient;
     @Autowired
     private RouteStateMachineService stateMachineService;
-    @Autowired
-    private IArdFlightTaskPartService ardFlightTaskPartService;
     @Resource
-    private MediamtxConfig mediamtxConfig;
+    private UavPositionChangeService uavPositionChangeService;
+    @Resource
+    private UavRegionDetectService uavRegionDetectService;
     /**
      * 获取边缘服务器token信息
      *
@@ -981,6 +981,16 @@
                 // 处理无人机遥测消息
                 UavTelemetryDataCmd uavTelemetryDataCmd = (UavTelemetryDataCmd) tyCommand;
                 TelemetryData telemetryData = uavTelemetryDataCmd.getTelemetryData();
+
+                // 判断是否需要触发区域检测
+                if (uavPositionChangeService.shouldTriggerRegionCheck(telemetryData)) {
+                    log.debug("无人机 {} 位置变化,触发区域检测", telemetryData.getBoxSn());
+                    // 异步触发区域检测(不阻塞)
+                    uavRegionDetectService.detectRegionAsync(telemetryData);
+                } else {
+                    log.debug("无人机 {} 位置未变化,跳过区域检测", telemetryData.getBoxSn());
+                }
+
                 UavTelemetryDataCache.put(telemetryData);
                 //logPrint("无人机遥测", uavTelemetryDataCmd.getTelemetryData().getBoxSn());
                 //WebSocketUtil.sendMessageAll(uavTelemetryDataCmd.getTelemetryData());
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/UavPositionChangeService.java b/ard-work/src/main/java/com/ard/work/service/impl/UavPositionChangeService.java
new file mode 100644
index 0000000..468f696
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/service/impl/UavPositionChangeService.java
@@ -0,0 +1,89 @@
+package com.ard.work.service.impl;
+
+import com.ard.work.domain.cache.UavTelemetryDataCache;
+import com.ard.work.domain.dto.telemetry.TelemetryData;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * 无人机位置变化判断服务
+ *
+ * @author 刘苏义
+ */
+@Slf4j
+@Service
+public class UavPositionChangeService {
+
+    // 位置变化阈值(米)
+    private static final double POSITION_CHANGE_THRESHOLD = 5.0;
+    
+    // 无效坐标的默认值
+    private static final double INVALID_COORD = 0.0;
+
+    /**
+     * 判断是否需要触发区域检测
+     * @param telemetryData 当前遥测数据
+     * @return true-需要触发区域检测,false-不需要
+     */
+    public boolean shouldTriggerRegionCheck(TelemetryData telemetryData) {
+        String boxSn = telemetryData.getBoxSn();
+        
+        // 获取上次遥测数据
+        TelemetryData lastData = UavTelemetryDataCache.get(boxSn);
+        
+        // 首次上报,需要检测
+        if (lastData == null) {
+            log.debug("无人机 {} 首次上报,触发区域检测", boxSn);
+            return true;
+        }
+        
+        // 判断位置是否变化
+        return isPositionChanged(lastData, telemetryData);
+    }
+    
+    /**
+     * 判断位置是否发生变化
+     */
+    private boolean isPositionChanged(TelemetryData lastData, TelemetryData newData) {
+        double lastLng = lastData.getLng();
+        double lastLat = lastData.getLat();
+        double newLng = newData.getLng();
+        double newLat = newData.getLat();
+        
+        // 坐标无效(为0),认为需要检测
+        if (lastLng == INVALID_COORD || lastLat == INVALID_COORD || 
+            newLng == INVALID_COORD || newLat == INVALID_COORD) {
+            log.debug("坐标无效,触发区域检测");
+            return true;
+        }
+        
+        // 计算移动距离
+        double distance = calculateDistance(lastLat, lastLng, newLat, newLng);
+        
+        boolean changed = distance > POSITION_CHANGE_THRESHOLD;
+        
+        if (changed) {
+            log.debug("无人机 {} 移动了 {:.2f} 米,触发区域检测", newData.getBoxSn(), distance);
+        }
+        
+        return changed;
+    }
+    
+    /**
+     * 计算两点间距离(米)- Haversine公式
+     */
+    private double calculateDistance(double lat1, double lng1, double lat2, double lng2) {
+        double earthRadius = 6371000; // 地球半径(米)
+        
+        double dLat = Math.toRadians(lat2 - lat1);
+        double dLng = Math.toRadians(lng2 - lng1);
+        
+        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
+                   Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
+                   Math.sin(dLng / 2) * Math.sin(dLng / 2);
+        
+        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+        
+        return earthRadius * c;
+    }
+}
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/UavRegionDetectService.java b/ard-work/src/main/java/com/ard/work/service/impl/UavRegionDetectService.java
new file mode 100644
index 0000000..84dd64c
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/service/impl/UavRegionDetectService.java
@@ -0,0 +1,43 @@
+package com.ard.work.service.impl;
+
+import com.ard.data.region.domain.ArdDeptRegion;
+import com.ard.data.region.service.IArdDeptRegionService;
+import com.ard.work.domain.cache.UavRegionCache;
+import com.ard.work.domain.cache.UavTelemetryDataCache;
+import com.ard.work.domain.dto.telemetry.TelemetryData;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+/**
+ * 无人机区域检测服务(异步)
+ *
+ * @author 刘苏义
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class UavRegionDetectService {
+
+    private final IArdDeptRegionService ardDeptRegionService;
+
+    /**
+     * 异步检测无人机所在区域
+     */
+    @Async
+    public void detectRegionAsync(TelemetryData telemetryData) {
+        String boxSn = telemetryData.getBoxSn();
+        try {
+            // 1. 查询当前坐标所属区域
+            ArdDeptRegion region = null;
+            if (telemetryData.getLat() != 0.0 && telemetryData.getLng() != 0.0) {
+                region = ardDeptRegionService.selectRegionByPoint(telemetryData.getLat(), telemetryData.getLng());
+            }
+            // 3. 更新区域缓存
+            UavRegionCache.put(boxSn, region);
+        } catch (Exception e) {
+            log.error("无人机 {} 区域检测失败: {}", boxSn, e.getMessage(), e);
+        }
+    }
+}
\ No newline at end of file

--
Gitblit v1.9.3