|
|
@@ -17,7 +17,9 @@ import org.springframework.web.client.RestTemplate;
|
|
|
import shop.alien.entity.store.CommonPushChannelConfig;
|
|
|
import shop.alien.entity.store.CommonPushTask;
|
|
|
import shop.alien.store.config.CommonPushProperties;
|
|
|
+import shop.alien.store.dto.CommonPushChannelStatsDto;
|
|
|
import shop.alien.store.dto.CommonPushTargetDto;
|
|
|
+import shop.alien.store.dto.CommonPushVendorSendResult;
|
|
|
|
|
|
import java.net.URLEncoder;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
@@ -57,185 +59,200 @@ public class CommonPushVendorHttpClient {
|
|
|
/**
|
|
|
* OPPO 全量广播推送(target_type=1),用于 targetType=1 全量场景。
|
|
|
*/
|
|
|
- public boolean sendOppoBroadcast(CommonPushChannelConfig config, CommonPushTask task) {
|
|
|
+ public CommonPushVendorSendResult sendOppoBroadcast(CommonPushChannelConfig config, CommonPushTask task) {
|
|
|
if (config == null || task == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
JSONObject credential = parseCredential(config.getCredentialJson());
|
|
|
if (credential == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
try {
|
|
|
String authToken = obtainOppoAuthToken(credential);
|
|
|
if (StringUtils.isBlank(authToken)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
String messageId = saveOppoMessageContent(authToken, task, null);
|
|
|
if (StringUtils.isBlank(messageId)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
- return broadcastOppoMessage(authToken, messageId);
|
|
|
+ String taskId = broadcastOppoMessage(authToken, messageId);
|
|
|
+ if (StringUtils.isBlank(taskId)) {
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
+ }
|
|
|
+ return CommonPushVendorSendResult.ok(taskId, messageId);
|
|
|
} catch (Exception e) {
|
|
|
log.error("OPPO 广播推送失败, taskNo={}, err={}", task.getTaskNo(), e.getMessage(), e);
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 小米全量广播推送,用于 targetType=1 全量场景。
|
|
|
*/
|
|
|
- public boolean sendXiaomiBroadcast(CommonPushChannelConfig config, CommonPushTask task) {
|
|
|
+ public CommonPushVendorSendResult sendXiaomiBroadcast(CommonPushChannelConfig config, CommonPushTask task) {
|
|
|
if (config == null || task == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
JSONObject credential = parseCredential(config.getCredentialJson());
|
|
|
if (credential == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
String appSecret = credential.getString("appSecret");
|
|
|
if (StringUtils.isBlank(appSecret)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
if (!validateXiaomiCredential(credential, task.getTaskNo())) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
try {
|
|
|
MultiValueMap<String, String> form = buildXiaomiMessageForm(credential, task, null);
|
|
|
Map<String, String> headers = new HashMap<>();
|
|
|
headers.put("Authorization", "key=" + appSecret);
|
|
|
String resp = postForm("https://api.xmpush.xiaomi.com/v3/message/all", form, headers);
|
|
|
- return isXiaomiSendSuccess(resp, task.getTaskNo(), "全量广播");
|
|
|
+ return parseXiaomiSendResult(resp, task.getTaskNo(), "全量广播");
|
|
|
} catch (Exception e) {
|
|
|
log.error("小米广播推送失败, taskNo={}, err={}", task.getTaskNo(), e.getMessage(), e);
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* vivo 全量广播推送,用于 targetType=1 全量场景。
|
|
|
*/
|
|
|
- public boolean sendVivoBroadcast(CommonPushChannelConfig config, CommonPushTask task) {
|
|
|
+ public CommonPushVendorSendResult sendVivoBroadcast(CommonPushChannelConfig config, CommonPushTask task) {
|
|
|
if (config == null || task == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
JSONObject credential = parseCredential(config.getCredentialJson());
|
|
|
if (credential == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
try {
|
|
|
String authToken = obtainVivoAuthToken(credential);
|
|
|
if (StringUtils.isBlank(authToken)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
JSONObject sendBody = buildVivoNotificationBody(credential, task, null);
|
|
|
Map<String, String> headers = new HashMap<>();
|
|
|
headers.put("authToken", authToken);
|
|
|
String sendResp = postJson("https://api-push.vivo.com.cn/message/all", sendBody.toJSONString(), headers);
|
|
|
- return isVivoSendSuccess(sendResp, task.getTaskNo(), "全量广播");
|
|
|
+ return parseVivoSendResult(sendResp, task.getTaskNo(), "全量广播");
|
|
|
} catch (Exception e) {
|
|
|
log.error("vivo 广播推送失败, taskNo={}, err={}", task.getTaskNo(), e.getMessage(), e);
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 华为全量推送:官方无 /all 接口,按 token 批量下发(每批最多 1000 个)。
|
|
|
*/
|
|
|
- public boolean sendHuaweiBroadcast(CommonPushChannelConfig config, CommonPushTask task,
|
|
|
+ public CommonPushVendorSendResult sendHuaweiBroadcast(CommonPushChannelConfig config, CommonPushTask task,
|
|
|
List<CommonPushTargetDto> targets) {
|
|
|
if (config == null || task == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
JSONObject credential = parseCredential(config.getCredentialJson());
|
|
|
if (credential == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
List<String> deviceIds = extractDeviceIds(targets);
|
|
|
if (deviceIds.isEmpty()) {
|
|
|
log.warn("华为全量推送无可用 deviceId, taskNo={}", task.getTaskNo());
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
try {
|
|
|
int successBatches = 0;
|
|
|
int totalBatches = 0;
|
|
|
+ String requestId = null;
|
|
|
for (List<String> batch : partitionList(deviceIds, HUAWEI_BATCH_TOKEN_LIMIT)) {
|
|
|
totalBatches++;
|
|
|
- if (sendHuaweiLike(credential, task, batch)) {
|
|
|
+ CommonPushVendorSendResult batchResult = sendHuaweiLike(credential, task, batch);
|
|
|
+ if (batchResult.isSuccess()) {
|
|
|
successBatches++;
|
|
|
+ if (requestId == null) {
|
|
|
+ requestId = batchResult.getVendorTaskId();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
log.info("华为全量批量推送完成, taskNo={}, devices={}, batches={}/{}",
|
|
|
task.getTaskNo(), deviceIds.size(), successBatches, totalBatches);
|
|
|
- return successBatches > 0;
|
|
|
+ return successBatches > 0 ? CommonPushVendorSendResult.ok(requestId) : CommonPushVendorSendResult.fail();
|
|
|
} catch (Exception e) {
|
|
|
log.error("华为全量推送失败, taskNo={}, err={}", task.getTaskNo(), e.getMessage(), e);
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 荣耀全量推送:官方无 /all 接口,按 token 批量下发(每批最多 1000 个)。
|
|
|
*/
|
|
|
- public boolean sendHonorBroadcast(CommonPushChannelConfig config, CommonPushTask task,
|
|
|
+ public CommonPushVendorSendResult sendHonorBroadcast(CommonPushChannelConfig config, CommonPushTask task,
|
|
|
List<CommonPushTargetDto> targets) {
|
|
|
if (config == null || task == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
JSONObject credential = parseCredential(config.getCredentialJson());
|
|
|
if (credential == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
List<String> deviceIds = extractDeviceIds(targets);
|
|
|
if (deviceIds.isEmpty()) {
|
|
|
log.warn("荣耀全量推送无可用 deviceId, taskNo={}", task.getTaskNo());
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
try {
|
|
|
int successBatches = 0;
|
|
|
int totalBatches = 0;
|
|
|
+ String requestId = null;
|
|
|
for (List<String> batch : partitionList(deviceIds, HUAWEI_BATCH_TOKEN_LIMIT)) {
|
|
|
totalBatches++;
|
|
|
- if (sendHonorLike(credential, task, batch)) {
|
|
|
+ CommonPushVendorSendResult batchResult = sendHonorLike(credential, task, batch);
|
|
|
+ if (batchResult.isSuccess()) {
|
|
|
successBatches++;
|
|
|
+ if (requestId == null) {
|
|
|
+ requestId = batchResult.getVendorTaskId();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
log.info("荣耀全量批量推送完成, taskNo={}, devices={}, batches={}/{}",
|
|
|
task.getTaskNo(), deviceIds.size(), successBatches, totalBatches);
|
|
|
- return successBatches > 0;
|
|
|
+ return successBatches > 0 ? CommonPushVendorSendResult.ok(requestId) : CommonPushVendorSendResult.fail();
|
|
|
} catch (Exception e) {
|
|
|
log.error("荣耀全量推送失败, taskNo={}, err={}", task.getTaskNo(), e.getMessage(), e);
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* APNs 全量推送:按 iOS deviceToken 逐条下发(Apple 无 /all 接口)。
|
|
|
*/
|
|
|
- public boolean sendApnsBroadcast(CommonPushChannelConfig config, CommonPushTask task,
|
|
|
+ public CommonPushVendorSendResult sendApnsBroadcast(CommonPushChannelConfig config, CommonPushTask task,
|
|
|
List<CommonPushTargetDto> targets) {
|
|
|
if (config == null || task == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
JSONObject credential = parseCredential(config.getCredentialJson());
|
|
|
if (credential == null || !apnsPushClient.validateCredential(credential)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
List<CommonPushTargetDto> apnsTargets = ApnsPushClient.filterApnsTargets(targets, targets);
|
|
|
if (apnsTargets.isEmpty()) {
|
|
|
log.warn("APNs 全量推送无 iOS 设备 token, taskNo={}", task.getTaskNo());
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
- return apnsPushClient.sendBroadcast(credential, task, apnsTargets);
|
|
|
+ boolean ok = apnsPushClient.sendBroadcast(credential, task, apnsTargets);
|
|
|
+ return ok ? CommonPushVendorSendResult.ok(task.getTaskNo()) : CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
|
|
|
- public boolean send(CommonPushChannelConfig config, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
+ public CommonPushVendorSendResult send(CommonPushChannelConfig config, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
if (config == null || task == null || target == null || StringUtils.isBlank(target.getDeviceId())) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
String deviceId = target.getDeviceId();
|
|
|
JSONObject credential = parseCredential(config.getCredentialJson());
|
|
|
if (credential == null) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
String channelCode = StringUtils.lowerCase(StringUtils.trim(config.getChannelCode()));
|
|
|
try {
|
|
|
@@ -247,7 +264,8 @@ public class CommonPushVendorHttpClient {
|
|
|
case "oppo":
|
|
|
return sendOppo(credential, task, target);
|
|
|
case "samsung":
|
|
|
- return sendSamsung(credential, task, target);
|
|
|
+ return sendSamsung(credential, task, target) ? CommonPushVendorSendResult.ok(task.getTaskNo())
|
|
|
+ : CommonPushVendorSendResult.fail();
|
|
|
case "huawei":
|
|
|
return sendHuawei(credential, task, deviceId);
|
|
|
case "honor":
|
|
|
@@ -256,29 +274,29 @@ public class CommonPushVendorHttpClient {
|
|
|
return sendApns(credential, task, deviceId);
|
|
|
default:
|
|
|
log.warn("不支持的推送渠道: {}", channelCode);
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
log.error("渠道推送失败, channelCode={}, deviceId={}, err={}", channelCode, deviceId, e.getMessage(), e);
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private boolean sendVivo(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
+ private CommonPushVendorSendResult sendVivo(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
String deviceId = target.getDeviceId();
|
|
|
if (StringUtils.isBlank(deviceId)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
String authToken = obtainVivoAuthToken(credential);
|
|
|
if (StringUtils.isBlank(authToken)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
JSONObject sendBody = buildVivoNotificationBody(credential, task, target);
|
|
|
sendBody.put("regId", deviceId);
|
|
|
Map<String, String> headers = new HashMap<>();
|
|
|
headers.put("authToken", authToken);
|
|
|
String sendResp = postJson("https://api-push.vivo.com.cn/message/send", sendBody.toJSONString(), headers);
|
|
|
- return isVivoSendSuccess(sendResp, task.getTaskNo(), "单推");
|
|
|
+ return parseVivoSendResult(sendResp, task.getTaskNo(), "单推");
|
|
|
}
|
|
|
|
|
|
private String obtainVivoAuthToken(JSONObject credential) {
|
|
|
@@ -345,34 +363,35 @@ public class CommonPushVendorHttpClient {
|
|
|
return param.toJSONString();
|
|
|
}
|
|
|
|
|
|
- private boolean isVivoSendSuccess(String resp, String taskNo, String scene) {
|
|
|
+ private CommonPushVendorSendResult parseVivoSendResult(String resp, String taskNo, String scene) {
|
|
|
log.info("vivo{}响应: {}", scene, resp);
|
|
|
JSONObject json = JSONObject.parseObject(resp);
|
|
|
if (json != null && json.getIntValue("result") == 0) {
|
|
|
- return true;
|
|
|
+ String taskId = StringUtils.defaultIfBlank(json.getString("taskId"), json.getString("task_id"));
|
|
|
+ return CommonPushVendorSendResult.ok(taskId);
|
|
|
}
|
|
|
if (json != null) {
|
|
|
log.error("vivo{}失败, taskNo={}, result={}, desc={}",
|
|
|
scene, taskNo, json.getIntValue("result"), json.getString("desc"));
|
|
|
}
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
|
|
|
- private boolean sendXiaomi(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
+ private CommonPushVendorSendResult sendXiaomi(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
String deviceId = target.getDeviceId();
|
|
|
String appSecret = credential.getString("appSecret");
|
|
|
if (StringUtils.isAnyBlank(appSecret, deviceId)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
if (!validateXiaomiCredential(credential, task.getTaskNo())) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
MultiValueMap<String, String> form = buildXiaomiMessageForm(credential, task, target);
|
|
|
form.add("registration_id", deviceId);
|
|
|
Map<String, String> headers = new HashMap<>();
|
|
|
headers.put("Authorization", "key=" + appSecret);
|
|
|
String resp = postForm("https://api.xmpush.xiaomi.com/v3/message/regid", form, headers);
|
|
|
- return isXiaomiSendSuccess(resp, task.getTaskNo(), "单推");
|
|
|
+ return parseXiaomiSendResult(resp, task.getTaskNo(), "单推");
|
|
|
}
|
|
|
|
|
|
private boolean validateXiaomiCredential(JSONObject credential, String taskNo) {
|
|
|
@@ -398,17 +417,32 @@ public class CommonPushVendorHttpClient {
|
|
|
StringUtils.trimToNull(credential.getString("channel_id")));
|
|
|
}
|
|
|
|
|
|
- private boolean isXiaomiSendSuccess(String resp, String taskNo, String scene) {
|
|
|
+ private CommonPushVendorSendResult parseXiaomiSendResult(String resp, String taskNo, String scene) {
|
|
|
log.info("小米{}响应: {}", scene, resp);
|
|
|
JSONObject json = JSONObject.parseObject(resp);
|
|
|
if (json != null && "ok".equalsIgnoreCase(json.getString("result"))) {
|
|
|
- return true;
|
|
|
+ String msgId = extractXiaomiMessageId(json);
|
|
|
+ return CommonPushVendorSendResult.ok(msgId);
|
|
|
}
|
|
|
if (json != null) {
|
|
|
log.error("小米{}失败, taskNo={}, code={}, reason={}, description={}",
|
|
|
scene, taskNo, json.getString("code"), json.getString("reason"), json.getString("description"));
|
|
|
}
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String extractXiaomiMessageId(JSONObject json) {
|
|
|
+ if (json == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ JSONObject data = json.getJSONObject("data");
|
|
|
+ if (data != null) {
|
|
|
+ String id = StringUtils.defaultIfBlank(data.getString("id"), data.getString("msg_id"));
|
|
|
+ if (StringUtils.isNotBlank(id)) {
|
|
|
+ return id;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return StringUtils.defaultIfBlank(json.getString("messageId"), json.getString("msg_id"));
|
|
|
}
|
|
|
|
|
|
private MultiValueMap<String, String> buildXiaomiMessageForm(JSONObject credential, CommonPushTask task,
|
|
|
@@ -461,11 +495,11 @@ public class CommonPushVendorHttpClient {
|
|
|
/**
|
|
|
* OPPO 厂商直连:使用 RegistrationID(非 UniPush CID),并设置送达回执回调。
|
|
|
*/
|
|
|
- private boolean sendOppo(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
+ private CommonPushVendorSendResult sendOppo(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
String deviceId = target.getDeviceId();
|
|
|
String authToken = obtainOppoAuthToken(credential);
|
|
|
if (StringUtils.isBlank(authToken)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
|
|
|
JSONObject notification = buildOppoNotification(task, target);
|
|
|
@@ -489,12 +523,25 @@ public class CommonPushVendorHttpClient {
|
|
|
);
|
|
|
String body = response.getBody();
|
|
|
log.info("OPPO 单推响应: {}", body);
|
|
|
- JSONObject resp = JSONObject.parseObject(body);
|
|
|
- return resp != null && resp.getIntValue("code") == 0;
|
|
|
+ return parseOppoSendResult(body);
|
|
|
} catch (Exception e) {
|
|
|
log.error("OPPO 单推请求异常: {}", e.getMessage(), e);
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private CommonPushVendorSendResult parseOppoSendResult(String resp) {
|
|
|
+ JSONObject json = JSONObject.parseObject(resp);
|
|
|
+ if (json == null || json.getIntValue("code") != 0) {
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
+ }
|
|
|
+ JSONObject data = json.getJSONObject("data");
|
|
|
+ if (data == null) {
|
|
|
+ return CommonPushVendorSendResult.ok(null);
|
|
|
}
|
|
|
+ String taskId = StringUtils.defaultIfBlank(data.getString("task_id"), data.getString("message_id"));
|
|
|
+ String messageId = StringUtils.defaultIfBlank(data.getString("message_id"), data.getString("messageId"));
|
|
|
+ return CommonPushVendorSendResult.ok(taskId, messageId);
|
|
|
}
|
|
|
|
|
|
private String obtainOppoAuthToken(JSONObject credential) {
|
|
|
@@ -553,9 +600,9 @@ public class CommonPushVendorHttpClient {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * OPPO 广播下发,target_type=1 表示全量用户。
|
|
|
+ * OPPO 广播下发,target_type=1 表示全量用户,返回 task_id。
|
|
|
*/
|
|
|
- private boolean broadcastOppoMessage(String authToken, String messageId) {
|
|
|
+ private String broadcastOppoMessage(String authToken, String messageId) {
|
|
|
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
|
|
form.add("auth_token", authToken);
|
|
|
form.add("message_id", messageId);
|
|
|
@@ -563,7 +610,14 @@ public class CommonPushVendorHttpClient {
|
|
|
String resp = postForm("https://api.push.oppomobile.com/server/v1/message/notification/broadcast", form, null);
|
|
|
log.info("OPPO 广播推送响应: {}", resp);
|
|
|
JSONObject json = JSONObject.parseObject(resp);
|
|
|
- return json != null && json.getIntValue("code") == 0;
|
|
|
+ if (json == null || json.getIntValue("code") != 0) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ JSONObject data = json.getJSONObject("data");
|
|
|
+ if (data == null) {
|
|
|
+ return messageId;
|
|
|
+ }
|
|
|
+ return StringUtils.defaultIfBlank(data.getString("task_id"), messageId);
|
|
|
}
|
|
|
|
|
|
private String resolveCallbackUrl() {
|
|
|
@@ -690,26 +744,26 @@ public class CommonPushVendorHttpClient {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private boolean sendHuawei(JSONObject credential, CommonPushTask task, String deviceId) {
|
|
|
+ private CommonPushVendorSendResult sendHuawei(JSONObject credential, CommonPushTask task, String deviceId) {
|
|
|
return sendHuaweiLike(credential, task, Collections.singletonList(deviceId));
|
|
|
}
|
|
|
|
|
|
- private boolean sendHonor(JSONObject credential, CommonPushTask task, String deviceId) {
|
|
|
+ private CommonPushVendorSendResult sendHonor(JSONObject credential, CommonPushTask task, String deviceId) {
|
|
|
return sendHonorLike(credential, task, Collections.singletonList(deviceId));
|
|
|
}
|
|
|
|
|
|
- private boolean sendHuaweiLike(JSONObject credential, CommonPushTask task, List<String> deviceIds) {
|
|
|
+ private CommonPushVendorSendResult sendHuaweiLike(JSONObject credential, CommonPushTask task, List<String> deviceIds) {
|
|
|
if (deviceIds == null || deviceIds.isEmpty()) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
String appId = credential.getString("appId");
|
|
|
String appSecret = credential.getString("appSecret");
|
|
|
if (StringUtils.isAnyBlank(appId, appSecret)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
String accessToken = obtainHuaweiAccessToken(credential);
|
|
|
if (StringUtils.isBlank(accessToken)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
|
|
|
JSONObject body = buildHuaweiMessageBody(task, deviceIds);
|
|
|
@@ -718,26 +772,27 @@ public class CommonPushVendorHttpClient {
|
|
|
String sendResp = postJson(HUAWEI_PUSH_BASE_URL + "/v1/" + appId + "/messages:send",
|
|
|
body.toJSONString(), headers);
|
|
|
JSONObject sendJson = JSONObject.parseObject(sendResp);
|
|
|
- boolean ok = sendJson != null && StringUtils.isNotBlank(sendJson.getString("requestId"));
|
|
|
- if (!ok) {
|
|
|
+ String requestId = sendJson != null ? sendJson.getString("requestId") : null;
|
|
|
+ if (StringUtils.isBlank(requestId)) {
|
|
|
log.error("华为推送失败, taskNo={}, deviceCount={}, resp={}",
|
|
|
task.getTaskNo(), deviceIds.size(), sendResp);
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
- return ok;
|
|
|
+ return CommonPushVendorSendResult.ok(requestId);
|
|
|
}
|
|
|
|
|
|
- private boolean sendHonorLike(JSONObject credential, CommonPushTask task, List<String> deviceIds) {
|
|
|
+ private CommonPushVendorSendResult sendHonorLike(JSONObject credential, CommonPushTask task, List<String> deviceIds) {
|
|
|
if (deviceIds == null || deviceIds.isEmpty()) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
String appId = credential.getString("appId");
|
|
|
String appSecret = credential.getString("appSecret");
|
|
|
if (StringUtils.isAnyBlank(appId, appSecret)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
String accessToken = obtainHonorAccessToken(credential);
|
|
|
if (StringUtils.isBlank(accessToken)) {
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
|
|
|
JSONObject body = buildHuaweiMessageBody(task, deviceIds);
|
|
|
@@ -746,12 +801,13 @@ public class CommonPushVendorHttpClient {
|
|
|
String sendResp = postJson(HONOR_PUSH_BASE_URL + "/v1/" + appId + "/messages:send",
|
|
|
body.toJSONString(), headers);
|
|
|
JSONObject sendJson = JSONObject.parseObject(sendResp);
|
|
|
- boolean ok = sendJson != null && StringUtils.isNotBlank(sendJson.getString("requestId"));
|
|
|
- if (!ok) {
|
|
|
+ String requestId = sendJson != null ? sendJson.getString("requestId") : null;
|
|
|
+ if (StringUtils.isBlank(requestId)) {
|
|
|
log.error("荣耀推送失败, taskNo={}, deviceCount={}, resp={}",
|
|
|
task.getTaskNo(), deviceIds.size(), sendResp);
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
- return ok;
|
|
|
+ return CommonPushVendorSendResult.ok(requestId);
|
|
|
}
|
|
|
|
|
|
private String obtainHuaweiAccessToken(JSONObject credential) {
|
|
|
@@ -829,12 +885,13 @@ public class CommonPushVendorHttpClient {
|
|
|
return partitions;
|
|
|
}
|
|
|
|
|
|
- private boolean sendApns(JSONObject credential, CommonPushTask task, String deviceId) {
|
|
|
+ private CommonPushVendorSendResult sendApns(JSONObject credential, CommonPushTask task, String deviceId) {
|
|
|
if (!apnsPushClient.validateCredential(credential)) {
|
|
|
log.warn("APNs 凭证不完整, deviceId={}", deviceId);
|
|
|
- return false;
|
|
|
+ return CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
- return apnsPushClient.send(credential, task, deviceId);
|
|
|
+ boolean ok = apnsPushClient.send(credential, task, deviceId);
|
|
|
+ return ok ? CommonPushVendorSendResult.ok(task.getTaskNo()) : CommonPushVendorSendResult.fail();
|
|
|
}
|
|
|
|
|
|
private JSONObject parseCredential(String credentialJson) {
|
|
|
@@ -859,6 +916,141 @@ public class CommonPushVendorHttpClient {
|
|
|
return payload.toJSONString();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 查询 OPPO 广播推送统计(需 message_id + task_id)。
|
|
|
+ */
|
|
|
+ public CommonPushChannelStatsDto queryOppoStatistics(JSONObject credential, String messageId, String taskId) {
|
|
|
+ if (credential == null || StringUtils.isAnyBlank(messageId, taskId)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ String authToken = obtainOppoAuthToken(credential);
|
|
|
+ if (StringUtils.isBlank(authToken)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
|
|
+ form.add("auth_token", authToken);
|
|
|
+ form.add("message_id", messageId);
|
|
|
+ form.add("task_id", taskId);
|
|
|
+ String resp = postForm("https://api.push.oppomobile.com/server/v1/message/statistics", form, null);
|
|
|
+ log.info("OPPO 统计查询响应: messageId={}, taskId={}, resp={}", messageId, taskId, resp);
|
|
|
+ JSONObject json = JSONObject.parseObject(resp);
|
|
|
+ if (json == null || json.getIntValue("code") != 0) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ JSONObject data = json.getJSONObject("data");
|
|
|
+ if (data == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ CommonPushChannelStatsDto stats = new CommonPushChannelStatsDto();
|
|
|
+ stats.setRealSend(longToString(data.getLong("sendCount")));
|
|
|
+ stats.setRealDelivered(longToString(data.getLong("arriveCount")));
|
|
|
+ stats.setShowSum(longToString(data.getLong("showCount")));
|
|
|
+ stats.setClickSum(longToString(data.getLong("openCount")));
|
|
|
+ return stats;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("OPPO 统计查询失败, messageId={}, taskId={}, err={}", messageId, taskId, e.getMessage(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询 vivo 推送统计。
|
|
|
+ */
|
|
|
+ public CommonPushChannelStatsDto queryVivoStatistics(JSONObject credential, String taskId) {
|
|
|
+ if (credential == null || StringUtils.isBlank(taskId)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ String authToken = obtainVivoAuthToken(credential);
|
|
|
+ if (StringUtils.isBlank(authToken)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Map<String, String> headers = new HashMap<>();
|
|
|
+ headers.put("authToken", authToken);
|
|
|
+ String url = "https://api-push.vivo.com.cn/report/getStatistics?taskIds=" + urlEncode(taskId);
|
|
|
+ String resp = getJson(url, headers);
|
|
|
+ log.info("vivo 统计查询响应: taskId={}, resp={}", taskId, resp);
|
|
|
+ JSONObject json = JSONObject.parseObject(resp);
|
|
|
+ if (json == null || json.getIntValue("result") != 0) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ com.alibaba.fastjson.JSONArray statistics = json.getJSONArray("statistics");
|
|
|
+ if (statistics == null || statistics.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ JSONObject item = statistics.getJSONObject(0);
|
|
|
+ CommonPushChannelStatsDto stats = new CommonPushChannelStatsDto();
|
|
|
+ stats.setRealSend(longToString(item.getLong("send")));
|
|
|
+ stats.setRealDelivered(longToString(item.getLong("receive")));
|
|
|
+ stats.setShowSum(longToString(item.getLong("display")));
|
|
|
+ stats.setClickSum(longToString(item.getLong("click")));
|
|
|
+ return stats;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("vivo 统计查询失败, taskId={}, err={}", taskId, e.getMessage(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询小米推送统计(msg_id)。
|
|
|
+ */
|
|
|
+ public CommonPushChannelStatsDto queryXiaomiStatistics(JSONObject credential, String msgId) {
|
|
|
+ if (credential == null || StringUtils.isBlank(msgId)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String appSecret = credential.getString("appSecret");
|
|
|
+ if (StringUtils.isBlank(appSecret)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ Map<String, String> headers = new HashMap<>();
|
|
|
+ headers.put("Authorization", "key=" + appSecret);
|
|
|
+ String url = "https://api.xmpush.xiaomi.com/v1/trace/message/status?msg_id=" + urlEncode(msgId);
|
|
|
+ String resp = getJson(url, headers);
|
|
|
+ log.info("小米统计查询响应: msgId={}, resp={}", msgId, resp);
|
|
|
+ JSONObject json = JSONObject.parseObject(resp);
|
|
|
+ if (json == null || !"ok".equalsIgnoreCase(json.getString("result"))) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ JSONObject data = json.getJSONObject("data");
|
|
|
+ if (data == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ JSONObject status = data.getJSONObject(msgId);
|
|
|
+ if (status == null && !data.isEmpty()) {
|
|
|
+ status = data.getJSONObject(data.keySet().iterator().next());
|
|
|
+ }
|
|
|
+ if (status == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ CommonPushChannelStatsDto stats = new CommonPushChannelStatsDto();
|
|
|
+ stats.setRealSend(longToString(status.getLong("msg_send")));
|
|
|
+ stats.setRealDelivered(longToString(status.getLong("delivered")));
|
|
|
+ stats.setShowSum(longToString(status.getLong("display")));
|
|
|
+ stats.setClickSum(longToString(status.getLong("click")));
|
|
|
+ return stats;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("小米统计查询失败, msgId={}, err={}", msgId, e.getMessage(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String longToString(Long value) {
|
|
|
+ return value == null ? null : String.valueOf(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getJson(String url, Map<String, String> headers) {
|
|
|
+ RestTemplate client = buildRestTemplate();
|
|
|
+ HttpHeaders httpHeaders = new HttpHeaders();
|
|
|
+ if (headers != null) {
|
|
|
+ headers.forEach(httpHeaders::set);
|
|
|
+ }
|
|
|
+ ResponseEntity<String> response = client.exchange(url, org.springframework.http.HttpMethod.GET,
|
|
|
+ new HttpEntity<>(httpHeaders), String.class);
|
|
|
+ return response.getBody();
|
|
|
+ }
|
|
|
+
|
|
|
private String postJson(String url, String body, Map<String, String> headers) {
|
|
|
RestTemplate restTemplate = buildRestTemplate();
|
|
|
HttpHeaders httpHeaders = new HttpHeaders();
|