Explorar el Código

调整兔灵erp物流同步回调接口

cgp hace 1 semana
padre
commit
16dd076997

+ 85 - 83
fs-admin/src/main/java/com/fs/his/controller/JstOrderSyncController.java

@@ -2,13 +2,14 @@ package com.fs.his.controller;
 
 import com.baidu.dev2.thirdparty.commons.codec.digest.DigestUtils;
 import com.fs.erp.dto.tl.*;
-import com.fs.erp.http.JstErpHttpService;
+import com.fs.erp.service.TlErpOrderService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
 import java.time.Instant;
+import java.util.Collections;
 
 /**
  * 聚水潭订单同步接口
@@ -20,7 +21,7 @@ public class JstOrderSyncController {
 
 
     @Autowired
-    private JstErpHttpService jstErpHttpService;
+    private TlErpOrderService jstErpHttpService;
 
 
 
@@ -69,86 +70,87 @@ public class JstOrderSyncController {
     }
 
     // 模拟订单同步到兔灵erp接口
-//    @PostMapping("/create")
-//    public Object syncOrderToJst(@RequestBody TlCreateOrderRequest requestDto) {
-//        TlCreateOrderRequest request=fillRequest();
-//        log.info("【聚水潭】收到同步订单请求,corp_id={}, so_id={}",
-//                request.getCorpId(),
-//                request.getData() != null && !request.getData().isEmpty()
-//                        ? request.getData().get(0).getSoId() : "N/A");
-//        return jstErpHttpService.syncOrderToJst(request);
-//    }
-//
-//    /**
-//     * 辅助方法 填充入参对象JstCreateOrderRequest
-//     * */
-//    private TlCreateOrderRequest fillRequest() {
-//        // 1. 创建主请求对象
-//        TlCreateOrderRequest request = new TlCreateOrderRequest();
+    @PostMapping("/create")
+    public Object syncOrderToJst() {
+        TlCreateOrderRequest request=fillRequest();
+        log.info("同步订单请求,corp_id={}, so_id={}",
+                request.getCorpId(),
+                request.getData() != null && !request.getData().isEmpty()
+                        ? request.getData().get(0).getSoId() : "N/A");
+        return jstErpHttpService.syncOrderToJst(request);
+    }
+
+    /**
+     * 辅助方法 填充入参对象JstCreateOrderRequest
+     * */
+    private TlCreateOrderRequest fillRequest() {
+        // 1. 创建主请求对象
+        TlCreateOrderRequest request = new TlCreateOrderRequest();
 //        request.setCorpId("OGc3zcgK6H334BiPn8JNkVvh");
-//        //正式环境企业id
-//        //request.setCorpId("LwMAMdlBIH7agxn2t9N4Zv40");
-//
-//        // 2. 创建商品明细(items)
-//        TlOrderItemDetail item = new TlOrderItemDetail();
-//        item.setSkuId("SKU123");
-//        item.setShopSkuId("SHOP_SKU_001");
-//        item.setAmount(10000);          // 实付金额(分)
-//        item.setPrice(10000);           // 单价(分)
-//        item.setBasePrice(12000);       // 原价(分)
-//        item.setQty(1);
-//        item.setName("测试商品");
-//        item.setOuterOiId("OI_20251105_001");
-//        // produced_date 也要带时间(测试类是 "2025-11-05T00:00:00Z")
-//        item.setProducedDate("2025-11-05T00:00:00Z");
-//
-//        // 3. 创建订单主体
-//        TlOrderItem order = new TlOrderItem();
-//        order.setShopId(18150182L);
-//        order.setSoId("SO_20251105_001");
-//        // 使用 ISO 8601 格式:yyyy-MM-dd'T'HH:mm:ss'Z'
-//        order.setOrderDate("2025-11-05T10:30:00Z");
-//        order.setShopStatus("paid");                // 必须是字符串,如 "paid"
-//        order.setShopBuyerId("buyer_123");
-//
-//        order.setReceiverAddress("广东省深圳市南山区___科技园A座101");
-//        order.setReceiverName("张三");
-//        order.setReceiverMobile("13800138000");
-//
-//        order.setPayAmount(10000);   // 应付金额(分)
-//        order.setFreight(0);         // 运费(分)
-//
-//        order.setRemark("卖家备注");
-//        order.setBuyerMessage("请发顺丰");
-//        order.setLabels("测试,同步");
-//
-//        order.setCreatorName("李四");
-//        order.setShopModified("2025-11-05T10:30:00Z");
-//        order.setSendDate("2025-11-05T10:30:00Z");
-//
-//        // 关键:is_cod 不能为 null!聚水潭不接受 null 字段
-//        order.setIsCod(false);       // 显式设置为 false,避免 null
-//
-//        // l_id 为空字符串(签名时会被过滤,但保留字段)
-//        order.setLId("");
-//
-//        // 设置商品明细
-//        order.setItems(Collections.singletonList(item));
-//
-//        // 设置支付明细(必须设置,不能为 null)
-//        TlPayDetail pay = new TlPayDetail();
-//        pay.setPayment("online");
-//        pay.setAmount(10000);
-//        order.setPay(pay);
-//
-//        // 4. 设置 data 列表(仅支持1条)
-//        request.setData(Collections.singletonList(order));
-//
-//        // 5. 设置 options
-//        TlOptions options = new TlOptions();
-//        options.setJstServerId(1);
-//        request.setOptions(options);
-//
-//        return request;
-//    }
+        //正式环境企业id
+        request.setCorpId("LwMAMdlBIH7agxn2t9N4Zv40");
+
+        // 2. 创建商品明细(items)
+        TlOrderItemDetail item = new TlOrderItemDetail();
+        item.setSkuId("SKU123");
+        item.setShopSkuId("SHOP_SKU_001");
+        item.setAmount(10000);          // 实付金额(分)
+        item.setPrice(10000);           // 单价(分)
+        item.setBasePrice(12000);       // 原价(分)
+        item.setQty(1);
+        item.setName("测试商品");
+        item.setOuterOiId("OI_20251110_001");
+        // produced_date 也要带时间(测试类是 "2025-11-05T00:00:00Z")
+        item.setProducedDate("2025-11-10T00:00:00Z");
+
+        // 3. 创建订单主体
+        TlOrderItem order = new TlOrderItem();
+        //order.setShopId(18150182);
+        order.setShopId(19415819);
+        order.setSoId("SO_20251105_001");
+        // 使用 ISO 8601 格式:yyyy-MM-dd'T'HH:mm:ss'Z'
+        order.setOrderDate("2025-11-10T10:30:00Z");
+        order.setShopStatus("paid");                // 必须是字符串,如 "paid"
+        order.setShopBuyerId("buyer_123");
+
+        order.setReceiverAddress("广东省深圳市南山区___科技园A座101");
+        order.setReceiverName("张三");
+        order.setReceiverMobile("13800138000");
+
+        order.setPayAmount(10000);   // 应付金额(分)
+        order.setFreight(0);         // 运费(分)
+
+        order.setRemark("卖家备注");
+        order.setBuyerMessage("请发顺丰");
+        order.setLabels("测试,同步");
+
+        order.setCreatorName("李四");
+        order.setShopModified("2025-11-05T10:30:00Z");
+        order.setSendDate("2025-11-05T10:30:00Z");
+
+        // 关键:is_cod 不能为 null!聚水潭不接受 null 字段
+        order.setIsCod(false);       // 显式设置为 false,避免 null
+
+        // l_id 为空字符串(签名时会被过滤,但保留字段)
+        order.setLId("");
+
+        // 设置商品明细
+        order.setItems(Collections.singletonList(item));
+
+        // 设置支付明细(必须设置,不能为 null)
+        TlPayDetail pay = new TlPayDetail();
+        pay.setPayment("online");
+        pay.setAmount(10000);
+        order.setPay(pay);
+
+        // 4. 设置 data 列表(仅支持1条)
+        request.setData(Collections.singletonList(order));
+
+        // 5. 设置 options
+        TlOptions options = new TlOptions();
+        options.setJstServerId(1);
+        request.setOptions(options);
+
+        return request;
+    }
 }

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

