關於調用三方平台接口與推送接口的總結(2020.7.25)


前言:這兩個星期一直在寫公司項目里的千里眼系統,這個系統主要負責的就是將各個平台的快遞接收與跟單推送與公司的WMS倉儲系統跟維密客服系統對接起來。詳細的內容我就不做過多闡述,寫下這篇博客主要的原因也就是為了把這兩個星期所學的記錄一下,另外也方便以后的回顧復習。

 

正文開始:

既然是對接快遞平台,那么無非就有兩種對接的模式。第一種,是本方去調用第三方的接口,例如快遞查詢接口、路由查詢接口、路由訂閱接口等。這些都需要本方主動去請求三方提供的url,按照三方要去的請求參數來推送參數獲得數據。第二種,與第一種相反,是三方調我們的接口。例如路由推送接口等。這種模式需要本方封裝一個接口,並提供地址給第三方,然后第三方來調我們自己的接口從而實現我們封裝的接口實現的邏輯。

那么我們首先說說第一種,本方調用三方接口。

一、本方調用三方接口

1、第一步,我們首先要做的,是先將http請求的基本框架搭好,那么開始設計吧(提醒一下,為了便於理解,這里我以申通平台為例,申通的官方api地址:https://open.sto.cn/#/apiDocument/STO_TRACE_QUERY_COMMON  參照這來看,更容易理解哦!)。

首先是請求的基本方法:

 

/** * 執行請求 * * @param request 請求 * @param <T> 響應類型 * @return 響應值 */
    public <T extends BaseStoResponse> T execute(BaseStoRequest<T> request) { T response; String body = null; try { // 生成http請求
            Request httpRequest = generateHttpRequest(request); Call call = httpClient.newCall(httpRequest); Response execute = call.execute(); if (execute.code() == 200) { body = execute.body().byteString().string(Charset.forName(charsetName)); response = objectMapper.readValue(body, request.getResponseClass()); } else { response = request.fail("請求三方平台接口發生異常,狀態碼:" + execute.code()); } } catch (Exception e) { response = request.fail("請求三方平台接口發生異常," + e.getMessage()); } response.setBody(body); return response; }

這個方法里,請求參數會放到 BaseSfRequest 類里,返回的參數用 BaseSfResponse 類接收。這里的兩個base類,后面會再細說。

然后就是生成http請求的方法 generateHttpRequest(request)了。具體代碼如下:

/**
* 生成 Http 請求
*
* @param request 請求對象
* @return Http 請求對象
* @throws Exception 生成簽名異常
*/
private Request generateHttpRequest(BaseStoRequest<?> request) throws Exception {
// 請求報文
String content = objectMapper.writeValueAsString(request);

// 計算簽名
String sign = doSign(content);

// 生成 Request
return new Request.Builder().url(stoConfig.getUrl()).addHeader("Accept", "application/json")
.post(new FormBody.Builder().add("content", content).add("data_digest", sign)
.add("api_name", request.apiName()).add("from_appkey", stoConfig.getFromAppKey())
.add("from_code", stoConfig.getFromCode()).add("to_appkey", request.toAppKey())
.add("to_code", request.toAppKey()).build())
.build();
}

這里會根據每個平台的具體要求來傳公共參數與業務參數,另外還會對加密方法進行聲明。下面是最尋常的 base64(md5(參數))加密方法。

/** * 計算簽名 * * @param content 報文內容 * @return 簽名 * @throws Exception 異常 */
    public String doSign(String content) throws Exception { return Base64.getEncoder().encodeToString( MessageDigest.getInstance("MD5").digest((content + stoConfig.getSecretKey()).getBytes(charsetName))); }

以上三個方法,就實現了基本的http請求與請求參數的封裝了,這三個方法,我們把它放到manager層里面。

@Component public class StoApiManager { /** * httpClient */ @Autowired private OkHttpClient httpClient; /** * stoConfig */ @Autowired private StoConfig stoConfig; /** * 字符編碼格式 */
    private final String charsetName = "UTF-8"; /** * objectMapper */ @Autowired private ObjectMapper objectMapper; /** * 執行請求 * * @param request 請求 * @param <T> 響應類型 * @return 響應值 */
    public <T extends BaseStoResponse<?>> T execute(BaseStoRequest<T> request) { T response; String body = null; try { // 生成http請求
            Request httpRequest = generateHttpRequest(request); Call call = httpClient.newCall(httpRequest); Response execute = call.execute(); if (execute.code() == 200) { body = execute.body().byteString().string(Charset.forName(charsetName)); response = objectMapper.readValue(body, request.getResponseClass()); } else { response = request.fail("請求三方接口發生異常,狀態碼:" + execute.code()); } } catch (Exception e) { response = request.fail("請求三方接口發生異常," + e.getMessage()); } response.setBody(body); return response; } /** * 生成 Http 請求 * * @param request 請求對象 * @return Http 請求對象 * @throws Exception 生成簽名異常 */
    private Request generateHttpRequest(BaseStoRequest<?> request) throws Exception { // 請求報文
        String content = objectMapper.writeValueAsString(request); // 計算簽名
        String sign = doSign(content); // 生成 Request
        return new Request.Builder().url(stoConfig.getUrl()).addHeader("Accept", "application/json") .post(new FormBody.Builder().add("content", content).add("data_digest", sign) .add("api_name", request.apiName()).add("from_appkey", stoConfig.getFromAppKey()) .add("from_code", stoConfig.getFromCode()).add("to_appkey", request.toAppKey()) .add("to_code", request.toAppKey()).build()) .build(); } /** * 計算簽名 * * @param content 報文內容 * @return 簽名 * @throws Exception 異常 */
    public String doSign(String content) throws Exception { return Base64.getEncoder().encodeToString( MessageDigest.getInstance("MD5").digest((content + stoConfig.getSecretKey()).getBytes(charsetName))); } }

 

接下來就是關於如何封裝請求參數與封裝返回參數的問題。

1.1 請求參數的封裝

分三步,其實自己寫的話分兩步封裝就可以了,但是這里我們的架構大神規定了框架,所以都是按照baseRequest <一  baseStoRequest  <一 baseTraceQueryRequest(路由查詢接口)來三級封裝的。也就是基本公共請求,基本平台公共請求,平台具體接口請求來寫。

首先是基本公共請求類代碼(這里的方法是所有三方平台去請求都會用到的):

public abstract class BaseRequest<T extends BaseResponse> { /** * @return 獲取響應對象類型 */ @JsonIgnore public abstract Class<T> getResponseClass(); /** * @return 默認響應值 */
    public T generateDefaultResponse() { Object response; try { response = getResponseClass().newInstance(); } catch (Exception e) { e.printStackTrace(); throw new SntProjectException(SystemCodeEnum.PARAMETER_UNKNOWN, "生成 DefaultResponse 異常"); } return (T)response; } /** * @param errMsg 失敗信息 * @return 失敗響應 */
    public T fail(String errMsg) { T response = generateDefaultResponse(); response.setFail(errMsg); return response; } }

基本平台公共請求類代碼(這里的方法與參數是該三方平台所有接口都會用到的):

public abstract class BaseStoRequest<T extends BaseStoResponse<?>> extends BaseRequest<T> { /** * @return 接口名稱 */
    public abstract String apiName(); /** * @return AppKey */
    public abstract String toAppKey(); }

三方平台具體接口請求類代碼(以申通的路由查詢為例):

@EqualsAndHashCode(callSuper = true) @Data public class StoTraceQueryRequest extends BaseStoRequest<StoTraceQueryResponse> { @Override public Class<StoTraceQueryResponse> getResponseClass() { return StoTraceQueryResponse.class; } @Override public String apiName() { return "STO_TRACE_QUERY_COMMON"; } @Override public String toAppKey() { return "sto_trace_query"; } /** * 運單號集合 */ @JsonProperty("waybillNoList") private List<String> waybillNos; }

不知道大家注意到上面聲明的  private List<String> waybillNos 了沒,因為這個是申通接口文檔里聲明的,所以我們秩序要照着封裝就行了。這里可以跟大家說一下,現在的三方平台接口,一般都提供了xml與json兩種格式的參數,我推薦是用json的來封裝。而json的話,{  }代表一個object對象,[  ]代表一個list數組。

 

1.2 接收參數的封裝

同樣的,我們的架構大神將返回參數的封裝同樣分為三層。這里我就直接放代碼了,跟上面的請求三級封裝邏輯是一樣的。

基本公共返回類代碼:

@Data public class BaseResponse { /** * 響應原始報文 */
    private String body; /** * 是否成功 */ @JsonIgnore private Boolean isError; /** * 原因 */ @JsonIgnore private String errMsg; /** * 設置失敗響應 * * @param errMsg 失敗原因 */ @JsonIgnore public void setFail(String errMsg) { setIsError(true); setErrMsg(errMsg); } }

基本平台公共返回類代碼(這里的方法與參數是該三方平台所有接口都會用到的):

@EqualsAndHashCode(callSuper = true) @Data public class BaseStoResponse<T> extends BaseResponse { /** * 是否成功 */
    private Boolean success; /** * 錯誤編碼 */
    private String errorCode; /** * 錯誤信息 */
    private String errorMsg; /** * 是否重試 */
    private Boolean needRetry; /** * 請求id */
    private String requestId; /** * 異常信息 */
    private String expInfo; /** * 數據 */
    private T data; @Override public Boolean getIsError() { return BooleanUtils.isNotTrue(success); } @Override public void setIsError(Boolean isError) { success = BooleanUtils.isFalse(isError); } @Override public String getErrMsg() { return errorMsg; } @Override public void setErrMsg(String errMsg) { errorMsg = errMsg; } public static BaseStoResponse<?> fail(String errMsg) { BaseStoResponse<?> response = new BaseStoResponse<String>(); response.setFail(errMsg); response.setNeedRetry(Boolean.TRUE); return response; } public static <T> BaseStoResponse<T> ok(T data) { BaseStoResponse<T> response = new BaseStoResponse<T>(); response.setSuccess(Boolean.TRUE); response.setData(data); return response; } }

注意一下,這里的 fail 與 ok方法,是后面的路由推送接口也就是三方平台調我們接口會用到的。

三方平台具體接口返回類代碼(以申通的路由查詢為例):

public class StoTraceQueryResponse extends BaseStoResponse<Map<String, List<StoTraceDTO>>> { }

 這里的 Map<String, List<StoTraceDTO>>也是根據三方平台文檔返回數據來對應接收的。

 

好啦,寫到這里,基本就報本方調用三方接口的思路基本寫完啦。后面的第二種模式,讓三方平台調用本方的接口傳送參數的代碼實例與思路,我放到下篇博客接着來寫。

創作不易,分享技術!喜歡我的博客的話,給我一個贊吧!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM