如何快速写一个对接顺丰同城配送

2022-12-04 11:15:30 浏览数 (1)

前几天,公司要求我们需要去对接一个关于同城配送的功能,阿粉很荣幸,接到了这个比较简单的功能,有很多做开发的小伙伴,在对接某些开放平台的时候,会感觉到很麻木,因为感觉没啥技术含量,而且感觉还很low,实际上也不是这样,至少从代码层面,你会感觉到,有些人写的还不如你,今天阿粉就来教给大家如何去对接这个顺丰同城配送的api。

注册账号

首先我们可以先找到顺丰同城配送开放平台,顺丰同城开放平台

我们先注册一个账号,并且登录,阿粉已经登录过了,于是就可以看到下面这样的内容了。

申请测试开发者ID

这个开发者ID就是顺丰提供给你调用接口的,不管你去对接百度的API接口也好,还是你去对接腾讯的接口也好,一般都是需要你去创建一个开发者的ID,然后提供给服务商去获取一些加密的操作。

申请完成之后,这时候你实际上就可以开始看他的对接文档了,因为我们已经申请了appid和对应的key。

但是阿粉接下来没想到的时候,顺丰同城,竟然没有对接接口的Demo,只有一个模板的示例代码,阿粉就有点惊讶了,这什么鬼,Demo,看来只能细细的看这个文档了。

看这个,感觉好像不是很给力的样子,毕竟之前已经对接过百度的一些相关的接口了,都是用appid和key去服务商那边去换取一下这个token,然后每次调用接口的时候,直接把这个token带过去,让对方检验,这个顺丰倒好,直接让你把参数都得处理了,还得加密了。

好吧,那我们就按照他给的规则来呗。

先写一个这个加密的方法吧,这样每次调用接口之前,都不用再去处理了。

代码语言:javascript复制
public class SignUtil {
    /**
     * 签名方法
     * 参数:content:要发送的JSON结构字符串
     * 返回值:签名信息字符串
     * 注意:签名计算结果可以到此页面进行验证:http://sftc.jsonce.com/sign/
     */
    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));
    }

    private static String toHex(byte[] bytes) {
        final char[] hexDigits = "0123456789abcdef".toCharArray();
        StringBuilder ret = new StringBuilder(bytes.length * 2);
        for (byte aByte : bytes) {
            ret.append(hexDigits[(aByte >> 4) & 0x0f]);
            ret.append(hexDigits[aByte & 0x0f]);
        }
        return ret.toString();
    }

    private static String md5(byte[] toSignBytes) {
        try {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(toSignBytes);
            return toHex(bytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static String base64Encode(byte[] md5ResultBytes) {
        java.util.Base64.Encoder be = java.util.Base64.getEncoder();
        return be.encodeToString(md5ResultBytes);
    }
}

接下来我们就调用顺丰同城测试一个订单吧。

请求接口,那必然少不了Http的请求,我们先封装一个请求顺丰同城的http工具类,这样方便以后的调用。

代码语言:javascript复制
public class HttpUtil {

    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());
        }

        return response;
    }

}

下面就得看看他都是需要什么参数了。

我们给他创建一个实体类

代码语言:javascript复制
@Data
public class Shop {
    /**
     * string(64) 空 是 店铺名称
     */
    private String shopName;

    /**
     * string(64) 空 是 店铺电话
     */
    private String shopPhone;

    /**
     * string(255) 空 是 店铺地址
     */
    private String shopAddress;

    /**
     * string(32) 空 否 店铺经度
     */
    private String shopLng;

    /**
     * string(32) 空 否 店铺纬度
     */
    private String shopLat;
}

代码语言:javascript复制
public class Receive {
    /**
     * string(64) 空 是 用户姓名
     */
    @JsonProperty("user_name")
    private String userName;

    /**
     * string(64) 空 是 用户电话
     */
    @JsonProperty("user_phone")
    private String userPhone;

