java docking with SF Express for shipping operations (using RestTemplate request)

Relevant information

SF Express unified access platform: https://freight.sf-express.com/api/api.html#id=28

SF Express online query logistics information address: https://www.sf-express.com/cn/sc/dynamic_function/waybill/#search/bill-number/SF1193907051991

SF1193907051991] is the courier number -> modify it to your own

Insert picture description here

1. SF api interface request core class

Insert picture description here

1. SF Express requests tool SFUtil

package com.ws.ldy.others.kuaidi.sf.util;

import com.alibaba.fastjson.JSON;
import com.ws.ldy.config.error.ErrorException;
import com.ws.ldy.others.kuaidi.sf.entity.SFReturnData;
import com.ws.ldy.others.kuaidi.sf.entity.SFReturnError;
import com.ws.ldy.others.kuaidi.sf.entity.cancel.request.RequestCancel;
import com.ws.ldy.others.kuaidi.sf.entity.cancel.response.ResponseCancel;
import com.ws.ldy.others.kuaidi.sf.entity.send.request.RequestCreate;
import com.ws.ldy.others.kuaidi.sf.entity.send.response.ResponseCreate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import sun.misc.BASE64Encoder;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 顺丰快递 Api 接口对接, 官网文档-零担下单: https://freight.sf-express.com/api/api.html#id=30
 * <P>
 * 请求头必须添加: "Content-type","application/x-www-form-urlencoded;charset=UTF-8”
 * </P>
 * @author wangsong
 * @mail [email protected]
 * @date 2020/9/9 0009 9:38
 * @version 1.0.0
 */
@SuppressWarnings("all")
@Component
@Slf4j
public class SFUtil {

    @Autowired
    private RestTemplate restTemplate;

    //测试地址
    private static final String TEST_URL = "https://sfapi.sit.sf-express.com:45273/std/service";
    private static final String TEST_PARTNER_ID = "fop_test";
    private static final String TEST_MD5_KEY = "2Z7YPBNG2CKKBV17";
    //正式地址 final
    private static final String SF_URL = "https://sfapi.sf-express.com/std/service";
    private static final String SF_PARTNER_ID = "fop_test";
    private static final String SF_MD5_KEY = "2Z7YPBNG2CKKBV17";


    /**
     * 顺丰寄件下单
     * @author wangsong
     * @author requestCreate 下单参数
     * @date 2020/9/15 0015 15:42
     * @return ResponseCreate
     * @version 1.0.0
     */
    public ResponseCreate fopReceLtlCreateOrder(RequestCreate requestCreate) {
        String mgsData = JSON.toJSONString(requestCreate);
        //
        SFReturnData sfReturnData = http("FOP_RECE_LTL_CREATE_ORDER", mgsData);
        //
        if (!"A1000".equals(sfReturnData.getApiResultCode())) {
            //失败
            log.info(sfReturnData.toString());
            throw new ErrorException(10099, sfReturnData.getApiErrorMsg());
        }
        String apiResultData = sfReturnData.getApiResultData();
        ResponseCreate responseCreate = JSON.parseObject(apiResultData, ResponseCreate.class);
        if (!responseCreate.isSuccess()) {
            //失败
            log.info(sfReturnData.toString());
            SFReturnError sfReturnError = JSON.parseObject(apiResultData, SFReturnError.class);
            throw new ErrorException(10099, sfReturnError.getErrorMessage());
        }
        return responseCreate;
    }


    /**
     * 顺丰取消订单
     * @author wangsong
     * @author cancel 下单参数
     * @date 2020/9/15 0015 15:42
     * @return 取消结果信息
     * @version 1.0.0
     */
    public ResponseCancel fopReceLtlCancelOrder(RequestCancel cancel) {
        //
        String mgsData = JSON.toJSONString(cancel);
        //
        SFReturnData sfReturnData = http("FOP_RECE_LTL_CANCEL_ORDER", mgsData);
        if (!"A1000".equals(sfReturnData.getApiResultCode())) {
            //失败
            log.info(sfReturnData.toString());
            throw new ErrorException(10099, sfReturnData.getApiErrorMsg());
        }
        String apiResultData = sfReturnData.getApiResultData();
        ResponseCancel responseCancel = JSON.parseObject(apiResultData, ResponseCancel.class);
        if (!responseCancel.getSuccess()) {
            log.info(sfReturnData.toString());
            SFReturnError sfReturnError = JSON.parseObject(apiResultData, SFReturnError.class);
            throw new ErrorException(10099, sfReturnError.getErrorMessage());
        }
        return responseCancel;
    }

