diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WeixinCorpNotifier.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpNotifier.java similarity index 93% rename from notify-wechat/src/main/java/com/cicdi/notify/wechat/WeixinCorpNotifier.java rename to notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpNotifier.java index d2e3225..316ff6f 100644 --- a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WeixinCorpNotifier.java +++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpNotifier.java @@ -1,5 +1,4 @@ -package com.cicdi.notify.wechat; - +package com.cicdi.notify.wechat.corp; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -8,6 +7,7 @@ import com.cicdi.notify.DefaultNotifyType; import com.cicdi.notify.NotifyType; import com.cicdi.notify.Provider; import com.cicdi.notify.template.TemplateManager; +import com.cicdi.notify.wechat.WechatProvider; import org.apache.http.NameValuePair; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -25,7 +25,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -public class WeixinCorpNotifier extends AbstractNotifier { +public class WechatCorpNotifier extends AbstractNotifier { private String accessToken; @@ -41,18 +41,17 @@ public class WeixinCorpNotifier extends AbstractNotifier private final String notifierId; - @Override - public String getNotifierId() { - return notifierId; - } - - - public WeixinCorpNotifier(String id, WechatCorpProperties properties, TemplateManager templateManager) { + public WechatCorpNotifier(String id, WechatCorpProperties properties, TemplateManager templateManager) { super(templateManager); this.properties = properties; this.notifierId = id; } + @Override + public String getNotifierId() { + return notifierId; + } + @Override public NotifyType getType() { return DefaultNotifyType.wechat; @@ -64,7 +63,7 @@ public class WeixinCorpNotifier extends AbstractNotifier } @Override - public void send(WechatMessageTemplate template, Map context) { + public void send(WechatCorpTemplate template, Map context) { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpPost httpPost = new HttpPost(notifyApi + "?access_token=" + getToken()); httpPost.setHeader("Content-Type", "application/json;charset=utf8"); diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatNotifierProvider.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpNotifierProvider.java similarity index 71% rename from notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatNotifierProvider.java rename to notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpNotifierProvider.java index 0abd4ad..6d13eca 100644 --- a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatNotifierProvider.java +++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpNotifierProvider.java @@ -1,18 +1,20 @@ -package com.cicdi.notify.wechat; +package com.cicdi.notify.wechat.corp; import com.alibaba.fastjson.JSON; import com.cicdi.notify.*; +import com.cicdi.notify.template.Template; import com.cicdi.notify.template.TemplateManager; import com.cicdi.notify.template.TemplateProperties; import com.cicdi.notify.template.TemplateProvider; +import com.cicdi.notify.wechat.WechatProvider; import java.util.Map; -public class WechatNotifierProvider implements NotifierProvider, TemplateProvider { +public class WechatCorpNotifierProvider implements NotifierProvider, TemplateProvider { private final TemplateManager templateManager; - public WechatNotifierProvider(TemplateManager templateManager) { + public WechatCorpNotifierProvider(TemplateManager templateManager) { this.templateManager = templateManager; } @@ -38,21 +40,17 @@ public class WechatNotifierProvider implements NotifierProvider, TemplateProvide } @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))); + public Template createTemplate(TemplateProperties properties) { + return JSON.parseObject(properties.getTemplate(), WechatCorpTemplate.class); } @Override - public WeixinCorpNotifier createNotifier(NotifierProperties properties) { -// WechatCorpProperties wechatCorpProperties = FastBeanCopier.copy(properties.getConfiguration(), new WechatCorpProperties()); + public WechatCorpNotifier createNotifier(NotifierProperties properties) { 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)); - + return new WechatCorpNotifier(properties.getId(), wechatCorpProperties, templateManager); } // @Override diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatCorpProperties.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpProperties.java similarity index 91% rename from notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatCorpProperties.java rename to notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpProperties.java index 701da87..ec044df 100644 --- a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatCorpProperties.java +++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpProperties.java @@ -1,4 +1,4 @@ -package com.cicdi.notify.wechat; +package com.cicdi.notify.wechat.corp; public class WechatCorpProperties { diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatMessageTemplate.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpTemplate.java similarity index 81% rename from notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatMessageTemplate.java rename to notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpTemplate.java index daef2f9..359d6cb 100644 --- a/notify-wechat/src/main/java/com/cicdi/notify/wechat/WechatMessageTemplate.java +++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/corp/WechatCorpTemplate.java @@ -1,5 +1,4 @@ -package com.cicdi.notify.wechat; - +package com.cicdi.notify.wechat.corp; import com.alibaba.fastjson.JSONObject; import com.cicdi.notify.template.Template; @@ -8,7 +7,7 @@ import org.apache.commons.lang.StringUtils; import java.util.Collections; import java.util.Map; -public class WechatMessageTemplate implements Template { +public class WechatCorpTemplate implements Template { /** * 应用ID @@ -67,8 +66,6 @@ public class WechatMessageTemplate implements Template { 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.isNotBlank(toUser)) { @@ -86,7 +83,6 @@ public class WechatMessageTemplate implements Template { return toUser; } return toUser; -// return ExpressionUtils.analytical(toUser, context.getAllValues(), "spel"); } public String createDepartmentIdList(Map context) { @@ -94,13 +90,11 @@ public class WechatMessageTemplate implements Template { 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/subscription/WechatSubscriptionNotifier.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionNotifier.java new file mode 100644 index 0000000..67308ee --- /dev/null +++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionNotifier.java @@ -0,0 +1,150 @@ +package com.cicdi.notify.wechat.subscription; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.support.hsf.HSFJSONUtils; +import com.cicdi.notify.*; +import com.cicdi.notify.template.TemplateManager; +import com.cicdi.notify.wechat.WechatProvider; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 微信公众号消息通知器 + * + * @author xueye + */ +public class WechatSubscriptionNotifier extends AbstractNotifier { + + /** + * 通知器配置信息 + */ + private final NotifierProperties properties; + + /** + * 全局唯一接口调用凭证 + */ + private final AtomicReference accessToken = new AtomicReference<>(); + + /** + * 获取凭证的Api + */ + private static final String tokenApi = "https://api.weixin.qq.com/cgi-bin/token"; + + /** + * 发送消息通知的Api + */ + private static final String notifyApi = " https://api.weixin.qq.com/cgi-bin/message/template/send"; + + /** + * 凭证有效时间 + */ + private static final long tokenTimeOut = Duration.ofSeconds(7000).toMillis(); + + /** + * 凭据刷新时间 + *

一般接口对凭证的获取会有次数限制,记录上一次获取凭证的时间,每次发送通知的时候,配合凭证有效时间来判断是否重新获取

+ */ + private long refreshTokenTime; + + private final CloseableHttpClient httpClient; + + public WechatSubscriptionNotifier(NotifierProperties properties, TemplateManager templateManager) { + super(templateManager); + this.properties = properties; + this.httpClient = HttpClientBuilder.create().build(); + } + + @Override + public String getNotifierId() { + return properties.getId(); + } + + @Override + public NotifyType getType() { + return DefaultNotifyType.wechat; + } + + @Override + public Provider getProvider() { +// return WechatProvider.subscription; + return null; + } + + @Override + public void send(WechatSubscriptionTemplate template, Map context) { + try { + URI sendMessageURI = new URIBuilder(notifyApi) + .addParameter("access_token", getToken()) + .build(); + HttpPost httpPost = new HttpPost(sendMessageURI); + httpPost.setHeader("Content-Type", "application/json;charset=utf8"); + StringEntity requestBody = new StringEntity(template.createJsonRequest()); + httpPost.setEntity(requestBody); + CloseableHttpResponse response = httpClient.execute(httpPost); + JSONObject responseJson = JSON.parseObject(EntityUtils.toString(response.getEntity())); + if (!"0".equals(responseJson.get("errcode"))) { + throw new RuntimeException(responseJson.get("errmsg").toString()); + } + } catch (URISyntaxException | IOException e) { + e.printStackTrace(); + } + } + + @Override + public void close() { + accessToken.set(null); + refreshTokenTime = 0; + try { + httpClient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private String getToken() { + if (System.currentTimeMillis() - refreshTokenTime > tokenTimeOut || accessToken.get() == null) { + refreshToken(); + } + return accessToken.get(); + } + + /** + * 刷新接口访问凭证 + */ + private void refreshToken() { + WechatSubscriptionProperties config = new JSONObject(properties.getConfiguration()).toJavaObject(WechatSubscriptionProperties.class); + try { + URI getTokenURI = new URIBuilder(tokenApi) + .addParameter("grant_type", config.getGrantType()) + .addParameter("appid", config.getAppId()) + .addParameter("secret", config.getSecret()) + .build(); + HttpGet httpGet = new HttpGet(getTokenURI); + CloseableHttpResponse response = httpClient.execute(httpGet); + String responseString = EntityUtils.toString(response.getEntity()); + JSONObject responseJson = JSON.parseObject(responseString); + if (responseJson.containsKey("access_token")) { + accessToken.set(responseJson.get("access_token").toString()); + } else if (responseJson.containsKey("errcode")) { + throw new RuntimeException(responseJson.get("errmsg").toString()); + } + } catch (URISyntaxException | IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionNotifierProvider.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionNotifierProvider.java new file mode 100644 index 0000000..d8991a3 --- /dev/null +++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionNotifierProvider.java @@ -0,0 +1,35 @@ +package com.cicdi.notify.wechat.subscription; + +import com.alibaba.fastjson.JSON; +import com.cicdi.notify.*; +import com.cicdi.notify.template.Template; +import com.cicdi.notify.template.TemplateProperties; +import com.cicdi.notify.template.TemplateProvider; +import com.cicdi.notify.wechat.WechatProvider; + +/** + * 微信公众号通知器提供商 + * + * @author xueye + */ +public class WechatSubscriptionNotifierProvider implements NotifierProvider, TemplateProvider { + @Override + public NotifyType getType() { + return DefaultNotifyType.wechat; + } + + @Override + public Provider getProvider() { + return WechatProvider.corpMessage; + } + + @Override + public Template createTemplate(TemplateProperties properties) { + return JSON.parseObject(String.valueOf(properties), WechatSubscriptionTemplate.class); + } + + @Override + public Notifier createNotifier(NotifierProperties properties) { + return null; + } +} diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionProperties.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionProperties.java new file mode 100644 index 0000000..d25d8de --- /dev/null +++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionProperties.java @@ -0,0 +1,50 @@ +package com.cicdi.notify.wechat.subscription; + +import com.cicdi.notify.NotifierProperties; + +/** + * 微信公众号通知器配置信息 + * + * @author xueye + */ +public class WechatSubscriptionProperties extends NotifierProperties { + + /** + * 获取access_token填写client_credential + */ + private String grantType; + + /** + * 第三方用户唯一凭证 + */ + private String appId; + + /** + * 第三方用户唯一凭证密钥,即appsecret + */ + private String secret; + + public String getGrantType() { + return grantType; + } + + public void setGrantType(String grantType) { + this.grantType = grantType; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } +} \ No newline at end of file diff --git a/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionTemplate.java b/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionTemplate.java new file mode 100644 index 0000000..13693f3 --- /dev/null +++ b/notify-wechat/src/main/java/com/cicdi/notify/wechat/subscription/WechatSubscriptionTemplate.java @@ -0,0 +1,100 @@ +package com.cicdi.notify.wechat.subscription; + +import com.alibaba.fastjson.JSONObject; +import com.cicdi.notify.template.Template; + +import java.util.Map; + +/** + * 微信公众号消息模板 + * + * @author xueye + */ +public class WechatSubscriptionTemplate implements Template { + + /** + * 接收者openid + */ + private String toUser; + + /** + * 模板ID + */ + private String templateId; + + /** + * 模板跳转链接(海外帐号没有跳转能力) + */ + private String url; + + /** + * 跳小程序所需数据,不需跳小程序可不用传该数据 + *
    + *
  • appid:所需跳转到的小程序appid(该小程序appid必须与发模板消息的公众号是绑定关联关系,暂不支持小游戏)
  • + *
  • pagepath:所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar),要求该小程序已发布,暂不支持小游戏
  • + *
+ */ + private Map miniProgram; + + /** + * 模板数据 + *
    + *
  • keyword:模板数据名称
  • + *
      + *
    • value:模板数据值
    • + *
    • color:模板内容字体颜色,不填默认为黑色
    • + *
    + *
+ */ + private Map data; + + public String createJsonRequest() { + JSONObject json = new JSONObject(); + json.put("touser", toUser); + json.put("template_id", templateId); + json.put("url", url); + json.put("miniprogram", miniProgram); + json.put("data", data); + return json.toJSONString(); + } + + public String getToUser() { + return toUser; + } + + public void setToUser(String toUser) { + this.toUser = toUser; + } + + public String getTemplateId() { + return templateId; + } + + public void setTemplateId(String templateId) { + this.templateId = templateId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public Map getMiniProgram() { + return miniProgram; + } + + public void setMiniProgram(Map miniProgram) { + this.miniProgram = miniProgram; + } + + public Map getData() { + return data; + } + + public void setData(Map data) { + this.data = data; + } +} diff --git a/notify-wechat/src/test/java/com/cicdi/notify/wechat/WechatSubscriptionTest.java b/notify-wechat/src/test/java/com/cicdi/notify/wechat/WechatSubscriptionTest.java new file mode 100644 index 0000000..3221f4b --- /dev/null +++ b/notify-wechat/src/test/java/com/cicdi/notify/wechat/WechatSubscriptionTest.java @@ -0,0 +1,68 @@ +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 com.cicdi.notify.wechat.corp.WechatCorpNotifierProvider; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author xueye + */ +public class WechatSubscriptionTest { + @Test + public void test() { + // 通知器配置管理器 + NotifyConfigManager notifyConfigManager = (notifyType, 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 AbstractNotifierManager(notifyConfigManager){}; + + // register + WechatCorpNotifierProvider provider = new WechatCorpNotifierProvider(templateManager); + notifierManager.register(provider); + templateManager.register(provider); + + Notifier