消息推送第三方平台(个推)接入工具类
个推官方文档:https://docs.getui.com/getui/server/rest_v2/push/
首先申请个推官方账号,然后注册App获取AppID、AppKey、AppSecret、MasterSecret
接入教程
1、编写配置文件
修改.yml文件
getui:
AppID: OokKLlwRjU7tJMccVVra72
AppKey: f8C6lK7OGu1115ckOfVxD8
MasterSecret: aTxslPiUJy9kzzZaPONL26
AppSecret: sAoJ9sQ66S13P0PG3c1y02
编写映射文件
GeTuiConfig
package com.common.disposition;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "getui")
public class GeTuiConfig {
private String appId;
private String appKey;
private String masterSecret;
private String appSecret;
}
2、编写工具类
GeTuiUtil
package com.common.util;
import com.common.disposition.GeTuiConfig;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Component
public class GeTuiUtil {
private static String APP_ID;
private static String APP_KEY;
private static String MASTER_SECRET;
// 在构造函数中初始化配置
@Autowired
public GeTuiUtil(GeTuiConfig config) {
APP_ID = config.getAppId();
APP_KEY = config.getAppKey();
MASTER_SECRET = config.getMasterSecret();
}
private static final String REDIS_TOKEN_KEY = "GETUI_ACCESS_TOKEN";
private static RedisTemplate<String, String> redisTemplate;
private static ObjectMapper objectMapper = new ObjectMapper(); // 用于解析 JSON 响应
// 初始化 RedisTemplate,手动注入
@Autowired
public void setRedisTemplate(@Qualifier("stringRedisTemplate") RedisTemplate<String, String> redisTemplate) {
GeTuiUtil.redisTemplate = redisTemplate;
}
// 获取 access_token,优先从 Redis 中获取
public static String getAccessToken() throws Exception {
String accessToken = redisTemplate.opsForValue().get(REDIS_TOKEN_KEY);
if (accessToken == null || accessToken.isEmpty()) {
System.out.println("在Redis中找不到访问令牌,正在从GeTui获取...");
accessToken = getAccessTokenFromGeTui();
} else {
System.out.println("从Redis检索到的访问令牌。");
}
return accessToken;
}
// 从个推获取 access_token
private static String getAccessTokenFromGeTui() throws Exception {
long timestamp = System.currentTimeMillis();
String sign = sha256(APP_KEY + timestamp + MASTER_SECRET);
String jsonPayload = String.format(
"{\"sign\": \"%s\", \"timestamp\": \"%s\", \"appkey\": \"%s\"}",
sign, timestamp, APP_KEY
);
String response = postRequest("https://restapi.getui.com/v2/" + APP_ID + "/auth", jsonPayload);
String accessToken = parseAccessTokenFromResponse(response);
long expireTime = parseExpireTimeFromResponse(response); // 获取过期时间
// 将 token 存入 Redis,并设置过期时间
redisTemplate.opsForValue().set(REDIS_TOKEN_KEY, accessToken, expireTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
return accessToken;
}
// 生成唯一的 request_id,使用 UUID 并取前 32 位
private static String generateRequestId() {
return UUID.randomUUID().toString().replace("-", "").substring(0, 32);
}
// 通用的 POST 请求,带 Token
private static String postRequest(String requestUrl, String payload, String token) throws Exception {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("token", token);
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
byte[] input = payload.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
System.out.println("推送消息发送成功。");
} else {
System.out.println("发送推送消息失败。响应代码: " + responseCode);
}
return new String(conn.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
}
// 重载的 POST 请求,不带 Token
private static String postRequest(String requestUrl, String payload) throws Exception {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
byte[] input = payload.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
System.out.println("Request successful.");
} else {
System.out.println("发送请求失败。响应代码: " + responseCode);
}
return new String(conn.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
}
// 解析获取到的 access_token
private static String parseAccessTokenFromResponse(String response) throws Exception {
JsonNode rootNode = objectMapper.readTree(response);
return rootNode.path("data").path("token").asText();
}
// 解析过期时间
private static long parseExpireTimeFromResponse(String response) throws Exception {
JsonNode rootNode = objectMapper.readTree(response);
return rootNode.path("data").path("expire_time").asLong();
}
// 生成 SHA-256 签名
private static String sha256(String input) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1){
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
// 发送cid单推消息 (toSingle)
public static String sendPushMessageCid(String clientId, String title, String body) throws Exception {
String token = getAccessToken();
String requestId = generateRequestId();
String jsonPayload = String.format(
"{\"request_id\": \"%s\", \"audience\": {\"cid\": [\"%s\"]}, \"push_message\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}, \"push_channel\": {\"ios\": {\"type\": \"notify\"}, \"android\": {\"ups\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}}}}",
requestId, clientId, title, body, title, body
);
return postRequest("https://restapi.getui.com/v2/" + APP_ID + "/push/single/cid", jsonPayload, token);
}
// 发送别名(alias)单推消息 (toSingle)
public static void sendPushMessageAlias(String alias, String title, String body) throws Exception {
String token = getAccessToken();
String requestId = generateRequestId();
String jsonPayload = String.format(
"{\"request_id\": \"%s\", \"audience\": {\"alias\": [\"%s\"]}, \"push_message\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}, \"push_channel\": {\"ios\": {\"type\": \"notify\"}, \"android\": {\"ups\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}}}}",
requestId, alias, title, body, title, body
);
postRequest("https://restapi.getui.com/v2/" + APP_ID + "/push/single/alias", jsonPayload, token);
}
// 发送cid批量推消息 (toList)
public static void sendBatchPushMessageCid(String[] clientIds, String title, String body) throws Exception {
String token = getAccessToken();
String requestId = generateRequestId();
StringBuilder cidArray = new StringBuilder();
for (String clientId : clientIds) {
cidArray.append("\"").append(clientId).append("\",");
}
// 移除末尾的逗号
String cidList = cidArray.substring(0, cidArray.length() - 1);
String jsonPayload = String.format(
"{\"request_id\": \"%s\", \"audience\": {\"cid\": [%s]}, \"push_message\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}, \"push_channel\": {\"ios\": {\"type\": \"notify\"}, \"android\": {\"ups\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}}}}",
requestId, cidList, title, body, title, body
);
postRequest("https://restapi.getui.com/v2/" + APP_ID + "/push/list/cid", jsonPayload, token);
}
// 发送别名(alias)批量推消息 (toList)
public static void sendBatchPushMessageAlias(String[] aliases, String title, String body) throws Exception {
String token = getAccessToken();
String requestId = generateRequestId();
StringBuilder aliasArray = new StringBuilder();
for (String alias : aliases) {
aliasArray.append("\"").append(alias).append("\",");
}
// 移除末尾的逗号
String aliasList = aliasArray.substring(0, aliasArray.length() - 1);
String jsonPayload = String.format(
"{\"request_id\": \"%s\", \"audience\": {\"alias\": [%s]}, \"push_message\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}, \"push_channel\": {\"ios\": {\"type\": \"notify\"}, \"android\": {\"ups\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}}}}",
requestId, aliasList, title, body, title, body
);
postRequest("https://restapi.getui.com/v2/" + APP_ID + "/push/list/alias", jsonPayload, token);
}
// 发送群推消息 (toApp)
public static void sendPushToApp(String title, String body) throws Exception {
String token = getAccessToken();
String requestId = generateRequestId();
String jsonPayload = String.format(
"{\"request_id\": \"%s\", \"audience\": \"all\", \"push_message\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}, \"push_channel\": {\"ios\": {\"type\": \"notify\"}, \"android\": {\"ups\": {\"notification\": {\"title\": \"%s\", \"body\": \"%s\", \"click_type\": \"none\"}}}}}",
requestId, title, body, title, body
);
postRequest("https://restapi.getui.com/v2/" + APP_ID + "/push/all", jsonPayload, token);
}
// 停止特定推送任务
public static void stopTask(String taskId) throws Exception {
String token = getAccessToken();
String requestUrl = "https://restapi.getui.com/v2/" + APP_ID + "/task/" + taskId;
HttpURLConnection conn = (HttpURLConnection) new URL(requestUrl).openConnection();
conn.setRequestMethod("DELETE");
conn.setRequestProperty("token", token);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
System.out.println("任务已停止。");
} else {
System.out.println("停止任务失败。响应代码: " + responseCode);
}
}
// 查询定时任务状态
public static void queryScheduledTask(String taskId) throws Exception {
String token = getAccessToken();
String requestUrl = "https://restapi.getui.com/v2/" + APP_ID + "/task/schedule/" + taskId;
HttpURLConnection conn = (HttpURLConnection) new URL(requestUrl).openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("token", token);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
System.out.println("查询定时任务成功。");
} else {
System.out.println("查询定时任务失败。响应代码: " + responseCode);
}
}
// 删除定时任务
public static void deleteScheduledTask(String taskId) throws Exception {
String token = getAccessToken();
String requestUrl = "https://restapi.getui.com/v2/" + APP_ID + "/task/schedule/" + taskId;
HttpURLConnection conn = (HttpURLConnection) new URL(requestUrl).openConnection();
conn.setRequestMethod("DELETE");
conn.setRequestProperty("token", token);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
System.out.println("定时任务已删除。");
} else {
System.out.println("删除定时任务失败。响应代码: " + responseCode);
}
}
// 查询消息明细
public static void queryPushDetail(String cid, String taskId) throws Exception {
String token = getAccessToken();
String requestUrl = "https://restapi.getui.com/v2/" + APP_ID + "/task/detail/" + cid + "/" + taskId;
HttpURLConnection conn = (HttpURLConnection) new URL(requestUrl).openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("token", token);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
System.out.println("查询消息明细成功。");
} else {
System.out.println("查询消息明细失败。响应代码: " + responseCode);
}
}
// 根据 CID 查询别名
public static String getAliasByCid(String cid) throws Exception {
String token = getAccessToken();
String requestUrl = "https://restapi.getui.com/v2/" + APP_ID + "/user/alias/cid/" + cid;
HttpURLConnection conn = (HttpURLConnection) new URL(requestUrl).openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("token", token);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
String response = new String(conn.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
JsonNode rootNode = objectMapper.readTree(response);
String alias = rootNode.path("data").path("alias").asText();
System.out.println("查询到的别名: " + alias);
return alias;
} else {
System.out.println("根据CID查询别名失败。响应代码: " + responseCode);
return null;
}
}
// 根据别名查询 CID
public static String[] getCidByAlias(String alias) throws Exception {
String token = getAccessToken();
String requestUrl = "https://restapi.getui.com/v2/" + APP_ID + "/user/cid/alias/" + alias;
HttpURLConnection conn = (HttpURLConnection) new URL(requestUrl).openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("token", token);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
String response = new String(conn.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
JsonNode rootNode = objectMapper.readTree(response);
JsonNode cidArrayNode = rootNode.path("data").path("cid");
String[] cids = objectMapper.convertValue(cidArrayNode, String[].class);
System.out.println("查询到的CID: " + String.join(", ", cids));
return cids;
} else {
System.out.println("根据别名查询CID失败。响应代码: " + responseCode);
return null;
}
}
}
3、调用示例 (需要结合安卓端生成的 cid 或者 别名id 才能进行推送)
单推送消息
// 单推送cid消息
@PostMapping("sendBatchPushMessageCid")
@Operation(summary = "单推送消息cid")
public ApiResult<String> sendBatchPushMessageCid(@RequestParam("clientId") String clientId,
@RequestParam("title") String title,
@RequestParam("body") String body) {
try {
String s = GeTuiUtil.sendPushMessageCid(clientId, title, body);
System.out.println(s);
return ApiResult.success("推送成功");
} catch (Exception e) {
// 打印详细的错误信息以便调试
e.printStackTrace();
return ApiResult.fail("推送失败,内部错误:" + e.getMessage());
}
}
// 单推送别名消息
@PostMapping("sendBatchPushMessage")
@Operation(summary = "单推送消息")
public ApiResult<String> sendBatchPushMessage(@RequestParam("clientId") String clientId,
@RequestParam("title") String title,
@RequestParam("body") String body) {
try {
String s = GeTuiUtil.sendPushMessageAlias(clientId, title, body);
System.out.println(s);
return ApiResult.success("推送成功");
} catch (Exception e) {
// 打印详细的错误信息以便调试
e.printStackTrace();
return ApiResult.fail("推送失败,内部错误:" + e.getMessage());
}
}