Sfoglia il codice sorgente

对接算法盒子完成并添加算法设备入库增删改查操作

zhaojinyu 3 giorni fa
parent
commit
010472a47b
82 ha cambiato i file con 3792 aggiunte e 28 eliminazioni
  1. 19 10
      service-ai/service-ai-biz/pom.xml
  2. 17 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/AITestApplication.java
  3. 3 6
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiChatController.java
  4. 1 1
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiQuestionController.java
  5. 3 3
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiSessionController.java
  6. 32 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/BoardPingController.java
  7. 69 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/DeviceController.java
  8. 128 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/EdgeAlgController.java
  9. 62 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/EdgeMediaController.java
  10. 59 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/RepositoryController.java
  11. 27 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/TaskPreviewController.java
  12. 158 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/UniversalUploadController.java
  13. 104 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiAlarmLog.java
  14. 83 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiDevice.java
  15. 1 1
      service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiQuestion.java
  16. 1 1
      service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiQuestionItem.java
  17. 1 1
      service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiSession.java
  18. 13 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgAbilityFetchRequestDTO.java
  19. 131 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgAbilityFetchResponseDTO.java
  20. 80 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleCreateRequestDTO.java
  21. 25 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleCreateResponseDTO.java
  22. 16 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleDeleteRequestDTO.java
  23. 40 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleDeleteResponseDTO.java
  24. 13 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleFetchRequestDTO.java
  25. 113 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleFetchResponseDTO.java
  26. 20 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitAppendRequestDTO.java
  27. 36 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitAppendResponseDTO.java
  28. 16 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitCreateRequestDTO.java
  29. 36 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitCreateResponseDTO.java
  30. 13 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitFetchRequestDTO.java
  31. 65 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitFetchResponseDTO.java
  32. 18 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitGroupRemoveRequestDTO.java
  33. 33 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitGroupRemoveResponseDTO.java
  34. 20 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitRemoveRequestDTO.java
  35. 33 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitRemoveResponseDTO.java
  36. 16 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgTaskSnapRequestDTO.java
  37. 43 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgTaskSnapResponseDTO.java
  38. 109 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/BoardPingDTO.java
  39. 13 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/DeleteFaceRequestDTO.java
  40. 34 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/DeleteFaceResponseDTO.java
  41. 14 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskCommandRequestDTO.java
  42. 82 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskConfigRequestDTO.java
  43. 15 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskFetchRequestDTO.java
  44. 177 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskListResponseDTO.java
  45. 34 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskResponseDTO.java
  46. 32 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppControllerReplyDTO.java
  47. 42 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppControllerRequestDTO.java
  48. 16 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppDeleteRequestDTO.java
  49. 37 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppDeleteResponseDTO.java
  50. 116 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppFetchResponseDTO.java
  51. 15 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/FaceRecognitionRequestDTO.java
  52. 50 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/FaceRecognitionResponseDTO.java
  53. 16 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RegisterFaceRequestDTO.java
  54. 43 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RegisterFaceResponseDTO.java
  55. 52 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoriesInfoResponseDTO.java
  56. 11 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryCreateRequestDTO.java
  57. 36 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryCreateResponseDTO.java
  58. 10 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryDeleteRequestDTO.java
  59. 33 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryDeleteResponseDTO.java
  60. 16 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryFacesRequestDTO.java
  61. 48 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryFacesResponseDTO.java
  62. 13 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryUpdateRequestDTO.java
  63. 36 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryUpdateResponseDTO.java
  64. 19 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/UpdateFaceRequestDTO.java
  65. 43 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/UpdateFaceResponseDTO.java
  66. 27 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiDeviceMapper.java
  67. 1 1
      service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiQuestionMapper.java
  68. 2 2
      service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiSessionMapper.java
  69. 22 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiDeviceService.java
  70. 332 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/EdgeMediaService.java
  71. 13 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/config/AppConfig.java
  72. 48 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/enums/TopListener.java
  73. 60 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/impl/AiDeviceServiceImpl.java
  74. 299 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/listener/BoardPingListener.java
  75. 50 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/mqtt/MqttBaseConfig.java
  76. 17 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/mqtt/MqttGateway.java
  77. 56 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/mqtt/MqttInConfig.java
  78. 41 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/mqtt/MqttOutConfig.java
  79. 34 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/util/GlobalExceptionHandler.java
  80. 15 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/util/JsonUtils.java
  81. 62 0
      service-ai/service-ai-biz/src/main/resources/mapper/AiDeviceMapper.xml
  82. 3 2
      service-ai/service-ai-biz/src/main/resources/static/tyqw.html

+ 19 - 10
service-ai/service-ai-biz/pom.xml

@@ -35,6 +35,15 @@
             <artifactId>mysql-connector-java</artifactId>
             <scope>runtime</scope>
         </dependency>
+        <!--MQTT依赖-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-integration</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.integration</groupId>
+            <artifactId>spring-integration-mqtt</artifactId>
+        </dependency>
 
         <!-- 阿里dashscope依赖 -->
         <dependency>
@@ -48,8 +57,15 @@
                 </exclusion>
             </exclusions>
         </dependency>
-
-
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.4.0</version>
+        </dependency>
     </dependencies>
 
     <build>
@@ -72,17 +88,10 @@
                 <artifactId>smart-doc-maven-plugin</artifactId>
                 <version>2.1.1</version>
                 <configuration>
-                    <!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
                     <configFile>./src/main/resources/smart-doc.json</configFile>
-                    <!--指定项目名称-->
                     <projectName>test</projectName>
-                    <!--                    <excludes>-->
-                    <!--                        <exclude>com.bizmatics:product-service-provider</exclude>-->
-                    <!--                        <exclude>cn.afterturn:easypoi-web</exclude>-->
-                    <!--                    </excludes>-->
                 </configuration>
             </plugin>
         </plugins>
     </build>
-
-</project>
+</project>

+ 17 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/AITestApplication.java

@@ -0,0 +1,17 @@
+package com.usky.ai;
+
+import com.usky.ai.dto.AlgScheduleFetchResponseDTO;
+import java.util.List;
+
+public class AITestApplication {
+    public static void main(String[] args) {
+        String value = "100000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
+        AlgScheduleFetchResponseDTO.Schedule schedule = new AlgScheduleFetchResponseDTO.Schedule();
+        schedule.setValue(value);
+
+        List<String> timePeriods = schedule.getReadableTimePeriods();
+        for (String period : timePeriods) {
+            System.out.println(period);
+        }
+    }
+}

+ 3 - 6
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiChatController.java

@@ -11,11 +11,10 @@ import com.alibaba.fastjson.JSONObject;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.usky.ai.mapper.AiQuestionMapper;
 import com.usky.ai.mapper.AiSessionMapper;
-import com.usky.ai.service.AiQuestion;
-import com.usky.ai.service.AiQuestionItem;
-import com.usky.ai.service.AiSession;
+import com.usky.ai.domain.AiQuestion;
+import com.usky.ai.domain.AiQuestionItem;
+import com.usky.ai.domain.AiSession;
 import com.usky.ai.service.vo.AiStreamOutputVO;
-import com.usky.common.core.bean.ApiResult;
 import com.usky.common.security.utils.SecurityUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,12 +26,10 @@ import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
 
 import javax.annotation.Resource;
-import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.stream.Collectors;
 
 @Slf4j
 @RestController

+ 1 - 1
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiQuestionController.java

@@ -1,7 +1,7 @@
 package com.usky.ai.controller.web;
 
 import com.usky.ai.mapper.AiQuestionMapper;
-import com.usky.ai.service.AiQuestion;
+import com.usky.ai.domain.AiQuestion;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;

+ 3 - 3
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiSessionController.java

@@ -2,9 +2,9 @@ package com.usky.ai.controller.web;
 
 import com.usky.ai.mapper.AiQuestionMapper;
 import com.usky.ai.mapper.AiSessionMapper;
-import com.usky.ai.service.AiQuestion;
-import com.usky.ai.service.AiQuestionItem;
-import com.usky.ai.service.AiSession;
+import com.usky.ai.domain.AiQuestion;
+import com.usky.ai.domain.AiQuestionItem;
+import com.usky.ai.domain.AiSession;
 import com.usky.common.core.bean.ApiResult;
 import com.usky.common.security.utils.SecurityUtils;
 import lombok.extern.slf4j.Slf4j;

+ 32 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/BoardPingController.java

@@ -0,0 +1,32 @@
+package com.usky.ai.controller.web;
+
+
+import com.usky.ai.dto.BoardPingDTO;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@RestController
+@RequestMapping("/api/heartbeat")
+public class BoardPingController {
+
+    // 每个 BoardId 最新一条心跳
+    public static final Map<String, BoardPingDTO> CACHE = new ConcurrentHashMap<>();
+
+    /**
+     * 返回全部设备最新心跳
+     */
+    @GetMapping
+    public Map<String, BoardPingDTO> all() {
+        return CACHE;
+    }
+
+    /**
+     * 返回指定设备最新心跳
+     */
+    @GetMapping("/{boardId}")
+    public BoardPingDTO one(@PathVariable String boardId) {
+        return CACHE.get(boardId);
+    }
+}

+ 69 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/DeviceController.java

@@ -0,0 +1,69 @@
+package com.usky.ai.controller.web;
+
+import com.usky.ai.domain.AiDevice;
+import com.usky.ai.service.AiDeviceService;
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.core.bean.CommonPage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/device")
+public class DeviceController {
+
+    private final AiDeviceService aiDeviceService;
+
+    @Autowired
+    public DeviceController(AiDeviceService aiDeviceService) {
+        this.aiDeviceService = aiDeviceService;
+    }
+
+    // 返回设备信息,支持分页
+    @GetMapping
+    public ApiResult<CommonPage<AiDevice>> devices(
+            @RequestParam(required = false) String boardId,
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize) {
+        List<AiDevice> devices = aiDeviceService.getDevices(boardId, pageNum, pageSize);
+        long total = aiDeviceService.getTotalDevices(boardId); // 获取总记录数
+        CommonPage<AiDevice> page = new CommonPage<>(devices, total, pageSize, pageNum);
+        return ApiResult.success(page);
+    }
+
+    // 新增设备
+    @PostMapping
+    public ApiResult<Void> addDevice(@RequestBody AiDevice device) {
+        try {
+            aiDeviceService.addDevice(device);
+            return ApiResult.success();
+        } catch (IllegalArgumentException e) {
+            return ApiResult.error(e.getMessage());
+        } catch (Exception e) {
+            return ApiResult.error("新增失败", e.getMessage());
+        }
+    }
+
+    // 编辑设备
+    @PutMapping
+    public ApiResult<Void> editDevice(@RequestBody AiDevice device) {
+        try {
+            aiDeviceService.editDevice(device);
+            return ApiResult.success();
+        } catch (Exception e) {
+            return ApiResult.error("编辑失败", e.getMessage());
+        }
+    }
+
+    // 删除指定设备信息
+    @DeleteMapping("/{boardId}")
+    public ApiResult<Void> delete(@PathVariable String boardId) {
+        try {
+            aiDeviceService.deleteDevice(boardId);
+            return ApiResult.success();
+        } catch (Exception e) {
+            return ApiResult.error("删除失败", e.getMessage());
+        }
+    }
+}

+ 128 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/EdgeAlgController.java

@@ -0,0 +1,128 @@
+package com.usky.ai.controller.web;
+
+import com.usky.ai.dto.*;
+import com.usky.ai.service.EdgeMediaService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/edge-alg")
+@RequiredArgsConstructor
+public class EdgeAlgController {
+
+    private final EdgeMediaService service;
+
+    //算法任务配置
+    @PostMapping("/task/config")
+    public Object configTask(@RequestBody EdgeAlgTaskConfigRequestDTO req) {
+        return service.configTask(req);
+    }
+
+    //算法任务控制
+    @PostMapping("/task/control")
+    public Object controlTask(@RequestParam String boardId,
+                              @RequestParam String session,
+                              @RequestParam Integer command) {
+        return service.controlTask(boardId, session, command);
+    }
+
+    //算法任务删除
+    @DeleteMapping("/task/{boardId}/{session}")
+    public Object deleteTask(@PathVariable String boardId,
+                             @PathVariable String session) {
+        return service.deleteTask(boardId, session);
+    }
+
+    //算法任务查询
+    @GetMapping("/tasks/{boardId}")
+    public Object fetchTasks(@PathVariable String boardId) {
+        return service.fetchTasks(boardId);
+    }
+
+    //新建计划模板
+    @PostMapping("/schedule/create")
+    public Object createSchedule(@RequestBody AlgScheduleCreateRequestDTO req) {
+        return service.createSchedule(req);
+    }
+
+    //查询计划模板
+    @GetMapping("/schedule/fetch/{boardId}")
+    public Object fetchSchedules(@PathVariable String boardId) {
+        AlgScheduleFetchResponseDTO response = service.fetchSchedules(boardId);
+        System.out.println("获取到的计划模板响应: " + response);
+
+        // 获取计划模板数据,优先从result.content获取,其次从content获取
+        List<AlgScheduleFetchResponseDTO.Schedule> schedules = new ArrayList<>();
+        if (response.isSuccess()) {
+            if (response.getResult() != null && response.getResult().getContent() != null && !response.getResult().getContent().isEmpty()) {
+                schedules = response.getResult().getContent();
+            } else if (response.getContent() != null && !response.getContent().isEmpty()) {
+                schedules = response.getContent();
+            }
+        }
+
+        // 直接返回原始响应数据,不进行解析
+        if (!schedules.isEmpty()) {
+            // 打印每个计划模板的信息用于调试
+            for (AlgScheduleFetchResponseDTO.Schedule schedule : schedules) {
+                System.out.println("计划模板: ID=" + schedule.getId() +
+                        ", Name=" + schedule.getName() +
+                        ", Value长度=" + (schedule.getValue() != null ? schedule.getValue().length() : "null") +
+                        ", Value=" + schedule.getValue());
+            }
+            return schedules;
+        } else {
+            List<String> result = new ArrayList<>();
+            result.add("No schedules found");
+            return result;
+        }
+    }
+
+    //删除计划模板
+    @DeleteMapping("/schedule/delete/{boardId}/{id}")
+    public Object deleteSchedule(@PathVariable String boardId,
+                                 @PathVariable Long id) {
+        return service.deleteSchedule(boardId, id);
+    }
+
+    //新建工装组
+    @PostMapping("/suit/create")
+    public Object createSuitGroup(@RequestBody AlgSuitCreateRequestDTO req) {
+        return service.createSuitGroup(req);
+    }
+
+    //删除工装组
+    @DeleteMapping("/suit/group/{boardId}/{sid}")
+    public Object removeSuitGroup(@PathVariable String boardId, @PathVariable int sid) {
+        AlgSuitGroupRemoveRequestDTO req = new AlgSuitGroupRemoveRequestDTO();
+        req.setBoardId(boardId);
+        req.setSid(sid);
+        return service.removeSuitGroup(req);
+    }
+
+    //上传工装模版
+    @PostMapping("/suit/append")
+    public Object appendSuitTemplate(@RequestBody AlgSuitAppendRequestDTO req) {
+        return service.appendSuitTemplate(req);
+    }
+
+    //删除工装模版
+    @DeleteMapping("/suit/remove/{boardId}/{sid}/{fid}")
+    public Object removeSuitTemplate(@PathVariable String boardId, @PathVariable int sid, @PathVariable int fid) {
+        AlgSuitRemoveRequestDTO req = new AlgSuitRemoveRequestDTO();
+        req.setBoardId(boardId);
+        req.setSid(sid);
+        req.setFid(fid);
+        return service.removeSuitTemplate(req);
+    }
+
+    //获取当前配置的模板信息
+    @GetMapping("/suit/fetch/{boardId}")
+    public Object fetchSuitGroup(@PathVariable String boardId) {
+        return service.fetchSuitGroup(boardId);
+    }
+
+}

+ 62 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/EdgeMediaController.java

@@ -0,0 +1,62 @@
+package com.usky.ai.controller.web;
+
+import com.usky.ai.dto.*;
+import com.usky.ai.service.EdgeMediaService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/edge-media")
+@RequiredArgsConstructor
+public class EdgeMediaController {
+
+    private final EdgeMediaService service;
+
+    /**
+     * 新增/修改通道
+     */
+    @PostMapping("/channel")
+    public Object addChannel(@RequestBody EdgeAppControllerRequestDTO req) {
+        // 简单参数校验
+        if (req.getBoardId() == null || req.getMediaName() == null || req.getMediaUrl() == null) {
+            return "BoardId/MediaName/MediaUrl 不能为空";
+        }
+        EdgeAppControllerReplyDTO reply = service.addChannel(req);
+        if (reply.isSuccess()) {
+            // mediaRepository.save(req);
+            return "success";
+        }
+        return reply.getResult();
+    }
+
+
+    /**
+     * 查询通道
+     */
+    @GetMapping("/channels/{boardId}")
+    public Object fetchChannels(@PathVariable String boardId) {
+        EdgeAppFetchResponseDTO resp = service.fetchChannels(boardId);
+        if (resp.isSuccess()) {
+            return resp.getContent();
+        }
+        return resp.getResult();
+    }
+
+    /**
+     * 删除通道
+     */
+    @DeleteMapping("/delchannel/{boardId}/{mediaName}")
+    public Object deleteChannel(@PathVariable String boardId, @PathVariable String mediaName) {
+        EdgeAppDeleteResponseDTO resp = service.deleteChannel(boardId, mediaName);
+        return resp.isSuccess() ? "deleted" : resp.getResult();
+    }
+
+    @GetMapping("/algorithm/ability/{boardId}")
+    public Object fetchAlgorithmAbility(@PathVariable String boardId) {
+        AlgAbilityFetchResponseDTO resp = service.fetchAlgorithmAbility(boardId);
+        if (resp.isSuccess()) {
+            return resp.getAbility();
+        }
+        return resp.getResult();
+    }
+}

+ 59 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/RepositoryController.java

@@ -0,0 +1,59 @@
+package com.usky.ai.controller.web;
+
+import com.usky.ai.dto.*;
+import com.usky.ai.service.EdgeMediaService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/repositories")
+@RequiredArgsConstructor
+public class RepositoryController {
+
+    private final EdgeMediaService service;
+
+    @GetMapping
+    public RepositoriesInfoResponseDTO listRepositories() {
+        return service.listRepositories();
+    }
+
+    @PostMapping("/create")
+    public RepositoryCreateResponseDTO createRepository(@RequestBody RepositoryCreateRequestDTO request) {
+        return service.createRepository(request);
+    }
+
+    @PostMapping("/update")
+    public RepositoryUpdateResponseDTO updateRepository(@RequestBody RepositoryUpdateRequestDTO request) {
+        return service.updateRepository(request);
+    }
+
+    @DeleteMapping("/delete")
+    public RepositoryDeleteResponseDTO deleteRepository(@RequestBody RepositoryDeleteRequestDTO request) {
+        return service.deleteRepository(request);
+    }
+
+    @PostMapping("/faces")
+    public RepositoryFacesResponseDTO getRepositoryFaces(@RequestBody RepositoryFacesRequestDTO request) {
+        return service.getRepositoryFaces(request);
+    }
+
+    @PostMapping("/register")
+    public RegisterFaceResponseDTO registerFace(@RequestBody RegisterFaceRequestDTO request) {
+        return service.registerFace(request);
+    }
+
+    @PostMapping("/update-face")
+    public UpdateFaceResponseDTO updateFace(@RequestBody UpdateFaceRequestDTO request) {
+        return service.updateFace(request);
+    }
+
+    @DeleteMapping("/delete-face")
+    public DeleteFaceResponseDTO deleteFace(@RequestBody DeleteFaceRequestDTO request) {
+        return service.deleteFace(request);
+    }
+
+    @PostMapping("/recognize")
+    public FaceRecognitionResponseDTO recognizeFace(@RequestBody FaceRecognitionRequestDTO request) {
+        return service.recognizeFace(request);
+    }
+}

+ 27 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/TaskPreviewController.java

@@ -0,0 +1,27 @@
+package com.usky.ai.controller.web;
+
+import com.usky.ai.service.EdgeMediaService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class TaskPreviewController {
+
+    @Autowired
+    private EdgeMediaService edgeMediaService;
+
+    @GetMapping("/api/task/preview/{boardId}/{algTaskSession}")
+    public ResponseEntity<String> getTaskPreview(
+            @PathVariable String boardId,
+            @PathVariable String algTaskSession) {
+        try {
+            String imageUrl = edgeMediaService.getTaskPreviewUrl(boardId, algTaskSession);
+            return ResponseEntity.ok(imageUrl);
+        } catch (Exception e) {
+            return ResponseEntity.badRequest().body("Failed to get task preview: " + e.getMessage());
+        }
+    }
+}

+ 158 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/UniversalUploadController.java

@@ -0,0 +1,158 @@
+package com.usky.ai.controller.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.usky.ai.domain.AiAlarmLog;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import org.springframework.http.*;
+import java.time.format.DateTimeFormatter;
+import java.util.Base64;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@RestController
+@RequestMapping("/upload")
+public class UniversalUploadController {
+
+    @Value("${file.domain}")
+    private String fileDomain;
+
+    @Value("${file.path}")
+    private String filePath;
+
+    @Value("${file.prefix}")
+    private String filePrefix;
+
+    @Value("${aiAlarmLog.api}")
+    private String alarmLogApi;
+
+    // 定义一个线程安全的计数器
+    private static final AtomicInteger counter = new AtomicInteger(0);
+
+    @PostMapping(value = "/anything", consumes = "*/*")
+    public String receiveAnything(HttpServletRequest request) {
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) {
+            StringBuilder sb = new StringBuilder();
+            String line;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line).append("\n");
+            }
+            String body = sb.toString();
+            System.out.println("Received raw data:");
+            System.out.println(body);
+
+            // 解析 JSON 数据
+            ObjectMapper objectMapper = new ObjectMapper();
+            JsonNode jsonNode = objectMapper.readTree(body);
+
+            // 创建告警日志对象
+            AiAlarmLog alarmLog = new AiAlarmLog();
+
+            // 设置字段
+            alarmLog.setDeviceId(getTextValue(jsonNode, "BoardId"));
+            alarmLog.setAlarmTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+            alarmLog.setAlarmType("666");
+            alarmLog.setAlarmObject(getTextValue(jsonNode, "TaskSession"));
+            alarmLog.setAlarmData("0");
+            alarmLog.setAlarmAttribute(getTextValue(jsonNode, "Summary"));
+            alarmLog.setAlarmContent(getTextValue(jsonNode.get("Result"), "Description"));
+            alarmLog.setAlarmGrade(1);
+            alarmLog.setAlarmAddress("二楼研发中心");
+            alarmLog.setProductCode("513_202");
+
+
+            // 处理图片存储
+            String imageDataLabeled = getTextValue(jsonNode, "ImageDataLabeled");
+            if (!imageDataLabeled.isEmpty()) {
+                try {
+                    String imageUrl = saveImage(imageDataLabeled, "sitePhoto");
+                    alarmLog.setSitePhoto(imageUrl);
+                } catch (Exception e) {
+                    System.err.println("Failed to save image: " + e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+
+            // 调用新增告警日志接口
+            if (!sendAlarmLog(alarmLog)) {
+                return "Failed to send alarm log";
+            }
+
+            return "Received data:\n" + body;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "Error reading request body: " + e.getMessage();
+        }
+    }
+
+    // 添加辅助方法处理可能的空值
+    private String getTextValue(JsonNode node, String fieldName) {
+        JsonNode fieldNode = node.get(fieldName);
+        if (fieldNode != null && !fieldNode.isNull()) {
+            return fieldNode.asText();
+        }
+        return "";
+    }
+
+    // 保存图片并返回 URL
+    private String saveImage(String base64Image, String fileName) throws IOException {
+        // 检查 Base64 数据是否符合预期格式
+        byte[] imageBytes;
+
+        // 处理带有前缀的Base64数据 (如: data:image/jpeg;base64,/9j/...)
+        if (base64Image.contains(",")) {
+            String[] parts = base64Image.split(",", 2);
+            if (parts.length < 2) {
+                throw new IllegalArgumentException("Invalid Base64 image data format");
+            }
+            imageBytes = Base64.getDecoder().decode(parts[1]);
+        } else {
+            // 处理不带前缀的Base64数据 (如: /9j/...)
+            imageBytes = Base64.getDecoder().decode(base64Image);
+        }
+
+
+        // 生成文件路径 (使用yyyyMMddHHmmss格式)
+        LocalDateTime now = LocalDateTime.now();
+        String monthPath = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
+        String formattedTime = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+        int counterSuffix = counter.getAndIncrement(); // 获取并递增计数器
+        String filePathWithPrefix = filePath + "/" + monthPath + "/" + fileName + "_" + formattedTime + "_" + counterSuffix + ".jpg";
+        Path path = Paths.get(filePathWithPrefix);
+
+        // 保存图片到文件系统
+        Files.createDirectories(path.getParent());
+        Files.write(path, imageBytes);
+
+        // 返回可访问的 URL
+        return fileDomain + filePrefix + "/" + monthPath + "/" + fileName + "_" + formattedTime + "_" + counterSuffix + ".jpg";
+    }
+
+    // 调用新增告警日志接口
+    private boolean sendAlarmLog(AiAlarmLog alarmLog) {
+        RestTemplate restTemplate = new RestTemplate();
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+
+        HttpEntity<AiAlarmLog> request = new HttpEntity<>(alarmLog, headers);
+
+        try {
+            restTemplate.postForObject(alarmLogApi, request, String.class);
+            return true;
+        } catch (Exception e) {
+            System.err.println("Failed to send alarm log: " + e.getMessage());
+            e.printStackTrace();
+            return false;
+        }
+    }
+}

+ 104 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiAlarmLog.java

