fix(push): implement OPPO server API protocol
这个提交包含在:
父节点
e655ab9fd4
当前提交
d18d3f8c57
@ -8,9 +8,12 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -54,29 +57,39 @@ public class OppoPushProvider implements PushProvider {
|
|||||||
try {
|
try {
|
||||||
String authToken = getAccessToken(vendorAppKey, masterSecret);
|
String authToken = getAccessToken(vendorAppKey, masterSecret);
|
||||||
String messageId = appKey + "_" + System.currentTimeMillis();
|
String messageId = appKey + "_" + System.currentTimeMillis();
|
||||||
Map<String, Object> inner = new java.util.LinkedHashMap<>();
|
Map<String, Object> notification = new java.util.LinkedHashMap<>();
|
||||||
inner.put("app_message_id", messageId);
|
notification.put("app_message_id", messageId);
|
||||||
inner.put("title", title);
|
notification.put("title", title);
|
||||||
inner.put("content", body);
|
notification.put("content", body);
|
||||||
inner.put("target_type", 2);
|
notification.put("click_action_type", 0);
|
||||||
inner.put("target_value", token);
|
notification.put("off_line", true);
|
||||||
|
notification.put("off_line_ttl", 259200);
|
||||||
|
if (payload != null && !payload.isBlank()) {
|
||||||
|
notification.put("action_parameters", payload);
|
||||||
|
}
|
||||||
String channelId = options != null && options.channelId() != null && !options.channelId().isBlank()
|
String channelId = options != null && options.channelId() != null && !options.channelId().isBlank()
|
||||||
? options.channelId()
|
? options.channelId()
|
||||||
: resolveConfig(appKey, "channelId");
|
: resolveConfig(appKey, "channelId");
|
||||||
if (!channelId.isBlank()) inner.put("channel_id", channelId);
|
if (!channelId.isBlank()) notification.put("channel_id", channelId);
|
||||||
Map<String, Object> message = Map.of("message", inner);
|
Map<String, Object> message = new java.util.LinkedHashMap<>();
|
||||||
String requestBody = objectMapper.writeValueAsString(message);
|
message.put("target_type", 2);
|
||||||
|
message.put("target_value", token);
|
||||||
|
message.put("notification", notification);
|
||||||
|
String requestBody = form(Map.of("message", objectMapper.writeValueAsString(message)));
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(URI.create(PUSH_URL))
|
.uri(URI.create(PUSH_URL))
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||||
.header("auth_token", authToken)
|
.header("auth_token", authToken)
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
||||||
.build();
|
.build();
|
||||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
if (response.statusCode() == 200) {
|
|
||||||
JsonNode json = objectMapper.readTree(response.body());
|
JsonNode json = objectMapper.readTree(response.body());
|
||||||
return json.path("code").asInt(-1) == 0;
|
int code = json.path("code").asInt(-1);
|
||||||
|
if (response.statusCode() == 200 && code == 0) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
log.error("OPPO push rejected: httpStatus={} code={} message={}",
|
||||||
|
response.statusCode(), code, json.path("message").asText(""));
|
||||||
return false;
|
return false;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("OPPO push failed: {}", e.getMessage());
|
log.error("OPPO push failed: {}", e.getMessage());
|
||||||
@ -89,20 +102,43 @@ public class OppoPushProvider implements PushProvider {
|
|||||||
if (cached != null && cached.expiresAt > Instant.now().getEpochSecond()) {
|
if (cached != null && cached.expiresAt > Instant.now().getEpochSecond()) {
|
||||||
return cached.token;
|
return cached.token;
|
||||||
}
|
}
|
||||||
Map<String, String> body = Map.of("app_key", appKey, "master_secret", masterSecret);
|
long timestamp = System.currentTimeMillis();
|
||||||
|
String sign = sha256Hex(appKey + timestamp + masterSecret);
|
||||||
|
String body = form(Map.of(
|
||||||
|
"app_key", appKey,
|
||||||
|
"timestamp", Long.toString(timestamp),
|
||||||
|
"sign", sign
|
||||||
|
));
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(URI.create(AUTH_URL))
|
.uri(URI.create(AUTH_URL))
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(body)))
|
.POST(HttpRequest.BodyPublishers.ofString(body))
|
||||||
.build();
|
.build();
|
||||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
JsonNode json = objectMapper.readTree(response.body());
|
JsonNode json = objectMapper.readTree(response.body());
|
||||||
|
int code = json.path("code").asInt(-1);
|
||||||
String token = json.path("data").path("auth_token").asText("");
|
String token = json.path("data").path("auth_token").asText("");
|
||||||
long createTime = json.path("data").path("create_time").asLong(Instant.now().getEpochSecond());
|
if (response.statusCode() != 200 || code != 0 || token.isBlank()) {
|
||||||
tokenCache.put(appKey, new TokenCache(token, createTime + 86400));
|
throw new IllegalStateException("OPPO auth rejected: httpStatus=" + response.statusCode()
|
||||||
|
+ ", code=" + code + ", message=" + json.path("message").asText(""));
|
||||||
|
}
|
||||||
|
tokenCache.put(appKey, new TokenCache(token, Instant.now().getEpochSecond() + 23 * 3600));
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String form(Map<String, String> values) {
|
||||||
|
return values.entrySet().stream()
|
||||||
|
.map(entry -> URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) + "="
|
||||||
|
+ URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8))
|
||||||
|
.collect(java.util.stream.Collectors.joining("&"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sha256Hex(String value) throws Exception {
|
||||||
|
byte[] digest = MessageDigest.getInstance("SHA-256")
|
||||||
|
.digest(value.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return java.util.HexFormat.of().formatHex(digest);
|
||||||
|
}
|
||||||
|
|
||||||
private String resolveConfig(String appKey, String key) {
|
private String resolveConfig(String appKey, String key) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("vendors").path("oppo"))
|
.map(node -> node.path("vendors").path("oppo"))
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户