|
@@ -0,0 +1,296 @@
|
|
|
|
+package com.fs.erp.http;
|
|
|
|
+
|
|
|
|
+import cn.hutool.core.bean.BeanUtil;
|
|
|
|
+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.PropertyNamingStrategy;
|
|
|
|
+import com.alibaba.fastjson.TypeReference;
|
|
|
|
+import com.alibaba.fastjson.serializer.SerializeConfig;
|
|
|
|
+import com.fs.erp.dto.*;
|
|
|
|
+import com.fs.erp.service.impl.JstTokenService;
|
|
|
|
+import com.fs.erp.utils.SignUtil;
|
|
|
|
+import com.fs.ybPay.dto.RefundOrderDTO;
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import org.apache.commons.lang.ObjectUtils;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
+
|
|
|
|
+import java.util.Arrays;
|
|
|
|
+import java.util.Collections;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.Map;
|
|
|
|
+
|
|
|
|
+@Slf4j
|
|
|
|
+@Service
|
|
|
|
+public class JstErpHttpServiceImpl implements JstErpHttpService {
|
|
|
|
+
|
|
|
|
+ private static final String BASE_URL = "https://openapi.jushuitan.com/";
|
|
|
|
+ /**
|
|
|
|
+ * 获取access_token、refresh_token url
|
|
|
|
+ */
|
|
|
|
+ private static final String GET_INIT_TOKEN_URL = BASE_URL + "openWeb/auth/getInitToken";
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 更新access_token、refresh_token url
|
|
|
|
+ */
|
|
|
|
+ private static final String REFRESH_TOKEN_URL = BASE_URL + "openWeb/auth/refreshToken";
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ @Value("${jst.app_key:''}")
|
|
|
|
+ private String appKey;
|
|
|
|
+
|
|
|
|
+ @Value("${jst.app_secret:''}")
|
|
|
|
+ private String appSecret;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private JstTokenService jstTokenService;
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public GetInitTokenResponseDTO getInitToken(GetInitTokenRequestDTO dto) {
|
|
|
|
+ // 将DTO转换为表单参数
|
|
|
|
+ Map<String, Object> formParams = BeanUtil.beanToMap(dto);
|
|
|
|
+ log.info("请求初始化token - URL: {}, 参数: {}", GET_INIT_TOKEN_URL, JSON.toJSONString(formParams));
|
|
|
|
+
|
|
|
|
+ HttpResponse response = executeFormPost(GET_INIT_TOKEN_URL, formParams);
|
|
|
|
+ return parseResponse(response, new TypeReference<CommonResponse<GetInitTokenResponseDTO>>() {});
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public GetInitTokenResponseDTO refreshToken(RefreshTokenRequestDTO dto) {
|
|
|
|
+ log.info("刷新token - URL: {}, 请求体: {}", REFRESH_TOKEN_URL, JSON.toJSONString(dto));
|
|
|
|
+ Map<String, Object> formParams = BeanUtil.beanToMap(dto);
|
|
|
|
+
|
|
|
|
+ HttpResponse response = executeFormPost(REFRESH_TOKEN_URL, formParams);
|
|
|
|
+ return parseResponse(response, new TypeReference<CommonResponse<GetInitTokenResponseDTO>>() {});
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public ProductUploadResultDTO uploadGoods(GoodsInfoDTO dto) {
|
|
|
|
+ String url = BASE_URL + "/open/jushuitan/itemsku/upload";
|
|
|
|
+ log.info("上传商品信息 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
|
+
|
|
|
|
+ HttpResponse response = executeJsonPost(url, dto);
|
|
|
|
+ return parseResponse(response, new TypeReference<CommonResponse<ProductUploadResultDTO>>() {});
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public ProductResponseDTO queryGoods(ProductQueryRequestDTO dto) {
|
|
|
|
+ String url = BASE_URL + "/open/sku/query";
|
|
|
|
+ log.info("查询商品信息 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
|
+
|
|
|
|
+ HttpResponse response = executeJsonPost(url, dto);
|
|
|
|
+ return parseResponse(response, new TypeReference<CommonResponse<ProductResponseDTO>>() {});
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public ErpOrderResponseDTO upload(ShopOrderDTO dto) {
|
|
|
|
+ String url = BASE_URL + "open/jushuitan/orders/upload";
|
|
|
|
+ log.info("上传订单信息 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
|
+
|
|
|
|
+ HttpResponse response = executeJsonPost(url, Arrays.asList(dto));
|
|
|
|
+ return parseResponse(response, new TypeReference<CommonResponse<ErpOrderResponseDTO>>() {});
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public OrderQueryResponseDTO query(OrderQueryRequestDTO dto) {
|
|
|
|
+ String url = BASE_URL + "/open/orders/single/query";
|
|
|
|
+ log.info("查询订单信息 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
|
+
|
|
|
|
+ HttpResponse response = executeJsonPost(url, dto);
|
|
|
|
+ return parseResponse(response, new TypeReference<CommonResponse<OrderQueryResponseDTO>>() {});
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public CommonResponse cancel(OrderCancelRequestDTO dto) {
|
|
|
|
+ String url = BASE_URL + "/open/jushuitan/orderbyoid/cancel";
|
|
|
|
+ log.info("取消订单 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
|
+
|
|
|
|
+ HttpResponse response = executeJsonPost(url, dto);
|
|
|
|
+ // 这里直接返回CommonResponse类型
|
|
|
|
+ return parseCommonResponse(response);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public CommonResponse<AfterSaleResponseDTO> aftersaleUpload(RefundOrderDTO dto) {
|
|
|
|
+ String url = BASE_URL + "/open/aftersale/upload";
|
|
|
|
+ log.info("售后上传 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
|
+
|
|
|
|
+ HttpResponse response = executeJsonPost(url, Collections.singletonList(dto));
|
|
|
|
+ // 这里直接返回CommonResponse类型
|
|
|
|
+ return parseCommonResponse(response);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public CommonResponse<AssetProcessResultDTO> aftersaleConfirm(AfterSaleConfirmRequestDTO dto) {
|
|
|
|
+ String url = BASE_URL + "/open/webapi/aftersaleapi/open/confirm";
|
|
|
|
+ log.info("售后单确认 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
|
|
|
|
+
|
|
|
|
+ HttpResponse response = executeJsonPost(url, dto);
|
|
|
|
+ // 这里直接返回CommonResponse类型
|
|
|
|
+ return parseCommonResponse(response);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 执行JSON格式的POST请求
|
|
|
|
+ *
|
|
|
|
+ * @param url 请求URL
|
|
|
|
+ * @param dto 请求对象
|
|
|
|
+ * @param needToken 是否需要添加access_token(认证相关请求不需要)
|
|
|
|
+ * @return HttpResponse响应
|
|
|
|
+ */
|
|
|
|
+ private HttpResponse executeJsonPost(String url, Object dto, boolean needToken) {
|
|
|
|
+ SerializeConfig serializeConfig = new SerializeConfig();
|
|
|
|
+ serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
|
|
|
|
+ String jsonBody = JSON.toJSONString(dto, serializeConfig);
|
|
|
|
+ Map<String, Object> map = new HashMap<>();
|
|
|
|
+
|
|
|
|
+ // 只有在需要token的请求中才添加access_token
|
|
|
|
+ if (needToken) {
|
|
|
|
+ String accessToken = jstTokenService.getAccessToken();
|
|
|
|
+ map.put("access_token", accessToken);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ map.put("app_key", appKey);
|
|
|
|
+ map.put("timestamp", System.currentTimeMillis() / 1000);
|
|
|
|
+ map.put("version", "2");
|
|
|
|
+ map.put("charset", "utf-8");
|
|
|
|
+ map.put("biz", jsonBody);
|
|
|
|
+ map.put("sign", SignUtil.getSignNew(appSecret, map));
|
|
|
|
+ Map<String, String> headers = MapUtil.builder(new HashMap<String, String>())
|
|
|
|
+ .put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
|
|
|
|
+ .build();
|
|
|
|
+ log.debug("发送JSON请求 - URL: {}, Headers: {}, Body: {}", url, headers, JSON.toJSONString(map));
|
|
|
|
+ HttpResponse response = HttpRequest.post(url)
|
|
|
|
+ .headerMap(headers, true)
|
|
|
|
+ .form(map)
|
|
|
|
+ .execute();
|
|
|
|
+ logResponse(response);
|
|
|
|
+ return response;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 执行JSON格式的POST请求
|
|
|
|
+ *
|
|
|
|
+ * @param url 请求URL
|
|
|
|
+ * @param dto 请求对象
|
|
|
|
+ * @return HttpResponse响应
|
|
|
|
+ */
|
|
|
|
+ private HttpResponse executeJsonPost(String url, Object dto) {
|
|
|
|
+ SerializeConfig serializeConfig = new SerializeConfig();
|
|
|
|
+ serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
|
|
|
|
+
|
|
|
|
+ String jsonBody = JSON.toJSONString(dto, serializeConfig);
|
|
|
|
+
|
|
|
|
+ Map<String,Object> map = new HashMap<>();
|
|
|
|
+
|
|
|
|
+ String accessToken = jstTokenService.getAccessToken();
|
|
|
|
+
|
|
|
|
+ map.put("access_token",accessToken);
|
|
|
|
+ map.put("app_key",appKey);
|
|
|
|
+ map.put("timestamp",System.currentTimeMillis()/1000);
|
|
|
|
+ map.put("version","2");
|
|
|
|
+ map.put("charset","utf-8");
|
|
|
|
+ map.put("biz", jsonBody);
|
|
|
|
+
|
|
|
|
+ map.put("sign",SignUtil.getSignNew(appSecret, map));
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ Map<String, String> headers = MapUtil.builder(new HashMap<String, String>())
|
|
|
|
+ .put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
|
|
|
|
+ .build();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ log.debug("发送JSON请求 - URL: {}, Headers: {}, Body: {}", url, headers, JSON.toJSONString(map));
|
|
|
|
+
|
|
|
|
+ HttpResponse response = HttpRequest.post(url)
|
|
|
|
+ .headerMap(headers, true)
|
|
|
|
+ .form(map)
|
|
|
|
+ .execute();
|
|
|
|
+
|
|
|
|
+ logResponse(response);
|
|
|
|
+ return response;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 执行表单格式的POST请求
|
|
|
|
+ *
|
|
|
|
+ * @param url 请求URL
|
|
|
|
+ * @param formParams 表单参数
|
|
|
|
+ * @return HttpResponse响应
|
|
|
|
+ */
|
|
|
|
+ private HttpResponse executeFormPost(String url, Map<String, Object> formParams) {
|
|
|
|
+ Map<String, String> headers = MapUtil.builder(new HashMap<String, String>())
|
|
|
|
+ .put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
|
|
|
|
+ .build();
|
|
|
|
+
|
|
|
|
+ log.debug("发送表单请求 - URL: {}, Headers: {}, Form: {}", url, headers, formParams);
|
|
|
|
+
|
|
|
|
+ HttpResponse response = HttpRequest.post(url)
|
|
|
|
+ .headerMap(headers, true)
|
|
|
|
+ .form(formParams)
|
|
|
|
+ .execute();
|
|
|
|
+
|
|
|
|
+ logResponse(response);
|
|
|
|
+ return response;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 记录HTTP响应日志
|
|
|
|
+ *
|
|
|
|
+ * @param response HTTP响应
|
|
|
|
+ */
|
|
|
|
+ private void logResponse(HttpResponse response) {
|
|
|
|
+ log.debug("收到响应 - Status: {}, Headers: {}", response.getStatus(), response.headers());
|
|
|
|
+ log.debug("响应体: {}", response.body());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 解析带有泛型的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 (ObjectUtils.notEqual(commonResponse.getCode(), 0)) {
|
|
|
|
+ if (!Integer.valueOf(0).equals(commonResponse.getCode())) {
|
|
|
|
+ log.error("API请求失败 - 错误码: {}, 错误信息: {}", commonResponse.getCode(), commonResponse.getMsg());
|
|
|
|
+ throw new RuntimeException("请求接口失败!原因:" + commonResponse.getMsg());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ T result = commonResponse.getData();
|
|
|
|
+ log.info("API请求成功 - 结果: {}", JSON.toJSONString(result));
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 解析普通CommonResponse响应(无泛型)
|
|
|
|
+ *
|
|
|
|
+ * @param response HTTP响应
|
|
|
|
+ * @return CommonResponse对象
|
|
|
|
+ */
|
|
|
|
+ private CommonResponse parseCommonResponse(HttpResponse response) {
|
|
|
|
+ String body = response.body();
|
|
|
|
+ log.debug("解析响应体: {}", body);
|
|
|
|
+
|
|
|
|
+ CommonResponse commonResponse = JSON.parseObject(body, CommonResponse.class);
|
|
|
|
+
|
|
|
|
+ if (ObjectUtils.notEqual(commonResponse.getCode(), 0)) {
|
|
|
|
+ log.error("API请求失败 - 错误码: {}, 错误信息: {}", commonResponse.getCode(), commonResponse.getMsg());
|
|
|
|
+ throw new RuntimeException("请求接口失败!原因:" + commonResponse.getMsg());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ log.info("API请求成功 - 结果: {}", JSON.toJSONString(commonResponse));
|
|
|
|
+ return commonResponse;
|
|
|
|
+ }
|
|
|
|
+}
|