Commit c5894765b50f6ed7fac4bfa8a558bfd5d3b8a3e0
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
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<IotDeviceDO> { |
| 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<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<IotProductDO> { |
| 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
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> | ... | ... |