浏览代码

提交封装对接兔灵erp订单同步的接口代码

cgp 2 周之前
父节点
当前提交
0616731037

+ 25 - 0
fs-service/src/main/java/com/fs/erp/dto/tl/TlCreateOrderRequest.java

@@ -0,0 +1,25 @@
+package com.fs.erp.dto.tl;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 聚水潭订单同步请求主对象
+ */
+@Data
+public class TlCreateOrderRequest {
+
+    /** 企业ID(corp_id) */
+    @JsonProperty("corp_id")
+    private String corpId;
+
+    /** 订单数据列表(仅支持1条) */
+    @JsonProperty("data")
+    private List<TlOrderItem> data;
+
+    /** 请求选项 */
+    @JsonProperty("options")
+    private TlOptions options;
+}

+ 17 - 0
fs-service/src/main/java/com/fs/erp/dto/tl/TlCreateOrderResponse.java

@@ -0,0 +1,17 @@
+package com.fs.erp.dto.tl;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class TlCreateOrderResponse {
+    private ResponseData data;
+
+    @Data
+    public static class ResponseData {
+        private String status;          // "ok" or "error"
+        private String errmsg;         // 错误信息
+        private List<Integer> accept_index; // 接受的索引(通常 [0] 或 [1])
+    }
+}

+ 14 - 0
fs-service/src/main/java/com/fs/erp/dto/tl/TlIdCardInfo.java

@@ -0,0 +1,14 @@
+package com.fs.erp.dto.tl;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class TlIdCardInfo {
+    // 姓名
+    private String name;
+
+    // 身份证号
+    @JsonProperty("card_id")
+    private String cardId;
+}

+ 15 - 0
fs-service/src/main/java/com/fs/erp/dto/tl/TlOptions.java

@@ -0,0 +1,15 @@
+package com.fs.erp.dto.tl;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 请求选项参数
+ */
+@Data
+public class TlOptions {
+
+    /** 聚水潭服务器ID(固定传 1) */
+    @JsonProperty("jst_server_id")
+    private Integer jstServerId;
+}

+ 93 - 0
fs-service/src/main/java/com/fs/erp/dto/tl/TlOrderItem.java

@@ -0,0 +1,93 @@
+package com.fs.erp.dto.tl;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 聚水潭订单主体信息
+ */
+@Data
+public class TlOrderItem {
+
+    /** 店铺ID */
+    @JsonProperty("shop_id")
+    private Long shopId;
+
+    /** 商家订单号(so_id) */
+    @JsonProperty("so_id")
+    private String soId;
+
+    /** 下单时间,格式:yyyy-MM-dd HH:mm:ss */
+    @JsonProperty("order_date")
+    private String orderDate;
+
+    /** 订单状态(字符串):如 "paid"(已付款)、"unpaid"(未付款)等 */
+    @JsonProperty("shop_status")
+    private String shopStatus;
+
+    /** 买家在店铺的唯一标识 */
+    @JsonProperty("shop_buyer_id")
+    private String shopBuyerId;
+
+    /** 收货地址 */
+    @JsonProperty("receiver_address")
+    private String receiverAddress;
+
+    /** 收货人姓名 */
+    @JsonProperty("receiver_name")
+    private String receiverName;
+
+    /** 收货人手机号 */
+    @JsonProperty("receiver_mobile")
+    private String receiverMobile;
+
+    /** 应付金额(单位:分) */
+    @JsonProperty("pay_amount")
+    private Integer payAmount;
+
+    /** 运费(单位:分) */
+    @JsonProperty("freight")
+    private Integer freight;
+
+    /** 卖家备注 */
+    @JsonProperty("remark")
+    private String remark;
+
+    /** 买家留言 */
+    @JsonProperty("buyer_message")
+    private String buyerMessage;
+
+    /** 是否货到付款(COD) */
+    @JsonProperty("is_cod")
+    private Boolean isCod;
+
+    /** 订单最后修改时间,格式:yyyy-MM-dd HH:mm:ss */
+    @JsonProperty("shop_modified")
+    private String shopModified;
+
+    /** 订单标签,多个用英文逗号分隔,如 "测试,同步" */
+    @JsonProperty("labels")
+    private String labels;
+
+    /** 订单创建人姓名 */
+    @JsonProperty("creator_name")
+    private String creatorName;
+
+    /** 发货时间,格式:yyyy-MM-dd HH:mm:ss */
+    @JsonProperty("send_date")
+    private String sendDate;
+
+    /** 物流单ID(可为空字符串,签名时会被过滤) */
+    @JsonProperty("l_id")
+    private String lId;
+
+    /** 商品明细列表 */
+    @JsonProperty("items")
+    private List<TlOrderItemDetail> items;
+
+    /** 支付明细(可选) */
+    @JsonProperty("pay")
+    private TlPayDetail pay;
+}

