开源版新增规则脚本

This commit is contained in:
kerwincui
2024-04-23 16:34:20 +08:00
parent de7e2529a3
commit 054e414b48
50 changed files with 7233 additions and 13 deletions

View File

@@ -163,6 +163,11 @@ public interface FastBeeConstant {
/**固件版本key*/
String FIRMWARE_VERSION = "device:firmware:";
/**
* 设备信息
*/
String DEVICE_MSG = "device:msg:";
/**采集点变更记录缓存key*/
String COLLECT_POINT_CHANGE = "collect:point:change:";
/**属性下发回调*/

View File

@@ -81,4 +81,11 @@ public class RedisKeyBuilder {
return FastBeeConstant.REDIS.DEVICE_MESSAGE_ID;
}
/**
* 缓存产品id设备编号协议编号
*/
public static String buildDeviceMsgCacheKey(String serialNumber){
return FastBeeConstant.REDIS.DEVICE_MSG + serialNumber;
}
}

View File

@@ -0,0 +1,38 @@
package com.fastbee.common.core.thingsModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
/**
* 物模型值的项
*
* @author kerwincui
* @date 2021-12-16
*/
@AllArgsConstructor
@Builder
@Data
public class SceneThingsModelItem
{
/** 物模型唯一标识符 */
private String id;
/** 物模型值 */
private String value;
/** 类型1=属性, 2=功能3=事件, 4=设备升级5=设备上线6=设备下线 */
private int type;
/** 脚本ID */
private String stripId;
/** 场景ID*/
private Long sceneId;
/** 产品ID */
private Long productId;
/** 设备编号 */
private String DeviceNumber;
}

View File

@@ -4,8 +4,11 @@ import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.common.enums.ServerType;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.gateway.mq.TopicsPost;
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
import com.fastbee.iot.ruleEngine.MsgContext;
import com.fastbee.iot.ruleEngine.RuleProcess;
import com.fastbee.mq.redischannel.producer.MessageProducer;
import com.fastbee.mq.service.IDeviceReportMessageService;
import com.fastbee.mq.service.IMessagePublishService;
@@ -17,6 +20,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Objects;
@Component
@@ -28,6 +32,9 @@ public class MqttService {
@Resource
private IDeviceReportMessageService deviceReportMessageService;
@Resource
private RuleProcess ruleProcess;
public void subscribe(MqttAsyncClient client) throws MqttException {
@@ -48,6 +55,18 @@ public class MqttService {
log.info("接收消息主题 : " + topic);
log.info("接收消息Qos : " + mqttMessage.getQos());
log.info("接收消息内容 : " + message);
//这里默认设备编号长度超过9位
String[] split = topic.split("/");
String clientId = Arrays.stream(split).filter(imei -> imei.length() > 9).findFirst().get();
// 规则引擎脚本处理,完成后返回结果
MsgContext context = ruleProcess.processRuleScript(clientId, 1, topic, message);
if (!Objects.isNull(context) && StringUtils.isNotEmpty(context.getPayload())
&& StringUtils.isNotEmpty(context.getTopic())) {
topic = context.getTopic();
message = context.getPayload();
}
String serialNumber = topicsUtils.parseSerialNumber(topic);
Long productId = topicsUtils.parseProductId(topic);
String name = topicsUtils.parseTopicName(topic);

View File

@@ -0,0 +1,553 @@
package com.fastbee.mq.ruleEngine;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fastbee.common.core.mq.InvokeReqDto;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.core.redis.RedisKeyBuilder;
import com.fastbee.common.core.thingsModel.SceneThingsModelItem;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.spring.SpringUtils;
import com.fastbee.iot.model.ScriptTemplate;
import com.fastbee.iot.model.ThingsModels.ValueItem;
import com.fastbee.iot.service.IDeviceService;
import com.fastbee.iot.service.IDeviceUserService;
import com.fastbee.mq.service.IFunctionInvoke;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.regex.Pattern.compile;
@Data
public class SceneContext {
/**
* 上报信息的设备编号
*/
private String deviceNum;
/**
* 上报信息的设备所属产品ID
*/
private Long productId;
/**
* 上报信息的设备信息类型 1=属性, 2=功能3=事件4=设备升级5=设备上线6=设备下线
*/
private int type;
/**
* 上报的物模型集合
*/
private List<ThingsModelSimpleItem> thingsModelSimpleItems;
/**
* 触发成功的物模型集合,保留给告警记录
*/
private List<SceneThingsModelItem> sceneThingsModelItems;
private static IFunctionInvoke functionInvoke = SpringUtils.getBean(IFunctionInvoke.class);
private static IDeviceService deviceService = SpringUtils.getBean(IDeviceService.class);
// private static IAlertService alertService = SpringUtils.getBean(IAlertService.class);
// private static NotifySendService notifySendService = SpringUtils.getBean(NotifySendService.class);
private static RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
// private static AlertLogMapper alertLogMapper = SpringUtils.getBean(AlertLogMapper.class);
private static IDeviceUserService deviceUserService = SpringUtils.getBean(IDeviceUserService.class);
public SceneContext(String deviceNum, Long productId, int type, List<ThingsModelSimpleItem> thingsModelSimpleItems) {
this.deviceNum = deviceNum;
this.productId = productId;
this.type = type;
this.thingsModelSimpleItems = thingsModelSimpleItems;
}
/**
* 处理规则脚本
*
* @return
*/
private boolean process(String json) throws InterruptedException {
System.out.println("------------------[规则引擎执行...]---------------------");
ScriptTemplate scriptTemplate = JSON.parseObject(json, ScriptTemplate.class);
if (scriptTemplate.getPurpose() == 2) {
// 触发器,检查静默时间
if (!checkSilent(scriptTemplate.getSilent(), scriptTemplate.getSceneId())) {
// 触发条件为不满足时返回true
if (scriptTemplate.getCond() == 3) {
return true;
}
return false;
}
// 触发器
if (scriptTemplate.getSource() == 1) {
// 设备触发
return deviceTrigger(scriptTemplate);
} else if (scriptTemplate.getSource() == 3) {
// 产品触发
return productTrigger(scriptTemplate);
}
} /*else if (scriptTemplate.getPurpose() == 3) {
// 执行动作,延迟执行,线程休眠 delay x 1000毫秒
Thread.sleep(scriptTemplate.getDelay() * 1000);
if (scriptTemplate.getSource() == 4) {
// 告警
this.alert(scriptTemplate.getDelay(), scriptTemplate.getSceneId());
} else if (scriptTemplate.getSource() == 1 || scriptTemplate.getSource() == 3) {
// 下发指令
this.send(scriptTemplate);
}
// 更新静默时间
this.updateSilent(scriptTemplate.getSilent(), scriptTemplate.getSceneId());
}*/
return false;
}
/***
* 设备触发脚本处理
* @param scriptTemplate 解析后的Json脚本数据
* @return
*/
private boolean deviceTrigger(ScriptTemplate scriptTemplate) {
// 判断定制触发(执行一次)或设备上报
boolean isDeviceReport = StringUtils.isEmpty(deviceNum) ? false : true;
if (isDeviceReport) {
// 1. 匹配设备编号
boolean matchDeviceNum = Arrays.asList(scriptTemplate.getDeviceNums().split(",")).contains(deviceNum);
if (scriptTemplate.getType() < 4) {
// 2.匹配物模型标识符
ThingsModelSimpleItem matchItem = null;
if (thingsModelSimpleItems != null) {
for (ThingsModelSimpleItem item : thingsModelSimpleItems) {
if (item.getId().equals(scriptTemplate.getId())) {
matchItem = item;
break;
}
}
}
if (matchDeviceNum && matchItem != null) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), matchItem.getValue(), type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum);
sceneThingsModelItems.add(sceneItem);
// 3.设备上报值匹配
boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), matchItem.getValue());
if (isMatch) {
return true;
}
}
} else {
// 上线,下线
if (matchDeviceNum && scriptTemplate.getType() == type) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(type == 5 ? "online" : "offline", type == 5 ? "1" : "0", type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum);
sceneThingsModelItems.add(sceneItem);
// 记录结果
return true;
}
}
} else {
// 定时触发/执行一次
int resultCount = 0;
// 3.查询设备最新上报值去匹配
for (String num : Arrays.asList(scriptTemplate.getDeviceNums().split(","))) {
// 数组类型key去除前缀值从逗号分隔的字符串获取
String id = "";
String value = "";
int index = 0;
if (scriptTemplate.getId().startsWith("array_")) {
id = scriptTemplate.getId().substring(9);
index = Integer.parseInt(scriptTemplate.getId().substring(6, 8));
} else {
id = scriptTemplate.getId();
}
String key = RedisKeyBuilder.buildTSLVCacheKey(scriptTemplate.getProductId(), num);
String cacheValue = redisCache.getCacheMapValue(key, id);
if (StringUtils.isEmpty(cacheValue)) {
continue;
}
ValueItem valueItem = JSON.parseObject(cacheValue, ValueItem.class);
if (scriptTemplate.getId().startsWith("array_")) {
String[] values = valueItem.getValue().split(",");
value = values[index];
} else {
value = valueItem.getValue();
}
boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), value);
if (isMatch) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), value, type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), num);
sceneThingsModelItems.add(sceneItem);
resultCount++;
}
}
// 任意设备匹配成功返回true
return resultCount > 0 ? true : false;
}
return false;
}
/***
* 产品触发脚本处理
* @param scriptTemplate
* @return
*/
private boolean productTrigger(ScriptTemplate scriptTemplate) {
// 判断定制触发(执行一次)或设备上报
boolean isDeviceReport = StringUtils.isEmpty(deviceNum) ? false : true;
if (isDeviceReport) {
// 匹配产品编号
boolean matchProductId = scriptTemplate.getProductId().equals(productId);
if (scriptTemplate.getType() < 4) {
// 匹配物模型标识符
ThingsModelSimpleItem matchItem = null;
if (thingsModelSimpleItems != null) {
for (ThingsModelSimpleItem item : thingsModelSimpleItems) {
if (item.getId().equals(scriptTemplate.getId())) {
matchItem = item;
break;
}
}
}
if (matchProductId && matchItem != null) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), matchItem.getValue(), type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum);
sceneThingsModelItems.add(sceneItem);
// 设备上报值匹配
boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), matchItem.getValue());
if (isMatch) {
return true;
}
}
} else {
// 上线,下线
if (matchProductId && scriptTemplate.getType() == type) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(type == 5 ? "online" : "offline", type == 5 ? "1" : "0", type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum);
sceneThingsModelItems.add(sceneItem);
// 记录结果
return true;
}
}
} else {
// 定时触发/执行一次
int resultCount = 0;
// 查询设备最新上报值去匹配
String[] deviceNums = deviceService.getDeviceNumsByProductId(scriptTemplate.getProductId());
for (String num : deviceNums) {
// 数组类型key去除前缀值从逗号分隔的字符串获取
String id = "";
String value = "";
int index = 0;
if (scriptTemplate.getId().startsWith("array_")) {
id = scriptTemplate.getId().substring(9);
index = Integer.parseInt(scriptTemplate.getId().substring(6, 8));
} else {
id = scriptTemplate.getId();
}
String key = RedisKeyBuilder.buildTSLVCacheKey(scriptTemplate.getProductId(), num);
String cacheValue = redisCache.getCacheMapValue(key, id);
if (StringUtils.isEmpty(cacheValue)) {
continue;
}
ValueItem valueItem = JSON.parseObject(cacheValue, ValueItem.class);
if (scriptTemplate.getId().startsWith("array_")) {
String[] values = valueItem.getValue().split(",");
value = values[index];
} else {
value = valueItem.getValue();
}
boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), value);
if (isMatch) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), value, type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), num);
sceneThingsModelItems.add(sceneItem);
resultCount++;
}
}
// 任意设备匹配成功返回true
return resultCount > 0 ? true : false;
}
return false;
}
/**
* 执行动作,下发指令
*
* @param scriptTemplate
*/
private void send(ScriptTemplate scriptTemplate) {
String[] deviceNumbers = null;
if (scriptTemplate.getSource() == 1) {
// 下发给指定设备
deviceNumbers = scriptTemplate.getDeviceNums().split(",");
} else if (scriptTemplate.getSource() == 3) {
// 下发给产品下所有设备
deviceNumbers = deviceService.getDeviceNumsByProductId(scriptTemplate.getProductId());
}
for (String deviceNum : deviceNumbers) {
InvokeReqDto reqDto = new InvokeReqDto();
reqDto.setProductId(scriptTemplate.getProductId());
reqDto.setSerialNumber(deviceNum);
reqDto.setModelName("");
reqDto.setType(1);
reqDto.setIdentifier(scriptTemplate.getId());
Map<String, Object> params = new HashMap<>();
params.put(scriptTemplate.getId(), scriptTemplate.getValue());
reqDto.setRemoteCommand(params);
reqDto.setValue(new JSONObject(reqDto.getRemoteCommand()));
functionInvoke.invokeNoReply(reqDto);
}
}
/**
* 执行动作,告警处理
*
* @param sceneId 场景ID
* @param delay 延时(单位秒,90秒内
*/
/*private void alert(int delay, Long sceneId) {
for (SceneThingsModelItem sceneThingsModelItem : sceneThingsModelItems) {
// 查询设备信息
Device device = deviceService.selectDeviceBySerialNumber(sceneThingsModelItem.getDeviceNumber());
Optional.ofNullable(device).orElseThrow(() -> new ServiceException("告警推送,设备不存在" + "[{" + sceneThingsModelItem.getDeviceNumber() + "}]"));
// 获取场景相关的告警参数,告警必须要是启动状态
List<AlertSceneSendVO> sceneSendVOList = alertService.listByAlertIds(sceneId);
if (CollectionUtils.isEmpty(sceneSendVOList)) {
return;
}
// 获取告警推送参数
AlertPushParams alertPushParams = new AlertPushParams();
alertPushParams.setDeviceName(device.getDeviceName());
alertPushParams.setSerialNumber(sceneThingsModelItem.getDeviceNumber());
// 获取设备所属及分享用户信息
List<DeviceUser> deviceUserList = deviceUserService.selectDeviceUserByDeviceId(device.getDeviceId());
if (CollectionUtils.isNotEmpty(deviceUserList)) {
alertPushParams.setUserPhoneSet(deviceUserList.stream().map(DeviceUser::getPhonenumber).filter(StringUtils::isNotEmpty).collect(Collectors.toSet()));
alertPushParams.setUserIdSet(deviceUserList.stream().map(DeviceUser::getUserId).collect(Collectors.toSet()));
}
String address;
if (StringUtils.isNotEmpty(device.getNetworkAddress())) {
address = device.getNetworkAddress();
} else if (StringUtils.isNotEmpty(device.getNetworkIp())) {
address = device.getNetworkIp();
} else if (Objects.nonNull(device.getLongitude()) && Objects.nonNull(device.getLatitude())) {
address = device.getLongitude() + "," + device.getLatitude();
} else {
address = "未知地点";
}
alertPushParams.setAddress(address);
alertPushParams.setAlertTime(DateUtils.parseDateToStr(DateUtils.YY_MM_DD_HH_MM_SS, new Date()));
List<AlertLog> alertLogList = new ArrayList<>();
// 获取告警关联模版id
for (AlertSceneSendVO alertSceneSendVO : sceneSendVOList) {
List<AlertNotifyTemplate> alertNotifyTemplateList = alertService.listAlertNotifyTemplate(alertSceneSendVO.getAlertId());
alertPushParams.setAlertName(alertSceneSendVO.getAlertName());
for (AlertNotifyTemplate alertNotifyTemplate : alertNotifyTemplateList) {
alertPushParams.setNotifyTemplateId(alertNotifyTemplate.getNotifyTemplateId());
notifySendService.alertSend(alertPushParams);
}
AlertLog alertLog = getAlertLog(alertSceneSendVO, device, sceneThingsModelItem);
alertLogList.add(alertLog);
}
// 保存告警日志
if (CollectionUtils.isNotEmpty(alertLogList)) {
alertLogMapper.insertAlertLogBatch(alertLogList);
}
}
}
*//**
* 组装告警日志
*
* @param alertSceneSendVO
* @return com.fastbee.iot.domain.AlertLog
* @param: device
*//*
private AlertLog getAlertLog(AlertSceneSendVO alertSceneSendVO, Device device, SceneThingsModelItem sceneThingsModelItem) {
AlertLog alertLog = new AlertLog();
alertLog.setAlertName(alertSceneSendVO.getAlertName());
alertLog.setAlertLevel(alertSceneSendVO.getAlertLevel());
alertLog.setSerialNumber(sceneThingsModelItem.getDeviceNumber());
alertLog.setProductId(sceneThingsModelItem.getProductId());
alertLog.setDeviceName(device.getDeviceName());
// 统一未处理
alertLog.setStatus(2);
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", sceneThingsModelItem.getId());
jsonObject.put("value", sceneThingsModelItem.getValue());
jsonObject.put("remark", "");
alertLog.setDetail(jsonObject.toJSONString());
alertLog.setCreateTime(new Date());
return alertLog;
}*/
/**
* 检查静默周期物模型值是否匹配
*
* @param operator 操作符
* @param triggerValue 触发值
* @param value 上报的值
* @return
*/
private boolean matchValue(String operator, String triggerValue, String value) {
boolean result = false;
// 操作符比较
switch (operator) {
case "=":
result = value.equals(triggerValue);
break;
case "!=":
result = !value.equals(triggerValue);
break;
case ">":
if (isNumeric(value) && isNumeric(triggerValue)) {
result = Double.parseDouble(value) > Double.parseDouble(triggerValue);
}
break;
case "<":
if (isNumeric(value) && isNumeric(triggerValue)) {
result = Double.parseDouble(value) < Double.parseDouble(triggerValue);
}
break;
case ">=":
if (isNumeric(value) && isNumeric(triggerValue)) {
result = Double.parseDouble(value) >= Double.parseDouble(triggerValue);
}
break;
case "<=":
if (isNumeric(value) && isNumeric(triggerValue)) {
result = Double.parseDouble(value) <= Double.parseDouble(triggerValue);
}
break;
case "between":
// 比较值用英文中划线分割 -
String[] triggerValues = triggerValue.split("-");
if (isNumeric(value) && isNumeric(triggerValues[0]) && isNumeric(triggerValues[1])) {
result = Double.parseDouble(value) >= Double.parseDouble(triggerValues[0]) && Double.parseDouble(value) <= Double.parseDouble(triggerValues[1]);
}
break;
case "notBetween":
// 比较值用英文中划线分割 -
String[] trigValues = triggerValue.split("-");
if (isNumeric(value) && isNumeric(trigValues[0]) && isNumeric(trigValues[1])) {
result = Double.parseDouble(value) <= Double.parseDouble(trigValues[0]) || Double.parseDouble(value) >= Double.parseDouble(trigValues[1]);
}
break;
case "contain":
result = value.contains(triggerValue);
break;
case "notContain":
result = !value.contains(triggerValue);
break;
default:
break;
}
return result;
}
/**
* 检查静默时间
*
* @param silent
* @param sceneId
* @return
*/
private boolean checkSilent(int silent, Long sceneId) {
if (silent == 0 || sceneId == 0) {
return true;
}
// silent:scene_场景编号
String key = "silent:" + "scene_" + sceneId;
Calendar calendar = Calendar.getInstance();
// 查询静默截止时间
Long expireTime = redisCache.getCacheObject(key);
if (expireTime == null) {
// 添加场景静默时间
calendar.add(Calendar.MINUTE, silent);
redisCache.setCacheObject(key, calendar.getTimeInMillis());
return true;
} else {
Long NowTimestamp = Calendar.getInstance().getTimeInMillis();
if (NowTimestamp > expireTime) {
return true;
}
return false;
}
}
/**
* 更新静默时间
*
* @param sceneId
* @param silent
*/
private void updateSilent(int silent, Long sceneId) {
if (silent == 0 || sceneId == 0) {
return;
}
// 更新场景静默时间
String key = "silent:" + "scene_" + sceneId;
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, silent);
redisCache.setCacheObject(key, calendar.getTimeInMillis());
}
/**
* 判断字符串是否为整数或小数
*/
private boolean isNumeric(String str) {
Pattern pattern = compile("[0-9]*\\.?[0-9]+");
Matcher isNum = pattern.matcher(str);
if (!isNum.matches()) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,126 @@
package com.fastbee.data.controller;
import com.fastbee.common.annotation.Log;
import com.fastbee.common.core.controller.BaseController;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.core.page.TableDataInfo;
import com.fastbee.common.enums.BusinessType;
import com.fastbee.common.utils.poi.ExcelUtil;
import com.fastbee.iot.domain.Scene;
import com.fastbee.iot.service.ISceneService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 场景联动Controller
*
* @author kerwincui
* @date 2022-01-13
*/
@Api(tags = "场景联动")
@RestController
@RequestMapping("/iot/scene")
public class SceneController extends BaseController
{
@Autowired
private ISceneService sceneService;
/**
* 查询场景联动列表
*/
@ApiOperation("查询场景联动列表")
@PreAuthorize("@ss.hasPermi('iot:scene:list')")
@GetMapping("/list")
public TableDataInfo list(Scene scene)
{
startPage();
List<Scene> list = sceneService.selectSceneList(scene);
return getDataTable(list);
}
/**
* 导出场景联动列表
*/
@ApiOperation("导出场景联动列表")
@PreAuthorize("@ss.hasPermi('iot:scene:export')")
@Log(title = "场景联动", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, Scene scene)
{
List<Scene> list = sceneService.selectSceneList(scene);
ExcelUtil<Scene> util = new ExcelUtil<Scene>(Scene.class);
util.exportExcel(response, list, "场景联动数据");
}
/**
* 获取场景联动详细信息
*/
@ApiOperation("获取场景联动详细信息")
@PreAuthorize("@ss.hasPermi('iot:scene:query')")
@GetMapping(value = "/{sceneId}")
public AjaxResult getInfo(@PathVariable("sceneId") Long sceneId)
{
return AjaxResult.success(sceneService.selectSceneBySceneId(sceneId));
}
/**
* 新增场景联动
*/
@ApiOperation("新增场景联动")
@PreAuthorize("@ss.hasPermi('iot:scene:add')")
@Log(title = "场景联动", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody Scene scene)
{
LoginUser loginUser = getLoginUser();
if (loginUser == null || loginUser.getUser() == null) {
return error("请登录后重试!");
}
scene.setUserId(loginUser.getUser().getUserId());
scene.setUserName(loginUser.getUser().getUserName());
return toAjax(sceneService.insertScene(scene));
}
/**
* 修改场景联动
*/
@ApiOperation("修改场景联动")
@PreAuthorize("@ss.hasPermi('iot:scene:edit')")
@Log(title = "场景联动", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody Scene scene)
{
return toAjax(sceneService.updateScene(scene));
}
/**
* 删除场景联动
*/
@ApiOperation("删除场景联动")
@PreAuthorize("@ss.hasPermi('iot:scene:remove')")
@Log(title = "场景联动", businessType = BusinessType.DELETE)
@DeleteMapping("/{sceneIds}")
public AjaxResult remove(@PathVariable Long[] sceneIds)
{
return toAjax(sceneService.deleteSceneBySceneIds(sceneIds));
}
/**
* 修改场景联动状态
*/
@ApiOperation("修改场景联动状态")
@PreAuthorize("@ss.hasPermi('iot:scene:edit')")
@Log(title = "场景联动", businessType = BusinessType.UPDATE)
@PutMapping("/updateStatus")
public AjaxResult updateStatus(@RequestBody Scene scene)
{
return toAjax(sceneService.updateStatus(scene));
}
}

View File

@@ -0,0 +1,107 @@
package com.fastbee.data.controller;
import com.fastbee.common.annotation.Log;
import com.fastbee.common.core.controller.BaseController;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.page.TableDataInfo;
import com.fastbee.common.enums.BusinessType;
import com.fastbee.common.utils.poi.ExcelUtil;
import com.fastbee.iot.domain.Script;
import com.fastbee.iot.service.IScriptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 规则引擎脚本Controller
*
* @author lizhuangpeng
* @date 2023-07-01
*/
@RestController
@RequestMapping("/iot/script")
public class ScriptController extends BaseController
{
@Autowired
private IScriptService scriptService;
/**
* 查询规则引擎脚本列表
*/
@PreAuthorize("@ss.hasPermi('iot:script:list')")
@GetMapping("/list")
public TableDataInfo list(Script ruleScript)
{
startPage();
List<Script> list = scriptService.selectRuleScriptList(ruleScript);
return getDataTable(list);
}
/**
* 导出规则引擎脚本列表
*/
@PreAuthorize("@ss.hasPermi('iot:script:export')")
@Log(title = "规则引擎脚本", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, Script ruleScript)
{
List<Script> list = scriptService.selectRuleScriptList(ruleScript);
ExcelUtil<Script> util = new ExcelUtil<Script>(Script.class);
util.exportExcel(response, list, "规则引擎脚本数据");
}
/**
* 获取规则引擎脚本详细信息
*/
@PreAuthorize("@ss.hasPermi('iot:script:query')")
@GetMapping(value = "/{scriptId}")
public AjaxResult getInfo(@PathVariable("scriptId") String scriptId)
{
return success(scriptService.selectRuleScriptById(scriptId));
}
/**
* 新增规则引擎脚本
*/
@PreAuthorize("@ss.hasPermi('iot:script:add')")
@Log(title = "规则引擎脚本", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody Script ruleScript)
{
return toAjax(scriptService.insertRuleScript(ruleScript));
}
/**
* 修改规则引擎脚本
*/
@PreAuthorize("@ss.hasPermi('iot:script:edit')")
@Log(title = "规则引擎脚本", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody Script ruleScript)
{
return toAjax(scriptService.updateRuleScript(ruleScript));
}
/**
* 删除规则引擎脚本
*/
@PreAuthorize("@ss.hasPermi('iot:script:remove')")
@Log(title = "规则引擎脚本", businessType = BusinessType.DELETE)
@DeleteMapping("/{scriptIds}")
public AjaxResult remove(@PathVariable String[] scriptIds)
{
return toAjax(scriptService.deleteRuleScriptByIds(scriptIds));
}
/**
* 删除规则引擎脚本
*/
@PostMapping("/validate")
public AjaxResult validateScript(@RequestBody Script ruleScript)
{
return scriptService.validateScript(ruleScript);
}
}

View File

@@ -331,4 +331,9 @@ public class DeviceJobServiceImpl implements IDeviceJobService
{
return CronUtils.isValid(cronExpression);
}
@Override
public List<DeviceJob> listShortJobBySceneId(Long[] sceneIds) {
return jobMapper.selectShortJobListBySceneIds(sceneIds);
}
}

