diff --git a/push-service/src/main/java/com/xuqm/push/service/provider/OppoPushProvider.java b/push-service/src/main/java/com/xuqm/push/service/provider/OppoPushProvider.java index 5e80990..49672a0 100644 --- a/push-service/src/main/java/com/xuqm/push/service/provider/OppoPushProvider.java +++ b/push-service/src/main/java/com/xuqm/push/service/provider/OppoPushProvider.java @@ -8,9 +8,12 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.net.URI; +import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; import java.time.Instant; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -54,29 +57,39 @@ public class OppoPushProvider implements PushProvider { try { String authToken = getAccessToken(vendorAppKey, masterSecret); String messageId = appKey + "_" + System.currentTimeMillis(); - Map inner = new java.util.LinkedHashMap<>(); - inner.put("app_message_id", messageId); - inner.put("title", title); - inner.put("content", body); - inner.put("target_type", 2); - inner.put("target_value", token); + Map notification = new java.util.LinkedHashMap<>(); + notification.put("app_message_id", messageId); + notification.put("title", title); + notification.put("content", body); + notification.put("click_action_type", 0); + 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() ? options.channelId() : resolveConfig(appKey, "channelId"); - if (!channelId.isBlank()) inner.put("channel_id", channelId); - Map message = Map.of("message", inner); - String requestBody = objectMapper.writeValueAsString(message); + if (!channelId.isBlank()) notification.put("channel_id", channelId); + Map message = new java.util.LinkedHashMap<>(); + 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() .uri(URI.create(PUSH_URL)) - .header("Content-Type", "application/json") + .header("Content-Type", "application/x-www-form-urlencoded") .header("auth_token", authToken) .POST(HttpRequest.BodyPublishers.ofString(requestBody)) .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - if (response.statusCode() == 200) { - JsonNode json = objectMapper.readTree(response.body()); - return json.path("code").asInt(-1) == 0; + JsonNode json = objectMapper.readTree(response.body()); + 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; } catch (Exception e) { log.error("OPPO push failed: {}", e.getMessage()); @@ -89,20 +102,43 @@ public class OppoPushProvider implements PushProvider { if (cached != null && cached.expiresAt > Instant.now().getEpochSecond()) { return cached.token; } - Map 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() .uri(URI.create(AUTH_URL)) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(body))) + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); JsonNode json = objectMapper.readTree(response.body()); + int code = json.path("code").asInt(-1); String token = json.path("data").path("auth_token").asText(""); - long createTime = json.path("data").path("create_time").asLong(Instant.now().getEpochSecond()); - tokenCache.put(appKey, new TokenCache(token, createTime + 86400)); + if (response.statusCode() != 200 || code != 0 || token.isBlank()) { + 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; } + private static String form(Map 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) { JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH") .map(node -> node.path("vendors").path("oppo"))