Commit c5894765b50f6ed7fac4bfa8a558bfd5d3b8a3e0

Authored by puhui999
2 parents 741096e2 92c2717d

Merge remote-tracking branch 'yudao/feature/iot' into iot

Showing 58 changed files with 2504 additions and 236 deletions
yudao-dependencies/pom.xml
... ... @@ -66,6 +66,7 @@
66 66 <ip2region.version>2.7.0</ip2region.version>
67 67 <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
68 68 <mqtt.version>1.2.5</mqtt.version>
  69 + <pf4j-spring.version>0.9.0</pf4j-spring.version>
69 70 <!-- 三方云服务相关 -->
70 71 <okio.version>3.5.0</okio.version>
71 72 <okhttp3.version>4.11.0</okhttp3.version>
... ... @@ -605,6 +606,13 @@
605 606 <version>${mqtt.version}</version>
606 607 </dependency>
607 608  
  609 + <!-- PF4J -->
  610 + <dependency>
  611 + <groupId>org.pf4j</groupId>
  612 + <artifactId>pf4j-spring</artifactId>
  613 + <version>${pf4j-spring.version}</version>
  614 + </dependency>
  615 +
608 616 </dependencies>
609 617 </dependencyManagement>
610 618  
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongSetTypeHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mybatis.core.type;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.util.string.StrUtils;
  5 +import org.apache.ibatis.type.JdbcType;
  6 +import org.apache.ibatis.type.MappedJdbcTypes;
  7 +import org.apache.ibatis.type.MappedTypes;
  8 +import org.apache.ibatis.type.TypeHandler;
  9 +
  10 +import java.sql.CallableStatement;
  11 +import java.sql.PreparedStatement;
  12 +import java.sql.ResultSet;
  13 +import java.sql.SQLException;
  14 +import java.util.List;
  15 +import java.util.Set;
  16 +
  17 +/**
  18 + * Set<Long> 的类型转换器实现类,对应数据库的 varchar 类型
  19 + *
  20 + * @author 芋道源码
  21 + */
  22 +@MappedJdbcTypes(JdbcType.VARCHAR)
  23 +@MappedTypes(List.class)
  24 +public class LongSetTypeHandler implements TypeHandler<Set<Long>> {
  25 +
  26 + private static final String COMMA = ",";
  27 +
  28 + @Override
  29 + public void setParameter(PreparedStatement ps, int i, Set<Long> strings, JdbcType jdbcType) throws SQLException {
  30 + // 设置占位符
  31 + ps.setString(i, CollUtil.join(strings, COMMA));
  32 + }
  33 +
  34 + @Override
  35 + public Set<Long> getResult(ResultSet rs, String columnName) throws SQLException {
  36 + String value = rs.getString(columnName);
  37 + return getResult(value);
  38 + }
  39 +
  40 + @Override
  41 + public Set<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {
  42 + String value = rs.getString(columnIndex);
  43 + return getResult(value);
  44 + }
  45 +
  46 + @Override
  47 + public Set<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {
  48 + String value = cs.getString(columnIndex);
  49 + return getResult(value);
  50 + }
  51 +
  52 + private Set<Long> getResult(String value) {
  53 + if (value == null) {
  54 + return null;
  55 + }
  56 + return StrUtils.splitToLongSet(value, COMMA);
  57 + }
  58 +}
... ...
yudao-module-iot/pom.xml
... ... @@ -10,6 +10,7 @@
10 10 <modules>
11 11 <module>yudao-module-iot-api</module>
12 12 <module>yudao-module-iot-biz</module>
  13 + <module>yudao-module-iot-plugin</module>
13 14 </modules>
14 15 <modelVersion>4.0.0</modelVersion>
15 16  
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java
... ... @@ -13,4 +13,7 @@ public class DictTypeConstants {
13 13 public static final String PROTOCOL_TYPE = "iot_protocol_type";
14 14 public static final String DATA_FORMAT = "iot_data_format";
15 15 public static final String VALIDATE_TYPE = "iot_validate_type";
  16 +
  17 + public static final String DEVICE_STATUS = "iot_device_status";
  18 +
16 19 }
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
... ... @@ -26,11 +26,26 @@ public interface ErrorCodeConstants {
26 26 ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在");
27 27 ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, "设备名称在同一产品下必须唯一");
28 28 ErrorCode DEVICE_HAS_CHILDREN = new ErrorCode(1_050_003_002, "有子设备,不允许删除");
29   - ErrorCode DEVICE_NAME_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_003, "设备名称不能修改");
30   - ErrorCode DEVICE_PRODUCT_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_004, "产品不能修改");
31   - ErrorCode DEVICE_INVALID_DEVICE_STATUS = new ErrorCode(1_050_003_005, "无效的设备状态");
  29 + ErrorCode DEVICE_KEY_EXISTS = new ErrorCode(1_050_003_003, "设备标识已经存在");
  30 + ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在");
  31 + ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备");
  32 + ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!");
32 33  
33 34 // ========== 产品分类 1-050-004-000 ==========
34 35 ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在");
35 36  
36   -}
  37 + // ========== 设备分组 1-050-005-000 ==========
  38 + ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在");
  39 + ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除");
  40 +
  41 + // ========== 插件信息 1-050-006-000 ==========
  42 + ErrorCode PLUGIN_INFO_NOT_EXISTS = new ErrorCode(1_050_006_000, "插件信息不存在");
  43 + ErrorCode PLUGIN_INSTALL_FAILED = new ErrorCode(1_050_006_001, "插件安装失败");
  44 + ErrorCode PLUGIN_INSTALL_FAILED_FILE_NAME_NOT_MATCH = new ErrorCode(1_050_006_002, "插件安装失败,文件名与原插件id不匹配");
  45 + ErrorCode PLUGIN_INFO_DELETE_FAILED_RUNNING = new ErrorCode(1_050_006_003, "请先停止插件");
  46 + ErrorCode PLUGIN_STATUS_INVALID = new ErrorCode(1_050_006_004, "插件状态无效");
  47 +
  48 + // ========== 插件实例 1-050-007-000 ==========
  49 + ErrorCode PLUGIN_INSTANCE_NOT_EXISTS = new ErrorCode(1_050_007_000, "插件实例不存在");
  50 +
  51 +}
37 52 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.plugin;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.Getter;
  5 +
  6 +import java.util.Arrays;
  7 +
  8 +/**
  9 + * IoT 部署方式枚举
  10 + *
  11 + * @author haohao
  12 + */
  13 +@Getter
  14 +public enum IotPluginDeployTypeEnum implements IntArrayValuable {
  15 +
  16 + UPLOAD(0, "上传 jar"), // TODO @haohao:UPLOAD 和 ALONE 感觉有点冲突,前者是部署方式,后者是运行方式。这个后续再讨论下哈
  17 + ALONE(1, "独立运行");
  18 +
  19 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginDeployTypeEnum::getDeployType).toArray();
  20 +
  21 + /**
  22 + * 部署方式
  23 + */
  24 + private final Integer deployType;
  25 +
  26 + /**
  27 + * 部署方式名
  28 + */
  29 + private final String name;
  30 +
  31 + IotPluginDeployTypeEnum(Integer deployType, String name) {
  32 + this.deployType = deployType;
  33 + this.name = name;
  34 + }
  35 +
  36 + public static IotPluginDeployTypeEnum fromDeployType(Integer deployType) {
  37 + for (IotPluginDeployTypeEnum value : values()) {
  38 + if (value.getDeployType().equals(deployType)) {
  39 + return value;
  40 + }
  41 + }
  42 + return null;
  43 + }
  44 +
  45 + public static boolean isValidDeployType(Integer deployType) {
  46 + return fromDeployType(deployType) != null;
  47 + }
  48 +
  49 + @Override
  50 + public int[] array() {
  51 + return ARRAYS;
  52 + }
  53 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.plugin;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.Getter;
  5 +
  6 +import java.util.Arrays;
  7 +
  8 +/**
  9 + * IoT 插件状态枚举
  10 + *
  11 + * @author haohao
  12 + */
  13 +@Getter
  14 +public enum IotPluginStatusEnum implements IntArrayValuable {
  15 +
  16 + STOPPED(0, "停止"),
  17 + RUNNING(1, "运行");
  18 +
  19 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginStatusEnum::getStatus).toArray();
  20 +
  21 + /**
  22 + * 状态
  23 + */
  24 + private final Integer status;
  25 +
  26 + /**
  27 + * 状态名
  28 + */
  29 + private final String name;
  30 +
  31 + IotPluginStatusEnum(Integer status, String name) {
  32 + this.status = status;
  33 + this.name = name;
  34 + }
  35 +
  36 + public static IotPluginStatusEnum fromState(Integer state) {
  37 + for (IotPluginStatusEnum value : values()) {
  38 + if (value.getStatus().equals(state)) {
  39 + return value;
  40 + }
  41 + }
  42 + return null;
  43 + }
  44 +
  45 + @Override
  46 + public int[] array() {
  47 + return ARRAYS;
  48 + }
  49 +
  50 + public static boolean isValidState(Integer state) {
  51 + return fromState(state) != null;
  52 + }
  53 +
  54 + public static boolean contains(Integer status) {
  55 + return Arrays.stream(values()).anyMatch(e -> e.getStatus().equals(status));
  56 + }
  57 +
  58 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.plugin;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * IoT 插件类型枚举
  11 + *
  12 + * @author haohao
  13 + */
  14 +@AllArgsConstructor
  15 +@Getter
  16 +public enum IotPluginTypeEnum implements IntArrayValuable {
  17 +
  18 + NORMAL(0, "普通插件"),
  19 + DEVICE(1, "设备插件");
  20 +
  21 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginTypeEnum::getType).toArray();
  22 +
  23 + /**
  24 + * 类型
  25 + */
  26 + private final Integer type;
  27 +
  28 + /**
  29 + * 类型名
  30 + */
  31 + private final String name;
  32 +
  33 + @Override
  34 + public int[] array() {
  35 + return ARRAYS;
  36 + }
  37 +
  38 + // TODO @haohao:可以使用 hutool 简化
  39 + public static IotPluginTypeEnum fromType(Integer type) {
  40 + for (IotPluginTypeEnum value : values()) {
  41 + if (value.getType().equals(type)) {
  42 + return value;
  43 + }
  44 + }
  45 + return null;
  46 + }
  47 +
  48 + public static boolean isValidType(Integer type) {
  49 + return fromType(type) != null;
  50 + }
  51 +
  52 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java
... ... @@ -36,4 +36,14 @@ public enum IotProductDeviceTypeEnum implements IntArrayValuable {
36 36 return ARRAYS;
37 37 }
38 38  
  39 + /**
  40 + * 判断是否是网关
  41 + *
  42 + * @param type 类型
  43 + * @return 是否是网关
  44 + */
  45 + public static boolean isGateway(Integer type) {
  46 + return GATEWAY.getType().equals(type);
  47 + }
  48 +
39 49 }
... ...
yudao-module-iot/yudao-module-iot-biz/pom.xml
... ... @@ -69,6 +69,12 @@
69 69 <groupId>org.eclipse.paho</groupId>
70 70 <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
71 71 </dependency>
  72 +
  73 + <!-- PF4J -->
  74 + <dependency>
  75 + <groupId>org.pf4j</groupId>
  76 + <artifactId>pf4j-spring</artifactId>
  77 + </dependency>
72 78 </dependencies>
73 79  
74 80 </project>
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
1 1 package cn.iocoder.yudao.module.iot.controller.admin.device;
2 2  
  3 +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
3 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
7   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceRespVO;
8   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
9   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
  8 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
10 10 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
11 11 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
12 12 import io.swagger.v3.oas.annotations.Operation;
13 13 import io.swagger.v3.oas.annotations.Parameter;
14 14 import io.swagger.v3.oas.annotations.tags.Tag;
15 15 import jakarta.annotation.Resource;
  16 +import jakarta.servlet.http.HttpServletResponse;
16 17 import jakarta.validation.Valid;
17 18 import org.springframework.security.access.prepost.PreAuthorize;
18 19 import org.springframework.validation.annotation.Validated;
19 20 import org.springframework.web.bind.annotation.*;
  21 +import org.springframework.web.multipart.MultipartFile;
20 22  
  23 +import java.io.IOException;
  24 +import java.util.Arrays;
  25 +import java.util.Collection;
  26 +import java.util.List;
  27 +
  28 +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
21 29 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  30 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
22 31  
23 32 @Tag(name = "管理后台 - IoT 设备")
24 33 @RestController
... ... @@ -52,8 +61,16 @@ public class IotDeviceController {
52 61 return success(true);
53 62 }
54 63  
  64 + @PutMapping("/update-group")
  65 + @Operation(summary = "更新设备分组")
  66 + @PreAuthorize("@ss.hasPermission('iot:device:update')")
  67 + public CommonResult<Boolean> updateDeviceGroup(@Valid @RequestBody IotDeviceUpdateGroupReqVO updateReqVO) {
  68 + deviceService.updateDeviceGroup(updateReqVO);
  69 + return success(true);
  70 + }
  71 +
55 72 @DeleteMapping("/delete")
56   - @Operation(summary = "删除设备")
  73 + @Operation(summary = "删除单个设备")