@@ -0,0 +1,104 @@
+package com.usky.ai.domain;
+
+public class AiAlarmLog {
+    private String deviceId;
+    private String alarmTime;
+    private String alarmType;
+    private String alarmObject;
+    private String alarmData;
+    private String alarmAttribute;
+    private String alarmContent;
+    private Integer alarmGrade;
+    private String alarmAddress;
+    private String productCode;
+    private String sitePhoto;
+
+    // Getters and Setters
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getAlarmTime() {
+        return alarmTime;
+    }
+
+    public void setAlarmTime(String alarmTime) {
+        this.alarmTime = alarmTime;
+    }
+
+    public String getAlarmType() {
+        return alarmType;
+    }
+
+    public void setAlarmType(String alarmType) {
+        this.alarmType = alarmType;
+    }
+
+    public String getAlarmObject() {
+        return alarmObject;
+    }
+
+    public void setAlarmObject(String alarmObject) {
+        this.alarmObject = alarmObject;
+    }
+
+    public String getAlarmData() {
+        return alarmData;
+    }
+
+    public void setAlarmData(String alarmData) {
+        this.alarmData = alarmData;
+    }
+
+    public String getAlarmAttribute() {
+        return alarmAttribute;
+    }
+
+    public void setAlarmAttribute(String alarmAttribute) {
+        this.alarmAttribute = alarmAttribute;
+    }
+
+    public String getAlarmContent() {
+        return alarmContent;
+    }
+
+    public void setAlarmContent(String alarmContent) {
+        this.alarmContent = alarmContent;
+    }
+
+    public Integer getAlarmGrade() {
+        return alarmGrade;
+    }
+
+    public void setAlarmGrade(Integer alarmGrade) {
+        this.alarmGrade = alarmGrade;
+    }
+
+    public String getAlarmAddress() {
+        return alarmAddress;
+    }
+
+    public void setAlarmAddress(String alarmAddress) {
+        this.alarmAddress = alarmAddress;
+    }
+
+    public String getProductCode() {
+        return productCode;
+    }
+
+    public void setProductCode(String productCode) {
+        this.productCode = productCode;
+    }
+
+    public String getSitePhoto() {
+        return sitePhoto;
+    }
+
+    public void setSitePhoto(String sitePhoto) {
+        this.sitePhoto = sitePhoto;
+    }
+}

+ 83 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiDevice.java

@@ -0,0 +1,83 @@
+package com.usky.ai.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.sql.Timestamp;
+
+public class AiDevice {
+    private String boardId;
+    private String boardIp;
+    private String description;
+    private Integer status;
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Timestamp createTime;
+    private String createBy;
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Timestamp updateTime;
+    private String updateBy;
+
+    // Getters and Setters
+    public String getBoardId() {
+        return boardId;
+    }
+
+    public void setBoardId(String boardId) {
+        this.boardId = boardId;
+    }
+
+    public String getBoardIp() {
+        return boardIp;
+    }
+
+    public void setBoardIp(String boardIp) {
+        this.boardIp = boardIp;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public String getDescription() { // 新增 getter
+        return description;
+    }
+
+    public void setDescription(String description) { // 新增 setter
+        this.description = description;
+    }
+
+    public Timestamp getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Timestamp createTime) {
+        this.createTime = createTime;
+    }
+
+    public String getCreateBy() {
+        return createBy;
+    }
+
+    public void setCreateBy(String createBy) {
+        this.createBy = createBy;
+    }
+
+    public Timestamp getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Timestamp updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getUpdateBy() {
+        return updateBy;
+    }
+
+    public void setUpdateBy(String updateBy) {
+        this.updateBy = updateBy;
+    }
+}

+ 1 - 1
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiQuestion.java → service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiQuestion.java

@@ -1,4 +1,4 @@
-package com.usky.ai.service;
+package com.usky.ai.domain;
 
 import java.time.LocalDateTime;
 

+ 1 - 1
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiQuestionItem.java → service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiQuestionItem.java

@@ -1,4 +1,4 @@
-package com.usky.ai.service;
+package com.usky.ai.domain;
 
 import java.time.LocalDateTime;
 

+ 1 - 1
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiSession.java → service-ai/service-ai-biz/src/main/java/com/usky/ai/domain/AiSession.java

@@ -1,4 +1,4 @@
-package com.usky.ai.service;
+package com.usky.ai.domain;
 
 import java.time.LocalDateTime;
 import java.util.List;

+ 13 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgAbilityFetchRequestDTO.java

@@ -0,0 +1,13 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class AlgAbilityFetchRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_ability_fetch"; // 固定值
+}

+ 131 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgAbilityFetchResponseDTO.java

@@ -0,0 +1,131 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgAbilityFetchResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Ability")
+    private List<Algorithm> ability;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    private long time = System.currentTimeMillis();
+
+    public boolean isSuccess() {
+        return result != null && Integer.valueOf(0).equals(result.getCode());
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Algorithm {
+        @JsonProperty("attribute")
+        private JsonNode attribute;
+
+        @JsonProperty("parameters")
+        private List<Parameter> parameters;
+
+        @JsonProperty("permitted")
+        private boolean permitted;
+
+        @JsonProperty("code")
+        private int code;
+
+        @JsonProperty("sub")
+        private boolean sub;
+
+        @JsonProperty("name")
+        private String name;
+
+        @JsonProperty("desc")
+        private String desc;
+
+        @JsonProperty("item")
+        private int item;
+
+        @JsonProperty("policy")
+        private List<Policy> policy;
+
+        @Data
+        @JsonIgnoreProperties(ignoreUnknown = true)
+        public static class Parameter {
+            @JsonProperty("class")
+            private String classType;
+
+            @JsonProperty("type")
+            private int type;
+
+            @JsonProperty("max")
+            private Object max;
+
+            @JsonProperty("min")
+            private Object min;
+
+            @JsonProperty("default")
+            private Object defaultValue;
+
+            @JsonProperty("key")
+            private String key;
+
+            @JsonProperty("name")
+            private String name;
+
+            @JsonProperty("required")
+            private boolean required;
+
+            @JsonProperty("value")
+            private Object value;
+
+            @JsonProperty("options")
+            private List<Option> options;
+
+            @Data
+            @JsonIgnoreProperties(ignoreUnknown = true)
+            public static class Option {
+                @JsonProperty("enable")
+                private boolean enable;
+
+                @JsonProperty("key")
+                private String key;
+
+                @JsonProperty("value")
+                private int value;
+
+                @JsonProperty("name")
+                private String name;
+            }
+        }
+
+        @Data
+        @JsonIgnoreProperties(ignoreUnknown = true)
+        public static class Policy {
+            @JsonProperty("property")
+            private String property;
+
+            @JsonProperty("name")
+            private String name;
+        }
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private int code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+}

+ 80 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleCreateRequestDTO.java

@@ -0,0 +1,80 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class AlgScheduleCreateRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_schedule_create";
+
+    @JsonProperty("name")
+    private String name;
+
+    @JsonProperty("summary")
+    private String summary;
+
+    @JsonProperty("timePeriods")
+    private List<TimePeriod> timePeriods;
+
+    @JsonProperty("value")
+    private String value;
+
+    public void convertTimePeriodsToBinary() {
+        StringBuilder binaryValue = new StringBuilder();
+        for (int i = 0; i < 336; i++) {
+            binaryValue.append("0");
+        }
+
+        for (TimePeriod period : timePeriods) {
+            processTimePeriod(binaryValue, period);
+        }
+
+        this.value = binaryValue.toString();
+    }
+
+    private void processTimePeriod(StringBuilder binaryValue, TimePeriod period) {
+        // 将 dayOfWeek 转换为从 0 开始的索引
+        int startDay = period.getDayOfWeek() - 1;
+        int endDay = startDay; // 默认结束天数与开始天数相同
+        if (period.getToDayOfWeek() != null) {
+            endDay = period.getToDayOfWeek() - 1; // 如果有结束天数,也转换为从 0 开始的索引
+        }
+
+        // 计算开始和结束时间的分钟数
+        int startMinutes = Integer.parseInt(period.getStartTime().substring(0, 2)) * 60 + Integer.parseInt(period.getStartTime().substring(3));
+        int endMinutes = Integer.parseInt(period.getEndTime().substring(0, 2)) * 60 + Integer.parseInt(period.getEndTime().substring(3));
+
+        // 计算开始和结束时间的索引
+        int startIdx = (startDay * 48) + (startMinutes / 30);
+        int endIdx = (endDay * 48) + (endMinutes / 30);
+
+        // 填充时间段
+        for (int i = startIdx; i < endIdx; i++) {
+            binaryValue.setCharAt(i, '1');
+        }
+    }
+
+    @Data
+    @NoArgsConstructor
+    public static class TimePeriod {
+        @JsonProperty("dayOfWeek")
+        private int dayOfWeek; // 0-6 表示周日到周六
+
+        @JsonProperty("toDayOfWeek")
+        private Integer toDayOfWeek; // 可选,表示连续的结束星期几
+
+        @JsonProperty("startTime")
+        private String startTime; // 格式为 "HH:mm"
+
+        @JsonProperty("endTime")
+        private String endTime; // 格式为 "HH:mm"
+    }
+}

+ 25 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleCreateResponseDTO.java

@@ -0,0 +1,25 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgScheduleCreateResponseDTO {
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("ScheduleId")
+    private String scheduleId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_schedule_create";
+
+    private long time = System.currentTimeMillis();
+
+}

+ 16 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleDeleteRequestDTO.java

@@ -0,0 +1,16 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class AlgScheduleDeleteRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_schedule_delete";
+
+    @JsonProperty("id")
+    private Long id;
+}

+ 40 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleDeleteResponseDTO.java

@@ -0,0 +1,40 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgScheduleDeleteResponseDTO {
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_schedule_delete";
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("ScheduleId")
+    private Long scheduleId;
+
+    public boolean isSuccess() {
+        return result != null && result.getCode() == 0;
+    }
+
+    private long time = System.currentTimeMillis();
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private int code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+}

+ 13 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleFetchRequestDTO.java

@@ -0,0 +1,13 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class AlgScheduleFetchRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_schedule_fetch";
+}

+ 113 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgScheduleFetchResponseDTO.java

@@ -0,0 +1,113 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgScheduleFetchResponseDTO {
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_schedule_fetch";
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("Content")
+    private List<Schedule> content;
+
+    public boolean isSuccess() {
+        return result != null && result.getCode() == 0;
+    }
+
+    private long time = System.currentTimeMillis();
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")  //0成功/其他失败
+        private int code;
+
+        @JsonProperty("Content")
+        private List<Schedule> content; // Content 属性在 Result 中
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Schedule {
+        @JsonProperty("Id")
+        private int id;
+
+        @JsonProperty("Name")
+        private String name;
+
+        @JsonProperty("Summary")
+        private String summary;
+
+        @JsonProperty("Value")
+        private String value;
+
+        public List<String> getReadableTimePeriods() {
+            List<String> timePeriods = new ArrayList<>();
+            char[] binaryValue = value.toCharArray();
+            String[] days = {"星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日",};
+
+            // 检查 Value 字段的长度是否正确
+            if (binaryValue.length != 336) {
+                //System.err.println("Value 字段长度不正确,预期长度为 336,实际长度为 " + binaryValue.length);
+                return timePeriods; // 如果长度不足,返回空列表
+            }
+
+            for (int day = 0; day < 7; day++) {
+                StringBuilder dayPeriods = new StringBuilder();
+                boolean inPeriod = false;
+                int startHour = -1;
+                int startMinute = -1;
+
+                for (int halfHour = 0; halfHour < 48; halfHour++) {
+                    int hour = halfHour / 2;
+                    int minute = (halfHour % 2) * 30;
+                    int index = day * 48 + halfHour;
+
+                    if (index < binaryValue.length && binaryValue[index] == '1') {
+                        if (!inPeriod) {
+                            inPeriod = true;
+                            startHour = hour;
+                            startMinute = minute;
+                        }
+                    } else {
+                        if (inPeriod) {
+                            inPeriod = false;
+                            dayPeriods.append(String.format("%02d:%02d~%02d:%02d", startHour, startMinute, hour, minute)).append(", ");
+                        }
+                    }
+                }
+
+                if (inPeriod) {
+                    int hour = 23;
+                    int minute = 30;
+                    dayPeriods.append(String.format("%02d:%02d~%02d:%02d", startHour, startMinute, hour, minute)).append(", ");
+                }
+
+                if (dayPeriods.length() > 0) {
+                    timePeriods.add(days[day] + " " + dayPeriods.toString().trim().replaceAll(", $", ""));
+                }
+            }
+
+            System.out.println("Readable time periods: " + timePeriods);
+            return timePeriods;
+        }
+    }
+}

+ 20 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitAppendRequestDTO.java

@@ -0,0 +1,20 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class AlgSuitAppendRequestDTO {
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_suit_append";
+
+    @JsonProperty("sid")
+    private int sid;
+
+    @JsonProperty("jpeg")
+    private String jpeg;
+}

+ 36 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitAppendResponseDTO.java

@@ -0,0 +1,36 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgSuitAppendResponseDTO {
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    private long time = System.currentTimeMillis();
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private int code;
+
+        @JsonProperty("Desc")
+        private String desc;
+
+        @JsonProperty("FeatureId")
+        private Integer featureId;
+    }
+}

+ 16 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitCreateRequestDTO.java

@@ -0,0 +1,16 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class AlgSuitCreateRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_suit_create";
+
+    @JsonProperty("name")
+    private String name;
+}

+ 36 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitCreateResponseDTO.java

@@ -0,0 +1,36 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgSuitCreateResponseDTO {
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    private long time = System.currentTimeMillis();
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private int code;
+
+        @JsonProperty("Desc")
+        private String desc;
+
+        @JsonProperty("SuitId")
+        private Integer suitId;
+    }
+}

+ 13 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitFetchRequestDTO.java

@@ -0,0 +1,13 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class AlgSuitFetchRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_suit_fetch";
+}

+ 65 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitFetchResponseDTO.java

@@ -0,0 +1,65 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgSuitFetchResponseDTO {
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private int code;
+
+        @JsonProperty("Desc")
+        private String desc;
+
+        @JsonProperty("Content")
+        private List<SuitGroup> content;
+    }
+
+    @Data
+    public static class SuitGroup {
+        @JsonProperty("id")
+        private int id;
+
+        @JsonProperty("name")
+        private String name;
+
+        @JsonProperty("features")
+        private List<Feature> features;
+    }
+
+    @Data
+    public static class Feature {
+        @JsonProperty("feature")
+        private String feature;
+
+        @JsonProperty("fid")
+        private int fid;
+
+        @JsonProperty("jpeg")
+        private String jpeg;
+
+        @JsonProperty("size")
+        private int size;
+    }
+
+    private long time = System.currentTimeMillis();
+}

