消息推送第三方平台(个推)接入工具类

个推官方文档: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());
        }
    }

 

posted @ 2024-09-09 20:17  xd99  阅读(23)  评论(0编辑  收藏  举报