TRX 1 rok pred
rodič
commit
92984dd95c
58 zmenil súbory, kde vykonal 6743 pridanie a 1 odobranie
  1. 51 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/TestController.java
  2. 89 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/docker/DockerMetaController.java
  3. 31 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/free/FixDataController.java
  4. 61 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/free/GateWayFreeController.java
  5. 38 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/free/ProjectFromFullCardController.java
  6. 154 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/DeviceController.java
  7. 78 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/GateWayController.java
  8. 81 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/GateWayUserInfoController.java
  9. 71 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/MqttInfoController.java
  10. 43 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/OperationLogsController.java
  11. 77 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/OperationMessageController.java
  12. 165 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/iot/IotController.java
  13. 55 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/iot/IotSendController.java
  14. 35 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/payment/HxzController.java
  15. 21 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/event/DeviceOnLineTimeChangeEvent.java
  16. 21 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/event/DeviceStateChangeEvent.java
  17. 22 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/event/DeviceSyncEvent.java
  18. 21 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/event/GateWaySyncEvent.java
  19. 113 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/ApiRequestService.java
  20. 76 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/apiConf/APIResponseModel.java
  21. 26 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/apiConf/ApiConfParam.java
  22. 68 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/conf/FullCardAPIConfig.java
  23. 24 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/conf/FullCardConf.java
  24. 2 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/package-info.java
  25. 88 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/artemis/ArtemisListenerService.java
  26. 681 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/artemis/OperationMessageService.java
  27. 69 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/base/CommonService.java
  28. 32 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/base/InitService.java
  29. 81 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/base/RedisService.java
  30. 103 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/base/SuperService.java
  31. 292 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/docker/DockerMetaService.java
  32. 57 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/fix/FixDataService.java
  33. 237 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/iot/IotDataVerifyService.java
  34. 147 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/iot/IotSendMessageService.java
  35. 506 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/iot/IotServiceImpl.java
  36. 257 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/DeviceInfoService.java
  37. 217 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/DevicePingInfoService.java
  38. 345 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/GateWayInfoService.java
  39. 251 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/GateWayUserInfoService.java
  40. 133 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/MqttInfoService.java
  41. 109 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/ProjectInfoService.java
  42. 40 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/ServerTimeService.java
  43. 73 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/other/CollectionIdService.java
  44. 62 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/other/RequestInfoService.java
  45. 60 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/other/ScanExecuteService.java
  46. 95 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/other/ScanSupport.java
  47. 169 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/payment/HxzService.java
  48. 4 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/payment/package-info.java
  49. 248 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/sync/DeviceSyncFullCardService.java
  50. 42 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/sync/ProjectFromFullCardService.java
  51. 25 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/DepartmentService.java
  52. 147 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/OperationLogsService.java
  53. 8 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/UserService.java
  54. 158 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/impl/DepartmentServiceImpl.java
  55. 176 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/impl/RoleServiceImpl.java
  56. 247 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/impl/UserManagerServiceImpl.java
  57. 160 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/impl/UserServiceImpl.java
  58. 1 1
      OneCardIotServer/src/main/resources/application.yml

+ 51 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/TestController.java

@@ -0,0 +1,51 @@
+package com.zhongshu.iot.server.core.controller;
+
+import com.zhongshu.iot.client.model.mqtt.SendMessageModel;
+import com.zhongshu.iot.server.core.service.artemis.OperationMessageService;
+import com.zhongshu.iot.server.core.service.other.ScanExecuteService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ *
+ */
+@RequestMapping("/test")
+@RestController
+@Validated
+@Tag(name = "测试接口")
+public class TestController {
+
+    @Autowired
+    OperationMessageService operationMessageService;
+
+    @Autowired
+    ScanExecuteService scanExecuteService;
+
+    @Operation(summary = "发送指令")
+    @RequestMapping(value = "free/sendMessage", method = {RequestMethod.POST})
+    public ResultContent sendMessage(@RequestBody SendMessageModel param) {
+        return operationMessageService.sendMessage(param);
+    }
+
+    @Operation(summary = "ping")
+    @RequestMapping(value = "free/ping", method = {RequestMethod.GET})
+    public ResultContent ping() {
+        return ResultContent.buildSuccess(System.currentTimeMillis());
+    }
+
+    @Operation(summary = "scanSystemExecuteMethod")
+    @RequestMapping(value = "free/scanSystemExecuteMethod", method = {RequestMethod.GET})
+    public ResultContent scanSystemExecuteMethod() {
+        scanExecuteService.scanSystemExecuteMethod();
+        return ResultContent.buildSuccess();
+    }
+
+}
+

+ 89 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/docker/DockerMetaController.java

