基於順豐同城介面編寫sdk,java三方sdk編寫思路

蘇小林發表於2019-04-27

由於公司外賣業務需要用到順豐的配送體系,技術上需要對接順豐 個人比較感興趣,但是順豐沒有提供sdk,所以研究下自己寫了一個

完整程式碼已上傳github ?:github.com/neatlife/sp…

技術選型

三方sdk編寫有兩種實現方式

  1. 不依賴框架,更通用,但是整合成本較高
  2. 依賴框架,比如spring boot,這樣使用起來效率更高

為了提高使用效率,這裡選擇基於spring boot框架進行編寫

前期準備

有很多基於spring boot的sdk了,骨架就不需要自行搭建了,找了下面幾個進行參考

  1. github.com/jibaole/spr…
  2. github.com/spring-proj…

研究api文件

順豐同城api文件地址:commit-openic.sf-express.com/open/api/do…

目前順豐同城的開發者api個人可以註冊,註冊後可以設定回撥地址

image-20190425171710542

找出文件中的關鍵點

在後續設計sdk上,考慮下這些點,可以讓sdk更好用

順豐狀態回撥可能會失敗,通過定時呼叫查詢訂單狀態介面可以補齊狀態

image-20190426111354187

可以實時獲取配送員的座標,這個可用在app上實時顯示配送員位置功能

image-20190426111502513

建立專案

雖然專案是作為和spring boot一起使用的,但是我們並不需要依賴完整的spring boot框架,所以建立一個maven專案就是ok的

基於順豐同城介面編寫sdk,java三方sdk編寫思路

指定groupId, ArtifactId

基於順豐同城介面編寫sdk,java三方sdk編寫思路

為了享受spring boot的自動配置,需要pom.xml裡面加上spring-boot-autoconfigure庫依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
複製程式碼

加上常用的http,lombok等庫,最終pom檔案內容如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.neatlife</groupId>
    <artifactId>sfcity</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.8</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <!-- Import dependency management from Spring Boot -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.0.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
複製程式碼

自動配置api的金鑰

把從順豐api後臺獲取的配置資訊寫入配置檔案:src/main/resources/application.properties 所有配置如下

sfcity.developer-id= xxx
sfcity.developer-key= xxx
sfcity.shop-id= xxx
sfcity.api-url= https://commit-openic.sf-express.com
複製程式碼

使用spring boot的自動配置機制,能夠很方便的從配置檔案中讀取配置 核心程式碼如下

@ConfigurationProperties(prefix = "sfcity")
@Data
public class Properties {

    private Integer developerId;

    private String developerKey;

    private String shopId;

    private String apiUrl;

}
複製程式碼

參考:

  1. src/main/java/com/github/neatlife/AutoConfiguration.java
  2. src/main/java/com/github/neatlife/Properties.java

對映請求引數和響應引數

因為要做一個通用的sdk庫,那麼所有的請求引數和響應引數都需要對映,方便使用

這裡為了演示就拿建立訂單介面舉例了

基於順豐同城介面編寫sdk,java三方sdk編寫思路

建立訂單請求實體

基於順豐同城介面編寫sdk,java三方sdk編寫思路

響應實體

基於順豐同城介面編寫sdk,java三方sdk編寫思路

還有一些關聯的實體一併建立,最終實體效果如下:

基於順豐同城介面編寫sdk,java三方sdk編寫思路

http處理工具類

使用resetTemplate進行請求,參考:juejin.im/post/5b88b1… 核心程式碼如下:

public static Response post(Integer appId, String appSecret, String url, Request request) {
    String content = JsonUtil.toJsonString(request);
    String sign = SignUtil.sign(appId.toString(), appSecret, content);

    url = url + "?sign=" + sign;

    RestTemplate restTemplate = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON_UTF8);

    HttpEntity<String> httpEntity = new HttpEntity<>(content, headers);
    ResponseEntity<String> httpResponse = restTemplate.postForEntity(url, httpEntity, String.class);

    Response response = JsonUtil.toObject(httpResponse.getBody(), Response.class);

    if (response.getErrorCode() != 0) {
        log.error("errorData: {}", response.getErrorData());
        throw new RuntimeException(response.getErrorMsg());
    }

    return response;
}
複製程式碼

json處理工具類

json處理工具類直接從自己編寫的框架裡拿,參考:github.com/neatlife/jf…

簽名工具類

順豐提供了java的簽名示例程式碼

基於順豐同城介面編寫sdk,java三方sdk編寫思路

在其基礎上修改即可,核心程式碼如下

public static String sign(String appId, String appSecret, String content) {
    // 假設原始內容JSON為 {"hello":"kitty"}
    // content : "{\"hello\":\"kitty\"}"

    String toSign = content + "&" + appId + "&" + appSecret;
    // toSign : "{\"hello\":\"kitty\"}&1234567890&0123456789abcdef0123456789abcdef";

    String md5Result = md5(toSign.getBytes(StandardCharsets.UTF_8));
    // md5Result : "ef3435b1480e553480e19e3e162fb0be"

    // signResult : "ZWYzNDM1YjE0ODBlNTUzNDgwZTE5ZTNlMTYyZmIwYmU="

    return base64Encode(md5Result.getBytes(StandardCharsets.UTF_8));
}
複製程式碼

