package com.xcong.excoin.utils.dingtalk;
|
|
import com.dingtalk.api.DefaultDingTalkClient;
|
import com.dingtalk.api.DingTalkClient;
|
import com.dingtalk.api.request.OapiRobotSendRequest;
|
import com.dingtalk.api.response.OapiRobotSendResponse;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.codec.binary.Base64;
|
|
import javax.crypto.Mac;
|
import javax.crypto.spec.SecretKeySpec;
|
import java.net.URLEncoder;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
/**
|
* 钉钉群机器人消息发送工具。
|
*
|
* <h3>快速使用</h3>
|
* <pre>
|
* // 1. 创建发送器
|
* DingTalkRobot robot = new DingTalkRobot(accessToken, secret);
|
*
|
* // 2a. 发送文本
|
* robot.sendText("充值到账:1000 USDT");
|
*
|
* // 2b. 发送 Markdown(title 会作为 @所有人 通知标题)
|
* robot.sendMarkdown("策略通知", "## 盈亏汇总\n- 已实现: **+2.5 USDT**");
|
*
|
* // 2c. 发送 ActionCard
|
* robot.sendActionCard("止盈通知", "策略已达止盈目标", "查看详情", "https://xxx.com");
|
* </pre>
|
*
|
* @author wzy
|
*/
|
@Slf4j
|
public class DingTalkUtils {
|
|
private static final String ROBOT_URL = "https://oapi.dingtalk.com/robot/send?access_token=";
|
|
private final String accessToken;
|
private final String secret;
|
|
// ==================== 默认实例(向后兼容旧代码) ====================
|
|
private static volatile DingTalkUtils DEFAULT;
|
|
public static DingTalkUtils getDefault() {
|
if (DEFAULT == null) {
|
synchronized (DingTalkUtils.class) {
|
if (DEFAULT == null) {
|
// DEFAULT = new DingTalkUtils(
|
// "57a3e695f78d7547fe20fb7aef82cf35a27de1846bbc6966e0194761976d7597",
|
// "SECd59a93c8939eeaef0d97b5b714639df4af95d922002d0a440bc82ad42710a89e");
|
DEFAULT = new DingTalkUtils(
|
"e357a3417991da86a5f79ea5bc8785b529c1da8b9d27458febed3b3d10c857c4",
|
"SECf2b819e930cb4b367cf599f11a30eb8a5d0f4b0b1c069a57aa15328a3feebf8c");
|
}
|
}
|
}
|
return DEFAULT;
|
}
|
|
/**
|
* 向后兼容:按类型代码发送 ActionCard 通知。
|
*
|
* @param typeIndex {@link DingTalkType} 的 index
|
*/
|
public static void sendActionCard(int typeIndex) {
|
DingTalkType dt = DingTalkType.byIndex(typeIndex);
|
if (dt == null) {
|
log.warn("[DingTalk] 未知通知类型: {}", typeIndex);
|
return;
|
}
|
getDefault().sendActionCard(dt.getName(), dt.getName(), "查看详情", "http://baidu.com");
|
}
|
|
// ==================== 构造 ====================
|
|
/**
|
* @param accessToken 钉钉机器人 Webhook 地址中的 access_token
|
* @param secret 钉钉机器人安全设置中的加签密钥
|
*/
|
public DingTalkUtils(String accessToken, String secret) {
|
this.accessToken = accessToken;
|
this.secret = secret;
|
}
|
|
// ==================== 文本消息 ====================
|
|
/**
|
* 发送纯文本消息。
|
*
|
* @param content 文本内容,最大 4096 字节,支持 @手机号 提醒
|
*/
|
public void sendText(String content) {
|
OapiRobotSendRequest req = new OapiRobotSendRequest();
|
req.setMsgtype("text");
|
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
|
text.setContent(content);
|
req.setText(text);
|
execute(req);
|
}
|
|
// ==================== Markdown 消息 ====================
|
|
/**
|
* 发送 Markdown 消息。
|
*
|
* @param title 消息标题,显示在会话列表和推送通知中
|
* @param markdown Markdown 正文,支持标题/加粗/链接/列表等语法
|
*/
|
public void sendMarkdown(String title, String markdown) {
|
OapiRobotSendRequest req = new OapiRobotSendRequest();
|
req.setMsgtype("markdown");
|
OapiRobotSendRequest.Markdown md = new OapiRobotSendRequest.Markdown();
|
md.setTitle(title);
|
md.setText(markdown);
|
req.setMarkdown(md);
|
execute(req);
|
}
|
|
// ==================== ActionCard 消息 ====================
|
|
/**
|
* 发送单按钮 ActionCard 消息。
|
*
|
* @param title 卡片标题
|
* @param text 卡片正文(支持 Markdown,减号开头的行会被转为列表)
|
* @param btnTitle 按钮文字
|
* @param btnUrl 按钮跳转链接
|
*/
|
public void sendActionCard(String title, String text, String btnTitle, String btnUrl) {
|
OapiRobotSendRequest req = new OapiRobotSendRequest();
|
req.setMsgtype("actionCard");
|
OapiRobotSendRequest.Actioncard card = new OapiRobotSendRequest.Actioncard();
|
card.setTitle(title);
|
card.setText(text);
|
card.setBtnOrientation("1");
|
List<OapiRobotSendRequest.Btns> btns = new ArrayList<>();
|
OapiRobotSendRequest.Btns btn = new OapiRobotSendRequest.Btns();
|
btn.setTitle(btnTitle);
|
btn.setActionURL(btnUrl);
|
btns.add(btn);
|
card.setBtns(btns);
|
req.setActionCard(card);
|
execute(req);
|
}
|
|
// ==================== 内部执行 ====================
|
|
private void execute(OapiRobotSendRequest request) {
|
try {
|
Long timestamp = System.currentTimeMillis();
|
String sign = generateSign(timestamp);
|
String url = ROBOT_URL + accessToken + "×tamp=" + timestamp + "&sign=" + sign;
|
DingTalkClient client = new DefaultDingTalkClient(url);
|
OapiRobotSendResponse response = client.execute(request);
|
if (!response.isSuccess()) {
|
log.warn("[DingTalk] 发送失败, errcode:{}, errmsg:{}", response.getErrcode(), response.getErrmsg());
|
} else {
|
log.info("[DingTalk] 发送成功");
|
}
|
} catch (Exception e) {
|
log.error("[DingTalk] 发送异常", e);
|
}
|
}
|
|
private String generateSign(Long timestamp) throws Exception {
|
String stringToSign = timestamp + "\n" + secret;
|
Mac mac = Mac.getInstance("HmacSHA256");
|
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
|
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
|
return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
|
}
|
|
public static void main(String[] args) {
|
DingTalkUtils.getDefault().sendActionCard("风险提示", "测试123", "", "");
|
}
|
}
|