57 74 @Parameter(name = "id", description = "编号", required = true)
58 75 @PreAuthorize("@ss.hasPermission('iot:device:delete')")
59 76 public CommonResult<Boolean> deleteDevice(@RequestParam("id") Long id) {
... ... @@ -61,6 +78,15 @@ public class IotDeviceController {
61 78 return success(true);
62 79 }
63 80  
  81 + @DeleteMapping("/delete-list")
  82 + @Operation(summary = "删除多个设备")
  83 + @Parameter(name = "ids", description = "编号数组", required = true)
  84 + @PreAuthorize("@ss.hasPermission('iot:device:delete')")
  85 + public CommonResult<Boolean> deleteDeviceList(@RequestParam("ids") Collection<Long> ids) {
  86 + deviceService.deleteDeviceList(ids);
  87 + return success(true);
  88 + }
  89 +
64 90 @GetMapping("/get")
65 91 @Operation(summary = "获得设备")
66 92 @Parameter(name = "id", description = "编号", required = true, example = "1024")
... ... @@ -78,6 +104,19 @@ public class IotDeviceController {
78 104 return success(BeanUtils.toBean(pageResult, IotDeviceRespVO.class));
79 105 }
80 106  
  107 + @GetMapping("/export-excel")
  108 + @Operation(summary = "导出设备 Excel")
  109 + @PreAuthorize("@ss.hasPermission('iot:device:export')")
  110 + @ApiAccessLog(operateType = EXPORT)
  111 + public void exportDeviceExcel(@Valid IotDevicePageReqVO exportReqVO,
  112 + HttpServletResponse response) throws IOException {
  113 + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  114 + CommonResult<PageResult<IotDeviceRespVO>> result = getDevicePage(exportReqVO);
  115 + // 导出 Excel
  116 + ExcelUtils.write(response, "设备.xls", "数据", IotDeviceRespVO.class,
  117 + result.getData().getList());
  118 + }
  119 +
81 120 @GetMapping("/count")
82 121 @Operation(summary = "获得设备数量")
83 122 @Parameter(name = "productId", description = "产品编号", example = "1")
... ... @@ -86,4 +125,38 @@ public class IotDeviceController {
86 125 return success(deviceService.getDeviceCountByProductId(productId));
87 126 }
88 127  
  128 + @GetMapping("/simple-list")
  129 + @Operation(summary = "获取设备的精简信息列表", description = "主要用于前端的下拉选项")
  130 + @Parameter(name = "deviceType", description = "设备类型", example = "1")
  131 + public CommonResult<List<IotDeviceRespVO>> getSimpleDeviceList(
  132 + @RequestParam(value = "deviceType", required = false) Integer deviceType) {
  133 + List<IotDeviceDO> list = deviceService.getDeviceList(deviceType);
  134 + return success(convertList(list, device -> // 只返回 id、name 字段
  135 + new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName())));
  136 + }
  137 +
  138 + @PostMapping("/import")
  139 + @Operation(summary = "导入设备")
  140 + @PreAuthorize("@ss.hasPermission('iot:device:import')")
  141 + public CommonResult<IotDeviceImportRespVO> importDevice(
  142 + @RequestParam("file") MultipartFile file,
  143 + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport)
  144 + throws Exception {
  145 + List<IotDeviceImportExcelVO> list = ExcelUtils.read(file, IotDeviceImportExcelVO.class);
  146 + return success(deviceService.importDevice(list, updateSupport));
  147 + }
  148 +
  149 + @GetMapping("/get-import-template")
  150 + @Operation(summary = "获得导入设备模板")
  151 + public void importTemplate(HttpServletResponse response) throws IOException {
  152 + // 手动创建导出 demo
  153 + List<IotDeviceImportExcelVO> list = Arrays.asList(
  154 + IotDeviceImportExcelVO.builder().deviceName("温度传感器001").parentDeviceName("gateway110")
  155 + .productKey("1de24640dfe").groupNames("灰度分组,生产分组").build(),
  156 + IotDeviceImportExcelVO.builder().deviceName("biubiu")
  157 + .productKey("YzvHxd4r67sT4s2B").groupNames("").build());
  158 + // 输出
  159 + ExcelUtils.write(response, "设备导入模板.xls", "数据", IotDeviceImportExcelVO.class, list);
  160 + }
  161 +
89 162 }
90 163 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceGroupController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.device;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupRespVO;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;
  10 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
  11 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceGroupService;
  12 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  13 +import io.swagger.v3.oas.annotations.Operation;
  14 +import io.swagger.v3.oas.annotations.Parameter;
  15 +import io.swagger.v3.oas.annotations.tags.Tag;
  16 +import jakarta.annotation.Resource;
  17 +import jakarta.validation.Valid;
  18 +import org.springframework.security.access.prepost.PreAuthorize;
  19 +import org.springframework.validation.annotation.Validated;
  20 +import org.springframework.web.bind.annotation.*;
  21 +
  22 +import java.util.List;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  25 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  26 +
  27 +@Tag(name = "管理后台 - IoT 设备分组")
  28 +@RestController
  29 +@RequestMapping("/iot/device-group")
  30 +@Validated
  31 +public class IotDeviceGroupController {
  32 +
  33 + @Resource
  34 + private IotDeviceGroupService deviceGroupService;
  35 + @Resource
  36 + private IotDeviceService deviceService;
  37 +
  38 + @PostMapping("/create")
  39 + @Operation(summary = "创建设备分组")
  40 + @PreAuthorize("@ss.hasPermission('iot:device-group:create')")
  41 + public CommonResult<Long> createDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO createReqVO) {
  42 + return success(deviceGroupService.createDeviceGroup(createReqVO));
  43 + }
  44 +
  45 + @PutMapping("/update")
  46 + @Operation(summary = "更新设备分组")
  47 + @PreAuthorize("@ss.hasPermission('iot:device-group:update')")
  48 + public CommonResult<Boolean> updateDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO updateReqVO) {
  49 + deviceGroupService.updateDeviceGroup(updateReqVO);
  50 + return success(true);
  51 + }
  52 +
  53 + @DeleteMapping("/delete")
  54 + @Operation(summary = "删除设备分组")
  55 + @Parameter(name = "id", description = "编号", required = true)
  56 + @PreAuthorize("@ss.hasPermission('iot:device-group:delete')")
  57 + public CommonResult<Boolean> deleteDeviceGroup(@RequestParam("id") Long id) {
  58 + deviceGroupService.deleteDeviceGroup(id);
  59 + return success(true);
  60 + }
  61 +
  62 + @GetMapping("/get")
  63 + @Operation(summary = "获得设备分组")
  64 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  65 + @PreAuthorize("@ss.hasPermission('iot:device-group:query')")
  66 + public CommonResult<IotDeviceGroupRespVO> getDeviceGroup(@RequestParam("id") Long id) {
  67 + IotDeviceGroupDO deviceGroup = deviceGroupService.getDeviceGroup(id);
  68 + return success(BeanUtils.toBean(deviceGroup, IotDeviceGroupRespVO.class));
  69 + }
  70 +
  71 + @GetMapping("/page")
  72 + @Operation(summary = "获得设备分组分页")
  73 + @PreAuthorize("@ss.hasPermission('iot:device-group:query')")
  74 + public CommonResult<PageResult<IotDeviceGroupRespVO>> getDeviceGroupPage(@Valid IotDeviceGroupPageReqVO pageReqVO) {
  75 + PageResult<IotDeviceGroupDO> pageResult = deviceGroupService.getDeviceGroupPage(pageReqVO);
  76 + return success(BeanUtils.toBean(pageResult, IotDeviceGroupRespVO.class,
  77 + group -> group.setDeviceCount(deviceService.getDeviceCountByGroupId(group.getId()))));
  78 + }
  79 +
  80 + @GetMapping("/simple-list")
  81 + @Operation(summary = "获取设备分组的精简信息列表", description = "只包含被开启的分组,主要用于前端的下拉选项")
  82 + public CommonResult<List<IotDeviceGroupRespVO>> getSimpleDeviceGroupList() {
  83 + List<IotDeviceGroupDO> list = deviceGroupService.getDeviceGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());
  84 + return success(convertList(list, group -> // 只返回 id、name 字段
  85 + new IotDeviceGroupRespVO().setId(group.getId()).setName(group.getName())));
  86 + }
  87 +
  88 +}
0 89 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportExcelVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
  2 +
  3 +import com.alibaba.excel.annotation.ExcelProperty;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotEmpty;
  6 +import lombok.AllArgsConstructor;
  7 +import lombok.Builder;
  8 +import lombok.Data;
  9 +import lombok.NoArgsConstructor;
  10 +import lombok.experimental.Accessors;
  11 +
  12 +/**
  13 + * 设备 Excel 导入 VO
  14 + */
  15 +@Data
  16 +@Builder
  17 +@AllArgsConstructor
  18 +@NoArgsConstructor
  19 +@Accessors(chain = false) // 设置 chain = false,避免设备导入有问题
  20 +public class IotDeviceImportExcelVO {
  21 +
  22 + @ExcelProperty("设备名称")
  23 + @NotEmpty(message = "设备名称不能为空")
  24 + private String deviceName;
  25 +
  26 + @ExcelProperty("父设备名称")
  27 + @Schema(description = "父设备名称", example = "网关001")
  28 + private String parentDeviceName;
  29 +
  30 + @ExcelProperty("产品标识")
  31 + @NotEmpty(message = "产品标识不能为空")
  32 + private String productKey;
  33 +
  34 + @ExcelProperty("设备分组")
  35 + private String groupNames;
  36 +
  37 +}
