From 226b062ef301b61260efac126e1db373ace85b87 Mon Sep 17 00:00:00 2001
From: zhaowenyu <35589726@qq.com>
Date: Fri, 16 Jan 2026 09:01:43 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev' into dev

---
 ard-inspection/pom.xml                                                                          |   39 +++
 ard-admin/src/main/java/com/ard/web/core/config/ApiGroup.java                                   |    3 
 ard-inspection/src/main/java/com/ard/inspection/mapper/ArdInspectionGroupMapper.java            |  106 ++++++++
 ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java              |    2 
 pom.xml                                                                                         |    7 
 ard-admin/src/main/java/com/ard/web/core/config/SwaggerConfig.java                              |   13 
 ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java           |   12 
 ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java                   |   14 
 ard-inspection/src/main/java/com/ard/inspection/service/impl/ArdInspectionGroupServiceImpl.java |  173 +++++++++++++
 ard-admin/src/main/java/com/ard/web/controller/inspection/ArdInspectionGroupController.java     |  129 +++++++++
 ard-inspection/src/main/java/com/ard/inspection/domain/ArdInspectionGroup.java                  |   37 ++
 ard-admin/pom.xml                                                                               |   12 
 ard-inspection/src/main/java/com/ard/inspection/service/IArdInspectionGroupService.java         |   86 ++++++
 ard-inspection/src/main/resources/mapper/ArdInspectionGroupMapper.xml                           |  130 ++++++++++
 14 files changed, 745 insertions(+), 18 deletions(-)

diff --git a/ard-admin/pom.xml b/ard-admin/pom.xml
index d70e00a..62c0569 100644
--- a/ard-admin/pom.xml
+++ b/ard-admin/pom.xml
@@ -75,11 +75,23 @@
             <artifactId>ard-route</artifactId>
         </dependency>
 
+        <!-- 巡检模块-->
+        <dependency>
+            <groupId>com.ard</groupId>
+            <artifactId>ard-inspection</artifactId>
+        </dependency>
+
         <!-- SpringBootAdmin管控台 -->
         <dependency>
             <groupId>de.codecentric</groupId>
             <artifactId>spring-boot-admin-starter-client</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.ard</groupId>
+            <artifactId>ard-inspection</artifactId>
+            <version>3.8.8</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/ard-admin/src/main/java/com/ard/web/controller/inspection/ArdInspectionGroupController.java b/ard-admin/src/main/java/com/ard/web/controller/inspection/ArdInspectionGroupController.java
new file mode 100644
index 0000000..f001c77
--- /dev/null
+++ b/ard-admin/src/main/java/com/ard/web/controller/inspection/ArdInspectionGroupController.java
@@ -0,0 +1,129 @@
+package com.ard.web.controller.inspection;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+
+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.inspection.domain.ArdInspectionGroup;
+import com.ard.inspection.service.IArdInspectionGroupService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 精细化巡检分组Controller
+ * 
+ * @author xia
+ * @date 2026-01-15
+ */
+@Api(tags = "精细化巡检分组")
+@RestController
+@RequestMapping("/inspection/group")
+public class ArdInspectionGroupController extends BaseController {
+
+    @Autowired
+    private IArdInspectionGroupService ardInspectionGroupService;
+
+    /**
+     * 查询精细化巡检分组列表
+     */
+    @ApiOperation("查询精细化巡检分组列表")
+    @PreAuthorize("@ss.hasPermi('inspection:group:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(ArdInspectionGroup ardInspectionGroup) {
+        startPage();
+        List<ArdInspectionGroup> list = ardInspectionGroupService.selectArdInspectionGroupList(ardInspectionGroup);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出精细化巡检分组列表
+     */
+    @ApiOperation("导出精细化巡检分组列表")
+    @PreAuthorize("@ss.hasPermi('inspection:group:export')")
+    @Log(title = "精细化巡检分组", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, ArdInspectionGroup ardInspectionGroup) {
+        List<ArdInspectionGroup> list = ardInspectionGroupService.selectArdInspectionGroupList(ardInspectionGroup);
+        ExcelUtil<ArdInspectionGroup> util = new ExcelUtil<ArdInspectionGroup>(ArdInspectionGroup.class);
+        util.exportExcel(response, list, "精细化巡检分组数据");
+    }
+
+    /**
+     * 获取精细化巡检分组详细信息
+     */
+    @ApiOperation("获取精细化巡检分组详细信息")
+    @PreAuthorize("@ss.hasPermi('inspection:group:query')")
+    @GetMapping(value = "/{groupId}")
+    public AjaxResult getInfo(@PathVariable("groupId") Long groupId) {
+        ardInspectionGroupService.checkInspectionGroupDataScope(groupId);
+        return success(ardInspectionGroupService.selectArdInspectionGroupByGroupId(groupId));
+    }
+
+    /**
+     * 新增精细化巡检分组
+     */
+    @ApiOperation("新增精细化巡检分组")
+    @PreAuthorize("@ss.hasPermi('inspection:group:add')")
+    @Log(title = "精细化巡检分组", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody ArdInspectionGroup ardInspectionGroup) {
+        if (ardInspectionGroupService.checkInspectionGroupNameUnique(ardInspectionGroup)){
+            return error("新增分组'" + ardInspectionGroup.getGroupName() + "'失败,分组名称已存在");
+        }
+        ardInspectionGroup.setCreateBy(getUsername());
+        return toAjax(ardInspectionGroupService.insertArdInspectionGroup(ardInspectionGroup));
+    }
+
+    /**
+     * 修改精细化巡检分组
+     */
+    @ApiOperation("修改精细化巡检分组")
+    @PreAuthorize("@ss.hasPermi('inspection:group:edit')")
+    @Log(title = "精细化巡检分组", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody ArdInspectionGroup ardInspectionGroup) {
+        Long groupId = ardInspectionGroup.getGroupId();
+        ardInspectionGroupService.checkInspectionGroupDataScope(groupId);
+        if (!ardInspectionGroupService.checkInspectionGroupNameUnique(ardInspectionGroup)){
+            return error("修改分组'" + ardInspectionGroup.getGroupName() + "'失败,分组名称已存在");
+        } else if (ardInspectionGroup.getGroupParentId().equals(groupId)) {
+            return error("修改分组'" + ardInspectionGroup.getGroupName() + "'失败,上级分组不能是自己");
+        }
+        ardInspectionGroup.setUpdateBy(getUsername());
+        return toAjax(ardInspectionGroupService.updateArdInspectionGroup(ardInspectionGroup));
+    }
+
+    /**
+     * 删除精细化巡检分组
+     */
+    @ApiOperation("删除精细化巡检分组")
+    @PreAuthorize("@ss.hasPermi('inspection:group:remove')")
+    @Log(title = "精细化巡检分组", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{groupId}")
+    public AjaxResult remove(@PathVariable Long groupId) {
+        if (ardInspectionGroupService.hasChildByInspectionGroupId(groupId)){
+            return warn("存在下级分组,不允许删除");
+        }
+        if (ardInspectionGroupService.checkInspectionGroupExistUser(groupId)){
+            return warn("分组存在用户,不允许删除");
+        }
+        ardInspectionGroupService.checkInspectionGroupDataScope(groupId);
+        return toAjax(ardInspectionGroupService.deleteArdInspectionGroupByGroupId(groupId));
+    }
+
+}
diff --git a/ard-admin/src/main/java/com/ard/web/core/config/ApiGroup.java b/ard-admin/src/main/java/com/ard/web/core/config/ApiGroup.java
index 4882b1a..55019d2 100644
--- a/ard-admin/src/main/java/com/ard/web/core/config/ApiGroup.java
+++ b/ard-admin/src/main/java/com/ard/web/core/config/ApiGroup.java
@@ -12,7 +12,8 @@
     DEVICE("设备接口", "com.ard.web.controller.device", "设备管理接口"),
     DATA("数据接口", "com.ard.web.controller.data", "数据管理接口"),
     STORAGE("存储接口", "com.ard.web.controller.storage", "存储管理接口"),
-    ROUTE("航线接口", "com.ard.web.controller.route", "航线管理接口");
+    ROUTE("航线接口", "com.ard.web.controller.route", "航线管理接口"),
+    INSPECTION("巡检接口", "com.ard.web.controller.inspection", "巡检管理接口");
 
     // 分组名
     private final String groupName;
diff --git a/ard-admin/src/main/java/com/ard/web/core/config/SwaggerConfig.java b/ard-admin/src/main/java/com/ard/web/core/config/SwaggerConfig.java
index dfeb8d7..84da005 100644
--- a/ard-admin/src/main/java/com/ard/web/core/config/SwaggerConfig.java
+++ b/ard-admin/src/main/java/com/ard/web/core/config/SwaggerConfig.java
@@ -40,9 +40,11 @@
                 .apiInfo(apiInfo(apiGroup.getTitle()))
                 .select()
                 .apis(requestHandler ->
-                        requestHandler.declaringClass().getPackage().getName()
-                                .startsWith(apiGroup.getPackagePath()) &&
-                                requestHandler.isAnnotatedWith(io.swagger.annotations.ApiOperation.class)
+                        requestHandler
+                                .declaringClass()
+                                .getPackage()
+                                .getName()
+                                .startsWith(apiGroup.getPackagePath()) && requestHandler.isAnnotatedWith(io.swagger.annotations.ApiOperation.class)
                 )
                 .paths(PathSelectors.any())
                 .build()
@@ -90,6 +92,11 @@
         return createApiDocket(ApiGroup.ROUTE);
     }
 
+    @Bean
+    public Docket inspectionApi() {
+        return createApiDocket(ApiGroup.INSPECTION);
+    }
+
     /**
      * 安全模式,这里指定token通过Authorization头请求头传递
      */
diff --git a/ard-inspection/pom.xml b/ard-inspection/pom.xml
new file mode 100644
index 0000000..1227c63
--- /dev/null
+++ b/ard-inspection/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<parent>
+		<artifactId>ard</artifactId>
+		<groupId>com.ard</groupId>
+		<version>3.8.8</version>
+	</parent>
+	<modelVersion>4.0.0</modelVersion>
+
+	<artifactId>ard-inspection</artifactId>
+
+	<description>
+		inspection精细巡检
+	</description>
+
+	<dependencies>
+
+		<dependency>
+			<groupId>com.ard</groupId>
+			<artifactId>ard-common</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>cn.hutool</groupId>
+			<artifactId>hutool-all</artifactId>
+			<version>5.8.20</version>
+		</dependency>
+
+
+	</dependencies>
+
+</project>
diff --git a/ard-inspection/src/main/java/com/ard/inspection/domain/ArdInspectionGroup.java b/ard-inspection/src/main/java/com/ard/inspection/domain/ArdInspectionGroup.java
new file mode 100644
index 0000000..a0701c5
--- /dev/null
+++ b/ard-inspection/src/main/java/com/ard/inspection/domain/ArdInspectionGroup.java
@@ -0,0 +1,37 @@
+package com.ard.inspection.domain;
+
+import com.ard.common.annotation.Excel;
+import com.ard.common.core.domain.BaseEntity;
+import lombok.Data;
+
+/**
+ * 精细化巡检分组对象 ard_inspection_group
+ * 
+ * @author xia
+ * @date 2026-01-15
+ */
+@Data
+public class ArdInspectionGroup extends BaseEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 分组id */
+    private Long groupId;
+
+    /** 分组父部门id */
+    @Excel(name = "分组父部门id")
+    private Long groupParentId;
+
+    /** 祖级列表 */
+    @Excel(name = "祖级列表")
+    private String ancestors;
+
+    /** 分组名称 */
+    @Excel(name = "分组名称")
+    private String groupName;
+
+    /** 显示顺序 */
+    @Excel(name = "显示顺序")
+    private Long orderNum;
+
+}
diff --git a/ard-inspection/src/main/java/com/ard/inspection/mapper/ArdInspectionGroupMapper.java b/ard-inspection/src/main/java/com/ard/inspection/mapper/ArdInspectionGroupMapper.java
new file mode 100644
index 0000000..c076a4c
--- /dev/null
+++ b/ard-inspection/src/main/java/com/ard/inspection/mapper/ArdInspectionGroupMapper.java
@@ -0,0 +1,106 @@
+package com.ard.inspection.mapper;
+
+import com.ard.common.core.domain.entity.SysDept;
+import com.ard.inspection.domain.ArdInspectionGroup;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 精细化巡检分组Mapper接口
+ * 
+ * @author xia
+ * @date 2026-01-15
+ */
+public interface ArdInspectionGroupMapper {
+
+    /**
+     * 查询精细化巡检分组
+     * 
+     * @param groupId 精细化巡检分组主键
+     * @return 精细化巡检分组
+     */
+    public ArdInspectionGroup selectArdInspectionGroupByGroupId(Long groupId);
+
+    /**
+     * 查询精细化巡检分组列表
+     * 
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 精细化巡检分组集合
+     */
+    public List<ArdInspectionGroup> selectArdInspectionGroupList(ArdInspectionGroup ardInspectionGroup);
+
+    /**
+     * 新增精细化巡检分组
+     * 
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 结果
+     */
+    public int insertArdInspectionGroup(ArdInspectionGroup ardInspectionGroup);
+
+    /**
+     * 修改精细化巡检分组
+     * 
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 结果
+     */
+    public int updateArdInspectionGroup(ArdInspectionGroup ardInspectionGroup);
+
+    /**
+     * 删除精细化巡检分组
+     * 
+     * @param groupId 精细化巡检分组主键
+     * @return 结果
+     */
+    public int deleteArdInspectionGroupByGroupId(Long groupId);
+
+    /**
+     * 批量删除精细化巡检分组
+     * 
+     * @param groupIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteArdInspectionGroupByGroupIds(Long[] groupIds);
+
+    /**
+     * 校验分组名称是否唯一
+     *
+     * @param groupName 分组名称
+     * @param groupParentId 分组父部门ID
+     * @return 结果
+     */
+    public ArdInspectionGroup checkInspectionGroupNameUnique(@Param("groupName") String groupName,
+                                                             @Param("groupParentId") Long groupParentId);
+
+    /**
+     * 根据ID查询所有子分组
+     *
+     * @param groupId 分组ID
+     * @return 部门列表
+     */
+    public List<ArdInspectionGroup> selectChildrenInspectionGroupById(Long groupId);
+
+    /**
+     * 修改子元素关系
+     *
+     * @param inspectionGroups 子元素
+     * @return 结果
+     */
+    public int updateInspectionGroupChildren(@Param("inspectionGroups") List<ArdInspectionGroup> inspectionGroups);
+
+    /**
+     * 是否存在分组子节点
+     *
+     * @param groupId 分组ID
+     * @return 结果
+     */
+    public int hasChildByInspectionGroupId(Long groupId);
+
+    /**
+     * 查询分组是否存在用户
+     *
+     * @param groupId 分组ID
+     * @return 结果 true 存在 false 不存在
+     */
+    public int checkInspectionGroupExistUser(Long groupId);
+}
diff --git a/ard-inspection/src/main/java/com/ard/inspection/service/IArdInspectionGroupService.java b/ard-inspection/src/main/java/com/ard/inspection/service/IArdInspectionGroupService.java
new file mode 100644
index 0000000..4373fcd
--- /dev/null
+++ b/ard-inspection/src/main/java/com/ard/inspection/service/IArdInspectionGroupService.java
@@ -0,0 +1,86 @@
+package com.ard.inspection.service;
+
+import com.ard.inspection.domain.ArdInspectionGroup;
+
+import java.util.List;
+
+/**
+ * 精细化巡检分组Service接口
+ * 
+ * @author xia
+ * @date 2026-01-15
+ */
+public interface IArdInspectionGroupService {
+
+    /**
+     * 查询精细化巡检分组
+     * 
+     * @param groupId 精细化巡检分组主键
+     * @return 精细化巡检分组
+     */
+    public ArdInspectionGroup selectArdInspectionGroupByGroupId(Long groupId);
+
+    /**
+     * 查询精细化巡检分组列表
+     * 
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 精细化巡检分组集合
+     */
+    public List<ArdInspectionGroup> selectArdInspectionGroupList(ArdInspectionGroup ardInspectionGroup);
+
+    /**
+     * 新增精细化巡检分组
+     * 
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 结果
+     */
+    public int insertArdInspectionGroup(ArdInspectionGroup ardInspectionGroup);
+
+    /**
+     * 修改精细化巡检分组
+     * 
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 结果
+     */
+    public int updateArdInspectionGroup(ArdInspectionGroup ardInspectionGroup);
+
+    /**
+     * 删除精细化巡检分组信息
+     * 
+     * @param groupId 精细化巡检分组主键
+     * @return 结果
+     */
+    public int deleteArdInspectionGroupByGroupId(Long groupId);
+
+    /**
+     * 校验分组名称是否唯一
+     *
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 结果
+     */
+    public boolean checkInspectionGroupNameUnique(ArdInspectionGroup ardInspectionGroup);
+
+    /**
+     * 校验分组是否有数据权限
+     *
+     * @param groupId 精细化巡检分组主键
+     */
+    public void checkInspectionGroupDataScope(Long groupId);
+
+    /**
+     * 是否存在分组子节点
+     *
+     * @param groupId 分组ID
+     * @return 结果
+     */
+    public boolean hasChildByInspectionGroupId(Long groupId);
+
+    /**
+     * 查询分组是否存在用户
+     *
+     * @param groupId 分组ID
+     * @return 结果 true 存在 false 不存在
+     */
+    public boolean checkInspectionGroupExistUser(Long groupId);
+
+}
diff --git a/ard-inspection/src/main/java/com/ard/inspection/service/impl/ArdInspectionGroupServiceImpl.java b/ard-inspection/src/main/java/com/ard/inspection/service/impl/ArdInspectionGroupServiceImpl.java
new file mode 100644
index 0000000..bc2002b
--- /dev/null
+++ b/ard-inspection/src/main/java/com/ard/inspection/service/impl/ArdInspectionGroupServiceImpl.java
@@ -0,0 +1,173 @@
+package com.ard.inspection.service.impl;
+
+import java.util.List;
+
+import com.ard.common.annotation.DataScope;
+import com.ard.common.constant.UserConstants;
+import com.ard.common.core.domain.entity.SysUser;
+import com.ard.common.exception.ServiceException;
+import com.ard.common.utils.DateUtils;
+import com.ard.common.utils.SecurityUtils;
+import com.ard.common.utils.StringUtils;
+import com.ard.common.utils.spring.SpringUtils;
+import com.ard.inspection.domain.ArdInspectionGroup;
+import com.ard.inspection.mapper.ArdInspectionGroupMapper;
+import com.ard.inspection.service.IArdInspectionGroupService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 精细化巡检分组Service业务层处理
+ * 
+ * @author xia
+ * @date 2026-01-15
+ */
+@Service
+public class ArdInspectionGroupServiceImpl implements IArdInspectionGroupService {
+
+    @Autowired
+    private ArdInspectionGroupMapper ardInspectionGroupMapper;
+
+    /**
+     * 查询精细化巡检分组
+     * 
+     * @param groupId 精细化巡检分组主键
+     * @return 精细化巡检分组
+     */
+    @Override
+    public ArdInspectionGroup selectArdInspectionGroupByGroupId(Long groupId) {
+        return ardInspectionGroupMapper.selectArdInspectionGroupByGroupId(groupId);
+    }
+
+    /**
+     * 查询精细化巡检分组列表
+     * 
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 精细化巡检分组
+     */
+    @Override
+    @DataScope(deptAlias = "d")
+    public List<ArdInspectionGroup> selectArdInspectionGroupList(ArdInspectionGroup ardInspectionGroup) {
+        return ardInspectionGroupMapper.selectArdInspectionGroupList(ardInspectionGroup);
+    }
+
+    /**
+     * 新增精细化巡检分组
+     * 
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 结果
+     */
+    @Override
+    public int insertArdInspectionGroup(ArdInspectionGroup ardInspectionGroup) {
+        ardInspectionGroup.setCreateTime(DateUtils.getNowDate());
+        return ardInspectionGroupMapper.insertArdInspectionGroup(ardInspectionGroup);
+    }
+
+    /**
+     * 修改精细化巡检分组
+     * 
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 结果
+     */
+    @Override
+    public int updateArdInspectionGroup(ArdInspectionGroup ardInspectionGroup) {
+
+        ArdInspectionGroup newGroup = ardInspectionGroupMapper.selectArdInspectionGroupByGroupId(ardInspectionGroup.getGroupParentId());
+        ArdInspectionGroup oldGroup = ardInspectionGroupMapper.selectArdInspectionGroupByGroupId(ardInspectionGroup.getGroupId());
+        if(StringUtils.isNotNull(newGroup) && StringUtils.isNotNull(oldGroup)){
+            String newAncestors = newGroup.getAncestors() + "," + newGroup.getGroupId();
+            String oldAncestors = oldGroup.getAncestors();
+            ardInspectionGroup.setAncestors(newAncestors);
+            updateInspectionGroupChildren(ardInspectionGroup.getGroupId(), newAncestors, oldAncestors);
+        }
+        ardInspectionGroup.setUpdateTime(DateUtils.getNowDate());
+        return ardInspectionGroupMapper.updateArdInspectionGroup(ardInspectionGroup);
+    }
+
+    /**
+     * 修改子元素关系
+     *
+     * @param groupId 被修改的分组ID
+     * @param newAncestors 新的父ID集合
+     * @param oldAncestors 旧的父ID集合
+     */
+    public void updateInspectionGroupChildren(Long groupId, String newAncestors, String oldAncestors){
+        List<ArdInspectionGroup> children = ardInspectionGroupMapper.selectChildrenInspectionGroupById(groupId);
+        for (ArdInspectionGroup child : children) {
+            child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
+        }
+        if (!children.isEmpty()){
+            ardInspectionGroupMapper.updateInspectionGroupChildren(children);
+        }
+    }
+
+    /**
+     * 删除精细化巡检分组信息
+     * 
+     * @param groupId 精细化巡检分组主键
+     * @return 结果
+     */
+    @Override
+    public int deleteArdInspectionGroupByGroupId(Long groupId) {
+        return ardInspectionGroupMapper.deleteArdInspectionGroupByGroupId(groupId);
+    }
+
+    /**
+     * 校验分组名称是否唯一
+     *
+     * @param ardInspectionGroup 精细化巡检分组
+     * @return 结果
+     */
+    @Override
+    public boolean checkInspectionGroupNameUnique(ArdInspectionGroup ardInspectionGroup) {
+        Long groupId = StringUtils.isNull(ardInspectionGroup.getGroupId()) ? -1L : ardInspectionGroup.getGroupId();
+        ArdInspectionGroup info = ardInspectionGroupMapper.checkInspectionGroupNameUnique(ardInspectionGroup.getGroupName(),ardInspectionGroup.getGroupParentId());
+        if (StringUtils.isNotNull(info) && info.getGroupId().longValue() != groupId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 校验分组是否有数据权限
+     *
+     * @param groupId 精细化巡检分组主键
+     */
+    @Override
+    public void checkInspectionGroupDataScope(Long groupId) {
+        if (!SysUser.isAdmin(SecurityUtils.getUserId()) && StringUtils.isNotNull(groupId)){
+            ArdInspectionGroup group = new ArdInspectionGroup();
+            group.setGroupId(groupId);
+            List<ArdInspectionGroup> groups = SpringUtils.getAopProxy(this).selectArdInspectionGroupList(group);
+            if (StringUtils.isEmpty(groups)){
+                throw new ServiceException("没有权限访问分组数据!");
+            }
+        }
+    }
+
+    /**
+     * 是否存在分组子节点
+     *
+     * @param groupId 分组ID
+     * @return 结果
+     */
+    @Override
+    public boolean hasChildByInspectionGroupId(Long groupId) {
+        int result = ardInspectionGroupMapper.hasChildByInspectionGroupId(groupId);
+        return result > 0;
+    }
+
+    /**
+     * 查询分组是否存在用户
+     *
+     * @param groupId 分组ID
+     * @return 结果 true 存在 false 不存在
+     */
+    @Override
+    public boolean checkInspectionGroupExistUser(Long groupId) {
+        int result = ardInspectionGroupMapper.checkInspectionGroupExistUser(groupId);
+        return result > 0;
+    }
+
+}
diff --git a/ard-inspection/src/main/resources/mapper/ArdInspectionGroupMapper.xml b/ard-inspection/src/main/resources/mapper/ArdInspectionGroupMapper.xml
new file mode 100644
index 0000000..754ef85
--- /dev/null
+++ b/ard-inspection/src/main/resources/mapper/ArdInspectionGroupMapper.xml
@@ -0,0 +1,130 @@
+<?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.inspection.mapper.ArdInspectionGroupMapper">
+    
+    <resultMap type="ArdInspectionGroup" id="ArdInspectionGroupResult">
+        <result property="groupId"    column="group_id"    />
+        <result property="groupParentId"    column="group_parent_id"    />
+        <result property="ancestors"    column="ancestors"    />
+        <result property="groupName"    column="group_name"    />
+        <result property="orderNum"    column="order_num"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectArdInspectionGroupVo">
+        select group_id,
+               group_parent_id,
+               ancestors,
+               group_name,
+               order_num,
+               create_by,
+               create_time,
+               update_by,
+               update_time
+        from ard_inspection_group
+    </sql>
+
+    <select id="selectArdInspectionGroupList" parameterType="ArdInspectionGroup" resultMap="ArdInspectionGroupResult">
+        <include refid="selectArdInspectionGroupVo"/>
+        <where>  
+            <if test="groupParentId != null "> and group_parent_id = #{groupParentId}</if>
+            <if test="ancestors != null  and ancestors != ''"> and ancestors = #{ancestors}</if>
+            <if test="groupName != null  and groupName != ''"> and group_name like concat('%', #{groupName}, '%')</if>
+            <if test="orderNum != null "> and order_num = #{orderNum}</if>
+        </where>
+    </select>
+    
+    <select id="selectArdInspectionGroupByGroupId" parameterType="Long" resultMap="ArdInspectionGroupResult">
+        <include refid="selectArdInspectionGroupVo"/>
+        where group_id = #{groupId}
+    </select>
+
+    <insert id="insertArdInspectionGroup" parameterType="ArdInspectionGroup" useGeneratedKeys="true" keyProperty="groupId">
+        insert into ard_inspection_group
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="groupParentId != null">group_parent_id,</if>
+            <if test="ancestors != null">ancestors,</if>
+            <if test="groupName != null">group_name,</if>
+            <if test="orderNum != null">order_num,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="groupParentId != null">#{groupParentId},</if>
+            <if test="ancestors != null">#{ancestors},</if>
+            <if test="groupName != null">#{groupName},</if>
+            <if test="orderNum != null">#{orderNum},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateArdInspectionGroup" parameterType="ArdInspectionGroup">
+        update ard_inspection_group
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="groupParentId != null">group_parent_id = #{groupParentId},</if>
+            <if test="ancestors != null">ancestors = #{ancestors},</if>
+            <if test="groupName != null">group_name = #{groupName},</if>
+            <if test="orderNum != null">order_num = #{orderNum},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where group_id = #{groupId}
+    </update>
+
+    <delete id="deleteArdInspectionGroupByGroupId" parameterType="Long">
+        delete from ard_inspection_group where group_id = #{groupId}
+    </delete>
+
+    <delete id="deleteArdInspectionGroupByGroupIds" parameterType="String">
+        delete from ard_inspection_group where group_id in 
+        <foreach item="groupId" collection="array" open="(" separator="," close=")">
+            #{groupId}
+        </foreach>
+    </delete>
+
+    <select id="checkInspectionGroupNameUnique" resultMap="ArdInspectionGroupResult">
+        <include refid="selectArdInspectionGroupVo"/>
+        where group_name=#{groupName}
+        and group_parent_id = #{groupParentId}
+        and del_flag = '0' limit 1
+    </select>
+
+    <select id="selectChildrenInspectionGroupById" parameterType="Long" resultMap="ArdInspectionGroupResult">
+        select * from ard_inspection_group where find_in_set(#{groupId}, ancestors)
+    </select>
+
+    <update id="updateInspectionGroupChildren" parameterType="java.util.List">
+        update ard_inspection_group set ancestors =
+        <foreach collection="inspectionGroups" item="item" index="index"
+                 separator=" " open="case group_id" close="end">
+            when #{item.groupId} then #{item.ancestors}
+        </foreach>
+        where group_id in
+        <foreach collection="inspectionGroups" item="item" index="index"
+                 separator="," open="(" close=")">
+            #{item.groupId}
+        </foreach>
+    </update>
+
+    <select id="hasChildByInspectionGroupId" parameterType="Long" resultType="int">
+        select count(1) from ard_inspection_group
+        where group_parent_id = #{groupId} limit 1
+    </select>
+
+    <select id="checkInspectionGroupExistUser" parameterType="Long" resultType="int">
+        select count(1) from sys_user where group_id = #{groupId} and del_flag = '0'
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java b/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java
index f55bb2a..f719a7f 100644
--- a/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java
+++ b/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java
@@ -345,14 +345,14 @@
                 return;
             }
             if (!WebSocketSessionManager.containsUser(userId)) {
+                uavControlService.releaseAllControls(userId);
+                uavStreamManager.stopAllPullStreamByUser(userId);
+                uavStateManager.removeSubscribe(userId);
+                uavTelemetryManager.removeSubscribe(userId);
+                uavSignalManager.removeSubscribe(userId);
+                uavControlManager.removeSubscribe(userId);
                 LoginUser loginUser = tokenService.getLoginUserByUserId(userId);
                 if (loginUser != null) {
-                    uavControlService.releaseAllControls(userId);
-                    uavStreamManager.stopAllPullStreamByUser(userId);
-                    uavStateManager.removeSubscribe(userId);
-                    uavTelemetryManager.removeSubscribe(userId);
-                    uavSignalManager.removeSubscribe(userId);
-                    uavControlManager.removeSubscribe(userId);
                     tokenService.delLoginUser(loginUser.getToken(), userId);
                     log.info("【cmd】用户 [{}] 超时未重连,已删除 token", userId);
                 }
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java
index 7064f3f..2b85811 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java
@@ -340,15 +340,15 @@
                 ArdDroneInfo local = localMap.get(boxSn);
                 boolean dbOnline = local != null && local.getOnLine() == 1;
                 boolean needUpdate = (dbOnline != currentOnline);
-
+                // 只要远端设备在线,就刷新Redis缓存(无论状态是否变化),保证缓存一定存在
+                if (currentOnline) {
+                    redisCache.setCacheObject(redisKey, remote);
+                } else {
+                    // 远端离线,删除Redis缓存
+                    redisCache.deleteObject(redisKey);
+                }
                 // 如果状态变化才更新
                 if (needUpdate){
-                    // 更新 Redis
-                    if (currentOnline) {
-                        redisCache.setCacheObject(redisKey, remote);
-                    } else {
-                        redisCache.deleteObject(redisKey);
-                    }
                     // 发布事件
                     eventPublisher.publishEvent(new DeviceStatusEvent(boxSn, currentOnline));
                     // 更新数据库
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java
index da66e03..dd0d4b1 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java
@@ -60,7 +60,7 @@
             // 2️⃣ 使用遥测自身时间戳判断是否还活着
             long dataTs = telemetryData.getTimestamp(); // 设备时间 / 服务器接收时间
             if (now - dataTs > 3000) {
-                log.warn("超过 3 秒没有新遥测 → 认为已离线或冻结,不记录:{}", new Date(dataTs));
+                //log.warn("超过 3 秒没有新遥测 → 认为已离线或冻结,不记录:{}", new Date(dataTs));
                 return; // 超过 3 秒没有新遥测 → 认为已离线或冻结,不记录
             }
 
diff --git a/pom.xml b/pom.xml
index 1415f28..3ae5075 100644
--- a/pom.xml
+++ b/pom.xml
@@ -198,6 +198,12 @@
                 <artifactId>ard-route</artifactId>
                 <version>${ard.version}</version>
             </dependency>
+            <!-- 巡检模块-->
+            <dependency>
+                <groupId>com.ard</groupId>
+                <artifactId>ard-inspection</artifactId>
+                <version>${ard.version}</version>
+            </dependency>
             <!-- 通用工具-->
             <dependency>
                 <groupId>com.ard</groupId>
@@ -266,6 +272,7 @@
         <module>ard-sample</module>
         <module>ard-vtdu</module>
         <module>ard-route</module>
+        <module>ard-inspection</module>
         <module>ard-device</module>
         <module>ard-oss</module>
     </modules>

--
Gitblit v1.9.3