    /**
     * string(255) 空 是 用户地址
     */
    @JsonProperty("user_address")
    private String userAddress;

    /**
     * string(32) 空 是 用户经度
     */
    @JsonProperty("user_lng")
    private String userLng;

    /**
     * string(32) 空 是 用户纬度
     */
    @JsonProperty("user_lat")
    private String userLat;

    /**
     * string(32) 空 否 发单城市 用来校验是否跨城;请填写城市的中文名称,如北京市、深圳市
     */
    @JsonProperty("city_name")
    private String cityName;
}

秉着要啥给啥的原则:我们继续,

代码语言:javascript复制
@Data
public class OrderDetail {
    /**
     * int(11) 0 是 用户订单总金额(单位:分)
     */
    @JsonProperty("total_price")
    private Integer totalPrice;

    /**
     * int(11) 0 是 物品类型 1快餐;2送药;3百货;
     * 4脏衣服收;5干净衣服派;6生鲜;
     * 7保单;8饮品;9现场勘查;
     * 10快递;12文件证照;13蛋糕;
     * 14鲜花;15电子数码;16服装鞋帽;
     * 17汽车配件;18珠宝;20披萨;
     * 21中餐;99其他
     */
    @JsonProperty("product_type")
    private Integer productType;

    /**
     * int(11) 0 否 实收用户金额(单位:分)
     */
    @JsonProperty("user_money")
    private Integer userMoney;

    /**
     * int(11) 0 否 实付商户金额(单位:分)
     */
    @JsonProperty("shop_money")
    private Integer shopMoney;

    /**
     * int(11) 0 是 物品重量(单位:克)
     */
    @JsonProperty("weight_gram")
    private Integer weightGram;

    /**
     * int(11) 0 否 物品体积(单位:升)
     */
    @JsonProperty("volume_litre")
    private Integer volumeLitre;

    /**
     * int(11) 0 否 商户收取的配送费(单位:分)
     */
    @JsonProperty("delivery_money")
    private Integer deliveryMoney;

    /**
     * int(11) 0 是 物品个数
     */
    @JsonProperty("product_num")
    private Integer productNum;

    /**
     * int(11) 0 是 物品种类个数
     */
    @JsonProperty("product_type_num")
    private Integer productTypeNum;

    /**
     * Array 空 是 物品详情;数组结构,详见product_detail结构
     */
    @JsonProperty("product_detail")
    private List<ProductDetail> productDetail;
}

代码语言:javascript复制
public class ProductDetail {
    /**
     * string(64) 空 是 物品名称
     */
    @JsonProperty("product_name")
    private String productName;

    /**
     * int(11) 0 否 物品ID
     */
    @JsonProperty("product_id")
    private String productId;

    /**
     * int(11) 0 是 物品数量
     */
    @JsonProperty("product_num")
    private Integer productNum;

    /**
     * int(11) 0 否 物品价格
     */
    @JsonProperty("product_price")
    private String productPrice;

    /**
     * string(64) 空 否 物品单位
     */
    @JsonProperty("product_unit")
    private String productUnit;

    /**
     * string(256) 空 否 备注
     */
    @JsonProperty("product_remark")
    private String productRemark;

    /**
     * string(1024) 空 否 详情
     */
    @JsonProperty("item_detail")
    private String itemDetail;

}

参数我们都拼接完活了,那是不是就应该去写一个Demo试试,看看我们创建的这些类都好用不好用?

接下来我们再创建一个请求地址的类,这样就可以方便我们调用了。

代码语言:javascript复制
public class ApiUrlConstant {

    private static final String sfCityHost = "https://openic.sf-express.com";

    /**
     * 创建订单的URL
     */
    private static final String CREATE_ORDER_URL = "/open/api/external/createorder";

    /**
     * 预创建订单的URL
     */
    private static final String PRE_CREATE_ORDER_URL = "/open/api/external/precreateorder";

