package com.doumee.core.utils.jpush; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.doumee.core.utils.HttpsUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.Map; /** * 极光推送工具类(基于JPush REST API) * * @author rk * @date 2026/04/24 */ @Component @Slf4j public class JPushUtil { private static final String PUSH_URL = "https://bjapi.push.jiguang.cn/v3/push"; private static String authHeader; private static boolean apnsProduction; @Value("${jpush.appKey:}") private String appKey; @Value("${jpush.masterSecret:}") private String masterSecret; @Value("${jpush.apnsProduction:false}") private boolean apnsProd; @PostConstruct public void init() { if (StringUtils.isNotBlank(appKey) && StringUtils.isNotBlank(masterSecret)) { authHeader = "Basic " + Base64.getEncoder().encodeToString((appKey + ":" + masterSecret).getBytes()); apnsProduction = apnsProd; log.info("JPush initialized, appKey={}", appKey); } else { log.warn("JPush not configured, push disabled"); } } // ==================== 通知栏推送 ==================== /** * 按别名推送通知 * * @param alias 别名(如会员ID/司机ID/门店ID) * @param title 通知标题 * @param content 通知内容 * @param extras 附加字段(可选) * @return 是否推送成功 */ public static boolean sendByAlias(String alias, String title, String content, Map extras) { return sendByAliases(Collections.singletonList(alias), title, content, extras); } /** * 按别名批量推送通知 */ public static boolean sendByAliases(List aliases, String title, String content, Map extras) { if (!available() || aliases == null || aliases.isEmpty()) { return false; } JSONObject body = buildNotificationBody(aliases, "alias", title, content, extras); return doPush(body, "sendByAliases"); } /** * 按RegistrationId推送通知 */ public static boolean sendByRegId(String regId, String title, String content, Map extras) { return sendByRegIds(Collections.singletonList(regId), title, content, extras); } /** * 按RegistrationId批量推送通知 */ public static boolean sendByRegIds(List regIds, String title, String content, Map extras) { if (!available() || regIds == null || regIds.isEmpty()) { return false; } JSONObject body = buildNotificationBody(regIds, "registration_id", title, content, extras); return doPush(body, "sendByRegIds"); } /** * 广播推送通知(推送给所有用户) */ public static boolean sendToAll(String title, String content, Map extras) { if (!available()) { return false; } JSONObject body = buildNotificationBody(null, "all", title, content, extras); return doPush(body, "sendToAll"); } // ==================== 自定义消息(静默推送,不显示通知栏) ==================== /** * 按别名推送自定义消息 */ public static boolean sendMessageByAlias(String alias, String content, Map extras) { return sendMessagesByAlias(Collections.singletonList(alias), content, extras); } /** * 按别名批量推送自定义消息 */ public static boolean sendMessagesByAlias(List aliases, String content, Map extras) { if (!available() || aliases == null || aliases.isEmpty()) { return false; } JSONObject body = buildMessageBody(aliases, "alias", content, extras); return doPush(body, "sendMessageByAlias"); } // ==================== 内部方法 ==================== private static boolean available() { return StringUtils.isNotBlank(authHeader); } /** * 构建通知栏推送请求体 */ private static JSONObject buildNotificationBody(List targets, String audienceType, String title, String content, Map extras) { JSONObject body = new JSONObject(); // platform body.put("platform", "all"); // audience if ("all".equals(audienceType)) { body.put("audience", "all"); } else { JSONObject audience = new JSONObject(); JSONArray arr = new JSONArray(); arr.addAll(targets); audience.put(audienceType, arr); body.put("audience", audience); } // notification JSONObject notification = new JSONObject(); // Android JSONObject android = new JSONObject(); android.put("title", title); android.put("alert", content); if (extras != null && !extras.isEmpty()) { android.put("extras", mapToJson(extras)); } notification.put("android", android); // iOS JSONObject ios = new JSONObject(); ios.put("alert", content); ios.put("sound", "default"); if (extras != null && !extras.isEmpty()) { ios.put("extras", mapToJson(extras)); } notification.put("ios", ios); body.put("notification", notification); // options JSONObject options = new JSONObject(); options.put("apns_production", apnsProduction); body.put("options", options); return body; } /** * 构建自定义消息请求体(不显示通知栏) */ private static JSONObject buildMessageBody(List targets, String audienceType, String content, Map extras) { JSONObject body = new JSONObject(); body.put("platform", "all"); JSONObject audience = new JSONObject(); JSONArray arr = new JSONArray(); arr.addAll(targets); audience.put(audienceType, arr); body.put("audience", audience); // message JSONObject message = new JSONObject(); message.put("msg_content", content); if (extras != null && !extras.isEmpty()) { message.put("extras", mapToJson(extras)); } body.put("message", message); JSONObject options = new JSONObject(); options.put("apns_production", apnsProduction); body.put("options", options); return body; } /** * Map → JSONObject */ private static JSONObject mapToJson(Map map) { JSONObject json = new JSONObject(); json.putAll(map); return json; } /** * 执行推送请求 */ private static boolean doPush(JSONObject body, String action) { try { String response = HttpsUtil.postJson(PUSH_URL, body.toJSONString(), authHeader); log.info("JPush {}, response={}", action, response); if (StringUtils.isNotBlank(response)) { JSONObject resp = JSONObject.parseObject(response); return resp.containsKey("msg_id") || (resp.containsKey("statusCode") && resp.getIntValue("statusCode") == 0); } return false; } catch (Exception e) { log.error("JPush {} error", action, e); return false; } } }