|
@@ -0,0 +1,298 @@
|
|
|
|
|
+package com.ygtx.common.utils.getui;
|
|
|
|
|
+
|
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
|
|
+import cn.hutool.http.HttpRequest;
|
|
|
|
|
+import cn.hutool.http.HttpResponse;
|
|
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
|
|
+import com.alibaba.fastjson2.JSONObject;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.PostConstruct;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 个推推送服务工具类
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author ruoyi
|
|
|
|
|
+ */
|
|
|
|
|
+@Component
|
|
|
|
|
+public class GetuiTemplate {
|
|
|
|
|
+
|
|
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(GetuiTemplate.class);
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private GetuiConfig getuiConfig;
|
|
|
|
|
+
|
|
|
|
|
+ private static GetuiTemplate getuiTemplate;
|
|
|
|
|
+
|
|
|
|
|
+ private static final String AUTH_URL = "/auth";
|
|
|
|
|
+ private static final String PUSH_SINGLE_URL = "/push/single/cid";
|
|
|
|
|
+ private static final String PUSH_BATCH_URL = "/push/list/cid";
|
|
|
|
|
+
|
|
|
|
|
+ @PostConstruct
|
|
|
|
|
+ public void init() {
|
|
|
|
|
+ getuiTemplate = this;
|
|
|
|
|
+ getuiTemplate.getuiConfig = this.getuiConfig;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取鉴权Token
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return token
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getAuthToken() {
|
|
|
|
|
+ if (!getuiConfig.isEnabled()) {
|
|
|
|
|
+ throw new RuntimeException("个推服务未启用");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ Map<String, Object> paramMap = new HashMap<>();
|
|
|
|
|
+ paramMap.put("appkey", getuiConfig.getAppKey());
|
|
|
|
|
+ paramMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
|
|
|
|
|
+ paramMap.put("sign", generateSign(paramMap));
|
|
|
|
|
+
|
|
|
|
|
+ String url = getuiConfig.getPushUrl() + getuiConfig.getAppId() + AUTH_URL;
|
|
|
|
|
+
|
|
|
|
|
+ HttpResponse response = HttpRequest.post(url)
|
|
|
|
|
+ .header("content-type", "application/json")
|
|
|
|
|
+ .body(JSONUtil.toJsonStr(paramMap))
|
|
|
|
|
+ .execute();
|
|
|
|
|
+
|
|
|
|
|
+ if (response.getStatus() == 200) {
|
|
|
|
|
+ JSONObject result = JSONObject.parseObject(response.body());
|
|
|
|
|
+ if ("ok".equals(result.getString("code"))) {
|
|
|
|
|
+ return result.getJSONObject("data").getString("token");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.error("获取个推鉴权Token失败: {}", result.getString("msg"));
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("获取个推鉴权Token异常", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成签名
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param paramMap 参数map
|
|
|
|
|
+ * @return 签名
|
|
|
|
|
+ */
|
|
|
|
|
+ private String generateSign(Map<String, Object> paramMap) {
|
|
|
|
|
+ // 根据个推官方文档实现签名生成逻辑
|
|
|
|
|
+ String appKey = paramMap.get("appkey").toString();
|
|
|
|
|
+ String timestamp = paramMap.get("timestamp").toString();
|
|
|
|
|
+ String masterSecret = getuiConfig.getMasterSecret();
|
|
|
|
|
+
|
|
|
|
|
+ // 个推签名算法:SHA256(masterSecret + timestamp + appKey)
|
|
|
|
|
+ String signStr = masterSecret + timestamp + appKey;
|
|
|
|
|
+ return cn.hutool.crypto.SecureUtil.sha256(signStr);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 推送消息给单个用户
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param cid 用户cid
|
|
|
|
|
+ * @param title 消息标题
|
|
|
|
|
+ * @param content 消息内容
|
|
|
|
|
+ * @param payload 透传内容
|
|
|
|
|
+ * @return 推送结果
|
|
|
|
|
+ */
|
|
|
|
|
+ public boolean pushToSingle(String cid, String title, String content, String payload) {
|
|
|
|
|
+ if (!getuiConfig.isEnabled()) {
|
|
|
|
|
+ log.warn("个推服务未启用");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (StrUtil.isBlank(cid)) {
|
|
|
|
|
+ log.warn("用户cid不能为空");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String token = getAuthToken();
|
|
|
|
|
+ if (StrUtil.isBlank(token)) {
|
|
|
|
|
+ log.error("获取个推鉴权Token失败");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ Map<String, Object> paramMap = new HashMap<>();
|
|
|
|
|
+ paramMap.put("request_id", String.valueOf(System.currentTimeMillis()));
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> pushMessage = new HashMap<>();
|
|
|
|
|
+ pushMessage.put("notification", createNotification(title, content));
|
|
|
|
|
+ if (StrUtil.isNotBlank(payload)) {
|
|
|
|
|
+ pushMessage.put("transmission", createTransmission(payload));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ paramMap.put("settings", createSettings());
|
|
|
|
|
+ paramMap.put("push_message", pushMessage);
|
|
|
|
|
+ paramMap.put("audience", createAudience(cid));
|
|
|
|
|
+
|
|
|
|
|
+ String url = getuiConfig.getPushUrl() + getuiConfig.getAppId() + PUSH_SINGLE_URL;
|
|
|
|
|
+
|
|
|
|
|
+ HttpResponse response = HttpRequest.post(url)
|
|
|
|
|
+ .header("content-type", "application/json")
|
|
|
|
|
+ .header("token", token)
|
|
|
|
|
+ .body(JSONUtil.toJsonStr(paramMap))
|
|
|
|
|
+ .execute();
|
|
|
|
|
+
|
|
|
|
|
+ if (response.getStatus() == 200) {
|
|
|
|
|
+ JSONObject result = JSONObject.parseObject(response.body());
|
|
|
|
|
+ if ("ok".equals(result.getString("code"))) {
|
|
|
|
|
+ log.info("个推消息推送成功: cid={}", cid);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.error("个推消息推送失败: cid={}, 错误信息={}", cid, result.getString("msg"));
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("个推消息推送异常: cid={}", cid, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 批量推送消息给多个用户
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param cids 用户cid列表
|
|
|
|
|
+ * @param title 消息标题
|
|
|
|
|
+ * @param content 消息内容
|
|
|
|
|
+ * @param payload 透传内容
|
|
|
|
|
+ * @return 推送结果
|
|
|
|
|
+ */
|
|
|
|
|
+ public boolean pushToBatch(String[] cids, String title, String content, String payload) {
|
|
|
|
|
+ if (!getuiConfig.isEnabled()) {
|
|
|
|
|
+ log.warn("个推服务未启用");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (cids == null || cids.length == 0) {
|
|
|
|
|
+ log.warn("用户cid列表不能为空");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String token = getAuthToken();
|
|
|
|
|
+ if (StrUtil.isBlank(token)) {
|
|
|
|
|
+ log.error("获取个推鉴权Token失败");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ Map<String, Object> paramMap = new HashMap<>();
|
|
|
|
|
+ paramMap.put("request_id", String.valueOf(System.currentTimeMillis()));
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> pushMessage = new HashMap<>();
|
|
|
|
|
+ pushMessage.put("notification", createNotification(title, content));
|
|
|
|
|
+ if (StrUtil.isNotBlank(payload)) {
|
|
|
|
|
+ pushMessage.put("transmission", createTransmission(payload));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ paramMap.put("settings", createSettings());
|
|
|
|
|
+ paramMap.put("push_message", pushMessage);
|
|
|
|
|
+ paramMap.put("audience", createAudience(cids));
|
|
|
|
|
+
|
|
|
|
|
+ String url = getuiConfig.getPushUrl() + getuiConfig.getAppId() + PUSH_BATCH_URL;
|
|
|
|
|
+
|
|
|
|
|
+ HttpResponse response = HttpRequest.post(url)
|
|
|
|
|
+ .header("content-type", "application/json")
|
|
|
|
|
+ .header("token", token)
|
|
|
|
|
+ .body(JSONUtil.toJsonStr(paramMap))
|
|
|
|
|
+ .execute();
|
|
|
|
|
+
|
|
|
|
|
+ if (response.getStatus() == 200) {
|
|
|
|
|
+ JSONObject result = JSONObject.parseObject(response.body());
|
|
|
|
|
+ if ("ok".equals(result.getString("code"))) {
|
|
|
|
|
+ log.info("个推批量消息推送成功: cids数量={}", cids.length);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.error("个推批量消息推送失败: cids数量={}, 错误信息={}", cids.length, result.getString("msg"));
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("个推批量消息推送异常: cids数量={}", cids.length, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建通知消息
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> createNotification(String title, String content) {
|
|
|
|
|
+ Map<String, Object> notification = new HashMap<>();
|
|
|
|
|
+ notification.put("title", StrUtil.blankToDefault(title, "消息通知"));
|
|
|
|
|
+ notification.put("body", StrUtil.blankToDefault(content, "您有一条新消息"));
|
|
|
|
|
+ notification.put("click_type", "intent");
|
|
|
|
|
+ return notification;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建透传消息
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> createTransmission(String payload) {
|
|
|
|
|
+ Map<String, Object> transmission = new HashMap<>();
|
|
|
|
|
+ transmission.put("transmission_content", payload);
|
|
|
|
|
+ return transmission;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建推送设置
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> createSettings() {
|
|
|
|
|
+ Map<String, Object> settings = new HashMap<>();
|
|
|
|
|
+ settings.put("ttl", 3600000); // 消息离线时间1小时
|
|
|
|
|
+ return settings;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建推送目标(单个用户)
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> createAudience(String cid) {
|
|
|
|
|
+ Map<String, Object> audience = new HashMap<>();
|
|
|
|
|
+ audience.put("cid", new String[]{cid});
|
|
|
|
|
+ return audience;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建推送目标(多个用户)
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> createAudience(String[] cids) {
|
|
|
|
|
+ Map<String, Object> audience = new HashMap<>();
|
|
|
|
|
+ audience.put("cid", cids);
|
|
|
|
|
+ return audience;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 静态方法推送消息给单个用户
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean pushMessageToSingle(String cid, String title, String content) {
|
|
|
|
|
+ return getuiTemplate.pushToSingle(cid, title, content, null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 静态方法推送消息给单个用户(带透传内容)
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean pushMessageToSingle(String cid, String title, String content, String payload) {
|
|
|
|
|
+ return getuiTemplate.pushToSingle(cid, title, content, payload);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 静态方法批量推送消息给多个用户
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean pushMessageToBatch(String[] cids, String title, String content) {
|
|
|
|
|
+ return getuiTemplate.pushToBatch(cids, title, content, null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 静态方法批量推送消息给多个用户(带透传内容)
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean pushMessageToBatch(String[] cids, String title, String content, String payload) {
|
|
|
|
|
+ return getuiTemplate.pushToBatch(cids, title, content, payload);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|