Browse Source

Merge branch 'han' of uskycloud/usky-modules into master

hanzhengyi 2 weeks ago
parent
commit
8da7539662
100 changed files with 5299 additions and 22 deletions
  1. 28 19
      pom.xml
  2. 1 1
      service-cdi/service-cdi-biz/src/main/java/com/usky/cdi/MybatisGeneratorUtils.java
  3. 1 0
      service-cdi/service-cdi-biz/src/main/java/com/usky/cdi/controller/AlarmDataController.java
  4. 2 2
      service-cdi/service-cdi-biz/src/main/java/com/usky/cdi/service/impl/BaseDataTransferService.java
  5. 69 0
      service-cdi/service-cdi-biz/src/main/java/com/usky/cdi/service/vo/alarm/AlarmMessage1VO.java
  6. 61 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsAnalysisController.java
  7. 233 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsApiV1Controller.java
  8. 37 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsAuthController.java
  9. 106 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsReportController.java
  10. 39 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsChannel.java
  11. 69 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsDevice.java
  12. 50 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsDeviceFunction.java
  13. 44 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsEnergyItemCode.java
  14. 64 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsGateway.java
  15. 71 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsProject.java
  16. 45 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsSpace.java
  17. 46 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsSpaceArea.java
  18. 67 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsSpaceBuilding.java
  19. 45 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsSpaceFloor.java
  20. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsChannelMapper.java
  21. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsDeviceFunctionMapper.java
  22. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsDeviceMapper.java
  23. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsEnergyItemCodeMapper.java
  24. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsGatewayMapper.java
  25. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsProjectMapper.java
  26. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsSpaceAreaMapper.java
  27. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsSpaceBuildingMapper.java
  28. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsSpaceFloorMapper.java
  29. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsSpaceMapper.java
  30. 19 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsAnalysisService.java
  31. 14 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsAuthService.java
  32. 22 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsGatewayQueryService.java
  33. 72 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsModelService.java
  34. 20 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsOverviewService.java
  35. 32 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsReportService.java
  36. 61 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsAnalysisServiceImpl.java
  37. 30 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsAuthServiceImpl.java
  38. 87 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsGatewayQueryServiceImpl.java
  39. 401 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsModelServiceImpl.java
  40. 53 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsOverviewServiceImpl.java
  41. 164 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsReportServiceImpl.java
  42. 14 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCategoryRatioItemVO.java
  43. 17 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCollectionRealtimeItemVO.java
  44. 16 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCollectionRealtimeRequest.java
  45. 12 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCollectionRealtimeResponse.java
  46. 15 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCompareRequest.java
  47. 14 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCompareResponse.java
  48. 14 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCompareSeriesItemVO.java
  49. 12 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCompareValueVO.java
  50. 18 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsEnergyStatisticsItemVO.java
  51. 14 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsEnergyStatisticsRequest.java
  52. 14 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsEnergyStatisticsResponse.java
  53. 16 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsEnergyTypeVO.java
  54. 24 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsGatewayDetailResponse.java
  55. 22 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsGatewayListItem.java
  56. 21 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsGatewayPageRequest.java
  57. 16 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsIdResponse.java
  58. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsLoginRequest.java
  59. 10 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsLoginResponse.java
  60. 35 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsModelSaveRequest.java
  61. 23 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsProjectResponse.java
  62. 18 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsRegionAnalysisItemVO.java
  63. 12 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsRegionAnalysisResponse.java
  64. 12 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsReportAttributeVO.java
  65. 17 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsReportDeviceItemVO.java
  66. 12 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsReportDevicesResponse.java
  67. 18 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsStructureTreeNode.java
  68. 11 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsSummaryRequest.java
  69. 20 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsSummaryResponse.java
  70. 14 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsTrendCategoryResponse.java
  71. 18 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsTrendIndicatorsResponse.java
  72. 14 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsTrendItemVO.java
  73. 14 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsTrendResponse.java
  74. 21 0
      service-sas/pom.xml
  75. 28 0
      service-sas/service-sas-api/pom.xml
  76. 139 0
      service-sas/service-sas-biz/pom.xml
  77. 108 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/MybatisGeneratorUtils.java
  78. 47 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/ServiceSasApplication.java
  79. 16 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/StandardOnvifService.java
  80. 126 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/UnityVideoInfo.java
  81. 18 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/config/WebSocketConfig.java
  82. 71 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/DahuaNvrInfo.java
  83. 126 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/DahuaVideoInfo.java
  84. 185 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/DahuaVideoStreamService.java
  85. 10 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/EM_SEND_SEARCH_TYPE.java
  86. 58 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/FPlayDataCallBackEx.java
  87. 62 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/FRealDataCallBackEx.java
  88. 254 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/InitNetSDKLib.java
  89. 13 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/NET_BRIDGE_NET_CARDS_MAC_LIST.java
  90. 24 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/NET_IN_STARTSERACH_DEVICE.java
  91. 16 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/NET_OUT_STARTSERACH_DEVICE.java
  92. 575 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/NetSDKLib.java
  93. 99 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/RemoteDeviceInfo.java
  94. 9 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/entity/DahuaNvrInfo.java
  95. 181 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/entity/DeviceVideoInfo.java
  96. 327 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/entity/SearchDeviceInfo.java
  97. 39 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/enums/BusinessEnum.java
  98. 22 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/enums/DeviceTypeEnum.java
  99. 23 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/enums/ProtocolEnum.java
  100. 32 0
      service-sas/service-sas-biz/src/main/java/com/usky/sas/common/enums/SystemTypeCodeEnum.java

+ 28 - 19
pom.xml

@@ -90,25 +90,34 @@
 
         <!--    <module>service-data</module>-->
 
-    </modules>
-
-
-    <dependencies>
-
-
-        <dependency>
-
-
-            <groupId>org.projectlombok</groupId>
-
-
-            <artifactId>lombok</artifactId>
-
-
-        </dependency>
-
-
-    </dependencies>
+        <module>service-sas</module>
+
+  </modules>
+          
+  
+  
+  <dependencies>
+                    
+    
+    
+    <dependency>
+                              
+      
+      
+      <groupId>org.projectlombok</groupId>
+                              
+      
+      
+      <artifactId>lombok</artifactId>
+                          
+    
+    
+    </dependency>
+                
+  
+  
+  </dependencies>
+    
 
 
 </project>

+ 1 - 1
service-cdi/service-cdi-biz/src/main/java/com/usky/cdi/MybatisGeneratorUtils.java