View File

@@ -5,7 +5,10 @@ import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.enums.ServerType;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
import com.fastbee.iot.ruleEngine.MsgContext;
import com.fastbee.iot.ruleEngine.RuleProcess;
import com.fastbee.mq.redischannel.producer.MessageProducer;
import com.fastbee.mq.service.IDeviceReportMessageService;
import com.fastbee.mqtt.annotation.Process;
@@ -25,6 +28,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
* 客户端消息推送处理类
@@ -44,6 +49,9 @@ public class MqttPublish implements MqttHandler {
@Resource
private IDeviceReportMessageService deviceReportMessageService;
@Resource
private RuleProcess ruleProcess;
@Override
public void handler(ChannelHandlerContext ctx, MqttMessage message) {
MqttPublishMessage publishMessage = (MqttPublishMessage) message;
@@ -81,6 +89,7 @@ public class MqttPublish implements MqttHandler {
public void sendToMQ(MqttPublishMessage message) {
/*获取topic*/
String topicName = message.variableHeader().topicName();
byte[] source = ByteBufUtil.getBytes(message.content());
/*只处理上报数据*/
if (!topicName.endsWith(FastBeeConstant.MQTT.UP_TOPIC_SUFFIX)) {
return;
@@ -102,6 +111,13 @@ public class MqttPublish implements MqttHandler {
/*设备上报数据*/
reportBo.setReportType(1);
}
// 规则引擎脚本处理,完成后返回结果
MsgContext context = ruleProcess.processRuleScript(reportBo.getSerialNumber(),1, topicName, new String(source));
if (!Objects.isNull(context) && StringUtils.isNotEmpty(context.getPayload())
&& StringUtils.isNotEmpty(context.getTopic())) {
reportBo.setTopicName(context.getTopic());
reportBo.setData(context.getPayload().getBytes(StandardCharsets.UTF_8));
}
if (topicName.contains("property")) {
deviceReportMessageService.parseReportMsg(reportBo);
}

View File

@@ -24,6 +24,8 @@ import com.fastbee.iot.domain.FunctionLog;
import com.fastbee.iot.domain.Product;
import com.fastbee.iot.model.NtpModel;
import com.fastbee.iot.model.ThingsModels.PropertyDto;
import com.fastbee.iot.ruleEngine.MsgContext;
import com.fastbee.iot.ruleEngine.RuleProcess;
import com.fastbee.iot.service.IDeviceService;
import com.fastbee.iot.service.IProductService;
import com.fastbee.iot.service.IThingsModelService;
@@ -37,7 +39,6 @@ import com.fastbee.mq.service.IMqttMessagePublish;
import com.fastbee.mqtt.manager.MqttRemoteManager;
import com.fastbee.mqtt.model.PushMessageBo;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.protocols.IProtocol;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -76,6 +77,9 @@ public class MqttMessagePublishImpl implements IMqttMessagePublish {
private JsonProtocolService jsonProtocolService;
private SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(3);
@Resource
private RuleProcess ruleProcess;
@Override
public InstructionsMessage buildMessage(DeviceDownMessage downMessage, TopicType type) {
@@ -145,19 +149,19 @@ public class MqttMessagePublishImpl implements IMqttMessagePublish {
}
/* 下发服务数据存储对象*/
FunctionLog log = new FunctionLog();
log.setCreateTime(DateUtils.getNowDate());
log.setFunValue(bo.getValue().get(bo.getIdentifier()).toString());
log.setMessageId(bo.getMessageId());
log.setSerialNumber(bo.getSerialNumber());
log.setIdentify(bo.getIdentifier());
log.setShowValue(bo.getShowValue());
log.setFunType(1);
log.setModelName(bo.getModelName());
FunctionLog funcLog = new FunctionLog();
funcLog.setCreateTime(DateUtils.getNowDate());
funcLog.setFunValue(bo.getValue().get(bo.getIdentifier()).toString());
funcLog.setMessageId(bo.getMessageId());
funcLog.setSerialNumber(bo.getSerialNumber());
funcLog.setIdentify(bo.getIdentifier());
funcLog.setShowValue(bo.getShowValue());
funcLog.setFunType(1);
funcLog.setModelName(bo.getModelName());
//兼容子设备
if (null != bo.getSlaveId()) {
PropertyDto thingModels = thingsModelService.getSingleThingModels(bo.getProductId(), bo.getIdentifier() + "#" + bo.getSlaveId());
log.setSerialNumber(bo.getSerialNumber() + "_" + bo.getSlaveId());
funcLog.setSerialNumber(bo.getSerialNumber() + "_" + bo.getSlaveId());
bo.setCode(ModbusCode.Write06);
if (!Objects.isNull(thingModels.getCode())){
bo.setCode(ModbusCode.getInstance(Integer.parseInt(thingModels.getCode())));
@@ -182,8 +186,17 @@ public class MqttMessagePublishImpl implements IMqttMessagePublish {
case MQTT:
//组建下发服务指令
InstructionsMessage instruction = buildMessage(downMessage, TopicType.FUNCTION_GET);
mqttClient.publish(instruction.getTopicName(), instruction.getMessage(), log);
MqttMessagePublishImpl.log.debug("=>服务下发,topic=[{}],指令=[{}]", instruction.getTopicName(),new String(instruction.getMessage()));
// 规则引擎脚本处理,完成后返回结果
MsgContext context = ruleProcess.processRuleScript(bo.getSerialNumber(), 2,instruction.getTopicName(),new String(instruction.getMessage()));
if (!Objects.isNull(context) && StringUtils.isNotEmpty(context.getPayload())
&& StringUtils.isNotEmpty(context.getTopic())) {
instruction.setTopicName(context.getTopic());
instruction.setMessage(context.getPayload().getBytes());
}
mqttClient.publish(instruction.getTopicName(), instruction.getMessage(), funcLog);
log.debug("=>服务下发,topic=[{}],指令=[{}]", instruction.getTopicName(),new String(instruction.getMessage()));
break;
}

View File

@@ -15,6 +15,10 @@
设备业务模块
</description>
<properties>
<liteflow.version>2.11.3</liteflow.version>
</properties>
<dependencies>
<!-- 通用工具-->
@@ -102,6 +106,24 @@
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>${liteflow.version}</version>
</dependency>
<!--数据库规则配置源插件-->
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-rule-sql</artifactId>
<version>${liteflow.version}</version>
</dependency>
<!--groovy脚本插件-->
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-groovy</artifactId>
<version>${liteflow.version}</version>
</dependency>
</dependencies>

View File

@@ -0,0 +1,244 @@
package com.fastbee.iot.domain;
import com.fastbee.common.annotation.Excel;
import com.fastbee.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
/**
* 场景联动对象 iot_scene
*
* @author kerwincui
* @date 2023-12-27
*/
public class Scene extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 场景ID */
private Long sceneId;
/** 场景名称 */
@Excel(name = "场景名称")
private String sceneName;
/** 规则名称 */
private String chainName;
/** 用户ID */
@Excel(name = "用户ID")
private Long userId;
/** 用户名称 */
@Excel(name = "用户名称")
private String userName;
/** 执行条件1=或、任一条件2=且、所有条件3=非,不满足) */
@Excel(name = "执行条件", readConverterExp = "1==或、任一条件2=且、所有条件3=非,不满足")
private Integer cond;
/** 静默周期(分钟) */
@Excel(name = "静默周期", readConverterExp = "分=钟")
private Integer silentPeriod;
/** 执行方式1=串行顺序执行2=并行,同时执行) */
@Excel(name = "执行方式", readConverterExp = "1==串行顺序执行2=并行,同时执行")
private Integer executeMode;
/** 延时执行(秒钟) */
@Excel(name = "延时执行", readConverterExp = "秒=钟")
private Integer executeDelay;
/** 是否包含告警推送1=包含2=不包含) */
@Excel(name = "是否包含告警推送", readConverterExp = "1==包含2=不包含")
private Integer hasAlert;
/** 场景状态1-启动2-停止) */
@Excel(name = "场景状态", readConverterExp = "1=-启动2-停止")
private Integer enable;
/** 规则数据 */
@Excel(name = "规则数据")
private String elData;
/** 应用名称 */
@Excel(name = "应用名称")
private String applicationName;
/** 接收的触发器列表 */
private List<SceneScript> triggers;
/** 接收的执行动作列表 */
private List<SceneScript> actions;
/**
* 执行动作数量
*/
private Integer actionCount = 0;
public Integer getActionCount() {
return actionCount;
}
public void setActionCount(Integer actionCount) {
this.actionCount = actionCount;
}
public String getChainName() {
return chainName;
}
public void setChainName(String chainName) {
this.chainName = chainName;
}
public List<SceneScript> getTriggers() {
return triggers;
}
public void setTriggers(List<SceneScript> triggers) {
this.triggers = triggers;
}
public List<SceneScript> getActions() {
return actions;
}
public void setActions(List<SceneScript> actions) {
this.actions = actions;
}
public void setSceneId(Long sceneId)
{
this.sceneId = sceneId;
}
public Long getSceneId()
{
return sceneId;
}
public void setSceneName(String sceneName)
{
this.sceneName = sceneName;
}
public String getSceneName()
{
return sceneName;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
public void setUserName(String userName)
{
this.userName = userName;
}
public String getUserName()
{
return userName;
}
public void setCond(Integer cond)
{
this.cond = cond;
}
public Integer getCond()
{
return cond;
}
public void setSilentPeriod(Integer silentPeriod)
{
this.silentPeriod = silentPeriod;
}
public Integer getSilentPeriod()
{
return silentPeriod;
}
public void setExecuteMode(Integer executeMode)
{
this.executeMode = executeMode;
}
public Integer getExecuteMode()
{
return executeMode;
}
public void setExecuteDelay(Integer executeDelay)
{
this.executeDelay = executeDelay;
}
public Integer getExecuteDelay()
{
return executeDelay;
}
public void setHasAlert(Integer hasAlert)
{
this.hasAlert = hasAlert;
}
public Integer getHasAlert()
{
return hasAlert;
}
public void setEnable(Integer enable)
{
this.enable = enable;
}
public Integer getEnable()
{
return enable;
}
public void setElData(String elData)
{
this.elData = elData;
}
public String getElData()
{
return elData;
}
public void setApplicationName(String applicationName)
{
this.applicationName = applicationName;
}
public String getApplicationName()
{
return applicationName;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("sceneId", getSceneId())
.append("sceneName", getSceneName())
.append("userId", getUserId())
.append("userName", getUserName())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("cond", getCond())
.append("silentPeriod", getSilentPeriod())
.append("executeMode", getExecuteMode())
.append("executeDelay", getExecuteDelay())
.append("hasAlert", getHasAlert())
.append("enable", getEnable())
.append("elData", getElData())
.append("applicationName", getApplicationName())
.toString();
}
}

View File

@@ -0,0 +1,129 @@
package com.fastbee.iot.domain;
import com.fastbee.common.annotation.Excel;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 场景设备对象 iot_scene_device
*
* @author kerwincui
* @date 2023-12-28
*/
public class SceneDevice
{
private static final long serialVersionUID = 1L;
/** 场景设备ID */
private Long sceneDeviceId;
/** 设备编号(产品触发的没有设备编号) */
@Excel(name = "设备编号", readConverterExp = "产=品触发的没有设备编号")
private String serialNumber;
/** 产品ID */
@Excel(name = "产品ID")
private Long productId;
/** 产品ID */
@Excel(name = "产品名称")
private String productName;
/** 场景脚本ID */
@Excel(name = "场景脚本ID")
private String scriptId;
/** 场景ID */
@Excel(name = "场景ID")
private Long sceneId;
/** 触发源1=设备触发3=产品触发) */
@Excel(name = "触发源", readConverterExp = "1==设备触发3=产品触发")
private Integer source;
/** 类型2=触发器3=执行动作) */
private Integer type;
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public void setSceneDeviceId(Long sceneDeviceId)
{
this.sceneDeviceId = sceneDeviceId;
}
public Long getSceneDeviceId()
{
return sceneDeviceId;
}
public void setSerialNumber(String serialNumber)
{
this.serialNumber = serialNumber;
}
public String getSerialNumber()
{
return serialNumber;
}
public void setProductId(Long productId)
{
this.productId = productId;
}
public Long getProductId()
{
return productId;
}
public void setScriptId(String scriptId)
{
this.scriptId = scriptId;
}
public String getScriptId()
{
return scriptId;
}
public void setSceneId(Long sceneId)
{
this.sceneId = sceneId;
}
public Long getSceneId()
{
return sceneId;
}
public void setSource(Integer source)
{
this.source = source;
}
public Integer getSource()
{
return source;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("sceneDeviceId", getSceneDeviceId())
.append("serialNumber", getSerialNumber())
.append("productId", getProductId())
.append("scriptId", getScriptId())
.append("sceneId", getSceneId())
.append("source", getSource())
.toString();
}
}

View File

@@ -0,0 +1,278 @@
package com.fastbee.iot.domain;
import com.fastbee.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import java.util.Date;
/**
* 场景脚本对象 iot_scene_script
*
* @author kerwincui
* @date 2023-12-27
*/
@ApiModel(value = "SceneScript", description = "场景脚本")
public class SceneScript
{
private static final long serialVersionUID = 1L;
/** 场景脚本ID */
private String scriptId;
/** 脚本ID */
@Excel(name = "场景ID")
private Long sceneId;
/** 触发源1=设备触发2=定时触发3=产品触发4=执行告警) */
@Excel(name = "触发源", readConverterExp = "1=设备触发2=定时触发3=产品触发4=执行告警")
private Integer source;
/** 脚本用途(1=数据流2=触发器3=执行动作) */
private Integer scriptPurpose;
/** 物模型标识符 */
private String id;
/** 物模型名称 */
@Excel(name = "物模型名称")
private String name;
/** 物模型值 */
@Excel(name = "物模型值")
private String value;
/** 操作符 */
@Excel(name = "操作符")
private String operator;
/** 物模型类别1=属性2=功能3=事件4=设备升级5=设备上线6=设备下线) */
@Excel(name = "物模型类别", readConverterExp = "1==属性2=功能3=事件4=设备升级5=设备上线6=设备下线")
private Integer type;
/** 设备数量 */
@Excel(name = "设备数量")
private Integer deviceCount;
/** 任务ID */
@Excel(name = "任务ID")
private Long jobId;
/** cron执行表达式 */
@Excel(name = "cron执行表达式")
private String cronExpression;
/** 是否详细corn表达式1=是0=否) */
@Excel(name = "是否详细corn表达式", readConverterExp = "1==是0=否")
private Integer isAdvance;
/** 父物模id */
@Excel(name = "父物模id")
private String parentId;
/** 父物模名称 */
@Excel(name = "父物模名称")
private String parentName;
/** 数组索引 */
@Excel(name = "数组索引")
private String arrayIndex;
/** 数组索引名称(前端展示使用) */
@Excel(name = "数组索引名称", readConverterExp = "前=端展示使用")
private String arrayIndexName;
/** 产品ID*/
private Long productId;
/** 产品名称*/
private String productName;
/** 设备编号,仅用于传递*/
private String[] deviceNums;
/** 创建者 */
private String createBy;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
public String getScriptId() {
return scriptId;
}
public void setScriptId(String scriptId) {
this.scriptId = scriptId;
}
public Long getSceneId() {
return sceneId;
}
public void setSceneId(Long sceneId) {
this.sceneId = sceneId;
}
public Integer getSource() {
return source;
}
public void setSource(Integer source) {
this.source = source;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public Integer getDeviceCount() {
return deviceCount;
}
public void setDeviceCount(Integer deviceCount) {
this.deviceCount = deviceCount;
}
public Long getJobId() {
return jobId;
}
public void setJobId(Long jobId) {
this.jobId = jobId;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public Integer getIsAdvance() {
return isAdvance;
}
public void setIsAdvance(Integer isAdvance) {
this.isAdvance = isAdvance;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public String getArrayIndex() {
return arrayIndex;
}
public void setArrayIndex(String arrayIndex) {
this.arrayIndex = arrayIndex;
}
public String getArrayIndexName() {
return arrayIndexName;
}
public void setArrayIndexName(String arrayIndexName) {
this.arrayIndexName = arrayIndexName;
}
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String[] getDeviceNums() {
return deviceNums;
}
public void setDeviceNums(String[] deviceNums) {
this.deviceNums = deviceNums;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Integer getScriptPurpose() {
return scriptPurpose;
}
public void setScriptPurpose(Integer scriptPurpose) {
this.scriptPurpose = scriptPurpose;
}
}

View File

@@ -0,0 +1,237 @@
package com.fastbee.iot.domain;
import com.fastbee.common.annotation.Excel;
import com.fastbee.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 规则引擎脚本对象 rule_script
*
* @author lizhuangpeng
* @date 2023-07-01
*/
public class Script extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 脚本ID */
@Excel(name = "脚本ID")
private String scriptId;
/** 用户ID */
private Long userId;
/** 用名称 */
private String userName;
/** 应用名 */
@Excel(name = "应用名")
private String applicationName;
/** 脚本名 */
@Excel(name = "脚本名")
private String scriptName;
/** 脚本数据 */
@Excel(name = "脚本数据")
private String scriptData;
/** 脚本类型:script=普通脚本switch_script=选择脚本,if_script=条件脚本for_script=数量循环脚本while_script=条件循环break_script=退出循环脚本 */
@Excel(name = "脚本类型")
private String scriptType;
/** 场景ID */
private Long sceneId;
/** 脚本类型(1=设备上报2=平台下发3=设备上线4=设备离线) */
private Integer scriptEvent;
/** 脚本动作(1=消息重发2=消息通知3=Http推送4=Mqtt桥接5=数据库存储) */
private Integer scriptAction;
/** 脚本用途(1=数据流2=触发器3=执行动作) */
private Integer scriptPurpose;
/** 脚本执行顺序,值越大优先级越高 */
private Integer scriptOrder;
/** 脚本语言groovy | qlexpress | js | python | lua | aviator */
@Excel(name = "脚本语言", readConverterExp = "groovy,,qlexpress,js,python,lua,aviator")
private String scriptLanguage;
/** 产品ID */
private Long productId;
/** 产品名称 */
private String productName;
/**是否生效*/
private Integer enable;
/** 删除标志0代表存在 2代表删除 */
private String delFlag;
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Long getSceneId() {
return sceneId;
}
public void setSceneId(Long sceneId) {
this.sceneId = sceneId;
}
public Integer getScriptEvent() {
return scriptEvent;
}
public void setScriptEvent(Integer scriptEvent) {
this.scriptEvent = scriptEvent;
}
public Integer getScriptAction() {
return scriptAction;
}
public void setScriptAction(Integer scriptAction) {
this.scriptAction = scriptAction;
}
public Integer getScriptPurpose() {
return scriptPurpose;
}
public void setScriptPurpose(Integer scriptPurpose) {
this.scriptPurpose = scriptPurpose;
}
public Integer getScriptOrder() {
return scriptOrder;
}
public void setScriptOrder(Integer scriptOrder) {
this.scriptOrder = scriptOrder;
}
public void setApplicationName(String applicationName)
{
this.applicationName = applicationName;
}
public String getApplicationName()
{
return applicationName;
}
public void setScriptId(String scriptId)
{
this.scriptId = scriptId;
}
public String getScriptId()
{
return scriptId;
}
public void setScriptName(String scriptName)
{
this.scriptName = scriptName;
}
public String getScriptName()
{
return scriptName;
}
public void setScriptData(String scriptData)
{
this.scriptData = scriptData;
}
public String getScriptData()
{
return scriptData;
}
public void setScriptType(String scriptType)
{
this.scriptType = scriptType;
}
public String getScriptType()
{
return scriptType;
}
public void setScriptLanguage(String scriptLanguage)
{
this.scriptLanguage = scriptLanguage;
}
public String getScriptLanguage()
{
return scriptLanguage;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public Integer getEnable() {
return enable;
}
public void setEnable(Integer enable) {
this.enable = enable;
}
public String getDelFlag()
{
return delFlag;
}
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("applicationName", getApplicationName())
.append("scriptId", getScriptId())
.append("scriptName", getScriptName())
.append("scriptData", getScriptData())
.append("scriptType", getScriptType())
.append("scriptLanguage", getScriptLanguage())
.append("delFlag", getDelFlag())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@@ -295,4 +295,11 @@ public interface DeviceMapper
* @return
*/
DeviceRelateAlertLogVO selectRelateAlertLogBySerialNumber(String deviceNumber);
/**
* 根据产品ID获取产品下所有编号
* @param productId
* @return
*/
public String[] getDeviceNumsByProductId(Long productId);
}

View File

@@ -0,0 +1,104 @@
package com.fastbee.iot.mapper;
import com.fastbee.iot.domain.Scene;
import com.fastbee.iot.domain.SceneDevice;
import com.fastbee.iot.model.SceneDeviceBindVO;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 场景设备Mapper接口
*
* @author kerwincui
* @date 2023-12-28
*/
@Repository
public interface SceneDeviceMapper
{
/**
* 查询场景设备
*
* @param sceneDeviceId 场景设备主键
* @return 场景设备
*/
public SceneDevice selectSceneDeviceBySceneDeviceId(Long sceneDeviceId);
/**
* 查询场景设备列表
*
* @param sceneDevice 场景设备
* @return 场景设备集合
*/
public List<SceneDevice> selectSceneDeviceList(SceneDevice sceneDevice);
/**
* 新增场景设备
*
* @param sceneDevice 场景设备
* @return 结果
*/
public int insertSceneDevice(SceneDevice sceneDevice);
/**
* 修改场景设备
*
* @param sceneDevice 场景设备
* @return 结果
*/
public int updateSceneDevice(SceneDevice sceneDevice);
/**
* 删除场景设备
*
* @param sceneDeviceId 场景设备主键
* @return 结果
*/
public int deleteSceneDeviceBySceneDeviceId(Long sceneDeviceId);
/**
* 批量删除场景设备
*
* @param sceneDeviceIds 需要删除的数据主键集合
* @return 结果
*/
public int deleteSceneDeviceBySceneDeviceIds(Long[] sceneDeviceIds);
/**
* 批量删除场景设备
*
* @param sceneIds 需要删除的数据场景ID集合
* @return 结果
*/
public int deleteSceneDeviceBySceneIds(Long[] sceneIds);
/**
* 批量新增场景脚本
*
* @param sceneDeviceList 场景联动触发器集合
* @return 结果
*/
public int insertSceneDeviceList(List<SceneDevice> sceneDeviceList);
/**
* 查询设备关联的场景列表
*
* @param sceneDevice 场景设备
* @return 场景设备集合
*/
public List<Scene> selectTriggerDeviceRelateScenes(SceneDevice sceneDevice);
/**
* 查询场景联动绑定产品
* @param productIds 产品id数组
* @return java.util.List<com.fastbee.iot.model.SceneDeviceBindVO>
*/
List<SceneDeviceBindVO> listSceneProductBind(Long[] productIds);
/**
* 查询场景联动绑定设备
* @param serialNumber 设备编号
* @return java.util.List<com.fastbee.iot.model.SceneDeviceBindVO>
*/
List<SceneDeviceBindVO> listSceneDeviceBind(String serialNumber);
}

View File

@@ -0,0 +1,72 @@
package com.fastbee.iot.mapper;
import com.fastbee.iot.domain.Scene;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 场景联动Mapper接口
*
* @author kerwincui
* @date 2022-01-13
*/
@Repository
public interface SceneMapper
{
/**
* 查询场景联动
*
* @param sceneId 场景联动主键
* @return 场景联动
*/
public Scene selectSceneBySceneId(Long sceneId);
/**
* 查询场景联动列表
*
* @param scene 场景联动
* @return 场景联动集合
*/
public List<Scene> selectSceneList(Scene scene);
/**
* 新增场景联动
*
* @param scene 场景联动
* @return 结果
*/
public int insertScene(Scene scene);
/**
* 修改场景联动
*
* @param scene 场景联动
* @return 结果
*/
public int updateScene(Scene scene);
/**
* 删除场景联动
*
* @param sceneId 场景联动主键
* @return 结果
*/
public int deleteSceneBySceneId(Long sceneId);
/**
* 批量删除场景联动
*
* @param sceneIds 需要删除的数据主键集合
* @return 结果
*/
public int deleteSceneBySceneIds(Long[] sceneIds);
/**
* 根据ids批量查询场景联动
* @param sceneIds 场景联动id数组
* @return 集合
*/
List<Scene> selectSceneListBySceneIds(Long[] sceneIds);
}

View File

@@ -0,0 +1,89 @@
package com.fastbee.iot.mapper;
import com.fastbee.iot.domain.SceneScript;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 场景脚本Mapper接口
*
* @author kerwincui
* @date 2023-12-27
*/
@Repository
public interface SceneScriptMapper
{
/**
* 查询场景脚本
*
* @param scriptId 场景脚本主键
* @return 场景脚本
*/
public SceneScript selectSceneScriptBySceneScriptId(String scriptId);
/**
* 查询场景脚本列表
*
* @param sceneScript 场景脚本
* @return 场景脚本集合
*/
public List<SceneScript> selectSceneScriptList(SceneScript sceneScript);
/**
* 新增场景脚本
*
* @param sceneScript 场景脚本
* @return 结果
*/
public int insertSceneScript(SceneScript sceneScript);
/**
* 修改场景脚本
*
* @param sceneScript 场景脚本
* @return 结果
*/
public int updateSceneScript(SceneScript sceneScript);
/**
* 删除场景脚本
*
* @param scriptId 场景脚本主键
* @return 结果
*/
public int deleteSceneScriptBySceneScriptId(String scriptId);
/**
* 批量删除场景脚本
*
* @param scriptIds 需要删除的数据主键集合
* @return 结果
*/
public int deleteSceneScriptBySceneScriptIds(String[] scriptIds);
/**
* 批量删除场景联动脚本
*
* @param sceneIds 需要删除的数据场景ID集合
* @return 结果
*/
public int deleteSceneScriptBySceneIds(Long[] sceneIds);
/**
* 批量新增场景脚本
*
* @param sceneScriptList 场景联动触发器集合
* @return 结果
*/
public int insertSceneScriptList(List<SceneScript> sceneScriptList);
/**
* 通过脚本用途批量查询
* @param sceneIdList 场景id
* @param: scriptPurpose 脚本用途
* @return java.util.List<com.fastbee.iot.domain.SceneScript>
*/
List<SceneScript> listSceneScriptByPurpose(@Param("sceneIdList") List<Long> sceneIdList, @Param("scriptPurpose") Integer scriptPurpose);
}

View File

@@ -0,0 +1,95 @@
package com.fastbee.iot.mapper;
import com.fastbee.iot.domain.Script;
import com.fastbee.iot.model.ScriptCondition;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 规则引擎脚本Mapper接口
*
* @author lizhuangpeng
* @date 2023-07-01
*/
@Repository
public interface ScriptMapper
{
/**
* 查询规则引擎脚本
*
* @param scriptId 规则引擎脚本主键
* @return 规则引擎脚本
*/
public Script selectRuleScriptById(String scriptId);
/**
* 查询规则引擎脚本列表
*
* @param ruleScript 规则引擎脚本
* @return 规则引擎脚本集合
*/
public List<Script> selectRuleScriptList(Script ruleScript);
/**
* 查询规则引擎脚本标识数组
*
* @return 规则引擎脚本数组
*/
public String[] selectRuleScriptIdArray(ScriptCondition scriptCondition);
/***
* 查询scriptId的数量
* @param scriptId
* @return
*/
public int selectRuleScriptIdCount(String scriptId);
/**
* 新增规则引擎脚本
*
* @param ruleScript 规则引擎脚本
* @return 结果
*/
public int insertRuleScript(Script ruleScript);
/**
* 修改规则引擎脚本
*
* @param ruleScript 规则引擎脚本
* @return 结果
*/
public int updateRuleScript(Script ruleScript);
/**
* 删除规则引擎脚本
*
* @param scriptId 规则引擎脚本主键
* @return 结果
*/
public int deleteRuleScriptById(String scriptId);
/**
* 批量删除规则引擎脚本
*
* @param scriptIds 需要删除的数据主键集合
* @return 结果
*/
public int deleteRuleScriptByIds(String[] scriptIds);
/**
* 批量删除规则脚本
*
* @param sceneIds 需要删除的数据场景ID集合
* @return 结果
*/
public int deleteRuleScriptBySceneIds(Long[] sceneIds);
/**
* 批量新增场景脚本
*
* @param ruleScriptList 场景联动触发器集合
* @return 结果
*/
public int insertRuleScriptList(List<Script> ruleScriptList);
}

View File

@@ -0,0 +1,33 @@
package com.fastbee.iot.model;
import lombok.Data;
/**
* @author fastb
* @version 1.0
* @description: 场景关联设备、产品VO类
* @date 2024-02-06 15:10
*/
@Data
public class SceneDeviceBindVO {
/**
* 场景联动id
*/
private Long sceneId;
/**
* 场景联动名称
*/
private String sceneName;
/**
* 产品id
*/
private Long productId;
/**
* 设备编号
*/
private String serialNumber;
}

View File

@@ -0,0 +1,52 @@
package com.fastbee.iot.model;
import lombok.Data;
/**
* 规则引擎脚本查询条件
* @author kerwin
*/
@Data
public class ScriptCondition
{
/** 用户ID */
private Long userId;
/** 租户ID */
private Long tencentId;
/** 应用名 */
private String applicationName;
/** 脚本ID */
private String scriptId;
/** 脚本名 */
private String scriptName;
/** 脚本类型 */
private String scriptType;
/**
* 关联的产品ID
*/
private Long productId;
/** 场景ID */
private Long sceneId;
/** 脚本类型(1=设备上报2=平台下发3=设备上线4=设备离线) */
private Integer scriptEvent;
/** 脚本动作(1=消息重发2=消息通知3=Http推送4=Mqtt桥接5=数据库存储) */
private Integer scriptAction;
/** 脚本用途(1=数据流2=触发器3=执行动作) */
private Integer scriptPurpose;
/** 脚本语言groovy | ql express | js | python | lua | aviator */
private String scriptLanguage;
/**是否生效*/
private Integer enable;
}

View File

@@ -0,0 +1,49 @@
package com.fastbee.iot.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ScriptTemplate {
/** 场景ID */
private Long sceneId;
/** 脚本ID */
private String scriptId;
/** 类型 1=属性, 2=功能3=事件4=设备升级5=设备上线6=设备下线 */
private int type;
/** 触发执行源 1=设备触发/执行 2=定时触发 3=产品触发/执行 4=告警执行 */
private int source;
/** 静默时间,单位分钟 */
private int silent;
/** 延迟执行,单位秒钟 */
private int delay;
/** 执行条件1=或、任意条件2=且、所有条件3=非,不满足) */
private int cond;
/** 脚本用途1=数据流2=触发器3=执行动作 */
private int purpose;
/** 物模型标识符 */
private String id;
/** 物模型值 */
private String value;
/** 比较操作符,等于/不等于/大于/小于/包含/不包含/在...之间/不在...之间 */
private String operator;
/** 设备编号,多个设备英文逗号分隔 */
private String deviceNums;
/** 所属产品编号 */
private Long productId;
}

View File

@@ -0,0 +1,31 @@
package com.fastbee.iot.ruleEngine;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MsgContext {
/** 消息主题 */
private String topic;
/** 消息内容 */
private String payload;
/**
* 设备编号
*/
private String serialNumber;
/**
* 产品id
*/
private Long productId;
/**
* 协议编码
*/
private String protocolCode;
}

View File

@@ -0,0 +1,29 @@
package com.fastbee.iot.ruleEngine;
import com.fastbee.common.core.redis.RedisCache;
import com.yomahub.liteflow.script.annotation.ScriptBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 规则引擎上下文执行方法
* @author gsb
* @date 2024/2/2 14:19
*/
@Component
@Slf4j
@ScriptBean("msgContextService")
public class MsgContextService {
private final RedisCache redisCache;
public MsgContextService(RedisCache redisCache){
this.redisCache = redisCache;
}
private void process(String serialNumber){
//执行的业务逻辑
}
}

View File

@@ -0,0 +1,93 @@
package com.fastbee.iot.ruleEngine;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.core.redis.RedisKeyBuilder;
import com.fastbee.iot.model.ProductCode;
import com.fastbee.iot.model.ScriptCondition;
import com.fastbee.iot.service.IProductService;
import com.fastbee.iot.service.IScriptService;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Objects;
/**
* 执行规则引擎
*
* @author gsb
* @date 2024/2/3 16:07
*/
@Component
@Slf4j
public class RuleProcess {
@Resource
private FlowExecutor flowExecutor;
@Resource
private IScriptService scriptService;
@Resource
private RedisCache redisCache;
@Resource
private IProductService productService;
/**
* 规则引擎脚本处理
*
* @param topic
* @param payload
* @param event 1=设备上报 2=平台下发 3=设备上线 4=设备下线 (其他可以增加设备完成主题订阅之类)
* @return
*/
public MsgContext processRuleScript(String serialNumber, int event, String topic, String payload) {
System.out.println("执行产品规则脚本...");
ProductCode productCode = getDeviceDetail(serialNumber);
if (Objects.isNull(productCode)){
return new MsgContext();
}
// 查询数据流脚本组件
ScriptCondition scriptCondition = new ScriptCondition();
scriptCondition.setProductId(productCode.getProductId());
scriptCondition.setScriptEvent(event); // 事件 1=设备上报 2=平台下发 3=设备上线 4=设备下线
scriptCondition.setScriptPurpose(1); // 脚本用途:数据流=1
String[] scriptIds = scriptService.selectRuleScriptIdArray(scriptCondition);
MsgContext context = new MsgContext(topic, payload, serialNumber, productCode.getProductId(), productCode.getProtocolCode());
//如果查询不到脚本,则认为是不用处理
if (Objects.isNull(scriptIds) || scriptIds.length == 0) {
return new MsgContext();
}
// 动态构造Chain和EL表达式
String el = String.join(",", scriptIds); // THENa,b,c,d
LiteFlowChainELBuilder.createChain().setChainName("dataChain").setEL("THEN(" + el + ")").build();
// 执行规则脚本
LiteflowResponse response = flowExecutor.execute2Resp("dataChain", null, context);
if (!response.isSuccess()) {
log.error("规则脚本执行发生错误:" + response.getMessage());
}
return context;
}
/**
* 查询产品id,协议编号缓存到redis,后续查询协议的地方替换数据库查询
*
* @param serialNumber
*/
public ProductCode getDeviceDetail(String serialNumber) {
ProductCode productCode;
String cacheKey = RedisKeyBuilder.buildDeviceMsgCacheKey(serialNumber);
if (redisCache.containsKey(cacheKey)) {
Object cacheObject = redisCache.getCacheObject(cacheKey);
return JSON.parseObject(cacheObject.toString(), ProductCode.class);
}
productCode = productService.getProtocolBySerialNumber(serialNumber);
String jsonString = JSON.toJSONString(productCode);
redisCache.setCacheObject(cacheKey, jsonString);
return productCode;
}
}

View File

@@ -124,4 +124,11 @@ public interface IDeviceJobService
* @return 结果
*/
public boolean checkCronExpressionIsValid(String cronExpression);
/**
* 通过场景id查询关联定时任务
* @param sceneIds 场景id
* @return java.util.List<com.fastbee.iot.domain.DeviceJob>
*/
List<DeviceJob> listShortJobBySceneId(Long[] sceneIds);
}

View File

@@ -260,4 +260,11 @@ public interface IDeviceService
* @return
*/
public DeviceMqttConnectVO getMqttConnectData(Long deviceId);
/**
* 根据产品ID获取产品下所有编号
* @param productId
* @return
*/
public String[] getDeviceNumsByProductId(Long productId);
}

View File

@@ -0,0 +1,62 @@
package com.fastbee.iot.service;
import com.fastbee.iot.domain.SceneDevice;
import java.util.List;
/**
* 场景设备Service接口
*
* @author kerwincui
* @date 2023-12-28
*/
public interface ISceneDeviceService
{
/**
* 查询场景设备
*
* @param sceneDeviceId 场景设备主键
* @return 场景设备
*/
public SceneDevice selectSceneDeviceBySceneDeviceId(Long sceneDeviceId);
/**
* 查询场景设备列表
*
* @param sceneDevice 场景设备
* @return 场景设备集合
*/
public List<SceneDevice> selectSceneDeviceList(SceneDevice sceneDevice);
/**
* 新增场景设备
*
* @param sceneDevice 场景设备
* @return 结果
*/
public int insertSceneDevice(SceneDevice sceneDevice);
/**
* 修改场景设备
*
* @param sceneDevice 场景设备
* @return 结果
*/
public int updateSceneDevice(SceneDevice sceneDevice);
/**
* 批量删除场景设备
*
* @param sceneDeviceIds 需要删除的场景设备主键集合
* @return 结果
*/
public int deleteSceneDeviceBySceneDeviceIds(Long[] sceneDeviceIds);
/**
* 删除场景设备信息
*
* @param sceneDeviceId 场景设备主键
* @return 结果
*/
public int deleteSceneDeviceBySceneDeviceId(Long sceneDeviceId);
}

View File

@@ -0,0 +1,62 @@
package com.fastbee.iot.service;
import com.fastbee.iot.domain.SceneScript;
import java.util.List;
/**
* 场景脚本Service接口
*
* @author kerwincui
* @date 2023-12-27
*/
public interface ISceneScriptService
{
/**
* 查询场景脚本
*
* @param scriptId 场景脚本主键
* @return 场景脚本
*/
public SceneScript selectSceneScriptBySceneScriptId(String scriptId);
/**
* 查询场景脚本列表
*
* @param sceneScript 场景脚本
* @return 场景脚本集合
*/
public List<SceneScript> selectSceneScriptList(SceneScript sceneScript);
/**
* 新增场景脚本
*
* @param sceneScript 场景脚本
* @return 结果
*/
public int insertSceneScript(SceneScript sceneScript);
/**
* 修改场景脚本
*
* @param sceneScript 场景脚本
* @return 结果
*/
public int updateSceneScript(SceneScript sceneScript);
/**
* 批量删除场景脚本
*
* @param scriptIds 需要删除的场景脚本主键集合
* @return 结果
*/
public int deleteSceneScriptBySceneScriptIds(String[] scriptIds);
/**
* 删除场景脚本信息
*
* @param scriptId 场景脚本主键
* @return 结果
*/
public int deleteSceneScriptBySceneScriptId(String scriptId);
}

View File

@@ -0,0 +1,70 @@
package com.fastbee.iot.service;
import com.fastbee.iot.domain.Scene;
import java.util.List;
/**
* 场景联动Service接口
*
* @author kerwincui
* @date 2022-01-13
*/
public interface ISceneService
{
/**
* 查询场景联动
*
* @param sceneId 场景联动主键
* @return 场景联动
*/
public Scene selectSceneBySceneId(Long sceneId);
/**
* 查询场景联动列表
*
* @param scene 场景联动
* @return 场景联动集合
*/
public List<Scene> selectSceneList(Scene scene);
/**
* 新增场景联动
*
* @param scene 场景联动
* @return 结果
*/
public int insertScene(Scene scene);
/**
* 修改场景联动
*
* @param scene 场景联动
* @return 结果
*/
public int updateScene(Scene scene);
/**
* 批量删除场景联动
*
* @param sceneIds 需要删除的场景联动主键集合
* @return 结果
*/
public int deleteSceneBySceneIds(Long[] sceneIds);
/**
* 删除场景联动信息
*
* @param sceneId 场景联动主键
* @return 结果
*/
public int deleteSceneBySceneId(Long sceneId);
/**
* 修改场景联动状态
* @param scene 场景联动
* @return int
*/
int updateStatus(Scene scene);
}

View File

@@ -0,0 +1,79 @@
package com.fastbee.iot.service;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.iot.domain.Script;
import com.fastbee.iot.model.ScriptCondition;
import java.util.List;
/**
* 规则引擎脚本Service接口
*
* @author lizhuangpeng
* @date 2023-07-01
*/
public interface IScriptService
{
/**
* 查询规则引擎脚本
*
* @param scriptId 规则引擎脚本主键
* @return 规则引擎脚本
*/
public Script selectRuleScriptById(String scriptId);
/**
* 查询规则引擎脚本列表
*
* @param ruleScript 规则引擎脚本
* @return 规则引擎脚本集合
*/
public List<Script> selectRuleScriptList(Script ruleScript);
/**
* 查询规则引擎脚本标识数组(设备用户和租户的脚本)
*
* @return 规则引擎脚本
*/
public String[] selectRuleScriptIdArray(ScriptCondition scriptCondition);
/**
* 新增规则引擎脚本
*
* @param ruleScript 规则引擎脚本
* @return 结果
*/
public int insertRuleScript(Script ruleScript);
/**
* 修改规则引擎脚本
*
* @param ruleScript 规则引擎脚本
* @return 结果
*/
public int updateRuleScript(Script ruleScript);
/**
* 批量删除规则引擎脚本
*
* @param ids 需要删除的规则引擎脚本主键集合
* @return 结果
*/
public int deleteRuleScriptByIds(String[] ids);
/**
* 删除规则引擎脚本信息
*
* @param id 规则引擎脚本主键
* @return 结果
*/
public int deleteRuleScriptById(String id);
/**
* 验证脚本
* @param ruleScript 脚本数据
* @return
*/
public AjaxResult validateScript(Script ruleScript);
}

View File

@@ -1309,4 +1309,13 @@ public class DeviceServiceImpl implements IDeviceService {
return valueList;
}
/**
* 根据产品ID获取产品下所有编号
* @param productId
* @return
*/
public String[] getDeviceNumsByProductId(Long productId){
return deviceMapper.getDeviceNumsByProductId(productId);
}
}

View File

@@ -0,0 +1,94 @@
package com.fastbee.iot.service.impl;
import com.fastbee.iot.domain.SceneDevice;
import com.fastbee.iot.mapper.SceneDeviceMapper;
import com.fastbee.iot.service.ISceneDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 场景设备Service业务层处理
*
* @author kerwincui
* @date 2023-12-28
*/
@Service
public class SceneDeviceServiceImpl implements ISceneDeviceService
{
@Autowired
private SceneDeviceMapper sceneDeviceMapper;
/**
* 查询场景设备
*
* @param sceneDeviceId 场景设备主键
* @return 场景设备
*/
@Override
public SceneDevice selectSceneDeviceBySceneDeviceId(Long sceneDeviceId)
{
return sceneDeviceMapper.selectSceneDeviceBySceneDeviceId(sceneDeviceId);
}
/**
* 查询场景设备列表
*
* @param sceneDevice 场景设备
* @return 场景设备
*/
@Override
public List<SceneDevice> selectSceneDeviceList(SceneDevice sceneDevice)
{
return sceneDeviceMapper.selectSceneDeviceList(sceneDevice);
}
/**
* 新增场景设备
*
* @param sceneDevice 场景设备
* @return 结果
*/
@Override
public int insertSceneDevice(SceneDevice sceneDevice)
{
return sceneDeviceMapper.insertSceneDevice(sceneDevice);
}
/**
* 修改场景设备
*
* @param sceneDevice 场景设备
* @return 结果
*/
@Override
public int updateSceneDevice(SceneDevice sceneDevice)
{
return sceneDeviceMapper.updateSceneDevice(sceneDevice);
}
/**
* 批量删除场景设备
*
* @param sceneDeviceIds 需要删除的场景设备主键
* @return 结果
*/
@Override
public int deleteSceneDeviceBySceneDeviceIds(Long[] sceneDeviceIds)
{
return sceneDeviceMapper.deleteSceneDeviceBySceneDeviceIds(sceneDeviceIds);
}
/**
* 删除场景设备信息
*
* @param sceneDeviceId 场景设备主键
* @return 结果
*/
@Override
public int deleteSceneDeviceBySceneDeviceId(Long sceneDeviceId)
{
return sceneDeviceMapper.deleteSceneDeviceBySceneDeviceId(sceneDeviceId);
}
}

View File

@@ -0,0 +1,96 @@
package com.fastbee.iot.service.impl;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.iot.domain.SceneScript;
import com.fastbee.iot.mapper.SceneScriptMapper;
import com.fastbee.iot.service.ISceneScriptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 场景脚本Service业务层处理
*
* @author kerwincui
* @date 2023-12-27
*/
@Service
public class SceneScriptServiceImpl implements ISceneScriptService
{
@Autowired
private SceneScriptMapper sceneScriptMapper;
/**
* 查询场景脚本
*
* @param scriptId 场景脚本主键
* @return 场景脚本
*/
@Override
public SceneScript selectSceneScriptBySceneScriptId(String scriptId)
{
return sceneScriptMapper.selectSceneScriptBySceneScriptId(scriptId);
}
/**
* 查询场景脚本列表
*
* @param sceneScript 场景脚本
* @return 场景脚本
*/
@Override
public List<SceneScript> selectSceneScriptList(SceneScript sceneScript)
{
return sceneScriptMapper.selectSceneScriptList(sceneScript);
}
/**
* 新增场景脚本
*
* @param sceneScript 场景脚本
* @return 结果
*/
@Override
public int insertSceneScript(SceneScript sceneScript)
{
sceneScript.setCreateTime(DateUtils.getNowDate());
return sceneScriptMapper.insertSceneScript(sceneScript);
}
/**
* 修改场景脚本
*
* @param sceneScript 场景脚本
* @return 结果
*/
@Override
public int updateSceneScript(SceneScript sceneScript)
{
return sceneScriptMapper.updateSceneScript(sceneScript);
}
/**
* 批量删除场景脚本
*
* @param scriptIds 需要删除的场景脚本主键
* @return 结果
*/
@Override
public int deleteSceneScriptBySceneScriptIds(String[] scriptIds)
{
return sceneScriptMapper.deleteSceneScriptBySceneScriptIds(scriptIds);
}
/**
* 删除场景脚本信息
*
* @param scriptId 场景脚本主键
* @return 结果
*/
@Override
public int deleteSceneScriptBySceneScriptId(String scriptId)
{
return sceneScriptMapper.deleteSceneScriptBySceneScriptId(scriptId);
}
}

View File

@@ -0,0 +1,605 @@
package com.fastbee.iot.service.impl;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSONObject;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.exception.job.TaskException;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.iot.domain.*;
import com.fastbee.iot.mapper.SceneDeviceMapper;
import com.fastbee.iot.mapper.SceneMapper;
import com.fastbee.iot.mapper.SceneScriptMapper;
import com.fastbee.iot.mapper.ScriptMapper;
import com.fastbee.iot.model.ScriptTemplate;
import com.fastbee.iot.service.IDeviceJobService;
import com.fastbee.iot.service.ISceneService;
import com.fastbee.quartz.util.CronUtils;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static com.fastbee.common.utils.SecurityUtils.getLoginUser;
/**
* 场景联动Service业务层处理
*
* @author kerwincui
* @date 2022-01-13
*/
@Service
public class SceneServiceImpl implements ISceneService {
private static final Logger log = LoggerFactory.getLogger(SceneServiceImpl.class);
@Autowired
private Scheduler scheduler;
@Autowired
RedisCache redisCache;
@Autowired
private SceneMapper sceneMapper;
@Autowired
private IDeviceJobService jobService;
@Autowired
private ScriptMapper ruleScriptMapper;
@Autowired
private SceneScriptMapper sceneScriptMapper;
@Autowired
private SceneDeviceMapper sceneDeviceMapper;
// @Resource
// private IAlertService alertService;
/**
* 查询场景联动
*
* @param sceneId 场景联动主键
* @return 场景联动
*/
@Override
public Scene selectSceneBySceneId(Long sceneId) {
// 查询场景
Scene scene = sceneMapper.selectSceneBySceneId(sceneId);
// 查询场景脚本
SceneScript scriptParam = new SceneScript();
scriptParam.setSceneId(scene.getSceneId());
List<SceneScript> sceneScripts = sceneScriptMapper.selectSceneScriptList(scriptParam);
// 查询场景设备
SceneDevice deviceParam = new SceneDevice();
deviceParam.setSceneId(scene.getSceneId());
List<SceneDevice> sceneDevices = sceneDeviceMapper.selectSceneDeviceList(deviceParam);
// 构建场景设备列表,触发器和执行动作集合
List<SceneScript> triggers = new ArrayList<>();
List<SceneScript> actions = new ArrayList<>();
for (SceneScript sceneScript : sceneScripts) {
// 构建设备列表
List<String> deviceList = new ArrayList<>();
for (SceneDevice sceneDevice : sceneDevices) {
if (sceneDevice.getScriptId().equals(sceneScript.getScriptId())) {
deviceList.add(sceneDevice.getSerialNumber());
}
}
sceneScript.setDeviceNums(deviceList.toArray(new String[deviceList.size()]));
if (sceneScript.getScriptPurpose() == 2) {
triggers.add(sceneScript);
} else if (sceneScript.getScriptPurpose() == 3) {
actions.add(sceneScript);
}
}
scene.setTriggers(triggers);
scene.setActions(actions);
return scene;
}
/**
* 查询场景联动列表
*
* @param scene 场景联动
* @return 场景联动
*/
@Override
public List<Scene> selectSceneList(Scene scene) {
SysUser user = getLoginUser().getUser();
List<SysRole> roles = user.getRoles();
for (int i = 0; i < roles.size(); i++) {
// 租户和用户,只查看自己分组
if (roles.get(i).getRoleKey().equals("tenant") || roles.get(i).getRoleKey().equals("general")) {
scene.setUserId(user.getUserId());
break;
}
}
List<Scene> sceneList = sceneMapper.selectSceneList(scene);
if (CollectionUtils.isNotEmpty(sceneList)) {
List<Long> sceneIdList = sceneList.stream().map(Scene::getSceneId).collect(Collectors.toList());
List<SceneScript> sceneScriptList = sceneScriptMapper.listSceneScriptByPurpose(sceneIdList, 3);
Map<Long, List<SceneScript>> map = sceneScriptList.stream().collect(Collectors.groupingBy(SceneScript::getSceneId));
for (Scene updateScene : sceneList) {
List<SceneScript> sceneScripts = map.get(updateScene.getSceneId());
if (CollectionUtils.isNotEmpty(sceneScripts)) {
updateScene.setActionCount(sceneScripts.size());
}
}
}
return sceneList;
}
/**
* 新增场景联动
*
* @param scene 场景联动
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertScene(Scene scene) {
if (scene.getTriggers().size() == 0 || scene.getActions().size() == 0) {
log.error("场景中至少包含一个触发器或者执行动作,否则规则启动报错!");
return 0;
}
// 保存场景联动
SysUser user = getLoginUser().getUser();
scene.setCreateBy(user.getUserName());
scene.setCreateTime(DateUtils.getNowDate());
scene.setUserId(user.getUserId());
scene.setUserName(user.getUserName());
// 延时不超过90秒
if (scene.getExecuteDelay() > 90) {
scene.setExecuteDelay(90);
}
// 设置规则名称,D=数据流A=执行动作T=触发器,C=规则链 雪花算法生成唯一数
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
scene.setChainName("C" + String.valueOf(snowflake.nextId()));
sceneMapper.insertScene(scene);
// 构建EL和场景数据
List<Script> ruleScripts = new ArrayList<>();
List<SceneScript> sceneScripts = new ArrayList<>();
List<SceneDevice> sceneDevices = new ArrayList<>();
String elData = buildElData(scene, ruleScripts, sceneScripts, sceneDevices);
// 更新场景联动规则脚本
scene.setElData(elData);
sceneMapper.updateScene(scene);
// 保存规则脚本
ruleScriptMapper.insertRuleScriptList(ruleScripts);
// 保存场景脚本
sceneScriptMapper.insertSceneScriptList(sceneScripts);
// 保存场景设备
if (sceneDevices.size() > 0) {
sceneDeviceMapper.insertSceneDeviceList(sceneDevices);
}
// 动态构建规则链和脚本组件到内存中
dynamicBuildRule(ruleScripts,scene);
return 1;
}
/**
* 修改场景联动
*
* @param scene 场景联动
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int updateScene(Scene scene) {
if (scene.getTriggers().size() == 0 || scene.getActions().size() == 0) {
log.error("场景中至少包含一个触发器或者执行动作,否则规则启动报错!");
return 0;
}
// 批量删除规则脚本
ruleScriptMapper.deleteRuleScriptBySceneIds(new Long[]{scene.getSceneId()});
// 批量删除场景脚本
sceneScriptMapper.deleteSceneScriptBySceneIds(new Long[]{scene.getSceneId()});
// 批量删除场景设备
sceneDeviceMapper.deleteSceneDeviceBySceneIds(new Long[]{scene.getSceneId()});
// 批量删除定时任务
try {
jobService.deleteJobBySceneIds(new Long[]{scene.getSceneId()});
} catch (SchedulerException e) {
e.printStackTrace();
}
// 构建EL和场景数据
List<Script> ruleScripts = new ArrayList<>();
List<SceneScript> sceneScripts = new ArrayList<>();
List<SceneDevice> sceneDevices = new ArrayList<>();
String elData = buildElData(scene, ruleScripts, sceneScripts, sceneDevices);
scene.setElData(elData);
scene.setUpdateTime(DateUtils.getNowDate());
sceneMapper.updateScene(scene);
// 保存规则脚本
ruleScriptMapper.insertRuleScriptList(ruleScripts);
// 保存场景脚本
sceneScriptMapper.insertSceneScriptList(sceneScripts);
// 保存场景设备
if (sceneDevices.size() > 0) {
sceneDeviceMapper.insertSceneDeviceList(sceneDevices);
}
// 动态构建规则链和脚本组件到内存中
dynamicBuildRule(ruleScripts,scene);
return 1;
}
/**
* 动态构建规则链和脚本组件
* @param ruleScripts
*/
private void dynamicBuildRule(List<Script> ruleScripts,Scene scene){
for(Script ruleScript:ruleScripts){
// 规则引擎构建脚本组件
if(ruleScript.getScriptPurpose() == 2) {
//脚本条件组件
LiteFlowNodeBuilder.createScriptIfNode().setId(ruleScript.getScriptId())
.setName(ruleScript.getScriptName())
.setScript(ruleScript.getScriptData())
.build();
}else if(ruleScript.getScriptPurpose() == 3){
// 普通组件
LiteFlowNodeBuilder.createScriptNode().setId(ruleScript.getScriptId())
.setName(ruleScript.getScriptName())
.setScript(ruleScript.getScriptData())
.build();
}
}
// 构建规则链
LiteFlowChainELBuilder.createChain().setChainName(scene.getChainName()).setEL(scene.getElData()).build();
}
/**
* 构建EL数据
*
* @param scene 场景
* @param ruleScripts 规则脚本集合
* @param sceneScripts 场景脚本集合
* @param sceneDevices 场景设备集合
* @return
*/
private String buildElData(Scene scene, List<Script> ruleScripts, List<SceneScript> sceneScripts, List<SceneDevice> sceneDevices) {
// 排除定时后的触发器等于0不生成规则数据等于1移除AND和OR
Long triggerNodeCount = scene.getTriggers().stream().filter(x -> x.getSource() != 2).count();
Long triggerTimingCount = scene.getTriggers().stream().filter(x -> x.getSource() == 2).count();
// 拼接规则数据格式如IF(AND(T1,T2,T3),THEN(A1,A2,A3))
StringBuilder triggerBuilder = new StringBuilder();
StringBuilder actionBuilder = new StringBuilder();
if (0 == triggerNodeCount && triggerTimingCount != 0) {
switch (scene.getExecuteMode()) {
case 1:
actionBuilder.append("THEN(");
break;
case 2:
actionBuilder.append("WHEN(");
break;
default:
break;
}
} else {
switch (scene.getCond()) {
case 1:
triggerBuilder.append("IF(OR(");
break;
case 2:
triggerBuilder.append("IF(AND(");
break;
case 3:
triggerBuilder.append("IF(NOT(");
break;
default:
break;
}
switch (scene.getExecuteMode()) {
case 1:
actionBuilder.append(",THEN(");
break;
case 2:
actionBuilder.append(",WHEN(");
break;
default:
break;
}
}
for (int i = 0; i < scene.getTriggers().size(); i++) {
// 保存触发器和执行动作
String scriptId = buildTriggerAction(scene.getTriggers().get(i), 2, scene, ruleScripts, sceneScripts, sceneDevices);
// 构建触发器EL排除定时触发器
if (scene.getTriggers().get(i).getSource() != 2) {
triggerBuilder.append(scriptId + ",");
}
}
if (triggerNodeCount > 0) {
triggerBuilder.deleteCharAt(triggerBuilder.lastIndexOf(","));
triggerBuilder.append(")");
}
for (int i = 0; i < scene.getActions().size(); i++) {
// 保存触发器和执行动作
String scriptId = buildTriggerAction(scene.getActions().get(i), 3, scene, ruleScripts, sceneScripts, sceneDevices);
// 构建执行动作EL
actionBuilder.append(scriptId + ",");
}
if (scene.getActions().size() > 0) {
actionBuilder.deleteCharAt(actionBuilder.lastIndexOf(","));
}
String elData;
if (StringUtils.isEmpty(triggerBuilder)) {
actionBuilder.append(")");
elData = actionBuilder.toString();
} else {
actionBuilder.append("))");
elData = triggerBuilder.append(actionBuilder).toString();
}
if (triggerNodeCount == 1) {
// 移除AND和OR它们必须包含两个以上组件
if(elData.indexOf("AND(")!=-1){
elData=elData.replace("AND(", "").replace("),", ",");
}else if(elData.indexOf("OR(")!=-1){
elData=elData.replace("OR(", "").replace("),", ",");
}
}
return elData;
}
/**
* 构建场景中的触发器和执行动作
*
* @param sceneScript 场景脚本
* @param scriptPurpose 脚本用途1=数据流2=触发器3=执行动作
* @param scene 场景
* @return 返回规则脚本ID
*/
private String buildTriggerAction(SceneScript sceneScript, int scriptPurpose, Scene scene, List<Script> ruleScripts, List<SceneScript> sceneScripts, List<SceneDevice> sceneDevices) {
// 构建规则脚本
Script ruleScript = new Script();
// 设置脚本标识,D=数据流A=执行动作T=触发器,雪花算法生成唯一数
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
String scriptId = String.valueOf(snowflake.nextId());
if (scriptPurpose == 2) {
scriptId = "T" + scriptId;
ruleScript.setScriptType("if_script");
} else if (scriptPurpose == 3) {
scriptId = "A" + scriptId;
ruleScript.setScriptType("script");
}
ruleScript.setScriptId(scriptId);
ruleScript.setScriptName(scriptId);
ruleScript.setApplicationName("fastbee");
ruleScript.setScriptLanguage("groovy");
SysUser user = getLoginUser().getUser();
ruleScript.setUserId(user.getUserId());
ruleScript.setUserName(user.getUserName());
ruleScript.setScriptPurpose(scriptPurpose);
ruleScript.setEnable(1);
ruleScript.setScriptOrder(0);
ruleScript.setScriptEvent(0);
ruleScript.setScriptAction(0);
ruleScript.setSceneId(scene.getSceneId());
ruleScript.setCreateBy(user.getUserName());
ruleScript.setCreateTime(DateUtils.getNowDate());
// 构建脚本内容
ScriptTemplate template=new ScriptTemplate(
scene.getSceneId(),scriptId,sceneScript.getType(),sceneScript.getSource(),scene.getSilentPeriod(),
scene.getExecuteDelay(), scene.getCond(),sceneScript.getScriptPurpose(),sceneScript.getId(),sceneScript.getValue(),
StringUtils.isEmpty(sceneScript.getOperator())?"=":sceneScript.getOperator(),String.join(",", sceneScript.getDeviceNums()),sceneScript.getProductId()
);
String data = String.format("String json =\"%s\";\n" +
"sceneContext.process(json);", StringEscapeUtils.escapeJava(JSONObject.toJSONString(template)));
ruleScript.setScriptData(data);
// 构建脚本集合,规则脚本不需要存储定时触发器
if (sceneScript.getSource() != 2) {
ruleScripts.add(ruleScript);
}
if (scriptPurpose == 2) {
// 触发器
if (sceneScript.getSource() == 1) {
// 构建场景设备集合
for (int j = 0; j < sceneScript.getDeviceNums().length; j++) {
SceneDevice sceneDevice = new SceneDevice();
sceneDevice.setSceneId(scene.getSceneId());
sceneDevice.setScriptId(scriptId);
sceneDevice.setSource(sceneScript.getSource());
sceneDevice.setSerialNumber(sceneScript.getDeviceNums()[j]);
sceneDevice.setProductId(0L);
sceneDevice.setProductName("");
sceneDevice.setType(scriptPurpose);
sceneDevices.add(sceneDevice);
}
}else if (sceneScript.getSource() == 2) {
// 创建告警定时任务
createSceneTask(scene, sceneScript);
}else if (sceneScript.getSource() == 3) {
// 构建场景设备集合
SceneDevice sceneDevice = new SceneDevice();
sceneDevice.setSceneId(scene.getSceneId());
sceneDevice.setScriptId(scriptId);
sceneDevice.setSource(sceneScript.getSource());
sceneDevice.setSerialNumber("");
sceneDevice.setProductId(sceneScript.getProductId());
sceneDevice.setProductName(sceneScript.getProductName());
sceneDevice.setType(scriptPurpose);
sceneDevices.add(sceneDevice);
}
} else if (scriptPurpose == 3) {
if (sceneScript.getSource() == 1) {
// 构建场景设备集合
for (int j = 0; j < sceneScript.getDeviceNums().length; j++) {
SceneDevice sceneDevice = new SceneDevice();
sceneDevice.setSceneId(scene.getSceneId());
sceneDevice.setScriptId(scriptId);
sceneDevice.setSource(sceneScript.getSource());
sceneDevice.setSerialNumber(sceneScript.getDeviceNums()[j]);
sceneDevice.setProductId(0L);
sceneDevice.setProductName("");
sceneDevice.setType(scriptPurpose);
sceneDevices.add(sceneDevice);
}
} else if (sceneScript.getSource() == 3) {
// 构建场景设备集合
SceneDevice sceneDevice = new SceneDevice();
sceneDevice.setSceneId(scene.getSceneId());
sceneDevice.setScriptId(scriptId);
sceneDevice.setSource(sceneScript.getSource());
sceneDevice.setSerialNumber("");
sceneDevice.setProductId(sceneScript.getProductId());
sceneDevice.setProductName(sceneScript.getProductName());
sceneDevice.setType(scriptPurpose);
sceneDevices.add(sceneDevice);
}
}
// 构建场景脚本集合
sceneScript.setSceneId(scene.getSceneId());
sceneScript.setScriptId(scriptId);
sceneScript.setCreateTime(DateUtils.getNowDate());
sceneScript.setCreateBy(user.getUserName());
sceneScript.setOperator(StringUtils.isEmpty(sceneScript.getOperator())?"=":sceneScript.getOperator());
sceneScripts.add(sceneScript);
// 返回脚本ID
return scriptId;
}
/**
* 创建场景定时任务
*
* @param scene
* @param sceneScript
*/
private void createSceneTask(Scene scene, SceneScript sceneScript) {
// 创建定时任务
try {
if (!CronUtils.isValid(sceneScript.getCronExpression())) {
log.error("新增场景联动定时任务失败Cron表达式不正确");
throw new Exception("新增场景联动定时任务失败Cron表达式不正确");
}
DeviceJob deviceJob = new DeviceJob();
deviceJob.setJobName("场景联动定时触发");
deviceJob.setJobType(3);
deviceJob.setJobGroup("DEFAULT");
deviceJob.setConcurrent("1");
deviceJob.setMisfirePolicy("2");
deviceJob.setStatus(scene.getEnable() == 1 ? "0" : "1");
deviceJob.setCronExpression(sceneScript.getCronExpression());
deviceJob.setIsAdvance(sceneScript.getIsAdvance());
deviceJob.setSceneId(scene.getSceneId());
deviceJob.setDeviceName("场景联动定时触发");
jobService.insertJob(deviceJob);
} catch (SchedulerException e) {
e.printStackTrace();
} catch (TaskException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 批量删除场景联动
*
* @param sceneIds 需要删除的场景联动主键
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteSceneBySceneIds(Long[] sceneIds) {
// 批量删除规则脚本
ruleScriptMapper.deleteRuleScriptBySceneIds(sceneIds);
// 批量删除场景脚本
sceneScriptMapper.deleteSceneScriptBySceneIds(sceneIds);
// 批量删除场景设备
sceneDeviceMapper.deleteSceneDeviceBySceneIds(sceneIds);
// 批量删除定时任务
try {
jobService.deleteJobBySceneIds(sceneIds);
} catch (SchedulerException e) {
e.printStackTrace();
}
// 删除告警关联的场景
// alertService.deleteAlertByAlertIds(sceneIds);
// 删除缓存静默时间
for(Long sceneId:sceneIds){
String key = "silent:" + "scene_" + sceneId;
redisCache.deleteObject(key);
}
return sceneMapper.deleteSceneBySceneIds(sceneIds);
}
/**
* 删除场景联动信息
*
* @param sceneId 场景联动主键
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteSceneBySceneId(Long sceneId) {
// 批量删除规则脚本
ruleScriptMapper.deleteRuleScriptBySceneIds(new Long[]{sceneId});
// 批量删除场景脚本
sceneScriptMapper.deleteSceneScriptBySceneIds(new Long[]{sceneId});
// 批量删除场景设备
sceneDeviceMapper.deleteSceneDeviceBySceneIds(new Long[]{sceneId});
// 批量删除定时任务
try {
jobService.deleteJobBySceneIds(new Long[]{sceneId});
} catch (SchedulerException e) {
e.printStackTrace();
}
// 删除告警关联的场景
// alertService.deleteAlertByAlertIds(new Long[]{sceneId});
// 删除缓存静默时间
String key = "silent:" + "scene_" + sceneId;
redisCache.deleteObject(key);
return sceneMapper.deleteSceneBySceneId(sceneId);
}
@Override
public int updateStatus(Scene scene) {
Integer enable = scene.getEnable();
// 更新定时任务状态
List<DeviceJob> deviceJobList = jobService.listShortJobBySceneId(new Long[]{scene.getSceneId()});
try {
for (DeviceJob job : deviceJobList) {
DeviceJob deviceJob = new DeviceJob();
deviceJob.setJobId(job.getJobId());
deviceJob.setJobGroup(job.getJobGroup());
deviceJob.setActions(enable == 1 ? "0" : "1");
jobService.changeStatus(deviceJob);
}
} catch (Exception e) {
e.printStackTrace();
}
Scene updateScene = new Scene();
updateScene.setSceneId(scene.getSceneId());
updateScene.setEnable(enable);
return sceneMapper.updateScene(updateScene);
}
}

View File

@@ -0,0 +1,192 @@
package com.fastbee.iot.service.impl;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.iot.domain.Script;
import com.fastbee.iot.mapper.ScriptMapper;
import com.fastbee.iot.model.ScriptCondition;
import com.fastbee.iot.service.IScriptService;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.fastbee.common.core.domain.AjaxResult.success;
import static com.fastbee.common.utils.SecurityUtils.getLoginUser;
/**
* 规则引擎脚本Service业务层处理
*
* @author lizhuangpeng
* @date 2023-07-01
*/
@Service
public class ScriptServiceImpl implements IScriptService
{
@Autowired
private ScriptMapper ruleScriptMapper;
@Resource
private FlowExecutor flowExecutor;
/**
* 查询规则引擎脚本
*
* @param scriptId 规则引擎脚本主键
* @return 规则引擎脚本
*/
@Override
public Script selectRuleScriptById(String scriptId)
{
return ruleScriptMapper.selectRuleScriptById(scriptId);
}
/**
* 查询规则引擎脚本列表
*
* @param ruleScript 规则引擎脚本
* @return 规则引擎脚本
*/
@Override
public List<Script> selectRuleScriptList(Script ruleScript)
{
SysUser sysUser = getLoginUser().getUser();
List<SysRole> roles = sysUser.getRoles();
for (SysRole role : roles) {
if (role.getRoleKey().equals("general") || role.getRoleKey().equals("tenant")) {
// 用户和租户只能查看自己的规则脚本
ruleScript.setUserId(sysUser.getUserId());
break;
}
}
return ruleScriptMapper.selectRuleScriptList(ruleScript);
}
/**
* 查询规则引擎脚本标识数组(设备用户和租户的脚本)
*
* @return 规则引擎脚本
*/
@Override
public String[] selectRuleScriptIdArray(ScriptCondition scriptCondition)
{
return ruleScriptMapper.selectRuleScriptIdArray(scriptCondition);
}
/**
* 新增规则引擎脚本
*
* @param ruleScript 规则引擎脚本
* @return 结果
*/
@Override
public int insertRuleScript(Script ruleScript)
{
// 脚本中引用包替换为许可的包
ruleScript.setScriptData(replaceAllowPackage(ruleScript.getScriptData()));
// 设置脚本标识,D=数据流A=执行动作T=触发器,雪花算法生成唯一数
Snowflake snowflake = IdUtil.createSnowflake(1,1);
ruleScript.setScriptId("D"+snowflake.nextId());
SysUser sysUser = getLoginUser().getUser();
ruleScript.setUserId(sysUser.getUserId());
ruleScript.setUserName(sysUser.getUserName());
ruleScript.setCreateTime(DateUtils.getNowDate());
int result = ruleScriptMapper.insertRuleScript(ruleScript);
// 动态刷新脚本
if(result==1){
ScriptExecutorFactory.loadInstance().getScriptExecutor(ruleScript.getScriptLanguage()).load(ruleScript.getScriptId(), ruleScript.getScriptData());
}
return result;
}
/**
* 脚本中引用包替换为许可的包
* @return
*/
private String replaceAllowPackage(String scriptData){
String header="import cn.hutool.json.JSONArray;\n" +
"import cn.hutool.json.JSONObject;\n" +
"import cn.hutool.json.JSONUtil;\n" +
"import cn.hutool.core.util.NumberUtil;\n";
// 正则替换import为许可的引用包
String pattern = "import.*[;\\n\\s]";
String data = scriptData.replaceAll(pattern, "");
return header + data;
}
/**
* 修改规则引擎脚本
*
* @param ruleScript 规则引擎脚本
* @return 结果
*/
@Override
public int updateRuleScript(Script ruleScript)
{
// 脚本中引用包替换为许可的包
ruleScript.setScriptData(replaceAllowPackage(ruleScript.getScriptData()));
ruleScript.setUpdateTime(DateUtils.getNowDate());
int result = ruleScriptMapper.updateRuleScript(ruleScript);
// 动态刷新脚本
if(result==1){
ScriptExecutorFactory.loadInstance().getScriptExecutor(ruleScript.getScriptLanguage()).load(ruleScript.getScriptId(), ruleScript.getScriptData());
}
return result;
}
/**
* 批量删除规则引擎脚本
*
* @param ids 需要删除的规则引擎脚本主键
* @return 结果
*/
@Override
public int deleteRuleScriptByIds(String[] ids)
{
return ruleScriptMapper.deleteRuleScriptByIds(ids);
}
/**
* 删除规则引擎脚本信息
*
* @param id 规则引擎脚本主键
* @return 结果
*/
@Override
public int deleteRuleScriptById(String id)
{
return ruleScriptMapper.deleteRuleScriptById(id);
}
/**
* 验证脚本
* ruleScript.scriptData 脚本数据
* @return
*/
@Override
public AjaxResult validateScript(Script ruleScript){
// 检查安全性检查
String pattern = ".*while|for\\s*\\(|InputStream|OutputStream|Reader|Writer|File|Socket.*";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(ruleScript.getScriptData());
if(m.find()){
return success("验证失败,错误信息:" + "不能包含关键词for、while、Reader、Write、File、Socket等",false);
}
// 热刷新脚本
try {
ScriptExecutorFactory.loadInstance().getScriptExecutor("groovy").load("validateScript", ruleScript.getScriptData());
}catch(Exception e){
return success("验证失败,错误信息:" + e.getMessage(),false);
}
return success("验证成功,脚本的实际执行情况可以查看后端日志文件",true);
}
}

View File

@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fastbee.iot.mapper.SceneDeviceMapper">
<resultMap type="SceneDevice" id="SceneDeviceResult">
<result property="sceneDeviceId" column="scene_device_id" />
<result property="serialNumber" column="serial_number" />
<result property="productId" column="product_id" />
<result property="productName" column="product_name" />
<result property="scriptId" column="script_id" />
<result property="sceneId" column="scene_id" />
<result property="source" column="source" />
<result property="type" column="type" />
</resultMap>
<resultMap type="com.fastbee.iot.domain.Scene" id="SceneResult">
<result property="sceneId" column="scene_id" />
<result property="chainName" column="chain_name" />
<result property="elData" column="el_data" />
</resultMap>
<sql id="selectSceneDeviceVo">
select scene_device_id, serial_number, product_id, product_name, script_id, scene_id, source,type from iot_scene_device
</sql>
<select id="selectSceneDeviceList" parameterType="SceneDevice" resultMap="SceneDeviceResult">
<include refid="selectSceneDeviceVo"/>
<where>
<if test="serialNumber != null and serialNumber != ''"> and serial_number = #{serialNumber}</if>
<if test="productId != null "> and product_id = #{productId}</if>
<if test="productName != null "> and product_name = #{productName}</if>
<if test="scriptId != null "> and script_id = #{scriptId}</if>
<if test="sceneId != null "> and scene_id = #{sceneId}</if>
<if test="source != null "> and source = #{source}</if>
</where>
</select>
<select id="selectTriggerDeviceRelateScenes" parameterType="SceneDevice" resultMap="SceneResult">
select s.scene_id,s.chain_name
from (select distinct scene_id from iot_scene_device where type = 2 and (serial_number = #{serialNumber} OR product_id = #{productId})) d
left join iot_scene s on s.scene_id=d.scene_id
where s.`enable`= 1
</select>
<select id="selectSceneDeviceBySceneDeviceId" parameterType="Long" resultMap="SceneDeviceResult">
<include refid="selectSceneDeviceVo"/>
where scene_device_id = #{sceneDeviceId}
</select>
<select id="listSceneProductBind" resultType="com.fastbee.iot.model.SceneDeviceBindVO">
select d.scene_id, s.scene_name, d.product_id
from iot_scene_device d
left join iot_scene s on d.scene_id = s.scene_id
where d.product_id in
<foreach item="productId" collection="array" open="(" separator="," close=")">
#{productId}
</foreach>
</select>
<select id="listSceneDeviceBind" resultType="com.fastbee.iot.model.SceneDeviceBindVO">
select d.scene_id, s.scene_name, d.serial_number
from iot_scene_device d
left join iot_scene s on d.scene_id = s.scene_id
where d.serial_number = #{serialNumber}
</select>
<insert id="insertSceneDevice" parameterType="SceneDevice" useGeneratedKeys="true" keyProperty="sceneDeviceId" >
insert into iot_scene_device
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="serialNumber != null">serial_number,</if>
<if test="productId != null">product_id,</if>
<if test="productName != null">product_name,</if>
<if test="scriptId != null">script_id,</if>
<if test="sceneId != null">scene_id,</if>
<if test="source != null">source,</if>
<if test="type != null">type,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="serialNumber != null">#{serialNumber},</if>
<if test="productId != null">#{productId},</if>
<if test="productName != null">#{productName},</if>
<if test="scriptId != null">#{scriptId},</if>
<if test="sceneId != null">#{sceneId},</if>
<if test="source != null">#{source},</if>
<if test="type != null">#{type},</if>
</trim>
</insert>
<update id="updateSceneDevice" parameterType="SceneDevice">
update iot_scene_device
<trim prefix="SET" suffixOverrides=",">
<if test="serialNumber != null">serial_number = #{serialNumber},</if>
<if test="productId != null">product_id = #{productId},</if>
<if test="productName != null">product_name = #{productName},</if>
<if test="scriptId != null">script_id = #{scriptId},</if>
<if test="sceneId != null">scene_id = #{sceneId},</if>
<if test="source != null">source = #{source},</if>
<if test="type != null">type = #{type},</if>
</trim>
where scene_device_id = #{sceneDeviceId}
</update>
<delete id="deleteSceneDeviceBySceneDeviceId" parameterType="Long">
delete from iot_scene_device where scene_device_id = #{sceneDeviceId}
</delete>
<delete id="deleteSceneDeviceBySceneDeviceIds" parameterType="String">
delete from iot_scene_device where scene_device_id in
<foreach item="sceneDeviceId" collection="array" open="(" separator="," close=")">
#{sceneDeviceId}
</foreach>
</delete>
<insert id="insertSceneDeviceList" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="sceneDeviceId">
insert into iot_scene_device (serial_number,product_id,product_name, script_id,scene_id,source,type)
VALUES
<foreach collection ="list" item="sceneDevice" separator =",">
(#{sceneDevice.serialNumber},
#{sceneDevice.productId},
#{sceneDevice.productName},
#{sceneDevice.scriptId},
#{sceneDevice.sceneId},
#{sceneDevice.source},
#{sceneDevice.type})
</foreach >
</insert>
<delete id="deleteSceneDeviceBySceneIds" parameterType="String">
delete from iot_scene_device where scene_id in
<foreach item="sceneId" collection="array" open="(" separator="," close=")">
#{sceneId}
</foreach>
</delete>
</mapper>

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fastbee.iot.mapper.SceneMapper">
<resultMap type="com.fastbee.iot.domain.Scene" id="SceneResult">
<result property="sceneId" column="scene_id" />
<result property="sceneName" column="scene_name" />
<result property="chainName" column="chain_name" />
<result property="userId" column="user_id" />
<result property="userName" column="user_name" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<result property="cond" column="cond" />
<result property="silentPeriod" column="silent_period" />
<result property="executeMode" column="execute_mode" />
<result property="executeDelay" column="execute_delay" />
<result property="hasAlert" column="has_alert" />
<result property="enable" column="enable" />
<result property="elData" column="el_data" />
<result property="applicationName" column="application_name" />
</resultMap>
<sql id="selectSceneVo">
select scene_id, scene_name,chain_name, user_id, user_name, create_by, create_time, update_by, update_time, remark, cond, silent_period, execute_mode, execute_delay, has_alert, enable, el_data, application_name from iot_scene
</sql>
<sql id="selectSceneListVo">
select scene_id, scene_name,chain_name, user_id, user_name, create_by, create_time, update_by, update_time, remark, cond, silent_period, execute_mode, execute_delay, has_alert, enable, application_name from iot_scene
</sql>
<select id="selectSceneList" parameterType="Scene" resultMap="SceneResult">
<include refid="selectSceneListVo"/>
<where>
<if test="sceneName != null and sceneName != ''"> and scene_name like concat('%', #{sceneName}, '%')</if>
<if test="userId != null "> and user_id = #{userId}</if>
<if test="userName != null and userName != ''"> and user_name like concat('%', #{userName}, '%')</if>
<if test="cond != null "> and cond = #{cond}</if>
<if test="silentPeriod != null "> and silent_period = #{silentPeriod}</if>
<if test="executeMode != null "> and execute_mode = #{executeMode}</if>
<if test="executeDelay != null "> and execute_delay = #{executeDelay}</if>
<if test="hasAlert != null "> and has_alert = #{hasAlert}</if>
<if test="enable != null "> and enable = #{enable}</if>
</where>
order by create_time desc
</select>
<select id="selectSceneBySceneId" parameterType="Long" resultMap="SceneResult">
<include refid="selectSceneVo"/>
where scene_id = #{sceneId}
</select>
<select id="selectSceneListBySceneIds" parameterType="String" resultMap="SceneResult">
<include refid="selectSceneVo"/>
where scene_id in
<foreach item="sceneId" collection="array" open="(" separator="," close=")">
#{sceneId}
</foreach>
</select>
<insert id="insertScene" parameterType="com.fastbee.iot.domain.Scene" useGeneratedKeys="true" keyProperty="sceneId">
insert into iot_scene
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="sceneName != null and sceneName != ''">scene_name,</if>
<if test="chainName != null and chainName != ''">chain_name,</if>
<if test="userId != null">user_id,</if>
<if test="userName != null and userName != ''">user_name,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
<if test="cond != null">cond,</if>
<if test="silentPeriod != null">silent_period,</if>
<if test="executeMode != null">execute_mode,</if>
<if test="executeDelay != null">execute_delay,</if>
<if test="hasAlert != null">has_alert,</if>
<if test="enable != null">enable,</if>
<if test="elData != null">el_data,</if>
<if test="applicationName != null and applicationName != ''">application_name,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="sceneName != null and sceneName != ''">#{sceneName},</if>
<if test="chainName != null and chainName != ''">#{chainName},</if>
<if test="userId != null">#{userId},</if>
<if test="userName != null and userName != ''">#{userName},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
<if test="cond != null">#{cond},</if>
<if test="silentPeriod != null">#{silentPeriod},</if>
<if test="executeMode != null">#{executeMode},</if>
<if test="executeDelay != null">#{executeDelay},</if>
<if test="hasAlert != null">#{hasAlert},</if>
<if test="enable != null">#{enable},</if>
<if test="elData != null">#{elData},</if>
<if test="applicationName != null and applicationName != ''">#{applicationName},</if>
</trim>
</insert>
<update id="updateScene" parameterType="com.fastbee.iot.domain.Scene">
update iot_scene
<trim prefix="SET" suffixOverrides=",">
<if test="sceneName != null and sceneName != ''">scene_name = #{sceneName},</if>
<if test="chainName != null and chainName != ''">chain_name = #{chainName},</if>
<if test="userId != null">user_id = #{userId},</if>
<if test="userName != null and userName != ''">user_name = #{userName},</if>
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="cond != null">cond = #{cond},</if>
<if test="silentPeriod != null">silent_period = #{silentPeriod},</if>
<if test="executeMode != null">execute_mode = #{executeMode},</if>
<if test="executeDelay != null">execute_delay = #{executeDelay},</if>
<if test="hasAlert != null">has_alert = #{hasAlert},</if>
<if test="enable != null">enable = #{enable},</if>
<if test="elData != null">el_data = #{elData},</if>
<if test="applicationName != null and applicationName != ''">application_name = #{applicationName},</if>
</trim>
where scene_id = #{sceneId}
</update>
<delete id="deleteSceneBySceneId" parameterType="Long">
delete from iot_scene where scene_id = #{sceneId}
</delete>
<delete id="deleteSceneBySceneIds" parameterType="String">
delete from iot_scene where scene_id in
<foreach item="sceneId" collection="array" open="(" separator="," close=")">
#{sceneId}
</foreach>
</delete>
</mapper>

View File

@@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fastbee.iot.mapper.SceneScriptMapper">
<resultMap type="SceneScript" id="SceneScriptResult">
<result property="scriptId" column="script_id"/>
<result property="sceneId" column="scene_id"/>
<result property="productId" column="product_id"/>
<result property="productName" column="product_name"/>
<result property="source" column="source"/>
<result property="scriptPurpose" column="script_purpose"/>
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="value" column="value"/>
<result property="operator" column="operator"/>
<result property="type" column="type"/>
<result property="deviceCount" column="device_count"/>
<result property="jobId" column="job_id"/>
<result property="cronExpression" column="cron_expression"/>
<result property="isAdvance" column="is_advance"/>
<result property="parentId" column="parent_id"/>
<result property="parentName" column="parent_name"/>
<result property="arrayIndex" column="array_index"/>
<result property="arrayIndexName" column="array_index_name"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
</resultMap>
<sql id="selectSceneScriptVo">
select script_id,
scene_id,
product_id,
product_name,
source,
script_purpose,
id,
name,
value,
operator,
type,
device_count,
job_id,
cron_expression,
is_advance,
parent_id,
parent_name,
array_index,
array_index_name,
create_by,
create_time
from iot_scene_script
</sql>
<select id="selectSceneScriptList" parameterType="SceneScript" resultMap="SceneScriptResult">
<include refid="selectSceneScriptVo"/>
<where>
<if test="scriptId != null ">and script_id = #{scriptId}</if>
<if test="sceneId != null ">and scene_id = #{sceneId}</if>
<if test="productId != null ">and product_id = #{productId}</if>
<if test="productName != null ">and product_name = #{productName}</if>
<if test="source != null ">and source = #{source}</if>
<if test="scriptPurpose != null ">and script_purpose = #{scriptPurpose}</if>
<if test="name != null and name != ''">and name like concat('%', #{name}, '%')</if>
<if test="value != null and value != ''">and value = #{value}</if>
<if test="operator != null and operator != ''">and operator = #{operator}</if>
<if test="type != null ">and type = #{type}</if>
<if test="jobId != null ">and job_id = #{jobId}</if>
<if test="cronExpression != null and cronExpression != ''">and cron_expression = #{cronExpression}</if>
<if test="isAdvance != null ">and is_advance = #{isAdvance}</if>
<if test="parentId != null and parentId != ''">and parent_id = #{parentId}</if>
<if test="parentName != null and parentName != ''">and parent_name like concat('%', #{parentName}, '%')
</if>
<if test="arrayIndex != null and arrayIndex != ''">and array_index = #{arrayIndex}</if>
<if test="arrayIndexName != null and arrayIndexName != ''">and array_index_name like concat('%',
#{arrayIndexName}, '%')
</if>
</where>
</select>
<select id="selectSceneScriptBySceneScriptId" parameterType="String" resultMap="SceneScriptResult">
<include refid="selectSceneScriptVo"/>
where script_id = #{scriptId}
</select>
<select id="listSceneScriptByPurpose" resultMap="SceneScriptResult">
<include refid="selectSceneScriptVo"/>
where scene_id in
<foreach collection="sceneIdList" item="sceneId" open="(" separator="," close=")">
#{sceneId}
</foreach>
and script_purpose = #{scriptPurpose}
</select>
<insert id="insertSceneScript" parameterType="SceneScript" useGeneratedKeys="false">
insert into iot_scene_script
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="scriptId != null">script_id,</if>
<if test="sceneId != null">scene_id,</if>
<if test="productId != null">product_id,</if>
<if test="productName != null">product_name,</if>
<if test="source != null">source,</if>
<if test="scriptPurpose != null">script_purpose,</if>
<if test="id != null">id,</if>
<if test="name != null">name,</if>
<if test="value != null">value,</if>
<if test="operator != null">operator,</if>
<if test="type != null">type,</if>
<if test="deviceCount != null">device_count,</if>
<if test="jobId != null">job_id,</if>
<if test="cronExpression != null">cron_expression,</if>
<if test="isAdvance != null">is_advance,</if>
<if test="parentId != null">parent_id,</if>
<if test="parentName != null">parent_name,</if>
<if test="arrayIndex != null">array_index,</if>
<if test="arrayIndexName != null">array_index_name,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="scriptId != null">#{scriptId},</if>
<if test="sceneId != null">#{sceneId},</if>
<if test="productId != null">#{productId},</if>
<if test="productName != null">#{productName},</if>
<if test="source != null">#{source},</if>
<if test="scriptPurpose != null">#{scriptPurpose},</if>
<if test="id != null">#{id},</if>
<if test="name != null">#{name},</if>
<if test="value != null">#{value},</if>
<if test="operator != null">#{operator},</if>
<if test="type != null">#{type},</if>
<if test="deviceCount != null">#{deviceCount},</if>
<if test="jobId != null">#{jobId},</if>
<if test="cronExpression != null">#{cronExpression},</if>
<if test="isAdvance != null">#{isAdvance},</if>
<if test="parentId != null">#{parentId},</if>
<if test="parentName != null">#{parentName},</if>
<if test="arrayIndex != null">#{arrayIndex},</if>
<if test="arrayIndexName != null">#{arrayIndexName},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
</trim>
</insert>
<update id="updateSceneScript" parameterType="SceneScript">
update iot_scene_script
<trim prefix="SET" suffixOverrides=",">
<if test="scriptId != null">script_id = #{scriptId},</if>
<if test="sceneId != null">scene_id = #{sceneId},</if>
<if test="productId != null">product_id = #{productId},</if>
<if test="productName != null">product_name = #{productName},</if>
<if test="source != null">source = #{source},</if>
<if test="scriptPurpose != null">script_purpose = #{scriptPurpose},</if>
<if test="id != null">id = #{id},</if>
<if test="name != null">name = #{name},</if>
<if test="value != null">value = #{value},</if>
<if test="operator != null">operator = #{operator},</if>
<if test="type != null">type = #{type},</if>
<if test="deviceCount != null">device_count = #{deviceCount},</if>
<if test="jobId != null">job_id = #{jobId},</if>
<if test="cronExpression != null">cron_expression = #{cronExpression},</if>
<if test="isAdvance != null">is_advance = #{isAdvance},</if>
<if test="parentId != null">parent_id = #{parentId},</if>
<if test="parentName != null">parent_name = #{parentName},</if>
<if test="arrayIndex != null">array_index = #{arrayIndex},</if>
<if test="arrayIndexName != null">array_index_name = #{arrayIndexName},</if>
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
</trim>
where script_id = #{scriptId}
</update>
<delete id="deleteSceneScriptBySceneScriptId" parameterType="String">
delete
from iot_scene_script
where script_id = #{scriptId}
</delete>
<delete id="deleteSceneScriptBySceneScriptIds" parameterType="String">
delete from iot_scene_script where script_id in
<foreach item="sceneScriptId" collection="array" open="(" separator="," close=")">
#{scriptId}
</foreach>
</delete>
<insert id="insertSceneScriptList" parameterType="java.util.List">
insert into iot_scene_script (script_id,scene_id,product_id,product_name,source,script_purpose,id, name,value,operator,type,device_count,job_id,
cron_expression,is_advance,parent_id,parent_name,array_index,array_index_name,create_by,create_time)
VALUES
<foreach collection="list" item="sceneScript" separator=",">
(#{sceneScript.scriptId},
#{sceneScript.sceneId},
#{sceneScript.productId},
#{sceneScript.productName},
#{sceneScript.source},
#{sceneScript.scriptPurpose},
#{sceneScript.id},
#{sceneScript.name},
#{sceneScript.value},
#{sceneScript.operator},
#{sceneScript.type},
#{sceneScript.deviceCount},
#{sceneScript.jobId},
#{sceneScript.cronExpression},
#{sceneScript.isAdvance},
#{sceneScript.parentId},
#{sceneScript.parentName},
#{sceneScript.arrayIndex},
#{sceneScript.arrayIndexName},
#{sceneScript.createBy},
#{sceneScript.createTime})
</foreach>
</insert>
<delete id="deleteSceneScriptBySceneIds" parameterType="String">
delete from iot_scene_script where scene_id in
<foreach item="sceneId" collection="array" open="(" separator="," close=")">
#{sceneId}
</foreach>
</delete>
</mapper>

View File

@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fastbee.iot.mapper.ScriptMapper">
<resultMap type="com.fastbee.iot.domain.Script" id="RuleScriptResult">
<result property="userId" column="user_id" />
<result property="userName" column="user_name" />
<result property="sceneId" column="scene_id" />
<result property="productId" column="product_id" />
<result property="productName" column="product_name" />
<result property="scriptEvent" column="script_event" />
<result property="scriptAction" column="script_action" />
<result property="scriptPurpose" column="script_purpose" />
<result property="scriptOrder" column="script_order" />
<result property="applicationName" column="application_name" />
<result property="scriptId" column="script_id" />
<result property="scriptName" column="script_name" />
<result property="scriptData" column="script_data" />
<result property="scriptType" column="script_type" />
<result property="scriptLanguage" column="script_language" />
<result property="enable" column="enable" />
<result property="delFlag" column="del_flag" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
</resultMap>
<sql id="selectRuleScriptVo">
select user_id, user_name, scene_id,product_id,product_name,script_event,script_action,script_purpose,script_order,application_name, script_id, script_name, script_data, script_type, script_language,enable, del_flag, create_by, create_time, update_by, update_time, remark,product_id from iot_script
</sql>
<sql id="selectRuleScriptList">
select user_id, user_name, scene_id,product_id,product_name, script_event,script_action,script_purpose,script_order,application_name, script_id, script_name, script_type, script_language,enable, create_by, create_time, update_by, update_time,product_id from iot_script
</sql>
<select id="selectRuleScriptList" parameterType="com.fastbee.iot.model.ScriptCondition" resultMap="RuleScriptResult">
<include refid="selectRuleScriptList"/>
<where>
<if test="applicationName != null and applicationName != ''"> and application_name like concat('%', #{applicationName}, '%')</if>
<if test="scriptId != null and scriptId != ''"> and script_id = #{scriptId}</if>
<if test="userId != null and userId != ''"> and user_id = #{userId}</if>
<if test="sceneId != null and sceneId != ''"> and scene_id = #{sceneId}</if>
<if test="productId != null and productId != ''"> and product_id = #{productId}</if>
<if test="scriptEvent != null and scriptEvent != ''"> and script_event = #{scriptEvent}</if>
<if test="scriptAction != null and scriptAction != ''"> and script_action = #{scriptAction}</if>
<if test="scriptPurpose != null and scriptPurpose != ''"> and script_purpose = #{scriptPurpose}</if>
<if test="scriptName != null and scriptName != ''"> and script_name like concat('%', #{scriptName}, '%')</if>
<if test="scriptType != null and scriptType != ''"> and script_type = #{scriptType}</if>
<if test="enable != null"> and enable = #{enable}</if>
<if test="scriptLanguage != null and scriptLanguage != ''"> and script_language = #{scriptLanguage}</if>
</where>
order by script_order
</select>
<select id="selectRuleScriptIdArray" parameterType="com.fastbee.iot.model.ScriptCondition" resultType="String">
select script_id from iot_script
<where>
<if test="applicationName != null and applicationName != ''"> and application_name like concat('%', #{applicationName}, '%')</if>
<if test="sceneId != null and sceneId != ''"> and scene_id = #{sceneId}</if>
<if test="productId != null and productId != ''"> and product_id = #{productId}</if>
<if test="scriptId != null and scriptId != ''"> and script_id = #{scriptId}</if>
<if test="scriptEvent != null and scriptEvent != ''"> and script_event = #{scriptEvent}</if>
<if test="scriptAction != null and scriptAction != ''"> and script_action = #{scriptAction}</if>
<if test="scriptPurpose != null and scriptPurpose != ''"> and script_purpose = #{scriptPurpose}</if>
<if test="enable != null"> and enable = #{enable}</if>
<if test="scriptLanguage != null and scriptLanguage != ''"> and script_language = #{scriptLanguage}</if>
<if test="userId != null and userId != ''"> and (user_id = #{userId} or user_id = #{tencentId})</if>
</where>
order by script_order
</select>
<select id="selectRuleScriptIdCount" parameterType="String" resultType="int">
select count(script_id) from iot_script where script_id = #{scriptId}
</select>
<select id="selectRuleScriptById" parameterType="String" resultMap="RuleScriptResult">
<include refid="selectRuleScriptVo"/>
where script_id = #{scriptId}
</select>
<insert id="insertRuleScript" parameterType="Script" useGeneratedKeys="false">
insert into iot_script
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null and userId != ''">user_id,</if>
<if test="userName != null and userName != ''">user_name,</if>
<if test="sceneId != null">scene_id,</if>
<if test="scriptEvent != null ">script_event,</if>
<if test="scriptAction != null ">script_action,</if>
<if test="scriptPurpose != null ">script_purpose,</if>
<if test="scriptOrder != null ">script_order,</if>
<if test="applicationName != null and applicationName != ''">application_name,</if>
<if test="scriptId != null and scriptId != ''">script_id,</if>
<if test="scriptName != null and scriptName != ''">script_name,</if>
<if test="scriptData != null">script_data,</if>
<if test="scriptType != null ">script_type,</if>
<if test="scriptLanguage != null and scriptLanguage != ''">script_language,</if>
<if test="enable != null">enable,</if>
<if test="delFlag != null and delFlag != ''">del_flag,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
<if test="productId != null">product_id,</if>
<if test="productName != null">product_name,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null and userId != ''">#{userId},</if>
<if test="userName != null and userName != ''">#{userName},</if>
<if test="sceneId != null">#{sceneId},</if>
<if test="scriptEvent != null ">#{scriptEvent},</if>
<if test="scriptAction != null ">#{scriptAction},</if>
<if test="scriptPurpose != null ">#{scriptPurpose},</if>
<if test="scriptOrder != null ">#{scriptOrder},</if>
<if test="applicationName != null and applicationName != ''">#{applicationName},</if>
<if test="scriptId != null and scriptId != ''">#{scriptId},</if>
<if test="scriptName != null and scriptName != ''">#{scriptName},</if>
<if test="scriptData != null">#{scriptData},</if>
<if test="scriptType != null ">#{scriptType},</if>
<if test="scriptLanguage != null and scriptLanguage != ''">#{scriptLanguage},</if>
<if test="enable != null">#{enable},</if>
<if test="delFlag != null and delFlag != ''">#{delFlag},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
<if test="productId != null">#{productId},</if>
<if test="productName != null">#{productName},</if>
</trim>
</insert>
<update id="updateRuleScript" parameterType="Script">
update iot_script
<trim prefix="SET" suffixOverrides=",">
<if test="userId != null and userId != ''">user_id = #{userId},</if>
<if test="userName != null and userName != ''">user_name = #{userName},</if>
<if test="sceneId != null and sceneId != ''">scene_id = #{sceneId},</if>
<if test="scriptEvent != null ">script_event = #{scriptEvent},</if>
<if test="scriptAction != null ">script_action = #{scriptAction},</if>
<if test="scriptPurpose != null ">script_purpose = #{scriptPurpose},</if>
<if test="scriptOrder != null ">script_order = #{scriptOrder},</if>
<if test="applicationName != null and applicationName != ''">application_name = #{applicationName},</if>
<if test="scriptId != null and scriptId != ''">script_id = #{scriptId},</if>
<if test="scriptName != null and scriptName != ''">script_name = #{scriptName},</if>
<if test="scriptData != null">script_data = #{scriptData},</if>
<if test="scriptType != null ">script_type = #{scriptType},</if>
<if test="scriptLanguage != null and scriptLanguage != ''">script_language = #{scriptLanguage},</if>
<if test="enable != null">enable = #{enable},</if>
<if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
<if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="productId != null">product_id = #{productId},</if>
<if test="productName != null">product_name = #{productName},</if>
</trim>
where script_id = #{scriptId}
</update>
<delete id="deleteRuleScriptById" parameterType="String">
delete from iot_script where script_id = #{scriptId}
</delete>
<delete id="deleteRuleScriptByIds" parameterType="String">
delete from iot_script where script_id in
<foreach item="scriptId" collection="array" open="(" separator="," close=")">
#{scriptId}
</foreach>
</delete>
<insert id="insertRuleScriptList" parameterType="java.util.List">
insert into iot_script (user_id,user_name,scene_id,script_event, script_action, script_purpose,script_order,
application_name,script_id,script_name,script_data,script_type,script_language,enable,create_by,create_time,product_id,product_name)
VALUES
<foreach collection ="list" item="ruleScript" separator =",">
(#{ruleScript.userId},
#{ruleScript.userName},
#{ruleScript.sceneId},
#{ruleScript.scriptEvent},
#{ruleScript.scriptAction},
#{ruleScript.scriptPurpose},
#{ruleScript.scriptOrder},
#{ruleScript.applicationName},
#{ruleScript.scriptId},
#{ruleScript.scriptName},
#{ruleScript.scriptData},
#{ruleScript.scriptType},
#{ruleScript.scriptLanguage},
#{ruleScript.enable},
#{ruleScript.createBy},
#{ruleScript.createTime},
#{ruleScript.productId},
#{ruleScript.productName})
</foreach >
</insert>
<delete id="deleteRuleScriptBySceneIds" parameterType="String">
delete from iot_script where scene_id in
<foreach item="sceneId" collection="array" open="(" separator="," close=")">
#{sceneId}
</foreach>
</delete>
</mapper>

View File

@@ -73,6 +73,7 @@
"vue-qr": "^4.0.9",
"vue-router": "3.4.9",
"vue-seamless-scroll": "^1.1.23",
"vue2-ace-editor": "^0.0.15",
"vuedraggable": "2.24.3",
"vuex": "3.6.0"
},

53
vue/src/api/iot/scene.js Normal file
View File

@@ -0,0 +1,53 @@
import request from '@/utils/request';
// 查询场景联动列表
export function listScene(query) {
return request({
url: '/iot/scene/list',
method: 'get',
params: query,
});
}
// 查询场景联动详细
export function getScene(sceneId) {
return request({
url: '/iot/scene/' + sceneId,
method: 'get',
});
}
// 新增场景联动
export function addScene(data) {
return request({
url: '/iot/scene',
method: 'post',
data: data,
});
}
// 修改场景联动
export function updateScene(data) {
return request({
url: '/iot/scene',
method: 'put',
data: data,
});
}
// 删除场景联动
export function delScene(sceneId) {
return request({
url: '/iot/scene/' + sceneId,
method: 'delete',
});
}
// 执行场景
export function runScene(query) {
return request({
url: '/iot/runtime/runScene',
method: 'post',
params: query,
});
}

53
vue/src/api/iot/script.js Normal file
View File

@@ -0,0 +1,53 @@
import request from '@/utils/request';
// 查询规则引擎脚本列表
export function listScript(query) {
return request({
url: '/iot/script/list',
method: 'get',
params: query,
});
}
// 查询规则引擎脚本详细
export function getScript(scriptId) {
return request({
url: '/iot/script/' + scriptId,
method: 'get',
});
}
// 新增规则引擎脚本
export function addScript(data) {
return request({
url: '/iot/script',
method: 'post',
data: data,
});
}
// 修改规则引擎脚本
export function updateScript(data) {
return request({
url: '/iot/script',
method: 'put',
data: data,
});
}
// 删除规则引擎脚本
export function delScript(scriptId) {
return request({
url: '/iot/script/' + scriptId,
method: 'delete',
});
}
// 验证规则脚本
export function validateScript(scriptData) {
return request({
url: '/iot/script/validate',
method: 'post',
data: scriptData,
});
}

View File

@@ -0,0 +1,103 @@
<template>
<div style="border: 0px solid #ebebeb; overflow: hidden; border-radius: 6px; background-color: #ebebeb; padding: 8px 5px 8px 0">
<editor ref="codeEditor" v-model="currentContent" @init="editorInit" :options="options" :lang="lang" :theme="codeStyle" :width="width" :height="height" />
</div>
</template>
<script>
// import { mapState } from 'vuex';
export default {
name: 'AceEditor',
components: {
editor: require('vue2-ace-editor'),
},
props: {
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '500px',
},
content: {
type: String,
required: true,
default: () => null,
},
lang: {
type: String,
default: 'groovy',
},
readOnly: {
type: Boolean,
default: false,
},
codeStyle: {
type: String,
default: 'chrome',
},
},
data() {
return {
options: {
autoScrollEditorIntoView: true,
// enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
// 只读
readOnly: this.readOnly,
// 显示打印边距线
showPrintMargin: false,
// 字体大小
fontSize: 13,
},
};
},
computed: {
// ...mapState({
// codeStyle: (state) => state.settings.codeStyle,
// codeSize: (state) => state.settings.codeSize,
// }),
currentContent: {
get() {
return this.content;
},
set(val) {
this.$emit('update:content', val);
},
},
},
watch: {
codeSize: {
handler: function (value) {
this.$refs.codeEditor.editor.setOptions({
fontSize: value,
});
},
deep: true,
},
},
created() {},
mounted() {},
methods: {
editorInit(editor) {
require('brace/ext/language_tools');
require('brace/mode/groovy');
require('brace/mode/mysql');
require('brace/mode/json');
require('brace/theme/chrome');
require('brace/snippets/groovy');
require('brace/snippets/json');
require('brace/ext/beautify');
},
format() {
const ace = require('brace');
const editor = this.$refs.codeEditor.editor;
const beautify = ace.acequire('ace/ext/beautify');
beautify.beautify(editor.session);
},
},
};
</script>

View File

@@ -0,0 +1,180 @@
<template>
<el-dialog title="选择设备" :visible.sync="openDeviceList" width="900px" append-to-body>
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable size="small" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="设备编号" prop="serialNumber">
<el-input v-model="queryParams.serialNumber" placeholder="请输入设备编号" clearable size="small" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table ref="multipleTable" v-loading="loading" :data="deviceList" @select="handleSelectionChange" row-key="serialNumber" size="mini">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="设备名称" align="center" prop="deviceName" />
<el-table-column label="设备编号" align="center" prop="serialNumber" />
<el-table-column label="产品名称" align="center" prop="productName" />
<el-table-column label="设备类型" align="center" width="75">
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.isOwner == 0">分享</el-tag>
<el-tag type="primary" v-else>拥有</el-tag>
</template>
</el-table-column>
<el-table-column label="定位方式" align="center" prop="locationWay" width="100">
<template slot-scope="scope">
<dict-tag :options="dict.type.iot_location_way" :value="scope.row.locationWay" />
</template>
</el-table-column>
<el-table-column label="设备状态" align="center" prop="status" width="80">
<template slot-scope="scope">
<dict-tag :options="dict.type.iot_device_status" :value="scope.row.status" />
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList(null)" />
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="confirmSelectDevice"> </el-button>
<el-button @click="closeSelectDeviceList"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { listDeviceShort } from '@/api/iot/device';
export default {
name: 'device-list',
dicts: ['iot_device_status', 'iot_location_way'],
data() {
return {
// 遮罩层
loading: true,
// 选中设备
selectDeviceNums: [],
productId: 0,
productName: '',
// 是否显示设备列表
openDeviceList: false,
// 总条数
total: 0,
// 设备表格数据
deviceList: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
deviceName: null,
productId: null,
productName: null,
serialNumber: null,
status: null,
},
};
},
created() {},
methods: {
// 获取设备列表
getList() {
this.deviceList = [];
this.loading = true;
this.queryParams.productId = this.queryParams.productId == 0 ? null : this.queryParams.productId;
listDeviceShort(this.queryParams).then((response) => {
this.deviceList = response.rows;
this.total = response.total;
this.loading = false;
// 设置选中
if (this.selectDeviceNums) {
this.deviceList.forEach((row) => {
this.$nextTick(() => {
if (this.selectDeviceNums.some((x) => x === row.serialNumber)) {
this.$refs.multipleTable.toggleRowSelection(row, true);
}
});
});
} else {
// 初始化
this.selectDeviceNums = [];
}
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList(null);
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm');
this.handleQuery();
},
/** 多选框选中数据 */
handleSelectionChange(selection, row) {
// 设备ID是否存在于原始设备ID数组中
let index = this.selectDeviceNums.indexOf(row.serialNumber);
// 是否选中
let value = selection.indexOf(row);
if (index == -1 && value != -1) {
// 不存在且选中
this.selectDeviceNums.push(row.serialNumber);
this.productId = row.productId;
this.productName = row.productName;
} else if (index != -1 && value == -1) {
// 存在且取消选中
this.selectDeviceNums.splice(index, 1);
}
// 筛选产品下的设备比
if (this.selectDeviceNums.length == 0) {
this.queryParams.productId = null;
this.getList();
} else if (this.selectDeviceNums.length == 1) {
this.queryParams.productId = row.productId;
this.getList();
}
},
// 全选事件处理
handleSelectionAll(selection) {
for (let i = 0; i < this.deviceList.length; i++) {
// 设备ID是否存在于原始设备ID数组中
let index = this.selectDeviceNums.indexOf(this.deviceList[i].serialNumber);
// 是否选中
let value = selection.indexOf(this.deviceList[i]);
if (index == -1 && value != -1) {
// 不存在且选中
this.selectDeviceNums.push(this.deviceList[i].serialNumber);
} else if (index != -1 && value == -1) {
// 存在且取消选中
this.selectDeviceNums.splice(index, 1);
}
}
},
// 关闭选择设备列表
closeSelectDeviceList() {
this.openDeviceList = false;
},
/**确定选择设备,设备传递给父组件 */
confirmSelectDevice() {
if (this.selectDeviceNums.length > 0) {
var data = {
productId: this.productId,
productName: this.productName,
deviceNums: this.selectDeviceNums,
};
this.$emit('deviceEvent', data);
}
this.openDeviceList = false;
},
},
};
</script>
<style lang="scss" scoped>
/***隐藏全选,避免选中不同产品的设备**/
::v-deep .el-table__header-wrapper .el-checkbox {
display: none;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,253 @@
<template>
<div style="padding: 6px">
<el-card v-show="showSearch" style="margin-bottom: 6px">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px" style="margin-bottom: -20px">
<el-form-item label="告警名称" prop="alertName">
<el-input v-model="queryParams.alertName" placeholder="请输入告警名称" clearable size="small" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="告警级别" prop="alertLevel">
<el-select v-model="queryParams.alertLevel" placeholder="请选择告警级别" clearable size="small">
<el-option v-for="dict in dict.type.iot_alert_level" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="处理状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择处理状态" clearable size="small">
<el-option v-for="dict in dict.type.iot_process_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card style="padding-bottom: 100px">
<el-table v-loading="loading" :data="alertLogList" @selection-change="handleSelectionChange" border>
<el-table-column label="告警名称" align="center" prop="alertName" />
<el-table-column label="设备编号" align="center" prop="serialNumber" />
<el-table-column label="设备名称" align="center" prop="deviceName" />
<el-table-column label="告警级别" align="center" prop="alertLevel" width="120">
<template slot-scope="scope">
<dict-tag :options="dict.type.iot_alert_level" :value="scope.row.alertLevel" />
</template>
</el-table-column>
<el-table-column label="告警时间" align="center" prop="createTime" width="170">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="数据" align="left" header-align="center" prop="detail">
<template slot-scope="scope">
<div v-html="formatDetail(scope.row.detail)"></div>
</template>
</el-table-column>
<el-table-column label="处理状态" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :options="dict.type.iot_process_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['iot:alertLog:edit']">处理</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改设备告警对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="处理结果" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" rows="8" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</el-card>
</div>
</template>
<script>
// import { listAlertLog, getAlertLog, delAlertLog, addAlertLog, updateAlertLog } from '@/api/iot/alertLog';
export default {
name: 'SceneLog',
dicts: ['iot_alert_level', 'iot_process_status'],
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 设备告警表格数据
alertLogList: [],
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
alertName: null,
alertLevel: null,
status: null,
productId: null,
productName: null,
deviceId: null,
deviceName: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
remark: [
{
required: true,
message: '处理内容不能为空',
trigger: 'blur',
},
],
},
};
},
created() {
this.getList();
},
methods: {
/** 查询设备告警列表 */
getList() {
this.loading = true;
listAlertLog(this.queryParams).then((response) => {
this.alertLogList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
alertLogId: null,
alertName: null,
alertLevel: null,
status: null,
productId: null,
productName: null,
deviceId: null,
deviceName: null,
createBy: null,
createTime: null,
updateBy: null,
updateTime: null,
remark: null,
};
this.resetForm('form');
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm');
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map((item) => item.alertLogId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = '添加设备告警';
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const alertLogId = row.alertLogId || this.ids;
getAlertLog(alertLogId).then((response) => {
this.form = response.data;
this.open = true;
this.title = '修改设备告警';
});
},
/** 提交按钮 */
submitForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.form.alertLogId != null) {
updateAlertLog(this.form).then((response) => {
this.$modal.msgSuccess('修改成功');
this.open = false;
this.getList();
});
} else {
addAlertLog(this.form).then((response) => {
this.$modal.msgSuccess('新增成功');
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const alertLogIds = row.alertLogId || this.ids;
this.$modal
.confirm('是否确认删除设备告警编号为"' + alertLogIds + '"的数据项?')
.then(function () {
return delAlertLog(alertLogIds);
})
.then(() => {
this.getList();
this.$modal.msgSuccess('删除成功');
})
.catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
this.download(
'iot/alertLog/export',
{
...this.queryParams,
},
`alertLog_${new Date().getTime()}.xlsx`
);
},
/**格式化显示物模型**/
formatDetail(json) {
if (json == null || json == '') {
return;
}
let item = JSON.parse(json);
let result = 'id<span style="color:#F56C6C">' + item.id + '</span><br />';
result = result + 'value<span style="color:#F56C6C">' + item.value + '</span><br />';
result = result + 'remark<span style="color:#F56C6C">' + item.remark + '</span>';
return result;
},
},
};
</script>

View File

@@ -0,0 +1,147 @@
<template>
<el-dialog title="选择产品" :visible.sync="open" width="800px">
<div style="margin-top: -55px">
<el-divider style="margin-top: -30px"></el-divider>
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
<el-form-item label="产品名称" prop="productName">
<el-input v-model="queryParams.productName" placeholder="请输入产品名称" clearable size="small" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" ref="singleTable" :data="productList" @row-click="rowClick" highlight-current-row size="mini">
<el-table-column label="选择" width="50" align="center">
<template slot-scope="scope">
<input type="radio" :checked="scope.row.isSelect" name="product" />
</template>
</el-table-column>
<el-table-column label="产品名称" align="center" prop="productName" />
<el-table-column label="分类名称" align="center" prop="categoryName" />
<el-table-column label="租户名称" align="center" prop="tenantName" />
<el-table-column label="授权码" align="center" prop="status" width="70">
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.isAuthorize == 1">启用</el-tag>
<el-tag type="info" v-if="scope.row.isAuthorize == 0">未启用</el-tag>
</template>
</el-table-column>
<el-table-column label="认证方式" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :options="dict.type.iot_vertificate_method" :value="scope.row.vertificateMethod" />
</template>
</el-table-column>
<el-table-column label="联网方式" align="center" prop="networkMethod">
<template slot-scope="scope">
<dict-tag :options="dict.type.iot_network_method" :value="scope.row.networkMethod" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="confirmSelectProduct" type="primary">确定</el-button>
<el-button @click="closeDialog" type="info"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { listProduct } from '@/api/iot/product';
export default {
name: 'ProductList',
dicts: ['iot_vertificate_method', 'iot_network_method'],
data() {
return {
// 遮罩层
loading: true,
// 总条数
total: 0,
// 打开选择产品对话框
open: false,
// 产品列表
productList: [],
// 选中的产品编号
selectProductId: 0,
// 选中的产品
product: {},
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
productName: null,
categoryId: null,
categoryName: null,
tenantId: null,
tenantName: null,
isSys: null,
status: 2, //已发布
deviceType: null,
networkMethod: null,
},
};
},
created() {},
methods: {
/** 查询产品列表 */
getList() {
this.loading = true;
listProduct(this.queryParams).then((response) => {
//产品列表初始化isSelect值用于单选
for (let i = 0; i < response.rows.length; i++) {
response.rows[i].isSelect = false;
}
this.productList = response.rows;
this.total = response.total;
this.loading = false;
// 设置产品选中
this.setRadioSelected(this.selectProductId);
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm');
this.handleQuery();
},
/** 单选数据 */
rowClick(product) {
if (product != null) {
this.setRadioSelected(product.productId);
this.product = product;
}
},
/** 设置单选按钮选中 */
setRadioSelected(productId) {
for (let i = 0; i < this.productList.length; i++) {
if (this.productList[i].productId == productId) {
this.productList[i].isSelect = true;
} else {
this.productList[i].isSelect = false;
}
}
},
/**确定选择产品,产品传递给父组件 */
confirmSelectProduct() {
this.$emit('productEvent', this.product);
this.open = false;
},
/**关闭对话框 */
closeDialog() {
this.open = false;
},
},
};
</script>

View File

@@ -0,0 +1,384 @@
<template>
<div style="padding: 6px">
<el-card v-show="showSearch" style="margin-bottom: 6px">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
label-width="68px" style="margin-bottom: -20px">
<el-form-item label="脚本标识" prop="scriptId">
<el-input v-model="queryParams.scriptId" placeholder="请输入脚本名" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="脚本名" prop="scriptName">
<el-input v-model="queryParams.scriptName" placeholder="请输入脚本名" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
<el-form-item style="float: right">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['iot:script:add']">新增</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card style="padding-bottom: 100px">
<el-table v-loading="loading" :data="scriptList" @selection-change="handleSelectionChange">
<el-table-column label="脚本名称" align="center" prop="scriptName" />
<el-table-column label="所属产品" align="center" prop="productName" />
<el-table-column label="脚本标识" align="center" prop="scriptId" width="180" />
<el-table-column label="脚本事件" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :options="dict.type.rule_script_event" :value="scope.row.scriptEvent" size="small" />
</template>
</el-table-column>
<el-table-column label="脚本动作" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :options="dict.type.rule_script_action" :value="scope.row.scriptAction"
size="small" />
</template>
</el-table-column>
<el-table-column label="脚本语言" align="center" prop="scriptLanguage" />
<el-table-column label="状态" align="center" prop="enable" width="120">
<template slot-scope="scope">
<el-tag v-if="scope.row.enable == 1" type="success" size="small">启动</el-tag>
<el-tag v-if="scope.row.enable == 0" type="danger" size="small">暂停</el-tag>
</template>
</el-table-column>
<el-table-column label="执行顺序" align="center" prop="scriptOrder" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleUpdate(scope.row)"
v-hasPermi="['iot:script:query']">查看</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['iot:script:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改规则引擎脚本对话框 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body :close-on-click-modal="false"
:close-on-press-escape="false">
<el-form ref="form" :model="form" :rules="rules" label-width="90px">
<el-row :gutter="50">
<el-col :span="12">
<el-form-item label="脚本名称" prop="scriptName">
<el-input v-model="form.scriptName" placeholder="请输入脚本名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行顺序" prop="scriptOrder">
<el-input-number v-model="form.scriptOrder" placeholder="请输入脚本名" type="number"
controls-position="right" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="脚本事件" prop="scriptEvent">
<el-select v-model="form.scriptEvent" placeholder="请选择脚本事件" style="width: 100%">
<el-option v-for="dict in dict.type.rule_script_event" :key="dict.label"
:label="dict.label" :value="Number(dict.value)"
:disabled="dict.value !== '1' && dict.value !== '2'"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="脚本动作" prop="scriptAction">
<el-select v-model="form.scriptAction" placeholder="请选择脚本动作" style="width: 100%">
<el-option v-for="dict in dict.type.rule_script_action" :key="dict.label"
:label="dict.label" :value="Number(dict.value)"
:disabled="dict.value !== '1'"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="脚本状态" prop="enable">
<el-switch v-model="form.enable" :active-value="1" :inactive-value="0" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属产品" prop="productName">
<el-input readonly v-model="form.productName" size="small" placeholder="请选择产品"
style="margin-top: 3px">
<el-button slot="append" @click="handleSelectProduct()"
size="small">选择产品</el-button>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12" style="float: right"></el-col>
</el-row>
</el-form>
<div style="padding: 0px 10px" @click="editClick">
<AceEditor ref="codeEditor" :content.sync="form.scriptData" lang="groovy" codeStyle="chrome"
:read-only="false" width="100%" height="450px"></AceEditor>
</div>
<div style="padding: 0 10px; margin: 10px 0">
<el-alert :title="validateMsg" type="success" show-icon v-if="isValidate && validateMsg"
:closable="false"></el-alert>
<el-alert :title="validateMsg" type="error" show-icon v-if="!isValidate && validateMsg"
:closable="false"></el-alert>
</div>
<div slot="footer" class="dialog-footer">
<span style="float: left">
<el-link style="line-height: 40px; padding-left: 20px" icon="el-icon-question"
:underline="false" type="primary" href="https://fastbee.cn/doc/pages/rule_engine/"
target="_blank">
脚本使用Groovy引擎查看教程>>>
</el-link>
</span>
<el-button type="success" @click="handleValidate"> </el-button>
<el-button type="primary" @click="submitForm" v-hasPermi="['iot:script:edit']"
v-show="form.scriptId" :disabled="!isValidate">
</el-button>
<el-button type="primary" @click="submitForm" v-hasPermi="['iot:script:add']"
v-show="!form.scriptId" :disabled="!isValidate">
</el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</el-card>
<!-- 产品列表 -->
<productList ref="productList" @productEvent="getSelectProduct($event)"></productList>
</div>
</template>
<script>
import { listScript, getScript, delScript, addScript, updateScript, validateScript } from '@/api/iot/script';
import AceEditor from '@/views/components/editor/editor.vue';
import productList from './product-list';
export default {
name: 'Script',
dicts: ['rule_script_type', 'rule_script_language', 'rule_script_event', 'rule_script_action'],
components: {
AceEditor,
productList,
},
data() {
// 自定义验证规则
var checkScriptId = (rule, value, callback) => {
const regex = /^[a-zA-Z]+[0-9]*[a-zA-Z]*$/; // 定义只包含字母和数字,且字母开头的正则表达式
if (!regex.test(value)) {
return callback(new Error('脚本标识只能输入字母和数字,且字母开头'));
} else {
callback();
}
};
return {
// 脚本数据验证
isValidate: false,
// 脚本数据验证结果
validateMsg: '',
// 遮罩层
loading: true,
// 选中数组
scriptIds: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 规则引擎脚本表格数据
scriptList: [],
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
scriptPurpose: 1, // 只展示数据流(1=数据流2=触发器3=执行动作)
scriptId: null,
scriptName: null,
scriptData: null,
scriptType: null,
scriptLanguage: null,
enable: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
scriptId: [{ required: true, message: '脚本标识不能为空', trigger: 'blur' }],
productName: [{ required: true, message: '所属产品不能为空', trigger: 'blur' }],
scriptName: [{ required: true, message: '脚本名不能为空', trigger: 'blur' }],
scriptType: [{ required: true, message: '脚本类型不能为空', trigger: 'change' }],
scriptLanguage: [{ required: true, message: '脚本语言不能为空', trigger: 'change' }],
scriptEvent: [{ required: true, message: '', trigger: 'change' }],
scriptAction: [{ required: true, message: '', trigger: 'change' }],
scriptOrder: [{ required: true, message: '', trigger: 'change' }],
enable: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
},
};
},
created() {
this.getList();
},
methods: {
/** 查询规则引擎脚本列表 */
getList() {
this.loading = true;
listScript(this.queryParams).then((response) => {
this.scriptList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.validateMsg = '';
this.isValidate = false;
this.form = {
id: null,
applicationName: 'fastbee', // 后端配置和规则/脚本需要一致
scriptId: null,
productId: null,
productName: '',
scriptName: null,
scriptType: 'script',
remark: null,
scriptLanguage: 'groovy',
enable: 1,
scriptPurpose: 1, // 脚本用途(1=数据流2=触发器3=执行动作)
scriptOrder: 1, // 脚本执行顺序,数字越大越先执行
scriptAction: 1, // 脚本动作(1=消息重发2=消息通知3=Http推送4=Mqtt桥接5=数据库存储)
scriptEvent: 1, // 脚本事件(1=设备上报2=平台下发3=设备上线4=设备离线)
sceneId: 0,
scriptData: `import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.core.util.NumberUtil;
// 1. 获取主题和内容(必要)
String topic = msgContext.getTopic();
String payload = msgContext.getPayload();
// 2. 数据转换(自己处理)
println ("根据情况转换数据")
String NewTopic = topic;
String NewPayload = payload;
// 3. 返回新的数据(必要)
msgContext.setTopic(NewTopic);
msgContext.setPayload(NewPayload);`,
};
this.resetForm('form');
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm');
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.scriptIds = selection.map((item) => item.scriptId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = '编辑规则脚本';
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const scriptId = row.scriptId || this.scriptIds;
getScript(scriptId).then((response) => {
this.form = response.data;
this.open = true;
this.title = '修改规则引擎脚本';
});
},
/**选择产品 */
handleSelectProduct(data) {
// 刷新子组建
this.$refs.productList.queryParams.pageNum = 1;
this.$refs.productList.open = true;
this.$refs.productList.selectProductId = this.form.productId;
this.$refs.productList.getList();
},
getSelectProduct(data) {
this.form.productId = data.productId;
this.form.productName = data.productName;
},
/** 提交按钮 */
submitForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.form.scriptId != null) {
updateScript(this.form).then((response) => {
this.$modal.msgSuccess('修改成功');
this.open = false;
this.getList();
});
} else {
addScript(this.form).then((response) => {
this.$modal.msgSuccess('新增成功');
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const scriptIds = row.scriptId || this.scriptIds;
this.$modal
.confirm('是否确认删除规则引擎脚本编号为"' + scriptIds + '"的数据项?')
.then(function () {
return delScript(scriptIds);
})
.then(() => {
this.getList();
this.$modal.msgSuccess('删除成功');
})
.catch(() => { });
},
/** 验证按钮操作 */
handleValidate() {
this.validateMsg = '';
this.isValidate = false;
validateScript(this.form).then((response) => {
this.isValidate = response.data;
this.validateMsg = response.msg;
});
},
/** 编辑器单机事件 */
editClick() {
this.validateMsg = '';
this.isValidate = false;
},
/** 导出按钮操作 */
handleExport() {
this.download(
'iot/script/export',
{
...this.queryParams,
},
`script_${new Date().getTime()}.xlsx`
);
},
},
};
</script>