完整程式碼參考:src/main/java/com/github/neatlife/util/SignUtil.java

定義介面常量

把需要呼叫的介面地址放到統一的常量檔案中,方便管理 核心程式碼如下:

public class ApiUrlConstant {

    private static final String CREATE_ORDER_URL = "/open/api/external/createorder";
    private static String sfCityHost;

    public static String getCreateOrderUrl() {
        return sfCityHost + CREATE_ORDER_URL;
    }

    public static void setSfCityHost(String sfCityHost) {
        ApiUrlConstant.sfCityHost = sfCityHost;
    }
}
複製程式碼

呼叫順豐建立訂單介面

上面步驟都準備完成後,進行到最重要的呼叫環節了,有了上面的準備,這一步也比較容易了 核心程式碼如下

public CreateOrderResponse createOrder(CreateOrderRequest createOrderRequest) {
    createOrderRequest.setDevId(developerId);
    createOrderRequest.setShopId(shopId);
    Response response = HttpUtil.post(
            developerId,
            developerKey,
            ApiUrlConstant.getCreateOrderUrl(),
            createOrderRequest
    );
    return JsonUtil.toObject(response.getResult(), CreateOrderResponse.class);
}
複製程式碼

編寫自動測試

建立測試檔案:src/test/java/com/github/neatlife/SfClientTest.java 填充測試資料 呼叫建立訂單方法

@Test
public void createOrder() {
    CreateOrderResponse createOrderResponse = sfClient.createOrder(createOrderRequest());

    Assert.assertNotNull(createOrderResponse.getSfOrderId());
}

private CreateOrderRequest createOrderRequest() {
    CreateOrderRequest createOrderRequest = new CreateOrderRequest();
    createOrderRequest.setShopOrderId(System.currentTimeMillis() + "");
    createOrderRequest.setOrderSource("測試");
    createOrderRequest.setPayType(1);
    createOrderRequest.setOrderTime(DateUtil.currentSecond().intValue());
    createOrderRequest.setIsAppoint(0);
    createOrderRequest.setIsInsured(0);
    createOrderRequest.setRiderPickMethod(1);
    createOrderRequest.setPushTime(DateUtil.currentSecond().intValue());
    createOrderRequest.setVersion(17);
    createOrderRequest.setShop(
            Shop.builder()
                    .shopName("店鋪名")
                    .shopPhone("13266666666")
                    .shopAddress("朝陽區高碑店鎮四惠大廈F1-008")
                    .shopLng("116.514236")
                    .shopLat("39.905328")
                    .build()
    );
    createOrderRequest.setReceive(
            Receive.builder()
                    .userName("小明")
                    .userPhone("13288888888")
                    .userPhone("北京")
                    .userLng("116.3534196")
                    .userLat("40.0159778")
                    .userAddress("朝陽區高碑店鎮四惠大廈F1-008")
                    .cityName("北京市")
                    .build()
    );
    createOrderRequest.setOrderDetail(
            OrderDetail.builder()
                    .totalPrice(100)
                    .productType(1)
                    .weightGram(500)
                    .productNum(1)
                    .productTypeNum(1)
                    .productDetail(
                            Stream.of(
                                    ProductDetail.builder()
                                            .productName("小炒肉")
                                            .productNum(1)
                                            .build()
                            ).collect(Collectors.toList())
                    )
                    .build()
    );
    return createOrderRequest;
}
複製程式碼

填充測試資料時註釋對照順豐文件,保證必填欄位都有值

檢視執行效果:

基於順豐同城介面編寫sdk,java三方sdk編寫思路

順豐返回了建立訂單成功的響應?

一些注意的點

自動配置時,把api地址注入介面常量檔案中,方便讀取

基於順豐同城介面編寫sdk,java三方sdk編寫思路

http呼叫會有失敗的可能,需要考慮進行請求補償,一般有下面兩種重試方式

  1. 定時任務,定時進行補償
  2. 使用訊息佇列的補償機制

建立外賣訂單前,可以先呼叫順豐預建立訂單核查順豐是否會接單

打包

mvn clean package -Dmaven.test.skip=true

檢視效果

基於順豐同城介面編寫sdk,java三方sdk編寫思路
然後把jar包拷到需要的專案就可以使用了

後續傳到了maven中央倉庫,也可以直接使用maven下載

在spring boot專案中使用jar包

用idea開啟spring boot專案,在專案設定的庫依賴裡引用jar包

基於順豐同城介面編寫sdk,java三方sdk編寫思路

然後在啟動類中讓spring boot掃到這個庫,就可以自動配置載入了

基於順豐同城介面編寫sdk,java三方sdk編寫思路

其它介面

基於順豐同城介面編寫sdk,java三方sdk編寫思路

按照上面建立訂單的步驟編寫+測試即可

上傳maven中央倉庫

參考: blog.csdn.net/ljbmxsm/art…

持續更新...

相關文章