Merge remote-tracking branch 'yudao/feature/iot' into iot
This commit is contained in:
commit
c5894765b5
|
@ -66,6 +66,7 @@
|
|||
<ip2region.version>2.7.0</ip2region.version>
|
||||
<bizlog-sdk.version>3.0.6</bizlog-sdk.version>
|
||||
<mqtt.version>1.2.5</mqtt.version>
|
||||
<pf4j-spring.version>0.9.0</pf4j-spring.version>
|
||||
<!-- 三方云服务相关 -->
|
||||
<okio.version>3.5.0</okio.version>
|
||||
<okhttp3.version>4.11.0</okhttp3.version>
|
||||
|
@ -605,6 +606,13 @@
|
|||
<version>${mqtt.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- PF4J -->
|
||||
<dependency>
|
||||
<groupId>org.pf4j</groupId>
|
||||
<artifactId>pf4j-spring</artifactId>
|
||||
<version>${pf4j-spring.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package cn.iocoder.yudao.framework.mybatis.core.type;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Set<Long> 的类型转换器实现类,对应数据库的 varchar 类型
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@MappedJdbcTypes(JdbcType.VARCHAR)
|
||||
@MappedTypes(List.class)
|
||||
public class LongSetTypeHandler implements TypeHandler<Set<Long>> {
|
||||
|
||||
private static final String COMMA = ",";
|
||||
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, Set<Long> strings, JdbcType jdbcType) throws SQLException {
|
||||
// 设置占位符
|
||||
ps.setString(i, CollUtil.join(strings, COMMA));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
String value = rs.getString(columnName);
|
||||
return getResult(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
String value = rs.getString(columnIndex);
|
||||
return getResult(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
String value = cs.getString(columnIndex);
|
||||
return getResult(value);
|
||||
}
|
||||
|
||||
private Set<Long> getResult(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return StrUtils.splitToLongSet(value, COMMA);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
<modules>
|
||||
<module>yudao-module-iot-api</module>
|
||||
<module>yudao-module-iot-biz</module>
|
||||
<module>yudao-module-iot-plugin</module>
|
||||
</modules>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -13,4 +13,7 @@ public class DictTypeConstants {
|
|||
public static final String PROTOCOL_TYPE = "iot_protocol_type";
|
||||
public static final String DATA_FORMAT = "iot_data_format";
|
||||
public static final String VALIDATE_TYPE = "iot_validate_type";
|
||||
|
||||
public static final String DEVICE_STATUS = "iot_device_status";
|
||||
|
||||
}
|
||||
|
|
|
@ -26,11 +26,26 @@ public interface ErrorCodeConstants {
|
|||
ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在");
|
||||
ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, "设备名称在同一产品下必须唯一");
|
||||
ErrorCode DEVICE_HAS_CHILDREN = new ErrorCode(1_050_003_002, "有子设备,不允许删除");
|
||||
ErrorCode DEVICE_NAME_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_003, "设备名称不能修改");
|
||||
ErrorCode DEVICE_PRODUCT_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_004, "产品不能修改");
|
||||
ErrorCode DEVICE_INVALID_DEVICE_STATUS = new ErrorCode(1_050_003_005, "无效的设备状态");
|
||||
ErrorCode DEVICE_KEY_EXISTS = new ErrorCode(1_050_003_003, "设备标识已经存在");
|
||||
ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在");
|
||||
ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备");
|
||||
ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!");
|
||||
|
||||
// ========== 产品分类 1-050-004-000 ==========
|
||||
ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在");
|
||||
|
||||
// ========== 设备分组 1-050-005-000 ==========
|
||||
ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在");
|
||||
ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除");
|
||||
|
||||
// ========== 插件信息 1-050-006-000 ==========
|
||||
ErrorCode PLUGIN_INFO_NOT_EXISTS = new ErrorCode(1_050_006_000, "插件信息不存在");
|
||||
ErrorCode PLUGIN_INSTALL_FAILED = new ErrorCode(1_050_006_001, "插件安装失败");
|
||||
ErrorCode PLUGIN_INSTALL_FAILED_FILE_NAME_NOT_MATCH = new ErrorCode(1_050_006_002, "插件安装失败,文件名与原插件id不匹配");
|
||||
ErrorCode PLUGIN_INFO_DELETE_FAILED_RUNNING = new ErrorCode(1_050_006_003, "请先停止插件");
|
||||
ErrorCode PLUGIN_STATUS_INVALID = new ErrorCode(1_050_006_004, "插件状态无效");
|
||||
|
||||
// ========== 插件实例 1-050-007-000 ==========
|
||||
ErrorCode PLUGIN_INSTANCE_NOT_EXISTS = new ErrorCode(1_050_007_000, "插件实例不存在");
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package cn.iocoder.yudao.module.iot.enums.plugin;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* IoT 部署方式枚举
|
||||
*
|
||||
* @author haohao
|
||||
*/
|
||||
@Getter
|
||||
public enum IotPluginDeployTypeEnum implements IntArrayValuable {
|
||||
|
||||
UPLOAD(0, "上传 jar"), // TODO @haohao:UPLOAD 和 ALONE 感觉有点冲突,前者是部署方式,后者是运行方式。这个后续再讨论下哈
|
||||
ALONE(1, "独立运行");
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginDeployTypeEnum::getDeployType).toArray();
|
||||
|
||||
/**
|
||||
* 部署方式
|
||||
*/
|
||||
private final Integer deployType;
|
||||
|
||||
/**
|
||||
* 部署方式名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
IotPluginDeployTypeEnum(Integer deployType, String name) {
|
||||
this.deployType = deployType;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static IotPluginDeployTypeEnum fromDeployType(Integer deployType) {
|
||||
for (IotPluginDeployTypeEnum value : values()) {
|
||||
if (value.getDeployType().equals(deployType)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isValidDeployType(Integer deployType) {
|
||||
return fromDeployType(deployType) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package cn.iocoder.yudao.module.iot.enums.plugin;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* IoT 插件状态枚举
|
||||
*
|
||||
* @author haohao
|
||||
*/
|
||||
@Getter
|
||||
public enum IotPluginStatusEnum implements IntArrayValuable {
|
||||
|
||||
STOPPED(0, "停止"),
|
||||
RUNNING(1, "运行");
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginStatusEnum::getStatus).toArray();
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final Integer status;
|
||||
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
IotPluginStatusEnum(Integer status, String name) {
|
||||
this.status = status;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static IotPluginStatusEnum fromState(Integer state) {
|
||||
for (IotPluginStatusEnum value : values()) {
|
||||
if (value.getStatus().equals(state)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static boolean isValidState(Integer state) {
|
||||
return fromState(state) != null;
|
||||
}
|
||||
|
||||
public static boolean contains(Integer status) {
|
||||
return Arrays.stream(values()).anyMatch(e -> e.getStatus().equals(status));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package cn.iocoder.yudao.module.iot.enums.plugin;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* IoT 插件类型枚举
|
||||
*
|
||||
* @author haohao
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum IotPluginTypeEnum implements IntArrayValuable {
|
||||
|
||||
NORMAL(0, "普通插件"),
|
||||
DEVICE(1, "设备插件");
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginTypeEnum::getType).toArray();
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer type;
|
||||
|
||||
/**
|
||||
* 类型名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
// TODO @haohao:可以使用 hutool 简化
|
||||
public static IotPluginTypeEnum fromType(Integer type) {
|
||||
for (IotPluginTypeEnum value : values()) {
|
||||
if (value.getType().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isValidType(Integer type) {
|
||||
return fromType(type) != null;
|
||||
}
|
||||
|
||||
}
|
|
@ -36,4 +36,14 @@ public enum IotProductDeviceTypeEnum implements IntArrayValuable {
|
|||
return ARRAYS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是网关
|
||||
*
|
||||
* @param type 类型
|
||||
* @return 是否是网关
|
||||
*/
|
||||
public static boolean isGateway(Integer type) {
|
||||
return GATEWAY.getType().equals(type);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,6 +69,12 @@
|
|||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- PF4J -->
|
||||
<dependency>
|
||||
<groupId>org.pf4j</groupId>
|
||||
<artifactId>pf4j-spring</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,24 +1,33 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceRespVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - IoT 设备")
|
||||
@RestController
|
||||
|
@ -52,8 +61,16 @@ public class IotDeviceController {
|
|||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-group")
|
||||
@Operation(summary = "更新设备分组")
|
||||
@PreAuthorize("@ss.hasPermission('iot:device:update')")
|
||||
public CommonResult<Boolean> updateDeviceGroup(@Valid @RequestBody IotDeviceUpdateGroupReqVO updateReqVO) {
|
||||
deviceService.updateDeviceGroup(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除设备")
|
||||
@Operation(summary = "删除单个设备")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('iot:device:delete')")
|
||||
public CommonResult<Boolean> deleteDevice(@RequestParam("id") Long id) {
|
||||
|
@ -61,6 +78,15 @@ public class IotDeviceController {
|
|||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@Operation(summary = "删除多个设备")
|
||||
@Parameter(name = "ids", description = "编号数组", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('iot:device:delete')")
|
||||
public CommonResult<Boolean> deleteDeviceList(@RequestParam("ids") Collection<Long> ids) {
|
||||
deviceService.deleteDeviceList(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得设备")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
|
@ -78,6 +104,19 @@ public class IotDeviceController {
|
|||
return success(BeanUtils.toBean(pageResult, IotDeviceRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出设备 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('iot:device:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportDeviceExcel(@Valid IotDevicePageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
CommonResult<PageResult<IotDeviceRespVO>> result = getDevicePage(exportReqVO);
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "设备.xls", "数据", IotDeviceRespVO.class,
|
||||
result.getData().getList());
|
||||
}
|
||||
|
||||
@GetMapping("/count")
|
||||
@Operation(summary = "获得设备数量")
|
||||
@Parameter(name = "productId", description = "产品编号", example = "1")
|
||||
|
@ -86,4 +125,38 @@ public class IotDeviceController {
|
|||
return success(deviceService.getDeviceCountByProductId(productId));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获取设备的精简信息列表", description = "主要用于前端的下拉选项")
|
||||
@Parameter(name = "deviceType", description = "设备类型", example = "1")
|
||||
public CommonResult<List<IotDeviceRespVO>> getSimpleDeviceList(
|
||||
@RequestParam(value = "deviceType", required = false) Integer deviceType) {
|
||||
List<IotDeviceDO> list = deviceService.getDeviceList(deviceType);
|
||||
return success(convertList(list, device -> // 只返回 id、name 字段
|
||||
new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName())));
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "导入设备")
|
||||
@PreAuthorize("@ss.hasPermission('iot:device:import')")
|
||||
public CommonResult<IotDeviceImportRespVO> importDevice(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport)
|
||||
throws Exception {
|
||||
List<IotDeviceImportExcelVO> list = ExcelUtils.read(file, IotDeviceImportExcelVO.class);
|
||||
return success(deviceService.importDevice(list, updateSupport));
|
||||
}
|
||||
|
||||
@GetMapping("/get-import-template")
|
||||
@Operation(summary = "获得导入设备模板")
|
||||
public void importTemplate(HttpServletResponse response) throws IOException {
|
||||
// 手动创建导出 demo
|
||||
List<IotDeviceImportExcelVO> list = Arrays.asList(
|
||||
IotDeviceImportExcelVO.builder().deviceName("温度传感器001").parentDeviceName("gateway110")
|
||||
.productKey("1de24640dfe").groupNames("灰度分组,生产分组").build(),
|
||||
IotDeviceImportExcelVO.builder().deviceName("biubiu")
|
||||
.productKey("YzvHxd4r67sT4s2B").groupNames("").build());
|
||||
// 输出
|
||||
ExcelUtils.write(response, "设备导入模板.xls", "数据", IotDeviceImportExcelVO.class, list);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupRespVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceGroupService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - IoT 设备分组")
|
||||
@RestController
|
||||
@RequestMapping("/iot/device-group")
|
||||
@Validated
|
||||
public class IotDeviceGroupController {
|
||||
|
||||
@Resource
|
||||
private IotDeviceGroupService deviceGroupService;
|
||||
@Resource
|
||||
private IotDeviceService deviceService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建设备分组")
|
||||
@PreAuthorize("@ss.hasPermission('iot:device-group:create')")
|
||||
public CommonResult<Long> createDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO createReqVO) {
|
||||
return success(deviceGroupService.createDeviceGroup(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新设备分组")
|
||||
@PreAuthorize("@ss.hasPermission('iot:device-group:update')")
|
||||
public CommonResult<Boolean> updateDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO updateReqVO) {
|
||||
deviceGroupService.updateDeviceGroup(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除设备分组")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('iot:device-group:delete')")
|
||||
public CommonResult<Boolean> deleteDeviceGroup(@RequestParam("id") Long id) {
|
||||
deviceGroupService.deleteDeviceGroup(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得设备分组")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('iot:device-group:query')")
|
||||
public CommonResult<IotDeviceGroupRespVO> getDeviceGroup(@RequestParam("id") Long id) {
|
||||
IotDeviceGroupDO deviceGroup = deviceGroupService.getDeviceGroup(id);
|
||||
return success(BeanUtils.toBean(deviceGroup, IotDeviceGroupRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得设备分组分页")
|
||||
@PreAuthorize("@ss.hasPermission('iot:device-group:query')")
|
||||
public CommonResult<PageResult<IotDeviceGroupRespVO>> getDeviceGroupPage(@Valid IotDeviceGroupPageReqVO pageReqVO) {
|
||||
PageResult<IotDeviceGroupDO> pageResult = deviceGroupService.getDeviceGroupPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, IotDeviceGroupRespVO.class,
|
||||
group -> group.setDeviceCount(deviceService.getDeviceCountByGroupId(group.getId()))));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获取设备分组的精简信息列表", description = "只包含被开启的分组,主要用于前端的下拉选项")
|
||||
public CommonResult<List<IotDeviceGroupRespVO>> getSimpleDeviceGroupList() {
|
||||
List<IotDeviceGroupDO> list = deviceGroupService.getDeviceGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
return success(convertList(list, group -> // 只返回 id、name 字段
|
||||
new IotDeviceGroupRespVO().setId(group.getId()).setName(group.getName())));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 设备 Excel 导入 VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = false) // 设置 chain = false,避免设备导入有问题
|
||||
public class IotDeviceImportExcelVO {
|
||||
|
||||
@ExcelProperty("设备名称")
|
||||
@NotEmpty(message = "设备名称不能为空")
|
||||
private String deviceName;
|
||||
|
||||
@ExcelProperty("父设备名称")
|
||||
@Schema(description = "父设备名称", example = "网关001")
|
||||
private String parentDeviceName;
|
||||
|
||||
@ExcelProperty("产品标识")
|
||||
@NotEmpty(message = "产品标识不能为空")
|
||||
private String productKey;
|
||||
|
||||
@ExcelProperty("设备分组")
|
||||
private String groupNames;
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备导入 Response VO")
|
||||
@Data
|
||||
@Builder
|
||||
public class IotDeviceImportRespVO {
|
||||
|
||||
@Schema(description = "创建成功的设备名称数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> createDeviceNames;
|
||||
|
||||
@Schema(description = "更新成功的设备名称数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> updateDeviceNames;
|
||||
|
||||
@Schema(description = "导入失败的设备集合,key为设备名称,value为失败原因", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Map<String, String> failureDeviceNames;
|
||||
}
|
|
@ -8,11 +8,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备分页 Request VO")
|
||||
@Data
|
||||
|
@ -20,11 +15,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
|
|||
@ToString(callSuper = true)
|
||||
public class IotDevicePageReqVO extends PageParam {
|
||||
|
||||
// TODO @芋艿:需要去掉一些多余的字段;
|
||||
|
||||
@Schema(description = "设备唯一标识符", example = "24602")
|
||||
private String deviceKey;
|
||||
|
||||
@Schema(description = "设备名称", example = "王五")
|
||||
private String deviceName;
|
||||
|
||||
|
@ -34,53 +24,15 @@ public class IotDevicePageReqVO extends PageParam {
|
|||
@Schema(description = "产品编号", example = "26202")
|
||||
private Long productId;
|
||||
|
||||
@Schema(description = "产品标识")
|
||||
private String productKey;
|
||||
|
||||
@Schema(description = "设备类型", example = "1")
|
||||
@InEnum(IotProductDeviceTypeEnum.class)
|
||||
private Integer deviceType;
|
||||
|
||||
@Schema(description = "网关设备 ID", example = "16380")
|
||||
private Long gatewayId;
|
||||
|
||||
@Schema(description = "设备状态", example = "1")
|
||||
@InEnum(IotDeviceStatusEnum.class)
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "设备状态最后更新时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] statusLastUpdateTime;
|
||||
|
||||
@Schema(description = "最后上线时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] lastOnlineTime;
|
||||
|
||||
@Schema(description = "最后离线时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] lastOfflineTime;
|
||||
|
||||
@Schema(description = "设备激活时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] activeTime;
|
||||
|
||||
@Schema(description = "设备密钥,用于设备认证,需安全存储")
|
||||
private String deviceSecret;
|
||||
|
||||
@Schema(description = "MQTT 客户端 ID", example = "24602")
|
||||
private String mqttClientId;
|
||||
|
||||
@Schema(description = "MQTT 用户名", example = "芋艿")
|
||||
private String mqttUsername;
|
||||
|
||||
@Schema(description = "MQTT 密码")
|
||||
private String mqttPassword;
|
||||
|
||||
@Schema(description = "认证类型(如一机一密、动态注册)", example = "2")
|
||||
private String authType;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
@Schema(description = "设备分组编号", example = "1024")
|
||||
private Long groupId;
|
||||
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.DEVICE_STATUS;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备 Response VO")
|
||||
@Data
|
||||
|
@ -20,9 +25,24 @@ public class IotDeviceRespVO {
|
|||
private String deviceKey;
|
||||
|
||||
@Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
|
||||
@ExcelProperty("设备名称备")
|
||||
@ExcelProperty("设备名称")
|
||||
private String deviceName;
|
||||
|
||||
@Schema(description = "设备备注名称", example = "张三")
|
||||
@ExcelProperty("设备备注名称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "设备序列号", example = "1024")
|
||||
@ExcelProperty("设备序列号")
|
||||
private String serialNumber;
|
||||
|
||||
@Schema(description = "设备图片", example = "我是一名码农")
|
||||
@ExcelProperty("设备图片")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "设备分组编号数组", example = "1,2")
|
||||
private Set<Long> groupIds;
|
||||
|
||||
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202")
|
||||
@ExcelProperty("产品编号")
|
||||
private Long productId;
|
||||
|
@ -35,15 +55,12 @@ public class IotDeviceRespVO {
|
|||
@ExcelProperty("设备类型")
|
||||
private Integer deviceType;
|
||||
|
||||
@Schema(description = "设备备注名称", example = "张三")
|
||||
@ExcelProperty("设备备注名称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "网关设备 ID", example = "16380")
|
||||
private Long gatewayId;
|
||||
|
||||
@Schema(description = "设备状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty("设备状态")
|
||||
@ExcelProperty(value = "设备状态", converter = DictConvert.class)
|
||||
@DictFormat(DEVICE_STATUS)
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "设备状态最后更新时间")
|
||||
|
|
|
@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
|
|||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备新增/修改 Request VO")
|
||||
@Data
|
||||
public class IotDeviceSaveReqVO {
|
||||
|
@ -10,13 +12,28 @@ public class IotDeviceSaveReqVO {
|
|||
@Schema(description = "设备编号", example = "177")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "设备编号", requiredMode = Schema.RequiredMode.AUTO, example = "177")
|
||||
private String deviceKey;
|
||||
|
||||
@Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
|
||||
private String deviceName;
|
||||
|
||||
@Schema(description = "备注名称", example = "张三")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "设备序列号", example = "123456")
|
||||
private String serialNumber;
|
||||
|
||||
@Schema(description = "设备图片", example = "https://iocoder.cn/1.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "设备分组编号数组", example = "1,2")
|
||||
private Set<Long> groupIds;
|
||||
|
||||
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202")
|
||||
private Long productId;
|
||||
|
||||
@Schema(description = "网关设备 ID", example = "16380")
|
||||
private Long gatewayId;
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备更新分组 Request VO")
|
||||
@Data
|
||||
public class IotDeviceUpdateGroupReqVO {
|
||||
|
||||
@Schema(description = "设备编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
|
||||
@NotEmpty(message = "设备编号列表不能为空")
|
||||
private Set<Long> ids;
|
||||
|
||||
@Schema(description = "分组编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
|
||||
@NotEmpty(message = "分组编号列表不能为空")
|
||||
private Set<Long> groupIds;
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备分组分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class IotDeviceGroupPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "分组名字", example = "李四")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备分组 Response VO")
|
||||
@Data
|
||||
public class IotDeviceGroupRespVO {
|
||||
|
||||
@Schema(description = "分组 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3583")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "分组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "分组状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "分组描述", example = "你说的对")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long deviceCount;
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备分组新增/修改 Request VO")
|
||||
@Data
|
||||
public class IotDeviceGroupSaveReqVO {
|
||||
|
||||
@Schema(description = "分组 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3583")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "分组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||
@NotEmpty(message = "分组名字不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "分组状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "分组状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "分组描述", example = "你说的对")
|
||||
private String description;
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.plugininfo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoRespVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
|
||||
import cn.iocoder.yudao.module.iot.service.plugininfo.PluginInfoService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_IS_EMPTY;
|
||||
|
||||
@Tag(name = "管理后台 - IoT 插件信息")
|
||||
@RestController
|
||||
@RequestMapping("/iot/plugin-info")
|
||||
@Validated
|
||||
public class PluginInfoController {
|
||||
|
||||
@Resource
|
||||
private PluginInfoService pluginInfoService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建插件信息")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-info:create')")
|
||||
public CommonResult<Long> createPluginInfo(@Valid @RequestBody PluginInfoSaveReqVO createReqVO) {
|
||||
return success(pluginInfoService.createPluginInfo(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新插件信息")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-info:update')")
|
||||
public CommonResult<Boolean> updatePluginInfo(@Valid @RequestBody PluginInfoSaveReqVO updateReqVO) {
|
||||
pluginInfoService.updatePluginInfo(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除插件信息")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-info:delete')")
|
||||
public CommonResult<Boolean> deletePluginInfo(@RequestParam("id") Long id) {
|
||||
pluginInfoService.deletePluginInfo(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得插件信息")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-info:query')")
|
||||
public CommonResult<PluginInfoRespVO> getPluginInfo(@RequestParam("id") Long id) {
|
||||
PluginInfoDO pluginInfo = pluginInfoService.getPluginInfo(id);
|
||||
return success(BeanUtils.toBean(pluginInfo, PluginInfoRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得插件信息分页")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-info:query')")
|
||||
public CommonResult<PageResult<PluginInfoRespVO>> getPluginInfoPage(@Valid PluginInfoPageReqVO pageReqVO) {
|
||||
PageResult<PluginInfoDO> pageResult = pluginInfoService.getPluginInfoPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, PluginInfoRespVO.class));
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/update-jar",
|
||||
method = {RequestMethod.POST, RequestMethod.PUT}) // 解决 uni-app 不支持 Put 上传文件的问题
|
||||
@Operation(summary = "上传Jar包")
|
||||
public CommonResult<Boolean> uploadJar(
|
||||
@RequestParam("id") Long id,
|
||||
@RequestParam("jar") MultipartFile file) throws Exception {
|
||||
if (file.isEmpty()) {
|
||||
throw exception(FILE_IS_EMPTY);
|
||||
}
|
||||
pluginInfoService.uploadJar(id, file);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "修改插件状态")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-info:update')")
|
||||
public CommonResult<Boolean> updateUserStatus(@Valid @RequestBody PluginInfoSaveReqVO reqVO) {
|
||||
pluginInfoService.updatePluginStatus(reqVO.getId(), reqVO.getStatus());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum;
|
||||
import lombok.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
// TODO @haohao:只查询必要字段哈
|
||||
@Schema(description = "管理后台 - IoT 插件信息分页 Request VO")
|
||||
@Data
|
||||
public class PluginInfoPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "插件包 ID ", example = "24627")
|
||||
private String pluginId;
|
||||
|
||||
@Schema(description = "插件名称", example = "赵六")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "描述", example = "你猜")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "部署方式", example = "2")
|
||||
private Integer deployType;
|
||||
|
||||
@Schema(description = "插件包文件名")
|
||||
private String file;
|
||||
|
||||
@Schema(description = "插件版本")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "插件类型", example = "2")
|
||||
@InEnum(IotPluginTypeEnum.class)
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "设备插件协议类型")
|
||||
private String protocol;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "插件配置项描述信息")
|
||||
private String configSchema;
|
||||
|
||||
@Schema(description = "插件配置信息")
|
||||
private String config;
|
||||
|
||||
@Schema(description = "插件脚本")
|
||||
private String script;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 插件信息 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class PluginInfoRespVO {
|
||||
|
||||
@Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
|
||||
@ExcelProperty("主键 ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "插件包 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
|
||||
@ExcelProperty("插件包 ID")
|
||||
private String pluginId;
|
||||
|
||||
@Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
|
||||
@ExcelProperty("插件名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "描述", example = "你猜")
|
||||
@ExcelProperty("描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty("部署方式")
|
||||
private Integer deployType;
|
||||
|
||||
@Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("插件包文件名")
|
||||
private String file;
|
||||
|
||||
@Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("插件版本")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty("插件类型")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "设备插件协议类型")
|
||||
@ExcelProperty("设备插件协议类型")
|
||||
private String protocol;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("状态")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "插件配置项描述信息")
|
||||
@ExcelProperty("插件配置项描述信息")
|
||||
private String configSchema;
|
||||
|
||||
@Schema(description = "插件配置信息")
|
||||
@ExcelProperty("插件配置信息")
|
||||
private String config;
|
||||
|
||||
@Schema(description = "插件脚本")
|
||||
@ExcelProperty("插件脚本")
|
||||
private String script;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 插件信息新增/修改 Request VO")
|
||||
@Data
|
||||
public class PluginInfoSaveReqVO {
|
||||
|
||||
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
|
||||
private String pluginId;
|
||||
|
||||
@Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "描述", example = "你猜")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer deployType;
|
||||
|
||||
@Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String file;
|
||||
|
||||
@Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String version;
|
||||
|
||||
@Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "设备插件协议类型")
|
||||
private String protocol;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "插件配置项描述信息")
|
||||
private String configSchema;
|
||||
|
||||
@Schema(description = "插件配置信息")
|
||||
private String config;
|
||||
|
||||
@Schema(description = "插件脚本")
|
||||
private String script;
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.plugininstance;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
|
||||
import jakarta.validation.*;
|
||||
import jakarta.servlet.http.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
|
||||
import cn.iocoder.yudao.module.iot.service.plugininstance.PluginInstanceService;
|
||||
|
||||
@Tag(name = "管理后台 - IoT 插件实例")
|
||||
@RestController
|
||||
@RequestMapping("/iot/plugin-instance")
|
||||
@Validated
|
||||
public class PluginInstanceController {
|
||||
|
||||
@Resource
|
||||
private PluginInstanceService pluginInstanceService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建IoT 插件实例")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-instance:create')")
|
||||
public CommonResult<Long> createPluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO createReqVO) {
|
||||
return success(pluginInstanceService.createPluginInstance(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新IoT 插件实例")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-instance:update')")
|
||||
public CommonResult<Boolean> updatePluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO updateReqVO) {
|
||||
pluginInstanceService.updatePluginInstance(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除IoT 插件实例")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-instance:delete')")
|
||||
public CommonResult<Boolean> deletePluginInstance(@RequestParam("id") Long id) {
|
||||
pluginInstanceService.deletePluginInstance(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得IoT 插件实例")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')")
|
||||
public CommonResult<PluginInstanceRespVO> getPluginInstance(@RequestParam("id") Long id) {
|
||||
PluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstance(id);
|
||||
return success(BeanUtils.toBean(pluginInstance, PluginInstanceRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得IoT 插件实例分页")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')")
|
||||
public CommonResult<PageResult<PluginInstanceRespVO>> getPluginInstancePage(@Valid PluginInstancePageReqVO pageReqVO) {
|
||||
PageResult<PluginInstanceDO> pageResult = pluginInstanceService.getPluginInstancePage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, PluginInstanceRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出IoT 插件实例 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('iot:plugin-instance:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportPluginInstanceExcel(@Valid PluginInstancePageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<PluginInstanceDO> list = pluginInstanceService.getPluginInstancePage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "IoT 插件实例.xls", "数据", PluginInstanceRespVO.class,
|
||||
BeanUtils.toBean(list, PluginInstanceRespVO.class));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
|
||||
|
||||
import lombok.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
// TODO @haohao:建议搞个 plugin,然后里面分 info 和 instance。另外,是不是 info => config 会好点,插件配置?
|
||||
@Schema(description = "管理后台 - IoT 插件实例分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class PluginInstancePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "插件主程序id", example = "23738")
|
||||
private String mainId;
|
||||
|
||||
@Schema(description = "插件id", example = "26498")
|
||||
private Long pluginId;
|
||||
|
||||
@Schema(description = "插件主程序所在ip")
|
||||
private String ip;
|
||||
|
||||
@Schema(description = "插件主程序端口")
|
||||
private Integer port;
|
||||
|
||||
@Schema(description = "心跳时间,心路时间超过30秒需要剔除")
|
||||
private Long heartbeatAt;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import com.alibaba.excel.annotation.*;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 插件实例 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class PluginInstanceRespVO {
|
||||
|
||||
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864")
|
||||
@ExcelProperty("主键ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738")
|
||||
@ExcelProperty("插件主程序id")
|
||||
private String mainId;
|
||||
|
||||
@Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498")
|
||||
@ExcelProperty("插件id")
|
||||
private Long pluginId;
|
||||
|
||||
@Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("插件主程序所在ip")
|
||||
private String ip;
|
||||
|
||||
@Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("插件主程序端口")
|
||||
private Integer port;
|
||||
|
||||
@Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("心跳时间,心路时间超过30秒需要剔除")
|
||||
private Long heartbeatAt;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 插件实例新增/修改 Request VO")
|
||||
@Data
|
||||
public class PluginInstanceSaveReqVO {
|
||||
|
||||
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738")
|
||||
@NotEmpty(message = "插件主程序id不能为空")
|
||||
private String mainId;
|
||||
|
||||
@Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498")
|
||||
@NotNull(message = "插件id不能为空")
|
||||
private Long pluginId;
|
||||
|
||||
@Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "插件主程序所在ip不能为空")
|
||||
private String ip;
|
||||
|
||||
@Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "插件主程序端口不能为空")
|
||||
private Integer port;
|
||||
|
||||
@Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "心跳时间,心路时间超过30秒需要剔除不能为空")
|
||||
private Long heartbeatAt;
|
||||
|
||||
}
|
|
@ -121,12 +121,12 @@ public class IotProductController {
|
|||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得所有产品列表")
|
||||
@PreAuthorize("@ss.hasPermission('iot:product:query')")
|
||||
@Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项")
|
||||
public CommonResult<List<IotProductRespVO>> getSimpleProductList() {
|
||||
List<IotProductDO> list = productService.getProductList();
|
||||
return success(convertList(list, product -> // 只返回 id、name 字段
|
||||
new IotProductRespVO().setId(product.getId()).setName(product.getName())));
|
||||
new IotProductRespVO().setId(product.getId()).setName(product.getName())
|
||||
.setDeviceType(product.getDeviceType())));
|
||||
}
|
||||
|
||||
}
|
|
@ -38,8 +38,8 @@ public class IotProductRespVO {
|
|||
@ExcelProperty("产品图标")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "产品图标", example = "https://iocoder.cn/1.png")
|
||||
@ExcelProperty("产品图标")
|
||||
@Schema(description = "产品图片", example = "https://iocoder.cn/1.png")
|
||||
@ExcelProperty("产品图片")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "产品描述", example = "你猜")
|
||||
|
|
|
@ -28,7 +28,7 @@ public class IotProductSaveReqVO {
|
|||
@Schema(description = "产品图标", example = "https://iocoder.cn/1.svg")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "产品图标", example = "https://iocoder.cn/1.png")
|
||||
@Schema(description = "产品图片", example = "https://iocoder.cn/1.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "产品描述", example = "描述")
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* IoT 设备 DO
|
||||
*
|
||||
* @author haohao
|
||||
*/
|
||||
@TableName("iot_device")
|
||||
@TableName(value = "iot_device", autoResultMap = true)
|
||||
@KeySequence("iot_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
@ -47,6 +50,17 @@ public class IotDeviceDO extends BaseDO {
|
|||
* 设备序列号
|
||||
*/
|
||||
private String serialNumber;
|
||||
/**
|
||||
* 设备图片
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* 设备分组编号集合
|
||||
*
|
||||
* 关联 {@link IotDeviceGroupDO#getId()}
|
||||
*/
|
||||
@TableField(typeHandler = LongSetTypeHandler.class)
|
||||
private Set<Long> groupIds;
|
||||
|
||||
/**
|
||||
* 产品编号
|
||||
|
@ -66,13 +80,6 @@ public class IotDeviceDO extends BaseDO {
|
|||
* 冗余 {@link IotProductDO#getDeviceType()}
|
||||
*/
|
||||
private Integer deviceType;
|
||||
|
||||
/**
|
||||
* 设备状态
|
||||
* <p>
|
||||
* 枚举 {@link IotDeviceStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 网关设备编号
|
||||
* <p>
|
||||
|
@ -82,6 +89,13 @@ public class IotDeviceDO extends BaseDO {
|
|||
*/
|
||||
private Long gatewayId;
|
||||
|
||||
/**
|
||||
* 设备状态
|
||||
* <p>
|
||||
* 枚举 {@link IotDeviceStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 设备状态最后更新时间
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* IoT 设备分组 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("iot_device_group")
|
||||
@KeySequence("iot_device_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IotDeviceGroupDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 分组 ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 分组名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 分组状态
|
||||
*
|
||||
* 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 分组描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* IoT 插件信息 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("iot_plugin_info")
|
||||
@KeySequence("iot_plugin_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PluginInfoDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键 ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
// TODO @haohao:这个是不是改成类似 key 之类的字段哈?
|
||||
/**
|
||||
* 插件包 ID
|
||||
*/
|
||||
private String pluginId;
|
||||
/**
|
||||
* 插件名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 部署方式
|
||||
*/
|
||||
// TODO @haohao:枚举
|
||||
private Integer deployType;
|
||||
/**
|
||||
* 插件包文件名
|
||||
*/
|
||||
// TODO @haohao:是不是叫 fileName 哈?避免后续有别的字段,类似 fileUrl?
|
||||
private String file;
|
||||
/**
|
||||
* 插件版本
|
||||
*/
|
||||
private String version;
|
||||
/**
|
||||
* 插件类型
|
||||
*/
|
||||
// TODO @haohao:枚举
|
||||
private Integer type;
|
||||
/**
|
||||
* 设备插件协议类型
|
||||
*/
|
||||
private String protocol;
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
// TODO @haohao:枚举
|
||||
private Integer status;
|
||||
/**
|
||||
* 插件配置项描述信息
|
||||
*/
|
||||
private String configSchema;
|
||||
/**
|
||||
* 插件配置信息
|
||||
*/
|
||||
private String config;
|
||||
/**
|
||||
* 插件脚本
|
||||
*/
|
||||
private String script;
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
// TODO @haohao:一些必要的关联、枚举
|
||||
/**
|
||||
* IoT 插件实例 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("iot_plugin_instance")
|
||||
@KeySequence("iot_plugin_instance_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PluginInstanceDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 插件主程序 ID
|
||||
*/
|
||||
private String mainId;
|
||||
/**
|
||||
* 插件id
|
||||
*/
|
||||
private Long pluginId;
|
||||
/**
|
||||
* 插件主程序所在ip
|
||||
*/
|
||||
private String ip;
|
||||
/**
|
||||
* 插件主程序端口
|
||||
*/
|
||||
private Integer port;
|
||||
/**
|
||||
* 心跳时间,心路时间超过 30 秒需要剔除
|
||||
*/
|
||||
private Long heartbeatAt;
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.mysql.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IoT 设备分组 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface IotDeviceGroupMapper extends BaseMapperX<IotDeviceGroupDO> {
|
||||
|
||||
default PageResult<IotDeviceGroupDO> selectPage(IotDeviceGroupPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceGroupDO>()
|
||||
.likeIfPresent(IotDeviceGroupDO::getName, reqVO.getName())
|
||||
.betweenIfPresent(IotDeviceGroupDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(IotDeviceGroupDO::getId));
|
||||
}
|
||||
|
||||
default List<IotDeviceGroupDO> selectListByStatus(Integer status) {
|
||||
return selectList(IotDeviceGroupDO::getStatus, status);
|
||||
}
|
||||
|
||||
default IotDeviceGroupDO selectByName(String name) {
|
||||
return selectOne(IotDeviceGroupDO::getName, name);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.mysql.device;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
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
|
|||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IoT 设备 Mapper
|
||||
*
|
||||
|
@ -15,30 +18,21 @@ import org.apache.ibatis.annotations.Mapper;
|
|||
@Mapper
|
||||
public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
|
||||
|
||||
// TODO @haohao:可能多余的查询条件,要去掉哈
|
||||
default PageResult<IotDeviceDO> selectPage(IotDevicePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>()
|
||||
.eqIfPresent(IotDeviceDO::getDeviceKey, reqVO.getDeviceKey())
|
||||
.likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName())
|
||||
.eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId())
|
||||
.eqIfPresent(IotDeviceDO::getProductKey, reqVO.getProductKey())
|
||||
.eqIfPresent(IotDeviceDO::getDeviceType, reqVO.getDeviceType())
|
||||
.likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname())
|
||||
.eqIfPresent(IotDeviceDO::getGatewayId, reqVO.getGatewayId())
|
||||
.eqIfPresent(IotDeviceDO::getStatus, reqVO.getStatus())
|
||||
.betweenIfPresent(IotDeviceDO::getStatusLastUpdateTime, reqVO.getStatusLastUpdateTime())
|
||||
.betweenIfPresent(IotDeviceDO::getLastOnlineTime, reqVO.getLastOnlineTime())
|
||||
.betweenIfPresent(IotDeviceDO::getLastOfflineTime, reqVO.getLastOfflineTime())
|
||||
.betweenIfPresent(IotDeviceDO::getActiveTime, reqVO.getActiveTime())
|
||||
.eqIfPresent(IotDeviceDO::getDeviceSecret, reqVO.getDeviceSecret())
|
||||
.eqIfPresent(IotDeviceDO::getMqttClientId, reqVO.getMqttClientId())
|
||||
.likeIfPresent(IotDeviceDO::getMqttUsername, reqVO.getMqttUsername())
|
||||
.eqIfPresent(IotDeviceDO::getMqttPassword, reqVO.getMqttPassword())
|
||||
.eqIfPresent(IotDeviceDO::getAuthType, reqVO.getAuthType())
|
||||
.betweenIfPresent(IotDeviceDO::getCreateTime, reqVO.getCreateTime())
|
||||
.apply(ObjectUtil.isNotNull(reqVO.getGroupId()), "FIND_IN_SET(" + reqVO.getGroupId() + ",group_ids) > 0")
|
||||
.orderByDesc(IotDeviceDO::getId));
|
||||
}
|
||||
|
||||
default IotDeviceDO selectByDeviceName(String deviceName) {
|
||||
return selectOne(IotDeviceDO::getDeviceName, deviceName);
|
||||
}
|
||||
|
||||
default IotDeviceDO selectByProductKeyAndDeviceName(String productKey, String deviceName) {
|
||||
return selectOne(IotDeviceDO::getProductKey, productKey,
|
||||
IotDeviceDO::getDeviceName, deviceName);
|
||||
|
@ -51,4 +45,19 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
|
|||
default Long selectCountByProductId(Long productId) {
|
||||
return selectCount(IotDeviceDO::getProductId, productId);
|
||||
}
|
||||
|
||||
default IotDeviceDO selectByDeviceKey(String deviceKey) {
|
||||
return selectOne(IotDeviceDO::getDeviceKey, deviceKey);
|
||||
}
|
||||
|
||||
default List<IotDeviceDO> selectList(Integer deviceType) {
|
||||
return selectList(IotDeviceDO::getDeviceType, deviceType);
|
||||
}
|
||||
|
||||
default Long selectCountByGroupId(Long groupId) {
|
||||
return selectCount(new LambdaQueryWrapperX<IotDeviceDO>()
|
||||
.apply("FIND_IN_SET(" + groupId + ",group_ids) > 0")
|
||||
.orderByDesc(IotDeviceDO::getId));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.mysql.plugininfo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*;
|
||||
|
||||
/**
|
||||
* IoT 插件信息 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface PluginInfoMapper extends BaseMapperX<PluginInfoDO> {
|
||||
|
||||
default PageResult<PluginInfoDO> selectPage(PluginInfoPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<PluginInfoDO>()
|
||||
.eqIfPresent(PluginInfoDO::getPluginId, reqVO.getPluginId())
|
||||
.likeIfPresent(PluginInfoDO::getName, reqVO.getName())
|
||||
.eqIfPresent(PluginInfoDO::getDescription, reqVO.getDescription())
|
||||
.eqIfPresent(PluginInfoDO::getDeployType, reqVO.getDeployType())
|
||||
.eqIfPresent(PluginInfoDO::getFile, reqVO.getFile())
|
||||
.eqIfPresent(PluginInfoDO::getVersion, reqVO.getVersion())
|
||||
.eqIfPresent(PluginInfoDO::getType, reqVO.getType())
|
||||
.eqIfPresent(PluginInfoDO::getProtocol, reqVO.getProtocol())
|
||||
.eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(PluginInfoDO::getConfigSchema, reqVO.getConfigSchema())
|
||||
.eqIfPresent(PluginInfoDO::getConfig, reqVO.getConfig())
|
||||
.eqIfPresent(PluginInfoDO::getScript, reqVO.getScript())
|
||||
.betweenIfPresent(PluginInfoDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(PluginInfoDO::getId));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.mysql.plugininstance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*;
|
||||
|
||||
/**
|
||||
* IoT 插件实例 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface PluginInstanceMapper extends BaseMapperX<PluginInstanceDO> {
|
||||
|
||||
default PageResult<PluginInstanceDO> selectPage(PluginInstancePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<PluginInstanceDO>()
|
||||
.eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId())
|
||||
.eqIfPresent(PluginInstanceDO::getPluginId, reqVO.getPluginId())
|
||||
.eqIfPresent(PluginInstanceDO::getIp, reqVO.getIp())
|
||||
.eqIfPresent(PluginInstanceDO::getPort, reqVO.getPort())
|
||||
.eqIfPresent(PluginInstanceDO::getHeartbeatAt, reqVO.getHeartbeatAt())
|
||||
.betweenIfPresent(PluginInstanceDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(PluginInstanceDO::getId));
|
||||
}
|
||||
|
||||
}
|
|
@ -21,7 +21,7 @@ public interface IotProductCategoryMapper extends BaseMapperX<IotProductCategory
|
|||
return selectPage(reqVO, new LambdaQueryWrapperX<IotProductCategoryDO>()
|
||||
.likeIfPresent(IotProductCategoryDO::getName, reqVO.getName())
|
||||
.betweenIfPresent(IotProductCategoryDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(IotProductCategoryDO::getId));
|
||||
.orderByAsc(IotProductCategoryDO::getSort));
|
||||
}
|
||||
|
||||
default List<IotProductCategoryDO> selectListByStatus(Integer status) {
|
||||
|
|
|
@ -23,7 +23,7 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> {
|
|||
}
|
||||
|
||||
default IotProductDO selectByProductKey(String productKey) {
|
||||
return selectOne(new LambdaQueryWrapperX<IotProductDO>().eq(IotProductDO::getProductKey, productKey));
|
||||
return selectOne(IotProductDO::getProductKey, productKey);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package cn.iocoder.yudao.module.iot.framework.plugin;
|
||||
|
||||
import org.pf4j.spring.SpringPluginManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class SpringConfiguration {
|
||||
|
||||
@Bean
|
||||
public SpringPluginManager pluginManager() {
|
||||
return new SpringPluginManager();
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,6 @@ import java.util.Map;
|
|||
*/
|
||||
public interface IotDeviceDataService {
|
||||
|
||||
|
||||
/**
|
||||
* 保存设备数据
|
||||
*
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* IoT 设备分组 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IotDeviceGroupService {
|
||||
|
||||
/**
|
||||
* 创建设备分组
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createDeviceGroup(@Valid IotDeviceGroupSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新设备分组
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateDeviceGroup(@Valid IotDeviceGroupSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除设备分组
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteDeviceGroup(Long id);
|
||||
|
||||
/**
|
||||
* 校验设备分组是否存在
|
||||
*
|
||||
* @param ids 设备分组 ID 数组
|
||||
*/
|
||||
default List<IotDeviceGroupDO> validateDeviceGroupExists(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return List.of();
|
||||
}
|
||||
return convertList(ids, this::validateDeviceGroupExists);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验设备分组是否存在
|
||||
*
|
||||
* @param id 设备分组 ID
|
||||
* @return 设备分组
|
||||
*/
|
||||
IotDeviceGroupDO validateDeviceGroupExists(Long id);
|
||||
|
||||
/**
|
||||
* 获得设备分组
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 设备分组
|
||||
*/
|
||||
IotDeviceGroupDO getDeviceGroup(Long id);
|
||||
|
||||
/**
|
||||
* 获得设备分组
|
||||
*
|
||||
* @param name 名称
|
||||
* @return 设备分组
|
||||
*/
|
||||
IotDeviceGroupDO getDeviceGroupByName(String name);
|
||||
|
||||
/**
|
||||
* 获得设备分组分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 设备分组分页
|
||||
*/
|
||||
PageResult<IotDeviceGroupDO> getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得设备分组列表
|
||||
*
|
||||
* @param status 状态
|
||||
* @return 设备分组列表
|
||||
*/
|
||||
List<IotDeviceGroupDO> getDeviceGroupListByStatus(Integer status);
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceGroupMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS;
|
||||
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* IoT 设备分组 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class IotDeviceGroupServiceImpl implements IotDeviceGroupService {
|
||||
|
||||
@Resource
|
||||
private IotDeviceGroupMapper deviceGroupMapper;
|
||||
|
||||
@Resource
|
||||
private IotDeviceService deviceService;
|
||||
|
||||
@Override
|
||||
public Long createDeviceGroup(IotDeviceGroupSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
IotDeviceGroupDO deviceGroup = BeanUtils.toBean(createReqVO, IotDeviceGroupDO.class);
|
||||
deviceGroupMapper.insert(deviceGroup);
|
||||
// 返回
|
||||
return deviceGroup.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDeviceGroup(IotDeviceGroupSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateDeviceGroupExists(updateReqVO.getId());
|
||||
// 更新
|
||||
IotDeviceGroupDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceGroupDO.class);
|
||||
deviceGroupMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDeviceGroup(Long id) {
|
||||
// 1.1 校验存在
|
||||
validateDeviceGroupExists(id);
|
||||
// 1.2 校验是否存在设备
|
||||
if (deviceService.getDeviceCountByGroupId(id) > 0) {
|
||||
throw exception(DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS);
|
||||
}
|
||||
|
||||
// 删除
|
||||
deviceGroupMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IotDeviceGroupDO validateDeviceGroupExists(Long id) {
|
||||
IotDeviceGroupDO group = deviceGroupMapper.selectById(id);
|
||||
if (group == null) {
|
||||
throw exception(DEVICE_GROUP_NOT_EXISTS);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IotDeviceGroupDO getDeviceGroup(Long id) {
|
||||
return deviceGroupMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IotDeviceGroupDO getDeviceGroupByName(String name) {
|
||||
return deviceGroupMapper.selectByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<IotDeviceGroupDO> getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO) {
|
||||
return deviceGroupMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IotDeviceGroupDO> getDeviceGroupListByStatus(Integer status) {
|
||||
return deviceGroupMapper.selectListByStatus(status);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,18 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
|
||||
import jakarta.validation.*;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceUpdateGroupReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportRespVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportExcelVO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IoT 设备 Service 接口
|
||||
|
@ -30,12 +37,33 @@ public interface IotDeviceService {
|
|||
void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除设备
|
||||
* 更新设备状态
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 更新设备分组
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除单个设备
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteDevice(Long id);
|
||||
|
||||
/**
|
||||
* 删除多个设备
|
||||
*
|
||||
* @param ids 编号数组
|
||||
*/
|
||||
void deleteDeviceList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得设备
|
||||
*
|
||||
|
@ -45,7 +73,7 @@ public interface IotDeviceService {
|
|||
IotDeviceDO getDevice(Long id);
|
||||
|
||||
/**
|
||||
* 获得设备分页
|
||||
* <EFBFBD><EFBFBD>得设备分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return IoT 设备分页
|
||||
|
@ -53,11 +81,12 @@ public interface IotDeviceService {
|
|||
PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 更新设备状态
|
||||
* 获得设备列表
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
* @param deviceType 设备类型
|
||||
* @return 设备列表
|
||||
*/
|
||||
void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO);
|
||||
List<IotDeviceDO> getDeviceList(@Nullable Integer deviceType);
|
||||
|
||||
/**
|
||||
* 获得设备数量
|
||||
|
@ -67,6 +96,14 @@ public interface IotDeviceService {
|
|||
*/
|
||||
Long getDeviceCountByProductId(Long productId);
|
||||
|
||||
/**
|
||||
* 获得设备数量
|
||||
*
|
||||
* @param groupId 分组编号
|
||||
* @return 设备数量
|
||||
*/
|
||||
Long getDeviceCountByGroupId(Long groupId);
|
||||
|
||||
/**
|
||||
* 根据产品 key 和设备名称,获得设备信息
|
||||
*
|
||||
|
@ -75,4 +112,14 @@ public interface IotDeviceService {
|
|||
* @return 设备信息
|
||||
*/
|
||||
IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName);
|
||||
|
||||
/**
|
||||
* 导入设备
|
||||
*
|
||||
* @param importDevices 导入设备列表
|
||||
* @param updateSupport 是否支持更新
|
||||
* @return 导入结果
|
||||
*/
|
||||
IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport);
|
||||
|
||||
}
|
|
@ -1,31 +1,36 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
|
@ -40,68 +45,212 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
|||
|
||||
@Resource
|
||||
private IotDeviceMapper deviceMapper;
|
||||
|
||||
@Resource
|
||||
private IotProductService productService;
|
||||
@Resource
|
||||
@Lazy // 延迟加载,解决循环依赖
|
||||
private IotDeviceGroupService deviceGroupService;
|
||||
|
||||
/**
|
||||
* 创建 IoT 设备
|
||||
*
|
||||
* @param createReqVO 创建请求 VO
|
||||
* @return 设备 ID
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createDevice(IotDeviceSaveReqVO createReqVO) {
|
||||
// 1.1 校验产品是否存在
|
||||
IotProductDO product = productService.getProduct(createReqVO.getProductId());
|
||||
if (product == null) {
|
||||
throw exception(PRODUCT_NOT_EXISTS);
|
||||
}
|
||||
// 1.2 校验设备名称在同一产品下是否唯一
|
||||
if (StrUtil.isBlank(createReqVO.getDeviceName())) {
|
||||
createReqVO.setDeviceName(generateUniqueDeviceName(product.getProductKey()));
|
||||
} else {
|
||||
validateDeviceNameUnique(product.getProductKey(), createReqVO.getDeviceName());
|
||||
// 1.2 校验设备标识是否唯一
|
||||
if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) {
|
||||
throw exception(DEVICE_KEY_EXISTS);
|
||||
}
|
||||
// 1.3 校验设备名称在同一产品下是否唯一
|
||||
if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) {
|
||||
throw exception(DEVICE_NAME_EXISTS);
|
||||
}
|
||||
// 1.4 校验父设备是否为合法网关
|
||||
if (IotProductDeviceTypeEnum.isGateway(product.getDeviceType())
|
||||
&& createReqVO.getGatewayId() != null) {
|
||||
validateGatewayDeviceExists(createReqVO.getGatewayId());
|
||||
}
|
||||
// 1.5 校验分组存在
|
||||
deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds());
|
||||
|
||||
// 2.1 转换 VO 为 DO
|
||||
IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class)
|
||||
.setProductKey(product.getProductKey())
|
||||
.setDeviceType(product.getDeviceType());
|
||||
// 2.2 生成并设置必要的字段
|
||||
device.setDeviceKey(generateUniqueDeviceKey());
|
||||
device.setDeviceSecret(generateDeviceSecret());
|
||||
device.setMqttClientId(generateMqttClientId());
|
||||
device.setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey()));
|
||||
device.setMqttPassword(generateMqttPassword());
|
||||
// 2.3 设置设备状态为未激活
|
||||
device.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus());
|
||||
device.setStatusLastUpdateTime(LocalDateTime.now());
|
||||
// 2.4 插入到数据库
|
||||
IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> {
|
||||
o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
|
||||
// 生成并设置必要的字段
|
||||
o.setDeviceSecret(generateDeviceSecret())
|
||||
.setMqttClientId(generateMqttClientId())
|
||||
.setMqttUsername(generateMqttUsername(o.getDeviceName(), o.getProductKey()))
|
||||
.setMqttPassword(generateMqttPassword());
|
||||
// 设置设备状态为未激活
|
||||
o.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus()).setStatusLastUpdateTime(LocalDateTime.now());
|
||||
});
|
||||
// 2.2 插入到数据库
|
||||
deviceMapper.insert(device);
|
||||
return device.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验设备名称在同一产品下是否唯一
|
||||
*
|
||||
* @param productKey 产品 Key
|
||||
* @param deviceName 设备名称
|
||||
*/
|
||||
private void validateDeviceNameUnique(String productKey, String deviceName) {
|
||||
IotDeviceDO existingDevice = deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
|
||||
if (existingDevice != null) {
|
||||
throw exception(DEVICE_NAME_EXISTS);
|
||||
@Override
|
||||
public void updateDevice(IotDeviceSaveReqVO updateReqVO) {
|
||||
updateReqVO.setDeviceKey(null).setDeviceName(null).setProductId(null); // 不允许更新
|
||||
// 1.1 校验存在
|
||||
IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
|
||||
// 1.2 校验父设备是否为合法网关
|
||||
if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType())
|
||||
&& updateReqVO.getGatewayId() != null) {
|
||||
validateGatewayDeviceExists(updateReqVO.getGatewayId());
|
||||
}
|
||||
// 1.3 校验分组存在
|
||||
deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds());
|
||||
|
||||
// 2. 更新到数据库
|
||||
IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
|
||||
deviceMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateDeviceGroup(IotDeviceUpdateGroupReqVO updateReqVO) {
|
||||
// 1.1 校验设备存在
|
||||
List<IotDeviceDO> devices = deviceMapper.selectBatchIds(updateReqVO.getIds());
|
||||
if (CollUtil.isEmpty(devices)) {
|
||||
return;
|
||||
}
|
||||
// 1.2 校验分组存在
|
||||
deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds());
|
||||
|
||||
// 3. 更新设备分组
|
||||
deviceMapper.updateBatch(convertList(devices, device -> new IotDeviceDO()
|
||||
.setId(device.getId()).setGroupIds(updateReqVO.getGroupIds())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDevice(Long id) {
|
||||
// 1.1 校验存在
|
||||
IotDeviceDO device = validateDeviceExists(id);
|
||||
// 1.2 如果是网关设备,检查是否有子设备
|
||||
if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(id) > 0) {
|
||||
throw exception(DEVICE_HAS_CHILDREN);
|
||||
}
|
||||
|
||||
// 2. 删除设备
|
||||
deviceMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteDeviceList(Collection<Long> ids) {
|
||||
// 1.1 校验存在
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return;
|
||||
}
|
||||
List<IotDeviceDO> devices = deviceMapper.selectBatchIds(ids);
|
||||
if (CollUtil.isEmpty(devices)) {
|
||||
return;
|
||||
}
|
||||
// 1.2 校验网关设备是否存在
|
||||
for (IotDeviceDO device : devices) {
|
||||
if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(device.getId()) > 0) {
|
||||
throw exception(DEVICE_HAS_CHILDREN);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 删除设备
|
||||
deviceMapper.deleteByIds(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一的 deviceKey
|
||||
* 校验设备是否存在
|
||||
*
|
||||
* @param id 设备 ID
|
||||
* @return 设备对象
|
||||
*/
|
||||
private IotDeviceDO validateDeviceExists(Long id) {
|
||||
IotDeviceDO device = deviceMapper.selectById(id);
|
||||
if (device == null) {
|
||||
throw exception(DEVICE_NOT_EXISTS);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验网关设备是否存在
|
||||
*
|
||||
* @param id 设备 ID
|
||||
*/
|
||||
private void validateGatewayDeviceExists(Long id) {
|
||||
IotDeviceDO device = deviceMapper.selectById(id);
|
||||
if (device == null) {
|
||||
throw exception(DEVICE_GATEWAY_NOT_EXISTS);
|
||||
}
|
||||
if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {
|
||||
throw exception(DEVICE_NOT_GATEWAY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IotDeviceDO getDevice(Long id) {
|
||||
return deviceMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO) {
|
||||
return deviceMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IotDeviceDO> getDeviceList(@Nullable Integer deviceType) {
|
||||
return deviceMapper.selectList(deviceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) {
|
||||
// 1. 校验存在
|
||||
IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
|
||||
|
||||
// 2.1 更新状态和更新时间
|
||||
IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class)
|
||||
.setStatusLastUpdateTime(LocalDateTime.now());
|
||||
// 2.2 更新状态相关时间
|
||||
if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus())
|
||||
&& Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
|
||||
// 从未激活到在线,设置激活时间和最后上线时间
|
||||
updateDevice.setActiveTime(LocalDateTime.now()).setLastOnlineTime(LocalDateTime.now());
|
||||
} else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
|
||||
// 如果是上线,设置最后上线时间
|
||||
updateDevice.setLastOnlineTime(LocalDateTime.now());
|
||||
} else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) {
|
||||
// 如果是离线,设置最后离线时间
|
||||
updateDevice.setLastOfflineTime(LocalDateTime.now());
|
||||
}
|
||||
// 2.3 更新到数据库
|
||||
deviceMapper.updateById(updateDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDeviceCountByProductId(Long productId) {
|
||||
return deviceMapper.selectCountByProductId(productId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDeviceCountByGroupId(Long groupId) {
|
||||
return deviceMapper.selectCountByGroupId(groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TenantIgnore
|
||||
public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) {
|
||||
return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 deviceKey
|
||||
*
|
||||
* @return 生成的 deviceKey
|
||||
*/
|
||||
private String generateUniqueDeviceKey() {
|
||||
return UUID.randomUUID().toString();
|
||||
private String generateDeviceKey() {
|
||||
return RandomUtil.randomString(16);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,7 +268,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
|||
* @return 生成的 MQTT Client ID
|
||||
*/
|
||||
private String generateMqttClientId() {
|
||||
return UUID.randomUUID().toString();
|
||||
return IdUtil.fastSimpleUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,119 +288,77 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
|||
* @return 生成的 MQTT Password
|
||||
*/
|
||||
private String generateMqttPassword() {
|
||||
// TODO @浩浩:这里的 StrUtil 随机字符串?
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
byte[] passwordBytes = new byte[32]; // 256 位的随机数
|
||||
secureRandom.nextBytes(passwordBytes);
|
||||
return Base64.getUrlEncoder().withoutPadding().encodeToString(passwordBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一的 DeviceName
|
||||
*
|
||||
* @param productKey 产品标识
|
||||
* @return 生成的唯一 DeviceName
|
||||
*/
|
||||
private String generateUniqueDeviceName(String productKey) {
|
||||
for (int i = 0; i < Short.MAX_VALUE; i++) {
|
||||
String deviceName = IdUtil.fastSimpleUUID().substring(0, 20);
|
||||
if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) {
|
||||
return deviceName;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("生成 DeviceName 失败");
|
||||
return RandomUtil.randomString(32);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateDevice(IotDeviceSaveReqVO updateReqVO) {
|
||||
// 1. 校验存在
|
||||
validateDeviceExists(updateReqVO.getId());
|
||||
|
||||
// 2. 更新到数据库
|
||||
IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class)
|
||||
.setDeviceName(null).setProductId(null); // 设备名称 和 产品 ID 不能修改
|
||||
deviceMapper.updateById(updateObj);
|
||||
@Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
|
||||
public IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport) {
|
||||
// 1. 参数校验
|
||||
if (CollUtil.isEmpty(importDevices)) {
|
||||
throw exception(DEVICE_IMPORT_LIST_IS_EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteDevice(Long id) {
|
||||
// 1.1 校验存在
|
||||
IotDeviceDO device = validateDeviceExists(id);
|
||||
// 1.2 如果是网关设备,检查是否有子设备
|
||||
if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(id) > 0) {
|
||||
throw exception(DEVICE_HAS_CHILDREN);
|
||||
// 2. 遍历,逐个创建 or 更新
|
||||
IotDeviceImportRespVO respVO = IotDeviceImportRespVO.builder().createDeviceNames(new ArrayList<>())
|
||||
.updateDeviceNames(new ArrayList<>()).failureDeviceNames(new LinkedHashMap<>()).build();
|
||||
importDevices.forEach(importDevice -> {
|
||||
try {
|
||||
// 2.1.1 校验字段是否符合要求
|
||||
try {
|
||||
ValidationUtils.validate(importDevice);
|
||||
} catch (ConstraintViolationException ex){
|
||||
respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());
|
||||
return;
|
||||
}
|
||||
// 2.1.2 校验产品是否存在
|
||||
IotProductDO product = productService.validateProductExists(importDevice.getProductKey());
|
||||
// 2.1.3 校验父设备是否存在
|
||||
Long gatewayId = null;
|
||||
if (StrUtil.isNotEmpty(importDevice.getParentDeviceName())) {
|
||||
IotDeviceDO gatewayDevice = deviceMapper.selectByDeviceName(importDevice.getParentDeviceName());
|
||||
if (gatewayDevice == null) {
|
||||
throw exception(DEVICE_GATEWAY_NOT_EXISTS);
|
||||
}
|
||||
if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {
|
||||
throw exception(DEVICE_NOT_GATEWAY);
|
||||
}
|
||||
gatewayId = gatewayDevice.getId();
|
||||
}
|
||||
// 2.1.4 校验设备分组是否存在
|
||||
Set<Long> groupIds = new HashSet<>();
|
||||
if (StrUtil.isNotEmpty(importDevice.getGroupNames())) {
|
||||
String[] groupNames = importDevice.getGroupNames().split(",");
|
||||
for (String groupName : groupNames) {
|
||||
IotDeviceGroupDO group = deviceGroupService.getDeviceGroupByName(groupName);
|
||||
if (group == null) {
|
||||
throw exception(DEVICE_GROUP_NOT_EXISTS);
|
||||
}
|
||||
groupIds.add(group.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 删除设备
|
||||
deviceMapper.deleteById(id);
|
||||
// 2.2.1 判断如果不存在,在进行插入
|
||||
IotDeviceDO existDevice = deviceMapper.selectByDeviceName(importDevice.getDeviceName());
|
||||
if (existDevice == null) {
|
||||
createDevice(new IotDeviceSaveReqVO()
|
||||
.setDeviceName(importDevice.getDeviceName()).setDeviceKey(generateDeviceKey())
|
||||
.setProductId(product.getId()).setGatewayId(gatewayId).setGroupIds(groupIds));
|
||||
respVO.getCreateDeviceNames().add(importDevice.getDeviceName());
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验设备是否存在
|
||||
*
|
||||
* @param id 设备 ID
|
||||
* @return 设备对象
|
||||
*/
|
||||
private IotDeviceDO validateDeviceExists(Long id) {
|
||||
IotDeviceDO device = deviceMapper.selectById(id);
|
||||
if (device == null) {
|
||||
throw exception(DEVICE_NOT_EXISTS);
|
||||
// 2.2.2 如果存在,判断是否允许更新
|
||||
if (updateSupport) {
|
||||
throw exception(DEVICE_KEY_EXISTS);
|
||||
}
|
||||
return device;
|
||||
updateDevice(new IotDeviceSaveReqVO().setId(existDevice.getId())
|
||||
.setGatewayId(gatewayId).setGroupIds(groupIds));
|
||||
respVO.getUpdateDeviceNames().add(importDevice.getDeviceName());
|
||||
} catch (ServiceException ex) {
|
||||
respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IotDeviceDO getDevice(Long id) {
|
||||
IotDeviceDO device = deviceMapper.selectById(id);
|
||||
if (device == null) {
|
||||
throw exception(DEVICE_NOT_EXISTS);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO) {
|
||||
return deviceMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) {
|
||||
// 1. 校验存在
|
||||
IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
|
||||
|
||||
// 2.1 更新状态和更新时间
|
||||
IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
|
||||
// 2.2 更新状态相关时间
|
||||
if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus())
|
||||
&& Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
|
||||
// 从未激活到在线,设置激活时间和最后上线时间
|
||||
updateDevice.setActiveTime(LocalDateTime.now());
|
||||
updateDevice.setLastOnlineTime(LocalDateTime.now());
|
||||
} else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
|
||||
// 如果是上线,设置最后上线时间
|
||||
updateDevice.setLastOnlineTime(LocalDateTime.now());
|
||||
} else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) {
|
||||
// 如果是离线,设置最后离线时间
|
||||
updateDevice.setLastOfflineTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
// 2.3 设置状态更新时间
|
||||
updateDevice.setStatusLastUpdateTime(LocalDateTime.now());
|
||||
// 2.4 更新到数据库
|
||||
deviceMapper.updateById(updateDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDeviceCountByProductId(Long productId) {
|
||||
return deviceMapper.selectCountByProductId(productId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TenantIgnore
|
||||
public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) {
|
||||
return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
|
||||
});
|
||||
return respVO;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package cn.iocoder.yudao.module.iot.service.plugininfo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* IoT 插件信息 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface PluginInfoService {
|
||||
|
||||
/**
|
||||
* 创建IoT 插件信息
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createPluginInfo(@Valid PluginInfoSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新IoT 插件信息
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updatePluginInfo(@Valid PluginInfoSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除IoT 插件信息
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deletePluginInfo(Long id);
|
||||
|
||||
/**
|
||||
* 获得IoT 插件信息
|
||||
*
|
||||
* @param id 编号
|
||||
* @return IoT 插件信息
|
||||
*/
|
||||
PluginInfoDO getPluginInfo(Long id);
|
||||
|
||||
/**
|
||||
* 获得IoT 插件信息分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return IoT 插件信息分页
|
||||
*/
|
||||
PageResult<PluginInfoDO> getPluginInfoPage(PluginInfoPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 上传插件的 JAR 包
|
||||
*
|
||||
* @param id 插件id
|
||||
* @param file 文件
|
||||
*/
|
||||
void uploadJar(Long id, MultipartFile file);
|
||||
|
||||
/**
|
||||
* 更新插件的状态
|
||||
*
|
||||
* @param id 插件id
|
||||
* @param status 状态
|
||||
*/
|
||||
void updatePluginStatus(Long id, Integer status);
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
package cn.iocoder.yudao.module.iot.service.plugininfo;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.pf4j.PluginDescriptor;
|
||||
import org.pf4j.PluginState;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.pf4j.spring.SpringPluginManager;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* IoT 插件信息 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class PluginInfoServiceImpl implements PluginInfoService {
|
||||
|
||||
@Resource
|
||||
private PluginInfoMapper pluginInfoMapper;
|
||||
|
||||
@Resource
|
||||
private SpringPluginManager pluginManager;
|
||||
|
||||
@Resource
|
||||
private FileApi fileApi;
|
||||
|
||||
@Override
|
||||
public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class);
|
||||
pluginInfoMapper.insert(pluginInfo);
|
||||
// 返回
|
||||
return pluginInfo.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePluginInfo(PluginInfoSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validatePluginInfoExists(updateReqVO.getId());
|
||||
// 更新
|
||||
PluginInfoDO updateObj = BeanUtils.toBean(updateReqVO, PluginInfoDO.class);
|
||||
pluginInfoMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePluginInfo(Long id) {
|
||||
// 校验存在
|
||||
PluginInfoDO pluginInfoDO = validatePluginInfoExists(id);
|
||||
|
||||
// 停止插件
|
||||
if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
|
||||
throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING);
|
||||
}
|
||||
|
||||
// 卸载插件
|
||||
PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginId());
|
||||
if (plugin != null) {
|
||||
// 查询插件是否是启动状态
|
||||
if (plugin.getPluginState().equals(PluginState.STARTED)) {
|
||||
// 停止插件
|
||||
pluginManager.stopPlugin(plugin.getPluginId());
|
||||
}
|
||||
// 卸载插件
|
||||
pluginManager.unloadPlugin(plugin.getPluginId());
|
||||
}
|
||||
|
||||
// 删除
|
||||
pluginInfoMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private PluginInfoDO validatePluginInfoExists(Long id) {
|
||||
PluginInfoDO pluginInfo = pluginInfoMapper.selectById(id);
|
||||
if (pluginInfo == null) {
|
||||
throw exception(PLUGIN_INFO_NOT_EXISTS);
|
||||
}
|
||||
return pluginInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginInfoDO getPluginInfo(Long id) {
|
||||
return pluginInfoMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PluginInfoDO> getPluginInfoPage(PluginInfoPageReqVO pageReqVO) {
|
||||
return pluginInfoMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadJar(Long id, MultipartFile file) {
|
||||
// 1. 校验存在
|
||||
PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
|
||||
|
||||
// 2. 判断文件名称与插件 ID 是否匹配
|
||||
String pluginId = pluginInfoDo.getPluginId();
|
||||
|
||||
// 3. 停止卸载旧的插件
|
||||
// 3.1. 获取插件信息
|
||||
PluginWrapper plugin = pluginManager.getPlugin(pluginId);
|
||||
if (plugin != null) {
|
||||
// 3.2. 如果插件状态是启动的,停止插件
|
||||
if (plugin.getPluginState().equals(PluginState.STARTED)) {
|
||||
pluginManager.stopPlugin(pluginId);
|
||||
}
|
||||
// 3.3. 卸载插件
|
||||
pluginManager.unloadPlugin(pluginId);
|
||||
}
|
||||
|
||||
// 4. 上传插件
|
||||
String pluginIdNew;
|
||||
try {
|
||||
String path = fileApi.createFile(IoUtil.readBytes(file.getInputStream()));
|
||||
Path pluginPath = Path.of(path);
|
||||
pluginIdNew = pluginManager.loadPlugin(pluginPath);
|
||||
} catch (Exception e) {
|
||||
throw exception(PLUGIN_INSTALL_FAILED);
|
||||
}
|
||||
|
||||
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew);
|
||||
if (pluginWrapper == null) {
|
||||
throw exception(PLUGIN_INSTALL_FAILED);
|
||||
}
|
||||
|
||||
|
||||
// 5. 读取配置文件和脚本
|
||||
String configJson = "";
|
||||
String script = "";
|
||||
|
||||
pluginInfoDo.setPluginId(pluginIdNew);
|
||||
pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus());
|
||||
pluginInfoDo.setFile(file.getOriginalFilename());
|
||||
pluginInfoDo.setConfigSchema(configJson);
|
||||
pluginInfoDo.setScript(script);
|
||||
|
||||
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
|
||||
pluginInfoDo.setVersion(pluginDescriptor.getVersion());
|
||||
pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription());
|
||||
pluginInfoMapper.updateById(pluginInfoDo);
|
||||
|
||||
|
||||
// 5. 读取配置文件和脚本
|
||||
// String configJson = "";
|
||||
// String script = "";
|
||||
// try (JarFile jarFile = new JarFile(pluginInfoUpdate.getPluginPath())) {
|
||||
// // 5.1 获取config文件在jar包中的路径
|
||||
// String configFile = "classes/config.json";
|
||||
// JarEntry configEntry = jarFile.getJarEntry(configFile);
|
||||
//
|
||||
// if (configEntry != null) {
|
||||
// // 5.2 读取配置文件
|
||||
// configJson = IoUtil.read(jarFile.getInputStream(configEntry), Charset.defaultCharset());
|
||||
// log.info("configJson:{}", configJson);
|
||||
// }
|
||||
//
|
||||
// // 5.3 读取script.js脚本
|
||||
// String scriptFile = "classes/script.js";
|
||||
// JarEntry scriptEntity = jarFile.getJarEntry(scriptFile);
|
||||
// if (scriptEntity != null) {
|
||||
// // 5.4 读取脚本文件
|
||||
// script = IoUtil.read(jarFile.getInputStream(scriptEntity), Charset.defaultCharset());
|
||||
// log.info("script:{}", script);
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// throw exception(PLUGIN_INSTALL_FAILED);
|
||||
// }
|
||||
|
||||
|
||||
// PluginState pluginState = pluginInfoUpdate.getPluginState();
|
||||
// if (pluginState == PluginState.STARTED) {
|
||||
// pluginInfoDo.setStatus(IotPluginStatusEnum.RUNNING.getStatus());
|
||||
// }
|
||||
// pluginInfoDo.setPluginId(pluginInfoUpdate.getPluginId());
|
||||
// pluginInfoDo.setFile(file.getOriginalFilename());
|
||||
// pluginInfoDo.setConfigSchema(configJson);
|
||||
// pluginInfoDo.setScript(script);
|
||||
//
|
||||
// PluginDescriptor pluginDescriptor = pluginInfoUpdate.getPluginDescriptor();
|
||||
// pluginInfoDo.setVersion(pluginDescriptor.getPluginVersion());
|
||||
// pluginInfoDo.setDescription(pluginDescriptor.getDescription());
|
||||
// pluginInfoMapper.updateById(pluginInfoDo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePluginStatus(Long id, Integer status) {
|
||||
// 1. 校验存在
|
||||
PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
|
||||
|
||||
// 插件状态无效
|
||||
if (!IotPluginStatusEnum.contains(status)) {
|
||||
throw exception(PLUGIN_STATUS_INVALID);
|
||||
}
|
||||
|
||||
// 插件包为空
|
||||
// String pluginId = pluginInfoDo.getPluginId();
|
||||
// if (StrUtil.isBlank(pluginId)) {
|
||||
// throw exception(PLUGIN_INFO_NOT_EXISTS);
|
||||
// }
|
||||
// com.gitee.starblues.core.PluginInfo pluginInfo = pluginOperator.getPluginInfo(pluginId);
|
||||
// if (pluginInfo != null) {
|
||||
// if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.RUNNING.getStatus()) && pluginInfo.getPluginState() != PluginState.STARTED) {
|
||||
// // 启动插件
|
||||
// pluginOperator.start(pluginId);
|
||||
// } else if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.STOPPED.getStatus()) && pluginInfo.getPluginState() == PluginState.STARTED) {
|
||||
// // 停止插件
|
||||
// pluginOperator.stop(pluginId);
|
||||
// }
|
||||
// } else {
|
||||
// // 已经停止,未获取到插件
|
||||
// if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) {
|
||||
// throw exception(PLUGIN_STATUS_INVALID);
|
||||
// }
|
||||
// }
|
||||
pluginInfoDo.setStatus(status);
|
||||
pluginInfoMapper.updateById(pluginInfoDo);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void startPlugins() {
|
||||
// while (!pluginOperator.inited()) {
|
||||
// Thread.sleep(1000L);
|
||||
// }
|
||||
|
||||
for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) {
|
||||
if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
|
||||
continue;
|
||||
}
|
||||
log.info("start plugin:{}", pluginInfoDO.getPluginId());
|
||||
try {
|
||||
// com.gitee.starblues.core.PluginInfo plugin = pluginOperator.getPluginInfo(pluginInfoDO.getPluginId());
|
||||
// if (plugin != null) {
|
||||
// pluginOperator.start(plugin.getPluginId());
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("start plugin error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package cn.iocoder.yudao.module.iot.service.plugininstance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
* IoT 插件实例 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface PluginInstanceService {
|
||||
|
||||
/**
|
||||
* 创建IoT 插件实例
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createPluginInstance(@Valid PluginInstanceSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新IoT 插件实例
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updatePluginInstance(@Valid PluginInstanceSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除IoT 插件实例
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deletePluginInstance(Long id);
|
||||
|
||||
/**
|
||||
* 获得IoT 插件实例
|
||||
*
|
||||
* @param id 编号
|
||||
* @return IoT 插件实例
|
||||
*/
|
||||
PluginInstanceDO getPluginInstance(Long id);
|
||||
|
||||
/**
|
||||
* 获得IoT 插件实例分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return IoT 插件实例分页
|
||||
*/
|
||||
PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO);
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package cn.iocoder.yudao.module.iot.service.plugininstance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTANCE_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* IoT 插件实例 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class PluginInstanceServiceImpl implements PluginInstanceService {
|
||||
|
||||
@Resource
|
||||
private PluginInstanceMapper pluginInstanceMapper;
|
||||
|
||||
@Override
|
||||
public Long createPluginInstance(PluginInstanceSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
PluginInstanceDO pluginInstance = BeanUtils.toBean(createReqVO, PluginInstanceDO.class);
|
||||
pluginInstanceMapper.insert(pluginInstance);
|
||||
// 返回
|
||||
return pluginInstance.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePluginInstance(PluginInstanceSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validatePluginInstanceExists(updateReqVO.getId());
|
||||
// 更新
|
||||
PluginInstanceDO updateObj = BeanUtils.toBean(updateReqVO, PluginInstanceDO.class);
|
||||
pluginInstanceMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePluginInstance(Long id) {
|
||||
// 校验存在
|
||||
validatePluginInstanceExists(id);
|
||||
// 删除
|
||||
pluginInstanceMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validatePluginInstanceExists(Long id) {
|
||||
if (pluginInstanceMapper.selectById(id) == null) {
|
||||
throw exception(PLUGIN_INSTANCE_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginInstanceDO getPluginInstance(Long id) {
|
||||
return pluginInstanceMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO) {
|
||||
return pluginInstanceMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
}
|
|
@ -45,6 +45,22 @@ public interface IotProductService {
|
|||
*/
|
||||
IotProductDO getProduct(Long id);
|
||||
|
||||
/**
|
||||
* 校验产品存在
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 产品
|
||||
*/
|
||||
IotProductDO validateProductExists(Long id);
|
||||
|
||||
/**
|
||||
* 校验产品存在
|
||||
*
|
||||
* @param productKey 产品 key
|
||||
* @return 产品
|
||||
*/
|
||||
IotProductDO validateProductExists(String productKey);
|
||||
|
||||
/**
|
||||
* 获得产品分页
|
||||
*
|
||||
|
|
|
@ -71,16 +71,26 @@ public class IotProductServiceImpl implements IotProductService {
|
|||
productMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private IotProductDO validateProductExists(Long id) {
|
||||
IotProductDO iotProductDO = productMapper.selectById(id);
|
||||
if (iotProductDO == null) {
|
||||
@Override
|
||||
public IotProductDO validateProductExists(Long id) {
|
||||
IotProductDO product = productMapper.selectById(id);
|
||||
if (product == null) {
|
||||
throw exception(PRODUCT_NOT_EXISTS);
|
||||
}
|
||||
return iotProductDO;
|
||||
return product;
|
||||
}
|
||||
|
||||
private void validateProductStatus(IotProductDO iotProductDO) {
|
||||
if (Objects.equals(iotProductDO.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) {
|
||||
@Override
|
||||
public IotProductDO validateProductExists(String productKey) {
|
||||
IotProductDO product = productMapper.selectByProductKey(productKey);
|
||||
if (product == null) {
|
||||
throw exception(PRODUCT_NOT_EXISTS);
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
private void validateProductStatus(IotProductDO product) {
|
||||
if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) {
|
||||
throw exception(PRODUCT_STATUS_NOT_DELETE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper">
|
||||
|
||||
<!--
|
||||
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
|
||||
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
|
||||
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
|
||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||
-->
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper">
|
||||
|
||||
<!--
|
||||
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
|
||||
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
|
||||
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
|
||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||
-->
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>yudao-module-iot</artifactId>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-iot-plugin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
物联网 模块 - 插件
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.pf4j</groupId>
|
||||
<artifactId>pf4j-spring</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* 占位
|
||||
*
|
||||
* TODO 芋艿:后续删除
|
||||
*/
|
||||
package cn.iocoder.yudao.module.iot.plugin;
|
Loading…
Reference in New Issue