0 38 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Builder;
  5 +import lombok.Data;
  6 +
  7 +import java.util.List;
  8 +import java.util.Map;
  9 +
  10 +@Schema(description = "管理后台 - IoT 设备导入 Response VO")
  11 +@Data
  12 +@Builder
  13 +public class IotDeviceImportRespVO {
  14 +
  15 + @Schema(description = "创建成功的设备名称数组", requiredMode = Schema.RequiredMode.REQUIRED)
  16 + private List<String> createDeviceNames;
  17 +
  18 + @Schema(description = "更新成功的设备名称数组", requiredMode = Schema.RequiredMode.REQUIRED)
  19 + private List<String> updateDeviceNames;
  20 +
  21 + @Schema(description = "导入失败的设备集合,key为设备名称,value为失败原因", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private Map<String, String> failureDeviceNames;
  23 +}
0 24 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java
... ... @@ -8,11 +8,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
8 8 import lombok.Data;
9 9 import lombok.EqualsAndHashCode;
10 10 import lombok.ToString;
11   -import org.springframework.format.annotation.DateTimeFormat;
12   -
13   -import java.time.LocalDateTime;
14   -
15   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
16 11  
17 12 @Schema(description = "管理后台 - IoT 设备分页 Request VO")
18 13 @Data
... ... @@ -20,11 +15,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
20 15 @ToString(callSuper = true)
21 16 public class IotDevicePageReqVO extends PageParam {
22 17  
23   - // TODO @芋艿:需要去掉一些多余的字段;
24   -
25   - @Schema(description = "设备唯一标识符", example = "24602")
26   - private String deviceKey;
27   -
28 18 @Schema(description = "设备名称", example = "王五")
29 19 private String deviceName;
30 20  
... ... @@ -34,53 +24,15 @@ public class IotDevicePageReqVO extends PageParam {
34 24 @Schema(description = "产品编号", example = "26202")
35 25 private Long productId;
36 26  
37   - @Schema(description = "产品标识")
38   - private String productKey;
39   -
40 27 @Schema(description = "设备类型", example = "1")
41 28 @InEnum(IotProductDeviceTypeEnum.class)
42 29 private Integer deviceType;
43 30  
44   - @Schema(description = "网关设备 ID", example = "16380")
45   - private Long gatewayId;
46   -
47 31 @Schema(description = "设备状态", example = "1")
48 32 @InEnum(IotDeviceStatusEnum.class)
49 33 private Integer status;
50 34  
51   - @Schema(description = "设备状态最后更新时间")
52   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
53   - private LocalDateTime[] statusLastUpdateTime;
54   -
55   - @Schema(description = "最后上线时间")
56   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
57   - private LocalDateTime[] lastOnlineTime;
58   -
59   - @Schema(description = "最后离线时间")
60   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
61   - private LocalDateTime[] lastOfflineTime;
62   -
63   - @Schema(description = "设备激活时间")
64   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
65   - private LocalDateTime[] activeTime;
66   -
67   - @Schema(description = "设备密钥,用于设备认证,需安全存储")
68   - private String deviceSecret;
69   -
70   - @Schema(description = "MQTT 客户端 ID", example = "24602")
71   - private String mqttClientId;
72   -
73   - @Schema(description = "MQTT 用户名", example = "芋艿")
74   - private String mqttUsername;
75   -
76   - @Schema(description = "MQTT 密码")
77   - private String mqttPassword;
78   -
79   - @Schema(description = "认证类型(如一机一密、动态注册)", example = "2")
80   - private String authType;
81   -
82   - @Schema(description = "创建时间")
83   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
84   - private LocalDateTime[] createTime;
  35 + @Schema(description = "设备分组编号", example = "1024")
  36 + private Long groupId;
85 37  
86 38 }
87 39 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java
1 1 package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
2 2  
  3 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
3 5 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
4 6 import com.alibaba.excel.annotation.ExcelProperty;
5 7 import io.swagger.v3.oas.annotations.media.Schema;
6 8 import lombok.Data;
7 9  
8 10 import java.time.LocalDateTime;
  11 +import java.util.Set;
  12 +
  13 +import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.DEVICE_STATUS;
9 14  
10 15 @Schema(description = "管理后台 - IoT 设备 Response VO")
11 16 @Data
... ... @@ -20,9 +25,24 @@ public class IotDeviceRespVO {
20 25 private String deviceKey;
21 26  
22 27 @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
23   - @ExcelProperty("设备名称备")
  28 + @ExcelProperty("设备名称")
24 29 private String deviceName;
25 30  
  31 + @Schema(description = "设备备注名称", example = "张三")
  32 + @ExcelProperty("设备备注名称")
  33 + private String nickname;
  34 +
  35 + @Schema(description = "设备序列号", example = "1024")
  36 + @ExcelProperty("设备序列号")
  37 + private String serialNumber;
  38 +
  39 + @Schema(description = "设备图片", example = "我是一名码农")
  40 + @ExcelProperty("设备图片")
  41 + private String picUrl;
  42 +
  43 + @Schema(description = "设备分组编号数组", example = "1,2")
  44 + private Set<Long> groupIds;
  45 +
26 46 @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202")
27 47 @ExcelProperty("产品编号")
28 48 private Long productId;
... ... @@ -35,15 +55,12 @@ public class IotDeviceRespVO {
35 55 @ExcelProperty("设备类型")
36 56 private Integer deviceType;
37 57  
38   - @Schema(description = "设备备注名称", example = "张三")
39   - @ExcelProperty("设备备注名称")
40   - private String nickname;
41   -
42 58 @Schema(description = "网关设备 ID", example = "16380")
43 59 private Long gatewayId;
44 60  
45 61 @Schema(description = "设备状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
46   - @ExcelProperty("设备状态")
  62 + @ExcelProperty(value = "设备状态", converter = DictConvert.class)
  63 + @DictFormat(DEVICE_STATUS)
47 64 private Integer status;
48 65  
49 66 @Schema(description = "设备状态最后更新时间")
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java
... ... @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
  6 +import java.util.Set;
  7 +
6 8 @Schema(description = "管理后台 - IoT 设备新增/修改 Request VO")
7 9 @Data
8 10 public class IotDeviceSaveReqVO {
... ... @@ -10,13 +12,28 @@ public class IotDeviceSaveReqVO {
10 12 @Schema(description = "设备编号", example = "177")
11 13 private Long id;
12 14  
  15 + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.AUTO, example = "177")
  16 + private String deviceKey;
  17 +
13 18 @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
14 19 private String deviceName;
15 20  
16 21 @Schema(description = "备注名称", example = "张三")
17 22 private String nickname;
18 23  
  24 + @Schema(description = "设备序列号", example = "123456")
  25 + private String serialNumber;
  26 +
  27 + @Schema(description = "设备图片", example = "https://iocoder.cn/1.png")
  28 + private String picUrl;
  29 +
  30 + @Schema(description = "设备分组编号数组", example = "1,2")
  31 + private Set<Long> groupIds;
  32 +
19 33 @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202")
20 34 private Long productId;
21 35  
  36 + @Schema(description = "网关设备 ID", example = "16380")
  37 + private Long gatewayId;
  38 +
22 39 }
23 40 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUpdateGroupReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import lombok.Data;
  6 +
  7 +import java.util.Set;
  8 +
  9 +@Schema(description = "管理后台 - IoT 设备更新分组 Request VO")
  10 +@Data
  11 +public class IotDeviceUpdateGroupReqVO {
  12 +
  13 + @Schema(description = "设备编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
  14 + @NotEmpty(message = "设备编号列表不能为空")
  15 + private Set<Long> ids;
  16 +
  17 + @Schema(description = "分组编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
  18 + @NotEmpty(message = "分组编号列表不能为空")
  19 + private Set<Long> groupIds;
  20 +
  21 +}
0 22 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - IoT 设备分组分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class IotDeviceGroupPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "分组名字", example = "李四")
  21 + private String name;
  22 +
  23 + @Schema(description = "创建时间")
  24 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  25 + private LocalDateTime[] createTime;
  26 +
  27 +}
0 28 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - IoT 设备分组 Response VO")
  9 +@Data
  10 +public class IotDeviceGroupRespVO {
  11 +
  12 + @Schema(description = "分组 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3583")
  13 + private Long id;
  14 +
  15 + @Schema(description = "分组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  16 + private String name;
  17 +
  18 + @Schema(description = "分组状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  19 + private Integer status;
  20 +
  21 + @Schema(description = "分组描述", example = "你说的对")
  22 + private String description;
  23 +
  24 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  25 + private LocalDateTime createTime;
  26 +
  27 + @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  28 + private Long deviceCount;
  29 +
  30 +}
0 31 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - IoT 设备分组新增/修改 Request VO")
  9 +@Data
  10 +public class IotDeviceGroupSaveReqVO {
  11 +
  12 + @Schema(description = "分组 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3583")
  13 + private Long id;
  14 +
  15 + @Schema(description = "分组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  16 + @NotEmpty(message = "分组名字不能为空")
  17 + private String name;
  18 +
  19 + @Schema(description = "分组状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  20 + @NotNull(message = "分组状态不能为空")
  21 + private Integer status;
  22 +
  23 + @Schema(description = "分组描述", example = "你说的对")
  24 + private String description;
  25 +
  26 +}
0 27 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoRespVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  10 +import cn.iocoder.yudao.module.iot.service.plugininfo.PluginInfoService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.*;
  19 +import org.springframework.web.multipart.MultipartFile;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  22 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  23 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_IS_EMPTY;
  24 +
  25 +@Tag(name = "管理后台 - IoT 插件信息")
  26 +@RestController
  27 +@RequestMapping("/iot/plugin-info")
  28 +@Validated
  29 +public class PluginInfoController {
  30 +
  31 + @Resource
  32 + private PluginInfoService pluginInfoService;
  33 +
  34 + @PostMapping("/create")
  35 + @Operation(summary = "创建插件信息")
  36 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:create')")
  37 + public CommonResult<Long> createPluginInfo(@Valid @RequestBody PluginInfoSaveReqVO createReqVO) {
  38 + return success(pluginInfoService.createPluginInfo(createReqVO));
  39 + }
  40 +
  41 + @PutMapping("/update")
  42 + @Operation(summary = "更新插件信息")
  43 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')")
  44 + public CommonResult<Boolean> updatePluginInfo(@Valid @RequestBody PluginInfoSaveReqVO updateReqVO) {
  45 + pluginInfoService.updatePluginInfo(updateReqVO);
  46 + return success(true);
  47 + }
  48 +
  49 + @DeleteMapping("/delete")
  50 + @Operation(summary = "删除插件信息")
  51 + @Parameter(name = "id", description = "编号", required = true)
  52 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:delete')")
  53 + public CommonResult<Boolean> deletePluginInfo(@RequestParam("id") Long id) {
  54 + pluginInfoService.deletePluginInfo(id);
  55 + return success(true);
  56 + }
  57 +
  58 + @GetMapping("/get")
  59 + @Operation(summary = "获得插件信息")
  60 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  61 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:query')")
  62 + public CommonResult<PluginInfoRespVO> getPluginInfo(@RequestParam("id") Long id) {
  63 + PluginInfoDO pluginInfo = pluginInfoService.getPluginInfo(id);
  64 + return success(BeanUtils.toBean(pluginInfo, PluginInfoRespVO.class));
  65 + }
  66 +
  67 + @GetMapping("/page")
  68 + @Operation(summary = "获得插件信息分页")
  69 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:query')")
  70 + public CommonResult<PageResult<PluginInfoRespVO>> getPluginInfoPage(@Valid PluginInfoPageReqVO pageReqVO) {
  71 + PageResult<PluginInfoDO> pageResult = pluginInfoService.getPluginInfoPage(pageReqVO);
  72 + return success(BeanUtils.toBean(pageResult, PluginInfoRespVO.class));
  73 + }
  74 +
  75 + @RequestMapping(value = "/update-jar",
  76 + method = {RequestMethod.POST, RequestMethod.PUT}) // 解决 uni-app 不支持 Put 上传文件的问题
  77 + @Operation(summary = "上传Jar包")
  78 + public CommonResult<Boolean> uploadJar(
  79 + @RequestParam("id") Long id,
  80 + @RequestParam("jar") MultipartFile file) throws Exception {
  81 + if (file.isEmpty()) {
  82 + throw exception(FILE_IS_EMPTY);
  83 + }
  84 + pluginInfoService.uploadJar(id, file);
  85 + return success(true);
  86 + }
  87 +
  88 + @PutMapping("/update-status")
  89 + @Operation(summary = "修改插件状态")
  90 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')")
  91 + public CommonResult<Boolean> updateUserStatus(@Valid @RequestBody PluginInfoSaveReqVO reqVO) {
  92 + pluginInfoService.updatePluginStatus(reqVO.getId(), reqVO.getStatus());
  93 + return success(true);
  94 + }
  95 +
  96 +}
0 97 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum;
  5 +import lombok.*;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +import java.time.LocalDateTime;
  10 +
  11 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  12 +
  13 +// TODO @haohao:只查询必要字段哈
  14 +@Schema(description = "管理后台 - IoT 插件信息分页 Request VO")
  15 +@Data
  16 +public class PluginInfoPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "插件包 ID ", example = "24627")
  19 + private String pluginId;
  20 +
  21 + @Schema(description = "插件名称", example = "赵六")
  22 + private String name;
  23 +
  24 + @Schema(description = "描述", example = "你猜")
  25 + private String description;
  26 +
  27 + @Schema(description = "部署方式", example = "2")
  28 + private Integer deployType;
  29 +
  30 + @Schema(description = "插件包文件名")
  31 + private String file;
  32 +
  33 + @Schema(description = "插件版本")
  34 + private String version;
  35 +
  36 + @Schema(description = "插件类型", example = "2")
  37 + @InEnum(IotPluginTypeEnum.class)
  38 + private Integer type;
  39 +
  40 + @Schema(description = "设备插件协议类型")
  41 + private String protocol;
  42 +
  43 + @Schema(description = "状态")
  44 + private Integer status;
  45 +
  46 + @Schema(description = "插件配置项描述信息")
  47 + private String configSchema;
  48 +
  49 + @Schema(description = "插件配置信息")
  50 + private String config;
  51 +
  52 + @Schema(description = "插件脚本")
  53 + private String script;
  54 +
  55 + @Schema(description = "创建时间")
  56 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  57 + private LocalDateTime[] createTime;
  58 +
  59 +}
0 60 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo;
  2 +
  3 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  4 +import com.alibaba.excel.annotation.ExcelProperty;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.Data;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - IoT 插件信息 Response VO")
  11 +@Data
  12 +@ExcelIgnoreUnannotated
  13 +public class PluginInfoRespVO {
  14 +
  15 + @Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
  16 + @ExcelProperty("主键 ID")
  17 + private Long id;
  18 +
  19 + @Schema(description = "插件包 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
  20 + @ExcelProperty("插件包 ID")
  21 + private String pluginId;
  22 +
  23 + @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  24 + @ExcelProperty("插件名称")
  25 + private String name;
  26 +
  27 + @Schema(description = "描述", example = "你猜")
  28 + @ExcelProperty("描述")
  29 + private String description;
  30 +
  31 + @Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  32 + @ExcelProperty("部署方式")
  33 + private Integer deployType;
  34 +
  35 + @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED)
  36 + @ExcelProperty("插件包文件名")
  37 + private String file;
  38 +
  39 + @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED)
  40 + @ExcelProperty("插件版本")
  41 + private String version;
  42 +
  43 + @Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  44 + @ExcelProperty("插件类型")
  45 + private Integer type;
  46 +
  47 + @Schema(description = "设备插件协议类型")
  48 + @ExcelProperty("设备插件协议类型")
  49 + private String protocol;
  50 +
  51 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
  52 + @ExcelProperty("状态")
  53 + private Integer status;
  54 +
  55 + @Schema(description = "插件配置项描述信息")
  56 + @ExcelProperty("插件配置项描述信息")
  57 + private String configSchema;
  58 +
  59 + @Schema(description = "插件配置信息")
  60 + @ExcelProperty("插件配置信息")
  61 + private String config;
  62 +
  63 + @Schema(description = "插件脚本")
  64 + @ExcelProperty("插件脚本")
  65 + private String script;
  66 +
  67 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  68 + @ExcelProperty("创建时间")
  69 + private LocalDateTime createTime;
  70 +
  71 +}
0 72 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +
  6 +@Schema(description = "管理后台 - IoT 插件信息新增/修改 Request VO")
  7 +@Data
  8 +public class PluginInfoSaveReqVO {
  9 +
  10 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
  11 + private Long id;
  12 +
  13 + @Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
  14 + private String pluginId;
  15 +
  16 + @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  17 + private String name;
  18 +
  19 + @Schema(description = "描述", example = "你猜")
  20 + private String description;
  21 +
  22 + @Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  23 + private Integer deployType;
  24 +
  25 + @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED)
  26 + private String file;
  27 +
  28 + @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED)
  29 + private String version;
  30 +
  31 + @Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  32 + private Integer type;
  33 +
  34 + @Schema(description = "设备插件协议类型")
  35 + private String protocol;
  36 +
  37 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
  38 + private Integer status;
  39 +
  40 + @Schema(description = "插件配置项描述信息")
  41 + private String configSchema;
  42 +
  43 + @Schema(description = "插件配置信息")
  44 + private String config;
  45 +
  46 + @Schema(description = "插件脚本")
  47 + private String script;
  48 +
  49 +}
0 50 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance;
  2 +
  3 +import org.springframework.web.bind.annotation.*;
  4 +import jakarta.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.security.access.prepost.PreAuthorize;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import io.swagger.v3.oas.annotations.Parameter;
  9 +import io.swagger.v3.oas.annotations.Operation;
  10 +
  11 +import jakarta.validation.*;
  12 +import jakarta.servlet.http.*;
  13 +import java.util.*;
  14 +import java.io.IOException;
  15 +
  16 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  17 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  18 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  19 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +
  22 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  23 +
  24 +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
  25 +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
  26 +
  27 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*;
  28 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
  29 +import cn.iocoder.yudao.module.iot.service.plugininstance.PluginInstanceService;
  30 +
  31 +@Tag(name = "管理后台 - IoT 插件实例")
  32 +@RestController
  33 +@RequestMapping("/iot/plugin-instance")
  34 +@Validated
  35 +public class PluginInstanceController {
  36 +
  37 + @Resource
  38 + private PluginInstanceService pluginInstanceService;
  39 +
  40 + @PostMapping("/create")
  41 + @Operation(summary = "创建IoT 插件实例")
  42 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:create')")
  43 + public CommonResult<Long> createPluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO createReqVO) {
  44 + return success(pluginInstanceService.createPluginInstance(createReqVO));
  45 + }
  46 +
  47 + @PutMapping("/update")
  48 + @Operation(summary = "更新IoT 插件实例")
  49 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:update')")
  50 + public CommonResult<Boolean> updatePluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO updateReqVO) {
  51 + pluginInstanceService.updatePluginInstance(updateReqVO);
  52 + return success(true);
  53 + }
  54 +
  55 + @DeleteMapping("/delete")
  56 + @Operation(summary = "删除IoT 插件实例")
  57 + @Parameter(name = "id", description = "编号", required = true)
  58 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:delete')")
  59 + public CommonResult<Boolean> deletePluginInstance(@RequestParam("id") Long id) {
  60 + pluginInstanceService.deletePluginInstance(id);
  61 + return success(true);
  62 + }
  63 +
  64 + @GetMapping("/get")
  65 + @Operation(summary = "获得IoT 插件实例")
  66 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  67 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')")
  68 + public CommonResult<PluginInstanceRespVO> getPluginInstance(@RequestParam("id") Long id) {
  69 + PluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstance(id);
  70 + return success(BeanUtils.toBean(pluginInstance, PluginInstanceRespVO.class));
  71 + }
  72 +
  73 + @GetMapping("/page")
  74 + @Operation(summary = "获得IoT 插件实例分页")
  75 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')")
  76 + public CommonResult<PageResult<PluginInstanceRespVO>> getPluginInstancePage(@Valid PluginInstancePageReqVO pageReqVO) {
  77 + PageResult<PluginInstanceDO> pageResult = pluginInstanceService.getPluginInstancePage(pageReqVO);
  78 + return success(BeanUtils.toBean(pageResult, PluginInstanceRespVO.class));
  79 + }
  80 +
  81 + @GetMapping("/export-excel")
  82 + @Operation(summary = "导出IoT 插件实例 Excel")
  83 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:export')")
  84 + @ApiAccessLog(operateType = EXPORT)
  85 + public void exportPluginInstanceExcel(@Valid PluginInstancePageReqVO pageReqVO,
  86 + HttpServletResponse response) throws IOException {
  87 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  88 + List<PluginInstanceDO> list = pluginInstanceService.getPluginInstancePage(pageReqVO).getList();
  89 + // 导出 Excel
  90 + ExcelUtils.write(response, "IoT 插件实例.xls", "数据", PluginInstanceRespVO.class,
  91 + BeanUtils.toBean(list, PluginInstanceRespVO.class));
  92 + }
  93 +
  94 +}
0 95 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
  2 +
  3 +import lombok.*;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +import java.time.LocalDateTime;
  8 +
  9 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  10 +
  11 +// TODO @haohao:建议搞个 plugin,然后里面分 info 和 instance。另外,是不是 info => config 会好点,插件配置?
  12 +@Schema(description = "管理后台 - IoT 插件实例分页 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class PluginInstancePageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "插件主程序id", example = "23738")
  19 + private String mainId;
  20 +
  21 + @Schema(description = "插件id", example = "26498")
  22 + private Long pluginId;
  23 +
  24 + @Schema(description = "插件主程序所在ip")
  25 + private String ip;
  26 +
  27 + @Schema(description = "插件主程序端口")
  28 + private Integer port;
  29 +
  30 + @Schema(description = "心跳时间,心路时间超过30秒需要剔除")
  31 + private Long heartbeatAt;
  32 +
  33 + @Schema(description = "创建时间")
  34 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  35 + private LocalDateTime[] createTime;
  36 +
  37 +}
0 38 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +import java.time.LocalDateTime;
  8 +import com.alibaba.excel.annotation.*;
  9 +
  10 +@Schema(description = "管理后台 - IoT 插件实例 Response VO")
  11 +@Data
  12 +@ExcelIgnoreUnannotated
  13 +public class PluginInstanceRespVO {
  14 +
  15 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864")
  16 + @ExcelProperty("主键ID")
  17 + private Long id;
  18 +
  19 + @Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738")
  20 + @ExcelProperty("插件主程序id")
  21 + private String mainId;
  22 +
  23 + @Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498")
  24 + @ExcelProperty("插件id")
  25 + private Long pluginId;
  26 +
  27 + @Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED)
  28 + @ExcelProperty("插件主程序所在ip")
  29 + private String ip;
  30 +
  31 + @Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + @ExcelProperty("插件主程序端口")
  33 + private Integer port;
  34 +
  35 + @Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED)
  36 + @ExcelProperty("心跳时间,心路时间超过30秒需要剔除")
  37 + private Long heartbeatAt;
  38 +
  39 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  40 + @ExcelProperty("创建时间")
  41 + private LocalDateTime createTime;
  42 +
  43 +}
