Skip to content

ThingsBoard 自定义阿里云 SMS 规则节点 ​

正文与配图来自原稿。结构与 自定义华为云 SMS 规则节点 类似,本篇换成阿里云短信。
相关:如何发送告警邮件 · 配置发送邮件

1、概述 ​

一个物联网平台承载着很多设备的连接,当设备出现异常的时候,能够快速通知到运维管理员非常重要。ThingsBoard 提供了自定义配置邮箱,但是它对支持发送短信不是很友好,内置多是国外的 SMS 服务商。

在国内常见就是阿里、腾讯、华为、七牛等 SMS 服务商。如果需要发短信,肯定优先用国内厂商。但 ThingsBoard 里没看到哪里可以直接配这些,于是自定义规则节点来实现这个功能,同时把规则节点自定义也介绍了。本文首先以阿里 SMS 为例。

规则链中使用自定义阿里云短信节点示意

2、自定义规则节点 ​

概述 ​

我是直接在本地的 ThingsBoard 程序中增加的节点,也可以按照官方使用官方提供的那个程序来写,然后打成 jar 引入。

官方教程 ​

这个官方教程说得不清楚,另外一个类如何定义也没说:

https://thingsboard.io/docs/user-guide/contribution/rule-node-development/#step-7-rule-node-ui-customization-optional

创建包 ​

在本地程序创建了一个 external 包,意思是扩展 SMS。

创建 external 扩展包

然后在里面分别创建了国内常见的 SMS 服务商文件夹:

按厂商分子目录(含 ali)

定义接口 ​

定义了一个发送短信的接口 SendSms,其它每个厂商来实现这个接口:

java
package org.thingsboard.rule.engine.sms.external;

public interface SendSms {
    boolean sendMsg();
    ExternalSmsType getType();
}

定义枚举类 ​

定义了 SMS 厂商的名字:

java
package org.thingsboard.rule.engine.sms.external;

/**
 * 扩展外部的 sms 厂商
 */
public enum ExternalSmsType {
    ALI_SMS,
    TENCENT_SMS,
    HUAWEI_SMS,
    QINIU_SMS,
    YUNPIAN_SMS;
}

阿里云发送短信服务 ​

封装了阿里云发送短信依赖的方法,可以参考阿里云官方文档:

java
package org.thingsboard.rule.engine.sms.external.ali;

import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.sms.external.ExternalSmsType;
import org.thingsboard.rule.engine.sms.external.SendSms;

@Slf4j
public class AliSendSmsService implements SendSms {

    private String accessKeyId;
    private String accessKeySecret;
    private String phoneNumbers;
    private String signName;
    private String templateCode;
    private String templateParam;

    public AliSendSmsService(String accessKeyId, String accessKeySecret, String phoneNumbers,
                             String signName, String templateCode, String templateParam) {
        this.accessKeyId = accessKeyId;
        this.accessKeySecret = accessKeySecret;
        this.phoneNumbers = phoneNumbers;
        this.signName = signName;
        this.templateCode = templateCode;
        this.templateParam = templateParam;
    }

    @Override
    public boolean sendMsg() throws Exception {
        com.aliyun.dysmsapi20170525.Client client = this.createClient(accessKeyId, accessKeySecret);
        SendSmsRequest sendReq = new SendSmsRequest()
                .setPhoneNumbers(phoneNumbers)
                .setSignName(signName)
                .setTemplateCode(templateCode)
                .setTemplateParam(templateParam);
        SendSmsResponse sendResp = client.sendSms(sendReq);
        String code = sendResp.body.code;
        if (com.aliyun.teautil.Common.equalString(code, "OK")) {
            return true;
        }
        return false;
    }

    @Override
    public ExternalSmsType getType() {
        return ExternalSmsType.ALI_SMS;
    }

    /**
     * 使用 AK&SK 初始化账号 Client
     */
    public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret)
            throws Exception {
        Config config = new Config();
        config.accessKeyId = accessKeyId;
        config.accessKeySecret = accessKeySecret;
        return new com.aliyun.dysmsapi20170525.Client(config);
    }
}

补充(辅): 需在工程中引入阿里云 Dysmsapi SDK 依赖(版本按你工程与官方 README 选择)。SendSms 接口若未声明 throws Exception,请与 AliSendSmsService#sendMsg 签名对齐(接口加 throws Exception,或在实现里自行捕获)。

定义规则节点配置 ​

这个类的定义我在官方文档没看到有说明。这个类的作用就是接收规则节点的参数,同时需要实现 NodeConfiguration<AliSendSmsNodeConfiguration> 接口,并实现 defaultConfiguration 方法来进行参数封装。

注意: 下列默认字符串多为说明文案占位,上线前请改成控制台真实的 AccessKey、签名、模板 CODE、手机号与模板变量 JSON,切勿把真实密钥提交到公开仓库。

java
package org.thingsboard.rule.engine.sms.external.ali;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.NodeConfiguration;

@Data
@Slf4j
public class AliSendSmsNodeConfiguration implements NodeConfiguration<AliSendSmsNodeConfiguration> {

    // 文档见上文阿里云链接

