mirror of
https://gitee.com/dromara/sms4j.git
synced 2025-12-07 17:38:38 +08:00
commit
5cbbdd209c
1
pom.xml
1
pom.xml
@ -21,6 +21,7 @@
|
|||||||
<module>sms4j-spring-boot-example</module>
|
<module>sms4j-spring-boot-example</module>
|
||||||
<module>sms4j-javase-plugin</module>
|
<module>sms4j-javase-plugin</module>
|
||||||
<module>sms4j-Email-plugin</module>
|
<module>sms4j-Email-plugin</module>
|
||||||
|
<module>sms4j-oa-plugin</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<!-- 开源协议 Apache 2.0 -->
|
<!-- 开源协议 Apache 2.0 -->
|
||||||
|
|||||||
91
sms4j-oa-plugin/pom.xml
Normal file
91
sms4j-oa-plugin/pom.xml
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<artifactId>sms4j-oa-plugin</artifactId>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>sms4j-oa-comm</module>
|
||||||
|
<module>sms4j-oa-api</module>
|
||||||
|
<module>sms4j-oa-core</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<name>sms4j-oa-plugin</name>
|
||||||
|
<description>sms4j-oa-plugin</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j-oa-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j-oa-comm</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j-oa-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun.activation</groupId>
|
||||||
|
<artifactId>javax.activation</artifactId>
|
||||||
|
<version>${sunactivation.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.activation</groupId>
|
||||||
|
<artifactId>jakarta.activation-api</artifactId>
|
||||||
|
<version>${jakarta.activation.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-cron</artifactId>
|
||||||
|
<version>${hutool.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!--配置文件提示-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>com.feishu.openplatform</groupId>-->
|
||||||
|
<!-- <artifactId>feishu-sdk-java</artifactId>-->
|
||||||
|
<!-- <version>${feishu.openplatform.version}</version>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
||||||
29
sms4j-oa-plugin/sms4j-oa-api/pom.xml
Normal file
29
sms4j-oa-plugin/sms4j-oa-api/pom.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j-oa-plugin</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>sms4j-oa-api</artifactId>
|
||||||
|
|
||||||
|
<name>sms4j-oa-api</name>
|
||||||
|
<description>sms4j-oa-api</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j-oa-comm</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package org.dromara.oa.api;
|
||||||
|
|
||||||
|
import org.dromara.oa.comm.entity.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-10-28 14:26
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface OaCallBack {
|
||||||
|
void callBack(Response smsResponse);
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
package org.dromara.oa.api;
|
||||||
|
|
||||||
|
import org.dromara.oa.comm.entity.Request;
|
||||||
|
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步(回调)
|
||||||
|
*/
|
||||||
|
void senderAsync(Request request, MessageType messageType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步(不回调)
|
||||||
|
* @param request oa请求体
|
||||||
|
* @param messageType 消息类型
|
||||||
|
* @param callBack 回调方法
|
||||||
|
*/
|
||||||
|
void senderAsync(Request request, MessageType messageType, OaCallBack callBack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送带优先级的消息
|
||||||
|
* @param request oa请求体
|
||||||
|
* @param messageType 消息类型
|
||||||
|
*/
|
||||||
|
|
||||||
|
void senderAsyncByPriority(Request request, MessageType messageType);
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package org.dromara.oa.api;
|
||||||
|
|
||||||
|
import org.dromara.oa.comm.entity.Request;
|
||||||
|
import org.dromara.oa.comm.entity.Response;
|
||||||
|
import org.dromara.oa.comm.enums.MessageType;
|
||||||
|
|
||||||
|
public interface OaSender {
|
||||||
|
|
||||||
|
Response Sender(Request request, MessageType messageType);
|
||||||
|
}
|
||||||
37
sms4j-oa-plugin/sms4j-oa-comm/pom.xml
Normal file
37
sms4j-oa-plugin/sms4j-oa-comm/pom.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j-oa-plugin</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>sms4j-oa-comm</artifactId>
|
||||||
|
<name>sms4j-oa-comm</name>
|
||||||
|
<description>sms4j-oa-comm</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>org.slf4j</groupId>-->
|
||||||
|
<!-- <artifactId>slf4j-api</artifactId>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-cron</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-http</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package org.dromara.oa.comm.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-10-19 13:36
|
||||||
|
*/
|
||||||
|
public interface OaSupplierConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配置标识名(唯一)
|
||||||
|
*/
|
||||||
|
String getConfigId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取供应商
|
||||||
|
*/
|
||||||
|
String getSupplier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取是否使用
|
||||||
|
*/
|
||||||
|
Boolean getIsEnable();
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package org.dromara.oa.comm.content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dongfeng
|
||||||
|
* 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";
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
package org.dromara.oa.comm.entity;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.dromara.oa.comm.enums.MessageType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Request implements Comparable<Request> {
|
||||||
|
// 标题
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
// 消息内容
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
// link类型的参数
|
||||||
|
private String picUrl;
|
||||||
|
|
||||||
|
// link类型的参数
|
||||||
|
private String messageUrl;
|
||||||
|
|
||||||
|
private List<String> phoneList;
|
||||||
|
|
||||||
|
private List<String> userIdList;
|
||||||
|
|
||||||
|
private List<String> userNamesList;
|
||||||
|
|
||||||
|
private Boolean isNoticeAll = false;
|
||||||
|
|
||||||
|
// oa类型
|
||||||
|
private String oaType;
|
||||||
|
|
||||||
|
// 优先级
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
// 消息类型,用于优先级队列
|
||||||
|
private MessageType messageType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Request other) {
|
||||||
|
return Integer.compare(other.priority,this.priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package org.dromara.oa.comm.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Response {
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 厂商原返回体
|
||||||
|
*/
|
||||||
|
private Object data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置标识名 如未配置取对应渠道名例如
|
||||||
|
*/
|
||||||
|
private String oaConfigId;
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.dromara.oa.comm.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SignTimesTamp {
|
||||||
|
String sign;
|
||||||
|
|
||||||
|
Long timestamp;
|
||||||
|
|
||||||
|
public SignTimesTamp(String newSign, Long timestamp) {
|
||||||
|
this.sign = newSign;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package org.dromara.oa.comm.enums;
|
||||||
|
|
||||||
|
public enum MessageType {
|
||||||
|
// text 类型
|
||||||
|
TEXT("text"),
|
||||||
|
|
||||||
|
// md 格式
|
||||||
|
MARKDOWN("markdown"),
|
||||||
|
|
||||||
|
// link 格式
|
||||||
|
LINK("link");
|
||||||
|
|
||||||
|
MessageType(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package org.dromara.oa.comm.enums;
|
||||||
|
|
||||||
|
public enum OaType {
|
||||||
|
DINGTALK("dingding", "https://oapi.dingtalk.com/robot/send?access_token=", true),
|
||||||
|
WETALK("wetalk", "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=", true),
|
||||||
|
BYTETALK("feishu", "https://open.feishu.cn/open-apis/bot/v2/hook/", true),
|
||||||
|
;
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
private String robotUrl;
|
||||||
|
|
||||||
|
|
||||||
|
OaType(String type, String robotUrl, boolean enabled) {
|
||||||
|
this.type = type;
|
||||||
|
this.robotUrl = robotUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return robotUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package org.dromara.oa.comm.errors;
|
||||||
|
|
||||||
|
public class OaException extends RuntimeException {
|
||||||
|
|
||||||
|
public OaException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OaException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OaException(String message,String configId) {
|
||||||
|
super("configId为{"+configId+"}抛出异常:"+message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package org.dromara.oa.comm.task.delayed;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
public class DelayedTime {
|
||||||
|
|
||||||
|
private final Timer timer = new Timer(true);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延迟队列添加新任务
|
||||||
|
*/
|
||||||
|
public void schedule(TimerTask task, long delay) {
|
||||||
|
timer.schedule(task,delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
58
sms4j-oa-plugin/sms4j-oa-core/pom.xml
Normal file
58
sms4j-oa-plugin/sms4j-oa-core/pom.xml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j-oa-plugin</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>sms4j-oa-core</artifactId>
|
||||||
|
<name>sms4j-oa-core</name>
|
||||||
|
<description>sms4j-oa-core</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j-oa-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-json</artifactId>
|
||||||
|
<version>5.8.18</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>com.feishu.openplatform</groupId>-->
|
||||||
|
<!-- <artifactId>feishu-sdk-java</artifactId>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
|
||||||
|
<!--配置文件提示-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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.OaAbstractProviderFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 飞书通知对象建造
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-10-22 21:00
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class ByteTalkFactory extends OaAbstractProviderFactory<ByteTalkOaImpl, ByteTalkConfig> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
* 2023-10-22 21:01
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ByteTalkOaImpl extends AbstractOaBlend<ByteTalkConfig> {
|
||||||
|
|
||||||
|
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<String, String> getHeaders() {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", "application/json");
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
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
|
||||||
|
* 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<String> userNamesList = request.getUserNamesList();
|
||||||
|
Boolean isNoticeAll = request.getIsNoticeAll();
|
||||||
|
if (isNoticeAll) {
|
||||||
|
content.append("<at user_id=\"all\">所有人</at>");
|
||||||
|
}
|
||||||
|
userNamesList.forEach(l -> content.append("<at user_id=\"ou_xxx\">").append(l).append("</at>"));
|
||||||
|
content.append(request.getContent());
|
||||||
|
JSONObject text = new JSONObject();
|
||||||
|
text.set("text", content);
|
||||||
|
message.set("content", text);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
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.config.OaConfig;
|
||||||
|
import org.dromara.oa.core.provider.factory.OaBaseProviderFactory;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册工厂, 读取yaml配置并根据配置生成对象
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-10-22 12:39
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class OaBlendsInitializer {
|
||||||
|
private List<OaBaseProviderFactory<? extends OaSender, ? extends OaSupplierConfig>> factoryList;
|
||||||
|
|
||||||
|
private final OaConfig oaConfig;
|
||||||
|
private final Map<String, Map<String, Object>> blends;
|
||||||
|
|
||||||
|
public OaBlendsInitializer(
|
||||||
|
List<OaBaseProviderFactory<? extends OaSender, ? extends OaSupplierConfig>> factoryList,
|
||||||
|
OaConfig oaConfig,
|
||||||
|
Map<String, Map<String, Object>> oas
|
||||||
|
) {
|
||||||
|
this.factoryList=factoryList;
|
||||||
|
this.oaConfig = oaConfig;
|
||||||
|
this.blends = oas;
|
||||||
|
onApplicationEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onApplicationEvent() {
|
||||||
|
registerDefaultFactory();
|
||||||
|
// 解析供应商配置
|
||||||
|
for (String configId : blends.keySet()) {
|
||||||
|
Map<String, Object> configMap = blends.get(configId);
|
||||||
|
if (Boolean.FALSE.equals(configMap.get("isEnable"))) {
|
||||||
|
log.warn("configId为"+configId+"的配置未启用,请注意是否需要开启");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Object supplierObj = configMap.get(OaContent.SUPPLIER_KEY);
|
||||||
|
String supplier = supplierObj == null ? "" : String.valueOf(supplierObj);
|
||||||
|
supplier = StrUtil.isEmpty(supplier) ? configId : supplier;
|
||||||
|
OaBaseProviderFactory<OaSender, OaSupplierConfig> providerFactory = (OaBaseProviderFactory<OaSender, OaSupplierConfig>) 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package org.dromara.oa.core.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.dromara.oa.comm.task.delayed.DelayedTime;
|
||||||
|
import org.dromara.oa.core.provider.config.OaConfig;
|
||||||
|
import org.dromara.oa.core.provider.factory.OaBeanFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-11-01 18:05
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class OaMainConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConfigurationProperties(prefix = "sms-oa") //指定配置文件注入属性前缀
|
||||||
|
@ConditionalOnProperty(prefix = "sms-oa", name = "config-type", havingValue = "yaml")
|
||||||
|
protected OaConfig oaConfig() {
|
||||||
|
return OaBeanFactory.getSmsConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入一个定时器
|
||||||
|
*/
|
||||||
|
@Bean("oaDelayedTime")
|
||||||
|
@Lazy
|
||||||
|
protected DelayedTime delayedTime() {
|
||||||
|
return OaBeanFactory.getDelayedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入线程池
|
||||||
|
*/
|
||||||
|
@Bean("oaExecutor")
|
||||||
|
protected Executor taskExecutor(OaConfig config) {
|
||||||
|
return OaBeanFactory.setExecutor(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package org.dromara.oa.core.config;
|
||||||
|
|
||||||
|
import org.dromara.oa.api.OaSender;
|
||||||
|
import org.dromara.oa.core.provider.config.OaConfig;
|
||||||
|
import org.dromara.oa.core.provider.factory.OaBaseProviderFactory;
|
||||||
|
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.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-10-22 12:50
|
||||||
|
*/
|
||||||
|
public class OaSupplierConfig {
|
||||||
|
/**
|
||||||
|
* 注入配置
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConfigurationProperties(prefix = "sms-oa.oas")
|
||||||
|
@ConditionalOnProperty(prefix = "sms-oa", name = "config-type", havingValue = "yaml")
|
||||||
|
protected Map<String, Map<String, Object>> oas() {
|
||||||
|
return new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
protected OaBlendsInitializer smsOasInitializer(
|
||||||
|
List<OaBaseProviderFactory<? extends OaSender, ? extends org.dromara.oa.comm.config.OaSupplierConfig>> factoryList,
|
||||||
|
OaConfig oaConfig,
|
||||||
|
Map<String, Map<String, Object>> oas) {
|
||||||
|
return new OaBlendsInitializer(factoryList,oaConfig,oas);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
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.OaAbstractProviderFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉通知对象建造
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-10-22 21:00
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class DingTalkFactory extends OaAbstractProviderFactory<DingTalkOaImpl, DingTalkConfig> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
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.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 java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import static org.dromara.oa.comm.enums.OaType.DINGTALK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-10-22 21:01
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DingTalkOaImpl extends AbstractOaBlend<DingTalkConfig> {
|
||||||
|
|
||||||
|
private HttpClientImpl httpClient = new HttpClientImpl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建造一个微信通知对象服务
|
||||||
|
*/
|
||||||
|
public DingTalkOaImpl(DingTalkConfig config) {
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建造一个微信通知对象服务
|
||||||
|
*/
|
||||||
|
public DingTalkOaImpl(DingTalkConfig config, Executor pool) {
|
||||||
|
super(config,pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSupplier() {
|
||||||
|
return getConfig().getSupplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response sender(Request request, MessageType messageType) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(10000L);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (Objects.isNull(request.getContent())) {
|
||||||
|
throw new OaException("消息体content不能为空",getConfig().getConfigId());
|
||||||
|
}
|
||||||
|
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(),getConfig().getConfigId());
|
||||||
|
}
|
||||||
|
// 后续解析响应体提取errorCode判断是否成功
|
||||||
|
return new Response(true, post, config.getConfigId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> getHeaders() {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", "application/json");
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package org.dromara.oa.core.dingTalk.utils;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONArray;
|
||||||
|
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 java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static org.dromara.oa.comm.enums.MessageType.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉通知签名和信息构建
|
||||||
|
* @author dongfeng
|
||||||
|
* 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());
|
||||||
|
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());
|
||||||
|
message.set("markdown", markdown);
|
||||||
|
} else if (messageType == LINK) {
|
||||||
|
message.set("msgtype", "link");
|
||||||
|
JSONObject link = new JSONObject();
|
||||||
|
link.set("text", request.getContent());
|
||||||
|
link.set("title", request.getTitle());
|
||||||
|
link.set("picUrl", request.getPicUrl());
|
||||||
|
link.set("messageUrl", request.getMessageUrl());
|
||||||
|
message.set("link", link);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 处理提到的人
|
||||||
|
JSONObject at = new JSONObject();
|
||||||
|
List<String> phoneList = request.getPhoneList();
|
||||||
|
List<String> userIdList = request.getUserIdList();
|
||||||
|
if(!Objects.isNull(phoneList)){
|
||||||
|
JSONArray phoneArray = new JSONArray();
|
||||||
|
phoneList.forEach(phoneArray::set);
|
||||||
|
at.set("atMobiles", phoneArray);
|
||||||
|
}
|
||||||
|
if(!Objects.isNull(userIdList)){
|
||||||
|
JSONArray userIdArray = new JSONArray();
|
||||||
|
userIdList.forEach(userIdArray::set);
|
||||||
|
at.set("atUserIds", userIdArray);
|
||||||
|
}
|
||||||
|
at.set("isAtAll", request.getIsNoticeAll());
|
||||||
|
message.set("at", at);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package org.dromara.oa.core.provider.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Oa线程池配置
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-11-01 17:55
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class OaConfig {
|
||||||
|
/**
|
||||||
|
* 核心线程池大小
|
||||||
|
*/
|
||||||
|
private Integer corePoolSize = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大线程数
|
||||||
|
*/
|
||||||
|
private Integer maxPoolSize = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 队列容量
|
||||||
|
*/
|
||||||
|
private Integer queueCapacity = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
|
||||||
|
*/
|
||||||
|
private Boolean shutdownStrategy = true;
|
||||||
|
}
|
||||||
@ -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 OaAbstractProviderFactory<S extends OaSender, C extends OaSupplierConfig> implements OaBaseProviderFactory<S, C> {
|
||||||
|
|
||||||
|
private Class<C> configClass;
|
||||||
|
|
||||||
|
public OaAbstractProviderFactory() {
|
||||||
|
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<C>) typeArguments[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配置类
|
||||||
|
*
|
||||||
|
* @return 配置类
|
||||||
|
*/
|
||||||
|
public Class<C> getConfigClass() {
|
||||||
|
return configClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package org.dromara.oa.core.provider.factory;
|
||||||
|
|
||||||
|
import org.dromara.oa.api.OaSender;
|
||||||
|
import org.dromara.oa.comm.config.OaSupplierConfig;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
public interface OaBaseProviderFactory<S extends OaSender, C extends OaSupplierConfig> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建通知webhook实现对象
|
||||||
|
*
|
||||||
|
* @param c 通知webhook配置对象
|
||||||
|
* @return 通知webhook实现对象
|
||||||
|
*/
|
||||||
|
S createSmsOa(C c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配置类
|
||||||
|
*
|
||||||
|
* @return 配置类
|
||||||
|
*/
|
||||||
|
Class<C> getConfigClass();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取供应商
|
||||||
|
*
|
||||||
|
* @return 供应商
|
||||||
|
*/
|
||||||
|
String getSupplier();
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
package org.dromara.oa.core.provider.factory;
|
||||||
|
|
||||||
|
import org.dromara.oa.comm.task.delayed.DelayedTime;
|
||||||
|
import org.dromara.oa.comm.entity.Request;
|
||||||
|
import org.dromara.oa.core.provider.config.OaConfig;
|
||||||
|
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
public class OaBeanFactory {
|
||||||
|
|
||||||
|
/** 定时器*/
|
||||||
|
private static DelayedTime delayedTime;
|
||||||
|
|
||||||
|
/** 线程池*/
|
||||||
|
private static Executor executor;
|
||||||
|
|
||||||
|
/** 核心配置信息*/
|
||||||
|
private static OaConfig oaConfig;
|
||||||
|
|
||||||
|
/** 优先级队列*/
|
||||||
|
private static PriorityBlockingQueue<Request> priorityBlockingQueue;
|
||||||
|
|
||||||
|
/** 优先级队列*/
|
||||||
|
private static Boolean priorityExecutorThreadStatus = false;
|
||||||
|
|
||||||
|
private OaBeanFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DelayedTime getDelayedTime() {
|
||||||
|
if (delayedTime == null){
|
||||||
|
delayedTime = new DelayedTime();
|
||||||
|
}
|
||||||
|
return delayedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Executor setExecutor(OaConfig config) {
|
||||||
|
if (executor == null){
|
||||||
|
// 创建一个线程池对象
|
||||||
|
ThreadPoolExecutor ex = new ThreadPoolExecutor(
|
||||||
|
config.getCorePoolSize(),
|
||||||
|
config.getMaxPoolSize(),
|
||||||
|
config.getQueueCapacity(),
|
||||||
|
TimeUnit.SECONDS,
|
||||||
|
new ArrayBlockingQueue<>(config.getMaxPoolSize())
|
||||||
|
);
|
||||||
|
// 线程池对拒绝任务的处理策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
|
||||||
|
ex.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
executor = ex;
|
||||||
|
}
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PriorityBlockingQueue<Request> initPriorityBlockingQueue() {
|
||||||
|
if (priorityBlockingQueue == null){
|
||||||
|
// 创建一个线程池对象
|
||||||
|
priorityBlockingQueue=new PriorityBlockingQueue<>();
|
||||||
|
}
|
||||||
|
return priorityBlockingQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Executor getExecutor() {
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OaConfig getSmsConfig(){
|
||||||
|
if (oaConfig == null){
|
||||||
|
oaConfig = new OaConfig();
|
||||||
|
}
|
||||||
|
return oaConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PriorityBlockingQueue<Request> getPriorityBlockingQueue(){
|
||||||
|
return priorityBlockingQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean getPriorityExecutorThreadStatus() {
|
||||||
|
return priorityExecutorThreadStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean setPriorityExecutorThreadStatus(Boolean bo) {
|
||||||
|
priorityExecutorThreadStatus=bo;
|
||||||
|
return priorityExecutorThreadStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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<String, OaSender> configs = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>创建各个厂商的实现类
|
||||||
|
*
|
||||||
|
* @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) {
|
||||||
|
OaBaseProviderFactory factory = ProviderFactoryHolder.requireForSupplier(config.getSupplier());
|
||||||
|
if (factory == null) {
|
||||||
|
throw new OaException("不支持"+config.getSupplier()+"供应商配置");
|
||||||
|
}
|
||||||
|
return factory.createSmsOa(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过configId获取通知webhook服务对象
|
||||||
|
*
|
||||||
|
* @param configId 唯一标识
|
||||||
|
* @return 返回通知webhook服务对象。如果未找到则返回null
|
||||||
|
*/
|
||||||
|
public static OaSender getSmsOaBlend(String configId) {
|
||||||
|
return configs.get(configId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
* 2023-10-22 21:12
|
||||||
|
*/
|
||||||
|
public class ProviderFactoryHolder {
|
||||||
|
|
||||||
|
|
||||||
|
private static final Map<String, OaBaseProviderFactory<? extends OaSender, ? extends OaSupplierConfig>> factories = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static void registerFactory(OaBaseProviderFactory<? extends OaSender, ? extends OaSupplierConfig> factory) {
|
||||||
|
if (factory == null) {
|
||||||
|
throw new OaException("注册供应商工厂失败,工厂实例不能为空");
|
||||||
|
}
|
||||||
|
factories.put(factory.getSupplier(), factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerFactory(List<OaBaseProviderFactory<? extends OaSender, ? extends OaSupplierConfig>> factoryList) {
|
||||||
|
if (CollUtil.isEmpty(factoryList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (OaBaseProviderFactory<? extends OaSender, ? extends OaSupplierConfig> factory : factoryList) {
|
||||||
|
if (factory == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
registerFactory(factory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OaBaseProviderFactory<? extends OaSender, ? extends OaSupplierConfig> requireForSupplier(String supplier) {
|
||||||
|
return factories.getOrDefault(supplier, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package org.dromara.oa.core.provider.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.dromara.oa.api.OaCallBack;
|
||||||
|
import org.dromara.oa.api.OaSender;
|
||||||
|
import org.dromara.oa.comm.config.OaSupplierConfig;
|
||||||
|
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.core.provider.factory.OaBeanFactory;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-10-22 21:03
|
||||||
|
*/
|
||||||
|
public abstract class AbstractOaBlend<C extends OaSupplierConfig> implements OaSender {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final String configId;
|
||||||
|
|
||||||
|
private final C config;
|
||||||
|
|
||||||
|
protected final Executor pool;
|
||||||
|
|
||||||
|
protected final PriorityBlockingQueue<Request> priorityQueueMap;
|
||||||
|
|
||||||
|
protected AbstractOaBlend(C config, Executor pool) {
|
||||||
|
this.configId = StrUtil.isEmpty(config.getConfigId()) ? getSupplier() : config.getConfigId();
|
||||||
|
this.config = config;
|
||||||
|
this.pool = pool;
|
||||||
|
this.priorityQueueMap = OaBeanFactory.initPriorityBlockingQueue();
|
||||||
|
priorityQueueMapThreadInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractOaBlend(C config) {
|
||||||
|
this.configId = StrUtil.isEmpty(config.getConfigId()) ? getSupplier() : config.getConfigId();
|
||||||
|
this.config = config;
|
||||||
|
this.pool = OaBeanFactory.getExecutor();
|
||||||
|
this.priorityQueueMap = OaBeanFactory.initPriorityBlockingQueue();
|
||||||
|
priorityQueueMapThreadInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected C getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void priorityQueueMapThreadInit() {
|
||||||
|
Boolean status = OaBeanFactory.getPriorityExecutorThreadStatus();
|
||||||
|
if(Boolean.FALSE.equals(status)){
|
||||||
|
OaBeanFactory.setPriorityExecutorThreadStatus(true);
|
||||||
|
pool.execute(() -> {
|
||||||
|
Thread.currentThread().setName("oa-priorityQueueMap-thread");
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
Request request = priorityQueueMap.poll();
|
||||||
|
if (!Objects.isNull(request)) {
|
||||||
|
pool.execute(() -> {
|
||||||
|
System.out.println("优先级为"+request.getPriority()+"已发送");
|
||||||
|
sender(request, request.getMessageType());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public final void senderAsync(Request request, MessageType messageType) {
|
||||||
|
pool.execute(() -> sender(request, messageType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void senderAsync(Request request, MessageType messageType, OaCallBack callBack) {
|
||||||
|
CompletableFuture<Response> future = CompletableFuture.supplyAsync(() -> sender(request, messageType));
|
||||||
|
future.thenAcceptAsync(callBack::callBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void senderAsyncByPriority(Request request, MessageType messageType) {
|
||||||
|
request.setMessageType(messageType);
|
||||||
|
priorityQueueMap.offer(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package org.dromara.oa.core.support;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class AbstractHttpClient implements MsgHttpClient {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(String url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(String url, Map<String, String> headers) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String post(String url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String post(String url, Map<String, String> headers) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package org.dromara.oa.core.support;
|
||||||
|
|
||||||
|
import cn.hutool.http.HttpRequest;
|
||||||
|
import cn.hutool.http.HttpResponse;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class HttpClientImpl extends AbstractHttpClient {
|
||||||
|
@Override
|
||||||
|
public <T> String post(StringBuilder url, Map<String, String> headers, T message) throws Exception {
|
||||||
|
// 构建请求体
|
||||||
|
// 发送POST请求
|
||||||
|
HttpResponse response = HttpRequest.post(url.toString())
|
||||||
|
.headerMap(headers, true)
|
||||||
|
.body(message.toString())
|
||||||
|
.execute();
|
||||||
|
return response.body();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package org.dromara.oa.core.support;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface MsgHttpClient {
|
||||||
|
|
||||||
|
String get(String url);
|
||||||
|
|
||||||
|
String get(String url, Map<String, String> headers);
|
||||||
|
|
||||||
|
String post(String url);
|
||||||
|
|
||||||
|
String post(String url, Map<String, String> headers);
|
||||||
|
|
||||||
|
<T> String post(StringBuilder url, Map<String, String> headers, T message) throws Exception;
|
||||||
|
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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.OaAbstractProviderFactory;
|
||||||
|
import org.dromara.oa.core.weTalk.service.WeTalkOaImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信通知对象建造
|
||||||
|
* @author dongfeng
|
||||||
|
* 2023-10-22 21:00
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class WeTalkFactory extends OaAbstractProviderFactory<WeTalkOaImpl, WeTalkConfig> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
* 2023-10-22 21:01
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class WeTalkOaImpl extends AbstractOaBlend<WeTalkConfig> {
|
||||||
|
|
||||||
|
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<String, String> getHeaders() {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", "application/json");
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
* 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<String> userIdList = request.getUserIdList();
|
||||||
|
List<String> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
org.dromara.oa.core.config.OaMainConfig,\
|
||||||
|
org.dromara.oa.core.config.OaSupplierConfig
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
org.dromara.oa.core.config.OaMainConfig
|
||||||
|
org.dromara.oa.core.config.OaSupplierConfig
|
||||||
@ -0,0 +1 @@
|
|||||||
|
|
||||||
@ -29,6 +29,11 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
|
<artifactId>sms4j-oa-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara.sms4j</groupId>
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
<artifactId>sms4j-spring-boot-starter</artifactId>
|
<artifactId>sms4j-spring-boot-starter</artifactId>
|
||||||
|
|||||||
@ -85,4 +85,22 @@ sms:
|
|||||||
templateId: d2a****777
|
templateId: d2a****777
|
||||||
appId: 64c52d2a****77775fe72e3
|
appId: 64c52d2a****77775fe72e3
|
||||||
sid: d2a****777
|
sid: d2a****777
|
||||||
url: https://sms.idowe.com/**/**/**/send
|
url: https://sms.idowe.com/**/**/**/send
|
||||||
|
sms-oa:
|
||||||
|
config-type: yaml
|
||||||
|
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
|
||||||
|
core-pool-size: 20
|
||||||
|
queue-capacity: 20
|
||||||
|
max-pool-size: 20
|
||||||
@ -0,0 +1,209 @@
|
|||||||
|
package org.dromara.sms4j.example;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.oa.api.OaSender;
|
||||||
|
import org.dromara.oa.comm.entity.Request;
|
||||||
|
import org.dromara.oa.comm.enums.MessageType;
|
||||||
|
import org.dromara.oa.core.dingTalk.config.DingTalkConfig;
|
||||||
|
import org.dromara.oa.core.provider.factory.OaFactory;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@SpringBootTest
|
||||||
|
public class SmsOaTest {
|
||||||
|
//***********************DingTalk-Test************************//
|
||||||
|
/**
|
||||||
|
* 填测试手机号
|
||||||
|
*/
|
||||||
|
private static final String DingTalkPHONE = "";
|
||||||
|
/**
|
||||||
|
* 填access_token
|
||||||
|
*/
|
||||||
|
private static final String DingTalkTOKENID = "";
|
||||||
|
/**
|
||||||
|
* 填secret
|
||||||
|
*/
|
||||||
|
private static final String DingTalkSIGN = "";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DingTalk的Text测试
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void oaDingTalkText() {
|
||||||
|
String key = "oaDingTalk";
|
||||||
|
DingTalkConfig dingTalkConfig = new DingTalkConfig();
|
||||||
|
dingTalkConfig.setConfigId(key);
|
||||||
|
dingTalkConfig.setSign(DingTalkSIGN);
|
||||||
|
dingTalkConfig.setTokenId(DingTalkTOKENID);
|
||||||
|
|
||||||
|
// 根据配置创建服务实例并注册
|
||||||
|
OaFactory.createAndRegisterOaSender(dingTalkConfig);
|
||||||
|
OaSender alarm = OaFactory.getSmsOaBlend(key);
|
||||||
|
|
||||||
|
Request request = new Request();
|
||||||
|
ArrayList<String> phones = new ArrayList<>();
|
||||||
|
phones.add(DingTalkPHONE);
|
||||||
|
//测试text
|
||||||
|
request.setPhoneList(phones);
|
||||||
|
request.setIsNoticeAll(false);
|
||||||
|
request.setContent("测试消息");
|
||||||
|
request.setTitle("测试消息标题");
|
||||||
|
|
||||||
|
// 异步发送方式
|
||||||
|
alarm.senderAsync(request, MessageType.TEXT);
|
||||||
|
alarm.senderAsync(request, MessageType.TEXT, smsResponse -> System.out.println("ConfigId为" + smsResponse.getOaConfigId() + "的异步任务发送成功"));
|
||||||
|
|
||||||
|
System.out.println("异步任务已全部提交");
|
||||||
|
System.out.println("下面是同步任务");
|
||||||
|
|
||||||
|
alarm.sender(request, MessageType.TEXT);
|
||||||
|
|
||||||
|
System.out.println("同步任务已执行完");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DingTalk的Markdown测试
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void oaDingTalkMarkdown() {
|
||||||
|
String key = "oaDingTalk";
|
||||||
|
DingTalkConfig dingTalkConfig = new DingTalkConfig();
|
||||||
|
dingTalkConfig.setConfigId(key);
|
||||||
|
dingTalkConfig.setSign(DingTalkSIGN);
|
||||||
|
dingTalkConfig.setTokenId(DingTalkTOKENID);
|
||||||
|
|
||||||
|
// 根据配置创建服务实例并注册
|
||||||
|
OaFactory.createAndRegisterOaSender(dingTalkConfig);
|
||||||
|
OaSender alarm = OaFactory.getSmsOaBlend(key);
|
||||||
|
|
||||||
|
Request request = new Request();
|
||||||
|
ArrayList<String> phones = new ArrayList<>();
|
||||||
|
phones.add(DingTalkPHONE);
|
||||||
|
request.setPhoneList(phones);
|
||||||
|
request.setIsNoticeAll(true);
|
||||||
|
request.setContent("#### 杭州天气 @150XXXXXXXX \n > 9度,西北风1级,空气良89,相对温度73%\n > \n > ###### 10点20分发布 [天气](https://www.dingtalk.com) \n");
|
||||||
|
request.setTitle("测试消息标题");
|
||||||
|
alarm.senderAsync(request, MessageType.MARKDOWN);
|
||||||
|
|
||||||
|
alarm.senderAsync(request, MessageType.MARKDOWN, smsResponse -> System.out.println("ConfigId为" + smsResponse.getOaConfigId() + "的异步任务发送成功"));
|
||||||
|
|
||||||
|
System.out.println("异步任务已全部提交");
|
||||||
|
System.out.println("下面是同步任务");
|
||||||
|
|
||||||
|
alarm.sender(request, MessageType.MARKDOWN);
|
||||||
|
|
||||||
|
System.out.println("同步任务已执行完");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DingTalk的Link测试
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void oaDingTalkLink() {
|
||||||
|
String key = "oaDingTalk";
|
||||||
|
DingTalkConfig dingTalkConfig = new DingTalkConfig();
|
||||||
|
dingTalkConfig.setConfigId(key);
|
||||||
|
dingTalkConfig.setSign(DingTalkSIGN);
|
||||||
|
dingTalkConfig.setTokenId(DingTalkTOKENID);
|
||||||
|
|
||||||
|
// 根据配置创建服务实例并注册
|
||||||
|
OaFactory.createAndRegisterOaSender(dingTalkConfig);
|
||||||
|
OaSender alarm = OaFactory.getSmsOaBlend(key);
|
||||||
|
|
||||||
|
Request request = new Request();
|
||||||
|
ArrayList<String> phones = new ArrayList<>();
|
||||||
|
phones.add(DingTalkPHONE);
|
||||||
|
// 测试link
|
||||||
|
request.setPhoneList(phones);
|
||||||
|
request.setIsNoticeAll(true);
|
||||||
|
request.setContent("这个即将发布的新版本,创始人xx称它为红树林。而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是红树林");
|
||||||
|
request.setTitle("点击跳转到钉钉");
|
||||||
|
request.setMessageUrl("https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI");
|
||||||
|
request.setPicUrl("https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png");
|
||||||
|
|
||||||
|
alarm.senderAsync(request, MessageType.LINK);
|
||||||
|
|
||||||
|
alarm.senderAsync(request, MessageType.LINK, smsResponse -> System.out.println("ConfigId为" + smsResponse.getOaConfigId() + "的异步任务发送成功"));
|
||||||
|
|
||||||
|
System.out.println("异步任务已全部提交");
|
||||||
|
System.out.println("下面是同步任务");
|
||||||
|
|
||||||
|
alarm.sender(request, MessageType.LINK);
|
||||||
|
|
||||||
|
System.out.println("同步任务已执行完");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步优先级尽可能优先级高的消息先发送,但是获取响应会受网络影响
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void oaDingTalkAsyncByPriority() {
|
||||||
|
String key = "oaDingTalk";
|
||||||
|
DingTalkConfig dingTalkConfig = new DingTalkConfig();
|
||||||
|
dingTalkConfig.setConfigId(key);
|
||||||
|
dingTalkConfig.setSign(DingTalkSIGN);
|
||||||
|
dingTalkConfig.setTokenId(DingTalkTOKENID);
|
||||||
|
|
||||||
|
// 根据配置创建服务实例并注册
|
||||||
|
OaFactory.createAndRegisterOaSender(dingTalkConfig);
|
||||||
|
OaSender alarm = OaFactory.getSmsOaBlend(key);
|
||||||
|
|
||||||
|
// 模拟10条不同优先级的消息
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
Random random = new Random();
|
||||||
|
int priority = random.nextInt(10);
|
||||||
|
|
||||||
|
Request request = new Request();
|
||||||
|
ArrayList<String> phones = new ArrayList<>();
|
||||||
|
phones.add(DingTalkPHONE);
|
||||||
|
request.setPhoneList(phones);
|
||||||
|
request.setIsNoticeAll(false);
|
||||||
|
request.setPriority(priority);
|
||||||
|
request.setTitle("优先级为" + priority);
|
||||||
|
//测试-1-TEXT
|
||||||
|
// request.setContent("该消息优先级为"+ priority);
|
||||||
|
// alarm.senderAsyncByPriority(request, MessageType.TEXT);
|
||||||
|
// System.out.println("优先级为"+priority+"的异步任务已提交");
|
||||||
|
|
||||||
|
// 测试-2-MARKDOWN
|
||||||
|
// request.setContent("该消息优先级为"+ priority+"\n#### ++杭州天气 @150XXXXXXXX \n > 9度,西北风1级,空气良89,相对温度73%\n > \n > ###### 10点20分发布 [天气](https://www.dingtalk.com) \n");
|
||||||
|
// alarm.senderAsyncByPriority(request, MessageType.MARKDOWN);
|
||||||
|
|
||||||
|
// 测试-3-LINK
|
||||||
|
request.setContent("该消息优先级为" + priority + "这个即将发布的新版本,创始人xx称它为红树林。而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是红树林");
|
||||||
|
request.setMessageUrl("https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI");
|
||||||
|
request.setPicUrl("https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png");
|
||||||
|
|
||||||
|
// 发送
|
||||||
|
alarm.senderAsyncByPriority(request, MessageType.LINK);
|
||||||
|
|
||||||
|
System.out.println("优先级为" + priority + "的异步任务已提交");
|
||||||
|
}
|
||||||
|
while (true){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void oaDingTalkByYamlTest() {
|
||||||
|
String configId = "oaDingTalkByYaml";
|
||||||
|
OaSender alarm = OaFactory.getSmsOaBlend(configId);
|
||||||
|
Request request = new Request();
|
||||||
|
ArrayList<String> phones = new ArrayList<>();
|
||||||
|
phones.add(DingTalkPHONE);
|
||||||
|
request.setPhoneList(phones);
|
||||||
|
request.setIsNoticeAll(false);
|
||||||
|
request.setContent("HertzBeat");
|
||||||
|
request.setTitle("HertzBeat");
|
||||||
|
alarm.sender(request, MessageType.TEXT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user