@@ -13,7 +13,7 @@ public class TlOrderItem {
 
     /** 店铺ID */
     @JsonProperty("shop_id")
-    private Long shopId;
+    private Integer shopId;
 
     /** 商家订单号(so_id) */
     @JsonProperty("so_id")

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

@@ -79,17 +79,4 @@ public interface JstErpHttpService {
      * @return CommonResponse<AssetProcessResultDTO>
      */
     CommonResponse<AssetProcessResultDTO> aftersaleConfirm(AfterSaleConfirmRequestDTO dto);
-
-    /**
-     * 兔灵erp订单同步
-     * @param request 请求参数
-     * @return CommonResponse<AfterSaleResponseDTO>
-     */
-    CommonResponse<TlCreateOrderResponse> syncOrderToJst(TlCreateOrderRequest request);
-
-    /**
-     * 兔灵erp物流同步
-     * @param request 订单物流信息
-     */
-    public void processLogisticsPush(JstLogisticsPushRequest request);
 }

+ 0 - 151
fs-service/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java

@@ -68,19 +68,6 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
     private String appSecret;
 
 
-    //    测试兔灵ERP
-    @Value("${jst.api.url:https://api.xiangyue.life/api}")
-    //@Value("${jst.api.url:https://papi.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;
 
@@ -329,142 +316,4 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
         log.info("API请求成功 - 结果: {}", JSON.toJSONString(commonResponse));
         return commonResponse;
     }
-
-    @Override
-    public CommonResponse<TlCreateOrderResponse> syncOrderToJst(TlCreateOrderRequest request) {
-        try {
-            String corpId = request.getCorpId();
-            List<?> rawData = request.getData();
-
-            if (rawData == null || rawData.isEmpty()) {
-                return new CommonResponse<TlCreateOrderResponse>()
-                        .setCode(-1)
-                        .setMsg("订单数据不能为空")
-                        .setData(null);
-            }
-
-            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();
-
-            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 && response.getBody() != null) {
-                return new CommonResponse<TlCreateOrderResponse>()
-                        .setCode(0)
-                        .setMsg("success")
-                        .setData(response.getBody());
-            } else {
-                String errorMsg = "HTTP状态码非200或响应体为空";
-                log.error("订单同步失败: {}", errorMsg);
-                return new CommonResponse<TlCreateOrderResponse>()
-                        .setCode(-1)
-                        .setMsg(errorMsg)
-                        .setData(null);
-            }
-
-        } catch (JsonProcessingException e) {
-            log.error("JSON 序列化失败", e);
-            return new CommonResponse<TlCreateOrderResponse>()
-                    .setCode(-2)
-                    .setMsg("订单数据格式异常: " + e.getMessage())
-                    .setData(null);
-        } catch (Exception e) {
-            log.error("调用同步兔灵(聚水潭)订单接口失败", e);
-            return new CommonResponse<TlCreateOrderResponse>()
-                    .setCode(-3)
-                    .setMsg("同步订单到兔灵(聚水潭)失败: " + e.getMessage())
-                    .setData(null);
-        }
-    }
-
-    /**
-     * 递归对 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);
-    }
-
-    public void processLogisticsPush(JstLogisticsPushRequest request) {
-        // 关键业务字段
-        String soId = request.getSoId();       // 外部单号
-        String lId = request.getLId();         // 物流单号
-        String lcId = request.getLcId();       // 快递公司编码
-        String sendDate = request.getSendDate();
-        log.info("【兔灵】物流单号推送: 外部单号:{}, 物流单号:{}, 快递公司编码:{}, 发送时间:{}", soId, lId, lcId, sendDate);
-
-        // TODO: 根据 id 查询系统中的订单,并更新物流信息
-        // Order order = orderRepository.findBySoId(soId);
-        // if (order != null) {
-        //     order.setLogisticsNo(lId);
-        //     order.setLogisticsCompany(lcId);
-        //     order.setShipTime(LocalDateTime.parse(sendDate, ...));
-        //     orderRepository.save(order);
-        // }
-
-        //遍历处理items
-        if (request.getItems() != null) {
-            request.getItems().forEach(item -> {
-                log.debug("商品项: skuId={}, qty={}", item.getSkuId(), item.getQty());
-            });
-        }
-    }
 }

+ 23 - 0
fs-service/src/main/java/com/fs/erp/service/TlErpOrderService.java

@@ -0,0 +1,23 @@
+package com.fs.erp.service;
+
+import com.fs.erp.dto.CommonResponse;
+import com.fs.erp.dto.tl.JstLogisticsPushRequest;
+import com.fs.erp.dto.tl.TlCreateOrderRequest;
+import com.fs.erp.dto.tl.TlCreateOrderResponse;
+
+public interface TlErpOrderService {
+
+
+    /**
+     * 兔灵erp订单同步(推送)
+     * @param request 请求参数
+     * @return CommonResponse<AfterSaleResponseDTO>
+     */
+    CommonResponse<TlCreateOrderResponse> syncOrderToJst(TlCreateOrderRequest request);
+
+    /**
+     * 兔灵erp物流回调接口
+     * @param request 订单物流信息
+     */
+    public void processLogisticsPush(JstLogisticsPushRequest request);
+}