0 44 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import jakarta.validation.constraints.*;
  7 +
  8 +@Schema(description = "管理后台 - IoT 插件实例新增/修改 Request VO")
  9 +@Data
  10 +public class PluginInstanceSaveReqVO {
  11 +
  12 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864")
  13 + private Long id;
  14 +
  15 + @Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738")
  16 + @NotEmpty(message = "插件主程序id不能为空")
  17 + private String mainId;
  18 +
  19 + @Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498")
  20 + @NotNull(message = "插件id不能为空")
  21 + private Long pluginId;
  22 +
  23 + @Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED)
  24 + @NotEmpty(message = "插件主程序所在ip不能为空")
  25 + private String ip;
  26 +
  27 + @Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED)
  28 + @NotNull(message = "插件主程序端口不能为空")
  29 + private Integer port;
  30 +
  31 + @Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + @NotNull(message = "心跳时间,心路时间超过30秒需要剔除不能为空")
  33 + private Long heartbeatAt;
  34 +
  35 +}
0 36 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java
... ... @@ -121,12 +121,12 @@ public class IotProductController {
121 121 }
122 122  
123 123 @GetMapping("/simple-list")
124   - @Operation(summary = "获得所有产品列表")
125   - @PreAuthorize("@ss.hasPermission('iot:product:query')")
  124 + @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项")
126 125 public CommonResult<List<IotProductRespVO>> getSimpleProductList() {
127 126 List<IotProductDO> list = productService.getProductList();
128 127 return success(convertList(list, product -> // 只返回 id、name 字段
129   - new IotProductRespVO().setId(product.getId()).setName(product.getName())));
  128 + new IotProductRespVO().setId(product.getId()).setName(product.getName())
  129 + .setDeviceType(product.getDeviceType())));
130 130 }
131 131  
132 132 }
133 133 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java
... ... @@ -38,8 +38,8 @@ public class IotProductRespVO {
38 38 @ExcelProperty("产品图标")
39 39 private String icon;
40 40  
41   - @Schema(description = "产品图标", example = "https://iocoder.cn/1.png")
42   - @ExcelProperty("产品图标")
  41 + @Schema(description = "产品图片", example = "https://iocoder.cn/1.png")
  42 + @ExcelProperty("产品图片")
43 43 private String picUrl;
44 44  
45 45 @Schema(description = "产品描述", example = "你猜")
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java
... ... @@ -28,7 +28,7 @@ public class IotProductSaveReqVO {
28 28 @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg")
29 29 private String icon;
30 30  
31   - @Schema(description = "产品图标", example = "https://iocoder.cn/1.png")
  31 + @Schema(description = "产品图片", example = "https://iocoder.cn/1.png")
32 32 private String picUrl;
33 33  
34 34 @Schema(description = "产品描述", example = "描述")
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java
1 1 package cn.iocoder.yudao.module.iot.dal.dataobject.device;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler;
4 5 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
5 6 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
6 7 import com.baomidou.mybatisplus.annotation.KeySequence;
  8 +import com.baomidou.mybatisplus.annotation.TableField;
7 9 import com.baomidou.mybatisplus.annotation.TableId;
8 10 import com.baomidou.mybatisplus.annotation.TableName;
9 11 import lombok.*;
10 12  
11 13 import java.math.BigDecimal;
12 14 import java.time.LocalDateTime;
  15 +import java.util.Set;
13 16  
14 17 /**
15 18 * IoT 设备 DO
16 19 *
17 20 * @author haohao
18 21 */
19   -@TableName("iot_device")
  22 +@TableName(value = "iot_device", autoResultMap = true)
20 23 @KeySequence("iot_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
21 24 @Data
22 25 @EqualsAndHashCode(callSuper = true)
... ... @@ -47,6 +50,17 @@ public class IotDeviceDO extends BaseDO {
47 50 * 设备序列号
48 51 */
49 52 private String serialNumber;
  53 + /**
  54 + * 设备图片
  55 + */
  56 + private String picUrl;
  57 + /**
  58 + * 设备分组编号集合
  59 + *
  60 + * 关联 {@link IotDeviceGroupDO#getId()}
  61 + */
  62 + @TableField(typeHandler = LongSetTypeHandler.class)
  63 + private Set<Long> groupIds;
50 64  
51 65 /**
52 66 * 产品编号
... ... @@ -66,13 +80,6 @@ public class IotDeviceDO extends BaseDO {
66 80 * 冗余 {@link IotProductDO#getDeviceType()}
67 81 */
68 82 private Integer deviceType;
69   -
70   - /**
71   - * 设备状态
72   - * <p>
73   - * 枚举 {@link IotDeviceStatusEnum}
74   - */
75   - private Integer status;
76 83 /**
77 84 * 网关设备编号
78 85 * <p>
... ... @@ -83,6 +90,13 @@ public class IotDeviceDO extends BaseDO {
83 90 private Long gatewayId;
84 91  
85 92 /**
  93 + * 设备状态
  94 + * <p>
  95 + * 枚举 {@link IotDeviceStatusEnum}
  96 + */
  97 + private Integer status;
  98 +
  99 + /**
86 100 * 设备状态最后更新时间
87 101 */
88 102 private LocalDateTime statusLastUpdateTime;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.device;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
  8 +
  9 +/**
  10 + * IoT 设备分组 DO
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@TableName("iot_device_group")
  15 +@KeySequence("iot_device_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +@Builder
  20 +@NoArgsConstructor
  21 +@AllArgsConstructor
  22 +public class IotDeviceGroupDO extends BaseDO {
  23 +
  24 + /**
  25 + * 分组 ID
  26 + */
  27 + @TableId
  28 + private Long id;
  29 + /**
  30 + * 分组名字
  31 + */
  32 + private String name;
  33 + /**
  34 + * 分组状态
  35 + *
  36 + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
  37 + */
  38 + private Integer status;
  39 + /**
  40 + * 分组描述
  41 + */
  42 + private String description;
  43 +
  44 +}
0 45 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
  8 +
  9 +/**
  10 + * IoT 插件信息 DO
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@TableName("iot_plugin_info")
  15 +@KeySequence("iot_plugin_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +@Builder
  20 +@NoArgsConstructor
  21 +@AllArgsConstructor
  22 +public class PluginInfoDO extends BaseDO {
  23 +
  24 + /**
  25 + * 主键 ID
  26 + */
  27 + @TableId
  28 + private Long id;
  29 + // TODO @haohao:这个是不是改成类似 key 之类的字段哈?
  30 + /**
  31 + * 插件包 ID
  32 + */
  33 + private String pluginId;
  34 + /**
  35 + * 插件名称
  36 + */
  37 + private String name;
  38 + /**
  39 + * 描述
  40 + */
  41 + private String description;
  42 + /**
  43 + * 部署方式
  44 + */
  45 + // TODO @haohao:枚举
  46 + private Integer deployType;
  47 + /**
  48 + * 插件包文件名
  49 + */
  50 + // TODO @haohao:是不是叫 fileName 哈?避免后续有别的字段,类似 fileUrl?
  51 + private String file;
  52 + /**
  53 + * 插件版本
  54 + */
  55 + private String version;
  56 + /**
  57 + * 插件类型
  58 + */
  59 + // TODO @haohao:枚举
  60 + private Integer type;
  61 + /**
  62 + * 设备插件协议类型
  63 + */
  64 + private String protocol;
  65 + /**
  66 + * 状态
  67 + */
  68 + // TODO @haohao:枚举
  69 + private Integer status;
  70 + /**
  71 + * 插件配置项描述信息
  72 + */
  73 + private String configSchema;
  74 + /**
  75 + * 插件配置信息
  76 + */
  77 + private String config;
  78 + /**
  79 + * 插件脚本
  80 + */
  81 + private String script;
  82 +
  83 +}
0 84 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
  8 +
  9 +// TODO @haohao:一些必要的关联、枚举
  10 +/**
  11 + * IoT 插件实例 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("iot_plugin_instance")
  16 +@KeySequence("iot_plugin_instance_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class PluginInstanceDO extends BaseDO {
  24 +
  25 + /**
  26 + * 主键ID
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 插件主程序 ID
  32 + */
  33 + private String mainId;
  34 + /**
  35 + * 插件id
  36 + */
  37 + private Long pluginId;
  38 + /**
  39 + * 插件主程序所在ip
  40 + */
  41 + private String ip;
  42 + /**
  43 + * 插件主程序端口
  44 + */
  45 + private Integer port;
  46 + /**
  47 + * 心跳时间,心路时间超过 30 秒需要剔除
  48 + */
  49 + private Long heartbeatAt;
  50 +
  51 +}
0 52 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.device;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * IoT 设备分组 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface IotDeviceGroupMapper extends BaseMapperX<IotDeviceGroupDO> {
  19 +
  20 + default PageResult<IotDeviceGroupDO> selectPage(IotDeviceGroupPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceGroupDO>()
  22 + .likeIfPresent(IotDeviceGroupDO::getName, reqVO.getName())
  23 + .betweenIfPresent(IotDeviceGroupDO::getCreateTime, reqVO.getCreateTime())
  24 + .orderByDesc(IotDeviceGroupDO::getId));
  25 + }
  26 +
  27 + default List<IotDeviceGroupDO> selectListByStatus(Integer status) {
  28 + return selectList(IotDeviceGroupDO::getStatus, status);
  29 + }
  30 +
  31 + default IotDeviceGroupDO selectByName(String name) {
  32 + return selectOne(IotDeviceGroupDO::getName, name);
  33 + }
  34 +
  35 +}
