Przeglądaj źródła

顺丰状态回调 费用清单回调

zxfqwert 1 tydzień temu
rodzic
commit
837201c0e2

+ 4 - 2
jd-logistics-modules/jd-logistics-system/pom.xml

@@ -95,8 +95,10 @@
             <artifactId>ECAP</artifactId>
             <version>6.9</version>
         </dependency>
-
-
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
 
 
     </dependencies>

+ 107 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/callback/SfFeePushCallback.java

@@ -0,0 +1,107 @@
+package com.ruoyi.logistics.callback;
+import com.ruoyi.logistics.service.impl.FeePushService;
+import org.springframework.web.bind.annotation.*;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ruoyi.logistics.domain.SfFeeApiResponse;
+import com.ruoyi.logistics.domain.SfFeePushRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+
+
+
+/**
+ * 顺丰物流费用清单回调
+ * 目前涉及的回调有:
+ * 1.订单状态回调
+ * 2.费用清单回调
+ *
+ * @author zxf
+ * @date 2026-02-03
+ */
+@RestController
+@RequestMapping("/feecallback")
+public class SfFeePushCallback {
+    private static final Logger logger = LoggerFactory.getLogger(SfFeePushCallback.class);
+
+    private final ObjectMapper objectMapper;
+    private final FeePushService feePushService;
+
+    public SfFeePushCallback(ObjectMapper objectMapper, FeePushService feePushService) {
+        this.objectMapper = objectMapper;
+        this.feePushService = feePushService;
+    }
+
+    /**
+     * 接收顺丰运费清单推送
+     * 注意:接口整体报文为form表单格式,但content参数是JSON数据
+     * @param sign 数字签名
+     * @param content 业务数据JSON字符串
+     * @return 标准响应
+     */
+    @PostMapping("/feePush")
+    public ResponseEntity<SfFeeApiResponse> receiveFeePush(
+            @RequestParam("sign") String sign,
+            @RequestParam("content") String content) {
+
+        logger.info("收到顺丰运费清单推送,sign: {}", sign);
+
+        try {
+          /*  // 1. 验证签名(根据顺丰文档实现)
+            if (!verifySignature(sign, content)) {
+                logger.error("签名验证失败");
+                return ResponseEntity.ok(SfFeeApiResponse.error("签名验证失败"));
+            }*/
+
+            // 2. 解析JSON数据
+            SfFeePushRequest feePushRequest = objectMapper.readValue(content, SfFeePushRequest.class);
+
+            // 3. 参数验证
+            if (feePushRequest.getWaybillNo() == null || feePushRequest.getWaybillNo().isEmpty()) {
+                return ResponseEntity.ok(SfFeeApiResponse.error("运单号不能为空"));
+            }
+            if (feePushRequest.getOrderNo() == null || feePushRequest.getOrderNo().isEmpty()) {
+                return ResponseEntity.ok(SfFeeApiResponse.error("订单号不能为空"));
+            }
+
+            // 4. 处理业务逻辑
+            boolean success = feePushService.processFeePush(feePushRequest);
+
+            if (success) {
+                logger.info("运费清单处理成功,运单号: {}, 订单号: {}",
+                        feePushRequest.getWaybillNo(), feePushRequest.getOrderNo());
+                return ResponseEntity.ok(SfFeeApiResponse.success());
+            } else {
+                logger.error("运费清单处理失败,运单号: {}", feePushRequest.getWaybillNo());
+                return ResponseEntity.ok(SfFeeApiResponse.error("业务处理失败"));
+            }
+
+        } catch (com.fasterxml.jackson.core.JsonParseException e) {
+            logger.error("JSON解析失败: {}", e.getMessage());
+            return ResponseEntity.ok(SfFeeApiResponse.error("非法的JSON格式"));
+        } catch (Exception e) {
+            logger.error("处理运费清单推送异常: {}", e.getMessage(), e);
+            return ResponseEntity.ok(SfFeeApiResponse.error("系统发生数据错误或运行时异常"));
+        }
+    }
+
+    /**
+     * 验证签名
+     * 根据顺丰文档实现签名验证逻辑
+     */
+    /*private boolean verifySignature(String sign, String content) {
+        // 实际实现应根据顺丰提供的签名算法
+        // 通常使用checkword对content进行加密,然后与sign比较
+        // 这里简化处理,实际应按照文档实现
+        try {
+            // 示例:使用MD5或SHA256等算法
+             String calculatedSign = calculateSign(content, yourCheckword);
+             return calculatedSign.equals(sign);
+
+        } catch (Exception e) {
+            logger.error("签名验证异常: {}", e.getMessage());
+            return false;
+        }
+    }*/
+}

