From a73f5c6d11b2680d97fbdd7fa96db2217b33f43f Mon Sep 17 00:00:00 2001 From: zhangyang Date: Mon, 23 Oct 2023 10:20:04 +0800 Subject: [PATCH] =?UTF-8?q?sms4j-oa-plugin=E6=94=AF=E6=8C=81=E9=92=89?= =?UTF-8?q?=E9=92=89=E3=80=81=E9=A3=9E=E4=B9=A6=E3=80=81=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E7=9A=84webhook=E6=99=AE=E9=80=9A=E4=BF=A1=E6=81=AF=E5=8F=91?= =?UTF-8?q?=E9=80=81---=E6=94=AF=E6=8C=81yaml=E9=85=8D=E7=BD=AE=E5=92=8Cja?= =?UTF-8?q?va=E4=BB=A3=E7=A0=81=E9=85=8D=E7=BD=AE=E4=B8=A4=E7=A7=8D?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sms4j-oa-plugin/pom.xml | 13 ++ .../java/org/dromara/oa/api/OaSender.java | 14 ++ sms4j-oa-plugin/sms4j-oa-comm/pom.xml | 5 + .../org/dromara/oa/comm/config/OaConfig.java | 20 --- .../oa/comm/config/OaSupplierConfig.java | 23 +++ .../dromara/oa/comm/content/OaContent.java | 26 ++++ .../org/dromara/oa/comm/entity/Request.java | 10 +- .../org/dromara/oa/comm/entity/Response.java | 16 +- .../dromara/oa/comm/enums/MessageType.java | 1 + .../dromara/oa/comm/errors/OaException.java | 2 +- sms4j-oa-plugin/sms4j-oa-core/pom.xml | 25 +++- .../core/byteTalk/config/ByteTalkConfig.java | 18 +++ .../core/byteTalk/config/ByteTalkFactory.java | 40 +++++ .../core/byteTalk/service/ByteTalkOaImpl.java | 70 +++++++++ .../core/byteTalk/utils/ByteTalkBuilder.java | 62 ++++++++ .../oa/core/config/OaBlendsInitializer.java | 69 +++++++++ .../oa/core/config/OaSupplierConfig.java | 31 ++++ .../core/dingTalk/config/DingTalkConfig.java | 22 +++ .../core/dingTalk/config/DingTalkFactory.java | 40 +++++ .../core/dingTalk/service/DingTalkOaImpl.java | 76 ++++++++++ .../core/dingTalk/utils/DingTalkBuilder.java | 70 +++++++++ .../dromara/oa/core/factory/OaFactory.java | 38 ----- .../oa/core/provider/config/OaBaseConfig.java | 26 ++++ .../factory/AbstractProviderFactory.java | 33 +++++ .../provider/factory/BaseProviderFactory.java | 30 ++++ .../oa/core/provider/factory/OaFactory.java | 53 +++++++ .../factory/ProviderFactoryHolder.java | 43 ++++++ .../provider/service/AbstractOaBlend.java | 27 ++++ .../org/dromara/oa/core/service/OaBuild.java | 22 --- .../dromara/oa/core/service/SenderImpl.java | 120 --------------- .../oa/core/support/AbstractHttpClient.java | 2 +- .../oa/core/support/HttpClientImpl.java | 3 +- .../oa/core/weTalk/config/WeTalkConfig.java | 18 +++ .../oa/core/weTalk/config/WeTalkFactory.java | 40 +++++ .../oa/core/weTalk/service/WeTalkOaImpl.java | 68 +++++++++ .../oa/core/weTalk/utils/WeTalkBuilder.java | 56 +++++++ .../main/resources/META-INF/spring.factories | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../src/main/resources/application.properties | 1 + .../src/main/resources/application.yml | 16 +- .../org/dromara/sms4j/example/SmsOaTest.java | 139 ++++++++++++++++-- 41 files changed, 1159 insertions(+), 232 deletions(-) delete mode 100644 sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/config/OaConfig.java create mode 100644 sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/config/OaSupplierConfig.java create mode 100644 sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/content/OaContent.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/config/ByteTalkConfig.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/config/ByteTalkFactory.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/service/ByteTalkOaImpl.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/utils/ByteTalkBuilder.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/config/OaBlendsInitializer.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/config/OaSupplierConfig.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/config/DingTalkConfig.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/config/DingTalkFactory.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/service/DingTalkOaImpl.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/utils/DingTalkBuilder.java delete mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/factory/OaFactory.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/config/OaBaseConfig.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/AbstractProviderFactory.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/BaseProviderFactory.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/OaFactory.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/ProviderFactoryHolder.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/service/AbstractOaBlend.java delete mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/service/OaBuild.java delete mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/service/SenderImpl.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/config/WeTalkConfig.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/config/WeTalkFactory.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/service/WeTalkOaImpl.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/utils/WeTalkBuilder.java create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/resources/META-INF/spring.factories create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 sms4j-oa-plugin/sms4j-oa-core/src/main/resources/application.properties diff --git a/sms4j-oa-plugin/pom.xml b/sms4j-oa-plugin/pom.xml index 2a25be5c..3999ddea 100644 --- a/sms4j-oa-plugin/pom.xml +++ b/sms4j-oa-plugin/pom.xml @@ -65,6 +65,19 @@ + + + org.springframework.boot + spring-boot-configuration-processor + ${spring.boot.version} + true + + + + org.springframework.boot + spring-boot-starter + ${spring.boot.version} + diff --git a/sms4j-oa-plugin/sms4j-oa-api/src/main/java/org/dromara/oa/api/OaSender.java b/sms4j-oa-plugin/sms4j-oa-api/src/main/java/org/dromara/oa/api/OaSender.java index 08e4b1b3..8b079c36 100644 --- a/sms4j-oa-plugin/sms4j-oa-api/src/main/java/org/dromara/oa/api/OaSender.java +++ b/sms4j-oa-plugin/sms4j-oa-api/src/main/java/org/dromara/oa/api/OaSender.java @@ -5,5 +5,19 @@ import org.dromara.oa.comm.entity.Response; import org.dromara.oa.comm.enums.MessageType; public interface OaSender { + + /** + * 获取通知webhook实例唯一标识 + */ + String getConfigId(); + + /** + * 获取供应商标识 + */ + String getSupplier(); + + /** + * 发送消息 + */ Response sender(Request request, MessageType messageType); } diff --git a/sms4j-oa-plugin/sms4j-oa-comm/pom.xml b/sms4j-oa-plugin/sms4j-oa-comm/pom.xml index cec4cbbf..41002a0a 100644 --- a/sms4j-oa-plugin/sms4j-oa-comm/pom.xml +++ b/sms4j-oa-plugin/sms4j-oa-comm/pom.xml @@ -18,6 +18,11 @@ + + + + + cn.hutool hutool-cron diff --git a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/config/OaConfig.java b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/config/OaConfig.java deleted file mode 100644 index 663d64fc..00000000 --- a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/config/OaConfig.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.dromara.oa.comm.config; - -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; - -@Builder -@ToString -@Getter -@EqualsAndHashCode -public class OaConfig { - - private String OaType; - - private String tokenId; - - private String sign; -} - diff --git a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/config/OaSupplierConfig.java b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/config/OaSupplierConfig.java new file mode 100644 index 00000000..215ae747 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/config/OaSupplierConfig.java @@ -0,0 +1,23 @@ +package org.dromara.oa.comm.config; + +/** + * @author dongfeng + * @date 2023-10-19 13:36 + */ +public interface OaSupplierConfig { + + /** + * 获取配置标识名(唯一) + */ + String getConfigId(); + + /** + * 获取供应商 + */ + String getSupplier(); + + /** + * 获取是否使用 + */ + Boolean getIsEnable(); +} diff --git a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/content/OaContent.java b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/content/OaContent.java new file mode 100644 index 00000000..ad24c74e --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/content/OaContent.java @@ -0,0 +1,26 @@ +package org.dromara.oa.comm.content; + +/** + * @author dongfeng + * @date 2023-10-22 13:50 + */ +public class OaContent { + /** + * 供应商配置键名 + */ + public static final String SUPPLIER_KEY = "supplier"; + + /** + * 钉钉 + */ + public static final String DINGTALK = "dingding"; + /** + * 飞书 + */ + public static final String BYTETALK = "feishu"; + /** + * 微信 + */ + public static final String WETALK = "wetalk"; + +} diff --git a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/entity/Request.java b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/entity/Request.java index 8964ea34..a1e6ee95 100644 --- a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/entity/Request.java +++ b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/entity/Request.java @@ -13,9 +13,15 @@ public class Request { // 消息内容 private String content; - private List phones; + private List phoneList; + + private List userIdList; + + private List userNamesList; + + private Boolean isNoticeAll = false; // oa类型 - private String oaType; + private String oaType; } diff --git a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/entity/Response.java b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/entity/Response.java index 15b038f6..66ef8db7 100644 --- a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/entity/Response.java +++ b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/entity/Response.java @@ -1,19 +1,23 @@ package org.dromara.oa.comm.entity; +import lombok.AllArgsConstructor; import lombok.Data; @Data +@AllArgsConstructor public class Response { /** - * 响应码 + * 是否成功 */ - private String code; + private boolean success; + /** - * 响应消息 + * 厂商原返回体 */ - private String message; + private Object data; + /** - * 响应数据 + * 配置标识名 如未配置取对应渠道名例如 */ - private String data; + private String oaConfigId; } diff --git a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/enums/MessageType.java b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/enums/MessageType.java index fd106a01..49dffc47 100644 --- a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/enums/MessageType.java +++ b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/enums/MessageType.java @@ -13,6 +13,7 @@ public enum MessageType { MessageType(String name) { this.name = name; } + private final String name; public String getName() { diff --git a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/errors/OaException.java b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/errors/OaException.java index 5ec375a8..d3a19e49 100644 --- a/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/errors/OaException.java +++ b/sms4j-oa-plugin/sms4j-oa-comm/src/main/java/org/dromara/oa/comm/errors/OaException.java @@ -1,6 +1,6 @@ package org.dromara.oa.comm.errors; -public class OaException extends RuntimeException{ +public class OaException extends RuntimeException { public OaException() { super(); diff --git a/sms4j-oa-plugin/sms4j-oa-core/pom.xml b/sms4j-oa-plugin/sms4j-oa-core/pom.xml index a694dc26..d29874d6 100644 --- a/sms4j-oa-plugin/sms4j-oa-core/pom.xml +++ b/sms4j-oa-plugin/sms4j-oa-core/pom.xml @@ -22,7 +22,10 @@ org.dromara.sms4j sms4j-oa-api - + + org.slf4j + slf4j-api + com.sun.activation javax.activation @@ -44,9 +47,23 @@ - - - + + + + + + + + org.springframework.boot + spring-boot-configuration-processor + ${spring.boot.version} + true + + + + org.springframework.boot + spring-boot-starter + diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/config/ByteTalkConfig.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/config/ByteTalkConfig.java new file mode 100644 index 00000000..7208bea8 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/config/ByteTalkConfig.java @@ -0,0 +1,18 @@ +package org.dromara.oa.core.byteTalk.config; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.oa.comm.enums.OaType; +import org.dromara.oa.core.provider.config.OaBaseConfig; + +@Data +@EqualsAndHashCode(callSuper = true) +public class ByteTalkConfig extends OaBaseConfig { + + private final String requestUrl = OaType.BYTETALK.getUrl(); + + @Override + public String getSupplier() { + return OaType.BYTETALK.getType(); + } +} \ No newline at end of file diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/config/ByteTalkFactory.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/config/ByteTalkFactory.java new file mode 100644 index 00000000..a92a1d99 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/config/ByteTalkFactory.java @@ -0,0 +1,40 @@ +package org.dromara.oa.core.byteTalk.config; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.oa.comm.content.OaContent; +import org.dromara.oa.core.byteTalk.service.ByteTalkOaImpl; +import org.dromara.oa.core.provider.factory.AbstractProviderFactory; + +/** + * @author dongfeng + * @description 飞书通知对象建造 + * @date 2023-10-22 21:00 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ByteTalkFactory extends AbstractProviderFactory { + private static final ByteTalkFactory INSTANCE = new ByteTalkFactory(); + + /** + * 建造一个飞书通知对象实现 + */ + @Override + public ByteTalkOaImpl createSmsOa(ByteTalkConfig byteTalkConfig) { + return new ByteTalkOaImpl(byteTalkConfig); + } + + @Override + public String getSupplier() { + return OaContent.BYTETALK; + } + + /** + * 获取建造者实例 + * + * @return 建造者实例 + */ + public static ByteTalkFactory instance() { + return INSTANCE; + } + +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/service/ByteTalkOaImpl.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/service/ByteTalkOaImpl.java new file mode 100644 index 00000000..1da5b306 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/service/ByteTalkOaImpl.java @@ -0,0 +1,70 @@ +package org.dromara.oa.core.byteTalk.service; + +import cn.hutool.json.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.dromara.oa.comm.entity.Request; +import org.dromara.oa.comm.entity.Response; +import org.dromara.oa.comm.enums.MessageType; +import org.dromara.oa.comm.enums.OaType; +import org.dromara.oa.comm.errors.OaException; +import org.dromara.oa.core.byteTalk.config.ByteTalkConfig; +import org.dromara.oa.core.byteTalk.utils.ByteTalkBuilder; +import org.dromara.oa.core.provider.service.AbstractOaBlend; +import org.dromara.oa.core.support.HttpClientImpl; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static org.dromara.oa.comm.enums.OaType.BYTETALK; + +/** + * @author dongfeng + * @date 2023-10-22 21:01 + */ +@Slf4j +public class ByteTalkOaImpl extends AbstractOaBlend { + + private HttpClientImpl httpClient = new HttpClientImpl(); + + public ByteTalkOaImpl(ByteTalkConfig config) { + super(config); + } + + @Override + public String getSupplier() { + return OaType.BYTETALK.getType(); + } + + @Override + public Response sender(Request request, MessageType messageType) { + + if (Objects.isNull(request.getContent())) { + throw new OaException("消息体content不能为空"); + } + StringBuilder webhook = new StringBuilder(); + JSONObject message = null; + ByteTalkConfig config = getConfig(); + webhook.append(BYTETALK.getUrl()); + webhook.append(config.getTokenId()); + long now = System.currentTimeMillis() / 1000; + String sign = ByteTalkBuilder.byteTalkSign(config.getSign(), now); + message = ByteTalkBuilder.createByteTalkMessage(request, messageType, sign, now); + String post; + try { + post = httpClient.post(webhook, getHeaders(), message); + log.info("请求返回结果:" + post); + } catch (Exception e) { + log.warn("请求失败问题:" + e.getMessage()); + throw new OaException(e.getMessage()); + } + // 后续解析响应体提取errorCode判断是否成功 + return new Response(true, post, config.getConfigId()); + } + + public static Map getHeaders() { + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + return headers; + } +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/utils/ByteTalkBuilder.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/utils/ByteTalkBuilder.java new file mode 100644 index 00000000..d29d7361 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/byteTalk/utils/ByteTalkBuilder.java @@ -0,0 +1,62 @@ +package org.dromara.oa.core.byteTalk.utils; + +import cn.hutool.json.JSONObject; +import org.dromara.oa.comm.entity.Request; +import org.dromara.oa.comm.enums.MessageType; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.List; + +import static org.dromara.oa.comm.enums.MessageType.TEXT; + +/** + * @author dongfeng + * @description 飞书通知签名和信息构建 + * @date 2023-10-19 13:07 + */ +public class ByteTalkBuilder { + + public static String byteTalkSign(String secret, Long timestamp) { + //把timestamp+"\n"+密钥当做签名字符串 + String stringToSign = timestamp + "\n" + secret; + //使用HmacSHA256算法计算签名 + Mac mac = null; + try { + mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new RuntimeException(e); + } + byte[] signData = mac.doFinal(new byte[]{}); + return new String(Base64.getEncoder().encode(signData)); + } + + + public static JSONObject createByteTalkMessage(Request request, MessageType messageType, String sign, Long timestamp) { + JSONObject message = new JSONObject(); + if (messageType == TEXT) { + message.set("msg_type", "text"); + message.set("timestamp", timestamp); + message.set("sign", sign); + StringBuilder content = new StringBuilder(); + List userNamesList = request.getUserNamesList(); + Boolean isNoticeAll = request.getIsNoticeAll(); + if (isNoticeAll) { + content.append("所有人"); + } + userNamesList.forEach(l -> { + content.append("").append(l).append(""); + }); + content.append(request.getContent()); + JSONObject text = new JSONObject(); + text.set("text", content); + message.set("content", text); + } + return message; + } +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/config/OaBlendsInitializer.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/config/OaBlendsInitializer.java new file mode 100644 index 00000000..5ef7626b --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/config/OaBlendsInitializer.java @@ -0,0 +1,69 @@ +package org.dromara.oa.core.config; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.dromara.oa.api.OaSender; +import org.dromara.oa.comm.config.OaSupplierConfig; +import org.dromara.oa.comm.content.OaContent; +import org.dromara.oa.core.byteTalk.config.ByteTalkFactory; +import org.dromara.oa.core.dingTalk.config.DingTalkFactory; +import org.dromara.oa.core.provider.factory.BaseProviderFactory; +import org.dromara.oa.core.provider.factory.OaFactory; +import org.dromara.oa.core.provider.factory.ProviderFactoryHolder; +import org.dromara.oa.core.weTalk.config.WeTalkFactory; + +import java.util.List; +import java.util.Map; + +/** + * @author dongfeng + * @description 注册工厂, 读取yaml配置并根据配置生成对象 + * @date 2023-10-22 12:39 + */ + +@Slf4j +public class OaBlendsInitializer { + private List> factoryList; + + private final Map> blends; + + public OaBlendsInitializer(Map> oas + ) { + this.blends = oas; + onApplicationEvent(); + } + + public void onApplicationEvent() { + registerDefaultFactory(); + // 解析供应商配置 + for (String configId : blends.keySet()) { + Map configMap = blends.get(configId); + if (Boolean.FALSE.equals(configMap.get("isEnable"))) { + continue; + } + Object supplierObj = configMap.get(OaContent.SUPPLIER_KEY); + String supplier = supplierObj == null ? "" : String.valueOf(supplierObj); + supplier = StrUtil.isEmpty(supplier) ? configId : supplier; + BaseProviderFactory providerFactory = (BaseProviderFactory) ProviderFactoryHolder.requireForSupplier(supplier); + if (providerFactory == null) { + log.warn("创建\"{}\"的通知webhook服务失败,未找到供应商为\"{}\"的服务", configId, supplier); + continue; + } + configMap.put("configId", configId); + JSONObject configJson = new JSONObject(configMap); + OaSupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass()); + OaFactory.createAndRegisterOaSender(supplierConfig); + } + } + + /** + * 注册默认工厂实例 + */ + private void registerDefaultFactory() { + ProviderFactoryHolder.registerFactory(DingTalkFactory.instance()); + ProviderFactoryHolder.registerFactory(ByteTalkFactory.instance()); + ProviderFactoryHolder.registerFactory(WeTalkFactory.instance()); + } +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/config/OaSupplierConfig.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/config/OaSupplierConfig.java new file mode 100644 index 00000000..184429d6 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/config/OaSupplierConfig.java @@ -0,0 +1,31 @@ +package org.dromara.oa.core.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author dongfeng + * @date 2023-10-22 12:50 + */ +public class OaSupplierConfig { + /** + * 注入配置 + */ + @Bean + @ConfigurationProperties(prefix = "sms.oas") + @ConditionalOnProperty(prefix = "sms", name = "config-type", havingValue = "yaml") + protected Map> oas() { + return new LinkedHashMap<>(); + } + + + @Bean + protected OaBlendsInitializer smsOasInitializer( + Map> oas) { + return new OaBlendsInitializer(oas); + } +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/config/DingTalkConfig.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/config/DingTalkConfig.java new file mode 100644 index 00000000..41761c22 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/config/DingTalkConfig.java @@ -0,0 +1,22 @@ +package org.dromara.oa.core.dingTalk.config; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.oa.comm.enums.OaType; +import org.dromara.oa.core.provider.config.OaBaseConfig; + + +/** + * @author dongfeng + * @date 2023-10-19 13:38 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class DingTalkConfig extends OaBaseConfig { + private final String requestUrl = OaType.DINGTALK.getUrl(); + + @Override + public String getSupplier() { + return OaType.DINGTALK.getType(); + } +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/config/DingTalkFactory.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/config/DingTalkFactory.java new file mode 100644 index 00000000..a80408f1 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/config/DingTalkFactory.java @@ -0,0 +1,40 @@ +package org.dromara.oa.core.dingTalk.config; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.oa.comm.content.OaContent; +import org.dromara.oa.core.dingTalk.service.DingTalkOaImpl; +import org.dromara.oa.core.provider.factory.AbstractProviderFactory; + +/** + * @author dongfeng + * @description 钉钉通知对象建造 + * @date 2023-10-22 21:00 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DingTalkFactory extends AbstractProviderFactory { + private static final DingTalkFactory INSTANCE = new DingTalkFactory(); + + /** + * 建造一个钉钉通知实现 + */ + @Override + public DingTalkOaImpl createSmsOa(DingTalkConfig dingTalkConfig) { + return new DingTalkOaImpl(dingTalkConfig); + } + + @Override + public String getSupplier() { + return OaContent.DINGTALK; + } + + /** + * 获取建造者实例 + * + * @return 建造者实例 + */ + public static DingTalkFactory instance() { + return INSTANCE; + } + +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/service/DingTalkOaImpl.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/service/DingTalkOaImpl.java new file mode 100644 index 00000000..2d002d7a --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/service/DingTalkOaImpl.java @@ -0,0 +1,76 @@ +package org.dromara.oa.core.dingTalk.service; + +import cn.hutool.json.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.dromara.oa.comm.entity.Request; +import org.dromara.oa.comm.entity.Response; +import org.dromara.oa.comm.enums.MessageType; +import org.dromara.oa.comm.enums.OaType; +import org.dromara.oa.comm.errors.OaException; +import org.dromara.oa.core.dingTalk.config.DingTalkConfig; +import org.dromara.oa.core.dingTalk.utils.DingTalkBuilder; +import org.dromara.oa.core.provider.service.AbstractOaBlend; +import org.dromara.oa.core.support.HttpClientImpl; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static org.dromara.oa.comm.enums.OaType.DINGTALK; + +/** + * @author dongfeng + * @date 2023-10-22 21:01 + */ +@Slf4j +public class DingTalkOaImpl extends AbstractOaBlend { + + private HttpClientImpl httpClient = new HttpClientImpl(); + + /** + * 建造一个微信通知对象服务 + */ + public DingTalkOaImpl(DingTalkConfig config) { + super(config); + } + + @Override + public String getSupplier() { + return OaType.DINGTALK.getType(); + } + + @Override + public Response sender(Request request, MessageType messageType) { + + if (Objects.isNull(request.getContent())) { + throw new OaException("消息体content不能为空"); + } + StringBuilder webhook = new StringBuilder(); + JSONObject message = null; + DingTalkConfig config = getConfig(); + webhook.append(DINGTALK.getUrl()); + webhook.append(config.getTokenId()); + String sign = config.getSign(); + if (!Objects.isNull(sign)) { + sign = DingTalkBuilder.sign(sign); + webhook.append(sign); + } + message = DingTalkBuilder.createMessage(request, messageType); + String post; + try { + post = httpClient.post(webhook, getHeaders(), message); + log.info("请求返回结果:" + post); + } catch (Exception e) { + log.warn("请求失败问题:" + e.getMessage()); + throw new OaException(e.getMessage()); + } + // 后续解析响应体提取errorCode判断是否成功 + return new Response(true, post, config.getConfigId()); + } + + public static Map getHeaders() { + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + return headers; + } +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/utils/DingTalkBuilder.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/utils/DingTalkBuilder.java new file mode 100644 index 00000000..e23aadf8 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/dingTalk/utils/DingTalkBuilder.java @@ -0,0 +1,70 @@ +package org.dromara.oa.core.dingTalk.utils; + +import cn.hutool.json.JSONObject; +import org.dromara.oa.comm.entity.Request; +import org.dromara.oa.comm.enums.MessageType; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +import static org.dromara.oa.comm.enums.MessageType.MARKDOWN; +import static org.dromara.oa.comm.enums.MessageType.TEXT; + +/** + * @author dongfeng + * @description 钉钉通知签名和信息构建 + * @date 2023-10-19 13:07 + */ +public class DingTalkBuilder { + public static String sign(String secret) { + Long timestamp = System.currentTimeMillis(); + + String stringToSign = timestamp + "\n" + secret; + Mac mac = null; + try { + mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new RuntimeException(e); + } + byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)); + String sign = null; + try { + sign = URLEncoder.encode(new String(Base64.getEncoder().encode(signData)), StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return "×tamp=" + timestamp + "&sign=" + sign; + } + + + public static JSONObject createMessage(Request request, MessageType messageType) { + JSONObject message = new JSONObject(); + if (messageType == TEXT) { + message.set("msgtype", "text"); + JSONObject text = new JSONObject(); + text.set("content", request.getContent()); + JSONObject at = new JSONObject(); + at.set("atMobiles", request.getPhoneList()); + at.set("isAtAll", request.getIsNoticeAll()); + message.set("at", at); + message.set("text", text); + } else if (messageType == MARKDOWN) { + message.set("msgtype", "markdown"); + JSONObject markdown = new JSONObject(); + markdown.set("text", request.getContent()); + markdown.set("title", request.getTitle()); + JSONObject at = new JSONObject(); + at.set("atMobiles", request.getPhoneList()); + message.set("at", at); + message.set("markdown", markdown); + } + return message; + } +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/factory/OaFactory.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/factory/OaFactory.java deleted file mode 100644 index a4415d37..00000000 --- a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/factory/OaFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.dromara.oa.core.factory; - -import org.dromara.oa.comm.config.OaConfig; -import org.dromara.oa.comm.errors.OaException; -import org.dromara.oa.core.service.OaBuild; -import org.dromara.oa.core.service.SenderImpl; - -import java.util.HashMap; -import java.util.Map; - -public class OaFactory { - private static final Map configs = new HashMap<>(); - - /** - * createMailClient - *

从工厂获取一个OA发送实例 - * @param key 配置的标识key - */ - public static SenderImpl createSender(Object key) { - try { - return OaBuild.build(configs.get(key)); - } catch (Exception e) { - throw new OaException(e.getMessage()); - } - } - - - /** - * set - *

将一个配置对象交给工厂 - * @param key 标识 - * @param config 配置对象 - */ - public static void put(Object key, OaConfig config){ - configs.put(key, config); - } - -} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/config/OaBaseConfig.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/config/OaBaseConfig.java new file mode 100644 index 00000000..68ba0e45 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/config/OaBaseConfig.java @@ -0,0 +1,26 @@ +package org.dromara.oa.core.provider.config; + +import lombok.Data; +import org.dromara.oa.comm.config.OaSupplierConfig; + +@Data +public abstract class OaBaseConfig implements OaSupplierConfig { + /** + * 供应商 + */ + private String supplier; + + + /** + * 获取配置标识名(唯一) + */ + private String configId; + + private String tokenId; + + private String sign; + + private Boolean isEnable = true; + +} + diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/AbstractProviderFactory.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/AbstractProviderFactory.java new file mode 100644 index 00000000..3281c24d --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/AbstractProviderFactory.java @@ -0,0 +1,33 @@ +package org.dromara.oa.core.provider.factory; + +import org.dromara.oa.api.OaSender; +import org.dromara.oa.comm.config.OaSupplierConfig; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +public abstract class AbstractProviderFactory implements BaseProviderFactory { + + private Class configClass; + + public AbstractProviderFactory() { + Type genericSuperclass = getClass().getGenericSuperclass(); + if (genericSuperclass instanceof ParameterizedType) { + ParameterizedType paramType = (ParameterizedType) genericSuperclass; + Type[] typeArguments = paramType.getActualTypeArguments(); + if (typeArguments.length > 1 && typeArguments[1] instanceof Class) { + configClass = (Class) typeArguments[1]; + } + } + } + + /** + * 获取配置类 + * + * @return 配置类 + */ + public Class getConfigClass() { + return configClass; + } + +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/BaseProviderFactory.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/BaseProviderFactory.java new file mode 100644 index 00000000..d16765d4 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/BaseProviderFactory.java @@ -0,0 +1,30 @@ +package org.dromara.oa.core.provider.factory; + +import org.dromara.oa.api.OaSender; +import org.dromara.oa.comm.config.OaSupplierConfig; + +public interface BaseProviderFactory { + + /** + * 创建通知webhook实现对象 + * + * @param c 通知webhook配置对象 + * @return 通知webhook实现对象 + */ + S createSmsOa(C c); + + /** + * 获取配置类 + * + * @return 配置类 + */ + Class getConfigClass(); + + /** + * 获取供应商 + * + * @return 供应商 + */ + String getSupplier(); + +} \ No newline at end of file diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/OaFactory.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/OaFactory.java new file mode 100644 index 00000000..487ae81a --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/OaFactory.java @@ -0,0 +1,53 @@ +package org.dromara.oa.core.provider.factory; + +import org.dromara.oa.api.OaSender; +import org.dromara.oa.comm.config.OaSupplierConfig; +import org.dromara.oa.comm.errors.OaException; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class OaFactory { + private final static Map configs = new ConcurrentHashMap<>(); + + /** + *

创建各个厂商的实现类 + * + * @param config 通知webhook配置 + */ + public static void createAndRegisterOaSender(OaSupplierConfig config) { + OaSender oaSender = createAndGetOa(config); + register(oaSender); + } + + /** + * 注册通知webhook服务对象 + * + * @param smsBlend 通知webhook服务对象 + */ + public static void register(OaSender smsBlend) { + if (smsBlend == null) { + throw new OaException("通知webhook服务对象不能为空"); + } + configs.put(smsBlend.getConfigId(), smsBlend); + } + + public static OaSender createAndGetOa(OaSupplierConfig config) { + BaseProviderFactory factory = ProviderFactoryHolder.requireForSupplier(config.getSupplier()); + if (factory == null) { + throw new OaException("不支持当前供应商配置"); + } + return factory.createSmsOa(config); + } + + /** + * 通过configId获取通知webhook服务对象 + * + * @param configId 唯一标识 + * @return 返回通知webhook服务对象。如果未找到则返回null + */ + public static OaSender getSmsOaBlend(String configId) { + return configs.get(configId); + } + +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/ProviderFactoryHolder.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/ProviderFactoryHolder.java new file mode 100644 index 00000000..02270949 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/factory/ProviderFactoryHolder.java @@ -0,0 +1,43 @@ +package org.dromara.oa.core.provider.factory; + +import cn.hutool.core.collection.CollUtil; +import org.dromara.oa.api.OaSender; +import org.dromara.oa.comm.config.OaSupplierConfig; +import org.dromara.oa.comm.errors.OaException; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author dongfeng + * @date 2023-10-22 21:12 + */ +public class ProviderFactoryHolder { + + + private static final Map> factories = new ConcurrentHashMap<>(); + + public static void registerFactory(BaseProviderFactory factory) { + if (factory == null) { + throw new OaException("注册供应商工厂失败,工厂实例不能为空"); + } + factories.put(factory.getSupplier(), factory); + } + + public static void registerFactory(List> factoryList) { + if (CollUtil.isEmpty(factoryList)) { + return; + } + for (BaseProviderFactory factory : factoryList) { + if (factory == null) { + continue; + } + registerFactory(factory); + } + } + + public static BaseProviderFactory requireForSupplier(String supplier) { + return factories.getOrDefault(supplier, null); + } +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/service/AbstractOaBlend.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/service/AbstractOaBlend.java new file mode 100644 index 00000000..f8dbe026 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/provider/service/AbstractOaBlend.java @@ -0,0 +1,27 @@ +package org.dromara.oa.core.provider.service; + +import cn.hutool.core.util.StrUtil; +import lombok.Getter; +import org.dromara.oa.api.OaSender; +import org.dromara.oa.comm.config.OaSupplierConfig; + +/** + * @author dongfeng + * @date 2023-10-22 21:03 + */ +public abstract class AbstractOaBlend implements OaSender { + + @Getter + private final String configId; + + private final C config; + + protected AbstractOaBlend(C config) { + this.configId = StrUtil.isEmpty(config.getConfigId()) ? getSupplier() : config.getConfigId(); + this.config = config; + } + + protected C getConfig() { + return config; + } +} \ No newline at end of file diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/service/OaBuild.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/service/OaBuild.java deleted file mode 100644 index d7f00ee0..00000000 --- a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/service/OaBuild.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.dromara.oa.core.service; - - -import lombok.Data; -import org.dromara.oa.comm.config.OaConfig; - -@Data -public class OaBuild { - - private String OaType; - - private OaConfig config; - - public OaBuild(OaConfig config) { - this.config = config; - this.OaType = getOaType(); - } - - public static SenderImpl build(OaConfig config) { - return SenderImpl.NewSender(new OaBuild(config)); - } -} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/service/SenderImpl.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/service/SenderImpl.java deleted file mode 100644 index d432b0d5..00000000 --- a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/service/SenderImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.dromara.oa.core.service; - -import org.dromara.oa.api.OaSender; -import org.dromara.oa.comm.entity.Request; -import org.dromara.oa.comm.entity.Response; -import org.dromara.oa.comm.enums.MessageType; -import org.dromara.oa.comm.errors.OaException; -import org.dromara.oa.core.support.HttpClientImpl; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Logger; -import cn.hutool.json.JSONObject; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -import static org.dromara.oa.comm.enums.MessageType.*; -import static org.dromara.oa.comm.enums.OaType.DINGTALK; - -public class SenderImpl implements OaSender { - - private static Logger logger = Logger.getLogger("oaLog"); - - private OaBuild oaBuild; - - private HttpClientImpl httpClient = new HttpClientImpl(); - - public SenderImpl() { - } - public SenderImpl(OaBuild oaBuild) { - this.oaBuild = oaBuild; - } - - public static SenderImpl NewSender(OaBuild oaBuild){ - return new SenderImpl(oaBuild); - } - - @Override - public Response sender(Request request, MessageType messageType) { - StringBuilder webhook = new StringBuilder(); - webhook.append(DINGTALK.getUrl()); - webhook.append(oaBuild.getConfig().getTokenId()); - if (request.getOaType().equals(DINGTALK.getType())) { - // todo 等待完善钉钉和飞书的sign - webhook.append(sign(oaBuild.getConfig().getSign())); - } - JSONObject message = createMessage(request, messageType); - try { - String post = httpClient.post(webhook, getHeaders(), message); - logger.info("请求返回结果:" + post); - } catch (Exception e) { - logger.warning("请求失败问题:" + e.getMessage()); - throw new OaException(e.getMessage()); - } - return new Response(); - } - - public static Map getHeaders() { - Map headers = new HashMap<>(); - headers.put("Content-Type", "application/json"); - return headers; - } - public static String sign(String secret) { - Long timestamp = System.currentTimeMillis(); - - String stringToSign = timestamp + "\n" + secret; - Mac mac = null; - try { - mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); - } catch (NoSuchAlgorithmException | InvalidKeyException e) { - throw new RuntimeException(e); - } - byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)); - String sign = null; - try { - sign = URLEncoder.encode(new String(Base64.getEncoder().encode(signData)),StandardCharsets.UTF_8.toString()); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - return "×tamp=" + timestamp + "&sign=" + sign; - } - public static JSONObject createMessage(Request request, MessageType messageType) { - JSONObject message = new JSONObject(); - if (messageType == TEXT){ - message.set("msgtype", "text"); - JSONObject text = new JSONObject(); - text.set("content", request.getContent()); - JSONObject at = new JSONObject(); - at.set("atMobiles", request.getPhones()); - message.set("at", at); - message.set("text", text); - } else if (messageType == LINK) { - message.set("msgtype", "link"); - JSONObject link = new JSONObject(); - link.set("text", request.getContent()); - link.set("title", request.getTitle()); - message.set("link", link); - } else if (messageType == MARKDOWN) { - message.set("msgtype", "markdown"); - JSONObject markdown = new JSONObject(); - markdown.set("text", request.getContent()); - markdown.set("title", request.getTitle()); - JSONObject at = new JSONObject(); - at.set("atMobiles", request.getPhones()); - message.set("at", at); - message.set("markdown", markdown); - } - return message; - } - - -} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/support/AbstractHttpClient.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/support/AbstractHttpClient.java index 3cb73e21..49d2f234 100644 --- a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/support/AbstractHttpClient.java +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/support/AbstractHttpClient.java @@ -2,7 +2,7 @@ package org.dromara.oa.core.support; import java.util.Map; -public abstract class AbstractHttpClient implements MsgHttpClient{ +public abstract class AbstractHttpClient implements MsgHttpClient { @Override public String get(String url) { diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/support/HttpClientImpl.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/support/HttpClientImpl.java index f4142631..7b6f72a7 100644 --- a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/support/HttpClientImpl.java +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/support/HttpClientImpl.java @@ -5,11 +5,10 @@ import cn.hutool.http.HttpResponse; import java.util.Map; -public class HttpClientImpl extends AbstractHttpClient{ +public class HttpClientImpl extends AbstractHttpClient { @Override public String post(StringBuilder url, Map headers, T message) throws Exception { // 构建请求体 - String payload = "{\"msgtype\":\"text\",\"text\":{\"content\":\"This HertzBeat 通知\"},\"at\":{\"isAtAll\":false}}"; // 发送POST请求 HttpResponse response = HttpRequest.post(url.toString()) .headerMap(headers, true) diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/config/WeTalkConfig.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/config/WeTalkConfig.java new file mode 100644 index 00000000..88b045b8 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/config/WeTalkConfig.java @@ -0,0 +1,18 @@ +package org.dromara.oa.core.weTalk.config; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.oa.comm.enums.OaType; +import org.dromara.oa.core.provider.config.OaBaseConfig; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WeTalkConfig extends OaBaseConfig { + + private final String requestUrl = OaType.WETALK.getUrl(); + + @Override + public String getSupplier() { + return OaType.WETALK.getType(); + } +} \ No newline at end of file diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/config/WeTalkFactory.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/config/WeTalkFactory.java new file mode 100644 index 00000000..3a1969ef --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/config/WeTalkFactory.java @@ -0,0 +1,40 @@ +package org.dromara.oa.core.weTalk.config; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.oa.comm.content.OaContent; +import org.dromara.oa.core.provider.factory.AbstractProviderFactory; +import org.dromara.oa.core.weTalk.service.WeTalkOaImpl; + +/** + * @author dongfeng + * @description 微信通知对象建造 + * @date 2023-10-22 21:00 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class WeTalkFactory extends AbstractProviderFactory { + private static final WeTalkFactory INSTANCE = new WeTalkFactory(); + + /** + * 建造一个微信通知服务 + */ + @Override + public WeTalkOaImpl createSmsOa(WeTalkConfig weTalkConfig) { + return new WeTalkOaImpl(weTalkConfig); + } + + @Override + public String getSupplier() { + return OaContent.WETALK; + } + + /** + * 获取建造者实例 + * + * @return 建造者实例 + */ + public static WeTalkFactory instance() { + return INSTANCE; + } + +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/service/WeTalkOaImpl.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/service/WeTalkOaImpl.java new file mode 100644 index 00000000..bc800521 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/service/WeTalkOaImpl.java @@ -0,0 +1,68 @@ +package org.dromara.oa.core.weTalk.service; + +import cn.hutool.json.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.dromara.oa.comm.entity.Request; +import org.dromara.oa.comm.entity.Response; +import org.dromara.oa.comm.enums.MessageType; +import org.dromara.oa.comm.enums.OaType; +import org.dromara.oa.comm.errors.OaException; +import org.dromara.oa.core.provider.service.AbstractOaBlend; +import org.dromara.oa.core.support.HttpClientImpl; +import org.dromara.oa.core.weTalk.config.WeTalkConfig; +import org.dromara.oa.core.weTalk.utils.WeTalkBuilder; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static org.dromara.oa.comm.enums.OaType.WETALK; + +/** + * @author dongfeng + * @date 2023-10-22 21:01 + */ +@Slf4j +public class WeTalkOaImpl extends AbstractOaBlend { + + private HttpClientImpl httpClient = new HttpClientImpl(); + + public WeTalkOaImpl(WeTalkConfig config) { + super(config); + } + + @Override + public String getSupplier() { + return OaType.WETALK.getType(); + } + + @Override + public Response sender(Request request, MessageType messageType) { + + if (Objects.isNull(request.getContent())) { + throw new OaException("消息体content不能为空"); + } + StringBuilder webhook = new StringBuilder(); + JSONObject message = null; + WeTalkConfig config = getConfig(); + webhook.append(WETALK.getUrl()); + webhook.append(config.getTokenId()); + message = WeTalkBuilder.createWeTalkMessage(request, messageType); + String post; + try { + post = httpClient.post(webhook, getHeaders(), message); + log.info("请求返回结果:" + post); + } catch (Exception e) { + log.warn("请求失败问题:" + e.getMessage()); + throw new OaException(e.getMessage()); + } + // 后续解析响应体提取errorCode判断是否成功 + return new Response(true, post, config.getConfigId()); + } + + public static Map getHeaders() { + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + return headers; + } +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/utils/WeTalkBuilder.java b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/utils/WeTalkBuilder.java new file mode 100644 index 00000000..11046292 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/java/org/dromara/oa/core/weTalk/utils/WeTalkBuilder.java @@ -0,0 +1,56 @@ +package org.dromara.oa.core.weTalk.utils; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONObject; +import org.dromara.oa.comm.entity.Request; +import org.dromara.oa.comm.enums.MessageType; + +import java.util.List; + +import static org.dromara.oa.comm.enums.MessageType.MARKDOWN; +import static org.dromara.oa.comm.enums.MessageType.TEXT; + +/** + * @author dongfeng + * @description 微信通知签名和信息构建 + * @date 2023-10-19 13:07 + */ +public class WeTalkBuilder { + + + public static JSONObject createWeTalkMessage(Request request, MessageType messageType) { + + JSONObject message = new JSONObject(); + if (messageType == TEXT) { + message.set("msgtype", "text"); + JSONObject text = new JSONObject(); + text.set("content", request.getContent()); + boolean isContain = false; + List userIdList = request.getUserIdList(); + List phoneList = request.getPhoneList(); + Boolean isNoticeAll = request.getIsNoticeAll(); + if (!ObjectUtil.isNull(userIdList)) { + if (isNoticeAll) { + userIdList.add("@all"); + isContain = true; + } + text.set("mentioned_list", userIdList.toArray()); + } + if (!ObjectUtil.isNull(phoneList)) { + if (isNoticeAll && !isContain) { + phoneList.add("@all"); + } + text.set("mentioned_mobile_list", phoneList.toArray()); + } + message.set("text", text); + } else if (messageType == MARKDOWN) { + message.set("msgtype", "markdown"); + JSONObject markdown = new JSONObject(); + markdown.set("content", request.getContent()); + markdown.set("title", request.getTitle()); + message.set("markdown", markdown); + } + return message; + } + +} diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/resources/META-INF/spring.factories b/sms4j-oa-plugin/sms4j-oa-core/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..9157acc8 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + org.dromara.oa.core.config.OaSupplierConfig diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/sms4j-oa-plugin/sms4j-oa-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..9dc50ba4 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.dromara.oa.core.config.OaSupplierConfig diff --git a/sms4j-oa-plugin/sms4j-oa-core/src/main/resources/application.properties b/sms4j-oa-plugin/sms4j-oa-core/src/main/resources/application.properties new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/sms4j-oa-plugin/sms4j-oa-core/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/sms4j-spring-boot-example/src/main/resources/application.yml b/sms4j-spring-boot-example/src/main/resources/application.yml index e212bec1..61b6e526 100644 --- a/sms4j-spring-boot-example/src/main/resources/application.yml +++ b/sms4j-spring-boot-example/src/main/resources/application.yml @@ -59,4 +59,18 @@ sms: #模板ID template-id: pub_verif_short # 模版名称 - templateName: code \ No newline at end of file + templateName: code + + oas: + oaDingTalkByYaml: # configId + isEnable: true # 表示该配置是否生效(默认生效,false表示不生效) + supplier: dingding # 厂商标识 + tokenId: 您的accessKey + sign: 您的sign + oaByteTalkByYaml: # configId + supplier: feishu # 厂商标识 + tokenId: 您的accessKey + sign: 您的sign + oaWeTalkByYaml: + supplier: wetalk # 厂商标识 + tokenId: 您的sign \ No newline at end of file diff --git a/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsOaTest.java b/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsOaTest.java index efe9ad99..d7b4bcdb 100644 --- a/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsOaTest.java +++ b/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsOaTest.java @@ -1,12 +1,14 @@ package org.dromara.sms4j.example; import lombok.extern.slf4j.Slf4j; -import org.dromara.oa.comm.config.OaConfig; +import org.dromara.oa.api.OaSender; import org.dromara.oa.comm.entity.Request; import org.dromara.oa.comm.enums.MessageType; import org.dromara.oa.comm.enums.OaType; -import org.dromara.oa.core.factory.OaFactory; -import org.dromara.oa.core.service.SenderImpl; +import org.dromara.oa.core.byteTalk.config.ByteTalkConfig; +import org.dromara.oa.core.dingTalk.config.DingTalkConfig; +import org.dromara.oa.core.provider.factory.OaFactory; +import org.dromara.oa.core.weTalk.config.WeTalkConfig; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -28,27 +30,134 @@ public class SmsOaTest { * 填secret */ private static final String SIGN = ""; - /** - * 填oa的key - * value是一个OaConfig - */ - private static final String oaKey = ""; @Test public void oaDingTalkTest() { - OaFactory.put(oaKey, OaConfig.builder() - .OaType(OaType.DINGTALK.getType()) - .tokenId(TOKENID) - .sign(SIGN).build()); - SenderImpl alarm = OaFactory.createSender(oaKey); + String key = "oaDingTalk"; + DingTalkConfig dingTalkConfig = new DingTalkConfig(); + dingTalkConfig.setConfigId(key); + dingTalkConfig.setSign(SIGN); + dingTalkConfig.setTokenId(TOKENID); +// OaFactory.createAndRegisterOaSender(dingTalkConfig); + OaSender alarm = OaFactory.createAndGetOa(dingTalkConfig); Request request = new Request(); - request.setOaType(OaType.DINGTALK.getType()); ArrayList phones = new ArrayList<>(); phones.add(PHONE); - request.setPhones(phones); + request.setPhoneList(phones); + request.setIsNoticeAll(true); request.setContent("测试消息"); request.setTitle("测试消息标题"); alarm.sender(request, MessageType.TEXT); + + // 测试markdown,无法@ +// request.setContent("#### 杭州天气 @150XXXXXXXX \n > 9度,西北风1级,空气良89,相对温度73%\n > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n > ###### 10点20分发布 [天气](https://www.dingtalk.com) \n"); +// request.setTitle("杭州天气"); +// alarm.sender(request, MessageType.MARKDOWN); + + } + + @Test + public void oaDingTalkByYamlTest() { + String configId = "oaDingTalkByYaml"; + OaSender alarm = OaFactory.getSmsOaBlend(configId); + Request request = new Request(); + ArrayList phones = new ArrayList<>(); + phones.add(PHONE); + request.setPhoneList(phones); + request.setIsNoticeAll(false); + request.setContent("HertzBeat"); + request.setTitle("HertzBeat"); + alarm.sender(request, MessageType.TEXT); + + } + + @Test + public void oaByteTalkTest() { + String configId = "oaByteTalk"; + ByteTalkConfig dingTalkConfig = new ByteTalkConfig(); + dingTalkConfig.setConfigId(configId); + dingTalkConfig.setSign(SIGN); + dingTalkConfig.setTokenId(TOKENID); + OaSender alarm = OaFactory.createAndGetOa(dingTalkConfig); + Request request = new Request(); + ArrayList userNameList = new ArrayList<>(); + userNameList.add("user1"); + userNameList.add("user2"); + request.setUserNamesList(userNameList); + request.setContent("测试消息"); + request.setIsNoticeAll(true); +// request.setTitle("测试消息标题"); + alarm.sender(request, MessageType.TEXT); + } + + + @Test + public void oaByteTalkByYamlTest() { + String configId = "oaByteTalkByYaml"; + OaSender alarm = OaFactory.getSmsOaBlend(configId); + Request request = new Request(); + request.setOaType(OaType.BYTETALK.getType()); + ArrayList userNameList = new ArrayList<>(); + userNameList.add("user1"); + userNameList.add("user2"); + request.setUserNamesList(userNameList); + request.setContent("测试消息"); + request.setIsNoticeAll(true); +// request.setTitle("测试消息标题"); + alarm.sender(request, MessageType.TEXT); + } + + @Test + public void oaWeTalkTest() { + String configId = "oaWeTalk"; + WeTalkConfig weTalkConfig = new WeTalkConfig(); + weTalkConfig.setConfigId(configId); + weTalkConfig.setTokenId(TOKENID); + OaSender alarm = OaFactory.createAndGetOa(weTalkConfig); + Request request = new Request(); + ArrayList phones = new ArrayList<>(); + phones.add(PHONE); + phones.add("131"); + request.setPhoneList(phones); + ArrayList userIds = new ArrayList<>(); + userIds.add("123"); + request.setUserIdList(userIds); +// request.setIsNoticeAll(true); + request.setContent("测试消息"); + request.setTitle("测试消息标题"); + alarm.sender(request, MessageType.TEXT); + + // 测试markdown,无法@ + // 企业微信的markdown直接是content,没有title +// request.setContent("#### 杭州天气 @150XXXXXXXX \n > 9度,西北风1级,空气良89,相对温度73%\n > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n > ###### 10点20分发布 [天气](https://www.dingtalk.com) \n"); +// request.setTitle("杭州天气"); +// alarm.sender(request, MessageType.MARKDOWN); + } + + + @Test + public void oaWeTalkByYamlTest() { + String configId = "oaWeTalkByYaml"; + OaSender alarm = OaFactory.getSmsOaBlend(configId); + Request request = new Request(); + request.setOaType(OaType.WETALK.getType()); + ArrayList phones = new ArrayList<>(); + phones.add(PHONE); + phones.add("131"); + request.setPhoneList(phones); + ArrayList userIds = new ArrayList<>(); + userIds.add("123"); + request.setUserIdList(userIds); +// request.setIsNoticeAll(true); + request.setContent("测试消息"); + request.setTitle("测试消息标题"); + alarm.sender(request, MessageType.TEXT); + + // 测试markdown,无法@ + // 企业微信的markdown直接是content,没有title +// request.setContent("#### 杭州天气 @150XXXXXXXX \n > 9度,西北风1级,空气良89,相对温度73%\n > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n > ###### 10点20分发布 [天气](https://www.dingtalk.com) \n"); +// request.setTitle("杭州天气"); +// alarm.sender(request, MessageType.MARKDOWN); } }