diff --git a/notify-core/src/main/java/com/cicdi/notify/DefaultNotifyType.java b/notify-core/src/main/java/com/cicdi/notify/DefaultNotifyType.java
index 79132ee..a61a3dd 100644
--- a/notify-core/src/main/java/com/cicdi/notify/DefaultNotifyType.java
+++ b/notify-core/src/main/java/com/cicdi/notify/DefaultNotifyType.java
@@ -8,8 +8,8 @@ package com.cicdi.notify;
public enum DefaultNotifyType implements NotifyType {
email("邮件"),
- sms("短信")
- ;
+ sms("短信"),
+ wechat("微信");
private final String name;
diff --git a/notify-wechat/pom.xml b/notify-wechat/pom.xml
new file mode 100644
index 0000000..58036b5
--- /dev/null
+++ b/notify-wechat/pom.xml
@@ -0,0 +1,44 @@
+
+
+
+ notify
+ com.cicdi
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ notify-wechat
+
+
+ com.cicdi
+ notify-core
+ 1.0-SNAPSHOT
+ compile
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+ 2.3.11.RELEASE
+
+
+ com.alibaba
+ fastjson
+ 1.2.73
+ compile
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+
+ 8
+ 8
+
+
+
\ No newline at end of file
diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatCorpProperties.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatCorpProperties.java
new file mode 100644
index 0000000..701da87
--- /dev/null
+++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatCorpProperties.java
@@ -0,0 +1,24 @@
+package com.cicdi.notify.wechat;
+
+public class WechatCorpProperties {
+
+ private String corpId;
+
+ private String corpSecret;
+
+ public String getCorpId() {
+ return corpId;
+ }
+
+ public void setCorpId(String corpId) {
+ this.corpId = corpId;
+ }
+
+ public String getCorpSecret() {
+ return corpSecret;
+ }
+
+ public void setCorpSecret(String corpSecret) {
+ this.corpSecret = corpSecret;
+ }
+}
diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatMessageTemplate.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatMessageTemplate.java
new file mode 100644
index 0000000..f4f278a
--- /dev/null
+++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatMessageTemplate.java
@@ -0,0 +1,137 @@
+package com.cicdi.notify.wechat;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.cicdi.notify.template.Template;
+import org.springframework.util.StringUtils;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class WechatMessageTemplate implements Template {
+
+ /**
+ * 应用ID
+ */
+ private String agentId;
+
+ private String toUser;
+
+ private String toParty;
+
+ private String toTag;
+
+ private String message;
+
+ public String getAgentId() {
+ return agentId;
+ }
+
+ public void setAgentId(String agentId) {
+ this.agentId = agentId;
+ }
+
+ public String getToUser() {
+ return toUser;
+ }
+
+ public void setToUser(String toUser) {
+ this.toUser = toUser;
+ }
+
+ public String getToParty() {
+ return toParty;
+ }
+
+ public void setToParty(String toParty) {
+ this.toParty = toParty;
+ }
+
+ public String getToTag() {
+ return toTag;
+ }
+
+ public void setToTag(String toTag) {
+ this.toTag = toTag;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public BodyInserters.FormInserter createFormInserter(BodyInserters.FormInserter inserter, Map context) {
+ inserter.with("agentid", this.getAgentId())
+ .with("msgtype","text")
+ .with("text",this.createMessage(context));
+ if (StringUtils.hasText(toUser)) {
+ inserter.with("touser", this.createUserIdList(context));
+ }
+ if (StringUtils.hasText(toParty)) {
+ inserter.with("toparty", this.createDepartmentIdList(context));
+ }
+ return inserter;
+
+ }
+
+ public String createJsonRequest(Map context){
+ JSONObject json=new JSONObject();
+ json.put("agentid",getAgentId());
+ json.put("msgtype","text");
+ // TODO
+// json.put("text",Collections.singletonMap("content",ExpressionUtils.analytical(message, context.getAllValues(), "spel")));
+ json.put("text",Collections.singletonMap("content", message));
+
+ if (StringUtils.hasText(toUser)) {
+ json.put("touser", this.createUserIdList(context));
+ }
+ if (StringUtils.hasText(toParty)) {
+ json.put("toparty", this.createDepartmentIdList(context));
+ }
+
+ return json.toJSONString();
+ }
+
+
+ public UriComponentsBuilder createUriParameter(UriComponentsBuilder builder, Map context){
+ builder.queryParam("agentid", this.getAgentId())
+ .queryParam("msgtype","text")
+ .queryParam("text",this.createMessage(context));
+ if (StringUtils.hasText(toUser)) {
+ builder.queryParam("touser", this.createUserIdList(context));
+ }
+ if (StringUtils.hasText(toParty)) {
+ builder.queryParam("toparty", this.createDepartmentIdList(context));
+ }
+ return builder;
+ }
+
+ public String createUserIdList(Map context) {
+ if (StringUtils.isEmpty(toUser)) {
+ return toUser;
+ }
+ return toUser;
+// return ExpressionUtils.analytical(toUser, context.getAllValues(), "spel");
+ }
+
+ public String createDepartmentIdList(Map context) {
+ if (StringUtils.isEmpty(toParty)) {
+ return toParty;
+ }
+ return toParty;
+// return ExpressionUtils.analytical(toParty, context.getAllValues(), "spel");
+ }
+
+ public String createMessage(Map context) {
+ JSONObject json = new JSONObject();
+ json.put("content", message);
+// json.put("content", ExpressionUtils.analytical(message, context.getAllValues(), "spel"));
+ return json.toJSONString();
+ }
+
+}
diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatNotifierProvider.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatNotifierProvider.java
new file mode 100644
index 0000000..0abd4ad
--- /dev/null
+++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatNotifierProvider.java
@@ -0,0 +1,67 @@
+package com.cicdi.notify.wechat;
+
+import com.alibaba.fastjson.JSON;
+import com.cicdi.notify.*;
+import com.cicdi.notify.template.TemplateManager;
+import com.cicdi.notify.template.TemplateProperties;
+import com.cicdi.notify.template.TemplateProvider;
+
+import java.util.Map;
+
+public class WechatNotifierProvider implements NotifierProvider, TemplateProvider {
+
+ private final TemplateManager templateManager;
+
+ public WechatNotifierProvider(TemplateManager templateManager) {
+ this.templateManager = templateManager;
+ }
+
+// public static final DefaultConfigMetadata notifierConfig = new DefaultConfigMetadata("通知配置", "")
+// .add("corpId", "corpId", "", new StringType().expand(ConfigMetadataConstants.required.value(true)))
+// .add("corpSecret", "corpSecret", "", new StringType());
+//
+// public static final DefaultConfigMetadata templateConfig = new DefaultConfigMetadata("模版配置", "")
+// .add("agentId", "应用ID", "", new StringType().expand(ConfigMetadataConstants.required.value(true)))
+// .add("toUser", "收信人ID", "与部门ID不能同时为空", new StringType())
+// .add("toParty", "收信部门ID", "与收信人ID不能同时为空", new StringType())
+// .add("toTag", "按标签推送", "", new StringType())
+// .add("message", "内容", "最大不超过500字", new StringType().expand(ConfigMetadataConstants.maxLength.value(500L)));
+
+ @Override
+ public NotifyType getType() {
+ return DefaultNotifyType.wechat;
+ }
+
+ @Override
+ public Provider getProvider() {
+ return WechatProvider.corpMessage;
+ }
+
+ @Override
+ public WechatMessageTemplate createTemplate(TemplateProperties properties) {
+ return JSON.parseObject(properties.getTemplate(), WechatMessageTemplate.class);
+// return Mono.fromSupplier(() -> ValidatorUtils.tryValidate(JSON.parseObject(properties.getTemplate(), org.jetlinks.community.notify.wechat.WechatMessageTemplate.class)));
+ }
+
+ @Override
+ public WeixinCorpNotifier createNotifier(NotifierProperties properties) {
+// WechatCorpProperties wechatCorpProperties = FastBeanCopier.copy(properties.getConfiguration(), new WechatCorpProperties());
+ Map config = properties.getConfiguration();
+ WechatCorpProperties wechatCorpProperties = new WechatCorpProperties();
+ wechatCorpProperties.setCorpId((String) config.get("corpId"));
+ wechatCorpProperties.setCorpSecret((String) config.get("corpSecret"));
+ return new WeixinCorpNotifier(properties.getId(), wechatCorpProperties, templateManager);
+// return Mono.just(new WeixinCorpNotifier(properties.getId(), client, ValidatorUtils.tryValidate(wechatCorpProperties), templateManager));
+
+ }
+
+// @Override
+// public ConfigMetadata getNotifierConfigMetadata() {
+// return notifierConfig;
+// }
+//
+// @Override
+// public ConfigMetadata getTemplateConfigMetadata() {
+// return templateConfig;
+// }
+}
diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatProvider.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatProvider.java
new file mode 100644
index 0000000..217da65
--- /dev/null
+++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatProvider.java
@@ -0,0 +1,25 @@
+package com.cicdi.notify.wechat;
+
+import com.cicdi.notify.Provider;
+
+public enum WechatProvider implements Provider {
+ corpMessage("微信企业消息通知");
+
+ private final String name;
+
+ WechatProvider(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getId() {
+ return name();
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+
+}
diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WeixinCorpNotifier.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WeixinCorpNotifier.java
new file mode 100644
index 0000000..c2a1645
--- /dev/null
+++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/WeixinCorpNotifier.java
@@ -0,0 +1,124 @@
+package com.cicdi.notify.wechat;
+
+
+import com.cicdi.notify.AbstractNotifier;
+import com.cicdi.notify.DefaultNotifyType;
+import com.cicdi.notify.NotifyType;
+import com.cicdi.notify.Provider;
+import com.cicdi.notify.template.TemplateManager;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.util.UriComponentsBuilder;
+import reactor.core.publisher.Mono;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class WeixinCorpNotifier extends AbstractNotifier {
+
+ private final AtomicReference accessToken = new AtomicReference<>();
+
+ private long refreshTokenTime;
+
+ private final long tokenTimeOut = Duration.ofSeconds(7000).toMillis();
+
+ private static final String tokenApi = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
+
+ private static final String notify = "https://qyapi.weixin.qq.com/cgi-bin/message/send";
+
+ private final WechatCorpProperties properties;
+
+ private final String notifierId;
+
+ @Override
+ public String getNotifierId() {
+ return notifierId;
+ }
+
+
+ public WeixinCorpNotifier(String id, WechatCorpProperties properties, TemplateManager templateManager) {
+ super(templateManager);
+ this.properties = properties;
+ this.notifierId = id;
+ }
+
+ @Override
+ public NotifyType getType() {
+ return DefaultNotifyType.wechat;
+ }
+
+ @Override
+ public Provider getProvider() {
+ return WechatProvider.corpMessage;
+ }
+
+ @Override
+ public void send(WechatMessageTemplate template, Map context) {
+ String token1 = getToken();
+ WebClient.create().post()
+ .uri(UriComponentsBuilder.fromUriString(notify).queryParam("access_token", token1).toUriString())
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue(template.createJsonRequest(context)))
+ .exchange()
+ .flatMap(clientResponse -> clientResponse.bodyToMono(HashMap.class))
+ .as(this::checkResult)
+ .then();
+ }
+
+ private Mono checkResult(Mono msg) {
+ return msg.doOnNext(map -> {
+ String code = String.valueOf(map.get("errcode"));
+ if ("0".equals(code)) {
+// log.info("发送微信企业通知成功");
+ } else {
+// log.warn("发送微信企业通知失败:{}", map);
+// throw new BusinessException("发送微信企业通知失败:" + map.get("errmsg"), code);
+ }
+ });
+ }
+
+ private String getToken() {
+ if (System.currentTimeMillis() - refreshTokenTime > tokenTimeOut || accessToken.get() == null) {
+ return requestToken();
+ }
+ return accessToken.get();
+ }
+
+ private String requestToken() {
+ Mono responseMono = WebClient.create().get()
+ .uri(UriComponentsBuilder.fromUriString(tokenApi)
+ .queryParam("corpId", properties.getCorpId())
+ .queryParam("corpSecret", properties.getCorpSecret())
+ .build().toUri())
+ .exchange();
+
+ responseMono.flatMap(resp -> {
+ System.out.println(resp);
+ return resp.bodyToMono(HashMap.class);
+ })
+ .map(map -> {
+ if (map.containsKey("access_token")) {
+ return map.get("access_token");
+ }
+ // TODO 自定义异常
+// throw new BusinessException("获取Token失败:" + map.get("errmsg"), String.valueOf(map.get("errcode")));
+ return Mono.empty();
+ })
+ .cast(String.class)
+ .doOnNext(r -> {
+ refreshTokenTime = System.currentTimeMillis();
+ accessToken.set(r);
+ });
+ return accessToken.get();
+ }
+
+ @Override
+ public void close() {
+ accessToken.set(null);
+ refreshTokenTime = 0;
+ }
+}
diff --git a/notify-wechat/src/test/java/com/cicdi/notify/wechat/WechatTest.java b/notify-wechat/src/test/java/com/cicdi/notify/wechat/WechatTest.java
new file mode 100644
index 0000000..4a3843d
--- /dev/null
+++ b/notify-wechat/src/test/java/com/cicdi/notify/wechat/WechatTest.java
@@ -0,0 +1,72 @@
+package com.cicdi.notify.wechat;
+
+import com.alibaba.fastjson.JSONObject;
+import com.cicdi.notify.*;
+import com.cicdi.notify.template.AbstractTemplateManager;
+import com.cicdi.notify.template.Template;
+import com.cicdi.notify.template.TemplateManager;
+import com.cicdi.notify.template.TemplateProperties;
+import org.junit.Test;
+import reactor.core.publisher.Hooks;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author xueye
+ */
+public class WechatTest {
+ @Test
+ public void test() {
+ Hooks.onOperatorDebug();
+ // 通知器配置管理器
+ NotifyConfigManager notifyConfigManager = new NotifyConfigManager() {
+ @Override
+ public NotifierProperties getNotifyConfig(NotifyType notifyType, String configId) {
+ NotifierProperties notifierProperties = new NotifierProperties();
+ notifierProperties.setType(DefaultNotifyType.wechat.getId());
+ notifierProperties.setProvider(WechatProvider.corpMessage.getId());
+ notifierProperties.setId("12");
+
+ Map config = new HashMap<>();
+ config.put("corpId", "wwbd49ae2419a55a9f");
+ config.put("corpSecret", "TXDRQw_H8gpVKX0E01EmwMXJ4VooXmj65I-mDe0wQ1k");
+
+ notifierProperties.setConfiguration(config);
+
+ return notifierProperties;
+ }
+ };
+
+ // 模板管理器
+ TemplateManager templateManager = new AbstractTemplateManager() {
+ @Override
+ protected TemplateProperties getProperties(NotifyType type, String id) {
+ TemplateProperties templateProperties = new TemplateProperties();
+ templateProperties.setType(DefaultNotifyType.wechat.getId());
+ templateProperties.setProvider(WechatProvider.corpMessage.getId());
+
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("agentId", "3010084");
+ jsonObject.put("toUser", "XueYe");
+ jsonObject.put("toParty", "");
+ jsonObject.put("toTag", "");
+ jsonObject.put("message", "Hello");
+
+ templateProperties.setTemplate(jsonObject.toJSONString());
+
+ return templateProperties;
+ }
+ };
+
+ NotifierManager notifierManager = new DefaultNotifierManager(notifyConfigManager);
+
+ // register
+ WechatNotifierProvider provider = new WechatNotifierProvider(templateManager);
+ notifierManager.registerProvider(provider);
+ templateManager.register(provider);
+
+ Notifier notifier = notifierManager.getNotifier(DefaultNotifyType.wechat, "123");
+ notifier.send(templateManager.getTemplate(DefaultNotifyType.wechat, "124"), new HashMap<>());
+ }
+}
diff --git a/pom.xml b/pom.xml
index 3ca1eb2..44a1023 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,6 +12,7 @@
notify-core
notify-email
notify-sms
+ notify-wechat