@@ -71,7 +71,7 @@ public class MybatisGeneratorUtils {
         // strategy.setTablePrefix("t_"); // 表名前缀
         strategy.setEntityLombokModel(true); //使用lombokbase_build_plane
         //修改自己想要生成的表
-        strategy.setInclude("base_build");  // 逆向工程使用的表   如果要生成多个,这里可以传入String[]
+        strategy.setInclude("dmp_device");  // 逆向工程使用的表   如果要生成多个,这里可以传入String[]
         mpg.setStrategy(strategy);
 
         // 关闭默认 xml 生成,调整生成 至 根目录

+ 1 - 0
service-cdi/service-cdi-biz/src/main/java/com/usky/cdi/controller/AlarmDataController.java

@@ -2,6 +2,7 @@ package com.usky.cdi.controller;
 
 import com.usky.cdi.service.impl.AlarmDataTransferService;
 import com.usky.cdi.service.vo.alarm.AlarmMessageVO;
+import com.usky.cdi.service.vo.alarm.AlarmMessage1VO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

+ 2 - 2
service-cdi/service-cdi-biz/src/main/java/com/usky/cdi/service/impl/BaseDataTransferService.java

@@ -228,8 +228,8 @@ public class BaseDataTransferService {
             userIdToName.put(709, 15);
             userIdToName.put(710, 16);
             userIdToName.put(711, 2);
-            // userIdToName.put(712, 34);
-            // userIdToName.put(713, 36);
+            //userIdToName.put(712, 34);
+            //userIdToName.put(713, 36);
             userIdToName.put(714, 37);
 
             HashMap<String, Object> map = new HashMap<>();

+ 69 - 0
service-cdi/service-cdi-biz/src/main/java/com/usky/cdi/service/vo/alarm/AlarmMessage1VO.java

@@ -0,0 +1,69 @@
+package com.usky.cdi.service.vo.alarm;
+
+import lombok.Data;
+
+@Data
+public class AlarmMessage1VO {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 数据包ID
+     */
+    private Long dataPacketID;
+
+    /**
+     * 人防工程ID
+     */
+    private Long engineeringID;
+
+    /**
+     * 事件ID
+     */
+    private Integer alarmID;
+
+    /**
+     * 事件来源
+     */
+    private Integer alarmSource;
+
+    /**
+     * 物联设施ID
+     */
+    private Integer sensorID;
+
+    /**
+     * 事件类型
+     */
+    private String alarmType;
+
+    /**
+     * 事件状态
+     */
+    private Integer alarmStatus;
+
+    /**
+     * 最新水浸状态
+     */
+    private Integer sensorValue;
+
+    /**
+     * 事件发生/更新时间
+     */
+    private String alarmUpdateTime;
+
+    /**
+     * 监测对象编号
+     */
+    private String monitorObjNo;
+
+    /**
+     * 事件描述
+     */
+    private String alarmDesc;
+
+    /**
+     * 上报时间
+     */
+    private String publishTime;
+
+}

+ 61 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsAnalysisController.java

@@ -0,0 +1,61 @@
+package com.usky.ems.controller.web;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.ems.service.EmsAnalysisService;
+import com.usky.ems.service.vo.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 能耗分析接口
+ * 趋势、指标、分类占比、区域分析、对比分析
+ */
+@RestController
+@RequestMapping("/prod-api/service-ems")
+public class EmsAnalysisController {
+
+    @Autowired
+    private EmsAnalysisService emsAnalysisService;
+
+    @GetMapping("/analysis/trend")
+    public ApiResult<EmsTrendResponse> getTrend(
+            @RequestParam(required = false) Long projectId,
+            @RequestParam String timeDimension,
+            @RequestParam String timeValue,
+            @RequestParam(required = false) Long energyTypeId) {
+        return ApiResult.success(emsAnalysisService.getTrend(projectId, timeDimension, timeValue, energyTypeId));
+    }
+
+    @GetMapping("/analysis/trend/indicators")
+    public ApiResult<EmsTrendIndicatorsResponse> getTrendIndicators(
+            @RequestParam(required = false) Long projectId,
+            @RequestParam String timeDimension,
+            @RequestParam String timeValue,
+            @RequestParam(required = false) Long energyTypeId) {
+        return ApiResult.success(emsAnalysisService.getTrendIndicators(projectId, timeDimension, timeValue, energyTypeId));
+    }
+
+    @GetMapping("/analysis/trend/category")
+    public ApiResult<EmsTrendCategoryResponse> getTrendCategory(
+            @RequestParam(required = false) Long projectId,
+            @RequestParam String timeDimension,
+            @RequestParam String timeValue,
+            @RequestParam(required = false) Long energyTypeId) {
+        return ApiResult.success(emsAnalysisService.getTrendCategory(projectId, timeDimension, timeValue, energyTypeId));
+    }
+
+    @GetMapping("/analysis/region")
+    public ApiResult<EmsRegionAnalysisResponse> getRegionAnalysis(
+            @RequestParam(required = false) Long projectId,
+            @RequestParam(required = false) String regionIds,
+            @RequestParam String timeDimension,
+            @RequestParam String timeValue,
+            @RequestParam(required = false) Long energyTypeId) {
+        return ApiResult.success(emsAnalysisService.getRegionAnalysis(projectId, regionIds, timeDimension, timeValue, energyTypeId));
+    }
+
+    @PostMapping("/analysis/compare")
+    public ApiResult<EmsCompareResponse> getCompare(@RequestBody EmsCompareRequest request) {
+        return ApiResult.success(emsAnalysisService.getCompare(request));
+    }
+}

+ 233 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsApiV1Controller.java

@@ -0,0 +1,233 @@
+package com.usky.ems.controller.web;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.ems.service.EmsGatewayQueryService;
+import com.usky.ems.service.EmsModelService;
+import com.usky.ems.service.EmsOverviewService;
+import com.usky.ems.service.vo.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 能源能耗系统 API
+ *
+ * 对应 API 文档基础路径:/prod-api/service-ems
+ * GET  /prod-api/service-ems/overview/project           获取项目信息
+ * GET  /prod-api/service-ems/overview/summary            获取项目数据概括
+ * GET  /prod-api/service-ems/model/structure/tree        获取项目层级树
+ * GET  /prod-api/service-ems/model/energy-type/list      能源类型列表
+ * POST /prod-api/service-ems/model/building              新增建筑
+ * PUT  /prod-api/service-ems/model/building/{id}        编辑建筑
+ * DELETE /prod-api/service-ems/model/building/{id}      删除建筑
+ * POST /prod-api/service-ems/model/region                新增区域
+ * PUT  /prod-api/service-ems/model/region/{id}           编辑区域
+ * DELETE /prod-api/service-ems/model/region/{id}         删除区域
+ * POST /prod-api/service-ems/model/floor                 新增楼层
+ * PUT  /prod-api/service-ems/model/floor/{id}            编辑楼层
+ * DELETE /prod-api/service-ems/model/floor/{id}          删除楼层
+ * POST /prod-api/service-ems/model/gateway               新增网关
+ * PUT  /prod-api/service-ems/model/gateway/{id}          编辑网关
+ * DELETE /prod-api/service-ems/model/gateway/{id}        删除网关
+ * POST /prod-api/service-ems/model/channel               新增通道
+ * PUT  /prod-api/service-ems/model/channel/{id}          编辑通道
+ * DELETE /prod-api/service-ems/model/channel/{id}        删除通道
+ * POST /prod-api/service-ems/model/device                新增设备
+ * PUT  /prod-api/service-ems/model/device/{id}           编辑设备
+ * DELETE /prod-api/service-ems/model/device/{id}         删除设备
+ * POST /prod-api/service-ems/model/attribute-point      新增属性点位
+ * PUT  /prod-api/service-ems/model/attribute-point/{id}  编辑属性点位
+ * DELETE /prod-api/service-ems/model/attribute-point/{id} 删除属性点位
+ * GET  /prod-api/service-ems/device/gateway/list         网关列表(分页)
+ * GET  /prod-api/service-ems/device/gateway/{id}         网关详情
+ */
+@RestController
+@RequestMapping("/prod-api/service-ems")
+public class EmsApiV1Controller {
+
+    @Autowired
+    private EmsOverviewService emsOverviewService;
+    @Autowired
+    private EmsModelService emsModelService;
+    @Autowired
+    private EmsGatewayQueryService emsGatewayQueryService;
+
+    /**
+     * 获取项目信息
+     */
+    @GetMapping("/overview/project")
+    public ApiResult<EmsProjectResponse> getProject(@RequestParam(required = false) Long projectId) {
+        return ApiResult.success(emsOverviewService.getProject(projectId));
+    }
+
+    /**
+     * 获取项目数据概括(时间维度联动)
+     */
+    @GetMapping("/overview/summary")
+    public ApiResult<EmsSummaryResponse> getSummary(EmsSummaryRequest request) {
+        return ApiResult.success(emsOverviewService.getSummary(
+                request.getProjectId(), request.getTimeDimension(), request.getTimeValue()));
+    }
+
+    /**
+     * 获取项目层级树(建筑、区域、楼层、网关)
+     */
+    @GetMapping("/model/structure/tree")
+    public ApiResult<EmsStructureTreeNode> getStructureTree(
+            @RequestParam(required = false) Long projectId,
+            @RequestParam(required = false, defaultValue = "true") Boolean includeGateway) {
+        return ApiResult.success(emsModelService.getStructureTree(projectId, includeGateway));
+    }
+
+    /**
+     * 能源类型列表(电、水、气)
+     */
+    @GetMapping("/model/energy-type/list")
+    public ApiResult<List<EmsEnergyTypeVO>> getEnergyTypeList() {
+        return ApiResult.success(emsModelService.getEnergyTypeList());
+    }
+
+    @PostMapping("/model/building")
+    public ApiResult<EmsIdResponse> createBuilding(@RequestBody EmsModelSaveRequest request) {
+        Long id = emsModelService.createBuilding(request);
+        return ApiResult.success(new EmsIdResponse(id));
+    }
+
+    @PutMapping("/model/building/{id}")
+    public ApiResult<Void> updateBuilding(@PathVariable Long id, @RequestBody EmsModelSaveRequest request) {
+        emsModelService.updateBuilding(id, request);
+        return ApiResult.success();
+    }
+
+    @DeleteMapping("/model/building/{id}")
+    public ApiResult<Void> deleteBuilding(@PathVariable Long id) {
+        emsModelService.deleteBuilding(id);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/model/region")
+    public ApiResult<EmsIdResponse> createRegion(@RequestBody EmsModelSaveRequest request) {
+        Long id = emsModelService.createRegion(request);
+        return ApiResult.success(new EmsIdResponse(id));
+    }
+
+    @PutMapping("/model/region/{id}")
+    public ApiResult<Void> updateRegion(@PathVariable Long id, @RequestBody EmsModelSaveRequest request) {
+        emsModelService.updateRegion(id, request);
+        return ApiResult.success();
+    }
+
+    @DeleteMapping("/model/region/{id}")
+    public ApiResult<Void> deleteRegion(@PathVariable Long id) {
+        emsModelService.deleteRegion(id);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/model/floor")
+    public ApiResult<EmsIdResponse> createFloor(@RequestBody EmsModelSaveRequest request) {
+        Long id = emsModelService.createFloor(request);
+        return ApiResult.success(new EmsIdResponse(id));
+    }
+
+    @PutMapping("/model/floor/{id}")
+    public ApiResult<Void> updateFloor(@PathVariable Long id, @RequestBody EmsModelSaveRequest request) {
+        emsModelService.updateFloor(id, request);
+        return ApiResult.success();
+    }
+
+    @DeleteMapping("/model/floor/{id}")
+    public ApiResult<Void> deleteFloor(@PathVariable Long id) {
+        emsModelService.deleteFloor(id);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/model/gateway")
+    public ApiResult<EmsIdResponse> createGateway(@RequestBody EmsModelSaveRequest request) {
+        String id = emsModelService.createGateway(request);
+        return ApiResult.success(new EmsIdResponse(id));
+    }
+
+    @PutMapping("/model/gateway/{id}")
+    public ApiResult<Void> updateGateway(@PathVariable String id, @RequestBody EmsModelSaveRequest request) {
+        emsModelService.updateGateway(id, request);
+        return ApiResult.success();
+    }
+
+    @DeleteMapping("/model/gateway/{id}")
+    public ApiResult<Void> deleteGateway(@PathVariable String id) {
+        emsModelService.deleteGateway(id);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/model/channel")
+    public ApiResult<EmsIdResponse> createChannel(@RequestBody EmsModelSaveRequest request) {
+        Long id = emsModelService.createChannel(request);
+        return ApiResult.success(new EmsIdResponse(id));
+    }
+
+    @PutMapping("/model/channel/{id}")
+    public ApiResult<Void> updateChannel(@PathVariable Long id, @RequestBody EmsModelSaveRequest request) {
+        emsModelService.updateChannel(id, request);
+        return ApiResult.success();
+    }
+
+    @DeleteMapping("/model/channel/{id}")
+    public ApiResult<Void> deleteChannel(@PathVariable Long id) {
+        emsModelService.deleteChannel(id);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/model/device")
+    public ApiResult<EmsIdResponse> createDevice(@RequestBody EmsModelSaveRequest request) {
+        String id = emsModelService.createDevice(request);
+        return ApiResult.success(new EmsIdResponse(id));
+    }
+
+    @PutMapping("/model/device/{id}")
+    public ApiResult<Void> updateDevice(@PathVariable String id, @RequestBody EmsModelSaveRequest request) {
+        emsModelService.updateDevice(id, request);
+        return ApiResult.success();
+    }
+
+    @DeleteMapping("/model/device/{id}")
+    public ApiResult<Void> deleteDevice(@PathVariable String id) {
+        emsModelService.deleteDevice(id);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/model/attribute-point")
+    public ApiResult<EmsIdResponse> createAttributePoint(@RequestBody EmsModelSaveRequest request) {
+        Long id = emsModelService.createAttributePoint(request);
+        return ApiResult.success(new EmsIdResponse(id));
+    }
+
+    @PutMapping("/model/attribute-point/{id}")
+    public ApiResult<Void> updateAttributePoint(@PathVariable Long id, @RequestBody EmsModelSaveRequest request) {
+        emsModelService.updateAttributePoint(id, request);
+        return ApiResult.success();
+    }
+
+    @DeleteMapping("/model/attribute-point/{id}")
+    public ApiResult<Void> deleteAttributePoint(@PathVariable Long id) {
+        emsModelService.deleteAttributePoint(id);
+        return ApiResult.success();
+    }
+
+    /**
+     * 网关列表(分页)
+     */
+    @GetMapping("/device/gateway/list")
+    public ApiResult<CommonPage<EmsGatewayListItem>> gatewayList(EmsGatewayPageRequest request) {
+        return ApiResult.success(emsGatewayQueryService.listGateways(request));
+    }
+
+    /**
+     * 网关详情
+     */
+    @GetMapping("/device/gateway/{id}")
+    public ApiResult<EmsGatewayDetailResponse> getGateway(@PathVariable String id) {
+        return ApiResult.success(emsGatewayQueryService.getGatewayById(id));
+    }
+}

+ 37 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsAuthController.java

@@ -0,0 +1,37 @@
+package com.usky.ems.controller.web;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.ems.service.EmsAuthService;
+import com.usky.ems.service.vo.EmsLoginRequest;
+import com.usky.ems.service.vo.EmsLoginResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 能源系统认证接口
+ *
+ * POST /prod-api/service-ems/auth/login  用户登录
+ * POST /prod-api/service-ems/auth/logout 退出登录
+ */
+@RestController
+@RequestMapping("/prod-api/service-ems")
+public class EmsAuthController {
+
+    @Autowired
+    private EmsAuthService emsAuthService;
+
+    @PostMapping("/auth/login")
+    public ApiResult<EmsLoginResponse> login(@RequestBody EmsLoginRequest request) {
+        EmsLoginResponse data = emsAuthService.login(request);
+        return ApiResult.success("登录成功", data);
+    }
+
+    @PostMapping("/auth/logout")
+    public ApiResult<Void> logout() {
+        emsAuthService.logout();
+        return ApiResult.success("退出成功");
+    }
+}

+ 106 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsReportController.java

@@ -0,0 +1,106 @@
+package com.usky.ems.controller.web;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.ems.service.EmsReportService;
+import com.usky.ems.service.vo.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 统计报表接口
+ * 能源报表、区域报表、采集报表
+ */
+@RestController
+@RequestMapping("/prod-api/service-ems")
+public class EmsReportController {
+
+    @Autowired
+    private EmsReportService emsReportService;
+
+    // ---------- 能源报表 ----------
+    @GetMapping("/report/energy/devices")
+    public ApiResult<EmsReportDevicesResponse> getEnergyDevices(
+            @RequestParam Long energyTypeId,
+            @RequestParam(required = false) String keyword,
+            @RequestParam(required = false) Long projectId) {
+        return ApiResult.success(emsReportService.getEnergyDevices(energyTypeId, keyword, projectId));
+    }
+
+    @PostMapping("/report/energy/statistics")
+    public ApiResult<EmsEnergyStatisticsResponse> getEnergyStatistics(@RequestBody EmsEnergyStatisticsRequest request) {
+        return ApiResult.success(emsReportService.getEnergyStatistics(request));
+    }
+
+    @GetMapping("/report/energy/export")
+    public void exportEnergy(
+            @RequestParam String deviceIds,
+            @RequestParam(required = false) String attributePointIds,
+            @RequestParam String timeDimension,
+            @RequestParam String timeValue,
+            @RequestParam(required = false, defaultValue = "excel") String format,
+            HttpServletResponse response) {
+        emsReportService.exportEnergy(deviceIds, attributePointIds, timeDimension, timeValue, format, response);
+    }
+
+    // ---------- 区域报表 ----------
+    @GetMapping("/report/region/devices")
+    public ApiResult<EmsReportDevicesResponse> getRegionDevices(
+            @RequestParam Long energyTypeId,
+            @RequestParam(required = false) Long regionId,
+            @RequestParam(required = false) String keyword,
+            @RequestParam(required = false) Long projectId) {
+        return ApiResult.success(emsReportService.getRegionDevices(energyTypeId, regionId, keyword, projectId));
+    }
+
+    @PostMapping("/report/region/statistics")
+    public ApiResult<EmsEnergyStatisticsResponse> getRegionStatistics(
+            @RequestBody EmsEnergyStatisticsRequest request,
+            @RequestParam(required = false) List<Long> regionIds) {
+        return ApiResult.success(emsReportService.getRegionStatistics(request, regionIds));
+    }
+
+    @GetMapping("/report/region/export")
+    public void exportRegion(
+            @RequestParam String deviceIds,
+            @RequestParam(required = false) String attributePointIds,
+            @RequestParam String timeDimension,
+            @RequestParam String timeValue,
+            @RequestParam(required = false) String regionIds,
+            @RequestParam(required = false, defaultValue = "excel") String format,
+            HttpServletResponse response) {
+        emsReportService.exportRegion(deviceIds, attributePointIds, timeDimension, timeValue, regionIds, format, response);
+    }
+
+    // ---------- 采集报表 ----------
+    @GetMapping("/report/collection/devices")
+    public ApiResult<EmsReportDevicesResponse> getCollectionDevices(
+            @RequestParam Long energyTypeId,
+            @RequestParam(required = false) String keyword,
+            @RequestParam(required = false) Long projectId) {
+        return ApiResult.success(emsReportService.getCollectionDevices(energyTypeId, keyword, projectId));
+    }
+
+    @PostMapping("/report/collection/realtime")
+    public ApiResult<EmsCollectionRealtimeResponse> getCollectionRealtime(@RequestBody EmsCollectionRealtimeRequest request) {
+        return ApiResult.success(emsReportService.getCollectionRealtime(request));
+    }
+
+    @PostMapping("/report/collection/statistics")
+    public ApiResult<EmsEnergyStatisticsResponse> getCollectionStatistics(@RequestBody EmsEnergyStatisticsRequest request) {
+        return ApiResult.success(emsReportService.getCollectionStatistics(request));
+    }
+
+    @GetMapping("/report/collection/export")
+    public void exportCollection(
+            @RequestParam String deviceIds,
+            @RequestParam(required = false) String attributePointIds,
+            @RequestParam String timeDimension,
+            @RequestParam String timeValue,
+            @RequestParam(required = false, defaultValue = "excel") String format,
+            HttpServletResponse response) {
+        emsReportService.exportCollection(deviceIds, attributePointIds, timeDimension, timeValue, format, response);
+    }
+}

+ 39 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsChannel.java

@@ -0,0 +1,39 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 网关通道(leo.ems_channel)
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_channel")
+public class EmsChannel implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @TableField("gateway_id")
+    private String gatewayId;
+    private String name;
+    @TableField("channel_type_id")
+    private Integer channelTypeId;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 69 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsDevice.java

@@ -0,0 +1,69 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 设备(leo.ems_device)
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_device")
+public class EmsDevice implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.INPUT)
+    private String id;
+
+    @TableField("project_id")
+    private Long projectId;
+    private String number;
+    private String name;
+    @TableField("product_id")
+    private Long productId;
+    @TableField("product_template_id")
+    private Long productTemplateId;
+    @TableField("installation_location")
+    private Long installationLocation;
+    @TableField("monitoring_location")
+    private Long monitoringLocation;
+    private String location;
+    @TableField("comm_address")
+    private String commAddress;
+    @TableField("channel_id")
+    private Long channelId;
+    @TableField("gateway_id")
+    private String gatewayId;
+    @TableField("virtual_device")
+    private Integer virtualDevice;
+    private Integer focus;
+    @TableField("device_system")
+    private Integer deviceSystem;
+    private Integer status;
+    @TableField("comm_status")
+    private Integer commStatus;
+    @TableField("comm_status_code")
+    private String commStatusCode;
+    @TableField("online_time")
+    private LocalDateTime onlineTime;
+    @TableField("offline_time")
+    private LocalDateTime offlineTime;
+    @TableField("external_id")
+    private String externalId;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 50 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsDeviceFunction.java

@@ -0,0 +1,50 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 设备功能数据/属性点位(leo.ems_device_function)
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_device_function")
+public class EmsDeviceFunction implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @TableField("product_id")
+    private Long productId;
+    @TableField("product_template_id")
+    private Long productTemplateId;
+    @TableField("device_id")
+    private String deviceId;
+    private String identifier;
+    private String name;
+    private String value;
+    @TableField("acq_time")
+    private LocalDateTime acqTime;
+    private BigDecimal ratio;
+    private Integer preservable;
+    @TableField("binding_acq")
+    private Integer bindingAcq;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 44 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsEnergyItemCode.java

@@ -0,0 +1,44 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 能源分项编码(leo.ems_energy_item_code),用于能源类型列表
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_energy_item_code")
+public class EmsEnergyItemCode implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private String code;
+    @TableField("parent_code")
+    private String parentCode;
+    private String unit;
+    @TableField("unit_name")
+    private String unitName;
+    private String name;
+    private String identifier;
+    @TableField("energy_type")
+    private Integer energyType;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 64 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsGateway.java

@@ -0,0 +1,64 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 网关(leo.ems_gateway)
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_gateway")
+public class EmsGateway implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.INPUT)
+    private String id;
+
+    @TableField("project_id")
+    private Long projectId;
+    private String name;
+    @TableField("space_id")
+    private Long spaceId;
+    private String version;
+    private String type;
+    private String ip;
+    private Integer port;
+    @TableField("comm_status")
+    private Integer commStatus;
+    @TableField("online_time")
+    private LocalDateTime onlineTime;
+    @TableField("offline_time")
+    private LocalDateTime offlineTime;
+    @TableField("update_config_time")
+    private LocalDateTime updateConfigTime;
+    @TableField("update_protocol_time")
+    private LocalDateTime updateProtocolTime;
+    @TableField("upgrade_time")
+    private LocalDateTime upgradeTime;
+    @TableField("data_center_id")
+    private Long dataCenterId;
+    private String iccid;
+    private Integer rssi;
+    @TableField("secret_key")
+    private String secretKey;
+    @TableField("virtual_device")
+    private Integer virtualDevice;
+    private String remark;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 71 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsProject.java

@@ -0,0 +1,71 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 项目(leo.ems_project)
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_project")
+public class EmsProject implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @TableField("space_id")
+    private Long spaceId;
+    private String name;
+    @TableField("platform_name")
+    private String platformName;
+    private String abbreviation;
+    private BigDecimal area;
+    @TableField("common_area")
+    private BigDecimal commonArea;
+    @TableField("air_conditioned_area")
+    private BigDecimal airConditionedArea;
+    @TableField("resident_population")
+    private Integer residentPopulation;
+    @TableField("province_code")
+    private String provinceCode;
+    @TableField("province_name")
+    private String provinceName;
+    @TableField("city_code")
+    private String cityCode;
+    @TableField("city_name")
+    private String cityName;
+    @TableField("district_code")
+    private String districtCode;
+    @TableField("district_name")
+    private String districtName;
+    private String location;
+    private String address;
+    @TableField("type_id")
+    private Integer typeId;
+    @TableField("type_name")
+    private String typeName;
+    private String image;
+    private String introduction;
+    private String logo;
+    @TableField("logo_min")
+    private String logoMin;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 45 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsSpace.java

@@ -0,0 +1,45 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 空间(leo.ems_space)
+ * 空间类型:1项目 2区域 3建筑 4楼层 5房间
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_space")
+public class EmsSpace implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private String name;
+    @TableField("parent_id")
+    private Long parentId;
+    private Integer type;
+    @TableField("root_id")
+    private Long rootId;
+    private String path;
+    @TableField("path_name")
+    private String pathName;
+    private Integer deep;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 46 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsSpaceArea.java

@@ -0,0 +1,46 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 区域(leo.ems_space_area)
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_space_area")
+public class EmsSpaceArea implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @TableField("space_id")
+    private Long spaceId;
+    private String name;
+    private Integer type;
+    private BigDecimal area;
+    @TableField("common_area")
+    private BigDecimal commonArea;
+    @TableField("air_conditioned_area")
+    private BigDecimal airConditionedArea;
+    @TableField("resident_population")
+    private Integer residentPopulation;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 67 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsSpaceBuilding.java

@@ -0,0 +1,67 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 建筑(leo.ems_space_building)
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_space_building")
+public class EmsSpaceBuilding implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @TableField("space_id")
+    private Long spaceId;
+    private String name;
+    private BigDecimal area;
+    @TableField("common_area")
+    private BigDecimal commonArea;
+    @TableField("air_conditioned_area")
+    private BigDecimal airConditionedArea;
+    @TableField("resident_population")
+    private Integer residentPopulation;
+    @TableField("province_code")
+    private String provinceCode;
+    @TableField("province_name")
+    private String provinceName;
+    @TableField("city_code")
+    private String cityCode;
+    @TableField("city_name")
+    private String cityName;
+    @TableField("district_code")
+    private String districtCode;
+    @TableField("district_name")
+    private String districtName;
+    private String location;
+    private String address;
+    private Integer floor;
+    private BigDecimal height;
+    @TableField("type_id")
+    private Integer typeId;
+    @TableField("type_name")
+    private String typeName;
+    private String image;
+    private String introduction;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 45 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsSpaceFloor.java

@@ -0,0 +1,45 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 楼层(leo.ems_space_floor)
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ems_space_floor")
+public class EmsSpaceFloor implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @TableField("space_id")
+    private Long spaceId;
+    private String name;
+    private BigDecimal area;
+    @TableField("common_area")
+    private BigDecimal commonArea;
+    @TableField("air_conditioned_area")
+    private BigDecimal airConditionedArea;
+    @TableField("resident_population")
+    private Integer residentPopulation;
+    @TableField("updated_by")
+    private Long updatedBy;
+    @TableField("update_time")
+    private LocalDateTime updateTime;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("create_time")
+    private LocalDateTime createTime;
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsChannelMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsChannel;
+
+/**
+ * 通道 Mapper(leo.ems_channel)
+ */
+public interface EmsChannelMapper extends CrudMapper<EmsChannel> {
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsDeviceFunctionMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsDeviceFunction;
+
+/**
+ * 设备功能/属性点位 Mapper(leo.ems_device_function)
+ */
+public interface EmsDeviceFunctionMapper extends CrudMapper<EmsDeviceFunction> {
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsDeviceMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsDevice;
+
+/**
+ * 设备 Mapper(leo.ems_device)
+ */
+public interface EmsDeviceMapper extends CrudMapper<EmsDevice> {
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsEnergyItemCodeMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsEnergyItemCode;
+
+/**
+ * 能源分项编码 Mapper(leo.ems_energy_item_code)
+ */
+public interface EmsEnergyItemCodeMapper extends CrudMapper<EmsEnergyItemCode> {
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsGatewayMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsGateway;
+
+/**
+ * 网关 Mapper(leo.ems_gateway)
+ */
+public interface EmsGatewayMapper extends CrudMapper<EmsGateway> {
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsProjectMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsProject;
+
+/**
+ * 项目 Mapper(leo.ems_project)
+ */
+public interface EmsProjectMapper extends CrudMapper<EmsProject> {
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsSpaceAreaMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsSpaceArea;
+
+/**
+ * 区域 Mapper(leo.ems_space_area)
+ */
+public interface EmsSpaceAreaMapper extends CrudMapper<EmsSpaceArea> {
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsSpaceBuildingMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsSpaceBuilding;
+
+/**
+ * 建筑 Mapper(leo.ems_space_building)
+ */
+public interface EmsSpaceBuildingMapper extends CrudMapper<EmsSpaceBuilding> {
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsSpaceFloorMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsSpaceFloor;
+
+/**
+ * 楼层 Mapper(leo.ems_space_floor)
+ */
+public interface EmsSpaceFloorMapper extends CrudMapper<EmsSpaceFloor> {
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsSpaceMapper.java

@@ -0,0 +1,10 @@
+package com.usky.ems.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.ems.domain.EmsSpace;
+
+/**
+ * 空间 Mapper(leo.ems_space)
+ */
+public interface EmsSpaceMapper extends CrudMapper<EmsSpace> {
+}

+ 19 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsAnalysisService.java

@@ -0,0 +1,19 @@
+package com.usky.ems.service;
+
+import com.usky.ems.service.vo.*;
+
+/**
+ * 能耗分析服务:趋势、指标、分类占比、区域分析、对比分析
+ */
+public interface EmsAnalysisService {
+
+    EmsTrendResponse getTrend(Long projectId, String timeDimension, String timeValue, Long energyTypeId);
+
+    EmsTrendIndicatorsResponse getTrendIndicators(Long projectId, String timeDimension, String timeValue, Long energyTypeId);
+
+    EmsTrendCategoryResponse getTrendCategory(Long projectId, String timeDimension, String timeValue, Long energyTypeId);
+
+    EmsRegionAnalysisResponse getRegionAnalysis(Long projectId, String regionIds, String timeDimension, String timeValue, Long energyTypeId);
+
+    EmsCompareResponse getCompare(EmsCompareRequest request);
+}

+ 14 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsAuthService.java

@@ -0,0 +1,14 @@
+package com.usky.ems.service;
+
+import com.usky.ems.service.vo.EmsLoginRequest;
+import com.usky.ems.service.vo.EmsLoginResponse;
+
+/**
+ * 能源系统认证服务(登录/登出)
+ */
+public interface EmsAuthService {
+
+    EmsLoginResponse login(EmsLoginRequest request);
+
+    void logout();
+}

+ 22 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsGatewayQueryService.java

@@ -0,0 +1,22 @@
+package com.usky.ems.service;
+
+import com.usky.common.core.bean.CommonPage;
+import com.usky.ems.service.vo.EmsGatewayDetailResponse;
+import com.usky.ems.service.vo.EmsGatewayListItem;
+import com.usky.ems.service.vo.EmsGatewayPageRequest;
+
+/**
+ * 网关列表与详情查询服务
+ */
+public interface EmsGatewayQueryService {
+
+    /**
+     * 分页查询网关列表
+     */
+    CommonPage<EmsGatewayListItem> listGateways(EmsGatewayPageRequest request);
+
+    /**
+     * 网关详情
+     */
+    EmsGatewayDetailResponse getGatewayById(String id);
+}

+ 72 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsModelService.java

@@ -0,0 +1,72 @@
+package com.usky.ems.service;
+
+import com.usky.ems.service.vo.EmsEnergyTypeVO;
+import com.usky.ems.service.vo.EmsModelSaveRequest;
+import com.usky.ems.service.vo.EmsStructureTreeNode;
+
+import java.util.List;
+
+/**
+ * 基础建模服务(model:结构树、能源类型、建筑/区域/楼层/网关/通道/设备/属性点位)
+ */
+public interface EmsModelService {
+
+    /**
+     * 获取项目层级树(建筑、区域、楼层、网关)
+     */
+    EmsStructureTreeNode getStructureTree(Long projectId, Boolean includeGateway);
+
+    /**
+     * 能源类型列表(电、水、气)
+     */
+    List<EmsEnergyTypeVO> getEnergyTypeList();
+
+    /** 建筑:新增 */
+    Long createBuilding(EmsModelSaveRequest request);
+    /** 建筑:编辑 */
+    void updateBuilding(Long id, EmsModelSaveRequest request);
+    /** 建筑:删除 */
+    void deleteBuilding(Long id);
+
+    /** 区域:新增 */
+    Long createRegion(EmsModelSaveRequest request);
+    /** 区域:编辑 */
+    void updateRegion(Long id, EmsModelSaveRequest request);
+    /** 区域:删除 */
+    void deleteRegion(Long id);
+
+    /** 楼层:新增 */
+    Long createFloor(EmsModelSaveRequest request);
+    /** 楼层:编辑 */
+    void updateFloor(Long id, EmsModelSaveRequest request);
+    /** 楼层:删除 */
+    void deleteFloor(Long id);
+
+    /** 网关:新增 */
+    String createGateway(EmsModelSaveRequest request);
+    /** 网关:编辑 */
+    void updateGateway(String id, EmsModelSaveRequest request);
+    /** 网关:删除 */
+    void deleteGateway(String id);
+
+    /** 通道:新增 */
+    Long createChannel(EmsModelSaveRequest request);
+    /** 通道:编辑 */
+    void updateChannel(Long id, EmsModelSaveRequest request);
+    /** 通道:删除 */
+    void deleteChannel(Long id);
+
+    /** 设备:新增 */
+    String createDevice(EmsModelSaveRequest request);
+    /** 设备:编辑 */
+    void updateDevice(String id, EmsModelSaveRequest request);
+    /** 设备:删除 */
+    void deleteDevice(String id);
+
+    /** 属性点位:新增 */
+    Long createAttributePoint(EmsModelSaveRequest request);
+    /** 属性点位:编辑 */
+    void updateAttributePoint(Long id, EmsModelSaveRequest request);
+    /** 属性点位:删除 */
+    void deleteAttributePoint(Long id);
+}

+ 20 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsOverviewService.java

@@ -0,0 +1,20 @@
+package com.usky.ems.service;
+
+import com.usky.ems.service.vo.EmsProjectResponse;
+import com.usky.ems.service.vo.EmsSummaryResponse;
+
+/**
+ * 能源总览服务(overview)
+ */
+public interface EmsOverviewService {
+
+    /**
+     * 获取项目信息(当前项目或指定 projectId)
+     */
+    EmsProjectResponse getProject(Long projectId);
+
+    /**
+     * 获取项目数据概括(时间维度联动)
+     */
+    EmsSummaryResponse getSummary(Long projectId, String timeDimension, String timeValue);
+}

+ 32 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsReportService.java

@@ -0,0 +1,32 @@
+package com.usky.ems.service;
+
+import com.usky.ems.service.vo.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 统计报表服务:能源报表、区域报表、采集报表
+ */
+public interface EmsReportService {
+
+    EmsReportDevicesResponse getEnergyDevices(Long energyTypeId, String keyword, Long projectId);
+
+    EmsEnergyStatisticsResponse getEnergyStatistics(EmsEnergyStatisticsRequest request);
+
+    void exportEnergy(String deviceIds, String attributePointIds, String timeDimension, String timeValue, String format, HttpServletResponse response);
+
+    EmsReportDevicesResponse getRegionDevices(Long energyTypeId, Long regionId, String keyword, Long projectId);
+
+    EmsEnergyStatisticsResponse getRegionStatistics(EmsEnergyStatisticsRequest request, List<Long> regionIds);
+
+    void exportRegion(String deviceIds, String attributePointIds, String timeDimension, String timeValue, String regionIds, String format, HttpServletResponse response);
+
+    EmsReportDevicesResponse getCollectionDevices(Long energyTypeId, String keyword, Long projectId);
+
+    EmsCollectionRealtimeResponse getCollectionRealtime(EmsCollectionRealtimeRequest request);
+
+    EmsEnergyStatisticsResponse getCollectionStatistics(EmsEnergyStatisticsRequest request);
+
+    void exportCollection(String deviceIds, String attributePointIds, String timeDimension, String timeValue, String format, HttpServletResponse response);
+}

+ 61 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsAnalysisServiceImpl.java

@@ -0,0 +1,61 @@
+package com.usky.ems.service.impl;
+
+import com.usky.ems.service.EmsAnalysisService;
+import com.usky.ems.service.vo.*;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+
+/**
+ * 能耗分析服务实现(占位数据,后续对接时序/聚合数据)
+ */
+@Service
+public class EmsAnalysisServiceImpl implements EmsAnalysisService {
+
+    private static final String[] ENERGY_TYPE_NAMES = {"", "电", "水", "气"};
+
+    @Override
+    public EmsTrendResponse getTrend(Long projectId, String timeDimension, String timeValue, Long energyTypeId) {
+        EmsTrendResponse resp = new EmsTrendResponse();
+        resp.setTimeDimension(timeDimension);
+        resp.setTimeValue(timeValue);
+        return resp;
+    }
+
+    @Override
+    public EmsTrendIndicatorsResponse getTrendIndicators(Long projectId, String timeDimension, String timeValue, Long energyTypeId) {
+        EmsTrendIndicatorsResponse resp = new EmsTrendIndicatorsResponse();
+        resp.setTimeDimension(timeDimension);
+        resp.setTimeValue(timeValue);
+        resp.setTotalUsage(BigDecimal.ZERO);
+        resp.setStandardCoal(BigDecimal.ZERO);
+        resp.setCarbonEmission(BigDecimal.ZERO);
+        resp.setAreaUsage(BigDecimal.ZERO);
+        resp.setPerCapitaUsage(BigDecimal.ZERO);
+        return resp;
+    }
+
+    @Override
+    public EmsTrendCategoryResponse getTrendCategory(Long projectId, String timeDimension, String timeValue, Long energyTypeId) {
+        EmsTrendCategoryResponse resp = new EmsTrendCategoryResponse();
+        resp.setTimeDimension(timeDimension);
+        resp.setTimeValue(timeValue);
+        return resp;
+    }
+
+    @Override
+    public EmsRegionAnalysisResponse getRegionAnalysis(Long projectId, String regionIds, String timeDimension, String timeValue, Long energyTypeId) {
+        return new EmsRegionAnalysisResponse();
+    }
+
+    @Override
+    public EmsCompareResponse getCompare(EmsCompareRequest request) {
+        EmsCompareResponse resp = new EmsCompareResponse();
+        if (request != null && request.getEnergyTypeId() != null) {
+            long id = request.getEnergyTypeId();
+            resp.setEnergyTypeName(id >= 1 && id <= 3 ? ENERGY_TYPE_NAMES[(int) id] : "");
+        }
+        if (request != null) resp.setDimension(request.getTimeDimension());
+        return resp;
+    }
+}

+ 30 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsAuthServiceImpl.java

@@ -0,0 +1,30 @@
+package com.usky.ems.service.impl;
+
+import com.usky.ems.service.EmsAuthService;
+import com.usky.ems.service.vo.EmsLoginRequest;
+import com.usky.ems.service.vo.EmsLoginResponse;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+
+/**
+ * 能源系统认证服务实现(预留与统一认证对接,当前返回占位 token)
+ */
+@Service
+public class EmsAuthServiceImpl implements EmsAuthService {
+
+    private static final long EXPIRES_IN_SECONDS = 7200L;
+
+    @Override
+    public EmsLoginResponse login(EmsLoginRequest request) {
+        EmsLoginResponse resp = new EmsLoginResponse();
+        resp.setAccessToken("Bearer " + UUID.randomUUID().toString().replace("-", ""));
+        resp.setExpiresIn(EXPIRES_IN_SECONDS);
+        return resp;
+    }
+
+    @Override
+    public void logout() {
+        // 预留:使当前 token 失效
+    }
+}

+ 87 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsGatewayQueryServiceImpl.java

@@ -0,0 +1,87 @@
+package com.usky.ems.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.ems.domain.EmsGateway;
+import com.usky.ems.mapper.EmsGatewayMapper;
+import com.usky.ems.mapper.EmsSpaceMapper;
+import com.usky.ems.service.EmsGatewayQueryService;
+import com.usky.ems.service.vo.EmsGatewayDetailResponse;
+import com.usky.ems.service.vo.EmsGatewayListItem;
+import com.usky.ems.service.vo.EmsGatewayPageRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 网关列表与详情查询服务实现
+ */
+@Service
+public class EmsGatewayQueryServiceImpl implements EmsGatewayQueryService {
+
+    @Autowired
+    private EmsGatewayMapper emsGatewayMapper;
+    @Autowired
+    private EmsSpaceMapper emsSpaceMapper;
+
+    @Override
+    public CommonPage<EmsGatewayListItem> listGateways(EmsGatewayPageRequest request) {
+        int current = request.getCurrent() == null ? 1 : request.getCurrent();
+        int size = request.getSize() == null ? 10 : request.getSize();
+        Page<EmsGateway> p = new Page<>(current, size);
+        LambdaQueryWrapper<EmsGateway> q = new LambdaQueryWrapper<>();
+        if (request.getCommunicationStatus() != null) {
+            q.eq(EmsGateway::getCommStatus, request.getCommunicationStatus());
+        }
+        if (StringUtils.hasText(request.getName())) {
+            q.like(EmsGateway::getName, request.getName());
+        }
+        if (StringUtils.hasText(request.getCode())) {
+            q.eq(EmsGateway::getId, request.getCode());
+        }
+        Page<EmsGateway> page = emsGatewayMapper.selectPage(p, q);
+        List<EmsGatewayListItem> list = page.getRecords().stream().map(this::toListItem).collect(Collectors.toList());
+        return new CommonPage<>(list, page.getTotal(), size, current);
+    }
+
+    @Override
+    public EmsGatewayDetailResponse getGatewayById(String id) {
+        EmsGateway g = emsGatewayMapper.selectById(id);
+        if (g == null) return null;
+        EmsGatewayDetailResponse resp = new EmsGatewayDetailResponse();
+        resp.setId(g.getId());
+        resp.setName(g.getName());
+        resp.setSpaceId(g.getSpaceId());
+        resp.setCommunicationStatus(g.getCommStatus());
+        resp.setOnlineTime(g.getOnlineTime());
+        resp.setCreateTime(g.getCreateTime());
+        resp.setUpdateTime(g.getUpdateTime());
+        if (g.getSpaceId() != null) {
+            com.usky.ems.domain.EmsSpace space = emsSpaceMapper.selectById(g.getSpaceId());
+            if (space != null) {
+                resp.setFloorName(space.getName());
+            }
+        }
+        return resp;
+    }
+
+    private EmsGatewayListItem toListItem(EmsGateway g) {
+        EmsGatewayListItem item = new EmsGatewayListItem();
+        item.setId(g.getId());
+        item.setName(g.getName());
+        item.setSpaceId(g.getSpaceId());
+        item.setCommunicationStatus(g.getCommStatus());
+        item.setOnlineTime(g.getOnlineTime());
+        item.setCreateTime(g.getCreateTime());
+        item.setUpdateTime(g.getUpdateTime());
+        if (g.getSpaceId() != null) {
+            com.usky.ems.domain.EmsSpace space = emsSpaceMapper.selectById(g.getSpaceId());
+            if (space != null) item.setFloorName(space.getName());
+        }
+        return item;
+    }
+}

+ 401 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsModelServiceImpl.java

@@ -0,0 +1,401 @@
+package com.usky.ems.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.usky.ems.domain.*;
+import com.usky.ems.mapper.*;
+import com.usky.ems.service.EmsModelService;
+import com.usky.ems.service.vo.EmsEnergyTypeVO;
+import com.usky.ems.service.vo.EmsModelSaveRequest;
+import com.usky.ems.service.vo.EmsStructureTreeNode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+/**
+ * 基础建模服务实现(结构树、能源类型、建筑/区域/楼层/网关/通道/设备/属性点位 CRUD)
+ */
+@Service
+public class EmsModelServiceImpl implements EmsModelService {
+
+    @Autowired
+    private EmsProjectMapper emsProjectMapper;
+    @Autowired
+    private EmsSpaceMapper emsSpaceMapper;
+    @Autowired
+    private EmsSpaceBuildingMapper emsSpaceBuildingMapper;
+    @Autowired
+    private EmsSpaceAreaMapper emsSpaceAreaMapper;
+    @Autowired
+    private EmsSpaceFloorMapper emsSpaceFloorMapper;
+    @Autowired
+    private EmsGatewayMapper emsGatewayMapper;
+    @Autowired
+    private EmsChannelMapper emsChannelMapper;
+    @Autowired
+    private EmsDeviceMapper emsDeviceMapper;
+    @Autowired
+    private EmsDeviceFunctionMapper emsDeviceFunctionMapper;
+    @Autowired
+    private EmsEnergyItemCodeMapper emsEnergyItemCodeMapper;
+
+    private static final int SPACE_TYPE_PROJECT = 1;
+    private static final int SPACE_TYPE_REGION = 2;
+    private static final int SPACE_TYPE_BUILDING = 3;
+    private static final int SPACE_TYPE_FLOOR = 4;
+    private static final int SPACE_TYPE_ROOM = 5;
+
+    private EmsProject firstProject() {
+        List<EmsProject> list = emsProjectMapper.selectList(null);
+        return list.isEmpty() ? null : list.get(0);
+    }
+
+    @Override
+    public EmsStructureTreeNode getStructureTree(Long projectId, Boolean includeGateway) {
+        EmsProject project = projectId != null ? emsProjectMapper.selectById(projectId) : firstProject();
+        if (project == null) {
+            return null;
+        }
+        EmsStructureTreeNode root = new EmsStructureTreeNode();
+        root.setId(project.getId());
+        root.setName(project.getName());
+        root.setType("project");
+        root.setChildren(buildSpaceChildren(project.getSpaceId(), includeGateway == null || includeGateway));
+        return root;
+    }
+
+    private List<EmsStructureTreeNode> buildSpaceChildren(Long parentId, boolean includeGateway) {
+        if (parentId == null) return Collections.emptyList();
+        List<EmsSpace> list = emsSpaceMapper.selectList(new LambdaQueryWrapper<EmsSpace>().eq(EmsSpace::getParentId, parentId));
+        List<EmsStructureTreeNode> children = new ArrayList<>();
+        for (EmsSpace s : list) {
+            EmsStructureTreeNode node = new EmsStructureTreeNode();
+            node.setId(s.getId());
+            node.setName(s.getName());
+            if (s.getType() == SPACE_TYPE_BUILDING) {
+                node.setType("building");
+                EmsSpaceBuilding b = emsSpaceBuildingMapper.selectOne(new LambdaQueryWrapper<EmsSpaceBuilding>().eq(EmsSpaceBuilding::getSpaceId, s.getId()));
+                if (b != null) node.setName(b.getName());
+            } else if (s.getType() == SPACE_TYPE_REGION) {
+                node.setType("region");
+                EmsSpaceArea a = emsSpaceAreaMapper.selectOne(new LambdaQueryWrapper<EmsSpaceArea>().eq(EmsSpaceArea::getSpaceId, s.getId()));
+                if (a != null) node.setName(a.getName());
+            } else if (s.getType() == SPACE_TYPE_FLOOR) {
+                node.setType("floor");
+                EmsSpaceFloor f = emsSpaceFloorMapper.selectOne(new LambdaQueryWrapper<EmsSpaceFloor>().eq(EmsSpaceFloor::getSpaceId, s.getId()));
+                if (f != null) node.setName(f.getName());
+            } else {
+                node.setType("space");
+            }
+            List<EmsStructureTreeNode> sub = buildSpaceChildren(s.getId(), includeGateway);
+            if (includeGateway && (s.getType() == SPACE_TYPE_FLOOR || s.getType() == SPACE_TYPE_ROOM)) {
+                List<EmsGateway> gateways = emsGatewayMapper.selectList(new LambdaQueryWrapper<EmsGateway>().eq(EmsGateway::getSpaceId, s.getId()));
+                for (EmsGateway g : gateways) {
+                    EmsStructureTreeNode gw = new EmsStructureTreeNode();
+                    gw.setId(g.getId());
+                    gw.setName(g.getName());
+                    gw.setType("gateway");
+                    sub.add(gw);
+                }
+            }
+            node.setChildren(sub);
+            children.add(node);
+        }
+        return children;
+    }
+
+    @Override
+    public List<EmsEnergyTypeVO> getEnergyTypeList() {
+        String[] names = {"", "电", "水", "气"};
+        String[] codes = {"", "electric", "water", "gas"};
+        String[] units = {"", "kWh", "m³", "m³"};
+        List<EmsEnergyTypeVO> result = new ArrayList<>();
+        for (int i = 1; i <= 3; i++) {
+            EmsEnergyTypeVO vo = new EmsEnergyTypeVO();
+            vo.setId((long) i);
+            vo.setName(names[i]);
+            vo.setCode(codes[i]);
+            vo.setUnit(units[i]);
+            vo.setSortOrder(i - 1);
+            result.add(vo);
+        }
+        return result;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createBuilding(EmsModelSaveRequest request) {
+        Long projectId = request.getProjectId();
+        String name = request.getName();
+        EmsProject project = projectId != null ? emsProjectMapper.selectById(projectId) : firstProject();
+        if (project == null) return null;
+        EmsSpace space = new EmsSpace();
+        space.setName(name);
+        space.setParentId(project.getSpaceId());
+        space.setType(SPACE_TYPE_BUILDING);
+        space.setRootId(project.getSpaceId());
+        emsSpaceMapper.insert(space);
+        EmsSpaceBuilding building = new EmsSpaceBuilding();
+        building.setSpaceId(space.getId());
+        building.setName(name);
+        emsSpaceBuildingMapper.insert(building);
+        return space.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateBuilding(Long id, EmsModelSaveRequest request) {
+        String name = request.getName();
+        EmsSpace space = emsSpaceMapper.selectById(id);
+        if (space != null && name != null) {
+            space.setName(name);
+            emsSpaceMapper.updateById(space);
+        }
+        EmsSpaceBuilding b = emsSpaceBuildingMapper.selectOne(new LambdaQueryWrapper<EmsSpaceBuilding>().eq(EmsSpaceBuilding::getSpaceId, id));
+        if (b != null && name != null) {
+            b.setName(name);
+            emsSpaceBuildingMapper.updateById(b);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteBuilding(Long id) {
+        emsSpaceBuildingMapper.delete(new LambdaQueryWrapper<EmsSpaceBuilding>().eq(EmsSpaceBuilding::getSpaceId, id));
+        emsSpaceMapper.deleteById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createRegion(EmsModelSaveRequest request) {
+        Long buildingId = request.getBuildingId();
+        String name = request.getName();
+        EmsSpace space = new EmsSpace();
+        space.setName(name);
+        space.setParentId(buildingId);
+        EmsSpace parent = emsSpaceMapper.selectById(buildingId);
+        space.setRootId(parent != null ? parent.getRootId() : buildingId);
+        space.setType(SPACE_TYPE_REGION);
+        emsSpaceMapper.insert(space);
+        EmsSpaceArea area = new EmsSpaceArea();
+        area.setSpaceId(space.getId());
+        area.setName(name);
+        if (request.getArea() != null) area.setArea(request.getArea());
+        return space.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateRegion(Long id, EmsModelSaveRequest request) {
+        String name = request.getName();
+        EmsSpace space = emsSpaceMapper.selectById(id);
+        if (space != null && name != null) {
+            space.setName(name);
+            emsSpaceMapper.updateById(space);
+        }
+        EmsSpaceArea a = emsSpaceAreaMapper.selectOne(new LambdaQueryWrapper<EmsSpaceArea>().eq(EmsSpaceArea::getSpaceId, id));
+        if (a != null) {
+            if (name != null) a.setName(name);
+            if (request.getArea() != null) a.setArea(request.getArea());
+            emsSpaceAreaMapper.updateById(a);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteRegion(Long id) {
+        emsSpaceAreaMapper.delete(new LambdaQueryWrapper<EmsSpaceArea>().eq(EmsSpaceArea::getSpaceId, id));
+        emsSpaceMapper.deleteById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createFloor(EmsModelSaveRequest request) {
+        Long regionId = request.getRegionId();
+        String name = request.getName();
+        EmsSpace space = new EmsSpace();
+        space.setName(name);
+        space.setParentId(regionId);
+        EmsSpace parent = emsSpaceMapper.selectById(regionId);
+        space.setRootId(parent != null ? parent.getRootId() : regionId);
+        space.setType(SPACE_TYPE_FLOOR);
+        emsSpaceMapper.insert(space);
+        EmsSpaceFloor floor = new EmsSpaceFloor();
+        floor.setSpaceId(space.getId());
+        floor.setName(name);
+        emsSpaceFloorMapper.insert(floor);
+        return space.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateFloor(Long id, EmsModelSaveRequest request) {
+        String name = request.getName();
+        EmsSpace space = emsSpaceMapper.selectById(id);
+        if (space != null && name != null) {
+            space.setName(name);
+            emsSpaceMapper.updateById(space);
+        }
+        EmsSpaceFloor f = emsSpaceFloorMapper.selectOne(new LambdaQueryWrapper<EmsSpaceFloor>().eq(EmsSpaceFloor::getSpaceId, id));
+        if (f != null && name != null) {
+            f.setName(name);
+            emsSpaceFloorMapper.updateById(f);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteFloor(Long id) {
+        emsSpaceFloorMapper.delete(new LambdaQueryWrapper<EmsSpaceFloor>().eq(EmsSpaceFloor::getSpaceId, id));
+        emsSpaceMapper.deleteById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String createGateway(EmsModelSaveRequest request) {
+        Long floorId = request.getFloorId();
+        String name = request.getName();
+        String gwId = "GW" + System.currentTimeMillis();
+        if (gwId.length() > 13) gwId = gwId.substring(0, 13);
+        EmsGateway g = new EmsGateway();
+        g.setId(gwId);
+        g.setName(name);
+        g.setSpaceId(floorId);
+        g.setProjectId(0L);
+        EmsSpace space = floorId != null ? emsSpaceMapper.selectById(floorId) : null;
+        if (space != null) {
+            EmsProject p = firstProject();
+            if (p != null) g.setProjectId(p.getId());
+        }
+        g.setCommStatus(0);
+        g.setDataCenterId(0L);
+        g.setSecretKey("");
+        g.setVirtualDevice(0);
+        emsGatewayMapper.insert(g);
+        return g.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateGateway(String id, EmsModelSaveRequest request) {
+        EmsGateway g = emsGatewayMapper.selectById(id);
+        if (g == null) return;
+        if (request.getName() != null) g.setName(request.getName());
+        if (request.getFloorId() != null) g.setSpaceId(request.getFloorId());
+        emsGatewayMapper.updateById(g);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteGateway(String id) {
+        emsGatewayMapper.deleteById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createChannel(EmsModelSaveRequest request) {
+        String gatewayId = request.getGatewayId();
+        String name = request.getName();
+        EmsChannel c = new EmsChannel();
+        c.setGatewayId(gatewayId);
+        c.setName(name);
+        c.setChannelTypeId(1);
+        emsChannelMapper.insert(c);
+        return c.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateChannel(Long id, EmsModelSaveRequest request) {
+        EmsChannel c = emsChannelMapper.selectById(id);
+        if (c == null) return;
+        if (request.getName() != null) c.setName(request.getName());
+        if (request.getGatewayId() != null) c.setGatewayId(request.getGatewayId());
+        emsChannelMapper.updateById(c);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteChannel(Long id) {
+        emsChannelMapper.deleteById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String createDevice(EmsModelSaveRequest request) {
+        Long channelId = request.getChannelId();
+        String name = request.getName();
+        String devId = "DV" + System.currentTimeMillis();
+        if (devId.length() > 13) devId = devId.substring(0, 13);
+        EmsDevice d = new EmsDevice();
+        d.setId(devId);
+        d.setName(name);
+        d.setProjectId(0L);
+        d.setChannelId(channelId != null ? channelId : 0L);
+        d.setGatewayId("");
+        EmsChannel ch = channelId != null ? emsChannelMapper.selectById(channelId) : null;
+        if (ch != null) d.setGatewayId(ch.getGatewayId());
+        d.setProductId(0L);
+        d.setInstallationLocation(0L);
+        d.setMonitoringLocation(0L);
+        d.setCommAddress("");
+        d.setVirtualDevice(0);
+        d.setFocus(0);
+        d.setDeviceSystem(0);
+        d.setStatus(1);
+        d.setCommStatus(0);
+        emsDeviceMapper.insert(d);
+        return d.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateDevice(String id, EmsModelSaveRequest request) {
+        EmsDevice d = emsDeviceMapper.selectById(id);
+        if (d == null) return;
+        if (request.getName() != null) d.setName(request.getName());
+        if (request.getChannelId() != null) d.setChannelId(request.getChannelId());
+        emsDeviceMapper.updateById(d);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteDevice(String id) {
+        emsDeviceMapper.deleteById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createAttributePoint(EmsModelSaveRequest request) {
+        String deviceId = request.getDeviceId();
+        String name = request.getName();
+        String code = request.getCode();
+        EmsDeviceFunction f = new EmsDeviceFunction();
+        f.setDeviceId(deviceId);
+        f.setName(name);
+        f.setIdentifier(code != null ? code : name);
+        f.setProductId(0L);
+        f.setProductTemplateId(0L);
+        f.setPreservable(1);
+        f.setBindingAcq(0);
+        emsDeviceFunctionMapper.insert(f);
+        return f.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateAttributePoint(Long id, EmsModelSaveRequest request) {
+        EmsDeviceFunction f = emsDeviceFunctionMapper.selectById(id);
+        if (f == null) return;
+        if (request.getName() != null) f.setName(request.getName());
+        if (request.getCode() != null) f.setIdentifier(request.getCode());
+        emsDeviceFunctionMapper.updateById(f);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteAttributePoint(Long id) {
+        emsDeviceFunctionMapper.deleteById(id);
+    }
+}

+ 53 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsOverviewServiceImpl.java

@@ -0,0 +1,53 @@
+package com.usky.ems.service.impl;
+
+import com.usky.ems.domain.EmsProject;
+import com.usky.ems.mapper.EmsProjectMapper;
+import com.usky.ems.service.EmsOverviewService;
+import com.usky.ems.service.vo.EmsProjectResponse;
+import com.usky.ems.service.vo.EmsSummaryResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 能源总览服务实现
+ */
+@Service
+public class EmsOverviewServiceImpl implements EmsOverviewService {
+
+    @Autowired
+    private EmsProjectMapper emsProjectMapper;
+
+    @Override
+    public EmsProjectResponse getProject(Long projectId) {
+        EmsProject project;
+        if (projectId != null) {
+            project = emsProjectMapper.selectById(projectId);
+        } else {
+            List<EmsProject> list = emsProjectMapper.selectList(null);
+            project = list.isEmpty() ? null : list.get(0);
+        }
+        if (project == null) {
+            return null;
+        }
+        EmsProjectResponse resp = new EmsProjectResponse();
+        resp.setId(project.getId());
+        resp.setName(project.getName());
+        resp.setCode(project.getAbbreviation());
+        resp.setDescription(project.getIntroduction());
+        resp.setArea(project.getArea());
+        resp.setResidentPopulation(project.getResidentPopulation());
+        resp.setCreateTime(project.getCreateTime());
+        resp.setUpdateTime(project.getUpdateTime());
+        return resp;
+    }
+
+    @Override
+    public EmsSummaryResponse getSummary(Long projectId, String timeDimension, String timeValue) {
+        EmsSummaryResponse resp = new EmsSummaryResponse();
+        resp.setTimeDimension(timeDimension);
+        resp.setTimeValue(timeValue);
+        return resp;
+    }
+}

+ 164 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsReportServiceImpl.java

@@ -0,0 +1,164 @@
+package com.usky.ems.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.usky.ems.domain.EmsDevice;
+import com.usky.ems.domain.EmsDeviceFunction;
+import com.usky.ems.domain.EmsProject;
+import com.usky.ems.mapper.EmsDeviceFunctionMapper;
+import com.usky.ems.mapper.EmsDeviceMapper;
+import com.usky.ems.mapper.EmsProjectMapper;
+import com.usky.ems.service.EmsReportService;
+import com.usky.ems.service.vo.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 统计报表服务实现(设备列表基于 leo 设备与属性点位,统计与导出为占位/预留)
+ */
+@Service
+public class EmsReportServiceImpl implements EmsReportService {
+
+    private static final String[] ENERGY_TYPE_NAMES = {"", "电", "水", "气"};
+
+    @Autowired
+    private EmsProjectMapper emsProjectMapper;
+    @Autowired
+    private EmsDeviceMapper emsDeviceMapper;
+    @Autowired
+    private EmsDeviceFunctionMapper emsDeviceFunctionMapper;
+
+    private Long resolveProjectId(Long projectId) {
+        if (projectId != null) return projectId;
+        List<EmsProject> list = emsProjectMapper.selectList(null);
+        return list.isEmpty() ? null : list.get(0).getId();
+    }
+
+    private String energyTypeName(Long energyTypeId) {
+        if (energyTypeId == null || energyTypeId < 1 || energyTypeId > 3) return "";
+        return ENERGY_TYPE_NAMES[energyTypeId.intValue()];
+    }
+
+    @Override
+    public EmsReportDevicesResponse getEnergyDevices(Long energyTypeId, String keyword, Long projectId) {
+        Long pid = resolveProjectId(projectId);
+        if (pid == null) return new EmsReportDevicesResponse();
+        LambdaQueryWrapper<EmsDevice> q = new LambdaQueryWrapper<EmsDevice>().eq(EmsDevice::getProjectId, pid);
+        if (StringUtils.hasText(keyword)) {
+            q.and(w -> w.like(EmsDevice::getName, keyword).or().like(EmsDevice::getNumber, keyword));
+        }
+        List<EmsDevice> devices = emsDeviceMapper.selectList(q);
+        return buildReportDevicesResponse(devices, energyTypeId, null);
+    }
+
+    @Override
+    public EmsEnergyStatisticsResponse getEnergyStatistics(EmsEnergyStatisticsRequest request) {
+        EmsEnergyStatisticsResponse resp = new EmsEnergyStatisticsResponse();
+        if (request != null) {
+            resp.setTimeDimension(request.getTimeDimension());
+            resp.setTimeValue(request.getTimeValue());
+        }
+        return resp;
+    }
+
+    @Override
+    public void exportEnergy(String deviceIds, String attributePointIds, String timeDimension, String timeValue, String format, HttpServletResponse response) {
+        doExport(response, format, "energy_report");
+    }
+
+    @Override
+    public EmsReportDevicesResponse getRegionDevices(Long energyTypeId, Long regionId, String keyword, Long projectId) {
+        Long pid = resolveProjectId(projectId);
+        if (pid == null) return new EmsReportDevicesResponse();
+        LambdaQueryWrapper<EmsDevice> q = new LambdaQueryWrapper<EmsDevice>().eq(EmsDevice::getProjectId, pid);
+        if (regionId != null) q.eq(EmsDevice::getInstallationLocation, regionId);
+        if (StringUtils.hasText(keyword)) {
+            q.and(w -> w.like(EmsDevice::getName, keyword).or().like(EmsDevice::getNumber, keyword));
+        }
+        List<EmsDevice> devices = emsDeviceMapper.selectList(q);
+        return buildReportDevicesResponse(devices, energyTypeId, regionId);
+    }
+
+    @Override
+    public EmsEnergyStatisticsResponse getRegionStatistics(EmsEnergyStatisticsRequest request, List<Long> regionIds) {
+        return getEnergyStatistics(request);
+    }
+
+    @Override
+    public void exportRegion(String deviceIds, String attributePointIds, String timeDimension, String timeValue, String regionIds, String format, HttpServletResponse response) {
+        doExport(response, format, "region_report");
+    }
+
+    @Override
+    public EmsReportDevicesResponse getCollectionDevices(Long energyTypeId, String keyword, Long projectId) {
+        return getEnergyDevices(energyTypeId, keyword, projectId);
+    }
+
+    @Override
+    public EmsCollectionRealtimeResponse getCollectionRealtime(EmsCollectionRealtimeRequest request) {
+        EmsCollectionRealtimeResponse resp = new EmsCollectionRealtimeResponse();
+        return resp;
+    }
+
+    @Override
+    public EmsEnergyStatisticsResponse getCollectionStatistics(EmsEnergyStatisticsRequest request) {
+        return getEnergyStatistics(request);
+    }
+
+    @Override
+    public void exportCollection(String deviceIds, String attributePointIds, String timeDimension, String timeValue, String format, HttpServletResponse response) {
+        doExport(response, format, "collection_report");
+    }
+
+    private EmsReportDevicesResponse buildReportDevicesResponse(List<EmsDevice> devices, Long energyTypeId, Long regionId) {
+        EmsReportDevicesResponse resp = new EmsReportDevicesResponse();
+        String typeName = energyTypeName(energyTypeId);
+        for (EmsDevice d : devices) {
+            EmsReportDeviceItemVO item = new EmsReportDeviceItemVO();
+            item.setId(d.getId());
+            item.setName(d.getName());
+            item.setCode(d.getNumber());
+            item.setEnergyTypeName(typeName);
+            List<EmsDeviceFunction> funcs = emsDeviceFunctionMapper.selectList(
+                    new LambdaQueryWrapper<EmsDeviceFunction>().eq(EmsDeviceFunction::getDeviceId, d.getId()));
+            List<EmsReportAttributeVO> attrs = funcs.stream().map(f -> {
+                EmsReportAttributeVO a = new EmsReportAttributeVO();
+                a.setId(f.getId());
+                a.setName(f.getName());
+                a.setCode(f.getIdentifier());
+                a.setUnit("");
+                return a;
+            }).collect(Collectors.toList());
+            item.setAttributes(attrs);
+            resp.getDevices().add(item);
+        }
+        return resp;
+    }
+
+    private void doExport(HttpServletResponse response, String format, String fileName) {
+        try {
+            if ("csv".equalsIgnoreCase(format != null ? format : "excel")) {
+                response.setContentType("text/csv;charset=UTF-8");
+                response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".csv");
+                try (OutputStream os = response.getOutputStream()) {
+                    os.write("\uFEFF".getBytes(StandardCharsets.UTF_8));
+                    os.write("时间维度,时间值\n".getBytes(StandardCharsets.UTF_8));
+                }
+            } else {
+                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+                response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
+                response.getOutputStream().flush();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("导出失败", e);
+        }
+    }
+}

+ 14 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCategoryRatioItemVO.java

@@ -0,0 +1,14 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class EmsCategoryRatioItemVO {
+
+    private String energyTypeName;
+    private BigDecimal ratio;
+    private BigDecimal usage;
+    private String unit;
+}

+ 17 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCollectionRealtimeItemVO.java

@@ -0,0 +1,17 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+public class EmsCollectionRealtimeItemVO {
+
+    private String deviceId;
+    private Long pointId;
+    private String pointName;
+    private BigDecimal value;
+    private String unit;
+    private LocalDateTime collectedAt;
+}

+ 16 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCollectionRealtimeRequest.java

@@ -0,0 +1,16 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class EmsCollectionRealtimeRequest {
+
+    private List<String> deviceIds;
+    private List<Long> attributePointIds;
+    private String timeDimension;
+    private String timeValue;
+    private String startTime;
+    private String endTime;
+}

+ 12 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCollectionRealtimeResponse.java

@@ -0,0 +1,12 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class EmsCollectionRealtimeResponse {
+
+    private List<EmsCollectionRealtimeItemVO> items = new ArrayList<>();
+}

+ 15 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCompareRequest.java

@@ -0,0 +1,15 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class EmsCompareRequest {
+
+    private Long energyTypeId;
+    private List<String> deviceIds;
+    private String timeDimension;
+    private List<String> timeValues;
+}

+ 14 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCompareResponse.java

@@ -0,0 +1,14 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class EmsCompareResponse {
+
+    private String energyTypeName;
+    private String dimension;
+    private List<EmsCompareSeriesItemVO> series = new ArrayList<>();
+}

+ 14 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCompareSeriesItemVO.java

@@ -0,0 +1,14 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class EmsCompareSeriesItemVO {
+
+    private String deviceId;
+    private String deviceName;
+    private List<EmsCompareValueVO> values = new ArrayList<>();
+}

+ 12 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsCompareValueVO.java

@@ -0,0 +1,12 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class EmsCompareValueVO {
+
+    private String timeValue;
+    private BigDecimal usage;
+}

+ 18 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsEnergyStatisticsItemVO.java

@@ -0,0 +1,18 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class EmsEnergyStatisticsItemVO {
+
+    private String deviceId;
+    private String deviceName;
+    private Long pointId;
+    private String pointName;
+    private String unit;
+    private BigDecimal value;
+    private List<Object> statistics;
+}

+ 14 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsEnergyStatisticsRequest.java

@@ -0,0 +1,14 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class EmsEnergyStatisticsRequest {
+
+    private List<String> deviceIds;
+    private List<Long> attributePointIds;
+    private String timeDimension;
+    private String timeValue;
+}

+ 14 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsEnergyStatisticsResponse.java

@@ -0,0 +1,14 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class EmsEnergyStatisticsResponse {
+
+    private String timeDimension;
+    private String timeValue;
+    private List<EmsEnergyStatisticsItemVO> items = new ArrayList<>();
+}

+ 16 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsEnergyTypeVO.java

@@ -0,0 +1,16 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+/**
+ * 能源类型(电、水、气)
+ */
+@Data
+public class EmsEnergyTypeVO {
+
+    private Long id;
+    private String name;
+    private String code;
+    private String unit;
+    private Integer sortOrder;
+}

+ 24 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsGatewayDetailResponse.java

@@ -0,0 +1,24 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 网关详情响应
+ */
+@Data
+public class EmsGatewayDetailResponse {
+
+    private String id;
+    private String name;
+    private Long spaceId;
+    private String floorName;
+    private String regionName;
+    private String buildingName;
+    private String installLocation;
+    private Integer communicationStatus;
+    private LocalDateTime onlineTime;
+    private LocalDateTime createTime;
+    private LocalDateTime updateTime;
+}

+ 22 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsGatewayListItem.java

@@ -0,0 +1,22 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 网关列表项(字段与 leo.ems_gateway 对应)
+ */
+@Data
+public class EmsGatewayListItem {
+
+    private String id;
+    private String name;
+    private Long spaceId;
+    private String floorName;
+    private String installLocation;
+    private Integer communicationStatus;
+    private LocalDateTime onlineTime;
+    private LocalDateTime createTime;
+    private LocalDateTime updateTime;
+}

+ 21 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsGatewayPageRequest.java

@@ -0,0 +1,21 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+/**
+ * 网关列表分页请求
+ */
+@Data
+public class EmsGatewayPageRequest {
+
+    private Integer current = 1;
+    private Integer size = 10;
+    /** 安装位置(模糊) */
+    private String installLocation;
+    /** 网关名称(模糊) */
+    private String name;
+    /** 网关编码(模糊) */
+    private String code;
+    /** 通讯状态:0-离线 1-在线,不传为全部 */
+    private Integer communicationStatus;
+}

+ 16 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsIdResponse.java

@@ -0,0 +1,16 @@
+package com.usky.ems.service.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 新增接口返回 id(Long 或 String 转 Object)
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class EmsIdResponse {
+
+    private Object id;
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsLoginRequest.java

@@ -0,0 +1,10 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+@Data
+public class EmsLoginRequest {
+
+    private String username;
+    private String password;
+}

+ 10 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsLoginResponse.java

@@ -0,0 +1,10 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+@Data
+public class EmsLoginResponse {
+
+    private String accessToken;
+    private Long expiresIn;
+}

+ 35 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsModelSaveRequest.java

@@ -0,0 +1,35 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 基础建模 - 建筑/区域/楼层/网关/通道/设备/属性点位 新增与编辑请求(通用字段)
+ * 各接口按需使用部分字段
+ */
+@Data
+public class EmsModelSaveRequest {
+
+    private Long projectId;
+    private Long buildingId;
+    private Long regionId;
+    private Long floorId;
+    private String gatewayId;
+    private Long channelId;
+    private String deviceId;
+    private Long energyTypeId;
+
+    private String name;
+    private String code;
+    private BigDecimal area;
+    private Integer floorNumber;
+    private Integer sortOrder;
+    private String installLocation;
+    private String configJson;
+    private Integer channelNo;
+    private String model;
+    private String dataType;
+    private String unit;
+    private String pointAddress;
+}

+ 23 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsProjectResponse.java

@@ -0,0 +1,23 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 项目信息响应(数据总览 - 获取项目信息)
+ * 字段名与 leo.ems_project 一致
+ */
+@Data
+public class EmsProjectResponse {
+
+    private Long id;
+    private String name;
+    private String code;
+    private String description;
+    private BigDecimal area;
+    private Integer residentPopulation;
+    private LocalDateTime createTime;
+    private LocalDateTime updateTime;
+}

+ 18 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsRegionAnalysisItemVO.java

@@ -0,0 +1,18 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class EmsRegionAnalysisItemVO {
+
+    private Long regionId;
+    private String regionName;
+    private BigDecimal area;
+    private BigDecimal totalUsage;
+    private BigDecimal areaUsage;
+    private String unit;
+    private String timeDimension;
+    private String timeValue;
+}

+ 12 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsRegionAnalysisResponse.java

@@ -0,0 +1,12 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class EmsRegionAnalysisResponse {
+
+    private List<EmsRegionAnalysisItemVO> items = new ArrayList<>();
+}

+ 12 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsReportAttributeVO.java

@@ -0,0 +1,12 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+@Data
+public class EmsReportAttributeVO {
+
+    private Long id;
+    private String name;
+    private String code;
+    private String unit;
+}

+ 17 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsReportDeviceItemVO.java

@@ -0,0 +1,17 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class EmsReportDeviceItemVO {
+
+    private String id;
+    private String name;
+    private String code;
+    private String energyTypeName;
+    private String regionName;
+    private List<EmsReportAttributeVO> attributes = new ArrayList<>();
+}

+ 12 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsReportDevicesResponse.java

@@ -0,0 +1,12 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class EmsReportDevicesResponse {
+
+    private List<EmsReportDeviceItemVO> devices = new ArrayList<>();
+}

+ 18 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsStructureTreeNode.java

@@ -0,0 +1,18 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 项目结构树节点(建筑、区域、楼层、网关)
+ */
+@Data
+public class EmsStructureTreeNode {
+
+    private Object id;
+    private String name;
+    private String type;
+    private List<EmsStructureTreeNode> children = new ArrayList<>();
+}

+ 11 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsSummaryRequest.java

@@ -0,0 +1,11 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+@Data
+public class EmsSummaryRequest {
+
+    private Long projectId;
+    private String timeDimension;
+    private String timeValue;
+}

+ 20 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsSummaryResponse.java

@@ -0,0 +1,20 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 项目数据概括响应
+ */
+@Data
+public class EmsSummaryResponse {
+
+    private String timeDimension;
+    private String timeValue;
+    private List<Object> categoryRatio = new ArrayList<>();
+    private List<Object> buildingRanking = new ArrayList<>();
+    private List<Object> usageTrend = new ArrayList<>();
+    private List<Object> subItemRatio = new ArrayList<>();
+}

+ 14 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsTrendCategoryResponse.java

@@ -0,0 +1,14 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class EmsTrendCategoryResponse {
+
+    private String timeDimension;
+    private String timeValue;
+    private List<EmsCategoryRatioItemVO> categoryRatio = new ArrayList<>();
+}

+ 18 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsTrendIndicatorsResponse.java

@@ -0,0 +1,18 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class EmsTrendIndicatorsResponse {
+
+    private String timeDimension;
+    private String timeValue;
+    private BigDecimal totalUsage;
+    private BigDecimal standardCoal;
+    private BigDecimal carbonEmission;
+    private BigDecimal areaUsage;
+    private BigDecimal perCapitaUsage;
+    private Object rating;
+}

+ 14 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsTrendItemVO.java

@@ -0,0 +1,14 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class EmsTrendItemVO {
+
+    private String timeLabel;
+    private BigDecimal usage;
+    private BigDecimal standardCoal;
+    private BigDecimal carbonEmission;
+}

+ 14 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EmsTrendResponse.java

@@ -0,0 +1,14 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class EmsTrendResponse {
+
+    private String timeDimension;
+    private String timeValue;
+    private List<EmsTrendItemVO> trend = new ArrayList<>();
+}

+ 21 - 0
service-sas/pom.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>usky-modules</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-sas</artifactId>
+
+    <packaging>pom</packaging>
+    <version>0.0.1</version>
+
+    <modules>
+        <module>service-sas-biz</module>
+        <module>service-sas-api</module>
+    </modules>
+</project>
+

+ 28 - 0
service-sas/service-sas-api/pom.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-sas</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-sas-api</artifactId>
+    <!-- SpringCloud Openfeign -->
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>usky-common-core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+
+</project>
+

+ 139 - 0
service-sas/service-sas-biz/pom.xml

@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-sas</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-sas-biz</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>common-cloud-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-sas-api</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Pagehelper -->
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-system-api</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+        <!-- Excel & Word 工具 -->
+        <dependency>
+            <groupId>cn.afterturn</groupId>
+            <artifactId>easypoi-spring-boot-starter</artifactId>
+            <version>4.1.0</version>
+        </dependency>
+
+        <!-- MQTT -->
+        <dependency>
+            <groupId>org.eclipse.paho</groupId>
+            <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
+            <version>1.2.5</version>
+        </dependency>
+
+        <!-- 工具类 -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.20</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>ruoyi-common-swagger</artifactId>
+        </dependency>
+
+        <!-- Hutool 用于 AG 接口 HTTP 请求 -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+
+        <!-- JNA -->
+        <dependency>
+            <groupId>net.java.dev.jna</groupId>
+            <artifactId>jna</artifactId>
+            <version>5.13.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.java.dev.jna</groupId>
+            <artifactId>jna-platform</artifactId>
+            <version>5.13.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+            <!-- 建议使用最新稳定版,可到Maven中央仓库确认最新版本号 -->
+            <version>2.0.54</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-websocket</artifactId>
+        </dependency>
+
+        <!-- ZIP 压缩工具 -->
+        <dependency>
+            <groupId>net.lingala.zip4j</groupId>
+            <artifactId>zip4j</artifactId>
+            <version>2.11.5</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.2.6.RELEASE</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>com.github.shalousun</groupId>
+                <artifactId>smart-doc-maven-plugin</artifactId>
+                <version>2.1.1</version>
+                <configuration>
+                    <!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
+                    <configFile>./src/main/resources/smart-doc.json</configFile>
+                    <!--指定项目名称-->
+                    <projectName>sas</projectName>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
+

+ 108 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/MybatisGeneratorUtils.java

@@ -0,0 +1,108 @@
+package com.usky.sas;
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.generator.AutoGenerator;
+import com.baomidou.mybatisplus.generator.InjectionConfig;
+import com.baomidou.mybatisplus.generator.config.*;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Description:
+ * @Author: fu
+ * @Date: 2026/03/01 14:44
+ */
+public class MybatisGeneratorUtils {
+    public static void main(String[] args) {
+
+        shell("service-sas", "service-sas-biz");
+    }
+
+    private static void shell(String parentName, String model) {
+
+        AutoGenerator mpg = new AutoGenerator();
+        // 1、全局配置
+        GlobalConfig gc = new GlobalConfig();
+//        File file = new File(model);
+//        String path = file.getAbsolutePath();
+        String projectPath = System.getProperty("user.dir");
+        projectPath += "/" + parentName;
+        projectPath += "/" + model;
+        gc.setOutputDir(projectPath + "/src/main/java");  // 生成路径(一般都是生成在此项目的src/main/java下面)
+        // 修改为自己的名字
+        gc.setAuthor("fu"); // 设置作者
+        gc.setOpen(false);
+        gc.setFileOverride(true); // 第二次生成会把第一次生成的覆盖掉
+        gc.setServiceName("%sService"); // 生成的service接口名字首字母是否为I,这样设置就没有
+        gc.setBaseResultMap(true); // 生成resultMap
+        mpg.setGlobalConfig(gc);
+
+        // 2、数据源配置
+        // 修改数据源
+        DataSourceConfig dsc = new DataSourceConfig();
+        dsc.setUrl("jdbc:mysql://192.168.10.165:3306/usky-cloud?useUnicode=true&serverTimezone=GMT&useSSL=false&characterEncoding=utf8");
+        dsc.setDriverName("com.mysql.jdbc.Driver");
+        dsc.setUsername("root");
+        dsc.setPassword("yt123456");
+        mpg.setDataSource(dsc);
+
+        // 3、包配置
+        PackageConfig pc = new PackageConfig();
+        pc.setParent("com.usky.sas");
+        pc.setController("controller.web");
+        pc.setEntity("domain");
+        pc.setMapper("mapper");
+        pc.setService("service");
+        pc.setServiceImpl("service.impl");
+//        pc.setXml("mapper.demo");
+        // pc.setModuleName("test");
+        mpg.setPackageInfo(pc);
+
+        // 4、策略配置
+        StrategyConfig strategy = new StrategyConfig();
+        strategy.setNaming(NamingStrategy.underline_to_camel);
+        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
+        strategy.setSuperMapperClass("com.usky.common.mybatis.core.CrudMapper");
+        strategy.setSuperServiceClass("com.usky.common.mybatis.core.CrudService");
+        strategy.setSuperServiceImplClass("com.usky.common.mybatis.core.AbstractCrudService");
+        // strategy.setTablePrefix("t_"); // 表名前缀
+        strategy.setEntityLombokModel(true); // 使用lombok
+        // 修改自己想要生成的表
+        strategy.setInclude("sas_system_type_code");  // 逆向工程使用的表   如果要生成多个,这里可以传入String[]
+        mpg.setStrategy(strategy);
+
+        // 关闭默认 xml 生成,调整生成 至 根目录
+        // 修改对应的模块名称
+        TemplateConfig tc = new TemplateConfig();
+        // 自定义配置
+        InjectionConfig cfg = new InjectionConfig() {
+            @Override
+            public void initMap() {
+                // to do nothing
+            }
+        };
+        // 如果模板引擎是 velocity
+        String templatePath = "/templates/mapper.xml.vm";
+        // 自定义输出配置
+        List<FileOutConfig> focList = new ArrayList<>();
+        // 自定义配置会被优先输出
+        String finalProjectPath = projectPath;
+        focList.add(new FileOutConfig(templatePath) {
+            @Override
+            public String outputFile(TableInfo tableInfo) {
+                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
+                return finalProjectPath + "/src/main/resources/mapper/pm" + "/"
+                        + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
+            }
+        });
+        cfg.setFileOutConfigList(focList);
+        mpg.setCfg(cfg);
+        tc.setXml(null);
+        mpg.setTemplate(tc);
+        // 5、执行
+        mpg.execute();
+    }
+}

+ 47 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/ServiceSasApplication.java

@@ -0,0 +1,47 @@
+package com.usky.sas;
+
+import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
+import org.mybatis.spring.annotation.MapperScan;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.core.env.Environment;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * SAS 模块启动类
+ *
+ * @author han
+ */
+@EnableCustomSwagger2
+@EnableFeignClients(basePackages = "com.usky")
+@MapperScan(value = "com.usky.sas.mapper")
+@ComponentScan("com.usky")
+@SpringBootApplication
+@EnableAsync
+public class ServiceSasApplication {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceSasApplication.class);
+
+    public static void main(String[] args) throws UnknownHostException {
+        ConfigurableApplicationContext application = SpringApplication.run(ServiceSasApplication.class, args);
+        Environment env = application.getEnvironment();
+        String ip = InetAddress.getLocalHost().getHostAddress();
+        String port = env.getProperty("server.port");
+        String path = env.getProperty("server.servlet.context-path");
+        LOGGER.info("\n----------------------------------------------------------\n\t" +
+                "Application is running! Access URLs:\n\t" +
+                "Local: \t\thttp://localhost:" + port + (null == path ? "" : path) + "/\n\t" +
+                "External: \thttp://" + ip + ":" + port + (null == path ? "" : path) + "/\n\t" +
+                "Api: \t\thttp://" + ip + ":" + port + (null == path ? "" : path) + "/swagger-ui/index.html\n\t" +
+                "----------------------------------------------------------");
+    }
+}
+

+ 16 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/StandardOnvifService.java

@@ -0,0 +1,16 @@
+package com.usky.sas.common;
+
+import org.springframework.stereotype.Service;
+
+@Service
+public class StandardOnvifService {
+    public String getNvrRealTimeStreamUrl(String ip, String user, String password, String token) {
+        // Placeholder implementation
+        return String.format("rtsp://%s:%s@%s:554/stream/%s", user, password, ip, token);
+    }
+
+    public String getRealTimeStreamUrl(String ip, String user, String password) {
+        // Placeholder implementation
+        return String.format("rtsp://%s:%s@%s:554/stream", user, password, ip);
+    }
+}

+ 126 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/UnityVideoInfo.java

@@ -0,0 +1,126 @@
+package com.usky.sas.common;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class UnityVideoInfo {
+    @ApiModelProperty("设备mac地址")
+    private String macAddr;
+    @ApiModelProperty("ZLM主动拉流唯一标识")
+    private String key;
+    @ApiModelProperty("视频流唯一标识")
+    private String ssrc;
+    @ApiModelProperty("视频流播放地址")
+    private String url;
+
+    public UnityVideoInfo() {
+    }
+
+    public String getMacAddr() {
+        return this.macAddr;
+    }
+
+    public String getKey() {
+        return this.key;
+    }
+
+    public String getSsrc() {
+        return this.ssrc;
+    }
+
+    public String getUrl() {
+        return this.url;
+    }
+
+    public void setMacAddr(String macAddr) {
+        this.macAddr = macAddr;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof UnityVideoInfo)) {
+            return false;
+        } else {
+            UnityVideoInfo other = (UnityVideoInfo)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$macAddr = this.getMacAddr();
+                Object other$macAddr = other.getMacAddr();
+                if (this$macAddr == null) {
+                    if (other$macAddr != null) {
+                        return false;
+                    }
+                } else if (!this$macAddr.equals(other$macAddr)) {
+                    return false;
+                }
+
+                Object this$key = this.getKey();
+                Object other$key = other.getKey();
+                if (this$key == null) {
+                    if (other$key != null) {
+                        return false;
+                    }
+                } else if (!this$key.equals(other$key)) {
+                    return false;
+                }
+
+                Object this$ssrc = this.getSsrc();
+                Object other$ssrc = other.getSsrc();
+                if (this$ssrc == null) {
+                    if (other$ssrc != null) {
+                        return false;
+                    }
+                } else if (!this$ssrc.equals(other$ssrc)) {
+                    return false;
+                }
+
+                Object this$url = this.getUrl();
+                Object other$url = other.getUrl();
+                if (this$url == null) {
+                    if (other$url != null) {
+                        return false;
+                    }
+                } else if (!this$url.equals(other$url)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(Object other) {
+        return other instanceof UnityVideoInfo;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $macAddr = this.getMacAddr();
+        result = result * 59 + ($macAddr == null ? 43 : $macAddr.hashCode());
+        Object $key = this.getKey();
+        result = result * 59 + ($key == null ? 43 : $key.hashCode());
+        Object $ssrc = this.getSsrc();
+        result = result * 59 + ($ssrc == null ? 43 : $ssrc.hashCode());
+        Object $url = this.getUrl();
+        result = result * 59 + ($url == null ? 43 : $url.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "UnityVideoInfo(macAddr=" + this.getMacAddr() + ", key=" + this.getKey() + ", ssrc=" + this.getSsrc() + ", url=" + this.getUrl() + ")";
+    }
+}

+ 18 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/config/WebSocketConfig.java

@@ -0,0 +1,18 @@
+package com.usky.sas.common.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+/**
+ * WebSocket 配置类
+ * 开启 WebSocket 支持
+ */
+@Configuration
+public class WebSocketConfig {
+
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+}

+ 71 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/DahuaNvrInfo.java

@@ -0,0 +1,71 @@
+package com.usky.sas.common.dahua;
+
+public class DahuaNvrInfo {
+    private Long loginHandle;
+    private int maxChannelNum;
+
+    public DahuaNvrInfo() {
+    }
+
+    public Long getLoginHandle() {
+        return this.loginHandle;
+    }
+
+    public int getMaxChannelNum() {
+        return this.maxChannelNum;
+    }
+
+    public void setLoginHandle(Long loginHandle) {
+        this.loginHandle = loginHandle;
+    }
+
+    public void setMaxChannelNum(int maxChannelNum) {
+        this.maxChannelNum = maxChannelNum;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof DahuaNvrInfo)) {
+            return false;
+        } else {
+            DahuaNvrInfo other = (DahuaNvrInfo)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$loginHandle = this.getLoginHandle();
+                Object other$loginHandle = other.getLoginHandle();
+                if (this$loginHandle == null) {
+                    if (other$loginHandle != null) {
+                        return false;
+                    }
+                } else if (!this$loginHandle.equals(other$loginHandle)) {
+                    return false;
+                }
+
+                if (this.getMaxChannelNum() != other.getMaxChannelNum()) {
+                    return false;
+                } else {
+                    return true;
+                }
+            }
+        }
+    }
+
+    protected boolean canEqual(Object other) {
+        return other instanceof DahuaNvrInfo;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $loginHandle = this.getLoginHandle();
+        result = result * 59 + ($loginHandle == null ? 43 : $loginHandle.hashCode());
+        result = result * 59 + this.getMaxChannelNum();
+        return result;
+    }
+
+    public String toString() {
+        return "DahuaNvrInfo(loginHandle=" + this.getLoginHandle() + ", maxChannelNum=" + this.getMaxChannelNum() + ")";
+    }
+}

+ 126 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/DahuaVideoInfo.java

@@ -0,0 +1,126 @@
+package com.usky.sas.common.dahua;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class DahuaVideoInfo {
+    @ApiModelProperty("设备mac地址")
+    private String macAddr;
+    @ApiModelProperty("视频流句柄")
+    private Long handle;
+    @ApiModelProperty("视频流唯一标识")
+    private String ssrc;
+    @ApiModelProperty("视频流播放地址")
+    private String url;
+
+    public DahuaVideoInfo() {
+    }
+
+    public String getMacAddr() {
+        return this.macAddr;
+    }
+
+    public Long getHandle() {
+        return this.handle;
+    }
+
+    public String getSsrc() {
+        return this.ssrc;
+    }
+
+    public String getUrl() {
+        return this.url;
+    }
+
+    public void setMacAddr(String macAddr) {
+        this.macAddr = macAddr;
+    }
+
+    public void setHandle(Long handle) {
+        this.handle = handle;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof DahuaVideoInfo)) {
+            return false;
+        } else {
+            DahuaVideoInfo other = (DahuaVideoInfo)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$macAddr = this.getMacAddr();
+                Object other$macAddr = other.getMacAddr();
+                if (this$macAddr == null) {
+                    if (other$macAddr != null) {
+                        return false;
+                    }
+                } else if (!this$macAddr.equals(other$macAddr)) {
+                    return false;
+                }
+
+                Object this$handle = this.getHandle();
+                Object other$handle = other.getHandle();
+                if (this$handle == null) {
+                    if (other$handle != null) {
+                        return false;
+                    }
+                } else if (!this$handle.equals(other$handle)) {
+                    return false;
+                }
+
+                Object this$ssrc = this.getSsrc();
+                Object other$ssrc = other.getSsrc();
+                if (this$ssrc == null) {
+                    if (other$ssrc != null) {
+                        return false;
+                    }
+                } else if (!this$ssrc.equals(other$ssrc)) {
+                    return false;
+                }
+
+                Object this$url = this.getUrl();
+                Object other$url = other.getUrl();
+                if (this$url == null) {
+                    if (other$url != null) {
+                        return false;
+                    }
+                } else if (!this$url.equals(other$url)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(Object other) {
+        return other instanceof DahuaVideoInfo;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $macAddr = this.getMacAddr();
+        result = result * 59 + ($macAddr == null ? 43 : $macAddr.hashCode());
+        Object $handle = this.getHandle();
+        result = result * 59 + ($handle == null ? 43 : $handle.hashCode());
+        Object $ssrc = this.getSsrc();
+        result = result * 59 + ($ssrc == null ? 43 : $ssrc.hashCode());
+        Object $url = this.getUrl();
+        result = result * 59 + ($url == null ? 43 : $url.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "DahuaVideoInfo(macAddr=" + this.getMacAddr() + ", handle=" + this.getHandle() + ", ssrc=" + this.getSsrc() + ", url=" + this.getUrl() + ")";
+    }
+}

+ 185 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/DahuaVideoStreamService.java

@@ -0,0 +1,185 @@
+package com.usky.sas.common.dahua;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.usky.sas.common.global.Constant;
+import com.usky.sas.common.global.StreamContext;
+import com.usky.sas.common.util.GetIpUtils;
+import com.usky.sas.service.vo.VideoStreamVo;
+import com.usky.sas.common.exception.BusinessException;
+import com.usky.sas.common.global.GlobalMemoryMap;
+import com.usky.sas.common.entity.DahuaNvrInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static java.lang.Long.toHexString;
+
+@Component
+public class DahuaVideoStreamService {
+    private static final Logger log = LoggerFactory.getLogger(DahuaVideoStreamService.class);
+    static int iPlayBack;
+    static final int RTP_HEADER_SIZE = 12;
+    static final int RTP_VERSION = 2;
+    static final int RTP_PAYLOAD_TYPE = 96;
+    public static final int MAX_RTP_PACKET_SIZE = 1400;
+    static Map<String, NetSDKLib.fDataCallBack> dahuaPlayDataCallBackMap;
+    static Map<String, StreamContext> streamContextMap;
+    static Map<String, DahuaVideoInfo> dahuaVideoInfo;
+
+    public DahuaVideoStreamService() {
+    }
+
+    public static byte[] createRtpPacket(byte[] data, int offset, int size, boolean marker, long timestamp, int sequenceNumber, long ssrc) {
+        byte[] rtpPacket = new byte[12 + size];
+        rtpPacket[0] = -128;
+        rtpPacket[1] = 96;
+        if (marker) {
+            rtpPacket[1] |= -128;
+        }
+
+        rtpPacket[2] = (byte)(sequenceNumber >> 8);
+        rtpPacket[3] = (byte)(sequenceNumber & 255);
+        rtpPacket[4] = (byte)((int)(timestamp >> 24));
+        rtpPacket[5] = (byte)((int)(timestamp >> 16));
+        rtpPacket[6] = (byte)((int)(timestamp >> 8));
+        rtpPacket[7] = (byte)((int)(timestamp & 255L));
+        rtpPacket[8] = (byte)((int)(ssrc >> 24));
+        rtpPacket[9] = (byte)((int)(ssrc >> 16));
+        rtpPacket[10] = (byte)((int)(ssrc >> 8));
+        rtpPacket[11] = (byte)((int)(ssrc & 255L));
+        System.arraycopy(data, offset, rtpPacket, 12, size);
+        return rtpPacket;
+    }
+
+    public static byte[] createTcpPacket(byte[] rtpPacket) {
+        byte[] tcpPacket = new byte[rtpPacket.length + 2];
+        tcpPacket[0] = (byte)(rtpPacket.length >> 8 & 255);
+        tcpPacket[1] = (byte)(rtpPacket.length & 255);
+        System.arraycopy(rtpPacket, 0, tcpPacket, 2, rtpPacket.length);
+        return tcpPacket;
+    }
+
+    public static VideoStreamVo playBackByTime(String oldMac, String macAddr, Long userID, int lChannel, int mediaRtpPort, Integer mediaHttpPort, Date beginTime, Date endTime) {
+        if (StrUtil.isNotBlank(oldMac) && streamContextMap.containsKey(oldMac)) {
+            ((StreamContext)streamContextMap.get(oldMac)).close();
+            streamContextMap.remove(oldMac);
+            dahuaPlayDataCallBackMap.remove(oldMac);
+        }
+
+        StreamContext context = null;
+
+        try {
+            context = new StreamContext(GetIpUtils.getServerIP(), mediaRtpPort);
+            log.info("设备 {} RTP连接已建立: {}:{}", new Object[]{macAddr, GetIpUtils.getServerIP(), mediaRtpPort});
+        } catch (IOException e) {
+            log.error("设备 {} 连接失败: {}", macAddr, e.getMessage());
+            throw new BusinessException("设备 " + macAddr + " 连接失败", -1);
+        }
+
+        String newMac = macAddr + System.currentTimeMillis();
+        Constant.globalSsrc = Constant.globalSsrc >= 4294967295L ? 65535L : Constant.globalSsrc + 1L;
+        context.ssrc = Constant.globalSsrc;
+        streamContextMap.put(newMac, context);
+        FPlayDataCallBackEx playDataCallBack = new FPlayDataCallBackEx(context, newMac);
+        dahuaPlayDataCallBackMap.put(newMac, playDataCallBack);
+        int beginYear = DateUtil.year(beginTime);
+        int beginMonth = DateUtil.month(beginTime) + 1;
+        int beginDay = DateUtil.dayOfMonth(beginTime);
+        int beginHour = DateUtil.hour(beginTime, true);
+        int beginMinute = DateUtil.minute(beginTime);
+        int beginSecond = DateUtil.second(beginTime);
+        NetSDKLib.NET_TIME begin = new NetSDKLib.NET_TIME();
+        begin.setTime(beginYear, beginMonth, beginDay, beginHour, beginMinute, beginSecond);
+        int endYear = DateUtil.year(endTime);
+        int endMonth = DateUtil.month(endTime) + 1;
+        int endDay = DateUtil.dayOfMonth(endTime);
+        int endHour = DateUtil.hour(endTime, true);
+        int endMinute = DateUtil.minute(endTime);
+        int endSecond = DateUtil.second(endTime);
+        NetSDKLib.NET_TIME end = new NetSDKLib.NET_TIME();
+        end.setTime(endYear, endMonth, endDay, endHour, endMinute, endSecond);
+        NetSDKLib.NET_IN_PLAYBACK_BY_DATA_TYPE stIn = new NetSDKLib.NET_IN_PLAYBACK_BY_DATA_TYPE();
+        stIn.emDataType = 1;
+        stIn.nChannelID = lChannel;
+        stIn.stStartTime = begin;
+        stIn.stStopTime = end;
+        stIn.nPlayDirection = 0;
+        stIn.dwPosUser = null;
+        stIn.hWnd = null;
+        stIn.cbDownLoadPos = null;
+        stIn.fDownLoadDataCallBack = playDataCallBack;
+        stIn.fDownLoadDataCallBackEx = null;
+        stIn.dwDataUser = null;
+        NetSDKLib.NET_OUT_PLAYBACK_BY_DATA_TYPE stOut = new NetSDKLib.NET_OUT_PLAYBACK_BY_DATA_TYPE();
+        Long lPlayHandle = InitNetSDKLib.dhNetSDK.CLIENT_PlayBackByDataType(userID, stIn, stOut, 5000);
+        if (lPlayHandle != 0L) {
+            log.info("回放取流成功,回放句柄:{}", lPlayHandle);
+            log.info("取流成功,播放地址:" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + context.ssrc + "&type=play");
+            VideoStreamVo streamVo = new VideoStreamVo();
+            streamVo.setHandle(lPlayHandle);
+            streamVo.setMac(newMac);
+            streamVo.setUrl("http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
+            DahuaVideoInfo videoInfo = new DahuaVideoInfo();
+            videoInfo.setSsrc(toHexString(context.ssrc));
+            videoInfo.setUrl("http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
+            videoInfo.setHandle(lPlayHandle);
+            dahuaVideoInfo.put(newMac, videoInfo);
+            return streamVo;
+        } else {
+            log.info("回放取流失败,原因码:{}", InitNetSDKLib.dhNetSDK.CLIENT_GetLastError());
+            throw new BusinessException("回放失败", -1);
+        }
+    }
+
+    public static void stopPlayStreamData(Long PlayHandle, String macAddr) {
+        if (PlayHandle == 0L) {
+            log.info("回放取流未开启,请先开启回放取流");
+        } else if (!InitNetSDKLib.dhNetSDK.CLIENT_StopPlayBack(PlayHandle)) {
+            log.error("停止取流失败,err:" + InitNetSDKLib.dhNetSDK.CLIENT_GetLastError());
+        } else {
+            log.info("停止取流成功");
+            StreamContext context = (StreamContext)streamContextMap.get(macAddr);
+            if (context != null) {
+                context.close();
+            }
+
+            dahuaPlayDataCallBackMap.remove(macAddr);
+        }
+    }
+
+    public static String toHexString(long number) {
+        if (number == 0L) {
+            return "0";
+        } else {
+            StringBuilder hex = new StringBuilder();
+            char[] hexChars = "0123456789ABCDEF".toCharArray();
+
+            for(long current = number; current != 0L; current >>>= 4) {
+                int digit = (int)(current & 15L);
+                hex.insert(0, hexChars[digit]);
+            }
+
+            if (hex.length() < 8) {
+                int zerosToAdd = 8 - hex.length();
+
+                for(int i = 0; i < zerosToAdd; ++i) {
+                    hex.insert(0, '0');
+                }
+            }
+
+            return hex.toString();
+        }
+    }
+
+    static {
+        dahuaPlayDataCallBackMap = GlobalMemoryMap.dahuaPlayDataCallBackMap;
+        streamContextMap = GlobalMemoryMap.streamContextMap;
+        dahuaVideoInfo = GlobalMemoryMap.dahuaVideoInfo;
+    }
+}

+ 10 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/EM_SEND_SEARCH_TYPE.java

@@ -0,0 +1,10 @@
+package com.usky.sas.common.dahua;
+
+public enum EM_SEND_SEARCH_TYPE {
+    EM_SEND_SEARCH_TYPE_MULTICAST_AND_BROADCAST,
+    EM_SEND_SEARCH_TYPE_MULTICAST,
+    EM_SEND_SEARCH_TYPE_BROADCAST;
+
+    private EM_SEND_SEARCH_TYPE() {
+    }
+}

+ 58 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/FPlayDataCallBackEx.java

@@ -0,0 +1,58 @@
+package com.usky.sas.common.dahua;
+
+import com.usky.sas.common.global.StreamContext;
+import com.sun.jna.Pointer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FPlayDataCallBackEx implements NetSDKLib.fDataCallBack {
+    private static final Logger log = LoggerFactory.getLogger(FPlayDataCallBackEx.class);
+    StreamContext context;
+    String macAddr;
+
+    public FPlayDataCallBackEx(StreamContext context, String macAddr) {
+        this.context = context;
+        this.macAddr = macAddr;
+    }
+
+    public void invoke(Long lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, Pointer dwUser) {
+        try {
+            if (dwBufSize > 2097152) {
+                log.warn("Frame size too large ({}) - truncating to {}", dwBufSize, 2097152);
+                dwBufSize = 2097152;
+            }
+
+            ByteBuffer buffer = pBuffer.getByteBuffer(0L, (long)dwBufSize);
+            buffer.get(this.context.frameBuffer, 0, dwBufSize);
+            long currentTimestamp = (long)this.context.baseTimestamp;
+            StreamContext var10000 = this.context;
+            var10000.baseTimestamp += 3600;
+            int offset = 0;
+
+            int packetSize;
+            for(int remaining = dwBufSize; remaining > 0; remaining -= packetSize) {
+                packetSize = Math.min(remaining, 1400);
+                boolean marker = remaining == packetSize;
+                byte[] rtpPacket = DahuaVideoStreamService.createRtpPacket(this.context.frameBuffer, offset, packetSize, marker, currentTimestamp, this.context.sequenceNumber, this.context.ssrc);
+                byte[] tcpPacket = DahuaVideoStreamService.createTcpPacket(rtpPacket);
+                this.context.sendExecutor.submit(() -> {
+                    try {
+                        this.context.rtpOutputStream.write(tcpPacket);
+                        this.context.rtpOutputStream.flush();
+                    } catch (IOException e) {
+                        log.error("RTP数据发送失败: {}", e.getMessage(), e);
+                    }
+
+                });
+                this.context.sequenceNumber = this.context.sequenceNumber + 1 & '\uffff';
+                offset += packetSize;
+            }
+        } catch (Exception var15) {
+            DahuaVideoStreamService.stopPlayStreamData(lRealHandle, this.macAddr);
+            this.context.close();
+        }
+
+    }
+}

+ 62 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/FRealDataCallBackEx.java

@@ -0,0 +1,62 @@
+package com.usky.sas.common.dahua;
+
+import com.usky.sas.common.global.StreamContext;
+import com.sun.jna.Pointer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FRealDataCallBackEx implements NetSDKLib.fRealDataCallBackEx {
+    private static final Logger log = LoggerFactory.getLogger(FRealDataCallBackEx.class);
+    StreamContext context;
+    String macAddr;
+
+    public FRealDataCallBackEx(StreamContext context, String macAddr) {
+        this.context = context;
+        this.macAddr = macAddr;
+    }
+
+    public void invoke(Long lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, int param, Pointer dwUser) {
+        try {
+            log.info("实时预览回调");
+            if (dwBufSize > 2097152) {
+                log.warn("Frame size too large ({}) - truncating to {}", dwBufSize, 2097152);
+                dwBufSize = 2097152;
+            }
+
+            ByteBuffer buffer = pBuffer.getByteBuffer(0L, (long)dwBufSize);
+            buffer.get(this.context.frameBuffer, 0, dwBufSize);
+            if (dwDataType == 0) {
+                log.info("原始数据");
+                long currentTimestamp = (long)this.context.baseTimestamp;
+                StreamContext var10000 = this.context;
+                var10000.baseTimestamp += 3600;
+                int offset = 0;
+
+                int packetSize;
+                for(int remaining = dwBufSize; remaining > 0; remaining -= packetSize) {
+                    packetSize = Math.min(remaining, 1400);
+                    boolean marker = remaining == packetSize;
+                    byte[] rtpPacket = DahuaVideoStreamService.createRtpPacket(this.context.frameBuffer, offset, packetSize, marker, currentTimestamp, this.context.sequenceNumber, this.context.ssrc);
+                    byte[] tcpPacket = DahuaVideoStreamService.createTcpPacket(rtpPacket);
+                    this.context.sendExecutor.submit(() -> {
+                        try {
+                            this.context.rtpOutputStream.write(tcpPacket);
+                            this.context.rtpOutputStream.flush();
+                        } catch (IOException e) {
+                            log.error("RTP数据发送失败: {}", e.getMessage(), e);
+                        }
+
+                    });
+                    this.context.sequenceNumber = this.context.sequenceNumber + 1 & '\uffff';
+                    offset += packetSize;
+                }
+            }
+        } catch (Exception e) {
+            log.error("设备 {} 数据发送失败: {}", this.macAddr, e.getMessage());
+            this.context.close();
+        }
+
+    }
+}

+ 254 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/InitNetSDKLib.java

@@ -0,0 +1,254 @@
+package com.usky.sas.common.dahua;
+
+import cn.hutool.core.util.StrUtil;
+import com.usky.sas.common.exception.BusinessException;
+import com.usky.sas.common.global.GlobalMemoryMap;
+import com.usky.sas.common.hik.OsSelect;
+import com.usky.sas.service.impl.SasDeviceServiceImpl;
+import com.sun.jna.Callback;
+import com.sun.jna.Memory;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InitNetSDKLib {
+    private static final Logger log = LoggerFactory.getLogger(InitNetSDKLib.class);
+    static Map<String, DahuaNvrInfo> dhNvrUser;
+    public static Map<String, NetSDKLib> dhSDKHashMap;
+    public static NetSDKLib dhNetSDK;
+
+    public InitNetSDKLib() {
+    }
+
+    private static boolean createSDKInstance() {
+        if (dhNetSDK == null) {
+            synchronized(NetSDKLib.class) {
+                String strDllPath = "";
+
+                try {
+                    if (OsSelect.isWindows()) {
+                        strDllPath = "D:\\文档和图片\\工作文档\\厂家协议\\海康\\设备网络SDK_JAVA_Win64_IS_V3.060.0000003.0.R.251127\\General_NetSDK_ChnEng_JAVA_Win64_IS_V3.060.0000003.0.R.251127\\libs\\win64\\dhnetsdk.dll";
+                    } else if (OsSelect.isLinux()) {
+                        strDllPath = "/home/uskycloud_c01/conf/libdhnetsdk.so";
+                    }
+
+                    dhNetSDK = (NetSDKLib)Native.loadLibrary(strDllPath, NetSDKLib.class);
+                    log.info("初始化NetSDKLib成功============");
+                    dhNetSDK.CLIENT_Init((Callback)null, (Pointer)null);
+                    dhNetSDK.CLIENT_SetAutoReconnect((Callback)null, (Pointer)null);
+                    dhNetSDK.CLIENT_SetConnectTime(10000, 1);
+                    dhSDKHashMap.put("dhNetSDK", dhNetSDK);
+                    NetSDKLib.LOG_SET_PRINT_INFO setLog = new NetSDKLib.LOG_SET_PRINT_INFO();
+                    File path = new File("./dhsdklog/");
+                    if (!path.exists()) {
+                        path.mkdir();
+                    }
+
+                    String logPath = path.getAbsoluteFile().getParent() + "\\dhsdklog\\" + getDate() + ".log";
+                    setLog.nPrintStrategy = 0;
+                    setLog.bSetFilePath = 1;
+                    System.arraycopy(logPath.getBytes(), 0, setLog.szLogFilePath, 0, logPath.getBytes().length);
+                    setLog.bSetPrintStrategy = 1;
+                    boolean bLogopen = dhNetSDK.CLIENT_LogOpen(setLog);
+                    if (!bLogopen) {
+                        log.error("打开日志失败");
+                    }
+                } catch (Exception ex) {
+                    log.error("初始化失败,loadLibrary: " + strDllPath + " Error: " + ex.getMessage());
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public static String getDate() {
+        SimpleDateFormat simpleDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String date = simpleDate.format(new Date()).replace(" ", "_").replace(":", "-");
+        return date;
+    }
+
+    public static Long login(String m_strIp, int m_nPort, String m_strUser, String m_strPassword) {
+        NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
+        pstInParam.nPort = m_nPort;
+        pstInParam.szIP = m_strIp.getBytes();
+        pstInParam.szPassword = m_strPassword.getBytes();
+        pstInParam.szUserName = m_strUser.getBytes();
+        NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();
+        Long m_hLoginHandle = dhNetSDK.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
+        if (m_hLoginHandle == 0L) {
+            log.error("设备:{}:{}登录失败,原因:{}", new Object[]{m_strIp, m_nPort, dhNetSDK.CLIENT_GetLastError()});
+            throw new BusinessException("请检查设备IP、端口、用户名、密码等配置是否正确!", -1);
+        } else {
+            log.info("设备:{}:{}登陆成功,句柄:{}", new Object[]{m_strIp, m_nPort, m_hLoginHandle});
+            return m_hLoginHandle;
+        }
+    }
+
+    public static DahuaNvrInfo nvrLogin(String m_strIp, int m_nPort, String m_strUser, String m_strPassword, String mac) {
+        NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
+        pstInParam.nPort = m_nPort;
+        pstInParam.szIP = m_strIp.getBytes();
+        pstInParam.szPassword = m_strPassword.getBytes();
+        pstInParam.szUserName = m_strUser.getBytes();
+        NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();
+        Long m_hLoginHandle = dhNetSDK.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
+        if (m_hLoginHandle == 0L) {
+            log.error("设备:{}:{}登录失败,原因:{}", new Object[]{m_strIp, m_nPort, dhNetSDK.CLIENT_GetLastError()});
+            throw new BusinessException("请检查设备IP、端口、用户名、密码等配置是否正确!", -1);
+        } else {
+            log.info("设备:{}:{}登陆成功,句柄:{}", new Object[]{m_strIp, m_nPort, m_hLoginHandle});
+            DahuaNvrInfo dahuaNvrInfo = new DahuaNvrInfo();
+            dahuaNvrInfo.setLoginHandle(m_hLoginHandle);
+            dahuaNvrInfo.setMaxChannelNum(pstOutParam.stuDeviceInfo.byChanNum);
+            dhNvrUser.put(mac, dahuaNvrInfo);
+            return dahuaNvrInfo;
+        }
+    }
+
+    public static List<RemoteDeviceInfo> getNvrChannel(String m_strIp, int m_nPort, String m_strUser, String m_strPassword) {
+        NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
+        pstInParam.nPort = m_nPort;
+        pstInParam.szIP = m_strIp.getBytes();
+        pstInParam.szPassword = m_strPassword.getBytes();
+        pstInParam.szUserName = m_strUser.getBytes();
+        NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();
+        Long m_hLoginHandle = dhNetSDK.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
+        if (m_hLoginHandle == 0L) {
+            log.error("设备:{}:{}登录失败,原因:{}", new Object[]{m_strIp, m_nPort, dhNetSDK.CLIENT_GetLastError()});
+            throw new BusinessException("请检查NVR设备IP、端口、用户名、密码等配置是否正确!", -1);
+        } else {
+            log.info("设备:{}:{}登陆成功,句柄:{}", new Object[]{m_strIp, m_nPort, m_hLoginHandle});
+            DahuaNvrInfo dahuaNvrInfo = new DahuaNvrInfo();
+            dahuaNvrInfo.setLoginHandle(m_hLoginHandle);
+            dahuaNvrInfo.setMaxChannelNum(pstOutParam.stuDeviceInfo.byChanNum);
+            dhNvrUser.put(m_strIp, dahuaNvrInfo);
+            List<RemoteDeviceInfo> list = new ArrayList();
+            int byChanNum = pstOutParam.stuDeviceInfo.byChanNum;
+            if (byChanNum != 0) {
+                NetSDKLib.NET_MATRIX_CAMERA_INFO[] cameras = new NetSDKLib.NET_MATRIX_CAMERA_INFO[byChanNum];
+
+                for(int i = 0; i < byChanNum; ++i) {
+                    NetSDKLib.NET_MATRIX_CAMERA_INFO camera = new NetSDKLib.NET_MATRIX_CAMERA_INFO();
+                    NetSDKLib.NET_REMOTE_DEVICE device = new NetSDKLib.NET_REMOTE_DEVICE();
+                    camera.stuRemoteDevice = device;
+                    cameras[i] = camera;
+                }
+
+                NetSDKLib.NET_IN_MATRIX_GET_CAMERAS inMatrixGetCameras = new NetSDKLib.NET_IN_MATRIX_GET_CAMERAS();
+                inMatrixGetCameras.dwSize = inMatrixGetCameras.size();
+                NetSDKLib.NET_OUT_MATRIX_GET_CAMERAS outMatrixGetCameras = new NetSDKLib.NET_OUT_MATRIX_GET_CAMERAS();
+                outMatrixGetCameras.dwSize = outMatrixGetCameras.size();
+                outMatrixGetCameras.nMaxCameraCount = byChanNum;
+                outMatrixGetCameras.pstuCameras = new Memory((long)(cameras[0].size() * byChanNum));
+                outMatrixGetCameras.pstuCameras.clear((long)(cameras[0].size() * byChanNum));
+                SetStructArrToPointerData(cameras, outMatrixGetCameras.pstuCameras);
+                boolean getCameras = dhNetSDK.CLIENT_MatrixGetCameras(m_hLoginHandle, inMatrixGetCameras, outMatrixGetCameras, 5000);
+                if (getCameras) {
+                    GetPointerDataToStructArr(outMatrixGetCameras.pstuCameras, cameras);
+
+                    for(int i = 0; i < outMatrixGetCameras.nRetCameraCount; ++i) {
+                        int isRemoteDevice = cameras[i].bRemoteDevice;
+                        if (isRemoteDevice == 1) {
+                            NetSDKLib.NET_REMOTE_DEVICE remoteDevice = cameras[i].stuRemoteDevice;
+                            String deviceName = (new String(remoteDevice.szDevName)).trim();
+                            String ipAddr = (new String(remoteDevice.szIp)).trim();
+                            if (StrUtil.isNotBlank(ipAddr) && !ipAddr.equals("0.0.0.0") && !ipAddr.equals("192.168.0.0") && StrUtil.isNotBlank(deviceName)) {
+                                RemoteDeviceInfo info = new RemoteDeviceInfo();
+                                info.setChannel(cameras[i].nUniqueChannel + 1);
+                                info.setIpAddr(ipAddr);
+                                info.setNote(deviceName);
+                                list.add(info);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return list;
+        }
+    }
+
+    public static Integer getIpcChannel(Long loginHandle, String ipAddr, int byChanNum) {
+        if (byChanNum != 0) {
+            NetSDKLib.NET_MATRIX_CAMERA_INFO[] cameras = new NetSDKLib.NET_MATRIX_CAMERA_INFO[byChanNum];
+
+            for(int i = 0; i < byChanNum; ++i) {
+                NetSDKLib.NET_MATRIX_CAMERA_INFO camera = new NetSDKLib.NET_MATRIX_CAMERA_INFO();
+                NetSDKLib.NET_REMOTE_DEVICE device = new NetSDKLib.NET_REMOTE_DEVICE();
+                camera.stuRemoteDevice = device;
+                cameras[i] = camera;
+            }
+
+            NetSDKLib.NET_IN_MATRIX_GET_CAMERAS inMatrixGetCameras = new NetSDKLib.NET_IN_MATRIX_GET_CAMERAS();
+            inMatrixGetCameras.dwSize = inMatrixGetCameras.size();
+            NetSDKLib.NET_OUT_MATRIX_GET_CAMERAS outMatrixGetCameras = new NetSDKLib.NET_OUT_MATRIX_GET_CAMERAS();
+            outMatrixGetCameras.dwSize = outMatrixGetCameras.size();
+            outMatrixGetCameras.nMaxCameraCount = byChanNum;
+            outMatrixGetCameras.pstuCameras = new Memory((long)(cameras[0].size() * byChanNum));
+            outMatrixGetCameras.pstuCameras.clear((long)(cameras[0].size() * byChanNum));
+            SetStructArrToPointerData(cameras, outMatrixGetCameras.pstuCameras);
+            boolean getCameras = dhNetSDK.CLIENT_MatrixGetCameras(loginHandle, inMatrixGetCameras, outMatrixGetCameras, 5000);
+            if (getCameras) {
+                GetPointerDataToStructArr(outMatrixGetCameras.pstuCameras, cameras);
+
+                for(int i = 0; i < outMatrixGetCameras.nRetCameraCount; ++i) {
+                    int isRemoteDevice = cameras[i].bRemoteDevice;
+                    if (isRemoteDevice == 1) {
+                        NetSDKLib.NET_REMOTE_DEVICE remoteDevice = cameras[i].stuRemoteDevice;
+                        String ip = (new String(remoteDevice.szIp)).trim();
+                        if (ip.equals(ipAddr)) {
+                            return cameras[i].nUniqueChannel;
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public static void SetStructArrToPointerData(Structure[] pJavaStuArr, Pointer pNativeData) {
+        long offset = 0L;
+
+        for(int i = 0; i < pJavaStuArr.length; ++i) {
+            SasDeviceServiceImpl.SetStructDataToPointer(pJavaStuArr[i], pNativeData, offset);
+            offset += (long)pJavaStuArr[i].size();
+        }
+
+    }
+
+    public static void GetPointerDataToStruct(Pointer pNativeData, long OffsetOfpNativeData, Structure pJavaStu) {
+        pJavaStu.write();
+        Pointer pJavaMem = pJavaStu.getPointer();
+        pJavaMem.write(0L, pNativeData.getByteArray(OffsetOfpNativeData, pJavaStu.size()), 0, pJavaStu.size());
+        pJavaStu.read();
+    }
+
+    public static void GetPointerDataToStructArr(Pointer pNativeData, Structure[] pJavaStuArr) {
+        long offset = 0L;
+
+        for(int i = 0; i < pJavaStuArr.length; ++i) {
+            GetPointerDataToStruct(pNativeData, offset, pJavaStuArr[i]);
+            offset += (long)pJavaStuArr[i].size();
+        }
+
+    }
+
+    static {
+        dhNvrUser = GlobalMemoryMap.dhNvrUser;
+        dhSDKHashMap = new ConcurrentHashMap();
+        dhNetSDK = (NetSDKLib)dhSDKHashMap.get("dhNetSDK");
+        createSDKInstance();
+    }
+}

+ 13 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/NET_BRIDGE_NET_CARDS_MAC_LIST.java

@@ -0,0 +1,13 @@
+package com.usky.sas.common.dahua;
+
+import com.sun.jna.Structure;
+
+public class NET_BRIDGE_NET_CARDS_MAC_LIST extends Structure {
+    public byte[] szNetCardName = new byte[32];
+    public byte[] szNetCardMac = new byte[18];
+    public byte[] szReserved = new byte[14];
+
+    public NET_BRIDGE_NET_CARDS_MAC_LIST() {
+    }
+}
+

+ 24 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/NET_IN_STARTSERACH_DEVICE.java

@@ -0,0 +1,24 @@
+package com.usky.sas.common.dahua;
+
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import java.util.Arrays;
+import java.util.List;
+
+public class NET_IN_STARTSERACH_DEVICE extends Structure {
+    public int dwSize;
+    public byte[] szLocalIp = new byte[64];
+    public NetSDKLib.fSearchDevicesCBEx cbSearchDevices;
+    public Pointer pUserData;
+    public int emSendType;
+
+    public NET_IN_STARTSERACH_DEVICE() {
+        // 必须在数组字段初始化之后再设置 size
+        this.dwSize = this.size();
+    }
+
+    @Override
+    protected List<String> getFieldOrder() {
+        return Arrays.asList("dwSize", "szLocalIp", "cbSearchDevices", "pUserData", "emSendType");
+    }
+}

+ 16 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/NET_OUT_STARTSERACH_DEVICE.java

@@ -0,0 +1,16 @@
+package com.usky.sas.common.dahua;
+
+import com.sun.jna.Structure;
+import java.util.Arrays;
+import java.util.List;
+
+public class NET_OUT_STARTSERACH_DEVICE extends Structure {
+    public int dwSize = this.size();
+
+    public NET_OUT_STARTSERACH_DEVICE() {
+    }
+
+    protected List<String> getFieldOrder() {
+        return Arrays.asList("dwSize");
+    }
+}

+ 575 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/NetSDKLib.java

@@ -0,0 +1,575 @@
+package com.usky.sas.common.dahua;
+
+import com.sun.jna.Callback;
+import com.sun.jna.Library;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import java.util.Arrays;
+import java.util.List;
+
+public interface NetSDKLib extends Library {
+    int NET_SERIALNO_LEN = 48;
+    int MAX_LOG_PATH_LEN = 260;
+    int NET_MAX_SAERCH_IP_NUM = 256;
+    int NET_COMMON_STRING_64 = 64;
+    int NET_USER_NAME_LENGTH_EX = 16;
+    int NET_MACHINE_NAME_NUM = 64;
+    int NET_MACADDR_LEN = 40;
+    int NET_DEV_TYPE_LEN = 32;
+    int NET_DEV_SERIALNO_LEN = 48;
+    int NET_MAX_URL_LEN = 128;
+    int NET_MAX_STRING_LEN = 128;
+    int NET_DEV_ID_LEN_EX = 128;
+    int NET_DEV_NAME_LEN = 128;
+    int NET_COMMON_STRING_512 = 512;
+    int NET_DEV_CLASS_LEN = 16;
+    int NET_MAX_IPADDR_LEN = 16;
+    int NET_COMMON_STRING_128 = 128;
+    int NET_COMMON_STRING_32 = 32;
+
+    boolean CLIENT_Init(Callback var1, Pointer var2);
+
+    void CLIENT_Cleanup();
+
+    void CLIENT_SetAutoReconnect(Callback var1, Pointer var2);
+
+    int CLIENT_GetLastError();
+
+    void CLIENT_SetConnectTime(int var1, int var2);
+
+    void CLIENT_SetNetworkParam(NET_PARAM var1);
+
+    Long CLIENT_LoginWithHighLevelSecurity(NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY var1, NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY var2);
+
+    boolean CLIENT_Logout(Long var1);
+
+    Long CLIENT_StartSearchDevicesEx(Pointer var1, Pointer var2);
+
+    boolean CLIENT_SearchDevicesByIPs(DEVICE_IP_SEARCH_INFO var1, Callback var2, Pointer var3, String var4, int var5);
+
+    boolean CLIENT_StopSearchDevices(Long var1);
+
+    boolean CLIENT_LogOpen(LOG_SET_PRINT_INFO var1);
+
+    boolean CLIENT_GetChannelInfo(Long var1, NET_IN_GET_CHANNEL_INFO var2, NET_OUT_GET_CHANNEL_INFO var3, int var4);
+
+    Long CLIENT_StartRealPlay(Long var1, int var2, Pointer var3, int var4, Callback var5, Callback var6, Pointer var7, int var8);
+
+    boolean CLIENT_StopRealPlay(Long var1);
+
+    Long CLIENT_PlayBackByTimeEx(Long var1, int var2, NET_TIME var3, NET_TIME var4, Pointer var5, Callback var6, Pointer var7, Callback var8, Pointer var9);
+
+    Long CLIENT_PlayBackByDataType(Long var1, NET_IN_PLAYBACK_BY_DATA_TYPE var2, NET_OUT_PLAYBACK_BY_DATA_TYPE var3, int var4);
+
+    boolean CLIENT_FastPlayBack(Long var1);
+
+    boolean CLIENT_SlowPlayBack(Long var1);
+
+    boolean CLIENT_SetPlayBackSpeed(Long var1, int var2);
+
+    boolean CLIENT_MatrixGetCameras(Long var1, NET_IN_MATRIX_GET_CAMERAS var2, NET_OUT_MATRIX_GET_CAMERAS var3, int var4);
+
+    boolean CLIENT_SetDeviceMode(Long var1, int var2, Pointer var3);
+
+    boolean CLIENT_StopPlayBack(Long var1);
+
+    public static class NET_REMOTE_DEVICE extends Structure {
+        public int dwSize = this.size();
+        public int bEnable;
+        public byte[] szIp = new byte[16];
+        public byte[] szUser = new byte[8];
+        public byte[] szPwd = new byte[8];
+        public int nPort;
+        public int nDefinition;
+        public int emProtocol;
+        public byte[] szDevName = new byte[64];
+        public int nVideoInputChannels;
+        public int nAudioInputChannels;
+        public byte[] szDevClass = new byte[32];
+        public byte[] szDevType = new byte[32];
+        public int nHttpPort;
+        public int nMaxVideoInputCount;
+        public int nRetVideoInputCount;
+        public Pointer pstuVideoInputs;
+        public byte[] szMachineAddress = new byte[256];
+        public byte[] szSerialNo = new byte[48];
+        public int nRtspPort;
+        public byte[] szUserEx = new byte[32];
+        public byte[] szPwdEx = new byte[32];
+        public byte[] szVendorAbbr = new byte[32];
+        public byte[] szSoftwareVersion = new byte[64];
+        public NET_TIME stuActivationTime;
+        public byte[] szMac = new byte[20];
+        public int nHttpsPort;
+        public byte[] byReserved = new byte[4];
+        public Pointer pstuRemoteDevEx;
+
+        public NET_REMOTE_DEVICE() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "bEnable", "szIp", "szUser", "szPwd", "nPort", "nDefinition", "emProtocol", "szDevName", "nVideoInputChannels", "nAudioInputChannels", "szDevClass", "szDevType", "nHttpPort", "nMaxVideoInputCount", "nRetVideoInputCount", "pstuVideoInputs", "szMachineAddress", "szSerialNo", "nRtspPort", "szUserEx", "szPwdEx", "szVendorAbbr", "szSoftwareVersion", "stuActivationTime", "szMac", "nHttpsPort", "byReserved", "pstuRemoteDevEx");
+        }
+    }
+
+    public static class NET_MATRIX_CAMERA_INFO extends Structure {
+        public int dwSize = this.size();
+        public byte[] szName = new byte[128];
+        public byte[] szDevID = new byte[128];
+        public byte[] szszControlID = new byte[128];
+        public int nChannelID;
+        public int nUniqueChannel;
+        public int bRemoteDevice;
+        public NET_REMOTE_DEVICE stuRemoteDevice = new NET_REMOTE_DEVICE();
+        public int emStreamType;
+        public int emChannelType;
+        public int bEnable;
+        public int emVideoStream;
+
+        public NET_MATRIX_CAMERA_INFO() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "szName", "szDevID", "szszControlID", "nChannelID", "nUniqueChannel", "bRemoteDevice", "stuRemoteDevice", "emStreamType", "emChannelType", "bEnable", "emVideoStream");
+        }
+    }
+
+    public static class NET_IN_MATRIX_GET_CAMERAS extends Structure {
+        public int dwSize = this.size();
+
+        public NET_IN_MATRIX_GET_CAMERAS() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize");
+        }
+    }
+
+    public static class NET_OUT_MATRIX_GET_CAMERAS extends Structure {
+        public int dwSize = this.size();
+        public Pointer pstuCameras;
+        public int nMaxCameraCount;
+        public int nRetCameraCount;
+
+        public NET_OUT_MATRIX_GET_CAMERAS() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "pstuCameras", "nMaxCameraCount", "nRetCameraCount");
+        }
+    }
+
+    public static class EM_REAL_DATA_TYPE extends Structure {
+        public static final int EM_REAL_DATA_TYPE_PRIVATE = 0;
+        public static final int EM_REAL_DATA_TYPE_GBPS = 1;
+        public static final int EM_REAL_DATA_TYPE_TS = 2;
+        public static final int EM_REAL_DATA_TYPE_MP4 = 3;
+        public static final int EM_REAL_DATA_TYPE_H264 = 4;
+        public static final int EM_REAL_DATA_TYPE_FLV_STREAM = 5;
+
+        public EM_REAL_DATA_TYPE() {
+        }
+    }
+
+    public static class NET_IN_PLAYBACK_BY_DATA_TYPE extends Structure {
+        public int dwSize = this.size();
+        public int nChannelID;
+        public NET_TIME stStartTime;
+        public NET_TIME stStopTime;
+        public Pointer hWnd;
+        public Callback cbDownLoadPos;
+        public Pointer dwPosUser;
+        public Callback fDownLoadDataCallBack;
+        public int emDataType;
+        public Pointer dwDataUser;
+        public int nPlayDirection;
+        public int emAudioType;
+        public Callback fDownLoadDataCallBackEx;
+
+        public NET_IN_PLAYBACK_BY_DATA_TYPE() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "nChannelID", "stStartTime", "stStopTime", "hWnd", "cbDownLoadPos", "dwPosUser", "fDownLoadDataCallBack", "emDataType", "dwDataUser", "nPlayDirection", "emAudioType", "fDownLoadDataCallBackEx");
+        }
+    }
+
+    public static class NET_OUT_PLAYBACK_BY_DATA_TYPE extends Structure {
+        public int dwSize = this.size();
+
+        public NET_OUT_PLAYBACK_BY_DATA_TYPE() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize");
+        }
+    }
+
+    public static class NET_IN_GET_CHANNEL_INFO extends Structure {
+        public int dwSize = this.size();
+        public byte[] szDeviceID = new byte[128];
+
+        public NET_IN_GET_CHANNEL_INFO() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "szDeviceID");
+        }
+    }
+
+    public static class NET_OUT_GET_CHANNEL_INFO extends Structure {
+        public int dwSize = this.size();
+        public int nMaxCount;
+        public int nRetCount;
+        public Pointer pstuChannelInfo;
+
+        public NET_OUT_GET_CHANNEL_INFO() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "nMaxCount", "nRetCount", "pstuChannelInfo");
+        }
+    }
+
+    public static class NET_GET_CHANNEL_INFO extends Structure {
+        public int nRemoteChannel;
+        public int nLogicChannel;
+        public byte[] szName = new byte[128];
+        public byte[] szDetail = new byte[512];
+        public byte[] szDeviceType = new byte[64];
+        public byte[] szDeviceClass = new byte[16];
+        public byte[] szIP = new byte[16];
+        public byte[] szMac = new byte[40];
+        public byte[] szSerialNo = new byte[48];
+        public byte[] szDevSoftVersion = new byte[128];
+        public int nVideoInputCh;
+        public int nVideoOutputCh;
+        public int nAudioInputCh;
+        public int nAudioOutputCh;
+        public int nAlarmInputCh;
+        public int nAlarmOutputCh;
+        public byte byOnline;
+        public byte byUsed;
+        public byte[] byReserved = new byte[510];
+
+        public NET_GET_CHANNEL_INFO() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("nRemoteChannel", "nLogicChannel", "szName", "szDetail", "szDeviceType", "szDeviceClass", "szIP", "szMac", "szSerialNo", "szDevSoftVersion", "nVideoInputCh", "nVideoOutputCh", "nAudioInputCh", "nAudioOutputCh", "nAlarmInputCh", "nAlarmOutputCh", "byOnline", "byUsed", "byReserved");
+        }
+    }
+
+    public static class NET_PARAM extends Structure {
+        public int nWaittime;
+        public int nConnectTime;
+        public int nConnectTryNum;
+        public int nSubConnectSpaceTime;
+        public int nGetDevInfoTime;
+        public int nConnectBufSize;
+        public int nGetConnInfoTime;
+        public int nSearchRecordTime;
+        public int nsubDisconnetTime;
+        public byte byNetType;
+        public byte byPlaybackBufSize;
+        public byte bDetectDisconnTime;
+        public byte bKeepLifeInterval;
+        public int nPicBufSize;
+        public short wBSIDLowPowerSubDisconnTime;
+        public byte[] bReserved = new byte[2];
+
+        public NET_PARAM() {
+        }
+    }
+
+    public static class EM_USEDEV_MODE extends Structure {
+        public static final int NET_TALK_CLIENT_MODE = 0;
+        public static final int NET_TALK_SERVER_MODE = 1;
+        public static final int NET_TALK_ENCODE_TYPE = 2;
+        public static final int NET_ALARM_LISTEN_MODE = 3;
+        public static final int NET_CONFIG_AUTHORITY_MODE = 4;
+        public static final int NET_TALK_TALK_CHANNEL = 5;
+        public static final int NET_RECORD_STREAM_TYPE = 6;
+        public static final int NET_TALK_SPEAK_PARAM = 7;
+        public static final int NET_RECORD_TYPE = 8;
+        public static final int NET_TALK_MODE3 = 9;
+        public static final int NET_PLAYBACK_REALTIME_MODE = 10;
+        public static final int NET_TALK_TRANSFER_MODE = 11;
+        public static final int NET_TALK_VT_PARAM = 12;
+        public static final int NET_TARGET_DEV_ID = 13;
+
+        public EM_USEDEV_MODE() {
+        }
+    }
+
+    public static class NET_TIME extends Structure {
+        public int dwYear;
+        public int dwMonth;
+        public int dwDay;
+        public int dwHour;
+        public int dwMinute;
+        public int dwSecond;
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwYear", "dwMonth", "dwDay", "dwHour", "dwMinute", "dwSecond");
+        }
+
+        public NET_TIME() {
+            this.dwYear = 0;
+            this.dwMonth = 0;
+            this.dwDay = 0;
+            this.dwHour = 0;
+            this.dwMinute = 0;
+            this.dwSecond = 0;
+        }
+
+        public void setTime(int year, int month, int day, int hour, int minute, int second) {
+            this.dwYear = year;
+            this.dwMonth = month;
+            this.dwDay = day;
+            this.dwHour = hour;
+            this.dwMinute = minute;
+            this.dwSecond = second;
+        }
+
+        public NET_TIME(NET_TIME other) {
+            this.dwYear = other.dwYear;
+            this.dwMonth = other.dwMonth;
+            this.dwDay = other.dwDay;
+            this.dwHour = other.dwHour;
+            this.dwMinute = other.dwMinute;
+            this.dwSecond = other.dwSecond;
+        }
+
+        public String toStringTime() {
+            return String.format("%02d/%02d/%02d %02d:%02d:%02d", this.dwYear, this.dwMonth, this.dwDay, this.dwHour, this.dwMinute, this.dwSecond);
+        }
+
+        public String toStringTimeEx() {
+            return String.format("%02d-%02d-%02d %02d:%02d:%02d", this.dwYear, this.dwMonth, this.dwDay, this.dwHour, this.dwMinute, this.dwSecond);
+        }
+
+        public String toString() {
+            return String.format("%02d%02d%02d%02d%02d%02d", this.dwYear, this.dwMonth, this.dwDay, this.dwHour, this.dwMinute, this.dwSecond);
+        }
+    }
+
+    public static class NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY extends Structure {
+        public int dwSize = this.size();
+        public byte[] szIP = new byte[64];
+        public int nPort;
+        public byte[] szUserName = new byte[64];
+        public byte[] szPassword = new byte[64];
+        public int emSpecCap;
+        public byte[] byReserved = new byte[4];
+        public Pointer pCapParam;
+        public int emTLSCap;
+        public byte[] szLocalIP = new byte[64];
+
+        public NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "szIP", "nPort", "szUserName", "szPassword", "emSpecCap", "byReserved", "pCapParam", "emTLSCap", "szLocalIP");
+        }
+    }
+
+    public static class DEVICE_NET_INFO_EX2 extends Structure {
+        public DEVICE_NET_INFO_EX stuDevInfo;
+        public byte[] szLocalIP = new byte[64];
+        public byte[] szDeviceSubClass = new byte[16];
+        public byte[] szSID = new byte[32];
+        public byte byRole;
+        public byte[] szReserved = new byte[3];
+        public int nBridgeNetCardsMacListNum;
+        public NET_BRIDGE_NET_CARDS_MAC_LIST[] stuBridgeNetCardsMacList = new NET_BRIDGE_NET_CARDS_MAC_LIST[64];
+        public byte[] cReserved = new byte[1992];
+
+        public DEVICE_NET_INFO_EX2() {
+        }
+    }
+
+    public static class NET_DEVICEINFO_Ex extends Structure {
+        public byte[] sSerialNumber = new byte[48];
+        public int byAlarmInPortNum;
+        public int byAlarmOutPortNum;
+        public int byDiskNum;
+        public int byDVRType;
+        public int byChanNum;
+        public byte byLimitLoginTime;
+        public byte byLeftLogTimes;
+        public byte[] bReserved = new byte[2];
+        public int byLockLeftTime;
+        public byte[] Reserved = new byte[4];
+        public int nNTlsPort;
+        public byte[] Reserved2 = new byte[16];
+
+        public NET_DEVICEINFO_Ex() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("sSerialNumber", "byAlarmInPortNum", "byAlarmOutPortNum", "byDiskNum", "byDVRType", "byChanNum", "byLimitLoginTime", "byLeftLogTimes", "bReserved", "byLockLeftTime", "Reserved", "nNTlsPort", "Reserved2");
+        }
+    }
+
+    public static class DEVICE_IP_SEARCH_INFO extends Structure {
+        public int dwSize = this.size();
+        public int nIpNum;
+        public DEVICE_IP[] szIPArr = (DEVICE_IP[])(new DEVICE_IP()).toArray(256);
+        public fSearchDevicesCBEx cbSearchDevicesEx;
+
+        public DEVICE_IP_SEARCH_INFO() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "nIpNum", "szIPArr", "cbSearchDevicesEx");
+        }
+    }
+
+    public static class DEVICE_IP extends Structure {
+        public byte[] szIP = new byte[64];
+
+        public DEVICE_IP() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("szIP");
+        }
+    }
+
+    public static class NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY extends Structure {
+        public int dwSize = this.size();
+        public NET_DEVICEINFO_Ex stuDeviceInfo;
+        public int nError;
+        public byte[] byReserved = new byte[132];
+
+        public NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "stuDeviceInfo", "nError", "byReserved");
+        }
+    }
+
+    public static class DEVICE_NET_INFO_EX extends Structure {
+        public int iIPVersion;
+        public byte[] szIP = new byte[64];
+        public int nPort;
+        public byte[] szSubmask = new byte[64];
+        public byte[] szGateway = new byte[64];
+        public byte[] szMac = new byte[40];
+        public byte[] szDeviceType = new byte[32];
+        public byte byManuFactory;
+        public byte byDefinition;
+        public byte bDhcpEn;
+        public byte byReserved1;
+        public byte[] verifyData = new byte[88];
+        public byte[] szSerialNo = new byte[48];
+        public byte[] szDevSoftVersion = new byte[128];
+        public byte[] szDetailType = new byte[32];
+        public byte[] szVendor = new byte[128];
+        public byte[] szDevName = new byte[64];
+        public byte[] szUserName = new byte[16];
+        public byte[] szPassWord = new byte[16];
+        public short nHttpPort;
+        public short wVideoInputCh;
+        public short wRemoteVideoInputCh;
+        public short wVideoOutputCh;
+        public short wAlarmInputCh;
+        public short wAlarmOutputCh;
+        public int bNewWordLen;
+        public byte[] szNewPassWord = new byte[64];
+        public byte byInitStatus;
+        public byte byPwdResetWay;
+        public byte bySpecialAbility;
+        public byte[] szNewDetailType = new byte[64];
+        public int bNewUserName;
+        public byte[] szNewUserName = new byte[64];
+        public byte byPwdFindVersion;
+        public byte[] szDeviceID = new byte[24];
+        public int dwUnLoginFuncMask;
+        public byte[] szMachineGroup = new byte[64];
+        public int emIPVersionFrom;
+        public byte[] szCountryCode = new byte[3];
+        public byte byAbroadInfo;
+        public byte[] cReserved = new byte[4];
+
+        public DEVICE_NET_INFO_EX(Pointer p) {
+            super(p);
+        }
+
+        public DEVICE_NET_INFO_EX() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("iIPVersion", "szIP", "nPort", "szSubmask", "szGateway", "szMac", "szDeviceType", "byManuFactory", "byDefinition", "bDhcpEn", "byReserved1", "verifyData", "szSerialNo", "szDevSoftVersion", "szDetailType", "szVendor", "szDevName", "szUserName", "szPassWord", "nHttpPort", "wVideoInputCh", "wRemoteVideoInputCh", "wVideoOutputCh", "wAlarmInputCh", "wAlarmOutputCh", "bNewWordLen", "szNewPassWord", "byInitStatus", "byPwdResetWay", "bySpecialAbility", "szNewDetailType", "bNewUserName", "szNewUserName", "byPwdFindVersion", "szDeviceID", "dwUnLoginFuncMask", "szMachineGroup", "emIPVersionFrom", "szCountryCode", "byAbroadInfo", "cReserved");
+        }
+    }
+
+    public static class LOG_SET_PRINT_INFO extends Structure {
+        public int dwSize = this.size();
+        public int bSetFilePath;
+        public byte[] szLogFilePath = new byte[260];
+        public int bSetFileSize;
+        public int nFileSize;
+        public int bSetFileNum;
+        public int nFileNum;
+        public int bSetPrintStrategy;
+        public int nPrintStrategy;
+        public byte[] byReserved = new byte[4];
+        public Pointer cbSDKLogCallBack;
+        public Pointer dwUser;
+
+        public LOG_SET_PRINT_INFO() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("dwSize", "bSetFilePath", "szLogFilePath", "bSetFileSize", "nFileSize", "bSetFileNum", "nFileNum", "bSetPrintStrategy", "nPrintStrategy", "byReserved", "cbSDKLogCallBack", "dwUser");
+        }
+    }
+
+    public static class NET_RealPlayType extends Structure {
+        public static final int NET_RType_Realplay = 0;
+        public static final int NET_RType_Multiplay = 1;
+        public static final int NET_RType_Realplay_0 = 2;
+        public static final int NET_RType_Realplay_1 = 3;
+        public static final int NET_RType_Realplay_2 = 4;
+        public static final int NET_RType_Realplay_3 = 5;
+        public static final int NET_RType_Multiplay_1 = 6;
+        public static final int NET_RType_Multiplay_4 = 7;
+        public static final int NET_RType_Multiplay_8 = 8;
+        public static final int NET_RType_Multiplay_9 = 9;
+        public static final int NET_RType_Multiplay_16 = 10;
+        public static final int NET_RType_Multiplay_6 = 11;
+        public static final int NET_RType_Multiplay_12 = 12;
+        public static final int NET_RType_Multiplay_25 = 13;
+        public static final int NET_RType_Multiplay_36 = 14;
+        public static final int NET_RType_Multiplay_64 = 15;
+        public static final int NET_RType_Multiplay_255 = 16;
+        public static final int NET_RType_Realplay_Audio = 17;
+        public static final int NET_RType_Realplay_Test = 255;
+
+        public NET_RealPlayType() {
+        }
+
+        protected List<String> getFieldOrder() {
+            return Arrays.asList("NET_RType_Realplay", "NET_RType_Multiplay", "NET_RType_Realplay_0", "NET_RType_Realplay_1", "NET_RType_Realplay_2", "NET_RType_Realplay_3", "NET_RType_Multiplay_1", "NET_RType_Multiplay_4", "NET_RType_Multiplay_8", "NET_RType_Multiplay_9", "NET_RType_Multiplay_16", "NET_RType_Multiplay_6", "NET_RType_Multiplay_12", "NET_RType_Multiplay_25", "NET_RType_Multiplay_36", "NET_RType_Multiplay_64", "NET_RType_Multiplay_255", "NET_RType_Realplay_Audio", "NET_RType_Realplay_Test");
+        }
+    }
+
+    public interface fDataCallBack extends Callback {
+        void invoke(Long var1, int var2, Pointer var3, int var4, Pointer var5);
+    }
+
+    public interface fDisConnect extends Callback {
+        void invoke(Long var1, String var2, int var3, Pointer var4);
+    }
+
+    public interface fRealDataCallBackEx extends Callback {
+        void invoke(Long var1, int var2, Pointer var3, int var4, int var5, Pointer var6);
+    }
+
+    public interface fSearchDevicesCBEx extends Callback {
+        void invoke(Long var1, Pointer var2, Pointer var3);
+    }
+}

+ 99 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/dahua/RemoteDeviceInfo.java

@@ -0,0 +1,99 @@
+package com.usky.sas.common.dahua;
+
+public class RemoteDeviceInfo {
+    private String note;
+    private String ipAddr;
+    private Integer channel;
+
+    public RemoteDeviceInfo() {
+    }
+
+    public String getNote() {
+        return this.note;
+    }
+
+    public String getIpAddr() {
+        return this.ipAddr;
+    }
+
+    public Integer getChannel() {
+        return this.channel;
+    }
+
+    public void setNote(String note) {
+        this.note = note;
+    }
+
+    public void setIpAddr(String ipAddr) {
+        this.ipAddr = ipAddr;
+    }
+
+    public void setChannel(Integer channel) {
+        this.channel = channel;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof RemoteDeviceInfo)) {
+            return false;
+        } else {
+            RemoteDeviceInfo other = (RemoteDeviceInfo)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$note = this.getNote();
+                Object other$note = other.getNote();
+                if (this$note == null) {
+                    if (other$note != null) {
+                        return false;
+                    }
+                } else if (!this$note.equals(other$note)) {
+                    return false;
+                }
+
+                Object this$ipAddr = this.getIpAddr();
+                Object other$ipAddr = other.getIpAddr();
+                if (this$ipAddr == null) {
+                    if (other$ipAddr != null) {
+                        return false;
+                    }
+                } else if (!this$ipAddr.equals(other$ipAddr)) {
+                    return false;
+                }
+
+                Object this$channel = this.getChannel();
+                Object other$channel = other.getChannel();
+                if (this$channel == null) {
+                    if (other$channel != null) {
+                        return false;
+                    }
+                } else if (!this$channel.equals(other$channel)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(Object other) {
+        return other instanceof RemoteDeviceInfo;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $note = this.getNote();
+        result = result * 59 + ($note == null ? 43 : $note.hashCode());
+        Object $ipAddr = this.getIpAddr();
+        result = result * 59 + ($ipAddr == null ? 43 : $ipAddr.hashCode());
+        Object $channel = this.getChannel();
+        result = result * 59 + ($channel == null ? 43 : $channel.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "RemoteDeviceInfo(note=" + this.getNote() + ", ipAddr=" + this.getIpAddr() + ", channel=" + this.getChannel() + ")";
+    }
+}

+ 9 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/entity/DahuaNvrInfo.java

@@ -0,0 +1,9 @@
+package com.usky.sas.common.entity;
+
+import lombok.Data;
+
+@Data
+public class DahuaNvrInfo {
+    private Long loginHandle;
+    private Integer maxChannelNum;
+}

+ 181 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/entity/DeviceVideoInfo.java

@@ -0,0 +1,181 @@
+package com.usky.sas.common.entity;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class DeviceVideoInfo {
+    @ApiModelProperty("登录句柄")
+    private Integer userId;
+    @ApiModelProperty("同步源标识符")
+    private String ssrc;
+    @ApiModelProperty("视频流句柄")
+    private Integer handle;
+    @ApiModelProperty("播放地址")
+    private String url;
+    @ApiModelProperty("视频码率")
+    private Integer bitrate;
+    @ApiModelProperty("视频流类型 1实时预览 2回放")
+    private Integer streamType;
+
+    public DeviceVideoInfo() {
+    }
+
+    public Integer getUserId() {
+        return this.userId;
+    }
+
+    public String getSsrc() {
+        return this.ssrc;
+    }
+
+    public Integer getHandle() {
+        return this.handle;
+    }
+
+    public String getUrl() {
+        return this.url;
+    }
+
+    public Integer getBitrate() {
+        return this.bitrate;
+    }
+
+    public Integer getStreamType() {
+        return this.streamType;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public void setHandle(Integer handle) {
+        this.handle = handle;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public void setBitrate(Integer bitrate) {
+        this.bitrate = bitrate;
+    }
+
+    public void setStreamType(Integer streamType) {
+        this.streamType = streamType;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof DeviceVideoInfo)) {
+            return false;
+        } else {
+            DeviceVideoInfo other = (DeviceVideoInfo)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$userId = this.getUserId();
+                Object other$userId = other.getUserId();
+                if (this$userId == null) {
+                    if (other$userId != null) {
+                        return false;
+                    }
+                } else if (!this$userId.equals(other$userId)) {
+                    return false;
+                }
+
+                Object this$ssrc = this.getSsrc();
+                Object other$ssrc = other.getSsrc();
+                if (this$ssrc == null) {
+                    if (other$ssrc != null) {
+                        return false;
+                    }
+                } else if (!this$ssrc.equals(other$ssrc)) {
+                    return false;
+                }
+
+                Object this$handle = this.getHandle();
+                Object other$handle = other.getHandle();
+                if (this$handle == null) {
+                    if (other$handle != null) {
+                        return false;
+                    }
+                } else if (!this$handle.equals(other$handle)) {
+                    return false;
+                }
+
+                label62: {
+                    Object this$url = this.getUrl();
+                    Object other$url = other.getUrl();
+                    if (this$url == null) {
+                        if (other$url == null) {
+                            break label62;
+                        }
+                    } else if (this$url.equals(other$url)) {
+                        break label62;
+                    }
+
+                    return false;
+                }
+
+                label55: {
+                    Object this$bitrate = this.getBitrate();
+                    Object other$bitrate = other.getBitrate();
+                    if (this$bitrate == null) {
+                        if (other$bitrate == null) {
+                            break label55;
+                        }
+                    } else if (this$bitrate.equals(other$bitrate)) {
+                        break label55;
+                    }
+
+                    return false;
+                }
+
+                Object this$streamType = this.getStreamType();
+                Object other$streamType = other.getStreamType();
+                if (this$streamType == null) {
+                    if (other$streamType != null) {
+                        return false;
+                    }
+                } else if (!this$streamType.equals(other$streamType)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(Object other) {
+        return other instanceof DeviceVideoInfo;
+    }
+
+    public int hashCode() {
+        final int PRIME = 59;
+        int result = 1;
+        Object $userId = this.getUserId();
+        result = result * PRIME + ($userId == null ? 43 : $userId.hashCode());
+        Object $ssrc = this.getSsrc();
+        result = result * PRIME + ($ssrc == null ? 43 : $ssrc.hashCode());
+        Object $handle = this.getHandle();
+        result = result * PRIME + ($handle == null ? 43 : $handle.hashCode());
+        Object $url = this.getUrl();
+        result = result * PRIME + ($url == null ? 43 : $url.hashCode());
+        Object $bitrate = this.getBitrate();
+        result = result * PRIME + ($bitrate == null ? 43 : $bitrate.hashCode());
+        Object $streamType = this.getStreamType();
+        result = result * PRIME + ($streamType == null ? 43 : $streamType.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "DeviceVideoInfo(userId=" + this.getUserId() + ", ssrc=" + this.getSsrc() + ", handle=" + this.getHandle() + ", url=" + this.getUrl() + ", bitrate=" + this.getBitrate() + ", streamType=" + this.getStreamType() + ")";
+    }
+}
+

+ 327 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/entity/SearchDeviceInfo.java

@@ -0,0 +1,327 @@
+package com.usky.sas.common.entity;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class SearchDeviceInfo {
+    @ApiModelProperty("设备ip")
+    private String ipAddr;
+    @ApiModelProperty("设备网关")
+    private String gateway;
+    @ApiModelProperty("子网掩码")
+    private String netMask;
+    @ApiModelProperty("mac地址")
+    private String macAddr;
+    @ApiModelProperty("设备端口")
+    private Integer port;
+    @ApiModelProperty("型号")
+    private String deviceId;
+    @ApiModelProperty("序列号")
+    private String serialNo;
+    @ApiModelProperty("描述信息")
+    private String note;
+    @ApiModelProperty("制造商")
+    private String oemInfo;
+    @ApiModelProperty("已添加")
+    private Boolean isAdded;
+    @ApiModelProperty("已激活")
+    private Boolean isActive;
+    @ApiModelProperty("协议编码")
+    private Integer protocol;
+
+    public SearchDeviceInfo() {
+    }
+
+    public String getIpAddr() {
+        return this.ipAddr;
+    }
+
+    public String getGateway() {
+        return this.gateway;
+    }
+
+    public String getNetMask() {
+        return this.netMask;
+    }
+
+    public String getMacAddr() {
+        return this.macAddr;
+    }
+
+    public Integer getPort() {
+        return this.port;
+    }
+
+    public String getDeviceId() {
+        return this.deviceId;
+    }
+
+    public String getSerialNo() {
+        return this.serialNo;
+    }
+
+    public String getNote() {
+        return this.note;
+    }
+
+    public String getOemInfo() {
+        return this.oemInfo;
+    }
+
+    public Boolean getIsAdded() {
+        return this.isAdded;
+    }
+
+    public Boolean getIsActive() {
+        return this.isActive;
+    }
+
+    public Integer getProtocol() {
+        return this.protocol;
+    }
+
+    public void setIpAddr(String ipAddr) {
+        this.ipAddr = ipAddr;
+    }
+
+    public void setGateway(String gateway) {
+        this.gateway = gateway;
+    }
+
+    public void setNetMask(String netMask) {
+        this.netMask = netMask;
+    }
+
+    public void setMacAddr(String macAddr) {
+        this.macAddr = macAddr;
+    }
+
+    public void setPort(Integer port) {
+        this.port = port;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public void setSerialNo(String serialNo) {
+        this.serialNo = serialNo;
+    }
+
+    public void setNote(String note) {
+        this.note = note;
+    }
+
+    public void setOemInfo(String oemInfo) {
+        this.oemInfo = oemInfo;
+    }
+
+    public void setIsAdded(Boolean isAdded) {
+        this.isAdded = isAdded;
+    }
+
+    public void setIsActive(Boolean isActive) {
+        this.isActive = isActive;
+    }
+
+    public void setProtocol(Integer protocol) {
+        this.protocol = protocol;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof SearchDeviceInfo)) {
+            return false;
+        } else {
+            SearchDeviceInfo other = (SearchDeviceInfo)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                label155: {
+                    Object this$ipAddr = this.getIpAddr();
+                    Object other$ipAddr = other.getIpAddr();
+                    if (this$ipAddr == null) {
+                        if (other$ipAddr == null) {
+                            break label155;
+                        }
+                    } else if (this$ipAddr.equals(other$ipAddr)) {
+                        break label155;
+                    }
+
+                    return false;
+                }
+
+                Object this$gateway = this.getGateway();
+                Object other$gateway = other.getGateway();
+                if (this$gateway == null) {
+                    if (other$gateway != null) {
+                        return false;
+                    }
+                } else if (!this$gateway.equals(other$gateway)) {
+                    return false;
+                }
+
+                Object this$netMask = this.getNetMask();
+                Object other$netMask = other.getNetMask();
+                if (this$netMask == null) {
+                    if (other$netMask != null) {
+                        return false;
+                    }
+                } else if (!this$netMask.equals(other$netMask)) {
+                    return false;
+                }
+
+                label134: {
+                    Object this$macAddr = this.getMacAddr();
+                    Object other$macAddr = other.getMacAddr();
+                    if (this$macAddr == null) {
+                        if (other$macAddr == null) {
+                            break label134;
+                        }
+                    } else if (this$macAddr.equals(other$macAddr)) {
+                        break label134;
+                    }
+
+                    return false;
+                }
+
+                label127: {
+                    Object this$port = this.getPort();
+                    Object other$port = other.getPort();
+                    if (this$port == null) {
+                        if (other$port == null) {
+                            break label127;
+                        }
+                    } else if (this$port.equals(other$port)) {
+                        break label127;
+                    }
+
+                    return false;
+                }
+
+                label120: {
+                    Object this$deviceId = this.getDeviceId();
+                    Object other$deviceId = other.getDeviceId();
+                    if (this$deviceId == null) {
+                        if (other$deviceId == null) {
+                            break label120;
+                        }
+                    } else if (this$deviceId.equals(other$deviceId)) {
+                        break label120;
+                    }
+
+                    return false;
+                }
+
+                Object this$serialNo = this.getSerialNo();
+                Object other$serialNo = other.getSerialNo();
+                if (this$serialNo == null) {
+                    if (other$serialNo != null) {
+                        return false;
+                    }
+                } else if (!this$serialNo.equals(other$serialNo)) {
+                    return false;
+                }
+
+                label106: {
+                    Object this$note = this.getNote();
+                    Object other$note = other.getNote();
+                    if (this$note == null) {
+                        if (other$note == null) {
+                            break label106;
+                        }
+                    } else if (this$note.equals(other$note)) {
+                        break label106;
+                    }
+
+                    return false;
+                }
+
+                Object this$oemInfo = this.getOemInfo();
+                Object other$oemInfo = other.getOemInfo();
+                if (this$oemInfo == null) {
+                    if (other$oemInfo != null) {
+                        return false;
+                    }
+                } else if (!this$oemInfo.equals(other$oemInfo)) {
+                    return false;
+                }
+
+                label92: {
+                    Object this$isAdded = this.getIsAdded();
+                    Object other$isAdded = other.getIsAdded();
+                    if (this$isAdded == null) {
+                        if (other$isAdded == null) {
+                            break label92;
+                        }
+                    } else if (this$isAdded.equals(other$isAdded)) {
+                        break label92;
+                    }
+
+                    return false;
+                }
+
+                Object this$isActive = this.getIsActive();
+                Object other$isActive = other.getIsActive();
+                if (this$isActive == null) {
+                    if (other$isActive != null) {
+                        return false;
+                    }
+                } else if (!this$isActive.equals(other$isActive)) {
+                    return false;
+                }
+
+                Object this$protocol = this.getProtocol();
+                Object other$protocol = other.getProtocol();
+                if (this$protocol == null) {
+                    if (other$protocol != null) {
+                        return false;
+                    }
+                } else if (!this$protocol.equals(other$protocol)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(Object other) {
+        return other instanceof SearchDeviceInfo;
+    }
+
+    public int hashCode() {
+        final int PRIME = 59;
+        int result = 1;
+        Object $ipAddr = this.getIpAddr();
+        result = result * PRIME + ($ipAddr == null ? 43 : $ipAddr.hashCode());
+        Object $gateway = this.getGateway();
+        result = result * PRIME + ($gateway == null ? 43 : $gateway.hashCode());
+        Object $netMask = this.getNetMask();
+        result = result * PRIME + ($netMask == null ? 43 : $netMask.hashCode());
+        Object $macAddr = this.getMacAddr();
+        result = result * PRIME + ($macAddr == null ? 43 : $macAddr.hashCode());
+        Object $port = this.getPort();
+        result = result * PRIME + ($port == null ? 43 : $port.hashCode());
+        Object $deviceId = this.getDeviceId();
+        result = result * PRIME + ($deviceId == null ? 43 : $deviceId.hashCode());
+        Object $serialNo = this.getSerialNo();
+        result = result * PRIME + ($serialNo == null ? 43 : $serialNo.hashCode());
+        Object $note = this.getNote();
+        result = result * PRIME + ($note == null ? 43 : $note.hashCode());
+        Object $oemInfo = this.getOemInfo();
+        result = result * PRIME + ($oemInfo == null ? 43 : $oemInfo.hashCode());
+        Object $isAdded = this.getIsAdded();
+        result = result * PRIME + ($isAdded == null ? 43 : $isAdded.hashCode());
+        Object $isActive = this.getIsActive();
+        result = result * PRIME + ($isActive == null ? 43 : $isActive.hashCode());
+        Object $protocol = this.getProtocol();
+        result = result * PRIME + ($protocol == null ? 43 : $protocol.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "SearchDeviceInfo(ipAddr=" + this.getIpAddr() + ", gateway=" + this.getGateway() + ", netMask=" + this.getNetMask() + ", macAddr=" + this.getMacAddr() + ", port=" + this.getPort() + ", deviceId=" + this.getDeviceId() + ", serialNo=" + this.getSerialNo() + ", note=" + this.getNote() + ", oemInfo=" + this.getOemInfo() + ", isAdded=" + this.getIsAdded() + ", isActive=" + this.getIsActive() + ", protocol=" + this.getProtocol() + ")";
+    }
+}
+

+ 39 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/enums/BusinessEnum.java

@@ -0,0 +1,39 @@
+package com.usky.sas.common.enums;
+
+public enum BusinessEnum {
+    USER_NOT_EXIST(-1001, "账号不存在"),
+    USER_EXIST(-1002, "账号已存在"),
+    PASSWORD_ERROR(-1003, "用户名或密码错误"),
+    ADD_FAIL(-1004, "新增失败"),
+    UPDATE_FAIL(-1005, "更新失败"),
+    DELETE_FAIL(-1006, "删除失败"),
+    SYSTEM_NOT_ACTIVATION(-1007, "系统未激活"),
+    LICENSE_NULL(-1008, "激活码不能为空"),
+    LICENSE_EXPIRE(-1009, "系统激活码已过期"),
+    UNAUTHORIZED_LICENSE(-1010, "无效激活码"),
+    MQTT_CONNECT_FAIL(-1011, "Mqtt连接失败"),
+    MQTT_UN_CONNECT(-1012, "Mqtt连接未初始化"),
+    MQTT_PAUSE_FAIL(-1013, "暂停Mqtt事件订阅错误"),
+    MQTT_RESUME_FAIL(-1014, "恢复Mqtt事件订阅错误"),
+    EXIST(-1015, "重复添加"),
+    DEVICE_NOT_ACTIVATION(-1016, "设备未激活"),
+    DEVICE_ACTIVATION_EXPIRE(-1017, "设备激活已过期"),
+    DEVICE_ACTIVATION_FAILED(-1018, "设备激活失败"),
+    DEVICE_NOT_LOGIN(-1019, "设备未注册");
+
+    private int code;
+    private String description;
+
+    private BusinessEnum(int code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+
+    public int getCode() {
+        return this.code;
+    }
+
+    public String getDescription() {
+        return this.description;
+    }
+}

+ 22 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/enums/DeviceTypeEnum.java

@@ -0,0 +1,22 @@
+package com.usky.sas.common.enums;
+
+public enum DeviceTypeEnum {
+    IPC(1, "IPC"),
+    NVR(2, "NVR");
+
+    private final Integer code;
+    private final String desc;
+
+    DeviceTypeEnum(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+}

+ 23 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/enums/ProtocolEnum.java

@@ -0,0 +1,23 @@
+package com.usky.sas.common.enums;
+
+public enum ProtocolEnum {
+    ONVIF(1, "onvif"),
+    HIK(2, "海康"),
+    DA_HUA(3, "大华");
+
+    private final Integer code;
+    private final String desc;
+
+    private ProtocolEnum(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return this.code;
+    }
+
+    public String getDesc() {
+        return this.desc;
+    }
+}

+ 32 - 0
service-sas/service-sas-biz/src/main/java/com/usky/sas/common/enums/SystemTypeCodeEnum.java

@@ -0,0 +1,32 @@
+package com.usky.sas.common.enums;
+
+public enum SystemTypeCodeEnum {
+    snap(1001, "实时智能分析"),
+    video(1002, "视频安防监控"),
+    usb(1003, "视频导出防护"),
+    gauth(1004, "组合认证控制"),
+    entrance(1005, "出入门禁控制"),
+    parking(1006, "车牌抓拍分析"),
+    alarm(1007, "入侵紧急报警"),
+    patrol(1008, "实时电子巡检"),
+    perception(1009, "状态感知探测"),
+    acquisition(1010, "数据采集探测"),
+    collection(1011, "状态采集探测"),
+    roadblock(1012, "阻车路障探测");
+
+    private final int code;
+    private final String message;
+
+    private SystemTypeCodeEnum(int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public int getCode() {
+        return this.code;
+    }
+
+    public String getMessage() {
+        return this.message;
+    }
+}

Some files were not shown because too many files changed in this diff