0 36 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
1 1 package cn.iocoder.yudao.module.iot.dal.mysql.device;
2 2  
  3 +import cn.hutool.core.util.ObjectUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
... ... @@ -7,6 +8,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePa
7 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
8 9 import org.apache.ibatis.annotations.Mapper;
9 10  
  11 +import java.util.List;
  12 +
10 13 /**
11 14 * IoT 设备 Mapper
12 15 *
... ... @@ -15,30 +18,21 @@ import org.apache.ibatis.annotations.Mapper;
15 18 @Mapper
16 19 public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
17 20  
18   - // TODO @haohao:可能多余的查询条件,要去掉哈
19 21 default PageResult<IotDeviceDO> selectPage(IotDevicePageReqVO reqVO) {
20 22 return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>()
21   - .eqIfPresent(IotDeviceDO::getDeviceKey, reqVO.getDeviceKey())
22 23 .likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName())
23 24 .eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId())
24   - .eqIfPresent(IotDeviceDO::getProductKey, reqVO.getProductKey())
25 25 .eqIfPresent(IotDeviceDO::getDeviceType, reqVO.getDeviceType())
26 26 .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname())
27   - .eqIfPresent(IotDeviceDO::getGatewayId, reqVO.getGatewayId())
28 27 .eqIfPresent(IotDeviceDO::getStatus, reqVO.getStatus())
29   - .betweenIfPresent(IotDeviceDO::getStatusLastUpdateTime, reqVO.getStatusLastUpdateTime())
30   - .betweenIfPresent(IotDeviceDO::getLastOnlineTime, reqVO.getLastOnlineTime())
31   - .betweenIfPresent(IotDeviceDO::getLastOfflineTime, reqVO.getLastOfflineTime())
32   - .betweenIfPresent(IotDeviceDO::getActiveTime, reqVO.getActiveTime())
33   - .eqIfPresent(IotDeviceDO::getDeviceSecret, reqVO.getDeviceSecret())
34   - .eqIfPresent(IotDeviceDO::getMqttClientId, reqVO.getMqttClientId())
35   - .likeIfPresent(IotDeviceDO::getMqttUsername, reqVO.getMqttUsername())
36   - .eqIfPresent(IotDeviceDO::getMqttPassword, reqVO.getMqttPassword())
37   - .eqIfPresent(IotDeviceDO::getAuthType, reqVO.getAuthType())
38   - .betweenIfPresent(IotDeviceDO::getCreateTime, reqVO.getCreateTime())
  28 + .apply(ObjectUtil.isNotNull(reqVO.getGroupId()), "FIND_IN_SET(" + reqVO.getGroupId() + ",group_ids) > 0")
39 29 .orderByDesc(IotDeviceDO::getId));
40 30 }
41 31  
  32 + default IotDeviceDO selectByDeviceName(String deviceName) {
  33 + return selectOne(IotDeviceDO::getDeviceName, deviceName);
  34 + }
  35 +
42 36 default IotDeviceDO selectByProductKeyAndDeviceName(String productKey, String deviceName) {
43 37 return selectOne(IotDeviceDO::getProductKey, productKey,
44 38 IotDeviceDO::getDeviceName, deviceName);
... ... @@ -51,4 +45,19 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; {
51 45 default Long selectCountByProductId(Long productId) {
52 46 return selectCount(IotDeviceDO::getProductId, productId);
53 47 }
  48 +
  49 + default IotDeviceDO selectByDeviceKey(String deviceKey) {
  50 + return selectOne(IotDeviceDO::getDeviceKey, deviceKey);
  51 + }
  52 +
  53 + default List<IotDeviceDO> selectList(Integer deviceType) {
  54 + return selectList(IotDeviceDO::getDeviceType, deviceType);
  55 + }
  56 +
  57 + default Long selectCountByGroupId(Long groupId) {
  58 + return selectCount(new LambdaQueryWrapperX<IotDeviceDO>()
  59 + .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0")
  60 + .orderByDesc(IotDeviceDO::getId));
  61 + }
  62 +
54 63 }
55 64 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.plugininfo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*;
  9 +
  10 +/**
  11 + * IoT 插件信息 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface PluginInfoMapper extends BaseMapperX<PluginInfoDO> {
  17 +
  18 + default PageResult<PluginInfoDO> selectPage(PluginInfoPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<PluginInfoDO>()
  20 + .eqIfPresent(PluginInfoDO::getPluginId, reqVO.getPluginId())
  21 + .likeIfPresent(PluginInfoDO::getName, reqVO.getName())
  22 + .eqIfPresent(PluginInfoDO::getDescription, reqVO.getDescription())
  23 + .eqIfPresent(PluginInfoDO::getDeployType, reqVO.getDeployType())
  24 + .eqIfPresent(PluginInfoDO::getFile, reqVO.getFile())
  25 + .eqIfPresent(PluginInfoDO::getVersion, reqVO.getVersion())
  26 + .eqIfPresent(PluginInfoDO::getType, reqVO.getType())
  27 + .eqIfPresent(PluginInfoDO::getProtocol, reqVO.getProtocol())
  28 + .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus())
  29 + .eqIfPresent(PluginInfoDO::getConfigSchema, reqVO.getConfigSchema())
  30 + .eqIfPresent(PluginInfoDO::getConfig, reqVO.getConfig())
  31 + .eqIfPresent(PluginInfoDO::getScript, reqVO.getScript())
  32 + .betweenIfPresent(PluginInfoDO::getCreateTime, reqVO.getCreateTime())
  33 + .orderByDesc(PluginInfoDO::getId));
  34 + }
  35 +
  36 +}
0 37 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.plugininstance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*;
  9 +
  10 +/**
  11 + * IoT 插件实例 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface PluginInstanceMapper extends BaseMapperX<PluginInstanceDO> {
  17 +
  18 + default PageResult<PluginInstanceDO> selectPage(PluginInstancePageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<PluginInstanceDO>()
  20 + .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId())
  21 + .eqIfPresent(PluginInstanceDO::getPluginId, reqVO.getPluginId())
  22 + .eqIfPresent(PluginInstanceDO::getIp, reqVO.getIp())
  23 + .eqIfPresent(PluginInstanceDO::getPort, reqVO.getPort())
  24 + .eqIfPresent(PluginInstanceDO::getHeartbeatAt, reqVO.getHeartbeatAt())
  25 + .betweenIfPresent(PluginInstanceDO::getCreateTime, reqVO.getCreateTime())
  26 + .orderByDesc(PluginInstanceDO::getId));
  27 + }
  28 +
  29 +}
0 30 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java
... ... @@ -21,7 +21,7 @@ public interface IotProductCategoryMapper extends BaseMapperX&lt;IotProductCategory
21 21 return selectPage(reqVO, new LambdaQueryWrapperX<IotProductCategoryDO>()
22 22 .likeIfPresent(IotProductCategoryDO::getName, reqVO.getName())
23 23 .betweenIfPresent(IotProductCategoryDO::getCreateTime, reqVO.getCreateTime())
24   - .orderByDesc(IotProductCategoryDO::getId));
  24 + .orderByAsc(IotProductCategoryDO::getSort));
25 25 }
26 26  
27 27 default List<IotProductCategoryDO> selectListByStatus(Integer status) {
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java
... ... @@ -23,7 +23,7 @@ public interface IotProductMapper extends BaseMapperX&lt;IotProductDO&gt; {
23 23 }
24 24  
25 25 default IotProductDO selectByProductKey(String productKey) {
26   - return selectOne(new LambdaQueryWrapperX<IotProductDO>().eq(IotProductDO::getProductKey, productKey));
  26 + return selectOne(IotProductDO::getProductKey, productKey);
27 27 }
28 28  
29 29 }
30 30 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.plugin;
  2 +
  3 +import org.pf4j.spring.SpringPluginManager;
  4 +import org.springframework.context.annotation.Bean;
  5 +import org.springframework.context.annotation.Configuration;
  6 +
  7 +@Configuration
  8 +public class SpringConfiguration {
  9 +
  10 + @Bean
  11 + public SpringPluginManager pluginManager() {
  12 + return new SpringPluginManager();
  13 + }
  14 +
  15 +}
0 16 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java
... ... @@ -15,7 +15,6 @@ import java.util.Map;
15 15 */
16 16 public interface IotDeviceDataService {
17 17  
18   -
19 18 /**
20 19 * 保存设备数据
21 20 *
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.device;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
  8 +import jakarta.validation.Valid;
  9 +
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  14 +
  15 +/**
  16 + * IoT 设备分组 Service 接口
  17 + *
  18 + * @author 芋道源码
  19 + */
  20 +public interface IotDeviceGroupService {
  21 +
  22 + /**
  23 + * 创建设备分组
  24 + *
  25 + * @param createReqVO 创建信息
  26 + * @return 编号
  27 + */
  28 + Long createDeviceGroup(@Valid IotDeviceGroupSaveReqVO createReqVO);
  29 +
  30 + /**
  31 + * 更新设备分组
  32 + *
  33 + * @param updateReqVO 更新信息
  34 + */
  35 + void updateDeviceGroup(@Valid IotDeviceGroupSaveReqVO updateReqVO);
  36 +
  37 + /**
  38 + * 删除设备分组
  39 + *
  40 + * @param id 编号
  41 + */
  42 + void deleteDeviceGroup(Long id);
  43 +
  44 + /**
  45 + * 校验设备分组是否存在
  46 + *
  47 + * @param ids 设备分组 ID 数组
  48 + */
  49 + default List<IotDeviceGroupDO> validateDeviceGroupExists(Collection<Long> ids) {
  50 + if (CollUtil.isEmpty(ids)) {
  51 + return List.of();
  52 + }
  53 + return convertList(ids, this::validateDeviceGroupExists);
  54 + }
  55 +
  56 + /**
  57 + * 校验设备分组是否存在
  58 + *
  59 + * @param id 设备分组 ID
  60 + * @return 设备分组
  61 + */
  62 + IotDeviceGroupDO validateDeviceGroupExists(Long id);
  63 +
  64 + /**
  65 + * 获得设备分组
  66 + *
  67 + * @param id 编号
  68 + * @return 设备分组
  69 + */
  70 + IotDeviceGroupDO getDeviceGroup(Long id);
  71 +
  72 + /**
  73 + * 获得设备分组
  74 + *
  75 + * @param name 名称
  76 + * @return 设备分组
  77 + */
  78 + IotDeviceGroupDO getDeviceGroupByName(String name);
  79 +
  80 + /**
  81 + * 获得设备分组分页
  82 + *
  83 + * @param pageReqVO 分页查询
  84 + * @return 设备分组分页
  85 + */
  86 + PageResult<IotDeviceGroupDO> getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO);
  87 +
  88 + /**
  89 + * 获得设备分组列表
  90 + *
  91 + * @param status 状态
  92 + * @return 设备分组列表
  93 + */
  94 + List<IotDeviceGroupDO> getDeviceGroupListByStatus(Integer status);
  95 +
  96 +}
