Browse Source

接入华为 PUSH API 新版本

YanLu 1 week ago
parent
commit
e84d6a6d32

+ 103 - 40
src/main/java/cn/wildfirechat/push/android/hms/HMSPush.java

@@ -3,10 +3,13 @@ package cn.wildfirechat.push.android.hms;
 
 import cn.wildfirechat.push.PushMessage;
 import cn.wildfirechat.push.PushMessageType;
+import cn.wildfirechat.push.hm.payload.AlertPayload;
+
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.google.gson.Gson;
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -24,75 +27,128 @@ import java.util.List;
 @Component
 public class HMSPush {
     private static final Logger LOG = LoggerFactory.getLogger(HMSPush.class);
-    private static final String tokenUrl = "https://login.vmall.com/oauth2/token"; //获取认证Token的URL
-    private static final String apiUrl = "https://api.push.hicloud.com/pushsend.do"; //应用级消息下发API
+    private static final String tokenUrl = "https://oauth-login.cloud.huawei.com/oauth2/v3/token"; //获取认证Token的URL
+    private static final String apiUrl = "https://push-api.cloud.huawei.com/v1/%s/messages:send"; //应用级消息下发API
     private String accessToken;//下发通知消息的认证Token
-    private long tokenExpiredTime;  //accessToken的过期时间
+    private long tokenExpiredTime = 0;  //accessToken的过期时间,初始化为0
 
     @Autowired
     private HMSConfig mConfig;
 
+    /**
+     * 检查token是否有效
+     * @return true if token is valid and not expired
+     */
+    private boolean isTokenValid() {
+        if (StringUtils.isEmpty(accessToken)) {
+            LOG.debug("HMS token is empty");
+            return false;
+        }
+
+        long currentTime = System.currentTimeMillis();
+        if (tokenExpiredTime <= currentTime) {
+            LOG.debug("HMS token expired. Current: {}, Expired: {}", currentTime, tokenExpiredTime);
+            return false;
+        }
+
+        long remainingTime = (tokenExpiredTime - currentTime) / 1000;
+        LOG.debug("HMS token is valid, remaining {} seconds", remainingTime);
+        return true;
+    }
+
     //获取下发通知消息的认证Token
     private void refreshToken() throws IOException {
         LOG.info("hms refresh token");
         String msgBody = MessageFormat.format(
             "grant_type=client_credentials&client_secret={0}&client_id={1}",
             URLEncoder.encode(mConfig.getAppSecret(), "UTF-8"), mConfig.getAppId());
-        String response = httpPost(tokenUrl, msgBody, 5000, 5000);
+        String response = httpPost(tokenUrl, "", msgBody, 5000, 5000);
         JSONObject obj = JSONObject.parseObject(response);
-        accessToken = obj.getString("access_token");
-        tokenExpiredTime = System.currentTimeMillis() + obj.getLong("expires_in") - 5*60*1000;
-        LOG.info("hms refresh token with result {}", response);
+
+        if (obj.containsKey("access_token")) {
+            accessToken = obj.getString("access_token");
+            // 设置过期时间,提前5分钟刷新
+            long expiresIn = obj.getLong("expires_in") * 1000; // 转换为毫秒
+            tokenExpiredTime = System.currentTimeMillis() + expiresIn - 5*60*1000;
+            LOG.info("hms token refreshed successfully, expires in {} seconds", obj.getLong("expires_in"));
+        } else {
+            LOG.error("Failed to get access_token from response: {}", response);
+            throw new IOException("Failed to get access_token from hms auth response");
+        }
     }
 
     //发送Push消息
     public void push(PushMessage pushMessage) {
-        if (tokenExpiredTime <= System.currentTimeMillis()) {
+        // 检查token是否有效,无效则刷新
+        if (!isTokenValid()) {
             try {
                 refreshToken();
             } catch (IOException e) {
-                e.printStackTrace();
+                LOG.error("Failed to refresh hms token", e);
+                return; // token刷新失败,直接返回
             }
         }
         /*PushManager.requestToken为客户端申请token的方法,可以调用多次以防止申请token失败*/
         /*PushToken不支持手动编写,需使用客户端的onToken方法获取*/
-        JSONArray deviceTokens = new JSONArray();//目标设备Token
-        deviceTokens.add(pushMessage.getDeviceToken());
-
-
-        JSONObject msg = new JSONObject();
-        msg.put("type", 1);//3: 通知栏消息,异步透传消息请根据接口文档设置
-        String token = pushMessage.getDeviceToken();
-        pushMessage.deviceToken = null;
-        msg.put("body", new Gson().toJson(pushMessage));//通知栏消息body内容
-
-        JSONObject hps = new JSONObject();//华为PUSH消息总结构体
-        hps.put("msg", msg);
-
-        JSONObject payload = new JSONObject();
-        payload.put("hps", hps);
-
-        LOG.info("send push to HMS {}", payload);
+//        JSONArray deviceTokens = new JSONArray();//目标设备Token
+//        deviceTokens.add(pushMessage.getDeviceToken());
+//
+//        JSONObject param = new JSONObject();
+//        param.put("appPkgName", pushMessage.packageName);//定义需要打开的appPkgName
+//        JSONObject action = new JSONObject();
+//        action.put("type", 3);//类型3为打开APP,其他行为请参考接口文档设置
+//        action.put("param", param);//消息点击动作参数
+//
+//
+//        JSONObject msg = new JSONObject();
+//        // 透传消息
+//        msg.put("type", 3);//3: 通知栏消息,异步透传消息请根据接口文档设置
+//        msg.put("action", action);//消息点击动作 add by liguangyu
+//
+//        String token = pushMessage.getDeviceToken();
+//        pushMessage.deviceToken = null;
+////        msg.put("body", new Gson().toJson(pushMessage));//通知栏消息body内容
+//
+//        JSONObject body = new JSONObject();//仅通知栏消息需要设置标题和内容,透传消息key和value为用户自定义
+//        body.put("title", pushMessage.senderName);//消息标题
+//        body.put("content", pushMessage.pushContent);//消息内容体
+//        //body.put("info", new Gson().toJson(pushMessage));//消息内容体
+//        msg.put("body", body);//通知栏消息body内容示例代码
+//        LOG.info("liguangyu test body: {} pushMessage{}",body,new Gson().toJson(pushMessage) );
+//
+//        // 华为消息分类
+//        msg.put("importance", "NORMAL");
+//        msg.put("category", "IM");
+//
+//        JSONObject hps = new JSONObject();//华为PUSH消息总结构体
+//        hps.put("msg", msg);
+//
+//        JSONObject payload = new JSONObject();
+//        payload.put("hps", hps);
+//
+//        LOG.info("send push to HMS {}", payload);
 
         try {
-            String postBody = MessageFormat.format(
-                "access_token={0}&nsp_svc={1}&nsp_ts={2}&device_token_list={3}&payload={4}",
-                URLEncoder.encode(accessToken,"UTF-8"),
-                URLEncoder.encode("openpush.message.api.send","UTF-8"),
-                URLEncoder.encode(String.valueOf(System.currentTimeMillis() / 1000),"UTF-8"),
-                URLEncoder.encode(deviceTokens.toString(),"UTF-8"),
-                URLEncoder.encode(payload.toString(),"UTF-8"));
-
-            String postUrl = apiUrl + "?nsp_ctx=" + URLEncoder.encode("{\"ver\":\"1\", \"appId\":\"" + mConfig.getAppId() + "\"}", "UTF-8");
-            String response = httpPost(postUrl, postBody, 5000, 5000);
-            LOG.info("Push to {} response {}", token, response);
+//            String postBody = MessageFormat.format(
+//                "access_token={0}&nsp_svc={1}&nsp_ts={2}&device_token_list={3}&payload={4}",
+//                URLEncoder.encode(accessToken,"UTF-8"),
+//                URLEncoder.encode("openpush.message.api.send","UTF-8"),
+//                URLEncoder.encode(String.valueOf(System.currentTimeMillis() / 1000),"UTF-8"),
+//                URLEncoder.encode(deviceTokens.toString(),"UTF-8"),
+//                URLEncoder.encode(payload.toString(),"UTF-8"));
+            HMSPushPayload alertPayload = HMSPushPayload.buildAlertPayload(pushMessage,  mConfig.getAppId());
+            LOG.info("Push message {}", alertPayload);
+            //String postUrl = apiUrl + "?nsp_ctx=" + URLEncoder.encode("{\"ver\":\"1\", \"appId\":\"" + mConfig.getAppId() + "\"}", "UTF-8");
+            String postUrl = String.format(apiUrl, mConfig.getAppId());
+            String response = httpPost(postUrl, accessToken, alertPayload.toString(), 8000, 8000);
+            LOG.info("Push to {} response {}", pushMessage.getDeviceToken(), response);
         } catch (IOException e) {
             e.printStackTrace();
-            LOG.info("Push to {} with exception", token, e);
+            LOG.info("Push to {} with exception", pushMessage.getDeviceToken(), e);
         }
     }
 
-    public String httpPost(String httpUrl, String data, int connectTimeout, int readTimeout) throws IOException {
+    public String httpPost(String httpUrl, String jwt, String data, int connectTimeout, int readTimeout) throws IOException {
         OutputStream outPut = null;
         HttpURLConnection urlConnection = null;
         InputStream in = null;
@@ -103,7 +159,14 @@ public class HMSPush {
             urlConnection.setRequestMethod("POST");
             urlConnection.setDoOutput(true);
             urlConnection.setDoInput(true);
-            urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+            if (StringUtils.isNotEmpty(jwt)) {
+                urlConnection.setRequestProperty("Content-Type", "application/json");
+                urlConnection.setRequestProperty("Authorization", "Bearer " + jwt);
+                //urlConnection.setRequestProperty("push-type", pushType + "");
+            } else {
+                urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+            }
+
             urlConnection.setConnectTimeout(connectTimeout);
             urlConnection.setReadTimeout(readTimeout);
             urlConnection.connect();

+ 76 - 0
src/main/java/cn/wildfirechat/push/android/hms/HMSPushPayload.java

@@ -0,0 +1,76 @@
+package cn.wildfirechat.push.android.hms;
+
+import com.google.gson.Gson;
+
+import org.json.simple.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.wildfirechat.push.PushMessage;
+import cn.wildfirechat.push.PushMessageType;
+import cn.wildfirechat.push.Utility;
+import cn.wildfirechat.push.android.hms.internal.HMSMessageNotification;
+import cn.wildfirechat.push.android.hms.internal.HMSPushAndroidInfo;
+import cn.wildfirechat.push.android.hms.internal.HMSPushClickAction;
+import cn.wildfirechat.push.android.hms.internal.HMSPushMessage;
+import cn.wildfirechat.push.android.hms.internal.HMSPushNotification;
+import cn.wildfirechat.push.hm.payload.internal.ClickAction;
+import cn.wildfirechat.push.hm.payload.internal.Notification;
+import cn.wildfirechat.push.hm.payload.internal.Payload;
+import cn.wildfirechat.push.hm.payload.internal.Target;
+
+// https://developer.huawei.com/consumer/cn/doc/HMSCore-Guides/rest-sample-code-0000001050040242
+public class HMSPushPayload {
+    // 华为推送长度限制:title最大40字符,body最大256字符
+    private static final int HMS_PUSH_MAX_TITLE = 40;
+    private static final int HMS_PUSH_MAX_BODY = 256;
+
+    public boolean validate_only = false;
+    public HMSPushMessage message;
+
+
+    @Override
+    public String toString() {
+        return new Gson().toJson(this);
+    }
+
+    public static HMSPushPayload buildAlertPayload(PushMessage pushMessage, String appId) {
+        HMSPushMessage hmsMessage = new HMSPushMessage();
+
+        hmsMessage.notification = new HMSPushNotification();
+        String[] titleAndBody = Utility.getPushTitleAndContent(pushMessage);
+        String title = titleAndBody[0];
+        String body = titleAndBody[1];
+
+        // 处理华为推送的长度限制
+        if (title != null && title.length() > HMS_PUSH_MAX_TITLE) {
+            title = title.substring(0, HMS_PUSH_MAX_TITLE - 3) + "...";
+        }
+
+        if (body != null && body.length() > HMS_PUSH_MAX_BODY) {
+            body = body.substring(0, HMS_PUSH_MAX_BODY - 3) + "...";
+        }
+
+        hmsMessage.notification.title = title;
+        hmsMessage.notification.body = body;
+
+        List<String> tokens = new ArrayList<>();
+        tokens.add(pushMessage.deviceToken);
+        hmsMessage.token = tokens;
+
+        hmsMessage.android = new HMSPushAndroidInfo();
+        hmsMessage.android.notification = new HMSMessageNotification();
+        hmsMessage.android.notification.channel_id = "channel_offline_" + appId;
+
+        hmsMessage.android.notification.clickAction = new HMSPushClickAction();
+        hmsMessage.android.notification.clickAction.type = 3;
+
+        HMSPushPayload alertPayload = new HMSPushPayload();
+        alertPayload.validate_only = false;
+        alertPayload.message = hmsMessage;
+
+        return alertPayload;
+    }
+}
+

+ 17 - 0
src/main/java/cn/wildfirechat/push/android/hms/internal/HMSMessageNotification.java

@@ -0,0 +1,17 @@
+package cn.wildfirechat.push.android.hms.internal;
+
+import com.google.gson.annotations.SerializedName;
+
+public class HMSMessageNotification {
+    public String importance = "NORMAL";
+    @SerializedName("click_action")
+    public HMSPushClickAction clickAction;
+//    public String ticker;
+//    public String notify_summary;
+//
+    public String channel_id;
+//    public int style = 0;
+//    public int notify_id;
+//    public String visibility;
+//    public String when;
+}

+ 7 - 0
src/main/java/cn/wildfirechat/push/android/hms/internal/HMSPushAndroidInfo.java

@@ -0,0 +1,7 @@
+package cn.wildfirechat.push.android.hms.internal;
+
+public class HMSPushAndroidInfo {
+    public String category = "IM";
+    public HMSMessageNotification notification;
+    public String ttl = "86400s";
+}

+ 17 - 0
src/main/java/cn/wildfirechat/push/android/hms/internal/HMSPushClickAction.java

@@ -0,0 +1,17 @@
+package cn.wildfirechat.push.android.hms.internal;
+
+public class HMSPushClickAction {
+    /**
+     * 消息点击行为类型,取值如下:
+     * <p>
+     * 1:打开应用自定义页面
+     * 2:点击后打开特定URL
+     * 3:点击后打开应用
+     */
+
+    public int type = 3;
+
+    public String intent;
+    public String url;
+    public String action;
+}

+ 9 - 0
src/main/java/cn/wildfirechat/push/android/hms/internal/HMSPushMessage.java

@@ -0,0 +1,9 @@
+package cn.wildfirechat.push.android.hms.internal;
+
+import java.util.List;
+
+public class HMSPushMessage {
+    public HMSPushNotification notification;
+    public HMSPushAndroidInfo android;
+    public List<String> token;
+}

+ 6 - 0
src/main/java/cn/wildfirechat/push/android/hms/internal/HMSPushNotification.java

@@ -0,0 +1,6 @@
+package cn.wildfirechat.push.android.hms.internal;
+
+public class HMSPushNotification {
+    public String title;
+    public String body;
+}