+ 177 - 0
fs-service/src/main/java/com/fs/erp/service/impl/TlErpOrderServiceImpl.java

@@ -0,0 +1,177 @@
+package com.fs.erp.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fs.erp.dto.CommonResponse;
+import com.fs.erp.dto.tl.JstLogisticsPushRequest;
+import com.fs.erp.dto.tl.TlCreateOrderRequest;
+import com.fs.erp.dto.tl.TlCreateOrderResponse;
+import com.fs.erp.service.TlErpOrderService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+@Slf4j
+@Service
+public class TlErpOrderServiceImpl implements TlErpOrderService {
+
+    //测试兔灵ERP
+    //@Value("${jst.api.url:https://api.xiangyue.life/api}")
+    @Value("${jst.api.url:https://papi.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);
+
+    @Override
+    public CommonResponse<TlCreateOrderResponse> syncOrderToJst(TlCreateOrderRequest request) {
+        try {
+            String corpId = request.getCorpId();
+            List<?> rawData = request.getData();
+
+            if (rawData == null || rawData.isEmpty()) {
+                return new CommonResponse<TlCreateOrderResponse>()
+                        .setCode(-1)
+                        .setMsg("订单数据不能为空")
+                        .setData(null);
+            }
+
+            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();
+
+            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 && response.getBody() != null) {
+                return new CommonResponse<TlCreateOrderResponse>()
+                        .setCode(200)
+                        .setMsg("success")
+                        .setData(response.getBody());
+            } else {
+                String errorMsg = "HTTP状态码非200或响应体为空";
+                log.error("订单同步失败: {}", errorMsg);
+                return new CommonResponse<TlCreateOrderResponse>()
+                        .setCode(-1)
+                        .setMsg(errorMsg)
+                        .setData(null);
+            }
+
+        } catch (JsonProcessingException e) {
+            log.error("JSON 序列化失败", e);
+            return new CommonResponse<TlCreateOrderResponse>()
+                    .setCode(-2)
+                    .setMsg("订单数据格式异常: " + e.getMessage())
+                    .setData(null);
+        } catch (Exception e) {
+            log.error("调用同步兔灵(聚水潭)订单接口失败", e);
+            return new CommonResponse<TlCreateOrderResponse>()
+                    .setCode(-3)
+                    .setMsg("同步订单到兔灵(聚水潭)失败: " + e.getMessage())
+                    .setData(null);
+        }
+    }
+
+    /**
+     * 递归对 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);
+    }
+
+    public void processLogisticsPush(JstLogisticsPushRequest request) {
+        // 关键业务字段
+        String soId = request.getSoId();       // 外部单号
+        String lId = request.getLId();         // 物流单号
+        String lcId = request.getLcId();       // 快递公司编码
+        String sendDate = request.getSendDate();
+        log.info("【兔灵】物流单号推送: 外部单号:{}, 物流单号:{}, 快递公司编码:{}, 发送时间:{}", soId, lId, lcId, sendDate);
+
+        // TODO: 根据 id 查询系统中的订单,并更新物流信息
+        // Order order = orderRepository.findBySoId(soId);
+        // if (order != null) {
+        //     order.setLogisticsNo(lId);
+        //     order.setLogisticsCompany(lcId);
+        //     order.setShipTime(LocalDateTime.parse(sendDate, ...));
+        //     orderRepository.save(order);
+        // }
+
+        //遍历处理items
+        if (request.getItems() != null) {
+            request.getItems().forEach(item -> {
+                log.debug("商品项: skuId={}, qty={}", item.getSkuId(), item.getQty());
+            });
+        }
+    }
+}