0 97 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.device;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
  8 +import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceGroupMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import java.util.List;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  16 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS;
  17 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_NOT_EXISTS;
  18 +
  19 +/**
  20 + * IoT 设备分组 Service 实现类
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@Service
  25 +@Validated
  26 +public class IotDeviceGroupServiceImpl implements IotDeviceGroupService {
  27 +
  28 + @Resource
  29 + private IotDeviceGroupMapper deviceGroupMapper;
  30 +
  31 + @Resource
  32 + private IotDeviceService deviceService;
  33 +
  34 + @Override
  35 + public Long createDeviceGroup(IotDeviceGroupSaveReqVO createReqVO) {
  36 + // 插入
  37 + IotDeviceGroupDO deviceGroup = BeanUtils.toBean(createReqVO, IotDeviceGroupDO.class);
  38 + deviceGroupMapper.insert(deviceGroup);
  39 + // 返回
  40 + return deviceGroup.getId();
  41 + }
  42 +
  43 + @Override
  44 + public void updateDeviceGroup(IotDeviceGroupSaveReqVO updateReqVO) {
  45 + // 校验存在
  46 + validateDeviceGroupExists(updateReqVO.getId());
  47 + // 更新
  48 + IotDeviceGroupDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceGroupDO.class);
  49 + deviceGroupMapper.updateById(updateObj);
  50 + }
  51 +
  52 + @Override
  53 + public void deleteDeviceGroup(Long id) {
  54 + // 1.1 校验存在
  55 + validateDeviceGroupExists(id);
  56 + // 1.2 校验是否存在设备
  57 + if (deviceService.getDeviceCountByGroupId(id) > 0) {
  58 + throw exception(DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS);
  59 + }
  60 +
  61 + // 删除
  62 + deviceGroupMapper.deleteById(id);
  63 + }
  64 +
  65 + @Override
  66 + public IotDeviceGroupDO validateDeviceGroupExists(Long id) {
  67 + IotDeviceGroupDO group = deviceGroupMapper.selectById(id);
  68 + if (group == null) {
  69 + throw exception(DEVICE_GROUP_NOT_EXISTS);
  70 + }
  71 + return group;
  72 + }
  73 +
  74 + @Override
  75 + public IotDeviceGroupDO getDeviceGroup(Long id) {
  76 + return deviceGroupMapper.selectById(id);
  77 + }
  78 +
  79 + @Override
  80 + public IotDeviceGroupDO getDeviceGroupByName(String name) {
  81 + return deviceGroupMapper.selectByName(name);
  82 + }
  83 +
  84 + @Override
  85 + public PageResult<IotDeviceGroupDO> getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO) {
  86 + return deviceGroupMapper.selectPage(pageReqVO);
  87 + }
  88 +
  89 + @Override
  90 + public List<IotDeviceGroupDO> getDeviceGroupListByStatus(Integer status) {
  91 + return deviceGroupMapper.selectListByStatus(status);
  92 + }
  93 +
  94 +}
0 95 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
1 1 package cn.iocoder.yudao.module.iot.service.device;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
4 5 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
5 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
6   -import jakarta.validation.*;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceUpdateGroupReqVO;
7 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
8   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportRespVO;
  10 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportExcelVO;
  11 +import jakarta.validation.Valid;
  12 +
  13 +import javax.annotation.Nullable;
  14 +import java.util.Collection;
  15 +import java.util.List;
9 16  
10 17 /**
11 18 * IoT 设备 Service 接口
... ... @@ -30,13 +37,34 @@ public interface IotDeviceService {
30 37 void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO);
31 38  
32 39 /**
33   - * 删除设备
  40 + * 更新设备状态
  41 + *
  42 + * @param updateReqVO 更新信息
  43 + */
  44 + void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO);
  45 +
  46 + /**
  47 + * 更新设备分组
  48 + *
  49 + * @param updateReqVO 更新信息
  50 + */
  51 + void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO);
  52 +
  53 + /**
  54 + * 删除单个设备
34 55 *
35 56 * @param id 编号
36 57 */
37 58 void deleteDevice(Long id);
38 59  
39 60 /**
  61 + * 删除多个设备
  62 + *
  63 + * @param ids 编号数组
  64 + */
  65 + void deleteDeviceList(Collection<Long> ids);
  66 +
  67 + /**
40 68 * 获得设备
41 69 *
42 70 * @param id 编号
... ... @@ -45,7 +73,7 @@ public interface IotDeviceService {
45 73 IotDeviceDO getDevice(Long id);
46 74  
47 75 /**
48   - * 获得设备分页
  76 + * ��得设备分页
49 77 *
50 78 * @param pageReqVO 分页查询
51 79 * @return IoT 设备分页
... ... @@ -53,11 +81,12 @@ public interface IotDeviceService {
53 81 PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO);
54 82  
55 83 /**
56   - * 更新设备状态
  84 + * 获得设备列表
57 85 *
58   - * @param updateReqVO 更新信息
  86 + * @param deviceType 设备类型
  87 + * @return 设备列表
59 88 */
60   - void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO);
  89 + List<IotDeviceDO> getDeviceList(@Nullable Integer deviceType);
61 90  
62 91 /**
63 92 * 获得设备数量
... ... @@ -68,6 +97,14 @@ public interface IotDeviceService {
68 97 Long getDeviceCountByProductId(Long productId);
69 98  
70 99 /**
  100 + * 获得设备数量
  101 + *
  102 + * @param groupId 分组编号
  103 + * @return 设备数量
  104 + */
  105 + Long getDeviceCountByGroupId(Long groupId);
  106 +
  107 + /**
71 108 * 根据产品 key 和设备名称,获得设备信息
72 109 *
73 110 * @param productKey 产品 key
... ... @@ -75,4 +112,14 @@ public interface IotDeviceService {
75 112 * @return 设备信息
76 113 */
77 114 IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName);
  115 +
  116 + /**
  117 + * 导入设备
  118 + *
  119 + * @param importDevices 导入设备列表
  120 + * @param updateSupport 是否支持更新
  121 + * @return 导入结果
  122 + */
  123 + IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport);
  124 +
78 125 }
79 126 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
1 1 package cn.iocoder.yudao.module.iot.service.device;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.IdUtil;
  5 +import cn.hutool.core.util.RandomUtil;
4 6 import cn.hutool.core.util.StrUtil;
  7 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
5 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  10 +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
7 11 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
8   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
9   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
10   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
  12 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
11 13 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  14 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
12 15 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
13 16 import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper;
14 17 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
  18 +import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
15 19 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
16 20 import jakarta.annotation.Resource;
  21 +import jakarta.validation.ConstraintViolationException;
17 22 import lombok.extern.slf4j.Slf4j;
  23 +import org.springframework.context.annotation.Lazy;
18 24 import org.springframework.stereotype.Service;
19 25 import org.springframework.transaction.annotation.Transactional;
20 26 import org.springframework.validation.annotation.Validated;
21 27  
22   -import java.security.SecureRandom;
  28 +import javax.annotation.Nullable;
23 29 import java.time.LocalDateTime;
24   -import java.util.Base64;
25   -import java.util.Objects;
26   -import java.util.UUID;
  30 +import java.util.*;
27 31  
28 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  33 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
29 34 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
30 35  
31 36 /**
... ... @@ -40,142 +45,87 @@ public class IotDeviceServiceImpl implements IotDeviceService {
40 45  
41 46 @Resource
42 47 private IotDeviceMapper deviceMapper;
  48 +
43 49 @Resource
44 50 private IotProductService productService;
  51 + @Resource
  52 + @Lazy // 延迟加载,解决循环依赖
  53 + private IotDeviceGroupService deviceGroupService;
45 54  
46   - /**
47   - * 创建 IoT 设备
48   - *
49   - * @param createReqVO 创建请求 VO
50   - * @return 设备 ID
51   - */
52 55 @Override
53   - @Transactional(rollbackFor = Exception.class)
54 56 public Long createDevice(IotDeviceSaveReqVO createReqVO) {
55 57 // 1.1 校验产品是否存在
56 58 IotProductDO product = productService.getProduct(createReqVO.getProductId());
57 59 if (product == null) {
58 60 throw exception(PRODUCT_NOT_EXISTS);
59 61 }
60   - // 1.2 校验设备名称在同一产品下是否唯一
61   - if (StrUtil.isBlank(createReqVO.getDeviceName())) {
62   - createReqVO.setDeviceName(generateUniqueDeviceName(product.getProductKey()));
63   - } else {
64   - validateDeviceNameUnique(product.getProductKey(), createReqVO.getDeviceName());
  62 + // 1.2 校验设备标识是否唯一
  63 + if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) {
  64 + throw exception(DEVICE_KEY_EXISTS);
  65 + }
  66 + // 1.3 校验设备名称在同一产品下是否唯一
  67 + if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) {
  68 + throw exception(DEVICE_NAME_EXISTS);
65 69 }
  70 + // 1.4 校验父设备是否为合法网关
  71 + if (IotProductDeviceTypeEnum.isGateway(product.getDeviceType())
  72 + && createReqVO.getGatewayId() != null) {
  73 + validateGatewayDeviceExists(createReqVO.getGatewayId());
  74 + }
  75 + // 1.5 校验分组存在
  76 + deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds());
66 77  
67 78 // 2.1 转换 VO 为 DO
68   - IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class)
69   - .setProductKey(product.getProductKey())
70   - .setDeviceType(product.getDeviceType());
71   - // 2.2 生成并设置必要的字段
72   - device.setDeviceKey(generateUniqueDeviceKey());
73   - device.setDeviceSecret(generateDeviceSecret());
74   - device.setMqttClientId(generateMqttClientId());
75   - device.setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey()));
76   - device.setMqttPassword(generateMqttPassword());
77   - // 2.3 设置设备状态为未激活
78   - device.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus());
79   - device.setStatusLastUpdateTime(LocalDateTime.now());
80   - // 2.4 插入到数据库
  79 + IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> {
  80 + o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
  81 + // 生成并设置必要的字段
  82 + o.setDeviceSecret(generateDeviceSecret())
  83 + .setMqttClientId(generateMqttClientId())
  84 + .setMqttUsername(generateMqttUsername(o.getDeviceName(), o.getProductKey()))
  85 + .setMqttPassword(generateMqttPassword());
  86 + // 设置设备状态为未激活
  87 + o.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus()).setStatusLastUpdateTime(LocalDateTime.now());
  88 + });
  89 + // 2.2 插入到数据库
81 90 deviceMapper.insert(device);
82 91 return device.getId();
83 92 }
84 93  
85   - /**
86   - * 校验设备名称在同一产品下是否唯一
87   - *
88   - * @param productKey 产品 Key
89   - * @param deviceName 设备名称
90   - */
91   - private void validateDeviceNameUnique(String productKey, String deviceName) {
92   - IotDeviceDO existingDevice = deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
93   - if (existingDevice != null) {
94   - throw exception(DEVICE_NAME_EXISTS);
  94 + @Override
  95 + public void updateDevice(IotDeviceSaveReqVO updateReqVO) {
  96 + updateReqVO.setDeviceKey(null).setDeviceName(null).setProductId(null); // 不允许更新
  97 + // 1.1 校验存在
  98 + IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
  99 + // 1.2 校验父设备是否为合法网关
  100 + if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType())
  101 + && updateReqVO.getGatewayId() != null) {
  102 + validateGatewayDeviceExists(updateReqVO.getGatewayId());
95 103 }
96   - }
  104 + // 1.3 校验分组存在
  105 + deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds());
97 106  
98   - /**
99   - * 生成唯一的 deviceKey
100   - *
101   - * @return 生成的 deviceKey
102   - */
103   - private String generateUniqueDeviceKey() {
104   - return UUID.randomUUID().toString();
105   - }
106   -
107   - /**
108   - * 生成 deviceSecret
109   - *
110   - * @return 生成的 deviceSecret
111   - */
112   - private String generateDeviceSecret() {
113   - return IdUtil.fastSimpleUUID();
114   - }
115   -
116   - /**
117   - * 生成 MQTT Client ID
118   - *
119   - * @return 生成的 MQTT Client ID
120   - */
121   - private String generateMqttClientId() {
122   - return UUID.randomUUID().toString();
123   - }
124   -
125   - /**
126   - * 生成 MQTT Username
127   - *
128   - * @param deviceName 设备名称
129   - * @param productKey 产品 Key
130   - * @return 生成的 MQTT Username
131   - */
132   - private String generateMqttUsername(String deviceName, String productKey) {
133   - return deviceName + "&" + productKey;
134   - }
135   -
136   - /**
137   - * 生成 MQTT Password
138   - *
139   - * @return 生成的 MQTT Password
140   - */
141   - private String generateMqttPassword() {
142   - // TODO @浩浩:这里的 StrUtil 随机字符串?
143   - SecureRandom secureRandom = new SecureRandom();
144   - byte[] passwordBytes = new byte[32]; // 256 位的随机数
145   - secureRandom.nextBytes(passwordBytes);
146   - return Base64.getUrlEncoder().withoutPadding().encodeToString(passwordBytes);
147   - }
148   -
149   - /**
150   - * 生成唯一的 DeviceName
151   - *
152   - * @param productKey 产品标识
153   - * @return 生成的唯一 DeviceName
154   - */
155   - private String generateUniqueDeviceName(String productKey) {
156   - for (int i = 0; i < Short.MAX_VALUE; i++) {
157   - String deviceName = IdUtil.fastSimpleUUID().substring(0, 20);
158   - if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) {
159   - return deviceName;
160   - }
161   - }
162   - throw new IllegalArgumentException("生成 DeviceName 失败");
  107 + // 2. 更新到数据库
  108 + IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
  109 + deviceMapper.updateById(updateObj);
163 110 }
164 111  
165 112 @Override
166 113 @Transactional(rollbackFor = Exception.class)
167   - public void updateDevice(IotDeviceSaveReqVO updateReqVO) {
168   - // 1. 校验存在
169   - validateDeviceExists(updateReqVO.getId());
  114 + public void updateDeviceGroup(IotDeviceUpdateGroupReqVO updateReqVO) {
  115 + // 1.1 校验设备存在
  116 + List<IotDeviceDO> devices = deviceMapper.selectBatchIds(updateReqVO.getIds());
  117 + if (CollUtil.isEmpty(devices)) {
  118 + return;
  119 + }
  120 + // 1.2 校验分组存在
  121 + deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds());
170 122  
171   - // 2. 更新到数据库
172   - IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class)
173   - .setDeviceName(null).setProductId(null); // 设备名称 和 产品 ID 不能修改
174   - deviceMapper.updateById(updateObj);
  123 + // 3. 更新设备分组
  124 + deviceMapper.updateBatch(convertList(devices, device -> new IotDeviceDO()
  125 + .setId(device.getId()).setGroupIds(updateReqVO.getGroupIds())));
