Ver código fonte

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

cgp 1 semana atrás
pai
commit
5237580df6

+ 11 - 7
fs-admin/src/main/java/com/fs/his/controller/JstOrderSyncController.java

@@ -76,7 +76,7 @@ public class JstOrderSyncController {
 
     // 模拟订单同步到兔灵erp接口
     @PostMapping("/create")
-    public Object syncOrderToJst() throws JsonProcessingException {
+    public Object syncOrderToJst(){
         TlCreateOrderRequest request=fillRequest();
         log.info("同步订单请求,corp_id={}, so_id={}",
                 request.getCorpId(),
@@ -121,8 +121,10 @@ public class JstOrderSyncController {
         order.setReceiverAddress("科技园A座101");
         order.setReceiverName("张三");
         order.setReceiverMobile("13800138000");
-        order.setPayAmount(10000.0);
-        order.setFreight(0.0);
+//        order.setPayAmount(10000.0);
+//        order.setFreight(0.0);
+        order.setPayAmount(10000);
+        order.setFreight(0);
 
         order.setRemark("卖家备注");
         order.setBuyerMessage("请发顺丰");
@@ -147,9 +149,12 @@ public class JstOrderSyncController {
         //OrderItemDTO item = new OrderItemDTO();
         item.setSkuId("SKU123");
         item.setShopSkuId("SHOP_SKU_001");
-        item.setAmount(BigDecimal.valueOf(10000));      // 实付金额(分)
-        item.setPrice(BigDecimal.valueOf(10000));       // 单价(分)
-        item.setBasePrice(12000.0);   // 原价(分)
+//        item.setAmount(BigDecimal.valueOf(10000));      // 实付金额(分)
+//        item.setPrice(BigDecimal.valueOf(10000));       // 单价(分)
+        item.setAmount(10000);      // 实付金额(分)
+        item.setPrice(10000);       // 单价(分)
+//        item.setBasePrice(12000.0);   // 原价(分)
+        item.setBasePrice(12000);   // 原价(分)
         item.setQty(1);
         item.setName("测试商品");
         item.setOuterOiId("OI_20251110_001");
@@ -166,7 +171,6 @@ public class JstOrderSyncController {
         pay.setPayment("online");
         pay.setSellerAccount("alipay@company.com"); // 必填
         pay.setBuyerAccount("buyer@example.com");   // 必填
-        //pay.setAmount(10000.0);
         pay.setAmount(10000);
         return pay;
     }

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

@@ -18,7 +18,6 @@ public class TlCreateOrderRequest {
 
     /** 订单数据列表 */
     @JsonProperty("data")
-    //private List<ShopOrderDTO> data;
     private List<TlOrderItem> data;
 
     /** 请求选项 */

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

@@ -71,11 +71,13 @@ public class TlOrderItem {
     // --- 金额与状态 ---
     /** 应付金额(单位:分,注意文档中类型为 integer,但描述为保留两位小数 → 实际应为“分”) */
     @JsonProperty("pay_amount")
-    private Double payAmount;
+    private Integer payAmount;
+//    private Double payAmount;
 
     /** 运费(单位:分) */
     @JsonProperty("freight")
-    private Double freight;
+    private Integer freight;
+//    private Double freight;
 
     /** 卖家备注 */
     @JsonProperty("remark")

+ 6 - 3
fs-service/src/main/java/com/fs/erp/dto/tl/TlOrderItemDetail.java

@@ -21,15 +21,18 @@ public class TlOrderItemDetail {
 
     /** 商品实付金额(单位:分) - 必填 */
     @JsonProperty("amount")
-    private BigDecimal amount;
+//    private BigDecimal amount;
+    private Integer amount;
 
     /** 商品单价(单位:分,支持4位小数 → 实际应为“万分之一元”,但文档写 integer,建议确认) */
     @JsonProperty("price")
-    private BigDecimal price;
+//    private BigDecimal price;
+    private Integer price;
 
     /** 商品原价(单位:分) - 必填 */
     @JsonProperty("base_price")
-    private Double basePrice;
+//    private Double basePrice;
+    private Integer basePrice;
 
     /** 商品数量 - 必填 */
     @JsonProperty("qty")

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

@@ -15,7 +15,7 @@ public interface TlErpOrderService {
      * @param request 请求参数
      * @return CommonResponse<AfterSaleResponseDTO>
      */
-    ResponseEntity<TlCreateOrderResponse> syncOrderToJst(TlCreateOrderRequest request);
+    HttpResponse syncOrderToJst(TlCreateOrderRequest request);
 
     /**
      * 兔灵erp物流回调接口

+ 3 - 1
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -48,6 +48,8 @@ import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.stream.Collectors;
 
+import static com.fs.erp.utils.TlOrderConverter.toTlOrderItem;
+
 @Slf4j
 @Service
 public class JSTErpOrderServiceImpl implements IErpOrderService {
@@ -196,7 +198,7 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
             upload = jstErpHttpService.upload(shopOrderDTO);
         } else if (order.getErpType() == 2) {
             TlCreateOrderRequest request = new TlCreateOrderRequest();
-            request.setData(Arrays.asList(shopOrderDTO));
+            request.setData(Arrays.asList(toTlOrderItem(shopOrderDTO)));
             request.setCorpId("LwMAMdlBIH7agxn2t9N4Zv40");
             HttpResponse response = tlErpOrderService.syncOrderToJst(request);
             upload = parseResponse(response,  new TypeReference<CommonResponse<ErpOrderResponseDTO>>() {});

+ 53 - 57
fs-service/src/main/java/com/fs/erp/service/impl/TlErpOrderServiceImpl.java

@@ -1,22 +1,16 @@
 package com.fs.erp.service.impl;
 
-import cn.hutool.core.map.MapUtil;
 import cn.hutool.http.HttpResponse;
 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 com.fs.erp.utils.SignUtil;
 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.*;
@@ -33,63 +27,65 @@ public class TlErpOrderServiceImpl implements TlErpOrderService {
     @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 ResponseEntity<TlCreateOrderResponse> syncOrderToJst(TlCreateOrderRequest request) {
+    public HttpResponse 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
-                );
+            String corpId = request.getCorpId();
+            List<?> rawData = request.getData();
+
+            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);
+            // 使用 Hutool HttpRequest 发送 POST 请求
+            HttpResponse response = cn.hutool.http.HttpRequest.post(url)
+                    .header("Content-Type", "application/json;charset=UTF-8")
+                    .body(requestBody)
+                    .timeout(10000) // 可选:设置超时(毫秒)
+                    .execute();
+
+            if (!response.isOk()) {
+                log.error("HTTP 请求失败,状态码: {}", response.getStatus());
+                throw new RuntimeException("调用聚水潭接口失败,HTTP 状态码: " + response.getStatus());
+            }
+
+            // 解析响应体
+            String responseBody = response.body();
+            Map<String, Object> result;
+            try {
+                result = objectMapper.readValue(responseBody, Map.class);
+            } catch (Exception e) {
+                log.error("无法解析聚水潭响应体: {}", responseBody, e);
+                throw new RuntimeException("聚水潭返回非 JSON 响应", e);
+            }
+            // 判断业务状态是否为 "ok"
+            String status = (String) result.get("status");
+            if (!"ok".equals(status)) {
+                String errmsg = (String) result.get("errmsg");
+                log.error("聚水潭业务处理失败,status: {}, errmsg: {}", status, errmsg);
+                throw new RuntimeException("聚水潭同步订单失败: " + (errmsg != null ? errmsg : "未知错误"));
+            }
+            log.info("订单同步到聚水潭成功!");
             return response;
-//                Map<String, String> headers = MapUtil.builder(new HashMap<String, String>())
-//                    .put("Content-Type", "application/json;charset=UTF-8")
-//                    .build();
-//                HttpResponse response = cn.hutool.http.HttpRequest.post(url)
-//                        .headerMap(headers, true)
-//                        .body(requestBody)
-//                        .execute();
-//                return response;
-       } catch (JsonProcessingException e) {
+        } catch (Exception e) {
             throw new RuntimeException(e);
         }
 

+ 144 - 0
fs-service/src/main/java/com/fs/erp/utils/TlOrderConverter.java

@@ -0,0 +1,144 @@
+package com.fs.erp.utils;
+
+import com.fs.erp.dto.*;
+import com.fs.erp.dto.tl.*;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TlOrderConverter {
+
+    // =============== 主入口 ===============
+    public static TlOrderItem toTlOrderItem(ShopOrderDTO shopOrder) {
+        if (shopOrder == null) return null;
+
+        TlOrderItem item = new TlOrderItem();
+
+        // 基础字段
+        item.setShopId(safeLongToInt(shopOrder.getShopId()));
+        item.setSoId(shopOrder.getSoId());
+        item.setOrderDate(shopOrder.getOrderDate());
+        item.setShopStatus(shopOrder.getShopStatus());
+        item.setShopBuyerId(shopOrder.getShopBuyerId());
+
+        // 收货地址
+        item.setReceiverState(shopOrder.getReceiverState());
+        item.setReceiverCity(shopOrder.getReceiverCity());
+        item.setReceiverDistrict(shopOrder.getReceiverDistrict());
+        item.setReceiverTown(null); // 无对应字段
+        item.setReceiverZip(shopOrder.getReceiverZip());
+        item.setReceiverAddress(shopOrder.getReceiverAddress());
+        item.setReceiverName(shopOrder.getReceiverName());
+        item.setReceiverPhone(shopOrder.getReceiverPhone());
+        item.setReceiverMobile(shopOrder.getReceiverPhone()); // 复用 phone 作为 mobile
+
+        // 金额(元 → 分)
+        item.setPayAmount(toFen(shopOrder.getPayAmount()));     // Double → Integer
+        item.setFreight(toFen(shopOrder.getFreight()));         // Double → Integer
+
+        // 其他
+        item.setRemark(shopOrder.getRemark());
+        item.setBuyerMessage(shopOrder.getBuyerMessage());
+        item.setIsCod(shopOrder.getIsCod());
+        item.setShopModified(shopOrder.getShopModified());
+
+        // 物流
+        item.setLId(shopOrder.getLId());
+        item.setLogisticsCompany(shopOrder.getLogisticsCompany());
+        item.setLcId(shopOrder.getLcId());
+
+        // 标签等
+        item.setLabels(shopOrder.getLabels());
+        item.setQuestionDesc(null);
+        item.setSellerFlag(0);
+
+        // 嵌套对象
+        item.setItems(toTlOrderItemDetails(shopOrder.getItems()));
+        item.setPay(toTlPayDetail(shopOrder.getPay()));
+
+        // 必填字段:发货日期(用订单日期代替,建议业务层传入正确值)
+        item.setSendDate(shopOrder.getOrderDate());
+
+        // 其他(无对应字段)
+        item.setNode(null);
+        item.setCreator(null);
+        item.setCreatorName(null);
+        item.setCart(null); // 身份证信息未提供,设为 null
+
+        return item;
+    }
+
+    // =============== 商品明细转换 ===============
+    private static List<TlOrderItemDetail> toTlOrderItemDetails(List<OrderItemDTO> items) {
+        if (items == null) return null;
+        return items.stream()
+                .map(TlOrderConverter::toTlOrderItemDetail)
+                .collect(Collectors.toList());
+    }
+
+    private static TlOrderItemDetail toTlOrderItemDetail(OrderItemDTO dto) {
+        if (dto == null) return null;
+
+        TlOrderItemDetail detail = new TlOrderItemDetail();
+
+        detail.setSkuId(dto.getSkuId());
+        detail.setShopSkuId(dto.getShopSkuId());
+
+        // 金额转换:元 → 分(Integer)
+        detail.setAmount(toFen(dto.getAmount()));           // BigDecimal → Integer
+        detail.setPrice(toFen(dto.getPrice()));             // BigDecimal → Integer
+        detail.setBasePrice(toFen(dto.getBasePrice()));     // Double → Integer
+
+        detail.setQty(dto.getQty());
+        detail.setName(dto.getName());
+        detail.setOuterOiId(dto.getOuterOiId());
+        detail.setRemark(null); // OrderItemDTO 无 remark 字段(或可映射 propertiesValue?)
+        detail.setBatchId(dto.getBatchId());
+        detail.setProducedDate(dto.getProducedDate());
+
+        return detail;
+    }
+
+    // =============== 支付明细转换 ===============
+    private static TlPayDetail toTlPayDetail(PaymentDTO dto) {
+        if (dto == null) return null;
+
+        TlPayDetail pay = new TlPayDetail();
+        pay.setOuterPayId(dto.getOuterPayId());
+        pay.setPayDate(dto.getPayDate());
+        pay.setPayment(dto.getPayment());
+        pay.setSellerAccount(dto.getSellerAccount());
+        pay.setBuyerAccount(dto.getBuyerAccount());
+
+        // 注意:TlPayDetail.amount 是 Double,但语义是“分”
+        // 如果聚水潭实际要求整数,请将 TlPayDetail.amount 改为 Integer,并用 toFen()
+        // 目前按你定义的 Double 处理:假设 dto.getAmount() 是“元”,需转为“分”的 Double
+        if (dto.getAmount() != null) {
+            pay.setAmount(dto.getAmount() * 100); // 元 → 分(Double)
+        } else {
+            pay.setAmount(null);
+        }
+
+        return pay;
+    }
+
+    // =============== 工具方法 ===============
+    private static Integer toFen(Double yuan) {
+        if (yuan == null) return null;
+        return (int) Math.round(yuan * 100);
+    }
+
+    private static Integer toFen(BigDecimal yuan) {
+        if (yuan == null) return null;
+        return yuan.multiply(BigDecimal.valueOf(100)).intValue();
+    }
+
+    private static Integer safeLongToInt(Long value) {
+        if (value == null) return null;
+        if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
+            throw new IllegalArgumentException("Long value out of Integer range: " + value);
+        }
+        return value.intValue();
+    }
+}