Commit 2905efec28827af68918d24bcfdb52d6980003c8

Authored by alwayssuper
2 parents b3398019 e5ce1952

Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot

Showing 192 changed files with 4623 additions and 2981 deletions
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java
1 package cn.iocoder.yudao.framework.mybatis.config; 1 package cn.iocoder.yudao.framework.mybatis.config;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler; 5 import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
5 import com.baomidou.mybatisplus.annotation.DbType; 6 import com.baomidou.mybatisplus.annotation.DbType;
6 import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; 7 import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
7 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; 8 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
8 import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; 9 import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
  10 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
9 import com.baomidou.mybatisplus.extension.incrementer.*; 11 import com.baomidou.mybatisplus.extension.incrementer.*;
10 import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal; 12 import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
11 import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache; 13 import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache;
12 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 14 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
13 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 15 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
  16 +import com.fasterxml.jackson.databind.ObjectMapper;
14 import org.apache.ibatis.annotations.Mapper; 17 import org.apache.ibatis.annotations.Mapper;
15 import org.mybatis.spring.annotation.MapperScan; 18 import org.mybatis.spring.annotation.MapperScan;
16 import org.springframework.boot.autoconfigure.AutoConfiguration; 19 import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -18,6 +21,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -18,6 +21,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
18 import org.springframework.context.annotation.Bean; 21 import org.springframework.context.annotation.Bean;
19 import org.springframework.core.env.ConfigurableEnvironment; 22 import org.springframework.core.env.ConfigurableEnvironment;
20 23
  24 +import java.util.List;
21 import java.util.concurrent.TimeUnit; 25 import java.util.concurrent.TimeUnit;
22 26
23 /** 27 /**
@@ -73,4 +77,11 @@ public class YudaoMybatisAutoConfiguration { @@ -73,4 +77,11 @@ public class YudaoMybatisAutoConfiguration {
73 throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType)); 77 throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType));
74 } 78 }
75 79
  80 + @Bean
  81 + public JacksonTypeHandler jacksonTypeHandler(List<ObjectMapper> objectMappers) {
  82 + // 特殊:设置 JacksonTypeHandler 的 ObjectMapper!
  83 + JacksonTypeHandler.setObjectMapper(CollUtil.getFirst(objectMappers));
  84 + return new JacksonTypeHandler(Object.class);
  85 + }
  86 +
76 } 87 }
yudao-module-iot/pom.xml
@@ -7,7 +7,6 @@ @@ -7,7 +7,6 @@
7 <version>${revision}</version> 7 <version>${revision}</version>
8 </parent> 8 </parent>
9 <modules> 9 <modules>
10 - <module>yudao-module-iot-api</module>  
11 <module>yudao-module-iot-biz</module> 10 <module>yudao-module-iot-biz</module>
12 <module>yudao-module-iot-core</module> 11 <module>yudao-module-iot-core</module>
13 <module>yudao-module-iot-gateway</module> 12 <module>yudao-module-iot-gateway</module>
yudao-module-iot/yudao-module-iot-api/pom.xml deleted
1 -<?xml version="1.0" encoding="UTF-8"?>  
2 -<project xmlns="http://maven.apache.org/POM/4.0.0"  
3 - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4 - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
5 - <parent>  
6 - <artifactId>yudao-module-iot</artifactId>  
7 - <groupId>cn.iocoder.boot</groupId>  
8 - <version>${revision}</version>  
9 - </parent>  
10 - <modelVersion>4.0.0</modelVersion>  
11 - <artifactId>yudao-module-iot-api</artifactId>  
12 - <packaging>jar</packaging>  
13 -  
14 - <name>${project.artifactId}</name>  
15 - <!-- TODO 芋艿:需要在整理下,特别是 PF4J -->  
16 - <description>  
17 - 物联网 模块 API,暴露给其它模块调用  
18 - </description>  
19 -  
20 - <dependencies>  
21 - <dependency>  
22 - <groupId>cn.iocoder.boot</groupId>  
23 - <artifactId>yudao-common</artifactId>  
24 - </dependency>  
25 -  
26 - <!-- Web 相关 -->  
27 - <dependency>  
28 - <groupId>org.springframework</groupId>  
29 - <artifactId>spring-web</artifactId>  
30 - <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->  
31 - </dependency>  
32 -  
33 - <!-- 工具类相关 -->  
34 - <dependency>  
35 - <groupId>com.fasterxml.jackson.core</groupId>  
36 - <artifactId>jackson-databind</artifactId>  
37 - <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->  
38 - </dependency>  
39 -  
40 - <!-- <dependency>-->  
41 - <!-- <groupId>org.pf4j</groupId> &lt;!&ndash; PF4J:内置插件机制 &ndash;&gt;-->  
42 - <!-- <artifactId>pf4j-spring</artifactId>-->  
43 - <!-- </dependency>-->  
44 -  
45 - <!-- 参数校验 -->  
46 - <dependency>  
47 - <groupId>org.springframework.boot</groupId>  
48 - <artifactId>spring-boot-starter-validation</artifactId>  
49 - <optional>true</optional>  
50 - </dependency>  
51 - </dependencies>  
52 -  
53 -</project>  
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java deleted
1 -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;  
2 -  
3 -import jakarta.validation.constraints.NotEmpty;  
4 -import lombok.Data;  
5 -  
6 -import java.util.Map;  
7 -  
8 -/**  
9 - * IoT 设备【事件】上报 Request DTO  
10 - *  
11 - * @author 芋道源码  
12 - */  
13 -@Data  
14 -public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {  
15 -  
16 - /**  
17 - * 事件标识  
18 - */  
19 - @NotEmpty(message = "事件标识不能为空")  
20 - private String identifier;  
21 - /**  
22 - * 事件参数  
23 - */  
24 - private Map<String, Object> params;  
25 -  
26 -}  
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java deleted
1 -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;  
2 -  
3 -import jakarta.validation.constraints.NotEmpty;  
4 -import lombok.Data;  
5 -  
6 -import java.util.Map;  
7 -  
8 -/**  
9 - * IoT 设备【属性】上报 Request DTO  
10 - *  
11 - * @author 芋道源码  
12 - */  
13 -@Data  
14 -public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {  
15 -  
16 - /**  
17 - * 属性参数  
18 - */  
19 - @NotEmpty(message = "属性参数不能为空")  
20 - private Map<String, Object> properties;  
21 -  
22 -}  
23 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceUpstreamAbstractReqDTO.java deleted
1 -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;  
2 -  
3 -import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;  
4 -import com.fasterxml.jackson.databind.annotation.JsonSerialize;  
5 -import jakarta.validation.constraints.NotEmpty;  
6 -import lombok.Data;  
7 -  
8 -import java.time.LocalDateTime;  
9 -  
10 -/**  
11 - * IoT 设备上行的抽象 Request DTO  
12 - *  
13 - * @author 芋道源码  
14 - */  
15 -@Data  
16 -public abstract class IotDeviceUpstreamAbstractReqDTO {  
17 -  
18 - /**  
19 - * 请求编号  
20 - */  
21 - private String requestId;  
22 -  
23 - /**  
24 - * 插件实例的进程编号  
25 - */  
26 - private String processId;  
27 -  
28 - /**  
29 - * 产品标识  
30 - */  
31 - @NotEmpty(message = "产品标识不能为空")  
32 - private String productKey;  
33 - /**  
34 - * 设备名称  
35 - */  
36 - @NotEmpty(message = "设备名称不能为空")  
37 - private String deviceName;  
38 -  
39 - /**  
40 - * 上报时间  
41 - */  
42 - @JsonSerialize(using = TimestampLocalDateTimeSerializer.class) // 解决 iot plugins 序列化 LocalDateTime 是数组,导致无法解析的问题  
43 - private LocalDateTime reportTime;  
44 -  
45 -}  
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeTypeEnum.java deleted
1 -package cn.iocoder.yudao.module.iot.enums.rule;  
2 -  
3 -import cn.iocoder.yudao.framework.common.core.ArrayValuable;  
4 -import lombok.Getter;  
5 -import lombok.RequiredArgsConstructor;  
6 -  
7 -import java.util.Arrays;  
8 -  
9 -/**  
10 - * IoT 数据桥接的类型枚举  
11 - *  
12 - * @author 芋道源码  
13 - */  
14 -@RequiredArgsConstructor  
15 -@Getter  
16 -public enum IotDataBridgeTypeEnum implements ArrayValuable<Integer> {  
17 -  
18 - HTTP(1, "HTTP"),  
19 - TCP(2, "TCP"),  
20 - WEBSOCKET(3, "WEBSOCKET"),  
21 -  
22 - MQTT(10, "MQTT"),  
23 -  
24 - DATABASE(20, "DATABASE"),  
25 - REDIS_STREAM(21, "REDIS_STREAM"),  
26 -  
27 - ROCKETMQ(30, "ROCKETMQ"),  
28 - RABBITMQ(31, "RABBITMQ"),  
29 - KAFKA(32, "KAFKA");  
30 -  
31 - private final Integer type;  
32 -  
33 - private final String name;  
34 -  
35 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgeTypeEnum::getType).toArray(Integer[]::new);  
36 -  
37 - @Override  
38 - public Integer[] array() {  
39 - return ARRAYS;  
40 - }  
41 -  
42 -}  
yudao-module-iot/yudao-module-iot-biz/pom.xml
@@ -25,11 +25,6 @@ @@ -25,11 +25,6 @@
25 </dependency> 25 </dependency>
26 <dependency> 26 <dependency>
27 <groupId>cn.iocoder.boot</groupId> 27 <groupId>cn.iocoder.boot</groupId>
28 - <artifactId>yudao-module-iot-api</artifactId>  
29 - <version>${revision}</version>  
30 - </dependency>  
31 - <dependency>  
32 - <groupId>cn.iocoder.boot</groupId>  
33 <artifactId>yudao-module-iot-core</artifactId> 28 <artifactId>yudao-module-iot-core</artifactId>
34 <version>${revision}</version> 29 <version>${revision}</version>
35 </dependency> 30 </dependency>
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertConfigController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.alert;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigRespVO;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO;
  10 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
  11 +import cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService;
  12 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  13 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  14 +import io.swagger.v3.oas.annotations.Operation;
  15 +import io.swagger.v3.oas.annotations.Parameter;
  16 +import io.swagger.v3.oas.annotations.tags.Tag;
  17 +import jakarta.annotation.Resource;
  18 +import jakarta.validation.Valid;
  19 +
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.bind.annotation.*;
  23 +
  24 +import java.util.List;
  25 +import java.util.Map;
  26 +import java.util.Objects;
  27 +import java.util.stream.Collectors;
  28 +
  29 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  30 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
  32 +
  33 +@Tag(name = "管理后台 - IoT 告警配置")
  34 +@RestController
  35 +@RequestMapping("/iot/alert-config")
  36 +@Validated
  37 +public class IotAlertConfigController {
  38 +
  39 + @Resource
  40 + private IotAlertConfigService alertConfigService;
  41 +
  42 + @Resource
  43 + private AdminUserApi adminUserApi;
  44 +
  45 + @PostMapping("/create")
  46 + @Operation(summary = "创建告警配置")
  47 + @PreAuthorize("@ss.hasPermission('iot:alert-config:create')")
  48 + public CommonResult<Long> createAlertConfig(@Valid @RequestBody IotAlertConfigSaveReqVO createReqVO) {
  49 + return success(alertConfigService.createAlertConfig(createReqVO));
  50 + }
  51 +
  52 + @PutMapping("/update")
  53 + @Operation(summary = "更新告警配置")
  54 + @PreAuthorize("@ss.hasPermission('iot:alert-config:update')")
  55 + public CommonResult<Boolean> updateAlertConfig(@Valid @RequestBody IotAlertConfigSaveReqVO updateReqVO) {
  56 + alertConfigService.updateAlertConfig(updateReqVO);
  57 + return success(true);
  58 + }
  59 +
  60 + @DeleteMapping("/delete")
  61 + @Operation(summary = "删除告警配置")
  62 + @Parameter(name = "id", description = "编号", required = true)
  63 + @PreAuthorize("@ss.hasPermission('iot:alert-config:delete')")
  64 + public CommonResult<Boolean> deleteAlertConfig(@RequestParam("id") Long id) {
  65 + alertConfigService.deleteAlertConfig(id);
  66 + return success(true);
  67 + }
  68 +
  69 + @GetMapping("/get")
  70 + @Operation(summary = "获得告警配置")
  71 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  72 + @PreAuthorize("@ss.hasPermission('iot:alert-config:query')")
  73 + public CommonResult<IotAlertConfigRespVO> getAlertConfig(@RequestParam("id") Long id) {
  74 + IotAlertConfigDO alertConfig = alertConfigService.getAlertConfig(id);
  75 + return success(BeanUtils.toBean(alertConfig, IotAlertConfigRespVO.class));
  76 + }
  77 +
  78 + @GetMapping("/page")
  79 + @Operation(summary = "获得告警配置分页")
  80 + @PreAuthorize("@ss.hasPermission('iot:alert-config:query')")
  81 + public CommonResult<PageResult<IotAlertConfigRespVO>> getAlertConfigPage(@Valid IotAlertConfigPageReqVO pageReqVO) {
  82 + PageResult<IotAlertConfigDO> pageResult = alertConfigService.getAlertConfigPage(pageReqVO);
  83 +
  84 + // 转换返回
  85 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  86 + convertSetByFlatMap(pageResult.getList(), config -> config.getReceiveUserIds().stream()));
  87 + return success(BeanUtils.toBean(pageResult, IotAlertConfigRespVO.class, vo -> {
  88 + vo.setReceiveUserNames(vo.getReceiveUserIds().stream()
  89 + .map(userMap::get)
  90 + .filter(Objects::nonNull)
  91 + .map(AdminUserRespDTO::getNickname)
  92 + .collect(Collectors.toList()));
  93 + }));
  94 + }
  95 +
  96 + @GetMapping("/simple-list")
  97 + @Operation(summary = "获得告警配置简单列表", description = "只包含被开启的告警配置,主要用于前端的下拉选项")
  98 + @PreAuthorize("@ss.hasPermission('iot:alert-config:query')")
  99 + public CommonResult<List<IotAlertConfigRespVO>> getAlertConfigSimpleList() {
  100 + List<IotAlertConfigDO> list = alertConfigService.getAlertConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());
  101 + return success(convertList(list, config -> // 只返回 id、name 字段
  102 + new IotAlertConfigRespVO().setId(config.getId()).setName(config.getName())));
  103 + }
  104 +
  105 +}
0 \ No newline at end of file 106 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertRecordController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.alert;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordProcessReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordRespVO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
  10 +import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.*;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +import static java.util.Collections.singleton;
  22 +
  23 +@Tag(name = "管理后台 - IoT 告警记录")
  24 +@RestController
  25 +@RequestMapping("/iot/alert-record")
  26 +@Validated
  27 +public class IotAlertRecordController {
  28 +
  29 + @Resource
  30 + private IotAlertRecordService alertRecordService;
  31 +
  32 + @GetMapping("/get")
  33 + @Operation(summary = "获得告警记录")
  34 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  35 + @PreAuthorize("@ss.hasPermission('iot:alert-record:query')")
  36 + public CommonResult<IotAlertRecordRespVO> getAlertRecord(@RequestParam("id") Long id) {
  37 + IotAlertRecordDO alertRecord = alertRecordService.getAlertRecord(id);
  38 + return success(BeanUtils.toBean(alertRecord, IotAlertRecordRespVO.class));
  39 + }
  40 +
  41 + @GetMapping("/page")
  42 + @Operation(summary = "获得告警记录分页")
  43 + @PreAuthorize("@ss.hasPermission('iot:alert-record:query')")
  44 + public CommonResult<PageResult<IotAlertRecordRespVO>> getAlertRecordPage(@Valid IotAlertRecordPageReqVO pageReqVO) {
  45 + PageResult<IotAlertRecordDO> pageResult = alertRecordService.getAlertRecordPage(pageReqVO);
  46 + return success(BeanUtils.toBean(pageResult, IotAlertRecordRespVO.class));
  47 + }
  48 +
  49 + @PutMapping("/process")
  50 + @Operation(summary = "处理告警记录")
  51 + @PreAuthorize("@ss.hasPermission('iot:alert-record:process')")
  52 + public CommonResult<Boolean> processAlertRecord(@Valid @RequestBody IotAlertRecordProcessReqVO processReqVO) {
  53 + alertRecordService.processAlertRecordList(singleton(processReqVO.getId()), processReqVO.getProcessRemark());
  54 + return success(true);
  55 + }
  56 +
  57 +}
0 \ No newline at end of file 58 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - IoT 告警配置分页 Request VO")
  13 +@Data
  14 +public class IotAlertConfigPageReqVO extends PageParam {
  15 +
  16 + @Schema(description = "配置名称", example = "赵六")
  17 + private String name;
  18 +
  19 + @Schema(description = "配置状态", example = "1")
  20 + private Integer status;
  21 +
  22 + @Schema(description = "创建时间")
  23 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  24 + private LocalDateTime[] createTime;
  25 +
  26 +}
0 \ No newline at end of file 27 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - IoT 告警配置 Response VO")
  10 +@Data
  11 +public class IotAlertConfigRespVO {
  12 +
  13 + @Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3566")
  14 + private Long id;
  15 +
  16 + @Schema(description = "配置名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  17 + private String name;
  18 +
  19 + @Schema(description = "配置描述", example = "你猜")
  20 + private String description;
  21 +
  22 + @Schema(description = "告警级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  23 + private Integer level;
  24 +
  25 + @Schema(description = "配置状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  26 + private Integer status;
  27 +
  28 + @Schema(description = "关联的场景联动规则编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
  29 + private List<Long> sceneRuleIds;
  30 +
  31 + @Schema(description = "接收的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "100,200")
  32 + private List<Long> receiveUserIds;
  33 +
  34 + @Schema(description = "接收的用户名称数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三,李四")
  35 + private List<String> receiveUserNames;
  36 +
  37 + @Schema(description = "接收的类型数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
  38 + private List<Integer> receiveTypes;
  39 +
  40 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  41 + private LocalDateTime createTime;
  42 +
  43 +}
0 \ No newline at end of file 44 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.NotEmpty;
  7 +import jakarta.validation.constraints.NotNull;
  8 +import lombok.Data;
  9 +
  10 +import java.util.List;
  11 +
  12 +@Schema(description = "管理后台 - IoT 告警配置新增/修改 Request VO")
  13 +@Data
  14 +public class IotAlertConfigSaveReqVO {
  15 +
  16 + @Schema(description = "配置编号", example = "3566")
  17 + private Long id;
  18 +
  19 + @Schema(description = "配置名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  20 + @NotEmpty(message = "配置名称不能为空")
  21 + private String name;
  22 +
  23 + @Schema(description = "配置描述", example = "你猜")
  24 + private String description;
  25 +
  26 + @Schema(description = "告警级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  27 + @NotNull(message = "告警级别不能为空")
  28 + private Integer level;
  29 +
  30 + @Schema(description = "配置状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  31 + @NotNull(message = "配置状态不能为空")
  32 + @InEnum(CommonStatusEnum.class)
  33 + private Integer status;
  34 +
  35 + @Schema(description = "关联的场景联动规则编号数组")
  36 + @NotEmpty(message = "关联的场景联动规则编号数组不能为空")
  37 + private List<Long> sceneRuleIds;
  38 +
  39 + @Schema(description = "接收的用户编号数组")
  40 + @NotEmpty(message = "接收的用户编号数组不能为空")
  41 + private List<Long> receiveUserIds;
  42 +
  43 + @Schema(description = "接收的类型数组")
  44 + @NotEmpty(message = "接收的类型数组不能为空")
  45 + private List<Integer> receiveTypes;
  46 +
  47 +}
0 \ No newline at end of file 48 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - IoT 告警记录分页 Request VO")
  13 +@Data
  14 +public class IotAlertRecordPageReqVO extends PageParam {
  15 +
  16 + @Schema(description = "告警配置编号", example = "29320")
  17 + private Long configId;
  18 +
  19 + @Schema(description = "告警级别", example = "1")
  20 + private Integer level;
  21 +
  22 + @Schema(description = "产品编号", example = "2050")
  23 + private Long productId;
  24 +
  25 + @Schema(description = "设备编号", example = "21727")
  26 + private String deviceId;
  27 +
  28 + @Schema(description = "是否处理", example = "true")
  29 + private Boolean processStatus;
  30 +
  31 + @Schema(description = "创建时间")
  32 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  33 + private LocalDateTime[] createTime;
  34 +
  35 +}
0 \ No newline at end of file 36 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordProcessReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - IoT 告警记录处理 Request VO")
  8 +@Data
  9 +public class IotAlertRecordProcessReqVO {
  10 +
  11 + @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  12 + @NotNull(message = "记录编号不能为空")
  13 + private Long id;
  14 +
  15 + @Schema(description = "处理结果(备注)", requiredMode = Schema.RequiredMode.REQUIRED, example = "已处理告警,问题已解决")
  16 + private String processRemark;
  17 +
  18 +}
0 \ No newline at end of file 19 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +@Schema(description = "管理后台 - IoT 告警记录 Response VO")
  10 +@Data
  11 +public class IotAlertRecordRespVO {
  12 +
  13 + @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19904")
  14 + private Long id;
  15 +
  16 + @Schema(description = "告警配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29320")
  17 + private Long configId;
  18 +
  19 + @Schema(description = "告警名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
  20 + private String configName;
  21 +
  22 + @Schema(description = "告警级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  23 + private Integer configLevel;
  24 +
  25 + @Schema(description = "产品编号", example = "2050")
  26 + private Long productId;
  27 +
  28 + @Schema(description = "设备编号", example = "21727")
  29 + private Long deviceId;
  30 +
  31 + @Schema(description = "触发的设备消息")
  32 + private IotDeviceMessage deviceMessage;
  33 +
  34 + @Schema(description = "是否处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  35 + private Boolean processStatus;
  36 +
  37 + @Schema(description = "处理结果(备注)", example = "你说的对")
  38 + private String processRemark;
  39 +
  40 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  41 + private LocalDateTime createTime;
  42 +
  43 +}
0 \ No newline at end of file 44 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
@@ -9,9 +9,9 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; @@ -9,9 +9,9 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; 9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
10 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 10 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
11 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 11 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
12 -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;  
13 import io.swagger.v3.oas.annotations.Operation; 12 import io.swagger.v3.oas.annotations.Operation;
14 import io.swagger.v3.oas.annotations.Parameter; 13 import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.Parameters;
15 import io.swagger.v3.oas.annotations.tags.Tag; 15 import io.swagger.v3.oas.annotations.tags.Tag;
16 import jakarta.annotation.Resource; 16 import jakarta.annotation.Resource;
17 import jakarta.servlet.http.HttpServletResponse; 17 import jakarta.servlet.http.HttpServletResponse;
@@ -38,8 +38,6 @@ public class IotDeviceController { @@ -38,8 +38,6 @@ public class IotDeviceController {
38 38
39 @Resource 39 @Resource
40 private IotDeviceService deviceService; 40 private IotDeviceService deviceService;
41 - @Resource  
42 - private IotDeviceMessageService deviceMessageService;  
43 41
44 @PostMapping("/create") 42 @PostMapping("/create")
45 @Operation(summary = "创建设备") 43 @Operation(summary = "创建设备")
@@ -124,12 +122,17 @@ public class IotDeviceController { @@ -124,12 +122,17 @@ public class IotDeviceController {
124 122
125 @GetMapping("/simple-list") 123 @GetMapping("/simple-list")
126 @Operation(summary = "获取设备的精简信息列表", description = "主要用于前端的下拉选项") 124 @Operation(summary = "获取设备的精简信息列表", description = "主要用于前端的下拉选项")
127 - @Parameter(name = "deviceType", description = "设备类型", example = "1")  
128 - public CommonResult<List<IotDeviceRespVO>> getSimpleDeviceList(  
129 - @RequestParam(value = "deviceType", required = false) Integer deviceType) {  
130 - List<IotDeviceDO> list = deviceService.getDeviceListByDeviceType(deviceType);  
131 - return success(convertList(list, device -> // 只返回 id、name 字段  
132 - new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName()))); 125 + @Parameters({
  126 + @Parameter(name = "deviceType", description = "设备类型", example = "1"),
  127 + @Parameter(name = "productId", description = "产品编号", example = "1024")
  128 + })
  129 + public CommonResult<List<IotDeviceRespVO>> getDeviceSimpleList(
  130 + @RequestParam(value = "deviceType", required = false) Integer deviceType,
  131 + @RequestParam(value = "productId", required = false) Long productId) {
  132 + List<IotDeviceDO> list = deviceService.getDeviceListByCondition(deviceType, productId);
  133 + return success(convertList(list, device -> // 只返回 id、name、productId 字段
  134 + new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName())
  135 + .setProductId(device.getProductId())));
133 } 136 }
134 137
135 @PostMapping("/import") 138 @PostMapping("/import")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java
@@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyDetailRespVO; 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyDetailRespVO;
7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO;
8 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO; 8 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO;
9 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; 9 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;
10 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 10 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
11 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; 11 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
12 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; 12 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyDetailRespVO.java
1 package cn.iocoder.yudao.module.iot.controller.admin.device.vo.property; 1 package cn.iocoder.yudao.module.iot.controller.admin.device.vo.property;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; 3 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDataSpecs;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 5 import lombok.Data;
6 6
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java
@@ -3,12 +3,14 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota; @@ -3,12 +3,14 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO;
6 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO;
7 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareRespVO; 8 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareRespVO;
8 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO;  
9 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO; 9 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO;
10 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; 10 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
  11 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
11 import cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService; 12 import cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService;
  13 +import cn.iocoder.yudao.module.iot.service.product.IotProductService;
12 import io.swagger.v3.oas.annotations.Operation; 14 import io.swagger.v3.oas.annotations.Operation;
13 import io.swagger.v3.oas.annotations.tags.Tag; 15 import io.swagger.v3.oas.annotations.tags.Tag;
14 import jakarta.annotation.Resource; 16 import jakarta.annotation.Resource;
@@ -21,12 +23,14 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -21,12 +23,14 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
21 23
22 @Tag(name = "管理后台 - IoT OTA 固件") 24 @Tag(name = "管理后台 - IoT OTA 固件")
23 @RestController 25 @RestController
24 -@RequestMapping("/iot/ota-firmware") 26 +@RequestMapping("/iot/ota/firmware")
25 @Validated 27 @Validated
26 public class IotOtaFirmwareController { 28 public class IotOtaFirmwareController {
27 29
28 @Resource 30 @Resource
29 private IotOtaFirmwareService otaFirmwareService; 31 private IotOtaFirmwareService otaFirmwareService;
  32 + @Resource
  33 + private IotProductService productService;
30 34
31 @PostMapping("/create") 35 @PostMapping("/create")
32 @Operation(summary = "创建 OTA 固件") 36 @Operation(summary = "创建 OTA 固件")
@@ -47,8 +51,16 @@ public class IotOtaFirmwareController { @@ -47,8 +51,16 @@ public class IotOtaFirmwareController {
47 @Operation(summary = "获得 OTA 固件") 51 @Operation(summary = "获得 OTA 固件")
48 @PreAuthorize("@ss.hasPermission('iot:ota-firmware:query')") 52 @PreAuthorize("@ss.hasPermission('iot:ota-firmware:query')")
49 public CommonResult<IotOtaFirmwareRespVO> getOtaFirmware(@RequestParam("id") Long id) { 53 public CommonResult<IotOtaFirmwareRespVO> getOtaFirmware(@RequestParam("id") Long id) {
50 - IotOtaFirmwareDO otaFirmware = otaFirmwareService.getOtaFirmware(id);  
51 - return success(BeanUtils.toBean(otaFirmware, IotOtaFirmwareRespVO.class)); 54 + IotOtaFirmwareDO firmware = otaFirmwareService.getOtaFirmware(id);
  55 + if (firmware == null) {
  56 + return success(null);
  57 + }
  58 + return success(BeanUtils.toBean(firmware, IotOtaFirmwareRespVO.class, o -> {
  59 + IotProductDO product = productService.getProduct(firmware.getProductId());
  60 + if (product != null) {
  61 + o.setProductName(product.getName());
  62 + }
  63 + }));
52 } 64 }
53 65
54 @GetMapping("/page") 66 @GetMapping("/page")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.ota;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskRespVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
  10 +import cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.*;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +
  22 +@Tag(name = "管理后台 - IoT OTA 升级任务")
  23 +@RestController
  24 +@RequestMapping("/iot/ota/task")
  25 +@Validated
  26 +public class IotOtaTaskController {
  27 +
  28 + @Resource
  29 + private IotOtaTaskService otaTaskService;
  30 +
  31 + @PostMapping("/create")
  32 + @Operation(summary = "创建 OTA 升级任务")
  33 + @PreAuthorize(value = "@ss.hasPermission('iot:ota-task:create')")
  34 + public CommonResult<Long> createOtaTask(@Valid @RequestBody IotOtaTaskCreateReqVO createReqVO) {
  35 + return success(otaTaskService.createOtaTask(createReqVO));
  36 + }
  37 +
  38 + @PostMapping("/cancel")
  39 + @Operation(summary = "取消 OTA 升级任务")
  40 + @Parameter(name = "id", description = "升级任务编号", required = true)
  41 + @PreAuthorize(value = "@ss.hasPermission('iot:ota-task:cancel')")
  42 + public CommonResult<Boolean> cancelOtaTask(@RequestParam("id") Long id) {
  43 + otaTaskService.cancelOtaTask(id);
  44 + return success(true);
  45 + }
  46 +
  47 + @GetMapping("/page")
  48 + @Operation(summary = "获得 OTA 升级任务分页")
  49 + @PreAuthorize(value = "@ss.hasPermission('iot:ota-task:query')")
  50 + public CommonResult<PageResult<IotOtaTaskRespVO>> getOtaTaskPage(@Valid IotOtaTaskPageReqVO pageReqVO) {
  51 + PageResult<IotOtaTaskDO> pageResult = otaTaskService.getOtaTaskPage(pageReqVO);
  52 + return success(BeanUtils.toBean(pageResult, IotOtaTaskRespVO.class));
  53 + }
  54 +
  55 + @GetMapping("/get")
  56 + @Operation(summary = "获得 OTA 升级任务")
  57 + @Parameter(name = "id", description = "升级任务编号", required = true, example = "1024")
  58 + @PreAuthorize(value = "@ss.hasPermission('iot:ota-task:query')")
  59 + public CommonResult<IotOtaTaskRespVO> getOtaTask(@RequestParam("id") Long id) {
  60 + IotOtaTaskDO upgradeTask = otaTaskService.getOtaTask(id);
  61 + return success(BeanUtils.toBean(upgradeTask, IotOtaTaskRespVO.class));
  62 + }
  63 +
  64 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskRecordController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.ota;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordRespVO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  10 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
  11 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
  12 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  13 +import cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService;
  14 +import cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService;
  15 +import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.Parameters;
  18 +import io.swagger.v3.oas.annotations.tags.Tag;
  19 +import jakarta.annotation.Resource;
  20 +import jakarta.validation.Valid;
  21 +import org.dromara.hutool.core.collection.CollUtil;
  22 +import org.springframework.security.access.prepost.PreAuthorize;
  23 +import org.springframework.validation.annotation.Validated;
  24 +import org.springframework.web.bind.annotation.GetMapping;
  25 +import org.springframework.web.bind.annotation.RequestMapping;
  26 +import org.springframework.web.bind.annotation.RequestParam;
  27 +import org.springframework.web.bind.annotation.RestController;
  28 +
  29 +import java.util.Map;
  30 +
  31 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  33 +
  34 +@Tag(name = "管理后台 - IoT OTA 升级任务记录")
  35 +@RestController
  36 +@RequestMapping("/iot/ota/task/record")
  37 +@Validated
  38 +public class IotOtaTaskRecordController {
  39 +
  40 + @Resource
  41 + private IotOtaTaskRecordService otaTaskRecordService;
  42 + @Resource
  43 + private IotDeviceService deviceService;
  44 + @Resource
  45 + private IotOtaFirmwareService otaFirmwareService;
  46 +
  47 + @GetMapping("/get-status-statistics")
  48 + @Operation(summary = "获得 OTA 升级记录状态统计")
  49 + @Parameters({
  50 + @Parameter(name = "firmwareId", description = "固件编号", example = "1024"),
  51 + @Parameter(name = "taskId", description = "升级任务编号", example = "2048")
  52 + })
  53 + @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
  54 + public CommonResult<Map<Integer, Long>> getOtaTaskRecordStatusStatistics(
  55 + @RequestParam(value = "firmwareId", required = false) Long firmwareId,
  56 + @RequestParam(value = "taskId", required = false) Long taskId) {
  57 + return success(otaTaskRecordService.getOtaTaskRecordStatusStatistics(firmwareId, taskId));
  58 + }
  59 +
  60 + @GetMapping("/page")
  61 + @Operation(summary = "获得 OTA 升级记录分页")
  62 + @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
  63 + public CommonResult<PageResult<IotOtaTaskRecordRespVO>> getOtaTaskRecordPage(
  64 + @Valid IotOtaTaskRecordPageReqVO pageReqVO) {
  65 + PageResult<IotOtaTaskRecordDO> pageResult = otaTaskRecordService.getOtaTaskRecordPage(pageReqVO);
  66 + if (CollUtil.isEmpty(pageResult.getList())) {
  67 + return success(PageResult.empty());
  68 + }
  69 +
  70 + // 批量查询固件信息
  71 + Map<Long, IotOtaFirmwareDO> firmwareMap = otaFirmwareService.getOtaFirmwareMap(
  72 + convertSet(pageResult.getList(), IotOtaTaskRecordDO::getFromFirmwareId));
  73 + Map<Long, IotDeviceDO> deviceMap = deviceService.getDeviceMap(
  74 + convertSet(pageResult.getList(), IotOtaTaskRecordDO::getDeviceId));
  75 + // 转换为响应 VO
  76 + return success(BeanUtils.toBean(pageResult, IotOtaTaskRecordRespVO.class, (vo) -> {
  77 + MapUtils.findAndThen(firmwareMap, vo.getFromFirmwareId(), firmware ->
  78 + vo.setFromFirmwareVersion(firmware.getVersion()));
  79 + MapUtils.findAndThen(deviceMap, vo.getDeviceId(), device ->
  80 + vo.setDeviceName(device.getDeviceName()));
  81 + }));
  82 + }
  83 +
  84 + @GetMapping("/get")
  85 + @Operation(summary = "获得 OTA 升级记录")
  86 + @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
  87 + @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024")
  88 + public CommonResult<IotOtaTaskRecordRespVO> getOtaTaskRecord(@RequestParam("id") Long id) {
  89 + IotOtaTaskRecordDO upgradeRecord = otaTaskRecordService.getOtaTaskRecord(id);
  90 + return success(BeanUtils.toBean(upgradeRecord, IotOtaTaskRecordRespVO.class));
  91 + }
  92 +
  93 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.ota;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
4 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
5 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO;  
7 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordRespVO;  
8 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO;  
9 -import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeRecordService;  
10 -import io.swagger.v3.oas.annotations.Operation;  
11 -import io.swagger.v3.oas.annotations.Parameter;  
12 -import io.swagger.v3.oas.annotations.tags.Tag;  
13 -import jakarta.annotation.Resource;  
14 -import jakarta.validation.Valid;  
15 -import org.springframework.security.access.prepost.PreAuthorize;  
16 -import org.springframework.validation.annotation.Validated;  
17 -import org.springframework.web.bind.annotation.*;  
18 -  
19 -import java.util.Map;  
20 -  
21 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
22 -  
23 -@Tag(name = "管理后台 - IoT OTA 升级记录")  
24 -@RestController  
25 -@RequestMapping("/iot/ota-upgrade-record")  
26 -@Validated  
27 -public class IotOtaUpgradeRecordController {  
28 -  
29 - @Resource  
30 - private IotOtaUpgradeRecordService upgradeRecordService;  
31 -  
32 - @GetMapping("/get-statistics")  
33 - @Operation(summary = "固件升级设备统计")  
34 - @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')")  
35 - @Parameter(name = "firmwareId", description = "固件编号", required = true, example = "1024")  
36 - public CommonResult<Map<Integer, Long>> getOtaUpgradeRecordStatistics(@RequestParam(value = "firmwareId") Long firmwareId) {  
37 - return success(upgradeRecordService.getOtaUpgradeRecordStatistics(firmwareId));  
38 - }  
39 -  
40 - @GetMapping("/get-count")  
41 - @Operation(summary = "获得升级记录分页 tab 数量")  
42 - @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')")  
43 - public CommonResult<Map<Integer, Long>> getOtaUpgradeRecordCount(  
44 - @Valid IotOtaUpgradeRecordPageReqVO pageReqVO) {  
45 - return success(upgradeRecordService.getOtaUpgradeRecordCount(pageReqVO));  
46 - }  
47 -  
48 - @GetMapping("/page")  
49 - @Operation(summary = "获得升级记录分页")  
50 - @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')")  
51 - public CommonResult<PageResult<IotOtaUpgradeRecordRespVO>> getUpgradeRecordPage(  
52 - @Valid IotOtaUpgradeRecordPageReqVO pageReqVO) {  
53 - PageResult<IotOtaUpgradeRecordDO> pageResult = upgradeRecordService.getUpgradeRecordPage(pageReqVO);  
54 - return success(BeanUtils.toBean(pageResult, IotOtaUpgradeRecordRespVO.class));  
55 - }  
56 -  
57 - @GetMapping("/get")  
58 - @Operation(summary = "获得升级记录")  
59 - @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')")  
60 - @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024")  
61 - public CommonResult<IotOtaUpgradeRecordRespVO> getUpgradeRecord(@RequestParam("id") Long id) {  
62 - IotOtaUpgradeRecordDO upgradeRecord = upgradeRecordService.getUpgradeRecord(id);  
63 - return success(BeanUtils.toBean(upgradeRecord, IotOtaUpgradeRecordRespVO.class));  
64 - }  
65 -  
66 - @PutMapping("/retry")  
67 - @Operation(summary = "重试升级记录")  
68 - @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:retry')")  
69 - @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024")  
70 - public CommonResult<Boolean> retryUpgradeRecord(@RequestParam("id") Long id) {  
71 - upgradeRecordService.retryUpgradeRecord(id);  
72 - return success(true);  
73 - }  
74 -  
75 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.ota;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
4 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
5 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskPageReqVO;  
7 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskRespVO;  
8 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskSaveReqVO;  
9 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO;  
10 -import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeTaskService;  
11 -import io.swagger.v3.oas.annotations.Operation;  
12 -import io.swagger.v3.oas.annotations.Parameter;  
13 -import io.swagger.v3.oas.annotations.tags.Tag;  
14 -import jakarta.annotation.Resource;  
15 -import jakarta.validation.Valid;  
16 -import org.springframework.security.access.prepost.PreAuthorize;  
17 -import org.springframework.validation.annotation.Validated;  
18 -import org.springframework.web.bind.annotation.*;  
19 -  
20 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
21 -  
22 -@Tag(name = "管理后台 - IoT OTA 升级任务")  
23 -@RestController  
24 -@RequestMapping("/iot/ota-upgrade-task")  
25 -@Validated  
26 -public class IotOtaUpgradeTaskController {  
27 -  
28 - @Resource  
29 - private IotOtaUpgradeTaskService upgradeTaskService;  
30 -  
31 - @PostMapping("/create")  
32 - @Operation(summary = "创建升级任务")  
33 - @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:create')")  
34 - public CommonResult<Long> createUpgradeTask(@Valid @RequestBody IotOtaUpgradeTaskSaveReqVO createReqVO) {  
35 - return success(upgradeTaskService.createUpgradeTask(createReqVO));  
36 - }  
37 -  
38 - @PostMapping("/cancel")  
39 - @Operation(summary = "取消升级任务")  
40 - @Parameter(name = "id", description = "升级任务编号", required = true)  
41 - @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:cancel')")  
42 - public CommonResult<Boolean> cancelUpgradeTask(@RequestParam("id") Long id) {  
43 - upgradeTaskService.cancelUpgradeTask(id);  
44 - return success(true);  
45 - }  
46 -  
47 - @GetMapping("/page")  
48 - @Operation(summary = "获得升级任务分页")  
49 - @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:query')")  
50 - public CommonResult<PageResult<IotOtaUpgradeTaskRespVO>> getUpgradeTaskPage(@Valid IotOtaUpgradeTaskPageReqVO pageReqVO) {  
51 - PageResult<IotOtaUpgradeTaskDO> pageResult = upgradeTaskService.getUpgradeTaskPage(pageReqVO);  
52 - return success(BeanUtils.toBean(pageResult, IotOtaUpgradeTaskRespVO.class));  
53 - }  
54 -  
55 - @GetMapping("/get")  
56 - @Operation(summary = "获得升级任务")  
57 - @Parameter(name = "id", description = "升级任务编号", required = true, example = "1024")  
58 - @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:query')")  
59 - public CommonResult<IotOtaUpgradeTaskRespVO> getUpgradeTask(@RequestParam("id") Long id) {  
60 - IotOtaUpgradeTaskDO upgradeTask = upgradeTaskService.getUpgradeTask(id);  
61 - return success(BeanUtils.toBean(upgradeTask, IotOtaUpgradeTaskRespVO.class));  
62 - }  
63 -  
64 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java
@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
4 import jakarta.validation.constraints.NotEmpty; 4 import jakarta.validation.constraints.NotEmpty;
5 import jakarta.validation.constraints.NotNull; 5 import jakarta.validation.constraints.NotNull;
6 import lombok.Data; 6 import lombok.Data;
  7 +import org.hibernate.validator.constraints.URL;
7 8
8 @Schema(description = "管理后台 - IoT OTA 固件创建 Request VO") 9 @Schema(description = "管理后台 - IoT OTA 固件创建 Request VO")
9 @Data 10 @Data
@@ -22,17 +23,11 @@ public class IotOtaFirmwareCreateReqVO { @@ -22,17 +23,11 @@ public class IotOtaFirmwareCreateReqVO {
22 23
23 @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 24 @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
24 @NotNull(message = "产品编号不能为空") 25 @NotNull(message = "产品编号不能为空")
25 - private String productId; 26 + private Long productId;
26 27
27 - @Schema(description = "签名方式", example = "MD5")  
28 - // TODO @li:是不是必传哈  
29 - private String signMethod;  
30 -  
31 - @Schema(description = "固件文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao-firmware.zip") 28 + @Schema(description = "固件文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.zip")
32 @NotEmpty(message = "固件文件 URL 不能为空") 29 @NotEmpty(message = "固件文件 URL 不能为空")
  30 + @URL(message = "固件文件 URL 格式错误")
33 private String fileUrl; 31 private String fileUrl;
34 32
35 - @Schema(description = "自定义信息,建议使用 JSON 格式", example = "{\"key1\":\"value1\",\"key2\":\"value2\"}")  
36 - private String information;  
37 -  
38 } 33 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java
@@ -3,21 +3,24 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; @@ -3,21 +3,24 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 5 import lombok.Data;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
6 11
7 -@Data  
8 @Schema(description = "管理后台 - IoT OTA 固件分页 Request VO") 12 @Schema(description = "管理后台 - IoT OTA 固件分页 Request VO")
  13 +@Data
9 public class IotOtaFirmwarePageReqVO extends PageParam { 14 public class IotOtaFirmwarePageReqVO extends PageParam {
10 15
11 - /**  
12 - * 固件名称  
13 - */  
14 @Schema(description = "固件名称", example = "智能开关固件") 16 @Schema(description = "固件名称", example = "智能开关固件")
15 private String name; 17 private String name;
16 18
17 - /**  
18 - * 产品标识  
19 - */  
20 @Schema(description = "产品标识", example = "1024") 19 @Schema(description = "产品标识", example = "1024")
21 private String productId; 20 private String productId;
22 21
  22 + @Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
  23 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  24 + private LocalDateTime[] createTime;
  25 +
23 } 26 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java
1 package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; 1 package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;
2 2
3 -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;  
4 -import com.fhs.core.trans.anno.Trans;  
5 -import com.fhs.core.trans.constant.TransType;  
6 import com.fhs.core.trans.vo.VO; 3 import com.fhs.core.trans.vo.VO;
7 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
8 import lombok.Data; 5 import lombok.Data;
9 6
10 -@Data 7 +import java.time.LocalDateTime;
  8 +
11 @Schema(description = "管理后台 - IoT OTA 固件 Response VO") 9 @Schema(description = "管理后台 - IoT OTA 固件 Response VO")
  10 +@Data
12 public class IotOtaFirmwareRespVO implements VO { 11 public class IotOtaFirmwareRespVO implements VO {
13 12
14 - /**  
15 - * 固件编号  
16 - */  
17 @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 13 @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
18 private Long id; 14 private Long id;
19 - /**  
20 - * 固件名称  
21 - */  
22 - @Schema(description = "固件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "OTA固件") 15 +
  16 + @Schema(description = "固件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "OTA 固件")
23 private String name; 17 private String name;
24 - /**  
25 - * 固件描述  
26 - */ 18 +
27 @Schema(description = "固件描述") 19 @Schema(description = "固件描述")
28 private String description; 20 private String description;
29 - /**  
30 - * 版本号  
31 - */ 21 +
32 @Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0.0") 22 @Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0.0")
33 private String version; 23 private String version;
34 24
35 - /**  
36 - * 产品编号  
37 - * <p>  
38 - * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()}  
39 - */  
40 @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 25 @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
41 - @Trans(type = TransType.SIMPLE, target = IotProductDO.class, fields = {"name"}, refs = {"productName"})  
42 - private String productId;  
43 - /**  
44 - * 产品标识  
45 - * <p>  
46 - * 冗余 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getProductKey()}  
47 - */  
48 - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "iot-product-key")  
49 - private String productKey;  
50 - /**  
51 - * 产品名称  
52 - */  
53 - @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "OTA产品") 26 + private Long productId;
  27 +
  28 + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "智能设备")
54 private String productName; 29 private String productName;
55 - /**  
56 - * 签名方式  
57 - * <p>  
58 - * 例如说:MD5、SHA256  
59 - */  
60 - @Schema(description = "签名方式", example = "MD5")  
61 - private String signMethod;  
62 - /**  
63 - * 固件文件签名  
64 - */  
65 - @Schema(description = "固件文件签名", example = "1024")  
66 - private String fileSign;  
67 - /**  
68 - * 固件文件大小  
69 - */ 30 +
  31 + @Schema(description = "固件文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/firmware.bin")
  32 + private String fileUrl;
  33 +
70 @Schema(description = "固件文件大小", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 34 @Schema(description = "固件文件大小", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
71 private Long fileSize; 35 private Long fileSize;
72 - /**  
73 - * 固件文件 URL  
74 - */  
75 - @Schema(description = "固件文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")  
76 - private String fileUrl;  
77 - /**  
78 - * 自定义信息,建议使用 JSON 格式  
79 - */  
80 - @Schema(description = "自定义信息,建议使用 JSON 格式")  
81 - private String information; 36 +
  37 + @Schema(description = "固件文件签名算法", example = "MD5")
  38 + private String fileDigestAlgorithm;
  39 +
  40 + @Schema(description = "固件文件签名结果", example = "d41d8cd98f00b204e9800998ecf8427e")
  41 + private String fileDigestValue;
  42 +
  43 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  44 + private LocalDateTime createTime;
  45 +
  46 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  47 + private LocalDateTime updateTime;
82 48
83 } 49 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java
1 package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; 1 package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 -import jakarta.validation.constraints.NotEmpty;  
5 import jakarta.validation.constraints.NotNull; 4 import jakarta.validation.constraints.NotNull;
6 import lombok.Data; 5 import lombok.Data;
7 6
@@ -13,9 +12,7 @@ public class IotOtaFirmwareUpdateReqVO { @@ -13,9 +12,7 @@ public class IotOtaFirmwareUpdateReqVO {
13 @NotNull(message = "固件编号不能为空") 12 @NotNull(message = "固件编号不能为空")
14 private Long id; 13 private Long id;
15 14
16 - // TODO @li:name 是不是可以飞必传哈  
17 - @Schema(description = "固件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "智能开关固件")  
18 - @NotEmpty(message = "固件名称不能为空") 15 + @Schema(description = "固件名称", example = "智能开关固件")
19 private String name; 16 private String name;
20 17
21 @Schema(description = "固件描述", example = "某品牌型号固件,测试用") 18 @Schema(description = "固件描述", example = "某品牌型号固件,测试用")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskCreateReqVO.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task; 1 +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;  
5 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;  
6 -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum; 4 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;
7 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
8 import jakarta.validation.constraints.NotEmpty; 6 import jakarta.validation.constraints.NotEmpty;
9 import jakarta.validation.constraints.NotNull; 7 import jakarta.validation.constraints.NotNull;
@@ -11,51 +9,29 @@ import lombok.Data; @@ -11,51 +9,29 @@ import lombok.Data;
11 9
12 import java.util.List; 10 import java.util.List;
13 11
  12 +@Schema(description = "管理后台 - IoT OTA 升级任务创建 Request VO")
14 @Data 13 @Data
15 -@Schema(description = "管理后台 - IoT OTA 升级任务创建/修改 Request VO")  
16 -public class IotOtaUpgradeTaskSaveReqVO { 14 +public class IotOtaTaskCreateReqVO {
17 15
18 - // TODO @li:已经有注解,不用重复注释  
19 - // TODO @li: @Schema 写在参数校验前面。先有定义;其他的,也检查下;  
20 -  
21 - /**  
22 - * 任务名称  
23 - */  
24 @NotEmpty(message = "任务名称不能为空") 16 @NotEmpty(message = "任务名称不能为空")
25 @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "升级任务") 17 @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "升级任务")
26 private String name; 18 private String name;
27 19
28 - /**  
29 - * 任务描述  
30 - */  
31 @Schema(description = "任务描述", example = "升级任务") 20 @Schema(description = "任务描述", example = "升级任务")
32 private String description; 21 private String description;
33 22
34 - /**  
35 - * 固件编号  
36 - * <p>  
37 - * 关联 {@link IotOtaFirmwareDO#getId()}  
38 - */  
39 - @NotNull(message = "固件编号不能为空")  
40 @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 23 @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  24 + @NotNull(message = "固件编号不能为空")
41 private Long firmwareId; 25 private Long firmwareId;
42 26
43 - /**  
44 - * 升级范围  
45 - * <p>  
46 - * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum}  
47 - */  
48 - @NotNull(message = "升级范围不能为空")  
49 - @InEnum(value = IotOtaUpgradeTaskScopeEnum.class)  
50 @Schema(description = "升级范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 27 @Schema(description = "升级范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
51 - private Integer scope;  
52 -  
53 - /**  
54 - * 选中的设备编号数组  
55 - * <p>  
56 - * 关联 {@link IotDeviceDO#getId()}  
57 - */  
58 - @Schema(description = "选中的设备编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3,4]") 28 + @NotNull(message = "升级范围不能为空")
  29 + @InEnum(value = IotOtaTaskDeviceScopeEnum.class)
  30 + private Integer deviceScope;
  31 +
  32 + @Schema(description = "选中的设备编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
59 private List<Long> deviceIds; 33 private List<Long> deviceIds;
60 34
  35 + // TODO @li:如果 deviceScope 等于 2 时,deviceIds 校验非空;
  36 +
61 } 37 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskPageReqVO.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task; 1 +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
5 -import jakarta.validation.constraints.NotNull;  
6 import lombok.Data; 5 import lombok.Data;
7 6
8 -@Data  
9 @Schema(description = "管理后台 - IoT OTA 升级任务分页 Request VO") 7 @Schema(description = "管理后台 - IoT OTA 升级任务分页 Request VO")
10 -public class IotOtaUpgradeTaskPageReqVO extends PageParam { 8 +@Data
  9 +public class IotOtaTaskPageReqVO extends PageParam {
11 10
12 - /**  
13 - * 任务名称字段,用于描述任务的名称  
14 - */  
15 @Schema(description = "任务名称", example = "升级任务") 11 @Schema(description = "任务名称", example = "升级任务")
16 private String name; 12 private String name;
17 13
18 - /**  
19 - * 固件编号字段,用于唯一标识固件,不能为空  
20 - */  
21 - @NotNull(message = "固件编号不能为空")  
22 - @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 14 + @Schema(description = "固件编号", example = "1024")
23 private Long firmwareId; 15 private Long firmwareId;
24 16
25 } 17 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task;
  2 +
  3 +import com.fhs.core.trans.vo.VO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +@Schema(description = "管理后台 - IoT OTA 升级任务 Response VO")
  10 +@Data
  11 +public class IotOtaTaskRespVO implements VO {
  12 +
  13 + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "升级任务")
  17 + private String name;
  18 +
  19 + @Schema(description = "任务描述", example = "升级任务")
  20 + private String description;
  21 +
  22 + @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  23 + private Long firmwareId;
  24 +
  25 + @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  26 + private Integer status;
  27 +
  28 + @Schema(description = "升级范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  29 + private Integer deviceScope;
  30 +
  31 + @Schema(description = "设备总共数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  32 + private Integer deviceTotalCount;
  33 +
  34 + @Schema(description = "设备成功数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "66")
  35 + private Integer deviceSuccessCount;
  36 +
  37 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-08 07:30:00")
  38 + private LocalDateTime createTime;
  39 +
  40 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +
  9 +@Schema(description = "管理后台 - IoT OTA 升级记录分页 Request VO")
  10 +@Data
  11 +public class IotOtaTaskRecordPageReqVO extends PageParam {
  12 +
  13 + @Schema(description = "升级任务编号", example = "1024")
  14 + private Long taskId;
  15 +
  16 + @Schema(description = "升级记录状态", example = "5")
  17 + @InEnum(IotOtaTaskRecordStatusEnum.class)
  18 + private Integer status;
  19 +
  20 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - IoT OTA 升级任务记录 Response VO")
  9 +@Data
  10 +public class IotOtaTaskRecordRespVO {
  11 +
  12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long firmwareId;
  17 +
  18 + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  19 + private Long taskId;
  20 +
  21 + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  22 + private Long deviceId;
  23 +
  24 + @Schema(description = "设备名称", example = "智能开关")
  25 + private String deviceName;
  26 +
  27 + @Schema(description = "来源的固件编号", example = "1023")
  28 + private Long fromFirmwareId;
  29 +
  30 + @Schema(description = "来源固件版本", example = "1.0.0")
  31 + private String fromFirmwareVersion;
  32 +
  33 + @Schema(description = "升级状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  34 + private Integer status;
  35 +
  36 + @Schema(description = "升级进度,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
  37 + private Integer progress;
  38 +
  39 + @Schema(description = "升级进度描述", example = "正在下载固件...")
  40 + private String description;
  41 +
  42 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  43 + private LocalDateTime createTime;
  44 +
  45 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  46 + private LocalDateTime updateTime;
  47 +
  48 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
4 -import io.swagger.v3.oas.annotations.media.Schema;  
5 -import jakarta.validation.constraints.NotNull;  
6 -import lombok.Data;  
7 -  
8 -@Data  
9 -@Schema(description = "管理后台 - IoT OTA 升级记录分页 Request VO")  
10 -public class IotOtaUpgradeRecordPageReqVO extends PageParam {  
11 -  
12 - // TODO @li:已经有注解,不用重复注释  
13 - /**  
14 - * 升级任务编号字段。  
15 - * <p>  
16 - * 该字段用于标识升级任务的唯一编号,不能为空。  
17 - */  
18 - @Schema(description = "升级任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
19 - @NotNull(message = "升级任务编号不能为空")  
20 - private Long taskId;  
21 -  
22 - /**  
23 - * 设备标识字段。  
24 - * <p>  
25 - * 该字段用于标识设备的名称,通常用于区分不同的设备。  
26 - */  
27 - @Schema(description = "设备标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "摄像头A1-1")  
28 - private String deviceName;  
29 -  
30 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record;  
2 -  
3 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;  
4 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;  
5 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;  
6 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO;  
7 -import com.fhs.core.trans.anno.Trans;  
8 -import com.fhs.core.trans.constant.TransType;  
9 -import io.swagger.v3.oas.annotations.media.Schema;  
10 -import lombok.Data;  
11 -  
12 -import java.time.LocalDateTime;  
13 -  
14 -@Data  
15 -@Schema(description = "管理后台 - IoT OTA 升级记录 Response VO")  
16 -public class IotOtaUpgradeRecordRespVO {  
17 -  
18 - /**  
19 - * 升级记录编号  
20 - */  
21 - @Schema(description = "升级记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
22 - private Long id;  
23 - /**  
24 - * 固件编号  
25 - * <p>  
26 - * 关联 {@link IotOtaFirmwareDO#getId()}  
27 - */  
28 - @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
29 - @Trans(type = TransType.SIMPLE, target = IotOtaFirmwareDO.class, fields = {"version"}, refs = {"firmwareVersion"})  
30 - private Long firmwareId;  
31 - /**  
32 - * 固件版本  
33 - */  
34 - @Schema(description = "固件版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "v1.0.0")  
35 - private String firmwareVersion;  
36 - /**  
37 - * 任务编号  
38 - * <p>  
39 - * 关联 {@link IotOtaUpgradeTaskDO#getId()}  
40 - */  
41 - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
42 - private Long taskId;  
43 - /**  
44 - * 产品标识  
45 - * <p>  
46 - * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()}  
47 - */  
48 - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "iot")  
49 - private String productKey;  
50 - /**  
51 - * 设备名称  
52 - * <p>  
53 - * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()}  
54 - */  
55 - @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "iot")  
56 - private String deviceName;  
57 - /**  
58 - * 设备编号  
59 - * <p>  
60 - * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()}  
61 - */  
62 - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
63 - private String deviceId;  
64 - /**  
65 - * 来源的固件编号  
66 - * <p>  
67 - * 关联 {@link IotDeviceDO#getFirmwareId()}  
68 - */  
69 - @Schema(description = "来源的固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
70 - @Trans(type = TransType.SIMPLE, target = IotOtaFirmwareDO.class, fields = {"version"}, refs = {"fromFirmwareVersion"})  
71 - private Long fromFirmwareId;  
72 - /**  
73 - * 来源的固件版本  
74 - */  
75 - @Schema(description = "来源的固件版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "v1.0.0")  
76 - private String fromFirmwareVersion;  
77 - /**  
78 - * 升级状态  
79 - * <p>  
80 - * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum}  
81 - */  
82 - @Schema(description = "升级状态", requiredMode = Schema.RequiredMode.REQUIRED, allowableValues = {"0", "10", "20", "30", "40", "50"})  
83 - private Integer status;  
84 - /**  
85 - * 升级进度,百分比  
86 - */  
87 - @Schema(description = "升级进度,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")  
88 - private Integer progress;  
89 - /**  
90 - * 升级进度描述  
91 - * <p>  
92 - * 注意,只记录设备最后一次的升级进度描述  
93 - * 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志  
94 - */  
95 - @Schema(description = "升级进度描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")  
96 - private String description;  
97 - /**  
98 - * 升级开始时间  
99 - */  
100 - @Schema(description = "升级开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-08 07:30:00")  
101 - private LocalDateTime startTime;  
102 - /**  
103 - * 升级结束时间  
104 - */  
105 - @Schema(description = "升级结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-08 07:30:00")  
106 - private LocalDateTime endTime;  
107 -  
108 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task;  
2 -  
3 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;  
4 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;  
5 -import com.fhs.core.trans.vo.VO;  
6 -import io.swagger.v3.oas.annotations.media.Schema;  
7 -import lombok.Data;  
8 -  
9 -import java.time.LocalDateTime;  
10 -import java.util.List;  
11 -  
12 -@Data  
13 -@Schema(description = "管理后台 - IoT OTA 升级任务 Response VO")  
14 -public class IotOtaUpgradeTaskRespVO implements VO {  
15 -  
16 - /**  
17 - * 任务编号  
18 - */  
19 - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
20 - private Long id;  
21 - /**  
22 - * 任务名称  
23 - */  
24 - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "升级任务")  
25 - private String name;  
26 - /**  
27 - * 任务描述  
28 - */  
29 - @Schema(description = "任务描述", example = "升级任务")  
30 - private String description;  
31 - /**  
32 - * 固件编号  
33 - * <p>  
34 - * 关联 {@link IotOtaFirmwareDO#getId()}  
35 - */  
36 - @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
37 - private Long firmwareId;  
38 - /**  
39 - * 任务状态  
40 - * <p>  
41 - * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum}  
42 - */  
43 - @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, allowableValues = {"10", "20", "21", "30"})  
44 - private Integer status;  
45 - /**  
46 - * 任务状态名称  
47 - */  
48 - @Schema(description = "任务状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中")  
49 - private String statusName;  
50 - /**  
51 - * 升级范围  
52 - * <p>  
53 - * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum}  
54 - */  
55 - @Schema(description = "升级范围", requiredMode = Schema.RequiredMode.REQUIRED, allowableValues = {"1", "2"})  
56 - private Integer scope;  
57 - /**  
58 - * 设备数量  
59 - */  
60 - @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
61 - private Long deviceCount;  
62 - /**  
63 - * 选中的设备编号数组  
64 - * <p>  
65 - * 关联 {@link IotDeviceDO#getId()}  
66 - */  
67 - @Schema(description = "选中的设备编号数组", example = "1024")  
68 - private List<Long> deviceIds;  
69 - /**  
70 - * 选中的设备名字数组  
71 - * <p>  
72 - * 关联 {@link IotDeviceDO#getDeviceName()}  
73 - */  
74 - @Schema(description = "选中的设备名字数组", example = "1024")  
75 - private List<String> deviceNames;  
76 - /**  
77 - * 创建时间  
78 - */  
79 - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-08 07:30:00")  
80 - private LocalDateTime createTime;  
81 -  
82 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java
@@ -143,7 +143,7 @@ public class IotProductController { @@ -143,7 +143,7 @@ public class IotProductController {
143 143
144 @GetMapping("/simple-list") 144 @GetMapping("/simple-list")
145 @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项") 145 @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项")
146 - public CommonResult<List<IotProductRespVO>> getSimpleProductList() { 146 + public CommonResult<List<IotProductRespVO>> getProductSimpleList() {
147 List<IotProductDO> list = productService.getProductList(); 147 List<IotProductDO> list = productService.getProductList();
148 return success(convertList(list, product -> // 只返回 id、name 字段 148 return success(convertList(list, product -> // 只返回 id、name 字段
149 new IotProductRespVO().setId(product.getId()).setName(product.getName()) 149 new IotProductRespVO().setId(product.getId()).setName(product.getName())
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule;  
2 -  
3 -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;  
4 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
5 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
6 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
7 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO;  
8 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeRespVO;  
9 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO;  
10 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;  
11 -import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService;  
12 -import io.swagger.v3.oas.annotations.Operation;  
13 -import io.swagger.v3.oas.annotations.Parameter;  
14 -import io.swagger.v3.oas.annotations.tags.Tag;  
15 -import jakarta.annotation.Resource;  
16 -import jakarta.validation.Valid;  
17 -import org.springframework.security.access.prepost.PreAuthorize;  
18 -import org.springframework.validation.annotation.Validated;  
19 -import org.springframework.web.bind.annotation.*;  
20 -  
21 -import java.util.List;  
22 -  
23 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
24 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;  
25 -  
26 -@Tag(name = "管理后台 - IoT 数据桥梁")  
27 -@RestController  
28 -@RequestMapping("/iot/data-bridge")  
29 -@Validated  
30 -public class IotDataBridgeController {  
31 -  
32 - @Resource  
33 - private IotDataBridgeService dataBridgeService;  
34 -  
35 - @PostMapping("/create")  
36 - @Operation(summary = "创建数据桥梁")  
37 - @PreAuthorize("@ss.hasPermission('iot:data-bridge:create')")  
38 - public CommonResult<Long> createDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO createReqVO) {  
39 - return success(dataBridgeService.createDataBridge(createReqVO));  
40 - }  
41 -  
42 - @PutMapping("/update")  
43 - @Operation(summary = "更新数据桥梁")  
44 - @PreAuthorize("@ss.hasPermission('iot:data-bridge:update')")  
45 - public CommonResult<Boolean> updateDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO updateReqVO) {  
46 - dataBridgeService.updateDataBridge(updateReqVO);  
47 - return success(true);  
48 - }  
49 -  
50 - @DeleteMapping("/delete")  
51 - @Operation(summary = "删除数据桥梁")  
52 - @Parameter(name = "id", description = "编号", required = true)  
53 - @PreAuthorize("@ss.hasPermission('iot:data-bridge:delete')")  
54 - public CommonResult<Boolean> deleteDataBridge(@RequestParam("id") Long id) {  
55 - dataBridgeService.deleteDataBridge(id);  
56 - return success(true);  
57 - }  
58 -  
59 - @GetMapping("/get")  
60 - @Operation(summary = "获得数据桥梁")  
61 - @Parameter(name = "id", description = "编号", required = true, example = "1024")  
62 - @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')")  
63 - public CommonResult<IotDataBridgeRespVO> getDataBridge(@RequestParam("id") Long id) {  
64 - IotDataBridgeDO dataBridge = dataBridgeService.getDataBridge(id);  
65 - return success(BeanUtils.toBean(dataBridge, IotDataBridgeRespVO.class));  
66 - }  
67 -  
68 - @GetMapping("/page")  
69 - @Operation(summary = "获得数据桥梁分页")  
70 - @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')")  
71 - public CommonResult<PageResult<IotDataBridgeRespVO>> getDataBridgePage(@Valid IotDataBridgePageReqVO pageReqVO) {  
72 - PageResult<IotDataBridgeDO> pageResult = dataBridgeService.getDataBridgePage(pageReqVO);  
73 - return success(BeanUtils.toBean(pageResult, IotDataBridgeRespVO.class));  
74 - }  
75 -  
76 - @GetMapping("/simple-list")  
77 - @Operation(summary = "获取数据桥梁的精简信息列表", description = "主要用于前端的下拉选项")  
78 - public CommonResult<List<IotDataBridgeRespVO>> getSimpleDataBridgeList() {  
79 - List<IotDataBridgeDO> list = dataBridgeService.getDataBridgeList(CommonStatusEnum.ENABLE.getStatus());  
80 - return success(convertList(list, dataBridge -> // 只返回 id、name 字段  
81 - new IotDataBridgeRespVO().setId(dataBridge.getId()).setName(dataBridge.getName())));  
82 - }  
83 -  
84 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataRuleController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleRespVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleSaveReqVO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;
  10 +import cn.iocoder.yudao.module.iot.service.rule.data.IotDataRuleService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.*;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +
  22 +@Tag(name = "管理后台 - IoT 数据流转规则")
  23 +@RestController
  24 +@RequestMapping("/iot/data-rule")
  25 +@Validated
  26 +public class IotDataRuleController {
  27 +
  28 + @Resource
  29 + private IotDataRuleService dataRuleService;
  30 +
  31 + @PostMapping("/create")
  32 + @Operation(summary = "创建数据流转规则")
  33 + @PreAuthorize("@ss.hasPermission('iot:data-rule:create')")
  34 + public CommonResult<Long> createDataRule(@Valid @RequestBody IotDataRuleSaveReqVO createReqVO) {
  35 + return success(dataRuleService.createDataRule(createReqVO));
  36 + }
  37 +
  38 + @PutMapping("/update")
  39 + @Operation(summary = "更新数据流转规则")
  40 + @PreAuthorize("@ss.hasPermission('iot:data-rule:update')")
  41 + public CommonResult<Boolean> updateDataRule(@Valid @RequestBody IotDataRuleSaveReqVO updateReqVO) {
  42 + dataRuleService.updateDataRule(updateReqVO);
  43 + return success(true);
  44 + }
  45 +
  46 + @DeleteMapping("/delete")
  47 + @Operation(summary = "删除数据流转规则")
  48 + @Parameter(name = "id", description = "编号", required = true)
  49 + @PreAuthorize("@ss.hasPermission('iot:data-rule:delete')")
  50 + public CommonResult<Boolean> deleteDataRule(@RequestParam("id") Long id) {
  51 + dataRuleService.deleteDataRule(id);
  52 + return success(true);
  53 + }
  54 +
  55 + @GetMapping("/get")
  56 + @Operation(summary = "获得数据流转规则")
  57 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  58 + @PreAuthorize("@ss.hasPermission('iot:data-rule:query')")
  59 + public CommonResult<IotDataRuleRespVO> getDataRule(@RequestParam("id") Long id) {
  60 + IotDataRuleDO dataRule = dataRuleService.getDataRule(id);
  61 + return success(BeanUtils.toBean(dataRule, IotDataRuleRespVO.class));
  62 + }
  63 +
  64 + @GetMapping("/page")
  65 + @Operation(summary = "获得数据流转规则分页")
  66 + @PreAuthorize("@ss.hasPermission('iot:data-rule:query')")
  67 + public CommonResult<PageResult<IotDataRuleRespVO>> getDataRulePage(@Valid IotDataRulePageReqVO pageReqVO) {
  68 + PageResult<IotDataRuleDO> pageResult = dataRuleService.getDataRulePage(pageReqVO);
  69 + return success(BeanUtils.toBean(pageResult, IotDataRuleRespVO.class));
  70 + }
  71 +
  72 +}
0 \ No newline at end of file 73 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkRespVO;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO;
  10 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
  11 +import cn.iocoder.yudao.module.iot.service.rule.data.IotDataSinkService;
  12 +import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.tags.Tag;
  15 +import jakarta.annotation.Resource;
  16 +import jakarta.validation.Valid;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.*;
  20 +
  21 +import java.util.List;
  22 +
  23 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  25 +
  26 +@Tag(name = "管理后台 - IoT 数据流转目的")
  27 +@RestController
  28 +@RequestMapping("/iot/data-sink")
  29 +@Validated
  30 +public class IotDataSinkController {
  31 +
  32 + @Resource
  33 + private IotDataSinkService dataSinkService;
  34 +
  35 + @PostMapping("/create")
  36 + @Operation(summary = "创建数据目的")
  37 + @PreAuthorize("@ss.hasPermission('iot:data-sink:create')")
  38 + public CommonResult<Long> createDataSink(@Valid @RequestBody IotDataSinkSaveReqVO createReqVO) {
  39 + return success(dataSinkService.createDataSink(createReqVO));
  40 + }
  41 +
  42 + @PutMapping("/update")
  43 + @Operation(summary = "更新数据目的")
  44 + @PreAuthorize("@ss.hasPermission('iot:data-sink:update')")
  45 + public CommonResult<Boolean> updateDataSink(@Valid @RequestBody IotDataSinkSaveReqVO updateReqVO) {
  46 + dataSinkService.updateDataSink(updateReqVO);
  47 + return success(true);
  48 + }
  49 +
  50 + @DeleteMapping("/delete")
  51 + @Operation(summary = "删除数据目的")
  52 + @Parameter(name = "id", description = "编号", required = true)
  53 + @PreAuthorize("@ss.hasPermission('iot:data-sink:delete')")
  54 + public CommonResult<Boolean> deleteDataSink(@RequestParam("id") Long id) {
  55 + dataSinkService.deleteDataSink(id);
  56 + return success(true);
  57 + }
  58 +
  59 + @GetMapping("/get")
  60 + @Operation(summary = "获得数据目的")
  61 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  62 + @PreAuthorize("@ss.hasPermission('iot:data-sink:query')")
  63 + public CommonResult<IotDataSinkRespVO> getDataSink(@RequestParam("id") Long id) {
  64 + IotDataSinkDO sink = dataSinkService.getDataSink(id);
  65 + return success(BeanUtils.toBean(sink, IotDataSinkRespVO.class));
  66 + }
  67 +
  68 + @GetMapping("/page")
  69 + @Operation(summary = "获得数据目的分页")
  70 + @PreAuthorize("@ss.hasPermission('iot:data-sink:query')")
  71 + public CommonResult<PageResult<IotDataSinkRespVO>> getDataSinkPage(@Valid IotDataSinkPageReqVO pageReqVO) {
  72 + PageResult<IotDataSinkDO> pageResult = dataSinkService.getDataSinkPage(pageReqVO);
  73 + return success(BeanUtils.toBean(pageResult, IotDataSinkRespVO.class));
  74 + }
  75 +
  76 + @GetMapping("/simple-list")
  77 + @Operation(summary = "获取数据目的的精简信息列表", description = "主要用于前端的下拉选项")
  78 + public CommonResult<List<IotDataSinkRespVO>> getDataSinkSimpleList() {
  79 + List<IotDataSinkDO> list = dataSinkService.getDataSinkListByStatus(CommonStatusEnum.ENABLE.getStatus());
  80 + return success(convertList(list, sink -> // 只返回 id、name 字段
  81 + new IotDataSinkRespVO().setId(sink.getId()).setName(sink.getName())));
  82 + }
  83 +
  84 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java
1 package cn.iocoder.yudao.module.iot.controller.admin.rule; 1 package cn.iocoder.yudao.module.iot.controller.admin.rule;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -7,7 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePa @@ -7,7 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePa
7 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneRespVO; 8 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneRespVO;
8 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO; 9 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO;
9 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; 10 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
10 -import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; 11 +import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService;
11 import io.swagger.v3.oas.annotations.Operation; 12 import io.swagger.v3.oas.annotations.Operation;
12 import io.swagger.v3.oas.annotations.Parameter; 13 import io.swagger.v3.oas.annotations.Parameter;
13 import io.swagger.v3.oas.annotations.tags.Tag; 14 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -18,7 +19,10 @@ import org.springframework.security.access.prepost.PreAuthorize; @@ -18,7 +19,10 @@ import org.springframework.security.access.prepost.PreAuthorize;
18 import org.springframework.validation.annotation.Validated; 19 import org.springframework.validation.annotation.Validated;
19 import org.springframework.web.bind.annotation.*; 20 import org.springframework.web.bind.annotation.*;
20 21
  22 +import java.util.List;
  23 +
21 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  25 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
22 26
23 @Tag(name = "管理后台 - IoT 场景联动") 27 @Tag(name = "管理后台 - IoT 场景联动")
24 @RestController 28 @RestController
@@ -70,6 +74,14 @@ public class IotRuleSceneController { @@ -70,6 +74,14 @@ public class IotRuleSceneController {
70 return success(BeanUtils.toBean(pageResult, IotRuleSceneRespVO.class)); 74 return success(BeanUtils.toBean(pageResult, IotRuleSceneRespVO.class));
71 } 75 }
72 76
  77 + @GetMapping("/simple-list")
  78 + @Operation(summary = "获取场景联动的精简信息列表", description = "主要用于前端的下拉选项")
  79 + public CommonResult<List<IotRuleSceneRespVO>> getRuleSceneSimpleList() {
  80 + List<IotRuleSceneDO> list = ruleSceneService.getRuleSceneListByStatus(CommonStatusEnum.ENABLE.getStatus());
  81 + return success(convertList(list, scene -> // 只返回 id、name 字段
  82 + new IotRuleSceneRespVO().setId(scene.getId()).setName(scene.getName())));
  83 + }
  84 +
73 @GetMapping("/test") 85 @GetMapping("/test")
74 @PermitAll 86 @PermitAll
75 public void test() { 87 public void test() {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRulePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - IoT 数据流转规则分页 Request VO")
  13 +@Data
  14 +public class IotDataRulePageReqVO extends PageParam {
  15 +
  16 + @Schema(description = "数据流转规则名称", example = "芋艿")
  17 + private String name;
  18 +
  19 + @Schema(description = "数据流转规则状态", example = "1")
  20 + private Integer status;
  21 +
  22 + @Schema(description = "创建时间")
  23 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  24 + private LocalDateTime[] createTime;
  25 +
  26 +}
0 \ No newline at end of file 27 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule;
  2 +
  3 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.time.LocalDateTime;
  8 +import java.util.List;
  9 +
  10 +@Schema(description = "管理后台 - IoT 数据流转规则 Response VO")
  11 +@Data
  12 +public class IotDataRuleRespVO {
  13 +
  14 + @Schema(description = "数据流转规则编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8540")
  15 + private Long id;
  16 +
  17 + @Schema(description = "数据流转规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
  18 + private String name;
  19 +
  20 + @Schema(description = "数据流转规则描述", example = "你猜")
  21 + private String description;
  22 +
  23 + @Schema(description = "数据流转规则状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  24 + private Integer status;
  25 +
  26 + @Schema(description = "数据源配置数组", requiredMode = Schema.RequiredMode.REQUIRED)
  27 + private List<IotDataRuleDO.SourceConfig> sourceConfigs;
  28 +
  29 + @Schema(description = "数据目的编号数组", requiredMode = Schema.RequiredMode.REQUIRED)
  30 + private List<Long> sinkIds;
  31 +
  32 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  33 + private LocalDateTime createTime;
  34 +
  35 +}
0 \ No newline at end of file 36 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import jakarta.validation.constraints.NotEmpty;
  8 +import jakarta.validation.constraints.NotNull;
  9 +import lombok.Data;
  10 +
  11 +import java.util.List;
  12 +
  13 +@Schema(description = "管理后台 - IoT 数据流转规则新增/修改 Request VO")
  14 +@Data
  15 +public class IotDataRuleSaveReqVO {
  16 +
  17 + @Schema(description = "数据流转规则编号", example = "8540")
  18 + private Long id;
  19 +
  20 + @Schema(description = "数据流转规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
  21 + @NotEmpty(message = "数据流转规则名称不能为空")
  22 + private String name;
  23 +
  24 + @Schema(description = "数据流转规则描述", example = "你猜")
  25 + private String description;
  26 +
  27 + @Schema(description = "数据流转规则状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  28 + @NotNull(message = "数据流转规则状态不能为空")
  29 + @InEnum(CommonStatusEnum.class)
  30 + private Integer status;
  31 +
  32 + @Schema(description = "数据源配置数组", requiredMode = Schema.RequiredMode.REQUIRED)
  33 + @NotEmpty(message = "数据源配置数组不能为空")
  34 + private List<IotDataRuleDO.SourceConfig> sourceConfigs;
  35 +
  36 + @Schema(description = "数据目的编号数组", requiredMode = Schema.RequiredMode.REQUIRED)
  37 + @NotEmpty(message = "数据目的编号数组不能为空")
  38 + private List<Long> sinkIds;
  39 +
  40 +}
0 \ No newline at end of file 41 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkPageReqVO.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; 1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink;
2 2
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.PageParam; 4 import cn.iocoder.yudao.framework.common.pojo.PageParam;
@@ -11,14 +11,14 @@ import java.time.LocalDateTime; @@ -11,14 +11,14 @@ import java.time.LocalDateTime;
11 11
12 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; 12 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
13 13
14 -@Schema(description = "管理后台 - IoT 数据桥梁分页 Request VO") 14 +@Schema(description = "管理后台 - IoT 数据流转目的分页 Request VO")
15 @Data 15 @Data
16 -public class IotDataBridgePageReqVO extends PageParam { 16 +public class IotDataSinkPageReqVO extends PageParam {
17 17
18 - @Schema(description = "桥梁名称", example = "赵六") 18 + @Schema(description = "数据目的名称", example = "赵六")
19 private String name; 19 private String name;
20 20
21 - @Schema(description = "桥梁状态", example = "1") 21 + @Schema(description = "数据目的状态", example = "2")
22 @InEnum(CommonStatusEnum.class) 22 @InEnum(CommonStatusEnum.class)
23 private Integer status; 23 private Integer status;
24 24
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink;
  2 +
  3 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotAbstractDataSinkConfig;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +@Schema(description = "管理后台 - IoT 数据流转目的 Response VO")
  10 +@Data
  11 +public class IotDataSinkRespVO {
  12 +
  13 + @Schema(description = "数据目的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564")
  14 + private Long id;
  15 +
  16 + @Schema(description = "数据目的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  17 + private String name;
  18 +
  19 + @Schema(description = "数据目的描述", example = "随便")
  20 + private String description;
  21 +
  22 + @Schema(description = "数据目的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  23 + private Integer status;
  24 +
  25 + @Schema(description = "数据目的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  26 + private Integer type;
  27 +
  28 + @Schema(description = "数据目的配置")
  29 + private IotAbstractDataSinkConfig config;
  30 +
  31 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + private LocalDateTime createTime;
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotAbstractDataSinkConfig;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import jakarta.validation.constraints.NotEmpty;
  9 +import jakarta.validation.constraints.NotNull;
  10 +import lombok.Data;
  11 +
  12 +@Schema(description = "管理后台 - IoT 数据流转目的新增/修改 Request VO")
  13 +@Data
  14 +public class IotDataSinkSaveReqVO {
  15 +
  16 + @Schema(description = "数据目的编号", example = "18564")
  17 + private Long id;
  18 +
  19 + @Schema(description = "数据目的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  20 + @NotEmpty(message = "数据目的名称不能为空")
  21 + private String name;
  22 +
  23 + @Schema(description = "数据目的描述", example = "随便")
  24 + private String description;
  25 +
  26 + @Schema(description = "数据目的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  27 + @NotNull(message = "数据目的状态不能为空")
  28 + @InEnum(CommonStatusEnum.class)
  29 + private Integer status;
  30 +
  31 + @Schema(description = "数据目的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  32 + @NotNull(message = "数据目的类型不能为空")
  33 + @InEnum(IotDataSinkTypeEnum.class)
  34 + private Integer type;
  35 +
  36 + @Schema(description = "数据目的配置")
  37 + @NotNull(message = "数据目的配置不能为空")
  38 + private IotAbstractDataSinkConfig config;
  39 +
  40 +}
0 \ No newline at end of file 41 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge;  
2 -  
3 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig;  
4 -import io.swagger.v3.oas.annotations.media.Schema;  
5 -import lombok.Data;  
6 -  
7 -import java.time.LocalDateTime;  
8 -  
9 -@Schema(description = "管理后台 - IoT 数据桥梁 Response VO")  
10 -@Data  
11 -public class IotDataBridgeRespVO {  
12 -  
13 - @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564")  
14 - private Long id;  
15 -  
16 - @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")  
17 - private String name;  
18 -  
19 - @Schema(description = "桥梁描述", example = "随便")  
20 - private String description;  
21 -  
22 - @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
23 - private Integer status;  
24 -  
25 - @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED)  
26 - private Integer direction;  
27 -  
28 - @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
29 - private Integer type;  
30 -  
31 - @Schema(description = "桥梁配置")  
32 - private IotDataBridgeAbstractConfig config;  
33 -  
34 - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)  
35 - private LocalDateTime createTime;  
36 -  
37 -}  
38 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge;  
2 -  
3 -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;  
4 -import cn.iocoder.yudao.framework.common.validation.InEnum;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig;  
6 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum;  
7 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;  
8 -import io.swagger.v3.oas.annotations.media.Schema;  
9 -import jakarta.validation.constraints.NotEmpty;  
10 -import jakarta.validation.constraints.NotNull;  
11 -import lombok.Data;  
12 -  
13 -@Schema(description = "管理后台 - IoT 数据桥梁新增/修改 Request VO")  
14 -@Data  
15 -public class IotDataBridgeSaveReqVO {  
16 -  
17 - @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564")  
18 - private Long id;  
19 -  
20 - @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")  
21 - @NotEmpty(message = "桥梁名称不能为空")  
22 - private String name;  
23 -  
24 - @Schema(description = "桥梁描述", example = "随便")  
25 - private String description;  
26 -  
27 - @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
28 - @NotNull(message = "桥梁状态不能为空")  
29 - @InEnum(CommonStatusEnum.class)  
30 - private Integer status;  
31 -  
32 - @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED)  
33 - @NotNull(message = "桥梁方向不能为空")  
34 - @InEnum(IotDataBridgeDirectionEnum.class)  
35 - private Integer direction;  
36 -  
37 - @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
38 - @NotNull(message = "桥梁类型不能为空")  
39 - @InEnum(IotDataBridgeTypeEnum.class)  
40 - private Integer type;  
41 -  
42 - @Schema(description = "桥梁配置")  
43 - @NotNull(message = "桥梁配置不能为空")  
44 - private IotDataBridgeAbstractConfig config;  
45 -  
46 -}  
47 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java
1 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel; 1 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel;
2 2
3 -import cn.hutool.core.collection.CollUtil;  
4 -import cn.hutool.core.util.ObjUtil;  
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.*; 6 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.*;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
9 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
10 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; 9 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
  10 +import cn.iocoder.yudao.module.iot.service.product.IotProductService;
11 import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; 11 import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
  12 +import com.google.common.base.Objects;
12 import io.swagger.v3.oas.annotations.Operation; 13 import io.swagger.v3.oas.annotations.Operation;
13 import io.swagger.v3.oas.annotations.Parameter; 14 import io.swagger.v3.oas.annotations.Parameter;
14 import io.swagger.v3.oas.annotations.tags.Tag; 15 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -32,6 +33,8 @@ public class IotThingModelController { @@ -32,6 +33,8 @@ public class IotThingModelController {
32 33
33 @Resource 34 @Resource
34 private IotThingModelService thingModelService; 35 private IotThingModelService thingModelService;
  36 + @Resource
  37 + private IotProductService productService;
35 38
36 @PostMapping("/create") 39 @PostMapping("/create")
37 @Operation(summary = "创建产品物模型") 40 @Operation(summary = "创建产品物模型")
@@ -71,23 +74,21 @@ public class IotThingModelController { @@ -71,23 +74,21 @@ public class IotThingModelController {
71 @Parameter(name = "productId", description = "产品 ID", required = true, example = "1024") 74 @Parameter(name = "productId", description = "产品 ID", required = true, example = "1024")
72 @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") 75 @PreAuthorize("@ss.hasPermission('iot:thing-model:query')")
73 public CommonResult<IotThingModelTSLRespVO> getThingModelTsl(@RequestParam("productId") Long productId) { 76 public CommonResult<IotThingModelTSLRespVO> getThingModelTsl(@RequestParam("productId") Long productId) {
74 - IotThingModelTSLRespVO tslRespVO = new IotThingModelTSLRespVO();  
75 - // TODO @puhui999:是不是要先查询产品哈?原因是,万一没配置物模型,但是产品已经有了!  
76 - // 1. 获得产品所有物模型定义  
77 - List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductId(productId);  
78 - if (CollUtil.isEmpty(thingModels)) {  
79 - return success(tslRespVO); 77 + // 1. 获得产品
  78 + IotProductDO product = productService.getProduct(productId);
  79 + if (product == null) {
  80 + return success(null);
80 } 81 }
81 -  
82 - // 2. 设置公共部分参数  
83 - IotThingModelDO thingModel = thingModels.get(0);  
84 - tslRespVO.setProductId(thingModel.getProductId()).setProductKey(thingModel.getProductKey()); 82 + IotThingModelTSLRespVO tslRespVO = new IotThingModelTSLRespVO()
  83 + .setProductId(product.getId()).setProductKey(product.getProductKey());
  84 + // 2. 获得物模型定义
  85 + List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductId(productId);
85 tslRespVO.setProperties(convertList(filterList(thingModels, item -> 86 tslRespVO.setProperties(convertList(filterList(thingModels, item ->
86 - ObjUtil.equal(IotThingModelTypeEnum.PROPERTY.getType(), item.getType())), IotThingModelDO::getProperty));  
87 - tslRespVO.setServices(convertList(filterList(thingModels, item ->  
88 - ObjUtil.equal(IotThingModelTypeEnum.SERVICE.getType(), item.getType())), IotThingModelDO::getService));  
89 - tslRespVO.setEvents(convertList(filterList(thingModels, item ->  
90 - ObjUtil.equal(IotThingModelTypeEnum.EVENT.getType(), item.getType())), IotThingModelDO::getEvent)); 87 + Objects.equal(IotThingModelTypeEnum.PROPERTY.getType(), item.getType())), IotThingModelDO::getProperty))
  88 + .setServices(convertList(filterList(thingModels, item ->
  89 + Objects.equal(IotThingModelTypeEnum.SERVICE.getType(), item.getType())), IotThingModelDO::getService))
  90 + .setEvents(convertList(filterList(thingModels, item ->
  91 + Objects.equal(IotThingModelTypeEnum.EVENT.getType(), item.getType())), IotThingModelDO::getEvent));
91 return success(tslRespVO); 92 return success(tslRespVO);
92 } 93 }
93 94
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java
1 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; 1 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;  
4 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; 3 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;
6 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
7 import lombok.Data; 7 import lombok.Data;
8 8
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java
1 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; 1 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; 4 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;
7 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; 7 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
8 import io.swagger.v3.oas.annotations.media.Schema; 8 import io.swagger.v3.oas.annotations.media.Schema;
9 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelTSLRespVO.java
1 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; 1 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;  
4 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; 3 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;
6 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
7 import lombok.Data; 7 import lombok.Data;
8 8
@@ -12,7 +12,7 @@ import java.util.List; @@ -12,7 +12,7 @@ import java.util.List;
12 @Data 12 @Data
13 public class IotThingModelTSLRespVO { 13 public class IotThingModelTSLRespVO {
14 14
15 - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 15 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
16 private Long productId; 16 private Long productId;
17 17
18 @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature_sensor") 18 @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature_sensor")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java
1 package cn.iocoder.yudao.module.iot.convert.thingmodel; 1 package cn.iocoder.yudao.module.iot.convert.thingmodel;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;  
4 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; 3 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;
6 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; 6 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
8 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; 8 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertConfig.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertConfigDO.java
1 -package cn.iocoder.yudao.module.iot.dal.dataobject.rule; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.alert;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 -import cn.iocoder.yudao.module.iot.enums.rule.IotAlertConfigReceiveTypeEnum; 5 +import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;
  6 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  8 +import cn.iocoder.yudao.module.iot.enums.DictTypeConstants;
  9 +import cn.iocoder.yudao.module.iot.enums.alert.IotAlertReceiveTypeEnum;
5 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 10 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
6 import com.baomidou.mybatisplus.annotation.KeySequence; 11 import com.baomidou.mybatisplus.annotation.KeySequence;
7 import com.baomidou.mybatisplus.annotation.TableField; 12 import com.baomidou.mybatisplus.annotation.TableField;
8 import com.baomidou.mybatisplus.annotation.TableId; 13 import com.baomidou.mybatisplus.annotation.TableId;
9 import com.baomidou.mybatisplus.annotation.TableName; 14 import com.baomidou.mybatisplus.annotation.TableName;
10 -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;  
11 -import lombok.*; 15 +import lombok.AllArgsConstructor;
  16 +import lombok.Builder;
  17 +import lombok.Data;
  18 +import lombok.NoArgsConstructor;
12 19
13 import java.util.List; 20 import java.util.List;
14 21
@@ -17,13 +24,13 @@ import java.util.List; @@ -17,13 +24,13 @@ import java.util.List;
17 * 24 *
18 * @author 芋道源码 25 * @author 芋道源码
19 */ 26 */
20 -@TableName("iot_alert_config") 27 +@TableName(value = "iot_alert_config", autoResultMap = true)
21 @KeySequence("iot_alert_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 28 @KeySequence("iot_alert_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
22 @Data 29 @Data
23 @Builder 30 @Builder
24 @NoArgsConstructor 31 @NoArgsConstructor
25 @AllArgsConstructor 32 @AllArgsConstructor
26 -public class IotAlertConfig extends BaseDO { 33 +public class IotAlertConfigDO extends BaseDO {
27 34
28 /** 35 /**
29 * 配置编号 36 * 配置编号
@@ -41,37 +48,37 @@ public class IotAlertConfig extends BaseDO { @@ -41,37 +48,37 @@ public class IotAlertConfig extends BaseDO {
41 /** 48 /**
42 * 配置状态 49 * 配置状态
43 * 50 *
44 - * TODO 数据字典 51 + * 字典 {@link DictTypeConstants#ALERT_LEVEL}
45 */ 52 */
46 private Integer level; 53 private Integer level;
47 /** 54 /**
48 * 配置状态 55 * 配置状态
49 * 56 *
50 - * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} 57 + * 枚举 {@link CommonStatusEnum}
51 */ 58 */
52 private Integer status; 59 private Integer status;
53 60
54 /** 61 /**
55 - * 关联的规则场景编号数组 62 + * 关联的场景联动规则编号数组
56 * 63 *
57 * 关联 {@link IotRuleSceneDO#getId()} 64 * 关联 {@link IotRuleSceneDO#getId()}
58 */ 65 */
59 - @TableField(typeHandler = JacksonTypeHandler.class)  
60 - private List<Long> ruleSceneIds; 66 + @TableField(typeHandler = LongListTypeHandler.class)
  67 + private List<Long> sceneRuleIds;
61 68
62 /** 69 /**
63 * 接收的用户编号数组 70 * 接收的用户编号数组
64 * 71 *
65 * 关联 {@link AdminUserRespDTO#getId()} 72 * 关联 {@link AdminUserRespDTO#getId()}
66 */ 73 */
67 - @TableField(typeHandler = JacksonTypeHandler.class) 74 + @TableField(typeHandler = LongListTypeHandler.class)
68 private List<Long> receiveUserIds; 75 private List<Long> receiveUserIds;
69 /** 76 /**
70 * 接收的类型数组 77 * 接收的类型数组
71 * 78 *
72 - * 枚举 {@link IotAlertConfigReceiveTypeEnum} 79 + * 枚举 {@link IotAlertReceiveTypeEnum}
73 */ 80 */
74 - @TableField(typeHandler = JacksonTypeHandler.class) 81 + @TableField(typeHandler = IntegerListTypeHandler.class)
75 private List<Integer> receiveTypes; 82 private List<Integer> receiveTypes;
76 83
77 } 84 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertRecordDO.java
1 -package cn.iocoder.yudao.module.iot.dal.dataobject.rule; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.alert;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
7 import com.baomidou.mybatisplus.annotation.KeySequence; 8 import com.baomidou.mybatisplus.annotation.KeySequence;
8 import com.baomidou.mybatisplus.annotation.TableField; 9 import com.baomidou.mybatisplus.annotation.TableField;
  10 +import com.baomidou.mybatisplus.annotation.TableId;
9 import com.baomidou.mybatisplus.annotation.TableName; 11 import com.baomidou.mybatisplus.annotation.TableName;
10 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; 12 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
11 import lombok.AllArgsConstructor; 13 import lombok.AllArgsConstructor;
@@ -18,7 +20,7 @@ import lombok.NoArgsConstructor; @@ -18,7 +20,7 @@ import lombok.NoArgsConstructor;
18 * 20 *
19 * @author 芋道源码 21 * @author 芋道源码
20 */ 22 */
21 -@TableName("iot_alert_record") 23 +@TableName(value = "iot_alert_record", autoResultMap = true)
22 @KeySequence("iot_alert_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 24 @KeySequence("iot_alert_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
23 @Data 25 @Data
24 @Builder 26 @Builder
@@ -29,47 +31,54 @@ public class IotAlertRecordDO extends BaseDO { @@ -29,47 +31,54 @@ public class IotAlertRecordDO extends BaseDO {
29 /** 31 /**
30 * 记录编号 32 * 记录编号
31 */ 33 */
32 - @TableField 34 + @TableId
33 private Long id; 35 private Long id;
34 /** 36 /**
35 * 告警名称 37 * 告警名称
36 * 38 *
37 - * 冗余 {@link IotAlertConfig#getName()} 39 + * 冗余 {@link IotAlertConfigDO#getId()}
38 */ 40 */
39 private Long configId; 41 private Long configId;
40 /** 42 /**
41 * 告警名称 43 * 告警名称
42 * 44 *
43 - * 冗余 {@link IotAlertConfig#getName()} 45 + * 冗余 {@link IotAlertConfigDO#getName()}
44 */ 46 */
45 - private String name;  
46 - 47 + private String configName;
47 /** 48 /**
48 - * 产品标识 49 + * 告警级别
49 * 50 *
50 - * 关联 {@link IotProductDO#getProductKey()} ()} 51 + * 冗余 {@link IotAlertConfigDO#getLevel()}
  52 + * 字典 {@link cn.iocoder.yudao.module.iot.enums.DictTypeConstants#ALERT_LEVEL}
51 */ 53 */
52 - private String productKey; 54 + private Integer configLevel;
53 /** 55 /**
54 - * 设备名称 56 + * 场景规则编号
55 * 57 *
56 - * 冗余 {@link IotDeviceDO#getDeviceName()} 58 + * 关联 {@link IotRuleSceneDO#getId()}
57 */ 59 */
58 - private String deviceName; 60 + private Long sceneRuleId;
59 61
60 - // TODO @芋艿:有没更好的方式 62 + /**
  63 + * 产品编号
  64 + *
  65 + * 关联 {@link IotProductDO#getId()}
  66 + */
  67 + private Long productId;
  68 + /**
  69 + * 设备编号
  70 + *
  71 + * 关联 {@link IotDeviceDO#getId()}
  72 + */
  73 + private Long deviceId;
61 /** 74 /**
62 * 触发的设备消息 75 * 触发的设备消息
63 */ 76 */
64 @TableField(typeHandler = JacksonTypeHandler.class) 77 @TableField(typeHandler = JacksonTypeHandler.class)
65 private IotDeviceMessage deviceMessage; 78 private IotDeviceMessage deviceMessage;
66 79
67 - // TODO @芋艿:换成枚举,枚举对应 ApiErrorLogProcessStatusEnum  
68 /** 80 /**
69 - * 处理状态  
70 - *  
71 - * true - 已处理  
72 - * false - 未处理 81 + * 是否处理
73 */ 82 */
74 private Boolean processStatus; 83 private Boolean processStatus;
75 /** 84 /**
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java
@@ -29,6 +29,11 @@ import java.util.Set; @@ -29,6 +29,11 @@ import java.util.Set;
29 public class IotDeviceDO extends TenantBaseDO { 29 public class IotDeviceDO extends TenantBaseDO {
30 30
31 /** 31 /**
  32 + * 设备编号 - 全部设备
  33 + */
  34 + public static final Long DEVICE_ID_ALL = 0L;
  35 +
  36 + /**
32 * 设备 ID,主键,自增 37 * 设备 ID,主键,自增
33 */ 38 */
34 @TableId 39 @TableId
@@ -112,7 +117,7 @@ public class IotDeviceDO extends TenantBaseDO { @@ -112,7 +117,7 @@ public class IotDeviceDO extends TenantBaseDO {
112 * 117 *
113 * 关联 {@link IotOtaFirmwareDO#getId()} 118 * 关联 {@link IotOtaFirmwareDO#getId()}
114 */ 119 */
115 - private String firmwareId; 120 + private Long firmwareId;
116 121
117 /** 122 /**
118 * 设备密钥,用于设备认证 123 * 设备密钥,用于设备认证
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java
1 package cn.iocoder.yudao.module.iot.dal.dataobject.ota; 1 package cn.iocoder.yudao.module.iot.dal.dataobject.ota;
2 2
  3 +import cn.hutool.crypto.digest.DigestAlgorithm;
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import com.baomidou.mybatisplus.annotation.KeySequence; 5 import com.baomidou.mybatisplus.annotation.KeySequence;
5 -import com.baomidou.mybatisplus.annotation.TableField; 6 +import com.baomidou.mybatisplus.annotation.TableId;
6 import com.baomidou.mybatisplus.annotation.TableName; 7 import com.baomidou.mybatisplus.annotation.TableName;
7 -import lombok.*; 8 +import lombok.AllArgsConstructor;
  9 +import lombok.Builder;
  10 +import lombok.Data;
  11 +import lombok.NoArgsConstructor;
8 12
9 /** 13 /**
10 * IoT OTA 固件 DO 14 * IoT OTA 固件 DO
@@ -24,14 +28,14 @@ public class IotOtaFirmwareDO extends BaseDO { @@ -24,14 +28,14 @@ public class IotOtaFirmwareDO extends BaseDO {
24 /** 28 /**
25 * 固件编号 29 * 固件编号
26 */ 30 */
27 - @TableField 31 + @TableId
28 private Long id; 32 private Long id;
29 /** 33 /**
30 * 固件名称 34 * 固件名称
31 */ 35 */
32 private String name; 36 private String name;
33 /** 37 /**
34 - * 固件版本 38 + * 固件描述
35 */ 39 */
36 private String description; 40 private String description;
37 /** 41 /**
@@ -44,37 +48,25 @@ public class IotOtaFirmwareDO extends BaseDO { @@ -44,37 +48,25 @@ public class IotOtaFirmwareDO extends BaseDO {
44 * 48 *
45 * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} 49 * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()}
46 */ 50 */
47 - // TODO @li:帮我改成 Long 哈,写错了  
48 - private String productId;  
49 - /**  
50 - * 产品标识  
51 - *  
52 - * 冗余 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getProductKey()}  
53 - */  
54 - private String productKey; 51 + private Long productId;
55 52
56 /** 53 /**
57 - * 签名方式  
58 - *  
59 - * 例如说:MD5、SHA256  
60 - */  
61 - private String signMethod;  
62 - /**  
63 - * 固件文件签名 54 + * 固件文件 URL
64 */ 55 */
65 - private String fileSign; 56 + private String fileUrl;
66 /** 57 /**
67 * 固件文件大小 58 * 固件文件大小
68 */ 59 */
69 private Long fileSize; 60 private Long fileSize;
70 /** 61 /**
71 - * 固件文件 URL 62 + * 固件文件签名算法
  63 + *
  64 + * 枚举 {@link DigestAlgorithm},目前只使用 MD5
72 */ 65 */
73 - private String fileUrl;  
74 - 66 + private String fileDigestAlgorithm;
75 /** 67 /**
76 - * 自定义信息,建议使用 JSON 格式 68 + * 固件文件签名结果
77 */ 69 */
78 - private String information; 70 + private String fileDigestValue;
79 71
80 } 72 }
81 \ No newline at end of file 73 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskDO.java
1 package cn.iocoder.yudao.module.iot.dal.dataobject.ota; 1 package cn.iocoder.yudao.module.iot.dal.dataobject.ota;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 4 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;
  5 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskStatusEnum;
5 import com.baomidou.mybatisplus.annotation.KeySequence; 6 import com.baomidou.mybatisplus.annotation.KeySequence;
6 -import com.baomidou.mybatisplus.annotation.TableField; 7 +import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 8 import com.baomidou.mybatisplus.annotation.TableName;
8 -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;  
9 -import lombok.*;  
10 -  
11 -import java.util.List; 9 +import lombok.AllArgsConstructor;
  10 +import lombok.Builder;
  11 +import lombok.Data;
  12 +import lombok.NoArgsConstructor;
12 13
13 /** 14 /**
14 * IoT OTA 升级任务 DO 15 * IoT OTA 升级任务 DO
15 * 16 *
16 * @author 芋道源码 17 * @author 芋道源码
17 */ 18 */
18 -@TableName(value = "iot_ota_upgrade_task", autoResultMap = true)  
19 -@KeySequence("iot_ota_upgrade_task_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 19 +@TableName(value = "iot_ota_task", autoResultMap = true)
  20 +@KeySequence("iot_ota_task_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
20 @Data 21 @Data
21 @Builder 22 @Builder
22 @NoArgsConstructor 23 @NoArgsConstructor
23 @AllArgsConstructor 24 @AllArgsConstructor
24 -public class IotOtaUpgradeTaskDO extends BaseDO { 25 +public class IotOtaTaskDO extends BaseDO {
25 26
26 /** 27 /**
27 * 任务编号 28 * 任务编号
28 */ 29 */
29 - @TableField 30 + @TableId
30 private Long id; 31 private Long id;
31 /** 32 /**
32 * 任务名称 33 * 任务名称
@@ -47,26 +48,23 @@ public class IotOtaUpgradeTaskDO extends BaseDO { @@ -47,26 +48,23 @@ public class IotOtaUpgradeTaskDO extends BaseDO {
47 /** 48 /**
48 * 任务状态 49 * 任务状态
49 * <p> 50 * <p>
50 - * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum} 51 + * 关联 {@link IotOtaTaskStatusEnum}
51 */ 52 */
52 private Integer status; 53 private Integer status;
53 54
54 /** 55 /**
55 - * 升级范围 56 + * 设备升级范围
56 * <p> 57 * <p>
57 - * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum} 58 + * 关联 {@link IotOtaTaskDeviceScopeEnum}
58 */ 59 */
59 - private Integer scope; 60 + private Integer deviceScope;
60 /** 61 /**
61 - * 设备数量 62 + * 设备总数数量
62 */ 63 */
63 - private Long deviceCount; 64 + private Integer deviceTotalCount;
64 /** 65 /**
65 - * 选中的设备编号数组  
66 - * <p>  
67 - * 关联 {@link IotDeviceDO#getId()} 66 + * 设备成功数量
68 */ 67 */
69 - @TableField(typeHandler = JacksonTypeHandler.class)  
70 - private List<Long> deviceIds; 68 + private Integer deviceSuccessCount;
71 69
72 } 70 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskRecordDO.java
@@ -3,25 +3,27 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.ota; @@ -3,25 +3,27 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.ota;
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; 5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
  6 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
6 import com.baomidou.mybatisplus.annotation.KeySequence; 7 import com.baomidou.mybatisplus.annotation.KeySequence;
7 import com.baomidou.mybatisplus.annotation.TableId; 8 import com.baomidou.mybatisplus.annotation.TableId;
8 import com.baomidou.mybatisplus.annotation.TableName; 9 import com.baomidou.mybatisplus.annotation.TableName;
9 -import lombok.*;  
10 -  
11 -import java.time.LocalDateTime; 10 +import lombok.AllArgsConstructor;
  11 +import lombok.Builder;
  12 +import lombok.Data;
  13 +import lombok.NoArgsConstructor;
12 14
13 /** 15 /**
14 - * IoT OTA 升级记录 DO 16 + * IoT OTA 升级任务记录 DO
15 * 17 *
16 * @author 芋道源码 18 * @author 芋道源码
17 */ 19 */
18 -@TableName(value = "iot_ota_upgrade_record", autoResultMap = true)  
19 -@KeySequence("iot_ota_upgrade_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 20 +@TableName(value = "iot_ota_task_record", autoResultMap = true)
  21 +@KeySequence("iot_ota_task_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
20 @Data 22 @Data
21 @Builder 23 @Builder
22 @NoArgsConstructor 24 @NoArgsConstructor
23 @AllArgsConstructor 25 @AllArgsConstructor
24 -public class IotOtaUpgradeRecordDO extends BaseDO { 26 +public class IotOtaTaskRecordDO extends BaseDO {
25 27
26 @TableId 28 @TableId
27 private Long id; 29 private Long id;
@@ -35,28 +37,16 @@ public class IotOtaUpgradeRecordDO extends BaseDO { @@ -35,28 +37,16 @@ public class IotOtaUpgradeRecordDO extends BaseDO {
35 /** 37 /**
36 * 任务编号 38 * 任务编号
37 * 39 *
38 - * 关联 {@link IotOtaUpgradeTaskDO#getId()} 40 + * 关联 {@link IotOtaTaskDO#getId()}
39 */ 41 */
40 private Long taskId; 42 private Long taskId;
41 43
42 /** 44 /**
43 - * 产品标识  
44 - *  
45 - * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()}  
46 - */  
47 - private String productKey;  
48 - /**  
49 - * 设备名称  
50 - *  
51 - * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()}  
52 - */  
53 - private String deviceName;  
54 - /**  
55 * 设备编号 45 * 设备编号
56 * 46 *
57 - * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} 47 + * 关联 {@link IotDeviceDO#getId()}
58 */ 48 */
59 - private String deviceId; 49 + private Long deviceId;
60 /** 50 /**
61 * 来源的固件编号 51 * 来源的固件编号
62 * 52 *
@@ -67,7 +57,7 @@ public class IotOtaUpgradeRecordDO extends BaseDO { @@ -67,7 +57,7 @@ public class IotOtaUpgradeRecordDO extends BaseDO {
67 /** 57 /**
68 * 升级状态 58 * 升级状态
69 * 59 *
70 - * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum} 60 + * 关联 {@link IotOtaTaskRecordStatusEnum}
71 */ 61 */
72 private Integer status; 62 private Integer status;
73 /** 63 /**
@@ -81,13 +71,5 @@ public class IotOtaUpgradeRecordDO extends BaseDO { @@ -81,13 +71,5 @@ public class IotOtaUpgradeRecordDO extends BaseDO {
81 * 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志 71 * 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志
82 */ 72 */
83 private String description; 73 private String description;
84 - /**  
85 - * 升级开始时间  
86 - */  
87 - private LocalDateTime startTime;  
88 - /**  
89 - * 升级结束时间  
90 - */  
91 - private LocalDateTime endTime;  
92 74
93 } 75 }
94 \ No newline at end of file 76 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
  6 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
  10 +import com.baomidou.mybatisplus.annotation.KeySequence;
  11 +import com.baomidou.mybatisplus.annotation.TableField;
  12 +import com.baomidou.mybatisplus.annotation.TableName;
  13 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  14 +import jakarta.validation.constraints.NotEmpty;
  15 +import lombok.AllArgsConstructor;
  16 +import lombok.Builder;
  17 +import lombok.Data;
  18 +import lombok.NoArgsConstructor;
  19 +
  20 +import java.util.List;
  21 +
  22 +/**
  23 + * IoT 数据流转规则 DO
  24 + *
  25 + * 监听 {@link SourceConfig} 数据源,转发到 {@link IotDataSinkDO} 数据目的
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +@TableName(value = "iot_data_rule", autoResultMap = true)
  30 +@KeySequence("iot_data_rule_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  31 +@Data
  32 +@Builder
  33 +@NoArgsConstructor
  34 +@AllArgsConstructor
  35 +public class IotDataRuleDO extends BaseDO {
  36 +
  37 + /**
  38 + * 数据流转规格编号
  39 + */
  40 + private Long id;
  41 + /**
  42 + * 数据流转规格名称
  43 + */
  44 + private String name;
  45 + /**
  46 + * 数据流转规格描述
  47 + */
  48 + private String description;
  49 + /**
  50 + * 数据流转规格状态
  51 + *
  52 + * 枚举 {@link CommonStatusEnum}
  53 + */
  54 + private Integer status;
  55 +
  56 + /**
  57 + * 数据源配置数组
  58 + */
  59 + @TableField(typeHandler = JacksonTypeHandler.class)
  60 + private List<SourceConfig> sourceConfigs;
  61 + /**
  62 + * 数据目的编号数组
  63 + *
  64 + * 关联 {@link IotDataSinkDO#getId()}
  65 + */
  66 + @TableField(typeHandler = LongListTypeHandler.class)
  67 + private List<Long> sinkIds;
  68 +
  69 + // TODO @芋艿:未来考虑使用 groovy;支持数据处理;
  70 +
  71 + /**
  72 + * 数据源配置
  73 + */
  74 + @Data
  75 + public static class SourceConfig {
  76 +
  77 + /**
  78 + * 消息方法
  79 + *
  80 + * 枚举 {@link IotDeviceMessageMethodEnum} 中的 upstream 上行部分
  81 + */
  82 + @NotEmpty(message = "消息方法不能为空")
  83 + private String method;
  84 +
  85 + /**
  86 + * 产品编号
  87 + *
  88 + * 关联 {@link IotProductDO#getId()}
  89 + */
  90 + private Long productId;
  91 + /**
  92 + * 设备编号
  93 + *
  94 + * 关联 {@link IotDeviceDO#getId()}
  95 + * 特殊:如果为 {@link IotDeviceDO#DEVICE_ID_ALL} 时,则是全部设备
  96 + */
  97 + @NotEmpty(message = "设备编号不能为空")
  98 + private Long deviceId;
  99 +
  100 + /**
  101 + * 标识符
  102 + *
  103 + * 1. 物模型时,对应:{@link IotThingModelDO#getIdentifier()}
  104 + */
  105 + private String identifier;
  106 +
  107 + }
  108 +
  109 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java
@@ -2,66 +2,61 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.rule; @@ -2,66 +2,61 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.rule;
2 2
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig;  
6 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum;  
7 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; 5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotAbstractDataSinkConfig;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
8 import com.baomidou.mybatisplus.annotation.KeySequence; 7 import com.baomidou.mybatisplus.annotation.KeySequence;
9 import com.baomidou.mybatisplus.annotation.TableField; 8 import com.baomidou.mybatisplus.annotation.TableField;
10 import com.baomidou.mybatisplus.annotation.TableId; 9 import com.baomidou.mybatisplus.annotation.TableId;
11 import com.baomidou.mybatisplus.annotation.TableName; 10 import com.baomidou.mybatisplus.annotation.TableName;
12 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; 11 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
13 -import lombok.*; 12 +import lombok.AllArgsConstructor;
  13 +import lombok.Builder;
  14 +import lombok.Data;
  15 +import lombok.NoArgsConstructor;
14 16
15 /** 17 /**
16 - * IoT 数据桥梁 DO 18 + * IoT 数据流转目的 DO
17 * 19 *
18 * @author 芋道源码 20 * @author 芋道源码
19 */ 21 */
20 -@TableName(value = "iot_data_bridge", autoResultMap = true) 22 +@TableName(value = "iot_data_sink", autoResultMap = true)
21 @KeySequence("iot_data_bridge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 23 @KeySequence("iot_data_bridge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
22 @Data 24 @Data
23 @Builder 25 @Builder
24 @NoArgsConstructor 26 @NoArgsConstructor
25 @AllArgsConstructor 27 @AllArgsConstructor
26 -public class IotDataBridgeDO extends BaseDO { 28 +public class IotDataSinkDO extends BaseDO {
27 29
28 /** 30 /**
29 - * 桥梁编号 31 + * 数据流转目的编号
30 */ 32 */
31 @TableId 33 @TableId
32 private Long id; 34 private Long id;
33 /** 35 /**
34 - * 桥梁名称 36 + * 数据流转目的名称
35 */ 37 */
36 private String name; 38 private String name;
37 /** 39 /**
38 - * 桥梁描述 40 + * 数据流转目的描述
39 */ 41 */
40 private String description; 42 private String description;
41 /** 43 /**
42 - * 桥梁状态 44 + * 数据流转目的状态
43 * 45 *
44 - * 枚举 {@link CommonStatusEnum} 46 + * 枚举 {@link CommonStatusEnum}
45 */ 47 */
46 private Integer status; 48 private Integer status;
47 - /**  
48 - * 桥梁方向  
49 - *  
50 - * 枚举 {@link IotDataBridgeDirectionEnum}  
51 - */  
52 - private Integer direction;  
53 49
54 /** 50 /**
55 - * 桥梁类型 51 + * 数据流转目的类型
56 * 52 *
57 - * 枚举 {@link IotDataBridgeTypeEnum} 53 + * 枚举 {@link IotDataSinkTypeEnum}
58 */ 54 */
59 private Integer type; 55 private Integer type;
60 -  
61 /** 56 /**
62 - * 桥梁配置 57 + * 数据流转目的配置
63 */ 58 */
64 @TableField(typeHandler = JacksonTypeHandler.class) 59 @TableField(typeHandler = JacksonTypeHandler.class)
65 - private IotDataBridgeAbstractConfig config; 60 + private IotAbstractDataSinkConfig config;
66 61
67 } 62 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java
@@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
7 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; 7 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
8 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; 8 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
9 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; 9 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
10 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum; 10 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionOperatorEnum;
11 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; 11 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
12 import com.baomidou.mybatisplus.annotation.KeySequence; 12 import com.baomidou.mybatisplus.annotation.KeySequence;
13 import com.baomidou.mybatisplus.annotation.TableField; 13 import com.baomidou.mybatisplus.annotation.TableField;
@@ -22,8 +22,9 @@ import lombok.NoArgsConstructor; @@ -22,8 +22,9 @@ import lombok.NoArgsConstructor;
22 import java.util.List; 22 import java.util.List;
23 import java.util.Map; 23 import java.util.Map;
24 24
  25 +// TODO @芋艿:优化注释;
25 /** 26 /**
26 - * IoT 场景联动 DO 27 + * IoT 场景联动规则 DO
27 * 28 *
28 * @author 芋道源码 29 * @author 芋道源码
29 */ 30 */
@@ -163,7 +164,7 @@ public class IotRuleSceneDO extends TenantBaseDO { @@ -163,7 +164,7 @@ public class IotRuleSceneDO extends TenantBaseDO {
163 /** 164 /**
164 * 操作符 165 * 操作符
165 * 166 *
166 - * 枚举 {@link IotRuleSceneTriggerConditionParameterOperatorEnum} 167 + * 枚举 {@link IotRuleSceneConditionOperatorEnum}
167 */ 168 */
168 private String operator; 169 private String operator;
169 170
@@ -171,7 +172,7 @@ public class IotRuleSceneDO extends TenantBaseDO { @@ -171,7 +172,7 @@ public class IotRuleSceneDO extends TenantBaseDO {
171 * 比较值 172 * 比较值
172 * 173 *
173 * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。 174 * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。
174 - * 例如说,{@link IotRuleSceneTriggerConditionParameterOperatorEnum#IN}、{@link IotRuleSceneTriggerConditionParameterOperatorEnum#BETWEEN} 175 + * 例如说,{@link IotRuleSceneConditionOperatorEnum#IN}、{@link IotRuleSceneConditionOperatorEnum#BETWEEN}
175 */ 176 */
176 private String value; 177 private String value;
177 178
@@ -193,18 +194,10 @@ public class IotRuleSceneDO extends TenantBaseDO { @@ -193,18 +194,10 @@ public class IotRuleSceneDO extends TenantBaseDO {
193 /** 194 /**
194 * 设备控制 195 * 设备控制
195 * 196 *
196 - * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DEVICE_CONTROL} 时 197 + * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DEVICE_PROPERTY_SET} 时
197 */ 198 */
198 private ActionDeviceControl deviceControl; 199 private ActionDeviceControl deviceControl;
199 200
200 - /**  
201 - * 数据桥接编号  
202 - *  
203 - * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时  
204 - * 关联:{@link IotDataBridgeDO#getId()}  
205 - */  
206 - private Long dataBridgeId;  
207 -  
208 } 201 }
209 202
210 /** 203 /**
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotSceneRuleDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  5 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
  10 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
  11 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionOperatorEnum;
  12 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionTypeEnum;
  13 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
  14 +import com.baomidou.mybatisplus.annotation.KeySequence;
  15 +import com.baomidou.mybatisplus.annotation.TableField;
  16 +import com.baomidou.mybatisplus.annotation.TableId;
  17 +import com.baomidou.mybatisplus.annotation.TableName;
  18 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  19 +import lombok.AllArgsConstructor;
  20 +import lombok.Builder;
  21 +import lombok.Data;
  22 +import lombok.NoArgsConstructor;
  23 +
  24 +import java.util.List;
  25 +
  26 +// TODO @puhui999:还是在 IotRuleSceneDO 里搞,这里主要可以看到变化字段哈。
  27 +/**
  28 + * IoT 场景联动 DO
  29 + *
  30 + * 基于 {@link Trigger} 触发 {@link Action}
  31 + *
  32 + * @author 芋道源码
  33 + */
  34 +@TableName(value = "iot_scene_rule", autoResultMap = true)
  35 +@KeySequence("iot_scene_rule_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  36 +@Data
  37 +@Builder
  38 +@NoArgsConstructor
  39 +@AllArgsConstructor
  40 +public class IotSceneRuleDO extends TenantBaseDO {
  41 +
  42 + /**
  43 + * 场景联动编号
  44 + */
  45 + @TableId
  46 + private Long id;
  47 + /**
  48 + * 场景联动名称
  49 + */
  50 + private String name;
  51 + /**
  52 + * 场景联动描述
  53 + */
  54 + private String description;
  55 + /**
  56 + * 场景联动状态
  57 + *
  58 + * 枚举 {@link CommonStatusEnum}
  59 + */
  60 + private Integer status;
  61 +
  62 + /**
  63 + * 场景定义配置
  64 + */
  65 + @TableField(typeHandler = JacksonTypeHandler.class)
  66 + private List<Trigger> triggers;
  67 +
  68 + /**
  69 + * 场景动作配置
  70 + */
  71 + @TableField(typeHandler = JacksonTypeHandler.class)
  72 + private List<Action> actions;
  73 +
  74 + /**
  75 + * 场景定义配置
  76 + */
  77 + @Data
  78 + public static class Trigger {
  79 +
  80 + // ========== 事件部分 ==========
  81 +
  82 + /**
  83 + * 场景事件类型
  84 + *
  85 + * 枚举 {@link IotRuleSceneTriggerTypeEnum}
  86 + * 1. {@link IotRuleSceneTriggerTypeEnum#DEVICE_STATE_UPDATE} 时,operator 非空,并且 value 为在线状态
  87 + * 2. {@link IotRuleSceneTriggerTypeEnum#DEVICE_PROPERTY_POST}
  88 + * {@link IotRuleSceneTriggerTypeEnum#DEVICE_EVENT_POST} 时,identifier、operator 非空,并且 value 为属性值
  89 + * 3. {@link IotRuleSceneTriggerTypeEnum#DEVICE_EVENT_POST}
  90 + * {@link IotRuleSceneTriggerTypeEnum#DEVICE_SERVICE_INVOKE} 时,identifier 非空,但是 operator、value 为空
  91 + * 4. {@link IotRuleSceneTriggerTypeEnum#TIMER} 时,conditions 非空,并且设备无关(无需 productId、deviceId 字段)
  92 + */
  93 + private Integer type;
  94 +
  95 + /**
  96 + * 产品编号
  97 + *
  98 + * 关联 {@link IotProductDO#getId()}
  99 + */
  100 + private Long productId;
  101 + /**
  102 + * 设备编号
  103 + *
  104 + * 关联 {@link IotDeviceDO#getId()}
  105 + * 特殊:如果为 {@link IotDeviceDO#DEVICE_ID_ALL} 时,则是全部设备
  106 + */
  107 + private Long deviceId;
  108 + /**
  109 + * 物模型标识符
  110 + *
  111 + * 对应:{@link IotThingModelDO#getIdentifier()}
  112 + */
  113 + private String identifier;
  114 + /**
  115 + * 操作符
  116 + *
  117 + * 枚举 {@link IotRuleSceneConditionOperatorEnum}
  118 + */
  119 + private String operator;
  120 + /**
  121 + * 参数(属性值、在线状态)
  122 + *
  123 + * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。
  124 + * 例如说,{@link IotRuleSceneConditionOperatorEnum#IN}、{@link IotRuleSceneConditionOperatorEnum#BETWEEN}
  125 + */
  126 + private String value;
  127 +
  128 + /**
  129 + * CRON 表达式
  130 + */
  131 + private String cronExpression;
  132 +
  133 + // ========== 条件部分 ==========
  134 +
  135 + /**
  136 + * 触发条件分组(状态条件分组)的数组
  137 + *
  138 + * 第一层 List:分组与分组之间,是“或”的关系
  139 + * 第二层 List:条件与条件之间,是“且”的关系
  140 + */
  141 + private List<List<TriggerCondition>> conditionGroups;
  142 +
  143 + }
  144 +
  145 + /**
  146 + * 触发条件(状态条件)
  147 + */
  148 + @Data
  149 + public static class TriggerCondition {
  150 +
  151 + /**
  152 + * 触发条件类型
  153 + *
  154 + * 枚举 {@link IotRuleSceneConditionTypeEnum}
  155 + * 1. {@link IotRuleSceneConditionTypeEnum#DEVICE_STATE} 时,operator 非空,并且 value 为在线状态
  156 + * 2. {@link IotRuleSceneConditionTypeEnum#DEVICE_PROPERTY} 时,identifier、operator 非空,并且 value 为属性值
  157 + * 3. {@link IotRuleSceneConditionTypeEnum#CURRENT_TIME} 时,operator 非空(使用 DATE_TIME_ 和 TIME_ 部分),并且 value 非空
  158 + */
  159 + private Integer type;
  160 +
  161 + /**
  162 + * 产品编号
  163 + *
  164 + * 关联 {@link IotProductDO#getId()}
  165 + */
  166 + private Long productId;
  167 + /**
  168 + * 设备编号
  169 + *
  170 + * 关联 {@link IotDeviceDO#getId()}
  171 + */
  172 + private Long deviceId;
  173 + /**
  174 + * 标识符(属性)
  175 + *
  176 + * 关联 {@link IotThingModelDO#getIdentifier()}
  177 + */
  178 + private String identifier;
  179 + /**
  180 + * 操作符
  181 + *
  182 + * 枚举 {@link IotRuleSceneConditionOperatorEnum}
  183 + */
  184 + private String operator;
  185 + /**
  186 + * 参数
  187 + *
  188 + * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。
  189 + * 例如说,{@link IotRuleSceneConditionOperatorEnum#IN}、{@link IotRuleSceneConditionOperatorEnum#BETWEEN}
  190 + */
  191 + private String param;
  192 +
  193 + }
  194 +
  195 + /**
  196 + * 场景动作配置
  197 + */
  198 + @Data
  199 + public static class Action {
  200 +
  201 + /**
  202 + * 执行类型
  203 + *
  204 + * 枚举 {@link IotRuleSceneActionTypeEnum}
  205 + * 1. {@link IotRuleSceneActionTypeEnum#DEVICE_PROPERTY_SET} 时,params 非空
  206 + * {@link IotRuleSceneActionTypeEnum#DEVICE_SERVICE_INVOKE} 时,params 非空
  207 + * 2. {@link IotRuleSceneActionTypeEnum#ALERT_TRIGGER} 时,alertConfigId 为空,因为是 {@link IotAlertConfigDO} 里面关联它
  208 + * 3. {@link IotRuleSceneActionTypeEnum#ALERT_RECOVER} 时,alertConfigId 非空
  209 + */
  210 + private Integer type;
  211 +
  212 + /**
  213 + * 产品编号
  214 + *
  215 + * 关联 {@link IotProductDO#getId()}
  216 + */
  217 + private Long productId;
  218 + /**
  219 + * 设备编号
  220 + *
  221 + * 关联 {@link IotDeviceDO#getId()}
  222 + */
  223 + private Long deviceId;
  224 + /**
  225 + * 请求参数
  226 + *
  227 + * 一般来说,对应 {@link IotDeviceMessage#getParams()} 请求参数
  228 + */
  229 + private Object params;
  230 +
  231 + /**
  232 + * 告警配置编号
  233 + *
  234 + * 关联 {@link IotAlertConfigDO#getId()}
  235 + */
  236 + private Long alertConfigId;
  237 +
  238 + }
  239 +
  240 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotAbstractDataSinkConfig.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;
2 2
3 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; 3 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
4 import com.fasterxml.jackson.annotation.JsonSubTypes; 4 import com.fasterxml.jackson.annotation.JsonSubTypes;
5 import com.fasterxml.jackson.annotation.JsonTypeInfo; 5 import com.fasterxml.jackson.annotation.JsonTypeInfo;
6 import lombok.Data; 6 import lombok.Data;
@@ -8,7 +8,7 @@ import lombok.Data; @@ -8,7 +8,7 @@ import lombok.Data;
8 /** 8 /**
9 * IoT IotDataBridgeConfig 抽象类 9 * IoT IotDataBridgeConfig 抽象类
10 * 10 *
11 - * 用于表示数据桥梁配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类 11 + * 用于表示数据目的配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类
12 * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 12 * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。
13 * 13 *
14 * @author HUIHUI 14 * @author HUIHUI
@@ -16,19 +16,19 @@ import lombok.Data; @@ -16,19 +16,19 @@ import lombok.Data;
16 @Data 16 @Data
17 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) 17 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
18 @JsonSubTypes({ 18 @JsonSubTypes({
19 - @JsonSubTypes.Type(value = IotDataBridgeHttpConfig.class, name = "1"),  
20 - @JsonSubTypes.Type(value = IotDataBridgeMqttConfig.class, name = "10"),  
21 - @JsonSubTypes.Type(value = IotDataBridgeRedisStreamConfig.class, name = "21"),  
22 - @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "30"),  
23 - @JsonSubTypes.Type(value = IotDataBridgeRabbitMQConfig.class, name = "31"),  
24 - @JsonSubTypes.Type(value = IotDataBridgeKafkaMQConfig.class, name = "32"), 19 + @JsonSubTypes.Type(value = IotDataSinkHttpConfig.class, name = "1"),
  20 + @JsonSubTypes.Type(value = IotDataSinkMqttConfig.class, name = "10"),
  21 + @JsonSubTypes.Type(value = IotDataSinkRedisStreamConfig.class, name = "21"),
  22 + @JsonSubTypes.Type(value = IotDataSinkRocketMQConfig.class, name = "30"),
  23 + @JsonSubTypes.Type(value = IotDataSinkRabbitMQConfig.class, name = "31"),
  24 + @JsonSubTypes.Type(value = IotDataSinkKafkaConfig.class, name = "32"),
25 }) 25 })
26 -public abstract class IotDataBridgeAbstractConfig { 26 +public abstract class IotAbstractDataSinkConfig {
27 27
28 /** 28 /**
29 * 配置类型 29 * 配置类型
30 * 30 *
31 - * 枚举 {@link IotDataBridgeTypeEnum#getType()} 31 + * 枚举 {@link IotDataSinkTypeEnum#getType()}
32 */ 32 */
33 private String type; 33 private String type;
34 34
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkHttpConfig.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
5 import java.util.Map; 5 import java.util.Map;
6 6
7 /** 7 /**
8 - * IoT HTTP 配置 {@link IotDataBridgeAbstractConfig} 实现类 8 + * IoT HTTP 配置 {@link IotAbstractDataSinkConfig} 实现类
9 * 9 *
10 * @author HUIHUI 10 * @author HUIHUI
11 */ 11 */
12 @Data 12 @Data
13 -public class IotDataBridgeHttpConfig extends IotDataBridgeAbstractConfig { 13 +public class IotDataSinkHttpConfig extends IotAbstractDataSinkConfig {
14 14
15 /** 15 /**
16 * 请求 URL 16 * 请求 URL
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkKafkaConfig.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
5 /** 5 /**
6 - * IoT Kafka 配置 {@link IotDataBridgeAbstractConfig} 实现类 6 + * IoT Kafka 配置 {@link IotAbstractDataSinkConfig} 实现类
7 * 7 *
8 * @author HUIHUI 8 * @author HUIHUI
9 */ 9 */
10 @Data 10 @Data
11 -public class IotDataBridgeKafkaMQConfig extends IotDataBridgeAbstractConfig { 11 +public class IotDataSinkKafkaConfig extends IotAbstractDataSinkConfig {
12 12
13 /** 13 /**
14 * Kafka 服务器地址 14 * Kafka 服务器地址
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkMqttConfig.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
5 /** 5 /**
6 - * IoT MQTT 配置 {@link IotDataBridgeAbstractConfig} 实现类 6 + * IoT MQTT 配置 {@link IotAbstractDataSinkConfig} 实现类
7 * 7 *
8 * @author HUIHUI 8 * @author HUIHUI
9 */ 9 */
10 @Data 10 @Data
11 -public class IotDataBridgeMqttConfig extends IotDataBridgeAbstractConfig { 11 +public class IotDataSinkMqttConfig extends IotAbstractDataSinkConfig {
12 12
13 /** 13 /**
14 * MQTT 服务器地址 14 * MQTT 服务器地址
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRabbitMQConfig.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
5 /** 5 /**
6 - * IoT RabbitMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类 6 + * IoT RabbitMQ 配置 {@link IotAbstractDataSinkConfig} 实现类
7 * 7 *
8 * @author HUIHUI 8 * @author HUIHUI
9 */ 9 */
10 @Data 10 @Data
11 -public class IotDataBridgeRabbitMQConfig extends IotDataBridgeAbstractConfig { 11 +public class IotDataSinkRabbitMQConfig extends IotAbstractDataSinkConfig {
12 12
13 /** 13 /**
14 * RabbitMQ 服务器地址 14 * RabbitMQ 服务器地址
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamConfig.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRedisStreamConfig.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
5 /** 5 /**
6 - * IoT Redis Stream 配置 {@link IotDataBridgeAbstractConfig} 实现类 6 + * IoT Redis Stream 配置 {@link IotAbstractDataSinkConfig} 实现类
7 * 7 *
8 * @author HUIHUI 8 * @author HUIHUI
9 */ 9 */
10 @Data 10 @Data
11 -public class IotDataBridgeRedisStreamConfig extends IotDataBridgeAbstractConfig { 11 +public class IotDataSinkRedisStreamConfig extends IotAbstractDataSinkConfig {
12 12
13 /** 13 /**
14 * Redis 服务器地址 14 * Redis 服务器地址
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRocketMQConfig.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
5 /** 5 /**
6 - * IoT RocketMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类 6 + * IoT RocketMQ 配置 {@link IotAbstractDataSinkConfig} 实现类
7 * 7 *
8 * @author HUIHUI 8 * @author HUIHUI
9 */ 9 */
10 @Data 10 @Data
11 -public class IotDataBridgeRocketMQConfig extends IotDataBridgeAbstractConfig { 11 +public class IotDataSinkRocketMQConfig extends IotAbstractDataSinkConfig {
12 12
13 /** 13 /**
14 * RocketMQ 名称服务器地址 14 * RocketMQ 名称服务器地址
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java
1 package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel; 1 package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; 4 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
8 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; 8 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
9 import com.baomidou.mybatisplus.annotation.KeySequence; 9 import com.baomidou.mybatisplus.annotation.KeySequence;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelEvent.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceEventTypeEnum; 4 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceEventTypeEnum;
@@ -9,6 +9,7 @@ import lombok.Data; @@ -9,6 +9,7 @@ import lombok.Data;
9 9
10 import java.util.List; 10 import java.util.List;
11 11
  12 +// TODO @puhui999:感觉这个,是不是放到 dal 里会好点?(讨论下,先不改哈)
12 /** 13 /**
13 * IoT 物模型中的事件 14 * IoT 物模型中的事件
14 * 15 *
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelParam.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; 4 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDataSpecs;
5 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; 5 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
6 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelParamDirectionEnum; 6 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelParamDirectionEnum;
7 import jakarta.validation.constraints.NotEmpty; 7 import jakarta.validation.constraints.NotEmpty;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelProperty.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; 4 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDataSpecs;
5 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; 5 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
6 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; 6 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum;
7 import jakarta.validation.constraints.NotEmpty; 7 import jakarta.validation.constraints.NotEmpty;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelService.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceCallTypeEnum; 4 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceCallTypeEnum;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelArrayDataSpecs.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;
2 2
3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 import jakarta.validation.Valid; 4 import jakarta.validation.Valid;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;
2 2
3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 import jakarta.validation.constraints.NotEmpty; 4 import jakarta.validation.constraints.NotEmpty;
@@ -10,7 +10,7 @@ import lombok.EqualsAndHashCode; @@ -10,7 +10,7 @@ import lombok.EqualsAndHashCode;
10 /** 10 /**
11 * IoT 物模型数据类型为布尔型或枚举型的 DataSpec 定义 11 * IoT 物模型数据类型为布尔型或枚举型的 DataSpec 定义
12 * 12 *
13 - * 数据类型,取值为 bool 或 enum。 13 + * 数据类型,取值为 bool 或 enum
14 * 14 *
15 * @author HUIHUI 15 * @author HUIHUI
16 */ 16 */
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDataSpecs.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDataSpecs.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;
2 2
3 import com.fasterxml.jackson.annotation.JsonSubTypes; 3 import com.fasterxml.jackson.annotation.JsonSubTypes;
4 import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 import com.fasterxml.jackson.annotation.JsonTypeInfo;
@@ -7,8 +7,8 @@ import lombok.Data; @@ -7,8 +7,8 @@ import lombok.Data;
7 /** 7 /**
8 * IoT ThingModelDataSpecs 抽象类 8 * IoT ThingModelDataSpecs 抽象类
9 * 9 *
10 - * 用于表示物模型数据的通用类型,根据具体的 "dataType" 字段动态映射到对应的子类。  
11 - * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 10 + * 用于表示物模型数据的通用类型,根据具体的 "dataType" 字段动态映射到对应的子类
  11 + * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景
12 * 12 *
13 * @author HUIHUI 13 * @author HUIHUI
14 */ 14 */
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;
2 2
3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 import jakarta.validation.constraints.Max; 4 import jakarta.validation.constraints.Max;
@@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode;
8 /** 8 /**
9 * IoT 物模型数据类型为时间型或文本型的 DataSpec 定义 9 * IoT 物模型数据类型为时间型或文本型的 DataSpec 定义
10 * 10 *
11 - * 数据类型,取值为 date 或 text。 11 + * 数据类型,取值为 date 或 text
12 * 12 *
13 * @author HUIHUI 13 * @author HUIHUI
14 */ 14 */
@@ -18,13 +18,14 @@ import lombok.EqualsAndHashCode; @@ -18,13 +18,14 @@ import lombok.EqualsAndHashCode;
18 public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { 18 public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs {
19 19
20 /** 20 /**
21 - * 数据长度,单位为字节。取值不能超过 2048。  
22 - * 当 dataType 为 text 时,需传入该参数。 21 + * 数据长度,单位为字节。取值不能超过 2048
  22 + *
  23 + * 当 dataType 为 text 时,需传入该参数
23 */ 24 */
24 @Max(value = 2048, message = "数据长度不能超过 2048") 25 @Max(value = 2048, message = "数据长度不能超过 2048")
25 private Integer length; 26 private Integer length;
26 /** 27 /**
27 - * 默认值,可选参数,用于存储默认值。 28 + * 默认值,可选参数,用于存储默认值
28 */ 29 */
29 private String defaultValue; 30 private String defaultValue;
30 31
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelNumericDataSpec.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;
2 2
3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 import jakarta.validation.constraints.NotEmpty; 4 import jakarta.validation.constraints.NotEmpty;
@@ -9,7 +9,7 @@ import lombok.EqualsAndHashCode; @@ -9,7 +9,7 @@ import lombok.EqualsAndHashCode;
9 /** 9 /**
10 * IoT 物模型数据类型为数值的 DataSpec 定义 10 * IoT 物模型数据类型为数值的 DataSpec 定义
11 * 11 *
12 - * 数据类型,取值为 int、float 或 double。 12 + * 数据类型,取值为 int、float 或 double
13 * 13 *
14 * @author HUIHUI 14 * @author HUIHUI
15 */ 15 */
@@ -19,37 +19,37 @@ import lombok.EqualsAndHashCode; @@ -19,37 +19,37 @@ import lombok.EqualsAndHashCode;
19 public class ThingModelNumericDataSpec extends ThingModelDataSpecs { 19 public class ThingModelNumericDataSpec extends ThingModelDataSpecs {
20 20
21 /** 21 /**
22 - * 最大值,需转为字符串类型。值必须与 dataType 类型一致。 22 + * 最大值,需转为字符串类型。值必须与 dataType 类型一致
23 */ 23 */
24 @NotEmpty(message = "最大值不能为空") 24 @NotEmpty(message = "最大值不能为空")
25 @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "最大值必须为数值类型") 25 @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "最大值必须为数值类型")
26 private String max; 26 private String max;
27 /** 27 /**
28 - * 最小值,需转为字符串类型。值必须与 dataType 类型一致。 28 + * 最小值,需转为字符串类型。值必须与 dataType 类型一致
29 */ 29 */
30 @NotEmpty(message = "最小值不能为空") 30 @NotEmpty(message = "最小值不能为空")
31 @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "最小值必须为数值类型") 31 @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "最小值必须为数值类型")
32 private String min; 32 private String min;
33 /** 33 /**
34 - * 步长,需转为字符串类型。值必须与 dataType 类型一致。 34 + * 步长,需转为字符串类型。值必须与 dataType 类型一致
35 */ 35 */
36 @NotEmpty(message = "步长不能为空") 36 @NotEmpty(message = "步长不能为空")
37 @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "步长必须为数值类型") 37 @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "步长必须为数值类型")
38 private String step; 38 private String step;
39 /** 39 /**
40 - * 精度。当 dataType 为 float 或 double 时可选传入。 40 + * 精度。当 dataType 为 float 或 double 时可选传入
41 */ 41 */
42 private String precise; 42 private String precise;
43 /** 43 /**
44 - * 默认值,可传入用于存储的默认值。 44 + * 默认值,可传入用于存储的默认值
45 */ 45 */
46 private String defaultValue; 46 private String defaultValue;
47 /** 47 /**
48 - * 单位的符号。 48 + * 单位的符号
49 */ 49 */
50 private String unit; 50 private String unit;
51 /** 51 /**
52 - * 单位的名称。 52 + * 单位的名称
53 */ 53 */
54 private String unitName; 54 private String unitName;
55 55
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelStructDataSpecs.java
1 -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; 1 +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; 4 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.alert;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * IoT 告警配置 Mapper
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Mapper
  19 +public interface IotAlertConfigMapper extends BaseMapperX<IotAlertConfigDO> {
  20 +
  21 + default PageResult<IotAlertConfigDO> selectPage(IotAlertConfigPageReqVO reqVO) {
  22 + return selectPage(reqVO, new LambdaQueryWrapperX<IotAlertConfigDO>()
  23 + .likeIfPresent(IotAlertConfigDO::getName, reqVO.getName())
  24 + .eqIfPresent(IotAlertConfigDO::getStatus, reqVO.getStatus())
  25 + .betweenIfPresent(IotAlertConfigDO::getCreateTime, reqVO.getCreateTime())
  26 + .orderByDesc(IotAlertConfigDO::getId));
  27 + }
  28 +
  29 + default List<IotAlertConfigDO> selectListByStatus(Integer status) {
  30 + return selectList(IotAlertConfigDO::getStatus, status);
  31 + }
  32 +
  33 + default List<IotAlertConfigDO> selectListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) {
  34 + return selectList(new LambdaQueryWrapperX<IotAlertConfigDO>()
  35 + .eq(IotAlertConfigDO::getStatus, status)
  36 + .apply(MyBatisUtils.findInSet("scene_rule_id", sceneRuleId)));
  37 + }
  38 +
  39 +}
0 \ No newline at end of file 40 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertRecordMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.alert;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
  8 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.Collection;
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * IoT 告警记录 Mapper
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +@Mapper
  20 +public interface IotAlertRecordMapper extends BaseMapperX<IotAlertRecordDO> {
  21 +
  22 + default PageResult<IotAlertRecordDO> selectPage(IotAlertRecordPageReqVO reqVO) {
  23 + return selectPage(reqVO, new LambdaQueryWrapperX<IotAlertRecordDO>()
  24 + .eqIfPresent(IotAlertRecordDO::getConfigId, reqVO.getConfigId())
  25 + .eqIfPresent(IotAlertRecordDO::getConfigLevel, reqVO.getLevel())
  26 + .eqIfPresent(IotAlertRecordDO::getProductId, reqVO.getProductId())
  27 + .eqIfPresent(IotAlertRecordDO::getDeviceId, reqVO.getDeviceId())
  28 + .eqIfPresent(IotAlertRecordDO::getProcessStatus, reqVO.getProcessStatus())
  29 + .betweenIfPresent(IotAlertRecordDO::getCreateTime, reqVO.getCreateTime())
  30 + .orderByDesc(IotAlertRecordDO::getId));
  31 + }
  32 +
  33 + default List<IotAlertRecordDO> selectListBySceneRuleId(Long sceneRuleId, Long deviceId, Boolean processStatus) {
  34 + return selectList(new LambdaQueryWrapperX<IotAlertRecordDO>()
  35 + .eq(IotAlertRecordDO::getSceneRuleId, sceneRuleId)
  36 + .eqIfPresent(IotAlertRecordDO::getDeviceId, deviceId)
  37 + .eqIfPresent(IotAlertRecordDO::getProcessStatus, processStatus)
  38 + .orderByDesc(IotAlertRecordDO::getId));
  39 + }
  40 +
  41 + default int updateList(Collection<Long> ids, IotAlertRecordDO updateObj) {
  42 + return update(updateObj, new LambdaUpdateWrapper<IotAlertRecordDO>()
  43 + .in(IotAlertRecordDO::getId, ids));
  44 + }
  45 +
  46 +}
0 \ No newline at end of file 47 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
@@ -6,13 +6,15 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -6,13 +6,15 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  9 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  10 +import jakarta.annotation.Nullable;
9 import org.apache.ibatis.annotations.Mapper; 11 import org.apache.ibatis.annotations.Mapper;
10 12
11 -import javax.annotation.Nullable;  
12 import java.time.LocalDateTime; 13 import java.time.LocalDateTime;
13 import java.util.Collection; 14 import java.util.Collection;
14 import java.util.List; 15 import java.util.List;
15 import java.util.Map; 16 import java.util.Map;
  17 +import java.util.stream.Collectors;
16 18
17 /** 19 /**
18 * IoT 设备 Mapper 20 * IoT 设备 Mapper
@@ -50,8 +52,11 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; { @@ -50,8 +52,11 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; {
50 return selectCount(IotDeviceDO::getProductId, productId); 52 return selectCount(IotDeviceDO::getProductId, productId);
51 } 53 }
52 54
53 - default List<IotDeviceDO> selectListByDeviceType(Integer deviceType) {  
54 - return selectList(IotDeviceDO::getDeviceType, deviceType); 55 + default List<IotDeviceDO> selectListByCondition(@Nullable Integer deviceType,
  56 + @Nullable Long productId) {
  57 + return selectList(new LambdaQueryWrapperX<IotDeviceDO>()
  58 + .eqIfPresent(IotDeviceDO::getDeviceType, deviceType)
  59 + .eqIfPresent(IotDeviceDO::getProductId, productId));
55 } 60 }
56 61
57 default List<IotDeviceDO> selectListByState(Integer state) { 62 default List<IotDeviceDO> selectListByState(Integer state) {
@@ -78,20 +83,38 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; { @@ -78,20 +83,38 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; {
78 .in(IotDeviceDO::getDeviceName, deviceNames)); 83 .in(IotDeviceDO::getDeviceName, deviceNames));
79 } 84 }
80 85
  86 + default IotDeviceDO selectBySerialNumber(String serialNumber) {
  87 + return selectOne(IotDeviceDO::getSerialNumber, serialNumber);
  88 + }
  89 +
81 /** 90 /**
82 - * 查询指定产品下各状态的设备数量 91 + * 查询指定产品下的设备数量
83 * 92 *
84 - * @return 设备数量统计列表 93 + * @return 产品编号 -> 设备数量的映射
85 */ 94 */
86 - // TODO @super:通过 mybatis-plus 来写哈,然后返回 Map 貌似就行了?!  
87 - List<Map<String, Object>> selectDeviceCountMapByProductId(); 95 + default Map<Long, Integer> selectDeviceCountMapByProductId() {
  96 + List<Map<String, Object>> result = selectMaps(new QueryWrapper<IotDeviceDO>()
  97 + .select("product_id AS productId", "COUNT(1) AS deviceCount")
  98 + .groupBy("product_id"));
  99 + return result.stream().collect(Collectors.toMap(
  100 + map -> Long.valueOf(map.get("productId").toString()),
  101 + map -> Integer.valueOf(map.get("deviceCount").toString())
  102 + ));
  103 + }
88 104
89 - // TODO @super:通过 mybatis-plus 来写哈,然后返回 Map 貌似就行了?!  
90 /** 105 /**
91 * 查询各个状态下的设备数量 106 * 查询各个状态下的设备数量
92 * 107 *
93 - * @return 设备数量统计列表 108 + * @return 设备状态 -> 设备数量的映射
94 */ 109 */
95 - List<Map<String, Object>> selectDeviceCountGroupByState(); 110 + default Map<Integer, Long> selectDeviceCountGroupByState() {
  111 + List<Map<String, Object>> result = selectMaps(new QueryWrapper<IotDeviceDO>()
  112 + .select("state", "COUNT(1) AS deviceCount")
  113 + .groupBy("state"));
  114 + return result.stream().collect(Collectors.toMap(
  115 + map -> Integer.valueOf(map.get("state").toString()),
  116 + map -> Long.valueOf(map.get("deviceCount").toString())
  117 + ));
  118 + }
96 119
97 } 120 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java
@@ -7,34 +7,19 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwa @@ -7,34 +7,19 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwa
7 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
10 -import java.util.List;  
11 -  
12 -// TODO @li:参考 IotOtaUpgradeRecordMapper 的写法  
13 @Mapper 10 @Mapper
14 public interface IotOtaFirmwareMapper extends BaseMapperX<IotOtaFirmwareDO> { 11 public interface IotOtaFirmwareMapper extends BaseMapperX<IotOtaFirmwareDO> {
15 12
16 - /**  
17 - * 根据产品ID和固件版本号查询固件信息列表。  
18 - *  
19 - * @param productId 产品ID,用于筛选固件信息。  
20 - * @param version 固件版本号,用于筛选固件信息。  
21 - * @return 返回符合条件的固件信息列表。  
22 - */  
23 - default List<IotOtaFirmwareDO> selectByProductIdAndVersion(String productId, String version) {  
24 - return selectList(IotOtaFirmwareDO::getProductId, productId, 13 + default IotOtaFirmwareDO selectByProductIdAndVersion(Long productId, String version) {
  14 + return selectOne(IotOtaFirmwareDO::getProductId, productId,
25 IotOtaFirmwareDO::getVersion, version); 15 IotOtaFirmwareDO::getVersion, version);
26 } 16 }
27 17
28 - /**  
29 - * 分页查询固件信息,支持根据名称和产品ID进行筛选,并按创建时间降序排列。  
30 - *  
31 - * @param pageReqVO 分页查询请求对象,包含分页参数和筛选条件。  
32 - * @return 返回分页查询结果,包含符合条件的固件信息列表。  
33 - */  
34 default PageResult<IotOtaFirmwareDO> selectPage(IotOtaFirmwarePageReqVO pageReqVO) { 18 default PageResult<IotOtaFirmwareDO> selectPage(IotOtaFirmwarePageReqVO pageReqVO) {
35 return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaFirmwareDO>() 19 return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaFirmwareDO>()
36 .likeIfPresent(IotOtaFirmwareDO::getName, pageReqVO.getName()) 20 .likeIfPresent(IotOtaFirmwareDO::getName, pageReqVO.getName())
37 .eqIfPresent(IotOtaFirmwareDO::getProductId, pageReqVO.getProductId()) 21 .eqIfPresent(IotOtaFirmwareDO::getProductId, pageReqVO.getProductId())
  22 + .betweenIfPresent(IotOtaFirmwareDO::getCreateTime, pageReqVO.getCreateTime())
38 .orderByDesc(IotOtaFirmwareDO::getCreateTime)); 23 .orderByDesc(IotOtaFirmwareDO::getCreateTime));
39 } 24 }
40 25
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.ota;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +@Mapper
  11 +public interface IotOtaTaskMapper extends BaseMapperX<IotOtaTaskDO> {
  12 +
  13 + default IotOtaTaskDO selectByFirmwareIdAndName(Long firmwareId, String name) {
  14 + return selectOne(IotOtaTaskDO::getFirmwareId, firmwareId,
  15 + IotOtaTaskDO::getName, name);
  16 + }
  17 +
  18 + default PageResult<IotOtaTaskDO> selectUpgradeTaskPage(IotOtaTaskPageReqVO pageReqVO) {
  19 + return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaTaskDO>()
  20 + .eqIfPresent(IotOtaTaskDO::getFirmwareId, pageReqVO.getFirmwareId())
  21 + .likeIfPresent(IotOtaTaskDO::getName, pageReqVO.getName()));
  22 + }
  23 +
  24 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskRecordMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.ota;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
  8 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.List;
  12 +import java.util.Set;
  13 +
  14 +@Mapper
  15 +public interface IotOtaTaskRecordMapper extends BaseMapperX<IotOtaTaskRecordDO> {
  16 +
  17 + default List<IotOtaTaskRecordDO> selectListByFirmwareIdAndTaskId(Long firmwareId, Long taskId) {
  18 + return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
  19 + .eqIfPresent(IotOtaTaskRecordDO::getFirmwareId, firmwareId)
  20 + .eqIfPresent(IotOtaTaskRecordDO::getTaskId, taskId)
  21 + .select(IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus));
  22 + }
  23 +
  24 + default PageResult<IotOtaTaskRecordDO> selectPage(IotOtaTaskRecordPageReqVO pageReqVO) {
  25 + return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
  26 + .eqIfPresent(IotOtaTaskRecordDO::getTaskId, pageReqVO.getTaskId())
  27 + .eqIfPresent(IotOtaTaskRecordDO::getStatus, pageReqVO.getStatus()));
  28 + }
  29 +
  30 + default void updateByTaskIdAndStatus(Long taskId, Integer fromStatus, IotOtaTaskRecordDO updateRecord) {
  31 + update(updateRecord, new LambdaUpdateWrapper<IotOtaTaskRecordDO>()
  32 + .eq(IotOtaTaskRecordDO::getTaskId, taskId)
  33 + .eq(IotOtaTaskRecordDO::getStatus, fromStatus));
  34 + }
  35 +
  36 + default List<IotOtaTaskRecordDO> selectListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses) {
  37 + return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()
  38 + .inIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceIds)
  39 + .inIfPresent(IotOtaTaskRecordDO::getStatus, statuses));
  40 + }
  41 +
  42 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java deleted
1 -package cn.iocoder.yudao.module.iot.dal.mysql.ota;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;  
5 -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO;  
7 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO;  
8 -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;  
9 -import org.apache.ibatis.annotations.Mapper;  
10 -import org.apache.ibatis.annotations.Param;  
11 -import org.apache.ibatis.annotations.Select;  
12 -  
13 -import java.util.List;  
14 -import java.util.Map;  
15 -  
16 -@Mapper  
17 -public interface IotOtaUpgradeRecordMapper extends BaseMapperX<IotOtaUpgradeRecordDO> {  
18 -  
19 - // TODO @li:selectByFirmwareIdAndTaskIdAndDeviceId;让方法自解释  
20 - /**  
21 - * 根据条件查询单个OTA升级记录  
22 - *  
23 - * @param firmwareId 固件ID,可选参数,用于筛选固件ID匹配的记录  
24 - * @param taskId 任务ID,可选参数,用于筛选任务ID匹配的记录  
25 - * @param deviceId 设备ID,可选参数,用于筛选设备ID匹配的记录  
26 - * @return 返回符合条件的单个OTA升级记录,如果不存在则返回null  
27 - */  
28 - default IotOtaUpgradeRecordDO selectByConditions(Long firmwareId, Long taskId, String deviceId) {  
29 - // 使用LambdaQueryWrapperX构建查询条件,根据传入的参数动态添加查询条件  
30 - return selectOne(new LambdaQueryWrapperX<IotOtaUpgradeRecordDO>()  
31 - .eqIfPresent(IotOtaUpgradeRecordDO::getFirmwareId, firmwareId)  
32 - .eqIfPresent(IotOtaUpgradeRecordDO::getTaskId, taskId)  
33 - .eqIfPresent(IotOtaUpgradeRecordDO::getDeviceId, deviceId));  
34 - }  
35 -  
36 - // TODO @li:这个是不是 groupby status 就 ok 拉?  
37 - /**  
38 - * 根据任务ID和设备名称查询OTA升级记录的状态统计信息。  
39 - * 该函数通过SQL查询统计不同状态(0到5)的记录数量,并返回一个包含统计结果的Map列表。  
40 - *  
41 - * @param taskId 任务ID,用于筛选特定任务的OTA升级记录。  
42 - * @param deviceName 设备名称,支持模糊查询,用于筛选特定设备的OTA升级记录。  
43 - * @return 返回一个Map列表,每个Map包含不同状态(0到5)的记录数量。  
44 - */  
45 - @Select("select count(case when status = 0 then 1 else 0) as `0` " +  
46 - "count(case when status = 1 then 1 else 0) as `1` " +  
47 - "count(case when status = 2 then 1 else 0) as `2` " +  
48 - "count(case when status = 3 then 1 else 0) as `3` " +  
49 - "count(case when status = 4 then 1 else 0) as `4` " +  
50 - "count(case when status = 5 then 1 else 0) as `5` " +  
51 - "from iot_ota_upgrade_record " +  
52 - "where task_id = #{taskId} " +  
53 - "and device_name like concat('%', #{deviceName}, '%') " +  
54 - "and status = #{status}")  
55 - List<Map<String, Object>> selectOtaUpgradeRecordCount(@Param("taskId") Long taskId,  
56 - @Param("deviceName") String deviceName);  
57 -  
58 - /**  
59 - * 根据固件ID查询OTA升级记录的状态统计信息。  
60 - * 该函数通过SQL查询统计不同状态(0到5)的记录数量,并返回一个包含统计结果的Map列表。  
61 - *  
62 - * @param firmwareId 固件ID,用于筛选特定固件的OTA升级记录。  
63 - * @return 返回一个Map列表,每个Map包含不同状态(0到5)的记录数量。  
64 - */  
65 - @Select("select count(case when status = 0 then 1 else 0) as `0` " +  
66 - "count(case when status = 1 then 1 else 0) as `1` " +  
67 - "count(case when status = 2 then 1 else 0) as `2` " +  
68 - "count(case when status = 3 then 1 else 0) as `3` " +  
69 - "count(case when status = 4 then 1 else 0) as `4` " +  
70 - "count(case when status = 5 then 1 else 0) as `5` " +  
71 - "from iot_ota_upgrade_record " +  
72 - "where firmware_id = #{firmwareId}")  
73 - List<Map<String, Object>> selectOtaUpgradeRecordStatistics(Long firmwareId);  
74 -  
75 - // TODO @li:这里的注释,可以去掉哈  
76 - /**  
77 - * 根据分页查询条件获取 OTA升级记录的分页结果  
78 - *  
79 - * @param pageReqVO 分页查询请求参数,包含设备名称、任务ID等查询条件  
80 - * @return 返回分页查询结果,包含符合条件的 OTA升级记录列表  
81 - */  
82 - // TODO @li:selectPage 就 ok 拉。  
83 - default PageResult<IotOtaUpgradeRecordDO> selectUpgradeRecordPage(IotOtaUpgradeRecordPageReqVO pageReqVO) {  
84 - // TODO @li:这里的注释,可以去掉哈;然后下面的“如果”。。。也没必要注释  
85 - // 使用LambdaQueryWrapperX构建查询条件,并根据请求参数动态添加查询条件  
86 - return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaUpgradeRecordDO>()  
87 - .likeIfPresent(IotOtaUpgradeRecordDO::getDeviceName, pageReqVO.getDeviceName()) // 如果设备名称存在,则添加模糊查询条件  
88 - .eqIfPresent(IotOtaUpgradeRecordDO::getTaskId, pageReqVO.getTaskId())); // 如果任务ID存在,则添加等值查询条件  
89 - }  
90 -  
91 - // TODO @li:这里的注释,可以去掉哈  
92 - /**  
93 - * 根据任务ID和状态更新升级记录的状态  
94 - * <p>  
95 - * 该函数用于将符合指定任务ID和状态的升级记录的状态更新为新的状态。  
96 - *  
97 - * @param setStatus 要设置的新状态值,类型为Integer  
98 - * @param taskId 要更新的升级记录对应的任务ID,类型为Long  
99 - * @param whereStatus 用于筛选升级记录的当前状态值,类型为Integer  
100 - */  
101 - // TODO @li:改成 updateByTaskIdAndStatus(taskId, status, IotOtaUpgradeRecordDO) 更通用一些。  
102 - default void updateUpgradeRecordStatusByTaskIdAndStatus(Integer setStatus, Long taskId, Integer whereStatus) {  
103 - // 使用LambdaUpdateWrapper构建更新条件,将指定状态的记录更新为指定状态  
104 - update(new LambdaUpdateWrapper<IotOtaUpgradeRecordDO>()  
105 - .set(IotOtaUpgradeRecordDO::getStatus, setStatus)  
106 - .eq(IotOtaUpgradeRecordDO::getTaskId, taskId)  
107 - .eq(IotOtaUpgradeRecordDO::getStatus, whereStatus)  
108 - );  
109 - }  
110 -  
111 - // TODO @li:参考上面的建议,调整下这个方法  
112 - /**  
113 - * 根据状态查询符合条件的升级记录列表  
114 - * <p>  
115 - * 该函数使用LambdaQueryWrapperX构建查询条件,查询指定状态的升级记录。  
116 - *  
117 - * @param state 升级记录的状态,用于筛选符合条件的记录  
118 - * @return 返回符合指定状态的升级记录列表,类型为List<IotOtaUpgradeRecordDO>  
119 - */  
120 - default List<IotOtaUpgradeRecordDO> selectUpgradeRecordListByState(Integer state) {  
121 - // 使用LambdaQueryWrapperX构建查询条件,根据状态查询符合条件的升级记录  
122 - return selectList(new LambdaQueryWrapperX<IotOtaUpgradeRecordDO>()  
123 - .eq(IotOtaUpgradeRecordDO::getStatus, state));  
124 - }  
125 -  
126 - // TODO @li:参考上面的建议,调整下这个方法  
127 - /**  
128 - * 更新升级记录状态  
129 - * <p>  
130 - * 该函数用于批量更新指定ID列表中的升级记录状态。通过传入的ID列表和状态值,使用LambdaUpdateWrapper构建更新条件,  
131 - * 并执行更新操作。  
132 - *  
133 - * @param ids 需要更新的升级记录ID列表,类型为List<Long>。传入的ID列表中的记录将被更新。  
134 - * @param status 要更新的状态值,类型为Integer。该值将被设置到符合条件的升级记录中。  
135 - */  
136 - default void updateUpgradeRecordStatus(List<Long> ids, Integer status) {  
137 - // 使用LambdaUpdateWrapper构建更新条件,设置状态字段,并根据ID列表进行筛选  
138 - update(new LambdaUpdateWrapper<IotOtaUpgradeRecordDO>()  
139 - .set(IotOtaUpgradeRecordDO::getStatus, status)  
140 - .in(IotOtaUpgradeRecordDO::getId, ids)  
141 - );  
142 - }  
143 -  
144 - // TODO @li:参考上面的建议,调整下这个方法  
145 - /**  
146 - * 根据任务ID查询升级记录列表  
147 - * <p>  
148 - * 该函数通过任务ID查询符合条件的升级记录,并返回查询结果列表。  
149 - *  
150 - * @param taskId 任务ID,用于筛选升级记录  
151 - * @return 返回符合条件的升级记录列表,若未找到则返回空列表  
152 - */  
153 - default List<IotOtaUpgradeRecordDO> selectUpgradeRecordListByTaskId(Long taskId) {  
154 - // 使用LambdaQueryWrapperX构建查询条件,根据任务ID查询符合条件的升级记录  
155 - return selectList(new LambdaQueryWrapperX<IotOtaUpgradeRecordDO>()  
156 - .eq(IotOtaUpgradeRecordDO::getTaskId, taskId));  
157 - }  
158 -  
159 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java deleted
1 -package cn.iocoder.yudao.module.iot.dal.mysql.ota;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;  
5 -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskPageReqVO;  
7 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO;  
8 -import org.apache.ibatis.annotations.Mapper;  
9 -  
10 -import java.util.List;  
11 -  
12 -/**  
13 - * OTA 升级任务Mapper  
14 - *  
15 - * @author Shelly  
16 - */  
17 -@Mapper  
18 -public interface IotOtaUpgradeTaskMapper extends BaseMapperX<IotOtaUpgradeTaskDO> {  
19 -  
20 - /**  
21 - * 根据固件ID和任务名称查询升级任务列表。  
22 - *  
23 - * @param firmwareId 固件ID,用于筛选升级任务  
24 - * @param name 任务名称,用于筛选升级任务  
25 - * @return 符合条件的升级任务列表  
26 - */  
27 - default List<IotOtaUpgradeTaskDO> selectByFirmwareIdAndName(Long firmwareId, String name) {  
28 - return selectList(new LambdaQueryWrapperX<IotOtaUpgradeTaskDO>()  
29 - .eqIfPresent(IotOtaUpgradeTaskDO::getFirmwareId, firmwareId)  
30 - .eqIfPresent(IotOtaUpgradeTaskDO::getName, name));  
31 - }  
32 -  
33 - /**  
34 - * 分页查询升级任务列表,支持根据固件ID和任务名称进行筛选。  
35 - *  
36 - * @param pageReqVO 分页查询请求对象,包含分页参数和筛选条件  
37 - * @return 分页结果,包含符合条件的升级任务列表  
38 - */  
39 - default PageResult<IotOtaUpgradeTaskDO> selectUpgradeTaskPage(IotOtaUpgradeTaskPageReqVO pageReqVO) {  
40 - return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaUpgradeTaskDO>()  
41 - .eqIfPresent(IotOtaUpgradeTaskDO::getFirmwareId, pageReqVO.getFirmwareId())  
42 - .likeIfPresent(IotOtaUpgradeTaskDO::getName, pageReqVO.getName()));  
43 - }  
44 -  
45 - /**  
46 - * 根据任务状态查询升级任务列表  
47 - * <p>  
48 - * 该函数通过传入的任务状态,查询数据库中符合条件的升级任务列表。  
49 - *  
50 - * @param status 任务状态,用于筛选升级任务的状态值  
51 - * @return 返回符合条件的升级任务列表,列表中的每个元素为 IotOtaUpgradeTaskDO 对象  
52 - */  
53 - default List<IotOtaUpgradeTaskDO> selectUpgradeTaskByState(Integer status) {  
54 - return selectList(IotOtaUpgradeTaskDO::getStatus, status);  
55 - }  
56 -  
57 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataRuleMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * IoT 数据流转规则 Mapper
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Mapper
  19 +public interface IotDataRuleMapper extends BaseMapperX<IotDataRuleDO> {
  20 +
  21 + default PageResult<IotDataRuleDO> selectPage(IotDataRulePageReqVO reqVO) {
  22 + return selectPage(reqVO, new LambdaQueryWrapperX<IotDataRuleDO>()
  23 + .likeIfPresent(IotDataRuleDO::getName, reqVO.getName())
  24 + .eqIfPresent(IotDataRuleDO::getStatus, reqVO.getStatus())
  25 + .betweenIfPresent(IotDataRuleDO::getCreateTime, reqVO.getCreateTime())
  26 + .orderByDesc(IotDataRuleDO::getId));
  27 + }
  28 +
  29 + default List<IotDataRuleDO> selectListBySinkId(Long sinkId) {
  30 + return selectList(new LambdaQueryWrapperX<IotDataRuleDO>()
  31 + .apply(MyBatisUtils.findInSet("sink_ids", sinkId)));
  32 + }
  33 +
  34 + default List<IotDataRuleDO> selectListByStatus(Integer status) {
  35 + return selectList(IotDataRuleDO::getStatus, status);
  36 + }
  37 +
  38 +}
0 \ No newline at end of file 39 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java
@@ -3,32 +3,30 @@ package cn.iocoder.yudao.module.iot.dal.mysql.rule; @@ -3,32 +3,30 @@ package cn.iocoder.yudao.module.iot.dal.mysql.rule;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO;  
7 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; 6 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
10 import java.util.List; 10 import java.util.List;
11 11
12 /** 12 /**
13 - * IoT 数据桥梁 Mapper 13 + * IoT 数据流转目的 Mapper
14 * 14 *
15 * @author HUIHUI 15 * @author HUIHUI
16 */ 16 */
17 @Mapper 17 @Mapper
18 -public interface IotDataBridgeMapper extends BaseMapperX<IotDataBridgeDO> { 18 +public interface IotDataSinkMapper extends BaseMapperX<IotDataSinkDO> {
19 19
20 - default PageResult<IotDataBridgeDO> selectPage(IotDataBridgePageReqVO reqVO) {  
21 - return selectPage(reqVO, new LambdaQueryWrapperX<IotDataBridgeDO>()  
22 - .likeIfPresent(IotDataBridgeDO::getName, reqVO.getName())  
23 - .eqIfPresent(IotDataBridgeDO::getStatus, reqVO.getStatus())  
24 - .betweenIfPresent(IotDataBridgeDO::getCreateTime, reqVO.getCreateTime())  
25 - .orderByDesc(IotDataBridgeDO::getId)); 20 + default PageResult<IotDataSinkDO> selectPage(IotDataSinkPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<IotDataSinkDO>()
  22 + .likeIfPresent(IotDataSinkDO::getName, reqVO.getName())
  23 + .eqIfPresent(IotDataSinkDO::getStatus, reqVO.getStatus())
  24 + .betweenIfPresent(IotDataSinkDO::getCreateTime, reqVO.getCreateTime())
  25 + .orderByDesc(IotDataSinkDO::getId));
26 } 26 }
27 27
28 - default List<IotDataBridgeDO> selectList(Integer status) {  
29 - return selectList(new LambdaQueryWrapperX<IotDataBridgeDO>()  
30 - .eqIfPresent(IotDataBridgeDO::getStatus, status)  
31 - .orderByDesc(IotDataBridgeDO::getId)); 28 + default List<IotDataSinkDO> selectListByStatus(Integer status) {
  29 + return selectList(IotDataSinkDO::getStatus, status);
32 } 30 }
33 31
34 } 32 }
35 \ No newline at end of file 33 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotRuleSceneMapper.java
@@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePa @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePa
7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
  10 +import java.util.List;
  11 +
10 /** 12 /**
11 * IoT 场景联动 Mapper 13 * IoT 场景联动 Mapper
12 * 14 *
@@ -24,4 +26,8 @@ public interface IotRuleSceneMapper extends BaseMapperX&lt;IotRuleSceneDO&gt; { @@ -24,4 +26,8 @@ public interface IotRuleSceneMapper extends BaseMapperX&lt;IotRuleSceneDO&gt; {
24 .orderByDesc(IotRuleSceneDO::getId)); 26 .orderByDesc(IotRuleSceneDO::getId));
25 } 27 }
26 28
  29 + default List<IotRuleSceneDO> selectListByStatus(Integer status) {
  30 + return selectList(IotRuleSceneDO::getStatus, status);
  31 + }
  32 +
27 } 33 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java
@@ -25,8 +25,6 @@ public interface IotThingModelMapper extends BaseMapperX&lt;IotThingModelDO&gt; { @@ -25,8 +25,6 @@ public interface IotThingModelMapper extends BaseMapperX&lt;IotThingModelDO&gt; {
25 .likeIfPresent(IotThingModelDO::getName, reqVO.getName()) 25 .likeIfPresent(IotThingModelDO::getName, reqVO.getName())
26 .eqIfPresent(IotThingModelDO::getType, reqVO.getType()) 26 .eqIfPresent(IotThingModelDO::getType, reqVO.getType())
27 .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId()) 27 .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId())
28 - // TODO @芋艿:看看要不要加枚举  
29 - .notIn(IotThingModelDO::getIdentifier, "get", "set", "post")  
30 .orderByDesc(IotThingModelDO::getId)); 28 .orderByDesc(IotThingModelDO::getId));
31 } 29 }
32 30
@@ -36,8 +34,6 @@ public interface IotThingModelMapper extends BaseMapperX&lt;IotThingModelDO&gt; { @@ -36,8 +34,6 @@ public interface IotThingModelMapper extends BaseMapperX&lt;IotThingModelDO&gt; {
36 .likeIfPresent(IotThingModelDO::getName, reqVO.getName()) 34 .likeIfPresent(IotThingModelDO::getName, reqVO.getName())
37 .eqIfPresent(IotThingModelDO::getType, reqVO.getType()) 35 .eqIfPresent(IotThingModelDO::getType, reqVO.getType())
38 .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId()) 36 .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId())
39 - // TODO @芋艿:看看要不要加枚举  
40 - .notIn(IotThingModelDO::getIdentifier, "get", "set", "post")  
41 .orderByDesc(IotThingModelDO::getId)); 37 .orderByDesc(IotThingModelDO::getId));
42 } 38 }
43 39
@@ -61,15 +57,6 @@ public interface IotThingModelMapper extends BaseMapperX&lt;IotThingModelDO&gt; { @@ -61,15 +57,6 @@ public interface IotThingModelMapper extends BaseMapperX&lt;IotThingModelDO&gt; {
61 IotThingModelDO::getType, type); 57 IotThingModelDO::getType, type);
62 } 58 }
63 59
64 - default List<IotThingModelDO> selectListByProductIdAndIdentifiersAndTypes(Long productId,  
65 - List<String> identifiers,  
66 - List<Integer> types) {  
67 - return selectList(new LambdaQueryWrapperX<IotThingModelDO>()  
68 - .eq(IotThingModelDO::getProductId, productId)  
69 - .in(IotThingModelDO::getIdentifier, identifiers)  
70 - .in(IotThingModelDO::getType, types));  
71 - }  
72 -  
73 default IotThingModelDO selectByProductIdAndName(Long productId, String name) { 60 default IotThingModelDO selectByProductIdAndName(Long productId, String name) {
74 return selectOne(IotThingModelDO::getProductId, productId, 61 return selectOne(IotThingModelDO::getProductId, productId,
75 IotThingModelDO::getName, name); 62 IotThingModelDO::getName, name);
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java
@@ -60,4 +60,20 @@ public interface RedisKeyConstants { @@ -60,4 +60,20 @@ public interface RedisKeyConstants {
60 */ 60 */
61 String THING_MODEL_LIST = "iot:thing_model_list"; 61 String THING_MODEL_LIST = "iot:thing_model_list";
62 62
  63 + /**
  64 + * 数据流转规则的数据缓存,使用 Spring Cache 操作
  65 + *
  66 + * KEY 格式:data_rule_list_${deviceId}_${method}_${identifier}
  67 + * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO} 列表
  68 + */
  69 + String DATA_RULE_LIST = "iot:data_rule_list";
  70 +
  71 + /**
  72 + * 数据目的的数据缓存,使用 Spring Cache 操作
  73 + *
  74 + * KEY 格式:data_sink_${id}
  75 + * VALUE 数据类型:String(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO}
  76 + */
  77 + String DATA_SINK = "iot:data_sink";
  78 +
63 } 79 }
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java
@@ -14,5 +14,10 @@ public class DictTypeConstants { @@ -14,5 +14,10 @@ public class DictTypeConstants {
14 14
15 public static final String DEVICE_STATE = "iot_device_state"; 15 public static final String DEVICE_STATE = "iot_device_state";
16 16
  17 + public static final String ALERT_LEVEL = "iot_alert_level";
  18 +
  19 + public static final String OTA_TASK_DEVICE_SCOPE = "iot_ota_task_device_scope";
  20 + public static final String OTA_TASK_STATUS = "iot_ota_task_status";
  21 + public static final String OTA_TASK_RECORD_STATUS = "iot_ota_task_record_status";
17 22
18 } 23 }
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
@@ -14,6 +14,7 @@ public interface ErrorCodeConstants { @@ -14,6 +14,7 @@ public interface ErrorCodeConstants {
14 ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); 14 ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在");
15 ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); 15 ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除");
16 ErrorCode PRODUCT_STATUS_NOT_ALLOW_THING_MODEL = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); 16 ErrorCode PRODUCT_STATUS_NOT_ALLOW_THING_MODEL = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型");
  17 + ErrorCode PRODUCT_DELETE_FAIL_HAS_DEVICE = new ErrorCode(1_050_001_004, "产品下存在设备,不允许删除");
17 18
18 // ========== 产品物模型 1-050-002-000 ============ 19 // ========== 产品物模型 1-050-002-000 ============
19 ErrorCode THING_MODEL_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); 20 ErrorCode THING_MODEL_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在");
@@ -31,6 +32,7 @@ public interface ErrorCodeConstants { @@ -31,6 +32,7 @@ public interface ErrorCodeConstants {
31 ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备"); 32 ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备");
32 ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!"); 33 ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!");
33 ErrorCode DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL = new ErrorCode(1_050_003_007, "下行设备消息失败,原因:设备未连接网关"); 34 ErrorCode DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL = new ErrorCode(1_050_003_007, "下行设备消息失败,原因:设备未连接网关");
  35 + ErrorCode DEVICE_SERIAL_NUMBER_EXISTS = new ErrorCode(1_050_003_008, "设备序列号已存在,序列号必须全局唯一");
34 36
35 // ========== 产品分类 1-050-004-000 ========== 37 // ========== 产品分类 1-050-004-000 ==========
36 ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); 38 ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在");
@@ -39,25 +41,40 @@ public interface ErrorCodeConstants { @@ -39,25 +41,40 @@ public interface ErrorCodeConstants {
39 ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在"); 41 ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在");
40 ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除"); 42 ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除");
41 43
42 - // ========== 固件相关 1-050-008-000 ========== 44 + // ========== OTA 固件相关 1-050-008-000 ==========
43 45
44 ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, "固件信息不存在"); 46 ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, "固件信息不存在");
45 ErrorCode OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE = new ErrorCode(1_050_008_001, "产品版本号重复"); 47 ErrorCode OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE = new ErrorCode(1_050_008_001, "产品版本号重复");
46 48
47 - ErrorCode OTA_UPGRADE_TASK_NOT_EXISTS = new ErrorCode(1_050_008_100, "升级任务不存在");  
48 - ErrorCode OTA_UPGRADE_TASK_NAME_DUPLICATE = new ErrorCode(1_050_008_101, "升级任务名称重复");  
49 - ErrorCode OTA_UPGRADE_TASK_DEVICE_IDS_EMPTY = new ErrorCode(1_050_008_102, "设备编号列表不能为空");  
50 - ErrorCode OTA_UPGRADE_TASK_DEVICE_LIST_EMPTY = new ErrorCode(1_050_008_103, "设备列表不能为空");  
51 - ErrorCode OTA_UPGRADE_TASK_CANNOT_CANCEL = new ErrorCode(1_050_008_104, "升级任务不能取消"); 49 + // ========== OTA 升级任务相关 1-050-008-100 ==========
52 50
53 - ErrorCode OTA_UPGRADE_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, "升级记录不存在");  
54 - ErrorCode OTA_UPGRADE_RECORD_DUPLICATE = new ErrorCode(1_050_008_201, "升级记录重复");  
55 - ErrorCode OTA_UPGRADE_RECORD_CANNOT_RETRY = new ErrorCode(1_050_008_202, "升级记录不能重试"); 51 + ErrorCode OTA_TASK_NOT_EXISTS = new ErrorCode(1_050_008_100, "升级任务不存在");
  52 + ErrorCode OTA_TASK_CREATE_FAIL_NAME_DUPLICATE = new ErrorCode(1_050_008_101, "创建 OTA 任务失败,原因:任务名称重复");
  53 + ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_FIRMWARE_EXISTS = new ErrorCode(1_050_008_102,
  54 + "创建 OTA 任务失败,原因:设备({})已经是该固件版本");
  55 + ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_OTA_IN_PROCESS = new ErrorCode(1_050_008_102,
  56 + "创建 OTA 任务失败,原因:设备({})已经在升级中...");
  57 + ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_EMPTY = new ErrorCode(1_050_008_103, "创建 OTA 任务失败,原因:没有可升级的设备");
  58 + ErrorCode OTA_TASK_CANCEL_FAIL_STATUS_END = new ErrorCode(1_050_008_104, "取消 OTA 任务失败,原因:任务状态不是进行中");
56 59
57 - // ========== IoT 数据桥梁 1-050-010-000 ==========  
58 - ErrorCode DATA_BRIDGE_NOT_EXISTS = new ErrorCode(1_050_010_000, "IoT 数据桥梁不存在"); 60 + // ========== OTA 升级任务相关 1-050-008-100 ==========
59 61
60 - // ========== IoT 场景联动 1-050-011-000 ==========  
61 - ErrorCode RULE_SCENE_NOT_EXISTS = new ErrorCode(1_050_011_000, "IoT 场景联动不存在"); 62 + ErrorCode OTA_TASK_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, "升级记录不存在");
  63 +
  64 + // ========== IoT 数据流转规则 1-050-010-000 ==========
  65 + ErrorCode DATA_RULE_NOT_EXISTS = new ErrorCode(1_050_010_000, "数据流转规则不存在");
  66 +
  67 + // ========== IoT 数据流转目的 1-050-011-000 ==========
  68 + ErrorCode DATA_SINK_NOT_EXISTS = new ErrorCode(1_050_011_000, "数据桥梁不存在");
  69 + ErrorCode DATA_SINK_DELETE_FAIL_USED_BY_RULE = new ErrorCode(1_050_011_001, "数据流转目的正在被数据流转规则使用,无法删除");
  70 +
  71 + // ========== IoT 场景联动 1-050-012-000 ==========
  72 + ErrorCode RULE_SCENE_NOT_EXISTS = new ErrorCode(1_050_012_000, "场景联动不存在");
  73 +
  74 + // ========== IoT 告警配置 1-050-013-000 ==========
  75 + ErrorCode ALERT_CONFIG_NOT_EXISTS = new ErrorCode(1_050_013_000, "IoT 告警配置不存在");
  76 +
  77 + // ========== IoT 告警记录 1-050-014-000 ==========
  78 + ErrorCode ALERT_RECORD_NOT_EXISTS = new ErrorCode(1_050_014_000, "IoT 告警记录不存在");
62 79
63 } 80 }
64 \ No newline at end of file 81 \ No newline at end of file
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotAlertConfigReceiveTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/alert/IotAlertReceiveTypeEnum.java
1 -package cn.iocoder.yudao.module.iot.enums.rule; 1 +package cn.iocoder.yudao.module.iot.enums.alert;
2 2
3 import cn.iocoder.yudao.framework.common.core.ArrayValuable; 3 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 import lombok.Getter; 4 import lombok.Getter;
@@ -7,21 +7,22 @@ import lombok.RequiredArgsConstructor; @@ -7,21 +7,22 @@ import lombok.RequiredArgsConstructor;
7 import java.util.Arrays; 7 import java.util.Arrays;
8 8
9 /** 9 /**
10 - * IoT 告警配置的接收方式枚举 10 + * IoT 告警的接收方式枚举
11 * 11 *
12 * @author 芋道源码 12 * @author 芋道源码
13 */ 13 */
14 @RequiredArgsConstructor 14 @RequiredArgsConstructor
15 @Getter 15 @Getter
16 -public enum IotAlertConfigReceiveTypeEnum implements ArrayValuable<Integer> { 16 +public enum IotAlertReceiveTypeEnum implements ArrayValuable<Integer> {
17 17
18 SMS(1), // 短信 18 SMS(1), // 短信
19 MAIL(2), // 邮箱 19 MAIL(2), // 邮箱
20 - NOTIFY(3); // 通知 20 + NOTIFY(3); // 站内信
  21 + // TODO 待实现(欢迎 pull request):webhook 4
21 22
22 private final Integer type; 23 private final Integer type;
23 24
24 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotAlertConfigReceiveTypeEnum::getType).toArray(Integer[]::new); 25 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotAlertReceiveTypeEnum::getType).toArray(Integer[]::new);
25 26
26 @Override 27 @Override
27 public Integer[] array() { 28 public Integer[] array() {
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java
@@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor;
7 /** 7 /**
8 * IoT 设备消息标识符枚举 8 * IoT 设备消息标识符枚举
9 */ 9 */
  10 +@Deprecated
10 @Getter 11 @Getter
11 @RequiredArgsConstructor 12 @RequiredArgsConstructor
12 public enum IotDeviceMessageIdentifierEnum { 13 public enum IotDeviceMessageIdentifierEnum {
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java
@@ -9,6 +9,7 @@ import java.util.Arrays; @@ -9,6 +9,7 @@ import java.util.Arrays;
9 /** 9 /**
10 * IoT 设备消息类型枚举 10 * IoT 设备消息类型枚举
11 */ 11 */
  12 +@Deprecated
12 @Getter 13 @Getter
13 @RequiredArgsConstructor 14 @RequiredArgsConstructor
14 public enum IotDeviceMessageTypeEnum implements ArrayValuable<String> { 15 public enum IotDeviceMessageTypeEnum implements ArrayValuable<String> {
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskScopeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaTaskDeviceScopeEnum.java
@@ -7,18 +7,19 @@ import lombok.RequiredArgsConstructor; @@ -7,18 +7,19 @@ import lombok.RequiredArgsConstructor;
7 import java.util.Arrays; 7 import java.util.Arrays;
8 8
9 /** 9 /**
10 - * IoT OTA 升级任务的范围枚举 10 + * IoT OTA 升级任务的设备范围枚举
11 * 11 *
12 * @author haohao 12 * @author haohao
13 */ 13 */
14 @RequiredArgsConstructor 14 @RequiredArgsConstructor
15 @Getter 15 @Getter
16 -public enum IotOtaUpgradeTaskScopeEnum implements ArrayValuable<Integer> { 16 +public enum IotOtaTaskDeviceScopeEnum implements ArrayValuable<Integer> {
17 17
18 ALL(1), // 全部设备:只包括当前产品下的设备,不包括未来创建的设备 18 ALL(1), // 全部设备:只包括当前产品下的设备,不包括未来创建的设备
19 SELECT(2); // 指定设备 19 SELECT(2); // 指定设备
20 20
21 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotOtaUpgradeTaskScopeEnum::getScope).toArray(Integer[]::new); 21 + public static final Integer[] ARRAYS = Arrays.stream(values())
  22 + .map(IotOtaTaskDeviceScopeEnum::getScope).toArray(Integer[]::new);
22 23
23 /** 24 /**
24 * 范围 25 * 范围
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeRecordStatusEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaTaskRecordStatusEnum.java
@@ -2,31 +2,46 @@ package cn.iocoder.yudao.module.iot.enums.ota; @@ -2,31 +2,46 @@ package cn.iocoder.yudao.module.iot.enums.ota;
2 2
3 3
4 import cn.iocoder.yudao.framework.common.core.ArrayValuable; 4 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  5 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
5 import lombok.Getter; 6 import lombok.Getter;
6 import lombok.RequiredArgsConstructor; 7 import lombok.RequiredArgsConstructor;
7 8
8 import java.util.Arrays; 9 import java.util.Arrays;
  10 +import java.util.List;
  11 +import java.util.Set;
9 12
10 /** 13 /**
11 - * IoT OTA 升级记录的范围枚举 14 + * IoT OTA 升级任务记录的状态枚举
12 * 15 *
13 - * @author haohao 16 + * @author 芋道源码
14 */ 17 */
15 @RequiredArgsConstructor 18 @RequiredArgsConstructor
16 @Getter 19 @Getter
17 -public enum IotOtaUpgradeRecordStatusEnum implements ArrayValuable<Integer> { 20 +public enum IotOtaTaskRecordStatusEnum implements ArrayValuable<Integer> {
18 21
19 PENDING(0), // 待推送 22 PENDING(0), // 待推送
20 PUSHED(10), // 已推送 23 PUSHED(10), // 已推送
21 UPGRADING(20), // 升级中 24 UPGRADING(20), // 升级中
22 SUCCESS(30), // 升级成功 25 SUCCESS(30), // 升级成功
23 FAILURE(40), // 升级失败 26 FAILURE(40), // 升级失败
24 - CANCELED(50),; // 已取消 27 + CANCELED(50),; // 升级取消
25 28
26 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotOtaUpgradeRecordStatusEnum::getStatus).toArray(Integer[]::new); 29 + public static final Integer[] ARRAYS = Arrays.stream(values())
  30 + .map(IotOtaTaskRecordStatusEnum::getStatus).toArray(Integer[]::new);
  31 +
  32 + public static final Set<Integer> IN_PROCESS_STATUSES = SetUtils.asSet(
  33 + PENDING.getStatus(),
  34 + PUSHED.getStatus(),
  35 + UPGRADING.getStatus(),
  36 + SUCCESS.getStatus());
  37 +
  38 + public static final List<Integer> PRIORITY_STATUSES = Arrays.asList(
  39 + SUCCESS.getStatus(),
  40 + PENDING.getStatus(), PUSHED.getStatus(), UPGRADING.getStatus(),
  41 + FAILURE.getStatus(), CANCELED.getStatus());
27 42
28 /** 43 /**
29 - * 范围 44 + * 状态
30 */ 45 */
31 private final Integer status; 46 private final Integer status;
32 47
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskStatusEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaTaskStatusEnum.java
@@ -7,23 +7,23 @@ import lombok.RequiredArgsConstructor; @@ -7,23 +7,23 @@ import lombok.RequiredArgsConstructor;
7 import java.util.Arrays; 7 import java.util.Arrays;
8 8
9 /** 9 /**
10 - * IoT OTA 升级任务的范围枚举 10 + * IoT OTA 升级任务的状态
11 * 11 *
12 - * @author haohao 12 + * @author 芋道源码
13 */ 13 */
14 @RequiredArgsConstructor 14 @RequiredArgsConstructor
15 @Getter 15 @Getter
16 -public enum IotOtaUpgradeTaskStatusEnum implements ArrayValuable<Integer> { 16 +public enum IotOtaTaskStatusEnum implements ArrayValuable<Integer> {
17 17
18 - IN_PROGRESS(10), // 进行中:升级中  
19 - COMPLETED(20), // 已完成:已结束,全部升级完成  
20 - INCOMPLETE(21), // 未完成:已结束,部分升级完成  
21 - CANCELED(30),; // 已取消:一般是主动取消任务 18 + IN_PROGRESS(10), // 进行中(升级中)
  19 + COMPLETED(20), // 已完成(包括全部成功、部分成功)
  20 + CANCELED(30),; // 已取消(一般是主动取消任务)
22 21
23 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotOtaUpgradeTaskStatusEnum::getStatus).toArray(Integer[]::new); 22 + public static final Integer[] ARRAYS = Arrays.stream(values())
  23 + .map(IotOtaTaskStatusEnum::getStatus).toArray(Integer[]::new);
24 24
25 /** 25 /**
26 - * 范围 26 + * 状态
27 */ 27 */
28 private final Integer status; 28 private final Integer status;
29 29
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotNetTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotNetTypeEnum.java
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java
@@ -7,20 +7,32 @@ import lombok.RequiredArgsConstructor; @@ -7,20 +7,32 @@ import lombok.RequiredArgsConstructor;
7 import java.util.Arrays; 7 import java.util.Arrays;
8 8
9 /** 9 /**
10 - * IoT 场景流转的触发类型枚举 10 + * IoT 数据目的的类型枚举
11 * 11 *
12 * @author 芋道源码 12 * @author 芋道源码
13 */ 13 */
14 @RequiredArgsConstructor 14 @RequiredArgsConstructor
15 @Getter 15 @Getter
16 -public enum IotRuleSceneTriggerTypeEnum implements ArrayValuable<Integer> { 16 +public enum IotDataSinkTypeEnum implements ArrayValuable<Integer> {
17 17
18 - DEVICE(1), // 设备触发  
19 - TIMER(2); // 定时触发 18 + HTTP(1, "HTTP"),
  19 + TCP(2, "TCP"), // TODO @puhui999:待实现;
  20 + WEBSOCKET(3, "WebSocket"), // TODO @puhui999:待实现;
  21 +
  22 + MQTT(10, "MQTT"), // TODO 待实现;
  23 +
  24 + DATABASE(20, "Database"), // TODO @puhui999:待实现;可以简单点,对应的表名是什么,字段先固定了。
  25 + REDIS_STREAM(21, "Redis Stream"), // TODO @puhui999:改成 Redis;然后枚举不同的数据结构?这样,枚举就可以是 Redis 了
  26 +
  27 + ROCKETMQ(30, "RocketMQ"),
  28 + RABBITMQ(31, "RabbitMQ"),
  29 + KAFKA(32, "Kafka");
20 30
21 private final Integer type; 31 private final Integer type;
22 32
23 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneTriggerTypeEnum::getType).toArray(Integer[]::new); 33 + private final String name;
  34 +
  35 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataSinkTypeEnum::getType).toArray(Integer[]::new);
24 36
25 @Override 37 @Override
26 public Integer[] array() { 38 public Integer[] array() {
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java
1 package cn.iocoder.yudao.module.iot.enums.rule; 1 package cn.iocoder.yudao.module.iot.enums.rule;
2 2
3 import cn.iocoder.yudao.framework.common.core.ArrayValuable; 3 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
4 import lombok.Getter; 5 import lombok.Getter;
5 import lombok.RequiredArgsConstructor; 6 import lombok.RequiredArgsConstructor;
6 7
@@ -15,9 +16,29 @@ import java.util.Arrays; @@ -15,9 +16,29 @@ import java.util.Arrays;
15 @Getter 16 @Getter
16 public enum IotRuleSceneActionTypeEnum implements ArrayValuable<Integer> { 17 public enum IotRuleSceneActionTypeEnum implements ArrayValuable<Integer> {
17 18
18 - DEVICE_CONTROL(1), // 设备执行  
19 - ALERT(2), // 告警执行  
20 - DATA_BRIDGE(3); // 桥接执行 19 + /**
  20 + * 设备属性设置
  21 + *
  22 + * 对应 {@link IotDeviceMessageMethodEnum#PROPERTY_SET}
  23 + */
  24 + DEVICE_PROPERTY_SET(1),
  25 + /**
  26 + * 设备服务调用
  27 + *
  28 + * 对应 {@link IotDeviceMessageMethodEnum#SERVICE_INVOKE}
  29 + */
  30 + DEVICE_SERVICE_INVOKE(2),
  31 +
  32 + /**
  33 + * 告警触发
  34 + */
  35 + ALERT_TRIGGER(100),
  36 + /**
  37 + * 告警恢复
  38 + */
  39 + ALERT_RECOVER(101),
  40 +
  41 + ;
21 42
22 private final Integer type; 43 private final Integer type;
23 44
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneConditionOperatorEnum.java
@@ -8,13 +8,13 @@ import lombok.RequiredArgsConstructor; @@ -8,13 +8,13 @@ import lombok.RequiredArgsConstructor;
8 import java.util.Arrays; 8 import java.util.Arrays;
9 9
10 /** 10 /**
11 - * IoT 场景触发条件参数的操作符枚举 11 + * IoT 场景触发条件的操作符枚举
12 * 12 *
13 * @author 芋道源码 13 * @author 芋道源码
14 */ 14 */
15 @RequiredArgsConstructor 15 @RequiredArgsConstructor
16 @Getter 16 @Getter
17 -public enum IotRuleSceneTriggerConditionParameterOperatorEnum implements ArrayValuable<String> { 17 +public enum IotRuleSceneConditionOperatorEnum implements ArrayValuable<String> {
18 18
19 EQUALS("=", "#source == #value"), 19 EQUALS("=", "#source == #value"),
20 NOT_EQUALS("!=", "!(#source == #value)"), 20 NOT_EQUALS("!=", "!(#source == #value)"),
@@ -32,12 +32,28 @@ public enum IotRuleSceneTriggerConditionParameterOperatorEnum implements ArrayVa @@ -32,12 +32,28 @@ public enum IotRuleSceneTriggerConditionParameterOperatorEnum implements ArrayVa
32 NOT_BETWEEN("not between", "(#source < #values.get(0)) || (#source > #values.get(1))"), 32 NOT_BETWEEN("not between", "(#source < #values.get(0)) || (#source > #values.get(1))"),
33 33
34 LIKE("like", "#source.contains(#value)"), // 字符串匹配 34 LIKE("like", "#source.contains(#value)"), // 字符串匹配
35 - NOT_NULL("not null", "#source != null && #source.length() > 0"); // 非空 35 + NOT_NULL("not null", "#source != null && #source.length() > 0"), // 非空
  36 +
  37 + // ========== 特殊:不放在字典里 ==========
  38 +
  39 + // TODO @puhui999:@芋艿:需要测试下
  40 + DATE_TIME_GREATER_THAN("date_time_>", "#source > #value"), // 在时间之后:时间戳
  41 + DATE_TIME_LESS_THAN("date_time_<", "#source < #value"), // 在时间之前:时间戳
  42 + DATE_TIME_BETWEEN("date_time_between", // 在时间之间:时间戳
  43 + "(#source >= #values.get(0)) && (#source <= #values.get(1))"),
  44 +
  45 + // TODO @puhui999:@芋艿:需要测试下
  46 + TIME_GREATER_THAN("time_>", "#source.isAfter(#value)"), // 在当日时间之后:HH:mm:ss
  47 + TIME_LESS_THAN("time_<", "#source.isBefore(#value)"), // 在当日时间之前:HH:mm:ss
  48 + TIME_BETWEEN("time_between", // 在当日时间之间:HH:mm:ss
  49 + "(#source >= #values.get(0)) && (#source <= #values.get(1))"),
  50 +
  51 + ;
36 52
37 private final String operator; 53 private final String operator;
38 private final String springExpression; 54 private final String springExpression;
39 55
40 - public static final String[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneTriggerConditionParameterOperatorEnum::getOperator).toArray(String[]::new); 56 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneConditionOperatorEnum::getOperator).toArray(String[]::new);
41 57
42 /** 58 /**
43 * Spring 表达式 - 原始值 59 * Spring 表达式 - 原始值
@@ -52,7 +68,7 @@ public enum IotRuleSceneTriggerConditionParameterOperatorEnum implements ArrayVa @@ -52,7 +68,7 @@ public enum IotRuleSceneTriggerConditionParameterOperatorEnum implements ArrayVa
52 */ 68 */
53 public static final String SPRING_EXPRESSION_VALUE_LIST = "values"; 69 public static final String SPRING_EXPRESSION_VALUE_LIST = "values";
54 70
55 - public static IotRuleSceneTriggerConditionParameterOperatorEnum operatorOf(String operator) { 71 + public static IotRuleSceneConditionOperatorEnum operatorOf(String operator) {
56 return ArrayUtil.firstMatch(item -> item.getOperator().equals(operator), values()); 72 return ArrayUtil.firstMatch(item -> item.getOperator().equals(operator), values());
57 } 73 }
58 74
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneConditionTypeEnum.java
@@ -7,20 +7,25 @@ import lombok.RequiredArgsConstructor; @@ -7,20 +7,25 @@ import lombok.RequiredArgsConstructor;
7 import java.util.Arrays; 7 import java.util.Arrays;
8 8
9 /** 9 /**
10 - * IoT 数据桥接的方向枚举 10 + * IoT 条件类型枚举
11 * 11 *
12 * @author 芋道源码 12 * @author 芋道源码
13 */ 13 */
14 @RequiredArgsConstructor 14 @RequiredArgsConstructor
15 @Getter 15 @Getter
16 -public enum IotDataBridgeDirectionEnum implements ArrayValuable<Integer> { 16 +public enum IotRuleSceneConditionTypeEnum implements ArrayValuable<Integer> {
17 17
18 - INPUT(1), // 输入  
19 - OUTPUT(2); // 输出 18 + DEVICE_STATE(1, "设备状态"),
  19 + DEVICE_PROPERTY(2, "设备属性"),
  20 +
  21 + CURRENT_TIME(100, "当前时间"),
  22 +
  23 + ;
20 24
21 private final Integer type; 25 private final Integer type;
  26 + private final String name;
22 27
23 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgeDirectionEnum::getType).toArray(Integer[]::new); 28 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneConditionTypeEnum::getType).toArray(Integer[]::new);
24 29
25 @Override 30 @Override
26 public Integer[] array() { 31 public Integer[] array() {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import lombok.Getter;
  5 +import lombok.RequiredArgsConstructor;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * IoT 场景流转的触发类型枚举
  11 + *
  12 + * 为什么不直接使用 IotDeviceMessageMethodEnum 呢?
  13 + * 原因是,物模型属性上报,存在批量上报的情况,不只对应一个 method!!!
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@RequiredArgsConstructor
  18 +@Getter
  19 +public enum IotRuleSceneTriggerTypeEnum implements ArrayValuable<Integer> {
  20 +
  21 + @Deprecated
  22 + DEVICE(1), // 设备触发 // TODO @puhui999:@芋艿:这个可以作废
  23 +
  24 + // TODO @芋艿:后续“对应”部分,要 @下,等包结构梳理完;
  25 + /**
  26 + * 设备上下线变更
  27 + *
  28 + * 对应 IotDeviceMessageMethodEnum.STATE_UPDATE
  29 + */
  30 + DEVICE_STATE_UPDATE(1),
  31 + /**
  32 + * 物模型属性上报
  33 + *
  34 + * 对应 IotDeviceMessageMethodEnum.DEVICE_PROPERTY_POST
  35 + */
  36 + DEVICE_PROPERTY_POST(2),
  37 + /**
  38 + * 设备事件上报
  39 + *
  40 + * 对应 IotDeviceMessageMethodEnum.DEVICE_EVENT_POST
  41 + */
  42 + DEVICE_EVENT_POST(3),
  43 + /**
  44 + * 设备服务调用
  45 + *
  46 + * 对应 IotDeviceMessageMethodEnum.DEVICE_SERVICE_INVOKE
  47 + */
  48 + DEVICE_SERVICE_INVOKE(4),
  49 +
  50 + TIMER(100) // 定时触发
  51 +
  52 + ;
  53 +
  54 + private final Integer type;
  55 +
  56 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneTriggerTypeEnum::getType).toArray(Integer[]::new);
  57 +
  58 + @Override
  59 + public Integer[] array() {
  60 + return ARRAYS;
  61 + }
  62 +
  63 +}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/iot/config/YudaoIotProperties.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.iot.config;
  2 +
  3 +import lombok.Data;
  4 +import org.springframework.stereotype.Component;
  5 +
  6 +import java.time.Duration;
  7 +
  8 +/**
  9 + * 芋道 IoT 全局配置类
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Component
  14 +@Data
  15 +public class YudaoIotProperties {
  16 +
  17 + /**
  18 + * 设备连接超时时间
  19 + */
  20 + private Duration keepAliveTime = Duration.ofMinutes(10);
  21 + /**
  22 + * 设备连接超时时间的因子
  23 + *
  24 + * 因为设备可能会有网络抖动,所以需要乘以一个因子,避免误判
  25 + */
  26 + private double keepAliveFactor = 1.5D;
  27 +
  28 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/iot/package-info.java 0 → 100644
  1 +/**
  2 + * iot 模块的【全局】拓展封装
  3 + */
  4 +package cn.iocoder.yudao.module.iot.framework.iot;
0 \ No newline at end of file 5 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java
@@ -23,9 +23,15 @@ public class TDengineTableField { @@ -23,9 +23,15 @@ public class TDengineTableField {
23 public static final String TYPE_DOUBLE = "DOUBLE"; 23 public static final String TYPE_DOUBLE = "DOUBLE";
24 public static final String TYPE_BOOL = "BOOL"; 24 public static final String TYPE_BOOL = "BOOL";
25 public static final String TYPE_NCHAR = "NCHAR"; 25 public static final String TYPE_NCHAR = "NCHAR";
  26 + public static final String TYPE_VARCHAR = "VARCHAR";
26 public static final String TYPE_TIMESTAMP = "TIMESTAMP"; 27 public static final String TYPE_TIMESTAMP = "TIMESTAMP";
27 28
28 /** 29 /**
  30 + * 字段长度 - VARCHAR 默认长度
  31 + */
  32 + public static final int LENGTH_VARCHAR = 1024;
  33 +
  34 + /**
29 * 注释 - TAG 字段 35 * 注释 - TAG 字段
30 */ 36 */
31 public static final String NOTE_TAG = "TAG"; 37 public static final String NOTE_TAG = "TAG";
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
7 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; 7 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
9 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 9 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  10 +import cn.iocoder.yudao.module.iot.framework.iot.config.YudaoIotProperties;
10 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 11 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
11 import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; 12 import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
12 import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; 13 import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
@@ -24,17 +25,14 @@ import java.util.Set; @@ -24,17 +25,14 @@ import java.util.Set;
24 * 25 *
25 * 检测逻辑:设备最后一条 {@link IotDeviceMessage} 消息超过一定时间,则认为设备离线 26 * 检测逻辑:设备最后一条 {@link IotDeviceMessage} 消息超过一定时间,则认为设备离线
26 * 27 *
  28 + * @see <a href="https://help.aliyun.com/zh/iot/support/faq-about-device-status#98f7056b2957y">阿里云 IoT —— 设备离线分析</a>
27 * @author 芋道源码 29 * @author 芋道源码
28 */ 30 */
29 @Component 31 @Component
30 public class IotDeviceOfflineCheckJob implements JobHandler { 32 public class IotDeviceOfflineCheckJob implements JobHandler {
31 33
32 - /**  
33 - * 设备离线超时时间  
34 - *  
35 - * TODO 芋艿:暂定 10 分钟,后续看看要不要基于设备或者全局有配置文件  
36 - */  
37 - public static final Duration OFFLINE_TIMEOUT = Duration.ofMinutes(10); 34 + @Resource
  35 + private YudaoIotProperties iotProperties;
38 36
39 @Resource 37 @Resource
40 private IotDeviceService deviceService; 38 private IotDeviceService deviceService;
@@ -52,8 +50,7 @@ public class IotDeviceOfflineCheckJob implements JobHandler { @@ -52,8 +50,7 @@ public class IotDeviceOfflineCheckJob implements JobHandler {
52 return JsonUtils.toJsonString(Collections.emptyList()); 50 return JsonUtils.toJsonString(Collections.emptyList());
53 } 51 }
54 // 1.2 获取超时的设备集合 52 // 1.2 获取超时的设备集合
55 - Set<Long> timeoutDeviceIds = devicePropertyService.getDeviceIdListByReportTime(  
56 - LocalDateTime.now().minus(OFFLINE_TIMEOUT)); 53 + Set<Long> timeoutDeviceIds = devicePropertyService.getDeviceIdListByReportTime(getTimeoutTime());
57 54
58 // 2. 下线设备 55 // 2. 下线设备
59 List<String[]> offlineDevices = CollUtil.newArrayList(); 56 List<String[]> offlineDevices = CollUtil.newArrayList();
@@ -68,4 +65,9 @@ public class IotDeviceOfflineCheckJob implements JobHandler { @@ -68,4 +65,9 @@ public class IotDeviceOfflineCheckJob implements JobHandler {
68 return JsonUtils.toJsonString(offlineDevices); 65 return JsonUtils.toJsonString(offlineDevices);
69 } 66 }
70 67
  68 + private LocalDateTime getTimeoutTime() {
  69 + return LocalDateTime.now().minus(Duration.ofNanos(
  70 + (long) (iotProperties.getKeepAliveTime().toNanos() * iotProperties.getKeepAliveFactor())));
  71 + }
  72 +
71 } 73 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java
@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.job.rule; @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.job.rule;
2 2
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
4 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; 4 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
5 -import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; 5 +import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService;
6 import jakarta.annotation.Resource; 6 import jakarta.annotation.Resource;
7 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
8 import org.quartz.JobExecutionContext; 8 import org.quartz.JobExecutionContext;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java
1 package cn.iocoder.yudao.module.iot.mq.consumer.device; 1 package cn.iocoder.yudao.module.iot.mq.consumer.device;
2 2
3 import cn.hutool.core.util.ObjectUtil; 3 import cn.hutool.core.util.ObjectUtil;
4 -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;  
5 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 4 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
6 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; 5 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
7 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; 6 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
@@ -19,6 +18,7 @@ import lombok.extern.slf4j.Slf4j; @@ -19,6 +18,7 @@ import lombok.extern.slf4j.Slf4j;
19 import org.springframework.stereotype.Component; 18 import org.springframework.stereotype.Component;
20 19
21 import java.time.LocalDateTime; 20 import java.time.LocalDateTime;
  21 +import java.util.Objects;
22 22
23 /** 23 /**
24 * 针对 {@link IotDeviceMessage} 的业务处理器:调用 method 对应的逻辑。例如说: 24 * 针对 {@link IotDeviceMessage} 的业务处理器:调用 method 对应的逻辑。例如说:
@@ -83,15 +83,14 @@ public class IotDeviceMessageSubscriber implements IotMessageSubscriber&lt;IotDevic @@ -83,15 +83,14 @@ public class IotDeviceMessageSubscriber implements IotMessageSubscriber&lt;IotDevic
83 return; 83 return;
84 } 84 }
85 // 如果是 STATE 相关的消息,无需处理,不然就重复处理状态了 85 // 如果是 STATE 相关的消息,无需处理,不然就重复处理状态了
86 - if (ObjectUtils.equalsAny(message.getMethod(), IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(),  
87 - IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod())) { 86 + if (Objects.equals(message.getMethod(), IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod())) {
88 return; 87 return;
89 } 88 }
90 89
91 // 特殊:设备非在线时,主动标记设备为在线 90 // 特殊:设备非在线时,主动标记设备为在线
92 // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志、规则引擎等等 91 // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志、规则引擎等等
93 try { 92 try {
94 - deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateOnline().setDeviceId(device.getId())); 93 + deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateUpdateOnline().setDeviceId(device.getId()));
95 } catch (Exception e) { 94 } catch (Exception e) {
96 // 注意:即使执行失败,也不影响主流程 95 // 注意:即使执行失败,也不影响主流程
97 log.error("[forceDeviceOnline][message({}) device({}) 强制设备上线失败]", message, device, e); 96 log.error("[forceDeviceOnline][message({}) device({}) 强制设备上线失败]", message, device, e);
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotDataRuleMessageHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.mq.consumer.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  4 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  5 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
  6 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.service.rule.data.IotDataRuleService;
  8 +import jakarta.annotation.PostConstruct;
  9 +import jakarta.annotation.Resource;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +import org.springframework.stereotype.Component;
  12 +
  13 +/**
  14 + * 针对 {@link IotDeviceMessage} 的消费者,处理数据流转
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Component
  19 +@Slf4j
  20 +public class IotDataRuleMessageHandler implements IotMessageSubscriber<IotDeviceMessage> {
  21 +
  22 + @Resource
  23 + private IotDataRuleService dataRuleService;
  24 +
  25 + @Resource
  26 + private IotMessageBus messageBus;
  27 +
  28 + @PostConstruct
  29 + public void init() {
  30 + messageBus.register(this);
  31 + }
  32 +
  33 + @Override
  34 + public String getTopic() {
  35 + return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC;
  36 + }
  37 +
  38 + @Override
  39 + public String getGroup() {
  40 + return "iot_data_rule_consumer";
  41 + }
  42 +
  43 + @Override
  44 + public void onMessage(IotDeviceMessage message) {
  45 + TenantUtils.execute(message.getTenantId(), () -> dataRuleService.executeDataRule(message));
  46 + }
  47 +
  48 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.rule; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.rule;
3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; 4 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; 6 +import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService;
7 import jakarta.annotation.PostConstruct; 7 import jakarta.annotation.PostConstruct;
8 import jakarta.annotation.Resource; 8 import jakarta.annotation.Resource;
9 import lombok.extern.slf4j.Slf4j; 9 import lombok.extern.slf4j.Slf4j;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.alert;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * IoT 告警配置 Service 接口
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public interface IotAlertConfigService {
  17 +
  18 + /**
  19 + * 创建告警配置
  20 + *
  21 + * @param createReqVO 创建信息
  22 + * @return 编号
  23 + */
  24 + Long createAlertConfig(@Valid IotAlertConfigSaveReqVO createReqVO);
  25 +
  26 + /**
  27 + * 更新告警配置
  28 + *
  29 + * @param updateReqVO 更新信息
  30 + */
  31 + void updateAlertConfig(@Valid IotAlertConfigSaveReqVO updateReqVO);
  32 +
  33 + /**
  34 + * 删除告警配置
  35 + *
  36 + * @param id 编号
  37 + */
  38 + void deleteAlertConfig(Long id);
  39 +
  40 + /**
  41 + * 获得告警配置
  42 + *
  43 + * @param id 编号
  44 + * @return 告警配置
  45 + */
  46 + IotAlertConfigDO getAlertConfig(Long id);
  47 +
  48 + /**
  49 + * 获得告警配置分页
  50 + *
  51 + * @param pageReqVO 分页查询
  52 + * @return 告警配置分页
  53 + */
  54 + PageResult<IotAlertConfigDO> getAlertConfigPage(IotAlertConfigPageReqVO pageReqVO);
  55 +
  56 + /**
  57 + * 获得告警配置列表
  58 + *
  59 + * @param status 状态
  60 + * @return 告警配置列表
  61 + */
  62 + List<IotAlertConfigDO> getAlertConfigListByStatus(Integer status);
  63 +
  64 + /**
  65 + * 获得告警配置列表
  66 + *
  67 + * @param sceneRuleId 场景流动规则编号
  68 + * @return 告警配置列表
  69 + */
  70 + List<IotAlertConfigDO> getAlertConfigListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status);
  71 +
  72 +}
0 \ No newline at end of file 73 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.alert;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
  8 +import cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertConfigMapper;
  9 +import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService;
  10 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  11 +import jakarta.annotation.Resource;
  12 +import org.springframework.context.annotation.Lazy;
  13 +import org.springframework.stereotype.Service;
  14 +import org.springframework.validation.annotation.Validated;
  15 +
  16 +import java.util.List;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  19 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.ALERT_CONFIG_NOT_EXISTS;
  20 +
  21 +/**
  22 + * IoT 告警配置 Service 实现类
  23 + *
  24 + * @author 芋道源码
  25 + */
  26 +@Service
  27 +@Validated
  28 +public class IotAlertConfigServiceImpl implements IotAlertConfigService {
  29 +
  30 + @Resource
  31 + private IotAlertConfigMapper alertConfigMapper;
  32 +
  33 + @Resource
  34 + @Lazy // 延迟,避免循环依赖报错
  35 + private IotRuleSceneService ruleSceneService;
  36 +
  37 + @Resource
  38 + private AdminUserApi adminUserApi;
  39 +
  40 + @Override
  41 + public Long createAlertConfig(IotAlertConfigSaveReqVO createReqVO) {
  42 + // 校验关联数据是否存在
  43 + ruleSceneService.validateRuleSceneList(createReqVO.getSceneRuleIds());
  44 + adminUserApi.validateUserList(createReqVO.getReceiveUserIds());
  45 +
  46 + // 插入
  47 + IotAlertConfigDO alertConfig = BeanUtils.toBean(createReqVO, IotAlertConfigDO.class);
  48 + alertConfigMapper.insert(alertConfig);
  49 + return alertConfig.getId();
  50 + }
  51 +
  52 + @Override
  53 + public void updateAlertConfig(IotAlertConfigSaveReqVO updateReqVO) {
  54 + // 校验存在
  55 + validateAlertConfigExists(updateReqVO.getId());
  56 + // 校验关联数据是否存在
  57 + ruleSceneService.validateRuleSceneList(updateReqVO.getSceneRuleIds());
  58 + adminUserApi.validateUserList(updateReqVO.getReceiveUserIds());
  59 +
  60 + // 更新
  61 + IotAlertConfigDO updateObj = BeanUtils.toBean(updateReqVO, IotAlertConfigDO.class);
  62 + alertConfigMapper.updateById(updateObj);
  63 + }
  64 +
  65 + @Override
  66 + public void deleteAlertConfig(Long id) {
  67 + // 校验存在
  68 + validateAlertConfigExists(id);
  69 + // 删除
  70 + alertConfigMapper.deleteById(id);
  71 + }
  72 +
  73 + private void validateAlertConfigExists(Long id) {
  74 + if (alertConfigMapper.selectById(id) == null) {
  75 + throw exception(ALERT_CONFIG_NOT_EXISTS);
  76 + }
  77 + }
  78 +
  79 + @Override
  80 + public IotAlertConfigDO getAlertConfig(Long id) {
  81 + return alertConfigMapper.selectById(id);
  82 + }
  83 +
  84 + @Override
  85 + public PageResult<IotAlertConfigDO> getAlertConfigPage(IotAlertConfigPageReqVO pageReqVO) {
  86 + return alertConfigMapper.selectPage(pageReqVO);
  87 + }
  88 +
  89 + @Override
  90 + public List<IotAlertConfigDO> getAlertConfigListByStatus(Integer status) {
  91 + return alertConfigMapper.selectListByStatus(status);
  92 + }
  93 +
  94 + @Override
  95 + public List<IotAlertConfigDO> getAlertConfigListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) {
  96 + return alertConfigMapper.selectListBySceneRuleIdAndStatus(sceneRuleId, status);
  97 + }
  98 +
  99 +}
0 \ No newline at end of file 100 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.alert;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;
  5 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
  8 +import jakarta.validation.constraints.NotNull;
  9 +
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * IoT 告警记录 Service 接口
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +public interface IotAlertRecordService {
  19 +
  20 + /**
  21 + * 获得告警记录
  22 + *
  23 + * @param id 编号
  24 + * @return 告警记录
  25 + */
  26 + IotAlertRecordDO getAlertRecord(Long id);
  27 +
  28 + /**
  29 + * 获得告警记录分页
  30 + *
  31 + * @param pageReqVO 分页查询
  32 + * @return 告警记录分页
  33 + */
  34 + PageResult<IotAlertRecordDO> getAlertRecordPage(IotAlertRecordPageReqVO pageReqVO);
  35 +
  36 + /**
  37 + * 获得指定场景规则的告警记录列表
  38 + *
  39 + * @param sceneRuleId 场景规则编号
  40 + * @param deviceId 设备编号
  41 + * @param processStatus 处理状态,允许空
  42 + * @return 告警记录列表
  43 + */
  44 + List<IotAlertRecordDO> getAlertRecordListBySceneRuleId(@NotNull(message = "场景规则编号不能为空") Long sceneRuleId,
  45 + Long deviceId, Boolean processStatus);
  46 +
  47 + /**
  48 + * 处理告警记录
  49 + *
  50 + * @param ids 告警记录编号
  51 + * @param remark 处理结果(备注)
  52 + */
  53 + void processAlertRecordList(Collection<Long> ids, String remark);
  54 +
  55 + /**
  56 + * 创建告警记录(包含场景规则编号)
  57 + *
  58 + * @param config 告警配置
  59 + * @param sceneRuleId 场景规则编号
  60 + * @param deviceMessage 设备消息,可为空
  61 + * @return 告警记录编号
  62 + */
  63 + Long createAlertRecord(IotAlertConfigDO config, Long sceneRuleId, IotDeviceMessage deviceMessage);
  64 +
  65 +}
0 \ No newline at end of file 66 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.alert;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;
  6 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  10 +import cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertRecordMapper;
  11 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  12 +import jakarta.annotation.Resource;
  13 +import org.springframework.stereotype.Service;
  14 +import org.springframework.validation.annotation.Validated;
  15 +
  16 +import java.util.Collection;
  17 +import java.util.List;
  18 +
  19 +/**
  20 + * IoT 告警记录 Service 实现类
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@Service
  25 +@Validated
  26 +public class IotAlertRecordServiceImpl implements IotAlertRecordService {
  27 +
  28 + @Resource
  29 + private IotAlertRecordMapper alertRecordMapper;
  30 +
  31 + @Resource
  32 + private IotDeviceService deviceService;
  33 +
  34 + @Override
  35 + public IotAlertRecordDO getAlertRecord(Long id) {
  36 + return alertRecordMapper.selectById(id);
  37 + }
  38 +
  39 + @Override
  40 + public PageResult<IotAlertRecordDO> getAlertRecordPage(IotAlertRecordPageReqVO pageReqVO) {
  41 + return alertRecordMapper.selectPage(pageReqVO);
  42 + }
  43 +
  44 + @Override
  45 + public List<IotAlertRecordDO> getAlertRecordListBySceneRuleId(Long sceneRuleId, Long deviceId, Boolean processStatus) {
  46 + return alertRecordMapper.selectListBySceneRuleId(sceneRuleId, deviceId, processStatus);
  47 + }
  48 +
  49 + @Override
  50 + public void processAlertRecordList(Collection<Long> ids, String processRemark) {
  51 + if (CollUtil.isEmpty(ids)) {
  52 + return;
  53 + }
  54 + // 批量更新告警记录的处理状态
  55 + alertRecordMapper.updateList(ids, IotAlertRecordDO.builder()
  56 + .processStatus(true).processRemark(processRemark).build());
  57 + }
  58 +
  59 + @Override
  60 + public Long createAlertRecord(IotAlertConfigDO config, Long sceneRuleId, IotDeviceMessage message) {
  61 + // 构建告警记录
  62 + IotAlertRecordDO.IotAlertRecordDOBuilder builder = IotAlertRecordDO.builder()
  63 + .configId(config.getId()).configName(config.getName()).configLevel(config.getLevel())
  64 + .sceneRuleId(sceneRuleId).processStatus(false);
  65 + if (message != null) {
  66 + builder.deviceMessage(message);
  67 + // 填充设备信息
  68 + IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId());
  69 + if (device != null) {
  70 + builder.productId(device.getProductId()).deviceId(device.getId());
  71 + }
  72 + }
  73 +
  74 + // 插入记录
  75 + IotAlertRecordDO record = builder.build();
  76 + alertRecordMapper.insert(record);
  77 + return record.getId();
  78 + }
  79 +
  80 +}
0 \ No newline at end of file 81 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
@@ -3,10 +3,9 @@ package cn.iocoder.yudao.module.iot.service.device; @@ -3,10 +3,9 @@ package cn.iocoder.yudao.module.iot.service.device;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
6 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;  
7 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; 6 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
8 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
9 -import jakarta.validation.constraints.NotEmpty;  
10 9
11 import javax.annotation.Nullable; 10 import javax.annotation.Nullable;
12 import java.time.LocalDateTime; 11 import java.time.LocalDateTime;
@@ -14,6 +13,8 @@ import java.util.Collection; @@ -14,6 +13,8 @@ import java.util.Collection;
14 import java.util.List; 13 import java.util.List;
15 import java.util.Map; 14 import java.util.Map;
16 15
  16 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  17 +
17 /** 18 /**
18 * IoT 设备 Service 接口 19 * IoT 设备 Service 接口
19 * 20 *
@@ -30,18 +31,6 @@ public interface IotDeviceService { @@ -30,18 +31,6 @@ public interface IotDeviceService {
30 Long createDevice(@Valid IotDeviceSaveReqVO createReqVO); 31 Long createDevice(@Valid IotDeviceSaveReqVO createReqVO);
31 32
32 /** 33 /**
33 - * 【设备注册】创建设备  
34 - *  
35 - * @param productKey 产品标识  
36 - * @param deviceName 设备名称  
37 - * @param gatewayId 网关设备 ID  
38 - * @return 设备  
39 - */  
40 - IotDeviceDO createDevice(@NotEmpty(message = "产品标识不能为空") String productKey,  
41 - @NotEmpty(message = "设备名称不能为空") String deviceName,  
42 - Long gatewayId);  
43 -  
44 - /**  
45 * 更新设备 34 * 更新设备
46 * 35 *
47 * @param updateReqVO 更新信息 36 * @param updateReqVO 更新信息
@@ -151,12 +140,14 @@ public interface IotDeviceService { @@ -151,12 +140,14 @@ public interface IotDeviceService {
151 PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO); 140 PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO);
152 141
153 /** 142 /**
154 - * 基于设备类型,获得设备列表 143 + * 根据条件,获得设备列表
155 * 144 *
156 * @param deviceType 设备类型 145 * @param deviceType 设备类型
  146 + * @param productId 产品编号
157 * @return 设备列表 147 * @return 设备列表
158 */ 148 */
159 - List<IotDeviceDO> getDeviceListByDeviceType(@Nullable Integer deviceType); 149 + List<IotDeviceDO> getDeviceListByCondition(@Nullable Integer deviceType,
  150 + @Nullable Long productId);
160 151
161 /** 152 /**
162 * 获得状态,获得设备列表 153 * 获得状态,获得设备列表
@@ -167,22 +158,14 @@ public interface IotDeviceService { @@ -167,22 +158,14 @@ public interface IotDeviceService {
167 List<IotDeviceDO> getDeviceListByState(Integer state); 158 List<IotDeviceDO> getDeviceListByState(Integer state);
168 159
169 /** 160 /**
170 - * 根据产品ID获取设备列表 161 + * 根据产品编号,获取设备列表
171 * 162 *
172 - * @param productId 产品ID,用于查询特定产品的设备列表  
173 - * @return 返回与指定产品ID关联的设备列表,列表中的每个元素为IotDeviceDO对象 163 + * @param productId 产品编号
  164 + * @return 设备列表
174 */ 165 */
175 List<IotDeviceDO> getDeviceListByProductId(Long productId); 166 List<IotDeviceDO> getDeviceListByProductId(Long productId);
176 167
177 /** 168 /**
178 - * 根据设备ID列表获取设备信息列表  
179 - *  
180 - * @param deviceIdList 设备ID列表,包含需要查询的设备ID  
181 - * @return 返回与设备ID列表对应的设备信息列表,列表中的每个元素为IotDeviceDO对象  
182 - */  
183 - List<IotDeviceDO> getDeviceListByIdList(List<Long> deviceIdList);  
184 -  
185 - /**  
186 * 基于产品编号,获得设备数量 169 * 基于产品编号,获得设备数量
187 * 170 *
188 * @param productId 产品编号 171 * @param productId 产品编号
@@ -255,4 +238,29 @@ public interface IotDeviceService { @@ -255,4 +238,29 @@ public interface IotDeviceService {
255 */ 238 */
256 boolean authDevice(@Valid IotDeviceAuthReqDTO authReqDTO); 239 boolean authDevice(@Valid IotDeviceAuthReqDTO authReqDTO);
257 240
  241 + /**
  242 + * 校验设备是否存在
  243 + *
  244 + * @param ids 设备编号数组
  245 + */
  246 + List<IotDeviceDO> validateDeviceListExists(Collection<Long> ids);
  247 +
  248 + /**
  249 + * 获得设备列表
  250 + *
  251 + * @param ids 设备编号数组
  252 + * @return 设备列表
  253 + */
  254 + List<IotDeviceDO> getDeviceList(Collection<Long> ids);
  255 +
  256 + /**
  257 + * 获得设备 Map
  258 + *
  259 + * @param ids 设备编号数组
  260 + * @return 设备 Map
  261 + */
  262 + default Map<Long, IotDeviceDO> getDeviceMap(Collection<Long> ids) {
  263 + return convertMap(getDeviceList(ids), IotDeviceDO::getId);
  264 + }
  265 +
258 } 266 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
@@ -36,7 +36,6 @@ import org.springframework.validation.annotation.Validated; @@ -36,7 +36,6 @@ import org.springframework.validation.annotation.Validated;
36 import javax.annotation.Nullable; 36 import javax.annotation.Nullable;
37 import java.time.LocalDateTime; 37 import java.time.LocalDateTime;
38 import java.util.*; 38 import java.util.*;
39 -import java.util.stream.Collectors;  
40 39
41 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 40 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
42 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -56,6 +55,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -56,6 +55,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
56 private IotDeviceMapper deviceMapper; 55 private IotDeviceMapper deviceMapper;
57 56
58 @Resource 57 @Resource
  58 + @Lazy // 延迟加载,解决循环依赖
59 private IotProductService productService; 59 private IotProductService productService;
60 @Resource 60 @Resource
61 @Lazy // 延迟加载,解决循环依赖 61 @Lazy // 延迟加载,解决循环依赖
@@ -73,6 +73,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -73,6 +73,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
73 createReqVO.getGatewayId(), product); 73 createReqVO.getGatewayId(), product);
74 // 1.3 校验分组存在 74 // 1.3 校验分组存在
75 deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds()); 75 deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds());
  76 + // 1.4 校验设备序列号全局唯一
  77 + validateSerialNumberUnique(createReqVO.getSerialNumber(), null);
76 78
77 // 2. 插入到数据库 79 // 2. 插入到数据库
78 IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class); 80 IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class);
@@ -81,34 +83,14 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -81,34 +83,14 @@ public class IotDeviceServiceImpl implements IotDeviceService {
81 return device.getId(); 83 return device.getId();
82 } 84 }
83 85
84 - @Override  
85 - public IotDeviceDO createDevice(String productKey, String deviceName, Long gatewayId) {  
86 - // 1.1 校验产品是否存在  
87 - IotProductDO product = TenantUtils.executeIgnore(() -> productService.getProductByProductKey(productKey));  
88 - if (product == null) {  
89 - throw exception(PRODUCT_NOT_EXISTS);  
90 - }  
91 - return TenantUtils.execute(product.getTenantId(), () -> {  
92 - // 1.2 校验设备名称在同一产品下是否唯一  
93 - validateCreateDeviceParam(productKey, deviceName, gatewayId, product);  
94 -  
95 - // 2. 插入到数据库  
96 - IotDeviceDO device = new IotDeviceDO().setDeviceName(deviceName).setGatewayId(gatewayId);  
97 - initDevice(device, product);  
98 - deviceMapper.insert(device);  
99 - return device;  
100 - });  
101 - }  
102 -  
103 private void validateCreateDeviceParam(String productKey, String deviceName, 86 private void validateCreateDeviceParam(String productKey, String deviceName,
104 Long gatewayId, IotProductDO product) { 87 Long gatewayId, IotProductDO product) {
  88 + // 校验设备名称在同一产品下是否唯一
105 TenantUtils.executeIgnore(() -> { 89 TenantUtils.executeIgnore(() -> {
106 - // 校验设备名称在同一产品下是否唯一  
107 if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) { 90 if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) {
108 throw exception(DEVICE_NAME_EXISTS); 91 throw exception(DEVICE_NAME_EXISTS);
109 } 92 }
110 }); 93 });
111 -  
112 // 校验父设备是否为合法网关 94 // 校验父设备是否为合法网关
113 if (IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType()) 95 if (IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType())
114 && gatewayId != null) { 96 && gatewayId != null) {
@@ -116,6 +98,22 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -116,6 +98,22 @@ public class IotDeviceServiceImpl implements IotDeviceService {
116 } 98 }
117 } 99 }
118 100
  101 + /**
  102 + * 校验设备序列号全局唯一性
  103 + *
  104 + * @param serialNumber 设备序列号
  105 + * @param excludeId 排除的设备编号(用于更新时排除自身)
  106 + */
  107 + private void validateSerialNumberUnique(String serialNumber, Long excludeId) {
  108 + if (StrUtil.isBlank(serialNumber)) {
  109 + return;
  110 + }
  111 + IotDeviceDO existDevice = deviceMapper.selectBySerialNumber(serialNumber);
  112 + if (existDevice != null && ObjUtil.notEqual(existDevice.getId(), excludeId)) {
  113 + throw exception(DEVICE_SERIAL_NUMBER_EXISTS);
  114 + }
  115 + }
  116 +
119 private void initDevice(IotDeviceDO device, IotProductDO product) { 117 private void initDevice(IotDeviceDO device, IotProductDO product) {
120 device.setProductId(product.getId()).setProductKey(product.getProductKey()) 118 device.setProductId(product.getId()).setProductKey(product.getProductKey())
121 .setDeviceType(product.getDeviceType()); 119 .setDeviceType(product.getDeviceType());
@@ -137,6 +135,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -137,6 +135,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
137 } 135 }
138 // 1.3 校验分组存在 136 // 1.3 校验分组存在
139 deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds()); 137 deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds());
  138 + // 1.4 校验设备序列号全局唯一
  139 + validateSerialNumberUnique(updateReqVO.getSerialNumber(), updateReqVO.getId());
140 140
141 // 2. 更新到数据库 141 // 2. 更新到数据库
142 IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); 142 IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
@@ -264,8 +264,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -264,8 +264,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
264 } 264 }
265 265
266 @Override 266 @Override
267 - public List<IotDeviceDO> getDeviceListByDeviceType(@Nullable Integer deviceType) {  
268 - return deviceMapper.selectListByDeviceType(deviceType); 267 + public List<IotDeviceDO> getDeviceListByCondition(@Nullable Integer deviceType, @Nullable Long productId) {
  268 + return deviceMapper.selectListByCondition(deviceType, productId);
269 } 269 }
270 270
271 @Override 271 @Override
@@ -279,11 +279,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -279,11 +279,6 @@ public class IotDeviceServiceImpl implements IotDeviceService {
279 } 279 }
280 280
281 @Override 281 @Override
282 - public List<IotDeviceDO> getDeviceListByIdList(List<Long> deviceIdList) {  
283 - return deviceMapper.selectByIds(deviceIdList);  
284 - }  
285 -  
286 - @Override  
287 public void updateDeviceState(IotDeviceDO device, Integer state) { 282 public void updateDeviceState(IotDeviceDO device, Integer state) {
288 // 1. 更新状态和时间 283 // 1. 更新状态和时间
289 IotDeviceDO updateObj = new IotDeviceDO().setId(device.getId()).setState(state); 284 IotDeviceDO updateObj = new IotDeviceDO().setId(device.getId()).setState(state);
@@ -417,6 +412,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -417,6 +412,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
417 devices.forEach(this::deleteDeviceCache); 412 devices.forEach(this::deleteDeviceCache);
418 } 413 }
419 414
  415 + @SuppressWarnings("unused")
420 @Caching(evict = { 416 @Caching(evict = {
421 @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.id"), 417 @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.id"),
422 @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName") 418 @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName")
@@ -429,25 +425,14 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -429,25 +425,14 @@ public class IotDeviceServiceImpl implements IotDeviceService {
429 return deviceMapper.selectCountByCreateTime(createTime); 425 return deviceMapper.selectCountByCreateTime(createTime);
430 } 426 }
431 427
432 - // TODO @super:简化  
433 @Override 428 @Override
434 public Map<Long, Integer> getDeviceCountMapByProductId() { 429 public Map<Long, Integer> getDeviceCountMapByProductId() {
435 - // 查询结果转换成Map  
436 - List<Map<String, Object>> list = deviceMapper.selectDeviceCountMapByProductId();  
437 - return list.stream().collect(Collectors.toMap(  
438 - map -> Long.valueOf(map.get("key").toString()),  
439 - map -> Integer.valueOf(map.get("value").toString())  
440 - )); 430 + return deviceMapper.selectDeviceCountMapByProductId();
441 } 431 }
442 432
443 @Override 433 @Override
444 public Map<Integer, Long> getDeviceCountMapByState() { 434 public Map<Integer, Long> getDeviceCountMapByState() {
445 - // 查询结果转换成Map  
446 - List<Map<String, Object>> list = deviceMapper.selectDeviceCountGroupByState();  
447 - return list.stream().collect(Collectors.toMap(  
448 - map -> Integer.valueOf(map.get("key").toString()),  
449 - map -> Long.valueOf(map.get("value").toString())  
450 - )); 435 + return deviceMapper.selectDeviceCountGroupByState();
451 } 436 }
452 437
453 @Override 438 @Override
@@ -483,6 +468,23 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -483,6 +468,23 @@ public class IotDeviceServiceImpl implements IotDeviceService {
483 return true; 468 return true;
484 } 469 }
485 470
  471 + @Override
  472 + public List<IotDeviceDO> validateDeviceListExists(Collection<Long> ids) {
  473 + List<IotDeviceDO> devices = getDeviceList(ids);
  474 + if (devices.size() != ids.size()) {
  475 + throw exception(DEVICE_NOT_EXISTS);
  476 + }
  477 + return devices;
  478 + }
  479 +
  480 + @Override
  481 + public List<IotDeviceDO> getDeviceList(Collection<Long> ids) {
  482 + if (CollUtil.isEmpty(ids)) {
  483 + return Collections.emptyList();
  484 + }
  485 + return deviceMapper.selectByIds(ids);
  486 + }
  487 +
486 private IotDeviceServiceImpl getSelf() { 488 private IotDeviceServiceImpl getSelf() {
487 return SpringUtil.getBean(getClass()); 489 return SpringUtil.getBean(getClass());
488 } 490 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java
1 package cn.iocoder.yudao.module.iot.service.device.message; 1 package cn.iocoder.yudao.module.iot.service.device.message;
2 2
3 import cn.hutool.core.date.LocalDateTimeUtil; 3 import cn.hutool.core.date.LocalDateTimeUtil;
  4 +import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.map.MapUtil; 5 import cn.hutool.core.map.MapUtil;
5 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
6 import cn.hutool.extra.spring.SpringUtil; 7 import cn.hutool.extra.spring.SpringUtil;
@@ -13,7 +14,6 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceM @@ -13,7 +14,6 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceM
13 import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; 14 import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO;
14 import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; 15 import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO;
15 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; 16 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
16 -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;  
17 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 17 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
18 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; 18 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
19 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 19 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
@@ -176,15 +176,12 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -176,15 +176,12 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
176 // TODO @芋艿:可优化:未来逻辑复杂后,可以独立拆除 Processor 处理器 176 // TODO @芋艿:可优化:未来逻辑复杂后,可以独立拆除 Processor 处理器
177 @SuppressWarnings("SameReturnValue") 177 @SuppressWarnings("SameReturnValue")
178 private Object handleUpstreamDeviceMessage0(IotDeviceMessage message, IotDeviceDO device) { 178 private Object handleUpstreamDeviceMessage0(IotDeviceMessage message, IotDeviceDO device) {
179 - // 设备上线  
180 - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod())) {  
181 - deviceService.updateDeviceState(device, IotDeviceStateEnum.ONLINE.getState());  
182 - // TODO 芋艿:子设备的关联  
183 - return null;  
184 - }  
185 - // 设备下线  
186 - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod())) {  
187 - deviceService.updateDeviceState(device, IotDeviceStateEnum.OFFLINE.getState()); 179 + // 设备上下线
  180 + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod())) {
  181 + String stateStr = IotDeviceMessageUtils.getIdentifier(message);
  182 + assert stateStr != null;
  183 + Assert.notEmpty(stateStr, "设备状态不能为空");
  184 + deviceService.updateDeviceState(device, Integer.valueOf(stateStr));
188 // TODO 芋艿:子设备的关联 185 // TODO 芋艿:子设备的关联
189 return null; 186 return null;
190 } 187 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java
@@ -4,14 +4,16 @@ import cn.hutool.core.collection.CollUtil; @@ -4,14 +4,16 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.date.LocalDateTimeUtil; 4 import cn.hutool.core.date.LocalDateTimeUtil;
5 import cn.hutool.core.map.MapUtil; 5 import cn.hutool.core.map.MapUtil;
6 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
  7 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  8 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO; 9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO;
8 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO; 10 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO;
9 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs;  
10 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
11 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 12 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
12 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; 13 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
13 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 14 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
14 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; 15 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
  16 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs;
15 import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO; 17 import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO;
16 import cn.iocoder.yudao.module.iot.dal.redis.device.DeviceReportTimeRedisDAO; 18 import cn.iocoder.yudao.module.iot.dal.redis.device.DeviceReportTimeRedisDAO;
17 import cn.iocoder.yudao.module.iot.dal.redis.device.DeviceServerIdRedisDAO; 19 import cn.iocoder.yudao.module.iot.dal.redis.device.DeviceServerIdRedisDAO;
@@ -23,6 +25,7 @@ import cn.iocoder.yudao.module.iot.service.product.IotProductService; @@ -23,6 +25,7 @@ import cn.iocoder.yudao.module.iot.service.product.IotProductService;
23 import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; 25 import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
24 import jakarta.annotation.Resource; 26 import jakarta.annotation.Resource;
25 import lombok.extern.slf4j.Slf4j; 27 import lombok.extern.slf4j.Slf4j;
  28 +import org.springframework.context.annotation.Lazy;
26 import org.springframework.scheduling.annotation.Async; 29 import org.springframework.scheduling.annotation.Async;
27 import org.springframework.stereotype.Service; 30 import org.springframework.stereotype.Service;
28 31
@@ -42,22 +45,25 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { @@ -42,22 +45,25 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
42 45
43 /** 46 /**
44 * 物模型的数据类型,与 TDengine 数据类型的映射关系 47 * 物模型的数据类型,与 TDengine 数据类型的映射关系
  48 + *
  49 + * @see <a href="https://docs.taosdata.com/reference/taos-sql/data-type/">TDEngine 数据类型</a>
45 */ 50 */
46 private static final Map<String, String> TYPE_MAPPING = MapUtil.<String, String>builder() 51 private static final Map<String, String> TYPE_MAPPING = MapUtil.<String, String>builder()
47 .put(IotDataSpecsDataTypeEnum.INT.getDataType(), TDengineTableField.TYPE_INT) 52 .put(IotDataSpecsDataTypeEnum.INT.getDataType(), TDengineTableField.TYPE_INT)
48 .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT) 53 .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT)
49 .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE) 54 .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE)
50 - .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明?  
51 - .put(IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明?  
52 - .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_NCHAR) 55 + .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT)
  56 + .put(IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT)
  57 + .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_VARCHAR)
53 .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP) 58 .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP)
54 - .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!!  
55 - .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! 59 + .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_VARCHAR)
  60 + .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_VARCHAR)
56 .build(); 61 .build();
57 62
58 @Resource 63 @Resource
59 private IotThingModelService thingModelService; 64 private IotThingModelService thingModelService;
60 @Resource 65 @Resource
  66 + @Lazy // 延迟加载,解决循环依赖
61 private IotProductService productService; 67 private IotProductService productService;
62 68
63 @Resource 69 @Resource
@@ -107,8 +113,12 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { @@ -107,8 +113,12 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
107 TDengineTableField field = new TDengineTableField( 113 TDengineTableField field = new TDengineTableField(
108 StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写 114 StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写
109 TYPE_MAPPING.get(thingModel.getProperty().getDataType())); 115 TYPE_MAPPING.get(thingModel.getProperty().getDataType()));
110 - if (thingModel.getProperty().getDataType().equals(IotDataSpecsDataTypeEnum.TEXT.getDataType())) { 116 + String dataType = thingModel.getProperty().getDataType();
  117 + if (Objects.equals(dataType, IotDataSpecsDataTypeEnum.TEXT.getDataType())) {
111 field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength()); 118 field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength());
  119 + } else if (ObjectUtils.equalsAny(dataType, IotDataSpecsDataTypeEnum.STRUCT.getDataType(),
  120 + IotDataSpecsDataTypeEnum.ARRAY.getDataType())) {
  121 + field.setLength(TDengineTableField.LENGTH_VARCHAR);
112 } 122 }
113 return field; 123 return field;
114 }); 124 });
@@ -116,7 +126,6 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { @@ -116,7 +126,6 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
116 126
117 @Override 127 @Override
118 public void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message) { 128 public void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message) {
119 - // TODO @芋艿:这里要改下协议!  
120 if (!(message.getParams() instanceof Map)) { 129 if (!(message.getParams() instanceof Map)) {
121 log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message); 130 log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message);
122 return; 131 return;
@@ -127,11 +136,18 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { @@ -127,11 +136,18 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
127 List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductIdFromCache(device.getProductId()); 136 List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductIdFromCache(device.getProductId());
128 Map<String, Object> properties = new HashMap<>(); 137 Map<String, Object> properties = new HashMap<>();
129 ((Map<?, ?>) message.getParams()).forEach((key, value) -> { 138 ((Map<?, ?>) message.getParams()).forEach((key, value) -> {
130 - if (CollUtil.findOne(thingModels, thingModel -> thingModel.getIdentifier().equals(key)) == null) { 139 + IotThingModelDO thingModel = CollUtil.findOne(thingModels, o -> o.getIdentifier().equals(key));
  140 + if (thingModel == null || thingModel.getProperty() == null) {
131 log.error("[saveDeviceProperty][消息({}) 的属性({}) 不存在]", message, key); 141 log.error("[saveDeviceProperty][消息({}) 的属性({}) 不存在]", message, key);
132 return; 142 return;
133 } 143 }
134 - properties.put((String) key, value); 144 + if (ObjectUtils.equalsAny(thingModel.getProperty().getDataType(),
  145 + IotDataSpecsDataTypeEnum.STRUCT.getDataType(), IotDataSpecsDataTypeEnum.ARRAY.getDataType())) {
  146 + // 特殊:STRUCT 和 ARRAY 类型,在 TDengine 里,有没对应数据类型,只能通过 JSON 来存储
  147 + properties.put((String) key, JsonUtils.toJsonString(value));
  148 + } else {
  149 + properties.put((String) key, value);
  150 + }
135 }); 151 });
136 if (CollUtil.isEmpty(properties)) { 152 if (CollUtil.isEmpty(properties)) {
137 log.error("[saveDeviceProperty][消息({}) 没有合法的属性]", message); 153 log.error("[saveDeviceProperty][消息({}) 没有合法的属性]", message);
@@ -139,8 +155,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { @@ -139,8 +155,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
139 } 155 }
140 156
141 // 2.1 保存设备属性【数据】 157 // 2.1 保存设备属性【数据】
142 - devicePropertyMapper.insert(device, properties,  
143 - LocalDateTimeUtil.toEpochMilli(message.getReportTime())); 158 + devicePropertyMapper.insert(device, properties, LocalDateTimeUtil.toEpochMilli(message.getReportTime()));
144 159
145 // 2.2 保存设备属性【日志】 160 // 2.2 保存设备属性【日志】
146 Map<String, IotDevicePropertyDO> properties2 = convertMap(properties.entrySet(), Map.Entry::getKey, entry -> 161 Map<String, IotDevicePropertyDO> properties2 = convertMap(properties.entrySet(), Map.Entry::getKey, entry ->
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java
@@ -7,9 +7,14 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwa @@ -7,9 +7,14 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwa
7 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
8 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
9 9
10 -// TODO @li:注释写的有点冗余,可以看看别的模块哈。= = AI 生成的注释,有的时候太啰嗦了,需要处理下的哈 10 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  11 +
  12 +import java.util.Collection;
  13 +import java.util.List;
  14 +import java.util.Map;
  15 +
11 /** 16 /**
12 - * OTA 固件管理 Service 17 + * OTA 固件管理 Service 接口
13 * 18 *
14 * @author Shelly Chan 19 * @author Shelly Chan
15 */ 20 */
@@ -18,41 +23,57 @@ public interface IotOtaFirmwareService { @@ -18,41 +23,57 @@ public interface IotOtaFirmwareService {
18 /** 23 /**
19 * 创建 OTA 固件 24 * 创建 OTA 固件
20 * 25 *
21 - * @param saveReqVO OTA固件保存请求对象,包含固件的相关信息  
22 - * @return 返回新创建的固件的ID 26 + * @param saveReqVO 固件信息
  27 + * @return 固件编号
23 */ 28 */
24 Long createOtaFirmware(@Valid IotOtaFirmwareCreateReqVO saveReqVO); 29 Long createOtaFirmware(@Valid IotOtaFirmwareCreateReqVO saveReqVO);
25 30
26 /** 31 /**
27 * 更新 OTA 固件信息 32 * 更新 OTA 固件信息
28 * 33 *
29 - * @param updateReqVO OTA固件保存请求对象,包含需要更新的固件信息 34 + * @param updateReqVO 固件信息
30 */ 35 */
31 void updateOtaFirmware(@Valid IotOtaFirmwareUpdateReqVO updateReqVO); 36 void updateOtaFirmware(@Valid IotOtaFirmwareUpdateReqVO updateReqVO);
32 37
33 /** 38 /**
34 - * 根据 ID 获取 OTA 固件信息 39 + * 获取 OTA 固件信息
35 * 40 *
36 - * @param id OTA固件的唯一标识符  
37 - * @return 返回OTA固件的详细信息对象 41 + * @param id 固件编号
  42 + * @return 固件信息
38 */ 43 */
39 IotOtaFirmwareDO getOtaFirmware(Long id); 44 IotOtaFirmwareDO getOtaFirmware(Long id);
40 45
41 /** 46 /**
  47 + * 获取 OTA 固件信息列表
  48 + *
  49 + * @param ids 固件编号集合
  50 + * @return 固件信息列表
  51 + */
  52 + List<IotOtaFirmwareDO> getOtaFirmwareList(Collection<Long> ids);
  53 +
  54 + /**
  55 + * 获取 OTA 固件信息 Map
  56 + *
  57 + * @param ids 固件编号集合
  58 + * @return 固件信息 Map
  59 + */
  60 + default Map<Long, IotOtaFirmwareDO> getOtaFirmwareMap(Collection<Long> ids) {
  61 + return convertMap(getOtaFirmwareList(ids), IotOtaFirmwareDO::getId);
  62 + }
  63 +
  64 + /**
42 * 分页查询 OTA 固件信息 65 * 分页查询 OTA 固件信息
43 * 66 *
44 - * @param pageReqVO 包含分页查询条件的请求对象  
45 - * @return 返回分页查询结果,包含固件信息列表和分页信息 67 + * @param pageReqVO 分页查询条件
  68 + * @return 分页结果
46 */ 69 */
47 PageResult<IotOtaFirmwareDO> getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO); 70 PageResult<IotOtaFirmwareDO> getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO);
48 71
49 /** 72 /**
50 - * 验证物联网 OTA 固件是否存在 73 + * 验证 OTA 固件是否存在
51 * 74 *
52 - * @param id 固件的唯一标识符  
53 - * 该方法用于检查系统中是否存在与给定ID关联的物联网OTA固件信息  
54 - * 主要目的是在进行固件更新操作前,确保目标固件已经存在并可以被访问  
55 - * 如果固件不存在,该方法可能抛出异常或返回错误信息,具体行为未定义 75 + * @param id 固件编号
  76 + * @return 固件信息
56 */ 77 */
57 IotOtaFirmwareDO validateFirmwareExists(Long id); 78 IotOtaFirmwareDO validateFirmwareExists(Long id);
58 79
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java
1 package cn.iocoder.yudao.module.iot.service.ota; 1 package cn.iocoder.yudao.module.iot.service.ota;
2 2
3 -import cn.hutool.core.collection.CollUtil;  
4 -import cn.hutool.core.convert.Convert; 3 +import cn.hutool.crypto.digest.DigestAlgorithm;
  4 +import cn.hutool.crypto.digest.DigestUtil;
  5 +import cn.hutool.http.HttpUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO; 8 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO;
8 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; 9 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO;
9 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO; 10 import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO;
10 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; 11 import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
11 -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;  
12 import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaFirmwareMapper; 12 import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaFirmwareMapper;
13 import cn.iocoder.yudao.module.iot.service.product.IotProductService; 13 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
14 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
@@ -17,16 +17,25 @@ import org.springframework.context.annotation.Lazy; @@ -17,16 +17,25 @@ import org.springframework.context.annotation.Lazy;
17 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
18 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
19 19
  20 +import java.io.ByteArrayInputStream;
  21 +import java.util.Collection;
  22 +import java.util.Collections;
20 import java.util.List; 23 import java.util.List;
21 -import java.util.Objects; 24 +import java.util.Map;
22 25
23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
24 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_NOT_EXISTS; 28 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_NOT_EXISTS;
25 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE; 29 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE;
26 30
27 -@Slf4j 31 +/**
  32 + * OTA 固件管理 Service 实现类
  33 + *
  34 + * @author Shelly Chan
  35 + */
28 @Service 36 @Service
29 @Validated 37 @Validated
  38 +@Slf4j
30 public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { 39 public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
31 40
32 @Resource 41 @Resource
@@ -37,16 +46,22 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { @@ -37,16 +46,22 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
37 46
38 @Override 47 @Override
39 public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) { 48 public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) {
40 - // 1. 校验固件产品 + 版本号不能重复  
41 - validateProductAndVersionDuplicate(saveReqVO.getProductId(), saveReqVO.getVersion()); 49 + // 1.1 校验固件产品 + 版本号不能重复
  50 + if (otaFirmwareMapper.selectByProductIdAndVersion(saveReqVO.getProductId(), saveReqVO.getVersion()) != null) {
  51 + throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE);
  52 + }
  53 + // 1.2 校验产品存在
  54 + productService.validateProductExists(saveReqVO.getProductId());
42 55
43 - // 2.1.转化数据格式,准备存储到数据库中 56 + // 2. 构建对象 + 存储
44 IotOtaFirmwareDO firmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class); 57 IotOtaFirmwareDO firmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class);
45 - // 2.2.查询ProductKey  
46 - // TODO @li:productService.getProduct(Convert.toLong(firmware.getProductId())) 放到 1. 后面,先做参考校验。逻辑两段:1)先参数校验;2)构建对象 + 存储  
47 - IotProductDO product = productService.getProduct(Convert.toLong(firmware.getProductId()));  
48 - firmware.setProductKey(Objects.requireNonNull(product).getProductKey());  
49 - // TODO @芋艿: 附件、附件签名等属性的计算 58 + // 2.1 计算文件签名等属性
  59 + try {
  60 + calculateFileDigest(firmware);
  61 + } catch (Exception e) {
  62 + log.error("[createOtaFirmware][url({}) 计算文件签名失败]", firmware.getFileUrl(), e);
  63 + throw new RuntimeException("计算文件签名失败: " + e.getMessage());
  64 + }
50 otaFirmwareMapper.insert(firmware); 65 otaFirmwareMapper.insert(firmware);
51 return firmware.getId(); 66 return firmware.getId();
52 } 67 }
@@ -67,6 +82,14 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { @@ -67,6 +82,14 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
67 } 82 }
68 83
69 @Override 84 @Override
  85 + public List<IotOtaFirmwareDO> getOtaFirmwareList(Collection<Long> ids) {
  86 + if (ids == null || ids.isEmpty()) {
  87 + return Collections.emptyList();
  88 + }
  89 + return otaFirmwareMapper.selectByIds(ids);
  90 + }
  91 +
  92 + @Override
70 public PageResult<IotOtaFirmwareDO> getOtaFirmwarePage(IotOtaFirmwarePageReqVO pageReqVO) { 93 public PageResult<IotOtaFirmwareDO> getOtaFirmwarePage(IotOtaFirmwarePageReqVO pageReqVO) {
71 return otaFirmwareMapper.selectPage(pageReqVO); 94 return otaFirmwareMapper.selectPage(pageReqVO);
72 } 95 }
@@ -80,25 +103,21 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { @@ -80,25 +103,21 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {
80 return firmware; 103 return firmware;
81 } 104 }
82 105
83 - // TODO @li:注释有点冗余  
84 /** 106 /**
85 - * 验证产品和版本号是否重复  
86 - * <p>  
87 - * 该方法用于确保在系统中不存在具有相同产品ID和版本号的固件条目  
88 - * 它通过调用otaFirmwareMapper的selectByProductIdAndVersion方法来查询数据库中是否存在匹配的产品ID和版本号的固件信息  
89 - * 如果查询结果非空且不为null,则抛出异常,提示固件信息已存在,从而避免数据重复 107 + * 计算文件签名
90 * 108 *
91 - * @param productId 产品ID,用于数据库查询  
92 - * @param version 版本号,用于数据库查询  
93 - * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出异常,提示固件信息已存在 109 + * @param firmware 固件对象
94 */ 110 */
95 - private void validateProductAndVersionDuplicate(String productId, String version) {  
96 - // 查询数据库中是否存在具有相同产品ID和版本号的固件信息  
97 - List<IotOtaFirmwareDO> list = otaFirmwareMapper.selectByProductIdAndVersion(productId, version);  
98 - // 如果查询结果非空且不为null,则抛出异常,提示固件信息已存在  
99 - if (CollUtil.isNotEmpty(list)) {  
100 - throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE);  
101 - } 111 + private void calculateFileDigest(IotOtaFirmwareDO firmware) {
  112 + String fileUrl = firmware.getFileUrl();
  113 + // 下载文件并计算签名
  114 + byte[] fileBytes = HttpUtil.downloadBytes(fileUrl);
  115 + // 设置文件大小
  116 + firmware.setFileSize((long) fileBytes.length);
  117 + // 计算 MD5 签名
  118 + firmware.setFileDigestAlgorithm(DigestAlgorithm.MD5.getValue());
  119 + String md5Hex = DigestUtil.digester(firmware.getFileDigestAlgorithm()).digestHex(new ByteArrayInputStream(fileBytes));
  120 + firmware.setFileDigestValue(md5Hex);
102 } 121 }
103 122
104 } 123 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.ota;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +import java.util.List;
  10 +import java.util.Map;
  11 +import java.util.Set;
  12 +
  13 +/**
  14 + * IoT OTA 升级记录 Service 接口
  15 + */
  16 +public interface IotOtaTaskRecordService {
  17 +
  18 + /**
  19 + * 批量创建 OTA 升级记录
  20 + *
  21 + * @param devices 设备列表
  22 + * @param firmwareId 固件编号
  23 + * @param taskId 任务编号
  24 + */
  25 + void createOtaTaskRecordList(List<IotDeviceDO> devices, Long firmwareId, Long taskId);
  26 +
  27 + /**
  28 + * 获取 OTA 升级记录的状态统计
  29 + *
  30 + * @param firmwareId 固件编号
  31 + * @param taskId 任务编号
  32 + * @return 状态统计 Map,key 为状态码,value 为对应状态的升级记录数量
  33 + */
  34 + Map<Integer, Long> getOtaTaskRecordStatusStatistics(Long firmwareId, Long taskId);
  35 +
  36 + /**
  37 + * 获取 OTA 升级记录
  38 + *
  39 + * @param id 编号
  40 + * @return OTA 升级记录
  41 + */
  42 + IotOtaTaskRecordDO getOtaTaskRecord(Long id);
  43 +
  44 + /**
  45 + * 获取 OTA 升级记录分页
  46 + *
  47 + * @param pageReqVO 分页查询
  48 + * @return OTA 升级记录分页
  49 + */
  50 + PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(@Valid IotOtaTaskRecordPageReqVO pageReqVO);
  51 +
  52 + /**
  53 + * 根据 OTA 任务编号,取消未结束的升级记录
  54 + *
  55 + * @param taskId 升级任务编号
  56 + */
  57 + void cancelTaskRecordListByTaskId(Long taskId);
  58 +
  59 + /**
  60 + * 根据设备编号和记录状态,获取 OTA 升级记录列表
  61 + *
  62 + * @param deviceIds 设备编号集合
  63 + * @param statuses 记录状态集合
  64 + * @return OTA 升级记录列表
  65 + */
  66 + List<IotOtaTaskRecordDO> getOtaTaskRecordListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses);
  67 +
  68 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.ota;
  2 +
  3 +import cn.hutool.core.convert.Convert;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
  8 +import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskRecordMapper;
  9 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
  10 +import jakarta.annotation.Resource;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +import org.springframework.stereotype.Service;
  13 +import org.springframework.validation.annotation.Validated;
  14 +
  15 +import java.util.Arrays;
  16 +import java.util.List;
  17 +import java.util.Map;
  18 +import java.util.Set;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  22 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_TASK_RECORD_NOT_EXISTS;
  23 +
  24 +/**
  25 + * OTA 升级任务记录 Service 实现类
  26 + */
  27 +@Service
  28 +@Validated
  29 +@Slf4j
  30 +public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService {
  31 +
  32 + @Resource
  33 + private IotOtaTaskRecordMapper otaTaskRecordMapper;
  34 +
  35 + @Override
  36 + public void createOtaTaskRecordList(List<IotDeviceDO> devices, Long firmwareId, Long taskId) {
  37 + List<IotOtaTaskRecordDO> records = convertList(devices, device ->
  38 + IotOtaTaskRecordDO.builder().firmwareId(firmwareId).taskId(taskId)
  39 + .deviceId(device.getId()).fromFirmwareId(Convert.toLong(device.getFirmwareId()))
  40 + .status(IotOtaTaskRecordStatusEnum.PENDING.getStatus()).progress(0).build());
  41 + otaTaskRecordMapper.insertBatch(records);
  42 + }
  43 +
  44 + @Override
  45 + public Map<Integer, Long> getOtaTaskRecordStatusStatistics(Long firmwareId, Long taskId) {
  46 + // 按照 status 枚举,初始化 countMap 为 0
  47 + Map<Integer, Long> countMap = convertMap(Arrays.asList(IotOtaTaskRecordStatusEnum.values()),
  48 + IotOtaTaskRecordStatusEnum::getStatus, iotOtaTaskRecordStatusEnum -> 0L);
  49 +
  50 + // 查询记录,只返回 id、status 字段
  51 + List<IotOtaTaskRecordDO> records = otaTaskRecordMapper.selectListByFirmwareIdAndTaskId(firmwareId, taskId);
  52 + Map<Long, List<Integer>> deviceStatusesMap = convertMultiMap(records,
  53 + IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus);
  54 + // 找到第一个匹配的优先级状态,避免重复计算
  55 + deviceStatusesMap.forEach((deviceId, statuses) -> {
  56 + for (Integer priorityStatus : IotOtaTaskRecordStatusEnum.PRIORITY_STATUSES) {
  57 + if (statuses.contains(priorityStatus)) {
  58 + countMap.put(priorityStatus, countMap.get(priorityStatus) + 1);
  59 + return;
  60 + }
  61 + }
  62 + });
  63 + return countMap;
  64 + }
  65 +
  66 + @Override
  67 + public IotOtaTaskRecordDO getOtaTaskRecord(Long id) {
  68 + return otaTaskRecordMapper.selectById(id);
  69 + }
  70 +
  71 + @Override
  72 + public PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(IotOtaTaskRecordPageReqVO pageReqVO) {
  73 + return otaTaskRecordMapper.selectPage(pageReqVO);
  74 + }
  75 +
  76 + @Override
  77 + public void cancelTaskRecordListByTaskId(Long taskId) {
  78 + // 设置取消记录的描述
  79 + IotOtaTaskRecordDO updateRecord = IotOtaTaskRecordDO.builder()
  80 + .status(IotOtaTaskRecordStatusEnum.CANCELED.getStatus())
  81 + .description("管理员取消升级任务")
  82 + .build();
  83 +
  84 + otaTaskRecordMapper.updateByTaskIdAndStatus(
  85 + taskId, IotOtaTaskRecordStatusEnum.PENDING.getStatus(), updateRecord);
  86 + }
  87 +
  88 + @Override
  89 + public List<IotOtaTaskRecordDO> getOtaTaskRecordListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses) {
  90 + return otaTaskRecordMapper.selectListByDeviceIdAndStatus(deviceIds, statuses);
  91 + }
  92 +
  93 + private IotOtaTaskRecordDO validateUpgradeRecordExists(Long id) {
  94 + IotOtaTaskRecordDO upgradeRecord = otaTaskRecordMapper.selectById(id);
  95 + if (upgradeRecord == null) {
  96 + throw exception(OTA_TASK_RECORD_NOT_EXISTS);
  97 + }
  98 + return upgradeRecord;
  99 + }
  100 +
  101 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.ota;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * IoT OTA 升级任务 Service 接口
  11 + *
  12 + * @author Shelly Chan
  13 + */
  14 +public interface IotOtaTaskService {
  15 +
  16 + /**
  17 + * 创建 OTA 升级任务
  18 + *
  19 + * @param createReqVO 创建请求对象
  20 + * @return 升级任务编号
  21 + */
  22 + Long createOtaTask(@Valid IotOtaTaskCreateReqVO createReqVO);
  23 +
  24 + /**
  25 + * 取消 OTA 升级任务
  26 + *
  27 + * @param id 升级任务编号
  28 + */
  29 + void cancelOtaTask(Long id);
  30 +
  31 + /**
  32 + * 获取 OTA 升级任务
  33 + *
  34 + * @param id 升级任务编号
  35 + * @return 升级任务
  36 + */
  37 + IotOtaTaskDO getOtaTask(Long id);
  38 +
  39 + /**
  40 + * 分页查询 OTA 升级任务
  41 + *
  42 + * @param pageReqVO 分页查询请求
  43 + * @return 升级任务分页结果
  44 + */
  45 + PageResult<IotOtaTaskDO> getOtaTaskPage(@Valid IotOtaTaskPageReqVO pageReqVO);
  46 +
  47 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.ota;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  10 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
  11 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;
  12 +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;
  13 +import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskMapper;
  14 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;
  15 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;
  16 +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskStatusEnum;
  17 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  18 +import jakarta.annotation.Resource;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.context.annotation.Lazy;
  21 +import org.springframework.stereotype.Service;
  22 +import org.springframework.transaction.annotation.Transactional;
  23 +import org.springframework.validation.annotation.Validated;
  24 +
  25 +import java.util.List;
  26 +import java.util.Objects;
  27 +
  28 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  30 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
  31 +
  32 +/**
  33 + * IoT OTA 升级任务 Service 实现类
  34 + *
  35 + * @author Shelly Chan
  36 + */
  37 +@Service
  38 +@Validated
  39 +@Slf4j
  40 +public class IotOtaTaskServiceImpl implements IotOtaTaskService {
  41 +
  42 + @Resource
  43 + private IotOtaTaskMapper otaTaskMapper;
  44 +
  45 + @Resource
  46 + private IotDeviceService deviceService;
  47 + @Resource
  48 + private IotOtaFirmwareService otaFirmwareService;
  49 + @Resource
  50 + @Lazy // 延迟,避免循环依赖报错
  51 + private IotOtaTaskRecordService otaTaskRecordService;
  52 +
  53 + @Override
  54 + @Transactional(rollbackFor = Exception.class)
  55 + public Long createOtaTask(IotOtaTaskCreateReqVO createReqVO) {
  56 + // 1.1 校验固件信息是否存在
  57 + IotOtaFirmwareDO firmware = otaFirmwareService.validateFirmwareExists(createReqVO.getFirmwareId());
  58 + // 1.2 校验同一固件的升级任务名称不重复
  59 + if (otaTaskMapper.selectByFirmwareIdAndName(firmware.getId(), createReqVO.getName()) != null) {
  60 + throw exception(OTA_TASK_CREATE_FAIL_NAME_DUPLICATE);
  61 + }
  62 + // 1.3 校验设备范围信息
  63 + List<IotDeviceDO> devices = validateOtaTaskDeviceScope(createReqVO, firmware.getProductId());
  64 +
  65 + // 2. 保存升级任务,直接转换
  66 + IotOtaTaskDO task = BeanUtils.toBean(createReqVO, IotOtaTaskDO.class)
  67 + .setStatus(IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())
  68 + .setDeviceTotalCount(devices.size()).setDeviceSuccessCount(0);
  69 + otaTaskMapper.insert(task);
  70 +
  71 + // 3. 生成设备升级记录
  72 + otaTaskRecordService.createOtaTaskRecordList(devices, firmware.getId(), task.getId());
  73 + return task.getId();
  74 + }
  75 +
  76 + @Override
  77 + @Transactional(rollbackFor = Exception.class)
  78 + public void cancelOtaTask(Long id) {
  79 + // 1.1 校验升级任务是否存在
  80 + IotOtaTaskDO upgradeTask = validateUpgradeTaskExists(id);
  81 + // 1.2 校验升级任务是否可以取消
  82 + if (ObjUtil.notEqual(upgradeTask.getStatus(), IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())) {
  83 + throw exception(OTA_TASK_CANCEL_FAIL_STATUS_END);
  84 + }
  85 +
  86 + // 2. 更新升级任务状态为已取消
  87 + otaTaskMapper.updateById(IotOtaTaskDO.builder()
  88 + .id(id).status(IotOtaTaskStatusEnum.CANCELED.getStatus())
  89 + .build());
  90 +
  91 + // 3. 更新升级记录状态为已取消
  92 + otaTaskRecordService.cancelTaskRecordListByTaskId(id);
  93 + }
  94 +
  95 + @Override
  96 + public IotOtaTaskDO getOtaTask(Long id) {
  97 + return otaTaskMapper.selectById(id);
  98 + }
  99 +
  100 + @Override
  101 + public PageResult<IotOtaTaskDO> getOtaTaskPage(IotOtaTaskPageReqVO pageReqVO) {
  102 + return otaTaskMapper.selectUpgradeTaskPage(pageReqVO);
  103 + }
  104 +
  105 + private List<IotDeviceDO> validateOtaTaskDeviceScope(IotOtaTaskCreateReqVO createReqVO, Long productId) {
  106 + // 情况一:选择设备
  107 + if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.SELECT.getScope())) {
  108 + // 1.1 校验设备存在
  109 + List<IotDeviceDO> devices = deviceService.validateDeviceListExists(createReqVO.getDeviceIds());
  110 + for (IotDeviceDO device : devices) {
  111 + if (ObjUtil.notEqual(device.getProductId(), productId)) {
  112 + throw exception(DEVICE_NOT_EXISTS);
  113 + }
  114 + }
  115 + // 1.2 校验设备是否已经是该固件版本
  116 + devices.forEach(device -> {
  117 + if (Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId())) {
  118 + throw exception(OTA_TASK_CREATE_FAIL_DEVICE_FIRMWARE_EXISTS, device.getDeviceName());
  119 + }
  120 + });
  121 + // 1.3 校验设备是否已经在升级中
  122 + List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(
  123 + convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);
  124 + devices.forEach(device -> {
  125 + if (CollUtil.contains(records, item -> item.getDeviceId().equals(device.getId()))) {
  126 + throw exception(OTA_TASK_CREATE_FAIL_DEVICE_OTA_IN_PROCESS, device.getDeviceName());
  127 + }
  128 + });
  129 + return devices;
  130 + }
  131 + // 情况二:全部设备
  132 + if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.ALL.getScope())) {
  133 + List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(productId);
  134 + // 2.1.1 移除已经是该固件版本的设备
  135 + devices.removeIf(device -> Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId()));
  136 + // 2.1.2 移除已经在升级中的设备
  137 + List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(
  138 + convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);
  139 + devices.removeIf(device -> CollUtil.contains(records,
  140 + item -> item.getDeviceId().equals(device.getId())));
  141 + // 2.2 校验是否有可升级的设备
  142 + if (CollUtil.isEmpty(devices)) {
  143 + throw exception(OTA_TASK_CREATE_FAIL_DEVICE_EMPTY);
  144 + }
  145 + return devices;
  146 + }
  147 + throw new IllegalArgumentException("不支持的设备范围:" + createReqVO.getDeviceScope());
  148 + }
  149 +
  150 + private IotOtaTaskDO validateUpgradeTaskExists(Long id) {
  151 + IotOtaTaskDO upgradeTask = otaTaskMapper.selectById(id);
  152 + if (Objects.isNull(upgradeTask)) {
  153 + throw exception(OTA_TASK_NOT_EXISTS);
  154 + }
  155 + return upgradeTask;
  156 + }
  157 +
  158 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java deleted
1 -package cn.iocoder.yudao.module.iot.service.ota;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO;  
5 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO;  
6 -import jakarta.validation.Valid;  
7 -  
8 -import java.util.List;  
9 -import java.util.Map;  
10 -  
11 -// TODO @li:注释写的有点冗余,可以看看别的模块哈。= = AI 生成的注释,有的时候太啰嗦了,需要处理下的哈  
12 -/**  
13 - * IotOtaUpgradeRecordService 接口定义了与物联网设备OTA升级记录相关的操作。  
14 - * 该接口提供了创建、更新、查询、统计和重试升级记录的功能。  
15 - */  
16 -public interface IotOtaUpgradeRecordService {  
17 -  
18 - /**  
19 - * 批量创建 OTA 升级记录  
20 - * 该函数用于为指定的设备列表、固件ID和升级任务ID创建OTA升级记录。  
21 - *  
22 - * @param deviceIds 设备ID列表,表示需要升级的设备集合。  
23 - * @param firmwareId 固件ID,表示要升级到的固件版本。  
24 - * @param upgradeTaskId 升级任务ID,表示此次升级任务的唯一标识。  
25 - */  
26 - void createOtaUpgradeRecordBatch(List<Long> deviceIds, Long firmwareId, Long upgradeTaskId);  
27 -  
28 - /**  
29 - * 获取 OTA 升级记录的数量统计  
30 - *  
31 - * @return 返回一个 Map,其中键为状态码,值为对应状态的升级记录数量  
32 - */  
33 - Map<Integer, Long> getOtaUpgradeRecordCount(@Valid IotOtaUpgradeRecordPageReqVO pageReqVO);  
34 -  
35 - /**  
36 - * 获取 OTA 升级记录的统计信息。  
37 - *  
38 - * @return 返回一个 Map,其中键为状态码,值为对应状态的升级记录统计信息  
39 - */  
40 - Map<Integer, Long> getOtaUpgradeRecordStatistics(Long firmwareId);  
41 -  
42 - /**  
43 - * 重试指定的 OTA 升级记录  
44 - *  
45 - * @param id 需要重试的升级记录的ID。  
46 - */  
47 - void retryUpgradeRecord(Long id);  
48 -  
49 - /**  
50 - * 获取指定 ID 的 OTA 升级记录的详细信息。  
51 - *  
52 - * @param id 需要查询的升级记录的ID。  
53 - * @return 返回包含升级记录详细信息的响应对象。  
54 - */  
55 - IotOtaUpgradeRecordDO getUpgradeRecord(Long id);  
56 -  
57 - /**  
58 - * 分页查询 OTA 升级记录。  
59 - *  
60 - * @param pageReqVO 包含分页查询条件的请求对象,必须经过验证。  
61 - * @return 返回包含分页查询结果的响应对象。  
62 - */  
63 - PageResult<IotOtaUpgradeRecordDO> getUpgradeRecordPage(@Valid IotOtaUpgradeRecordPageReqVO pageReqVO);  
64 -  
65 - /**  
66 - * 根据任务 ID 取消升级记录  
67 - * <p>  
68 - * 该函数用于根据给定的任务ID,取消与该任务相关的升级记录。通常用于在任务执行失败或用户手动取消时,  
69 - * 清理或标记相关的升级记录为取消状态。  
70 - *  
71 - * @param taskId 要取消升级记录的任务ID。该ID唯一标识一个任务,通常由任务管理系统生成。  
72 - */  
73 - void cancelUpgradeRecordByTaskId(Long taskId);  
74 -  
75 - // TODO @li:不要的方法,可以删除下哈。  
76 - /**  
77 - * 根据升级状态获取升级记录列表  
78 - *  
79 - * @param state 升级状态,用于筛选符合条件的升级记录  
80 - * @return 返回符合指定状态的升级记录列表,列表中的元素为 {@link IotOtaUpgradeRecordDO} 对象  
81 - */  
82 - List<IotOtaUpgradeRecordDO> getUpgradeRecordListByState(Integer state);  
83 -  
84 - /**  
85 - * 更新升级记录的状态。  
86 - * <p>  
87 - * 该函数用于批量更新指定升级记录的状态。通过传入的ID列表和状态值,将对应的升级记录的状态更新为指定的值。  
88 - *  
89 - * @param ids 需要更新状态的升级记录的ID列表。列表中的每个元素代表一个升级记录的ID。  
90 - * @param status 要更新的状态值。该值应为有效的状态标识符,通常为整数类型。  
91 - */  
92 - void updateUpgradeRecordStatus(List<Long> ids, Integer status);  
93 -  
94 - /**  
95 - * 根据任务ID获取升级记录列表  
96 - * <p>  
97 - * 该函数通过给定的任务ID,查询并返回与该任务相关的所有升级记录。  
98 - *  
99 - * @param taskId 任务ID,用于指定需要查询的任务  
100 - * @return 返回一个包含升级记录的列表,列表中的每个元素为IotOtaUpgradeRecordDO对象  
101 - */  
102 - List<IotOtaUpgradeRecordDO> getUpgradeRecordListByTaskId(Long taskId);  
103 -  
104 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.iot.service.ota;  
2 -  
3 -import cn.hutool.core.convert.Convert;  
4 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
5 -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO;  
7 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;  
8 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;  
9 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO;  
10 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO;  
11 -import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeRecordMapper;  
12 -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum;  
13 -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;  
14 -import jakarta.annotation.Resource;  
15 -import lombok.extern.slf4j.Slf4j;  
16 -import org.springframework.context.annotation.Lazy;  
17 -import org.springframework.stereotype.Service;  
18 -import org.springframework.transaction.annotation.Transactional;  
19 -import org.springframework.validation.annotation.Validated;  
20 -  
21 -import java.util.List;  
22 -import java.util.Map;  
23 -import java.util.Objects;  
24 -import java.util.stream.Collectors;  
25 -  
26 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
27 -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;  
28 -  
29 -// TODO @li:@Service、@Validated、@Slf4j,先用关键注解;2)类注释,简单写  
30 -@Slf4j  
31 -@Service  
32 -@Validated  
33 -public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordService {  
34 -  
35 - @Resource  
36 - private IotOtaUpgradeRecordMapper upgradeRecordMapper;  
37 - // TODO @li:1)@Resource 写在 @Lazy 之前,先用关键注解;2)有必要的情况下,在写 @Lazy 注解。  
38 - @Lazy  
39 - @Resource  
40 - private IotDeviceService deviceService;  
41 - @Lazy  
42 - @Resource  
43 - private IotOtaFirmwareService firmwareService;  
44 - @Lazy  
45 - @Resource  
46 - private IotOtaUpgradeTaskService upgradeTaskService;  
47 -  
48 - @Override  
49 - public void createOtaUpgradeRecordBatch(List<Long> deviceIds, Long firmwareId, Long upgradeTaskId) {  
50 - // 1. 校验升级记录信息是否存在,并且已经取消的任务可以重新开始  
51 - // TODO @li:批量查询。。  
52 - deviceIds.forEach(deviceId -> validateUpgradeRecordDuplicate(firmwareId, upgradeTaskId, String.valueOf(deviceId)));  
53 -  
54 - // 2.初始化OTA升级记录列表信息  
55 - IotOtaUpgradeTaskDO upgradeTask = upgradeTaskService.getUpgradeTask(upgradeTaskId);  
56 - IotOtaFirmwareDO firmware = firmwareService.getOtaFirmware(firmwareId);  
57 - List<IotDeviceDO> deviceList = deviceService.getDeviceListByIdList(deviceIds);  
58 - List<IotOtaUpgradeRecordDO> upgradeRecordList = deviceList.stream().map(device -> {  
59 - IotOtaUpgradeRecordDO upgradeRecord = new IotOtaUpgradeRecordDO();  
60 - upgradeRecord.setFirmwareId(firmware.getId());  
61 - upgradeRecord.setTaskId(upgradeTask.getId());  
62 - upgradeRecord.setProductKey(device.getProductKey());  
63 - upgradeRecord.setDeviceName(device.getDeviceName());  
64 - upgradeRecord.setDeviceId(Convert.toStr(device.getId()));  
65 - upgradeRecord.setFromFirmwareId(Convert.toLong(device.getFirmwareId()));  
66 - upgradeRecord.setStatus(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus());  
67 - upgradeRecord.setProgress(0);  
68 - return upgradeRecord;  
69 - }).toList();  
70 - // 3.保存数据  
71 - upgradeRecordMapper.insertBatch(upgradeRecordList);  
72 - // TODO @芋艿:在这里需要处理推送升级任务的逻辑  
73 -  
74 - }  
75 -  
76 - // TODO @li:1)方法注释,简单写;2)父类写了注释,子类就不用写了。。。  
77 - /**  
78 - * 获取OTA升级记录的数量统计。  
79 - * 该方法根据传入的查询条件,统计不同状态的OTA升级记录数量,并返回一个包含各状态数量的映射。  
80 - *  
81 - * @param pageReqVO 包含查询条件的请求对象,主要包括任务ID和设备名称等信息。  
82 - * @return 返回一个Map,其中键为状态常量,值为对应状态的记录数量。  
83 - */  
84 - @Override  
85 - @Transactional  
86 - public Map<Integer, Long> getOtaUpgradeRecordCount(IotOtaUpgradeRecordPageReqVO pageReqVO) {  
87 - // 分别查询不同状态的OTA升级记录数量  
88 - List<Map<String, Object>> upgradeRecordCountList = upgradeRecordMapper.selectOtaUpgradeRecordCount(  
89 - pageReqVO.getTaskId(), pageReqVO.getDeviceName());  
90 - Map<String, Object> upgradeRecordCountMap = ObjectUtils.defaultIfNull(upgradeRecordCountList.get(0));  
91 - Objects.requireNonNull(upgradeRecordCountMap);  
92 - return upgradeRecordCountMap.entrySet().stream().collect(Collectors.toMap(  
93 - entry -> Convert.toInt(entry.getKey()),  
94 - entry -> Convert.toLong(entry.getValue())));  
95 - }  
96 -  
97 - // TODO @li:1)方法注释,简单写;2)父类写了注释,子类就不用写了。。。  
98 - /**  
99 - * 获取指定固件ID的OTA升级记录统计信息。  
100 - * 该方法通过查询数据库,统计不同状态的OTA升级记录数量,并返回一个包含各状态数量的映射。  
101 - *  
102 - * @param firmwareId 固件ID,用于指定需要统计的固件升级记录。  
103 - * @return 返回一个Map,其中键为升级记录状态(如PENDING、PUSHED等),值为对应状态的记录数量。  
104 - */  
105 - @Override  
106 - @Transactional  
107 - public Map<Integer, Long> getOtaUpgradeRecordStatistics(Long firmwareId) {  
108 - // 查询并统计不同状态的OTA升级记录数量  
109 - List<Map<String, Object>> upgradeRecordStatisticsList = upgradeRecordMapper.selectOtaUpgradeRecordStatistics(firmwareId);  
110 - Map<String, Object> upgradeRecordStatisticsMap = ObjectUtils.defaultIfNull(upgradeRecordStatisticsList.get(0));  
111 - Objects.requireNonNull(upgradeRecordStatisticsMap);  
112 - return upgradeRecordStatisticsMap.entrySet().stream().collect(Collectors.toMap(  
113 - entry -> Convert.toInt(entry.getKey()),  
114 - entry -> Convert.toLong(entry.getValue())));  
115 - }  
116 -  
117 - @Override  
118 - public void retryUpgradeRecord(Long id) {  
119 - // 1.1.校验升级记录信息是否存在  
120 - IotOtaUpgradeRecordDO upgradeRecord = validateUpgradeRecordExists(id);  
121 - // 1.2.校验升级记录是否可以重新升级  
122 - validateUpgradeRecordCanRetry(upgradeRecord);  
123 -  
124 - // 2. 将一些数据重置,这样定时任务轮询就可以重启任务  
125 - // TODO @li:更新的时候,wherestatus;  
126 - upgradeRecordMapper.updateById(new IotOtaUpgradeRecordDO()  
127 - .setId(upgradeRecord.getId()).setProgress(0)  
128 - .setStatus(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()));  
129 - }  
130 -  
131 - @Override  
132 - public IotOtaUpgradeRecordDO getUpgradeRecord(Long id) {  
133 - return upgradeRecordMapper.selectById(id);  
134 - }  
135 -  
136 - @Override  
137 - public PageResult<IotOtaUpgradeRecordDO> getUpgradeRecordPage(IotOtaUpgradeRecordPageReqVO pageReqVO) {  
138 - return upgradeRecordMapper.selectUpgradeRecordPage(pageReqVO);  
139 - }  
140 -  
141 - @Override  
142 - public void cancelUpgradeRecordByTaskId(Long taskId) {  
143 - // 暂定只有待推送的升级记录可以取消 TODO @芋艿:可以看看阿里云,哪些可以取消  
144 - upgradeRecordMapper.updateUpgradeRecordStatusByTaskIdAndStatus(  
145 - IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus(), taskId,  
146 - IotOtaUpgradeRecordStatusEnum.PENDING.getStatus());  
147 - }  
148 -  
149 - @Override  
150 - public List<IotOtaUpgradeRecordDO> getUpgradeRecordListByState(Integer state) {  
151 - return upgradeRecordMapper.selectUpgradeRecordListByState(state);  
152 - }  
153 -  
154 - @Override  
155 - public void updateUpgradeRecordStatus(List<Long> ids, Integer status) {  
156 - upgradeRecordMapper.updateUpgradeRecordStatus(ids, status);  
157 - }  
158 -  
159 - @Override  
160 - public List<IotOtaUpgradeRecordDO> getUpgradeRecordListByTaskId(Long taskId) {  
161 - return upgradeRecordMapper.selectUpgradeRecordListByTaskId(taskId);  
162 - }  
163 -  
164 - /**  
165 - * 验证指定的升级记录是否存在。  
166 - * <p>  
167 - * 该函数通过给定的ID查询升级记录,如果查询结果为空,则抛出异常,表示升级记录不存在。  
168 - *  
169 - * @param id 升级记录的唯一标识符,类型为Long。  
170 - * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出异常,异常类型为OTA_UPGRADE_RECORD_NOT_EXISTS。  
171 - */  
172 - private IotOtaUpgradeRecordDO validateUpgradeRecordExists(Long id) {  
173 - // 根据ID查询升级记录  
174 - IotOtaUpgradeRecordDO upgradeRecord = upgradeRecordMapper.selectById(id);  
175 - // 如果查询结果为空,抛出异常  
176 - if (upgradeRecord == null) {  
177 - throw exception(OTA_UPGRADE_RECORD_NOT_EXISTS);  
178 - }  
179 - return upgradeRecord;  
180 - }  
181 -  
182 - // TODO @li:注释有点冗余  
183 - /**  
184 - * 校验固件升级记录是否重复。  
185 - * <p>  
186 - * 该函数用于检查给定的固件ID、任务ID和设备ID是否已经存在未取消的升级记录。  
187 - * 如果存在未取消的记录,则抛出异常,提示升级记录重复。  
188 - *  
189 - * @param firmwareId 固件ID,用于标识特定的固件版本  
190 - * @param taskId 任务ID,用于标识特定的升级任务  
191 - * @param deviceId 设备ID,用于标识特定的设备  
192 - */  
193 - private void validateUpgradeRecordDuplicate(Long firmwareId, Long taskId, String deviceId) {  
194 - // 根据条件查询升级记录  
195 - IotOtaUpgradeRecordDO upgradeRecord = upgradeRecordMapper.selectByConditions(firmwareId, taskId, deviceId);  
196 - // 如果查询到升级记录且状态不是已取消,则抛出异常  
197 - // TODO @li:if return,减少括号层级;  
198 - // TODO @li:ObjUtil.notEquals,尽量不用 !取否逻辑;  
199 - if (upgradeRecord != null) {  
200 - if (!IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus().equals(upgradeRecord.getStatus())) {  
201 - // TODO @li:提示的时候,需要把 deviceName 给提示出来,不然用户不知道哪个重复啦。  
202 - throw exception(OTA_UPGRADE_RECORD_DUPLICATE);  
203 - }  
204 - }  
205 - }  
206 -  
207 - // TODO @li:注释有点冗余  
208 - /**  
209 - * 验证升级记录是否可以重试。  
210 - * <p>  
211 - * 该方法用于检查给定的升级记录是否处于允许重试的状态。如果升级记录的状态为  
212 - * PENDING、PUSHED 或 UPGRADING,则抛出异常,表示不允许重试。  
213 - *  
214 - * @param upgradeRecord 需要验证的升级记录对象,类型为 IotOtaUpgradeRecordDO  
215 - * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出 OTA_UPGRADE_RECORD_CANNOT_RETRY 异常  
216 - */  
217 - // TODO @li:这种一次性的方法(不复用的),其实一步一定要抽成小方法;  
218 - private void validateUpgradeRecordCanRetry(IotOtaUpgradeRecordDO upgradeRecord) {  
219 - // 检查升级记录的状态是否为 PENDING、PUSHED 或 UPGRADING  
220 - if (ObjectUtils.equalsAny(upgradeRecord.getStatus(),  
221 - IotOtaUpgradeRecordStatusEnum.PENDING.getStatus(),  
222 - IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus(),  
223 - IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus())) {  
224 - // 如果升级记录处于上述状态之一,则抛出异常,表示不允许重试  
225 - throw exception(OTA_UPGRADE_RECORD_CANNOT_RETRY);  
226 - }  
227 - }  
228 -  
229 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java deleted
1 -package cn.iocoder.yudao.module.iot.service.ota;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskPageReqVO;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskSaveReqVO;  
6 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO;  
7 -import jakarta.validation.Valid;  
8 -  
9 -import java.util.List;  
10 -  
11 -/**  
12 - * IoT OTA升级任务 Service 接口  
13 - *  
14 - * @author Shelly Chan  
15 - */  
16 -public interface IotOtaUpgradeTaskService {  
17 -  
18 - /**  
19 - * 创建OTA升级任务  
20 - *  
21 - * @param createReqVO OTA升级任务的创建请求对象,包含创建任务所需的信息  
22 - * @return 创建成功的OTA升级任务的ID  
23 - */  
24 - Long createUpgradeTask(@Valid IotOtaUpgradeTaskSaveReqVO createReqVO);  
25 -  
26 - /**  
27 - * 取消OTA升级任务  
28 - *  
29 - * @param id 要取消的OTA升级任务的ID  
30 - */  
31 - void cancelUpgradeTask(Long id);  
32 -  
33 - /**  
34 - * 根据ID获取OTA升级任务的详细信息  
35 - *  
36 - * @param id OTA升级任务的ID  
37 - * @return OTA升级任务的详细信息对象  
38 - */  
39 - IotOtaUpgradeTaskDO getUpgradeTask(Long id);  
40 -  
41 - /**  
42 - * 分页查询OTA升级任务  
43 - *  
44 - * @param pageReqVO OTA升级任务的分页查询请求对象,包含查询条件和分页信息  
45 - * @return 分页查询结果,包含OTA升级任务列表和总记录数  
46 - */  
47 - PageResult<IotOtaUpgradeTaskDO> getUpgradeTaskPage(@Valid IotOtaUpgradeTaskPageReqVO pageReqVO);  
48 -  
49 - /**  
50 - * 根据任务状态获取升级任务列表  
51 - *  
52 - * @param state 任务状态,用于筛选符合条件的升级任务  
53 - * @return 返回符合指定状态的升级任务列表,列表中的元素为 IotOtaUpgradeTaskDO 对象  
54 - */  
55 - List<IotOtaUpgradeTaskDO> getUpgradeTaskByState(Integer state);  
56 -  
57 - /**  
58 - * 更新升级任务的状态。  
59 - * <p>  
60 - * 该函数用于根据任务ID更新指定升级任务的状态。通常用于在任务执行过程中  
61 - * 更新任务的状态,例如从“进行中”变为“已完成”或“失败”。  
62 - *  
63 - * @param id 升级任务的唯一标识符,类型为Long。不能为null。  
64 - * @param status 要更新的任务状态,类型为Integer。通常表示任务的状态码,如0表示未开始,1表示进行中,2表示已完成等。  
65 - */  
66 - void updateUpgradeTaskStatus(Long id, Integer status);  
67 -  
68 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.iot.service.ota;  
2 -  
3 -import cn.hutool.core.collection.CollUtil;  
4 -import cn.hutool.core.convert.Convert;  
5 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
6 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
7 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskPageReqVO;  
8 -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskSaveReqVO;  
9 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;  
10 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;  
11 -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO;  
12 -import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeTaskMapper;  
13 -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum;  
14 -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum;  
15 -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;  
16 -import jakarta.annotation.Resource;  
17 -import lombok.extern.slf4j.Slf4j;  
18 -import org.springframework.context.annotation.Lazy;  
19 -import org.springframework.stereotype.Service;  
20 -import org.springframework.transaction.annotation.Transactional;  
21 -import org.springframework.validation.annotation.Validated;  
22 -  
23 -import java.util.List;  
24 -import java.util.Objects;  
25 -import java.util.stream.Collectors;  
26 -  
27 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
28 -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;  
29 -  
30 -// TODO @li:完善注释、注解顺序  
31 -@Slf4j  
32 -@Service  
33 -@Validated  
34 -public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService {  
35 -  
36 - @Resource  
37 - private IotOtaUpgradeTaskMapper upgradeTaskMapper;  
38 -  
39 - @Resource  
40 - @Lazy  
41 - private IotDeviceService deviceService;  
42 - @Resource  
43 - @Lazy  
44 - private IotOtaFirmwareService firmwareService;  
45 - @Resource  
46 - @Lazy  
47 - private IotOtaUpgradeRecordService upgradeRecordService;  
48 -  
49 - @Override  
50 - @Transactional(rollbackFor = Exception.class)  
51 - public Long createUpgradeTask(IotOtaUpgradeTaskSaveReqVO createReqVO) {  
52 - // 1.1 校验同一固件的升级任务名称不重复  
53 - validateFirmwareTaskDuplicate(createReqVO.getFirmwareId(), createReqVO.getName());  
54 - // 1.2 校验固件信息是否存在  
55 - IotOtaFirmwareDO firmware = firmwareService.validateFirmwareExists(createReqVO.getFirmwareId());  
56 - // 1.3 补全设备范围信息,并且校验是否又设备可以升级,如果没有设备可以升级,则报错  
57 - validateScopeAndDevice(createReqVO.getScope(), createReqVO.getDeviceIds(), firmware.getProductId());  
58 -  
59 - // 2. 保存 OTA 升级任务信息到数据库  
60 - IotOtaUpgradeTaskDO upgradeTask = initOtaUpgradeTask(createReqVO, firmware.getProductId());  
61 - upgradeTaskMapper.insert(upgradeTask);  
62 -  
63 - // 3. 生成设备升级记录信息并存储,等待定时任务轮询  
64 - upgradeRecordService.createOtaUpgradeRecordBatch(upgradeTask.getDeviceIds(), firmware.getId(), upgradeTask.getId());  
65 - return upgradeTask.getId();  
66 - }  
67 -  
68 - @Override  
69 - @Transactional(rollbackFor = Exception.class)  
70 - public void cancelUpgradeTask(Long id) {  
71 - // 1.1 校验升级任务是否存在  
72 - IotOtaUpgradeTaskDO upgradeTask = validateUpgradeTaskExists(id);  
73 - // 1.2 校验升级任务是否可以取消  
74 - // TODO @li:ObjUtil notequals  
75 - if (!Objects.equals(upgradeTask.getStatus(), IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus())) {  
76 - throw exception(OTA_UPGRADE_TASK_CANNOT_CANCEL);  
77 - }  
78 -  
79 - // 2. 更新 OTA 升级任务状态为已取消  
80 - upgradeTaskMapper.updateById(IotOtaUpgradeTaskDO.builder()  
81 - .id(id).status(IotOtaUpgradeTaskStatusEnum.CANCELED.getStatus())  
82 - .build());  
83 -  
84 - // 3. 更新 OTA 升级记录状态为已取消  
85 - upgradeRecordService.cancelUpgradeRecordByTaskId(id);  
86 - }  
87 -  
88 - @Override  
89 - public IotOtaUpgradeTaskDO getUpgradeTask(Long id) {  
90 - return upgradeTaskMapper.selectById(id);  
91 - }  
92 -  
93 - @Override  
94 - public PageResult<IotOtaUpgradeTaskDO> getUpgradeTaskPage(IotOtaUpgradeTaskPageReqVO pageReqVO) {  
95 - return upgradeTaskMapper.selectUpgradeTaskPage(pageReqVO);  
96 - }  
97 -  
98 - @Override  
99 - public List<IotOtaUpgradeTaskDO> getUpgradeTaskByState(Integer state) {  
100 - return upgradeTaskMapper.selectUpgradeTaskByState(state);  
101 - }  
102 -  
103 - @Override  
104 - public void updateUpgradeTaskStatus(Long id, Integer status) {  
105 - upgradeTaskMapper.updateById(IotOtaUpgradeTaskDO.builder().id(id).status(status).build());  
106 - }  
107 -  
108 - // TODO @li:注释有点冗余  
109 - /**  
110 - * 校验固件升级任务是否重复  
111 - * <p>  
112 - * 该方法用于检查给定固件ID和任务名称组合是否已存在于数据库中,如果存在则抛出异常,  
113 - * 表示任务名称对于该固件而言是重复的此检查确保用户不能创建具有相同名称的任务,  
114 - * 从而避免数据重复和混淆  
115 - *  
116 - * @param firmwareId 固件的唯一标识符,用于区分不同的固件  
117 - * @param taskName 升级任务的名称,用于与固件ID一起检查重复性  
118 - * @throws cn.iocoder.yudao.framework.common.exception.ServerException 则抛出预定义的异常  
119 - */  
120 - private void validateFirmwareTaskDuplicate(Long firmwareId, String taskName) {  
121 - // 查询数据库中是否有相同固件ID和任务名称的升级任务存在  
122 - List<IotOtaUpgradeTaskDO> upgradeTaskList = upgradeTaskMapper.selectByFirmwareIdAndName(firmwareId, taskName);  
123 - // 如果查询结果不为空,说明存在重复的任务名称,抛出异常  
124 - if (CollUtil.isNotEmpty(upgradeTaskList)) {  
125 - throw exception(OTA_UPGRADE_TASK_NAME_DUPLICATE);  
126 - }  
127 - }  
128 -  
129 - // TODO @li:注释有点冗余  
130 - /**  
131 - * 验证升级任务的范围和设备列表的有效性。  
132 - * <p>  
133 - * 根据升级任务的范围(scope),验证设备列表(deviceIds)或产品ID(productId)是否有效。  
134 - * 如果范围是“选择设备”(SELECT),则必须提供设备列表;如果范围是“所有设备”(ALL),则必须根据产品ID获取设备列表,并确保列表不为空。  
135 - *  
136 - * @param scope 升级任务的范围,参考 IotOtaUpgradeTaskScopeEnum 枚举值  
137 - * @param deviceIds 设备ID列表,当范围为“选择设备”时,该列表不能为空  
138 - * @param productId 产品ID,当范围为“所有设备”时,用于获取设备列表  
139 - * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,抛出相应的异常  
140 - */  
141 - private void validateScopeAndDevice(Integer scope, List<Long> deviceIds, String productId) {  
142 - // TODO @li:if return  
143 - // 验证范围为“选择设备”时,设备列表不能为空  
144 - if (Objects.equals(scope, IotOtaUpgradeTaskScopeEnum.SELECT.getScope())) {  
145 - if (CollUtil.isEmpty(deviceIds)) {  
146 - throw exception(OTA_UPGRADE_TASK_DEVICE_IDS_EMPTY);  
147 - }  
148 - } else if (Objects.equals(scope, IotOtaUpgradeTaskScopeEnum.ALL.getScope())) {  
149 - // 验证范围为“所有设备”时,根据产品ID获取的设备列表不能为空  
150 - List<IotDeviceDO> deviceList = deviceService.getDeviceListByProductId(Convert.toLong(productId));  
151 - if (CollUtil.isEmpty(deviceList)) {  
152 - throw exception(OTA_UPGRADE_TASK_DEVICE_LIST_EMPTY);  
153 - }  
154 - }  
155 - }  
156 -  
157 - // TODO @li:注释有点冗余  
158 - /**  
159 - * 验证升级任务是否存在  
160 - * <p>  
161 - * 通过查询数据库来验证给定ID的升级任务是否存在此方法主要用于确保后续操作所针对的升级任务是有效的  
162 - *  
163 - * @param id 升级任务的唯一标识符如果为null或数据库中不存在对应的记录,则认为任务不存在  
164 - * @throws cn.iocoder.yudao.framework.common.exception.ServiceException 如果升级任务不存在,则抛出异常提示任务不存在  
165 - */  
166 - private IotOtaUpgradeTaskDO validateUpgradeTaskExists(Long id) {  
167 - // 查询数据库中是否有相同固件ID和任务名称的升级任务存在  
168 - IotOtaUpgradeTaskDO upgradeTask = upgradeTaskMapper.selectById(id);  
169 - // 如果查询结果不为空,说明存在重复的任务名称,抛出异常  
170 - if (Objects.isNull(upgradeTask)) {  
171 - throw exception(OTA_UPGRADE_TASK_NOT_EXISTS);  
172 - }  
173 - return upgradeTask;  
174 - }  
175 -  
176 - // TODO @li:注释有点冗余  
177 - /**  
178 - * 初始化升级任务  
179 - * <p>  
180 - * 根据请求参数创建升级任务对象,并根据选择的范围初始化设备数量  
181 - * 如果选择特定设备进行升级,则设备数量为所选设备的总数  
182 - * 如果选择全部设备进行升级,则设备数量为该固件对应产品下的所有设备总数  
183 - *  
184 - * @param createReqVO 升级任务保存请求对象,包含创建升级任务所需的信息  
185 - * @return 返回初始化后的升级任务对象  
186 - */  
187 - // TODO @li:一次性的方法,不用特别抽小方法  
188 - private IotOtaUpgradeTaskDO initOtaUpgradeTask(IotOtaUpgradeTaskSaveReqVO createReqVO, String productId) {  
189 - // 将请求参数转换为升级任务对象  
190 - IotOtaUpgradeTaskDO upgradeTask = BeanUtils.toBean(createReqVO, IotOtaUpgradeTaskDO.class);  
191 - // 初始化的时候,设置设备数量和状态  
192 - upgradeTask.setDeviceCount(Convert.toLong(CollUtil.size(createReqVO.getDeviceIds())))  
193 - .setStatus(IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus());  
194 - // 如果选择全选,则需要查询设备数量  
195 - if (Objects.equals(createReqVO.getScope(), IotOtaUpgradeTaskScopeEnum.ALL.getScope())) {  
196 - // 根据产品ID查询设备数量  
197 - List<IotDeviceDO> deviceList = deviceService.getDeviceListByProductId(Convert.toLong(productId));  
198 - // 设置升级任务的设备数量  
199 - upgradeTask.setDeviceCount((long) deviceList.size());  
200 - upgradeTask.setDeviceIds(  
201 - deviceList.stream().map(IotDeviceDO::getId).collect(Collectors.toList()));  
202 - }  
203 - // 返回初始化后的升级任务对象  
204 - return upgradeTask;  
205 - }  
206 -  
207 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java
@@ -17,6 +17,8 @@ import java.time.LocalDateTime; @@ -17,6 +17,8 @@ import java.time.LocalDateTime;
17 import java.util.*; 17 import java.util.*;
18 18
19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  20 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
20 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS; 22 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS;
21 23
22 /** 24 /**
@@ -99,23 +101,21 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService @@ -99,23 +101,21 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService
99 @Override 101 @Override
100 public Map<String, Integer> getProductCategoryDeviceCountMap() { 102 public Map<String, Integer> getProductCategoryDeviceCountMap() {
101 // 1. 获取所有数据 103 // 1. 获取所有数据
102 - List<IotProductCategoryDO> categoryList = iotProductCategoryMapper.selectList();  
103 - List<IotProductDO> productList = productService.getProductList();  
104 - // TODO @super:不要 list 查询,返回内存,而是查询一个 Map<productId, count> 104 + List<IotProductCategoryDO> categories = iotProductCategoryMapper.selectList();
  105 + List<IotProductDO> products = productService.getProductList();
105 Map<Long, Integer> deviceCountMapByProductId = deviceService.getDeviceCountMapByProductId(); 106 Map<Long, Integer> deviceCountMapByProductId = deviceService.getDeviceCountMapByProductId();
106 107
107 // 2. 统计每个分类下的设备数量 108 // 2. 统计每个分类下的设备数量
108 Map<String, Integer> categoryDeviceCountMap = new HashMap<>(); 109 Map<String, Integer> categoryDeviceCountMap = new HashMap<>();
109 - for (IotProductCategoryDO category : categoryList) {  
110 - categoryDeviceCountMap.put(category.getName(), 0);  
111 - // TODO @super:CollectionUtils.getSumValue(),看看能不能简化下  
112 - // 2.2 找到该分类下的所有产品,累加设备数量  
113 - for (IotProductDO product : productList) {  
114 - if (Objects.equals(product.getCategoryId(), category.getId())) {  
115 - Integer deviceCount = deviceCountMapByProductId.getOrDefault(product.getId(), 0);  
116 - categoryDeviceCountMap.merge(category.getName(), deviceCount, Integer::sum);  
117 - }  
118 - } 110 + for (IotProductCategoryDO category : categories) {
  111 + // 2.1 找到该分类下的所有产品
  112 + List<IotProductDO> categoryProducts = filterList(products,
  113 + product -> Objects.equals(product.getCategoryId(), category.getId()));
  114 + // 2.2 累加设备数量
  115 + Integer totalDeviceCount = getSumValue(categoryProducts,
  116 + product -> deviceCountMapByProductId.getOrDefault(product.getId(), 0),
  117 + Integer::sum, 0);
  118 + categoryDeviceCountMap.put(category.getName(), totalDeviceCount);
119 } 119 }
120 return categoryDeviceCountMap; 120 return categoryDeviceCountMap;
121 } 121 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
@@ -8,6 +8,7 @@ import jakarta.validation.Valid; @@ -8,6 +8,7 @@ import jakarta.validation.Valid;
8 8
9 import javax.annotation.Nullable; 9 import javax.annotation.Nullable;
10 import java.time.LocalDateTime; 10 import java.time.LocalDateTime;
  11 +import java.util.Collection;
11 import java.util.List; 12 import java.util.List;
12 13
13 /** 14 /**
@@ -112,5 +113,11 @@ public interface IotProductService { @@ -112,5 +113,11 @@ public interface IotProductService {
112 */ 113 */
113 Long getProductCount(@Nullable LocalDateTime createTime); 114 Long getProductCount(@Nullable LocalDateTime createTime);
114 115
  116 + /**
  117 + * 批量校验产品存在
  118 + *
  119 + * @param ids 产品编号集合
  120 + */
  121 + void validateProductsExist(Collection<Long> ids);
115 122
116 } 123 }
117 \ No newline at end of file 124 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
1 package cn.iocoder.yudao.module.iot.service.product; 1 package cn.iocoder.yudao.module.iot.service.product;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; 6 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
6 -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;  
7 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;
8 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; 8 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO;
9 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 9 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
10 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; 10 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper;
11 import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; 11 import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;
12 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; 12 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
  13 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
13 import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; 14 import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
14 import com.baomidou.dynamic.datasource.annotation.DSTransactional; 15 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
15 import jakarta.annotation.Resource; 16 import jakarta.annotation.Resource;
16 import org.springframework.cache.annotation.CacheEvict; 17 import org.springframework.cache.annotation.CacheEvict;
17 import org.springframework.cache.annotation.Cacheable; 18 import org.springframework.cache.annotation.Cacheable;
18 -import org.springframework.context.annotation.Lazy;  
19 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
20 import org.springframework.validation.annotation.Validated; 20 import org.springframework.validation.annotation.Validated;
21 21
22 import java.time.LocalDateTime; 22 import java.time.LocalDateTime;
  23 +import java.util.Collection;
23 import java.util.List; 24 import java.util.List;
24 import java.util.Objects; 25 import java.util.Objects;
25 26
@@ -39,18 +40,16 @@ public class IotProductServiceImpl implements IotProductService { @@ -39,18 +40,16 @@ public class IotProductServiceImpl implements IotProductService {
39 private IotProductMapper productMapper; 40 private IotProductMapper productMapper;
40 41
41 @Resource 42 @Resource
42 - @Lazy // 延迟加载,解决循环依赖  
43 private IotDevicePropertyService devicePropertyDataService; 43 private IotDevicePropertyService devicePropertyDataService;
  44 + @Resource
  45 + private IotDeviceService deviceService;
44 46
45 @Override 47 @Override
46 public Long createProduct(IotProductSaveReqVO createReqVO) { 48 public Long createProduct(IotProductSaveReqVO createReqVO) {
47 // 1. 校验 ProductKey 49 // 1. 校验 ProductKey
48 - TenantUtils.executeIgnore(() -> {  
49 - // 为什么忽略租户?避免多个租户之间,productKey 重复,导致 TDengine 设备属性表重复  
50 - if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) {  
51 - throw exception(PRODUCT_KEY_EXISTS);  
52 - }  
53 - }); 50 + if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) {
  51 + throw exception(PRODUCT_KEY_EXISTS);
  52 + }
54 53
55 // 2. 插入 54 // 2. 插入
56 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) 55 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class)
@@ -67,6 +66,7 @@ public class IotProductServiceImpl implements IotProductService { @@ -67,6 +66,7 @@ public class IotProductServiceImpl implements IotProductService {
67 IotProductDO iotProductDO = validateProductExists(updateReqVO.getId()); 66 IotProductDO iotProductDO = validateProductExists(updateReqVO.getId());
68 // 1.2 发布状态不可更新 67 // 1.2 发布状态不可更新
69 validateProductStatus(iotProductDO); 68 validateProductStatus(iotProductDO);
  69 +
70 // 2. 更新 70 // 2. 更新
71 IotProductDO updateObj = BeanUtils.toBean(updateReqVO, IotProductDO.class); 71 IotProductDO updateObj = BeanUtils.toBean(updateReqVO, IotProductDO.class);
72 productMapper.updateById(updateObj); 72 productMapper.updateById(updateObj);
@@ -76,9 +76,14 @@ public class IotProductServiceImpl implements IotProductService { @@ -76,9 +76,14 @@ public class IotProductServiceImpl implements IotProductService {
76 @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id") 76 @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id")
77 public void deleteProduct(Long id) { 77 public void deleteProduct(Long id) {
78 // 1.1 校验存在 78 // 1.1 校验存在
79 - IotProductDO iotProductDO = validateProductExists(id); 79 + IotProductDO product = validateProductExists(id);
80 // 1.2 发布状态不可删除 80 // 1.2 发布状态不可删除
81 - validateProductStatus(iotProductDO); 81 + validateProductStatus(product);
  82 + // 1.3 校验是否有设备
  83 + if (deviceService.getDeviceCountByProductId(id) > 0) {
  84 + throw exception(PRODUCT_DELETE_FAIL_HAS_DEVICE);
  85 + }
  86 +
82 // 2. 删除 87 // 2. 删除
83 productMapper.deleteById(id); 88 productMapper.deleteById(id);
84 } 89 }
@@ -157,4 +162,15 @@ public class IotProductServiceImpl implements IotProductService { @@ -157,4 +162,15 @@ public class IotProductServiceImpl implements IotProductService {
157 return productMapper.selectCountByCreateTime(createTime); 162 return productMapper.selectCountByCreateTime(createTime);
158 } 163 }
159 164
  165 + @Override
  166 + public void validateProductsExist(Collection<Long> ids) {
  167 + if (CollUtil.isEmpty(ids)) {
  168 + return;
  169 + }
  170 + List<IotProductDO> products = productMapper.selectByIds(ids);
  171 + if (products.size() != ids.size()) {
  172 + throw exception(PRODUCT_NOT_EXISTS);
  173 + }
  174 + }
  175 +
160 } 176 }
161 \ No newline at end of file 177 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.iot.service.rule;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO;  
7 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;  
8 -import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataBridgeMapper;  
9 -import jakarta.annotation.Resource;  
10 -import org.springframework.stereotype.Service;  
11 -import org.springframework.validation.annotation.Validated;  
12 -  
13 -import java.util.List;  
14 -  
15 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
16 -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_BRIDGE_NOT_EXISTS;  
17 -  
18 -/**  
19 - * IoT 数据桥梁 Service 实现类  
20 - *  
21 - * @author HUIHUI  
22 - */  
23 -@Service  
24 -@Validated  
25 -public class IotDataBridgeServiceImpl implements IotDataBridgeService {  
26 -  
27 - @Resource  
28 - private IotDataBridgeMapper dataBridgeMapper;  
29 -  
30 - @Override  
31 - public Long createDataBridge(IotDataBridgeSaveReqVO createReqVO) {  
32 - // 插入  
33 - IotDataBridgeDO dataBridge = BeanUtils.toBean(createReqVO, IotDataBridgeDO.class);  
34 - dataBridgeMapper.insert(dataBridge);  
35 - // 返回  
36 - return dataBridge.getId();  
37 - }  
38 -  
39 - @Override  
40 - public void updateDataBridge(IotDataBridgeSaveReqVO updateReqVO) {  
41 - // 校验存在  
42 - validateDataBridgeExists(updateReqVO.getId());  
43 - // 更新  
44 - IotDataBridgeDO updateObj = BeanUtils.toBean(updateReqVO, IotDataBridgeDO.class);  
45 - dataBridgeMapper.updateById(updateObj);  
46 - }  
47 -  
48 - @Override  
49 - public void deleteDataBridge(Long id) {  
50 - // 校验存在  
51 - validateDataBridgeExists(id);  
52 - // 删除  
53 - dataBridgeMapper.deleteById(id);  
54 - }  
55 -  
56 - private void validateDataBridgeExists(Long id) {  
57 - if (dataBridgeMapper.selectById(id) == null) {  
58 - throw exception(DATA_BRIDGE_NOT_EXISTS);  
59 - }  
60 - }  
61 -  
62 - @Override  
63 - public IotDataBridgeDO getDataBridge(Long id) {  
64 - return dataBridgeMapper.selectById(id);  
65 - }  
66 -  
67 - @Override  
68 - public PageResult<IotDataBridgeDO> getDataBridgePage(IotDataBridgePageReqVO pageReqVO) {  
69 - return dataBridgeMapper.selectPage(pageReqVO);  
70 - }  
71 -  
72 - @Override  
73 - public List<IotDataBridgeDO> getDataBridgeList(Integer status) {  
74 - return dataBridgeMapper.selectList(status);  
75 - }  
76 -  
77 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java deleted
1 -package cn.iocoder.yudao.module.iot.service.rule.action;  
2 -  
3 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
4 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;  
5 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;  
6 -import org.springframework.stereotype.Component;  
7 -  
8 -import javax.annotation.Nullable;  
9 -  
10 -/**  
11 - * IoT 告警的 {@link IotRuleSceneAction} 实现类  
12 - *  
13 - * @author 芋道源码  
14 - */  
15 -@Component  
16 -public class IotRuleSceneAlertAction implements IotRuleSceneAction {  
17 -  
18 - @Override  
19 - public void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {  
20 - // TODO @芋艿:待实现  
21 - }  
22 -  
23 - @Override  
24 - public IotRuleSceneActionTypeEnum getType() {  
25 - return IotRuleSceneActionTypeEnum.ALERT;  
26 - }  
27 -  
28 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java deleted
1 -package cn.iocoder.yudao.module.iot.service.rule.action;  
2 -  
3 -import cn.hutool.core.lang.Assert;  
4 -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;  
5 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
6 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;  
7 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;  
8 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;  
9 -import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService;  
10 -import cn.iocoder.yudao.module.iot.service.rule.action.databridge.IotDataBridgeExecute;  
11 -import jakarta.annotation.Resource;  
12 -import lombok.extern.slf4j.Slf4j;  
13 -import org.springframework.stereotype.Component;  
14 -  
15 -import java.util.List;  
16 -  
17 -/**  
18 - * IoT 数据桥梁的 {@link IotRuleSceneAction} 实现类  
19 - *  
20 - * @author 芋道源码  
21 - */  
22 -@Component  
23 -@Slf4j  
24 -public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction {  
25 -  
26 - @Resource  
27 - private IotDataBridgeService dataBridgeService;  
28 - @Resource  
29 - private List<IotDataBridgeExecute<?>> dataBridgeExecutes;  
30 -  
31 - @Override  
32 - public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception {  
33 - // 1.1 如果消息为空,直接返回  
34 - if (message == null) {  
35 - return;  
36 - }  
37 - // 1.2 获得数据桥梁  
38 - Assert.notNull(config.getDataBridgeId(), "数据桥梁编号不能为空");  
39 - IotDataBridgeDO dataBridge = dataBridgeService.getDataBridge(config.getDataBridgeId());  
40 - if (dataBridge == null || dataBridge.getConfig() == null) {  
41 - log.error("[execute][message({}) config({}) 对应的数据桥梁不存在]", message, config);  
42 - return;  
43 - }  
44 - if (CommonStatusEnum.isDisable(dataBridge.getStatus())) {  
45 - log.info("[execute][message({}) config({}) 对应的数据桥梁({}) 状态为禁用]", message, config, dataBridge);  
46 - return;  
47 - }  
48 -  
49 - // 2. 执行数据桥接操作  
50 - for (IotDataBridgeExecute<?> execute : dataBridgeExecutes) {  
51 - execute.execute(message, dataBridge);  
52 - }  
53 - }  
54 -  
55 - @Override  
56 - public IotRuleSceneActionTypeEnum getType() {  
57 - return IotRuleSceneActionTypeEnum.DATA_BRIDGE;  
58 - }  
59 -  
60 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java deleted
1 -package cn.iocoder.yudao.module.iot.service.rule.action.databridge;  
2 -  
3 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
4 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;  
5 -  
6 -/**  
7 - * IoT 数据桥梁的执行器 execute 接口  
8 - *  
9 - * @author HUIHUI  
10 - */  
11 -public interface IotDataBridgeExecute<Config> {  
12 -  
13 - /**  
14 - * 获取数据桥梁类型  
15 - *  
16 - * @return 数据桥梁类型  
17 - */  
18 - Integer getType();  
19 -  
20 - /**  
21 - * 执行数据桥梁操作  
22 - *  
23 - * @param message 设备消息  
24 - * @param dataBridge 数据桥梁  
25 - */  
26 - @SuppressWarnings({"unchecked"})  
27 - default void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) throws Exception {  
28 - // 1.1 校验数据桥梁类型  
29 - if (!getType().equals(dataBridge.getType())) {  
30 - return;  
31 - }  
32 -  
33 - // 1.2 执行对应的数据桥梁发送消息  
34 - execute0(message, (Config) dataBridge.getConfig());  
35 - }  
36 -  
37 - /**  
38 - * 【真正】执行数据桥梁操作  
39 - *  
40 - * @param message 设备消息  
41 - * @param config 桥梁配置  
42 - */  
43 - void execute0(IotDeviceMessage message, Config config) throws Exception;  
44 -  
45 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.data;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleSaveReqVO;
  6 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;
  8 +import jakarta.validation.Valid;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * IoT 数据流转规则 Service 接口
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public interface IotDataRuleService {
  18 +
  19 + /**
  20 + * 创建数据流转规则
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createDataRule(@Valid IotDataRuleSaveReqVO createReqVO);
  26 +
  27 + /**
  28 + * 更新数据流转规则
  29 + *
  30 + * @param updateReqVO 更新信息
  31 + */
  32 + void updateDataRule(@Valid IotDataRuleSaveReqVO updateReqVO);
  33 +
  34 + /**
  35 + * 删除数据流转规则
  36 + *
  37 + * @param id 编号
  38 + */
  39 + void deleteDataRule(Long id);
  40 +
  41 + /**
  42 + * 获得数据流转规则
  43 + *
  44 + * @param id 编号
  45 + * @return 数据流转规则
  46 + */
  47 + IotDataRuleDO getDataRule(Long id);
  48 +
  49 + /**
  50 + * 获得数据流转规则分页
  51 + *
  52 + * @param pageReqVO 分页查询
  53 + * @return 数据流转规则分页
  54 + */
  55 + PageResult<IotDataRuleDO> getDataRulePage(IotDataRulePageReqVO pageReqVO);
  56 +
  57 + /**
  58 + * 根据数据目的编号,获得数据流转规则列表
  59 + *
  60 + * @param sinkId 数据目的编号
  61 + * @return 是否被使用
  62 + */
  63 + List<IotDataRuleDO> getDataRuleListBySinkId(Long sinkId);
  64 +
  65 + /**
  66 + * 执行数据流转规则
  67 + *
  68 + * @param message 消息
  69 + */
  70 + void executeDataRule(IotDeviceMessage message);
  71 +
  72 +}
0 \ No newline at end of file 73 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.data;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  10 +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
  11 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO;
  12 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleSaveReqVO;
  13 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  14 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  15 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  16 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;
  17 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
  18 +import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataRuleMapper;
  19 +import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;
  20 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  21 +import cn.iocoder.yudao.module.iot.service.product.IotProductService;
  22 +import cn.iocoder.yudao.module.iot.service.rule.data.action.IotDataRuleAction;
  23 +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
  24 +import jakarta.annotation.Resource;
  25 +import lombok.extern.slf4j.Slf4j;
  26 +import org.springframework.cache.annotation.CacheEvict;
  27 +import org.springframework.cache.annotation.Cacheable;
  28 +import org.springframework.stereotype.Service;
  29 +import org.springframework.validation.annotation.Validated;
  30 +
  31 +import java.util.*;
  32 +
  33 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  35 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_RULE_NOT_EXISTS;
  36 +
  37 +/**
  38 + * IoT 数据流转规则 Service 实现类
  39 + *
  40 + * @author 芋道源码
  41 + */
  42 +@Service
  43 +@Validated
  44 +@Slf4j
  45 +public class IotDataRuleServiceImpl implements IotDataRuleService {
  46 +
  47 + @Resource
  48 + private IotDataRuleMapper dataRuleMapper;
  49 +
  50 + @Resource
  51 + private IotProductService productService;
  52 + @Resource
  53 + private IotDeviceService deviceService;
  54 + @Resource
  55 + private IotThingModelService thingModelService;
  56 + @Resource
  57 + private IotDataSinkService dataSinkService;
  58 +
  59 + @Resource
  60 + private List<IotDataRuleAction> dataRuleActions;
  61 +
  62 + @Override
  63 + @CacheEvict(value = RedisKeyConstants.DATA_RULE_LIST, allEntries = true)
  64 + public Long createDataRule(IotDataRuleSaveReqVO createReqVO) {
  65 + // 校验数据源配置和数据目的
  66 + validateDataRuleConfig(createReqVO);
  67 + // 新增
  68 + IotDataRuleDO dataRule = BeanUtils.toBean(createReqVO, IotDataRuleDO.class);
  69 + dataRuleMapper.insert(dataRule);
  70 + return dataRule.getId();
  71 + }
  72 +
  73 + @Override
  74 + @CacheEvict(value = RedisKeyConstants.DATA_RULE_LIST, allEntries = true)
  75 + public void updateDataRule(IotDataRuleSaveReqVO updateReqVO) {
  76 + // 校验存在
  77 + validateDataRuleExists(updateReqVO.getId());
  78 + // 校验数据源配置和数据目的
  79 + validateDataRuleConfig(updateReqVO);
  80 +
  81 + // 更新
  82 + IotDataRuleDO updateObj = BeanUtils.toBean(updateReqVO, IotDataRuleDO.class);
  83 + dataRuleMapper.updateById(updateObj);
  84 + }
  85 +
  86 + @Override
  87 + @CacheEvict(value = RedisKeyConstants.DATA_RULE_LIST, allEntries = true)
  88 + public void deleteDataRule(Long id) {
  89 + // 校验存在
  90 + validateDataRuleExists(id);
  91 + // 删除
  92 + dataRuleMapper.deleteById(id);
  93 + }
  94 +
  95 + private void validateDataRuleExists(Long id) {
  96 + if (dataRuleMapper.selectById(id) == null) {
  97 + throw exception(DATA_RULE_NOT_EXISTS);
  98 + }
  99 + }
  100 +
  101 + /**
  102 + * 校验数据流转规则配置
  103 + *
  104 + * @param reqVO 数据流转规则保存请求VO
  105 + */
  106 + private void validateDataRuleConfig(IotDataRuleSaveReqVO reqVO) {
  107 + // 1. 校验数据源配置
  108 + validateSourceConfigs(reqVO.getSourceConfigs());
  109 + // 2. 校验数据目的
  110 + dataSinkService.validateDataSinksExist(reqVO.getSinkIds());
  111 + }
  112 +
  113 + /**
  114 + * 校验数据源配置
  115 + *
  116 + * @param sourceConfigs 数据源配置列表
  117 + */
  118 + private void validateSourceConfigs(List<IotDataRuleDO.SourceConfig> sourceConfigs) {
  119 + // 1. 校验产品
  120 + productService.validateProductsExist(
  121 + convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getProductId));
  122 +
  123 + // 2. 校验设备
  124 + deviceService.validateDeviceListExists(convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getDeviceId,
  125 + config -> ObjUtil.notEqual(config.getDeviceId(), IotDeviceDO.DEVICE_ID_ALL)));
  126 +
  127 + // 3. 校验物模型存在
  128 + validateThingModelsExist(sourceConfigs);
  129 + }
  130 +
  131 + /**
  132 + * 校验物模型存在
  133 + *
  134 + * @param sourceConfigs 数据源配置列表
  135 + */
  136 + private void validateThingModelsExist(List<IotDataRuleDO.SourceConfig> sourceConfigs) {
  137 + Map<Long, Set<String>> productIdIdentifiers = new HashMap<>();
  138 + for (IotDataRuleDO.SourceConfig config : sourceConfigs) {
  139 + if (StrUtil.isEmpty(config.getIdentifier())) {
  140 + continue;
  141 + }
  142 + productIdIdentifiers.computeIfAbsent(config.getProductId(),
  143 + productId -> new HashSet<>()).add(config.getIdentifier());
  144 + }
  145 + for (Map.Entry<Long, Set<String>> entry : productIdIdentifiers.entrySet()) {
  146 + thingModelService.validateThingModelListExists(entry.getKey(), entry.getValue());
  147 + }
  148 + }
  149 +
  150 + @Override
  151 + public IotDataRuleDO getDataRule(Long id) {
  152 + return dataRuleMapper.selectById(id);
  153 + }
  154 +
  155 + @Override
  156 + public PageResult<IotDataRuleDO> getDataRulePage(IotDataRulePageReqVO pageReqVO) {
  157 + return dataRuleMapper.selectPage(pageReqVO);
  158 + }
  159 +
  160 + @Override
  161 + public List<IotDataRuleDO> getDataRuleListBySinkId(Long sinkId) {
  162 + return dataRuleMapper.selectListBySinkId(sinkId);
  163 + }
  164 +
  165 + @Cacheable(value = RedisKeyConstants.DATA_RULE_LIST,
  166 + key = "#deviceId + '_' + #method + '_' + (#identifier ?: '')")
  167 + public List<IotDataRuleDO> getDataRuleListByConditionFromCache(Long deviceId, String method, String identifier) {
  168 + // 1. 查询所有开启的数据流转规则
  169 + List<IotDataRuleDO> rules = dataRuleMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
  170 + // 2. 内存里过滤匹配的规则
  171 + List<IotDataRuleDO> matchedRules = new ArrayList<>();
  172 + for (IotDataRuleDO rule : rules) {
  173 + IotDataRuleDO.SourceConfig found = CollUtil.findOne(rule.getSourceConfigs(),
  174 + config -> ObjectUtils.equalsAny(config.getDeviceId(), deviceId, IotDeviceDO.DEVICE_ID_ALL)
  175 + && Objects.equals(config.getMethod(), method)
  176 + && (StrUtil.isEmpty(config.getIdentifier()) || ObjUtil.equal(config.getIdentifier(), identifier)));
  177 + if (found != null) {
  178 + matchedRules.add(new IotDataRuleDO().setId(rule.getId()).setSinkIds(rule.getSinkIds()));
  179 + }
  180 + }
  181 + return matchedRules;
  182 + }
  183 +
  184 + @Override
  185 + public void executeDataRule(IotDeviceMessage message) {
  186 + try {
  187 + // 1. 获取匹配的数据流转规则
  188 + Long deviceId = message.getDeviceId();
  189 + String method = message.getMethod();
  190 + String identifier = IotDeviceMessageUtils.getIdentifier(message);
  191 + List<IotDataRuleDO> rules = getSelf().getDataRuleListByConditionFromCache(deviceId, method, identifier);
  192 + if (CollUtil.isEmpty(rules)) {
  193 + log.debug("[executeDataRule][设备({}) 方法({}) 标识符({}) 没有匹配的数据流转规则]",
  194 + deviceId, method, identifier);
  195 + return;
  196 + }
  197 + log.info("[executeDataRule][设备({}) 方法({}) 标识符({}) 匹配到 {} 条数据流转规则]",
  198 + deviceId, method, identifier, rules.size());
  199 +
  200 + // 2. 遍历规则,执行数据流转
  201 + rules.forEach(rule -> executeDataRule(message, rule));
  202 + } catch (Exception e) {
  203 + log.error("[executeDataRule][消息({}) 执行数据流转规则异常]", message, e);
  204 + }
  205 + }
  206 +
  207 + /**
  208 + * 为指定规则的所有数据目的执行数据流转
  209 + *
  210 + * @param message 设备消息
  211 + * @param rule 数据流转规则
  212 + */
  213 + private void executeDataRule(IotDeviceMessage message, IotDataRuleDO rule) {
  214 + rule.getSinkIds().forEach(sinkId -> {
  215 + try {
  216 + // 获取数据目的配置
  217 + IotDataSinkDO dataSink = dataSinkService.getDataSinkFromCache(sinkId);
  218 + if (dataSink == null) {
  219 + log.error("[executeDataRule][规则({}) 对应的数据目的({}) 不存在]", rule.getId(), sinkId);
  220 + return;
  221 + }
  222 + if (CommonStatusEnum.isDisable(dataSink.getStatus())) {
  223 + log.info("[executeDataRule][规则({}) 对应的数据目的({}) 状态为禁用]", rule.getId(), sinkId);
  224 + return;
  225 + }
  226 +
  227 + // 执行数据桥接操作
  228 + executeDataRuleAction(message, dataSink);
  229 + } catch (Exception e) {
  230 + log.error("[executeDataRule][规则({}) 数据目的({}) 执行异常]", rule.getId(), sinkId, e);
  231 + }
  232 + });
  233 + }
  234 +
  235 + /**
  236 + * 执行数据流转操作
  237 + *
  238 + * @param message 设备消息
  239 + * @param dataSink 数据目的
  240 + */
  241 + private void executeDataRuleAction(IotDeviceMessage message, IotDataSinkDO dataSink) {
  242 + dataRuleActions.forEach(action -> {
  243 + if (ObjUtil.notEqual(action.getType(), dataSink.getType())) {
  244 + return;
  245 + }
  246 + try {
  247 + action.execute(message, dataSink);
  248 + log.info("[executeDataRuleAction][消息({}) 数据目的({}) 执行成功]", message.getId(), dataSink.getId());
  249 + } catch (Exception e) {
  250 + log.error("[executeDataRuleAction][消息({}) 数据目的({}) 执行异常]", message.getId(), dataSink.getId(), e);
  251 + }
  252 + });
  253 + }
  254 +
  255 + private IotDataRuleServiceImpl getSelf() {
  256 + return SpringUtils.getBean(IotDataRuleServiceImpl.class);
  257 + }
  258 +
  259 +}
0 \ No newline at end of file 260 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java
1 -package cn.iocoder.yudao.module.iot.service.rule; 1 +package cn.iocoder.yudao.module.iot.service.rule.data;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO;  
6 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; 4 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
7 import jakarta.validation.Valid; 7 import jakarta.validation.Valid;
8 8
  9 +import java.util.Collection;
9 import java.util.List; 10 import java.util.List;
10 11
11 /** 12 /**
12 - * IoT 数据桥梁 Service 接口 13 + * IoT 数据流转目的 Service 接口
13 * 14 *
14 * @author HUIHUI 15 * @author HUIHUI
15 */ 16 */
16 -public interface IotDataBridgeService { 17 +public interface IotDataSinkService {
17 18
18 /** 19 /**
19 - * 创建数据桥梁 20 + * 创建数据流转目的
20 * 21 *
21 * @param createReqVO 创建信息 22 * @param createReqVO 创建信息
22 * @return 编号 23 * @return 编号
23 */ 24 */
24 - Long createDataBridge(@Valid IotDataBridgeSaveReqVO createReqVO); 25 + Long createDataSink(@Valid IotDataSinkSaveReqVO createReqVO);
25 26
26 /** 27 /**
27 - * 更新数据桥梁 28 + * 更新数据流转目的
28 * 29 *
29 * @param updateReqVO 更新信息 30 * @param updateReqVO 更新信息
30 */ 31 */
31 - void updateDataBridge(@Valid IotDataBridgeSaveReqVO updateReqVO); 32 + void updateDataSink(@Valid IotDataSinkSaveReqVO updateReqVO);
32 33
33 /** 34 /**
34 - * 删除数据桥梁 35 + * 删除数据流转目的
35 * 36 *
36 * @param id 编号 37 * @param id 编号
37 */ 38 */
38 - void deleteDataBridge(Long id); 39 + void deleteDataSink(Long id);
39 40
40 /** 41 /**
41 - * 获得数据桥梁 42 + * 获得数据流转目的
42 * 43 *
43 * @param id 编号 44 * @param id 编号
44 - * @return 数据桥梁 45 + * @return 数据流转目的
45 */ 46 */
46 - IotDataBridgeDO getDataBridge(Long id); 47 + IotDataSinkDO getDataSink(Long id);
47 48
48 /** 49 /**
49 - * 获得数据桥梁分页 50 + * 从缓存中获得数据流转目的
  51 + *
  52 + * @param id 编号
  53 + * @return 数据流转目的
  54 + */
  55 + IotDataSinkDO getDataSinkFromCache(Long id);
  56 +
  57 + /**
  58 + * 获得数据流转目的分页
50 * 59 *
51 * @param pageReqVO 分页查询 60 * @param pageReqVO 分页查询
52 - * @return 数据桥梁分页 61 + * @return 数据流转目的分页
53 */ 62 */
54 - PageResult<IotDataBridgeDO> getDataBridgePage(IotDataBridgePageReqVO pageReqVO); 63 + PageResult<IotDataSinkDO> getDataSinkPage(IotDataSinkPageReqVO pageReqVO);
55 64
56 /** 65 /**
57 - * 获取数据桥梁列表 66 + * 获取数据流转目的列表
58 * 67 *
59 * @param status 状态,如果为空,则不进行筛选 68 * @param status 状态,如果为空,则不进行筛选
60 - * @return 数据桥梁列表 69 + * @return 数据流转目的列表
  70 + */
  71 + List<IotDataSinkDO> getDataSinkListByStatus(Integer status);
  72 +
  73 + /**
  74 + * 批量校验数据目的存在
  75 + *
  76 + * @param ids 数据目的编号集合
61 */ 77 */
62 - List<IotDataBridgeDO> getDataBridgeList(Integer status); 78 + void validateDataSinksExist(Collection<Long> ids);
63 79
64 } 80 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.data;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
  9 +import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataSinkMapper;
  10 +import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;
  11 +import jakarta.annotation.Resource;
  12 +import org.springframework.cache.annotation.Cacheable;
  13 +import org.springframework.context.annotation.Lazy;
  14 +import org.springframework.stereotype.Service;
  15 +import org.springframework.validation.annotation.Validated;
  16 +
  17 +import java.util.Collection;
  18 +import java.util.List;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  21 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_SINK_DELETE_FAIL_USED_BY_RULE;
  22 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_SINK_NOT_EXISTS;
  23 +
  24 +/**
  25 + * IoT 数据流转目的 Service 实现类
  26 + *
  27 + * @author HUIHUI
  28 + */
  29 +@Service
  30 +@Validated
  31 +public class IotDataSinkServiceImpl implements IotDataSinkService {
  32 +
  33 + @Resource
  34 + private IotDataSinkMapper dataSinkMapper;
  35 +
  36 + @Resource
  37 + @Lazy // 延迟,避免循环依赖报错
  38 + private IotDataRuleService dataRuleService;
  39 +
  40 + @Override
  41 + public Long createDataSink(IotDataSinkSaveReqVO createReqVO) {
  42 + IotDataSinkDO dataBridge = BeanUtils.toBean(createReqVO, IotDataSinkDO.class);
  43 + dataSinkMapper.insert(dataBridge);
  44 + return dataBridge.getId();
  45 + }
  46 +
  47 + @Override
  48 + public void updateDataSink(IotDataSinkSaveReqVO updateReqVO) {
  49 + // 校验存在
  50 + validateDataBridgeExists(updateReqVO.getId());
  51 + // 更新
  52 + IotDataSinkDO updateObj = BeanUtils.toBean(updateReqVO, IotDataSinkDO.class);
  53 + dataSinkMapper.updateById(updateObj);
  54 + }
  55 +
  56 + @Override
  57 + public void deleteDataSink(Long id) {
  58 + // 校验存在
  59 + validateDataBridgeExists(id);
  60 + // 校验是否被数据流转规则使用
  61 + if (CollUtil.isNotEmpty(dataRuleService.getDataRuleListBySinkId(id))) {
  62 + throw exception(DATA_SINK_DELETE_FAIL_USED_BY_RULE);
  63 + }
  64 + // 删除
  65 + dataSinkMapper.deleteById(id);
  66 + }
  67 +
  68 + private void validateDataBridgeExists(Long id) {
  69 + if (dataSinkMapper.selectById(id) == null) {
  70 + throw exception(DATA_SINK_NOT_EXISTS);
  71 + }
  72 + }
  73 +
  74 + @Override
  75 + public IotDataSinkDO getDataSink(Long id) {
  76 + return dataSinkMapper.selectById(id);
  77 + }
  78 +
  79 + @Override
  80 + @Cacheable(value = RedisKeyConstants.DATA_SINK, key = "#id")
  81 + public IotDataSinkDO getDataSinkFromCache(Long id) {
  82 + return dataSinkMapper.selectById(id);
  83 + }
  84 +
  85 + @Override
  86 + public PageResult<IotDataSinkDO> getDataSinkPage(IotDataSinkPageReqVO pageReqVO) {
  87 + return dataSinkMapper.selectPage(pageReqVO);
  88 + }
  89 +
  90 + @Override
  91 + public List<IotDataSinkDO> getDataSinkListByStatus(Integer status) {
  92 + return dataSinkMapper.selectListByStatus(status);
  93 + }
  94 +
  95 + @Override
  96 + public void validateDataSinksExist(Collection<Long> ids) {
  97 + if (CollUtil.isEmpty(ids)) {
  98 + return;
  99 + }
  100 + List<IotDataSinkDO> sinks = dataSinkMapper.selectByIds(ids);
  101 + if (sinks.size() != ids.size()) {
  102 + throw exception(DATA_SINK_NOT_EXISTS);
  103 + }
  104 + }
  105 +
  106 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleAction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.data.action;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
  5 +
  6 +/**
  7 + * IoT 数据流转目的的执行器 action 接口
  8 + *
  9 + * @author HUIHUI
  10 + */
  11 +public interface IotDataRuleAction {
  12 +
  13 + /**
  14 + * 获取数据流转目的类型
  15 + *
  16 + * @return 数据流转目的类型
  17 + */
  18 + Integer getType();
  19 +
  20 + /**
  21 + * 执行数据流转目的操作
  22 + *
  23 + * @param message 设备消息
  24 + * @param dataSink 数据流转目的
  25 + */
  26 + void execute(IotDeviceMessage message, IotDataSinkDO dataSink);
  27 +
  28 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleCacheableAction.java
1 -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; 1 +package cn.iocoder.yudao.module.iot.service.rule.data.action;
2 2
  3 +import cn.hutool.core.lang.Assert;
3 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; 6 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
6 import com.google.common.cache.CacheBuilder; 7 import com.google.common.cache.CacheBuilder;
7 import com.google.common.cache.CacheLoader; 8 import com.google.common.cache.CacheLoader;
8 import com.google.common.cache.LoadingCache; 9 import com.google.common.cache.LoadingCache;
@@ -17,14 +18,14 @@ import java.time.Duration; @@ -17,14 +18,14 @@ import java.time.Duration;
17 // TODO @芋艿:websocket 18 // TODO @芋艿:websocket
18 19
19 /** 20 /**
20 - * 带缓存功能的数据桥梁执行器抽象类 21 + * 可缓存的 {@link IotDataRuleAction} 抽象实现
21 * 22 *
22 * 该类提供了一个通用的缓存机制,用于管理各类数据桥接的生产者(Producer)实例。 23 * 该类提供了一个通用的缓存机制,用于管理各类数据桥接的生产者(Producer)实例。
23 * 24 *
24 * 主要特点: 25 * 主要特点:
25 * - 基于Guava Cache实现高效的生产者实例缓存管理 26 * - 基于Guava Cache实现高效的生产者实例缓存管理
26 * - 自动处理生产者的生命周期(创建、获取、关闭) 27 * - 自动处理生产者的生命周期(创建、获取、关闭)
27 - * - 支持30分钟未访问自动过期清理机制 28 + * - 支持 30 分钟未访问自动过期清理机制
28 * - 异常处理与日志记录,便于问题排查 29 * - 异常处理与日志记录,便于问题排查
29 * 30 *
30 * 子类需要实现: 31 * 子类需要实现:
@@ -36,7 +37,7 @@ import java.time.Duration; @@ -36,7 +37,7 @@ import java.time.Duration;
36 * @author HUIHUI 37 * @author HUIHUI
37 */ 38 */
38 @Slf4j 39 @Slf4j
39 -public abstract class AbstractCacheableDataBridgeExecute<Config, Producer> implements IotDataBridgeExecute<Config> { 40 +public abstract class IotDataRuleCacheableAction<Config, Producer> implements IotDataRuleAction {
40 41
41 /** 42 /**
42 * Producer 缓存 43 * Producer 缓存
@@ -45,10 +46,6 @@ public abstract class AbstractCacheableDataBridgeExecute&lt;Config, Producer&gt; imple @@ -45,10 +46,6 @@ public abstract class AbstractCacheableDataBridgeExecute&lt;Config, Producer&gt; imple
45 .expireAfterAccess(Duration.ofMinutes(30)) // 30 分钟未访问就提前过期 46 .expireAfterAccess(Duration.ofMinutes(30)) // 30 分钟未访问就提前过期
46 .removalListener((RemovalListener<Config, Producer>) notification -> { 47 .removalListener((RemovalListener<Config, Producer>) notification -> {
47 Producer producer = notification.getValue(); 48 Producer producer = notification.getValue();
48 - if (producer == null) {  
49 - return;  
50 - }  
51 -  
52 try { 49 try {
53 closeProducer(producer); 50 closeProducer(producer);
54 log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]", notification.getKey()); 51 log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]", notification.getKey());
@@ -100,15 +97,21 @@ public abstract class AbstractCacheableDataBridgeExecute&lt;Config, Producer&gt; imple @@ -100,15 +97,21 @@ public abstract class AbstractCacheableDataBridgeExecute&lt;Config, Producer&gt; imple
100 97
101 @Override 98 @Override
102 @SuppressWarnings({"unchecked"}) 99 @SuppressWarnings({"unchecked"})
103 - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {  
104 - if (ObjUtil.notEqual(dataBridge.getType(), getType())) {  
105 - return;  
106 - } 100 + public void execute(IotDeviceMessage message, IotDataSinkDO dataSink) {
  101 + Assert.isTrue(ObjUtil.equal(dataSink.getType(), getType()), "类型({})不匹配", dataSink.getType());
107 try { 102 try {
108 - execute0(message, (Config) dataBridge.getConfig()); 103 + execute(message, (Config) dataSink.getConfig());
109 } catch (Exception e) { 104 } catch (Exception e) {
110 - log.error("[execute][桥梁配置 config({}) 对应的 message({}) 发送异常]", dataBridge.getConfig(), message, e); 105 + log.error("[execute][桥梁配置 config({}) 对应的 message({}) 发送异常]", dataSink.getConfig(), message, e);
111 } 106 }
112 } 107 }
113 108
  109 + /**
  110 + * 执行数据流转
  111 + *
  112 + * @param message 设备消息
  113 + * @param config 配置信息
  114 + */
  115 + protected abstract void execute(IotDeviceMessage message, Config config) throws Exception;
  116 +
114 } 117 }
115 \ No newline at end of file 118 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataSinkAction.java
1 -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; 1 +package cn.iocoder.yudao.module.iot.service.rule.data.action;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 import cn.iocoder.yudao.framework.common.util.http.HttpUtils; 5 import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeHttpConfig;  
7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; 8 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkHttpConfig;
  10 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
9 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
10 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
11 import org.springframework.http.*; 13 import org.springframework.http.*;
@@ -16,26 +18,30 @@ import org.springframework.web.util.UriComponentsBuilder; @@ -16,26 +18,30 @@ import org.springframework.web.util.UriComponentsBuilder;
16 import java.util.HashMap; 18 import java.util.HashMap;
17 import java.util.Map; 19 import java.util.Map;
18 20
  21 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
  22 +
19 /** 23 /**
20 - * Http 的 {@link IotDataBridgeExecute} 实现类 24 + * HTTP 的 {@link IotDataRuleAction} 实现类
21 * 25 *
22 * @author HUIHUI 26 * @author HUIHUI
23 */ 27 */
24 @Component 28 @Component
25 @Slf4j 29 @Slf4j
26 -public class IotHttpDataBridgeExecute implements IotDataBridgeExecute<IotDataBridgeHttpConfig> { 30 +public class IotHttpDataSinkAction implements IotDataRuleAction {
27 31
28 @Resource 32 @Resource
29 private RestTemplate restTemplate; 33 private RestTemplate restTemplate;
30 34
31 @Override 35 @Override
32 public Integer getType() { 36 public Integer getType() {
33 - return IotDataBridgeTypeEnum.HTTP.getType(); 37 + return IotDataSinkTypeEnum.HTTP.getType();
34 } 38 }
35 39
36 @Override 40 @Override
37 - @SuppressWarnings({"unchecked", "deprecation"})  
38 - public void execute0(IotDeviceMessage message, IotDataBridgeHttpConfig config) { 41 + @SuppressWarnings("unchecked")
  42 + public void execute(IotDeviceMessage message, IotDataSinkDO dataSink) {
  43 + IotDataSinkHttpConfig config = (IotDataSinkHttpConfig) dataSink.getConfig();
  44 + Assert.notNull(config, "配置({})不能为空", dataSink.getId());
39 String url = null; 45 String url = null;
40 HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase()); 46 HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase());
41 HttpEntity<String> requestEntity = null; 47 HttpEntity<String> requestEntity = null;
@@ -46,8 +52,7 @@ public class IotHttpDataBridgeExecute implements IotDataBridgeExecute&lt;IotDataBri @@ -46,8 +52,7 @@ public class IotHttpDataBridgeExecute implements IotDataBridgeExecute&lt;IotDataBri
46 if (CollUtil.isNotEmpty(config.getHeaders())) { 52 if (CollUtil.isNotEmpty(config.getHeaders())) {
47 config.getHeaders().putAll(config.getHeaders()); 53 config.getHeaders().putAll(config.getHeaders());
48 } 54 }
49 - // TODO @puhui999:@yunai:可能需要通过设备查询到租户,然后 set  
50 -// headers.add(HEADER_TENANT_ID, message.getTenantId().toString()); 55 + headers.add(HEADER_TENANT_ID, message.getTenantId().toString());
51 // 1.2 构建 URL 56 // 1.2 构建 URL
52 UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(config.getUrl()); 57 UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(config.getUrl());
53 if (CollUtil.isNotEmpty(config.getQuery())) { 58 if (CollUtil.isNotEmpty(config.getQuery())) {
@@ -69,18 +74,17 @@ public class IotHttpDataBridgeExecute implements IotDataBridgeExecute&lt;IotDataBri @@ -69,18 +74,17 @@ public class IotHttpDataBridgeExecute implements IotDataBridgeExecute&lt;IotDataBri
69 requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers); 74 requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers);
70 } 75 }
71 76
72 - // 2.1 发送请求 77 + // 2. 发送请求
73 responseEntity = restTemplate.exchange(url, method, requestEntity, String.class); 78 responseEntity = restTemplate.exchange(url, method, requestEntity, String.class);
74 - // 2.2 记录日志  
75 if (responseEntity.getStatusCode().is2xxSuccessful()) { 79 if (responseEntity.getStatusCode().is2xxSuccessful()) {
76 - log.info("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求成功({})]", 80 + log.info("[execute][message({}) config({}) url({}) method({}) requestEntity({}) 请求成功({})]",
77 message, config, url, method, requestEntity, responseEntity); 81 message, config, url, method, requestEntity, responseEntity);
78 } else { 82 } else {
79 - log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求失败({})]", 83 + log.error("[execute][message({}) config({}) url({}) method({}) requestEntity({}) 请求失败({})]",
80 message, config, url, method, requestEntity, responseEntity); 84 message, config, url, method, requestEntity, responseEntity);
81 } 85 }
82 } catch (Exception e) { 86 } catch (Exception e) {
83 - log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求异常({})]", 87 + log.error("[execute][message({}) config({}) url({}) method({}) requestEntity({}) 请求异常({})]",
84 message, config, url, method, requestEntity, responseEntity, e); 88 message, config, url, method, requestEntity, responseEntity, e);
85 } 89 }
86 } 90 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotKafkaDataRuleAction.java
1 -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; 1 +package cn.iocoder.yudao.module.iot.service.rule.data.action;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeKafkaMQConfig; 3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkKafkaConfig;
4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; 6 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
6 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
7 import org.apache.kafka.clients.producer.ProducerConfig; 8 import org.apache.kafka.clients.producer.ProducerConfig;
8 import org.apache.kafka.common.serialization.StringSerializer; 9 import org.apache.kafka.common.serialization.StringSerializer;
9 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 10 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
10 import org.springframework.kafka.core.DefaultKafkaProducerFactory; 11 import org.springframework.kafka.core.DefaultKafkaProducerFactory;
11 import org.springframework.kafka.core.KafkaTemplate; 12 import org.springframework.kafka.core.KafkaTemplate;
  13 +import org.springframework.kafka.support.SendResult;
12 import org.springframework.stereotype.Component; 14 import org.springframework.stereotype.Component;
13 15
14 import java.time.Duration; 16 import java.time.Duration;
@@ -17,36 +19,50 @@ import java.util.Map; @@ -17,36 +19,50 @@ import java.util.Map;
17 import java.util.concurrent.TimeUnit; 19 import java.util.concurrent.TimeUnit;
18 20
19 /** 21 /**
20 - * Kafka 的 {@link IotDataBridgeExecute} 实现类 22 + * Kafka 的 {@link IotDataRuleAction} 实现类
21 * 23 *
22 * @author HUIHUI 24 * @author HUIHUI
23 */ 25 */
24 @ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate") 26 @ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate")
25 @Component 27 @Component
26 @Slf4j 28 @Slf4j
27 -public class IotKafkaMQDataBridgeExecute extends  
28 - AbstractCacheableDataBridgeExecute<IotDataBridgeKafkaMQConfig, KafkaTemplate<String, String>> { 29 +public class IotKafkaDataRuleAction extends
  30 + IotDataRuleCacheableAction<IotDataSinkKafkaConfig, KafkaTemplate<String, String>> {
29 31
30 - private static final Duration SEND_TIMEOUT = Duration.ofMillis(10000); // 10 秒超时时间 32 + private static final Duration SEND_TIMEOUT = Duration.ofSeconds(10);
31 33
32 @Override 34 @Override
33 public Integer getType() { 35 public Integer getType() {
34 - return IotDataBridgeTypeEnum.KAFKA.getType(); 36 + return IotDataSinkTypeEnum.KAFKA.getType();
35 } 37 }
36 38
37 @Override 39 @Override
38 - public void execute0(IotDeviceMessage message, IotDataBridgeKafkaMQConfig config) throws Exception {  
39 - // 1. 获取或创建 KafkaTemplate  
40 - KafkaTemplate<String, String> kafkaTemplate = getProducer(config); 40 + public void execute(IotDeviceMessage message, IotDataSinkKafkaConfig config) throws Exception {
  41 + try {
  42 + // 1. 获取或创建 KafkaTemplate
  43 + KafkaTemplate<String, String> kafkaTemplate = getProducer(config);
41 44
42 - // 2. 发送消息并等待结果  
43 - kafkaTemplate.send(config.getTopic(), message.toString())  
44 - .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS); // 添加超时等待  
45 - log.info("[execute0][message({}) 发送成功]", message); 45 + // 2. 发送消息并等待结果
  46 + SendResult<String, String> sendResult = kafkaTemplate.send(config.getTopic(), JsonUtils.toJsonString(message))
  47 + .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS);
  48 + // 3. 处理发送结果
  49 + if (sendResult != null && sendResult.getRecordMetadata() != null) {
  50 + log.info("[execute][message({}) config({}) 发送成功,结果: partition={}, offset={}, timestamp={}]",
  51 + message, config,
  52 + sendResult.getRecordMetadata().partition(),
  53 + sendResult.getRecordMetadata().offset(),
  54 + sendResult.getRecordMetadata().timestamp());
  55 + } else {
  56 + log.warn("[execute][message({}) config({}) 发送结果为空]", message, config);
  57 + }
  58 + } catch (Exception e) {
  59 + log.error("[execute][message({}) config({}) 发送失败]", message, config, e);
  60 + throw e;
  61 + }
46 } 62 }
47 63
48 @Override 64 @Override
49 - protected KafkaTemplate<String, String> initProducer(IotDataBridgeKafkaMQConfig config) { 65 + protected KafkaTemplate<String, String> initProducer(IotDataSinkKafkaConfig config) {
50 // 1.1 构建生产者配置 66 // 1.1 构建生产者配置
51 Map<String, Object> props = new HashMap<>(); 67 Map<String, Object> props = new HashMap<>();
52 props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers()); 68 props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers());
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataRuleAction.java
1 -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; 1 +package cn.iocoder.yudao.module.iot.service.rule.data.action;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRabbitMQConfig; 3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; 5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRabbitMQConfig;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
6 import com.rabbitmq.client.Channel; 7 import com.rabbitmq.client.Channel;
7 import com.rabbitmq.client.Connection; 8 import com.rabbitmq.client.Connection;
8 import com.rabbitmq.client.ConnectionFactory; 9 import com.rabbitmq.client.ConnectionFactory;
@@ -10,44 +11,45 @@ import lombok.extern.slf4j.Slf4j; @@ -10,44 +11,45 @@ import lombok.extern.slf4j.Slf4j;
10 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 11 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
11 import org.springframework.stereotype.Component; 12 import org.springframework.stereotype.Component;
12 13
13 -import java.nio.charset.StandardCharsets;  
14 -  
15 /** 14 /**
16 - * RabbitMQ 的 {@link IotDataBridgeExecute} 实现类 15 + * RabbitMQ 的 {@link IotDataRuleAction} 实现类
17 * 16 *
18 * @author HUIHUI 17 * @author HUIHUI
19 */ 18 */
20 @ConditionalOnClass(name = "com.rabbitmq.client.Channel") 19 @ConditionalOnClass(name = "com.rabbitmq.client.Channel")
21 @Component 20 @Component
22 @Slf4j 21 @Slf4j
23 -public class IotRabbitMQDataBridgeExecute extends  
24 - AbstractCacheableDataBridgeExecute<IotDataBridgeRabbitMQConfig, Channel> {  
25 - 22 +public class IotRabbitMQDataRuleAction
  23 + extends IotDataRuleCacheableAction<IotDataSinkRabbitMQConfig, Channel> {
26 24
27 @Override 25 @Override
28 public Integer getType() { 26 public Integer getType() {
29 - return IotDataBridgeTypeEnum.RABBITMQ.getType(); 27 + return IotDataSinkTypeEnum.RABBITMQ.getType();
30 } 28 }
31 29
32 @Override 30 @Override
33 - public void execute0(IotDeviceMessage message, IotDataBridgeRabbitMQConfig config) throws Exception {  
34 - // 1. 获取或创建 Channel  
35 - Channel channel = getProducer(config);  
36 -  
37 - // 2.1 声明交换机、队列和绑定关系  
38 - channel.exchangeDeclare(config.getExchange(), "direct", true);  
39 - channel.queueDeclare(config.getQueue(), true, false, false, null);  
40 - channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey()); 31 + public void execute(IotDeviceMessage message, IotDataSinkRabbitMQConfig config) throws Exception {
  32 + try {
  33 + // 1.1 获取或创建 Channel
  34 + Channel channel = getProducer(config);
  35 + // 1.2 声明交换机、队列和绑定关系
  36 + channel.exchangeDeclare(config.getExchange(), "direct", true);
  37 + channel.queueDeclare(config.getQueue(), true, false, false, null);
  38 + channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey());
41 39
42 - // 2.2 发送消息  
43 - channel.basicPublish(config.getExchange(), config.getRoutingKey(), null,  
44 - message.toString().getBytes(StandardCharsets.UTF_8));  
45 - log.info("[executeRabbitMQ][message({}) config({}) 发送成功]", message, config); 40 + // 2. 发送消息
  41 + channel.basicPublish(config.getExchange(), config.getRoutingKey(), null,
  42 + JsonUtils.toJsonByte(message));
  43 + log.info("[execute][message({}) config({}) 发送成功]", message, config);
  44 + } catch (Exception e) {
  45 + log.error("[execute][message({}) config({}) 发送失败]", message, config, e);
  46 + throw e;
  47 + }
46 } 48 }
47 49
48 @Override 50 @Override
49 @SuppressWarnings("resource") 51 @SuppressWarnings("resource")
50 - protected Channel initProducer(IotDataBridgeRabbitMQConfig config) throws Exception { 52 + protected Channel initProducer(IotDataSinkRabbitMQConfig config) throws Exception {
51 // 1. 创建连接工厂 53 // 1. 创建连接工厂
52 ConnectionFactory factory = new ConnectionFactory(); 54 ConnectionFactory factory = new ConnectionFactory();
53 factory.setHost(config.getHost()); 55 factory.setHost(config.getHost());
@@ -55,10 +57,8 @@ public class IotRabbitMQDataBridgeExecute extends @@ -55,10 +57,8 @@ public class IotRabbitMQDataBridgeExecute extends
55 factory.setVirtualHost(config.getVirtualHost()); 57 factory.setVirtualHost(config.getVirtualHost());
56 factory.setUsername(config.getUsername()); 58 factory.setUsername(config.getUsername());
57 factory.setPassword(config.getPassword()); 59 factory.setPassword(config.getPassword());
58 -  
59 // 2. 创建连接 60 // 2. 创建连接
60 Connection connection = factory.newConnection(); 61 Connection connection = factory.newConnection();
61 -  
62 // 3. 创建信道 62 // 3. 创建信道
63 return connection.createChannel(); 63 return connection.createChannel();
64 } 64 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamDataBridgeExecute.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisStreamRuleAction.java
1 -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; 1 +package cn.iocoder.yudao.module.iot.service.rule.data.action;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRedisStreamConfig; 4 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRedisStreamConfig;
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; 7 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
7 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
8 import org.redisson.Redisson; 9 import org.redisson.Redisson;
9 import org.redisson.api.RedissonClient; 10 import org.redisson.api.RedissonClient;
@@ -18,34 +19,34 @@ import org.springframework.data.redis.serializer.RedisSerializer; @@ -18,34 +19,34 @@ import org.springframework.data.redis.serializer.RedisSerializer;
18 import org.springframework.stereotype.Component; 19 import org.springframework.stereotype.Component;
19 20
20 /** 21 /**
21 - * Redis Stream 的 {@link IotDataBridgeExecute} 实现类 22 + * Redis Stream 的 {@link IotDataRuleAction} 实现类
22 * 23 *
23 * @author HUIHUI 24 * @author HUIHUI
24 */ 25 */
25 @Component 26 @Component
26 @Slf4j 27 @Slf4j
27 -public class IotRedisStreamDataBridgeExecute extends  
28 - AbstractCacheableDataBridgeExecute<IotDataBridgeRedisStreamConfig, RedisTemplate<String, Object>> { 28 +public class IotRedisStreamRuleAction extends
  29 + IotDataRuleCacheableAction<IotDataSinkRedisStreamConfig, RedisTemplate<String, Object>> {
29 30
30 @Override 31 @Override
31 public Integer getType() { 32 public Integer getType() {
32 - return IotDataBridgeTypeEnum.REDIS_STREAM.getType(); 33 + return IotDataSinkTypeEnum.REDIS_STREAM.getType();
33 } 34 }
34 35
35 @Override 36 @Override
36 - public void execute0(IotDeviceMessage message, IotDataBridgeRedisStreamConfig config) throws Exception { 37 + public void execute(IotDeviceMessage message, IotDataSinkRedisStreamConfig config) throws Exception {
37 // 1. 获取 RedisTemplate 38 // 1. 获取 RedisTemplate
38 RedisTemplate<String, Object> redisTemplate = getProducer(config); 39 RedisTemplate<String, Object> redisTemplate = getProducer(config);
39 40
40 // 2. 创建并发送 Stream 记录 41 // 2. 创建并发送 Stream 记录
41 - ObjectRecord<String, IotDeviceMessage> record = StreamRecords.newRecord()  
42 - .ofObject(message).withStreamKey(config.getTopic()); 42 + ObjectRecord<String, ?> record = StreamRecords.newRecord()
  43 + .ofObject(JsonUtils.toJsonString(message)).withStreamKey(config.getTopic());
43 String recordId = String.valueOf(redisTemplate.opsForStream().add(record)); 44 String recordId = String.valueOf(redisTemplate.opsForStream().add(record));
44 - log.info("[executeRedisStream][消息发送成功] messageId: {}, config: {}", recordId, config); 45 + log.info("[execute][消息发送成功] messageId: {}, config: {}", recordId, config);
45 } 46 }
46 47
47 @Override 48 @Override
48 - protected RedisTemplate<String, Object> initProducer(IotDataBridgeRedisStreamConfig config) { 49 + protected RedisTemplate<String, Object> initProducer(IotDataSinkRedisStreamConfig config) {
49 // 1.1 创建 Redisson 配置 50 // 1.1 创建 Redisson 配置
50 Config redissonConfig = new Config(); 51 Config redissonConfig = new Config();
51 SingleServerConfig serverConfig = redissonConfig.useSingleServer() 52 SingleServerConfig serverConfig = redissonConfig.useSingleServer()
@@ -56,11 +57,11 @@ public class IotRedisStreamDataBridgeExecute extends @@ -56,11 +57,11 @@ public class IotRedisStreamDataBridgeExecute extends
56 serverConfig.setPassword(config.getPassword()); 57 serverConfig.setPassword(config.getPassword());
57 } 58 }
58 59
59 - // 创建 RedisTemplate 并配置 60 + // 2.1 创建 RedisTemplate 并配置
60 RedissonClient redisson = Redisson.create(redissonConfig); 61 RedissonClient redisson = Redisson.create(redissonConfig);
61 RedisTemplate<String, Object> template = new RedisTemplate<>(); 62 RedisTemplate<String, Object> template = new RedisTemplate<>();
62 template.setConnectionFactory(new RedissonConnectionFactory(redisson)); 63 template.setConnectionFactory(new RedissonConnectionFactory(redisson));
63 - // 设置序列化器 64 + // 2.2 设置序列化器
64 template.setKeySerializer(RedisSerializer.string()); 65 template.setKeySerializer(RedisSerializer.string());
65 template.setHashKeySerializer(RedisSerializer.string()); 66 template.setHashKeySerializer(RedisSerializer.string());
66 template.setValueSerializer(RedisSerializer.json()); 67 template.setValueSerializer(RedisSerializer.json());
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataRuleAction.java
1 -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; 1 +package cn.iocoder.yudao.module.iot.service.rule.data.action;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRocketMQConfig; 3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; 5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRocketMQConfig;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
6 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
7 import org.apache.rocketmq.client.producer.DefaultMQProducer; 8 import org.apache.rocketmq.client.producer.DefaultMQProducer;
8 import org.apache.rocketmq.client.producer.SendResult; 9 import org.apache.rocketmq.client.producer.SendResult;
9 import org.apache.rocketmq.client.producer.SendStatus; 10 import org.apache.rocketmq.client.producer.SendStatus;
10 import org.apache.rocketmq.common.message.Message; 11 import org.apache.rocketmq.common.message.Message;
11 -import org.apache.rocketmq.remoting.common.RemotingHelper;  
12 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 12 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
13 import org.springframework.stereotype.Component; 13 import org.springframework.stereotype.Component;
14 14
15 /** 15 /**
16 - * RocketMQ 的 {@link IotDataBridgeExecute} 实现类 16 + * RocketMQ 的 {@link IotDataRuleAction} 实现类
17 * 17 *
18 * @author HUIHUI 18 * @author HUIHUI
19 */ 19 */
20 @ConditionalOnClass(name = "org.apache.rocketmq.client.producer.DefaultMQProducer") 20 @ConditionalOnClass(name = "org.apache.rocketmq.client.producer.DefaultMQProducer")
21 @Component 21 @Component
22 @Slf4j 22 @Slf4j
23 -public class IotRocketMQDataBridgeExecute extends  
24 - AbstractCacheableDataBridgeExecute<IotDataBridgeRocketMQConfig, DefaultMQProducer> { 23 +public class IotRocketMQDataRuleAction extends
  24 + IotDataRuleCacheableAction<IotDataSinkRocketMQConfig, DefaultMQProducer> {
25 25
26 @Override 26 @Override
27 public Integer getType() { 27 public Integer getType() {
28 - return IotDataBridgeTypeEnum.ROCKETMQ.getType(); 28 + return IotDataSinkTypeEnum.ROCKETMQ.getType();
29 } 29 }
30 30
31 @Override 31 @Override
32 - public void execute0(IotDeviceMessage message, IotDataBridgeRocketMQConfig config) throws Exception { 32 + public void execute(IotDeviceMessage message, IotDataSinkRocketMQConfig config) throws Exception {
33 // 1. 获取或创建 Producer 33 // 1. 获取或创建 Producer
34 DefaultMQProducer producer = getProducer(config); 34 DefaultMQProducer producer = getProducer(config);
35 35
36 - // 2.1 创建消息对象,指定Topic、Tag和消息体  
37 - Message msg = new Message(  
38 - config.getTopic(),  
39 - config.getTags(),  
40 - message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET)  
41 - ); 36 + // 2.1 创建消息对象,指定 Topic、Tag 和消息体
  37 + Message msg = new Message(config.getTopic(), config.getTags(), JsonUtils.toJsonByte(message));
42 // 2.2 发送同步消息并处理结果 38 // 2.2 发送同步消息并处理结果
43 SendResult sendResult = producer.send(msg); 39 SendResult sendResult = producer.send(msg);
44 // 2.3 处理发送结果 40 // 2.3 处理发送结果
45 if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { 41 if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) {
46 - log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult); 42 + log.info("[execute][message({}) config({}) 发送成功,结果({})]", message, config, sendResult);
47 } else { 43 } else {
48 - log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult); 44 + log.error("[execute][message({}) config({}) 发送失败,结果({})]", message, config, sendResult);
49 } 45 }
50 } 46 }
51 47
52 @Override 48 @Override
53 - protected DefaultMQProducer initProducer(IotDataBridgeRocketMQConfig config) throws Exception { 49 + protected DefaultMQProducer initProducer(IotDataSinkRocketMQConfig config) throws Exception {
54 DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); 50 DefaultMQProducer producer = new DefaultMQProducer(config.getGroup());
55 producer.setNamesrvAddr(config.getNameServer()); 51 producer.setNamesrvAddr(config.getNameServer());
56 producer.start(); 52 producer.start();
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneService.java
1 -package cn.iocoder.yudao.module.iot.service.rule; 1 +package cn.iocoder.yudao.module.iot.service.rule.scene;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO; 4 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;
@@ -8,10 +8,11 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; @@ -8,10 +8,11 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
8 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; 8 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
9 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
10 10
  11 +import java.util.Collection;
11 import java.util.List; 12 import java.util.List;
12 13
13 /** 14 /**
14 - * IoT 规则场景 Service 接口 15 + * IoT 规则场景规则 Service 接口
15 * 16 *
16 * @author 芋道源码 17 * @author 芋道源码
17 */ 18 */
@@ -56,6 +57,22 @@ public interface IotRuleSceneService { @@ -56,6 +57,22 @@ public interface IotRuleSceneService {
56 PageResult<IotRuleSceneDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO); 57 PageResult<IotRuleSceneDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO);
57 58
58 /** 59 /**
  60 + * 校验规则场景联动规则编号们是否存在。如下情况,视为无效:
  61 + * 1. 规则场景联动规则编号不存在
  62 + *
  63 + * @param ids 场景联动规则编号数组
  64 + */
  65 + void validateRuleSceneList(Collection<Long> ids);
  66 +
  67 + /**
  68 + * 获得指定状态的场景联动列表
  69 + *
  70 + * @param status 状态
  71 + * @return 场景联动列表
  72 + */
  73 + List<IotRuleSceneDO> getRuleSceneListByStatus(Integer status);
  74 +
  75 + /**
59 * 【缓存】获得指定设备的场景列表 76 * 【缓存】获得指定设备的场景列表
60 * 77 *
61 * @param productKey 产品 Key 78 * @param productKey 产品 Key
@@ -74,7 +91,7 @@ public interface IotRuleSceneService { @@ -74,7 +91,7 @@ public interface IotRuleSceneService {
74 /** 91 /**
75 * 基于 {@link IotRuleSceneTriggerTypeEnum#TIMER} 场景,执行规则场景 92 * 基于 {@link IotRuleSceneTriggerTypeEnum#TIMER} 场景,执行规则场景
76 * 93 *
77 - * @param id 场景编号 94 + * @param id 场景联动规则编号
78 */ 95 */
79 void executeRuleSceneByTimer(Long id); 96 void executeRuleSceneByTimer(Long id);
80 97
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceImpl.java
1 -package cn.iocoder.yudao.module.iot.service.rule; 1 +package cn.iocoder.yudao.module.iot.service.rule.scene;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.ListUtil; 4 import cn.hutool.core.collection.ListUtil;
@@ -23,11 +23,11 @@ import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper; @@ -23,11 +23,11 @@ import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;
23 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; 23 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
24 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; 24 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
25 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; 25 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
26 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum; 26 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionOperatorEnum;
27 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; 27 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
28 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; 28 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
29 import cn.iocoder.yudao.module.iot.job.rule.IotRuleSceneJob; 29 import cn.iocoder.yudao.module.iot.job.rule.IotRuleSceneJob;
30 -import cn.iocoder.yudao.module.iot.service.rule.action.IotRuleSceneAction; 30 +import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;
31 import jakarta.annotation.Resource; 31 import jakarta.annotation.Resource;
32 import lombok.SneakyThrows; 32 import lombok.SneakyThrows;
33 import lombok.extern.slf4j.Slf4j; 33 import lombok.extern.slf4j.Slf4j;
@@ -39,6 +39,7 @@ import org.quartz.impl.StdSchedulerFactory; @@ -39,6 +39,7 @@ import org.quartz.impl.StdSchedulerFactory;
39 import org.springframework.stereotype.Service; 39 import org.springframework.stereotype.Service;
40 import org.springframework.validation.annotation.Validated; 40 import org.springframework.validation.annotation.Validated;
41 41
  42 +import java.util.Collection;
42 import java.util.HashMap; 43 import java.util.HashMap;
43 import java.util.List; 44 import java.util.List;
44 import java.util.Map; 45 import java.util.Map;
@@ -62,7 +63,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -62,7 +63,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
62 private IotRuleSceneMapper ruleSceneMapper; 63 private IotRuleSceneMapper ruleSceneMapper;
63 64
64 @Resource 65 @Resource
65 - private List<IotRuleSceneAction> ruleSceneActions; 66 + private List<IotSceneRuleAction> ruleSceneActions;
66 67
67 @Resource(name = "iotSchedulerManager") 68 @Resource(name = "iotSchedulerManager")
68 private IotSchedulerManager schedulerManager; 69 private IotSchedulerManager schedulerManager;
@@ -109,6 +110,22 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -109,6 +110,22 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
109 return ruleSceneMapper.selectPage(pageReqVO); 110 return ruleSceneMapper.selectPage(pageReqVO);
110 } 111 }
111 112
  113 + @Override
  114 + public void validateRuleSceneList(Collection<Long> ids) {
  115 + if (CollUtil.isEmpty(ids)) {
  116 + return;
  117 + }
  118 + // 批量查询存在的规则场景
  119 + List<IotRuleSceneDO> existingScenes = ruleSceneMapper.selectByIds(ids);
  120 + if (existingScenes.size() != ids.size()) {
  121 + throw exception(RULE_SCENE_NOT_EXISTS);
  122 + }
  123 + }
  124 +
  125 + @Override
  126 + public List<IotRuleSceneDO> getRuleSceneListByStatus(Integer status) {
  127 + return ruleSceneMapper.selectListByStatus(status);
  128 + }
112 129
113 // TODO 芋艿,缓存待实现 130 // TODO 芋艿,缓存待实现
114 @Override 131 @Override
@@ -127,57 +144,57 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -127,57 +144,57 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
127 condition01.setParameters(CollUtil.newArrayList()); 144 condition01.setParameters(CollUtil.newArrayList());
128 // IotRuleSceneDO.TriggerConditionParameter parameter010 = new IotRuleSceneDO.TriggerConditionParameter(); 145 // IotRuleSceneDO.TriggerConditionParameter parameter010 = new IotRuleSceneDO.TriggerConditionParameter();
129 // parameter010.setIdentifier("width"); 146 // parameter010.setIdentifier("width");
130 -// parameter010.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator()); 147 +// parameter010.setOperator(IotRuleSceneConditionOperatorEnum.EQUALS.getOperator());
131 // parameter010.setValue("abc"); 148 // parameter010.setValue("abc");
132 // condition01.getParameters().add(parameter010); 149 // condition01.getParameters().add(parameter010);
133 IotRuleSceneDO.TriggerConditionParameter parameter011 = new IotRuleSceneDO.TriggerConditionParameter(); 150 IotRuleSceneDO.TriggerConditionParameter parameter011 = new IotRuleSceneDO.TriggerConditionParameter();
134 parameter011.setIdentifier("width"); 151 parameter011.setIdentifier("width");
135 - parameter011.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator()); 152 + parameter011.setOperator(IotRuleSceneConditionOperatorEnum.EQUALS.getOperator());
136 parameter011.setValue("1"); 153 parameter011.setValue("1");
137 condition01.getParameters().add(parameter011); 154 condition01.getParameters().add(parameter011);
138 IotRuleSceneDO.TriggerConditionParameter parameter012 = new IotRuleSceneDO.TriggerConditionParameter(); 155 IotRuleSceneDO.TriggerConditionParameter parameter012 = new IotRuleSceneDO.TriggerConditionParameter();
139 parameter012.setIdentifier("width"); 156 parameter012.setIdentifier("width");
140 - parameter012.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS.getOperator()); 157 + parameter012.setOperator(IotRuleSceneConditionOperatorEnum.NOT_EQUALS.getOperator());
141 parameter012.setValue("2"); 158 parameter012.setValue("2");
142 condition01.getParameters().add(parameter012); 159 condition01.getParameters().add(parameter012);
143 IotRuleSceneDO.TriggerConditionParameter parameter013 = new IotRuleSceneDO.TriggerConditionParameter(); 160 IotRuleSceneDO.TriggerConditionParameter parameter013 = new IotRuleSceneDO.TriggerConditionParameter();
144 parameter013.setIdentifier("width"); 161 parameter013.setIdentifier("width");
145 - parameter013.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN.getOperator()); 162 + parameter013.setOperator(IotRuleSceneConditionOperatorEnum.GREATER_THAN.getOperator());
146 parameter013.setValue("0"); 163 parameter013.setValue("0");
147 condition01.getParameters().add(parameter013); 164 condition01.getParameters().add(parameter013);
148 IotRuleSceneDO.TriggerConditionParameter parameter014 = new IotRuleSceneDO.TriggerConditionParameter(); 165 IotRuleSceneDO.TriggerConditionParameter parameter014 = new IotRuleSceneDO.TriggerConditionParameter();
149 parameter014.setIdentifier("width"); 166 parameter014.setIdentifier("width");
150 - parameter014.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator()); 167 + parameter014.setOperator(IotRuleSceneConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator());
151 parameter014.setValue("0"); 168 parameter014.setValue("0");
152 condition01.getParameters().add(parameter014); 169 condition01.getParameters().add(parameter014);
153 IotRuleSceneDO.TriggerConditionParameter parameter015 = new IotRuleSceneDO.TriggerConditionParameter(); 170 IotRuleSceneDO.TriggerConditionParameter parameter015 = new IotRuleSceneDO.TriggerConditionParameter();
154 parameter015.setIdentifier("width"); 171 parameter015.setIdentifier("width");
155 - parameter015.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN.getOperator()); 172 + parameter015.setOperator(IotRuleSceneConditionOperatorEnum.LESS_THAN.getOperator());
156 parameter015.setValue("2"); 173 parameter015.setValue("2");
157 condition01.getParameters().add(parameter015); 174 condition01.getParameters().add(parameter015);
158 IotRuleSceneDO.TriggerConditionParameter parameter016 = new IotRuleSceneDO.TriggerConditionParameter(); 175 IotRuleSceneDO.TriggerConditionParameter parameter016 = new IotRuleSceneDO.TriggerConditionParameter();
159 parameter016.setIdentifier("width"); 176 parameter016.setIdentifier("width");
160 - parameter016.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS.getOperator()); 177 + parameter016.setOperator(IotRuleSceneConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator());
161 parameter016.setValue("2"); 178 parameter016.setValue("2");
162 condition01.getParameters().add(parameter016); 179 condition01.getParameters().add(parameter016);
163 IotRuleSceneDO.TriggerConditionParameter parameter017 = new IotRuleSceneDO.TriggerConditionParameter(); 180 IotRuleSceneDO.TriggerConditionParameter parameter017 = new IotRuleSceneDO.TriggerConditionParameter();
164 parameter017.setIdentifier("width"); 181 parameter017.setIdentifier("width");
165 - parameter017.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.IN.getOperator()); 182 + parameter017.setOperator(IotRuleSceneConditionOperatorEnum.IN.getOperator());
166 parameter017.setValue("1,2,3"); 183 parameter017.setValue("1,2,3");
167 condition01.getParameters().add(parameter017); 184 condition01.getParameters().add(parameter017);
168 IotRuleSceneDO.TriggerConditionParameter parameter018 = new IotRuleSceneDO.TriggerConditionParameter(); 185 IotRuleSceneDO.TriggerConditionParameter parameter018 = new IotRuleSceneDO.TriggerConditionParameter();
169 parameter018.setIdentifier("width"); 186 parameter018.setIdentifier("width");
170 - parameter018.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN.getOperator()); 187 + parameter018.setOperator(IotRuleSceneConditionOperatorEnum.NOT_IN.getOperator());
171 parameter018.setValue("0,2,3"); 188 parameter018.setValue("0,2,3");
172 condition01.getParameters().add(parameter018); 189 condition01.getParameters().add(parameter018);
173 IotRuleSceneDO.TriggerConditionParameter parameter019 = new IotRuleSceneDO.TriggerConditionParameter(); 190 IotRuleSceneDO.TriggerConditionParameter parameter019 = new IotRuleSceneDO.TriggerConditionParameter();
174 parameter019.setIdentifier("width"); 191 parameter019.setIdentifier("width");
175 - parameter019.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN.getOperator()); 192 + parameter019.setOperator(IotRuleSceneConditionOperatorEnum.BETWEEN.getOperator());
176 parameter019.setValue("1,3"); 193 parameter019.setValue("1,3");
177 condition01.getParameters().add(parameter019); 194 condition01.getParameters().add(parameter019);
178 IotRuleSceneDO.TriggerConditionParameter parameter020 = new IotRuleSceneDO.TriggerConditionParameter(); 195 IotRuleSceneDO.TriggerConditionParameter parameter020 = new IotRuleSceneDO.TriggerConditionParameter();
179 parameter020.setIdentifier("width"); 196 parameter020.setIdentifier("width");
180 - parameter020.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN.getOperator()); 197 + parameter020.setOperator(IotRuleSceneConditionOperatorEnum.NOT_BETWEEN.getOperator());
181 parameter020.setValue("2,3"); 198 parameter020.setValue("2,3");
182 condition01.getParameters().add(parameter020); 199 condition01.getParameters().add(parameter020);
183 trigger01.getConditions().add(condition01); 200 trigger01.getConditions().add(condition01);
@@ -194,7 +211,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -194,7 +211,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
194 condition03.setParameters(CollUtil.newArrayList()); 211 condition03.setParameters(CollUtil.newArrayList());
195 IotRuleSceneDO.TriggerConditionParameter parameter030 = new IotRuleSceneDO.TriggerConditionParameter(); 212 IotRuleSceneDO.TriggerConditionParameter parameter030 = new IotRuleSceneDO.TriggerConditionParameter();
196 parameter030.setIdentifier("width"); 213 parameter030.setIdentifier("width");
197 - parameter030.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator()); 214 + parameter030.setOperator(IotRuleSceneConditionOperatorEnum.EQUALS.getOperator());
198 parameter030.setValue("1"); 215 parameter030.setValue("1");
199 trigger01.getConditions().add(condition03); 216 trigger01.getConditions().add(condition03);
200 ruleScene01.getTriggers().add(trigger01); 217 ruleScene01.getTriggers().add(trigger01);
@@ -202,7 +219,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -202,7 +219,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
202 ruleScene01.setActions(CollUtil.newArrayList()); 219 ruleScene01.setActions(CollUtil.newArrayList());
203 // 设备控制 220 // 设备控制
204 IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig(); 221 IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig();
205 - action01.setType(IotRuleSceneActionTypeEnum.DEVICE_CONTROL.getType()); 222 + action01.setType(IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET.getType());
206 IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl(); 223 IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl();
207 actionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT"); 224 actionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT");
208 actionDeviceControl01.setDeviceNames(ListUtil.of("small")); 225 actionDeviceControl01.setDeviceNames(ListUtil.of("small"));
@@ -214,11 +231,6 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -214,11 +231,6 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
214 .build()); 231 .build());
215 action01.setDeviceControl(actionDeviceControl01); 232 action01.setDeviceControl(actionDeviceControl01);
216 // ruleScene01.getActions().add(action01); // TODO 芋艿:先不测试了 233 // ruleScene01.getActions().add(action01); // TODO 芋艿:先不测试了
217 - // 数据桥接(http)  
218 - IotRuleSceneDO.ActionConfig action02 = new IotRuleSceneDO.ActionConfig();  
219 - action02.setType(IotRuleSceneActionTypeEnum.DATA_BRIDGE.getType());  
220 - action02.setDataBridgeId(1L);  
221 - ruleScene01.getActions().add(action02);  
222 return ListUtil.toList(ruleScene01); 234 return ListUtil.toList(ruleScene01);
223 } 235 }
224 236
@@ -266,7 +278,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -266,7 +278,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
266 scene.setTriggers(ListUtil.toList(triggerConfig)); 278 scene.setTriggers(ListUtil.toList(triggerConfig));
267 // 动作 279 // 动作
268 IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig(); 280 IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig();
269 - action01.setType(IotRuleSceneActionTypeEnum.DEVICE_CONTROL.getType()); 281 + action01.setType(IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET.getType());
270 IotRuleSceneDO.ActionDeviceControl iotRuleSceneActionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl(); 282 IotRuleSceneDO.ActionDeviceControl iotRuleSceneActionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl();
271 iotRuleSceneActionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT"); 283 iotRuleSceneActionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT");
272 iotRuleSceneActionDeviceControl01.setDeviceNames(ListUtil.of("small")); 284 iotRuleSceneActionDeviceControl01.setDeviceNames(ListUtil.of("small"));
@@ -366,8 +378,8 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -366,8 +378,8 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
366 private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter, 378 private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter,
367 IotRuleSceneDO ruleScene, IotRuleSceneDO.TriggerConfig trigger) { 379 IotRuleSceneDO ruleScene, IotRuleSceneDO.TriggerConfig trigger) {
368 // 1.1 校验操作符是否合法 380 // 1.1 校验操作符是否合法
369 - IotRuleSceneTriggerConditionParameterOperatorEnum operator =  
370 - IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator()); 381 + IotRuleSceneConditionOperatorEnum operator =
  382 + IotRuleSceneConditionOperatorEnum.operatorOf(parameter.getOperator());
371 if (operator == null) { 383 if (operator == null) {
372 log.error("[isTriggerConditionParameterMatched][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]", 384 log.error("[isTriggerConditionParameterMatched][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]",
373 ruleScene.getId(), trigger, parameter.getOperator()); 385 ruleScene.getId(), trigger, parameter.getOperator());
@@ -382,24 +394,24 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -382,24 +394,24 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
382 // 2.1 构建 Spring 表达式的变量 394 // 2.1 构建 Spring 表达式的变量
383 Map<String, Object> springExpressionVariables = new HashMap<>(); 395 Map<String, Object> springExpressionVariables = new HashMap<>();
384 try { 396 try {
385 - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue);  
386 - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, parameter.getValue()); 397 + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue);
  398 + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE, parameter.getValue());
387 List<String> parameterValues = StrUtil.splitTrim(parameter.getValue(), CharPool.COMMA); 399 List<String> parameterValues = StrUtil.splitTrim(parameter.getValue(), CharPool.COMMA);
388 - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues); 400 + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues);
389 // 特殊:解决数字的比较。因为 Spring 是基于它的 compareTo 方法,对数字的比较存在问题! 401 // 特殊:解决数字的比较。因为 Spring 是基于它的 compareTo 方法,对数字的比较存在问题!
390 - if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN,  
391 - IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN,  
392 - IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN,  
393 - IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS,  
394 - IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN,  
395 - IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS) 402 + if (ObjectUtils.equalsAny(operator, IotRuleSceneConditionOperatorEnum.BETWEEN,
  403 + IotRuleSceneConditionOperatorEnum.NOT_BETWEEN,
  404 + IotRuleSceneConditionOperatorEnum.GREATER_THAN,
  405 + IotRuleSceneConditionOperatorEnum.GREATER_THAN_OR_EQUALS,
  406 + IotRuleSceneConditionOperatorEnum.LESS_THAN,
  407 + IotRuleSceneConditionOperatorEnum.LESS_THAN_OR_EQUALS)
396 && NumberUtil.isNumber(messageValue) 408 && NumberUtil.isNumber(messageValue)
397 && NumberUtils.isAllNumber(parameterValues)) { 409 && NumberUtils.isAllNumber(parameterValues)) {
398 - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, 410 + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE,
399 NumberUtil.parseDouble(messageValue)); 411 NumberUtil.parseDouble(messageValue));
400 - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, 412 + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
401 NumberUtil.parseDouble(parameter.getValue())); 413 NumberUtil.parseDouble(parameter.getValue()));
402 - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, 414 + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
403 convertList(parameterValues, NumberUtil::parseDouble)); 415 convertList(parameterValues, NumberUtil::parseDouble));
404 } 416 }
405 // 2.2 计算 Spring 表达式 417 // 2.2 计算 Spring 表达式
@@ -423,7 +435,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -423,7 +435,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
423 // 2. 遍历规则场景的动作 435 // 2. 遍历规则场景的动作
424 ruleScene.getActions().forEach(actionConfig -> { 436 ruleScene.getActions().forEach(actionConfig -> {
425 // 3.1 获取对应的动作 Action 数组 437 // 3.1 获取对应的动作 Action 数组
426 - List<IotRuleSceneAction> actions = filterList(ruleSceneActions, 438 + List<IotSceneRuleAction> actions = filterList(ruleSceneActions,
427 action -> action.getType().getType().equals(actionConfig.getType())); 439 action -> action.getType().getType().equals(actionConfig.getType()));
428 if (CollUtil.isEmpty(actions)) { 440 if (CollUtil.isEmpty(actions)) {
429 return; 441 return;
@@ -431,7 +443,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -431,7 +443,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
431 // 3.2 执行动作 443 // 3.2 执行动作
432 actions.forEach(action -> { 444 actions.forEach(action -> {
433 try { 445 try {
434 - action.execute(message, actionConfig); 446 + action.execute(message, ruleScene, actionConfig);
435 log.info("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]", 447 log.info("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]",
436 message, ruleScene.getId(), actionConfig); 448 message, ruleScene.getId(), actionConfig);
437 } catch (Exception e) { 449 } catch (Exception e) {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertRecoverSceneRuleAction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.action;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  8 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
  9 +import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;
  10 +import jakarta.annotation.Resource;
  11 +import org.springframework.stereotype.Component;
  12 +
  13 +import java.util.List;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  16 +
  17 +// TODO @puhui999、@芋艿:未测试;需要场景联动开发完
  18 +/**
  19 + * IoT 告警恢复的 {@link IotSceneRuleAction} 实现类
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +@Component
  24 +public class IotAlertRecoverSceneRuleAction implements IotSceneRuleAction {
  25 +
  26 + private static final String PROCESS_REMARK = "告警自动回复,基于【{}】场景联动规则";
  27 +
  28 + @Resource
  29 + private IotAlertRecordService alertRecordService;
  30 +
  31 + @Override
  32 + public void execute(IotDeviceMessage message,
  33 + IotRuleSceneDO rule, IotRuleSceneDO.ActionConfig actionConfig) throws Exception {
  34 + Long deviceId = message != null ? message.getDeviceId() : null;
  35 + List<IotAlertRecordDO> alertRecords = alertRecordService.getAlertRecordListBySceneRuleId(
  36 + rule.getId(), deviceId, false);
  37 + if (CollUtil.isEmpty(alertRecords)) {
  38 + return;
  39 + }
  40 + alertRecordService.processAlertRecordList(convertList(alertRecords, IotAlertRecordDO::getId),
  41 + StrUtil.format(PROCESS_REMARK, rule.getName()));
  42 + }
  43 +
  44 + @Override
  45 + public IotRuleSceneActionTypeEnum getType() {
  46 + return IotRuleSceneActionTypeEnum.ALERT_RECOVER;
  47 + }
  48 +
  49 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.action;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  5 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  8 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
  9 +import cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService;
  10 +import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;
  11 +import cn.iocoder.yudao.module.system.api.mail.MailSendApi;
  12 +import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
  13 +import cn.iocoder.yudao.module.system.api.sms.SmsSendApi;
  14 +import jakarta.annotation.Resource;
  15 +import org.springframework.stereotype.Component;
  16 +
  17 +import javax.annotation.Nullable;
  18 +import java.util.List;
  19 +
  20 +// TODO @puhui999、@芋艿:未测试;需要场景联动开发完
  21 +/**
  22 + * IoT 告警触发的 {@link IotSceneRuleAction} 实现类
  23 + *
  24 + * @author 芋道源码
  25 + */
  26 +@Component
  27 +public class IotAlertTriggerSceneRuleAction implements IotSceneRuleAction {
  28 +
  29 + @Resource
  30 + private IotAlertConfigService alertConfigService;
  31 + @Resource
  32 + private IotAlertRecordService alertRecordService;
  33 +
  34 + @Resource
  35 + private SmsSendApi smsSendApi;
  36 + @Resource
  37 + private MailSendApi mailSendApi;
  38 + @Resource
  39 + private NotifyMessageSendApi notifyMessageSendApi;
  40 +
  41 + @Override
  42 + public void execute(@Nullable IotDeviceMessage message,
  43 + IotRuleSceneDO rule, IotRuleSceneDO.ActionConfig actionConfig) throws Exception {
  44 + List<IotAlertConfigDO> alertConfigs = alertConfigService.getAlertConfigListBySceneRuleIdAndStatus(
  45 + rule.getId(), CommonStatusEnum.ENABLE.getStatus());
  46 + if (CollUtil.isEmpty(alertConfigs)) {
  47 + return;
  48 + }
  49 + alertConfigs.forEach(alertConfig -> {
  50 + // 记录告警记录,传递场景规则ID
  51 + alertRecordService.createAlertRecord(alertConfig, rule.getId(), message);
  52 + // 发送告警消息
  53 + sendAlertMessage(alertConfig, message);
  54 + });
  55 + }
  56 +
  57 + private void sendAlertMessage(IotAlertConfigDO config, IotDeviceMessage deviceMessage) {
  58 + // TODO @芋艿:等场景联动开发完,再实现
  59 + // TODO @芋艿:短信
  60 + // TODO @芋艿:邮箱
  61 + // TODO @芋艿:站内信
  62 + }
  63 +
  64 + @Override
  65 + public IotRuleSceneActionTypeEnum getType() {
  66 + return IotRuleSceneActionTypeEnum.ALERT_TRIGGER;
  67 + }
  68 +
  69 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceControlRuleSceneAction.java
1 -package cn.iocoder.yudao.module.iot.service.rule.action; 1 +package cn.iocoder.yudao.module.iot.service.rule.scene.action;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
@@ -12,13 +12,13 @@ import lombok.extern.slf4j.Slf4j; @@ -12,13 +12,13 @@ import lombok.extern.slf4j.Slf4j;
12 import org.springframework.stereotype.Component; 12 import org.springframework.stereotype.Component;
13 13
14 /** 14 /**
15 - * IoT 设备控制的 {@link IotRuleSceneAction} 实现类 15 + * IoT 设备控制的 {@link IotSceneRuleAction} 实现类
16 * 16 *
17 * @author 芋道源码 17 * @author 芋道源码
18 */ 18 */
19 @Component 19 @Component
20 @Slf4j 20 @Slf4j
21 -public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction { 21 +public class IotDeviceControlRuleSceneAction implements IotSceneRuleAction {
22 22
23 @Resource 23 @Resource
24 private IotDeviceService deviceService; 24 private IotDeviceService deviceService;
@@ -26,30 +26,31 @@ public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction { @@ -26,30 +26,31 @@ public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction {
26 private IotDeviceMessageService deviceMessageService; 26 private IotDeviceMessageService deviceMessageService;
27 27
28 @Override 28 @Override
29 - public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {  
30 - IotRuleSceneDO.ActionDeviceControl control = config.getDeviceControl(); 29 + public void execute(IotDeviceMessage message,
  30 + IotRuleSceneDO rule, IotRuleSceneDO.ActionConfig actionConfig) {
  31 + IotRuleSceneDO.ActionDeviceControl control = actionConfig.getDeviceControl();
31 Assert.notNull(control, "设备控制配置不能为空"); 32 Assert.notNull(control, "设备控制配置不能为空");
32 // 遍历每个设备,下发消息 33 // 遍历每个设备,下发消息
33 control.getDeviceNames().forEach(deviceName -> { 34 control.getDeviceNames().forEach(deviceName -> {
34 IotDeviceDO device = deviceService.getDeviceFromCache(control.getProductKey(), deviceName); 35 IotDeviceDO device = deviceService.getDeviceFromCache(control.getProductKey(), deviceName);
35 if (device == null) { 36 if (device == null) {
36 - log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config); 37 + log.error("[execute][message({}) actionConfig({}) 对应的设备不存在]", message, actionConfig);
37 return; 38 return;
38 } 39 }
39 try { 40 try {
40 // TODO @芋艿:@puhui999:这块可能要改,从 type => method 41 // TODO @芋艿:@puhui999:这块可能要改,从 type => method
41 IotDeviceMessage downstreamMessage = deviceMessageService.sendDeviceMessage(IotDeviceMessage.requestOf( 42 IotDeviceMessage downstreamMessage = deviceMessageService.sendDeviceMessage(IotDeviceMessage.requestOf(
42 control.getType() + control.getIdentifier(), control.getData()).setDeviceId(device.getId())); 43 control.getType() + control.getIdentifier(), control.getData()).setDeviceId(device.getId()));
43 - log.info("[execute][message({}) config({}) 下发消息({})成功]", message, config, downstreamMessage); 44 + log.info("[execute][message({}) actionConfig({}) 下发消息({})成功]", message, actionConfig, downstreamMessage);
44 } catch (Exception e) { 45 } catch (Exception e) {
45 - log.error("[execute][message({}) config({}) 下发消息失败]", message, config, e); 46 + log.error("[execute][message({}) actionConfig({}) 下发消息失败]", message, actionConfig, e);
46 } 47 }
47 }); 48 });
48 } 49 }
49 50
50 @Override 51 @Override
51 public IotRuleSceneActionTypeEnum getType() { 52 public IotRuleSceneActionTypeEnum getType() {
52 - return IotRuleSceneActionTypeEnum.DEVICE_CONTROL; 53 + return IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET;
53 } 54 }
54 55
55 } 56 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotSceneRuleAction.java
1 -package cn.iocoder.yudao.module.iot.service.rule.action; 1 +package cn.iocoder.yudao.module.iot.service.rule.scene.action;
2 2
3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; 4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
@@ -7,23 +7,24 @@ import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; @@ -7,23 +7,24 @@ import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
7 import javax.annotation.Nullable; 7 import javax.annotation.Nullable;
8 8
9 /** 9 /**
10 - * IoT 规则场景的场景执行器接口 10 + * IoT 场景联动的执行器接口
11 * 11 *
12 * @author 芋道源码 12 * @author 芋道源码
13 */ 13 */
14 -public interface IotRuleSceneAction {  
15 -  
16 - // TODO @芋艿:groovy 或者 javascript 实现数据的转换;可以考虑基于 hutool 的 ScriptUtil 做 14 +public interface IotSceneRuleAction {
17 15
18 /** 16 /**
19 - * 执行场景 17 + * 执行场景联动
20 * 18 *
21 * @param message 消息,允许空 19 * @param message 消息,允许空
22 * 1. 空的情况:定时触发 20 * 1. 空的情况:定时触发
23 * 2. 非空的情况:设备触发 21 * 2. 非空的情况:设备触发
24 - * @param config 配置 22 + * @param rule 规则
  23 + * @param actionConfig 执行配置(实际对应规则里的哪条执行配置)
25 */ 24 */
26 - void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception; 25 + void execute(@Nullable IotDeviceMessage message,
  26 + IotRuleSceneDO rule,
  27 + IotRuleSceneDO.ActionConfig actionConfig) throws Exception;
27 28
28 /** 29 /**
29 * 获得类型 30 * 获得类型
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java
@@ -9,6 +9,7 @@ import jakarta.validation.Valid; @@ -9,6 +9,7 @@ import jakarta.validation.Valid;
9 9
10 import java.util.Collection; 10 import java.util.Collection;
11 import java.util.List; 11 import java.util.List;
  12 +import java.util.Set;
12 13
13 /** 14 /**
14 * IoT 产品物模型 Service 接口 15 * IoT 产品物模型 Service 接口
@@ -99,4 +100,12 @@ public interface IotThingModelService { @@ -99,4 +100,12 @@ public interface IotThingModelService {
99 */ 100 */
100 List<IotThingModelDO> getThingModelList(IotThingModelListReqVO reqVO); 101 List<IotThingModelDO> getThingModelList(IotThingModelListReqVO reqVO);
101 102
  103 + /**
  104 + * 批量校验物模型存在
  105 + *
  106 + * @param productId 产品编号
  107 + * @param identifiers 标识符集合
  108 + */
  109 + void validateThingModelListExists(Long productId, Set<String> identifiers);
  110 +
102 } 111 }
103 \ No newline at end of file 112 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java
@@ -5,11 +5,7 @@ import cn.hutool.core.util.ObjectUtil; @@ -5,11 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.hutool.extra.spring.SpringUtil; 6 import cn.hutool.extra.spring.SpringUtil;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
9 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; 8 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
10 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;  
11 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam;  
12 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService;  
13 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO; 9 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;
14 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; 10 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;
15 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; 11 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;
@@ -19,20 +15,23 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; @@ -19,20 +15,23 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
19 import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; 15 import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper;
20 import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; 16 import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;
21 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; 17 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
22 -import cn.iocoder.yudao.module.iot.enums.thingmodel.*;  
23 import cn.iocoder.yudao.module.iot.service.product.IotProductService; 18 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
24 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
25 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
26 import org.springframework.cache.annotation.CacheEvict; 21 import org.springframework.cache.annotation.CacheEvict;
27 import org.springframework.cache.annotation.Cacheable; 22 import org.springframework.cache.annotation.Cacheable;
  23 +import org.springframework.context.annotation.Lazy;
28 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
29 import org.springframework.transaction.annotation.Transactional; 25 import org.springframework.transaction.annotation.Transactional;
30 import org.springframework.validation.annotation.Validated; 26 import org.springframework.validation.annotation.Validated;
31 27
32 -import java.util.*; 28 +import java.util.Collection;
  29 +import java.util.List;
  30 +import java.util.Objects;
  31 +import java.util.Set;
33 32
34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
35 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
36 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; 35 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
37 36
38 /** 37 /**
@@ -49,6 +48,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -49,6 +48,7 @@ public class IotThingModelServiceImpl implements IotThingModelService {
49 private IotThingModelMapper thingModelMapper; 48 private IotThingModelMapper thingModelMapper;
50 49
51 @Resource 50 @Resource
  51 + @Lazy // 延迟加载,解决循环依赖
52 private IotProductService productService; 52 private IotProductService productService;
53 53
54 @Override 54 @Override
@@ -65,13 +65,8 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -65,13 +65,8 @@ public class IotThingModelServiceImpl implements IotThingModelService {
65 IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(createReqVO); 65 IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(createReqVO);
66 thingModelMapper.insert(thingModel); 66 thingModelMapper.insert(thingModel);
67 67
68 - // 3. 如果创建的是属性,需要更新默认的事件和服务  
69 - if (Objects.equals(createReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) {  
70 - createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey());  
71 - }  
72 -  
73 - // 4. 删除缓存  
74 - deleteThingModelListCache(createReqVO.getProductKey()); 68 + // 3. 删除缓存
  69 + deleteThingModelListCache(createReqVO.getProductId());
75 return thingModel.getId(); 70 return thingModel.getId();
76 } 71 }
77 72
@@ -89,13 +84,8 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -89,13 +84,8 @@ public class IotThingModelServiceImpl implements IotThingModelService {
89 IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO); 84 IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO);
90 thingModelMapper.updateById(thingModel); 85 thingModelMapper.updateById(thingModel);
91 86
92 - // 3. 如果更新的是属性,需要更新默认的事件和服务  
93 - if (Objects.equals(updateReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) {  
94 - createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey());  
95 - }  
96 -  
97 - // 4. 删除缓存  
98 - deleteThingModelListCache(updateReqVO.getProductKey()); 87 + // 3. 删除缓存
  88 + deleteThingModelListCache(updateReqVO.getProductId());
99 } 89 }
100 90
101 @Override 91 @Override
@@ -112,13 +102,8 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -112,13 +102,8 @@ public class IotThingModelServiceImpl implements IotThingModelService {
112 // 2. 删除功能 102 // 2. 删除功能
113 thingModelMapper.deleteById(id); 103 thingModelMapper.deleteById(id);
114 104
115 - // 3. 如果删除的是属性,需要更新默认的事件和服务  
116 - if (Objects.equals(thingModel.getType(), IotThingModelTypeEnum.PROPERTY.getType())) {  
117 - createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey());  
118 - }  
119 -  
120 - // 4. 删除缓存  
121 - deleteThingModelListCache(thingModel.getProductKey()); 105 + // 3. 删除缓存
  106 + deleteThingModelListCache(thingModel.getProductId());
122 } 107 }
123 108
124 @Override 109 @Override
@@ -143,7 +128,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -143,7 +128,7 @@ public class IotThingModelServiceImpl implements IotThingModelService {
143 128
144 @Override 129 @Override
145 @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productId") 130 @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productId")
146 - @TenantIgnore // 忽略租户信息,跨租户 productKey 是唯一的 131 + @TenantIgnore // 忽略租户信息
147 public List<IotThingModelDO> getThingModelListByProductIdFromCache(Long productId) { 132 public List<IotThingModelDO> getThingModelListByProductIdFromCache(Long productId) {
148 return thingModelMapper.selectListByProductId(productId); 133 return thingModelMapper.selectListByProductId(productId);
149 } 134 }
@@ -158,11 +143,21 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -158,11 +143,21 @@ public class IotThingModelServiceImpl implements IotThingModelService {
158 return thingModelMapper.selectList(reqVO); 143 return thingModelMapper.selectList(reqVO);
159 } 144 }
160 145
161 - /**  
162 - * 校验功能是否存在  
163 - *  
164 - * @param id 功能编号  
165 - */ 146 + @Override
  147 + public void validateThingModelListExists(Long productId, Set<String> identifiers) {
  148 + if (CollUtil.isEmpty(identifiers)) {
  149 + return;
  150 + }
  151 + List<IotThingModelDO> thingModels = thingModelMapper.selectListByProductIdAndIdentifiers(
  152 + productId, identifiers);
  153 + Set<String> foundIdentifiers = convertSet(thingModels, IotThingModelDO::getIdentifier);
  154 + for (String identifier : identifiers) {
  155 + if (!foundIdentifiers.contains(identifier)) {
  156 + throw exception(THING_MODEL_NOT_EXISTS);
  157 + }
  158 + }
  159 + }
  160 +
166 private void validateProductThingModelMapperExists(Long id) { 161 private void validateProductThingModelMapperExists(Long id) {
167 if (thingModelMapper.selectById(id) == null) { 162 if (thingModelMapper.selectById(id) == null) {
168 throw exception(THING_MODEL_NOT_EXISTS); 163 throw exception(THING_MODEL_NOT_EXISTS);
@@ -170,13 +165,12 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -170,13 +165,12 @@ public class IotThingModelServiceImpl implements IotThingModelService {
170 } 165 }
171 166
172 private void validateIdentifierUnique(Long id, Long productId, String identifier) { 167 private void validateIdentifierUnique(Long id, Long productId, String identifier) {
173 - // 1.0 情况一:创建时校验 168 + // 1. 情况一:创建时校验
174 if (id == null) { 169 if (id == null) {
175 // 1.1 系统保留字段,不能用于标识符定义 170 // 1.1 系统保留字段,不能用于标识符定义
176 if (StrUtil.equalsAny(identifier, "set", "get", "post", "property", "event", "time", "value")) { 171 if (StrUtil.equalsAny(identifier, "set", "get", "post", "property", "event", "time", "value")) {
177 throw exception(THING_MODEL_IDENTIFIER_INVALID); 172 throw exception(THING_MODEL_IDENTIFIER_INVALID);
178 } 173 }
179 -  
180 // 1.2 校验唯一 174 // 1.2 校验唯一
181 IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); 175 IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier);
182 if (thingModel != null) { 176 if (thingModel != null) {
@@ -185,7 +179,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -185,7 +179,7 @@ public class IotThingModelServiceImpl implements IotThingModelService {
185 return; 179 return;
186 } 180 }
187 181
188 - // 2.0 情况二:更新时校验 182 + // 2. 情况二:更新时校验
189 IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); 183 IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier);
190 if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { 184 if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) {
191 throw exception(THING_MODEL_IDENTIFIER_EXISTS); 185 throw exception(THING_MODEL_IDENTIFIER_EXISTS);
@@ -206,167 +200,14 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -206,167 +200,14 @@ public class IotThingModelServiceImpl implements IotThingModelService {
206 } 200 }
207 } 201 }
208 202
209 - /**  
210 - * 创建默认的事件和服务  
211 - *  
212 - * @param productId 产品编号  
213 - * @param productKey 产品标识  
214 - */  
215 - public void createDefaultEventsAndServices(Long productId, String productKey) {  
216 - // 1. 获取当前属性列表  
217 - List<IotThingModelDO> properties = thingModelMapper  
218 - .selectListByProductIdAndType(productId, IotThingModelTypeEnum.PROPERTY.getType());  
219 -  
220 - // 2. 生成新的事件和服务列表  
221 - List<IotThingModelDO> newThingModels = new ArrayList<>();  
222 - // 2.1 生成属性上报事件  
223 - ThingModelEvent propertyPostEvent = generatePropertyPostEvent(properties);  
224 - if (propertyPostEvent != null) {  
225 - newThingModels.add(buildEventThingModel(productId, productKey, propertyPostEvent, "属性上报事件"));  
226 - }  
227 - // 2.2 生成属性设置服务  
228 - ThingModelService propertySetService = generatePropertySetService(properties);  
229 - if (propertySetService != null) {  
230 - newThingModels.add(buildServiceThingModel(productId, productKey, propertySetService, "属性设置服务"));  
231 - }  
232 - // 2.3 生成属性获取服务  
233 - ThingModelService propertyGetService = generatePropertyGetService(properties);  
234 - if (propertyGetService != null) {  
235 - newThingModels.add(buildServiceThingModel(productId, productKey, propertyGetService, "属性获取服务"));  
236 - }  
237 -  
238 - // 3.1 获取数据库中的默认的旧事件和服务列表  
239 - List<IotThingModelDO> oldThingModels = thingModelMapper.selectListByProductIdAndIdentifiersAndTypes(  
240 - productId,  
241 - Arrays.asList("post", "set", "get"),  
242 - Arrays.asList(IotThingModelTypeEnum.EVENT.getType(), IotThingModelTypeEnum.SERVICE.getType())  
243 - );  
244 - // 3.2 创建默认的事件和服务  
245 - createDefaultEventsAndServices(oldThingModels, newThingModels);  
246 - }  
247 -  
248 - /**  
249 - * 创建默认的事件和服务  
250 - */  
251 - private void createDefaultEventsAndServices(List<IotThingModelDO> oldThingModels,  
252 - List<IotThingModelDO> newThingModels) {  
253 - // 使用 diffList 方法比较新旧列表  
254 - List<List<IotThingModelDO>> diffResult = diffList(oldThingModels, newThingModels,  
255 - (oldVal, newVal) -> {  
256 - // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。  
257 - boolean same = Objects.equals(oldVal.getIdentifier(), newVal.getIdentifier())  
258 - && Objects.equals(oldVal.getType(), newVal.getType());  
259 - if (same) {  
260 - newVal.setId(oldVal.getId()); // 设置编号  
261 - }  
262 - return same;  
263 - });  
264 - // 批量添加、修改、删除  
265 - if (CollUtil.isNotEmpty(diffResult.get(0))) {  
266 - thingModelMapper.insertBatch(diffResult.get(0));  
267 - }  
268 - if (CollUtil.isNotEmpty(diffResult.get(1))) {  
269 - thingModelMapper.updateBatch(diffResult.get(1));  
270 - }  
271 - if (CollUtil.isNotEmpty(diffResult.get(2))) {  
272 - thingModelMapper.deleteByIds(convertSet(diffResult.get(2), IotThingModelDO::getId));  
273 - }  
274 - }  
275 -  
276 - /**  
277 - * 构建事件功能对象  
278 - */  
279 - private IotThingModelDO buildEventThingModel(Long productId, String productKey,  
280 - ThingModelEvent event, String description) {  
281 - return new IotThingModelDO().setProductId(productId).setProductKey(productKey)  
282 - .setIdentifier(event.getIdentifier()).setName(event.getName()).setDescription(description)  
283 - .setType(IotThingModelTypeEnum.EVENT.getType()).setEvent(event);  
284 - }  
285 -  
286 - /**  
287 - * 构建服务功能对象  
288 - */  
289 - private IotThingModelDO buildServiceThingModel(Long productId, String productKey,  
290 - ThingModelService service, String description) {  
291 - return new IotThingModelDO().setProductId(productId).setProductKey(productKey)  
292 - .setIdentifier(service.getIdentifier()).setName(service.getName()).setDescription(description)  
293 - .setType(IotThingModelTypeEnum.SERVICE.getType()).setService(service);  
294 - }  
295 -  
296 - // TODO @haohao:是不是不用生成这个?目前属性上报,是个批量接口  
297 -  
298 - /**  
299 - * 生成属性上报事件  
300 - */  
301 - private ThingModelEvent generatePropertyPostEvent(List<IotThingModelDO> thingModels) {  
302 - // 没有属性则不生成  
303 - if (CollUtil.isEmpty(thingModels)) {  
304 - return null;  
305 - }  
306 -  
307 - // 生成属性上报事件  
308 - return new ThingModelEvent().setIdentifier("post").setName("属性上报").setMethod("thing.event.property.post")  
309 - .setType(IotThingModelServiceEventTypeEnum.INFO.getType())  
310 - .setOutputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.OUTPUT));  
311 - }  
312 -  
313 - // TODO @haohao:是不是不用生成这个?目前属性上报,是个批量接口  
314 -  
315 - /**  
316 - * 生成属性设置服务  
317 - */  
318 - private ThingModelService generatePropertySetService(List<IotThingModelDO> thingModels) {  
319 - // 1.1 过滤出所有可写属性  
320 - thingModels = filterList(thingModels, thingModel ->  
321 - IotThingModelAccessModeEnum.READ_WRITE.getMode().equals(thingModel.getProperty().getAccessMode()));  
322 - // 1.2 没有可写属性则不生成  
323 - if (CollUtil.isEmpty(thingModels)) {  
324 - return null;  
325 - }  
326 -  
327 - // 2. 生成属性设置服务  
328 - return new ThingModelService().setIdentifier("set").setName("属性设置").setMethod("thing.service.property.set")  
329 - .setCallType(IotThingModelServiceCallTypeEnum.ASYNC.getType())  
330 - .setInputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.INPUT))  
331 - .setOutputParams(Collections.emptyList()); // 属性设置服务一般不需要输出参数  
332 - }  
333 -  
334 - /**  
335 - * 生成属性获取服务  
336 - */  
337 - private ThingModelService generatePropertyGetService(List<IotThingModelDO> thingModels) {  
338 - // 1.1 没有属性则不生成  
339 - if (CollUtil.isEmpty(thingModels)) {  
340 - return null;  
341 - }  
342 -  
343 - // 1.2 生成属性获取服务  
344 - return new ThingModelService().setIdentifier("get").setName("属性获取").setMethod("thing.service.property.get")  
345 - .setCallType(IotThingModelServiceCallTypeEnum.ASYNC.getType())  
346 - .setInputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.INPUT))  
347 - .setOutputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.OUTPUT));  
348 - }  
349 -  
350 - /**  
351 - * 构建输入/输出参数列表  
352 - *  
353 - * @param thingModels 属性列表  
354 - * @return 输入/输出参数列表  
355 - */  
356 - private List<ThingModelParam> buildInputOutputParam(List<IotThingModelDO> thingModels,  
357 - IotThingModelParamDirectionEnum direction) {  
358 - return convertList(thingModels, thingModel ->  
359 - BeanUtils.toBean(thingModel.getProperty(), ThingModelParam.class).setParaOrder(0) // TODO @puhui999: 先搞个默认值看看怎么个事  
360 - .setDirection(direction.getDirection()));  
361 - }  
362 -  
363 - private void deleteThingModelListCache(String productKey) { 203 + private void deleteThingModelListCache(Long productId) {
364 // 保证 Spring AOP 触发 204 // 保证 Spring AOP 触发
365 - getSelf().deleteThingModelListCache0(productKey); 205 + getSelf().deleteThingModelListCache0(productId);
366 } 206 }
367 207
368 - @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey")  
369 - public void deleteThingModelListCache0(String productKey) { 208 + @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productId")
  209 + @TenantIgnore // 忽略租户信息
  210 + public void deleteThingModelListCache0(Long productId) {
370 } 211 }
371 212
372 private IotThingModelServiceImpl getSelf() { 213 private IotThingModelServiceImpl getSelf() {
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMapper.xml deleted
1 -<?xml version="1.0" encoding="UTF-8"?>  
2 -<!DOCTYPE mapper  
3 - PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
4 - "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
5 -<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper">  
6 -  
7 - <select id="selectDeviceCountGroupByState" resultType="java.util.Map">  
8 - SELECT  
9 - state AS `key`,  
10 - COUNT(1) AS `value`  
11 - FROM iot_device  
12 - WHERE deleted = 0  
13 - GROUP BY state  
14 - </select>  
15 -  
16 - <select id="selectDeviceCountMapByProductId" resultType="java.util.Map">  
17 - SELECT  
18 - product_id AS `key`,  
19 - COUNT(1) AS `value`  
20 - FROM iot_device  
21 - WHERE deleted = 0  
22 - GROUP BY product_id  
23 - </select>  
24 -  
25 -</mapper>  
26 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java
1 package cn.iocoder.yudao.module.iot.service.rule.action.databridge; 1 package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
2 2
3 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 3 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
4 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.*;  
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; 5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.*;
  7 +import cn.iocoder.yudao.module.iot.service.rule.data.action.*;
7 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
8 import org.junit.jupiter.api.BeforeEach; 9 import org.junit.jupiter.api.BeforeEach;
9 import org.junit.jupiter.api.Disabled; 10 import org.junit.jupiter.api.Disabled;
@@ -15,14 +16,12 @@ import org.springframework.http.HttpStatus; @@ -15,14 +16,12 @@ import org.springframework.http.HttpStatus;
15 import org.springframework.http.ResponseEntity; 16 import org.springframework.http.ResponseEntity;
16 import org.springframework.web.client.RestTemplate; 17 import org.springframework.web.client.RestTemplate;
17 18
18 -import java.time.LocalDateTime;  
19 -  
20 import static org.mockito.ArgumentMatchers.any; 19 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.anyString; 20 import static org.mockito.ArgumentMatchers.anyString;
22 import static org.mockito.Mockito.when; 21 import static org.mockito.Mockito.when;
23 22
24 /** 23 /**
25 - * {@link IotDataBridgeExecute} 实现类的单元测试 24 + * {@link IotDataRuleAction} 实现类的单元测试
26 * 25 *
27 * @author HUIHUI 26 * @author HUIHUI
28 */ 27 */
@@ -36,24 +35,25 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { @@ -36,24 +35,25 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {
36 private RestTemplate restTemplate; 35 private RestTemplate restTemplate;
37 36
38 @InjectMocks 37 @InjectMocks
39 - private IotHttpDataBridgeExecute httpDataBridgeExecute; 38 + private IotHttpDataSinkAction httpDataBridgeExecute;
40 39
41 @BeforeEach 40 @BeforeEach
42 public void setUp() { 41 public void setUp() {
  42 + // TODO @芋艿:@puhui999:需要调整下;
43 // 创建共享的测试消息 43 // 创建共享的测试消息
44 - message = IotDeviceMessage.builder().messageId("TEST-001").reportTime(LocalDateTime.now())  
45 - .productKey("testProduct").deviceName("testDevice")  
46 - .type("property").identifier("temperature").data("{\"value\": 60}")  
47 - .build(); 44 + //message = IotDeviceMessage.builder().messageId("TEST-001").reportTime(LocalDateTime.now())
  45 + // .productKey("testProduct").deviceName("testDevice")
  46 + // .type("property").identifier("temperature").data("{\"value\": 60}")
  47 + // .build();
48 } 48 }
49 49
50 @Test 50 @Test
51 public void testKafkaMQDataBridge() throws Exception { 51 public void testKafkaMQDataBridge() throws Exception {
52 // 1. 创建执行器实例 52 // 1. 创建执行器实例
53 - IotKafkaMQDataBridgeExecute action = new IotKafkaMQDataBridgeExecute(); 53 + IotKafkaDataRuleAction action = new IotKafkaDataRuleAction();
54 54
55 // 2. 创建配置 55 // 2. 创建配置
56 - IotDataBridgeKafkaMQConfig config = new IotDataBridgeKafkaMQConfig() 56 + IotDataSinkKafkaConfig config = new IotDataSinkKafkaConfig()
57 .setBootstrapServers("127.0.0.1:9092") 57 .setBootstrapServers("127.0.0.1:9092")
58 .setTopic("test-topic") 58 .setTopic("test-topic")
59 .setSsl(false) 59 .setSsl(false)
@@ -67,10 +67,10 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { @@ -67,10 +67,10 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {
67 @Test 67 @Test
68 public void testRabbitMQDataBridge() throws Exception { 68 public void testRabbitMQDataBridge() throws Exception {
69 // 1. 创建执行器实例 69 // 1. 创建执行器实例
70 - IotRabbitMQDataBridgeExecute action = new IotRabbitMQDataBridgeExecute(); 70 + IotRabbitMQDataRuleAction action = new IotRabbitMQDataRuleAction();
71 71
72 // 2. 创建配置 72 // 2. 创建配置
73 - IotDataBridgeRabbitMQConfig config = new IotDataBridgeRabbitMQConfig() 73 + IotDataSinkRabbitMQConfig config = new IotDataSinkRabbitMQConfig()
74 .setHost("localhost") 74 .setHost("localhost")
75 .setPort(5672) 75 .setPort(5672)
76 .setVirtualHost("/") 76 .setVirtualHost("/")
@@ -87,10 +87,10 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { @@ -87,10 +87,10 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {
87 @Test 87 @Test
88 public void testRedisStreamDataBridge() throws Exception { 88 public void testRedisStreamDataBridge() throws Exception {
89 // 1. 创建执行器实例 89 // 1. 创建执行器实例
90 - IotRedisStreamDataBridgeExecute action = new IotRedisStreamDataBridgeExecute(); 90 + IotRedisStreamRuleAction action = new IotRedisStreamRuleAction();
91 91
92 // 2. 创建配置 92 // 2. 创建配置
93 - IotDataBridgeRedisStreamConfig config = new IotDataBridgeRedisStreamConfig() 93 + IotDataSinkRedisStreamConfig config = new IotDataSinkRedisStreamConfig()
94 .setHost("127.0.0.1") 94 .setHost("127.0.0.1")
95 .setPort(6379) 95 .setPort(6379)
96 .setDatabase(0) 96 .setDatabase(0)
@@ -104,10 +104,10 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { @@ -104,10 +104,10 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {
104 @Test 104 @Test
105 public void testRocketMQDataBridge() throws Exception { 105 public void testRocketMQDataBridge() throws Exception {
106 // 1. 创建执行器实例 106 // 1. 创建执行器实例
107 - IotRocketMQDataBridgeExecute action = new IotRocketMQDataBridgeExecute(); 107 + IotRocketMQDataRuleAction action = new IotRocketMQDataRuleAction();
108 108
109 // 2. 创建配置 109 // 2. 创建配置
110 - IotDataBridgeRocketMQConfig config = new IotDataBridgeRocketMQConfig() 110 + IotDataSinkRocketMQConfig config = new IotDataSinkRocketMQConfig()
111 .setNameServer("127.0.0.1:9876") 111 .setNameServer("127.0.0.1:9876")
112 .setGroup("test-group") 112 .setGroup("test-group")
113 .setTopic("test-topic") 113 .setTopic("test-topic")
@@ -124,12 +124,12 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { @@ -124,12 +124,12 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {
124 .thenReturn(new ResponseEntity<>("Success", HttpStatus.OK)); 124 .thenReturn(new ResponseEntity<>("Success", HttpStatus.OK));
125 125
126 // 2. 创建配置 126 // 2. 创建配置
127 - IotDataBridgeHttpConfig config = new IotDataBridgeHttpConfig() 127 + IotDataSinkHttpConfig config = new IotDataSinkHttpConfig()
128 .setUrl("https://doc.iocoder.cn/").setMethod(HttpMethod.GET.name()); 128 .setUrl("https://doc.iocoder.cn/").setMethod(HttpMethod.GET.name());
129 129
130 // 3. 执行测试 130 // 3. 执行测试
131 log.info("[testHttpDataBridge][执行HTTP数据桥接测试]"); 131 log.info("[testHttpDataBridge][执行HTTP数据桥接测试]");
132 - httpDataBridgeExecute.execute(message, new IotDataBridgeDO() 132 + httpDataBridgeExecute.execute(message, new IotDataSinkDO()
133 .setType(httpDataBridgeExecute.getType()).setConfig(config)); 133 .setType(httpDataBridgeExecute.getType()).setConfig(config));
134 } 134 }
135 135
@@ -141,13 +141,13 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { @@ -141,13 +141,13 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {
141 * @param type MQ 类型 141 * @param type MQ 类型
142 * @throws Exception 如果执行过程中发生异常 142 * @throws Exception 如果执行过程中发生异常
143 */ 143 */
144 - private void executeAndVerifyCache(IotDataBridgeExecute<?> action, IotDataBridgeAbstractConfig config, String type) 144 + private void executeAndVerifyCache(IotDataRuleAction action, IotAbstractDataSinkConfig config, String type)
145 throws Exception { 145 throws Exception {
146 log.info("[test{}DataBridge][第一次执行,应该会创建新的 producer]", type); 146 log.info("[test{}DataBridge][第一次执行,应该会创建新的 producer]", type);
147 - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); 147 + action.execute(message, new IotDataSinkDO().setType(action.getType()).setConfig(config));
148 148
149 log.info("[test{}DataBridge][第二次执行,应该会复用缓存的 producer]", type); 149 log.info("[test{}DataBridge][第二次执行,应该会复用缓存的 producer]", type);
150 - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); 150 + action.execute(message, new IotDataSinkDO().setType(action.getType()).setConfig(config));
151 } 151 }
152 152
153 } 153 }
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java
@@ -19,8 +19,9 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable&lt;String&gt; { @@ -19,8 +19,9 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable&lt;String&gt; {
19 19
20 // ========== 设备状态 ========== 20 // ========== 设备状态 ==========
21 21
22 - STATE_ONLINE("thing.state.online", "设备上线", true),  
23 - STATE_OFFLINE("thing.state.offline", "设备下线", true), 22 + STATE_UPDATE("thing.state.update", "设备状态更新", true),
  23 +
  24 + // TODO 芋艿:要不要加个 ping 消息;
24 25
25 // ========== 设备属性 ========== 26 // ========== 设备属性 ==========
26 // 可参考:https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 27 // 可参考:https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services
@@ -49,7 +50,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable&lt;String&gt; { @@ -49,7 +50,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable&lt;String&gt; {
49 /** 50 /**
50 * 不进行 reply 回复的方法集合 51 * 不进行 reply 回复的方法集合
51 */ 52 */
52 - public static final Set<String> REPLY_DISABLED = Set.of(STATE_ONLINE.getMethod(), STATE_OFFLINE.getMethod()); 53 + public static final Set<String> REPLY_DISABLED = Set.of(STATE_UPDATE.getMethod());
53 54
54 private final String method; 55 private final String method;
55 56
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java
1 package cn.iocoder.yudao.module.iot.core.mq.message; 1 package cn.iocoder.yudao.module.iot.core.mq.message;
2 2
  3 +import cn.hutool.core.map.MapUtil;
3 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; 4 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
4 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; 5 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  6 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
5 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
6 import lombok.AllArgsConstructor; 8 import lombok.AllArgsConstructor;
7 import lombok.Builder; 9 import lombok.Builder;
@@ -128,12 +130,14 @@ public class IotDeviceMessage { @@ -128,12 +130,14 @@ public class IotDeviceMessage {
128 130
129 // ========== 核心方法:在 of 基础方法之上,添加对应 method ========== 131 // ========== 核心方法:在 of 基础方法之上,添加对应 method ==========
130 132
131 - public static IotDeviceMessage buildStateOnline() {  
132 - return requestOf(IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod()); 133 + public static IotDeviceMessage buildStateUpdateOnline() {
  134 + return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(),
  135 + MapUtil.of("state", IotDeviceStateEnum.ONLINE.getState()));
133 } 136 }
134 137
135 public static IotDeviceMessage buildStateOffline() { 138 public static IotDeviceMessage buildStateOffline() {
136 - return requestOf(IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod()); 139 + return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(),
  140 + MapUtil.of("state", IotDeviceStateEnum.OFFLINE.getState()));
137 } 141 }
138 142
139 } 143 }
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java
@@ -55,11 +55,16 @@ public class IotDeviceMessageUtils { @@ -55,11 +55,16 @@ public class IotDeviceMessageUtils {
55 */ 55 */
56 @SuppressWarnings("unchecked") 56 @SuppressWarnings("unchecked")
57 public static String getIdentifier(IotDeviceMessage message) { 57 public static String getIdentifier(IotDeviceMessage message) {
  58 + if (message.getParams() == null) {
  59 + return null;
  60 + }
58 if (StrUtil.equalsAny(message.getMethod(), IotDeviceMessageMethodEnum.EVENT_POST.getMethod(), 61 if (StrUtil.equalsAny(message.getMethod(), IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),
59 - message.getMethod(), IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod())  
60 - && message.getParams() != null) { 62 + IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod())) {
61 Map<String, Object> params = (Map<String, Object>) message.getParams(); 63 Map<String, Object> params = (Map<String, Object>) message.getParams();
62 return MapUtil.getStr(params, "identifier"); 64 return MapUtil.getStr(params, "identifier");
  65 + } else if (StrUtil.equalsAny(message.getMethod(), IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod())) {
  66 + Map<String, Object> params = (Map<String, Object>) message.getParams();
  67 + return MapUtil.getStr(params, "state");
63 } 68 }
64 return null; 69 return null;
65 } 70 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/modbus/package-info.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.codec.modbus;  
2 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/simple/package-info.java 0 → 100644
  1 +/**
  2 + * TODO @芋艿:实现一个 alink 的 xml 版本
  3 + */
  4 +package cn.iocoder.yudao.module.iot.gateway.codec.simple;
0 \ No newline at end of file 5 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
1 package cn.iocoder.yudao.module.iot.gateway.config; 1 package cn.iocoder.yudao.module.iot.gateway.config;
2 2
  3 +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 4 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol; 5 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol;
5 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber; 6 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber;
6 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol; 7 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
7 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber;
8 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 9 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
  10 +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConnectionManager;
  11 +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpDownstreamHandler;
  12 +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpDownstreamSubscriber;
  13 +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpUpstreamProtocol;
  14 +import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
  15 +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
  16 +import io.vertx.core.Vertx;
9 import lombok.extern.slf4j.Slf4j; 17 import lombok.extern.slf4j.Slf4j;
10 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 18 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
11 import org.springframework.boot.context.properties.EnableConfigurationProperties; 19 import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -32,7 +40,7 @@ public class IotGatewayConfiguration { @@ -32,7 +40,7 @@ public class IotGatewayConfiguration {
32 40
33 @Bean 41 @Bean
34 public IotHttpDownstreamSubscriber iotHttpDownstreamSubscriber(IotHttpUpstreamProtocol httpUpstreamProtocol, 42 public IotHttpDownstreamSubscriber iotHttpDownstreamSubscriber(IotHttpUpstreamProtocol httpUpstreamProtocol,
35 - IotMessageBus messageBus) { 43 + IotMessageBus messageBus) {
36 return new IotHttpDownstreamSubscriber(httpUpstreamProtocol, messageBus); 44 return new IotHttpDownstreamSubscriber(httpUpstreamProtocol, messageBus);
37 } 45 }
38 } 46 }
@@ -45,21 +53,60 @@ public class IotGatewayConfiguration { @@ -45,21 +53,60 @@ public class IotGatewayConfiguration {
45 @Slf4j 53 @Slf4j
46 public static class MqttProtocolConfiguration { 54 public static class MqttProtocolConfiguration {
47 55
  56 + @Bean(destroyMethod = "close")
  57 + public Vertx emqxVertx() {
  58 + return Vertx.vertx();
  59 + }
  60 +
48 @Bean 61 @Bean
49 - public IotEmqxAuthEventProtocol iotEmqxAuthEventProtocol(IotGatewayProperties gatewayProperties) {  
50 - return new IotEmqxAuthEventProtocol(gatewayProperties.getProtocol().getEmqx()); 62 + public IotEmqxAuthEventProtocol iotEmqxAuthEventProtocol(IotGatewayProperties gatewayProperties,
  63 + Vertx emqxVertx) {
  64 + return new IotEmqxAuthEventProtocol(gatewayProperties.getProtocol().getEmqx(), emqxVertx);
51 } 65 }
52 66
53 @Bean 67 @Bean
54 - public IotEmqxUpstreamProtocol iotEmqxUpstreamProtocol(IotGatewayProperties gatewayProperties) {  
55 - return new IotEmqxUpstreamProtocol(gatewayProperties.getProtocol().getEmqx()); 68 + public IotEmqxUpstreamProtocol iotEmqxUpstreamProtocol(IotGatewayProperties gatewayProperties,
  69 + Vertx emqxVertx) {
  70 + return new IotEmqxUpstreamProtocol(gatewayProperties.getProtocol().getEmqx(), emqxVertx);
56 } 71 }
57 72
58 @Bean 73 @Bean
59 public IotEmqxDownstreamSubscriber iotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol mqttUpstreamProtocol, 74 public IotEmqxDownstreamSubscriber iotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol mqttUpstreamProtocol,
60 - IotMessageBus messageBus) { 75 + IotMessageBus messageBus) {
61 return new IotEmqxDownstreamSubscriber(mqttUpstreamProtocol, messageBus); 76 return new IotEmqxDownstreamSubscriber(mqttUpstreamProtocol, messageBus);
62 } 77 }
63 } 78 }
64 79
  80 + /**
  81 + * IoT 网关 TCP 协议配置类
  82 + */
  83 + @Configuration
  84 + @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.tcp", name = "enabled", havingValue = "true")
  85 + @Slf4j
  86 + public static class TcpProtocolConfiguration {
  87 +
  88 + // TODO @haohao:close
  89 + @Bean
  90 + public Vertx tcpVertx() {
  91 + return Vertx.vertx();
  92 + }
  93 +
  94 + @Bean
  95 + public IotTcpUpstreamProtocol iotTcpUpstreamProtocol(Vertx tcpVertx, IotGatewayProperties gatewayProperties,
  96 + IotTcpConnectionManager connectionManager,
  97 + IotDeviceMessageService messageService,
  98 + IotDeviceService deviceService, IotDeviceCommonApi deviceApi) {
  99 + return new IotTcpUpstreamProtocol(tcpVertx, gatewayProperties, connectionManager,
  100 + messageService, deviceService, deviceApi);
  101 + }
  102 +
  103 + @Bean
  104 + public IotTcpDownstreamSubscriber iotTcpDownstreamSubscriber(IotTcpUpstreamProtocol tcpUpstreamProtocol,
  105 + IotMessageBus messageBus,
  106 + IotTcpDownstreamHandler downstreamHandler) {
  107 + return new IotTcpDownstreamSubscriber(tcpUpstreamProtocol, messageBus, downstreamHandler);
  108 + }
  109 +
  110 + }
  111 +
65 } 112 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
@@ -78,6 +78,11 @@ public class IotGatewayProperties { @@ -78,6 +78,11 @@ public class IotGatewayProperties {
78 */ 78 */
79 private EmqxProperties emqx; 79 private EmqxProperties emqx;
80 80
  81 + /**
  82 + * TCP 组件配置
  83 + */
  84 + private TcpProperties tcp;
  85 +
81 } 86 }
82 87
83 @Data 88 @Data
@@ -96,6 +101,27 @@ public class IotGatewayProperties { @@ -96,6 +101,27 @@ public class IotGatewayProperties {
96 } 101 }
97 102
98 @Data 103 @Data
  104 + public static class TcpProperties {
  105 +
  106 + /**
  107 + * 是否开启
  108 + */
  109 + @NotNull(message = "是否开启不能为空")
  110 + private Boolean enabled;
  111 + // TODO @haohao:加个默认值?
  112 + /**
  113 + * 服务端口
  114 + */
  115 + private Integer serverPort;
  116 + // TODO @haohao:应该不用?一般都监听 0.0.0.0 哈;
  117 + /**
  118 + * 服务主机
  119 + */
  120 + private String serverHost;
  121 +
  122 + }
  123 +
  124 + @Data
99 public static class EmqxProperties { 125 public static class EmqxProperties {
100 126
101 /** 127 /**
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxAuthEventProtocol.java
@@ -28,20 +28,20 @@ public class IotEmqxAuthEventProtocol { @@ -28,20 +28,20 @@ public class IotEmqxAuthEventProtocol {
28 28
29 private final String serverId; 29 private final String serverId;
30 30
31 - private Vertx vertx; 31 + private final Vertx vertx;
32 32
33 private HttpServer httpServer; 33 private HttpServer httpServer;
34 34
35 - public IotEmqxAuthEventProtocol(IotGatewayProperties.EmqxProperties emqxProperties) { 35 + public IotEmqxAuthEventProtocol(IotGatewayProperties.EmqxProperties emqxProperties,
  36 + Vertx vertx) {
36 this.emqxProperties = emqxProperties; 37 this.emqxProperties = emqxProperties;
  38 + this.vertx = vertx;
37 this.serverId = IotDeviceMessageUtils.generateServerId(emqxProperties.getMqttPort()); 39 this.serverId = IotDeviceMessageUtils.generateServerId(emqxProperties.getMqttPort());
38 } 40 }
39 41
40 @PostConstruct 42 @PostConstruct
41 public void start() { 43 public void start() {
42 try { 44 try {
43 - // 创建 Vertx 实例  
44 - this.vertx = Vertx.vertx();  
45 startHttpServer(); 45 startHttpServer();
46 log.info("[start][IoT 网关 EMQX 认证事件协议服务启动成功, 端口: {}]", emqxProperties.getHttpPort()); 46 log.info("[start][IoT 网关 EMQX 认证事件协议服务启动成功, 端口: {}]", emqxProperties.getHttpPort());
47 } catch (Exception e) { 47 } catch (Exception e) {
@@ -53,17 +53,6 @@ public class IotEmqxAuthEventProtocol { @@ -53,17 +53,6 @@ public class IotEmqxAuthEventProtocol {
53 @PreDestroy 53 @PreDestroy
54 public void stop() { 54 public void stop() {
55 stopHttpServer(); 55 stopHttpServer();
56 -  
57 - // 关闭 Vertx 实例  
58 - if (vertx != null) {  
59 - try {  
60 - vertx.close();  
61 - log.debug("[stop][Vertx 实例已关闭]");  
62 - } catch (Exception e) {  
63 - log.warn("[stop][关闭 Vertx 实例失败]", e);  
64 - }  
65 - }  
66 -  
67 log.info("[stop][IoT 网关 EMQX 认证事件协议服务已停止]"); 56 log.info("[stop][IoT 网关 EMQX 认证事件协议服务已停止]");
68 } 57 }
69 58
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxUpstreamProtocol.java
@@ -10,7 +10,6 @@ import io.vertx.mqtt.MqttClient; @@ -10,7 +10,6 @@ import io.vertx.mqtt.MqttClient;
10 import io.vertx.mqtt.MqttClientOptions; 10 import io.vertx.mqtt.MqttClientOptions;
11 import jakarta.annotation.PostConstruct; 11 import jakarta.annotation.PostConstruct;
12 import jakarta.annotation.PreDestroy; 12 import jakarta.annotation.PreDestroy;
13 -import jodd.util.ThreadUtil;  
14 import lombok.Getter; 13 import lombok.Getter;
15 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
16 15
@@ -32,7 +31,7 @@ public class IotEmqxUpstreamProtocol { @@ -32,7 +31,7 @@ public class IotEmqxUpstreamProtocol {
32 31
33 private volatile boolean isRunning = false; 32 private volatile boolean isRunning = false;
34 33
35 - private Vertx vertx; 34 + private final Vertx vertx;
36 35
37 @Getter 36 @Getter
38 private final String serverId; 37 private final String serverId;
@@ -41,9 +40,11 @@ public class IotEmqxUpstreamProtocol { @@ -41,9 +40,11 @@ public class IotEmqxUpstreamProtocol {
41 40
42 private IotEmqxUpstreamHandler upstreamHandler; 41 private IotEmqxUpstreamHandler upstreamHandler;
43 42
44 - public IotEmqxUpstreamProtocol(IotGatewayProperties.EmqxProperties emqxProperties) { 43 + public IotEmqxUpstreamProtocol(IotGatewayProperties.EmqxProperties emqxProperties,
  44 + Vertx vertx) {
45 this.emqxProperties = emqxProperties; 45 this.emqxProperties = emqxProperties;
46 this.serverId = IotDeviceMessageUtils.generateServerId(emqxProperties.getMqttPort()); 46 this.serverId = IotDeviceMessageUtils.generateServerId(emqxProperties.getMqttPort());
  47 + this.vertx = vertx;
47 } 48 }
48 49
49 @PostConstruct 50 @PostConstruct
@@ -53,13 +54,10 @@ public class IotEmqxUpstreamProtocol { @@ -53,13 +54,10 @@ public class IotEmqxUpstreamProtocol {
53 } 54 }
54 55
55 try { 56 try {
56 - // 1. 初始化 Vertx 实例  
57 - this.vertx = Vertx.vertx();  
58 -  
59 - // 2. 启动 MQTT 客户端 57 + // 1. 启动 MQTT 客户端
60 startMqttClient(); 58 startMqttClient();
61 59
62 - // 3. 标记服务为运行状态 60 + // 2. 标记服务为运行状态
63 isRunning = true; 61 isRunning = true;
64 log.info("[start][IoT 网关 EMQX 协议启动成功]"); 62 log.info("[start][IoT 网关 EMQX 协议启动成功]");
65 } catch (Exception e) { 63 } catch (Exception e) {
@@ -67,10 +65,16 @@ public class IotEmqxUpstreamProtocol { @@ -67,10 +65,16 @@ public class IotEmqxUpstreamProtocol {
67 stop(); 65 stop();
68 66
69 // 异步关闭应用 67 // 异步关闭应用
70 - // TODO haohao:是不是不用 sleep 也行哈?  
71 Thread shutdownThread = new Thread(() -> { 68 Thread shutdownThread = new Thread(() -> {
72 - ThreadUtil.sleep(1000);  
73 - log.error("[start][由于 MQTT 连接失败,正在关闭应用]"); 69 + try {
  70 + // 确保日志输出完成,使用更优雅的方式
  71 + log.error("[start][由于 MQTT 连接失败,正在关闭应用]");
  72 + // 等待日志输出完成
  73 + Thread.sleep(1000);
  74 + } catch (InterruptedException ie) {
  75 + Thread.currentThread().interrupt();
  76 + log.warn("[start][应用关闭被中断]");
  77 + }
74 System.exit(1); 78 System.exit(1);
75 }); 79 });
76 shutdownThread.setDaemon(true); 80 shutdownThread.setDaemon(true);
@@ -90,16 +94,7 @@ public class IotEmqxUpstreamProtocol { @@ -90,16 +94,7 @@ public class IotEmqxUpstreamProtocol {
90 // 1. 停止 MQTT 客户端 94 // 1. 停止 MQTT 客户端
91 stopMqttClient(); 95 stopMqttClient();
92 96
93 - // 2. 关闭 Vertx 实例  
94 - if (vertx != null) {  
95 - try {  
96 - vertx.close();  
97 - } catch (Exception e) {  
98 - log.warn("[stop][关闭 Vertx 实例失败]", e);  
99 - }  
100 - }  
101 -  
102 - // 3. 标记服务为停止状态 97 + // 2. 标记服务为停止状态
103 isRunning = false; 98 isRunning = false;
104 log.info("[stop][IoT 网关 MQTT 协议服务已停止]"); 99 log.info("[stop][IoT 网关 MQTT 协议服务已停止]");
105 } 100 }
@@ -147,7 +142,7 @@ public class IotEmqxUpstreamProtocol { @@ -147,7 +142,7 @@ public class IotEmqxUpstreamProtocol {
147 142
148 // 2. 等待连接结果 143 // 2. 等待连接结果
149 try { 144 try {
150 - // TODO @haohao:想了下,timeout 可以不设置,全靠 mqttclient 的超时时间? 145 + // 应用层超时控制:防止启动过程无限阻塞,与MQTT客户端的网络超时是不同层次的控制
151 boolean awaitResult = latch.await(10, java.util.concurrent.TimeUnit.SECONDS); 146 boolean awaitResult = latch.await(10, java.util.concurrent.TimeUnit.SECONDS);
152 if (!awaitResult) { 147 if (!awaitResult) {
153 log.error("[connectMqttSync][等待连接结果超时]"); 148 log.error("[connectMqttSync][等待连接结果超时]");
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxAuthEventHandler.java
@@ -209,7 +209,7 @@ public class IotEmqxAuthEventHandler { @@ -209,7 +209,7 @@ public class IotEmqxAuthEventHandler {
209 209
210 try { 210 try {
211 // 2. 构建设备状态消息 211 // 2. 构建设备状态消息
212 - IotDeviceMessage message = online ? IotDeviceMessage.buildStateOnline() 212 + IotDeviceMessage message = online ? IotDeviceMessage.buildStateUpdateOnline()
213 : IotDeviceMessage.buildStateOffline(); 213 : IotDeviceMessage.buildStateOffline();
214 214
215 // 3. 发送设备状态消息 215 // 3. 发送设备状态消息
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxDownstreamHandler.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router;
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.hutool.extra.spring.SpringUtil; 4 import cn.hutool.extra.spring.SpringUtil;
5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
6 -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;  
7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
9 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
@@ -54,7 +53,8 @@ public class IotEmqxDownstreamHandler { @@ -54,7 +53,8 @@ public class IotEmqxDownstreamHandler {
54 return; 53 return;
55 } 54 }
56 // 2.2 构建载荷 55 // 2.2 构建载荷
57 - byte[] payload = deviceMessageService.encodeDeviceMessage(message, deviceInfo.getProductKey(), deviceInfo.getDeviceName()); 56 + byte[] payload = deviceMessageService.encodeDeviceMessage(message, deviceInfo.getProductKey(),
  57 + deviceInfo.getDeviceName());
58 // 2.3 发布消息 58 // 2.3 发布消息
59 protocol.publishMessage(topic, payload); 59 protocol.publishMessage(topic, payload);
60 } 60 }
@@ -68,35 +68,10 @@ public class IotEmqxDownstreamHandler { @@ -68,35 +68,10 @@ public class IotEmqxDownstreamHandler {
68 * @return 构建的主题,如果方法不支持返回 null 68 * @return 构建的主题,如果方法不支持返回 null
69 */ 69 */
70 private String buildTopicByMethod(IotDeviceMessage message, String productKey, String deviceName) { 70 private String buildTopicByMethod(IotDeviceMessage message, String productKey, String deviceName) {
71 - // 1. 解析消息方法  
72 - IotDeviceMessageMethodEnum methodEnum = IotDeviceMessageMethodEnum.of(message.getMethod());  
73 - if (methodEnum == null) {  
74 - log.warn("[buildTopicByMethod][未知的消息方法: {}]", message.getMethod());  
75 - return null;  
76 - }  
77 -  
78 - // 2. 根据消息方法和回复状态,构建 topic 71 + // 1. 判断是否为回复消息
79 boolean isReply = IotDeviceMessageUtils.isReplyMessage(message); 72 boolean isReply = IotDeviceMessageUtils.isReplyMessage(message);
80 -  
81 - // TODO @芋艿:需要添加对应的 Topic,所以需要先判断消息方法类型  
82 - // TODO @haohao:基于 method,然后逆推对应的 topic,可以哇?约定好~  
83 - // 根据消息方法和回复状态构建对应的主题  
84 - switch (methodEnum) {  
85 - case PROPERTY_POST:  
86 - if (isReply) {  
87 - return IotMqttTopicUtils.buildPropertyPostReplyTopic(productKey, deviceName);  
88 - }  
89 - break;  
90 - case PROPERTY_SET:  
91 - if (!isReply) {  
92 - return IotMqttTopicUtils.buildPropertySetTopic(productKey, deviceName);  
93 - }  
94 - break;  
95 - }  
96 -  
97 - log.warn("[buildTopicByMethod][暂时不支持的下行消息: method={}, isReply={}]",  
98 - message.getMethod(), isReply);  
99 - return null; 73 + // 2. 根据消息方法类型构建对应的主题
  74 + return IotMqttTopicUtils.buildTopicByMethod(message.getMethod(), productKey, deviceName, isReply);
100 } 75 }
101 76
102 } 77 }
103 \ No newline at end of file 78 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java
@@ -78,7 +78,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -78,7 +78,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
78 Assert.notBlank(token, "生成 token 不能为空位"); 78 Assert.notBlank(token, "生成 token 不能为空位");
79 79
80 // 3. 执行上线 80 // 3. 执行上线
81 - IotDeviceMessage message = IotDeviceMessage.buildStateOnline(); 81 + IotDeviceMessage message = IotDeviceMessage.buildStateUpdateOnline();
82 deviceMessageService.sendDeviceMessage(message, 82 deviceMessageService.sendDeviceMessage(message,
83 deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId()); 83 deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId());
84 84
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpConnectionManager.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;
  2 +
  3 +import io.vertx.core.net.NetSocket;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +import org.springframework.stereotype.Component;
  6 +
  7 +import java.util.concurrent.ConcurrentHashMap;
  8 +import java.util.concurrent.ConcurrentMap;
  9 +
  10 +/**
  11 + * IoT TCP 连接管理器
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Component
  16 +@Slf4j
  17 +public class IotTcpConnectionManager {
  18 +
  19 + // TODO @haohao:要考虑,相同设备,多次连接的情况哇?
  20 + /**
  21 + * 连接集合
  22 + *
  23 + * key:设备唯一标识
  24 + */
  25 + private final ConcurrentMap<String, NetSocket> connectionMap = new ConcurrentHashMap<>();
  26 +
  27 + /**
  28 + * 添加一个新连接
  29 + *
  30 + * @param deviceId 设备唯一标识
  31 + * @param socket Netty Channel
  32 + */
  33 + public void addConnection(String deviceId, NetSocket socket) {
  34 + log.info("[addConnection][设备({}) 连接({})]", deviceId, socket.remoteAddress());
  35 + connectionMap.put(deviceId, socket);
  36 + }
  37 +
  38 + /**
  39 + * 根据设备 ID 获取连接
  40 + *
  41 + * @param deviceId 设备 ID
  42 + * @return 连接
  43 + */
  44 + public NetSocket getConnection(String deviceId) {
  45 + return connectionMap.get(deviceId);
  46 + }
  47 +
  48 + /**
  49 + * 移除指定连接
  50 + *
  51 + * @param socket Netty Channel
  52 + */
  53 + public void removeConnection(NetSocket socket) {
  54 + // TODO @haohao:vertx 的 socket,有没办法设置一些属性,类似 netty 的;目的是,避免遍历 connectionMap 去操作哈;
  55 + connectionMap.entrySet().stream()
  56 + .filter(entry -> entry.getValue().equals(socket))
  57 + .findFirst()
  58 + .ifPresent(entry -> {
  59 + log.info("[removeConnection][设备({}) 断开连接({})]", entry.getKey(), socket.remoteAddress());
  60 + connectionMap.remove(entry.getKey());
  61 + });
  62 + }
  63 +
  64 +}
0 \ No newline at end of file 65 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpDownstreamSubscriber.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  4 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
  5 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  6 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  7 +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpDownstreamHandler;
  8 +import jakarta.annotation.PostConstruct;
  9 +import lombok.RequiredArgsConstructor;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +
  12 +/**
  13 + * IoT 网关 TCP 订阅者:接收下行给设备的消息
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@RequiredArgsConstructor
  18 +@Slf4j
  19 +public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
  20 +
  21 + private final IotTcpUpstreamProtocol protocol;
  22 +
  23 + private final IotMessageBus messageBus;
  24 +
  25 + private final IotTcpDownstreamHandler downstreamHandler;
  26 +
  27 + @PostConstruct
  28 + public void init() {
  29 + messageBus.register(this);
  30 + }
  31 +
  32 + @Override
  33 + public String getTopic() {
  34 + return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());
  35 + }
  36 +
  37 + @Override
  38 + public String getGroup() {
  39 + // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group
  40 + return getTopic();
  41 + }
  42 +
  43 + @Override
  44 + public void onMessage(IotDeviceMessage message) {
  45 + log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]",
  46 + message.getId(), message.getMethod(), message.getDeviceId());
  47 + try {
  48 + // 1. 校验
  49 + String method = message.getMethod();
  50 + if (method == null) {
  51 + log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]",
  52 + message.getId(), message.getDeviceId());
  53 + return;
  54 + }
  55 +
  56 + // 2. 处理下行消息
  57 + downstreamHandler.handle(message);
  58 + } catch (Exception e) {
  59 + log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]",
  60 + message.getId(), message.getMethod(), message.getDeviceId(), e);
  61 + }
  62 + }
  63 +
  64 +}
0 \ No newline at end of file 65 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpUpstreamProtocol.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
  4 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  5 +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  6 +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpConnectionHandler;
  7 +import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
  8 +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
  9 +import io.vertx.core.Vertx;
  10 +import io.vertx.core.net.NetServer;
  11 +import jakarta.annotation.PostConstruct;
  12 +import jakarta.annotation.PreDestroy;
  13 +import lombok.Getter;
  14 +import lombok.RequiredArgsConstructor;
  15 +import lombok.extern.slf4j.Slf4j;
  16 +
  17 +/**
  18 + * IoT 网关 TCP 协议:接收设备上行消息
  19 + *
  20 + * @author 芋道源码
  21 + */
  22 +@Slf4j
  23 +@RequiredArgsConstructor
  24 +public class IotTcpUpstreamProtocol {
  25 +
  26 + private final Vertx vertx;
  27 +
  28 + private final IotGatewayProperties gatewayProperties;
  29 +
  30 + private final IotTcpConnectionManager connectionManager;
  31 +
  32 + private final IotDeviceMessageService messageService;
  33 +
  34 + private final IotDeviceService deviceService;
  35 +
  36 + private final IotDeviceCommonApi deviceApi;
  37 +
  38 + @Getter
  39 + private String serverId;
  40 +
  41 + private NetServer netServer;
  42 +
  43 + @PostConstruct
  44 + public void start() {
  45 + // 1. 初始化参数
  46 + IotGatewayProperties.TcpProperties tcpProperties = gatewayProperties.getProtocol().getTcp();
  47 + this.serverId = IotDeviceMessageUtils.generateServerId(tcpProperties.getServerPort());
  48 +
  49 + // 2. 创建 TCP 服务器
  50 + netServer = vertx.createNetServer();
  51 + netServer.connectHandler(socket -> {
  52 + new IotTcpConnectionHandler(socket, connectionManager,
  53 + messageService, deviceService, deviceApi, serverId).start();
  54 + });
  55 +
  56 + // 3. 启动 TCP 服务器
  57 + netServer.listen(tcpProperties.getServerPort(), tcpProperties.getServerHost())
  58 + .onSuccess(server -> log.info("[start][IoT 网关 TCP 服务启动成功,端口:{}]", server.actualPort()))
  59 + .onFailure(e -> log.error("[start][IoT 网关 TCP 服务启动失败]", e));
  60 + }
  61 +
  62 + @PreDestroy
  63 + public void stop() {
  64 + if (netServer != null) {
  65 + netServer.close()
  66 + .onSuccess(v -> log.info("[stop][IoT 网关 TCP 服务已停止]"))
  67 + .onFailure(e -> log.error("[stop][IoT 网关 TCP 服务停止失败]", e));
  68 + }
  69 + }
  70 +
  71 +}
0 \ No newline at end of file 72 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpConnectionHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router;
  2 +
  3 +import cn.hutool.core.util.BooleanUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
  6 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
  7 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
  8 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  9 +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConnectionManager;
  10 +import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
  11 +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
  12 +import io.vertx.core.Handler;
  13 +import io.vertx.core.buffer.Buffer;
  14 +import io.vertx.core.net.NetSocket;
  15 +import io.vertx.core.parsetools.RecordParser;
  16 +import lombok.RequiredArgsConstructor;
  17 +import lombok.extern.slf4j.Slf4j;
  18 +
  19 +/**
  20 + * IoT TCP 连接处理器
  21 + * <p>
  22 + * 核心负责:
  23 + * 1. 【认证】创建连接后,设备需要发送认证消息,认证通过后,才能进行后续的通信
  24 + * 2. 【消息处理】接收设备发送的消息,解码后,发送到消息队列
  25 + * 3. 【断开】设备断开连接后,清理资源
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +@RequiredArgsConstructor
  30 +@Slf4j
  31 +public class IotTcpConnectionHandler implements Handler<Buffer> {
  32 +
  33 + private final NetSocket socket;
  34 + /**
  35 + * 是否已认证
  36 + */
  37 + private boolean authenticated = false;
  38 + /**
  39 + * 设备信息
  40 + */
  41 + private IotDeviceRespDTO device;
  42 +
  43 + private final IotTcpConnectionManager connectionManager;
  44 +
  45 + private final IotDeviceMessageService messageService;
  46 +
  47 + private final IotDeviceService deviceService;
  48 +
  49 + private final IotDeviceCommonApi deviceApi;
  50 +
  51 + private final String serverId;
  52 +
  53 + public void start() {
  54 + // 1. 设置解析器
  55 + final RecordParser parser = RecordParser.newDelimited("\n", this);
  56 + socket.handler(parser);
  57 +
  58 + // 2. 设置处理器
  59 + socket.closeHandler(v -> handleConnectionClose());
  60 + socket.exceptionHandler(this::handleException);
  61 + }
  62 +
  63 + @Override
  64 + public void handle(Buffer buffer) {
  65 + log.info("[handle][接收到数据: {}]", buffer);
  66 + try {
  67 + // TODO @haohao:可以调研下,做个对比表格哈;
  68 + // 1. 处理认证
  69 + if (!authenticated) {
  70 + handleAuthentication(buffer);
  71 + return;
  72 + }
  73 + // 2. 处理消息
  74 + handleMessage(buffer);
  75 + } catch (Exception e) {
  76 + log.error("[handle][处理异常]", e);
  77 + socket.close();
  78 + }
  79 + }
  80 +
  81 + private void handleAuthentication(Buffer buffer) {
  82 + // 1. 解析认证信息
  83 + // TODO @芋艿:这里的认证协议,需要和设备端约定。默认为 productKey,deviceName,password
  84 + // TODO @haohao:这里,要不也 json 解析?类似 http 是 {
  85 + // "clientId": "4aymZgOTOOCrDKRT.small",
  86 + // "username": "small&4aymZgOTOOCrDKRT",
  87 + // "password": "509e2b08f7598eb139d276388c600435913ba4c94cd0d50aebc5c0d1855bcb75"
  88 + //}
  89 + String[] parts = buffer.toString().split(",");
  90 + if (parts.length != 3) {
  91 + log.error("[handleAuthentication][认证信息({})格式不正确]", buffer);
  92 + socket.close();
  93 + return;
  94 + }
  95 + String productKey = parts[0];
  96 + String deviceName = parts[1];
  97 + String password = parts[2];
  98 +
  99 + // 2. 执行认证
  100 + CommonResult<Boolean> authResult = deviceApi.authDevice(new IotDeviceAuthReqDTO()
  101 + .setClientId(socket.remoteAddress().toString()).setUsername(productKey + "/" + deviceName)
  102 + .setPassword(password));
  103 + if (authResult.isError() || !BooleanUtil.isTrue(authResult.getData())) {
  104 + log.error("[handleAuthentication][认证失败,productKey({}) deviceName({}) password({})]", productKey, deviceName,
  105 + password);
  106 + socket.close();
  107 + return;
  108 + }
  109 +
  110 + // 3. 认证成功
  111 + this.authenticated = true;
  112 + this.device = deviceService.getDeviceFromCache(productKey, deviceName);
  113 + connectionManager.addConnection(String.valueOf(device.getId()), socket);
  114 +
  115 + // 4. 发送上线消息
  116 + IotDeviceMessage message = IotDeviceMessage.buildStateUpdateOnline();
  117 + messageService.sendDeviceMessage(message, productKey, deviceName, serverId);
  118 + log.info("[handleAuthentication][认证成功]");
  119 + }
  120 +
  121 + private void handleMessage(Buffer buffer) {
  122 + // 1. 解码消息
  123 + IotDeviceMessage message = messageService.decodeDeviceMessage(buffer.getBytes(),
  124 + device.getProductKey(), device.getDeviceName());
  125 + if (message == null) {
  126 + log.warn("[handleMessage][解码消息失败]");
  127 + return;
  128 + }
  129 + // 2. 发送消息到队列
  130 + messageService.sendDeviceMessage(message, device.getProductKey(), device.getDeviceName(), serverId);
  131 + }
  132 +
  133 + private void handleConnectionClose() {
  134 + // 1. 移除连接
  135 + connectionManager.removeConnection(socket);
  136 + // 2. 发送离线消息
  137 + if (device != null) {
  138 + IotDeviceMessage message = IotDeviceMessage.buildStateOffline();
  139 + messageService.sendDeviceMessage(message, device.getProductKey(), device.getDeviceName(), serverId);
  140 + }
  141 + }
  142 +
  143 + private void handleException(Throwable e) {
  144 + log.error("[handleException][连接({}) 发生异常]", socket.remoteAddress(), e);
  145 + socket.close();
  146 + }
  147 +
  148 +}
0 \ No newline at end of file 149 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpDownstreamHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConnectionManager;
  5 +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
  6 +import io.vertx.core.buffer.Buffer;
  7 +import io.vertx.core.net.NetSocket;
  8 +import lombok.RequiredArgsConstructor;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.stereotype.Component;
  11 +
  12 +/**
  13 + * IoT 网关 TCP 下行消息处理器
  14 + * <p>
  15 + * 从消息总线接收到下行消息,然后发布到 TCP 连接,从而被设备所接收
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +@Slf4j
  20 +@Component
  21 +@RequiredArgsConstructor
  22 +public class IotTcpDownstreamHandler {
  23 +
  24 + private final IotTcpConnectionManager connectionManager;
  25 +
  26 + private final IotDeviceMessageService messageService;
  27 +
  28 + /**
  29 + * 处理下行消息
  30 + *
  31 + * @param message 设备消息
  32 + */
  33 + public void handle(IotDeviceMessage message) {
  34 + // 1. 获取设备对应的连接
  35 + NetSocket socket = connectionManager.getConnection(String.valueOf(message.getDeviceId()));
  36 + if (socket == null) {
  37 + log.error("[handle][设备({})的连接不存在]", message.getDeviceId());
  38 + return;
  39 + }
  40 +
  41 + // 2. 编码消息
  42 + byte[] bytes = messageService.encodeDeviceMessage(message, null, null);
  43 +
  44 + // 3. 发送消息
  45 + socket.write(Buffer.buffer(bytes));
  46 + // TODO @芋艿:这里的换行符,需要和设备端约定
  47 + // TODO @haohao:tcp 要不定长?很少 \n 哈。然后有个 magic number;可以参考 dubbo rpc;
  48 + socket.write("\n");
  49 + }
  50 +
  51 +}
0 \ No newline at end of file 52 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/util/IotMqttTopicUtils.java
1 package cn.iocoder.yudao.module.iot.gateway.util; 1 package cn.iocoder.yudao.module.iot.gateway.util;
2 2
  3 +import cn.hutool.core.util.StrUtil;
  4 +
3 /** 5 /**
4 * IoT 网关 MQTT 主题工具类 6 * IoT 网关 MQTT 主题工具类
5 * <p> 7 * <p>
@@ -17,9 +19,9 @@ public final class IotMqttTopicUtils { @@ -17,9 +19,9 @@ public final class IotMqttTopicUtils {
17 private static final String SYS_TOPIC_PREFIX = "/sys/"; 19 private static final String SYS_TOPIC_PREFIX = "/sys/";
18 20
19 /** 21 /**
20 - * 服务调用主题前缀 22 + * 回复主题后缀
21 */ 23 */
22 - private static final String SERVICE_TOPIC_PREFIX = "/thing/"; 24 + private static final String REPLY_TOPIC_SUFFIX = "_reply";
23 25
24 // ========== MQTT HTTP 接口路径常量 ========== 26 // ========== MQTT HTTP 接口路径常量 ==========
25 27
@@ -36,59 +38,29 @@ public final class IotMqttTopicUtils { @@ -36,59 +38,29 @@ public final class IotMqttTopicUtils {
36 */ 38 */
37 public static final String MQTT_EVENT_PATH = "/mqtt/event"; 39 public static final String MQTT_EVENT_PATH = "/mqtt/event";
38 40
39 - /**  
40 - * MQTT 授权接口路径(预留)  
41 - * 对应 EMQX HTTP 授权插件的授权检查接口  
42 - */  
43 - public static final String MQTT_AUTHZ_PATH = "/mqtt/authz";  
44 -  
45 // ========== 工具方法 ========== 41 // ========== 工具方法 ==========
46 42
47 /** 43 /**
48 - * 构建设备主题前缀 44 + * 根据消息方法构建对应的主题
49 * 45 *
  46 + * @param method 消息方法,例如 thing.property.post
50 * @param productKey 产品 Key 47 * @param productKey 产品 Key
51 * @param deviceName 设备名称 48 * @param deviceName 设备名称
52 - * @return 设备主题前缀:/sys/{productKey}/{deviceName}  
53 - */  
54 - private static String buildDeviceTopicPrefix(String productKey, String deviceName) {  
55 - return SYS_TOPIC_PREFIX + productKey + "/" + deviceName;  
56 - }  
57 -  
58 - /**  
59 - * 构建设备属性设置主题  
60 - *  
61 - * @param productKey 产品 Key  
62 - * @param deviceName 设备名称  
63 - * @return 完整的主题路径  
64 - */  
65 - public static String buildPropertySetTopic(String productKey, String deviceName) {  
66 - return buildDeviceTopicPrefix(productKey, deviceName) + "/thing/property/set";  
67 - }  
68 -  
69 - /**  
70 - * 构建设备属性上报回复主题  
71 - * <p>  
72 - * 当设备上报属性时,会收到该主题的回复  
73 - *  
74 - * @param productKey 产品 Key  
75 - * @param deviceName 设备名称  
76 - * @return 完整的主题路径  
77 - */  
78 - public static String buildPropertyPostReplyTopic(String productKey, String deviceName) {  
79 - return buildDeviceTopicPrefix(productKey, deviceName) + "/thing/property/post_reply";  
80 - }  
81 -  
82 - /**  
83 - * 构建设备服务调用主题  
84 - *  
85 - * @param productKey 产品 Key  
86 - * @param deviceName 设备名称  
87 - * @param serviceIdentifier 服务标识符 49 + * @param isReply 是否为回复消息
88 * @return 完整的主题路径 50 * @return 完整的主题路径
89 */ 51 */
90 - public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) {  
91 - return buildDeviceTopicPrefix(productKey, deviceName) + SERVICE_TOPIC_PREFIX + serviceIdentifier; 52 + public static String buildTopicByMethod(String method, String productKey, String deviceName, boolean isReply) {
  53 + if (StrUtil.isBlank(method)) {
  54 + return null;
  55 + }
  56 + // 1. 将点分隔符转换为斜杠
  57 + String topicSuffix = method.replace('.', '/');
  58 + // 2. 对于回复消息,添加 _reply 后缀
  59 + if (isReply) {
  60 + topicSuffix += REPLY_TOPIC_SUFFIX;
  61 + }
  62 + // 3. 构建完整主题
  63 + return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + "/" + topicSuffix;
92 } 64 }
93 65
94 } 66 }
95 \ No newline at end of file 67 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application-local.yaml
1 # ==================== IoT 网关本地开发环境配置 ==================== 1 # ==================== IoT 网关本地开发环境配置 ====================
2 -  
3 --- #################### 消息队列相关 #################### 2 --- #################### 消息队列相关 ####################
4 3
5 # rocketmq 配置项,对应 RocketMQProperties 配置类 4 # rocketmq 配置项,对应 RocketMQProperties 配置类
@@ -41,6 +40,13 @@ yudao: @@ -41,6 +40,13 @@ yudao:
41 mqtt-ssl: false # 是否开启 SSL 40 mqtt-ssl: false # 是否开启 SSL
42 mqtt-topics: 41 mqtt-topics:
43 - "/sys/#" # 系统主题 42 - "/sys/#" # 系统主题
  43 + # ====================================
  44 + # 针对引入的 TCP 组件的配置
  45 + # ====================================
  46 + tcp:
  47 + enabled: true
  48 + server-port: 8093
  49 + server-host: 0.0.0.0
44 50
45 # 消息总线配置 51 # 消息总线配置
46 message-bus: 52 message-bus:
@@ -52,7 +58,7 @@ yudao: @@ -52,7 +58,7 @@ yudao:
52 logging: 58 logging:
53 level: 59 level:
54 # 开发环境详细日志 60 # 开发环境详细日志
55 - cn.iocoder.yudao.module.iot.gateway.protocol.mqtt: DEBUG 61 + cn.iocoder.yudao.module.iot.gateway.protocol.emqx: DEBUG
56 cn.iocoder.yudao.module.iot.gateway.protocol.http: DEBUG 62 cn.iocoder.yudao.module.iot.gateway.protocol.http: DEBUG
57 # MQTT 客户端日志 63 # MQTT 客户端日志
58 # io.vertx.mqtt: DEBUG 64 # io.vertx.mqtt: DEBUG
59 \ No newline at end of file 65 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml
@@ -41,6 +41,11 @@ yudao: @@ -41,6 +41,11 @@ yudao:
41 mqtt-ssl: false 41 mqtt-ssl: false
42 mqtt-topics: 42 mqtt-topics:
43 - "/sys/#" # 系统主题 43 - "/sys/#" # 系统主题
  44 + # ====================================
  45 + # 针对引入的 TCP 组件的配置
  46 + # ====================================
  47 + tcp:
  48 + enabled: false
44 49
45 # 消息总线配置 50 # 消息总线配置
46 message-bus: 51 message-bus: