From f69eeb502c641019a6f1a17280a28289f34d09e3 Mon Sep 17 00:00:00 2001 From: xueye Date: Fri, 2 Jul 2021 17:17:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BC=81=E4=B8=9A?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E9=80=9A=E7=9F=A5=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cicdi/notify/DefaultNotifyType.java | 4 +- notify-wechat/pom.xml | 44 ++++++ .../notify/wechat/WechatCorpProperties.java | 24 +++ .../notify/wechat/WechatMessageTemplate.java | 137 ++++++++++++++++++ .../notify/wechat/WechatNotifierProvider.java | 67 +++++++++ .../cicdi/notify/wechat/WechatProvider.java | 25 ++++ .../notify/wechat/WeixinCorpNotifier.java | 124 ++++++++++++++++ .../com/cicdi/notify/wechat/WechatTest.java | 72 +++++++++ pom.xml | 1 + 9 files changed, 496 insertions(+), 2 deletions(-) create mode 100644 notify-wechat/pom.xml create mode 100644 notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatCorpProperties.java create mode 100644 notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatMessageTemplate.java create mode 100644 notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatNotifierProvider.java create mode 100644 notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatProvider.java create mode 100644 notify-wechat/src/main/java/com/cicdi/notify/wechat/WeixinCorpNotifier.java create mode 100644 notify-wechat/src/test/java/com/cicdi/notify/wechat/WechatTest.java 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