+ 18 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitGroupRemoveRequestDTO.java

@@ -0,0 +1,18 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class AlgSuitGroupRemoveRequestDTO {
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_suit_group_remove";
+
+    @JsonProperty("sid")
+    private int sid;
+}

+ 33 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitGroupRemoveResponseDTO.java

@@ -0,0 +1,33 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgSuitGroupRemoveResponseDTO {
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    private long time = System.currentTimeMillis();
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private int code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+}

+ 20 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitRemoveRequestDTO.java

@@ -0,0 +1,20 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class AlgSuitRemoveRequestDTO {
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_suit_remove";
+
+    @JsonProperty("sid")
+    private int sid;
+
+    @JsonProperty("fid")
+    private int fid;
+}

+ 33 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgSuitRemoveResponseDTO.java

@@ -0,0 +1,33 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgSuitRemoveResponseDTO {
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    private long time = System.currentTimeMillis();
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private int code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+}

+ 16 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgTaskSnapRequestDTO.java

@@ -0,0 +1,16 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class AlgTaskSnapRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_task_snap";
+
+    @JsonProperty("AlgTaskSession")
+    private String algTaskSession;
+}

+ 43 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/AlgTaskSnapResponseDTO.java

@@ -0,0 +1,43 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AlgTaskSnapResponseDTO {
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("AlgTaskSession")
+    private String algTaskSession;
+
+    @JsonProperty("Base64")
+    private String base64;
+
+    public boolean isSuccess() {
+        return result != null && Integer.valueOf(0).equals(result.getCode());
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private int code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    private long time = System.currentTimeMillis();
+}

+ 109 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/BoardPingDTO.java

@@ -0,0 +1,109 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BoardPingDTO {
+    //当前盒子唯一表示
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    //当前ip
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    //当前设备硬件类型
+    @JsonProperty("BoardPlatform")
+    private String boardPlatform;
+
+    //当前设备芯片温度
+    @JsonProperty("BoardTemp")
+    private String boardTemp;
+
+    //当前设备接入方式 WAN/4G
+    @JsonProperty("BoardType")
+    private String boardType;
+
+    //GB28181 设备号配置后透传
+    @JsonProperty("GBClientId")
+    private String gbClientId;
+
+    @JsonProperty("GrantCode")
+    private String grantCode;
+
+    @JsonProperty("GrantDesc")
+    private String grantDesc;
+
+    //当前设备状态
+    @JsonProperty("Key")
+    private String key;
+
+    //GPS 信息 部分设备支持
+    @JsonProperty("Latitude")
+    private String latitude;
+
+    //GPS 信息 部分设备支持
+    @JsonProperty("Longitude")
+    private String longitude;
+
+    // 当前应用VmRSS信息
+    @JsonProperty("Malloc")
+    private String malloc;
+
+    //本次应用PID
+    @JsonProperty("Pid")
+    private String pid;
+
+    //特定设备时透传主控端IP
+    @JsonProperty("Se6ip")
+    private String se6ip;
+
+    @JsonProperty("Status")
+    private String status;
+
+    //当前设备系统版本
+    @JsonProperty("System")
+    private String system;
+
+    //当前时间戳毫秒
+    @JsonProperty("Time")
+    private Long time;
+
+    //软件版本
+    @JsonProperty("Version")
+    private String version;
+
+    //GPS 信息 部分设备支持
+    @JsonProperty("angleCourse")
+    private String angleCourse;
+    @JsonProperty("kSpeed")
+    private String kSpeed;
+    @JsonProperty("nSpeed")
+    private String nSpeed;
+
+
+
+    //当前设备硬盘情况kB
+    @JsonProperty("HostDisk")
+    private JsonNode hostDisk;
+
+    //当前设备内存使用情况
+    @JsonProperty("HostMemory")
+    private JsonNode hostMemory;
+
+    //当前设备添加的通道信息
+    @JsonProperty("Medias")
+    private JsonNode medias;
+
+    //当前设备配置的任务信息
+    @JsonProperty("Tasks")
+    private JsonNode tasks;
+
+    //当前设备的算力资源使用情况
+    @JsonProperty("Tpu")
+    private JsonNode tpu;
+}

+ 13 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/DeleteFaceRequestDTO.java

@@ -0,0 +1,13 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class DeleteFaceRequestDTO {
+    @JsonProperty("albumName")
+    private String albumName;
+
+    @JsonProperty("photoId")
+    private int photoId;
+}

+ 34 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/DeleteFaceResponseDTO.java

@@ -0,0 +1,34 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class DeleteFaceResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("albumId")
+    private int albumId;
+
+    @JsonProperty("photoId")
+    private int photoId;
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+}

+ 14 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskCommandRequestDTO.java

@@ -0,0 +1,14 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EdgeAlgTaskCommandRequestDTO {
+    @JsonProperty("BoardId") private String boardId;
+    @JsonProperty("Event") private String event; // /alg_task_control 或 /alg_task_delete
+    @JsonProperty("ControlCommand") private Integer controlCommand; // 0 停止 1 启动
+    @JsonProperty("AlgTaskSession") private String algTaskSession;
+}

+ 82 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskConfigRequestDTO.java

@@ -0,0 +1,82 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EdgeAlgTaskConfigRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+    @JsonProperty("Event")
+    private String event = "/alg_task_config";
+    @JsonProperty("AlgTaskSession")
+    private String algTaskSession;
+    @JsonProperty("TaskDesc")
+    private String taskDesc;
+    @JsonProperty("AlgInfo")
+    private List<Integer> algInfo;
+    @JsonProperty("MediaName")
+    private String mediaName;
+    @JsonProperty("MetadataUrl")
+    private List<String> metadataUrl;
+    @JsonProperty("ScheduleId")
+    private Integer scheduleId = -1;
+    @JsonProperty("UserData")
+    private UserData userData;
+    @JsonProperty("RuleProperty")
+    private List<RuleProperty> ruleProperty;
+    @JsonProperty("Template")
+    private String template = "";
+    @JsonProperty("GB28181Channel")
+    private String gb28181Channel = "";
+    @JsonProperty("AlarmProtocol")
+    private Integer alarmProtocol = 0;
+    @JsonProperty("AlarmBody")
+    private Integer alarmBody = 0;
+    @JsonProperty("Restart")
+    private Boolean restart = true;
+
+    @Data
+    public static class UserData {
+        @JsonProperty("MethodConfig")
+        private List<Integer> methodConfig;
+
+        /* 离岗算法业务字段 */
+        @JsonProperty("staff_sec")
+        private Integer staffSec;
+        @JsonProperty("staff_number")
+        private Integer staffNumber;
+    }
+
+    @Data
+    public static class RuleProperty {
+        @JsonProperty("Algo")
+        private Algo algo;
+        @JsonProperty("Points")
+        private List<Point> points;
+        @JsonProperty("RuleId")
+        private String ruleId;
+        @JsonProperty("RuleType")
+        private Integer ruleType;
+
+        @Data
+        public static class Algo {
+            @JsonProperty("majorId")
+            private Integer majorId;
+            @JsonProperty("minorId")
+            private Integer minorId;
+        }
+
+        @Data
+        public static class Point {
+            @JsonProperty("X")
+            private Double x;
+            @JsonProperty("Y")
+            private Double y;
+        }
+    }
+}

+ 15 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskFetchRequestDTO.java

@@ -0,0 +1,15 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EdgeAlgTaskFetchRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_task_fetch";
+}

+ 177 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskListResponseDTO.java

@@ -0,0 +1,177 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EdgeAlgTaskListResponseDTO {
+
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Content")
+    private List<TaskItem> content;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    public boolean isSuccess() {
+        return result != null && Integer.valueOf(0).equals(result.getCode());
+    }
+
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class TaskItem {
+        @JsonProperty("AlgTaskSession")
+        private String algTaskSession;
+
+        @JsonProperty("MediaName")
+        private String mediaName;
+
+        @JsonProperty("AlgTaskStatus")
+        private AlgTaskStatus algTaskStatus;
+
+        @JsonProperty("AlarmBody")
+        private Integer alarmBody;
+
+        @JsonProperty("AlarmProtocol")
+        private Integer alarmProtocol;
+
+        @JsonProperty("MetadataUrl")
+        private String metadataUrl;
+
+        @JsonProperty("TaskDesc")
+        private String taskDesc;
+
+        @JsonProperty("UserData")
+        private UserData userData;
+
+        @JsonProperty("AlgInfo")
+        private List<Integer> algInfo;
+
+        @JsonProperty("GB28181Channel")
+        private String gb28181Channel;
+
+        @JsonProperty("Template")
+        private String template;
+
+        @JsonProperty("Restart")
+        private Boolean restart;
+
+        @JsonProperty("RuleProperty")
+        private List<RuleProperty> ruleProperty;
+
+        @JsonProperty("BaseAlgItem")
+        private List<BaseAlgItem> baseAlgItem;
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class AlgTaskStatus {
+        @JsonProperty("label")
+        private String label;
+
+        @JsonProperty("style")
+        private String style;
+
+        @JsonProperty("type")
+        private Integer type;
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class UserData {
+        @JsonProperty("MethodConfig")
+        private List<Integer> methodConfig;
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class RuleProperty {
+        @JsonProperty("RuleId")
+        private String ruleId;
+
+        @JsonProperty("RuleType")
+        private Integer ruleType;
+
+        @JsonProperty("RuleTypeName")
+        private String ruleTypeName;
+
+        @JsonProperty("Algo")
+        private Algo algo;
+
+        @JsonProperty("Points")
+        private List<Point> points;
+
+        @Data
+        @JsonIgnoreProperties(ignoreUnknown = true)
+        public static class Algo {
+            @JsonProperty("majorId")
+            private Integer majorId;
+
+            @JsonProperty("minorId")
+            private Integer minorId;
+        }
+
+        @Data
+        @JsonIgnoreProperties(ignoreUnknown = true)
+        public static class Point {
+            @JsonProperty("X")
+            private Double x;
+
+            @JsonProperty("Y")
+            private Double y;
+        }
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class BaseAlgItem {
+        @JsonProperty("lineDesc")
+        private String lineDesc;
+
+        @JsonProperty("lineRequired")
+        private Boolean lineRequired;
+
+        @JsonProperty("majorId")
+        private Integer majorId;
+
+        @JsonProperty("majorOnly")
+        private Boolean majorOnly;
+
+        @JsonProperty("minorId")
+        private Integer minorId;
+
+        @JsonProperty("name")
+        private String name;
+
+        @JsonProperty("zoneDesc")
+        private String zoneDesc;
+
+        @JsonProperty("zoneRequired")
+        private Boolean zoneRequired;
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    private long time = System.currentTimeMillis();
+}

+ 34 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAlgTaskResponseDTO.java

@@ -0,0 +1,34 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EdgeAlgTaskResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+    @JsonProperty("BoardIp")
+    private String boardIp;
+    @JsonProperty("Event")
+    private String event;
+    @JsonProperty("AlgTaskSession")
+    private String algTaskSession;
+    @JsonProperty("Result")
+    private Result result;
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    public boolean isSuccess() {
+        return result != null && Integer.valueOf(0).equals(result.code);
+    }
+
+    private long time = System.currentTimeMillis();
+}

+ 32 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppControllerReplyDTO.java

@@ -0,0 +1,32 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class EdgeAppControllerReplyDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+    @JsonProperty("BoardIp")
+    private String boardIp;
+    @JsonProperty("Event")
+    private String event = "/alg_media_config"; // 固定值
+    @JsonProperty("MediaName")
+    private String mediaName;
+    @JsonProperty("Result")
+    private Result result;
+
+    private long time = System.currentTimeMillis();
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    public boolean isSuccess() {
+        return result != null && Integer.valueOf(0).equals(result.code);
+    }
+}

+ 42 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppControllerRequestDTO.java

@@ -0,0 +1,42 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class EdgeAppControllerRequestDTO {
+
+    /** 与文档一致,首字母大写以兼容盒子 */
+    @JsonProperty("BoardId")
+    private String boardId;
+    @JsonProperty("Event")
+    private String event;
+    @JsonProperty("MediaName")
+    private String mediaName;
+    @JsonProperty("MediaUrl")
+    private String mediaUrl;
+    @JsonProperty("MediaDesc")
+    private String mediaDesc;
+    @JsonProperty("RtspTransport")
+    private Boolean rtspTransport;
+    @JsonProperty("GBTransport")
+    private Boolean gbTransport;
+    @JsonProperty("SubId")
+    private String subId;
+    @JsonProperty("Params")
+    private List<Param> params;
+
+    @Data
+    public static class Param {
+        @JsonProperty("Key")
+        private String key;
+        @JsonProperty("Name")
+        private String name;
+        @JsonProperty("Type")
+        private String type;
+        @JsonProperty("Value")
+        private String value;
+    }
+}

+ 16 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppDeleteRequestDTO.java

@@ -0,0 +1,16 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class EdgeAppDeleteRequestDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("Event")
+    private String event = "/alg_media_delete"; // 固定值
+
+    @JsonProperty("MediaName")
+    private String mediaName;
+}

+ 37 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppDeleteResponseDTO.java

@@ -0,0 +1,37 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class EdgeAppDeleteResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("MediaName")
+    private String mediaName;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    private long time = System.currentTimeMillis();
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    public boolean isSuccess() {
+        return result != null && Integer.valueOf(0).equals(result.code);
+    }
+}

+ 116 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/EdgeAppFetchResponseDTO.java

@@ -0,0 +1,116 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EdgeAppFetchResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Content")
+    private List<MediaContent> content;
+
+    @JsonProperty("Event")
+    private String event = "/alg_media_fetch"; // 固定值
+
+    @JsonProperty("Result")
+    private Result result;
+
+    private long time = System.currentTimeMillis();
+
+    public boolean isSuccess() {
+        return result != null && Integer.valueOf(0).equals(result.getCode());
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class MediaContent {
+        @JsonProperty("MediaName")
+        private String mediaName;
+
+        @JsonProperty("MediaDesc")
+        private String mediaDesc;
+
+        @JsonProperty("MediaUrl")
+        private String mediaUrl;
+
+        @JsonProperty("RtspTransport")
+        private Boolean rtspTransport;
+
+        @JsonProperty("GBTransport")
+        private Boolean gbTransport;
+
+        @JsonProperty("SubId")
+        private String subId;
+
+        @JsonProperty("MediaStatus")
+        private MediaStatus mediaStatus;
+
+        @JsonProperty("Params")
+        private List<Param> params;
+
+        @JsonProperty("ProtocolType")
+        private Integer protocolType;
+
+        @JsonProperty("SipBChannelId")
+        private String sipBChannelId;
+
+        @Data
+        @JsonIgnoreProperties(ignoreUnknown = true)
+        public static class Param {
+            @JsonProperty("Key")
+            private String key;
+            @JsonProperty("Name")
+            private String name;
+            @JsonProperty("Type")
+            private String type;
+
+            // 既能是 String 也能是 List<String>
+            @JsonProperty("Value")
+            private Object value;
+        }
+
+        @Data
+        @JsonIgnoreProperties(ignoreUnknown = true)
+        public static class MediaStatus {
+            @JsonProperty("type")
+            private Integer type;
+
+            @JsonProperty("style")
+            private String style;
+
+            @JsonProperty("label")
+            private String label;
+
+            @JsonProperty("size")
+            private Size size;
+
+            @Data
+            public static class Size {
+                @JsonProperty("width")
+                private Integer width;
+
+                @JsonProperty("height")
+                private Integer height;
+            }
+        }
+    }
+}

+ 15 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/FaceRecognitionRequestDTO.java

@@ -0,0 +1,15 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FaceRecognitionRequestDTO {
+    @JsonProperty("albumIdList")
+    private List<Integer> albumIdList;
+
+    @JsonProperty("imageBase64")
+    private String imageBase64;
+}

+ 50 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/FaceRecognitionResponseDTO.java

@@ -0,0 +1,50 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import java.util.List;
+
+@Data
+public class FaceRecognitionResponseDTO {
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("album1vnList")
+    private List<Album1vn> album1vnList;
+
+    @JsonProperty("totalCount")
+    private int totalCount;
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    @Data
+    public static class Album1vn {
+        @JsonProperty("Height")
+        private int height;
+
+        @JsonProperty("Width")
+        private int width;
+
+        @JsonProperty("X")
+        private int x;
+
+        @JsonProperty("Y")
+        private int y;
+
+        @JsonProperty("albumId")
+        private int albumId;
+
+        @JsonProperty("photoId")
+        private int photoId;
+
+        @JsonProperty("score")
+        private double score;
+    }
+}

+ 16 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RegisterFaceRequestDTO.java

@@ -0,0 +1,16 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class RegisterFaceRequestDTO {
+    @JsonProperty("albumName")
+    private String albumName;
+
+    @JsonProperty("name")
+    private String name;
+
+    @JsonProperty("image")
+    private String image;
+}

+ 43 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RegisterFaceResponseDTO.java

@@ -0,0 +1,43 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class RegisterFaceResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("albumId")
+    private int albumId;
+
+    @JsonProperty("alignedImage")
+    private String alignedImage;
+
+    @JsonProperty("croppedImage")
+    private String croppedImage;
+
+    @JsonProperty("photoId")
+    private int photoId;
+
+    @JsonProperty("photoName")
+    private String photoName;
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+}

