Commit eb7df7157c11c788bf40dd3feb88dceb71b636da

Authored by 芋道源码
Committed by Gitee
2 parents bed73351 895b6c29

!1398 perf:【IoT 物联网】场景联动代码优化

Merge pull request !1398 from puhui999/feature/iot
Showing 18 changed files with 836 additions and 141 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java
... ... @@ -8,13 +8,12 @@ import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePa
8 8 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneRespVO;
9 9 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO;
10 10 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneUpdateStatusReqVO;
11   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  11 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
12 12 import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService;
13 13 import io.swagger.v3.oas.annotations.Operation;
14 14 import io.swagger.v3.oas.annotations.Parameter;
15 15 import io.swagger.v3.oas.annotations.tags.Tag;
16 16 import jakarta.annotation.Resource;
17   -import jakarta.annotation.security.PermitAll;
18 17 import jakarta.validation.Valid;
19 18 import org.springframework.security.access.prepost.PreAuthorize;
20 19 import org.springframework.validation.annotation.Validated;
... ... @@ -71,7 +70,7 @@ public class IotRuleSceneController {
71 70 @Parameter(name = "id", description = "编号", required = true, example = "1024")
72 71 @PreAuthorize("@ss.hasPermission('iot:rule-scene:query')")
73 72 public CommonResult<IotRuleSceneRespVO> getRuleScene(@RequestParam("id") Long id) {
74   - IotRuleSceneDO ruleScene = ruleSceneService.getRuleScene(id);
  73 + IotSceneRuleDO ruleScene = ruleSceneService.getRuleScene(id);
75 74 return success(BeanUtils.toBean(ruleScene, IotRuleSceneRespVO.class));
76 75 }
77 76  
... ... @@ -79,22 +78,16 @@ public class IotRuleSceneController {
79 78 @Operation(summary = "获得场景联动分页")
80 79 @PreAuthorize("@ss.hasPermission('iot:rule-scene:query')")
81 80 public CommonResult<PageResult<IotRuleSceneRespVO>> getRuleScenePage(@Valid IotRuleScenePageReqVO pageReqVO) {
82   - PageResult<IotRuleSceneDO> pageResult = ruleSceneService.getRuleScenePage(pageReqVO);
  81 + PageResult<IotSceneRuleDO> pageResult = ruleSceneService.getRuleScenePage(pageReqVO);
83 82 return success(BeanUtils.toBean(pageResult, IotRuleSceneRespVO.class));
84 83 }
85 84  
86 85 @GetMapping("/simple-list")
87 86 @Operation(summary = "获取场景联动的精简信息列表", description = "主要用于前端的下拉选项")
88 87 public CommonResult<List<IotRuleSceneRespVO>> getRuleSceneSimpleList() {
89   - List<IotRuleSceneDO> list = ruleSceneService.getRuleSceneListByStatus(CommonStatusEnum.ENABLE.getStatus());
  88 + List<IotSceneRuleDO> list = ruleSceneService.getRuleSceneListByStatus(CommonStatusEnum.ENABLE.getStatus());
90 89 return success(convertList(list, scene -> // 只返回 id、name 字段
91 90 new IotRuleSceneRespVO().setId(scene.getId()).setName(scene.getName())));
92 91 }
93 92  
94   - @GetMapping("/test")
95   - @PermitAll
96   - public void test() {
97   - ruleSceneService.test();
98   - }
99   -
100 93 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleSceneRespVO.java
1 1 package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;
2 2  
3   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  3 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
4 4 import io.swagger.v3.oas.annotations.media.Schema;
5 5 import lombok.Data;
6 6  
... ... @@ -24,10 +24,10 @@ public class IotRuleSceneRespVO {
24 24 private Integer status;
25 25  
26 26 @Schema(description = "触发器数组", requiredMode = Schema.RequiredMode.REQUIRED)
27   - private List<IotRuleSceneDO.Trigger> triggers;
  27 + private List<IotSceneRuleDO.Trigger> triggers;
28 28  
29 29 @Schema(description = "执行器数组", requiredMode = Schema.RequiredMode.REQUIRED)
30   - private List<IotRuleSceneDO.Action> actions;
  30 + private List<IotSceneRuleDO.Action> actions;
31 31  
32 32 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
33 33 private LocalDateTime createTime;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleSceneSaveReqVO.java
... ... @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
5   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
6 6 import io.swagger.v3.oas.annotations.media.Schema;
7 7 import jakarta.validation.constraints.NotEmpty;
8 8 import jakarta.validation.constraints.NotNull;
... ... @@ -31,10 +31,10 @@ public class IotRuleSceneSaveReqVO {
31 31  
32 32 @Schema(description = "触发器数组", requiredMode = Schema.RequiredMode.REQUIRED)
33 33 @NotEmpty(message = "触发器数组不能为空")
34   - private List<IotRuleSceneDO.Trigger> triggers;
  34 + private List<IotSceneRuleDO.Trigger> triggers;
35 35  
36 36 @Schema(description = "执行器数组", requiredMode = Schema.RequiredMode.REQUIRED)
37 37 @NotEmpty(message = "执行器数组不能为空")
38   - private List<IotRuleSceneDO.Action> actions;
  38 + private List<IotSceneRuleDO.Action> actions;
39 39  
40 40 }
41 41 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertConfigDO.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5 5 import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;
6 6 import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
7   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 8 import cn.iocoder.yudao.module.iot.enums.DictTypeConstants;
9 9 import cn.iocoder.yudao.module.iot.enums.alert.IotAlertReceiveTypeEnum;
10 10 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
... ... @@ -61,7 +61,7 @@ public class IotAlertConfigDO extends BaseDO {
61 61 /**
62 62 * 关联的场景联动规则编号数组
63 63 *
64   - * 关联 {@link IotRuleSceneDO#getId()}
  64 + * 关联 {@link IotSceneRuleDO#getId()}
65 65 */
66 66 @TableField(typeHandler = LongListTypeHandler.class)
67 67 private List<Long> sceneRuleIds;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertRecordDO.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
6 6 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
7   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 8 import com.baomidou.mybatisplus.annotation.KeySequence;
9 9 import com.baomidou.mybatisplus.annotation.TableField;
10 10 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -55,7 +55,7 @@ public class IotAlertRecordDO extends BaseDO {
55 55 /**
56 56 * 场景规则编号
57 57 *
58   - * 关联 {@link IotRuleSceneDO#getId()}
  58 + * 关联 {@link IotSceneRuleDO#getId()}
59 59 */
60 60 private Long sceneRuleId;
61 61  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotSceneRuleDO.java
... ... @@ -23,19 +23,18 @@ import lombok.NoArgsConstructor;
23 23  
24 24 import java.util.List;
25 25  
26   -// TODO @puhui999:名字改成 IotSceneRuleDO
27 26 /**
28 27 * IoT 场景联动规则 DO
29 28 *
30 29 * @author 芋道源码
31 30 */
32   -@TableName(value = "iot_rule_scene", autoResultMap = true)
33   -@KeySequence("iot_rule_scene_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  31 +@TableName(value = "iot_scene_rule", autoResultMap = true)
  32 +@KeySequence("iot_scene_rule_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
34 33 @Data
35 34 @Builder
36 35 @NoArgsConstructor
37 36 @AllArgsConstructor
38   -public class IotRuleSceneDO extends TenantBaseDO {
  37 +public class IotSceneRuleDO extends TenantBaseDO {
39 38  
40 39 /**
41 40 * 场景联动编号
... ... @@ -219,12 +218,20 @@ public class IotRuleSceneDO extends TenantBaseDO {
219 218 * 关联 {@link IotDeviceDO#getId()}
220 219 */
221 220 private Long deviceId;
  221 +
  222 + /**
  223 + * 标识符(服务)
  224 + * <p>
  225 + * 关联 {@link IotThingModelDO#getIdentifier()}
  226 + */
  227 + private String identifier;
  228 +
222 229 /**
223 230 * 请求参数
224 231 *
225 232 * 一般来说,对应 {@link IotDeviceMessage#getParams()} 请求参数
226 233 */
227   - private Object params;
  234 + private String params;
228 235  
229 236 /**
230 237 * 告警配置编号
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotRuleSceneMapper.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;
7   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
10 10 import java.util.List;
... ... @@ -15,19 +15,19 @@ import java.util.List;
15 15 * @author HUIHUI
16 16 */
17 17 @Mapper
18   -public interface IotRuleSceneMapper extends BaseMapperX<IotRuleSceneDO> {
  18 +public interface IotRuleSceneMapper extends BaseMapperX<IotSceneRuleDO> {
19 19  
20   - default PageResult<IotRuleSceneDO> selectPage(IotRuleScenePageReqVO reqVO) {
21   - return selectPage(reqVO, new LambdaQueryWrapperX<IotRuleSceneDO>()
22   - .likeIfPresent(IotRuleSceneDO::getName, reqVO.getName())
23   - .likeIfPresent(IotRuleSceneDO::getDescription, reqVO.getDescription())
24   - .eqIfPresent(IotRuleSceneDO::getStatus, reqVO.getStatus())
25   - .betweenIfPresent(IotRuleSceneDO::getCreateTime, reqVO.getCreateTime())
26   - .orderByDesc(IotRuleSceneDO::getId));
  20 + default PageResult<IotSceneRuleDO> selectPage(IotRuleScenePageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<IotSceneRuleDO>()
  22 + .likeIfPresent(IotSceneRuleDO::getName, reqVO.getName())
  23 + .likeIfPresent(IotSceneRuleDO::getDescription, reqVO.getDescription())
  24 + .eqIfPresent(IotSceneRuleDO::getStatus, reqVO.getStatus())
  25 + .betweenIfPresent(IotSceneRuleDO::getCreateTime, reqVO.getCreateTime())
  26 + .orderByDesc(IotSceneRuleDO::getId));
27 27 }
28 28  
29   - default List<IotRuleSceneDO> selectListByStatus(Integer status) {
30   - return selectList(IotRuleSceneDO::getStatus, status);
  29 + default List<IotSceneRuleDO> selectListByStatus(Integer status) {
  30 + return selectList(IotSceneRuleDO::getStatus, status);
31 31 }
32 32  
33 33 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneService.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;
5 5 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO;
6 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
7   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 8 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
9 9 import jakarta.validation.Valid;
10 10  
... ... @@ -54,7 +54,7 @@ public interface IotRuleSceneService {
54 54 * @param id 编号
55 55 * @return 场景联动
56 56 */
57   - IotRuleSceneDO getRuleScene(Long id);
  57 + IotSceneRuleDO getRuleScene(Long id);
58 58  
59 59 /**
60 60 * 获得场景联动分页
... ... @@ -62,7 +62,7 @@ public interface IotRuleSceneService {
62 62 * @param pageReqVO 分页查询
63 63 * @return 场景联动分页
64 64 */
65   - PageResult<IotRuleSceneDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO);
  65 + PageResult<IotSceneRuleDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO);
66 66  
67 67 /**
68 68 * 校验规则场景联动规则编号们是否存在。如下情况,视为无效:
... ... @@ -78,7 +78,7 @@ public interface IotRuleSceneService {
78 78 * @param status 状态
79 79 * @return 场景联动列表
80 80 */
81   - List<IotRuleSceneDO> getRuleSceneListByStatus(Integer status);
  81 + List<IotSceneRuleDO> getRuleSceneListByStatus(Integer status);
82 82  
83 83 /**
84 84 * 【缓存】获得指定设备的场景列表
... ... @@ -87,7 +87,7 @@ public interface IotRuleSceneService {
87 87 * @param deviceName 设备名称
88 88 * @return 场景列表
89 89 */
90   - List<IotRuleSceneDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName);
  90 + List<IotSceneRuleDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName);
91 91  
92 92 /**
93 93 * 基于 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 场景,执行规则场景
... ... @@ -103,9 +103,4 @@ public interface IotRuleSceneService {
103 103 */
104 104 void executeRuleSceneByTimer(Long id);
105 105  
106   - /**
107   - * TODO 芋艿:测试方法,需要删除
108   - */
109   - void test();
110   -
111 106 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceImpl.java
... ... @@ -21,29 +21,24 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
21 21 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
22 22 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
23 23 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
24   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  24 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
25 25 import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;
26   -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
27 26 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionOperatorEnum;
28 27 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionTypeEnum;
29 28 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
30 29 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
31   -import cn.iocoder.yudao.module.iot.job.rule.IotRuleSceneJob;
32 30 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
33 31 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
34 32 import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;
35 33 import jakarta.annotation.Resource;
36   -import lombok.SneakyThrows;
37 34 import lombok.extern.slf4j.Slf4j;
38   -import org.quartz.JobKey;
39   -import org.quartz.Scheduler;
40   -import org.quartz.SchedulerException;
41   -import org.quartz.TriggerKey;
42   -import org.quartz.impl.StdSchedulerFactory;
43 35 import org.springframework.stereotype.Service;
44 36 import org.springframework.validation.annotation.Validated;
45 37  
46   -import java.util.*;
  38 +import java.util.Collection;
  39 +import java.util.HashMap;
  40 +import java.util.List;
  41 +import java.util.Map;
47 42  
48 43 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
49 44 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
... ... @@ -77,7 +72,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
77 72  
78 73 @Override
79 74 public Long createRuleScene(IotRuleSceneSaveReqVO createReqVO) {
80   - IotRuleSceneDO ruleScene = BeanUtils.toBean(createReqVO, IotRuleSceneDO.class);
  75 + IotSceneRuleDO ruleScene = BeanUtils.toBean(createReqVO, IotSceneRuleDO.class);
81 76 ruleSceneMapper.insert(ruleScene);
82 77 return ruleScene.getId();
83 78 }
... ... @@ -87,7 +82,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
87 82 // 校验存在
88 83 validateRuleSceneExists(updateReqVO.getId());
89 84 // 更新
90   - IotRuleSceneDO updateObj = BeanUtils.toBean(updateReqVO, IotRuleSceneDO.class);
  85 + IotSceneRuleDO updateObj = BeanUtils.toBean(updateReqVO, IotSceneRuleDO.class);
91 86 ruleSceneMapper.updateById(updateObj);
92 87 }
93 88  
... ... @@ -96,7 +91,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
96 91 // 校验存在
97 92 validateRuleSceneExists(id);
98 93 // 更新状态
99   - IotRuleSceneDO updateObj = new IotRuleSceneDO().setId(id).setStatus(status);
  94 + IotSceneRuleDO updateObj = new IotSceneRuleDO().setId(id).setStatus(status);
100 95 ruleSceneMapper.updateById(updateObj);
101 96 }
102 97  
... ... @@ -115,12 +110,12 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
115 110 }
116 111  
117 112 @Override
118   - public IotRuleSceneDO getRuleScene(Long id) {
  113 + public IotSceneRuleDO getRuleScene(Long id) {
119 114 return ruleSceneMapper.selectById(id);
120 115 }
121 116  
122 117 @Override
123   - public PageResult<IotRuleSceneDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO) {
  118 + public PageResult<IotSceneRuleDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO) {
124 119 return ruleSceneMapper.selectPage(pageReqVO);
125 120 }
126 121  
... ... @@ -130,27 +125,27 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
130 125 return;
131 126 }
132 127 // 批量查询存在的规则场景
133   - List<IotRuleSceneDO> existingScenes = ruleSceneMapper.selectByIds(ids);
  128 + List<IotSceneRuleDO> existingScenes = ruleSceneMapper.selectByIds(ids);
134 129 if (existingScenes.size() != ids.size()) {
135 130 throw exception(RULE_SCENE_NOT_EXISTS);
136 131 }
137 132 }
138 133  
139 134 @Override
140   - public List<IotRuleSceneDO> getRuleSceneListByStatus(Integer status) {
  135 + public List<IotSceneRuleDO> getRuleSceneListByStatus(Integer status) {
141 136 return ruleSceneMapper.selectListByStatus(status);
142 137 }
143 138  
144 139 // TODO 芋艿,缓存待实现
145 140 @Override
146 141 @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略
147   - public List<IotRuleSceneDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {
  142 + public List<IotSceneRuleDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {
148 143 // TODO @puhui999:一些注释,看看要不要优化下;
149 144 // 注意:旧的测试代码已删除,因为使用了废弃的数据结构
150 145 // 如需测试,请使用上面的新结构测试代码示例
151   - List<IotRuleSceneDO> list = ruleSceneMapper.selectList();
  146 + List<IotSceneRuleDO> list = ruleSceneMapper.selectList();
152 147 // 只返回启用状态的规则场景
153   - List<IotRuleSceneDO> enabledList = filterList(list,
  148 + List<IotSceneRuleDO> enabledList = filterList(list,
154 149 ruleScene -> CommonStatusEnum.ENABLE.getStatus().equals(ruleScene.getStatus()));
155 150  
156 151 // 根据 productKey 和 deviceName 进行匹配
... ... @@ -159,7 +154,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
159 154 return false;
160 155 }
161 156  
162   - for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) {
  157 + for (IotSceneRuleDO.Trigger trigger : ruleScene.getTriggers()) {
163 158 // 检查触发器是否匹配指定的产品和设备
164 159 if (isMatchProductAndDevice(trigger, productKey, deviceName)) {
165 160 return true;
... ... @@ -177,7 +172,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
177 172 * @param deviceName 设备名称
178 173 * @return 是否匹配
179 174 */
180   - private boolean isMatchProductAndDevice(IotRuleSceneDO.Trigger trigger, String productKey, String deviceName) {
  175 + private boolean isMatchProductAndDevice(IotSceneRuleDO.Trigger trigger, String productKey, String deviceName) {
181 176 try {
182 177 // 1. 检查产品是否匹配
183 178 if (trigger.getProductId() != null) {
... ... @@ -219,7 +214,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
219 214 // TODO @芋艿:这里的 tenantId,通过设备获取;
220 215 TenantUtils.execute(message.getTenantId(), () -> {
221 216 // 1. 获得设备匹配的规则场景
222   - List<IotRuleSceneDO> ruleScenes = getMatchedRuleSceneListByMessage(message);
  217 + List<IotSceneRuleDO> ruleScenes = getMatchedRuleSceneListByMessage(message);
223 218 if (CollUtil.isEmpty(ruleScenes)) {
224 219 return;
225 220 }
... ... @@ -232,7 +227,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
232 227 @Override
233 228 public void executeRuleSceneByTimer(Long id) {
234 229 // 1.1 获得规则场景
235   - IotRuleSceneDO scene = TenantUtils.executeIgnore(() -> ruleSceneMapper.selectById(id));
  230 + IotSceneRuleDO scene = TenantUtils.executeIgnore(() -> ruleSceneMapper.selectById(id));
236 231 if (scene == null) {
237 232 log.error("[executeRuleSceneByTimer][规则场景({}) 不存在]", id);
238 233 return;
... ... @@ -242,7 +237,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
242 237 return;
243 238 }
244 239 // 1.2 判断是否有定时触发器,避免脏数据
245   - IotRuleSceneDO.Trigger config = CollUtil.findOne(scene.getTriggers(),
  240 + IotSceneRuleDO.Trigger config = CollUtil.findOne(scene.getTriggers(),
246 241 trigger -> ObjUtil.equals(trigger.getType(), IotRuleSceneTriggerTypeEnum.TIMER.getType()));
247 242 if (config == null) {
248 243 log.error("[executeRuleSceneByTimer][规则场景({}) 不存在定时触发器]", scene);
... ... @@ -260,7 +255,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
260 255 * @param message 设备消息
261 256 * @return 规则场景列表
262 257 */
263   - private List<IotRuleSceneDO> getMatchedRuleSceneListByMessage(IotDeviceMessage message) {
  258 + private List<IotSceneRuleDO> getMatchedRuleSceneListByMessage(IotDeviceMessage message) {
264 259 // 1. 匹配设备
265 260 // TODO @芋艿:可能需要 getSelf(); 缓存
266 261 // 1.1 通过 deviceId 获取设备信息
... ... @@ -278,7 +273,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
278 273 }
279 274  
280 275 // 1.3 获取匹配的规则场景
281   - List<IotRuleSceneDO> ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache(
  276 + List<IotSceneRuleDO> ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache(
282 277 product.getProductKey(), device.getDeviceName());
283 278 if (CollUtil.isEmpty(ruleScenes)) {
284 279 return ruleScenes;
... ... @@ -286,7 +281,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
286 281  
287 282 // 2. 匹配 trigger 触发器的条件
288 283 return filterList(ruleScenes, ruleScene -> {
289   - for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) {
  284 + for (IotSceneRuleDO.Trigger trigger : ruleScene.getTriggers()) {
290 285 // 2.1 检查触发器类型,根据新的枚举值进行匹配
291 286 // TODO @芋艿:需要根据新的触发器类型枚举进行适配
292 287 // 原来使用 IotRuleSceneTriggerTypeEnum.DEVICE,新结构可能有不同的类型
... ... @@ -298,14 +293,14 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
298 293  
299 294 // 2.3 检查条件分组:分组与分组之间是"或"的关系,条件与条件之间是"且"的关系
300 295 boolean anyGroupMatched = false;
301   - for (List<IotRuleSceneDO.TriggerCondition> conditionGroup : trigger.getConditionGroups()) {
  296 + for (List<IotSceneRuleDO.TriggerCondition> conditionGroup : trigger.getConditionGroups()) {
302 297 if (CollUtil.isEmpty(conditionGroup)) {
303 298 continue;
304 299 }
305 300  
306 301 // 检查当前分组中的所有条件是否都匹配(且关系)
307 302 boolean allConditionsMatched = true;
308   - for (IotRuleSceneDO.TriggerCondition condition : conditionGroup) {
  303 + for (IotSceneRuleDO.TriggerCondition condition : conditionGroup) {
309 304 // TODO @芋艿:这里需要实现具体的条件匹配逻辑
310 305 // 根据新的 TriggerCondition 结构进行匹配
311 306 if (!isTriggerConditionMatched(message, condition, ruleScene, trigger)) {
... ... @@ -338,8 +333,8 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
338 333 * @param trigger 触发器(用于日志,无其它作用)
339 334 * @return 是否匹配
340 335 */
341   - private boolean isTriggerConditionMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerCondition condition,
342   - IotRuleSceneDO ruleScene, IotRuleSceneDO.Trigger trigger) {
  336 + private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,
  337 + IotSceneRuleDO ruleScene, IotSceneRuleDO.Trigger trigger) {
343 338 try {
344 339 // 1. 根据条件类型进行匹配
345 340 if (IotRuleSceneConditionTypeEnum.DEVICE_STATE.getType().equals(condition.getType())) {
... ... @@ -370,7 +365,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
370 365 * @param condition 触发条件
371 366 * @return 是否匹配
372 367 */
373   - private boolean matchDeviceStateCondition(IotDeviceMessage message, IotRuleSceneDO.TriggerCondition condition) {
  368 + private boolean matchDeviceStateCondition(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
374 369 // TODO @芋艿:需要根据设备状态进行匹配
375 370 // 这里需要检查消息中的设备状态是否符合条件中定义的状态
376 371 log.debug("[matchDeviceStateCondition][设备状态条件匹配逻辑待实现] condition: {}", condition);
... ... @@ -384,7 +379,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
384 379 * @param condition 触发条件
385 380 * @return 是否匹配
386 381 */
387   - private boolean matchDevicePropertyCondition(IotDeviceMessage message, IotRuleSceneDO.TriggerCondition condition) {
  382 + private boolean matchDevicePropertyCondition(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
388 383 // 1. 检查标识符是否匹配
389 384 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
390 385 if (StrUtil.isBlank(condition.getIdentifier()) || !condition.getIdentifier().equals(messageIdentifier)) {
... ... @@ -407,7 +402,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
407 402 * @param condition 触发条件
408 403 * @return 是否匹配
409 404 */
410   - private boolean matchCurrentTimeCondition(IotRuleSceneDO.TriggerCondition condition) {
  405 + private boolean matchCurrentTimeCondition(IotSceneRuleDO.TriggerCondition condition) {
411 406 // TODO @芋艿:需要根据当前时间进行匹配
412 407 // 这里需要检查当前时间是否符合条件中定义的时间范围
413 408 log.debug("[matchCurrentTimeCondition][当前时间条件匹配逻辑待实现] condition: {}", condition);
... ... @@ -474,8 +469,8 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
474 469 * @return 是否匹配
475 470 */
476 471 @SuppressWarnings({"unchecked", "DataFlowIssue"})
477   - private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerCondition condition,
478   - IotRuleSceneDO ruleScene, IotRuleSceneDO.Trigger trigger) {
  472 + private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,
  473 + IotSceneRuleDO ruleScene, IotSceneRuleDO.Trigger trigger) {
479 474 // 1.1 校验操作符是否合法
480 475 IotRuleSceneConditionOperatorEnum operator =
481 476 IotRuleSceneConditionOperatorEnum.operatorOf(condition.getOperator());
... ... @@ -528,7 +523,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
528 523 * @param message 设备消息
529 524 * @param ruleScenes 规则场景列表
530 525 */
531   - private void executeRuleSceneAction(IotDeviceMessage message, List<IotRuleSceneDO> ruleScenes) {
  526 + private void executeRuleSceneAction(IotDeviceMessage message, List<IotSceneRuleDO> ruleScenes) {
532 527 // 1. 遍历规则场景
533 528 ruleScenes.forEach(ruleScene -> {
534 529 // 2. 遍历规则场景的动作
... ... @@ -554,50 +549,4 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
554 549 });
555 550 }
556 551  
557   - @Override
558   - @SneakyThrows
559   - public void test() {
560   - // TODO @芋艿:测试思路代码,记得删除!!!
561   - // 1. Job 类:IotRuleSceneJob DONE
562   - // 2. 参数:id DONE
563   - // 3. jobHandlerName:IotRuleSceneJob + id DONE
564   -
565   - // 新增:addJob
566   - // 修改:不存在 addJob、存在 updateJob
567   - // 有 + 禁用:1)存在、停止;2)不存在:不处理;TODO 测试:直接暂停,是否可以???(结论:可以)pauseJob
568   - // 有 + 开启:1)存在,更新;2)不存在,新增;结论:使用 save(addOrUpdateJob)
569   - // 无 + 禁用、开启:1)存在,删除;TODO 测试:直接删除???(结论:可以)deleteJob
570   -
571   - //
572   - if (false) {
573   - Long id = 1L;
574   - Map<String, Object> jobDataMap = IotRuleSceneJob.buildJobDataMap(id);
575   - schedulerManager.addOrUpdateJob(IotRuleSceneJob.class,
576   - IotRuleSceneJob.buildJobName(id),
577   - "0/10 * * * * ?",
578   - jobDataMap);
579   - }
580   - if (false) {
581   - Long id = 1L;
582   - schedulerManager.pauseJob(IotRuleSceneJob.buildJobName(id));
583   - }
584   - if (true) {
585   - Long id = 1L;
586   - schedulerManager.deleteJob(IotRuleSceneJob.buildJobName(id));
587   - }
588   - }
589   -
590   - public static void main2(String[] args) throws SchedulerException {
591   -// System.out.println(QuartzJobBean.class);
592   - Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
593   - scheduler.start();
594   -
595   - String jobHandlerName = "123";
596   - // 暂停 Trigger 对象
597   - scheduler.pauseTrigger(new TriggerKey(jobHandlerName));
598   - // 取消并删除 Job 调度
599   - scheduler.unscheduleJob(new TriggerKey(jobHandlerName));
600   - scheduler.deleteJob(new JobKey(jobHandlerName));
601   - }
602   -
603 552 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertRecoverSceneRuleAction.java
... ... @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.StrUtil;
5 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 6 import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
7   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 8 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
9 9 import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;
10 10 import jakarta.annotation.Resource;
... ... @@ -30,7 +30,7 @@ public class IotAlertRecoverSceneRuleAction implements IotSceneRuleAction {
30 30  
31 31 @Override
32 32 public void execute(IotDeviceMessage message,
33   - IotRuleSceneDO rule, IotRuleSceneDO.Action actionConfig) throws Exception {
  33 + IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) throws Exception {
34 34 Long deviceId = message != null ? message.getDeviceId() : null;
35 35 List<IotAlertRecordDO> alertRecords = alertRecordService.getAlertRecordListBySceneRuleId(
36 36 rule.getId(), deviceId, false);
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java
... ... @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 6 import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
7   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 8 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
9 9 import cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService;
10 10 import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;
... ... @@ -40,7 +40,7 @@ public class IotAlertTriggerSceneRuleAction implements IotSceneRuleAction {
40 40  
41 41 @Override
42 42 public void execute(@Nullable IotDeviceMessage message,
43   - IotRuleSceneDO rule, IotRuleSceneDO.Action actionConfig) throws Exception {
  43 + IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) throws Exception {
44 44 List<IotAlertConfigDO> alertConfigs = alertConfigService.getAlertConfigListBySceneRuleIdAndStatus(
45 45 rule.getId(), CommonStatusEnum.ENABLE.getStatus());
46 46 if (CollUtil.isEmpty(alertConfigs)) {
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceControlRuleSceneAction.java
1 1 package cn.iocoder.yudao.module.iot.service.rule.scene.action;
2 2  
3 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.IotSceneRuleDO;
5 5 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
6 6 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
7 7 import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
... ... @@ -26,8 +26,8 @@ public class IotDeviceControlRuleSceneAction implements IotSceneRuleAction {
26 26 // TODO @puhui999:这里
27 27 @Override
28 28 public void execute(IotDeviceMessage message,
29   - IotRuleSceneDO rule, IotRuleSceneDO.Action actionConfig) {
30   - //IotRuleSceneDO.ActionDeviceControl control = actionConfig.getDeviceControl();
  29 + IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
  30 + //IotSceneRuleDO.ActionDeviceControl control = actionConfig.getDeviceControl();
31 31 //Assert.notNull(control, "设备控制配置不能为空");
32 32 //// 遍历每个设备,下发消息
33 33 //control.getDeviceNames().forEach(deviceName -> {
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotSceneRuleAction.java
1 1 package cn.iocoder.yudao.module.iot.service.rule.scene.action;
2 2  
3 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.IotSceneRuleDO;
5 5 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
6 6  
7 7 import javax.annotation.Nullable;
... ... @@ -23,8 +23,8 @@ public interface IotSceneRuleAction {
23 23 * @param actionConfig 执行配置(实际对应规则里的哪条执行配置)
24 24 */
25 25 void execute(@Nullable IotDeviceMessage message,
26   - IotRuleSceneDO rule,
27   - IotRuleSceneDO.Action actionConfig) throws Exception;
  26 + IotSceneRuleDO rule,
  27 + IotSceneRuleDO.Action actionConfig) throws Exception;
28 28  
29 29 /**
30 30 * 获得类型
... ...
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceSimpleTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  7 +import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;
  8 +import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
  9 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  10 +import cn.iocoder.yudao.module.iot.service.product.IotProductService;
  11 +import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;
  12 +import org.junit.jupiter.api.Test;
  13 +import org.mockito.InjectMocks;
  14 +import org.mockito.Mock;
  15 +
  16 +import java.util.Collections;
  17 +import java.util.List;
  18 +
  19 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  20 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  21 +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
  22 +import static org.junit.jupiter.api.Assertions.assertEquals;
  23 +import static org.mockito.ArgumentMatchers.any;
  24 +import static org.mockito.Mockito.*;
  25 +
  26 +/**
  27 + * {@link IotRuleSceneServiceImpl} 的简化单元测试类
  28 + * 使用 Mockito 进行纯单元测试,不依赖 Spring 容器
  29 + *
  30 + * @author 芋道源码
  31 + */
  32 +public class IotRuleSceneServiceSimpleTest extends BaseMockitoUnitTest {
  33 +
  34 + @InjectMocks
  35 + private IotRuleSceneServiceImpl ruleSceneService;
  36 +
  37 + @Mock
  38 + private IotRuleSceneMapper ruleSceneMapper;
  39 +
  40 + @Mock
  41 + private List<IotSceneRuleAction> ruleSceneActions;
  42 +
  43 + @Mock
  44 + private IotSchedulerManager schedulerManager;
  45 +
  46 + @Mock
  47 + private IotProductService productService;
  48 +
  49 + @Mock
  50 + private IotDeviceService deviceService;
  51 +
  52 + @Test
  53 + public void testCreateRuleScene_success() {
  54 + // 准备参数
  55 + IotRuleSceneSaveReqVO createReqVO = randomPojo(IotRuleSceneSaveReqVO.class, o -> {
  56 + o.setId(null);
  57 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  58 + o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class)));
  59 + o.setActions(Collections.singletonList(randomPojo(IotSceneRuleDO.Action.class)));
  60 + });
  61 +
  62 + // Mock 行为
  63 + Long expectedId = randomLongId();
  64 + when(ruleSceneMapper.insert(any(IotSceneRuleDO.class))).thenAnswer(invocation -> {
  65 + IotSceneRuleDO ruleScene = invocation.getArgument(0);
  66 + ruleScene.setId(expectedId);
  67 + return 1;
  68 + });
  69 +
  70 + // 调用
  71 + Long ruleSceneId = ruleSceneService.createRuleScene(createReqVO);
  72 +
  73 + // 断言
  74 + assertEquals(expectedId, ruleSceneId);
  75 + verify(ruleSceneMapper, times(1)).insert(any(IotSceneRuleDO.class));
  76 + }
  77 +
  78 + @Test
  79 + public void testUpdateRuleScene_success() {
  80 + // 准备参数
  81 + Long id = randomLongId();
  82 + IotRuleSceneSaveReqVO updateReqVO = randomPojo(IotRuleSceneSaveReqVO.class, o -> {
  83 + o.setId(id);
  84 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  85 + o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class)));
  86 + o.setActions(Collections.singletonList(randomPojo(IotSceneRuleDO.Action.class)));
  87 + });
  88 +
  89 + // Mock 行为
  90 + IotSceneRuleDO existingRuleScene = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));
  91 + when(ruleSceneMapper.selectById(id)).thenReturn(existingRuleScene);
  92 + when(ruleSceneMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1);
  93 +
  94 + // 调用
  95 + assertDoesNotThrow(() -> ruleSceneService.updateRuleScene(updateReqVO));
  96 +
  97 + // 验证
  98 + verify(ruleSceneMapper, times(1)).selectById(id);
  99 + verify(ruleSceneMapper, times(1)).updateById(any(IotSceneRuleDO.class));
  100 + }
  101 +
  102 + @Test
  103 + public void testDeleteRuleScene_success() {
  104 + // 准备参数
  105 + Long id = randomLongId();
  106 +
  107 + // Mock 行为
  108 + IotSceneRuleDO existingRuleScene = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));
  109 + when(ruleSceneMapper.selectById(id)).thenReturn(existingRuleScene);
  110 + when(ruleSceneMapper.deleteById(id)).thenReturn(1);
  111 +
  112 + // 调用
  113 + assertDoesNotThrow(() -> ruleSceneService.deleteRuleScene(id));
  114 +
  115 + // 验证
  116 + verify(ruleSceneMapper, times(1)).selectById(id);
  117 + verify(ruleSceneMapper, times(1)).deleteById(id);
  118 + }
  119 +
  120 + @Test
  121 + public void testGetRuleScene() {
  122 + // 准备参数
  123 + Long id = randomLongId();
  124 + IotSceneRuleDO expectedRuleScene = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));
  125 +
  126 + // Mock 行为
  127 + when(ruleSceneMapper.selectById(id)).thenReturn(expectedRuleScene);
  128 +
  129 + // 调用
  130 + IotSceneRuleDO result = ruleSceneService.getRuleScene(id);
  131 +
  132 + // 断言
  133 + assertEquals(expectedRuleScene, result);
  134 + verify(ruleSceneMapper, times(1)).selectById(id);
  135 + }
  136 +
  137 + @Test
  138 + public void testUpdateRuleSceneStatus_success() {
  139 + // 准备参数
  140 + Long id = randomLongId();
  141 + Integer status = CommonStatusEnum.DISABLE.getStatus();
  142 +
  143 + // Mock 行为
  144 + IotSceneRuleDO existingRuleScene = randomPojo(IotSceneRuleDO.class, o -> {
  145 + o.setId(id);
  146 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  147 + });
  148 + when(ruleSceneMapper.selectById(id)).thenReturn(existingRuleScene);
  149 + when(ruleSceneMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1);
  150 +
  151 + // 调用
  152 + assertDoesNotThrow(() -> ruleSceneService.updateRuleSceneStatus(id, status));
  153 +
  154 + // 验证
  155 + verify(ruleSceneMapper, times(1)).selectById(id);
  156 + verify(ruleSceneMapper, times(1)).updateById(any(IotSceneRuleDO.class));
  157 + }
  158 +
  159 + @Test
  160 + public void testExecuteRuleSceneByTimer_success() {
  161 + // 准备参数
  162 + Long id = randomLongId();
  163 +
  164 + // Mock 行为
  165 + IotSceneRuleDO ruleScene = randomPojo(IotSceneRuleDO.class, o -> {
  166 + o.setId(id);
  167 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  168 + });
  169 + when(ruleSceneMapper.selectById(id)).thenReturn(ruleScene);
  170 +
  171 + // 调用
  172 + assertDoesNotThrow(() -> ruleSceneService.executeRuleSceneByTimer(id));
  173 +
  174 + // 验证
  175 + verify(ruleSceneMapper, times(1)).selectById(id);
  176 + }
  177 +
  178 + @Test
  179 + public void testExecuteRuleSceneByTimer_notExists() {
  180 + // 准备参数
  181 + Long id = randomLongId();
  182 +
  183 + // Mock 行为
  184 + when(ruleSceneMapper.selectById(id)).thenReturn(null);
  185 +
  186 + // 调用 - 不存在的场景规则应该不会抛异常,只是记录日志
  187 + assertDoesNotThrow(() -> ruleSceneService.executeRuleSceneByTimer(id));
  188 +
  189 + // 验证
  190 + verify(ruleSceneMapper, times(1)).selectById(id);
  191 + }
  192 +
  193 + @Test
  194 + public void testExecuteRuleSceneByTimer_disabled() {
  195 + // 准备参数
  196 + Long id = randomLongId();
  197 +
  198 + // Mock 行为
  199 + IotSceneRuleDO ruleScene = randomPojo(IotSceneRuleDO.class, o -> {
  200 + o.setId(id);
  201 + o.setStatus(CommonStatusEnum.DISABLE.getStatus());
  202 + });
  203 + when(ruleSceneMapper.selectById(id)).thenReturn(ruleScene);
  204 +
  205 + // 调用 - 禁用的场景规则应该不会执行,只是记录日志
  206 + assertDoesNotThrow(() -> ruleSceneService.executeRuleSceneByTimer(id));
  207 +
  208 + // 验证
  209 + verify(ruleSceneMapper, times(1)).selectById(id);
  210 + }
  211 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/test/resources/application-unit-test.yaml 0 → 100644
  1 +spring:
  2 + main:
  3 + lazy-initialization: true # 开启懒加载,加快速度
  4 + banner-mode: off # 单元测试,禁用 Banner
  5 + # 数据源配置项
  6 + datasource:
  7 + name: ruoyi-vue-pro
  8 + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
  9 + driver-class-name: org.h2.Driver
  10 + username: sa
  11 + password:
  12 + druid:
  13 + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
  14 + initial-size: 1 # 单元测试,配置为 1,提升启动速度
  15 + sql:
  16 + init:
  17 + schema-locations: classpath:/sql/create_tables.sql
  18 +
  19 +mybatis-plus:
  20 + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
  21 + type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject
  22 +
  23 +--- #################### 定时任务相关配置 ####################
  24 +
  25 +--- #################### 配置中心相关配置 ####################
  26 +
  27 +--- #################### 服务保障相关配置 ####################
  28 +
  29 +# Lock4j 配置项(单元测试,禁用 Lock4j)
  30 +
  31 +--- #################### 监控相关配置 ####################
  32 +
  33 +--- #################### 芋道相关配置 ####################
  34 +
  35 +# 芋道配置项,设置当前项目所有自定义的配置
  36 +yudao:
  37 + info:
  38 + base-package: cn.iocoder.yudao
  39 + tenant: # 多租户相关配置项
  40 + enable: true
  41 + xss:
  42 + enable: false
  43 + demo: false # 关闭演示模式
... ...
yudao-module-iot/yudao-module-iot-biz/src/test/resources/logback.xml 0 → 100644
  1 +<configuration>
  2 + <!-- 引用 Spring Boot 的 logback 基础配置 -->
  3 + <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
  4 +</configuration>
... ...
yudao-module-iot/yudao-module-iot-biz/src/test/resources/sql/clean.sql 0 → 100644
  1 +-- IoT 模块测试数据清理脚本
  2 +DELETE
  3 +FROM "iot_scene_rule";
  4 +DELETE
  5 +FROM "iot_product";
  6 +DELETE
  7 +FROM "iot_device";
  8 +DELETE
  9 +FROM "iot_thing_model";
  10 +DELETE
  11 +FROM "iot_device_data";
  12 +DELETE
  13 +FROM "iot_alert_config";
  14 +DELETE
  15 +FROM "iot_alert_record";
  16 +DELETE
  17 +FROM "iot_ota_firmware";
  18 +DELETE
  19 +FROM "iot_ota_task";
  20 +DELETE
  21 +FROM "iot_ota_record";
... ...
yudao-module-iot/yudao-module-iot-biz/src/test/resources/sql/create_tables.sql 0 → 100644
  1 +-- IoT 模块测试数据库表结构
  2 +-- 基于 H2 数据库语法,兼容 MySQL 模式
  3 +
  4 +-- IoT 场景联动规则表
  5 +CREATE TABLE IF NOT EXISTS "iot_scene_rule"
  6 +(
  7 + "id"
  8 + bigint
  9 + NOT
  10 + NULL
  11 + GENERATED
  12 + BY
  13 + DEFAULT AS
  14 + IDENTITY,
  15 + "name"
  16 + varchar
  17 +(
  18 + 255
  19 +) NOT NULL DEFAULT '',
  20 + "description" varchar
  21 +(
  22 + 500
  23 +) DEFAULT NULL,
  24 + "status" tinyint NOT NULL DEFAULT '0',
  25 + "triggers" text,
  26 + "actions" text,
  27 + "creator" varchar
  28 +(
  29 + 64
  30 +) DEFAULT '',
  31 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  32 + "updater" varchar
  33 +(
  34 + 64
  35 +) DEFAULT '',
  36 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  37 + "deleted" bit NOT NULL DEFAULT FALSE,
  38 + "tenant_id" bigint NOT NULL DEFAULT '0',
  39 + PRIMARY KEY
  40 +(
  41 + "id"
  42 +)
  43 + ) COMMENT 'IoT 场景联动规则表';
  44 +
  45 +-- IoT 产品表
  46 +CREATE TABLE IF NOT EXISTS "iot_product"
  47 +(
  48 + "id"
  49 + bigint
  50 + NOT
  51 + NULL
  52 + GENERATED
  53 + BY
  54 + DEFAULT AS
  55 + IDENTITY,
  56 + "name"
  57 + varchar
  58 +(
  59 + 255
  60 +) NOT NULL DEFAULT '',
  61 + "product_key" varchar
  62 +(
  63 + 100
  64 +) NOT NULL DEFAULT '',
  65 + "protocol_type" tinyint NOT NULL DEFAULT '0',
  66 + "category_id" bigint DEFAULT NULL,
  67 + "description" varchar
  68 +(
  69 + 500
  70 +) DEFAULT NULL,
  71 + "data_format" tinyint NOT NULL DEFAULT '0',
  72 + "device_type" tinyint NOT NULL DEFAULT '0',
  73 + "net_type" tinyint NOT NULL DEFAULT '0',
  74 + "validate_type" tinyint NOT NULL DEFAULT '0',
  75 + "status" tinyint NOT NULL DEFAULT '0',
  76 + "creator" varchar
  77 +(
  78 + 64
  79 +) DEFAULT '',
  80 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  81 + "updater" varchar
  82 +(
  83 + 64
  84 +) DEFAULT '',
  85 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  86 + "deleted" bit NOT NULL DEFAULT FALSE,
  87 + "tenant_id" bigint NOT NULL DEFAULT '0',
  88 + PRIMARY KEY
  89 +(
  90 + "id"
  91 +)
  92 + ) COMMENT 'IoT 产品表';
  93 +
  94 +-- IoT 设备表
  95 +CREATE TABLE IF NOT EXISTS "iot_device"
  96 +(
  97 + "id"
  98 + bigint
  99 + NOT
  100 + NULL
  101 + GENERATED
  102 + BY
  103 + DEFAULT AS
  104 + IDENTITY,
  105 + "device_name"
  106 + varchar
  107 +(
  108 + 255
  109 +) NOT NULL DEFAULT '',
  110 + "product_id" bigint NOT NULL,
  111 + "device_key" varchar
  112 +(
  113 + 100
  114 +) NOT NULL DEFAULT '',
  115 + "device_secret" varchar
  116 +(
  117 + 100
  118 +) NOT NULL DEFAULT '',
  119 + "nickname" varchar
  120 +(
  121 + 255
  122 +) DEFAULT NULL,
  123 + "status" tinyint NOT NULL DEFAULT '0',
  124 + "status_last_update_time" timestamp DEFAULT NULL,
  125 + "last_online_time" timestamp DEFAULT NULL,
  126 + "last_offline_time" timestamp DEFAULT NULL,
  127 + "active_time" timestamp DEFAULT NULL,
  128 + "ip" varchar
  129 +(
  130 + 50
  131 +) DEFAULT NULL,
  132 + "firmware_version" varchar
  133 +(
  134 + 50
  135 +) DEFAULT NULL,
  136 + "device_type" tinyint NOT NULL DEFAULT '0',
  137 + "gateway_id" bigint DEFAULT NULL,
  138 + "sub_device_count" int NOT NULL DEFAULT '0',
  139 + "creator" varchar
  140 +(
  141 + 64
  142 +) DEFAULT '',
  143 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  144 + "updater" varchar
  145 +(
  146 + 64
  147 +) DEFAULT '',
  148 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  149 + "deleted" bit NOT NULL DEFAULT FALSE,
  150 + "tenant_id" bigint NOT NULL DEFAULT '0',
  151 + PRIMARY KEY
  152 +(
  153 + "id"
  154 +)
  155 + ) COMMENT 'IoT 设备表';
  156 +
  157 +-- IoT 物模型表
  158 +CREATE TABLE IF NOT EXISTS "iot_thing_model"
  159 +(
  160 + "id"
  161 + bigint
  162 + NOT
  163 + NULL
  164 + GENERATED
  165 + BY
  166 + DEFAULT AS
  167 + IDENTITY,
  168 + "product_id"
  169 + bigint
  170 + NOT
  171 + NULL,
  172 + "identifier"
  173 + varchar
  174 +(
  175 + 100
  176 +) NOT NULL DEFAULT '',
  177 + "name" varchar
  178 +(
  179 + 255
  180 +) NOT NULL DEFAULT '',
  181 + "description" varchar
  182 +(
  183 + 500
  184 +) DEFAULT NULL,
  185 + "type" tinyint NOT NULL DEFAULT '1',
  186 + "property" text,
  187 + "creator" varchar
  188 +(
  189 + 64
  190 +) DEFAULT '',
  191 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  192 + "updater" varchar
  193 +(
  194 + 64
  195 +) DEFAULT '',
  196 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  197 + "deleted" bit NOT NULL DEFAULT FALSE,
  198 + "tenant_id" bigint NOT NULL DEFAULT '0',
  199 + PRIMARY KEY
  200 +(
  201 + "id"
  202 +)
  203 + ) COMMENT 'IoT 物模型表';
  204 +
  205 +-- IoT 设备数据表
  206 +CREATE TABLE IF NOT EXISTS "iot_device_data"
  207 +(
  208 + "id"
  209 + bigint
  210 + NOT
  211 + NULL
  212 + GENERATED
  213 + BY
  214 + DEFAULT AS
  215 + IDENTITY,
  216 + "device_id"
  217 + bigint
  218 + NOT
  219 + NULL,
  220 + "product_id"
  221 + bigint
  222 + NOT
  223 + NULL,
  224 + "identifier"
  225 + varchar
  226 +(
  227 + 100
  228 +) NOT NULL DEFAULT '',
  229 + "type" tinyint NOT NULL DEFAULT '1',
  230 + "data" text,
  231 + "ts" bigint NOT NULL DEFAULT '0',
  232 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  233 + PRIMARY KEY
  234 +(
  235 + "id"
  236 +)
  237 + ) COMMENT 'IoT 设备数据表';
  238 +
  239 +-- IoT 告警配置表
  240 +CREATE TABLE IF NOT EXISTS "iot_alert_config"
  241 +(
  242 + "id"
  243 + bigint
  244 + NOT
  245 + NULL
  246 + GENERATED
  247 + BY
  248 + DEFAULT AS
  249 + IDENTITY,
  250 + "name"
  251 + varchar
  252 +(
  253 + 255
  254 +) NOT NULL DEFAULT '',
  255 + "product_id" bigint NOT NULL,
  256 + "device_id" bigint DEFAULT NULL,
  257 + "rule_id" bigint DEFAULT NULL,
  258 + "status" tinyint NOT NULL DEFAULT '0',
  259 + "creator" varchar
  260 +(
  261 + 64
  262 +) DEFAULT '',
  263 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  264 + "updater" varchar
  265 +(
  266 + 64
  267 +) DEFAULT '',
  268 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  269 + "deleted" bit NOT NULL DEFAULT FALSE,
  270 + "tenant_id" bigint NOT NULL DEFAULT '0',
  271 + PRIMARY KEY
  272 +(
  273 + "id"
  274 +)
  275 + ) COMMENT 'IoT 告警配置表';
  276 +
  277 +-- IoT 告警记录表
  278 +CREATE TABLE IF NOT EXISTS "iot_alert_record"
  279 +(
  280 + "id"
  281 + bigint
  282 + NOT
  283 + NULL
  284 + GENERATED
  285 + BY
  286 + DEFAULT AS
  287 + IDENTITY,
  288 + "alert_config_id"
  289 + bigint
  290 + NOT
  291 + NULL,
  292 + "alert_name"
  293 + varchar
  294 +(
  295 + 255
  296 +) NOT NULL DEFAULT '',
  297 + "product_id" bigint NOT NULL,
  298 + "device_id" bigint DEFAULT NULL,
  299 + "rule_id" bigint DEFAULT NULL,
  300 + "alert_data" text,
  301 + "alert_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  302 + "deal_status" tinyint NOT NULL DEFAULT '0',
  303 + "deal_time" timestamp DEFAULT NULL,
  304 + "deal_user_id" bigint DEFAULT NULL,
  305 + "deal_remark" varchar
  306 +(
  307 + 500
  308 +) DEFAULT NULL,
  309 + "creator" varchar
  310 +(
  311 + 64
  312 +) DEFAULT '',
  313 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  314 + "updater" varchar
  315 +(
  316 + 64
  317 +) DEFAULT '',
  318 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  319 + "deleted" bit NOT NULL DEFAULT FALSE,
  320 + "tenant_id" bigint NOT NULL DEFAULT '0',
  321 + PRIMARY KEY
  322 +(
  323 + "id"
  324 +)
  325 + ) COMMENT 'IoT 告警记录表';
  326 +
  327 +-- IoT OTA 固件表
  328 +CREATE TABLE IF NOT EXISTS "iot_ota_firmware"
  329 +(
  330 + "id"
  331 + bigint
  332 + NOT
  333 + NULL
  334 + GENERATED
  335 + BY
  336 + DEFAULT AS
  337 + IDENTITY,
  338 + "name"
  339 + varchar
  340 +(
  341 + 255
  342 +) NOT NULL DEFAULT '',
  343 + "product_id" bigint NOT NULL,
  344 + "version" varchar
  345 +(
  346 + 50
  347 +) NOT NULL DEFAULT '',
  348 + "description" varchar
  349 +(
  350 + 500
  351 +) DEFAULT NULL,
  352 + "file_url" varchar
  353 +(
  354 + 500
  355 +) DEFAULT NULL,
  356 + "file_size" bigint NOT NULL DEFAULT '0',
  357 + "status" tinyint NOT NULL DEFAULT '0',
  358 + "creator" varchar
  359 +(
  360 + 64
  361 +) DEFAULT '',
  362 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  363 + "updater" varchar
  364 +(
  365 + 64
  366 +) DEFAULT '',
  367 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  368 + "deleted" bit NOT NULL DEFAULT FALSE,
  369 + "tenant_id" bigint NOT NULL DEFAULT '0',
  370 + PRIMARY KEY
  371 +(
  372 + "id"
  373 +)
  374 + ) COMMENT 'IoT OTA 固件表';
  375 +
  376 +-- IoT OTA 升级任务表
  377 +CREATE TABLE IF NOT EXISTS "iot_ota_task"
  378 +(
  379 + "id"
  380 + bigint
  381 + NOT
  382 + NULL
  383 + GENERATED
  384 + BY
  385 + DEFAULT AS
  386 + IDENTITY,
  387 + "name"
  388 + varchar
  389 +(
  390 + 255
  391 +) NOT NULL DEFAULT '',
  392 + "firmware_id" bigint NOT NULL,
  393 + "product_id" bigint NOT NULL,
  394 + "upgrade_type" tinyint NOT NULL DEFAULT '0',
  395 + "status" tinyint NOT NULL DEFAULT '0',
  396 + "creator" varchar
  397 +(
  398 + 64
  399 +) DEFAULT '',
  400 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  401 + "updater" varchar
  402 +(
  403 + 64
  404 +) DEFAULT '',
  405 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  406 + "deleted" bit NOT NULL DEFAULT FALSE,
  407 + "tenant_id" bigint NOT NULL DEFAULT '0',
  408 + PRIMARY KEY
  409 +(
  410 + "id"
  411 +)
  412 + ) COMMENT 'IoT OTA 升级任务表';
  413 +
  414 +-- IoT OTA 升级记录表
  415 +CREATE TABLE IF NOT EXISTS "iot_ota_record"
  416 +(
  417 + "id"
  418 + bigint
  419 + NOT
  420 + NULL
  421 + GENERATED
  422 + BY
  423 + DEFAULT AS
  424 + IDENTITY,
  425 + "task_id"
  426 + bigint
  427 + NOT
  428 + NULL,
  429 + "firmware_id"
  430 + bigint
  431 + NOT
  432 + NULL,
  433 + "device_id"
  434 + bigint
  435 + NOT
  436 + NULL,
  437 + "status"
  438 + tinyint
  439 + NOT
  440 + NULL
  441 + DEFAULT
  442 + '0',
  443 + "progress"
  444 + int
  445 + NOT
  446 + NULL
  447 + DEFAULT
  448 + '0',
  449 + "error_msg"
  450 + varchar
  451 +(
  452 + 500
  453 +) DEFAULT NULL,
  454 + "start_time" timestamp DEFAULT NULL,
  455 + "end_time" timestamp DEFAULT NULL,
  456 + "creator" varchar
  457 +(
  458 + 64
  459 +) DEFAULT '',
  460 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  461 + "updater" varchar
  462 +(
  463 + 64
  464 +) DEFAULT '',
  465 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  466 + "deleted" bit NOT NULL DEFAULT FALSE,
  467 + "tenant_id" bigint NOT NULL DEFAULT '0',
  468 + PRIMARY KEY
  469 +(
  470 + "id"
  471 +)
  472 + ) COMMENT 'IoT OTA 升级记录表';
... ...