+ 47 - 0
fs-service/src/main/java/com/fs/erp/dto/tl/TlOrderItemDetail.java

@@ -0,0 +1,47 @@
+package com.fs.erp.dto.tl;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 聚水潭订单商品明细
+ */
+@Data
+public class TlOrderItemDetail {
+
+    /** 商品 SKU 编码(平台 SKU) */
+    @JsonProperty("sku_id")
+    private String skuId;
+
+    /** 店铺商品 SKU 编码 */
+    @JsonProperty("shop_sku_id")
+    private String shopSkuId;
+
+    /** 商品实付金额(单位:分) */
+    @JsonProperty("amount")
+    private Integer amount;
+
+    /** 商品单价(单位:分) */
+    @JsonProperty("price")
+    private Integer price;
+
+    /** 商品原价(单位:分) */
+    @JsonProperty("base_price")
+    private Integer basePrice;
+
+    /** 商品数量 */
+    @JsonProperty("qty")
+    private Integer qty;
+
+    /** 商品名称 */
+    @JsonProperty("name")
+    private String name;
+
+    /** 商家子订单号(outer_oi_id) */
+    @JsonProperty("outer_oi_id")
+    private String outerOiId;
+
+    /** 生产日期,格式:yyyy-MM-dd(仅日期,不含时间) */
+    @JsonProperty("produced_date")
+    private String producedDate;
+}

+ 19 - 0
fs-service/src/main/java/com/fs/erp/dto/tl/TlPayDetail.java

@@ -0,0 +1,19 @@
+package com.fs.erp.dto.tl;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 支付明细信息
+ */
+@Data
+public class TlPayDetail {
+
+    /** 支付方式:如 "online"(在线支付)、"offline"(线下支付) */
+    @JsonProperty("payment")
+    private String payment;
+
+    /** 支付金额(单位:分) */
+    @JsonProperty("amount")
+    private Integer amount;
+}

+ 8 - 0
fs-service/src/main/java/com/fs/erp/http/JstErpHttpService.java

@@ -2,6 +2,8 @@ package com.fs.erp.http;
 
 import com.alibaba.fastjson.JSONObject;
 import com.fs.erp.dto.*;
+import com.fs.erp.dto.tl.TlCreateOrderRequest;
+import com.fs.erp.dto.tl.TlCreateOrderResponse;
 import com.fs.ybPay.dto.RefundOrderDTO;
 
 /**
@@ -77,4 +79,10 @@ public interface JstErpHttpService {
      */
     CommonResponse<AssetProcessResultDTO> aftersaleConfirm(AfterSaleConfirmRequestDTO dto);
 
+    /**
+     * 兔灵erp订单同步
+     * @param request 请求参数
+     * @return CommonResponse<AfterSaleResponseDTO>
+     */
+    TlCreateOrderResponse syncOrderToJst(TlCreateOrderRequest request);
 }

+ 126 - 4
fs-service/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java

@@ -9,21 +9,27 @@ import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.PropertyNamingStrategy;
 import com.alibaba.fastjson.TypeReference;
 import com.alibaba.fastjson.serializer.SerializeConfig;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fs.erp.dto.*;
+import com.fs.erp.dto.tl.TlCreateOrderRequest;
+import com.fs.erp.dto.tl.TlCreateOrderResponse;
 import com.fs.erp.service.impl.JstTokenService;
 import com.fs.erp.utils.SignUtil;
 import com.fs.ybPay.dto.RefundOrderDTO;
 import com.hc.openapi.tool.util.ObjectUtils;
 import lombok.extern.slf4j.Slf4j;
 //import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.codec.digest.DigestUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
 import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
+import java.time.Instant;
+import java.util.*;
 
 @Slf4j
 @Service
@@ -53,12 +59,26 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
 //     */
 //    private static final String REFRESH_TOKEN_URL = BASE_URL + "openWebIsv/auth/refreshToken;
 
+
     @Value("${jst.app_key:''}")
     private String appKey;
 
     @Value("${jst.app_secret:''}")
     private String appSecret;
 