+ 52 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoriesInfoResponseDTO.java

@@ -0,0 +1,52 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class RepositoriesInfoResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("data")
+    private List<Album> data;
+
+    @JsonProperty("totalCount")
+    private int totalCount;
+
+    @Data
+    public static class Album {
+        @JsonProperty("albumId")
+        private int albumId;
+
+        @JsonProperty("albumName")
+        private String albumName;
+
+        @JsonProperty("pictureNo")
+        private int pictureNo;
+    }
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    private long time = System.currentTimeMillis();
+}

+ 11 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryCreateRequestDTO.java

@@ -0,0 +1,11 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class RepositoryCreateRequestDTO {
+    @JsonProperty("albumName")
+    private String albumName;
+}
+

+ 36 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryCreateResponseDTO.java

@@ -0,0 +1,36 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class RepositoryCreateResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("albumId")
+    private int albumId;
+
+    @JsonProperty("albumName")
+    private String albumName;
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    private long time = System.currentTimeMillis();
+}

+ 10 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryDeleteRequestDTO.java

@@ -0,0 +1,10 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class RepositoryDeleteRequestDTO {
+    @JsonProperty("albumName")
+    private String albumName;
+}

+ 33 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryDeleteResponseDTO.java

@@ -0,0 +1,33 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class RepositoryDeleteResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("albumName")
+    private String albumName;
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    private long time = System.currentTimeMillis();
+}

+ 16 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryFacesRequestDTO.java

@@ -0,0 +1,16 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class RepositoryFacesRequestDTO {
+    @JsonProperty("albumName")
+    private String albumName;
+
+    @JsonProperty("page")
+    private int page;
+
+    @JsonProperty("size")
+    private int size;
+}

+ 48 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryFacesResponseDTO.java

@@ -0,0 +1,48 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class RepositoryFacesResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("data")
+    private List<Face> data;
+
+    @JsonProperty("total")
+    private int total;
+
+    @Data
+    public static class Face {
+        @JsonProperty("albumId")
+        private String albumId;
+
+        @JsonProperty("photoId")
+        private int photoId;
+
+        @JsonProperty("photoName")
+        private String photoName;
+    }
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+}

+ 13 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryUpdateRequestDTO.java

@@ -0,0 +1,13 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class RepositoryUpdateRequestDTO {
+    @JsonProperty("albumName")
+    private String albumName;
+
+    @JsonProperty("name")
+    private String name;
+}

+ 36 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/RepositoryUpdateResponseDTO.java

@@ -0,0 +1,36 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class RepositoryUpdateResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("albumId")
+    private int albumId;
+
+    @JsonProperty("albumName")
+    private String albumName;
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+
+    private long time = System.currentTimeMillis();
+}

+ 19 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/UpdateFaceRequestDTO.java

@@ -0,0 +1,19 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class UpdateFaceRequestDTO {
+    @JsonProperty("albumName")
+    private String albumName;
+
+    @JsonProperty("name")
+    private String name;
+
+    @JsonProperty("photoId")
+    private int photoId;
+
+    @JsonProperty("image")
+    private String image;
+}

+ 43 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/dto/UpdateFaceResponseDTO.java

@@ -0,0 +1,43 @@
+package com.usky.ai.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class UpdateFaceResponseDTO {
+    @JsonProperty("BoardId")
+    private String boardId;
+
+    @JsonProperty("BoardIp")
+    private String boardIp;
+
+    @JsonProperty("Event")
+    private String event;
+
+    @JsonProperty("Result")
+    private Result result;
+
+    @JsonProperty("albumId")
+    private int albumId;
+
+    @JsonProperty("alignedImage")
+    private String alignedImage;
+
+    @JsonProperty("croppedImage")
+    private String croppedImage;
+
+    @JsonProperty("photoId")
+    private int photoId;
+
+    @JsonProperty("photoName")
+    private String photoName;
+
+    @Data
+    public static class Result {
+        @JsonProperty("Code")
+        private Integer code;
+
+        @JsonProperty("Desc")
+        private String desc;
+    }
+}

+ 27 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiDeviceMapper.java

@@ -0,0 +1,27 @@
+package com.usky.ai.mapper;
+
+import com.usky.ai.domain.AiDevice;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface AiDeviceMapper {
+
+    void insertDevice(AiDevice device);
+
+    void updateDevice(AiDevice device);
+
+    void saveOrUpdateDevice(AiDevice device);
+
+    void updateDeviceStatus();
+
+    List<AiDevice> getDevices(@Param("boardId") String boardId, @Param("offset") int offset, @Param("limit") int limit);
+
+    long getTotalDevices(@Param("boardId") String boardId);
+
+    void deleteDevice(@Param("boardId") String boardId);
+
+    boolean checkExists(@Param("boardId") String boardId, @Param("boardIp") String boardIp);
+}

+ 1 - 1
service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiQuestionMapper.java

@@ -1,6 +1,6 @@
 package com.usky.ai.mapper;
 
-import com.usky.ai.service.AiQuestion;
+import com.usky.ai.domain.AiQuestion;
 import org.apache.ibatis.annotations.*;
 
 import java.util.List;

+ 2 - 2
service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiSessionMapper.java

@@ -1,7 +1,7 @@
 package com.usky.ai.mapper;
 
-import com.usky.ai.service.AiQuestion;
-import com.usky.ai.service.AiSession;
+import com.usky.ai.domain.AiQuestion;
+import com.usky.ai.domain.AiSession;
 import org.apache.ibatis.annotations.*;
 
 import java.util.List;

+ 22 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiDeviceService.java

@@ -0,0 +1,22 @@
+package com.usky.ai.service;
+
+import com.usky.ai.domain.AiDevice;
+
+import java.util.List;
+
+public interface AiDeviceService {
+
+    void addDevice(AiDevice device);
+
+    void editDevice(AiDevice device);
+
+    void saveOrUpdateDevice(AiDevice device);
+
+    void updateDeviceStatus();
+
+    List<AiDevice> getDevices(String boardId, int pageNum, int pageSize);
+
+    long getTotalDevices(String boardId);
+
+    void deleteDevice(String boardId);
+}

+ 332 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/EdgeMediaService.java