    // 访问 key
    private String accessKeyId = "访问key";
    // 访问密钥
    private String accessKeySecret = "访问密钥";
    // 接收短信的手机号码。
    // 国内短信:+/+86/0086/86 或无任何前缀的 11 位手机号码,例如 1390000****。
    // 国际/港澳台消息:国际区号+号码,例如 852000012****。
    // 支持对多个手机号码发送短信,手机号码之间以半角逗号(,)分隔。上限为 1000 个手机号码。
    private String phoneNumbers = "接收短信的手机号码。手机号码格式:\n "
            + "国内短信:+/+86/0086/86或无任何前缀的11位手机号码,例如1390000**** \n"
            + "国际/港澳台消息:国际区号+号码,例如852000012**** \n"
            + "支持对多个手机号码发送短信,手机号码之间以半角逗号(,)分隔。上限为1000个手机号码。";
    // 短信签名名称。可在控制台「签名管理」获取。
    private String signName = "短信签名名称,您可以登录阿里云短信服务控制台,选择国内消息或国际/港澳台消息,在签名管理页面获取";
    // 短信模板 CODE。可在控制台「模板管理」查看。
    private String templateCode = "短信模板CODE,您可以登录短信服务控制台,选择国内消息或国际/港澳台消息,在模板管理页面查看模板";
    // 短信模板变量对应的实际值。示例:{"name":"张三","number":"1390000****"}
    private String templateParam = "短信模板变量对应的实际值。支持传入多个参数,示例:{\"name\":\"张三\",\"number\":\"1390000****\"}。";

    @Override
    public AliSendSmsNodeConfiguration defaultConfiguration() {
        AliSendSmsNodeConfiguration configuration = new AliSendSmsNodeConfiguration();
        configuration.setAccessKeyId(accessKeyId);
        configuration.setAccessKeySecret(accessKeySecret);
        configuration.setPhoneNumbers(phoneNumbers);
        configuration.setSignName(signName);
        configuration.setTemplateCode(templateCode);
        configuration.setTemplateParam(templateParam);
        return configuration;
    }
}

定义规则节点 ​

主要关注 @RuleNode 中的配置。这个节点 type 是 EXTERNAL,name 是 ali sms,然后就是 uiResources。这个 uiResources 指向一个静态文件,用来渲染节点的 js 文件。因为是自定义的,所以引入的是官方示例程序里的 static/rulenode/custom-nodes-config.js,并把这个文件拷贝到 ThingsBoard 的 resources 目录下。指定 icon 为 sms 即可。

java
package org.thingsboard.rule.engine.sms.external.ali;

import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.*;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;

@Slf4j
@RuleNode(
        type = ComponentType.EXTERNAL,
        name = "ali sms",
        relationTypes = {"True", "False"},
        configClazz = AliSendSmsNodeConfiguration.class,
        nodeDescription = "Use ali sms send msg.",
        nodeDetails = "If send msg success - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
        uiResources = {"static/rulenode/custom-nodes-config.js"},
        icon = "sms")
public class AliSendSmsNode implements TbNode {

    private AliSendSmsService aliSendSmsService;

    @Override
    public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
        AliSendSmsNodeConfiguration config =
                TbNodeUtils.convert(configuration, AliSendSmsNodeConfiguration.class);
        String accessKeyId = config.getAccessKeyId();
        String accessKeySecret = config.getAccessKeySecret();
        String phoneNumbers = config.getPhoneNumbers();
        String signName = config.getSignName();
        String templateCode = config.getTemplateCode();
        String templateParam = config.getTemplateParam();
        aliSendSmsService = new AliSendSmsService(accessKeyId, accessKeySecret,
                phoneNumbers, signName, templateCode, templateParam);
    }

    @Override
    public void onMsg(TbContext ctx, TbMsg msg) {
        try {
            ctx.tellNext(msg, aliSendSmsService.sendMsg() ? "True" : "False");
        } catch (Exception e) {
            ctx.tellFailure(msg, e);
        }
    }

    @Override
    public void destroy() {
    }
}

规则节点类与注解示意

3、如何使用 ​

第一步: 首先重启程序,然后打开规则链页面,可以看到自定义的节点已经出现在**外部(EXTERNAL)**分类下面。

规则链面板出现 ali sms 节点

第二步: 配置自定义的规则节点,按照阿里云官方配置指定的参数。需要配置的参数全部抽出来了:

节点配置面板

参数不解释,请看阿里云 Java 案例,说得很清楚。其它内容和上一节(华为云)讲解的类似,只要明白一种,其它就能够写出来。

关键字段对照:

配置项含义
accessKeyId / accessKeySecret阿里云 AK/SK
phoneNumbers接收号码(可多个,逗号分隔)
signName短信签名名称
templateCode短信模板 CODE
templateParam模板变量 JSON,如 {"name":"张三","number":"1390000****"}

成功时 SDK 返回 code 为 OK(与原稿判断一致)。

4、验证 ​

由于我使用的是测试,没有权限开通阿里云的 SMS(一般要企业才能开通),但是我调试过,规则节点都没有问题,就是在执行发送短信的时候提示没有开通服务,那些 key 都是无效的。如果你们公司开通了就可以配置进行测试。


补充(辅) ​

  • 与华为篇共用 SendSms / ExternalSmsType 时,注意 sendMsg 的异常签名统一。
  • Maven 需加入 dysmsapi20170525 等相关依赖,版本以阿里云当前文档为准。
  • Configuration 字段默认值会渲染到节点表单;生产环境应改成真实参数,勿提交密钥。
  • 改完代码需重新编译并重启;打 jar / 镜像见专栏前文。

相关文章 ​