rk
2 天以前 467fe3b3ec6aa9d449b094bdd9df4611323d88d1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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<String, String> extras) {
        return sendByAliases(Collections.singletonList(alias), title, content, extras);
    }
 
    /**
     * 按别名批量推送通知
     */
    public static boolean sendByAliases(List<String> aliases, String title, String content, Map<String, String> 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<String, String> extras) {
        return sendByRegIds(Collections.singletonList(regId), title, content, extras);
    }
 
    /**
     * 按RegistrationId批量推送通知
     */
    public static boolean sendByRegIds(List<String> regIds, String title, String content, Map<String, String> 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<String, String> 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<String, String> extras) {
        return sendMessagesByAlias(Collections.singletonList(alias), content, extras);
    }
 
    /**
     * 按别名批量推送自定义消息
     */
    public static boolean sendMessagesByAlias(List<String> aliases, String content, Map<String, String> 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<String> targets, String audienceType,
                                                    String title, String content, Map<String, String> 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<String> targets, String audienceType,
                                               String content, Map<String, String> 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<String, String> → JSONObject
     */
    private static JSONObject mapToJson(Map<String, String> 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;
        }
    }
}