@@ -0,0 +1,332 @@
+package com.usky.ai.service;
+
+
+import com.usky.ai.dto.*;
+import com.usky.ai.service.listener.BoardPingListener;
+import com.usky.ai.service.mqtt.MqttGateway;
+import com.usky.ai.service.util.JsonUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Base64Utils;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static com.usky.ai.service.listener.BoardPingListener.ReplyCache.*;
+
+/**
+ * 统一封装所有与盒子交互的 MQTT 请求
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class EdgeMediaService {
+
+    private final MqttGateway gateway;
+
+    private final RestTemplate restTemplate;
+
+    @Value("${api.base-url}")
+    private String baseUrl;
+
+    @Value("${file.path}")
+    private String filePath;
+
+    @Value("${file.domain}")
+    private String fileDomain;
+
+    @Value("${file.prefix}")
+    private String filePrefix;
+
+    public EdgeAppControllerReplyDTO addChannel(EdgeAppControllerRequestDTO req) {
+        req.setEvent("/alg_media_config");
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(REPLY, req.getBoardId() + "-" + req.getMediaName());
+    }
+
+    public EdgeAppFetchResponseDTO fetchChannels(String boardId) {
+        EdgeAppControllerRequestDTO req = new EdgeAppControllerRequestDTO();
+        req.setBoardId(boardId);
+        req.setEvent("/alg_media_fetch");
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        log.info("【发送】查询通道请求:{}", JsonUtils.toJson(req));
+        return waitCache(FETCH, boardId);
+    }
+
+    public EdgeAppDeleteResponseDTO deleteChannel(String boardId, String mediaName) {
+        EdgeAppControllerRequestDTO req = new EdgeAppControllerRequestDTO();
+        req.setBoardId(boardId);
+        req.setEvent("/alg_media_delete");
+        req.setMediaName(mediaName);
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(DELETE, boardId + "-" + mediaName);
+    }
+
+    public EdgeAlgTaskResponseDTO configTask(EdgeAlgTaskConfigRequestDTO req) {
+        req.setEvent("/alg_task_config");
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(TASK, req.getBoardId() + "-" + req.getAlgTaskSession());
+    }
+
+    public EdgeAlgTaskResponseDTO controlTask(String boardId, String algTaskSession, int command) {
+        EdgeAlgTaskCommandRequestDTO req = new EdgeAlgTaskCommandRequestDTO();
+        req.setBoardId(boardId);
+        req.setEvent("/alg_task_control");
+        req.setControlCommand(command);
+        req.setAlgTaskSession(algTaskSession);
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(TASK, boardId + "-" + algTaskSession);
+    }
+
+    public EdgeAlgTaskResponseDTO deleteTask(String boardId, String algTaskSession) {
+        EdgeAlgTaskCommandRequestDTO req = new EdgeAlgTaskCommandRequestDTO();
+        req.setBoardId(boardId);
+        req.setEvent("/alg_task_delete");
+        req.setAlgTaskSession(algTaskSession);
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(TASK, boardId + "-" + algTaskSession);
+    }
+
+    public EdgeAlgTaskListResponseDTO fetchTasks(String boardId) {
+        EdgeAlgTaskFetchRequestDTO req = new EdgeAlgTaskFetchRequestDTO();
+        req.setBoardId(boardId);
+        req.setEvent("/alg_task_fetch");
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(ALG_TASK_LIST, boardId);
+    }
+
+    public AlgAbilityFetchResponseDTO fetchAlgorithmAbility(String boardId) {
+        AlgAbilityFetchRequestDTO req = new AlgAbilityFetchRequestDTO();
+        req.setBoardId(boardId);
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.ALG_ABILITY, boardId + "-alg_ability_fetch");
+    }
+
+
+    public RepositoriesInfoResponseDTO listRepositories() {
+        String url = baseUrl + "/api_repositories_info";
+        ResponseEntity<RepositoriesInfoResponseDTO> response = restTemplate.getForEntity(url, RepositoriesInfoResponseDTO.class);
+        if (response.getStatusCode().is2xxSuccessful()) {
+            return response.getBody();
+        } else {
+            throw new RuntimeException("Failed to list repositories: " + response.getStatusCode());
+        }
+    }
+
+    public RepositoryCreateResponseDTO createRepository(RepositoryCreateRequestDTO request) {
+        String url = baseUrl + "/api_repository_create";
+        ResponseEntity<RepositoryCreateResponseDTO> response = restTemplate.postForEntity(url, request, RepositoryCreateResponseDTO.class);
+        if (response.getStatusCode().is2xxSuccessful()) {
+            return response.getBody();
+        } else {
+            throw new RuntimeException("Failed to create repository: " + response.getStatusCode());
+        }
+    }
+
+    public RepositoryUpdateResponseDTO updateRepository(RepositoryUpdateRequestDTO request) {
+        String url = baseUrl + "/api_repository_update";
+        ResponseEntity<RepositoryUpdateResponseDTO> response = restTemplate.postForEntity(url, request, RepositoryUpdateResponseDTO.class);
+        if (response.getStatusCode().is2xxSuccessful()) {
+            return response.getBody();
+        } else {
+            throw new RuntimeException("Failed to update repository: " + response.getStatusCode());
+        }
+    }
+
+    public RepositoryDeleteResponseDTO deleteRepository(RepositoryDeleteRequestDTO request) {
+        String url = baseUrl + "/api_repository_delete";
+        ResponseEntity<RepositoryDeleteResponseDTO> response = restTemplate.postForEntity(url, request, RepositoryDeleteResponseDTO.class);
+        if (response.getStatusCode().is2xxSuccessful()) {
+            return response.getBody();
+        } else {
+            throw new RuntimeException("Failed to delete repository: " + response.getStatusCode());
+        }
+    }
+
+    public RepositoryFacesResponseDTO getRepositoryFaces(RepositoryFacesRequestDTO request) {
+        String url = baseUrl + "/api_repository_faces";
+        ResponseEntity<RepositoryFacesResponseDTO> response = restTemplate.postForEntity(url, request, RepositoryFacesResponseDTO.class);
+        if (response.getStatusCode().is2xxSuccessful()) {
+            return response.getBody();
+        } else {
+            throw new RuntimeException("Failed to get repository faces: " + response.getStatusCode());
+        }
+    }
+
+    public RegisterFaceResponseDTO registerFace(RegisterFaceRequestDTO request) {
+        String url = baseUrl + "/api_register_face";
+        ResponseEntity<RegisterFaceResponseDTO> response = restTemplate.postForEntity(url, request, RegisterFaceResponseDTO.class);
+        if (response.getStatusCode().is2xxSuccessful()) {
+            return response.getBody();
+        } else {
+            throw new RuntimeException("Failed to register face: " + response.getStatusCode());
+        }
+    }
+
+    public UpdateFaceResponseDTO updateFace(UpdateFaceRequestDTO request) {
+        String url = baseUrl + "/api_face_update";
+        ResponseEntity<UpdateFaceResponseDTO> response = restTemplate.postForEntity(url, request, UpdateFaceResponseDTO.class);
+        if (response.getStatusCode().is2xxSuccessful()) {
+            return response.getBody();
+        } else {
+            throw new RuntimeException("Failed to update face: " + response.getStatusCode());
+        }
+    }
+
+    public DeleteFaceResponseDTO deleteFace(DeleteFaceRequestDTO request) {
+        String url = baseUrl + "/api_face_delete";
+        ResponseEntity<DeleteFaceResponseDTO> response = restTemplate.postForEntity(url, request, DeleteFaceResponseDTO.class);
+        if (response.getStatusCode().is2xxSuccessful()) {
+            return response.getBody();
+        } else {
+            throw new RuntimeException("Failed to delete face: " + response.getStatusCode());
+        }
+    }
+
+    public FaceRecognitionResponseDTO recognizeFace(FaceRecognitionRequestDTO request) {
+        String url = baseUrl + "/api/faceengine/face/1vn";
+        ResponseEntity<FaceRecognitionResponseDTO> response = restTemplate.postForEntity(url, request, FaceRecognitionResponseDTO.class);
+        if (response.getStatusCode().is2xxSuccessful()) {
+            return response.getBody();
+        } else {
+            throw new RuntimeException("Failed to recognize face: " + response.getStatusCode());
+        }
+    }
+
+    public AlgTaskSnapResponseDTO getTaskPreview(String boardId, String algTaskSession) {
+        AlgTaskSnapRequestDTO req = new AlgTaskSnapRequestDTO();
+        req.setBoardId(boardId);
+        req.setAlgTaskSession(algTaskSession);
+
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.TASK_PREVIEW, boardId + "-" + algTaskSession);
+    }
+
+    public AlgScheduleCreateResponseDTO createSchedule(AlgScheduleCreateRequestDTO req) {
+        req.setEvent("/alg_schedule_create");
+        req.convertTimePeriodsToBinary(); // 转换时间段为二进制数据
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.SCHEDULE_CREATE, req.getBoardId() + "-schedule-create");
+    }
+
+    public AlgScheduleFetchResponseDTO fetchSchedules(String boardId) {
+        AlgScheduleFetchRequestDTO req = new AlgScheduleFetchRequestDTO();
+        req.setBoardId(boardId);
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.SCHEDULE_FETCH, boardId + "-schedule-fetch");
+    }
+
+    public AlgScheduleDeleteResponseDTO deleteSchedule(String boardId, Long id) {
+        AlgScheduleDeleteRequestDTO req = new AlgScheduleDeleteRequestDTO();
+        req.setBoardId(boardId);
+        req.setId(id);
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.SCHEDULE_DELETE, boardId + "-schedule-delete-" + id);
+    }
+
+    public AlgSuitCreateResponseDTO createSuitGroup(AlgSuitCreateRequestDTO req) {
+        log.info("收到创建工装组请求:{}", req);
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.SUIT_CREATE, req.getBoardId() + "-suit-create");
+    }
+
+    public AlgSuitGroupRemoveResponseDTO removeSuitGroup(AlgSuitGroupRemoveRequestDTO req) {
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.SUIT_REMOVE, req.getBoardId() + "-suit-remove");
+    }
+
+    public AlgSuitAppendResponseDTO appendSuitTemplate(AlgSuitAppendRequestDTO req) {
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.SUIT_APPEND, req.getBoardId() + "-suit-append");
+    }
+
+    public AlgSuitRemoveResponseDTO removeSuitTemplate(AlgSuitRemoveRequestDTO req) {
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.SUIT_REMOVE_TEMPLATE, req.getBoardId() + "-suit-remove-template");
+    }
+
+    public AlgSuitFetchResponseDTO fetchSuitGroup(String boardId) {
+        AlgSuitFetchRequestDTO req = new AlgSuitFetchRequestDTO();
+        req.setBoardId(boardId);
+        gateway.sendToMqtt("/edge_app_controller", 2, JsonUtils.toJson(req));
+        return waitCache(BoardPingListener.ReplyCache.SUIT_FETCH, boardId + "-suit-fetch");
+    }
+
+    public String getTaskPreviewUrl(String boardId, String algTaskSession) {
+        AlgTaskSnapResponseDTO response = getTaskPreview(boardId, algTaskSession);
+        if (response.isSuccess() && response.getBase64() != null) {
+            try {
+                return saveImage(response.getBase64(), "taskPreview");
+            } catch (IOException e) {
+                log.error("Failed to save task preview image", e);
+                throw new RuntimeException("Failed to save task preview image", e);
+            }
+        } else {
+            throw new RuntimeException("Failed to get task preview: " + response.getResult().getDesc());
+        }
+    }
+
+    private String saveImage(String base64Image, String fileName) throws IOException {
+        // 检查 Base64 数据是否符合预期格式
+        byte[] imageBytes = Base64Utils.decodeFromString(base64Image);
+
+        // 生成文件路径 (使用yyyyMMddHHmmss格式)
+        LocalDateTime now = LocalDateTime.now();
+        String monthPath = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
+        String formattedTime = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+        int counterSuffix = new AtomicInteger(0).getAndIncrement(); // 获取并递增计数器
+        String filePathWithPrefix = filePath + "/" + monthPath + "/" + fileName + "_" + formattedTime + "_" + counterSuffix + ".jpg";
+        Path path = Paths.get(filePathWithPrefix);
+
+        // 保存图片到文件系统
+        Files.createDirectories(path.getParent());
+        Files.write(path, imageBytes);
+
+        // 返回可访问的 URL
+        return fileDomain + filePrefix + "/" + monthPath + "/" + fileName + "_" + formattedTime + "_" + counterSuffix + ".jpg";
+    }
+
+    private <T> T waitCache(Map<String, T> cache, String key) {
+        for (int i = 0; i < 50; i++) {
+            T resp = cache.get(key);
+            if (resp == null) {
+                try {
+                    TimeUnit.MILLISECONDS.sleep(100);
+                } catch (InterruptedException ignored) {
+                }
+                continue;
+            }
+
+            if (resp instanceof EdgeAppControllerReplyDTO) {
+                EdgeAppControllerReplyDTO reply = (EdgeAppControllerReplyDTO) resp;
+                if (!reply.isSuccess()) {
+                    cache.remove(key);
+                    throw new RuntimeException(reply.getResult().getDesc());
+                }
+            }
+
+            if (resp instanceof AlgAbilityFetchResponseDTO) {
+                AlgAbilityFetchResponseDTO reply = (AlgAbilityFetchResponseDTO) resp;
+                if (!reply.isSuccess()) {
+                    cache.remove(key);
+                    throw new RuntimeException(reply.getResult().getDesc());
+                }
+            }
+
+            cache.remove(key);
+            return resp;
+        }
+        throw new RuntimeException("等待盒子响应超时");
+    }
+}

+ 13 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/config/AppConfig.java

@@ -0,0 +1,13 @@
+package com.usky.ai.service.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class AppConfig {
+    @Bean
+    public RestTemplate restTemplate() {
+        return new RestTemplate();
+    }
+}

+ 48 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/enums/TopListener.java

@@ -0,0 +1,48 @@
+package com.usky.ai.service.enums;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum TopListener {
+
+    /**
+     * AI算法识别
+     */
+    // 主动心跳上报
+    BOARD_PING("boardPing", "/board_ping", 1),
+    //新增媒体通道
+    EDGE_APP_CONTROLLER("edgeAppController", "/edge_app_controller", 0),
+    //媒体通道响应结果
+    EDGE_APP_CONTROLLER_REPLY("edgeAppControllerReply", "/edge_app_controller_reply", 1);
+
+
+
+    private final String name;
+    private final String code;
+    private final Integer type;
+
+    TopListener(String name, String code, Integer type) {
+        this.name = name;
+        this.code = code;
+        this.type = type;
+    }
+
+    public static TopListener parse(String code) {
+        for (TopListener t : values()) {
+            if (t.code.equals(code)) return t;
+        }
+        return null;
+    }
+
+    public static List<TopListener> parse(Integer type) {
+        List<TopListener> list = new ArrayList<>();
+        for (TopListener t : values()) {
+            if (t.type.equals(type)) list.add(t);
+        }
+        return list;
+    }
+
+    public String getName() { return name; }
+    public String getCode() { return code; }
+    public Integer getType() { return type; }
+}

+ 60 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/impl/AiDeviceServiceImpl.java

@@ -0,0 +1,60 @@
+package com.usky.ai.service.impl;
+
+import com.usky.ai.domain.AiDevice;
+import com.usky.ai.mapper.AiDeviceMapper;
+import com.usky.ai.service.AiDeviceService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class AiDeviceServiceImpl implements AiDeviceService {
+
+    private final AiDeviceMapper aiDeviceMapper;
+
+    @Autowired
+    public AiDeviceServiceImpl(AiDeviceMapper aiDeviceMapper) {
+        this.aiDeviceMapper = aiDeviceMapper;
+    }
+
+    @Override
+    public void addDevice(AiDevice device) {
+        if (aiDeviceMapper.checkExists(device.getBoardId(), device.getBoardIp())) {
+            throw new IllegalArgumentException("设备已存在, 设备Id 或 设备Ip 已存在");
+        }
+        aiDeviceMapper.insertDevice(device);
+    }
+
+    @Override
+    public void editDevice(AiDevice device) {
+        aiDeviceMapper.updateDevice(device);
+    }
+
+    @Override
+    public void saveOrUpdateDevice(AiDevice device) {
+        aiDeviceMapper.saveOrUpdateDevice(device);
+    }
+
+    @Override
+    public void updateDeviceStatus() {
+        aiDeviceMapper.updateDeviceStatus();
+    }
+
+    @Override
+    public List<AiDevice> getDevices(String boardId, int pageNum, int pageSize) {
+        updateDeviceStatus(); // 在查询之前更新设备的 status 状态
+        int offset = (pageNum - 1) * pageSize;
+        return aiDeviceMapper.getDevices(boardId, offset, pageSize);
+    }
+
+    @Override
+    public long getTotalDevices(String boardId) {
+        return aiDeviceMapper.getTotalDevices(boardId);
+    }
+
+    @Override
+    public void deleteDevice(String boardId) {
+        aiDeviceMapper.deleteDevice(boardId);
+    }
+}

