|
|
@@ -0,0 +1,460 @@
|
|
|
+package shop.alien.store.service.channel;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.codec.binary.Hex;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.springframework.http.HttpEntity;
|
|
|
+import org.springframework.http.HttpHeaders;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.util.LinkedMultiValueMap;
|
|
|
+import org.springframework.util.MultiValueMap;
|
|
|
+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.CommonPushTargetDto;
|
|
|
+
|
|
|
+import java.net.URLEncoder;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 各厂商推送 HTTP 客户端,凭证从配置表 credential_json 读取。
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class CommonPushVendorHttpClient {
|
|
|
+
|
|
|
+ private static final int TIMEOUT_MS = 8000;
|
|
|
+ /** OPPO call_back_parameter 最大 100 字符 */
|
|
|
+ private static final int OPPO_CALLBACK_PARAM_MAX_LEN = 100;
|
|
|
+
|
|
|
+ private final CommonPushProperties commonPushProperties;
|
|
|
+
|
|
|
+ private static final RestTemplate restTemplate = new RestTemplate();
|
|
|
+
|
|
|
+ public boolean send(CommonPushChannelConfig config, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
+ if (config == null || task == null || target == null || StringUtils.isBlank(target.getDeviceId())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String deviceId = target.getDeviceId();
|
|
|
+ JSONObject credential = parseCredential(config.getCredentialJson());
|
|
|
+ if (credential == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String channelCode = StringUtils.lowerCase(StringUtils.trim(config.getChannelCode()));
|
|
|
+ try {
|
|
|
+ switch (channelCode) {
|
|
|
+ case "vivo":
|
|
|
+ return sendVivo(credential, task, target);
|
|
|
+ case "xiaomi":
|
|
|
+ return sendXiaomi(credential, task, target);
|
|
|
+ case "oppo":
|
|
|
+ return sendOppo(credential, task, target);
|
|
|
+ case "samsung":
|
|
|
+ return sendSamsung(credential, task, target);
|
|
|
+ case "huawei":
|
|
|
+ return sendHuawei(credential, task, deviceId);
|
|
|
+ case "honor":
|
|
|
+ return sendHonor(credential, task, deviceId);
|
|
|
+ case "apns":
|
|
|
+ return sendApns(credential, task, deviceId);
|
|
|
+ default:
|
|
|
+ log.warn("不支持的推送渠道: {}", channelCode);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("渠道推送失败, channelCode={}, deviceId={}, err={}", channelCode, deviceId, e.getMessage(), e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean sendVivo(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
+ String deviceId = target.getDeviceId();
|
|
|
+ String appId = credential.getString("appId");
|
|
|
+ String appKey = credential.getString("appKey");
|
|
|
+ String appSecret = credential.getString("appSecret");
|
|
|
+ if (StringUtils.isAnyBlank(appId, appKey, appSecret)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ long timestamp = System.currentTimeMillis();
|
|
|
+ String sign = md5Hex(appId + appKey + timestamp + appSecret);
|
|
|
+ JSONObject authBody = new JSONObject();
|
|
|
+ authBody.put("appId", appId);
|
|
|
+ authBody.put("appKey", appKey);
|
|
|
+ authBody.put("timestamp", timestamp);
|
|
|
+ authBody.put("sign", sign);
|
|
|
+ String authResp = postJson("https://api-push.vivo.com.cn/message/auth", authBody.toJSONString(), null);
|
|
|
+ JSONObject authJson = JSONObject.parseObject(authResp);
|
|
|
+ if (authJson == null || authJson.getIntValue("result") != 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String authToken = authJson.getString("authToken");
|
|
|
+
|
|
|
+ JSONObject sendBody = new JSONObject();
|
|
|
+ sendBody.put("appId", appId);
|
|
|
+ sendBody.put("regId", deviceId);
|
|
|
+ sendBody.put("notifyType", 1);
|
|
|
+ sendBody.put("title", task.getTitle());
|
|
|
+ sendBody.put("content", task.getContent());
|
|
|
+ sendBody.put("skipType", 1);
|
|
|
+ if (StringUtils.isNotBlank(task.getJumpUrl())) {
|
|
|
+ sendBody.put("skipContent", task.getJumpUrl());
|
|
|
+ }
|
|
|
+ Map<String, String> headers = new HashMap<>();
|
|
|
+ headers.put("authToken", authToken);
|
|
|
+ String sendResp = postJson("https://api-push.vivo.com.cn/message/send", sendBody.toJSONString(), headers);
|
|
|
+ JSONObject sendJson = JSONObject.parseObject(sendResp);
|
|
|
+ return sendJson != null && sendJson.getIntValue("result") == 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean sendXiaomi(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
+ String deviceId = target.getDeviceId();
|
|
|
+ String appSecret = credential.getString("appSecret");
|
|
|
+ if (StringUtils.isBlank(appSecret)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
|
|
+ form.add("registration_id", deviceId);
|
|
|
+ form.add("title", task.getTitle());
|
|
|
+ form.add("description", task.getContent());
|
|
|
+ form.add("payload", buildPayload(task));
|
|
|
+ Map<String, String> headers = new HashMap<>();
|
|
|
+ headers.put("Authorization", "key=" + appSecret);
|
|
|
+ String resp = postForm("https://api.xmpush.xiaomi.com/v3/message/regid", form, headers);
|
|
|
+ JSONObject json = JSONObject.parseObject(resp);
|
|
|
+ return json != null && "ok".equalsIgnoreCase(json.getString("result"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * OPPO 厂商直连:使用 RegistrationID(非 UniPush CID),并设置送达回执回调。
|
|
|
+ */
|
|
|
+ private boolean sendOppo(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) throws NoSuchAlgorithmException {
|
|
|
+ String deviceId = target.getDeviceId();
|
|
|
+ String appKey = credential.getString("appKey");
|
|
|
+ String masterSecret = credential.getString("appSecret");
|
|
|
+ if (StringUtils.isAnyBlank(appKey, masterSecret)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ long timestamp = System.currentTimeMillis();
|
|
|
+// String sign = md5Hex(appKey + timestamp + masterSecret);
|
|
|
+ String sign = cn.hutool.crypto.SecureUtil.sha256(appKey + timestamp + masterSecret);
|
|
|
+ // 正确的签名生成
|
|
|
+// String signStr = appKey + timestamp + masterSecret;
|
|
|
+// MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
|
|
+// byte[] hash = digest.digest(signStr.getBytes(StandardCharsets.UTF_8));
|
|
|
+// // 转小写十六进制字符串
|
|
|
+// String sign = Hex.encodeHexString(hash);
|
|
|
+ MultiValueMap<String, String> authForm = new LinkedMultiValueMap<>();
|
|
|
+ authForm.add("app_key", appKey);
|
|
|
+ authForm.add("timestamp", String.valueOf(timestamp));
|
|
|
+ authForm.add("sign", sign);
|
|
|
+ String authResp = postForm("https://api.push.oppomobile.com/server/v1/auth", authForm, null);
|
|
|
+ JSONObject authJson = JSONObject.parseObject(authResp);
|
|
|
+ if (authJson == null || authJson.getJSONObject("data") == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String authToken = authJson.getJSONObject("data").getString("auth_token");
|
|
|
+
|
|
|
+ JSONObject notification = new JSONObject();
|
|
|
+ notification.put("title", task.getTitle());
|
|
|
+ notification.put("content", task.getContent());
|
|
|
+ notification.put("call_back_url", resolveCallbackUrl());
|
|
|
+ notification.put("call_back_parameter", buildCallbackParameter(task, target, "oppo"));
|
|
|
+ if (StringUtils.isNotBlank(task.getJumpUrl())) {
|
|
|
+ notification.put("click_action_url", task.getJumpUrl());
|
|
|
+ }
|
|
|
+// 2. 构造最外层message完整JSON(官方要求顶层结构)
|
|
|
+ JSONObject messageRoot = new JSONObject();
|
|
|
+// messageRoot.put("auth_token", authToken);
|
|
|
+ messageRoot.put("target_type", 2); // 固定2=registration_id单设备推送
|
|
|
+ messageRoot.put("target_value", deviceId);
|
|
|
+ messageRoot.put("notification", notification);
|
|
|
+
|
|
|
+ // 3. 表单只放一个key:message,值为完整JSON字符串
|
|
|
+ MultiValueMap<String, String> sendForm = new LinkedMultiValueMap<>();
|
|
|
+ sendForm.add("auth_token", authToken); // 鉴权令牌放在表单顶层
|
|
|
+ sendForm.add("message", messageRoot.toJSONString());
|
|
|
+
|
|
|
+ HttpHeaders headers = new HttpHeaders();
|
|
|
+// // 显式指定UTF-8编码,避免中文乱码导致JSON解析失败
|
|
|
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
|
+//// headers.setContentType(MediaType.parseMediaType("application/x-www-form-urlencoded;charset=UTF-8"));
|
|
|
+ HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(sendForm, headers);
|
|
|
+ try {
|
|
|
+ ResponseEntity<String> response = restTemplate.postForEntity(
|
|
|
+ "https://api.push.oppomobile.com/server/v1/message/notification/unicast",
|
|
|
+ entity,
|
|
|
+ String.class
|
|
|
+ );
|
|
|
+ String body = response.getBody();
|
|
|
+ log.info("OPPO 推送响应: {}", body);
|
|
|
+ JSONObject resp = JSONObject.parseObject(body);
|
|
|
+ return resp != null && resp.getIntValue("code") == 0;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("OPPO 推送请求异常: {}", e.getMessage(), e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+// String sendResp = postForm("https://api.push.oppomobile.com/server/v1/message/notification/unicast", sendForm, null);
|
|
|
+// JSONObject sendJson = JSONObject.parseObject(sendResp);
|
|
|
+// return sendJson != null && sendJson.getIntValue("code") == 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveCallbackUrl() {
|
|
|
+ return StringUtils.defaultIfBlank(commonPushProperties.getCallbackUrl(),
|
|
|
+ "https://frp-off.com:40279/alienStore/commonPushTaskUser/callback");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * OPPO call_back_parameter 限 100 字符,仅传 pushTaskId + userId 短键,deviceId 由回执 registrationIds 带回。
|
|
|
+ */
|
|
|
+ private String buildCallbackParameter(CommonPushTask task, CommonPushTargetDto target, String channelCode) {
|
|
|
+ JSONObject param = new JSONObject(true);
|
|
|
+ if (task.getId() != null) {
|
|
|
+ param.put("p", task.getId());
|
|
|
+ } else if (StringUtils.isNotBlank(task.getTaskNo())) {
|
|
|
+ param.put("t", task.getTaskNo().trim());
|
|
|
+ }
|
|
|
+ if (target.getUserId() != null) {
|
|
|
+ param.put("u", target.getUserId());
|
|
|
+ }
|
|
|
+ String json = param.toJSONString();
|
|
|
+ if (json.length() > OPPO_CALLBACK_PARAM_MAX_LEN) {
|
|
|
+ log.warn("OPPO call_back_parameter 超长({}), 降级为仅 pushTaskId/taskNo", json.length());
|
|
|
+ JSONObject minimal = new JSONObject(true);
|
|
|
+ if (task.getId() != null) {
|
|
|
+ minimal.put("p", task.getId());
|
|
|
+ } else if (StringUtils.isNotBlank(task.getTaskNo())) {
|
|
|
+ String taskNo = task.getTaskNo().trim();
|
|
|
+ int maxTaskNoLen = OPPO_CALLBACK_PARAM_MAX_LEN - 7;
|
|
|
+ if (taskNo.length() > maxTaskNoLen) {
|
|
|
+ taskNo = taskNo.substring(0, maxTaskNoLen);
|
|
|
+ }
|
|
|
+ minimal.put("t", taskNo);
|
|
|
+ }
|
|
|
+ json = minimal.toJSONString();
|
|
|
+ }
|
|
|
+ if (json.length() > OPPO_CALLBACK_PARAM_MAX_LEN) {
|
|
|
+ log.error("OPPO call_back_parameter 仍超长({}): {}", json.length(), json);
|
|
|
+ }
|
|
|
+ return json;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 三星推送(Samsung Push Platform / SPP),根据 regID 前缀选择区域 RQM 节点。
|
|
|
+ */
|
|
|
+ private boolean sendSamsung(JSONObject credential, CommonPushTask task, CommonPushTargetDto target) {
|
|
|
+ String deviceId = target.getDeviceId();
|
|
|
+ String appId = StringUtils.defaultIfBlank(credential.getString("appId"), credential.getString("appID"));
|
|
|
+ String appSecret = StringUtils.defaultIfBlank(credential.getString("appSecret"),
|
|
|
+ credential.getString("app_secret"));
|
|
|
+ if (StringUtils.isAnyBlank(appId, appSecret)) {
|
|
|
+ log.warn("三星推送凭证不完整, deviceId={}", deviceId);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ JSONObject body = new JSONObject();
|
|
|
+ body.put("regID", deviceId);
|
|
|
+ body.put("requestID", StringUtils.defaultIfBlank(task.getTaskNo(), "REQ" + System.currentTimeMillis()));
|
|
|
+ body.put("message", buildSamsungMessage(task));
|
|
|
+
|
|
|
+ Map<String, String> headers = new HashMap<>();
|
|
|
+ headers.put("appID", appId);
|
|
|
+ headers.put("appSecret", appSecret);
|
|
|
+
|
|
|
+ String url = resolveSamsungPushUrl(deviceId);
|
|
|
+ String resp = postJson(url, body.toJSONString(), headers);
|
|
|
+ log.info("三星推送响应: url={}, body={}", url, resp);
|
|
|
+ JSONObject json = JSONObject.parseObject(resp);
|
|
|
+ return json != null && json.getIntValue("statusCode") == 1000;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildSamsungMessage(CommonPushTask task) {
|
|
|
+ StringBuilder sb = new StringBuilder("action=ALERT");
|
|
|
+ if (StringUtils.isNotBlank(task.getTitle())) {
|
|
|
+ sb.append("&alertTitle=").append(urlEncode(task.getTitle()));
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(task.getContent())) {
|
|
|
+ sb.append("&alertMessage=").append(urlEncode(task.getContent()));
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(task.getJumpUrl())) {
|
|
|
+ sb.append("&uri=").append(urlEncode(task.getJumpUrl()));
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveSamsungPushUrl(String regId) {
|
|
|
+ if (StringUtils.isBlank(regId) || regId.length() < 2) {
|
|
|
+ return "https://apchina.push.samsungosp.com.cn:8090/spp/pns/api/push";
|
|
|
+ }
|
|
|
+ switch (regId.substring(0, 2)) {
|
|
|
+ case "00":
|
|
|
+ return "https://useast.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "02":
|
|
|
+ return "https://apsoutheast.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "03":
|
|
|
+ return "https://euwest.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "04":
|
|
|
+ return "https://apnortheast.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "05":
|
|
|
+ return "https://apkorea.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "06":
|
|
|
+ return "https://apchina.push.samsungosp.com.cn:8090/spp/pns/api/push";
|
|
|
+ case "50":
|
|
|
+ return "https://useast.gateway.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "52":
|
|
|
+ return "https://apsoutheast.gateway.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "53":
|
|
|
+ return "https://euwest.gateway.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "54":
|
|
|
+ return "https://apnortheast.gateway.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "55":
|
|
|
+ return "https://apkorea.gateway.push.samsungosp.com:8090/spp/pns/api/push";
|
|
|
+ case "56":
|
|
|
+ return "https://apchina.gateway.push.samsungosp.com.cn:8090/spp/pns/api/push";
|
|
|
+ default:
|
|
|
+ return "https://apchina.push.samsungosp.com.cn:8090/spp/pns/api/push";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String urlEncode(String value) {
|
|
|
+ try {
|
|
|
+ return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
|
|
|
+ } catch (Exception e) {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean sendHuawei(JSONObject credential, CommonPushTask task, String deviceId) {
|
|
|
+ return sendHuaweiLike(credential, task, deviceId, "https://push-api.cloud.huawei.com");
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean sendHonor(JSONObject credential, CommonPushTask task, String deviceId) {
|
|
|
+ return sendHuaweiLike(credential, task, deviceId, "https://push-api.cloud.huawei.com");
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean sendHuaweiLike(JSONObject credential, CommonPushTask task, String deviceId, String baseUrl) {
|
|
|
+ String appId = credential.getString("appId");
|
|
|
+ String appSecret = credential.getString("appSecret");
|
|
|
+ if (StringUtils.isAnyBlank(appId, appSecret)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ MultiValueMap<String, String> tokenForm = new LinkedMultiValueMap<>();
|
|
|
+ tokenForm.add("grant_type", "client_credentials");
|
|
|
+ tokenForm.add("client_id", appId);
|
|
|
+ tokenForm.add("client_secret", appSecret);
|
|
|
+ String tokenResp = postForm(baseUrl + "/oauth2/v2/token", tokenForm, null);
|
|
|
+ JSONObject tokenJson = JSONObject.parseObject(tokenResp);
|
|
|
+ if (tokenJson == null || StringUtils.isBlank(tokenJson.getString("access_token"))) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String accessToken = tokenJson.getString("access_token");
|
|
|
+
|
|
|
+ JSONObject body = new JSONObject();
|
|
|
+ JSONObject message = new JSONObject();
|
|
|
+ JSONObject android = new JSONObject();
|
|
|
+ JSONObject notification = new JSONObject();
|
|
|
+ notification.put("title", task.getTitle());
|
|
|
+ notification.put("body", task.getContent());
|
|
|
+ android.put("notification", notification);
|
|
|
+ message.put("android", android);
|
|
|
+ body.put("message", message);
|
|
|
+ body.put("token", new String[]{deviceId});
|
|
|
+
|
|
|
+ Map<String, String> headers = new HashMap<>();
|
|
|
+ headers.put("Authorization", "Bearer " + accessToken);
|
|
|
+ String sendResp = postJson(baseUrl + "/v1/" + appId + "/messages:send", body.toJSONString(), headers);
|
|
|
+ JSONObject sendJson = JSONObject.parseObject(sendResp);
|
|
|
+ return sendJson != null && StringUtils.isNotBlank(sendJson.getString("requestId"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * APNs 需 HTTP/2 + ES256 JWT,当前环境未接入专用客户端,仅校验凭证字段完整性。
|
|
|
+ */
|
|
|
+ private boolean sendApns(JSONObject credential, CommonPushTask task, String deviceId) {
|
|
|
+ String bundleId = credential.getString("bundleId");
|
|
|
+ String teamId = credential.getString("teamId");
|
|
|
+ String keyId = credential.getString("keyId");
|
|
|
+ String privateKey = credential.getString("privateKey");
|
|
|
+ if (StringUtils.isAnyBlank(bundleId, teamId, keyId, privateKey)) {
|
|
|
+ log.warn("APNs 凭证不完整, deviceId={}", deviceId);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ log.warn("APNs 推送需 HTTP/2 客户端支持,当前跳过实际下发, deviceId={}, title={}", deviceId, task.getTitle());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private JSONObject parseCredential(String credentialJson) {
|
|
|
+ if (StringUtils.isBlank(credentialJson)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return JSONObject.parseObject(credentialJson);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("credential_json 解析失败: {}", e.getMessage());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildPayload(CommonPushTask task) {
|
|
|
+ JSONObject payload = new JSONObject();
|
|
|
+ payload.put("title", task.getTitle());
|
|
|
+ payload.put("content", task.getContent());
|
|
|
+ if (StringUtils.isNotBlank(task.getJumpUrl())) {
|
|
|
+ payload.put("jumpUrl", task.getJumpUrl());
|
|
|
+ }
|
|
|
+ return payload.toJSONString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String postJson(String url, String body, Map<String, String> headers) {
|
|
|
+ RestTemplate restTemplate = buildRestTemplate();
|
|
|
+ HttpHeaders httpHeaders = new HttpHeaders();
|
|
|
+ httpHeaders.setContentType(MediaType.APPLICATION_JSON);
|
|
|
+ if (headers != null) {
|
|
|
+ headers.forEach(httpHeaders::set);
|
|
|
+ }
|
|
|
+ ResponseEntity<String> response = restTemplate.postForEntity(url, new HttpEntity<>(body, httpHeaders), String.class);
|
|
|
+ return response.getBody();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String postForm(String url, MultiValueMap<String, String> form, Map<String, String> headers) {
|
|
|
+ RestTemplate restTemplate = buildRestTemplate();
|
|
|
+ HttpHeaders httpHeaders = new HttpHeaders();
|
|
|
+ httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
|
+ if (headers != null) {
|
|
|
+ headers.forEach(httpHeaders::set);
|
|
|
+ }
|
|
|
+ ResponseEntity<String> response = restTemplate.postForEntity(url, new HttpEntity<>(form, httpHeaders), String.class);
|
|
|
+ return response.getBody();
|
|
|
+ }
|
|
|
+
|
|
|
+ private RestTemplate buildRestTemplate() {
|
|
|
+ SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
|
|
+ factory.setConnectTimeout(TIMEOUT_MS);
|
|
|
+ factory.setReadTimeout(TIMEOUT_MS);
|
|
|
+ return new RestTemplate(factory);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String md5Hex(String input) {
|
|
|
+ try {
|
|
|
+ MessageDigest md = MessageDigest.getInstance("MD5");
|
|
|
+ byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (byte b : digest) {
|
|
|
+ sb.append(String.format("%02x", b));
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new IllegalStateException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|