|
|
@@ -0,0 +1,220 @@
|
|
|
+package com.fs.jd.http;
|
|
|
+
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
+import cn.hutool.core.map.MapUtil;
|
|
|
+import cn.hutool.http.HttpRequest;
|
|
|
+import cn.hutool.http.HttpResponse;
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.alibaba.fastjson.PropertyNamingStrategy;
|
|
|
+import com.alibaba.fastjson.TypeReference;
|
|
|
+import com.alibaba.fastjson.serializer.SerializeConfig;
|
|
|
+import com.fs.common.utils.DateUtils;
|
|
|
+import com.fs.erp.dto.CommonResponse;
|
|
|
+import com.fs.erp.dto.ErpOrderResponseDTO;
|
|
|
+import com.fs.erp.utils.SignUtil;
|
|
|
+import com.fs.his.config.FsSysConfig;
|
|
|
+import com.fs.his.utils.ConfigUtil;
|
|
|
+import com.fs.jd.dto.*;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.crypto.Mac;
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
+import java.net.URLEncoder;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.GeneralSecurityException;
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+import static com.fs.common.core.text.CharsetKit.UTF_8;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class JdHttpServiceImpl implements IJdHttpService{
|
|
|
+
|
|
|
+ //正式
|
|
|
+ private static final String BASE_URL = "https://api.jdl.com";
|
|
|
+ private static final String HEX_CHARACTERS = "0123456789ABCDEF";
|
|
|
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+
|
|
|
+ private static final String DELIVERY_CREATE_METHOD = "/integratedsupplychain/order/delivery/create/v1";
|
|
|
+ private static final String ORDER_CANCEL_METHOD = "/integratedsupplychain/order/cancel/v1";
|
|
|
+ private static final String QUERY_DELIVERY_METHOD = "/integratedsupplychain/order/delivery/query/v1";
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ConfigUtil configUtil;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public SoCreateResponseDTO deliveryCreate(SoCreateOrderRequestDTO dto) {
|
|
|
+ String method = DELIVERY_CREATE_METHOD;
|
|
|
+ String url = BASE_URL + method;
|
|
|
+ log.info("京东-销售出库单创建 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
+ HttpResponse response;
|
|
|
+ try {
|
|
|
+ response = executeJsonPost(url, Arrays.asList(dto), method);
|
|
|
+ } catch (GeneralSecurityException | UnsupportedEncodingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ return parseResponse(response, new TypeReference<CommonResponse<SoCreateResponseDTO>>() {});
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public OrderCancelResponseDTO orderCancel(OrderCancelRequestDTO dto) {
|
|
|
+ String method = ORDER_CANCEL_METHOD;
|
|
|
+ String url = BASE_URL + method;
|
|
|
+ log.info("京东-销售出库单取消 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
+ HttpResponse response;
|
|
|
+ try {
|
|
|
+ response = executeJsonPost(url, Arrays.asList(dto), method);
|
|
|
+ } catch (GeneralSecurityException | UnsupportedEncodingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ return parseResponse(response, new TypeReference<CommonResponse<OrderCancelResponseDTO>>() {});
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public SoQueryResponseDTO queryDelivery(SoQueryRequestDTO dto) {
|
|
|
+ String method = QUERY_DELIVERY_METHOD;
|
|
|
+ String url = BASE_URL + method;
|
|
|
+ log.info("京东-销售出库单查询 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
+ HttpResponse response;
|
|
|
+ try {
|
|
|
+ response = executeJsonPost(url, Arrays.asList(dto), method);
|
|
|
+ } catch (GeneralSecurityException | UnsupportedEncodingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ return parseResponse(response, new TypeReference<CommonResponse<SoQueryResponseDTO>>() {});
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行JSON格式的POST请求
|
|
|
+ *
|
|
|
+ * @param url 请求URL
|
|
|
+ * @param dto 请求对象
|
|
|
+ * @return HttpResponse响应
|
|
|
+ */
|
|
|
+ private HttpResponse executeJsonPost(String url, Object dto,String method) throws GeneralSecurityException, UnsupportedEncodingException {
|
|
|
+ String appKey = "38b123c8d2084beabe21be14549a9749";
|
|
|
+ String appSecret = "b088b97cc2924bfc9ec5d09310d17f73";
|
|
|
+ String accessToken = "144d2076bf7c4bcdaf1a2c0452eeb021";
|
|
|
+// FsSysConfig sysConfig = configUtil.getSysConfig();
|
|
|
+//
|
|
|
+// String appKey = sysConfig.getJdAppKey();
|
|
|
+// String appSecret = sysConfig.getJdAppSecret();
|
|
|
+// String accessToken = sysConfig.getJdAccessToken();
|
|
|
+
|
|
|
+ String algorithm = "md5-salt";
|
|
|
+ String domain = "IntegratedSupplyChain";
|
|
|
+ String timestamp = DATE_TIME_FORMATTER.format(LocalDateTime.now());
|
|
|
+ String jsonBody = JSON.toJSONString(dto);
|
|
|
+
|
|
|
+ log.info("timestamp: {}, jsonBody: {}", timestamp, jsonBody);
|
|
|
+
|
|
|
+ //生成签名字符拼接
|
|
|
+ String content = String.join("", new String[]{
|
|
|
+ appSecret,
|
|
|
+ "access_token", accessToken,
|
|
|
+ "app_key", appKey,
|
|
|
+ "method", method,
|
|
|
+ "param_json", jsonBody,
|
|
|
+ "timestamp", timestamp,
|
|
|
+ "v", "2.0",
|
|
|
+ appSecret});
|
|
|
+ String sign = sign(algorithm, content.getBytes(StandardCharsets.UTF_8), appSecret.getBytes(StandardCharsets.UTF_8)).toLowerCase();
|
|
|
+ Map<String,String> query = new HashMap<>();
|
|
|
+ query.put("LOP-DN", domain);
|
|
|
+ query.put("app_key", appKey);
|
|
|
+ query.put("access_token", accessToken);
|
|
|
+ query.put("timestamp", timestamp);
|
|
|
+ query.put("v", "2.0");
|
|
|
+ query.put("sign", URLEncoder.encode(sign, StandardCharsets.UTF_8.toString()));
|
|
|
+ String uri = url + "?" + httpBuildQuery(query);
|
|
|
+ Map<String, String> headers = MapUtil.builder(new HashMap<String, String>())
|
|
|
+ .put("Content-Type", "application/json;charset=utf-8")
|
|
|
+ .build();
|
|
|
+
|
|
|
+
|
|
|
+ log.debug("发送JSON请求 - URL: {}, Headers: {}, Body: {}", uri, headers, jsonBody);
|
|
|
+
|
|
|
+ return HttpRequest.post(uri)
|
|
|
+ .headerMap(headers, true)
|
|
|
+ .body(jsonBody)
|
|
|
+ .execute();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析带有泛型的HTTP响应
|
|
|
+ *
|
|
|
+ * @param response HTTP响应
|
|
|
+ * @param typeReference 响应类型引用
|
|
|
+ * @param <T> 期望的响应数据类型
|
|
|
+ * @return 解析后的数据
|
|
|
+ */
|
|
|
+ private <T> T parseResponse(HttpResponse response, TypeReference<CommonResponse<T>> typeReference) {
|
|
|
+ String body = response.body();
|
|
|
+ log.debug("解析响应体: {}", body);
|
|
|
+
|
|
|
+
|
|
|
+ CommonResponse<T> commonResponse = JSON.parseObject(body, typeReference);
|
|
|
+
|
|
|
+ if (!Integer.valueOf(1000).equals(commonResponse.getCode())) {
|
|
|
+ log.error("API请求失败 - 错误码: {}, 错误信息: {}", commonResponse.getCode(), commonResponse.getMessage());
|
|
|
+ throw new RuntimeException("请求接口失败!原因:" + commonResponse.getMessage());
|
|
|
+ }
|
|
|
+ log.info("response: {}", commonResponse);
|
|
|
+ T result = commonResponse.getData();
|
|
|
+ log.info("API请求成功 - 结果: {}", JSON.toJSONString(result));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String httpBuildQuery(Map<String, String> query) throws UnsupportedEncodingException {
|
|
|
+ StringBuilder stringBuilder = new StringBuilder();
|
|
|
+ boolean first = true;
|
|
|
+ for (Map.Entry<String, String> entry : query.entrySet()) {
|
|
|
+ if (!first) {
|
|
|
+ stringBuilder.append("&");
|
|
|
+ } else {
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+ stringBuilder.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), UTF_8));
|
|
|
+ }
|
|
|
+ return stringBuilder.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String sign(String algorithm, byte[] data, byte[] secret) throws GeneralSecurityException {
|
|
|
+ if (Objects.equals(algorithm, "md5-salt")) {
|
|
|
+ return bytesToHex(MessageDigest.getInstance("md5").digest(data));
|
|
|
+ } else if (Objects.equals(algorithm, "HMacMD5")) {
|
|
|
+ Mac mac = Mac.getInstance(algorithm);
|
|
|
+ mac.init(new SecretKeySpec(secret, algorithm));
|
|
|
+ return Base64.getEncoder().encodeToString(mac.doFinal(data));
|
|
|
+ } else if (Objects.equals(algorithm, "HMacSHA1")) {
|
|
|
+ Mac mac = Mac.getInstance(algorithm);
|
|
|
+ mac.init(new SecretKeySpec(secret, algorithm));
|
|
|
+ return Base64.getEncoder().encodeToString(mac.doFinal(data));
|
|
|
+ } else if (Objects.equals(algorithm, "HMacSHA256")) {
|
|
|
+ Mac mac = Mac.getInstance(algorithm);
|
|
|
+ mac.init(new SecretKeySpec(secret, algorithm));
|
|
|
+ return Base64.getEncoder().encodeToString(mac.doFinal(data));
|
|
|
+ } else if (Objects.equals(algorithm, "HMacSHA512")) {
|
|
|
+ Mac mac = Mac.getInstance(algorithm);
|
|
|
+ mac.init(new SecretKeySpec(secret, algorithm));
|
|
|
+ return Base64.getEncoder().encodeToString(mac.doFinal(data));
|
|
|
+ }
|
|
|
+ throw new GeneralSecurityException("Algorithm " + algorithm + " not supported yet");
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String bytesToHex(byte[] bytes) {
|
|
|
+ StringBuilder stringBuilder = new StringBuilder(bytes.length * 2);
|
|
|
+ for (byte b : bytes) {
|
|
|
+ stringBuilder.append(HEX_CHARACTERS.charAt((b >>> 4) & 0x0F));
|
|
|
+ stringBuilder.append(HEX_CHARACTERS.charAt(b & 0x0F));
|
|
|
+ }
|
|
|
+ return stringBuilder.toString();
|
|
|
+ }
|
|
|
+}
|