+ 299 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/listener/BoardPingListener.java

@@ -0,0 +1,299 @@
+package com.usky.ai.service.listener;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.usky.ai.controller.web.BoardPingController;
+import com.usky.ai.domain.AiDevice;
+import com.usky.ai.dto.*;
+import com.usky.ai.mapper.AiDeviceMapper;
+import com.usky.ai.service.mqtt.MqttInConfig;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.messaging.Message;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Slf4j
+@Component
+@ConditionalOnProperty(prefix = "mqtt", value = "enabled", havingValue = "true")
+public class BoardPingListener {
+
+    private static final Map<String, Long> LAST_PING = new ConcurrentHashMap<>();
+    private static final long TIMEOUT = 6_000;
+
+    private final AiDeviceMapper aiDeviceMapper;
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    @Autowired
+    public BoardPingListener(AiDeviceMapper aiDeviceMapper) {
+        this.aiDeviceMapper = aiDeviceMapper;
+    }
+
+    public static class ReplyCache {
+        public static final Map<String, EdgeAppControllerReplyDTO> REPLY = new ConcurrentHashMap<>();
+        public static final Map<String, EdgeAppFetchResponseDTO> FETCH = new ConcurrentHashMap<>();
+        public static final Map<String, EdgeAppDeleteResponseDTO> DELETE = new ConcurrentHashMap<>();
+        public static final Map<String, EdgeAlgTaskResponseDTO> TASK = new ConcurrentHashMap<>();
+        public static final Map<String, EdgeAlgTaskListResponseDTO> ALG_TASK_LIST = new ConcurrentHashMap<>();
+        public static final Map<String, AlgAbilityFetchResponseDTO> ALG_ABILITY = new ConcurrentHashMap<>();
+        public static final Map<String, RepositoriesInfoResponseDTO> REPOSITORIES_INFO = new ConcurrentHashMap<>();
+        public static final Map<String, RepositoryCreateResponseDTO> REPOSITORY_CREATE = new ConcurrentHashMap<>();
+        public static final Map<String, RepositoryUpdateResponseDTO> REPOSITORY_UPDATE = new ConcurrentHashMap<>();
+        public static final Map<String, RepositoryDeleteResponseDTO> REPOSITORY_DELETE = new ConcurrentHashMap<>();
+        public static final Map<String, AlgTaskSnapResponseDTO> TASK_PREVIEW = new ConcurrentHashMap<>();
+        public static final Map<String, AlgScheduleCreateResponseDTO> SCHEDULE_CREATE = new ConcurrentHashMap<>();
+        public static final Map<String, AlgScheduleFetchResponseDTO> SCHEDULE_FETCH = new ConcurrentHashMap<>();
+        public static final Map<String, AlgScheduleDeleteResponseDTO> SCHEDULE_DELETE = new ConcurrentHashMap<>();
+        public static final Map<String, AlgSuitCreateResponseDTO> SUIT_CREATE = new ConcurrentHashMap<>();
+        public static final Map<String, AlgSuitGroupRemoveResponseDTO> SUIT_REMOVE = new ConcurrentHashMap<>();
+        public static final Map<String, AlgSuitAppendResponseDTO> SUIT_APPEND = new ConcurrentHashMap<>();
+        public static final Map<String, AlgSuitRemoveResponseDTO> SUIT_REMOVE_TEMPLATE = new ConcurrentHashMap<>();
+        public static final Map<String, AlgSuitFetchResponseDTO> SUIT_FETCH = new ConcurrentHashMap<>();
+    }
+
+    @ServiceActivator(inputChannel = MqttInConfig.CHANNEL_NAME_INPUT)
+    public void handleHeartbeat(Message<?> message) {
+        String topic = (String) message.getHeaders().get("mqtt_receivedTopic");
+        if (!"/board_ping".equals(topic)) return;
+
+        String payload = (String) message.getPayload();
+        try {
+            if (payload == null) {
+                log.error("接收到的 payload 为空");
+                return;
+            }
+
+            BoardPingDTO dto = objectMapper.readValue(payload, BoardPingDTO.class);
+            if (dto == null) {
+                log.error("解析心跳消息失败,结果为 null");
+                return;
+            }
+
+            BoardPingController.CACHE.put(dto.getBoardId(), dto);
+            log.info("收到心跳:{}", dto.getBoardId());
+
+            // 将 BoardPingDTO 转换为 AiDevice
+            AiDevice aiDevice = new AiDevice();
+            aiDevice.setBoardId(dto.getBoardId());
+            aiDevice.setBoardIp(dto.getBoardIp());
+            aiDevice.setDescription(null);
+            aiDevice.setStatus(dto.getKey().equals("运行中") ? 1 : 0);
+            aiDevice.setCreateTime(new java.sql.Timestamp(System.currentTimeMillis()));
+            aiDevice.setCreateBy("system");
+            aiDevice.setUpdateTime(new java.sql.Timestamp(System.currentTimeMillis()));
+            aiDevice.setUpdateBy("system");
+
+            aiDeviceMapper.saveOrUpdateDevice(aiDevice);
+        } catch (Exception e) {
+            log.error("解析心跳失败", e);
+        }
+    }
+
+    @ServiceActivator(inputChannel = MqttInConfig.CHANNEL_NAME_INPUT)
+    public void handleAllReplies(Message<?> message) {
+        String topic = (String) message.getHeaders().get("mqtt_receivedTopic");
+        if (!"/edge_app_controller_reply".equals(topic)) return;
+
+        String payload = (String) message.getPayload();
+        log.info("收到 /edge_app_controller_reply 原始消息: {}", payload);
+        try {
+            if (payload == null) {
+                log.error("接收到的 payload 为空");
+                return;
+            }
+
+            JsonNode root = objectMapper.readTree(payload);
+            if (root == null) {
+                log.error("解析 payload 为空");
+                return;
+            }
+
+            JsonNode eventNode = root.get("Event");
+            if (eventNode == null) {
+                log.error("JSON 中缺少 'Event' 字段");
+                return;
+            }
+
+            String event = eventNode.asText();
+            if (event == null || event.isEmpty()) {
+                log.error("Event 字段为空");
+                return;
+            }
+
+            switch (event) {
+                case "/alg_media_config":
+                    EdgeAppControllerReplyDTO configResp = objectMapper.readValue(payload, EdgeAppControllerReplyDTO.class);
+                    String configKey = configResp.getBoardId() + "-" + configResp.getMediaName();
+                    ReplyCache.REPLY.put(configKey, configResp);
+                    log.info("收到通道配置响应:{}", configKey);
+                    break;
+
+                case "/alg_media_fetch":
+                    EdgeAppFetchResponseDTO fetchResp = objectMapper.readValue(payload, EdgeAppFetchResponseDTO.class);
+                    ReplyCache.FETCH.put(fetchResp.getBoardId(), fetchResp);
+                    log.info("收到通道查询响应:{}", fetchResp.getBoardId());
+                    break;
+
+                case "/alg_media_delete":
+                    EdgeAppDeleteResponseDTO deleteResp = objectMapper.readValue(payload, EdgeAppDeleteResponseDTO.class);
+                    String deleteKey = deleteResp.getBoardId() + "-" + deleteResp.getMediaName();
+                    ReplyCache.DELETE.put(deleteKey, deleteResp);
+                    log.info("收到通道删除响应:{}", deleteKey);
+                    break;
+
+                case "/alg_task_config":
+                case "/alg_task_control":
+                case "/alg_task_delete":
+                    EdgeAlgTaskResponseDTO taskResp = objectMapper.readValue(payload, EdgeAlgTaskResponseDTO.class);
+                    String taskKey = taskResp.getBoardId() + "-" + taskResp.getAlgTaskSession();
+                    ReplyCache.TASK.put(taskKey, taskResp);
+                    log.info("收到任务响应:{}-{}", taskResp.getBoardId(), taskResp.getAlgTaskSession());
+                    break;
+
+                case "/alg_task_fetch":
+                    EdgeAlgTaskListResponseDTO taskListResp = objectMapper.readValue(payload, EdgeAlgTaskListResponseDTO.class);
+                    ReplyCache.ALG_TASK_LIST.put(taskListResp.getBoardId(), taskListResp);
+                    log.info("✅ 收到任务列表响应:{},内容数量:{}", taskListResp.getBoardId(), taskListResp.getContent().size());
+                    break;
+
+                case "/alg_ability_fetch":
+                    AlgAbilityFetchResponseDTO abilityResp = objectMapper.readValue(payload, AlgAbilityFetchResponseDTO.class);
+                    String abilityKey = abilityResp.getBoardId() + "-alg_ability_fetch";
+                    ReplyCache.ALG_ABILITY.put(abilityKey, abilityResp);
+                    log.info("收到算法能力获取响应:{}", abilityKey);
+                    break;
+
+                case "/api_repositories_info":
+                    RepositoriesInfoResponseDTO repositoriesInfoResp = objectMapper.readValue(payload, RepositoriesInfoResponseDTO.class);
+                    ReplyCache.REPOSITORIES_INFO.put("repositories_info", repositoriesInfoResp);
+                    log.info("收到人脸库列表响应");
+                    break;
+
+                case "/api_repository_create":
+                    RepositoryCreateResponseDTO createResp = objectMapper.readValue(payload, RepositoryCreateResponseDTO.class);
+                    String createKey = "repository_create-" + createResp.getAlbumName();
+                    ReplyCache.REPOSITORY_CREATE.put(createKey, createResp);
+                    log.info("收到人脸库创建响应:{}", createKey);
+                    break;
+
+                case "/api_repository_update":
+                    RepositoryUpdateResponseDTO updateResp = objectMapper.readValue(payload, RepositoryUpdateResponseDTO.class);
+                    String updateKey = "repository_update-" + updateResp.getAlbumName();
+                    ReplyCache.REPOSITORY_UPDATE.put(updateKey, updateResp);
+                    log.info("收到人脸库更新响应:{}", updateKey);
+                    break;
+
+                case "/api_repository_delete":
+                    RepositoryDeleteResponseDTO repoDeleteResp = objectMapper.readValue(payload, RepositoryDeleteResponseDTO.class);
+                    String repoDeleteKey = "repository_delete-" + repoDeleteResp.getAlbumName();
+                    ReplyCache.REPOSITORY_DELETE.put(repoDeleteKey, repoDeleteResp);
+                    log.info("收到人脸库删除响应:{}", repoDeleteKey);
+                    break;
+
+                case "/alg_task_snap":
+                    AlgTaskSnapResponseDTO snapResp = objectMapper.readValue(payload, AlgTaskSnapResponseDTO.class);
+                    String snapKey = snapResp.getBoardId() + "-" + snapResp.getAlgTaskSession();
+                    ReplyCache.TASK_PREVIEW.put(snapKey, snapResp);
+                    log.info("收到任务预览图响应:{}", snapKey);
+                    break;
+
+                case "/alg_schedule_create":
+                    AlgScheduleCreateResponseDTO scheduleResp = objectMapper.readValue(payload, AlgScheduleCreateResponseDTO.class);
+                    String scheduleKey = scheduleResp.getBoardId() + "-schedule-create";
+                    ReplyCache.SCHEDULE_CREATE.put(scheduleKey, scheduleResp);
+                    log.info("收到计划模板创建响应:{}", scheduleKey);
+                    break;
+
+                case "/alg_schedule_fetch":
+                    AlgScheduleFetchResponseDTO scfetchResp = objectMapper.readValue(payload, AlgScheduleFetchResponseDTO.class);
+                    String scfetchKey = scfetchResp.getBoardId() + "-schedule-fetch";
+                    ReplyCache.SCHEDULE_FETCH.put(scfetchKey, scfetchResp);
+                    log.info("收到计划模板查询响应:{}", scfetchKey);
+                    break;
+
+                case "/alg_schedule_delete":
+                    AlgScheduleDeleteResponseDTO scheduleDeleteResp = objectMapper.readValue(payload, AlgScheduleDeleteResponseDTO.class);
+                    String scheduleDeleteKey = scheduleDeleteResp.getBoardId() + "-schedule-delete-" + scheduleDeleteResp.getScheduleId();
+                    ReplyCache.SCHEDULE_DELETE.put(scheduleDeleteKey, scheduleDeleteResp);
+                    log.info("收到计划模板删除响应:{}", scheduleDeleteKey);
+                    break;
+
+                case "/alg_suit_create":
+                    AlgSuitCreateResponseDTO suitCreateResp = objectMapper.readValue(payload, AlgSuitCreateResponseDTO.class);
+                    String suitCreateKey = suitCreateResp.getBoardId() + "-suit-create";
+                    ReplyCache.SUIT_CREATE.put(suitCreateKey, suitCreateResp);
+                    log.info("收到创建工装组响应:{}", suitCreateKey);
+                    break;
+
+                case "/alg_suit_group_remove":
+                    AlgSuitGroupRemoveResponseDTO suitRemoveResp = objectMapper.readValue(payload, AlgSuitGroupRemoveResponseDTO.class);
+                    String suitRemoveKey = suitRemoveResp.getBoardId() + "-suit-remove";
+                    ReplyCache.SUIT_REMOVE.put(suitRemoveKey, suitRemoveResp);
+                    log.info("收到删除工装组响应:{}", suitRemoveKey);
+                    break;
+
+                case "/alg_suit_append":
+                    AlgSuitAppendResponseDTO suitAppendResp = objectMapper.readValue(payload, AlgSuitAppendResponseDTO.class);
+                    String suitAppendKey = suitAppendResp.getBoardId() + "-suit-append";
+                    ReplyCache.SUIT_APPEND.put(suitAppendKey, suitAppendResp);
+                    log.info("收到上传工装模板响应:{}", suitAppendKey);
+                    break;
+
+                case "/alg_suit_remove":
+                    AlgSuitRemoveResponseDTO suitRemoveTemplateResp = objectMapper.readValue(payload, AlgSuitRemoveResponseDTO.class);
+                    String suitRemoveTemplateKey = suitRemoveTemplateResp.getBoardId() + "-suit-remove-template";
+                    ReplyCache.SUIT_REMOVE_TEMPLATE.put(suitRemoveTemplateKey, suitRemoveTemplateResp);
+                    log.info("收到删除工装模板响应:{}", suitRemoveTemplateKey);
+                    break;
+
+                case "/alg_suit_fetch":
+                    AlgSuitFetchResponseDTO suitFetchResp = objectMapper.readValue(payload, AlgSuitFetchResponseDTO.class);
+                    String suitFetchKey = suitFetchResp.getBoardId() + "-suit-fetch";
+                    ReplyCache.SUIT_FETCH.put(suitFetchKey, suitFetchResp);
+                    log.info("收到工装组信息响应:{}", suitFetchKey);
+                    break;
+
+                default:
+                    log.warn("未知事件类型:{}", event);
+            }
+        } catch (Exception e) {
+            log.error("解析 /edge_app_controller_reply 失败,原始消息: {}", payload, e);
+        }
+    }
+
+    @Scheduled(fixedDelay = 300_000) // 每 5 分钟
+    public void cleanExpiredCache() {
+        long now = System.currentTimeMillis();
+        long timeout = 300_000;
+
+        ReplyCache.REPLY.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.FETCH.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.DELETE.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.TASK.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.ALG_TASK_LIST.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.ALG_ABILITY.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.REPOSITORIES_INFO.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.REPOSITORY_CREATE.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.REPOSITORY_UPDATE.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.REPOSITORY_DELETE.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.TASK_PREVIEW.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.SCHEDULE_CREATE.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.SCHEDULE_FETCH.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.SCHEDULE_DELETE.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.SUIT_CREATE.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.SUIT_REMOVE.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.SUIT_APPEND.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.SUIT_REMOVE_TEMPLATE.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+        ReplyCache.SUIT_FETCH.entrySet().removeIf(e -> now - e.getValue().getTime() > timeout);
+
+        log.info("清理过期缓存:REPLY={} FETCH={} DELETE={} TASK={} TASK_FETCH={}",
+                ReplyCache.REPLY.size(), ReplyCache.FETCH.size(), ReplyCache.DELETE.size(),
+                ReplyCache.TASK.size(), ReplyCache.ALG_TASK_LIST.size());
+    }
+}