    /**
     * 订单实时信息查询
     */
    private static final String GET_ORDER_STATUS = "/open/api/external/getorderstatus";

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

    public static String getPreCreateOrderUrl() {
        return sfCityHost   PRE_CREATE_ORDER_URL;
    }

    public static String getOrderStatusUrl(){
        return sfCityHost   GET_ORDER_STATUS;
    }

}

这时候我们再封装一个请求的request类,

代码语言:javascript复制
public class CreateOrderRequest extends Request {
    // int(11) 0 是 同城开发者ID 可在顺丰同城开放平台上自助申请
    @JsonProperty("dev_id")
    private Integer devId;

    //  string(64) 0 是 店铺ID
    @JsonProperty("shop_id")
    private String shopId;

    //  int(11) 1 否 店铺ID类型 1:顺丰店铺ID ;2:接入方店铺ID
    @JsonProperty("shop_type")
    private Integer shopType;

    /**
     * string(128) 空 是 商家订单号 不允许重复
     */
    @JsonProperty("shop_order_id")
    private String shopOrderId;

    /**
     * string(12) 空 是 订单接入来源 1:美团;2:饿了么;3:百度;4:口碑;其他请直接填写中文字符串值
     */
    @JsonProperty("order_source")
    private String orderSource;

    /**
     * string(12) 空 否 取货序号 与order_source配合使用
     * 如:饿了么10号单,表示如下:
     * order_source=2;order_sequence=10。
     * 用于骑士快速寻找配送物
     */
    @JsonProperty("order_sequence")
    private String orderSequence;

    /**
     * int(2) 2 否 坐标类型 1:百度坐标,2:高德坐标
     */
    @JsonProperty("lbs_type")
    private Integer lbsType;

    /**
     * int(11) 1 是 用户支付方式 1:已付款 0:货到付款
     */
    @JsonProperty("pay_type")
    private Integer payType;

    /**
     * int(11) 0 否 代收金额 单位:分
     */
    @JsonProperty("receive_user_money")
    private Integer receiveUserMoney;

    /**
     * int(11) 0 是 用户下单时间 秒级时间戳
     */
    @JsonProperty("order_time")
    private Integer orderTime;

    /**
     * int(2) 0 是 是否是预约单 0:非预约单;1:预约单
     */
    @JsonProperty("is_appoint")
    private Integer isAppoint;

    /**
     * int(11) 0 否 用户期望送达时间 预约单需必传,秒级时间戳
     */
    @JsonProperty("expect_time")
    private Integer expectTime;
    /**
     * int(11) 0 是 是否保价,0:非保价;1:保价
     */
    @JsonProperty("is_insured")
    private Integer isInsured;

    /**
     * int(11) 0 否 保价金额 单位:分
     */
    @JsonProperty("declared_value")
    private Integer declaredValue;

    /**
     * string(1024) 空 否 订单备注
     */
    @JsonProperty("remark")
    private String remark;

    /**
     * int(11) 1 否 物流流向 1:从门店取件送至用户;
     * 2:从用户取件送至门店
     */
    @JsonProperty("rider_pick_method")
    private Integer riderPickMethod;

    /**
     * int 1 否 返回字段控制标志位(二进制) 1:价格,2:距离,4:重量,组合条件请相加
     * 例如全部返回为填入7
     */
    @JsonProperty("return_flag")
    private Integer returnFlag;

    /**
     * int(11) 0 是 推单时间 秒级时间戳
     */
    @JsonProperty("push_time")
    private Integer pushTime;

    /**
     * int(11) 0 是 版本号 参照文档主版本号填写
     * 如:文档版本号1.7,version=17
     */
    @JsonProperty("version")
    private Integer version;

    /**
     * OBJ 空 是 收货人信息 Obj,详见receive结构
     */
    @JsonProperty("receive")
    private Receive receive;