175 126 }
176 127  
177 128 @Override
178   - @Transactional(rollbackFor = Exception.class)
179 129 public void deleteDevice(Long id) {
180 130 // 1.1 校验存在
181 131 IotDeviceDO device = validateDeviceExists(id);
... ... @@ -188,6 +138,28 @@ public class IotDeviceServiceImpl implements IotDeviceService {
188 138 deviceMapper.deleteById(id);
189 139 }
190 140  
  141 + @Override
  142 + @Transactional(rollbackFor = Exception.class)
  143 + public void deleteDeviceList(Collection<Long> ids) {
  144 + // 1.1 校验存在
  145 + if (CollUtil.isEmpty(ids)) {
  146 + return;
  147 + }
  148 + List<IotDeviceDO> devices = deviceMapper.selectBatchIds(ids);
  149 + if (CollUtil.isEmpty(devices)) {
  150 + return;
  151 + }
  152 + // 1.2 校验网关设备是否存在
  153 + for (IotDeviceDO device : devices) {
  154 + if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(device.getId()) > 0) {
  155 + throw exception(DEVICE_HAS_CHILDREN);
  156 + }
  157 + }
  158 +
  159 + // 2. 删除设备
  160 + deviceMapper.deleteByIds(ids);
  161 + }
  162 +
191 163 /**
192 164 * 校验设备是否存在
193 165 *
... ... @@ -202,13 +174,24 @@ public class IotDeviceServiceImpl implements IotDeviceService {
202 174 return device;
203 175 }
204 176  
205   - @Override
206   - public IotDeviceDO getDevice(Long id) {
  177 + /**
  178 + * 校验网关设备是否存在
  179 + *
  180 + * @param id 设备 ID
  181 + */
  182 + private void validateGatewayDeviceExists(Long id) {
207 183 IotDeviceDO device = deviceMapper.selectById(id);
208 184 if (device == null) {
209   - throw exception(DEVICE_NOT_EXISTS);
  185 + throw exception(DEVICE_GATEWAY_NOT_EXISTS);
210 186 }
211   - return device;
  187 + if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {
  188 + throw exception(DEVICE_NOT_GATEWAY);
  189 + }
  190 + }
  191 +
  192 + @Override
  193 + public IotDeviceDO getDevice(Long id) {
  194 + return deviceMapper.selectById(id);
212 195 }
213 196  
214 197 @Override
... ... @@ -217,18 +200,23 @@ public class IotDeviceServiceImpl implements IotDeviceService {
217 200 }
218 201  
219 202 @Override
  203 + public List<IotDeviceDO> getDeviceList(@Nullable Integer deviceType) {
  204 + return deviceMapper.selectList(deviceType);
  205 + }
  206 +
  207 + @Override
220 208 public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) {
221 209 // 1. 校验存在
222 210 IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
223 211  
224 212 // 2.1 更新状态和更新时间
225   - IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
  213 + IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class)
  214 + .setStatusLastUpdateTime(LocalDateTime.now());
226 215 // 2.2 更新状态相关时间
227 216 if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus())
228 217 && Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
229 218 // 从未激活到在线,设置激活时间和最后上线时间
230   - updateDevice.setActiveTime(LocalDateTime.now());
231   - updateDevice.setLastOnlineTime(LocalDateTime.now());
  219 + updateDevice.setActiveTime(LocalDateTime.now()).setLastOnlineTime(LocalDateTime.now());
232 220 } else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
233 221 // 如果是上线,设置最后上线时间
234 222 updateDevice.setLastOnlineTime(LocalDateTime.now());
... ... @@ -236,10 +224,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
236 224 // 如果是离线,设置最后离线时间
237 225 updateDevice.setLastOfflineTime(LocalDateTime.now());
238 226 }
239   -
240   - // 2.3 设置状态更新时间
241   - updateDevice.setStatusLastUpdateTime(LocalDateTime.now());
242   - // 2.4 更新到数据库
  227 + // 2.3 更新到数据库
243 228 deviceMapper.updateById(updateDevice);
244 229 }
245 230  
... ... @@ -249,9 +234,131 @@ public class IotDeviceServiceImpl implements IotDeviceService {
249 234 }
250 235  
251 236 @Override
  237 + public Long getDeviceCountByGroupId(Long groupId) {
  238 + return deviceMapper.selectCountByGroupId(groupId);
  239 + }
  240 +
  241 + @Override
252 242 @TenantIgnore
253 243 public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) {
254 244 return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
255 245 }
256 246  
  247 + /**
  248 + * 生成 deviceKey
  249 + *
  250 + * @return 生成的 deviceKey
  251 + */
  252 + private String generateDeviceKey() {
  253 + return RandomUtil.randomString(16);
  254 + }
  255 +
  256 + /**
  257 + * 生成 deviceSecret
  258 + *
  259 + * @return 生成的 deviceSecret
  260 + */
  261 + private String generateDeviceSecret() {
  262 + return IdUtil.fastSimpleUUID();
  263 + }
  264 +
  265 + /**
  266 + * 生成 MQTT Client ID
  267 + *
  268 + * @return 生成的 MQTT Client ID
  269 + */
  270 + private String generateMqttClientId() {
  271 + return IdUtil.fastSimpleUUID();
  272 + }
  273 +
  274 + /**
  275 + * 生成 MQTT Username
  276 + *
  277 + * @param deviceName 设备名称
  278 + * @param productKey 产品 Key
  279 + * @return 生成的 MQTT Username
  280 + */
  281 + private String generateMqttUsername(String deviceName, String productKey) {
  282 + return deviceName + "&" + productKey;
  283 + }
  284 +
  285 + /**
  286 + * 生成 MQTT Password
  287 + *
  288 + * @return 生成的 MQTT Password
  289 + */
  290 + private String generateMqttPassword() {
  291 + return RandomUtil.randomString(32);
  292 + }
  293 +
  294 + @Override
  295 + @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
  296 + public IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport) {
  297 + // 1. 参数校验
  298 + if (CollUtil.isEmpty(importDevices)) {
  299 + throw exception(DEVICE_IMPORT_LIST_IS_EMPTY);
  300 + }
  301 +
  302 + // 2. 遍历,逐个创建 or 更新
  303 + IotDeviceImportRespVO respVO = IotDeviceImportRespVO.builder().createDeviceNames(new ArrayList<>())
  304 + .updateDeviceNames(new ArrayList<>()).failureDeviceNames(new LinkedHashMap<>()).build();
  305 + importDevices.forEach(importDevice -> {
  306 + try {
  307 + // 2.1.1 校验字段是否符合要求
  308 + try {
  309 + ValidationUtils.validate(importDevice);
  310 + } catch (ConstraintViolationException ex){
  311 + respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());
  312 + return;
  313 + }
  314 + // 2.1.2 校验产品是否存在
  315 + IotProductDO product = productService.validateProductExists(importDevice.getProductKey());
  316 + // 2.1.3 校验父设备是否存在
  317 + Long gatewayId = null;
  318 + if (StrUtil.isNotEmpty(importDevice.getParentDeviceName())) {
  319 + IotDeviceDO gatewayDevice = deviceMapper.selectByDeviceName(importDevice.getParentDeviceName());
  320 + if (gatewayDevice == null) {
  321 + throw exception(DEVICE_GATEWAY_NOT_EXISTS);
  322 + }
  323 + if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {
  324 + throw exception(DEVICE_NOT_GATEWAY);
  325 + }
  326 + gatewayId = gatewayDevice.getId();
  327 + }
  328 + // 2.1.4 校验设备分组是否存在
  329 + Set<Long> groupIds = new HashSet<>();
  330 + if (StrUtil.isNotEmpty(importDevice.getGroupNames())) {
  331 + String[] groupNames = importDevice.getGroupNames().split(",");
  332 + for (String groupName : groupNames) {
  333 + IotDeviceGroupDO group = deviceGroupService.getDeviceGroupByName(groupName);
  334 + if (group == null) {
  335 + throw exception(DEVICE_GROUP_NOT_EXISTS);
  336 + }
  337 + groupIds.add(group.getId());
  338 + }
  339 + }
  340 +
  341 + // 2.2.1 判断如果不存在,在进行插入
  342 + IotDeviceDO existDevice = deviceMapper.selectByDeviceName(importDevice.getDeviceName());
  343 + if (existDevice == null) {
  344 + createDevice(new IotDeviceSaveReqVO()
  345 + .setDeviceName(importDevice.getDeviceName()).setDeviceKey(generateDeviceKey())
  346 + .setProductId(product.getId()).setGatewayId(gatewayId).setGroupIds(groupIds));
  347 + respVO.getCreateDeviceNames().add(importDevice.getDeviceName());
  348 + return;
  349 + }
  350 + // 2.2.2 如果存在,判断是否允许更新
  351 + if (updateSupport) {
  352 + throw exception(DEVICE_KEY_EXISTS);
  353 + }
  354 + updateDevice(new IotDeviceSaveReqVO().setId(existDevice.getId())
  355 + .setGatewayId(gatewayId).setGroupIds(groupIds));
  356 + respVO.getUpdateDeviceNames().add(importDevice.getDeviceName());
  357 + } catch (ServiceException ex) {
  358 + respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());
  359 + }
  360 + });
  361 + return respVO;
  362 + }
  363 +
257 364 }
258 365 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininfo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  7 +import jakarta.validation.Valid;
  8 +import org.springframework.web.multipart.MultipartFile;
  9 +
  10 +import java.io.InputStream;
  11 +
  12 +/**
  13 + * IoT 插件信息 Service 接口
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public interface PluginInfoService {
  18 +
  19 + /**
  20 + * 创建IoT 插件信息
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createPluginInfo(@Valid PluginInfoSaveReqVO createReqVO);
  26 +
  27 + /**
  28 + * 更新IoT 插件信息
  29 + *
  30 + * @param updateReqVO 更新信息
  31 + */
  32 + void updatePluginInfo(@Valid PluginInfoSaveReqVO updateReqVO);
  33 +
  34 + /**
  35 + * 删除IoT 插件信息
  36 + *
  37 + * @param id 编号
  38 + */
  39 + void deletePluginInfo(Long id);
  40 +
  41 + /**
  42 + * 获得IoT 插件信息
  43 + *
  44 + * @param id 编号
  45 + * @return IoT 插件信息
  46 + */
  47 + PluginInfoDO getPluginInfo(Long id);
  48 +
  49 + /**
  50 + * 获得IoT 插件信息分页
  51 + *
  52 + * @param pageReqVO 分页查询
  53 + * @return IoT 插件信息分页
  54 + */
  55 + PageResult<PluginInfoDO> getPluginInfoPage(PluginInfoPageReqVO pageReqVO);
  56 +
  57 + /**
  58 + * 上传插件的 JAR 包
  59 + *
  60 + * @param id 插件id
  61 + * @param file 文件
  62 + */
  63 + void uploadJar(Long id, MultipartFile file);
  64 +
  65 + /**
  66 + * 更新插件的状态
  67 + *
  68 + * @param id 插件id
  69 + * @param status 状态
  70 + */
  71 + void updatePluginStatus(Long id, Integer status);
  72 +}