+ 115 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/callback/SfPushOrderStatusCallback.java

@@ -0,0 +1,115 @@
+package com.ruoyi.logistics.callback;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.ruoyi.logistics.domain.OrderStateDetail;
+import com.ruoyi.logistics.domain.SfApiResponse;
+import com.ruoyi.logistics.domain.SfPushOrderStateRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+/**
+ * 顺丰物流订单状态回调相关
+ * 目前涉及的回调有:
+ * 1.订单状态回调
+ * 2.费用清单回调
+ *
+ * @author zxf
+ * @date 2026-02-03
+ */
+@RestController
+@RequestMapping("/callback")
+public class SfPushOrderStatusCallback {
+    private static final Logger logger = LoggerFactory.getLogger(SfPushOrderStatusCallback.class);
+
+
+    /**
+     * 顺丰物流订单状态回调
+     * 说明:仅限于推送上门订单调度状态  非预约订单不推送
+     *
+     * @author zxf
+     * @date 2026-02-03
+     */
+    @PostMapping("/orderStatus")
+    public SfApiResponse orderStatus(@RequestBody SfPushOrderStateRequest pushRequest) throws UnsupportedEncodingException, JsonProcessingException {
+        logger.info("收到顺丰订单状态推送,requestId: {}", pushRequest.getRequestId());
+
+        // 1.订单状态为空直接返给顺丰
+        if (pushRequest.getOrderState() == null || pushRequest.getOrderState().isEmpty()) {
+            logger.error("订单状态数据为空");
+            return createErrorResponse("订单状态数据为空");
+        }
+
+        // 2. 处理每条订单状态更新
+        List<OrderStateDetail> stateList = pushRequest.getOrderState();
+        for (OrderStateDetail state : stateList) {
+            try {
+                processSingleOrderState(state);
+            } catch (Exception e) {
+                logger.error("处理订单状态失败,订单号: {}, 运单号: {}", state.getOrderNo(), state.getWaybillNo(), e);
+                // 可根据业务决定是记录日志后继续,还是直接返回失败
+            }
+        }
+
+        // 3. 返回成功响应(必须严格按此格式,否则顺丰可能认为推送失败)
+        SfApiResponse response = new SfApiResponse();
+        response.setSuccess("true");
+        response.setCode("0");
+        response.setMsg("");
+        logger.info("订单状态推送处理完成,requestId: {}", pushRequest.getRequestId());
+        return response;
+
+    }
+
+
+    /**
+            * 处理单个订单的状态更新
+     */
+    private void processSingleOrderState(OrderStateDetail state) {
+        logger.info("更新订单状态 - 订单号: {}, 运单号: {}, 状态码: {}, 状态描述: {}, 更新时间: {}",
+                state.getOrderNo(),
+                state.getWaybillNo(),
+                state.getOrderStateCode(),
+                state.getOrderStateDesc(),
+                state.getLastTime());
+        // 根据顺丰返回的状态吗进行系统状态变更  目前还没对
+        switch (state.getOrderStateCode()) {
+            case "04":
+
+                break;
+            case "04-40001":
+
+                break;
+            case "04-40037":
+
+                break;
+
+            default:
+                logger.info("收到未定义的状态码: {}", state.getOrderStateCode());
+        }
+    }
+
+
+
+
+
+
+
+
+
+
+
+
+
+    private SfApiResponse createErrorResponse(String errorMsg) {
+        SfApiResponse response = new SfApiResponse();
+        response.setSuccess("false");
+        response.setMsg(errorMsg);
+        return response;
+    }
+}

+ 23 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/OrderStateDetail.java

@@ -0,0 +1,23 @@
+package com.ruoyi.logistics.domain;
+
+
+import lombok.Data;
+/**
+ * 订单状态详情
+ * @author zxf
+ * @date 2026-02-03
+ */
+@Data
+public class OrderStateDetail {
+    private String orderNo;
+    private String waybillNo;
+    private String orderStateCode;
+    private String orderStateDesc;
+    private String empCode;
+    private String empPhone;
+    private String netCode;
+    private String lastTime;
+    private String bookTime;
+    private String carrierCode;
+    private String createTm;
+}

+ 13 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/SfApiResponse.java