+ 50 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/mqtt/MqttBaseConfig.java

@@ -0,0 +1,50 @@
+package com.usky.ai.service.mqtt;
+
+import lombok.Data;
+import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
+import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
+import org.springframework.stereotype.Component;
+
+@ConditionalOnProperty(prefix = "mqtt", value = {"enabled"}, havingValue = "true")
+@Data
+@Component
+@ConfigurationProperties(prefix = "mqtt")
+public class MqttBaseConfig {
+
+    @Value("${mqtt.username}")
+    private String username;
+
+    @Value("${mqtt.password}")
+    private String password;
+
+    @Value("${mqtt.url}")
+    private String hostUrl;
+
+    @Value("${mqtt.sub-topics}")
+    private String msgTopic;
+
+    @Value("${mqtt.keep-alive-interval}")
+    //心跳间隔
+    private int keepAliveInterval;
+    @Value("${mqtt.completionTimeout}")
+    //心跳间隔
+    private int completionTimeout;
+
+
+    @Bean
+    public MqttPahoClientFactory mqttClientFactory() {
+        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
+        MqttConnectOptions options = new MqttConnectOptions();
+        options.setServerURIs(new String[]{this.getHostUrl()});
+        options.setUserName(this.getUsername());
+        options.setPassword(this.getPassword().toCharArray());
+        factory.setConnectionOptions(options);
+        return factory;
+    }
+
+}

+ 17 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/mqtt/MqttGateway.java

@@ -0,0 +1,17 @@
+package com.usky.ai.service.mqtt;
+
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.mqtt.support.MqttHeaders;
+import org.springframework.messaging.handler.annotation.Header;
+
+@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
+public interface MqttGateway {
+
+    void sendToMqtt(String payload);
+
+    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload);
+
+    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic,
+                    @Header(MqttHeaders.QOS) int qos,
+                    String payload);
+}

+ 56 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/mqtt/MqttInConfig.java

@@ -0,0 +1,56 @@
+package com.usky.ai.service.mqtt;
+
+import com.usky.ai.service.enums.TopListener;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.endpoint.MessageProducerSupport;
+import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
+import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
+import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
+import org.springframework.messaging.MessageChannel;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Configuration
+public class MqttInConfig {
+
+    public static final String CHANNEL_NAME_INPUT = "mqttInboundChannel";
+    public static final String CHANNEL_BOARD_PING = "boardPingChannel";
+
+    @Bean
+    public MessageProducerSupport mqttInbound(MqttPahoClientFactory factory) {
+        // 获取所有需要订阅的主题
+        List<String> topics = TopListener.parse(1).stream()
+                .map(TopListener::getCode)
+                .collect(Collectors.toList()); // 使用 collect(Collectors.toList())
+
+        // 创建适配器实例,订阅所有主题
+        MqttPahoMessageDrivenChannelAdapter adapter =
+                new MqttPahoMessageDrivenChannelAdapter(
+                        "server-in", factory, topics.toArray(new String[0]));
+
+        // 设置消息转换器
+        adapter.setConverter(new DefaultPahoMessageConverter());
+        // 设置 QoS
+        adapter.setQos(1);
+        // 设置完成超时时间
+        adapter.setCompletionTimeout(5000);
+
+        // 设置输出通道
+        adapter.setOutputChannel(mqttInboundChannel());
+
+        return adapter;
+    }
+
+    @Bean(name = CHANNEL_NAME_INPUT)
+    public MessageChannel mqttInboundChannel() {
+        return new DirectChannel();
+    }
+
+    @Bean(name = CHANNEL_BOARD_PING)
+    public MessageChannel boardPingChannel() {
+        return new DirectChannel();
+    }
+}

+ 41 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/mqtt/MqttOutConfig.java

@@ -0,0 +1,41 @@
+package com.usky.ai.service.mqtt;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.annotation.IntegrationComponentScan;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+
+@Configuration
+@IntegrationComponentScan
+@ConditionalOnProperty(prefix = "mqtt", value = {"enabled"}, havingValue = "true")
+public class MqttOutConfig {
+
+    @Autowired
+    public MqttBaseConfig mqttBaseConfig;
+
+    public static final String CHANNEL_NAME_OUT = "mqttOutboundChannel";
+    public static final String MESSAGE_NAME = "messageOut";
+    public static final String DEFAULT_TOPIC = "testTopic";
+
+    @Bean(name = CHANNEL_NAME_OUT)
+    public MessageChannel mqttOutboundChannel() {
+        return new DirectChannel();
+    }
+
+    @Bean(name = MESSAGE_NAME)
+    @ServiceActivator(inputChannel = CHANNEL_NAME_OUT)
+    public MessageHandler outbound() {
+        String clientId = "h-backend-mqtt-out-" + System.currentTimeMillis();
+        MqttPahoMessageHandler messageHandler =
+                new MqttPahoMessageHandler(clientId, mqttBaseConfig.mqttClientFactory());
+        messageHandler.setAsync(true);
+        messageHandler.setDefaultTopic(DEFAULT_TOPIC);
+        return messageHandler;
+    }
+}

+ 34 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/util/GlobalExceptionHandler.java

@@ -0,0 +1,34 @@
+package com.usky.ai.service.util;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+    @ExceptionHandler(RuntimeException.class)
+    public ResponseEntity<Map<String, Object>> handleRuntimeException(RuntimeException ex) {
+        Map<String, Object> response = new HashMap<>();
+        response.put("timestamp", LocalDateTime.now());
+        response.put("message", ex.getMessage());
+        response.put("status", HttpStatus.NOT_FOUND.value());
+
+        return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
+    }
+
+    @ExceptionHandler(Exception.class)
+    public ResponseEntity<Map<String, Object>> handleGenericException(Exception ex) {
+        Map<String, Object> response = new HashMap<>();
+        response.put("timestamp", LocalDateTime.now());
+        response.put("message", "An error occurred: " + ex.getMessage());
+        response.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
+
+        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+}

+ 15 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/util/JsonUtils.java

@@ -0,0 +1,15 @@
+package com.usky.ai.service.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JsonUtils {
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+    public static String toJson(Object obj) {
+        try {
+            return MAPPER.writeValueAsString(obj);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 62 - 0
service-ai/service-ai-biz/src/main/resources/mapper/AiDeviceMapper.xml

@@ -0,0 +1,62 @@
+<?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.usky.ai.mapper.AiDeviceMapper">
+
+    <insert id="insertDevice" useGeneratedKeys="false">
+        INSERT INTO ai_device (board_id, board_ip, description, status, create_time, create_by, update_time, update_by)
+        VALUES (#{boardId}, #{boardIp}, #{description}, #{status}, #{createTime}, #{createBy}, #{updateTime}, #{updateBy})
+    </insert>
+
+    <insert id="saveOrUpdateDevice" useGeneratedKeys="false">
+        INSERT INTO ai_device (board_id, board_ip, description, status, create_time, create_by, update_time, update_by)
+        VALUES (#{boardId}, #{boardIp}, #{description}, #{status}, #{createTime}, #{createBy}, #{updateTime}, #{updateBy})
+            ON DUPLICATE KEY UPDATE
+                                 board_ip = VALUES(board_ip),
+                                 description = VALUES(description),
+                                 status = VALUES(status),
+                                 update_time = VALUES(update_time),
+                                 update_by = VALUES(update_by)
+    </insert>
+
+    <update id="updateDevice">
+        UPDATE ai_device
+        SET board_ip = #{boardIp},
+            description = #{description},
+            status = #{status},
+            update_time = #{updateTime},
+            update_by = #{updateBy}
+        WHERE board_id = #{boardId}
+    </update>
+
+    <update id="updateDeviceStatus">
+        UPDATE ai_device SET status = 0 WHERE update_time &lt;= NOW() - INTERVAL 10 MINUTE
+    </update>
+
+    <select id="getDevices" resultType="com.usky.ai.domain.AiDevice">
+        SELECT * FROM ai_device
+        <where>
+            <if test="boardId != null and boardId != ''">
+                AND board_id = #{boardId}
+            </if>
+        </where>
+        LIMIT #{limit} OFFSET #{offset}
+    </select>
+
+    <select id="getTotalDevices" resultType="long">
+        SELECT COUNT(*) FROM ai_device
+        <where>
+            <if test="boardId != null and boardId != ''">
+                AND board_id = #{boardId}
+            </if>
+        </where>
+    </select>
+
+    <delete id="deleteDevice">
+        DELETE FROM ai_device WHERE board_id = #{boardId}
+    </delete>
+
+    <select id="checkExists" resultType="boolean">
+        SELECT COUNT(*) FROM ai_device
+        WHERE board_id = #{boardId} OR board_ip = #{boardIp}
+    </select>
+</mapper>

+ 3 - 2
service-ai/service-ai-biz/src/main/resources/static/tyqw.html

@@ -92,7 +92,7 @@
 
         const requestBody = JSON.stringify({content: content});
 
-        const token = "eyJhbGciOiJIUzUxMiJ9.eyIiOjEwMDMsInVzZXJfaWQiOjIxMywidXNlcl9rZXkiOiJlYzUxODMzNjdmYTk0ODgwOGQwZjEwODEyOWVmNjgwOSIsInVzZXJuYW1lIjoi6LW16YeR6ZuoIn0.zWulXcesI1TRcDmiAHuQ9P2WHDE2l7mDmuunx13TmVl6E5Yvs8nZvu1ddtINdw0lrnnR3Q5lZaRH3mJJTaDhig";
+        const token = "eyJhbGciOiJIUzUxMiJ9.eyIiOjEwMDMsIm9zSW5mbyI6IldpbmRvd3MgMTAiLCJ1c2VyX2lkIjoxLCJ1c2VyX2tleSI6IjRlYTE2MTQ0NWRlMTQ5MzBiYTQ3N2ExN2M3YzVhNmEzIiwiYnJvd3NlckluZm8iOiJDaHJvbWUgMTQiLCJ1c2VybmFtZSI6ImFkbWluIn0.FGLbxHpe0nbIanCy5sCdnMCiAc2SNad5TrneVZsnmPyjz4CMF9Rix39U3IJm7C30gaDFYlCwER2Pqa02NqIXbw";
 
         fetch('/ai/aliTyqw', {
             method: 'POST',
@@ -147,4 +147,5 @@
     })
 </script>
 </body>
-</html>-->
+</html>
+-->