【功能新增】IoT: 规则场景基础 CRUD

This commit is contained in:
puhui999 2025-03-18 16:36:18 +08:00
parent 638976dac8
commit 6d6547be39
10 changed files with 272 additions and 118 deletions

View File

@ -72,4 +72,7 @@ public interface ErrorCodeConstants {
// ========== IoT 数据桥梁 1-050-010-000 ========== // ========== IoT 数据桥梁 1-050-010-000 ==========
ErrorCode DATA_BRIDGE_NOT_EXISTS = new ErrorCode(1_050_010_000, "IoT 数据桥梁不存在"); ErrorCode DATA_BRIDGE_NOT_EXISTS = new ErrorCode(1_050_010_000, "IoT 数据桥梁不存在");
// ========== IoT 规则场景场景联动 1-050-011-000 ==========
ErrorCode RULE_SCENE_NOT_EXISTS = new ErrorCode(1_050_011_000, "IoT 规则场景(场景联动)不存在");
} }

View File

@ -50,7 +50,7 @@ public enum IotRuleSceneTriggerConditionParameterOperatorEnum implements ArrayVa
/** /**
* Spring 表达式 - 目标值数组 * Spring 表达式 - 目标值数组
*/ */
public static final String SPRING_EXPRESSION_VALUE_List = "values"; public static final String SPRING_EXPRESSION_VALUE_LIST = "values";
public static IotRuleSceneTriggerConditionParameterOperatorEnum operatorOf(String operator) { public static IotRuleSceneTriggerConditionParameterOperatorEnum operatorOf(String operator) {
return ArrayUtil.firstMatch(item -> item.getOperator().equals(operator), values()); return ArrayUtil.firstMatch(item -> item.getOperator().equals(operator), values());

View File

@ -1,13 +1,24 @@
package cn.iocoder.yudao.module.iot.controller.admin.rule; package cn.iocoder.yudao.module.iot.controller.admin.rule;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneRespVO;
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - IoT 规则场景") @Tag(name = "管理后台 - IoT 规则场景")
@RestController @RestController
@ -18,6 +29,47 @@ public class IotRuleSceneController {
@Resource @Resource
private IotRuleSceneService ruleSceneService; private IotRuleSceneService ruleSceneService;
@PostMapping("/create")
@Operation(summary = "创建规则场景(场景联动)")
@PreAuthorize("@ss.hasPermission('iot:rule-scene:create')")
public CommonResult<Long> createRuleScene(@Valid @RequestBody IotRuleSceneSaveReqVO createReqVO) {
return success(ruleSceneService.createRuleScene(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新规则场景(场景联动)")
@PreAuthorize("@ss.hasPermission('iot:rule-scene:update')")
public CommonResult<Boolean> updateRuleScene(@Valid @RequestBody IotRuleSceneSaveReqVO updateReqVO) {
ruleSceneService.updateRuleScene(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除规则场景(场景联动)")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('iot:rule-scene:delete')")
public CommonResult<Boolean> deleteRuleScene(@RequestParam("id") Long id) {
ruleSceneService.deleteRuleScene(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得规则场景(场景联动)")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('iot:rule-scene:query')")
public CommonResult<IotRuleSceneRespVO> getRuleScene(@RequestParam("id") Long id) {
IotRuleSceneDO ruleScene = ruleSceneService.getRuleScene(id);
return success(BeanUtils.toBean(ruleScene, IotRuleSceneRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得规则场景(场景联动)分页")
@PreAuthorize("@ss.hasPermission('iot:rule-scene:query')")
public CommonResult<PageResult<IotRuleSceneRespVO>> getRuleScenePage(@Valid IotRuleScenePageReqVO pageReqVO) {
PageResult<IotRuleSceneDO> pageResult = ruleSceneService.getRuleScenePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, IotRuleSceneRespVO.class));
}
@GetMapping("/test") @GetMapping("/test")
@PermitAll @PermitAll
public void test() { public void test() {

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - IoT 规则场景(场景联动)分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class IotRuleScenePageReqVO extends PageParam {
@Schema(description = "场景名称", example = "赵六")
private String name;
@Schema(description = "场景描述", example = "你猜")
private String description;
@Schema(description = "场景状态", example = "1")
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IoT 规则场景(场景联动) Response VO")
@Data
public class IotRuleSceneRespVO {
@Schema(description = "场景编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15865")
private Long id;
@Schema(description = "场景名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
private String name;
@Schema(description = "场景描述", example = "你猜")
private String description;
@Schema(description = "场景状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "触发器数组", requiredMode = Schema.RequiredMode.REQUIRED)
private String triggers;
@Schema(description = "执行器数组", requiredMode = Schema.RequiredMode.REQUIRED)
private String actions;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - IoT 规则场景(场景联动)新增/修改 Request VO")
@Data
public class IotRuleSceneSaveReqVO {
@Schema(description = "场景编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15865")
private Long id;
@Schema(description = "场景名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@NotEmpty(message = "场景名称不能为空")
private String name;
@Schema(description = "场景描述", example = "你猜")
private String description;
@Schema(description = "场景状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "场景状态不能为空")
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "触发器数组", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "触发器数组不能为空")
private String triggers;
@Schema(description = "执行器数组", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "执行器数组不能为空")
private String actions;
}

View File

@ -1,112 +0,0 @@
### 请求 /iot/think-model-function/create 接口 => 成功
POST {{baseUrl}}/iot/think-model-function/create
Content-Type: application/json
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}
{
"productId": 1001,
"productKey": "smart-sensor-001",
"identifier": "Temperature",
"name": "温度",
"description": "当前温度值",
"type": 1,
"property": {
"identifier": "Temperature",
"name": "温度",
"accessMode": "r",
"required": true,
"dataType": {
"type": "float",
"specs": {
"min": -10.0,
"max": 100.0,
"step": 0.1,
"unit": "℃"
}
},
"description": "当前温度值"
}
}
### 请求 /iot/think-model-function/create 接口 => 成功
POST {{baseUrl}}/iot/think-model-function/create
Content-Type: application/json
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}
{
"productId": 1001,
"productKey": "smart-sensor-001",
"identifier": "Humidity",
"name": "湿度",
"description": "当前湿度值",
"type": 1,
"property": {
"identifier": "Humidity",
"name": "湿度",
"accessMode": "r",
"required": true,
"dataType": {
"type": "float",
"specs": {
"min": 0.0,
"max": 100.0,
"step": 0.1,
"unit": "%"
}
},
"description": "当前湿度值"
}
}
### 请求 /iot/think-model-function/update 接口 => 成功
PUT {{baseUrl}}/iot/think-model-function/update
Content-Type: application/json
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}
{
"id": 11,
"productId": 1001,
"productKey": "smart-sensor-001",
"identifier": "Temperature",
"name": "温度",
"description": "当前温度值",
"type": 1,
"property": {
"identifier": "Temperature",
"name": "温度",
"accessMode": "r",
"required": true,
"dataType": {
"type": "float",
"specs": {
"min": -111.0,
"max": 222.0,
"step": 0.1,
"unit": "℃"
}
},
"description": "当前温度值"
}
}
### 请求 /iot/think-model-function/delete 接口 => 成功
DELETE {{baseUrl}}/iot/think-model-function/delete?id=7
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}
### 请求 /iot/think-model-function/get 接口 => 成功
GET {{baseUrl}}/iot/think-model-function/get?id=10
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}
### 请求 /iot/think-model-function/list-by-product-id 接口 => 成功
GET {{baseUrl}}/iot/think-model-function/list-by-product-id?productId=1001
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}

View File

@ -1,10 +1,27 @@
package cn.iocoder.yudao.module.iot.dal.mysql.rule; package cn.iocoder.yudao.module.iot.dal.mysql.rule;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
/**
* IoT 规则场景场景联动 Mapper
*
* @author HUIHUI
*/
@Mapper @Mapper
public interface IotRuleSceneMapper extends BaseMapperX<IotRuleSceneDO> { public interface IotRuleSceneMapper extends BaseMapperX<IotRuleSceneDO> {
default PageResult<IotRuleSceneDO> selectPage(IotRuleScenePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<IotRuleSceneDO>()
.likeIfPresent(IotRuleSceneDO::getName, reqVO.getName())
.likeIfPresent(IotRuleSceneDO::getDescription, reqVO.getDescription())
.eqIfPresent(IotRuleSceneDO::getStatus, reqVO.getStatus())
.betweenIfPresent(IotRuleSceneDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(IotRuleSceneDO::getId));
}
} }

View File

@ -1,8 +1,12 @@
package cn.iocoder.yudao.module.iot.service.rule; package cn.iocoder.yudao.module.iot.service.rule;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
import jakarta.validation.Valid;
import java.util.List; import java.util.List;
@ -13,6 +17,44 @@ import java.util.List;
*/ */
public interface IotRuleSceneService { public interface IotRuleSceneService {
/**
* 创建规则场景场景联动
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createRuleScene(@Valid IotRuleSceneSaveReqVO createReqVO);
/**
* 更新规则场景场景联动
*
* @param updateReqVO 更新信息
*/
void updateRuleScene(@Valid IotRuleSceneSaveReqVO updateReqVO);
/**
* 删除规则场景场景联动
*
* @param id 编号
*/
void deleteRuleScene(Long id);
/**
* 获得规则场景场景联动
*
* @param id 编号
* @return 规则场景场景联动
*/
IotRuleSceneDO getRuleScene(Long id);
/**
* 获得规则场景场景联动分页
*
* @param pageReqVO 分页查询
* @return 规则场景场景联动分页
*/
PageResult<IotRuleSceneDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO);
/** /**
* 缓存获得指定设备的场景列表 * 缓存获得指定设备的场景列表
* *

View File

@ -8,11 +8,15 @@ import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper; import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
@ -39,8 +43,10 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NOT_EXISTS;
/** /**
* IoT 规则场景 Service 实现类 * IoT 规则场景 Service 实现类
@ -61,6 +67,49 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
@Resource(name = "iotSchedulerManager") @Resource(name = "iotSchedulerManager")
private IotSchedulerManager schedulerManager; private IotSchedulerManager schedulerManager;
@Override
public Long createRuleScene(IotRuleSceneSaveReqVO createReqVO) {
// 插入
IotRuleSceneDO ruleScene = BeanUtils.toBean(createReqVO, IotRuleSceneDO.class);
ruleSceneMapper.insert(ruleScene);
// 返回
return ruleScene.getId();
}
@Override
public void updateRuleScene(IotRuleSceneSaveReqVO updateReqVO) {
// 校验存在
validateRuleSceneExists(updateReqVO.getId());
// 更新
IotRuleSceneDO updateObj = BeanUtils.toBean(updateReqVO, IotRuleSceneDO.class);
ruleSceneMapper.updateById(updateObj);
}
@Override
public void deleteRuleScene(Long id) {
// 校验存在
validateRuleSceneExists(id);
// 删除
ruleSceneMapper.deleteById(id);
}
private void validateRuleSceneExists(Long id) {
if (ruleSceneMapper.selectById(id) == null) {
throw exception(RULE_SCENE_NOT_EXISTS);
}
}
@Override
public IotRuleSceneDO getRuleScene(Long id) {
return ruleSceneMapper.selectById(id);
}
@Override
public PageResult<IotRuleSceneDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO) {
return ruleSceneMapper.selectPage(pageReqVO);
}
// TODO 芋艿缓存待实现 // TODO 芋艿缓存待实现
@Override @Override
@TenantIgnore // 忽略租户隔离因为 IotRuleSceneMessageHandler 调用时一般未传递租户所以需要忽略 @TenantIgnore // 忽略租户隔离因为 IotRuleSceneMessageHandler 调用时一般未传递租户所以需要忽略
@ -331,7 +380,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue); springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue);
springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, parameter.getValue()); springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, parameter.getValue());
List<String> parameterValues = StrUtil.splitTrim(parameter.getValue(), CharPool.COMMA); List<String> parameterValues = StrUtil.splitTrim(parameter.getValue(), CharPool.COMMA);
springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, parameterValues); springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues);
// 特殊解决数字的比较因为 Spring 是基于它的 compareTo 方法对数字的比较存在问题 // 特殊解决数字的比较因为 Spring 是基于它的 compareTo 方法对数字的比较存在问题
if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN, if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN,
IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN, IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN,
@ -345,7 +394,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
NumberUtil.parseDouble(messageValue)); NumberUtil.parseDouble(messageValue));
springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE,
NumberUtil.parseDouble(parameter.getValue())); NumberUtil.parseDouble(parameter.getValue()));
springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
convertList(parameterValues, NumberUtil::parseDouble)); convertList(parameterValues, NumberUtil::parseDouble));
} }
// 2.2 计算 Spring 表达式 // 2.2 计算 Spring 表达式