@@ -0,0 +1,13 @@
+package com.ruoyi.logistics.domain;
+import lombok.Data;
+/**
+ * 顺丰标准响应体
+ * @author zxf
+ * @date 2026-02-03
+ */
+@Data
+public class SfApiResponse {
+    private String success; // 或使用Boolean类型
+    private String code;    // 成功时为"0"
+    private String msg;     //
+}

+ 36 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/SfFeeApiResponse.java

@@ -0,0 +1,36 @@
+package com.ruoyi.logistics.domain;
+
+import lombok.Data;
+
+/**
+ * 顺丰费用清单标准返回体
+ * @author zxf
+ * @date 2026-02-03
+ */
+@Data
+public class SfFeeApiResponse {
+    private Integer code;          // 200成功,400失败
+    private String partnerCode;    // 合作伙伴code
+    private String service;        // 服务code
+    private String msgData;        // 消息数据
+
+
+    public static SfFeeApiResponse success() {
+        SfFeeApiResponse response = new SfFeeApiResponse();
+        response.setCode(200);
+        response.setPartnerCode("yourPartnerCode");
+        response.setService("");
+        response.setMsgData("");
+        return response;
+    }
+
+    // 快速创建失败响应
+    public static SfFeeApiResponse error(String errorMsg) {
+        SfFeeApiResponse response = new SfFeeApiResponse();
+        response.setCode(400);
+        response.setPartnerCode("");
+        response.setService("");
+        response.setMsgData(errorMsg);
+        return response;
+    }
+}

+ 32 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/SfFeeInfo.java

@@ -0,0 +1,32 @@
+package com.ruoyi.logistics.domain;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 顺丰费用明细实体
+ * @author zxf
+ * @date 2026-02-03
+ */
+@Data
+public class SfFeeInfo {
+    private String bizOwnerZoneCode;
+    private String currencyCode;
+    private String customerAcctCode;
+    private BigDecimal feeAmt;
+    private BigDecimal feeAmtInd;
+    private Integer feeIndType;
+    private String feeTypeCode;
+    private String gatherEmpCode;
+    private String gatherZoneCode;
+    private String paymentChangeTypeCode;
+    private String paymentTypeCode;
+    private Integer serviceId;
+    private String settlementTypeCode;
+    private Integer versionNo;
+    private Integer waybillId;
+    private String waybillNo;
+    private Long inputTm;
+
+}

+ 22 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/SfFeePushRequest.java

@@ -0,0 +1,22 @@
+package com.ruoyi.logistics.domain;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 顺丰运费清单推送请求实体
+ * @author zxf
+ * @date 2026-02-03
+ */
+@Data
+public class SfFeePushRequest {
+    private String waybillNo;
+    private String customerAcctCode;
+    private String childNos;
+    private String orderNo;
+    private BigDecimal meterageWeightQty;
+    private BigDecimal volume;
+    private Integer quantity;
+    private List<SfFeeInfo> feeList;
+    private String productName;
+}

+ 18 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/SfPushOrderStateRequest.java

@@ -0,0 +1,18 @@
+package com.ruoyi.logistics.domain;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 顺丰订单状态回调接收参数
+ *
+ * @author RuiJing
+ * @date 2026-01-29
+ */
+@Data
+public class SfPushOrderStateRequest {
+    private String requestId;
+    private String timestamp;
+    private List<OrderStateDetail> orderState;
+}

+ 0 - 2
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/impl/BizWaybillOrderServiceImpl.java

@@ -78,9 +78,7 @@ public class BizWaybillOrderServiceImpl implements IBizWaybillOrderService
             throw new ServiceException("下单前置校验未通过! 原因:"+jsonObject.getString("msg"));
         }
         logisticsOrderService.createOrder(bizWaybillOrder);
-
         bizWaybillOrder.setCreateTime(DateUtils.getNowDate());
-
         bizWaybillOrder.setUserId(loginUser.getUserid());
         bizWaybillOrder.setDeptId(loginUser.getSysUser().getDeptId());
         return bizWaybillOrderMapper.insertBizWaybillOrder(bizWaybillOrder);

+ 104 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/impl/FeePushService.java

