diff --git a/jetlinks-components/notify-component/notify-sms/pom.xml b/jetlinks-components/notify-component/notify-sms/pom.xml index 02ffeaa9..f416b7c5 100644 --- a/jetlinks-components/notify-component/notify-sms/pom.xml +++ b/jetlinks-components/notify-component/notify-sms/pom.xml @@ -17,6 +17,12 @@ hsweb-easy-orm-rdb + + com.aliyun + aliyun-java-sdk-core + 4.5.2 + + org.hswebframework.web hsweb-starter diff --git a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/provider/PlainTextSmsTemplate.java b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/PlainTextSmsTemplate.java similarity index 96% rename from jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/provider/PlainTextSmsTemplate.java rename to jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/PlainTextSmsTemplate.java index bd78c1dd..9f9e20b1 100644 --- a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/provider/PlainTextSmsTemplate.java +++ b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/PlainTextSmsTemplate.java @@ -1,4 +1,4 @@ -package org.jetlinks.community.notify.sms.provider; +package org.jetlinks.community.notify.sms; import lombok.Getter; import lombok.Setter; diff --git a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/SmsProvider.java b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/SmsProvider.java new file mode 100644 index 00000000..41f89cea --- /dev/null +++ b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/SmsProvider.java @@ -0,0 +1,20 @@ +package org.jetlinks.community.notify.sms; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.jetlinks.community.notify.Provider; + +@Getter +@AllArgsConstructor +public enum SmsProvider implements Provider { + + aliyunSms("阿里云短信服务") + ; + private final String name; + + @Override + public String getId() { + return name(); + } + +} diff --git a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/provider/TestSmsProvider.java b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/TestSmsProvider.java similarity index 89% rename from jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/provider/TestSmsProvider.java rename to jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/TestSmsProvider.java index 48348e57..c51dffed 100644 --- a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/provider/TestSmsProvider.java +++ b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/TestSmsProvider.java @@ -1,10 +1,10 @@ -package org.jetlinks.community.notify.sms.provider; +package org.jetlinks.community.notify.sms; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; +import org.jetlinks.community.notify.*; import org.jetlinks.core.Values; import org.jetlinks.core.metadata.ConfigMetadata; -import org.jetlinks.community.notify.*; import org.jetlinks.community.notify.template.Template; import org.jetlinks.community.notify.template.TemplateManager; import org.jetlinks.community.notify.template.TemplateProperties; @@ -54,7 +54,7 @@ public class TestSmsProvider extends AbstractNotifier impl @Nonnull @Override public Mono send(@Nonnull PlainTextSmsTemplate template, @Nonnull Values context) { - return Mono.fromRunnable(() -> log.info("send sms [{}] message:{}", template.getSendTo(context.getAllValues()), template.getTextSms(context.getAllValues()))); + return Mono.fromRunnable(() -> log.info("send sms {} message:{}", template.getSendTo(context.getAllValues()), template.getTextSms(context.getAllValues()))); } @Nonnull @@ -71,7 +71,7 @@ public class TestSmsProvider extends AbstractNotifier impl @Override public String getNotifierId() { - return "test-sms-notify"; + return "test-sms-sender"; } @Override diff --git a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/aliyun/AliyunSmsNotifier.java b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/aliyun/AliyunSmsNotifier.java new file mode 100644 index 00000000..74da0174 --- /dev/null +++ b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/aliyun/AliyunSmsNotifier.java @@ -0,0 +1,115 @@ +package org.jetlinks.community.notify.sms.aliyun; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.aliyuncs.CommonRequest; +import com.aliyuncs.CommonResponse; +import com.aliyuncs.DefaultAcsClient; +import com.aliyuncs.IAcsClient; +import com.aliyuncs.http.MethodType; +import com.aliyuncs.profile.DefaultProfile; +import com.aliyuncs.profile.IClientProfile; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.hswebframework.web.exception.BusinessException; +import org.hswebframework.web.logger.ReactiveLogger; +import org.jetlinks.community.notify.*; +import org.jetlinks.community.notify.sms.SmsProvider; +import org.jetlinks.core.Values; +import org.jetlinks.community.notify.template.TemplateManager; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +import javax.annotation.Nonnull; +import java.util.Map; +import java.util.Objects; + +@Slf4j +public class AliyunSmsNotifier extends AbstractNotifier { + + private final IAcsClient client; + private final int connectTimeout = 1000; + private final int readTimeout = 5000; + + @Getter + private String notifierId; + + private String domain = "dysmsapi.aliyuncs.com"; + private String regionId = "cn-hangzhou"; + + public AliyunSmsNotifier(NotifierProperties profile, TemplateManager templateManager) { + super(templateManager); + Map config = profile.getConfiguration(); + DefaultProfile defaultProfile = DefaultProfile.getProfile( + this.regionId = (String) Objects.requireNonNull(config.get("regionId"), "regionId不能为空"), + (String) Objects.requireNonNull(config.get("accessKeyId"), "accessKeyId不能为空"), + (String) Objects.requireNonNull(config.get("secret"), "secret不能为空") + ); + this.client = new DefaultAcsClient(defaultProfile); + this.domain = (String) config.getOrDefault("domain", domain); + this.notifierId = profile.getId(); + } + + public AliyunSmsNotifier(IClientProfile profile, TemplateManager templateManager) { + this(new DefaultAcsClient(profile), templateManager); + } + + public AliyunSmsNotifier(IAcsClient client, TemplateManager templateManager) { + super(templateManager); + this.client = client; + } + + @Override + @Nonnull + public NotifyType getType() { + return DefaultNotifyType.sms; + } + + @Nonnull + @Override + public Provider getProvider() { + return SmsProvider.aliyunSms; + } + + @Override + @Nonnull + public Mono send(@Nonnull AliyunSmsTemplate template, @Nonnull Values context) { + + return Mono.defer(() -> { + try { + CommonRequest request = new CommonRequest(); + request.setSysMethod(MethodType.POST); + request.setSysDomain(domain); + request.setSysVersion("2017-05-25"); + request.setSysAction("SendSms"); + request.setSysConnectTimeout(connectTimeout); + request.setSysReadTimeout(readTimeout); + request.putQueryParameter("RegionId", regionId); + request.putQueryParameter("PhoneNumbers", template.getPhoneNumber()); + request.putQueryParameter("SignName", template.getSignName()); + request.putQueryParameter("TemplateCode", template.getCode()); + request.putQueryParameter("TemplateParam", template.createTtsParam(context.getAllValues())); + + CommonResponse response = client.getCommonResponse(request); + + log.info("发送短信通知完成 {}:{}", response.getHttpResponse().getStatus(), response.getData()); + + JSONObject json = JSON.parseObject(response.getData()); + if (!"ok".equalsIgnoreCase(json.getString("Code"))) { + return Mono.error(new BusinessException(json.getString("Message"), json.getString("Code"))); + } + } catch (Exception e) { + return Mono.error(e); + } + return Mono.empty(); + }).doOnEach(ReactiveLogger.onError(err -> { + log.info("发送短信通知失败", err); + })).subscribeOn(Schedulers.elastic()); + } + + @Override + @Nonnull + public Mono close() { + return Mono.fromRunnable(client::shutdown); + } +} diff --git a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/aliyun/AliyunSmsNotifierProvider.java b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/aliyun/AliyunSmsNotifierProvider.java new file mode 100644 index 00000000..6dea3459 --- /dev/null +++ b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/aliyun/AliyunSmsNotifierProvider.java @@ -0,0 +1,78 @@ +package org.jetlinks.community.notify.sms.aliyun; + +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hswebframework.web.validator.ValidatorUtils; +import org.jetlinks.community.notify.*; +import org.jetlinks.community.notify.sms.SmsProvider; +import org.jetlinks.core.metadata.ConfigMetadata; +import org.jetlinks.core.metadata.DefaultConfigMetadata; +import org.jetlinks.core.metadata.types.StringType; +import org.jetlinks.community.notify.template.TemplateManager; +import org.jetlinks.community.notify.template.TemplateProperties; +import org.jetlinks.community.notify.template.TemplateProvider; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +import javax.annotation.Nonnull; + +/** + * 阿里云短信通知服务 + * + * + * @author zhouhao + * @since 1.3 + */ +@Component +@Slf4j +@AllArgsConstructor +public class AliyunSmsNotifierProvider implements NotifierProvider, TemplateProvider { + + private final TemplateManager templateManager; + + @Nonnull + @Override + public Provider getProvider() { + return SmsProvider.aliyunSms; + } + + public static final DefaultConfigMetadata templateConfig = new DefaultConfigMetadata("阿里云短信模版", + "https://help.aliyun.com/document_detail/108086.html") + .add("signName", "签名", "", new StringType()) + .add("code", "模版编码", "", new StringType()) + .add("phoneNumber", "收信人", "", new StringType()); + + public static final DefaultConfigMetadata notifierConfig = new DefaultConfigMetadata("阿里云API配置" + ,"https://help.aliyun.com/document_detail/101300.html") + .add("regionId", "regionId", "regionId", new StringType()) + .add("accessKeyId", "accessKeyId", "", new StringType()) + .add("secret", "secret", "", new StringType()); + + @Override + public ConfigMetadata getTemplateConfigMetadata() { + return templateConfig; + } + + @Override + public ConfigMetadata getNotifierConfigMetadata() { + return notifierConfig; + } + + @Override + public Mono createTemplate(TemplateProperties properties) { + return Mono.fromCallable(() -> ValidatorUtils.tryValidate(JSON.parseObject(properties.getTemplate(), AliyunSmsTemplate.class))); + } + + @Nonnull + @Override + public NotifyType getType() { + return DefaultNotifyType.sms; + } + + @Nonnull + @Override + public Mono createNotifier(@Nonnull NotifierProperties properties) { + return Mono.fromSupplier(() -> new AliyunSmsNotifier(properties, templateManager)); + } +} diff --git a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/aliyun/AliyunSmsTemplate.java b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/aliyun/AliyunSmsTemplate.java new file mode 100644 index 00000000..8c3e0646 --- /dev/null +++ b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/aliyun/AliyunSmsTemplate.java @@ -0,0 +1,37 @@ +package org.jetlinks.community.notify.sms.aliyun; + +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; +import org.jetlinks.community.notify.template.Template; + +import javax.validation.constraints.NotBlank; +import java.util.Map; + +/** + * 阿里云短信模版 + * + * @since 1.3 + */ +@Getter +@Setter +public class AliyunSmsTemplate implements Template { + + //签名名称 + @NotBlank(message = "[signName]不能为空") + private String signName; + + //模版编码 + @NotBlank(message = "[code]不能为空") + private String code; + + @NotBlank(message = "[phoneNumber]不能为空") + private String phoneNumber; + + private Map param; + + public String createTtsParam(Map ctx) { + + return JSON.toJSONString(ctx); + } +} diff --git a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/provider/Hy2046SmsSenderProvider.java b/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/provider/Hy2046SmsSenderProvider.java deleted file mode 100644 index a468c3e7..00000000 --- a/jetlinks-components/notify-component/notify-sms/src/main/java/org/jetlinks/community/notify/sms/provider/Hy2046SmsSenderProvider.java +++ /dev/null @@ -1,172 +0,0 @@ -package org.jetlinks.community.notify.sms.provider; - -import com.alibaba.fastjson.JSON; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.digest.DigestUtils; -import org.jetlinks.core.Values; -import org.jetlinks.core.metadata.ConfigMetadata; -import org.jetlinks.core.metadata.DefaultConfigMetadata; -import org.jetlinks.core.metadata.types.PasswordType; -import org.jetlinks.core.metadata.types.StringType; -import org.jetlinks.community.notify.*; -import org.jetlinks.community.notify.template.Template; -import org.jetlinks.community.notify.template.TemplateManager; -import org.jetlinks.community.notify.template.TemplateProperties; -import org.jetlinks.community.notify.template.TemplateProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Profile; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Mono; - -import javax.annotation.Nonnull; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Map; - -@Component -@Slf4j -@Profile({"dev","test"}) -public class Hy2046SmsSenderProvider implements NotifierProvider, TemplateProvider, Provider { - - - private WebClient webClient = WebClient.builder() - .baseUrl("http://sms10692.com/v2sms.aspx") - .build(); - - @Autowired - private TemplateManager templateManager; - - @Nonnull - @Override - public NotifyType getType() { - return DefaultNotifyType.sms; - } - - @Nonnull - @Override - public Provider getProvider() { - return this; - } - - static DefaultConfigMetadata notifierConfig = new DefaultConfigMetadata("宏衍2046短信配置", "") - .add("userId", "userId", "用户ID", new StringType()) - .add("username", "用户名", "用户名", new StringType()) - .add("password", "密码", "密码", new PasswordType()); - - @Override - public ConfigMetadata getNotifierConfigMetadata() { - return notifierConfig; - } - - @Override - public ConfigMetadata getTemplateConfigMetadata() { - return PlainTextSmsTemplate.templateConfig; - } - - @Override - public Mono createTemplate(TemplateProperties properties) { - return Mono.fromSupplier(() -> JSON.parseObject(properties.getTemplate(), PlainTextSmsTemplate.class)); - } - - @Nonnull - @Override - public Mono createNotifier(@Nonnull NotifierProperties properties) { - return Mono.defer(() -> { - String userId = (String) properties.getConfigOrNull("userId"); - String username = (String) properties.getConfigOrNull("username"); - String password = (String) properties.getConfigOrNull("password"); - Assert.hasText(userId, "短信配置错误,缺少userId"); - Assert.hasText(username, "短信配置错误,缺少username"); - Assert.hasText(password, "短信配置错误,缺少password"); - return Mono.just(new Hy2046SmsSender(properties.getId(),userId, username, password)); - }); - } - - @Override - public String getId() { - return "hy2046"; - } - - @Override - public String getName() { - return "宏衍2046"; - } - - class Hy2046SmsSender extends AbstractNotifier { - - String userId; - String username; - String password; - @Getter - private final String notifierId; - - public Hy2046SmsSender(String id,String userId, String username, String password) { - super(templateManager); - this.userId = userId; - this.notifierId = id; - this.username = username; - this.password = password; - } - - @Nonnull - @Override - public NotifyType getType() { - return DefaultNotifyType.sms; - } - - @Nonnull - @Override - public Provider getProvider() { - return Hy2046SmsSenderProvider.this; - } - - @Nonnull - @Override - public Mono close() { - return Mono.empty(); - } - - - @Nonnull - @Override - public Mono send(@Nonnull PlainTextSmsTemplate template, @Nonnull Values context) { - return Mono.defer(() -> { - String ts = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()); - String sign = DigestUtils.md5Hex(username.concat(password).concat(ts)); - String[] sendTo = template.getSendTo(context.getAllValues()); - - String mobile = String.join(",", sendTo); - MultiValueMap formData = new LinkedMultiValueMap<>(); - formData.add("userid", userId); - formData.add("timestamp", ts); - formData.add("sign", sign); - formData.add("mobile", mobile); - formData.add("content", template.getTextSms(context.getAllValues())); - formData.add("action", "send"); - formData.add("rt", "json"); - - return webClient.post() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .body(BodyInserters.fromFormData(formData)) - .retrieve() - .bodyToMono(Map.class) - .map(map -> { - if (Integer.valueOf(sendTo.length).equals(map.get("SuccessCounts"))) { - return true; - } - throw new RuntimeException("发送短信失败:" + map.get("Message")); - }); - - }).then(); - } - - } -} -