更好的排版可以看這個http://note.youdao.com/s/Uoizqaym
釘釘官方的SDK對釘釘API提供了簡單的封裝,但官方的SDK使用起來較為臃腫,並且最重要的是官方SDK僅僅是封裝了API,對於accessToken的維護以及消息回調處理等等都沒有封裝,在項目中大規模使用比較麻煩,因此萌生了對釘釘官方SDK進行全面重構的想法
釘釘官方SDK存在的一些問題或不足
我們在使用官方SDK的過程中發現官方SDK存在一些比較蛋疼的問題:
- accessToken需要自己來維護,SDK並不維護,那么在實際使用中就要做好accessToken的過期處理、多線程刷新時保證刷新是正確的
- 除了自己維護accessToken外,基本上每次調用都需要將accessToken作為參數傳入,沒有自動化的將固定參數補齊
- 消息回調的加解密並不包含在SDK中,需要自己整合進來
- 對不同消息如何處理應該在SDK層面來解決掉,這樣業務層面就只需要關心具體的處理邏輯即可,但SDK並不包含這些
- 不支持HTTP代理,當代碼部署在內網后沒辦法直接和釘釘API交互,需要借助代理來完成交互,但SDK並不支持代理且由於第6點的原因想優化SDK卻不好下手優化
- SDK封裝方式比較蛋疼,底層又是用到了淘寶的SDK,不是很容易來修改
- 當然還有其他的小問題,比如調用SDK方法時並不知道需要傳哪些參數所以需要對照着開發文檔才能發起調用等等。
基於以上的原因,官方SDK不滿足我們的實際場景太多了,用起來太蛋疼了,主要是:
- 不維護accessToken
- 沒有加解密
- 沒有消息路由
- 不支持代理
- 對照開發文檔才能使用
我們是完全拋棄了釘釘官方SDK,從零搭建出了一套Java版本的釘釘SDK,並且已經開源出來了,可見https://github.com/tingyugetc520/DtJava,希望大家能多多點點Star、Fork,跪謝。
比官方SDK還好用的釘釘SDK
自認為DtJava(釘釘 Java SDK)
對開發者更加友好,使用起來比官方SDK好用多了。
基本的API調用封裝
下面簡單比較下官方SDK和DtJava之間的調用方式。
首先是官方SDK的調用方式:
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/get");
OapiUserGetRequest req = new OapiUserGetRequest();
req.setUserid("userid1");
req.setHttpMethod("GET");
OapiUserGetResponse rsp = client.execute(req, accessToken);
整個調用過程就是
- 初始化一個DingTalkClient出來,初始化的同時指定API的URL地址是啥
- 初始化請求參數,並設置好參數與請求方式
- 獲取accessToken,在這一步中還要處理好過期刷新的問題
- 發起請求
在這個調用過程中,完全沒必要這樣來使用,我們可以將API的地址、參數、請求全部封裝起來,整體對外暴露成一個方法,再進一步封裝可以將accessToken封裝進方法中。
這樣封裝之后基本上類似DtJava的調用方式:
DtUser user = dtService.getUserService().getById(userId);
一行代碼便足夠了,當然在這個背后整體的調用邏輯是
- 獲取到對應的API地址
- 拼接設置參數
- 獲取accessToken,包含了過期處理等等邏輯
- 發起請求
- 解析響應並封裝成對應的Bean
全部的邏輯都被封裝起來,具體的封裝細節大家可以到GitHub上面先Star后查看源碼(求Star求魔怔了),對開發者而言就是一個方法的調用即可,比官方SDK友好的多。
回調消息處理
除了基本的API調用外,釘釘的開放平台還有一個很重要的部分就是事件回調,在通訊錄同步等等場景中回調發揮着重要的作用。但官方SDK並沒有涉足到這一部分,對於開發者而言需要自己去整合加解密的流程以及對不同消息的處理邏輯。但好在官方已經將這部分的代碼舉例出來了:
public Map<String, String> callBack(HttpServletRequest request,
@RequestParam(value = "msg_signature", required = false) String msg_signature,
@RequestParam(value = "timestamp", required = false) String timeStamp,
@RequestParam(value = "nonce", required = false) String nonce,
@RequestBody(required = false) JSONObject json) {
try {
// 1. 從http請求中獲取加解密參數
// 2. 使用加解密類型
// Constant.OWNER_KEY 說明:
// 1、開發者后台配置的訂閱事件為應用級事件推送,
// 此時OWNER_KEY為應用的APP_KEY(企業內部應用)或SUITE_KEY(三方應用)。
// 2、調用訂閱事件接口訂閱的事件為企業級事件推送,
// 此時OWNER_KEY為:企業的CORP_ID(企業內部應用)或SUITE_KEY(三方應用)。
// 其中的DingCallBackCrypto參見https://github.com/open-dingtalk/dingtalk-callback-Crypto
DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(Constant.AES_TOKEN, Constant.AES_KEY, Constant.OWNER_KEY);
String encryptMsg = json.getString("encrypt");
String decryptMsg = callbackCrypto.getDecryptMsg(msg_signature, timeStamp, nonce, encryptMsg);
// 3. 反序列化回調事件json數據
JSONObject eventJson = JSON.parseObject(decryptMsg);
String eventType = eventJson.getString("EventType");
// 4. 根據EventType分類處理
if ("check_url".equals(eventType)) {
// 測試回調url的正確性
bizLogger.info("測試回調url的正確性");
} else if ("user_add_org".equals(eventType)) {
// 處理通訊錄用戶增加事件
bizLogger.info("發生了:" + eventType + "事件");
} else {
// 添加其他已注冊的
bizLogger.info("發生了:" + eventType + "事件");
}
// 5. 返回success的加密數據
Map<String, String> successMap = callbackCrypto.getEncryptedMap("success");
return successMap;
} catch (DingTalkEncryptException e) {
e.printStackTrace();
}
return null;
}
通過官方示例,開發者已經基本上能夠完成消息回調的處理。
但這僅僅是能夠完成回調消息處理,我相信隨着業務發展會出現很多的if...else...,在后續的開發維護中就變得比較麻煩了,當然大家都會想辦法將這里優化掉,DtJava也同樣做了這個優化的工作。
在DtJava中,消息通過消息路由器來完成不同消息的處理。
// 消息路由器
DtMessageRouter newRouter = new DtMessageRouter(dtService);
// 通訊錄用戶增加事件
newRouter.rule().async(false).eventType("user_add_org").handler(this.contactUserAddHandler).end();
// 默認,記錄所有事件的日志
newRouter.rule().async(true).handler(this.logHandler).end();
return newRouter;
消息路由器DtMessageRouter主要是從eventType來匹配不同的handler,降低系統復雜度,更多的內容可以查看Wiki,這里不再介紹了。
在最后再放一下地址:https://github.com/tingyugetc520/DtJava,還望大家能夠多多Star。