    /**
     * 下单结果查询
     * @author wangsong
     * @author orderId 寄件商家的订单Id(非寄件单号)
     * @date 2020/9/15 0015 15:42
     * @return 同下单相同数据
     * @version 1.0.0
     */
    public ResponseCreate fopReceLtlGetOrderResult(String orderId) {
        //
        Map<String,String> param = new HashMap<>();
        param.put("orderId",orderId);
        String mgsData = JSON.toJSONString(param);
        //
        SFReturnData sfReturnData = http("FOP_RECE_LTL_GET_ORDER_RESULT", mgsData);
        //
        if (!"A1000".equals(sfReturnData.getApiResultCode())) {
            //失败
            log.info(sfReturnData.toString());
            throw new ErrorException(10099, sfReturnData.getApiErrorMsg());
        }
        String apiResultData = sfReturnData.getApiResultData();
        ResponseCreate responseCreate = JSON.parseObject(apiResultData, ResponseCreate.class);
        if (!responseCreate.isSuccess()) {
            //失败
            log.info(sfReturnData.toString());
            SFReturnError sfReturnError = JSON.parseObject(apiResultData, SFReturnError.class);
            throw new ErrorException(10099, sfReturnError.getErrorMessage());
        }
        return responseCreate;
    }

    /**
     *
     * @param serviceCode 请求接口
     * @param mgsData 请求参数: json 参数
     *   <P>
     * //  1*	下快运订单	FOP_RECE_LTL_CREATE_ORDER
     * //  2*	取消订单   	FOP_RECE_LTL_CANCEL_ORDER
     * //  3	筛单结果查询	FOP_RECE_LTL_SEARCH_ORDER
     * //  4	路由查询 	FOP_RECE_LTL_SEARCH_ROUTER
     * //  5	路由推送	    FOP_PUSH_LTL_ROUTER
     * //  6	清单费用推送	FOP_PUSH_LTL_FEE
     * //  7	清单费用查询	FOP_RECE_LTL_QUERY_FEE
     * //  8*	下单结果查询	FOP_RECE_LTL_GET_ORDER_RESULT
     * //  9	注册路由	    FOP_RECE_LTL_REGISTER_ROUTER
     *   </P>
     * @return SFReturnData
     */
    private SFReturnData http(String serviceCode, String mgsData) {
        String timestamp = System.currentTimeMillis() + "";
        // 发送快递参数处理
        MultiValueMap<String, Object> sendBody = new LinkedMultiValueMap<>();
        sendBody.add("partnerID", TEST_PARTNER_ID);                   // 合作伙伴编码(由顺丰分配)
        sendBody.add("requestID", UUID.randomUUID().toString());      // 请求唯一号UUID
        sendBody.add("serviceCode", serviceCode);                     // 接口服务代码取消订单
        sendBody.add("timestamp", timestamp);                         // 调用接口时间戳
        sendBody.add("msgDigest", genDigest(timestamp, mgsData, TEST_MD5_KEY));  // 数字签名
        sendBody.add("msgData", mgsData);                                        // 业务数据报文
        //设置请求头参数
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=UTF-8");
        //发送请求
        HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<>(sendBody, headers);
        ResponseEntity<String> result = restTemplate.postForEntity(TEST_URL, formEntity, String.class);
        //
        SFReturnData sfReturnData = JSON.parseObject(result.getBody(), SFReturnData.class);
        return sfReturnData;
    }