    /**
     * OBJ 空 否 发货店铺信息 Obj,详见shop结构,
     * 平台级开发者(如饿了么)需传入
     * 如无特殊说明此字段可忽略
     */
    @JsonProperty("shop")
    private Shop shop;

    /**
     * OBJ 空 是 订单详情 Obj,详见order_detail结构
     */
    @JsonProperty("order_detail")
    private OrderDetail orderDetail;

    /**
     * 是否优先派单,0:否 1:是
     */
    @JsonProperty("is_priority_assign")
    private Integer isPriorityAssign;


    /**
     * 是否是专人直送订单,0:否;1:是
     */
    @JsonProperty("is_person_direct")
    private Integer isPersonDirect;

}

这样就万事大吉了,我们只需要拼接参数,然后调用一下了!

拼接参数,请求调用:

代码语言:javascript复制
CreateOrderRequest createOrderRequest = new CreateOrderRequest();

        createOrderRequest.setDevId(developerId);
        createOrderRequest.setShopId(shopId);
        createOrderRequest.setShopType(2);
        createOrderRequest.setShopOrderId(UUIDUtil.getUUID());
        createOrderRequest.setOrderSource("美团");
        createOrderRequest.setLbsType(2);
        createOrderRequest.setPayType(1);
        LocalDateTime now = LocalDateTime.now();
        long l = now.atZone(ZoneOffset.systemDefault()).toEpochSecond();
//        createOrderRequest.setOrderTime(DateUtil.currentSecond().intValue());
        createOrderRequest.setOrderTime(Integer.parseInt(String.valueOf(l)));
        createOrderRequest.setIsAppoint(0);
        createOrderRequest.setIsInsured(0);
        createOrderRequest.setIsPriorityAssign(0);
        createOrderRequest.setIsPersonDirect(0);
        createOrderRequest.setRiderPickMethod(1);
        createOrderRequest.setPushTime(DateUtil.currentSecond().intValue());
        createOrderRequest.setVersion(17);
        //初始化门店
        Shop shop = new Shop();
        shop.setShopName("顺丰同城开放平台");
        shop.setShopPhone("18315652836");
        shop.setShopLat("116.377441");
        shop.setShopLat("40.032368");

        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setTotalPrice(100);
        orderDetail.setProductType(1);
        orderDetail.setWeightGram(500);
        orderDetail.setProductNum(0);
        orderDetail.setProductTypeNum(1);

        ProductDetail productDetail = new ProductDetail();
        productDetail.setProductName("奶香提子麻薯");
        productDetail.setProductNum(1);
        List<ProductDetail> productDetails = new ArrayList<>();
        orderDetail.setProductDetail(productDetails);

        //初始化订单

        //初始化接收者
        Receive receive = Receive.builder()
                .userName("开发人员")
                .userPhone("15153221122")
                .userLng("116.4255952835083")
                .userLat("39.92558135671958")
                .userAddress("北京市海淀区")
                .cityName("北京市")
                .build();

        createOrderRequest.setShop(shop);
        createOrderRequest.setReceive(receive);
        createOrderRequest.setOrderDetail(orderDetail);
        
        HttpUtil.post(
                    developerId,
                    developerKey,
                    ApiUrlConstant.getCreateOrderUrl(),
                    createOrderRequest
            );

我们用postman试一下,看看能不能创建订单成功?

代码语言:javascript复制
{
    "pushTime": 1652863523,
    "sfBillId": "SF6509777913611",
    "sfOrderId": "3466306039564105729",
    "shopOrderId": "3aea9e25b23140068ecc7aefac389f23",
    "totalPrice": 1
} 

请求通过,有返回值了,并且他爸订单号都给我们了,这是不是就成功了呢?

看来对接这个同城的API 也是非常简单的了,毕竟创建订单需要的参数是非常麻烦的,当你静下心弄得时候,发现很快就完事了。你学会了么?

0 人点赞