+
+    //    测试兔灵ERP
+    @Value("${jst.api.url:https://api.xiangyue.life/api}")
+    private String jstApiBaseUrl;
+
+    @Value("${jst.secret.key:xysync_dLsSaheCzK7RU9gd}")
+    private String secretKey;
+
+    private final RestTemplate restTemplate = new RestTemplate();
+    private final ObjectMapper objectMapper = new ObjectMapper()
+            .configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
+
+
     @Autowired
     private JstTokenService jstTokenService;
 
@@ -307,4 +327,106 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
         log.info("API请求成功 - 结果: {}", JSON.toJSONString(commonResponse));
         return commonResponse;
     }
+
+    @Override
+    public TlCreateOrderResponse syncOrderToJst(TlCreateOrderRequest request) {
+        try {
+            String corpId = request.getCorpId();
+            List<?> rawData = request.getData();
+
+            if (rawData == null || rawData.isEmpty()) {
+                throw new IllegalArgumentException("订单数据不能为空");
+            }
+
+            List<Map<String, Object>> dataAsMaps = new ArrayList<>();
+            for (Object item : rawData) {
+                Map<String, Object> map = objectMapper.convertValue(item, Map.class);
+                dataAsMaps.add(map);
+            }
+
+            long timestamp = Instant.now().getEpochSecond();
+            String sortedDataJson = sortAndSerializeData(dataAsMaps);
+
+            String signSource = secretKey + corpId + sortedDataJson + timestamp;
+            String sign = DigestUtils.md5Hex(signSource).toLowerCase();
+
+            // 构建 URL
+            String url = jstApiBaseUrl + "/v1/mp/sync/order/jst/create?sign=" + sign + "&t=" + timestamp;
+
+            // 请求体
+            String requestBody = objectMapper.writeValueAsString(request);
+
+            //打印关键调试信息
+            log.info("【聚水潭】请求URL: {}", url);
+            log.info("【聚水潭】请求Body: {}", requestBody);
+            log.info("【聚水潭】sign = MD5({})", signSource);
+            log.info("【聚水潭】最终 sign: {}, t: {}", sign, timestamp);
+
+            // 发起请求
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
+
+            ResponseEntity<TlCreateOrderResponse> response = restTemplate.exchange(
+                    url,
+                    HttpMethod.POST,
+                    entity,
+                    TlCreateOrderResponse.class
+            );
+
+            log.info("【聚水潭】响应状态: {}, body: {}", response.getStatusCode(), response.getBody());
+            if (response.getStatusCode() == HttpStatus.OK) {
+                log.info("订单同步成功");
+            }else {
+                log.error("订单同步失败:{}", response);
+                throw new RuntimeException("订单同步失败");
+            }
+            return response.getBody();
+
+        } catch (JsonProcessingException e) {
+            log.error("JSON 序列化失败", e);
+            throw new RuntimeException("订单数据格式异常", e);
+        } catch (Exception e) {
+            log.error("调用同步聚水潭订单接口失败", e);
+            throw new RuntimeException("同步订单到聚水潭失败: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 递归对 Map 进行按键升序排序(支持嵌套 Map 和 List)
+     */
+    @SuppressWarnings("unchecked")
+    private Object deepSort(Object obj) {
+        if (obj instanceof Map) {
+            Map<String, Object> map = (Map<String, Object>) obj;
+            TreeMap<String, Object> sortedMap = new TreeMap<>();
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                sortedMap.put(entry.getKey(), deepSort(entry.getValue()));
+            }
+            return sortedMap;
+        } else if (obj instanceof List) {
+            List<?> list = (List<?>) obj;
+            List<Object> sortedList = new ArrayList<>();
+            for (Object item : list) {
+                sortedList.add(deepSort(item));
+            }
+            return sortedList;
+        } else {
+            return obj; // 基本类型、String、Number 等直接返回
+        }
+    }
+
+    /**
+     * 对 data 列表进行深度排序并序列化为紧凑 JSON(无空格)
+     */
+    private String sortAndSerializeData(List<Map<String, Object>> data) throws JsonProcessingException {
+        if (data == null || data.isEmpty()) {
+            return "[]";
+        }
+        List<Object> sortedList = new ArrayList<>();
+        for (Map<String, Object> item : data) {
+            sortedList.add(deepSort(item));
+        }
+        return objectMapper.writeValueAsString(sortedList);
+    }
 }