@@ -0,0 +1,104 @@
+package com.ruoyi.logistics.service.impl;
+
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.logistics.domain.*;
+import com.ruoyi.logistics.service.IBizInancialFeeItemsService;
+import com.ruoyi.logistics.service.IBizWaybillCostDetailsService;
+import com.ruoyi.logistics.service.IBizWaybillOrderService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.math.BigDecimal;
+import java.util.List;
+
+
+/**
+ * 费用明细回调入库
+ *
+ * @author zxf
+ * @date 2026-02-03
+ */
+
+@Service
+public class FeePushService {
+
+    @Autowired
+    private IBizWaybillOrderService bizWaybillOrderService;
+    @Autowired
+    private IBizInancialFeeItemsService bizInancialFeeItemsService;
+
+    @Autowired
+    private IBizWaybillCostDetailsService bizWaybillCostDetailsService;
+    private static final Logger logger = LoggerFactory.getLogger(FeePushService.class);
+
+    public boolean processFeePush(SfFeePushRequest request) {
+        try {
+            String waybillNo = request.getWaybillNo();
+            String orderNo = request.getOrderNo();
+            BigDecimal totalWeight = request.getMeterageWeightQty();
+            Integer quantity = request.getQuantity();
+            String productName = request.getProductName();
+            List<SfFeeInfo> feeList = request.getFeeList();
+            logger.info("开始处理运费清单 - 运单号: {}, 订单号: {}, 产品: {}, 计费重量: {}kg",
+                    waybillNo, orderNo, productName, totalWeight);
+
+            // 1. 根据订单号查询本地订单 如果不存在该订单号  则不进行明细的入库
+            BizWaybillOrder bizWaybillOrder= new BizWaybillOrder();
+            bizWaybillOrder.setWaybillNo(orderNo);
+            bizWaybillOrder.setExternalWaybillNo(waybillNo);
+            List<BizWaybillOrder> orderList=bizWaybillOrderService.selectBizWaybillOrderList(bizWaybillOrder);
+            if (orderList.size()==0) {
+                logger.error("订单不存在: {}", orderNo);
+                return false;
+            }
+            //存在的话 就去拿到这个部门id 要知道这个供应商的费率
+            Long deptId=orderList.get(0).getDeptId();
+            //先写死
+            String  Rate="1.5";
+
+
+
+            // 2. 目前订单主表不涉及费用总计啥的  先不管
+
+
+            // 3. 处理费用明细
+            if (feeList != null && !feeList.isEmpty()) {
+                for (SfFeeInfo fee : feeList) {
+                    BigDecimal feeAmt = fee.getFeeAmt();
+                    //回调回来的 费用类型  还需与系统 费用类型表-biz_inancial_fee_items
+                    String feeTypeCode = fee.getFeeTypeCode();
+                    BizInancialFeeItems bizInancialFeeItems= new BizInancialFeeItems();
+                    bizInancialFeeItems.setFeeItemCategory("sf");
+                    bizInancialFeeItems.setExtFeeCode(feeTypeCode);
+                    List<BizInancialFeeItems> feeItemsList=bizInancialFeeItemsService.selectBizInancialFeeItemsList(bizInancialFeeItems);
+                    BizWaybillCostDetails bizWaybillCostDetails= new BizWaybillCostDetails();
+                    //顺丰的费用类型代码无法在费用类型表中匹配 这里默认给值 给到外部叫什么名字
+                    bizWaybillCostDetails.setFeeName(feeItemsList.get(0).getExtFeeName());
+                    if(StringUtils.isBlank(feeItemsList.get(0).getFeeItemCode())){
+                        bizWaybillCostDetails.setFeeItemCode("00");
+                        bizWaybillCostDetails.setFeeItemName("瑞鲸-其他");
+                    }
+                    else{
+                        bizWaybillCostDetails.setFeeItemCode(feeItemsList.get(0).getFeeItemCode());
+                        bizWaybillCostDetails.setFeeItemName(feeItemsList.get(0).getFeeItemName());
+                    }
+                    bizWaybillCostDetails.setAmount(feeAmt);
+                    BigDecimal deptRate = new BigDecimal(Rate);
+                    BigDecimal result = feeAmt.multiply(deptRate);
+                    bizWaybillCostDetails.setRateAmount(result);
+                    bizWaybillCostDetails.setDeptId(deptId);
+                    // 记录费用明细
+                    bizWaybillCostDetailsService.insertBizWaybillCostDetails(bizWaybillCostDetails);
+                }
+            }
+            return true;
+
+        } catch (Exception e) {
+            logger.error("处理运费清单失败: {}", e.getMessage(), e);
+            return false;
+        }
+    }
+
+
+}