@@ -0,0 +1,89 @@
+package com.zhongshu.iot.server.core.controller.docker;
+
+import cn.hutool.json.JSONObject;
+import com.github.microservice.auth.security.annotations.ResourceAuth;
+import com.github.microservice.auth.security.type.AuthType;
+import com.zhongshu.iot.client.model.docker.DockerMetaLastTimeModel;
+import com.zhongshu.iot.client.model.docker.DockerMetaModel;
+import com.zhongshu.iot.client.model.docker.DockerMetaParam;
+import com.zhongshu.iot.client.model.docker.DockerMetaSearch;
+import com.zhongshu.iot.server.core.service.docker.DockerMetaService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * Docker元数据管理
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/v1/task")
+@RestController
+@Validated
+@Tag(name = "Docker元数据管理")
+public class DockerMetaController {
+
+    @Autowired
+    DockerMetaService dockerMetaService;
+
+    //-------------------------- Docker start------------------------------
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "添加-编辑Docker元数据")
+    @RequestMapping(value = "addOrUpdate", method = {RequestMethod.POST})
+    public ResultContent addOrUpdate(@RequestBody DockerMetaParam param) {
+        return dockerMetaService.addOrUpdate(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "docker元数据列表-分页查询")
+    @RequestMapping(value = {"page"}, method = {RequestMethod.POST})
+    public ResultContent<Page<DockerMetaModel>> page(@Parameter(hidden = true) @PageableDefault(page = 0, size = 10) Pageable pageable,
+            @Parameter(required = false) DockerMetaSearch param) {
+        return dockerMetaService.page(pageable, param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "删除元数据")
+    @RequestMapping(value = "deleteMeta", method = {RequestMethod.GET})
+    public ResultContent deleteMeta(@Parameter(name = "id", description = "数据id") String id) {
+        return dockerMetaService.deleteMeta(id);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "查询Docker元数据详情")
+    @RequestMapping(value = "getDockerMeta", method = {RequestMethod.GET})
+    public ResultContent<DockerMetaModel> getDockerMeta(@Parameter(name = "id", description = "数据id") String id) {
+        return dockerMetaService.getDockerMeta(id);
+    }
+
+    //-------------------------- docker end--------------------------------
+
+    //-------------------------- 镜像数据 start------------------------------
+
+    @Operation(summary = "getVersion")
+    @RequestMapping(value = "getVersion", method = {RequestMethod.GET})
+    public DockerMetaLastTimeModel getVersion() {
+        return dockerMetaService.getVersion();
+    }
+
+    @Operation(summary = "getList")
+    @RequestMapping(value = "getList", method = {RequestMethod.GET})
+    public List<JSONObject> getList() {
+        return dockerMetaService.getList();
+    }
+
+
+}

+ 31 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/free/FixDataController.java

@@ -0,0 +1,31 @@
+package com.zhongshu.iot.server.core.controller.free;
+
+import com.zhongshu.iot.server.core.service.fix.FixDataService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author TRX
+ * @date 2024/6/27
+ */
+@RequestMapping("/fixData/free")
+@RestController
+@Validated
+@Tag(name = "Fix修复数据")
+public class FixDataController {
+
+    @Autowired
+    private FixDataService fixDataService;
+
+    @Operation(summary = "同步全部设备-网关")
+    @RequestMapping(value = "fixSyncDevice", method = {RequestMethod.GET})
+    public ResultContent fixSyncDevice() {
+        return fixDataService.fixSyncDevice();
+    }
+}

+ 61 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/free/GateWayFreeController.java

@@ -0,0 +1,61 @@
+package com.zhongshu.iot.server.core.controller.free;
+
+import com.zhongshu.iot.client.model.mqtt.GateWayBindDeviceParam;
+import com.zhongshu.iot.client.model.mqtt.GateWayInfoAddParam;
+import com.zhongshu.iot.client.model.mqtt.MqttInfoReturnModel;
+import com.zhongshu.iot.server.core.service.mqtt.DeviceInfoService;
+import com.zhongshu.iot.server.core.service.mqtt.GateWayInfoService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.Assert;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 网关管理 服务
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/gateWayInfo/free")
+@RestController
+@Validated
+@Tag(name = "网关开发接口")
+@Slf4j
+public class GateWayFreeController {
+
+    @Autowired
+    GateWayInfoService gateWayInfoService;
+
+    @Autowired
+    DeviceInfoService deviceInfoService;
+
+    @Operation(summary = "注册网关")
+    @RequestMapping(value = "registerGateWay", method = {RequestMethod.POST})
+    public ResultContent<MqttInfoReturnModel> registerGateWay(@RequestBody GateWayInfoAddParam param) {
+        Assert.hasText(param.getGateWayId(), "网关ID不能为空");
+        log.info("-----------------------注册网关---------------------- {}", param);
+        return gateWayInfoService.registerGateWay(param);
+    }
+
+    @Operation(summary = "网关绑定设备")
+    @RequestMapping(value = "gateWayBindDevice", method = {RequestMethod.POST})
+    public ResultContent gateWayBindDevice(@RequestBody GateWayBindDeviceParam param) {
+        Assert.hasText(param.getGateWayId(), "网关ID不能为空");
+        log.info("-------------------------网关绑定设备------------------------: {}", param);
+        return gateWayInfoService.gateWayBindDevice(param);
+    }
+
+    @Operation(summary = "ServerTime")
+    @RequestMapping(value = "ServerTime", method = {RequestMethod.GET})
+    public ResultContent ServerTime() {
+        return ResultContent.buildSuccess(System.currentTimeMillis());
+    }
+
+}

+ 38 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/free/ProjectFromFullCardController.java

@@ -0,0 +1,38 @@
+package com.zhongshu.iot.server.core.controller.free;
+
+import com.github.microservice.models.project.ProjectSyncParam;
+import com.zhongshu.iot.server.core.service.sync.ProjectFromFullCardService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Hidden;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 项目同步
+ *
+ * @author TRX
+ * @date 2024/6/28
+ */
+@RequestMapping("/projectSync/free")
+@RestController
+@Validated
+@Tag(name = "项目信息同步")
+@Hidden
+public class ProjectFromFullCardController {
+
+    @Autowired
+    ProjectFromFullCardService projectFromFullCardService;
+
+    @Operation(summary = "全卡项目同步项目", hidden = true)
+    @RequestMapping(value = "syncFromFullCardProjects", method = {RequestMethod.POST})
+    public ResultContent syncFromFullCardProjects(@RequestBody ProjectSyncParam param) {
+        return projectFromFullCardService.syncFromFullCardProjects(param);
+    }
+
+}

+ 154 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/DeviceController.java

@@ -0,0 +1,154 @@
+package com.zhongshu.iot.server.core.controller.hardware;
+
+import com.github.microservice.auth.security.annotations.ResourceAuth;
+import com.github.microservice.auth.security.type.AuthType;
+import com.zhongshu.iot.client.model.iot.IotMainModel;
+import com.zhongshu.iot.client.model.mqtt.DeviceInfoAddParam;
+import com.zhongshu.iot.client.model.mqtt.DeviceInfoModel;
+import com.zhongshu.iot.client.model.mqtt.DeviceInfoSearchParam;
+import com.zhongshu.iot.client.model.mqtt.DeviceInfoUpdateRemark;
+import com.zhongshu.iot.client.type.FunctionType;
+import com.zhongshu.iot.server.core.service.mqtt.DeviceInfoService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.util.Assert;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 设备管理 服务
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/device")
+@RestController
+@Validated
+@Tag(name = "设备管理")
+public class DeviceController {
+
+    @Autowired
+    DeviceInfoService deviceInfoService;
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "添加-编辑设备")
+    @RequestMapping(value = "addDeviceInfo", method = {RequestMethod.POST})
+    public ResultContent addDeviceInfo(@RequestBody DeviceInfoAddParam param) {
+        Assert.hasText(param.getDeviceId(), "设备ID不能为空");
+        Assert.hasText(param.getDeviceName(), "设备名称不能为空");
+        return deviceInfoService.addDeviceInfo(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "设备列表-分页查询")
+    @RequestMapping(value = {"pageActivity"}, method = {RequestMethod.POST})
+    public ResultContent<Page<DeviceInfoModel>> pageActivity(@Parameter(hidden = true) @PageableDefault(page = 0, size = 10) Pageable pageable, @Parameter(required = false) DeviceInfoSearchParam param) {
+        return deviceInfoService.pageDevice(pageable, param);
+    }
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "删除设备")
+    @RequestMapping(value = "deleteDeviceInfo", method = {RequestMethod.GET})
+    public ResultContent deleteDeviceInfo(
+            @Parameter(name = "deviceId", description = "数据ID", example = "")
+            @RequestParam("deviceId") String deviceId) {
+        return deviceInfoService.deleteDeviceInfo(deviceId);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "查询设备详情")
+    @RequestMapping(value = "getDeviceById", method = {RequestMethod.GET})
+    public ResultContent<DeviceInfoModel> getDeviceById(
+            @Parameter(name = "deviceId", description = "数据ID", example = "")
+            @RequestParam("deviceId") String deviceId) {
+        return deviceInfoService.getDeviceById(deviceId);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "编辑备注")
+    @RequestMapping(value = "updateRemark", method = {RequestMethod.POST})
+    public ResultContent updateRemark(@RequestBody DeviceInfoUpdateRemark param) {
+        Assert.hasText(param.getId(), "ID不能为空");
+        return deviceInfoService.updateRemark(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "编辑关联的模版")
+    @RequestMapping(value = "updateIopTemplate", method = {RequestMethod.POST})
+    public ResultContent updateIopTemplate(@RequestBody DeviceInfoUpdateRemark param) {
+        Assert.hasText(param.getId(), "ID不能为空");
+        return deviceInfoService.updateIopTemplate(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "编辑关联的项目信息")
+    @RequestMapping(value = "updateProject", method = {RequestMethod.POST})
+    public ResultContent updateProject(@RequestBody DeviceInfoUpdateRemark param) {
+        Assert.hasText(param.getId(), "ID不能为空");
+        Assert.hasText(param.getProjectCode(), "projectCode不能为空");
+        return deviceInfoService.updateProject(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "启用-禁用 设备")
+    @RequestMapping(value = "updateDeviceState", method = {RequestMethod.POST})
+    public ResultContent updateDeviceState(@RequestBody DeviceInfoUpdateRemark param) {
+        Assert.hasText(param.getId(), "ID不能为空");
+        if (param.getState() != null) {
+            return ResultContent.buildFail("state不能为空");
+        }
+        return deviceInfoService.updateDeviceState(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "改变是否上传日志")
+    @RequestMapping(value = "updateIsReportLogs", method = {RequestMethod.POST})
+    public ResultContent updateIsReportLogs(@RequestBody DeviceInfoUpdateRemark param) {
+        Assert.hasText(param.getId(), "ID不能为空");
+        return deviceInfoService.updateIsReportLogs(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "ping设备")
+    @RequestMapping(value = "testDevice", method = {RequestMethod.GET})
+    public ResultContent<Long> testDevice(
+            @Parameter(name = "deviceId", description = "数据ID", example = "")
+            @RequestParam("deviceId") String deviceId) {
+        return deviceInfoService.testDevice(deviceId);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "物模型数据")
+    @RequestMapping(value = "getDeviceIotMainAttribute", method = {RequestMethod.GET})
+    public ResultContent<List<IotMainModel>> getDeviceIotMainAttribute(
+            @Parameter(name = "deviceId", description = "数据ID", example = "")
+            @RequestParam("deviceId") String deviceId) {
+        return deviceInfoService.getDeviceIotMain(deviceId, FunctionType.Attribute);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "事件管理")
+    @RequestMapping(value = "getDeviceIotMainEvent", method = {RequestMethod.GET})
+    public ResultContent<List<IotMainModel>> getDeviceIotMainEvent(
+            @Parameter(name = "deviceId", description = "数据ID", example = "")
+            @RequestParam("deviceId") String deviceId) {
+        return deviceInfoService.getDeviceIotMain(deviceId, FunctionType.Event);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "服务调用")
+    @RequestMapping(value = "getDeviceIotMainServer", method = {RequestMethod.GET})
+    public ResultContent<List<IotMainModel>> getDeviceIotMainServer(
+            @Parameter(name = "deviceId", description = "数据ID", example = "")
+            @RequestParam("deviceId") String deviceId) {
+        return deviceInfoService.getDeviceIotMain(deviceId, FunctionType.Server);
+    }
+
+}

+ 78 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/GateWayController.java

@@ -0,0 +1,78 @@
+package com.zhongshu.iot.server.core.controller.hardware;
+
+import com.github.microservice.auth.security.annotations.ResourceAuth;
+import com.github.microservice.auth.security.type.AuthType;
+import com.zhongshu.iot.client.model.mqtt.GateWayBindDeviceParam;
+import com.zhongshu.iot.client.model.mqtt.GateWayInfoAddParam;
+import com.zhongshu.iot.client.model.mqtt.GateWayInfoModel;
+import com.zhongshu.iot.client.model.mqtt.GateWayInfoSearchParam;
+import com.zhongshu.iot.server.core.service.mqtt.GateWayInfoService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.util.Assert;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 网关管理 服务
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/gateWay")
+@RestController
+@Validated
+@Tag(name = "网关管理")
+public class GateWayController {
+
+    @Autowired
+    GateWayInfoService gateWayInfoService;
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "添加网关")
+    @RequestMapping(value = "addDeviceInfo", method = {RequestMethod.POST})
+    public ResultContent addDeviceInfo(@RequestBody GateWayInfoAddParam param) {
+        Assert.hasText(param.getGateWayId(), "网关ID不能为空");
+        Assert.hasText(param.getGateWayName(), "网关名称不能为空");
+        return gateWayInfoService.addGateWayInfo(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "网关列表-分页查询")
+    @RequestMapping(value = {"pageGateWay"}, method = {RequestMethod.POST})
+    public ResultContent<Page<GateWayInfoModel>> pageGateWay(@Parameter(hidden = true) @PageableDefault(page = 0, size = 10) Pageable pageable, GateWayInfoSearchParam param) {
+        return gateWayInfoService.pageGateWay(pageable, param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "删除网关")
+    @RequestMapping(value = "deleteGateWayInfo", method = {RequestMethod.GET})
+    public ResultContent deleteGateWayInfo(String gateWayId) {
+        return gateWayInfoService.deleteGateWayInfo(gateWayId);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "查询网关")
+    @RequestMapping(value = "getGateWayById", method = {RequestMethod.GET})
+    public ResultContent<GateWayInfoModel> getGateWayById(String deviceId) {
+        return gateWayInfoService.getById(deviceId);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "网关绑定设备、连接账号")
+    @RequestMapping(value = "gateWayBindDevice", method = {RequestMethod.POST})
+    public ResultContent gateWayBindDevice(@RequestBody GateWayBindDeviceParam param) {
+        Assert.hasText(param.getGateWayId(), "网关ID不能为空");
+        Assert.hasText(param.getUserName(), "连接账号不能为空");
+        return gateWayInfoService.gateWayBindDevice(param);
+    }
+}

+ 81 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/GateWayUserInfoController.java

@@ -0,0 +1,81 @@
+package com.zhongshu.iot.server.core.controller.hardware;
+
+import com.github.microservice.auth.security.annotations.ResourceAuth;
+import com.github.microservice.auth.security.type.AuthType;
+import com.zhongshu.iot.client.model.mqtt.GateWayUserInfoAddParam;
+import com.zhongshu.iot.client.model.mqtt.GateWayUserInfoModel;
+import com.zhongshu.iot.client.model.mqtt.GateWayUserInfoNameParam;
+import com.zhongshu.iot.client.model.mqtt.GateWayUserInfoSearchParam;
+import com.zhongshu.iot.server.core.service.mqtt.GateWayUserInfoService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.util.Assert;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 连接用户管理 服务
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/gateWayUserInfo")
+@RestController
+@Validated
+@Tag(name = "连接用户管理")
+public class GateWayUserInfoController {
+
+    @Autowired
+    GateWayUserInfoService gateWayUserInfoService;
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "添加连接用户")
+    @RequestMapping(value = "addDeviceInfo", method = {RequestMethod.POST})
+    public ResultContent addDeviceInfo(@RequestBody GateWayUserInfoAddParam param) {
+        Assert.hasText(param.getUserName(), "连接账号不能为空");
+        Assert.hasText(param.getPassWord(), "连接密码不能为空");
+        return gateWayUserInfoService.addGateWayUser(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "连接用户列表-分页查询")
+    @RequestMapping(value = {"pageGateWayUser"}, method = {RequestMethod.POST})
+    public ResultContent<Page<GateWayUserInfoModel>> pageGateWayUser(@Parameter(hidden = true) @PageableDefault(page = 0, size = 10, sort = "") Pageable pageable,
+            @Parameter(required = false) GateWayUserInfoSearchParam param) {
+        return gateWayUserInfoService.pageGateWayUser(pageable, param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "删除连接用户")
+    @RequestMapping(value = "deleteMqttUser", method = {RequestMethod.POST})
+    public ResultContent deleteMqttUser(GateWayUserInfoNameParam param) {
+        Assert.hasText(param.getUserName(), "连接账号不能为空");
+        return gateWayUserInfoService.deleteMqttUser(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "启动-停用连接账号")
+    @RequestMapping(value = "updateState", method = {RequestMethod.POST})
+    public ResultContent updateState(GateWayUserInfoNameParam param) {
+        Assert.hasText(param.getUserName(), "连接账号不能为空");
+        return gateWayUserInfoService.updateState(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "查询连接账号")
+    @RequestMapping(value = "getUserByUserName", method = {RequestMethod.POST})
+    public ResultContent<GateWayUserInfoModel> getUserByUserName(GateWayUserInfoNameParam param) {
+        Assert.hasText(param.getUserName(), "连接账号不能为空");
+        return gateWayUserInfoService.getUserByUserName(param.getUserName());
+    }
+
+}

+ 71 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/MqttInfoController.java

@@ -0,0 +1,71 @@
+package com.zhongshu.iot.server.core.controller.hardware;
+
+import com.github.microservice.auth.security.annotations.ResourceAuth;
+import com.github.microservice.auth.security.type.AuthType;
+import com.zhongshu.iot.client.model.mqtt.MqttInfoAddParam;
+import com.zhongshu.iot.client.model.mqtt.MqttInfoModel;
+import com.zhongshu.iot.client.model.mqtt.MqttInfoSimpleModel;
+import com.zhongshu.iot.server.core.service.mqtt.MqttInfoService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * mqtt连接地址管理 服务
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/mqttInfo")
+@RestController
+@Validated
+@Tag(name = "MQTT连接地址管理")
+public class MqttInfoController {
+
+    @Autowired
+    MqttInfoService mqttInfoService;
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "添加地址")
+    @RequestMapping(value = "addMqttInfo", method = {RequestMethod.POST})
+    public ResultContent addMqttInfo(@RequestBody MqttInfoAddParam param) {
+        return mqttInfoService.addMqttInfo(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "所有地址列表")
+    @RequestMapping(value = {"findAllMqttInfo"}, method = {RequestMethod.POST})
+    public ResultContent<List<MqttInfoModel>> findAllMqttInfo() {
+        return mqttInfoService.findAllMqttInfo();
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "所有地址列表(简单信息)")
+    @RequestMapping(value = {"findAllMqttInfoSimple"}, method = {RequestMethod.POST})
+    public ResultContent<List<MqttInfoSimpleModel>> findAllMqttInfoSimple() {
+        return mqttInfoService.findAllMqttInfoSimple();
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "删除地址")
+    @RequestMapping(value = "deleteMqttInfo", method = {RequestMethod.GET})
+    public ResultContent deleteMqttInfo(String id) {
+        return mqttInfoService.deleteMqttInfo(id);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "查询地址详情")
+    @RequestMapping(value = "getMqttInfoById", method = {RequestMethod.GET})
+    public ResultContent<MqttInfoModel> getMqttInfoById(String id) {
+        return mqttInfoService.getMqttInfoById(id);
+    }
+
+}

+ 43 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/OperationLogsController.java

@@ -0,0 +1,43 @@
+package com.zhongshu.iot.server.core.controller.hardware;
+
+import com.github.microservice.auth.security.annotations.ResourceAuth;
+import com.github.microservice.auth.security.type.AuthType;
+import com.zhongshu.iot.client.model.operLogs.OperationLogsModel;
+import com.zhongshu.iot.client.model.operLogs.OperationLogsSearchParam;
+import com.zhongshu.iot.server.core.service.user.OperationLogsService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 日志管理 服务
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/operationLogs")
+@RestController
+@Validated
+@Tag(name = "日志管理")
+public class OperationLogsController {
+
+    @Autowired
+    OperationLogsService operationLogsService;
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "操作列表-分页查询")
+    @RequestMapping(value = {"page"}, method = {RequestMethod.POST})
+    public ResultContent<Page<OperationLogsModel>> page(@Parameter(hidden = true) @PageableDefault(page = 0, size = 10) Pageable pageable,
+            @Parameter(required = false) OperationLogsSearchParam param) {
+        return operationLogsService.pageLogs(pageable, param);
+    }
+}

+ 77 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/hardware/OperationMessageController.java

@@ -0,0 +1,77 @@
+package com.zhongshu.iot.server.core.controller.hardware;
+
+import com.github.microservice.auth.security.annotations.ResourceAuth;
+import com.github.microservice.auth.security.type.AuthType;
+import com.zhongshu.iot.client.model.artemis.OperationMessageModel;
+import com.zhongshu.iot.client.model.artemis.OperationMessageResultModel;
+import com.zhongshu.iot.client.model.artemis.OperationMessageResultSearch;
+import com.zhongshu.iot.client.model.artemis.OperationMessageSearchParam;
+import com.zhongshu.iot.server.core.service.artemis.OperationMessageService;
+import com.zhongshu.iot.server.core.service.mqtt.DeviceInfoService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 指令信息 服务
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/operationMessage")
+@RestController
+@Validated
+@Tag(name = "指令信息管理")
+public class OperationMessageController {
+
+    @Autowired
+    DeviceInfoService deviceInfoService;
+
+    @Autowired
+    OperationMessageService operationMessageService;
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "标准指令已响应")
+    @RequestMapping(value = "receiveMessage", method = {RequestMethod.GET})
+    @Parameter(name = "messageId", description = "指令ID")
+    public ResultContent receiveMessage(String messageId) {
+        return operationMessageService.receiveMessage(messageId);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "指令列表-分页查询")
+    @RequestMapping(value = {"page"}, method = {RequestMethod.POST})
+    public ResultContent<Page<OperationMessageModel>> page(@Parameter(hidden = true) @PageableDefault(page = 0, size = 10) Pageable pageable, @Parameter(required = false) OperationMessageSearchParam param) {
+        return operationMessageService.page(pageable, param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "日志列表-分页查询")
+    @RequestMapping(value = {"pageMessage"}, method = {RequestMethod.POST})
+    public ResultContent<Page<OperationMessageResultModel>> pageMessage(
+            @Parameter(hidden = true) @PageableDefault(page = 0, size = 10) Pageable pageable,
+            @Parameter(required = false) OperationMessageResultSearch param) {
+        return operationMessageService.pageMessage(pageable, param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "得到设备日志详情")
+    @RequestMapping(value = "getMessageById", method = {RequestMethod.GET})
+    public ResultContent<OperationMessageResultModel> getMessageById(
+            @Parameter(name = "id", description = "数据ID", example = "")
+            @RequestParam("id") String id) {
+        return operationMessageService.getMessageById(id);
+    }
+
+
+}

+ 165 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/iot/IotController.java

@@ -0,0 +1,165 @@
+package com.zhongshu.iot.server.core.controller.iot;
+
+import com.github.microservice.auth.security.annotations.ResourceAuth;
+import com.github.microservice.auth.security.type.AuthType;
+import com.zhongshu.iot.client.model.baseParam.NameModel;
+import com.zhongshu.iot.client.model.iot.*;
+import com.zhongshu.iot.server.core.service.iot.IotServiceImpl;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.util.Assert;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 设备管理 服务
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/iot")
+@RestController
+@Validated
+@Tag(name = "物模型-物模型管理")
+public class IotController {
+
+    @Autowired
+    IotServiceImpl iotService;
+
+    //-------------------------- 模版 start------------------------------
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "添加-编辑模版")
+    @RequestMapping(value = "addIotTemplate", method = {RequestMethod.POST})
+    public ResultContent addIotTemplate(@RequestBody IotTemplateParam param) {
+        Assert.hasText(param.getName(), "name不能为空");
+        return iotService.addIotTemplate(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "模版列表-分页查询")
+    @RequestMapping(value = {"pageTemplate"}, method = {RequestMethod.POST})
+    public ResultContent<Page<IotTemplateModel>> pageTemplate(@Parameter(hidden = true) @PageableDefault(page = 0, size = 10) Pageable pageable,
+            @Parameter(required = false) IotTemplateSearch param) {
+        return iotService.pageTemplate(pageable, param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "删除模版")
+    @RequestMapping(value = "deleteTemplate", method = {RequestMethod.GET})
+    public ResultContent deleteTemplate(@Parameter(name = "id", description = "数据id") String id) {
+        return iotService.deleteTemplate(id);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "查询模版详情")
+    @RequestMapping(value = "getIotTemplate", method = {RequestMethod.GET})
+    public ResultContent<IotTemplateModel> getIotTemplate(@Parameter(name = "id", description = "数据id") String id) {
+        return iotService.getIotTemplate(id);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "得到设备的物模型列表")
+    @RequestMapping(value = "getDeviceTemplateList", method = {RequestMethod.GET})
+    public ResultContent<List<IotTemplateModel>> getDeviceTemplateList(
+            @Parameter(name = "deviceId", description = "设备ID") String deviceId
+    ) {
+        return iotService.getDeviceTemplateList(deviceId);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "修改模型名称")
+    @RequestMapping(value = "updateIotTemplateName", method = {RequestMethod.POST})
+    public ResultContent<List<IotTemplateModel>> updateIotTemplateName(@RequestBody NameModel param
+    ) {
+        return iotService.updateIotTemplateName(param);
+    }
+
+    //-------------------------- 模版 end--------------------------------
+
+    //-------------------------- 物模型 start------------------------------
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "设备引用模版")
+    @RequestMapping(value = "deviceBindIotTemplate", method = {RequestMethod.POST})
+    public ResultContent deviceBindIotTemplate(@RequestBody DeviceBindIotTemplate param) {
+        return iotService.deviceBindIotTemplate(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "添加-编辑物属性等")
+    @RequestMapping(value = "addIotMain", method = {RequestMethod.POST})
+    public ResultContent addIotMain(@RequestBody IotMainParam param) {
+        Assert.hasText(param.getName(), "name不能为空");
+        return iotService.addIotMain(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "物模型属性-服务-方法列表-分页查询")
+    @RequestMapping(value = {"pageIotMain"}, method = {RequestMethod.POST})
+    public ResultContent<Page<IotMainModel>> pageIotMain(@Parameter(hidden = true) @PageableDefault(page = 0, size = 10) Pageable pageable,
+            @Parameter(required = false) IotMainSearch param) {
+        return iotService.pageIotMain(pageable, param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "删除物模型")
+    @RequestMapping(value = "deleteIotMain", method = {RequestMethod.GET})
+    public ResultContent deleteIotMain(@Parameter(name = "id", description = "数据id") String id) {
+        return iotService.deleteIotMain(id);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "查询物模型")
+    @RequestMapping(value = "getIotMain", method = {RequestMethod.GET})
+    public ResultContent<IotMainModel> getIotMain(@Parameter(name = "id", description = "数据id") String id) {
+        return iotService.getIotMain(id);
+    }
+
+    //-------------------------- 物模型 end--------------------------------
+
+
+    //-------------------------- Topic start------------------------------
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "添加-编辑Topic", hidden = true)
+    @RequestMapping(value = "addIotTopic", method = {RequestMethod.POST})
+    public ResultContent addIotTopic(@RequestBody IotTopicParam param) {
+        Assert.hasText(param.getTopic(), "topic不能为空");
+        return iotService.addIotTopic(param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "Topic列表-分页查询", hidden = true)
+    @RequestMapping(value = {"pageIotTopic"}, method = {RequestMethod.POST})
+    public ResultContent<Page<IotTopicModel>> pageIotTopic(@Parameter(hidden = true) @PageableDefault(page = 0, size = 10) Pageable pageable,
+            @Parameter(required = false) IotTopicSearch param) {
+        return iotService.pageIotTopic(pageable, param);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "删除Topic", hidden = true)
+    @RequestMapping(value = "deleteIotTopic", method = {RequestMethod.GET})
+    public ResultContent deleteIotTopic(@Parameter(name = "id", description = "数据id") String id) {
+        return iotService.deleteIotTopic(id);
+    }
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "查询Topic", hidden = true)
+    @RequestMapping(value = "getIotTopic", method = {RequestMethod.GET})
+    public ResultContent<IotTopicModel> getIotTopic(@Parameter(name = "id", description = "数据id") String id) {
+        return iotService.getIotTopic(id);
+    }
+
+    //-------------------------- Topic end--------------------------------
+
+}

+ 55 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/iot/IotSendController.java

@@ -0,0 +1,55 @@
+package com.zhongshu.iot.server.core.controller.iot;
+
+import com.github.microservice.auth.security.annotations.ResourceAuth;
+import com.github.microservice.auth.security.helper.AuthHelper;
+import com.github.microservice.auth.security.type.AuthType;
+import com.github.microservice.models.iot.IotSendParam;
+import com.zhongshu.iot.server.core.service.iot.IotSendMessageService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import io.swagger.v3.oas.annotations.Hidden;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 物模型下发数据
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+@RequestMapping("/iotSend")
+@RestController
+@Validated
+@Tag(name = "物模型-下发数据")
+public class IotSendController {
+
+    @Autowired
+    IotSendMessageService iotSendMessageService;
+
+    @Autowired
+    AuthHelper authHelper;
+
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "下发消息")
+    @RequestMapping(value = "sendIotMessage", method = {RequestMethod.POST})
+    public ResultContent sendIotMessage(@RequestBody @Valid IotSendParam param) {
+        param.setUserId(authHelper.getCurrentUser().getUserId());
+        return iotSendMessageService.sendIotMessage(param);
+    }
+
+    @Operation(summary = "下发消息")
+    @RequestMapping(value = "/free/sendIotMessage", method = {RequestMethod.POST})
+    @Hidden
+    public ResultContent sendIotMessageFree(@RequestBody @Valid IotSendParam param) {
+        return iotSendMessageService.sendIotMessage(param);
+    }
+
+}

+ 35 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/payment/HxzController.java

@@ -0,0 +1,35 @@
+package com.zhongshu.iot.server.core.controller.payment;
+
+
+import com.github.microservice.models.hxz.ServerTimeModel;
+import com.github.microservice.models.hxz.ServerTimeResult;
+import com.zhongshu.iot.server.core.service.payment.HxzService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+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;
+
+@Slf4j
+@RestController
+@RequestMapping("hxz/v1")
+@Tag(name = "云版消费机接口-HXZ")
+public class HxzController {
+
+    @Autowired
+    private HxzService hxzService;
+
+    /**
+     * 系统时间
+     */
+    @Operation(summary = "获取服务器时间接口")
+    @PostMapping(value = "ServerTime", consumes = MediaType.APPLICATION_JSON_VALUE)
+    public ServerTimeResult serverTime(@RequestBody ServerTimeModel serverTimeModel) {
+        return hxzService.serverTime(serverTimeModel);
+    }
+
+}

+ 21 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/event/DeviceOnLineTimeChangeEvent.java

@@ -0,0 +1,21 @@
+package com.zhongshu.iot.server.core.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * 设备状态改变
+ *
+ * @author TRX
+ * @date 2024/6/26
+ */
+public class DeviceOnLineTimeChangeEvent extends ApplicationEvent {
+
+    @Getter
+    private String deviceId;
+
+    public DeviceOnLineTimeChangeEvent(Object source, String deviceId) {
+        super(source);
+        this.deviceId = deviceId;
+    }
+}

+ 21 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/event/DeviceStateChangeEvent.java

@@ -0,0 +1,21 @@
+package com.zhongshu.iot.server.core.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * 设备状态改变
+ *
+ * @author TRX
+ * @date 2024/6/26
+ */
+public class DeviceStateChangeEvent extends ApplicationEvent {
+
+    @Getter
+    private String deviceId;
+
+    public DeviceStateChangeEvent(Object source, String deviceId) {
+        super(source);
+        this.deviceId = deviceId;
+    }
+}

+ 22 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/event/DeviceSyncEvent.java

@@ -0,0 +1,22 @@
+package com.zhongshu.iot.server.core.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/6/26
+ */
+public class DeviceSyncEvent extends ApplicationEvent {
+
+    @Getter
+    private List<String> deviceIds = new ArrayList<String>();
+
+    public DeviceSyncEvent(Object source, List<String> deviceIds) {
+        super(source);
+        this.deviceIds = deviceIds;
+    }
+}

+ 21 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/event/GateWaySyncEvent.java

@@ -0,0 +1,21 @@
+package com.zhongshu.iot.server.core.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/6/26
+ */
+public class GateWaySyncEvent extends ApplicationEvent {
+
+    @Getter
+    private List<String> gateWayIds;
+
+    public GateWaySyncEvent(Object source, List<String> gateWayIds) {
+        super(source);
+        this.gateWayIds = gateWayIds;
+    }
+}

+ 113 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/ApiRequestService.java

@@ -0,0 +1,113 @@
+package com.zhongshu.iot.server.core.httpRequest;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.zhongshu.iot.server.core.dao.payment.RequestInfoDao;
+import com.zhongshu.iot.server.core.httpRequest.apiConf.APIResponseModel;
+import com.zhongshu.iot.server.core.httpRequest.apiConf.ApiConfParam;
+import com.zhongshu.iot.server.core.httpRequest.conf.FullCardAPIConfig;
+import com.zhongshu.iot.server.core.httpRequest.conf.FullCardConf;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.service.other.RequestInfoService;
+import com.zhongshu.iot.server.core.util.net.apache.HttpClientUtil;
+import com.zhongshu.iot.server.core.util.net.apache.HttpModel;
+import com.zhongshu.iot.server.core.util.net.apache.MethodType;
+import com.zhongshu.iot.server.core.util.net.apache.ResponseModel;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StopWatch;
+
+/**
+ * @author TRX
+ * @date 2024/6/25
+ */
+@Slf4j
+@Service
+public class ApiRequestService extends SuperService {
+
+    @Autowired
+    FullCardConf fullCardConf;
+
+    @Autowired
+    RequestInfoDao requestInfoDao;
+
+    @Autowired
+    RequestInfoService requestInfoService;
+
+    /**
+     * 发送请求
+     *
+     * @param apiName
+     * @param data
+     * @return
+     */
+    public APIResponseModel sendFullCardAPI(String apiName, Object data) {
+        APIResponseModel responseModel = new APIResponseModel();
+        if (StringUtils.isEmpty(fullCardConf.getUrl())) {
+            responseModel.setIsFailed("未配置物联网平台URL");
+            return responseModel;
+        }
+        try {
+            ApiConfParam apiConfParam = FullCardAPIConfig.getApiConfParam(apiName);
+            if (ObjectUtils.isEmpty(apiConfParam)) {
+                responseModel.setIsFailed("未找到API配置");
+                return responseModel;
+            }
+            StopWatch stopWatch = new StopWatch();
+            stopWatch.start();
+            String url = fullCardConf.getUrl() + fullCardConf.getCardServerName() + apiConfParam.getApiName();
+            ResponseModel request = HttpClientUtil.request(HttpModel.builder()
+                    .url(url).method(apiConfParam.getMethodType()).charset("utf-8").body(data).build());
+            if (request.getCode() == 200) {
+                responseModel = BeanUtil.copyProperties(request.getBody(), APIResponseModel.class);
+            } else {
+                responseModel = BeanUtil.copyProperties(request.getBody(), APIResponseModel.class);
+            }
+            responseModel.setParam(apiConfParam);
+            stopWatch.stop();
+            responseModel.setMillis(stopWatch.getTotalTimeMillis());
+        } catch (Exception e) {
+            e.printStackTrace();
+            responseModel.setIsFailed(String.format("请求出错:%s", e.getMessage()));
+            return responseModel;
+        }
+        // 记录请求日志
+        requestInfoService.addRequestInfo(data, responseModel);
+        return responseModel;
+    }
+
+
+    public APIResponseModel requestAPI(String url, Object data) {
+        APIResponseModel responseModel = new APIResponseModel();
+        try {
+            ApiConfParam apiConfParam = new ApiConfParam();
+            apiConfParam.setApiName(url);
+            apiConfParam.setMethodType(MethodType.Json);
+            StopWatch stopWatch = new StopWatch();
+            stopWatch.start();
+            ResponseModel request = HttpClientUtil.request(HttpModel.builder()
+                    .url(url).method(apiConfParam.getMethodType()).charset("utf-8").body(data).build());
+            int code = request.getCode();
+            if (code == 200) {
+                responseModel = BeanUtil.copyProperties(request.getBody(), APIResponseModel.class);
+            } else if (code == 404) {
+                responseModel.setIsFailed("404 not found");
+            } else {
+                responseModel = BeanUtil.copyProperties(request.getBody(), APIResponseModel.class);
+            }
+            responseModel.setParam(apiConfParam);
+            stopWatch.stop();
+            responseModel.setMillis(stopWatch.getTotalTimeMillis());
+        } catch (Exception e) {
+            e.printStackTrace();
+            responseModel.setIsFailed(String.format("请求出错:%s", e.getMessage()));
+            return responseModel;
+        }
+        // 记录请求日志
+        requestInfoService.addRequestInfo(data, responseModel);
+        return responseModel;
+    }
+
+}

+ 76 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/apiConf/APIResponseModel.java

@@ -0,0 +1,76 @@
+package com.zhongshu.iot.server.core.httpRequest.apiConf;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author TRX
+ * @date 2024/6/25
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class APIResponseModel {
+
+    @Schema(description = "是否错误")
+    private boolean failed;
+
+    @Schema(description = "是否成功")
+    private boolean success;
+
+    @Schema(description = "返回消息")
+    private String msg;
+
+    private String state;
+
+    private String content;
+
+    @Schema(description = "访问的URL")
+    private ApiConfParam param;
+
+    private Object data;
+
+    @Schema(description = "请求耗时:毫秒")
+    private Long millis;
+
+    public <T> T toBean(Class<T> tClass) {
+        if (content != null) {
+            return JSONUtil.toBean(content, tClass);
+        }
+        return null;
+    }
+
+    public JSONObject toJson() {
+        if (content != null) {
+            return JSONUtil.parseObj(content);
+        }
+        return null;
+    }
+
+    public boolean isSuccess() {
+        if (StringUtils.isNotEmpty(this.state) && "Fail".equals(state)) {
+            return false;
+        }
+        return success;
+    }
+
+    public boolean isFailed() {
+        return !this.isSuccess();
+    }
+
+    public void setIsFailed() {
+        this.success = Boolean.FALSE;
+        this.failed = Boolean.TRUE;
+        this.state = "Fail";
+    }
+
+    public void setIsFailed(String msg) {
+        this.setIsFailed();
+        this.msg = msg;
+    }
+}

+ 26 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/apiConf/ApiConfParam.java

@@ -0,0 +1,26 @@
+package com.zhongshu.iot.server.core.httpRequest.apiConf;
+
+import com.zhongshu.iot.server.core.util.net.apache.MethodType;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author TRX
+ * @date 2024/6/25
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class ApiConfParam {
+
+    @Schema(description = "API名称")
+    private String apiName;
+
+    @Schema(description = "方式")
+    private MethodType methodType;
+
+}

+ 68 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/conf/FullCardAPIConfig.java

@@ -0,0 +1,68 @@
+package com.zhongshu.iot.server.core.httpRequest.conf;
+
+import com.zhongshu.iot.server.core.httpRequest.apiConf.ApiConfParam;
+import com.zhongshu.iot.server.core.util.net.apache.MethodType;
+
+import java.util.HashMap;
+
+/**
+ * 物联网平台的 APIP诶这
+ *
+ * @author TRX
+ * @date 2024/6/25
+ */
+public class FullCardAPIConfig {
+
+    public static final String api = "";
+
+    // 测试发送消息
+    public static final String sendMessage = api + "/mqttMessage/sendMessage";
+
+    public static final String ping = api + "/test/free/ping";
+
+    //-----------------------云版消费机 start -----------------------
+    public static final String ServerTime = api + "/hxz/v1/ServerTime";
+    public static final String ConsumTransactions = api + "/hxz/v1/ConsumTransactions";
+    public static final String orderQuery = api + "/hxz/v1/orderQuery";
+
+    public static final String QRCodeTransaction = api + "/hxz/v1/QRCodeTransaction";
+    public static final String TransactionInquiry = api + "/hxz/v1/TransactionInquiry";
+
+    // 全卡业务平台 start -----------------
+    // 同步设备
+    public static final String deviceSync = api + "/deviceSync/syncDevices";
+    public static final String syncGateWays = api + "/deviceSync/syncGateWays";
+
+    public static final String syncDeviceOnLineState = api + "/deviceSync/syncDeviceOnLineState";
+    public static final String lastDeviceOnLineTime = api + "/deviceSync/lastDeviceOnLineTime";
+
+    public static final HashMap<String, ApiConfParam> map = new HashMap<>();
+
+    static {
+        map.put(sendMessage, ApiConfParam.builder().apiName(sendMessage).methodType(MethodType.Json).build());
+        map.put(ping, ApiConfParam.builder().apiName(ping).methodType(MethodType.Get).build());
+
+        // ----------------------------云版消费机 start -----------------
+        map.put(ServerTime, ApiConfParam.builder().apiName(ServerTime).methodType(MethodType.Json).build());
+
+        // 刷卡支付相关的接口配置
+        map.put(ConsumTransactions, ApiConfParam.builder().apiName(ConsumTransactions).methodType(MethodType.Json).build());
+        map.put(orderQuery, ApiConfParam.builder().apiName(orderQuery).methodType(MethodType.Json).build());
+
+        // B扫C功能接口配置
+        map.put(QRCodeTransaction, ApiConfParam.builder().apiName(QRCodeTransaction).methodType(MethodType.Json).build());
+        map.put(TransactionInquiry, ApiConfParam.builder().apiName(TransactionInquiry).methodType(MethodType.Json).build());
+
+
+        // ----------------------------全卡项目 start -----------------
+        map.put(deviceSync, ApiConfParam.builder().apiName(deviceSync).methodType(MethodType.Json).build());
+        map.put(syncGateWays, ApiConfParam.builder().apiName(syncGateWays).methodType(MethodType.Json).build());
+        map.put(syncDeviceOnLineState, ApiConfParam.builder().apiName(syncDeviceOnLineState).methodType(MethodType.Json).build());
+        map.put(lastDeviceOnLineTime, ApiConfParam.builder().apiName(lastDeviceOnLineTime).methodType(MethodType.Json).build());
+    }
+
+    public static ApiConfParam getApiConfParam(String apiName) {
+        return map.get(apiName);
+    }
+
+}

+ 24 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/conf/FullCardConf.java

@@ -0,0 +1,24 @@
+package com.zhongshu.iot.server.core.httpRequest.conf;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@NoArgsConstructor
+@AllArgsConstructor
+@ConfigurationProperties(prefix = "fullcard")
+public class FullCardConf {
+    /**
+     * 全卡业务平台平台地址配置
+     */
+    private String url = "";
+
+    /**
+     * 全卡服务名称
+     */
+    private String cardServerName = "";
+}

+ 2 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/httpRequest/package-info.java

@@ -0,0 +1,2 @@
+package com.zhongshu.iot.server.core.httpRequest;
+//物联网平台的相关配置

+ 88 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/artemis/ArtemisListenerService.java

@@ -0,0 +1,88 @@
+package com.zhongshu.iot.server.core.service.artemis;
+
+import com.zhongshu.iot.server.core.service.mqtt.GateWayInfoService;
+import com.zhongshu.iot.server.core.util.mqtt.mqttConfig.client.MQClient;
+import com.zhongshu.iot.server.core.util.mqtt.mqttConfig.constant.MQConstant;
+import jakarta.jms.Destination;
+import jakarta.jms.Message;
+import jakarta.jms.Session;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
+import org.apache.activemq.artemis.jms.client.ActiveMQMessage;
+import org.apache.activemq.artemis.jms.client.ActiveMQTopic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jms.annotation.JmsListener;
+import org.springframework.stereotype.Service;
+
+import java.util.Enumeration;
+
+/**
+ * @author TRX
+ * @date 2024/6/27
+ */
+@Slf4j
+@Service
+public class ArtemisListenerService {
+
+    @Autowired
+    MQClient mqClient;
+
+    @Autowired
+    OperationMessageService operationMessageService;
+
+    @Autowired
+    GateWayInfoService gateWayInfoService;
+
+    // 网关来的消息
+    @JmsListener(destination = "/v1/gateway/#", containerFactory = MQConstant.TopicListenerContainerFactory)
+    @JmsListener(destination = ".v1.gateway.#", containerFactory = MQConstant.TopicListenerContainerFactory)
+    public void receiveGateWayMessage(Message message) {
+        operationMessageService.handlerGateWayMessage(message);
+    }
+
+    // 设备来的消息
+    @JmsListener(destination = "/v1/device/#", containerFactory = MQConstant.TopicListenerContainerFactory)
+    @JmsListener(destination = ".v1.device.#", containerFactory = MQConstant.TopicListenerContainerFactory)
+    public void receiveDeviceMessage(Message message) {
+        operationMessageService.handlerGateWayMessage(message);
+    }
+
+    // 终端连接的消息
+    @JmsListener(destination = "$sys.mqtt.sessions", containerFactory = MQConstant.TopicListenerContainerFactory)
+    public void receiveOnLineChangeMessage(Message message, Session session) {
+        try {
+            ActiveMQTopic activeMQTopic = (ActiveMQTopic) message.getJMSDestination();
+            String topicName = activeMQTopic.getTopicName();
+            if (!topicName.equals("$sys.mqtt.sessions")) {
+                return;
+            }
+            log.info("==sezz {}", session.getTransacted());
+            String messageId = message.getJMSMessageID();
+            String clientId = message.getStringProperty("_AMQ_LVQ_NAME");
+            log.info("receiveMessage {} 消息监听clientId: {}", messageId, clientId);
+            log.info("Topic: {}", topicName);
+            Enumeration enumeration = message.getPropertyNames();
+            if (message instanceof ActiveMQBuffer) {
+                log.info("---------------------");
+            }
+            Destination destination = message.getJMSDestination();
+
+            log.info("---------------------AA {} ", destination.toString());
+            ActiveMQMessage activeMQBytesMessage = (ActiveMQMessage) message;
+            ActiveMQBuffer buffer = activeMQBytesMessage.getCoreMessage().getBodyBuffer();
+
+            activeMQBytesMessage.checkBuffer();
+            log.info("bb: {}", buffer.readableBytes());
+            byte[] bytes = new byte[buffer.readableBytes()];
+            buffer.readBytes(bytes);
+
+            // 处理消息,解析出会话信息
+            String sessionInfo = new String(bytes, "gbk");
+            log.info("-------------------: {} {}", sessionInfo, sessionInfo.length());
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}

+ 681 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/artemis/OperationMessageService.java

@@ -0,0 +1,681 @@
+package com.zhongshu.iot.server.core.service.artemis;
+
+import cn.hutool.core.date.StopWatch;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.github.microservice.models.hxz.base.HxzBaseResult;
+import com.google.gson.JsonObject;
+import com.zhongshu.iot.client.model.artemis.OperationMessageModel;
+import com.zhongshu.iot.client.model.artemis.OperationMessageResultModel;
+import com.zhongshu.iot.client.model.artemis.OperationMessageResultSearch;
+import com.zhongshu.iot.client.model.artemis.OperationMessageSearchParam;
+import com.zhongshu.iot.client.model.mqtt.SendMessageModel;
+import com.zhongshu.iot.client.type.FunctionType;
+import com.zhongshu.iot.client.type.OperationType;
+import com.zhongshu.iot.server.core.dao.iot.IotMainDao;
+import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.OperationMessageDao;
+import com.zhongshu.iot.server.core.dao.mqtt.OperationMessageResultDao;
+import com.zhongshu.iot.server.core.dao.other.ExecuteMethodInfoDao;
+import com.zhongshu.iot.server.core.domain.iot.IotMain;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.DeviceInfo;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.OperationMessage;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.OperationMessageResult;
+import com.zhongshu.iot.server.core.domain.other.ExecuteMethodInfo;
+import com.zhongshu.iot.server.core.httpRequest.ApiRequestService;
+import com.zhongshu.iot.server.core.httpRequest.apiConf.APIResponseModel;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.service.iot.IotDataVerifyService;
+import com.zhongshu.iot.server.core.service.mqtt.DeviceInfoService;
+import com.zhongshu.iot.server.core.service.mqtt.GateWayInfoService;
+import com.zhongshu.iot.server.core.util.CommonUtil;
+import com.zhongshu.iot.server.core.util.DateUtils;
+import com.zhongshu.iot.server.core.util.TokenUtil;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.mqtt.MqttTopicUtils;
+import com.zhongshu.iot.server.core.util.mqtt.mqttConfig.client.MQClient;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import jakarta.jms.Message;
+import jakarta.jms.TextMessage;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.activemq.artemis.jms.client.ActiveMQBytesMessage;
+import org.apache.activemq.artemis.jms.client.ActiveMQTopic;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * 指令数据管理
+ *
+ * @author TRX
+ * @date 2024/5/14
+ */
+@Slf4j
+@Service
+public class OperationMessageService {
+
+    @Autowired
+    private OperationMessageDao operationMessageDao;
+
+    @Autowired
+    private DeviceInfoService deviceInfoService;
+
+    @Autowired
+    private GateWayInfoService gateWayInfoService;
+
+    @Autowired
+    private MQClient mqClient;
+
+    @Autowired
+    private DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    private ExecuteMethodInfoDao executeMethodInfoDao;
+
+    @Autowired
+    private ApplicationContext applicationContext;
+
+    @Autowired
+    private IotMainDao iotMainDao;
+
+    @Autowired
+    private OperationMessageResultDao operationMessageResultDao;
+
+    @Autowired
+    private ApiRequestService apiRequestService;
+
+    @Autowired
+    private IotDataVerifyService iotDataVerifyService;
+
+    @Value("${artemisstore.time}")
+    public Long ttlMill = 30 * 24L * 60 * 60 * 1000L;
+
+    /**
+     * 发送指令
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent sendMessage(SendMessageModel param) {
+        String msg = "发送成功";
+        try {
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.set("id", UUID.randomUUID().toString());
+            jsonObject.set("data", param.getMessage());
+            jsonObject.set("timeStr", DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyySSS));
+            jsonObject.set("time", System.currentTimeMillis());
+            jsonObject.set("ttl", 10 * 1000);
+            mqClient.sendObject(param.getTopic(), jsonObject.toString());
+            log.info("mqtt msg 发送成功");
+        } catch (Exception e) {
+            e.printStackTrace();
+            msg = "发送失败: " + e.getMessage();
+        }
+        return ResultContent.buildSuccess(msg);
+    }
+
+    /**
+     * 给设备下发指令
+     *
+     * @param deviceId 设备ID
+     * @param command  指令,如:on,off
+     * @param data     指令数据
+     * @return
+     */
+    public ResultContent sendMessage(String deviceId, String command, JSONObject data) {
+        DeviceInfo deviceInfo = deviceInfoDao.findTopById(deviceId);
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format("设备不存在:%s", deviceId));
+        }
+        try {
+            // 消息的TTL时间
+            Long ttl = 10 * 1000L;
+            // 消息的ID
+            String messageId = "";
+            OperationMessage message = new OperationMessage();
+            if (data.containsKey("messageId")) {
+                messageId = data.getStr("messageId");
+            } else {
+                messageId = UUID.randomUUID().toString();
+                data.append("messageId", messageId);
+            }
+            String topic = MqttTopicUtils.buildDeviceOperationInstructionsTopic(deviceId, command);
+
+            message.setMessageId(messageId);
+            message.setData(data);
+            message.setTopic(topic);
+            message.setDeviceId(deviceId);
+            message.setDeviceInfo(deviceInfo);
+            message.setIsReceive(Boolean.FALSE);
+            message.setTtlTime(ttl);
+            message.setTime(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+
+            JsonObject jsonObject = new JsonObject();
+            jsonObject.addProperty("id", messageId);
+            jsonObject.addProperty("data", data.toString());
+            jsonObject.addProperty("time", System.currentTimeMillis());
+            jsonObject.addProperty("ttl", ttl);
+            mqClient.sendObject(topic, jsonObject.toString());
+            log.info("mqtt msg 发送成功");
+
+            operationMessageDao.save(message);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * /v1/gateway/#
+     * 处理网关MQTT信息
+     *
+     * @param message
+     */
+    public void handlerGateWayMessage(Message message) {
+        // 处理接收到的消息
+        String msg = "";
+        try {
+            ActiveMQTopic activeMQTopic = (ActiveMQTopic) message.getJMSDestination();
+            String topicName = activeMQTopic.getTopicName();
+            if (topicName.equals("activemq.notifications")) {
+                return;
+            }
+            String messageId = message.getJMSMessageID();
+            String clientId = message.getStringProperty("__AMQ_CID");
+//            log.info("receiveMessage {} 消息监听clientId: {}", messageId, clientId);
+            if (StringUtils.isNotEmpty(topicName) && topicName.endsWith("reply")) {
+                // 这是响应的数据,不处理
+                return;
+            }
+            String messageClass = "";
+            if (message instanceof ActiveMQBytesMessage) {
+                ActiveMQBytesMessage activeMQBytesMessage = (ActiveMQBytesMessage) message;
+                messageClass = ActiveMQBytesMessage.class.getSimpleName();
+                byte[] messageBody = new byte[(int) activeMQBytesMessage.getBodyLength()];
+                // 读取消息内容到字节数组
+                activeMQBytesMessage.readBytes(messageBody);
+                msg = new String(messageBody);
+            }
+            if (message instanceof TextMessage) {
+                messageClass = TextMessage.class.getSimpleName();
+                TextMessage textMessage = (TextMessage) message;
+                msg = textMessage.getText();
+            }
+            // 提取内容里面的数据
+            JSONObject jsonObject = JSONUtil.parseObj(msg);
+            String id = jsonObject.getStr("id");
+            Long time = jsonObject.getLong("time");
+            Long ttl = jsonObject.getLong("ttl");
+            String event = jsonObject.getStr("event");
+            if (ObjectUtils.isEmpty(event)) {
+                String[] arr = topicName.split("/");
+                event = arr[arr.length - 1];
+                jsonObject.put("event", event);
+            }
+            // 是否是测试
+            boolean isTest = true;
+            // ping不执行
+            if (isTest && event.equals("ping")) {
+                return;
+            }
+            if (isTest) {
+                id = CommonUtil.UUID();
+            }
+            log.info("Topic: {} {}", event, topicName);
+
+            String gateWayId = jsonObject.getStr("gatewayId");
+            String deviceId = jsonObject.getStr("deviceId");
+            boolean isTimeOut = true;
+            if (time == null) {
+                time = System.currentTimeMillis();
+            }
+            if (System.currentTimeMillis() > (time + ttl)) {
+                isTimeOut = true;
+            }
+            log.info("消息内容: {}", msg);
+
+            String timeStr = DateUtils.paresTime(time, DateUtils.patternyyyySSS);
+            jsonObject.set("timeStr", timeStr);
+
+            // --------------------消息处理 start------------------
+            OperationMessage operationMessage = new OperationMessage();
+            operationMessage.setMessageId(messageId); // 消息ID
+            operationMessage.setClientId(clientId); // 终端ID
+            operationMessage.setTopic(topicName); // topic名称
+            operationMessage.setOperationType(OperationType.Sub);
+            operationMessage.setMessageClass(messageClass);
+            operationMessage.setData(jsonObject);
+            operationMessage.setTtlTime(ttl);
+            operationMessage.setSendTime(time);
+            operationMessage.setDataId(id);
+            operationMessage.setIsTimeOut(isTimeOut);
+            operationMessage.setEvent(event);
+            operationMessage.setGateWayId(gateWayId);
+            if (ObjectUtils.isNotEmpty(deviceId)) {
+                operationMessage.setDeviceId(deviceId);
+            }
+            initAddOperationMessage(operationMessage);
+        } catch (Exception e) {
+            log.error("消息出错了: {}", msg);
+            e.printStackTrace();
+        }
+    }
+
+    private ResultContent initAddOperationMessage(OperationMessage entity) {
+        // 判断DataId 是否存在,存在就不处理
+        String dataId = entity.getDataId();
+        try {
+            String token = TokenUtil.create();
+            OperationMessage temp = operationMessageDao.init(dataId, token);
+            if (ObjectUtils.isNotEmpty(temp)) {
+                log.info("获取到执行锁,开始执行");
+                entity.setId(temp.getId());
+                entity.setToken(temp.getToken());
+                entity.setCreateTime(System.currentTimeMillis());
+                addOperationMessage(entity);
+            } else {
+                log.warn("未获取到执行锁,跳过执行");
+            }
+        } catch (Exception e) {
+            log.error("错误: {}", e.getMessage());
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 添加
+     *
+     * @param entity
+     * @return
+     */
+    public ResultContent addOperationMessage(OperationMessage entity) {
+        // 服务器接收消息的时间
+        entity.setTime(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyySSS));
+        // 消息的过期时间
+        entity.setTtl(new Date(System.currentTimeMillis() + ttlMill));
+        boolean isTimeOut = entity.getIsTimeOut();
+        isTimeOut = false;
+        if (entity.getEvent().equals("ping")) {
+            Date da = new Date(System.currentTimeMillis() + 10 * 60 * 1000);
+            entity.setTtl(da);
+        }
+        if (entity.getIsTimeOut()) {
+            entity.setHandleMsg("超时不处理");
+        }
+        // TODO
+        if (!isTimeOut) {
+            // 未超时,处理消息
+            handleOperationMessage(entity);
+        } else {
+            // 超时
+            operationMessageDao.save(entity);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 处理消息
+     *
+     * @param entity
+     * @return
+     */
+    public ResultContent handleOperationMessage(OperationMessage entity) {
+        String event = entity.getEvent();
+        JSONObject json = (JSONObject) entity.getData();
+        if (json.containsKey("data")) {
+            StopWatch stopWatch = new StopWatch();
+            stopWatch.start();
+
+            boolean isHandleSuccess = true;
+            String handleMsg = "处理成功";
+            // 业务处理的消息内容
+            JSONObject requestData = (JSONObject) json.get("data");
+            requestData.put("mqttDataId", entity.getDataId());
+            requestData.put("GateWayId", entity.getGateWayId());
+            String DeviceId = requestData.getStr("DeviceId");
+            if (StringUtils.isEmpty(DeviceId)) {
+                DeviceId = entity.getDeviceId();
+                requestData.put("DeviceId", DeviceId);
+            }
+            entity.setDeviceId(DeviceId);
+
+            // 查询有对应事件的设备
+            List<IotMain> events = iotMainDao.findByRealIotTopicAndFunctionTypeOrderByCreateTimeAsc(entity.getTopic(), FunctionType.Event);
+            if (ObjectUtils.isNotEmpty(events)) {
+                for (IotMain iotMain : events) {
+                    executeOperationMessage(entity, requestData, iotMain);
+                }
+            } else {
+                isHandleSuccess = false;
+                handleMsg = "无对应物模型事件处理";
+            }
+            // 如果是ping,则平台也会处理
+            if (event != null && event.equals("ping")) {
+                pingHandler(entity, requestData);
+            } else if (event != null && event.equals("ServerTime")) {
+                pingHandler(entity, requestData);
+            } else {
+                // 业务处理失败
+                entity.setIsHandleSuccess(isHandleSuccess);
+                entity.setHandleMsg(handleMsg);
+                entity.setReTime(System.currentTimeMillis());
+                entity.setReTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyySSS));
+                operationMessageDao.save(entity);
+            }
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 处理ping消息
+     *
+     * @param entity
+     * @param requestData
+     * @return
+     */
+    public ResultContent pingHandler(OperationMessage entity, JSONObject requestData) {
+        String event = entity.getEvent();
+        Object result = null;
+        StopWatch stopWatch = new StopWatch();
+        stopWatch.start();
+
+        boolean isHandleSuccess = true;
+        String handleMsg = "处理成功";
+        // 查询业务处理端信息
+        try {
+            ExecuteMethodInfo executeMethodInfo = executeMethodInfoDao.findTopByEvent(event);
+            if (ObjectUtils.isNotEmpty(executeMethodInfo)) {
+                String dataStr = JSONUtil.toJsonStr(requestData);
+
+                String beanName = executeMethodInfo.getBeanName();
+                String methodName = executeMethodInfo.getMethodName();
+                Class c = applicationContext.getBean(beanName).getClass();
+                SuperService t = (SuperService) applicationContext.getBean(beanName);
+                Method method = c.getMethod(methodName, String.class);
+                ResultContent<Object> resultContent = (ResultContent<Object>) method.invoke(t, dataStr);
+                if (resultContent.isSuccess()) {
+                    isHandleSuccess = true;
+                    handleMsg = "处理成功";
+                    result = resultContent.getContent();
+                } else {
+                    isHandleSuccess = false;
+                    handleMsg = resultContent.getMsg();
+                }
+                entity.setBeanName(beanName);
+                entity.setMethodName(methodName);
+            } else {
+                isHandleSuccess = false;
+                handleMsg = "消息处理方法未找到";
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            isHandleSuccess = false;
+            handleMsg = String.format("业务处理出错:%S", e.getMessage());
+        }
+        stopWatch.stop();
+        entity.setHandlerTime(stopWatch.getLastTaskTimeMillis());
+        // 返回结果
+        entity.setResultData(result);
+        // 业务处理失败
+        entity.setIsHandleSuccess(isHandleSuccess);
+        entity.setHandleMsg(handleMsg);
+        log.info("消息处理结果: {} {}", isHandleSuccess, handleMsg);
+        if (isHandleSuccess) {
+            // 处理成功,返回响应
+            return responseMessage(entity);
+        } else {
+            // 处理失败,记录数据
+            entity.setReTime(System.currentTimeMillis());
+            entity.setReTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyySSS));
+            operationMessageDao.save(entity);
+            return ResultContent.buildFail(handleMsg);
+        }
+    }
+
+    /**
+     * 执行
+     *
+     * @param entity
+     * @param data
+     * @param iotMain
+     */
+    public void executeOperationMessage(OperationMessage entity, JSONObject data, IotMain iotMain) {
+        CompletableFuture.runAsync(() -> {
+            OperationMessageResult messageResult = new OperationMessageResult();
+            messageResult.setOperationMessage(entity);
+            messageResult.setIotMain(iotMain);
+            // 设备ID
+            messageResult.setDeviceId(iotMain.getDeviceId());
+            // 分组code
+            messageResult.setProjectCode(iotMain.getProjectCode());
+            messageResult.setGateWayId(entity.getGateWayId());
+            messageResult.setRealIotTopic(iotMain.getRealIotTopic());
+
+            boolean isHandleSuccess = true;
+            String handleMsg = "处理成功";
+
+            Long handlerTime = System.currentTimeMillis();
+            HxzBaseResult handleError = new HxzBaseResult();
+            // 是否需要返回
+            Boolean isNeedReplay = Boolean.TRUE;
+            String remoteUrl = iotMain.getRemoteUrl();
+            if (StringUtils.isEmpty(remoteUrl)) {
+                isHandleSuccess = false;
+                isNeedReplay = Boolean.TRUE;
+                handleMsg = "业务处理地址为空";
+            } else {
+                // 验证参数
+                ResultContent verifyContent = iotDataVerifyService.verifyIotParam(iotMain, data);
+                if (verifyContent.isSuccess()) {
+                    // 参数验证成功
+                    org.springframework.util.StopWatch stopWatch = new org.springframework.util.StopWatch();
+                    stopWatch.start();
+
+                    // 请求业务端处理
+                    APIResponseModel apiResponseModel = apiRequestService.requestAPI(remoteUrl, data);
+                    messageResult.setResultData(apiResponseModel);
+                    if (apiResponseModel.isSuccess()) {
+                        // 处理成功
+                        String content = apiResponseModel.getContent();
+                        JSONObject object = new JSONObject();
+                        if (ObjectUtils.isNotEmpty(content)) {
+                            object = JSONUtil.parseObj(content);
+                        }
+                        messageResult.setReplayData(object);
+                        isHandleSuccess = true;
+                        if (iotMain.getIsReturnData() != null && iotMain.getIsReturnData()) {
+                            isNeedReplay = true;
+                        } else {
+                            isNeedReplay = false;
+                            messageResult.setIsResult(Boolean.TRUE);
+                            messageResult.setReIsSuccess(Boolean.FALSE);
+                            messageResult.setReMsg("无需返回");
+                        }
+                    } else {
+                        // 业务响应失败
+                        isHandleSuccess = false;
+                        handleMsg = apiResponseModel.getMsg();
+                    }
+                    stopWatch.stop();
+                    messageResult.setUseTime(stopWatch.getTotalTimeMillis());
+                } else {
+                    // 参数验证失败
+                    isHandleSuccess = false;
+                    isNeedReplay = Boolean.TRUE;
+                    handleMsg = verifyContent.getMsg();
+                }
+            }
+
+            if (!isHandleSuccess) {
+                handleError.setFailed(handleMsg);
+                JSONObject replayData = JSONUtil.parseObj(handleError);
+                messageResult.setReplayData(replayData);
+            }
+
+            if (isNeedReplay) {
+                // 返回
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("id", entity.getDataId());
+                jsonObject.put("data", messageResult.getReplayData());
+                jsonObject.put("time", System.currentTimeMillis());
+                jsonObject.put("ttl", entity.getTtlTime());
+                jsonObject.put("event", entity.getEvent());
+                jsonObject.put("deviceId", entity.getDeviceId());
+                jsonObject.put("gateWayId", entity.getGateWayId());
+                String reTopic = String.format("%s/reply", entity.getTopic());
+                String reMsg = "响应成功";
+                Boolean reIsSuccess = Boolean.TRUE;
+                try {
+                    mqClient.sendObject(reTopic, JSONUtil.toJsonStr(jsonObject), entity.getDataId());
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    reIsSuccess = Boolean.FALSE;
+                    reMsg = "mqtt响应出错:" + e.getMessage();
+                }
+                messageResult.setIsResult(Boolean.TRUE);
+                messageResult.setReIsSuccess(reIsSuccess);
+                messageResult.setReMsg(reMsg);
+                messageResult.setReTime(System.currentTimeMillis());
+                messageResult.setReTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyySSS));
+                messageResult.setReTopic(reTopic);
+            }
+            messageResult.setIsHandleSuccess(isHandleSuccess);
+            messageResult.setHandleMsg(handleMsg);
+            messageResult.setHandlerTime(handlerTime);
+            operationMessageResultDao.save(messageResult);
+        });
+    }
+
+    /**
+     * 响应数据
+     *
+     * @param entity
+     * @return
+     */
+    public ResultContent responseMessage(OperationMessage entity) {
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("id", entity.getDataId());
+        Object data = entity.getResultData();
+        JSONObject object = new JSONObject();
+        if (ObjectUtils.isNotEmpty(data)) {
+            String _str = JSONUtil.toJsonStr(data);
+            object = JSONUtil.parseObj(_str);
+        }
+        jsonObject.put("data", object);
+        jsonObject.put("time", System.currentTimeMillis());
+        jsonObject.put("ttl", entity.getTtlTime());
+        jsonObject.put("event", entity.getEvent());
+        String reTopic = String.format("%s/reply", entity.getTopic());
+
+        String reMsg = "响应成功";
+        Boolean reIsSuccess = Boolean.TRUE;
+        try {
+            mqClient.sendObject(reTopic, JSONUtil.toJsonStr(jsonObject), entity.getDataId());
+        } catch (Exception e) {
+            e.printStackTrace();
+            reIsSuccess = Boolean.FALSE;
+            reMsg = "mqtt响应出错:" + e.getMessage();
+        }
+        entity.setIsResult(Boolean.TRUE);
+        entity.setReIsSuccess(reIsSuccess);
+        entity.setReMsg(reMsg);
+        entity.setReTime(System.currentTimeMillis());
+        entity.setReTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyySSS));
+        entity.setReTopic(reTopic);
+        operationMessageDao.save(entity);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 指令列表
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<OperationMessageModel>> page(Pageable pageable, OperationMessageSearchParam param) {
+        Page<OperationMessage> page = operationMessageDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+    }
+
+    /**
+     * 设备消息列表日志
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<OperationMessageResultModel>> pageMessage(Pageable pageable, OperationMessageResultSearch param) {
+        Page<OperationMessageResult> page = operationMessageResultDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+    }
+
+    /**
+     * 根据数据ID查询数据详情
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent<OperationMessageResultModel> getMessageById(String id) {
+        OperationMessageResult messageResult = operationMessageResultDao.findTopById(id);
+        if (ObjectUtils.isEmpty(messageResult)) {
+            return ResultContent.buildFail(String.format("数据ID不存在:%s", id));
+        }
+        OperationMessageResultModel model = toModel(messageResult);
+        return ResultContent.buildSuccess(model);
+    }
+
+    /**
+     * 标记消息已收到
+     *
+     * @param messageId
+     * @return
+     */
+    public ResultContent receiveMessage(String messageId) {
+        OperationMessage operationMessage = operationMessageDao.findTopByMessageId(messageId);
+        if (ObjectUtils.isEmpty(operationMessage)) {
+            return ResultContent.buildFail(String.format("%s指令信息不存在", messageId));
+        }
+        if (operationMessage.getIsReceive() != null && operationMessage.getIsReceive()) {
+            return ResultContent.buildFail("该指令已响应");
+        }
+        operationMessage.setIsReceive(Boolean.TRUE);
+        operationMessage.setReceiveTime(System.currentTimeMillis());
+        operationMessage.setReceiveTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+
+        operationMessageDao.save(operationMessage);
+        return ResultContent.buildSuccess();
+    }
+
+    public OperationMessageModel toModel(OperationMessage entity) {
+        OperationMessageModel model = new OperationMessageModel();
+        if (ObjectUtils.isNotEmpty(entity)) {
+            BeanUtils.copyProperties(entity, model);
+            model.setDeviceInfo(deviceInfoService.toModel(entity.getDeviceInfo()));
+            model.setGateWayInfo(gateWayInfoService.toModel(entity.getGateWayInfo()));
+        }
+        return model;
+    }
+
+    public OperationMessageResultModel toModel(OperationMessageResult entity) {
+        OperationMessageResultModel model = null;
+        if (ObjectUtils.isNotEmpty(entity)) {
+            model = new OperationMessageResultModel();
+
+        }
+        return model;
+    }
+
+}

+ 69 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/base/CommonService.java

@@ -0,0 +1,69 @@
+package com.zhongshu.iot.server.core.service.base;
+
+import com.mongodb.client.result.UpdateResult;
+import com.zhongshu.iot.server.core.util.CommonUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+/**
+ * @author TRX
+ * @date 2024/5/31
+ */
+@Slf4j
+@Service
+public class CommonService {
+
+    @Autowired
+    private MongoTemplate mongoTemplate;
+
+    /**
+     * 编辑数据
+     *
+     * @param standardData
+     * @param collectionName
+     * @return
+     */
+    public Object updateData(Map<String, Object> standardData, String collectionName) {
+        collectionName = CommonUtil.getCollectionName(collectionName);
+        Object id = standardData.get("id");
+        if (id != null) {
+            Query query = new Query(Criteria.where("_id").is(id));
+            Update update = new Update();
+            standardData.forEach((key, value) -> {
+                if (!"id".equals(key)) {
+                    update.set(key, value);
+                }
+            });
+            UpdateResult updateResult = mongoTemplate.upsert(query, update, collectionName);
+            return updateResult.getUpsertedId();
+        }
+        return null;
+    }
+
+    public Object updateData(Map<String, Object> where, Map<String, Object> standardData, String collectionName) {
+        collectionName = CommonUtil.getCollectionName(collectionName);
+        Criteria criteria = new Criteria();
+        if (where != null) {
+            where.forEach((key, value) -> {
+                criteria.and(key).is(value);
+            });
+        }
+        Query query = new Query(criteria);
+        Update update = new Update();
+        standardData.forEach((key, value) -> {
+            if (!"id".equals(key)) {
+                update.set(key, value);
+            }
+        });
+        UpdateResult updateResult = mongoTemplate.upsert(query, update, collectionName);
+        return updateResult.getUpsertedId();
+    }
+
+}

+ 32 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/base/InitService.java

@@ -0,0 +1,32 @@
+package com.zhongshu.iot.server.core.service.base;
+
+import com.zhongshu.iot.server.core.service.mqtt.GateWayUserInfoService;
+import com.zhongshu.iot.server.core.service.user.impl.UserServiceImpl;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author TRX
+ * @date 2024/6/26
+ */
+@Slf4j
+@Service
+public class InitService extends SuperService {
+
+    @Autowired
+    UserServiceImpl userService;
+
+    @Autowired
+    GateWayUserInfoService gateWayUserInfoService;
+
+    public ResultContent init() {
+        userService.initAdmin();
+
+        gateWayUserInfoService.initDefaultUser();
+
+        return ResultContent.buildSuccess();
+    }
+
+}

+ 81 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/base/RedisService.java

@@ -0,0 +1,81 @@
+package com.zhongshu.iot.server.core.service.base;
+
+import com.zhongshu.iot.server.core.util.exception.ServiceException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.TimeUnit;
+
+
+@Service
+public class RedisService {
+
+    @Autowired
+    private StringRedisTemplate template;
+
+    public void addExpireToken(String loginName, String token, long tokenExpirePeriod) {
+        try {
+            String[] tokens = token.split("\\.");
+            //template.boundValueOps(tokenKey).set(loginName, 10000, TimeUnit.MILLISECONDS);
+            template.opsForValue().set(tokens[1], loginName, tokenExpirePeriod, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            throw new ServiceException("系统故障,请联系服务商");
+        }
+    }
+
+    public void removeExpireToken(String loginName, String token) {
+        try {
+            String[] tokens = token.split("\\.");
+            //template.boundValueOps(tokenKey).set(loginName, 10000, TimeUnit.MILLISECONDS);
+            template.delete(tokens[1]);
+        } catch (Exception e) {
+            throw new ServiceException("系统故障,请联系服务商");
+        }
+    }
+
+    public boolean verifyExpireJwtToken(String loginName, String jwtToken) {
+        try {
+            String[] tokens = jwtToken.split("\\.");
+            //template.boundValueOps(tokenKey).set(loginName, 10000, TimeUnit.MILLISECONDS);
+            return template.hasKey(tokens[1]);
+        } catch (Exception e) {
+            throw new ServiceException("系统故障,请联系服务商");
+        }
+    }
+
+    public boolean verifyExpireCode(String code) {
+        try {
+            //template.boundValueOps(tokenKey).set(loginName, 10000, TimeUnit.MILLISECONDS);
+            return template.hasKey(code);
+        } catch (Exception e) {
+            throw new ServiceException("系统故障,请联系服务商");
+        }
+    }
+
+
+    public void setValue(String key, String value, long expirePeriod) {
+        try {
+            template.opsForValue().set(key, value, expirePeriod, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            throw new ServiceException("系统故障,请联系服务商");
+        }
+    }
+
+    public String getValue(String key) {
+        try {
+            return template.opsForValue().get(key);
+        } catch (Exception e) {
+            throw new ServiceException("系统故障,请联系服务商");
+        }
+    }
+
+    public void removeValue(String key) {
+        try {
+            template.delete(key);
+        } catch (Exception e) {
+            throw new ServiceException("系统故障,请联系服务商");
+        }
+    }
+
+}

+ 103 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/base/SuperService.java

@@ -0,0 +1,103 @@
+package com.zhongshu.iot.server.core.service.base;
+
+import com.github.microservice.auth.security.helper.AuthHelper;
+import com.zhongshu.iot.client.model.baseParam.SuperParam;
+import com.zhongshu.iot.client.model.baseParam.SuperSearchParam;
+import com.zhongshu.iot.server.core.dao.UserDao;
+import com.zhongshu.iot.server.core.domain.base.SuperEntity;
+import com.zhongshu.iot.server.core.domain.user.User;
+import com.zhongshu.iot.server.core.util.DateUtils;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/5/10
+ */
+@Slf4j
+public abstract class SuperService {
+
+    @Autowired
+    private UserDao userDao;
+
+    @Autowired
+    HttpServletRequest request;
+
+    @Autowired
+    private AuthHelper authHelper;
+
+    /**
+     * 得到当前用户对象
+     *
+     * @return
+     */
+    public User getCrrentUser() {
+        try {
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 得到当前用户ID
+     *
+     * @return
+     */
+    public String getCurrentUserId() {
+        if (authHelper.getCurrentUser() != null) {
+            return authHelper.getCurrentUser().getUserId();
+        }
+        return null;
+    }
+
+    public String getCurrentOid() {
+        String oid = request.getHeader("oid");
+        return oid;
+    }
+
+    public String getHeader(String key) {
+        return request.getHeader(key);
+    }
+
+    public void initDefaultUser(SuperParam param) {
+        if (param.getId() != null && param.getId().trim().equals("")) {
+            param.setId(null);
+        }
+        User user = getCrrentUser();
+        if (user != null && StringUtils.isEmpty(param.getCreateUserId())) {
+            param.setCreateUserId(user.getId());
+            param.setCreateUserName(user.getUserName());
+            param.setCreatePhone(user.getPhone());
+        }
+    }
+
+    public void initEntity(SuperEntity param) {
+        if (param.getId() != null && param.getId().trim().equals("")) {
+            param.setId(null);
+        }
+        User user = getCrrentUser();
+        if (user != null && StringUtils.isEmpty(param.getCreateUserId())) {
+            param.setCreateUserId(user.getId());
+            param.setCreateUserName(user.getUserName());
+            param.setCreatePhone(user.getPhone());
+        }
+    }
+
+    public void initSearchParam(SuperSearchParam param) {
+        List<Long> times = param.getTimes();
+        if (ObjectUtils.isNotEmpty(times) && times.size() == 2) {
+            Long startTime = times.get(0);
+            startTime = DateUtils.getDayStartTime(startTime);
+            Long endTime = times.get(1);
+            endTime = DateUtils.getDayEndTime(endTime);
+            param.setStartTime(startTime);
+            param.setEndTime(endTime);
+        }
+    }
+}

+ 292 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/docker/DockerMetaService.java

@@ -0,0 +1,292 @@
+package com.zhongshu.iot.server.core.service.docker;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import com.zhongshu.iot.client.model.docker.*;
+import com.zhongshu.iot.server.core.dao.docker.AkSkConfigTimeDao;
+import com.zhongshu.iot.server.core.dao.docker.DockerMetaDao;
+import com.zhongshu.iot.server.core.dao.docker.DockerMetaLastTimeDao;
+import com.zhongshu.iot.server.core.dataConfig.ResultMessage;
+import com.zhongshu.iot.server.core.domain.docker.AkSkConfig;
+import com.zhongshu.iot.server.core.domain.docker.DockerMeta;
+import com.zhongshu.iot.server.core.domain.docker.DockerMetaLastTime;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.util.SecurityUtil;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author TRX
+ * @date 2024/7/25
+ */
+@Slf4j
+@Service
+public class DockerMetaService extends SuperService {
+
+    @Autowired
+    private DockerMetaDao dockerMetaDao;
+
+    @Autowired
+    private DockerMetaLastTimeDao dockerMetaLastTimeDao;
+
+    @Autowired
+    HttpServletRequest request;
+
+    @Autowired
+    private AkSkConfigTimeDao akSkConfigTimeDao;
+
+    /**
+     * 添加docker元数据
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent addOrUpdate(DockerMetaParam param) {
+        String groupCode = param.getGroupCode();
+        Assert.hasText(param.getGroupCode(), "groupCode不能为空");
+        Assert.hasText(param.getName(), "name不能为空");
+        Assert.hasText(param.getImage(), "image不能为空");
+        if (StringUtils.isEmpty(param.getId())) {
+            param.setId(null);
+        }
+
+        DockerMeta dockerMeta = new DockerMeta();
+        DockerMeta temp = dockerMetaDao.findTopByGroupCodeAndName(groupCode, param.getName());
+        if (StringUtils.isNotEmpty(param.getId())) {
+            dockerMeta = dockerMetaDao.findTopById(param.getId());
+            if (ObjectUtils.isEmpty(dockerMeta)) {
+                return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+            }
+            if (ObjectUtils.isNotEmpty(temp) && !temp.getId().equals(dockerMeta.getId())) {
+                return ResultContent.buildFail(String.format("分组【%s】下已存在名称【%s】", groupCode, param.getName()));
+            }
+        } else {
+            if (ObjectUtils.isNotEmpty(temp)) {
+                return ResultContent.buildFail(String.format("分组【%s】下已存在名称【%s】", groupCode, param.getName()));
+            }
+            dockerMeta = new DockerMeta();
+        }
+        BeanUtils.copyProperties(param, dockerMeta);
+        if (ObjectUtils.isEmpty(dockerMeta.getRestartPolicy())) {
+            dockerMeta.setRestartPolicy(new RestartPolicy());
+        }
+        dockerMetaDao.save(dockerMeta);
+        updateLastTime(groupCode);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 删除docker元数据
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent deleteMeta(String id) {
+        DockerMeta dockerMeta = dockerMetaDao.findTopById(id);
+        if (ObjectUtils.isEmpty(dockerMeta)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        updateLastTime(dockerMeta.getGroupCode());
+        dockerMetaDao.delete(dockerMeta);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 得到详情
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent<DockerMetaModel> getDockerMeta(String id) {
+        DockerMeta dockerMeta = dockerMetaDao.findTopById(id);
+        if (ObjectUtils.isEmpty(dockerMeta)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        DockerMetaModel model = toModel(dockerMeta);
+        return ResultContent.buildSuccess(model);
+    }
+
+    public ResultContent<Page<DockerMetaModel>> page(Pageable pageable, DockerMetaSearch param) {
+        Page<DockerMeta> page = dockerMetaDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+    }
+
+    public ResultContent updateLastTime(String groupCode) {
+        DockerMetaLastTime lastTime = dockerMetaLastTimeDao.findTopByGroupCode(groupCode);
+        if (ObjectUtils.isEmpty(lastTime)) {
+            lastTime = new DockerMetaLastTime();
+        }
+        lastTime.setLastTime(System.currentTimeMillis());
+        lastTime.setGroupCode(groupCode);
+        dockerMetaLastTimeDao.save(lastTime);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 验证签名
+     *
+     * @return
+     */
+    public boolean verifySign() {
+        boolean b = false;
+        String ak = request.getHeader("db-ak");
+        AkSkConfig akSkConfig = akSkConfigTimeDao.findTopByAk(ak);
+        if (ObjectUtils.isNotEmpty(akSkConfig)) {
+            String sk = akSkConfig.getSk();
+            if (StringUtils.isNotEmpty(sk)) {
+                String dbSign = request.getHeader("db-sign");
+                String time = request.getHeader("db-time");
+                String input = String.format("%s%s%s", ak, time, sk);
+                String sign = SecurityUtil.getMD5Str(input).toLowerCase();
+                if (StringUtils.isNotEmpty(dbSign) && dbSign.equals(sign)) {
+                    b = true;
+                }
+            }
+        } else {
+            akSkConfig = new AkSkConfig();
+            akSkConfig.setAk(ak);
+            akSkConfig.setSk("");
+            akSkConfigTimeDao.save(akSkConfig);
+        }
+        return b;
+    }
+
+    public DockerMetaLastTimeModel getVersion() {
+        DockerMetaLastTimeModel model = new DockerMetaLastTimeModel();
+        String groupCode = getHeader("groupCode");
+        boolean b = verifySign();
+        if (!b) {
+            model.setUpdateTime(0L);
+            model.setCode("1000");
+            return model;
+        }
+        DockerMetaLastTime lastTime = dockerMetaLastTimeDao.findTopByGroupCode(groupCode);
+        if (ObjectUtils.isNotEmpty(lastTime)) {
+            model.setUpdateTime(lastTime.getLastTime());
+        }
+        return model;
+    }
+
+    public List<JSONObject> getList() {
+        List<JSONObject> list = new ArrayList<>();
+        boolean b = verifySign();
+        if (!b) {
+            return list;
+        }
+
+        String groupCode = getHeader("groupCode");
+        if (StringUtils.isNotEmpty(groupCode)) {
+            List<DockerMeta> datas = dockerMetaDao.findByGroupCodeOrderByUpdateTimeDesc(groupCode);
+            if (ObjectUtils.isNotEmpty(datas)) {
+                list = datas.stream().map(this::toJson).collect(Collectors.toList());
+            }
+        }
+        return list;
+    }
+
+    private JSONObject toJson(DockerMeta entity) {
+        JSONObject object = new JSONObject();
+
+        object.set("Remark", entity.getRemark());
+        object.set("UpdateTime", entity.getUpdateTime());
+
+        JSONObject Container = new JSONObject();
+        Container.set("Name", entity.getName());
+
+        JSONObject Config = new JSONObject();
+        Config.put("Image", entity.getImage());
+        if (StringUtils.isNotEmpty(entity.getWorkingDir())) {
+            Config.put("WorkingDir", entity.getWorkingDir());
+        }
+
+        JSONArray Env = new JSONArray();
+        if (StringUtils.isNotEmpty(entity.getNetworkTypes())) {
+            Env.add(String.format("networkTypes=%s", entity.getNetworkTypes()));
+        }
+        if (StringUtils.isNotEmpty(entity.getMessageProtocals())) {
+            Env.add(String.format("messageProtocals=%s", entity.getMessageProtocals()));
+        }
+        if (StringUtils.isNotEmpty(entity.getExtend())) {
+            Env.add(String.format("extend=%s", entity.getExtend()));
+        }
+        List<EnvModel> env = entity.getEnv();
+        if (ObjectUtils.isNotEmpty(env)) {
+            for (EnvModel envModel : env) {
+                Env.add(String.format("%s=%s", envModel.getKey(), envModel.getValue()));
+            }
+        }
+        Config.set("Env", Env);
+
+        JSONObject HostConfig = new JSONObject();
+        RestartPolicy restartPolicy = entity.getRestartPolicy();
+        if (ObjectUtils.isNotEmpty(restartPolicy)) {
+            JSONObject RestartPolicy = new JSONObject();
+            RestartPolicy.set("Name", restartPolicy.getName());
+            RestartPolicy.set("MaximumRetryCount", restartPolicy.getMaximumRetryCount());
+            HostConfig.set("RestartPolicy", RestartPolicy);
+        }
+
+        JSONArray Mounts = new JSONArray();
+        List<Mount> mounts = entity.getMounts();
+        if (ObjectUtils.isNotEmpty(mounts)) {
+            for (Mount mount : mounts) {
+                JSONObject Mount = new JSONObject();
+                Mount.set("Type", mount.getType());
+                Mount.set("Source", mount.getSource());
+                Mount.set("Target", mount.getTarget());
+                Mounts.add(Mount);
+            }
+        }
+        if (!Mounts.isEmpty()) {
+            HostConfig.set("Mounts", Mounts);
+        }
+
+        JSONObject ExposedPorts = new JSONObject();
+
+        JSONObject PortBindings = new JSONObject();
+        List<PortBinding> portBindings = entity.getPortBindings();
+        if (ObjectUtils.isNotEmpty(portBindings)) {
+            for (PortBinding portBinding : portBindings) {
+                String key = String.format("%s/%s", portBinding.getPort(), portBinding.getProtocol());
+                JSONArray bindings = new JSONArray();
+                JSONObject Binding = new JSONObject();
+                Binding.set("HostIp", portBinding.getHostIp());
+                Binding.set("HostPort", portBinding.getHostPort());
+                bindings.add(Binding);
+                PortBindings.set(key, bindings);
+                ExposedPorts.set(key, new JSONObject());
+            }
+        }
+        Config.set("ExposedPorts", ExposedPorts);
+        Container.set("Config", Config);
+        HostConfig.set("PortBindings", PortBindings);
+
+        Container.set("HostConfig", HostConfig);
+        object.set("Container", Container);
+        return object;
+    }
+
+    public DockerMetaModel toModel(DockerMeta entity) {
+        DockerMetaModel model = null;
+        if (ObjectUtils.isNotEmpty(entity)) {
+            model = new DockerMetaModel();
+            BeanUtils.copyProperties(entity, model);
+        }
+        return model;
+    }
+
+}

+ 57 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/fix/FixDataService.java

@@ -0,0 +1,57 @@
+package com.zhongshu.iot.server.core.service.fix;
+
+import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.GateWayInfoDao;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.DeviceInfo;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.GateWayInfo;
+import com.zhongshu.iot.server.core.service.sync.DeviceSyncFullCardService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/6/27
+ */
+@Slf4j
+@Service
+public class FixDataService {
+
+    @Autowired
+    DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    GateWayInfoDao gateWayInfoDao;
+
+    @Autowired
+    private DeviceSyncFullCardService deviceSyncFullCardService;
+
+    /**
+     * 同步全部设备
+     *
+     * @return
+     */
+    public ResultContent fixSyncDevice() {
+        List<String> strs = new ArrayList<>();
+
+        List<GateWayInfo> gateWayInfos = gateWayInfoDao.findAll();
+        if (ObjectUtils.isNotEmpty(gateWayInfos)) {
+            deviceSyncFullCardService.noticeSyncGateWay(gateWayInfos);
+            strs.add(String.format("同步网关数量:%d", gateWayInfos.size()));
+        }
+
+        List<DeviceInfo> deviceInfoList = deviceInfoDao.findAll();
+        if (ObjectUtils.isNotEmpty(deviceInfoList)) {
+            deviceSyncFullCardService.noticeSyncDevice(deviceInfoList);
+            strs.add(String.format("同步设备数量:%d", deviceInfoList.size()));
+        }
+
+        return ResultContent.buildSuccess(strs);
+    }
+
+}

+ 237 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/iot/IotDataVerifyService.java

@@ -0,0 +1,237 @@
+package com.zhongshu.iot.server.core.service.iot;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import com.zhongshu.iot.client.model.iot.IotAttribute;
+import com.zhongshu.iot.client.type.DataType;
+import com.zhongshu.iot.client.type.FunctionType;
+import com.zhongshu.iot.server.core.domain.iot.IotMain;
+import com.zhongshu.iot.server.core.util.CommonUtil;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/7/17
+ */
+@Slf4j
+@Service
+public class IotDataVerifyService {
+
+    /**
+     * 验证
+     *
+     * @param iotMain
+     * @param data
+     * @return
+     */
+    public ResultContent<Boolean> verifyIotParam(IotMain iotMain, JSONObject data) {
+        if (iotMain != null && data != null) {
+            FunctionType functionType = iotMain.getFunctionType();
+            if (functionType == FunctionType.Attribute) {
+                IotAttribute attribute = new IotAttribute();
+                BeanUtils.copyProperties(iotMain, attribute);
+                attribute.setChilds(iotMain.getChilds());
+                return loopCheck(attribute, data);
+            } else if (functionType == FunctionType.Event) {
+                List<IotAttribute> childs = iotMain.getChilds();
+                if (ObjectUtils.isNotEmpty(childs)) {
+                    for (IotAttribute child : childs) {
+                        ResultContent resultContent = loopCheck(child, data);
+                        if (resultContent.isFailed()) {
+                            return resultContent;
+                        }
+                    }
+                }
+            }
+        }
+        return ResultContent.buildSuccess();
+    }
+
+
+    public ResultContent loopCheck(IotAttribute attribute, JSONObject data) {
+        if (attribute != null && data != null) {
+            String identifier = attribute.getIdentifier();
+            // 属性
+            if (!data.containsKey(identifier)) {
+                // 没有该属性
+                if (attribute.getRequired() != null && attribute.getRequired()) {
+                    // 必须包含该属性
+                    return ResultContent.buildFail(String.format("数据中不包含参数【%s】", identifier));
+                }
+                return ResultContent.buildSuccess();
+            }
+            // 值
+            Object objectValue = data.get(identifier);
+            // 参数 数据类型
+            DataType dataType = attribute.getDataType();
+            if (objectValue == null) {
+                return ResultContent.buildSuccess();
+            }
+            // 检查内容类型
+            ResultContent resultContent = checkDataType(objectValue, identifier, dataType, null);
+            if (resultContent.isFailed()) {
+                return resultContent;
+            }
+
+            if (dataType == DataType.String) {
+                // 字符串
+                String value = data.get(identifier, String.class);
+                Integer maxLength = attribute.getMaxLength();
+                if (!CommonUtil.longIsEmpty(maxLength) && StringUtils.isNotEmpty(value) && value.length() > maxLength) {
+                    return ResultContent.buildFail(String.format("【%s】值超过最大长度【%d】", identifier, maxLength));
+                }
+            }
+
+            if (dataType == DataType.Number) {
+                // 数字
+                BigDecimal value = data.get(identifier, BigDecimal.class);
+                BigDecimal start = attribute.getStart();
+                BigDecimal end = attribute.getEnd();
+                if (start != null && start.compareTo(value) > 0) {
+                    return ResultContent.buildFail(String.format("【%s】值小于规定最小值【%s】", identifier, start.toString()));
+                }
+                if (end != null && end.compareTo(value) < 0) {
+                    return ResultContent.buildFail(String.format("【%s】值大于规定最大值【%s】", identifier, end.toString()));
+                }
+            }
+
+            // 对象
+            if (dataType == DataType.Object) {
+                JSONObject object = data.get(identifier, JSONObject.class);
+                List<IotAttribute> childs = attribute.getChilds();
+                if (childs != null) {
+                    for (IotAttribute iotAttribute : childs) {
+                        ResultContent resultChildContent = loopCheck(iotAttribute, object);
+                        if (resultChildContent.isFailed()) {
+                            return resultChildContent;
+                        }
+                    }
+                }
+            }
+
+            if (dataType == DataType.Array) {
+                // 数组
+                JSONArray array = data.get(identifier, JSONArray.class);
+                if (array != null && array.size() > 0) {
+                    // 数组里包含的元素类型
+                    DataType elementType = attribute.getElementType();
+                    for (int i = 0; i < array.size(); i++) {
+                        Object object = array.get(i);
+
+                        // 检查数组里的内容类型
+                        ResultContent childContent = checkDataType(object, identifier, elementType, i + 1);
+                        if (childContent.isFailed()) {
+                            return resultContent;
+                        }
+                        // 常规数据检查
+                        if (List.of(DataType.String, DataType.Boolean, DataType.Number).contains(elementType)) {
+                            JSONObject jsonObject = new JSONObject();
+                            jsonObject.put("value", jsonObject);
+                            IotAttribute tempAttribute = new IotAttribute();
+                            BeanUtils.copyProperties(attribute, tempAttribute);
+                            tempAttribute.setDataType(elementType);
+                            tempAttribute.setIdentifier("value");
+
+                            ResultContent resultContent1 = loopCheck(tempAttribute, jsonObject);
+                            if (resultContent1.isFailed()) {
+                                return resultContent1;
+                            }
+                        }
+
+                        // 对象
+                        if (elementType == DataType.Object) {
+                            List<IotAttribute> childs = attribute.getChilds();
+                            if (childs != null) {
+                                for (IotAttribute iotAttribute : childs) {
+                                    ResultContent resultChildContent = loopCheck(iotAttribute, array.getJSONObject(i));
+                                    if (resultChildContent.isFailed()) {
+                                        return resultChildContent;
+                                    }
+                                }
+                            }
+                        }
+
+                        if (elementType == DataType.Array) {
+                            List<IotAttribute> childs = attribute.getChilds();
+                            JSONArray temp = array.getJSONArray(i);
+                            if (childs != null && temp != null) {
+                                for (IotAttribute iotAttribute : childs) {
+                                    for (Object object1 : temp) {
+                                        String childIdentifier = iotAttribute.getIdentifier();
+                                        JSONObject childObject = new JSONObject();
+                                        childObject.put(childIdentifier, object1);
+                                        ResultContent resultChildContent = loopCheck(iotAttribute, childObject);
+                                        if (resultChildContent.isFailed()) {
+                                            return resultChildContent;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 检查内容类型
+     *
+     * @param object      内容
+     * @param identifier  参数名称,如:name
+     * @param elementType 数据类型
+     * @param index       第几项
+     * @return
+     */
+    public ResultContent checkDataType(Object object, String identifier, DataType elementType, Integer index) {
+        boolean b = true;
+        if (object != null && elementType != null) {
+            if (elementType == DataType.String) {
+                if (!(object instanceof String)) {
+                    b = false;
+                }
+            }
+            if (elementType == DataType.Number) {
+                if (!(object instanceof Number)) {
+                    b = false;
+                }
+            }
+            if (elementType == DataType.Boolean) {
+                if (!(object instanceof Boolean)) {
+                    b = false;
+                }
+            }
+            if (elementType == DataType.Object) {
+                if (!(object instanceof JSONObject)) {
+                    b = false;
+                }
+            }
+            if (elementType == DataType.Array) {
+                if (!(object instanceof JSONArray)) {
+                    b = false;
+                }
+            }
+        }
+        if (!b) {
+            if (index != null) {
+                return ResultContent.buildFail(
+                        String.format("【%s】属性第【%d】项内容不属于【%s】类型", identifier, index, elementType.getRemark())
+                );
+            } else {
+                return ResultContent.buildFail(
+                        String.format("【%s】属性内容不属于【%s】类型", identifier, elementType.getRemark())
+                );
+            }
+        }
+        return ResultContent.buildSuccess();
+    }
+}

+ 147 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/iot/IotSendMessageService.java

@@ -0,0 +1,147 @@
+package com.zhongshu.iot.server.core.service.iot;
+
+import cn.hutool.json.JSONObject;
+import com.github.microservice.models.iot.IotSendParam;
+import com.zhongshu.iot.client.type.IotDataType;
+import com.zhongshu.iot.client.type.OperationType;
+import com.zhongshu.iot.server.core.dao.iot.IotMainDao;
+import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.GateWay2DeviceDao;
+import com.zhongshu.iot.server.core.dao.mqtt.OperationMessageDao;
+import com.zhongshu.iot.server.core.dao.mqtt.OperationMessageResultDao;
+import com.zhongshu.iot.server.core.domain.iot.IotMain;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.*;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.util.CommonUtil;
+import com.zhongshu.iot.server.core.util.DateUtils;
+import com.zhongshu.iot.server.core.util.TokenUtil;
+import com.zhongshu.iot.server.core.util.mqtt.MqttTopicUtils;
+import com.zhongshu.iot.server.core.util.mqtt.mqttConfig.client.MQClient;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/8/9
+ */
+@Slf4j
+@Service
+public class IotSendMessageService extends SuperService {
+
+    @Autowired
+    private DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    private IotMainDao iotMainDao;
+
+    @Autowired
+    OperationMessageDao operationMessageDao;
+
+    @Autowired
+    private OperationMessageResultDao operationMessageResultDao;
+
+    @Autowired
+    private MQClient mqClient;
+
+    @Autowired
+    private GateWay2DeviceDao gateWay2DeviceDao;
+
+    // 保存90天
+    @Value("${artemisstore.time}")
+    public Long ttlMill = 30 * 24L * 60 * 60 * 1000L;
+
+    /**
+     * 发送信息
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent sendIotMessage(IotSendParam param) {
+        // 验证数据
+        DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceId(param.getDeviceId());
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format("设备不存在:%S", param.getDeviceId()));
+        }
+        List<IotMain> list = iotMainDao.findByDeviceIdAndIdentifierAndIotDataType(
+                param.getDeviceId(), param.getIdentifier(), IotDataType.Device
+        );
+        if (ObjectUtils.isEmpty(list)) {
+            return ResultContent.buildFail("没有对应的设备物模型");
+        }
+        GateWayInfo gateWayInfo = null;
+        GateWay2Device gateWay2Device = gateWay2DeviceDao.findTopByDeviceInfoOrderByUpdateTimeDesc(deviceInfo);
+        if (ObjectUtils.isEmpty(gateWay2Device)) {
+            gateWayInfo = gateWay2Device.getGateWayInfo();
+        }
+        String dataId = param.getDataId();
+        if (StringUtils.isEmpty(dataId)) {
+            dataId = CommonUtil.UUID();
+        }
+        // 组装要发送的数据
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.set("id", dataId);
+        jsonObject.set("data", param.getData());
+        jsonObject.set("timeStr", DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyySSS));
+        jsonObject.set("time", System.currentTimeMillis());
+        jsonObject.set("ttl", param.getTtl());
+        jsonObject.set("imitate", param.getIsImitate());
+
+        String token = TokenUtil.create();
+        OperationMessage entity = operationMessageDao.init(dataId, token);
+        if (ObjectUtils.isNotEmpty(entity)) {
+            entity.setMessageId(CommonUtil.UUID()); // 消息ID
+            entity.setClientId(""); // 终端ID
+            entity.setTopic(""); // topic名称
+            entity.setOperationType(OperationType.Push);
+            entity.setMessageClass("");
+            entity.setData(jsonObject);
+            entity.setTtlTime(param.getTtl());
+            entity.setSendTime(System.currentTimeMillis());
+            entity.setDataId(dataId);
+            entity.setIsTimeOut(false);
+            entity.setEvent("");
+            entity.setGateWayInfo(gateWayInfo);
+            if (ObjectUtils.isNotEmpty(gateWayInfo)) {
+                entity.setGateWayId(gateWayInfo.getGateWayId());
+            }
+            entity.setDeviceId(deviceInfo.getDeviceId());
+            entity.setDeviceInfo(deviceInfo);
+            entity.setTtl(new Date(System.currentTimeMillis() + ttlMill));
+            entity.setReceiveTime(System.currentTimeMillis());
+            entity.setReceiveTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+            operationMessageDao.save(entity);
+
+            for (IotMain iotMain : list) {
+                // %s/issued 下发数据 Topic
+                String topic = MqttTopicUtils.buildIssuedTopic(iotMain.getRealIotTopic());
+
+                OperationMessageResult messageResult = new OperationMessageResult();
+                messageResult.setOperationMessage(entity);
+                messageResult.setIotMain(iotMain);
+                // 设备ID
+                messageResult.setDeviceId(iotMain.getDeviceId());
+                // 分组code
+                messageResult.setProjectCode(iotMain.getProjectCode());
+                messageResult.setGateWayId(entity.getGateWayId());
+                messageResult.setRealIotTopic(topic);
+                messageResult.setData(jsonObject);
+                messageResult.setGateWayId(entity.getGateWayId());
+                messageResult.setOperationType(entity.getOperationType());
+
+                operationMessageResultDao.save(messageResult);
+            }
+        } else {
+            return ResultContent.buildFail("dataId重复");
+        }
+        return ResultContent.buildSuccess();
+    }
+
+}

+ 506 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/iot/IotServiceImpl.java

@@ -0,0 +1,506 @@
+package com.zhongshu.iot.server.core.service.iot;
+
+import com.zhongshu.iot.client.model.baseParam.NameModel;
+import com.zhongshu.iot.client.model.iot.*;
+import com.zhongshu.iot.client.type.FunctionType;
+import com.zhongshu.iot.client.type.IotDataType;
+import com.zhongshu.iot.server.core.dao.UserDao;
+import com.zhongshu.iot.server.core.dao.iot.IotDeviceDataDao;
+import com.zhongshu.iot.server.core.dao.iot.IotMainDao;
+import com.zhongshu.iot.server.core.dao.iot.IotTemplateDao;
+import com.zhongshu.iot.server.core.dao.iot.IotTopicDao;
+import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.GateWay2DeviceDao;
+import com.zhongshu.iot.server.core.dataConfig.ResultMessage;
+import com.zhongshu.iot.server.core.domain.iot.IotDeviceData;
+import com.zhongshu.iot.server.core.domain.iot.IotMain;
+import com.zhongshu.iot.server.core.domain.iot.IotTemplate;
+import com.zhongshu.iot.server.core.domain.iot.IotTopic;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.DeviceInfo;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.GateWay2Device;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.Assert;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author TRX
+ * @date 2024/6/20
+ */
+@Slf4j
+@Service
+public class IotServiceImpl extends SuperService {
+
+    @Autowired
+    private IotTemplateDao iotTemplateDao;
+
+    @Autowired
+    private IotMainDao iotMainDao;
+
+    @Autowired
+    private IotTopicDao iotTopicDao;
+
+    @Autowired
+    UserDao userDao;
+
+    @Autowired
+    DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    private GateWay2DeviceDao gateWay2DeviceDao;
+
+    @Autowired
+    IotDeviceDataDao iotDeviceDataDao;
+
+    //----------------------------- 模版 start----------------------------
+
+    /**
+     * 添加模版
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent addIotTemplate(IotTemplateParam param) {
+        IotTemplate template = null;
+        initDefaultUser(param);
+
+        IotTemplate temp = iotTemplateDao.findTopByNameAndIotDataType(param.getName(), IotDataType.IotTemplate);
+        if (StringUtils.isNotEmpty(param.getId())) {
+            template = iotTemplateDao.findTopById(param.getId());
+            if (ObjectUtils.isEmpty(template)) {
+                return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+            }
+            if (ObjectUtils.isNotEmpty(temp) && !temp.getId().equals(param.getId())) {
+                return ResultContent.buildFail(String.format(ResultMessage.NAME_EXIT, param.getName()));
+            }
+        } else {
+            template = new IotTemplate();
+            if (ObjectUtils.isNotEmpty(temp)) {
+                return ResultContent.buildFail(String.format(ResultMessage.NAME_EXIT, param.getName()));
+            }
+            template.setIotDataType(IotDataType.IotTemplate);
+        }
+        BeanUtils.copyProperties(param, template);
+        iotTemplateDao.save(template);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 删除模版
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent deleteTemplate(String id) {
+        IotTemplate template = iotTemplateDao.findTopById(id);
+        if (ObjectUtils.isEmpty(template)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        iotTemplateDao.delete(template);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 查询模版详情
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent<IotTemplateModel> getIotTemplate(String id) {
+        IotTemplate template = iotTemplateDao.findTopById(id);
+        if (ObjectUtils.isEmpty(template)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        return ResultContent.buildSuccess(toModel(template));
+    }
+
+    /**
+     * 模版分页
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<IotTemplateModel>> pageTemplate(Pageable pageable, IotTemplateSearch param) {
+        initSearchParam(param);
+        param.setIotDataType(IotDataType.IotTemplate);
+        Page<IotTemplate> page = iotTemplateDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+    }
+
+    /**
+     * 查询设备的物模型列表
+     *
+     * @param deviceId
+     * @return
+     */
+    public ResultContent<List<IotTemplateModel>> getDeviceTemplateList(String deviceId) {
+        List<IotTemplate> list = iotTemplateDao.findByDeviceIdAndIotDataTypeOrderByCreateTimeAsc(deviceId, IotDataType.Device);
+        List<IotTemplateModel> models = new ArrayList<>();
+        if (ObjectUtils.isNotEmpty(list)) {
+            models = list.stream().map(this::toModel).collect(Collectors.toList());
+        }
+        return ResultContent.buildSuccess(models);
+    }
+
+    /**
+     * 修改名称
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent updateIotTemplateName(NameModel param) {
+        Assert.hasText(param.getName(), "name不能为空");
+        IotTemplate template = iotTemplateDao.findTopById(param.getId());
+        if (ObjectUtils.isEmpty(template)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+        }
+
+        // 检查名称是否重复
+        if (template.getIotDataType() == IotDataType.IotTemplate) {
+            // 模版
+            IotTemplate temp = iotTemplateDao.findTopByNameAndIotDataType(param.getName(), IotDataType.IotTemplate);
+            if (ObjectUtils.isNotEmpty(temp) && !temp.getId().equals(template.getId())) {
+                return ResultContent.buildFail(String.format(ResultMessage.NAME_EXIT, param.getName()));
+            }
+        } else if (template.getIotDataType() == IotDataType.Device) {
+            // 物模型
+            IotTemplate temp = iotTemplateDao.findTopByNameAndDeviceIdAndIotDataType(
+                    param.getName(),
+                    template.getDeviceId(),
+                    IotDataType.Device);
+            if (ObjectUtils.isNotEmpty(temp) && !temp.getId().equals(template.getId())) {
+                return ResultContent.buildFail(String.format(ResultMessage.NAME_EXIT, param.getName()));
+            }
+        }
+        BeanUtils.copyProperties(param, template);
+        iotTemplateDao.save(template);
+        return ResultContent.buildSuccess();
+    }
+
+    //----------------------------- 模版 end------------------------------
+
+
+    //----------------------------- Topic start----------------------------
+    public ResultContent addIotTopic(IotTopicParam param) {
+        initDefaultUser(param);
+        String iotTemplateId = param.getIotTemplateId();
+        IotTemplate iotTemplate = iotTemplateDao.findTopById(iotTemplateId);
+        if (ObjectUtils.isEmpty(iotTemplate)) {
+            return ResultContent.buildFail(String.format("模版不存在:%s", iotTemplateId));
+        }
+
+        IotTopic entity = null;
+        if (StringUtils.isNotEmpty(param.getId())) {
+            entity = iotTopicDao.findTopById(param.getId());
+            if (ObjectUtils.isEmpty(entity)) {
+                return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+            }
+            IotTopic temp = iotTopicDao.findTopByTopicAndIotTemplate(param.getTopic(), iotTemplate);
+            if (ObjectUtils.isNotEmpty(temp) && !temp.getId().equals(param.getId())) {
+                return ResultContent.buildFail(String.format(ResultMessage.NAME_EXIT, param.getTopic()));
+            }
+        } else {
+            entity = new IotTopic();
+            IotTopic temp = iotTopicDao.findTopByTopicAndIotTemplate(param.getTopic(), iotTemplate);
+            if (ObjectUtils.isNotEmpty(temp)) {
+                return ResultContent.buildFail(String.format(ResultMessage.NAME_EXIT, param.getTopic()));
+            }
+        }
+        BeanUtils.copyProperties(param, entity);
+        entity.setIotTemplate(iotTemplate);
+        iotTopicDao.save(entity);
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent deleteIotTopic(String id) {
+        IotTopic entity = iotTopicDao.findTopById(id);
+        if (ObjectUtils.isEmpty(entity)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        iotTopicDao.delete(entity);
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent<IotTopicModel> getIotTopic(String id) {
+        IotTopic entity = iotTopicDao.findTopById(id);
+        if (ObjectUtils.isEmpty(entity)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        return ResultContent.buildSuccess(toModel(entity));
+    }
+
+    /**
+     * 分页查询 模版的Topic管理数据
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<IotTopicModel>> pageIotTopic(Pageable pageable, IotTopicSearch param) {
+        initSearchParam(param);
+        Page<IotTopic> page = iotTopicDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+    }
+    //----------------------------- Topic end------------------------------
+
+    //----------------------------- 物模型 start----------------------------
+
+    /**
+     * 设备绑定模版
+     *
+     * @param param
+     * @return
+     */
+    @Transactional
+    public ResultContent deviceBindIotTemplate(DeviceBindIotTemplate param) {
+        DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceIdAndProjectInfoCode(param.getDeviceId(), param.getProjectInfoCode());
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getDeviceId()));
+        }
+        // 模版信息
+        IotTemplate iotTemplate = iotTemplateDao.findTopById(param.getTemplateId());
+        if (ObjectUtils.isEmpty(iotTemplate)) {
+            return ResultContent.buildFail(String.format("模版ID不存在", param.getTemplateId()));
+        }
+        // 判断该设备是否已绑定该模版
+        IotTemplate temp = iotTemplateDao.findTopByDeviceIdAndIotTemplateIdAndIotDataType(param.getDeviceId(),
+                iotTemplate.getId(),
+                IotDataType.Device);
+        if (ObjectUtils.isNotEmpty(temp)) {
+            return ResultContent.buildFail(String.format("设备【%s】已绑定模版%s", deviceInfo.getDeviceName(), iotTemplate.getName()));
+        }
+
+        IotTemplate newIotTemplate = new IotTemplate();
+        BeanUtils.copyProperties(iotTemplate, newIotTemplate, "id", "createTime", "updateTime");
+        newIotTemplate.setIotDataType(IotDataType.Device);
+        newIotTemplate.setIotTemplateId(param.getTemplateId());
+        newIotTemplate.setDeviceId(deviceInfo.getDeviceId());
+        newIotTemplate.setProjectCode(deviceInfo.getProjectInfoCode());
+        iotTemplateDao.save(newIotTemplate);
+
+        // 属性等数据
+        List<IotMain> saveList = new ArrayList<>();
+        List<IotMain> list = iotMainDao.findByIotTemplateOrderByCreateTimeAsc(iotTemplate);
+        if (list != null) {
+            list.stream().forEach(it -> {
+                IotMain main = new IotMain();
+                BeanUtils.copyProperties(it, main, "id", "createTime", "updateTime");
+                main.setIotTemplate(newIotTemplate);
+                main.setIotDataType(IotDataType.Device);
+                main.setDeviceId(deviceInfo.getDeviceId());
+                main.setProjectCode(deviceInfo.getProjectInfoCode());
+                main.setAddTime(System.currentTimeMillis());
+                initRealTopic(main);
+                saveList.add(main);
+            });
+        }
+        iotMainDao.saveAll(saveList);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 添加属性  事件
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent addIotMain(IotMainParam param) {
+        initDefaultUser(param);
+        if (param.getFunctionType() == null) {
+            return ResultContent.buildFail("functionType 不能为空");
+        }
+        Assert.hasText(param.getName(), "name不能为空");
+        Assert.hasText(param.getIdentifier(), "identifier不能为空");
+        Assert.hasText(param.getIotTopic(), "iotTopic不能为空");
+
+        String iotTemplateId = param.getIotTemplateId();
+        IotTemplate iotTemplate = iotTemplateDao.findTopById(iotTemplateId);
+        if (ObjectUtils.isEmpty(iotTemplate)) {
+            return ResultContent.buildFail(String.format("模版不存在:%s", iotTemplateId));
+        }
+
+        IotMain entity = null;
+        // 判断名称是否重复
+        IotMain nameTemp = iotMainDao.findTopByNameAndIotTemplate(param.getName(), iotTemplate);
+        IotMain topicTemp = iotMainDao.findTopByIotTopicAndIotTemplate(param.getIotTopic(), iotTemplate);
+        if (StringUtils.isNotEmpty(param.getId())) {
+            entity = iotMainDao.findTopById(param.getId());
+            if (ObjectUtils.isEmpty(entity)) {
+                return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+            }
+            if (ObjectUtils.isNotEmpty(nameTemp) && !nameTemp.getId().equals(param.getId())) {
+                return ResultContent.buildFail(String.format(ResultMessage.NAME_EXIT, param.getName()));
+            }
+            if (ObjectUtils.isNotEmpty(topicTemp) && !topicTemp.getId().equals(param.getId())) {
+                return ResultContent.buildFail(String.format("iotTopic已存在:%s", param.getIotTopic()));
+            }
+        } else {
+            entity = new IotMain();
+            initEntity(entity);
+            if (ObjectUtils.isNotEmpty(nameTemp)) {
+                return ResultContent.buildFail(String.format(ResultMessage.NAME_EXIT, param.getName()));
+            }
+            if (ObjectUtils.isNotEmpty(topicTemp)) {
+                return ResultContent.buildFail(String.format("iotTopic已存在:%s", param.getIotTopic()));
+            }
+        }
+
+        BeanUtils.copyProperties(param, entity);
+        // 设备所属模型
+        entity.setIotTemplate(iotTemplate);
+        entity.setIotDataType(iotTemplate.getIotDataType());
+        if (iotTemplate.getIotDataType() == IotDataType.Device) {
+            entity.setDeviceId(iotTemplate.getDeviceId());
+            entity.setProjectCode(iotTemplate.getProjectCode());
+            initRealTopic(entity);
+        }
+        iotMainDao.save(entity);
+        return ResultContent.buildSuccess();
+    }
+
+    private void initRealTopic(IotMain iotMain) {
+        if (iotMain != null && iotMain.getIotDataType() == IotDataType.Device) {
+            // 把 topic有占位符的换为具体的值
+            String deviceId = iotMain.getDeviceId();
+            String iotTopic = iotMain.getIotTopic();
+            String realIotTopic = iotTopic;
+            if (StringUtils.isNotEmpty(iotTopic)) {
+                realIotTopic = realIotTopic.replaceAll(Pattern.quote("${deviceId}"), deviceId);
+            }
+            DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceId(deviceId);
+            if (ObjectUtils.isNotEmpty(deviceInfo)) {
+                GateWay2Device gateWay2Device = gateWay2DeviceDao.findTopByDeviceInfoOrderByUpdateTimeDesc(deviceInfo);
+                if (ObjectUtils.isNotEmpty(gateWay2Device)) {
+                    String gateWayId = gateWay2Device.getGateWayInfo().getGateWayId();
+                    realIotTopic = realIotTopic.replaceAll(Pattern.quote("${gateWayId}"), gateWayId);
+                }
+            }
+            iotMain.setRealIotTopic(realIotTopic);
+        }
+    }
+
+    public ResultContent deleteIotMain(String id) {
+        IotMain entity = iotMainDao.findTopById(id);
+        if (ObjectUtils.isEmpty(entity)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        iotMainDao.delete(entity);
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent<IotMainModel> getIotMain(String id) {
+        IotMain entity = iotMainDao.findTopById(id);
+        if (ObjectUtils.isEmpty(entity)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        return ResultContent.buildSuccess(toModel(entity));
+    }
+
+    /**
+     * 物模型 结构查询
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<IotMainModel>> pageIotMain(Pageable pageable, IotMainSearch param) {
+        initSearchParam(param);
+        if (StringUtils.isEmpty(param.getIotTemplateId())) {
+            return ResultContent.buildFail("iotTemplateId不能为空");
+        }
+        Page<IotMain> page = iotMainDao.page(pageable, param);
+        if (param.getFunctionType() != null && param.getFunctionType() == FunctionType.Attribute) {
+            return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toAttributeModel));
+        } else {
+            return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+        }
+    }
+
+    /**
+     * 得到模版物模型的顶用数据
+     *
+     * @param templateId
+     * @return
+     */
+    public ResultContent<List<IotMainModel>> getTemplateIotMainList(String templateId, FunctionType functionType) {
+        List<IotMain> list = iotMainDao.findByIotTemplateAndFunctionTypeOrderByCreateTimeAsc(IotTemplate.build(templateId), functionType);
+        List<IotMainModel> models = new ArrayList<>();
+        if (ObjectUtils.isNotEmpty(list)) {
+            models = list.stream().map(this::toModel).collect(Collectors.toList());
+        }
+        return ResultContent.buildSuccess(models);
+    }
+
+    //----------------------------- 物模型 end------------------------------
+
+    public IotTemplateModel toModel(IotTemplate entity) {
+        IotTemplateModel model = null;
+        if (ObjectUtils.isNotEmpty(entity)) {
+            model = new IotTemplateModel();
+            BeanUtils.copyProperties(entity, model);
+        }
+        return model;
+    }
+
+    public IotTopicModel toModel(IotTopic entity) {
+        IotTopicModel model = null;
+        if (ObjectUtils.isNotEmpty(entity)) {
+            model = new IotTopicModel();
+            BeanUtils.copyProperties(entity, model);
+        }
+        return model;
+    }
+
+    /**
+     * IOTMain模型
+     *
+     * @param entity
+     * @return
+     */
+    public IotMainModel toModel(IotMain entity) {
+        IotMainModel model = null;
+        if (ObjectUtils.isNotEmpty(entity)) {
+            model = new IotMainModel();
+            BeanUtils.copyProperties(entity, model);
+        }
+        return model;
+    }
+
+    /**
+     * 属性 类型的model,包含值
+     *
+     * @param entity
+     * @return
+     */
+    public IotMainAttributeModel toAttributeModel(IotMain entity) {
+        IotMainAttributeModel model = null;
+        if (ObjectUtils.isNotEmpty(entity)) {
+            model = new IotMainAttributeModel();
+            BeanUtils.copyProperties(entity, model);
+            IotDeviceData iotDeviceData = iotDeviceDataDao.findTopByIotMain(entity);
+            if (ObjectUtils.isNotEmpty(iotDeviceData)) {
+                IotDeviceDataModel deviceData = new IotDeviceDataModel();
+                BeanUtils.copyProperties(iotDeviceData, deviceData);
+                model.setDeviceData(deviceData);
+            }
+        }
+        return model;
+    }
+
+
+}

+ 257 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/DeviceInfoService.java

@@ -0,0 +1,257 @@
+package com.zhongshu.iot.server.core.service.mqtt;
+
+import com.github.microservice.models.type.OnLineState;
+import com.zhongshu.iot.client.model.iot.IotMainModel;
+import com.zhongshu.iot.client.model.mqtt.DeviceInfoAddParam;
+import com.zhongshu.iot.client.model.mqtt.DeviceInfoModel;
+import com.zhongshu.iot.client.model.mqtt.DeviceInfoSearchParam;
+import com.zhongshu.iot.client.model.mqtt.DeviceInfoUpdateRemark;
+import com.zhongshu.iot.client.type.FunctionType;
+import com.zhongshu.iot.client.type.type.LogsLevel;
+import com.zhongshu.iot.server.core.dao.iot.IotTemplateDao;
+import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.ProjectInfoDao;
+import com.zhongshu.iot.server.core.dataConfig.ResultMessage;
+import com.zhongshu.iot.server.core.domain.iot.IotTemplate;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.DeviceInfo;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.ProjectInfo;
+import com.zhongshu.iot.server.core.service.iot.IotServiceImpl;
+import com.zhongshu.iot.server.core.service.sync.DeviceSyncFullCardService;
+import com.zhongshu.iot.server.core.service.user.OperationLogsService;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/5/14
+ */
+@Service
+public class DeviceInfoService {
+    @Autowired
+    DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    OperationLogsService operationLogsService;
+
+    @Autowired
+    IotTemplateDao iotTemplateDao;
+
+    @Autowired
+    IotServiceImpl iotService;
+
+    @Autowired
+    ProjectInfoDao projectInfoDao;
+
+    @Autowired
+    ProjectInfoService projectInfoService;
+
+    @Autowired
+    DeviceSyncFullCardService deviceSyncFullCardService;
+
+    /**
+     * 添加-编辑设备 (基础信息)
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent<DeviceInfo> addDeviceInfo(DeviceInfoAddParam param) {
+        DeviceInfo deviceInfo = new DeviceInfo();
+        DeviceInfo temp = deviceInfoDao.findTopByDeviceId(param.getDeviceId());
+        if (ObjectUtils.isNotEmpty(temp)) {
+            deviceInfo = temp;
+            param.setId(null);
+        } else {
+            deviceInfo.setActivityTime(System.currentTimeMillis());
+        }
+        BeanUtils.copyProperties(param, deviceInfo, "id");
+        deviceInfo.setLastOnlineTime(System.currentTimeMillis());
+        deviceInfo.setOnLineState(OnLineState.OnLine);
+        // 项目
+        if (StringUtils.isNotEmpty(param.getProjectInfoCode())) {
+            ProjectInfo projectInfo = projectInfoDao.findTopByCode(param.getProjectInfoCode());
+            deviceInfo.setProjectInfo(projectInfo);
+            deviceInfo.setProjectInfoCode(projectInfo.getCode());
+        }
+        deviceInfo.setLastOnlineTime(System.currentTimeMillis());
+        deviceInfoDao.save(deviceInfo);
+        operationLogsService.addLogs(String.format("添加了设备;%s", deviceInfo.getDeviceName()), LogsLevel.Middle, deviceInfo);
+        return ResultContent.buildSuccess(deviceInfo);
+    }
+
+    /**
+     * 设备列表
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<DeviceInfoModel>> pageDevice(Pageable pageable, DeviceInfoSearchParam param) {
+        Page<DeviceInfo> page = deviceInfoDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+    }
+
+    /**
+     * @return
+     */
+    public ResultContent updateRemark(DeviceInfoUpdateRemark param) {
+        DeviceInfo deviceInfo = deviceInfoDao.findTopById(param.getId());
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+        }
+        deviceInfo.setRemark(param.getRemark());
+        deviceInfoDao.save(deviceInfo);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 编辑设备关联的模版
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent updateIopTemplate(DeviceInfoUpdateRemark param) {
+        DeviceInfo deviceInfo = deviceInfoDao.findTopById(param.getId());
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+        }
+        IotTemplate iotTemplate = iotTemplateDao.findTopById(param.getIotTemplateId());
+        if (ObjectUtils.isEmpty(iotTemplate)) {
+            return ResultContent.buildFail(String.format("模版ID不存在:", param.getId()));
+        }
+        deviceInfo.setIotTemplate(iotTemplate);
+        deviceInfoDao.save(deviceInfo);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 编辑设备关联的项目信息
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent updateProject(DeviceInfoUpdateRemark param) {
+        DeviceInfo deviceInfo = deviceInfoDao.findTopById(param.getId());
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+        }
+        ProjectInfo projectInfo = projectInfoDao.findTopByCode(param.getProjectCode());
+        if (ObjectUtils.isEmpty(projectInfo)) {
+            return ResultContent.buildFail(String.format("项目不存在:", param.getId()));
+        }
+        deviceInfo.setProjectInfoCode(projectInfo.getCode());
+        deviceInfo.setProjectInfo(projectInfo);
+        deviceInfoDao.save(deviceInfo);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 编辑设备关联的模版
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent updateIsReportLogs(DeviceInfoUpdateRemark param) {
+        DeviceInfo deviceInfo = deviceInfoDao.findTopById(param.getId());
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+        }
+        deviceInfo.setIsReportLogs(param.getIsReportLogs());
+        deviceInfoDao.save(deviceInfo);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 改变设备状态
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent updateDeviceState(DeviceInfoUpdateRemark param) {
+        DeviceInfo deviceInfo = deviceInfoDao.findTopById(param.getId());
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+        }
+        deviceInfo.setState(param.getState());
+        deviceInfoDao.save(deviceInfo);
+        deviceSyncFullCardService.noticeSyncDeviceOnlineStateChange(deviceInfo.getDeviceId());
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 测试设备的连接时间
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent<Long> testDevice(String id) {
+        Long time = 0L;
+
+        return ResultContent.buildSuccess(time);
+    }
+
+    /**
+     * 得到设备关联的物模型定义
+     *
+     * @param deviceId
+     * @return
+     */
+    public ResultContent<List<IotMainModel>> getDeviceIotMain(String deviceId, FunctionType functionType) {
+        DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceId(deviceId);
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, deviceId));
+        }
+        List<IotMainModel> models = new ArrayList<>();
+        if (deviceInfo.getIotTemplate() != null) {
+            ResultContent<List<IotMainModel>> resultContent = iotService.getTemplateIotMainList(deviceInfo.getIotTemplate().getId(), functionType);
+            models = resultContent.getContent();
+        }
+        return ResultContent.buildSuccess(models);
+    }
+
+
+    /**
+     * 删除设备
+     *
+     * @param deviceId
+     * @return
+     */
+    public ResultContent deleteDeviceInfo(String deviceId) {
+        DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceId(deviceId);
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            return ResultContent.buildFail(String.format("设备ID不存在:%s", deviceId));
+        }
+        deviceInfoDao.delete(deviceInfo);
+        operationLogsService.addLogs(String.format("删除了设备;%s", deviceInfo.getDeviceName()), LogsLevel.High, deviceInfo);
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent<DeviceInfoModel> getDeviceById(String deviceId) {
+        DeviceInfoModel model = null;
+        DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceId(deviceId);
+        if (ObjectUtils.isNotEmpty(deviceInfo)) {
+            model = toModel(deviceInfo);
+        }
+        return ResultContent.buildSuccess(model);
+    }
+
+    public DeviceInfoModel toModel(DeviceInfo deviceInfo) {
+        DeviceInfoModel deviceInfoModel = new DeviceInfoModel();
+        if (ObjectUtils.isNotEmpty(deviceInfo)) {
+            BeanUtils.copyProperties(deviceInfo, deviceInfoModel);
+            deviceInfoModel.setIotTemplate(iotService.toModel(deviceInfo.getIotTemplate()));
+            deviceInfoModel.setProjectInfo(projectInfoService.toModel(deviceInfo.getProjectInfo()));
+        }
+        return deviceInfoModel;
+    }
+
+}

+ 217 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/DevicePingInfoService.java

@@ -0,0 +1,217 @@
+package com.zhongshu.iot.server.core.service.mqtt;
+
+import cn.hutool.json.JSONUtil;
+import com.github.microservice.models.hxz.DevicePingInfoParam;
+import com.github.microservice.models.hxz.GateWayPingInfoParam;
+import com.github.microservice.models.hxz.PingResult;
+import com.github.microservice.models.type.DeviceType;
+import com.github.microservice.models.type.OnLineState;
+import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.DevicePingInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.GateWayInfoDao;
+import com.zhongshu.iot.server.core.domain.ExecuteAnnotationService;
+import com.zhongshu.iot.server.core.domain.ExecuteAnnotationServiceMethod;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.DeviceInfo;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.DevicePingInfo;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.GateWayInfo;
+import com.zhongshu.iot.server.core.service.base.CommonService;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.service.sync.DeviceSyncFullCardService;
+import com.zhongshu.iot.server.core.util.DateUtils;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author TRX
+ * @date 2024/7/3
+ */
+@Slf4j
+@Service
+@ExecuteAnnotationService
+public class DevicePingInfoService extends SuperService {
+
+    @Autowired
+    DevicePingInfoDao devicePingInfoDao;
+
+    @Autowired
+    DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    CommonService commonService;
+
+    @Autowired
+    DeviceSyncFullCardService deviceSyncFullCardService;
+
+    @Autowired
+    GateWayInfoDao gateWayInfoDao;
+
+    // ping记录保存时间
+    private Long pingTTl = 1 * 24L * 60 * 60 * 1000L;
+
+    //最大的心跳时间间隔 5分钟
+    private Long maxPingTime = 5 * 60 * 1000L;
+
+    @ExecuteAnnotationServiceMethod(value = "devicePing", remark = "设备心跳")
+    public ResultContent<Object> devicePing(String dataStr) {
+        DevicePingInfoParam param = JSONUtil.toBean(dataStr, DevicePingInfoParam.class);
+        String deviceId = param.getDeviceId();
+        DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceId(deviceId);
+        if (ObjectUtils.isNotEmpty(deviceInfo)) {
+            Long time = System.currentTimeMillis();
+            Map<String, Object> standardData = new HashMap<String, Object>();
+            standardData.put("id", deviceInfo.getId());
+            standardData.put("lastOnlineTime", time);
+            standardData.put("onLineState", OnLineState.OnLine);
+            commonService.updateData(standardData, DeviceInfo.class.getSimpleName());
+
+            // ping记录
+            DevicePingInfo devicePingInfo = new DevicePingInfo();
+            devicePingInfo.setPingType(DeviceType.Consumer.name());
+            devicePingInfo.setDeviceId(deviceId);
+            devicePingInfo.setDeviceName(deviceInfo.getDeviceName());
+            devicePingInfo.setGateWayId(param.getGateWayId());
+            devicePingInfo.setProjectInfoCode(deviceInfo.getProjectInfoCode());
+            devicePingInfo.setTTL(new Date(System.currentTimeMillis() + pingTTl));
+            devicePingInfo.setTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+            devicePingInfoDao.save(devicePingInfo);
+            // 通知设备
+            deviceSyncFullCardService.noticeSyncDeviceOnlineTimeChange(deviceId);
+        } else {
+            log.info("心跳设备未找到: {}", deviceId);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    @ExecuteAnnotationServiceMethod(value = "ping", remark = "网关心跳")
+    public ResultContent<Object> ping(String dataStr) {
+        PingResult pingResult = new PingResult();
+        GateWayPingInfoParam param = JSONUtil.toBean(dataStr, GateWayPingInfoParam.class);
+        if (StringUtils.isEmpty(param.getGateWayId())) {
+            pingResult.setFailed("");
+            return ResultContent.buildFail("GateWayId为空");
+        }
+        if (StringUtils.isNotEmpty(param.getDeviceId())) {
+            devicePing(dataStr);
+        } else {
+            GateWayInfo gateWayInfo = gateWayInfoDao.findTopByGateWayId(param.getGateWayId());
+            if (ObjectUtils.isNotEmpty(gateWayInfo)) {
+                Map<String, Object> where = new HashMap<>();
+                where.put("gateWayId", gateWayInfo.getGateWayId());
+                Map<String, Object> standardData = new HashMap<>();
+                standardData.put("onLineState", OnLineState.OnLine);
+                standardData.put("lastOnlineTime", System.currentTimeMillis());
+                commonService.updateData(where, standardData, GateWayInfo.class.getSimpleName());
+
+                // ping记录
+                DevicePingInfo devicePingInfo = new DevicePingInfo();
+                devicePingInfo.setPingType(DeviceType.GateWay.name());
+                devicePingInfo.setGateWayId(param.getGateWayId());
+                devicePingInfo.setProjectInfoCode(gateWayInfo.getProjectInfoCode());
+                devicePingInfo.setTTL(new Date(System.currentTimeMillis() + pingTTl));
+                devicePingInfo.setTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+                devicePingInfoDao.save(devicePingInfo);
+            }
+        }
+        pingResult.setSuccess();
+        pingResult.setTime(System.currentTimeMillis());
+        return ResultContent.buildSuccess(pingResult);
+    }
+
+
+    /**
+     * 检查设备的状态
+     */
+    public void checkDeviceState() {
+//        log.info("checkDeviceState: {}", DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+        List<DeviceInfo> list = deviceInfoDao.findAll();
+        if (ObjectUtils.isNotEmpty(list)) {
+            long time = System.currentTimeMillis();
+            list.parallelStream().forEach(deviceInfo -> {
+                // 默认离线
+                OnLineState onLineState = OnLineState.OffLine;
+                List<DevicePingInfo> _list = devicePingInfoDao.findTop5ByDeviceIdOrderByCreateTimeDesc(deviceInfo.getDeviceId());
+                if (_list != null) {
+                    Long firstTime = null;
+                    long avgTime = 0;
+                    if (_list.size() == 1) {
+                        firstTime = _list.get(0).getCreateTime();
+                        avgTime = maxPingTime;
+                    } else if (_list.size() > 2) {
+                        firstTime = _list.get(0).getCreateTime();
+                        Long lastTime = _list.get(_list.size() - 1).getCreateTime();
+                        // 2 次心跳间隔时间
+                        avgTime = ((firstTime - lastTime) / (_list.size() - 1)) * 2;
+                        if (avgTime > maxPingTime) {
+                            avgTime = maxPingTime;
+                        }
+                    }
+                    if (firstTime != null) {
+                        // 根据ping数据判断是否在线
+                        if ((time - firstTime) > avgTime) {
+                            onLineState = OnLineState.OffLine;
+                        } else {
+                            onLineState = OnLineState.OnLine;
+                        }
+                    }
+                }
+//                log.info("{} {}", deviceInfo.getDeviceId(), onLineState);
+                if (onLineState != deviceInfo.getOnLineState()) {
+                    log.info("设备在线状态改变:{} {}", deviceInfo.getDeviceName(), onLineState);
+                    Map<String, Object> standardData = new HashMap<String, Object>();
+                    standardData.put("id", deviceInfo.getId());
+                    standardData.put("onLineState", onLineState);
+                    commonService.updateData(standardData, DeviceInfo.class.getSimpleName());
+                    deviceSyncFullCardService.noticeSyncDeviceOnlineStateChange(deviceInfo.getDeviceId());
+                }
+            });
+        }
+    }
+
+    /**
+     * 定期检查网关的状态
+     */
+    public void checkGateWayState() {
+        List<GateWayInfo> list = gateWayInfoDao.findAll();
+        if (ObjectUtils.isNotEmpty(list)) {
+            long time = System.currentTimeMillis();
+            list.parallelStream().forEach(gateWayInfo -> {
+                OnLineState onLineState = OnLineState.OnLine;
+                List<DevicePingInfo> _list = devicePingInfoDao.findTop5ByGateWayIdAndPingTypeOrderByCreateTimeDesc(gateWayInfo.getGateWayId(), DeviceType.GateWay.name());
+                if (_list != null && _list.size() > 2) {
+                    Long firstTime = _list.get(0).getCreateTime();
+                    Long lastTime = _list.get(_list.size() - 1).getCreateTime();
+                    // 2 次心跳间隔时间
+                    long avgTime = ((firstTime - lastTime) / (_list.size() - 1)) * 2;
+                    if (avgTime > maxPingTime) {
+                        avgTime = maxPingTime;
+                    }
+                    // 根据ping数据判断是否在线
+                    if ((time - firstTime) > avgTime) {
+                        onLineState = OnLineState.OffLine;
+                    }
+                }
+                if (ObjectUtils.isEmpty(_list)) {
+                    onLineState = OnLineState.OffLine;
+                }
+
+                if (onLineState != gateWayInfo.getOnLineState()) {
+                    log.info("网关在线状态改变:{} {}", gateWayInfo.getGateWayName(), onLineState);
+                    Map<String, Object> standardData = new HashMap<String, Object>();
+                    standardData.put("id", gateWayInfo.getId());
+                    standardData.put("onLineState", onLineState);
+                    commonService.updateData(standardData, GateWayInfo.class.getSimpleName());
+                }
+            });
+        }
+    }
+
+}

+ 345 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/GateWayInfoService.java

@@ -0,0 +1,345 @@
+package com.zhongshu.iot.server.core.service.mqtt;
+
+import com.github.microservice.models.type.OnLineState;
+import com.zhongshu.iot.client.model.mqtt.*;
+import com.zhongshu.iot.server.core.dao.mqtt.*;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.*;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.service.sync.DeviceSyncFullCardService;
+import com.zhongshu.iot.server.core.service.user.OperationLogsService;
+import com.zhongshu.iot.server.core.util.DateUtils;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.mqtt.MqttTopicUtils;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.Cleanup;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl;
+import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerInvocationHandler;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/5/17
+ */
+@Slf4j
+@Service
+public class GateWayInfoService extends SuperService {
+
+    @Autowired
+    GateWayInfoDao gateWayInfoDao;
+
+    @Autowired
+    GateWayUserInfoDao gateWayUserInfoDao;
+
+    @Autowired
+    DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    GateWay2UserDao gateWay2UserDao;
+
+    @Autowired
+    DeviceInfoService deviceInfoService;
+
+    @Autowired
+    GateWay2DeviceDao gateWay2DeviceDao;
+
+    @Autowired
+    MqttInfoDao mqttInfoDao;
+
+    @Autowired
+    OperationLogsService operationLogsService;
+
+    @Autowired
+    ApplicationContext applicationContext;
+
+    @Autowired
+    DeviceSyncFullCardService deviceSyncFullCardService;
+
+    @Autowired
+    private ProjectInfoDao projectInfoDao;
+
+    @Autowired
+    ProjectInfoService projectInfoService;
+
+    @Autowired
+    DevicePingInfoDao devicePingInfoDao;
+
+    /**
+     * 添加网关
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent<GateWayInfo> addGateWayInfo(GateWayInfoAddParam param) {
+        initDefaultUser(param);
+        GateWayInfo gateWayInfo = gateWayInfoDao.findTopByGateWayId(param.getGateWayId());
+        if (ObjectUtils.isEmpty(gateWayInfo)) {
+            gateWayInfo = new GateWayInfo();
+            gateWayInfo.setActivityTime(System.currentTimeMillis());
+        }
+        gateWayInfo.setOnLineState(OnLineState.OnLine);
+        gateWayInfo.setLastOnlineTime(System.currentTimeMillis());
+        BeanUtils.copyProperties(param, gateWayInfo, "id");
+
+        gateWayInfoDao.save(gateWayInfo);
+        if (StringUtils.isNotEmpty(param.getProjectInfoCode())) {
+            ProjectInfo projectInfo = projectInfoDao.findTopByCode(param.getProjectInfoCode());
+            gateWayInfo.setProjectInfo(projectInfo);
+        }
+        log.info("网关注册成功");
+        // 通知同步
+        deviceSyncFullCardService.noticeSyncGateWay(gateWayInfo);
+        return ResultContent.buildSuccess(gateWayInfo);
+    }
+
+    /**
+     * 注册网关
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent<MqttInfoReturnModel> registerGateWay(GateWayInfoAddParam param) {
+        ProjectInfo projectInfo = projectInfoDao.findTopByCode(param.getProjectInfoCode());
+        if (ObjectUtils.isEmpty(projectInfo)) {
+            return ResultContent.buildFail(String.format("分组不存在:%s", param.getProjectInfoCode()));
+        }
+        // 添加网关信息
+        ResultContent<GateWayInfo> gateWayInfo = addGateWayInfo(param);
+
+        // 给网关分配个mqtt账号
+        MqttInfoReturnModel mqttInfoSimpleModel = new MqttInfoReturnModel();
+        mqttInfoSimpleModel.setBrokerAddress("tcp://162.14.78.247:61616");
+        mqttInfoSimpleModel.setBrokerUsername("admin");
+        mqttInfoSimpleModel.setBrokerPassword("admin123");
+        return ResultContent.buildSuccess(mqttInfoSimpleModel);
+    }
+
+    /**
+     * 网关绑定设备、连接账号
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent gateWayBindDevice(GateWayBindDeviceParam param) {
+        // 网关信息
+        GateWayInfo gateWayInfo = gateWayInfoDao.findTopByGateWayId(param.getGateWayId());
+        if (ObjectUtils.isEmpty(gateWayInfo)) {
+            log.error("网关未注册");
+            return ResultContent.buildFail(String.format("网关未注册,清先注册网关:%s", param.getGateWayId()));
+        }
+
+        String projectInfoCode = param.getProjectInfoCode();
+        if (ObjectUtils.isEmpty(projectInfoCode)) {
+            projectInfoCode = gateWayInfo.getProjectInfoCode();
+        }
+
+        ProjectInfo projectInfo = projectInfoDao.findTopByCode(projectInfoCode);
+        if (ObjectUtils.isEmpty(projectInfo)) {
+            log.error("分组不存在");
+            return ResultContent.buildFail(String.format("分组不存在:%s", projectInfoCode));
+        }
+
+        List<DeviceInfoAddParam> devices = param.getDevices();
+        if (ObjectUtils.isNotEmpty(devices)) {
+            // 检查设备数据合法性
+            for (DeviceInfoAddParam device : devices) {
+                if (StringUtils.isEmpty(device.getDeviceId())) {
+                    return ResultContent.buildFail("deviceId不能为空");
+                }
+            }
+        }
+
+        if (StringUtils.isEmpty(param.getUserName())) {
+            param.setUserName("admin");
+        }
+        // 验证连接账号
+        GateWayUserInfo gateWayUserInfo = gateWayUserInfoDao.findTopByUserName(param.getUserName());
+        if (ObjectUtils.isEmpty(gateWayUserInfo)) {
+            return ResultContent.buildFail(String.format("连接账号不存在:%s", param.getUserName()));
+        }
+        // 验证账号是否已绑定网关
+        GateWay2User gateWay2User = gateWay2UserDao.findTopByGateWayUserInfo(gateWayUserInfo);
+        if (ObjectUtils.isNotEmpty(gateWay2User)) {
+            // 如果有绑定关系
+            GateWayInfo oldGateWayInfo = gateWay2User.getGateWayInfo();
+            if (!oldGateWayInfo.getGateWayId().equals(gateWayInfo.getGateWayId())) {
+//                return ResultContent.buildFail(String.format("连接账号%s已使用", gateWayUserInfo.getUserName()));
+            }
+        }
+
+        // 设备列表
+        List<DeviceInfo> deviceInfos = new ArrayList<>();
+        if (ObjectUtils.isNotEmpty(devices)) {
+            // 绑定网关和设备的关系
+            for (DeviceInfoAddParam device : devices) {
+                device.setProjectInfoCode(projectInfoCode);
+                // 保存设备信息
+                ResultContent<DeviceInfo> resultContent = deviceInfoService.addDeviceInfo(device);
+                DeviceInfo deviceInfo = resultContent.getContent();
+
+                // 设备可以绑定到多个网关,一个网关只能绑定设备一次
+                GateWay2Device gateWay2Device = gateWay2DeviceDao.findTopByGateWayInfoAndDeviceInfo(gateWayInfo, deviceInfo);
+                if (ObjectUtils.isEmpty(gateWay2Device)) {
+                    gateWay2Device = new GateWay2Device();
+                    gateWay2Device.setState(OnLineState.OffLine);
+                }
+                gateWay2Device.setGateWayInfo(gateWayInfo);
+                gateWay2Device.setDeviceInfo(deviceInfo);
+                gateWay2Device.setBindTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+                gateWay2DeviceDao.save(gateWay2Device);
+                deviceInfos.add(deviceInfo);
+            }
+        }
+
+        // 绑定网关和用户的关系
+        if (ObjectUtils.isEmpty(gateWay2User)) {
+            gateWay2User = new GateWay2User();
+        }
+        gateWay2User.setGateWayUserInfo(gateWayUserInfo);
+        gateWay2User.setGateWayInfo(gateWayInfo);
+        gateWay2User.setBindTime(System.currentTimeMillis());
+        gateWay2User.setBindTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+        gateWay2UserDao.save(gateWay2User);
+
+        // 添加连接账号关于:网关、设备的 Topic权限
+        bindUserGateWayPermissions(MqttTopicUtils.getUserRoleName(gateWayUserInfo), gateWayInfo.getGateWayId());
+
+        // 绑定用户设备权限
+        bindUserDevicesPermissions(MqttTopicUtils.getUserRoleName(gateWayUserInfo), deviceInfos);
+
+        log.info("设备注册成功:{}", deviceInfos.size());
+        // 同步设备
+        deviceSyncFullCardService.noticeSyncDevice(deviceInfos);
+
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 删除网关
+     *
+     * @param gateWayId
+     * @return
+     */
+    public ResultContent deleteGateWayInfo(String gateWayId) {
+        GateWayInfo gateWayInfo = gateWayInfoDao.findTopByGateWayId(gateWayId);
+        if (ObjectUtils.isEmpty(gateWayInfo)) {
+            return ResultContent.buildFail(String.format("网关ID不存在:%s", gateWayId));
+        }
+        gateWayInfoDao.delete(gateWayInfo);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 网关列表
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<GateWayInfoModel>> pageGateWay(Pageable pageable, GateWayInfoSearchParam param) {
+        Page<GateWayInfo> page = gateWayInfoDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+    }
+
+    /**
+     * 查询网关详情
+     *
+     * @param gateWayId
+     * @return
+     */
+    public ResultContent<GateWayInfoModel> getById(String gateWayId) {
+        GateWayInfoModel model = null;
+        GateWayInfo deviceInfo = gateWayInfoDao.findTopByGateWayId(gateWayId);
+        if (ObjectUtils.isNotEmpty(deviceInfo)) {
+            model = toModel(deviceInfo);
+        }
+        return ResultContent.buildSuccess(model);
+    }
+
+    public GateWayInfoModel toModel(GateWayInfo entity) {
+        GateWayInfoModel model = new GateWayInfoModel();
+        if (ObjectUtils.isNotEmpty(entity)) {
+            BeanUtils.copyProperties(entity, model);
+            model.setProjectInfo(projectInfoService.toModel(entity.getProjectInfo()));
+        }
+        return model;
+    }
+
+    /**
+     * 绑定角色网关的权限
+     *
+     * @param roleName
+     * @param gateWayId
+     * @return
+     */
+    public ResultContent bindUserGateWayPermissions(String roleName, String gateWayId) {
+        try {
+            List<MqttInfo> list = mqttInfoDao.findAll();
+            if (ObjectUtils.isNotEmpty(list)) {
+                for (MqttInfo mqttInfo : list) {
+                    String urlStr = String.format("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", mqttInfo.getJmxHost(), mqttInfo.getJmxPort());
+                    JMXServiceURL url = new JMXServiceURL(urlStr);
+                    @Cleanup JMXConnector connector = JMXConnectorFactory.connect(url, null);
+                    connector.connect();
+                    log.info("JMX %s:%s 连接成功...", mqttInfo.getJmxHost(), mqttInfo.getJmxPort());
+                    MBeanServerConnection connection = connector.getMBeanServerConnection();
+                    ObjectName addressObjectName = ObjectNameBuilder.create("org.apache.activemq.artemis", mqttInfo.getBrokerName()).getActiveMQServerObjectName();
+                    ActiveMQServerControl addressControl = MBeanServerInvocationHandler.newProxyInstance(connection, addressObjectName, ActiveMQServerControl.class, false);
+
+                    addressControl.addSecuritySettings(MqttTopicUtils.buildGateWayAllTopic(gateWayId), roleName, roleName, roleName, roleName, roleName, roleName, roleName, roleName, roleName, roleName);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("bindUserGateWayPermissions 出错:%s", e.getMessage());
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent bindUserDevicesPermissions(String roleName, List<DeviceInfo> deviceInfos) {
+        try {
+            List<MqttInfo> list = mqttInfoDao.findAll();
+            if (ObjectUtils.isNotEmpty(list)) {
+                list.parallelStream().forEach(mqttInfo -> {
+                    try {
+                        String urlStr = String.format("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", mqttInfo.getJmxHost(), mqttInfo.getJmxPort());
+                        JMXServiceURL url = new JMXServiceURL(urlStr);
+                        @Cleanup JMXConnector connector = JMXConnectorFactory.connect(url, null);
+                        connector.connect();
+                        log.info("JMX %s:%s 连接成功...", mqttInfo.getJmxHost(), mqttInfo.getJmxPort());
+                        MBeanServerConnection connection = connector.getMBeanServerConnection();
+                        ObjectName addressObjectName = ObjectNameBuilder.create("org.apache.activemq.artemis", mqttInfo.getBrokerName()).getActiveMQServerObjectName();
+                        ActiveMQServerControl addressControl = MBeanServerInvocationHandler.newProxyInstance(connection, addressObjectName, ActiveMQServerControl.class, false);
+
+                        for (DeviceInfo deviceInfo : deviceInfos) {
+                            addressControl.addSecuritySettings(MqttTopicUtils.buildDeviceAllTopic(deviceInfo.getDeviceId()), roleName, roleName, roleName, roleName, roleName, roleName, roleName, roleName, roleName, roleName);
+                        }
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                });
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("bindUserDevicesPermissions 出错:%s", e.getMessage());
+        }
+        return ResultContent.buildSuccess();
+    }
+
+}

+ 251 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/GateWayUserInfoService.java

@@ -0,0 +1,251 @@
+package com.zhongshu.iot.server.core.service.mqtt;
+
+import com.zhongshu.iot.client.model.mqtt.GateWayUserInfoAddParam;
+import com.zhongshu.iot.client.model.mqtt.GateWayUserInfoModel;
+import com.zhongshu.iot.client.model.mqtt.GateWayUserInfoNameParam;
+import com.zhongshu.iot.client.model.mqtt.GateWayUserInfoSearchParam;
+import com.zhongshu.iot.client.type.type.MqttUserState;
+import com.zhongshu.iot.server.core.dao.mqtt.GateWayUserInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.Mqtt2UserDao;
+import com.zhongshu.iot.server.core.dao.mqtt.MqttInfoDao;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.GateWayUserInfo;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.Mqtt2User;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.MqttInfo;
+import com.zhongshu.iot.server.core.util.DateUtils;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.Cleanup;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl;
+import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerInvocationHandler;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/5/17
+ */
+@Slf4j
+@Service
+public class GateWayUserInfoService {
+
+    @Autowired
+    GateWayUserInfoDao gateWayUserInfoDao;
+
+    @Autowired
+    MqttInfoDao mqttInfoDao;
+
+    @Autowired
+    Mqtt2UserDao mqtt2UserDao;
+
+    /**
+     * 初始mqtt连接账号信息
+     * @return
+     */
+    public ResultContent initDefaultUser() {
+        String userName = "admin";
+        GateWayUserInfo entity = gateWayUserInfoDao.findTopByUserName(userName);
+        if (ObjectUtils.isEmpty(entity)) {
+            GateWayUserInfoAddParam param = new GateWayUserInfoAddParam();
+            param.setUserName("admin");
+            param.setPassWord("admin123");
+            addGateWayUser(param);
+            log.info(String.format("gateWayUser [%s] 初始化成功", userName));
+        }else {
+            log.info(String.format("gateWayUser [%s] 已初始化", userName));
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 添加用户
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent addGateWayUser(GateWayUserInfoAddParam param) {
+        GateWayUserInfo entity = gateWayUserInfoDao.findTopByUserName(param.getUserName());
+        if (ObjectUtils.isNotEmpty(entity)) {
+            return ResultContent.buildFail(String.format("%s 用户已存在", param.getUserName()));
+        }
+        entity = new GateWayUserInfo();
+        BeanUtils.copyProperties(param, entity);
+        entity.setState(MqttUserState.Enable);
+        entity.setRoleName(param.getUserName());
+        gateWayUserInfoDao.save(entity);
+        // 同步用户
+        syncUserToMQTTService(entity);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 修改用户状态
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent updateState(GateWayUserInfoNameParam param) {
+        if (StringUtils.isEmpty(param.getUserName())) {
+            return ResultContent.buildFail(String.format("用户名称不能为空"));
+        }
+        if (param.getState() == null) {
+            return ResultContent.buildFail("状态不能为空");
+        }
+        GateWayUserInfo entity = gateWayUserInfoDao.findTopByUserName(param.getUserName());
+        if (ObjectUtils.isEmpty(entity)) {
+            return ResultContent.buildFail(String.format("账号不存在: %s", param.getUserName()));
+        }
+        if (param.getState() == entity.getState()) {
+            return ResultContent.buildFail(String.format("当前用户状态以为:%s", param.getState()));
+        }
+        entity.setState(param.getState());
+        gateWayUserInfoDao.save(entity);
+
+        // 同步
+        if (param.getState() == MqttUserState.Disable) {
+            // 不过不可用,则删除QMTT服务上的用户信息
+            deleteGateWayUser(entity);
+        } else {
+            // 添加用户
+            syncUserToMQTTService(entity);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 删除mqtt用户信息
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent deleteMqttUser(GateWayUserInfoNameParam param) {
+        GateWayUserInfo entity = gateWayUserInfoDao.findTopByUserName(param.getUserName());
+        if (ObjectUtils.isEmpty(entity)) {
+            return ResultContent.buildFail(String.format("%s 账号不存在", param.getUserName()));
+        }
+        gateWayUserInfoDao.delete(entity);
+        // 删除服务文件上的用户
+        deleteGateWayUser(entity);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 网关用户列表
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<GateWayUserInfoModel>> pageGateWayUser(Pageable pageable, GateWayUserInfoSearchParam param) {
+        Page<GateWayUserInfo> page = gateWayUserInfoDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+    }
+
+    /**
+     * 根据用户名称查询
+     *
+     * @param userName
+     * @return
+     */
+    public ResultContent<GateWayUserInfoModel> getUserByUserName(String userName) {
+        GateWayUserInfoModel model = null;
+        GateWayUserInfo entity = gateWayUserInfoDao.findTopByUserName(userName);
+        if (ObjectUtils.isNotEmpty(entity)) {
+            model = toModel(entity);
+        }
+        return ResultContent.buildSuccess(model);
+    }
+
+    /**
+     * 把用户同步到MQTT服务中
+     *
+     * @param gateWayUserInfo
+     * @return
+     */
+    public ResultContent syncUserToMQTTService(GateWayUserInfo gateWayUserInfo) {
+        //todo同步用户
+        List<MqttInfo> list = mqttInfoDao.findAll();
+        if (ObjectUtils.isNotEmpty(list)) {
+            for (MqttInfo mqttInfo : list) {
+                try {
+                    String urlStr = String.format("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", mqttInfo.getJmxHost(), mqttInfo.getJmxPort());
+                    JMXServiceURL url = new JMXServiceURL(urlStr);
+                    @Cleanup JMXConnector connector = JMXConnectorFactory.connect(url, null);
+                    connector.connect();
+                    log.info("JMX %s:%s 连接成功...", mqttInfo.getJmxHost(), mqttInfo.getJmxPort());
+                    MBeanServerConnection connection = connector.getMBeanServerConnection();
+                    ObjectName addressObjectName = ObjectNameBuilder.create("org.apache.activemq.artemis", mqttInfo.getBrokerName()).getActiveMQServerObjectName();
+                    ActiveMQServerControl addressControl = MBeanServerInvocationHandler.newProxyInstance(connection, addressObjectName, ActiveMQServerControl.class, false);
+                    addressControl.addUser(gateWayUserInfo.getUserName(), gateWayUserInfo.getPassWord(), gateWayUserInfo.getRoleName(), false);
+
+                    Mqtt2User mqtt2User = mqtt2UserDao.findTopByMqttInfoAndGateWayUserInfo(mqttInfo, gateWayUserInfo);
+                    if (ObjectUtils.isEmpty(mqtt2User)) {
+                        mqtt2User = new Mqtt2User();
+                    }
+                    mqtt2User.setGateWayUserInfo(gateWayUserInfo);
+                    mqtt2User.setMqttInfo(mqttInfo);
+                    mqtt2User.setIsSync(Boolean.TRUE);
+                    mqtt2User.setSyncTime(System.currentTimeMillis());
+                    mqtt2User.setSyncTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+                    mqtt2UserDao.save(mqtt2User);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 删除用户 从MQTT服务
+     *
+     * @param gateWayUserInfo
+     * @return
+     */
+    public ResultContent deleteGateWayUser(GateWayUserInfo gateWayUserInfo) {
+        List<MqttInfo> list = mqttInfoDao.findAll();
+        if (ObjectUtils.isNotEmpty(list)) {
+            for (MqttInfo mqttInfo : list) {
+                try {
+                    String urlStr = String.format("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", mqttInfo.getJmxHost(), mqttInfo.getJmxPort());
+                    JMXServiceURL url = new JMXServiceURL(urlStr);
+                    @Cleanup JMXConnector connector = JMXConnectorFactory.connect(url, null);
+                    connector.connect();
+                    log.info("JMX %s:%s 连接成功...", mqttInfo.getJmxHost(), mqttInfo.getJmxPort());
+                    MBeanServerConnection connection = connector.getMBeanServerConnection();
+                    ObjectName addressObjectName = ObjectNameBuilder.create("org.apache.activemq.artemis", mqttInfo.getBrokerName()).getActiveMQServerObjectName();
+                    ActiveMQServerControl addressControl = MBeanServerInvocationHandler.newProxyInstance(connection, addressObjectName, ActiveMQServerControl.class, false);
+                    addressControl.removeUser(gateWayUserInfo.getUserName());
+                    // 删除关系记录
+                    mqtt2UserDao.deleteByMqttInfoAndGateWayUserInfo(mqttInfo, gateWayUserInfo);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return ResultContent.buildSuccess();
+    }
+
+
+    public GateWayUserInfoModel toModel(GateWayUserInfo entity) {
+        GateWayUserInfoModel model = new GateWayUserInfoModel();
+        if (ObjectUtils.isNotEmpty(entity)) {
+            BeanUtils.copyProperties(entity, model);
+        }
+        return model;
+    }
+
+}

+ 133 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/MqttInfoService.java

@@ -0,0 +1,133 @@
+package com.zhongshu.iot.server.core.service.mqtt;
+
+import com.github.microservice.models.type.CommonState;
+import com.zhongshu.iot.client.model.mqtt.MqttInfoAddParam;
+import com.zhongshu.iot.client.model.mqtt.MqttInfoModel;
+import com.zhongshu.iot.client.model.mqtt.MqttInfoSimpleModel;
+import com.zhongshu.iot.server.core.dao.mqtt.Mqtt2UserDao;
+import com.zhongshu.iot.server.core.dao.mqtt.MqttInfoDao;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.MqttInfo;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author TRX
+ * @date 2024/5/21
+ */
+@Service
+@Slf4j
+public class MqttInfoService {
+    @Autowired
+    MqttInfoDao mqttInfoDao;
+
+    @Autowired
+    Mqtt2UserDao mqtt2UserDao;
+
+    /**
+     * 添加/编辑MQTT地址
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent addMqttInfo(MqttInfoAddParam param) {
+        MqttInfo mqttInfo = mqttInfoDao.findTopByBrokerHost(param.getBrokerHost());
+        if (ObjectUtils.isNotEmpty(mqttInfo) && StringUtils.isNotEmpty(param.getId()) && !param.getId().equals(mqttInfo.getId())) {
+            return ResultContent.buildFail(String.format("%s已存在"));
+        }
+        if (ObjectUtils.isEmpty(mqttInfo)) {
+            mqttInfo = new MqttInfo();
+            mqttInfo.setState(CommonState.Enable);
+        }
+        BeanUtils.copyProperties(param, mqttInfo);
+        mqttInfoDao.save(mqttInfo);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 删除MQTT连接
+     *
+     * @param id 数据ID
+     * @return
+     */
+    public ResultContent deleteMqttInfo(String id) {
+        MqttInfo mqttInfo = mqttInfoDao.findTopById(id);
+        if (ObjectUtils.isEmpty(mqttInfo)) {
+            return ResultContent.buildFail(String.format("%s 数据不存在", id));
+        }
+        mqttInfoDao.deleteById(id);
+        // 删除关联的用户关联数据
+        mqtt2UserDao.deleteByMqttInfo(mqttInfo);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 查找地址详情
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent<MqttInfoModel> getMqttInfoById(String id) {
+        MqttInfo mqttInfo = mqttInfoDao.findTopById(id);
+        if (ObjectUtils.isEmpty(mqttInfo)) {
+            return ResultContent.buildFail(String.format("%s 数据不存在", id));
+        }
+        MqttInfoModel model = toModel(mqttInfo);
+        return ResultContent.buildSuccess(model);
+    }
+
+    /**
+     * 查询所有的MQTT连接
+     *
+     * @return
+     */
+    public ResultContent<List<MqttInfoModel>> findAllMqttInfo() {
+        List<MqttInfo> mqttInfos = mqttInfoDao.findAll();
+        List<MqttInfoModel> models = new ArrayList<>();
+        if (ObjectUtils.isNotEmpty(mqttInfos)) {
+            models = mqttInfos.stream().map(this::toModel).collect(Collectors.toList());
+        }
+        return ResultContent.buildSuccess(models);
+    }
+
+    /**
+     * 查询所有的MQTT连接
+     *
+     * @return
+     */
+    public ResultContent<List<MqttInfoSimpleModel>> findAllMqttInfoSimple() {
+        List<MqttInfo> mqttInfos = mqttInfoDao.findAll();
+        List<MqttInfoSimpleModel> models = new ArrayList<>();
+        if (ObjectUtils.isNotEmpty(mqttInfos)) {
+            models = mqttInfos.stream().map(this::toSimpleModel).collect(Collectors.toList());
+        }
+        return ResultContent.buildSuccess(models);
+    }
+
+    public MqttInfoModel toModel(MqttInfo entity) {
+        MqttInfoModel model = null;
+        if (ObjectUtils.isNotEmpty(entity)) {
+            model = new MqttInfoModel();
+            BeanUtils.copyProperties(entity, model);
+        }
+        return model;
+    }
+
+    public MqttInfoSimpleModel toSimpleModel(MqttInfo entity) {
+        MqttInfoSimpleModel model = null;
+        if (ObjectUtils.isNotEmpty(entity)) {
+            model = new MqttInfoSimpleModel();
+            BeanUtils.copyProperties(entity, model);
+        }
+        return model;
+    }
+
+}

+ 109 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/ProjectInfoService.java

@@ -0,0 +1,109 @@
+package com.zhongshu.iot.server.core.service.mqtt;
+
+import com.github.microservice.models.type.CommonState;
+import com.zhongshu.iot.client.model.mqtt.ProjectInfoAddParam;
+import com.zhongshu.iot.client.model.mqtt.ProjectInfoModel;
+import com.zhongshu.iot.client.model.mqtt.ProjectInfoSearchParam;
+import com.zhongshu.iot.server.core.dao.mqtt.ProjectInfoDao;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.ProjectInfo;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author TRX
+ * @date 2024/5/21
+ */
+@Slf4j
+@Service
+public class ProjectInfoService extends SuperService {
+
+    @Autowired
+    ProjectInfoDao projectInfoDao;
+
+    /**
+     * 添加项目
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent<ProjectInfo> addProjectInfo(ProjectInfoAddParam param) {
+        if (StringUtils.isEmpty(param.getCode())) {
+            return ResultContent.buildFail("code不能为空");
+        }
+        ProjectInfo projectInfo = projectInfoDao.findTopByCode(param.getCode());
+        if (ObjectUtils.isEmpty(projectInfo)) {
+            projectInfo = new ProjectInfo();
+            initEntity(projectInfo);
+            BeanUtils.copyProperties(param, projectInfo, "id");
+        }else {
+            BeanUtils.copyProperties(param, projectInfo, "id", "createTime", "updateTime", "isDelete");
+        }
+        projectInfoDao.save(projectInfo);
+        return ResultContent.buildSuccess(projectInfo);
+    }
+
+    /**
+     * 项目列表
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<ProjectInfoModel>> page(Pageable pageable, ProjectInfoSearchParam param) {
+        Page<ProjectInfo> page = projectInfoDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.concurrent2PageModel(page, this::toModel));
+    }
+
+    /**
+     * 得到所有可用的项目
+     *
+     * @return
+     */
+    public ResultContent<List<ProjectInfoModel>> getAllProject() {
+        List<ProjectInfoModel> models = new ArrayList<>();
+        List<ProjectInfo> list = projectInfoDao.findByStateOrderByCreateTimeDesc(CommonState.Enable);
+        if (ObjectUtils.isNotEmpty(list)) {
+            models = list.stream().map(this::toModel).collect(Collectors.toList());
+        }
+        return ResultContent.buildSuccess(models);
+    }
+
+    public ResultContent deleteProjectInfo(String id) {
+        ProjectInfo projectInfo = projectInfoDao.findTopById(id);
+        if (ObjectUtils.isEmpty(projectInfo)) {
+            return ResultContent.buildFail(String.format("数据ID不存在: %s", id));
+        }
+        projectInfoDao.delete(projectInfo);
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent<ProjectInfoModel> getProjectInfo(String id) {
+        ProjectInfo projectInfo = projectInfoDao.findTopById(id);
+        if (ObjectUtils.isEmpty(projectInfo)) {
+            return ResultContent.buildFail(String.format("数据ID不存在: %s", id));
+        }
+        return ResultContent.buildSuccess(toModel(projectInfo));
+    }
+
+    public ProjectInfoModel toModel(ProjectInfo entity) {
+        ProjectInfoModel projectInfoModel = null;
+        if (ObjectUtils.isNotEmpty(entity)) {
+            projectInfoModel = new ProjectInfoModel();
+            BeanUtils.copyProperties(entity, projectInfoModel);
+        }
+        return projectInfoModel;
+    }
+}

+ 40 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/mqtt/ServerTimeService.java

@@ -0,0 +1,40 @@
+package com.zhongshu.iot.server.core.service.mqtt;
+
+import com.github.microservice.models.hxz.ServerTimeResult;
+import com.zhongshu.iot.server.core.domain.ExecuteAnnotationService;
+import com.zhongshu.iot.server.core.domain.ExecuteAnnotationServiceMethod;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.text.SimpleDateFormat;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * @author TRX
+ * @date 2024/7/22
+ */
+@Slf4j
+@Service
+@ExecuteAnnotationService
+public class ServerTimeService extends SuperService {
+
+    @ExecuteAnnotationServiceMethod(value = "ServerTime", remark = "服务器时间")
+    public ResultContent ServerTime(String str) {
+        var nowTime = new Date(System.currentTimeMillis());
+        ServerTimeResult result = new ServerTimeResult();
+        LocalDate date = LocalDate.now();
+        int week = Map.of(DayOfWeek.MONDAY, 1, DayOfWeek.TUESDAY, 2, DayOfWeek.WEDNESDAY, 3, DayOfWeek.THURSDAY, 4, DayOfWeek.FRIDAY, 5, DayOfWeek.SATURDAY, 6, DayOfWeek.SUNDAY, 0).get(date.getDayOfWeek());
+        String Time = new SimpleDateFormat("yyyyMMddHHmmss").format(nowTime) + "" + week;
+        result.setSuccess();
+        result.setTime(Time);
+        result.setWLPage(0);
+        result.setWLUptate(0);
+        return ResultContent.buildSuccess(result);
+    }
+
+}

+ 73 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/other/CollectionIdService.java

@@ -0,0 +1,73 @@
+package com.zhongshu.iot.server.core.service.other;
+
+import com.zhongshu.iot.server.core.dao.CollectionIdDao;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Service;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author TRX
+ * @date 2024/6/28
+ */
+@Slf4j
+@Service
+public class CollectionIdService {
+
+    @Autowired
+    ApplicationContext applicationContext;
+
+    @Autowired
+    CollectionIdDao collectionIdDao;
+
+    private final static SimpleDateFormat shortSdf = new SimpleDateFormat("yyyyMMdd");
+
+    /**
+     * 生成编码 (每天从 0001开始)
+     *
+     * @param sign 开头,如:XS
+     * @return 如:XS202303290001
+     */
+    public String getNextNo(String sign) {
+        String collectionName = sign + shortSdf.format(new Date());
+        Long no = collectionIdDao.getNextId(collectionName);
+        if (no != null) {
+            return String.format("%s%s%04d", sign, shortSdf.format(new Date()), no);
+        }
+        return "";
+    }
+
+    /**
+     * 生成编码 (每天从 0001开始)
+     *
+     * @param oid  企业ID
+     * @param sign 开头,如:XS
+     * @return 如:XS202303290001
+     */
+    public String getNo(String oid, String sign) {
+        String collectionName = sign + shortSdf.format(new Date());
+        Long no = collectionIdDao.getNextId(oid, collectionName);
+        if (no != null) {
+            return String.format("%s%s%04d", sign, shortSdf.format(new Date()), no);
+        }
+        return "";
+    }
+
+    /**
+     * 生成编码 (一直累加)
+     *
+     * @param oid  企业ID
+     * @param sign 开头,如:XS
+     * @return 如:XS202303290001
+     */
+    public String getNextNo(String oid, String sign) {
+        Long no = collectionIdDao.getNextId(oid, sign);
+        if (no != null) {
+            return String.format("%s%s%04d", sign, shortSdf.format(new Date()), no);
+        }
+        return "";
+    }
+}

+ 62 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/other/RequestInfoService.java

@@ -0,0 +1,62 @@
+package com.zhongshu.iot.server.core.service.other;
+
+import com.github.microservice.models.hxz.base.HxzBaseResult;
+import com.zhongshu.iot.client.type.RequestType;
+import com.zhongshu.iot.server.core.dao.payment.RequestInfoDao;
+import com.zhongshu.iot.server.core.domain.payment.RequestInfo;
+import com.zhongshu.iot.server.core.httpRequest.apiConf.APIResponseModel;
+import com.zhongshu.iot.server.core.util.DateUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+
+/**
+ * @author TRX
+ * @date 2024/6/25
+ */
+@Slf4j
+@Service
+public class RequestInfoService {
+
+    @Autowired
+    RequestInfoDao requestInfoDao;
+
+    /**
+     * 添加 HXZ 请求日志
+     *
+     * @param param
+     * @param response
+     * @return
+     */
+    public boolean addHXZRequestInfo(Object param, HxzBaseResult response) {
+        RequestInfo requestInfo = new RequestInfo();
+        requestInfo.setParam(param);
+        requestInfo.setResponse(response);
+        requestInfo.setIsSuccess(response.isSuccess());
+        requestInfo.setMsg(requestInfo.getMsg());
+        requestInfo.setTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+        requestInfo.setRequestType(RequestType.HXZ);
+        // 保存30天
+        requestInfo.setTTL(new Date(System.currentTimeMillis() + 30 * 24 * 60 * 60 * 1000L));
+        requestInfoDao.save(requestInfo);
+        return true;
+    }
+
+    public boolean addRequestInfo(Object param, APIResponseModel response) {
+        RequestInfo requestInfo = new RequestInfo();
+        requestInfo.setParam(param);
+        requestInfo.setResponse(response);
+        requestInfo.setIsSuccess(response.isSuccess());
+        requestInfo.setMsg(response.getMsg());
+        requestInfo.setTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+        requestInfo.setRequestType(RequestType.FullCard);
+        requestInfo.setMillis(response.getMillis());
+        requestInfo.setApiParam(response.getParam());
+        // 保存30天
+        requestInfo.setTTL(new Date(System.currentTimeMillis() + 30 * 24 * 60 * 60 * 1000L));
+        requestInfoDao.save(requestInfo);
+        return true;
+    }
+}

+ 60 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/other/ScanExecuteService.java

@@ -0,0 +1,60 @@
+package com.zhongshu.iot.server.core.service.other;
+
+import com.zhongshu.iot.server.core.dao.other.ExecuteMethodInfoDao;
+import com.zhongshu.iot.server.core.domain.ExecuteAnnotationService;
+import com.zhongshu.iot.server.core.domain.ExecuteAnnotationServiceMethod;
+import com.zhongshu.iot.server.core.domain.other.ExecuteMethodInfo;
+import com.zhongshu.iot.server.core.util.CommonUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author TRX
+ * @date 2024/6/27
+ */
+@Slf4j
+@Service
+public class ScanExecuteService {
+
+    @Autowired
+    ExecuteMethodInfoDao executeMethodInfoDao;
+
+    /**
+     * 扫码方法
+     */
+    public void scanSystemExecuteMethod() {
+        Set<Class<?>> domainClasses = ScanSupport.classInfos();
+        List<Class> standardClasses = domainClasses.stream().filter(cls -> cls.isAnnotationPresent(ExecuteAnnotationService.class)).collect(Collectors.toList());
+        executeMethodInfoDao.deleteAll();
+        List<ExecuteMethodInfo> list = new ArrayList<>();
+        if (ObjectUtils.isNotEmpty(standardClasses)) {
+            for (Class cls : standardClasses) {
+                String className = CommonUtil.toFirstCharLowerCase(cls.getSimpleName());
+                Method[] methods = cls.getMethods();
+                for (Method method : methods) {
+                    if (method.isAnnotationPresent(ExecuteAnnotationServiceMethod.class)) {
+                        String methodName = method.getName();
+                        ExecuteAnnotationServiceMethod t = method.getAnnotation(ExecuteAnnotationServiceMethod.class);
+                        ExecuteMethodInfo methodInfo = new ExecuteMethodInfo();
+                        methodInfo.setMethodName(methodName);
+                        methodInfo.setBeanName(className);
+                        methodInfo.setEvent(t.value());
+                        methodInfo.setRemark(t.remark());
+                        list.add(methodInfo);
+                    }
+                }
+            }
+        }
+        executeMethodInfoDao.saveAll(list);
+        log.info("scanSystemExecuteMethod size: {} ", list.size());
+    }
+
+}

+ 95 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/other/ScanSupport.java

@@ -0,0 +1,95 @@
+package com.zhongshu.iot.server.core.service.other;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternUtils;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.SystemPropertyUtils;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+
+@Slf4j
+@Component
+public class ScanSupport implements ResourceLoaderAware {
+    /**
+     * Spring容器注入
+     */
+    private ResourceLoader resourceLoader;
+
+    private ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
+    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
+    private static final String FULLTEXT_SACN_PACKAGE_PATH = "com.zhongshu.iot.server.core";
+
+    /**
+     * set注入对象
+     */
+    @Override
+    public void setResourceLoader(ResourceLoader resourceLoader) {
+        this.resourceLoader = resourceLoader;
+    }
+
+    /**
+     * 利用spring提供的扫描包下面的类信息,再通过classfrom反射获得类信息
+     *
+     * @param scanPath
+     * @return
+     * @throws IOException
+     */
+    public Set<Class<?>> doScan(String scanPath) {
+        Set<Class<?>> classes = new HashSet<Class<?>>();
+        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+                .concat(ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(scanPath))
+                        .concat("/**/*.class"));
+        Resource[] resources = new Resource[0];
+        try {
+            resources = resolver.getResources(packageSearchPath);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        MetadataReader metadataReader = null;
+        for (Resource resource : resources) {
+            if (resource.isReadable()) {
+                try {
+                    metadataReader = metadataReaderFactory.getMetadataReader(resource);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+                try {
+                    if (metadataReader.getClassMetadata().isConcrete()) {// 当类型不是抽象类或接口在添加到集合
+                        classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
+                    }
+                } catch (ClassNotFoundException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return classes;
+    }
+
+    /**
+     * 指定包下面的类信息
+     *
+     * @return 多个类信息
+     */
+    public static Set<Class<?>> classInfos() {
+        try {
+//            String scanPath = ApplicationContextHolder.getContext().getEnvironment()
+//                    .getProperty(FULLTEXT_SACN_PACKAGE_PATH);
+//            scanPath = FULLTEXT_SACN_PACKAGE_PATH;
+            return new ScanSupport().doScan(FULLTEXT_SACN_PACKAGE_PATH);
+        } catch (Exception e) {
+            log.error("扫描包类信息错误", e);
+        }
+        return null;
+    }
+}

+ 169 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/payment/HxzService.java

@@ -0,0 +1,169 @@
+package com.zhongshu.iot.server.core.service.payment;
+
+import cn.hutool.json.JSONUtil;
+import com.github.microservice.models.hxz.*;
+import com.github.microservice.models.type.PaymentType;
+import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
+import com.zhongshu.iot.server.core.domain.ExecuteAnnotationService;
+import com.zhongshu.iot.server.core.domain.ExecuteAnnotationServiceMethod;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.DeviceInfo;
+import com.zhongshu.iot.server.core.httpRequest.ApiRequestService;
+import com.zhongshu.iot.server.core.httpRequest.apiConf.APIResponseModel;
+import com.zhongshu.iot.server.core.httpRequest.conf.FullCardAPIConfig;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.service.other.RequestInfoService;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.text.SimpleDateFormat;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * @author TRX
+ * @date 2024/6/25
+ */
+@Slf4j
+@Service
+@ExecuteAnnotationService
+public class HxzService extends SuperService {
+
+    @Autowired
+    DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    RequestInfoService requestInfoService;
+
+    @Autowired
+    ApiRequestService apiRequestService;
+
+    public ServerTimeResult serverTime(ServerTimeModel param) {
+        String deviceId = param.getDeviceID().toString();
+        ServerTimeResult result = new ServerTimeResult();
+        DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceId(deviceId);
+        if (ObjectUtils.isEmpty(deviceInfo)) {
+            result.setFailed("设备未注册");
+            requestInfoService.addHXZRequestInfo(param, result);
+            return result;
+        }
+        // 业务验证
+        APIResponseModel resultContent = apiRequestService.sendFullCardAPI(
+                FullCardAPIConfig.ServerTime, param);
+        if (resultContent.isSuccess()) {
+            ServerTimeResult timeResult = resultContent.toBean(ServerTimeResult.class);
+
+            if (timeResult.isSuccess()) {
+                var nowTime = new Date(System.currentTimeMillis());
+                LocalDate date = LocalDate.now();
+                int week = Map.of(DayOfWeek.MONDAY, 1, DayOfWeek.TUESDAY, 2, DayOfWeek.WEDNESDAY, 3, DayOfWeek.THURSDAY, 4, DayOfWeek.FRIDAY, 5, DayOfWeek.SATURDAY, 6, DayOfWeek.SUNDAY, 0).get(date.getDayOfWeek());
+                result.setSuccess();
+                result.setTime(new SimpleDateFormat("yyyyMMddHHmmss").format(nowTime) + "" + week);
+                result.setWLPage(0);
+                result.setWLUptate(0);
+                log.info("ServerTime : {} - {}", result);
+            } else {
+                result.setFailed(timeResult.getMsg());
+            }
+        } else {
+            result.setFailed(resultContent.getMsg());
+        }
+        return result;
+    }
+
+    /**
+     * 刷卡扣费
+     *
+     * @param dataStr
+     * @return
+     */
+    @ExecuteAnnotationServiceMethod(value = "ConsumTransactions", remark = "云版消费机 2.2 用户刷卡综合接口")
+    public ResultContent<Object> consumTransactions(String dataStr) {
+        ConsumTransactionsModel params = JSONUtil.toBean(dataStr, ConsumTransactionsModel.class);
+        ConsumTransactionsResult ret = new ConsumTransactionsResult();
+
+        ConsumTransactionsFullParam param = new ConsumTransactionsFullParam();
+        param.setParam(params);
+        APIResponseModel resultContent = apiRequestService.sendFullCardAPI(
+                FullCardAPIConfig.ConsumTransactions, param);
+        if (resultContent.isSuccess()) {
+            // 请求成功
+            ConsumTransactionsFullResult fullResult = resultContent.toBean(ConsumTransactionsFullResult.class);
+            ret = fullResult.getModel();
+        } else {
+            // 失败
+            ret.setStatus(0);
+            ret.setMsg(resultContent.getMsg());
+        }
+        return ResultContent.buildSuccess(ret);
+    }
+
+    @ExecuteAnnotationServiceMethod(value = "orderQuery", remark = "云版消费机 2.3 用户刷卡消费结果查询接口")
+    public ResultContent<Object> orderQuery(String dataStr) {
+        OrderQueryModel param = JSONUtil.toBean(dataStr, OrderQueryModel.class);
+
+        ConsumTransactionsResult ret = new ConsumTransactionsResult();
+        APIResponseModel resultContent = apiRequestService.sendFullCardAPI(
+                FullCardAPIConfig.orderQuery, param);
+        if (resultContent.isSuccess()) {
+            // 请求成功
+            ConsumTransactionsFullResult fullResult = resultContent.toBean(ConsumTransactionsFullResult.class);
+            ret = fullResult.getModel();
+        } else {
+            // 失败
+            ret.setStatus(0);
+            ret.setMsg(resultContent.getMsg());
+        }
+        return ResultContent.buildSuccess(ret);
+    }
+
+    /**
+     * 付款码(二维码)
+     *
+     * @param dataStr
+     * @return
+     */
+    @ExecuteAnnotationServiceMethod(value = "QRCodeTransaction", remark = "云版消费机 2.7 付款码(二维码)支付接口")
+    public ResultContent<Object> QRCodeTransaction(String dataStr) {
+        QRCodeTransactionModel params = JSONUtil.toBean(dataStr, QRCodeTransactionModel.class);
+        // 根据QR判断支付模式
+        params.setPaymentType(PaymentType.WxQrCode);
+
+        QRCodeTransactionResult ret = new QRCodeTransactionResult();
+        APIResponseModel resultContent = apiRequestService.sendFullCardAPI(
+                FullCardAPIConfig.QRCodeTransaction, params);
+        if (resultContent.isSuccess()) {
+            // 请求成功
+            QRCodeTransactionFullResult fullResult = resultContent.toBean(QRCodeTransactionFullResult.class);
+            ret = fullResult.getModel();
+        } else {
+            // 失败
+            ret.setStatus(0);
+            ret.setMsg(resultContent.getMsg());
+        }
+        return ResultContent.buildSuccess(ret);
+    }
+
+    @ExecuteAnnotationServiceMethod(value = "TransactionInquiry", remark = "云版消费机 2.8.二维码支付结果查询接口")
+    public ResultContent<Object> TransactionInquiry(String dataStr) {
+        TransactionInquiryModel params = JSONUtil.toBean(dataStr, TransactionInquiryModel.class);
+        ConsumTransactionsResult ret = new ConsumTransactionsResult();
+        APIResponseModel resultContent = apiRequestService.sendFullCardAPI(
+                FullCardAPIConfig.TransactionInquiry, params);
+        if (resultContent.isSuccess()) {
+            // 请求成功
+            ConsumTransactionsFullResult fullResult = resultContent.toBean(ConsumTransactionsFullResult.class);
+            ret = fullResult.getModel();
+        } else {
+            // 失败
+            ret.setStatus(0);
+            ret.setMsg(resultContent.getMsg());
+        }
+        return ResultContent.buildSuccess(ret);
+    }
+
+}

+ 4 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/payment/package-info.java

@@ -0,0 +1,4 @@
+package com.zhongshu.iot.server.core.service.payment;
+/**
+ * 支付相关的Service
+ */

+ 248 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/sync/DeviceSyncFullCardService.java

@@ -0,0 +1,248 @@
+package com.zhongshu.iot.server.core.service.sync;
+
+import com.github.microservice.models.device.DeviceInfoSyncParam;
+import com.github.microservice.models.device.DeviceSyncListParam;
+import com.github.microservice.models.device.GateWaySyncParam;
+import com.github.microservice.models.hxz.DevicePingInfoParam;
+import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.GateWay2DeviceDao;
+import com.zhongshu.iot.server.core.dao.mqtt.GateWayInfoDao;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.DeviceInfo;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.GateWay2Device;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.GateWayInfo;
+import com.zhongshu.iot.server.core.event.DeviceOnLineTimeChangeEvent;
+import com.zhongshu.iot.server.core.event.DeviceStateChangeEvent;
+import com.zhongshu.iot.server.core.event.DeviceSyncEvent;
+import com.zhongshu.iot.server.core.event.GateWaySyncEvent;
+import com.zhongshu.iot.server.core.httpRequest.ApiRequestService;
+import com.zhongshu.iot.server.core.httpRequest.apiConf.APIResponseModel;
+import com.zhongshu.iot.server.core.httpRequest.conf.FullCardAPIConfig;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.service.user.OperationLogsService;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 物联网平台 设备 网关同步到全卡项目
+ *
+ * @author TRX
+ * @date 2024/6/26
+ */
+@Slf4j
+@Service
+public class DeviceSyncFullCardService extends SuperService {
+
+    @Autowired
+    DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    GateWayInfoDao gateWayInfoDao;
+
+    @Autowired
+    OperationLogsService operationLogsService;
+
+    @Autowired
+    ApiRequestService apiRequestService;
+
+    @Autowired
+    ApplicationContext applicationContext;
+
+    @Autowired
+    GateWay2DeviceDao gateWay2DeviceDao;
+
+    /**
+     * 通知同步设备
+     *
+     * @param deviceInfo
+     * @return
+     */
+    public ResultContent noticeSyncDevice(DeviceInfo deviceInfo) {
+        if (ObjectUtils.isNotEmpty(deviceInfo)) {
+            List<String> deviceIds = new ArrayList<>();
+            deviceIds.add(deviceInfo.getDeviceId());
+            DeviceSyncEvent event = new DeviceSyncEvent(this, deviceIds);
+            applicationContext.publishEvent(event);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 通知同步设备
+     *
+     * @param deviceInfos
+     * @return
+     */
+    public ResultContent noticeSyncDevice(List<DeviceInfo> deviceInfos) {
+        if (ObjectUtils.isNotEmpty(deviceInfos)) {
+            List<String> deviceIds = deviceInfos.stream().map(it -> it.getDeviceId()).collect(Collectors.toList());
+            DeviceSyncEvent event = new DeviceSyncEvent(this, deviceIds);
+            applicationContext.publishEvent(event);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 通知同步设备
+     *
+     * @param deviceIds
+     * @return
+     */
+    public ResultContent noticeSyncDeviceIds(List<String> deviceIds) {
+        if (ObjectUtils.isNotEmpty(deviceIds)) {
+            DeviceSyncEvent event = new DeviceSyncEvent(this, deviceIds);
+            applicationContext.publishEvent(event);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 接收到同步设备的事件
+     *
+     * @param event
+     */
+    @EventListener(classes = DeviceSyncEvent.class)
+    @Async
+    @SneakyThrows
+    public void syncDeviceInfo(DeviceSyncEvent event) {
+        List<String> deviceIds = event.getDeviceIds();
+        log.info("event syncDeviceInfo: {}", deviceIds);
+        List<DeviceInfo> list = deviceInfoDao.findByDeviceIdIn(deviceIds);
+        DeviceSyncListParam param = new DeviceSyncListParam();
+        List<DeviceInfoSyncParam> deviceInfos = new ArrayList<>();
+        if (ObjectUtils.isNotEmpty(list)) {
+            deviceInfos = list.stream().map(it -> {
+                DeviceInfoSyncParam syncParam = new DeviceInfoSyncParam();
+
+                BeanUtils.copyProperties(it, syncParam);
+                List<GateWay2Device> gateWay2Devices = gateWay2DeviceDao.findByDeviceInfo(it);
+                List<String> gateWayIds = gateWay2Devices.stream().map(it2 -> {
+                    return it2.getGateWayInfo().getGateWayId();
+                }).collect(Collectors.toList());
+                syncParam.setGateWayId(String.join(",", gateWayIds));
+                // 项目code
+                syncParam.setProjectInfoCode(it.getProjectInfoCode());
+                return syncParam;
+            }).collect(Collectors.toList());
+        }
+        param.setList(deviceInfos);
+        APIResponseModel api = apiRequestService.sendFullCardAPI(FullCardAPIConfig.deviceSync, param);
+        log.info("同步设备情况:{} {}", api.isSuccess(), api.getMsg());
+    }
+
+    /**
+     * 通知同步网关信息
+     *
+     * @param gateWayInfo
+     * @return
+     */
+    public ResultContent noticeSyncGateWay(GateWayInfo gateWayInfo) {
+        if (ObjectUtils.isNotEmpty(gateWayInfo)) {
+            GateWaySyncEvent event = new GateWaySyncEvent(this, List.of(gateWayInfo.getGateWayId()));
+            applicationContext.publishEvent(event);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent noticeSyncGateWay(List<GateWayInfo> gateWayInfos) {
+        if (ObjectUtils.isNotEmpty(gateWayInfos)) {
+            GateWaySyncEvent event = new GateWaySyncEvent(this, gateWayInfos.stream().map(it -> it.getGateWayId()).collect(Collectors.toList()));
+            applicationContext.publishEvent(event);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 同步网关信息
+     *
+     * @param event
+     */
+    @EventListener(classes = GateWaySyncEvent.class)
+    @Async
+    @SneakyThrows
+    public void syncGateWayInfo(GateWaySyncEvent event) {
+        List<String> gatewayIds = event.getGateWayIds();
+        log.info("event syncGateWayInfo: {}", gatewayIds);
+        List<GateWayInfo> gateWayInfos = gateWayInfoDao.findByGateWayIdIn(gatewayIds);
+        if (ObjectUtils.isNotEmpty(gateWayInfos)) {
+            GateWaySyncParam syncParam = new GateWaySyncParam();
+            List<GateWaySyncParam.GateWaySyncInfo> list = gateWayInfos.stream().map(it -> {
+                GateWaySyncParam.GateWaySyncInfo syncInfo = new GateWaySyncParam.GateWaySyncInfo();
+                BeanUtils.copyProperties(it, syncInfo);
+                return syncInfo;
+            }).collect(Collectors.toList());
+            syncParam.setList(list);
+            APIResponseModel api = apiRequestService.sendFullCardAPI(FullCardAPIConfig.syncGateWays, syncParam);
+            log.info("同步网关情况:{} {}", api.isSuccess(), api.getMsg());
+        }
+    }
+
+    public ResultContent noticeSyncDeviceOnlineStateChange(String deviceId) {
+        if (ObjectUtils.isNotEmpty(deviceId)) {
+            DeviceStateChangeEvent event = new DeviceStateChangeEvent(this, deviceId);
+            applicationContext.publishEvent(event);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    @EventListener(classes = DeviceStateChangeEvent.class)
+    @Async
+    @SneakyThrows
+    public void syncDeviceOnlineStateChange(DeviceStateChangeEvent event) {
+        String deviceId = event.getDeviceId();
+        log.info("event syncDeviceOnlineStateChange: {}", deviceId);
+        DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceId(deviceId);
+        if (ObjectUtils.isNotEmpty(deviceInfo)) {
+            DevicePingInfoParam param = new DevicePingInfoParam();
+            param.setDeviceId(deviceInfo.getDeviceId());
+            param.setLastOnlineTime(deviceInfo.getLastOnlineTime());
+            param.setOnLineState(deviceInfo.getOnLineState().name());
+            param.setProjectInfoCode(deviceInfo.getProjectInfoCode());
+            String state = null;
+            if (deviceInfo.getState() != null) {
+                state = deviceInfo.getState().name();
+            }
+            param.setState(state);
+            APIResponseModel api = apiRequestService.sendFullCardAPI(FullCardAPIConfig.syncDeviceOnLineState, param);
+            log.info("同步设备在线情况:{} {}", api.isSuccess(), api.getMsg());
+        }
+    }
+
+    public ResultContent noticeSyncDeviceOnlineTimeChange(String deviceId) {
+        if (ObjectUtils.isNotEmpty(deviceId)) {
+            DeviceOnLineTimeChangeEvent event = new DeviceOnLineTimeChangeEvent(this, deviceId);
+            applicationContext.publishEvent(event);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    @EventListener(classes = DeviceOnLineTimeChangeEvent.class)
+    @Async
+    @SneakyThrows
+    public void syncDeviceOnlineTimeChange(DeviceOnLineTimeChangeEvent event) {
+        String deviceId = event.getDeviceId();
+        log.info("event syncDeviceOnlineTimeChange: {}", deviceId);
+        DeviceInfo deviceInfo = deviceInfoDao.findTopByDeviceId(deviceId);
+        if (ObjectUtils.isNotEmpty(deviceInfo)) {
+            DevicePingInfoParam param = new DevicePingInfoParam();
+            param.setDeviceId(deviceInfo.getDeviceId());
+            param.setLastOnlineTime(deviceInfo.getLastOnlineTime());
+            param.setOnLineState(deviceInfo.getOnLineState().name());
+            param.setProjectInfoCode(deviceInfo.getProjectInfoCode());
+            APIResponseModel api = apiRequestService.sendFullCardAPI(FullCardAPIConfig.syncDeviceOnLineState, param);
+            log.info("同步设备在线时间:{} {}", api.isSuccess(), api.getMsg());
+        }
+    }
+
+}

+ 42 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/sync/ProjectFromFullCardService.java

@@ -0,0 +1,42 @@
+package com.zhongshu.iot.server.core.service.sync;
+
+import com.github.microservice.models.project.ProjectSyncParam;
+import com.zhongshu.iot.client.model.mqtt.ProjectInfoAddParam;
+import com.zhongshu.iot.server.core.service.mqtt.ProjectInfoService;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/6/28
+ */
+@Slf4j
+@Service
+public class ProjectFromFullCardService {
+
+    @Autowired
+    ProjectInfoService projectInfoService;
+
+    /**
+     * 保存全卡同步过来的项目信息
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent syncFromFullCardProjects(ProjectSyncParam param) {
+        List<ProjectSyncParam.ProjectSyncInfo> list = param.getList();
+        log.info("syncFromFullCardProjects: {}", list.size());
+        for (ProjectSyncParam.ProjectSyncInfo syncInfo : list) {
+            ProjectInfoAddParam addParam = new ProjectInfoAddParam();
+            BeanUtils.copyProperties(syncInfo, addParam);
+            projectInfoService.addProjectInfo(addParam);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+}

+ 25 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/DepartmentService.java

@@ -0,0 +1,25 @@
+package com.zhongshu.iot.server.core.service.user;
+
+import com.zhongshu.iot.client.model.user.DepartmentModel;
+import com.zhongshu.iot.client.model.user.DepartmentParam;
+import com.zhongshu.iot.client.utils.ITree;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+
+import java.util.List;
+
+/**
+ * @author TRX
+ * @date 2024/6/20
+ */
+public interface DepartmentService {
+
+    ResultContent addDepartment(DepartmentParam param);
+
+    ResultContent<List<ITree>> getDepartmentTree();
+
+    // 查询所有上级部门
+    List<DepartmentModel> getParents(String id);
+
+    // 删除部门
+    ResultContent deleteDepartment(String id);
+}

+ 147 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/OperationLogsService.java

@@ -0,0 +1,147 @@
+package com.zhongshu.iot.server.core.service.user;
+
+import com.zhongshu.iot.client.model.operLogs.OperationLogsModel;
+import com.zhongshu.iot.client.model.operLogs.OperationLogsSearchParam;
+import com.zhongshu.iot.client.type.OperationLogType;
+import com.zhongshu.iot.client.type.type.LogsLevel;
+import com.zhongshu.iot.server.core.dao.UserDao;
+import com.zhongshu.iot.server.core.dao.mqtt.OperationLogsDao;
+import com.zhongshu.iot.server.core.domain.base.SuperEntity;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.OperationLogs;
+import com.zhongshu.iot.server.core.domain.user.User;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.util.DateUtils;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.net.IPUtil;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author TRX
+ * @date 2024/5/24
+ */
+@Slf4j
+@Service
+public class OperationLogsService extends SuperService {
+
+    @Autowired
+    private OperationLogsDao operationLogsDao;
+
+    @Autowired
+    private UserDao userDao;
+
+    @Autowired
+    HttpServletRequest request;
+
+    public ResultContent addLogs(String content, LogsLevel level) {
+        OperationLogs logs = new OperationLogs();
+        logs.setContent(content);
+        if (level == null) {
+            level = LogsLevel.Low;
+        }
+        logs.setLevel(level);
+        User user = getCrrentUser();
+        if (ObjectUtils.isNotEmpty(user)) {
+            logs.setUid(user.getId());
+            logs.setUserName(user.getUserName());
+            logs.setLoginName(user.getLoginName());
+        }
+        logs.setTime(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+        try {
+            logs.setUa(request.getHeader("User-Agent"));
+            logs.setIp(IPUtil.getRemoteIp(request));
+            logs.setUrl(request.getRequestURI());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        operationLogsDao.save(logs);
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent addLogs(String content, LogsLevel level, SuperEntity entity) {
+        OperationLogs logs = new OperationLogs();
+        logs.setContent(content);
+        if (level == null) {
+            level = LogsLevel.Low;
+        }
+        logs.setLevel(level);
+        User user = getCrrentUser();
+        if (ObjectUtils.isNotEmpty(user)) {
+            logs.setUid(user.getId());
+            logs.setUserName(user.getUserName());
+            logs.setLoginName(user.getLoginName());
+        }
+        logs.setTime(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+        if (ObjectUtils.isNotEmpty(entity)) {
+            logs.setDataId(entity.getId());
+            logs.setBackObj(entity);
+        }
+        try {
+            logs.setUa(request.getHeader("User-Agent"));
+            logs.setIp(IPUtil.getRemoteIp(request));
+            logs.setUrl(request.getRequestURI());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        operationLogsDao.save(logs);
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent addLogs(String content, LogsLevel level, OperationLogType operationLogType, SuperEntity entity) {
+        OperationLogs logs = new OperationLogs();
+        logs.setContent(content);
+        logs.setOperationLogType(operationLogType);
+        if (level == null) {
+            level = LogsLevel.Low;
+        }
+        logs.setLevel(level);
+        User user = getCrrentUser();
+        if (ObjectUtils.isNotEmpty(user)) {
+            logs.setUid(user.getId());
+            logs.setUserName(user.getUserName());
+            logs.setLoginName(user.getLoginName());
+        }
+        logs.setTime(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+        if (ObjectUtils.isNotEmpty(entity)) {
+            logs.setDataId(entity.getId());
+            logs.setBackObj(entity);
+        }
+        try {
+            logs.setUa(request.getHeader("User-Agent"));
+            logs.setIp(IPUtil.getRemoteIp(request));
+            logs.setUrl(request.getRequestURI());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        operationLogsDao.save(logs);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 日志列表
+     *
+     * @param pageable
+     * @param param
+     * @return
+     */
+    public ResultContent<Page<OperationLogsModel>> pageLogs(Pageable pageable, OperationLogsSearchParam param) {
+        Page<OperationLogs> page = operationLogsDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.toPageModel(page, this::toModel));
+    }
+
+    public OperationLogsModel toModel(OperationLogs entity) {
+        OperationLogsModel model = new OperationLogsModel();
+        if (ObjectUtils.isNotEmpty(entity)) {
+            BeanUtils.copyProperties(entity, model);
+        }
+        return model;
+    }
+
+}

+ 8 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/UserService.java

@@ -0,0 +1,8 @@
+package com.zhongshu.iot.server.core.service.user;
+
+/**
+ * @author TRX
+ * @date 2024/6/18
+ */
+public interface UserService {
+}

+ 158 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/impl/DepartmentServiceImpl.java

@@ -0,0 +1,158 @@
+package com.zhongshu.iot.server.core.service.user.impl;
+
+import com.zhongshu.iot.client.model.user.DepartmentModel;
+import com.zhongshu.iot.client.model.user.DepartmentParam;
+import com.zhongshu.iot.client.type.DataState;
+import com.zhongshu.iot.client.utils.ITree;
+import com.zhongshu.iot.client.utils.TreeUtil;
+import com.zhongshu.iot.server.core.dao.DepartmentDao;
+import com.zhongshu.iot.server.core.dataConfig.ResultMessage;
+import com.zhongshu.iot.server.core.domain.user.Department;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.service.user.DepartmentService;
+import com.zhongshu.iot.server.core.util.CommonUtil;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author TRX
+ * @date 2024/6/3
+ */
+@Slf4j
+@Service
+public class DepartmentServiceImpl extends SuperService implements DepartmentService {
+
+    @Autowired
+    DepartmentDao departmentDao;
+
+    /**
+     * 添加部门
+     *
+     * @param param
+     * @return
+     */
+    @Override
+    public ResultContent<Department> addDepartment(DepartmentParam param) {
+        initDefaultUser(param);
+
+        if (CommonUtil.longIsEmpty(param.getSort())) {
+            param.setSort(1L);
+        }
+        if (StringUtils.isEmpty(param.getParentId())) {
+            param.setParentId(ITree.ROOT_ID);
+        }
+        if (!param.getParentId().equals(ITree.ROOT_ID)) {
+            Department temp = departmentDao.findTopById(param.getParentId());
+            if (ObjectUtils.isEmpty(temp)) {
+                return ResultContent.buildFail(String.format("上级部门ID不存在:%s", param.getParentId()));
+            }
+        }
+        if (param.getState() == null) {
+            param.setState(DataState.Enable);
+        }
+        Department department = null;
+        if (StringUtils.isNotEmpty(param.getId())) {
+            department = departmentDao.findTopById(param.getId());
+            if (ObjectUtils.isEmpty(department)) {
+                return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+            }
+            if (StringUtils.isNotEmpty(param.getCode())) {
+                department = departmentDao.findTopByCode(param.getCode());
+                if (ObjectUtils.isNotEmpty(department) && !department.getId().equals(param.getId())) {
+                    return ResultContent.buildFail(String.format("%s code已存在", param.getCode()));
+                }
+            }
+        } else {
+            if (StringUtils.isNotEmpty(param.getCode())) {
+                department = departmentDao.findTopByCode(param.getCode());
+                if (ObjectUtils.isNotEmpty(department)) {
+                    return ResultContent.buildFail(String.format("%s code已存在", param.getCode()));
+                }
+            }
+            department = new Department();
+        }
+        BeanUtils.copyProperties(param, department);
+        departmentDao.save(department);
+        return ResultContent.buildSuccess(department);
+    }
+
+    /**
+     * 删除部门
+     *
+     * @param id
+     * @return
+     */
+    @Override
+    public ResultContent deleteDepartment(String id) {
+        Department department = departmentDao.findTopById(id);
+        if (ObjectUtils.isEmpty(department)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        List<Department> childrens = departmentDao.findByParentId(id);
+        if (ObjectUtils.isNotEmpty(childrens)) {
+            return ResultContent.buildFail(String.format("%s 有下级部门,不能删除", department.getName()));
+        }
+        departmentDao.delete(department);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 得到结构所有部门信息 (树形结构)
+     *
+     * @param
+     * @return
+     */
+    @Override
+    public ResultContent<List<ITree>> getDepartmentTree() {
+        List<Department> list = departmentDao.findAll(Sort.by(Sort.Order.asc("sort"),
+                Sort.Order.asc("createTime")));
+        List<ITree> models = list.stream().map(this::toModel).collect(Collectors.toList());
+        List<ITree> iTrees = TreeUtil.buildTree(models, ITree.ROOT_ID);
+        return ResultContent.buildSuccess(iTrees);
+    }
+
+    /**
+     * 加载当前部门的所有上级部门(包括自己)
+     *
+     * @param id
+     * @return
+     */
+    public List<DepartmentModel> getParents(String id) {
+        List<Department> list = new ArrayList<>();
+        loopLoad(id, list);
+        List<DepartmentModel> models = list.stream().map(this::toModel).collect(Collectors.toList());
+        if (ObjectUtils.isNotEmpty(models)) {
+            Collections.reverse(models);
+        }
+        return models;
+    }
+
+    private void loopLoad(String id, List<Department> list) {
+        Department entity = departmentDao.findTopById(id);
+        if (ObjectUtils.isNotEmpty(entity)) {
+            list.add(entity);
+            if (StringUtils.isNotEmpty(entity.getParentId()) && !entity.getParentId().equals(ITree.ROOT_ID)) {
+                loopLoad(entity.getParentId(), list);
+            }
+        }
+    }
+
+    public DepartmentModel toModel(Department entity) {
+        DepartmentModel model = new DepartmentModel();
+        if (ObjectUtils.isNotEmpty(entity)) {
+            BeanUtils.copyProperties(entity, model);
+        }
+        return model;
+    }
+}

+ 176 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/impl/RoleServiceImpl.java

@@ -0,0 +1,176 @@
+package com.zhongshu.iot.server.core.service.user.impl;
+
+import com.zhongshu.iot.client.model.user.RoleAddParam;
+import com.zhongshu.iot.client.model.user.RoleModel;
+import com.zhongshu.iot.client.model.user.RoleSearchParam;
+import com.zhongshu.iot.client.type.DataState;
+import com.zhongshu.iot.client.type.RoleType;
+import com.zhongshu.iot.server.core.dao.RoleDao;
+import com.zhongshu.iot.server.core.dataConfig.ResultMessage;
+import com.zhongshu.iot.server.core.domain.user.Role;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author TRX
+ * @date 2024/6/5
+ */
+@Slf4j
+@Service
+public class RoleServiceImpl extends SuperService {
+
+    @Autowired
+    RoleDao roleDao;
+
+    /**
+     * 添加/编辑角色
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent addRole(RoleAddParam param) {
+        Assert.hasText(param.getName(), "name不能为空");
+        Assert.hasText(param.getCode(), "code不能为空");
+        String oid = param.getOid();
+        if (StringUtils.isEmpty(oid)) {
+            oid = getCurrentOid();
+        }
+
+        param.setOid(oid);
+        if (param.getSort() == null) {
+            param.setSort(1L);
+        }
+        if (param.getState() == null) {
+            param.setState(DataState.Enable);
+        }
+        Role nameRole = roleDao.findTopByName(param.getName());
+        Role codeRole = roleDao.findTopByCode(param.getCode());
+        initDefaultUser(param);
+        if (StringUtils.isEmpty(param.getId())) {
+            param.setId(null);
+            // 添加
+            if (ObjectUtils.isNotEmpty(nameRole)) {
+                return ResultContent.buildFail(String.format("角色名称已存在:%s", param.getName()));
+            }
+            if (ObjectUtils.isNotEmpty(codeRole)) {
+                return ResultContent.buildFail(String.format("角色标识已存在:%s", param.getCode()));
+            }
+            Role role = new Role();
+            BeanUtils.copyProperties(param, role);
+            role.setRoleType(RoleType.Custom);
+            role.setCreateUserId(getCurrentUserId());
+            role.setIsAdmin(Boolean.FALSE);
+            roleDao.save(role);
+        } else {
+            // 编辑
+            Role role = roleDao.findTopById(param.getId());
+            if (ObjectUtils.isEmpty(role)) {
+                return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, param.getId()));
+            }
+            if (ObjectUtils.isNotEmpty(nameRole) && !nameRole.getId().equals(role.getId())) {
+                return ResultContent.buildFail(String.format("角色名称已存在:%s", param.getName()));
+            }
+            if (ObjectUtils.isNotEmpty(codeRole) && !codeRole.getId().equals(role.getId())) {
+                return ResultContent.buildFail(String.format("角色标识已存在:%s", param.getCode()));
+            }
+            // 编辑本地角色
+            BeanUtils.copyProperties(param, role);
+            roleDao.save(role);
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 删除角色
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent deleteRole(String id) {
+        Role role = roleDao.findTopById(id);
+        if (ObjectUtils.isEmpty(role)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        if (role.getIsAdmin() != null && role.getIsAdmin()) {
+            return ResultContent.buildFail(String.format("管理员角色不能删除"));
+        }
+        if (role.getRoleType() == RoleType.BuildIn) {
+            return ResultContent.buildFail(String.format("内置角色不能删除"));
+        }
+        // 删除本地角色
+        roleDao.delete(role);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 角色详情
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent<RoleModel> getRole(String id) {
+        Role role = roleDao.findTopById(id);
+        if (ObjectUtils.isEmpty(role)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        RoleModel roleModel = toModel(role);
+        return ResultContent.buildSuccess(roleModel);
+    }
+
+    /**
+     * 角色列表
+     *
+     * @param param
+     * @param pageable
+     * @return
+     */
+    public ResultContent<Page<RoleModel>> page(RoleSearchParam param, Pageable pageable) {
+        initSearchParam(param);
+        param.setIsSortDesc(Boolean.FALSE);
+        Page<Role> page = roleDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.concurrent2PageModel(page, this::toModel));
+    }
+
+    /**
+     * 得到机构所有的角色
+     *
+     * @param oid
+     * @return
+     */
+    public ResultContent<List<RoleModel>> getAllRoles(String oid) {
+        if (StringUtils.isEmpty(oid)) {
+            oid = getCurrentOid();
+        }
+        List<Role> list = roleDao.findAll(Sort.by(Sort.Order.asc("sort"),
+                Sort.Order.asc("createTime")));
+        List<RoleModel> models = new ArrayList<>();
+        if (ObjectUtils.isNotEmpty(list)) {
+            models = list.stream().map(this::toModel).collect(Collectors.toList());
+        }
+        return ResultContent.buildSuccess(models);
+    }
+
+    public RoleModel toModel(Role role) {
+        RoleModel roleModel = null;
+        if (ObjectUtils.isNotEmpty(role)) {
+            roleModel = new RoleModel();
+            BeanUtils.copyProperties(role, roleModel);
+        }
+        return roleModel;
+    }
+}

+ 247 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/impl/UserManagerServiceImpl.java

@@ -0,0 +1,247 @@
+package com.zhongshu.iot.server.core.service.user.impl;
+
+import com.zhongshu.iot.client.model.mqtt.ProjectInfoModel;
+import com.zhongshu.iot.client.model.user.*;
+import com.zhongshu.iot.client.type.ResultState;
+import com.zhongshu.iot.client.type.UserType;
+import com.zhongshu.iot.server.core.dao.DepartmentDao;
+import com.zhongshu.iot.server.core.dao.RoleDao;
+import com.zhongshu.iot.server.core.dao.UserDao;
+import com.zhongshu.iot.server.core.dao.mqtt.ProjectInfoDao;
+import com.zhongshu.iot.server.core.dataConfig.ResultMessage;
+import com.zhongshu.iot.server.core.domain.iot.mqtt.ProjectInfo;
+import com.zhongshu.iot.server.core.domain.user.Department;
+import com.zhongshu.iot.server.core.domain.user.Role;
+import com.zhongshu.iot.server.core.domain.user.User;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.service.mqtt.ProjectInfoService;
+import com.zhongshu.iot.server.core.util.ValidateResult;
+import com.zhongshu.iot.server.core.util.ValidateUtils;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 用户管理
+ *
+ * @author TRX
+ * @date 2024/6/20
+ */
+@Slf4j
+@Service
+public class UserManagerServiceImpl extends SuperService {
+
+    @Autowired
+    private UserDao userDao;
+
+    @Autowired
+    RoleDao roleDao;
+
+    @Autowired
+    ProjectInfoDao projectInfoDao;
+
+    @Autowired
+    DepartmentDao departmentDao;
+
+    @Autowired
+    DepartmentServiceImpl departmentService;
+
+    @Autowired
+    ProjectInfoService projectInfoService;
+
+    @Autowired
+    RoleServiceImpl roleService;
+
+    /**
+     * 添加编辑用户
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent addUser(UserCountParam param) {
+        initDefaultUser(param);
+        String loginName = param.getPhone();
+        boolean b = ValidateUtils.isPhoneNumber(param.getPhone());
+        if (!b) {
+            return ResultContent.buildFail("电话号码格式不正确");
+        }
+        User user = null;
+        User temp = userDao.findByLoginName(loginName);
+        if (StringUtils.isNotEmpty(param.getId())) {
+            // 编辑
+            user = userDao.findTopById(param.getId());
+            if (ObjectUtils.isEmpty(user)) {
+                return ResultContent.buildFail(String.format("用户ID不存在:%s", param.getId()));
+            }
+            if (ObjectUtils.isNotEmpty(temp) && !temp.getId().equals(user.getId())) {
+                return ResultContent.buildFail(String.format("该账号已存在:%s", loginName));
+            }
+            BeanUtils.copyProperties(param, user, "passWord", "loginName");
+        } else {
+            // 添加
+            if (!param.getNewPassWord().equals(param.getConfirmPass())) {
+                return ResultContent.buildFail("密码和验证密码不一致");
+            }
+            ValidateResult validateResult = ValidateUtils.validatePassWord(param.getNewPassWord());
+            if (!validateResult.isSuccess()) {
+                return ResultContent.buildFail(validateResult.getMsg());
+            }
+
+            if (ObjectUtils.isNotEmpty(temp)) {
+                return ResultContent.buildFail(String.format("该账号已存在:%s", loginName));
+            }
+            PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+            user = new User();
+            BeanUtils.copyProperties(param, user);
+            user.setLoginName(loginName);
+            user.setPassWord(passwordEncoder.encode(param.getNewPassWord()));
+            user.setUserType(UserType.Consumer);
+            user.setIsAdmin(Boolean.FALSE);
+        }
+
+        // 项目 角色 部门
+        List<ProjectInfo> projectInfos = new ArrayList<>();
+        List<String> projectInfoIds = param.getProjectInfos();
+        if (ObjectUtils.isNotEmpty(projectInfoIds)) {
+            projectInfos = projectInfoDao.findAllById(projectInfoIds);
+        }
+
+        // 角色
+        List<Role> roles = new ArrayList<>();
+        List<String> roleIds = param.getRoles();
+        if (ObjectUtils.isNotEmpty(roleIds)) {
+            roles = roleDao.findAllById(roleIds);
+        }
+
+        // 部门
+        Department department = null;
+        String departmentId = param.getDepartmentId();
+        if (StringUtils.isNotEmpty(departmentId)) {
+            department = departmentDao.findTopById(departmentId);
+        }
+        user.setProjectInfos(projectInfos);
+        user.setRoles(roles);
+        user.setDepartment(department);
+        userDao.save(user);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 用户列表
+     *
+     * @param param
+     * @param pageable
+     * @return
+     */
+    public ResultContent<Page<UserInfoModel>> page(UserSearchParams param, Pageable pageable) {
+        initSearchParam(param);
+        Page<User> page = userDao.page(pageable, param);
+        return ResultContent.buildSuccess(PageEntityUtil.concurrent2PageModel(page, this::toModel));
+    }
+
+    /**
+     * 用户详情
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent<UserInfoModel> getUserInfo(String id) {
+        User user = userDao.findTopById(id);
+        if (ObjectUtils.isEmpty(user)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        UserInfoModel model = toModel(user);
+        return ResultContent.buildSuccess(model);
+    }
+
+    /**
+     * 删除用户
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent delUser(String id) {
+        User user = userDao.findTopById(id);
+        if (ObjectUtils.isEmpty(user)) {
+            return ResultContent.buildFail(String.format(ResultMessage.DATA_NOT_EXIST, id));
+        }
+        if (user.getIsAdmin() != null && user.getIsAdmin()) {
+            return ResultContent.buildFail(String.format("该用户不能删除"));
+        }
+        if (user.getId().equals(getCurrentUserId())) {
+            return ResultContent.buildFail("不能删除当前用户");
+        }
+        userDao.delete(user);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 重置用户密码
+     *
+     * @param id
+     * @return
+     */
+    public ResultContent resetUserPassWord(String id) {
+        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        User user = userDao.findTopById(id);
+        if (ObjectUtils.isEmpty(user)) {
+            return ResultContent.buildFail(String.format(ResultState.UserNotExists.getRemark()));
+        }
+        user.setPassWord(passwordEncoder.encode("123abc"));
+        return ResultContent.buildSuccess();
+    }
+
+    public UserInfoModel toModel(User user) {
+        UserInfoModel model = new UserInfoModel();
+        if (ObjectUtils.isNotEmpty(user)) {
+            BeanUtils.copyProperties(user, model);
+
+            // 部门信息
+            Department department = user.getDepartment();
+            if (ObjectUtils.isNotEmpty(department)) {
+                DepartmentModel departmentModel = departmentService.toModel(department);
+                model.setDepartment(departmentModel);
+                model.setDepartments(departmentService.getParents(department.getId()));
+            }
+            // 角色信息
+            List<Role> roles = user.getRoles();
+            if (ObjectUtils.isNotEmpty(roles)) {
+                List<RoleModel> roleModels = new ArrayList<>();
+                roles.stream().forEach(it -> {
+                    RoleModel roleModel = roleService.toModel(it);
+                    if (roleModel != null) {
+                        roleModels.add(roleModel);
+                    }
+                });
+                model.setRoles(roleModels);
+            }
+
+            // 项目信息
+            List<ProjectInfo> projectInfos = user.getProjectInfos();
+            if (ObjectUtils.isNotEmpty(projectInfos)) {
+                List<ProjectInfoModel> projectInfoModels = new ArrayList<>();
+                projectInfos.stream().forEach(it -> {
+                    ProjectInfoModel projectInfoModel = projectInfoService.toModel(it);
+                    if (projectInfoModel != null) {
+                        projectInfoModels.add(projectInfoModel);
+                    }
+                });
+                model.setProjectInfos(projectInfoModels);
+            }
+        }
+        return model;
+    }
+
+}

+ 160 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/user/impl/UserServiceImpl.java

@@ -0,0 +1,160 @@
+package com.zhongshu.iot.server.core.service.user.impl;
+
+import com.zhongshu.iot.client.model.user.UpdateUserPassWordParam;
+import com.zhongshu.iot.client.model.user.UserInfoModel;
+import com.zhongshu.iot.client.model.user.UserUpdateParam;
+import com.zhongshu.iot.client.type.ResultState;
+import com.zhongshu.iot.client.type.UserState;
+import com.zhongshu.iot.client.type.UserType;
+import com.zhongshu.iot.server.core.dao.UserDao;
+import com.zhongshu.iot.server.core.domain.user.User;
+import com.zhongshu.iot.server.core.httpRequest.ApiRequestService;
+import com.zhongshu.iot.server.core.service.base.RedisService;
+import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.service.user.UserService;
+import com.zhongshu.iot.server.core.util.ValidateResult;
+import com.zhongshu.iot.server.core.util.ValidateUtils;
+import com.zhongshu.iot.server.core.util.bean.BeanUtils;
+import com.zhongshu.iot.server.core.util.mqtt.mqttConfig.client.MQClient;
+import com.zhongshu.iot.server.core.util.result.ResultContent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author TRX
+ * @date 2024/3/20
+ */
+@Slf4j
+@Service
+public class UserServiceImpl extends SuperService implements UserService {
+    @Autowired
+    UserDao userDao;
+
+    @Autowired
+    RedisService redisService;
+
+    @Autowired
+    MQClient mqClient;
+
+    @Autowired
+    UserManagerServiceImpl userManagerService;
+
+    @Autowired
+    ApiRequestService apiRequestService;
+
+    /**
+     * 初始超级管理员
+     *
+     * @return
+     */
+    public ResultContent initAdmin() {
+        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        String loginName = "admin";
+        User user = userDao.findByLoginName(loginName);
+        if (ObjectUtils.isEmpty(user)) {
+            user = new User();
+            String defaultPassWord = "123abc";
+            user.setLoginName(loginName);
+            user.setUserName("超级管理员");
+            user.setPassWord(passwordEncoder.encode(defaultPassWord));
+            user.setUserType(UserType.SuperAdmin);
+            user.setIsAdmin(Boolean.TRUE);
+            user.setUserState(UserState.Enable);
+            user.setPhone("");
+            user.setHeadUrl("");
+            userDao.save(user);
+            log.info("初始 admin 用户成功");
+        } else {
+            log.info("admin 用户已存在");
+        }
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 编辑当前用户密码
+     *
+     * @param param
+     * @return
+     */
+    public ResultContent updateUserPassWord(UpdateUserPassWordParam param) {
+        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        String userId = getCurrentUserId();
+        User user = userDao.findTopById(userId);
+        if (ObjectUtils.isEmpty(user)) {
+            return ResultContent.buildFail("当前用户信息为空");
+        }
+        if (!param.getConfirmPass().equals(param.getPassWord())) {
+            return ResultContent.buildFail("密码和验证密码不一致");
+        }
+        if (param.getOldPass().equals(param.getPassWord())) {
+            return ResultContent.buildFail("原密码和密码不能一致");
+        }
+        ValidateResult validateResult = ValidateUtils.validatePassWord(param.getPassWord());
+        if (!validateResult.isSuccess()) {
+            return ResultContent.buildFail(validateResult.getMsg());
+        }
+        ResultContent resultContent = checkLoginPassword(userId, param.getOldPass());
+        if (resultContent.isFailed()) {
+            return resultContent;
+        }
+        user.setPassWord(passwordEncoder.encode(param.getPassWord()));
+        userDao.save(user);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 得到当前用户信息
+     *
+     * @return
+     */
+    public ResultContent<UserInfoModel> getCurrentUserInfo() {
+        UserInfoModel model = new UserInfoModel();
+        User user = userDao.findTopById(getCurrentUserId());
+        if (ObjectUtils.isEmpty(user)) {
+            return ResultContent.buildFail("当前用户信息为空");
+        }
+        model = userManagerService.toModel(user);
+        return ResultContent.buildSuccess(model);
+    }
+
+    public ResultContent updateUserInfo(UserUpdateParam param) {
+        User user = userDao.findTopById(getCurrentUserId());
+        if (ObjectUtils.isEmpty(user)) {
+            return ResultContent.buildFail(ResultState.UserNotExists.getRemark());
+        }
+        BeanUtils.copyProperties(param, user);
+        userDao.save(user);
+        return ResultContent.buildSuccess();
+    }
+
+    public ResultContent updateUserHead(String url) {
+        User user = userDao.findTopById(getCurrentUserId());
+        if (ObjectUtils.isEmpty(user)) {
+            return ResultContent.buildFail(ResultState.UserNotExists.getRemark());
+        }
+        user.setHeadUrl(url);
+        userDao.save(user);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 验证用户密码是否正确
+     *
+     * @param uid
+     * @param passWord
+     * @return
+     */
+    public ResultContent checkLoginPassword(String uid, String passWord) {
+        User user = userDao.findTopById(uid);
+        if (user == null) {
+            return ResultContent.buildContent(ResultState.UserNotExists);
+        }
+        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        return ResultContent.build(passwordEncoder.matches(passWord, user.getPassWord()) ? ResultState.Success : ResultState.UserPasswordError);
+    }
+
+}

+ 1 - 1
OneCardIotServer/src/main/resources/application.yml

@@ -1,4 +1,4 @@
-#Web服务器端口
+#Web服务器端口 9500  测试 9503
 server:
   port: 9500