feat: 添加企业微信通知器

This commit is contained in:
椰子 2021-07-02 17:17:02 +08:00
parent a088d6440e
commit f69eeb502c
9 changed files with 496 additions and 2 deletions

View File

@ -8,8 +8,8 @@ package com.cicdi.notify;
public enum DefaultNotifyType implements NotifyType { public enum DefaultNotifyType implements NotifyType {
email("邮件"), email("邮件"),
sms("短信") sms("短信"),
; wechat("微信");
private final String name; private final String name;

44
notify-wechat/pom.xml Normal file
View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>notify</artifactId>
<groupId>com.cicdi</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>notify-wechat</artifactId>
<dependencies>
<dependency>
<groupId>com.cicdi</groupId>
<artifactId>notify-core</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.3.11.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>

View File

@ -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;
}
}

View File

@ -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<String> createFormInserter(BodyInserters.FormInserter<String> inserter, Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> context) {
if (StringUtils.isEmpty(toUser)) {
return toUser;
}
return toUser;
// return ExpressionUtils.analytical(toUser, context.getAllValues(), "spel");
}
public String createDepartmentIdList(Map<String, Object> context) {
if (StringUtils.isEmpty(toParty)) {
return toParty;
}
return toParty;
// return ExpressionUtils.analytical(toParty, context.getAllValues(), "spel");
}
public String createMessage(Map<String, Object> context) {
JSONObject json = new JSONObject();
json.put("content", message);
// json.put("content", ExpressionUtils.analytical(message, context.getAllValues(), "spel"));
return json.toJSONString();
}
}

View File

@ -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<String, Object> 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;
// }
}

View File

@ -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;
}
}

View File

@ -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<WechatMessageTemplate> {
private final AtomicReference<String> 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<String, Object> 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<HashMap> checkResult(Mono<HashMap> 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<ClientResponse> 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;
}
}

View File

@ -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<String, Object> 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<Template> notifier = notifierManager.getNotifier(DefaultNotifyType.wechat, "123");
notifier.send(templateManager.getTemplate(DefaultNotifyType.wechat, "124"), new HashMap<>());
}
}

View File

@ -12,6 +12,7 @@
<module>notify-core</module> <module>notify-core</module>
<module>notify-email</module> <module>notify-email</module>
<module>notify-sms</module> <module>notify-sms</module>
<module>notify-wechat</module>
</modules> </modules>
<properties> <properties>