0 73 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininfo;
  2 +
  3 +import cn.hutool.core.io.IoUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.infra.api.file.FileApi;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  10 +import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper;
  11 +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
  12 +import jakarta.annotation.PostConstruct;
  13 +import jakarta.annotation.Resource;
  14 +import lombok.SneakyThrows;
  15 +import lombok.extern.slf4j.Slf4j;
  16 +import org.pf4j.PluginDescriptor;
  17 +import org.pf4j.PluginState;
  18 +import org.pf4j.PluginWrapper;
  19 +import org.pf4j.spring.SpringPluginManager;
  20 +import org.springframework.stereotype.Service;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.multipart.MultipartFile;
  23 +
  24 +import java.nio.file.Path;
  25 +import java.util.concurrent.Executors;
  26 +import java.util.concurrent.TimeUnit;
  27 +
  28 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
  30 +
  31 +/**
  32 + * IoT 插件信息 Service 实现类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Service
  37 +@Validated
  38 +@Slf4j
  39 +public class PluginInfoServiceImpl implements PluginInfoService {
  40 +
  41 + @Resource
  42 + private PluginInfoMapper pluginInfoMapper;
  43 +
  44 + @Resource
  45 + private SpringPluginManager pluginManager;
  46 +
  47 + @Resource
  48 + private FileApi fileApi;
  49 +
  50 + @Override
  51 + public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) {
  52 + // 插入
  53 + PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class);
  54 + pluginInfoMapper.insert(pluginInfo);
  55 + // 返回
  56 + return pluginInfo.getId();
  57 + }
  58 +
  59 + @Override
  60 + public void updatePluginInfo(PluginInfoSaveReqVO updateReqVO) {
  61 + // 校验存在
  62 + validatePluginInfoExists(updateReqVO.getId());
  63 + // 更新
  64 + PluginInfoDO updateObj = BeanUtils.toBean(updateReqVO, PluginInfoDO.class);
  65 + pluginInfoMapper.updateById(updateObj);
  66 + }
  67 +
  68 + @Override
  69 + public void deletePluginInfo(Long id) {
  70 + // 校验存在
  71 + PluginInfoDO pluginInfoDO = validatePluginInfoExists(id);
  72 +
  73 + // 停止插件
  74 + if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
  75 + throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING);
  76 + }
  77 +
  78 + // 卸载插件
  79 + PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginId());
  80 + if (plugin != null) {
  81 + // 查询插件是否是启动状态
  82 + if (plugin.getPluginState().equals(PluginState.STARTED)) {
  83 + // 停止插件
  84 + pluginManager.stopPlugin(plugin.getPluginId());
  85 + }
  86 + // 卸载插件
  87 + pluginManager.unloadPlugin(plugin.getPluginId());
  88 + }
  89 +
  90 + // 删除
  91 + pluginInfoMapper.deleteById(id);
  92 + }
  93 +
  94 + private PluginInfoDO validatePluginInfoExists(Long id) {
  95 + PluginInfoDO pluginInfo = pluginInfoMapper.selectById(id);
  96 + if (pluginInfo == null) {
  97 + throw exception(PLUGIN_INFO_NOT_EXISTS);
  98 + }
  99 + return pluginInfo;
  100 + }
  101 +
  102 + @Override
  103 + public PluginInfoDO getPluginInfo(Long id) {
  104 + return pluginInfoMapper.selectById(id);
  105 + }
  106 +
  107 + @Override
  108 + public PageResult<PluginInfoDO> getPluginInfoPage(PluginInfoPageReqVO pageReqVO) {
  109 + return pluginInfoMapper.selectPage(pageReqVO);
  110 + }
  111 +
  112 + @Override
  113 + public void uploadJar(Long id, MultipartFile file) {
  114 + // 1. 校验存在
  115 + PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
  116 +
  117 + // 2. 判断文件名称与插件 ID 是否匹配
  118 + String pluginId = pluginInfoDo.getPluginId();
  119 +
  120 + // 3. 停止卸载旧的插件
  121 + // 3.1. 获取插件信息
  122 + PluginWrapper plugin = pluginManager.getPlugin(pluginId);
  123 + if (plugin != null) {
  124 + // 3.2. 如果插件状态是启动的,停止插件
  125 + if (plugin.getPluginState().equals(PluginState.STARTED)) {
  126 + pluginManager.stopPlugin(pluginId);
  127 + }
  128 + // 3.3. 卸载插件
  129 + pluginManager.unloadPlugin(pluginId);
  130 + }
  131 +
  132 + // 4. 上传插件
  133 + String pluginIdNew;
  134 + try {
  135 + String path = fileApi.createFile(IoUtil.readBytes(file.getInputStream()));
  136 + Path pluginPath = Path.of(path);
  137 + pluginIdNew = pluginManager.loadPlugin(pluginPath);
  138 + } catch (Exception e) {
  139 + throw exception(PLUGIN_INSTALL_FAILED);
  140 + }
  141 +
  142 + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew);
  143 + if (pluginWrapper == null) {
  144 + throw exception(PLUGIN_INSTALL_FAILED);
  145 + }
  146 +
  147 +
  148 + // 5. 读取配置文件和脚本
  149 + String configJson = "";
  150 + String script = "";
  151 +
  152 + pluginInfoDo.setPluginId(pluginIdNew);
  153 + pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus());
  154 + pluginInfoDo.setFile(file.getOriginalFilename());
  155 + pluginInfoDo.setConfigSchema(configJson);
  156 + pluginInfoDo.setScript(script);
  157 +
  158 + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
  159 + pluginInfoDo.setVersion(pluginDescriptor.getVersion());
  160 + pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription());
  161 + pluginInfoMapper.updateById(pluginInfoDo);
  162 +
  163 +
  164 + // 5. 读取配置文件和脚本
  165 +// String configJson = "";
  166 + // String script = "";
  167 +// try (JarFile jarFile = new JarFile(pluginInfoUpdate.getPluginPath())) {
  168 +// // 5.1 获取config文件在jar包中的路径
  169 +// String configFile = "classes/config.json";
  170 +// JarEntry configEntry = jarFile.getJarEntry(configFile);
  171 +//
  172 +// if (configEntry != null) {
  173 +// // 5.2 读取配置文件
  174 +// configJson = IoUtil.read(jarFile.getInputStream(configEntry), Charset.defaultCharset());
  175 +// log.info("configJson:{}", configJson);
  176 +// }
  177 +//
  178 +// // 5.3 读取script.js脚本
  179 +// String scriptFile = "classes/script.js";
  180 +// JarEntry scriptEntity = jarFile.getJarEntry(scriptFile);
  181 +// if (scriptEntity != null) {
  182 +// // 5.4 读取脚本文件
  183 +// script = IoUtil.read(jarFile.getInputStream(scriptEntity), Charset.defaultCharset());
  184 +// log.info("script:{}", script);
  185 +// }
  186 +// } catch (Exception e) {
  187 +// throw exception(PLUGIN_INSTALL_FAILED);
  188 +// }
  189 +
  190 +
  191 +// PluginState pluginState = pluginInfoUpdate.getPluginState();
  192 +// if (pluginState == PluginState.STARTED) {
  193 +// pluginInfoDo.setStatus(IotPluginStatusEnum.RUNNING.getStatus());
  194 +// }
  195 +// pluginInfoDo.setPluginId(pluginInfoUpdate.getPluginId());
  196 +// pluginInfoDo.setFile(file.getOriginalFilename());
  197 +// pluginInfoDo.setConfigSchema(configJson);
  198 +// pluginInfoDo.setScript(script);
  199 +//
  200 +// PluginDescriptor pluginDescriptor = pluginInfoUpdate.getPluginDescriptor();
  201 +// pluginInfoDo.setVersion(pluginDescriptor.getPluginVersion());
  202 +// pluginInfoDo.setDescription(pluginDescriptor.getDescription());
  203 +// pluginInfoMapper.updateById(pluginInfoDo);
  204 + }
  205 +
  206 + @Override
  207 + public void updatePluginStatus(Long id, Integer status) {
  208 + // 1. 校验存在
  209 + PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
  210 +
  211 + // 插件状态无效
  212 + if (!IotPluginStatusEnum.contains(status)) {
  213 + throw exception(PLUGIN_STATUS_INVALID);
  214 + }
  215 +
  216 + // 插件包为空
  217 +// String pluginId = pluginInfoDo.getPluginId();
  218 +// if (StrUtil.isBlank(pluginId)) {
  219 +// throw exception(PLUGIN_INFO_NOT_EXISTS);
  220 +// }
  221 +// com.gitee.starblues.core.PluginInfo pluginInfo = pluginOperator.getPluginInfo(pluginId);
  222 +// if (pluginInfo != null) {
  223 +// if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.RUNNING.getStatus()) && pluginInfo.getPluginState() != PluginState.STARTED) {
  224 +// // 启动插件
  225 +// pluginOperator.start(pluginId);
  226 +// } else if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.STOPPED.getStatus()) && pluginInfo.getPluginState() == PluginState.STARTED) {
  227 +// // 停止插件
  228 +// pluginOperator.stop(pluginId);
  229 +// }
  230 +// } else {
  231 +// // 已经停止,未获取到插件
  232 +// if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) {
  233 +// throw exception(PLUGIN_STATUS_INVALID);
  234 +// }
  235 +// }
  236 + pluginInfoDo.setStatus(status);
  237 + pluginInfoMapper.updateById(pluginInfoDo);
  238 + }
  239 +
  240 + @PostConstruct
  241 + public void init() {
  242 + Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS);
  243 + }
  244 +
  245 + @SneakyThrows
  246 + private void startPlugins() {
  247 +// while (!pluginOperator.inited()) {
  248 +// Thread.sleep(1000L);
  249 +// }
  250 +
  251 + for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) {
  252 + if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
  253 + continue;
  254 + }
  255 + log.info("start plugin:{}", pluginInfoDO.getPluginId());
  256 + try {
  257 +// com.gitee.starblues.core.PluginInfo plugin = pluginOperator.getPluginInfo(pluginInfoDO.getPluginId());
  258 +// if (plugin != null) {
  259 +// pluginOperator.start(plugin.getPluginId());
  260 +// }
  261 + } catch (Exception e) {
  262 + log.error("start plugin error", e);
  263 + }
  264 + }
  265 + }
  266 +
  267 +}
0 268 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininstance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * IoT 插件实例 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface PluginInstanceService {
  15 +
  16 + /**
  17 + * 创建IoT 插件实例
  18 + *
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
  21 + */
  22 + Long createPluginInstance(@Valid PluginInstanceSaveReqVO createReqVO);
  23 +
  24 + /**
  25 + * 更新IoT 插件实例
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updatePluginInstance(@Valid PluginInstanceSaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除IoT 插件实例
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deletePluginInstance(Long id);
  37 +
  38 + /**
  39 + * 获得IoT 插件实例
  40 + *
  41 + * @param id 编号
  42 + * @return IoT 插件实例
  43 + */
  44 + PluginInstanceDO getPluginInstance(Long id);
  45 +
  46 + /**
  47 + * 获得IoT 插件实例分页
  48 + *
  49 + * @param pageReqVO 分页查询
  50 + * @return IoT 插件实例分页
  51 + */
  52 + PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO);
  53 +
  54 +}
0 55 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininstance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
  8 +import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  14 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTANCE_NOT_EXISTS;
  15 +
  16 +/**
  17 + * IoT 插件实例 Service 实现类
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Service
  22 +@Validated
  23 +public class PluginInstanceServiceImpl implements PluginInstanceService {
  24 +
  25 + @Resource
  26 + private PluginInstanceMapper pluginInstanceMapper;
  27 +
  28 + @Override
  29 + public Long createPluginInstance(PluginInstanceSaveReqVO createReqVO) {
  30 + // 插入
  31 + PluginInstanceDO pluginInstance = BeanUtils.toBean(createReqVO, PluginInstanceDO.class);
  32 + pluginInstanceMapper.insert(pluginInstance);
  33 + // 返回
  34 + return pluginInstance.getId();
  35 + }
  36 +
  37 + @Override
  38 + public void updatePluginInstance(PluginInstanceSaveReqVO updateReqVO) {
  39 + // 校验存在
  40 + validatePluginInstanceExists(updateReqVO.getId());
  41 + // 更新
  42 + PluginInstanceDO updateObj = BeanUtils.toBean(updateReqVO, PluginInstanceDO.class);
  43 + pluginInstanceMapper.updateById(updateObj);
  44 + }
  45 +
  46 + @Override
  47 + public void deletePluginInstance(Long id) {
  48 + // 校验存在
  49 + validatePluginInstanceExists(id);
  50 + // 删除
  51 + pluginInstanceMapper.deleteById(id);
  52 + }
  53 +
  54 + private void validatePluginInstanceExists(Long id) {
  55 + if (pluginInstanceMapper.selectById(id) == null) {
  56 + throw exception(PLUGIN_INSTANCE_NOT_EXISTS);
  57 + }
  58 + }
  59 +
  60 + @Override
  61 + public PluginInstanceDO getPluginInstance(Long id) {
  62 + return pluginInstanceMapper.selectById(id);
  63 + }
  64 +
  65 + @Override
  66 + public PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO) {
  67 + return pluginInstanceMapper.selectPage(pageReqVO);
  68 + }
  69 +
  70 +}
0 71 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
... ... @@ -46,6 +46,22 @@ public interface IotProductService {
46 46 IotProductDO getProduct(Long id);
47 47  
48 48 /**
  49 + * 校验产品存在
  50 + *
  51 + * @param id 编号
  52 + * @return 产品
  53 + */
  54 + IotProductDO validateProductExists(Long id);
  55 +
  56 + /**
  57 + * 校验产品存在
  58 + *
  59 + * @param productKey 产品 key
  60 + * @return 产品
  61 + */
  62 + IotProductDO validateProductExists(String productKey);
  63 +
  64 + /**
49 65 * 获得产品分页
50 66 *
51 67 * @param pageReqVO 分页查询
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
... ... @@ -71,16 +71,26 @@ public class IotProductServiceImpl implements IotProductService {
71 71 productMapper.deleteById(id);
72 72 }
73 73  
74   - private IotProductDO validateProductExists(Long id) {
75   - IotProductDO iotProductDO = productMapper.selectById(id);
76   - if (iotProductDO == null) {
  74 + @Override
  75 + public IotProductDO validateProductExists(Long id) {
  76 + IotProductDO product = productMapper.selectById(id);
  77 + if (product == null) {
  78 + throw exception(PRODUCT_NOT_EXISTS);
  79 + }
  80 + return product;
  81 + }
  82 +
  83 + @Override
  84 + public IotProductDO validateProductExists(String productKey) {
  85 + IotProductDO product = productMapper.selectByProductKey(productKey);
  86 + if (product == null) {
77 87 throw exception(PRODUCT_NOT_EXISTS);
78 88 }
79   - return iotProductDO;
  89 + return product;
80 90 }
81 91  
82   - private void validateProductStatus(IotProductDO iotProductDO) {
83   - if (Objects.equals(iotProductDO.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) {
  92 + private void validateProductStatus(IotProductDO product) {
  93 + if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) {
84 94 throw exception(PRODUCT_STATUS_NOT_DELETE);
85 95 }
86 96 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 13 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 13 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-plugin/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <artifactId>yudao-module-iot</artifactId>
  7 + <groupId>cn.iocoder.boot</groupId>
  8 + <version>${revision}</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <artifactId>yudao-module-iot-plugin</artifactId>
  12 + <packaging>jar</packaging>
  13 +
  14 + <name>${project.artifactId}</name>
  15 + <description>
  16 + 物联网 模块 - 插件
  17 + </description>
  18 +
  19 + <dependencies>
  20 + <dependency>
  21 + <groupId>cn.iocoder.boot</groupId>
  22 + <artifactId>yudao-common</artifactId>
  23 + </dependency>
  24 +
  25 + <dependency>
  26 + <groupId>org.pf4j</groupId>
  27 + <artifactId>pf4j-spring</artifactId>
  28 + </dependency>
  29 + </dependencies>
  30 +
  31 +</project>
... ...
yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java 0 → 100644
  1 +/**
  2 + * 占位
  3 + *
  4 + * TODO 芋艿:后续删除
  5 + */
  6 +package cn.iocoder.yudao.module.iot.plugin;
... ...