    /**
     * 业务数据加密
     * @param timestamp
     * @param mgsData
     * @param md5key
     * @return
     * @throws Exception
     */
    private String genDigest(String timestamp, String mgsData, String md5key) {
        //将业务报文+时间戳+秘钥组合成需加密的字符串(注意顺序)
        String toVerifyText = mgsData + timestamp + md5key;
        //因业务报文中可能包含加号、空格等特殊字符,需要urlEnCode处理
        try {
            toVerifyText = URLEncoder.encode(toVerifyText, "UTF-8");
            //进行Md5加密
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(toVerifyText.getBytes("UTF-8"));
            byte[] md = md5.digest();
            //通过BASE64生成数字签名
            String msgDigest = new String(new BASE64Encoder().encode(md));
            return msgDigest;
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}

2. Provide interface SFService

/**
 * 顺丰快递
 * @author wangsong
 * @mail [email protected]
 * @date 2020/9/15 0015 17:22
 * @version 1.0.0
 */
public interface SFService {

    public String sendSF(RequestCreate en, String productName);

    public String cancelSF(String orderId);

    public ResponseCreate getOrderSF(String orderId);


}

3. The interface implements SFServiceImpl

package com.ws.ldy.others.kuaidi.sf.service.impl;

import com.ws.ldy.others.kuaidi.sf.entity.cancel.request.RequestCancel;
import com.ws.ldy.others.kuaidi.sf.entity.cancel.response.ResponseCancel;
import com.ws.ldy.others.kuaidi.sf.entity.send.request.CargoList;
import com.ws.ldy.others.kuaidi.sf.entity.send.request.RequestCreate;
import com.ws.ldy.others.kuaidi.sf.entity.send.response.ResponseCreate;
import com.ws.ldy.others.kuaidi.sf.service.SFService;
import com.ws.ldy.others.kuaidi.sf.util.SFUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


/**
 * 顺丰
 * @author wangsong
 * @mail [email protected]
 * @date 2020/9/15 0015 17:24 
 * @version 1.0.0
 */
@Service
public class SFServiceImpl implements SFService {

    @Autowired
    private SFUtil sfUtil;

    /**
     * 顺丰下单 /返回单号
     * @author wangsong
     * @mail [email protected]
     * @date 2020/9/15 0015 19:36
     * @version 1.0.0
     */
    @Override
    public String sendSF(RequestCreate en, String productName) {

        // 付款方式(邮费): 1.寄方付 2.收方付 3.第三方付(默认收件方付)
        en.setPayMethod("2");
        // 货物明细
        CargoList cargoList = new CargoList();
        cargoList.setName(productName);
        en.setCargoList(cargoList);
        ResponseCreate responseCreate = sfUtil.fopReceLtlCreateOrder(en);
        return responseCreate == null ? null : responseCreate.getObj().getWaybillNo();
    }


    /**
     * 顺丰取消订单
     * @author wangsong
     * @mail [email protected]
     * @date 2020/9/15 0015 19:36
     * @version 1.0.0
     */
    @Override
    public String cancelSF(String orderId) {
        RequestCancel requestCancel = new RequestCancel();
        requestCancel.setOrderid(orderId);  //客户订单号
        requestCancel.setCancelType("1");  //传1取消后,orderId可重复利用
        ResponseCancel responseCancel = sfUtil.fopReceLtlCancelOrder(requestCancel);
        return responseCancel == null ? null : responseCancel.getObj();
    }


    /**
     * 顺丰查询订单
     * @author wangsong
     * @mail [email protected]
     * @date 2020/9/15 0015 19:36
     * @version 1.0.0
     */
    @Override
    public ResponseCreate getOrderSF(String orderId) {
        ResponseCreate responseCreate = sfUtil.fopReceLtlGetOrderResult(orderId);
        return responseCreate;
    }
}

4. Provide test interface

   @ApiOperation("发货测试")
    @RequestMapping(value = "/sendTest", method = RequestMethod.GET)
    public R<String> findTable(String orderId) {
        //
        RequestCreate en = new RequestCreate();
        en.setOrderid(orderId); // 订单Id
        en.setSendContact("王松");            // 寄件方联系人
        en.setSendMobile("17628689969");     // 寄件方电话
        en.setSendProvince("四川省");         // 寄件方所在省级行政区名称,必须是标准的省级行政区名称,如:北京、广东省、广西壮族自治区等;
        en.setSendCity("成都市");             // 寄件方所在地级行政区名称,必须是标准的城市称谓,   如:北京市、深圳市、大理白族自治州等;
        en.setSendCounty("青羊区");           // 寄件人所在县/区级行政区名称必须是标准的县/区称谓,   如:福田区,南涧彝族自治县、准格尔旗等。
        en.setSendAddress("新洲十一街万基商务大厦10楼。");    // 寄件人详细地址(请勿包含省市区),        如:新洲十一街万基商务大厦10楼。
        //
        en.setDeliveryContact("王松");     // 到件方联系人
        en.setDeliveryMobile("17628689969");
        en.setDeliveryProvince("四川省");  // 到件方所在省级行政区名称,必须是标准的省级行政区名称 如:北京、广东省、广西壮族自治区等;
        en.setDeliveryCity("成都市");      // 到件方所在地级行政区名称,必须是标准的城市称谓
        en.setDeliveryCounty("武侯区");    // 到件方所在县/区级行政区名称,必须是标准的县/区称谓, 如:福田区,南涧彝族自治县、准格尔旗等
        en.setDeliveryAddress("新洲十一街万基商务大厦11楼");// 到件方详细地址(请勿包含省市区),   如:新洲十一街万基商务大厦10楼。
        //
        return R.success(sfService.sendSF(en, "测试商品"));
    }


Two, entity class encapsulation

1. Back

1.1, return SFReturnError correctly and uniformly

package com.ws.ldy.others.kuaidi.sf.entity;


import com.ws.ldy.others.base.model.Convert;
import lombok.Data;
import lombok.ToString;

/**
  * 统一返回正确获取对应数据时错误
  * @author wangsong
  * @mail  [email protected]`在这里插入代码片`
  * @date  2020/9/15 0015 20:29
  * @version 1.0.0
  */
@Data
@ToString
public class SFReturnError extends Convert {

    private String errorCode;

    private String errorMessage;

    private Boolean success;
}

1.2, error unified return SFReturnData

package com.ws.ldy.others.kuaidi.sf.entity;


import com.ws.ldy.others.base.model.Convert;
import lombok.Data;
import lombok.ToString;

/**
 * 顺丰请求的-统一返回数据格式
 */
@Data
@ToString
public class SFReturnData extends Convert {

    private String apiErrorMsg;
    private String apiResponseID;
    private String apiResultCode;
    private String apiResultData;

}

2. Sending request

2.1, request required parameters RequestCreate

/**
 * Copyright 2020 bejson.com
 */
package com.ws.ldy.others.kuaidi.sf.entity.send.request;

import com.ws.ldy.others.base.model.Convert;
import lombok.Data;
import lombok.ToString;


/**
 * 顺丰寄件必填参数的请求参数(当前系统使用参数)
 * @author wangsong
 * @date 2020/9/15 0015 16:05
 * @return
 * @version 1.0.0
 */
@Data
@ToString
public class RequestCreate extends Convert {

    private String orderid;           // 订单Id
    // 寄件方
    private String sendContact;       // 寄件方联系人
    private String sendMobile;        // 寄件方电话
    private String sendProvince;      // 寄件方所在省级行政区名称,必须是标准的省级行政区名称,如:北京、广东省、广西壮族自治区等;
    private String sendCity;          // 寄件方所在地级行政区名称,必须是标准的城市称谓,   如:北京市、深圳市、大理白族自治州等;
    private String sendCounty;        // 寄件人所在县/区级行政区名称必须是标准的县/区称谓,   如:福田区,南涧彝族自治县、准格尔旗等。
    private String sendAddress;       // 寄件人详细地址(请勿包含省市区),        如:新洲十一街万基商务大厦10楼。
    // 到件方
    private String deliveryContact;   // 到件方联系人
    private String deliveryMobile;    // 到件方电话
    private String deliveryProvince;  // 到件方所在省级行政区名称,必须是标准的省级行政区名称 如:北京、广东省、广西壮族自治区等;
    private String deliveryCity;      // 到件方所在地级行政区名称,必须是标准的城市称谓
    private String deliveryCounty;    // 到件方所在县/区级行政区名称,必须是标准的县/区称谓, 如:福田区,南涧彝族自治县、准格尔旗等
    private String deliveryAddress;   // 到件方详细地址(请勿包含省市区),   如:新洲十一街万基商务大厦10楼。
    //
    private String payMethod;         // 付款方式(邮费): 1.寄方付   2.收方付     3.第三方付
    private CargoList cargoList;      // 货物明细,-非必填 参见Cargo
    private String remark;            // 下单备注
    private String pickUpMode;         // 取件方式 1. 客户自送 2 上门接货。默认为2上门接货。
    private String expectedPickUpTime;  //  希望上门取件时间。 格式:yyyy-MM-dd HH:mm:ss

}

2.2. Required for request-sub-parameter CargoList

/**
  * 货物信息
  * @author wangsong
  * @mail  [email protected]
  * @date  2020/9/15 0015 20:13
  * @version 1.0.0
  */
@Data
@ToString
public class CargoList {

    private String name;
    private int count;
    private String unit;
    private int length;
    private int width;
    private double amount;
    private String currency;
    private String sourcearea;
    private String productrecordno;
    private String goodPrepardNo;
    private String taxNo;
    private String hsCode;
    private double volume;
    private int boxno;
    private int installcargotype;
    private int height;
    private int weight;
}

2.3, response parameter ResponseCreate

/**
  * 顺丰快递统一响应成功 SFReturnData 下的 apiResultData 内数据
  * @author wangsong
  * @mail  [email protected]
  * @date  2020/9/15 0015 20:15
  * @version 1.0.0
  */
@Data
@ToString
public class ResponseCreate extends Convert {

    private Obj obj;
    private boolean success;
}

2.4, response sub-parameter Obj

@Data
@ToString
public class Obj extends Convert {

    private String destCode;
    private String filterResult;
    private String orderId;
    private RlsInfo rlsInfo;
    private String waybillNo;  //顺丰订单号

}

2.5, response sub-parameter RlsDetail

@Data
@ToString
public class RlsDetail extends Convert {

    private String abFlag;
    private String cargoTypeCode;
    private String codingMapping;
    private String codingMappingOut;
    private String deliveryMode;
    private String destCityCode;
    private String destDeptCode;
    private String destDeptCodeMapping;
    private String destRouteLabel;
    private String destTeamCode;
    private String destTeamCodeMapping;
    private String destTransferCode;
    private String destinationStationCode;
    private String errMsg;
    private String expressTypeCode;
    private String fopIcon;
    private String goodsNumber;
    private String limitTypeCode;
    private String newIcon;
    private String printFlag;
    private String printIcon;
    private String proCode;
    private String proName;
    private String routeArray;
    private String sendAreaCode;
    private String sourceCityCode;
    private String sourceDeptCode;
    private String sourceTeamCode;
    private String sourceTransferCode;
    private String sxCompany;
    private String twoDimensionCode;
    private String waybillNo;
    private String xbFlag;
}

2.6, response sub-parameter RlsInfo

@Data
@ToString
public class RlsInfo extends Convert {

    private RlsDetail rlsDetail;

}

3. Cancel the shipment

3.1, request RequestCancel

@Data
@ToString
public class RequestCancel extends Convert {

    private String orderid;             // 客户订单号
    private String cancelType;          // 传1取消后,orderId可重复利用

}

3.2, response to ResponseCancel

/**
 * 顺丰快递统一响应成功 SFReturnData 下的 apiResultData 内数据
 * @author wangsong
 * @date 2020/9/15 0015 16:05
 * @return
 * @version 1.0.0
 */
@Data
@ToString
public class ResponseCancel extends Convert {

    private String errorCode;      // 错误代码
    private String errorMessage;   // 错误描述
    private Boolean success;       // 是否成功
    private String obj;            // 返回数据 //orderId	String(64)	R	客户订单号

}
  • Personal open source project (universal background management system) –> https://gitee.com/wslxm/spring-boot-plus2 , you can check it out if you like

  • This is the end of this article. If you find it useful, please like or pay attention to it. We will continue to update more content from time to time... Thank you for watching!

Guess you like

Origin blog.csdn.net/qq_41463655/article/details/108631569