Since the delivery business of the company needs the delivery system of SF Express, SF Express is interested in technical connection, but SF Express does not provide SDK, so I wrote one after studying

The complete code has been uploaded to github 🎉 : github.com/neatlife/sp…

Technology selection

There are two implementations of the tripartite SDK

  1. Framework independent, more general, but more costly to integrate
  2. Relying on frameworks, such as Spring Boot, is more efficient to use

In order to improve efficiency, we choose to write based on spring Boot framework

preparation

There are many SDK based on Spring Boot, skeleton does not need to build, find the following for reference

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

Study the API documentation

Sf Express City API documentation address: commit-openic.sf-express.com/open/api/do…

At present, the developer API of SF City can be registered by individuals, and the callback address can be set after registration

Identify key points in the document

Consider these points as you design your SDK later to make it more useful

The sf status callback may fail, and the status can be completed by periodically calling the order status query interface

The coordinates of deliverers can be obtained in real time, which can be used to display the location of deliverers in real time on the APP

Create a project

Although the project is intended to be used with Spring Boot, we do not need to rely on the full Spring Boot framework, so creating a Maven project is ok

Specify the groupId, ArtifactId

To enjoy the automatic configuration of Spring Boot, you need to add the spring-boot-Autoconfigure library dependency to POM.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
Copy the code

Add common HTTP, Lombok and other libraries, the final POM file content is as follows

<?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 the 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>
Copy the code

Automatically configure the API key

To get the configuration information from the API background motion to write configuration files: SRC/main/resources/application. The properties of all configuration is as follows

sfcity.developer-id= xxx
sfcity.developer-key= xxx
sfcity.shop-id= xxx
sfcity.api-url= https://commit-openic.sf-express.com
Copy the code

Using the automatic configuration mechanism of Spring Boot, you can easily read the configuration core code from the configuration file as follows

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

    private Integer developerId;

    private String developerKey;

    private String shopId;

    private String apiUrl;

}
Copy the code

Reference:

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

Map request parameters and response parameters

Because you want to make a common SDK library, all the request parameters and response parameters need to be mapped for easy use

Here is an example of creating an order interface for demonstration purposes

Create the order request entity

In response to the entity

Some related entities are created at the same time, and the resulting entity looks like this:

HTTP processing utility class

Use the resetTemplate to make a request, see juejin.cn/post/684490… The core code is as follows:

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;
}
Copy the code

Json processing utility class

Json processing tool class directly from the framework written by myself, refer to: github.com/neatlife/jf…

Signature utility class

Sf express provides Java signature sample code

Modify on its basis, the core code is as follows

public static String sign(String appId, String appSecret, String content) {
    // Assume the original JSON is {"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));
}
Copy the code

Complete code reference: SRC/main/Java/com/lot/neatlife/util/SignUtil Java

Defining interface constants

Put the interface address to be called into a uniform constant file for easy management of the core code as follows:

public class ApiUrlConstant {

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

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

    public static void setSfCityHost(String sfCityHost) { ApiUrlConstant.sfCityHost = sfCityHost; }}Copy the code

Call SF express create order interface

After the above steps are ready to complete, the most important call link, with the above preparation, this step is relatively easy core code is as follows

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);
}
Copy the code

Writing automated tests

Create a test file: SRC/test/Java/com/lot/neatlife/SfClientTest Java filling test data method calls to create order

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

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

private CreateOrderRequest createOrderRequest(a) {
    CreateOrderRequest createOrderRequest = new CreateOrderRequest();
    createOrderRequest.setShopOrderId(System.currentTimeMillis() + "");
    createOrderRequest.setOrderSource("Test");
    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("Shop Name")
                    .shopPhone("13266666666")
                    .shopAddress(F1-008, Sihui Building, Gaobeidian Town, Chaoyang District)
                    .shopLng("116.514236")
                    .shopLat("39.905328")
                    .build()
    );
    createOrderRequest.setReceive(
            Receive.builder()
                    .userName("Xiao Ming")
                    .userPhone("13288888888")
                    .userPhone("Beijing")
                    .userLng("116.3534196")
                    .userLat("40.0159778")
                    .userAddress(F1-008, Sihui Building, Gaobeidian Town, Chaoyang District)
                    .cityName("Beijing")
                    .build()
    );
    createOrderRequest.setOrderDetail(
            OrderDetail.builder()
                    .totalPrice(100)
                    .productType(1)
                    .weightGram(500)
                    .productNum(1)
                    .productTypeNum(1)
                    .productDetail(
                            Stream.of(
                                    ProductDetail.builder()
                                            .productName("Stir-fried pork.")
                                            .productNum(1)
                                            .build()
                            ).collect(Collectors.toList())
                    )
                    .build()
    );
    return createOrderRequest;
}
Copy the code

When filling the test data, annotate the sf document to ensure that the required fields have values

View the running effect:

Sf Express returns a successful order creation response 😄

A couple of points to note

Automatic configuration, the API address into the interface constant file, easy to read

HTTP calls may fail, so you need to compensate for the request. Generally, the following two retry methods are available

  1. Scheduled task, scheduled compensation
  2. Compensation mechanism using message queues

Before creating a takeout order, you can call SF Pre-created order to check whether SF will accept the order

packaging

mvn clean package -Dmaven.test.skip=true

See the effect

This is then uploaded to the Maven central repository and can also be downloaded directly using Maven

Use jar packages in spring Boot projects

Open the Spring Boot project with idea and reference the JAR package in the library dependencies of the project Settings

Then have Spring Boot scan the library in the boot class to automatically configure the load

Other interface

Write the + test as you did above to create the order

Upload the Maven central repository

Reference: blog.csdn.net/ljbmxsm/art…

Continuously updated…