記錄自己踩得坑,希望可以幫助更多人。
首先,想說釘釘官方文檔寫的,,也挺全,也不全,內容也挺豐富,有寫東西吧,也真不好找,內容比較散。。。
言歸正傳。。。
整個流程,依舊先閱讀官網:https://ding-doc.dingtalk.com/doc#/serverapi2/pwz3r5
- 第一步:註冊業務事件回調接口,主要目的就是,給釘釘一個接口,這個接口作用是:如有回調發生,釘釘就會給這個接口返回變更的數據,這個接口就是這一步驟中傳入的參數url 【具體url如何寫,後面文中會說】
- 第二步:解讀下這個流程:
這個流程意思:第一步中填入的url參數,也就是那個接收回調的接口,需要驗證它的可用性,需要給釘釘返回一個加密的“success”json數據。【加密/解密 後面講】
第三步:第一,第二步驟都沒問題了,就可以開始開發了,可以成功接收回調了。然後依舊多次閱讀官方文檔,每次讀可能都有新的發現。
下面就是疑難雜症。。。給出代碼:
回調事件通用類:
package com.wekj.peanut.utils;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* 回調事件通用類
* 竹蜻蜓 2020/2/22 21:21
*/
@Slf4j
public class EventChangeUtils {
/**
* 註冊事件回調接口
* 竹蜻蜓 2020/2/22 20:56
* @param accessToken
* @param callBackTag
* @param token
* @param aesKey
* @param url
* @return
*/
public static OapiCallBackRegisterCallBackResponse registerEventChange(String accessToken, List<String> callBackTag, String token, String aesKey, String url) throws ApiException {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/call_back/register_call_back");
OapiCallBackRegisterCallBackRequest request = new OapiCallBackRegisterCallBackRequest();
request.setUrl(url);
request.setAesKey(aesKey);
request.setToken(token);
request.setCallBackTag(callBackTag);
log.info("EventChangeUtils.registerEventChange()方法中傳入參數回調url 值爲:" + request.getUrl());
OapiCallBackRegisterCallBackResponse response = client.execute(request,accessToken);
return response;
}
/**
* 查詢事件回調接口
* 竹蜻蜓 2020/2/22 20:55
* @param accessToken
* @return
*/
public static String getEventChange(String accessToken) throws ApiException {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/call_back/get_call_back");
OapiCallBackGetCallBackRequest request = new OapiCallBackGetCallBackRequest();
request.setHttpMethod("GET");
OapiCallBackGetCallBackResponse response = client.execute(request,accessToken);
return response.toString();
}
/**
* 更新事件回調接口
* 竹蜻蜓 2020/2/22 20:55
* @param accessToken
* @param callBackTag
* @param token
* @param aesKey
* @param url
* @return
*/
public static String updateEventChange(String accessToken, List<String> callBackTag, String token, String aesKey, String url) throws ApiException {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/call_back/update_call_back");
OapiCallBackUpdateCallBackRequest request = new OapiCallBackUpdateCallBackRequest();
request.setUrl(url);
request.setAesKey(aesKey);
request.setToken(token);
request.setCallBackTag(callBackTag);
OapiCallBackUpdateCallBackResponse response = client.execute(request,accessToken);
return response.toString();
}
/**
* 刪除事件回調接口
* 竹蜻蜓 2020/2/22 20:55
* @param accessToken
* @return
*/
public static String deleteEventChange(String accessToken) throws ApiException {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/call_back/delete_call_back");
OapiCallBackDeleteCallBackRequest request = new OapiCallBackDeleteCallBackRequest();
request.setHttpMethod("GET");
OapiCallBackDeleteCallBackResponse response = client.execute(request,accessToken);
return response.toString();
}
/**
* 獲取回調失敗的結果
* 釘釘服務器給回調接口推送時,有可能因爲各種原因推送失敗(比如網絡異常),此時釘釘將保留此次變更事件。用戶可以通過此回調接口獲取推送失敗的變更事件。
* 竹蜻蜓 2020/2/22 20:57
* @param accessToken
* @return
*/
public static String getFailedResult(String accessToken) throws ApiException {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/call_back/get_call_back_failed_result");
OapiCallBackGetCallBackFailedResultRequest request = new OapiCallBackGetCallBackFailedResultRequest();
request.setHttpMethod("GET");
OapiCallBackGetCallBackFailedResultResponse response = client.execute(request,accessToken);
return response.toString();
}
}
package com.wekj.peanut.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiCallBackDeleteCallBackRequest;
import com.dingtalk.api.response.OapiCallBackDeleteCallBackResponse;
import com.dingtalk.api.response.OapiCallBackRegisterCallBackResponse;
import com.dingtalk.oapi.lib.aes.DingTalkEncryptException;
import com.dingtalk.oapi.lib.aes.DingTalkEncryptor;
import com.dingtalk.oapi.lib.aes.Utils;
import com.taobao.api.ApiException;
import com.wekj.peanut.service.*;
import com.wekj.peanut.utils.Constant;
import com.wekj.peanut.utils.DingTalkUtils;
import com.wekj.peanut.utils.EventChangeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 釘釘回調類
*
* @author 竹蜻蜓
* @date 2020/2/22 16:19
*/
@Slf4j
@RequestMapping(value = "/callBackController")
@RestController
public class CallbackController {
@Resource
private DingDepartmentService dingDepartmentService;
@Resource
private DingUserService dingUserService;
/**
* 1.註冊釘釘回調接口
* 竹蜻蜓 2020/2/25 13:03
*
* @param
* @return
*/
public static void registerCallback() throws Exception {
log.info("1.開始註冊");
String accessToken = DingTalkUtils.getAccessToken(Constant.APPKEY, Constant.APPSECRET);
try {
// 先刪除企業已有的回調
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/call_back/delete_call_back");
OapiCallBackDeleteCallBackRequest request = new OapiCallBackDeleteCallBackRequest();
request.setHttpMethod("GET");
OapiCallBackDeleteCallBackResponse execute = client.execute(request, accessToken);
log.info("2.刪除已有回調結果:" + execute.isSuccess());
// 重新爲企業註冊回調
List<String> depList = Arrays.asList("org_dept_create", "org_dept_modify", "org_dept_remove",
"user_add_org", "user_modify_org", "user_leave_org", "user_active_org",
"org_remove", "org_change",
"bpms_task_change", "bpms_instance_change");
String url = "http://****外網可以訪問的地址**/callBackController/callback";
OapiCallBackRegisterCallBackResponse res = EventChangeUtils.registerEventChange(accessToken, depList, Constant.TOKEN, Constant.ENCODING_AES_KEY, url);
if (res.isSuccess()) {
log.info("6.回調註冊成功了!!!");
} else {
log.error("6.回調註冊失敗了!!!");
}
} catch (ApiException e) {
log.error("回調註冊失敗了!!!EventChangeServlet.registerCallback()方法異常");
}
}
/**
* 2.回調地址裏的url方法 --- 獲取變更數據
* 竹蜻蜓 2020/2/25 13:03
*
* @param signature
* @param timestamp
* @param nonce
* @param json
* @return
*/
@RequestMapping(value = "/callback", method = RequestMethod.POST)
@ResponseBody
public Map<String, String> callback(@RequestParam(value = "signature", required = false) String signature,
@RequestParam(value = "timestamp", required = false) String timestamp,
@RequestParam(value = "nonce", required = false) String nonce,
@RequestBody(required = false) JSONObject json) throws DingTalkEncryptException {
log.info("3.進入方法callback()");
log.info("3.signature值爲:" + signature);
log.info("3.timestamp值爲:" + timestamp);
log.info("3.nonce值爲:" + nonce);
log.info("3.json值爲:" + json.toString());
DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(Constant.TOKEN, Constant.ENCODING_AES_KEY, Constant.CORPID);
// 從post請求的body中獲取回調信息的加密數據
String encrypt = json.getString("encrypt");
log.info("3.加密數據encrypt值爲:" + encrypt);
// 解密
String plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp, nonce, encrypt);
log.info("3.解密出的明文plainText值爲:" + plainText);
JSONObject obj = JSON.parseObject(plainText);
log.info("4.開始處理業務。。。Obj=" + obj);
// 根據回調數據類型做不同的業務處理
String eventType = obj.getString("EventType");
switch (eventType) {
//通訊錄用戶增加
case "user_add_org":
break;
//通訊錄用戶更改
case "user_modify_org":
break;
//通訊錄用戶離職
case "user_leave_org":
break;
// 通訊錄企業部門創建
case "org_dept_create":
break;
// 通訊錄企業部門修改
case "org_dept_modify":
break;
//通訊錄企業部門刪除
case "org_dept_remove":
break;
//企業被解散
case "org_remove":
break;
case "check_url":
break;
case "bpms_task_change":
break;
case "bpms_instance_change":
break;
default: //do something
break;
}
// 返回success的加密信息表示回調處理成功
log.info("5.開始加密‘success’字符串,並返回。。");
return dingTalkEncryptor.getEncryptedMap("success", System.currentTimeMillis(), Utils.getRandomStr(8));
}
}
這些代碼需要導入的jar包:想辦法去官網找,實在找不到私信我吧。
-
踩坑1:
如果沒有做冪等,是不是發現釘釘回調同時觸發好幾次。。。導入入庫多條相同數據。。。釘釘官方給出的消息
所以我們需要做冪等操作,,原因給出來了,根據你的業務,自行處理吧。 -
踩坑2:
註冊回調的 url 地址怎麼寫?
首先你先寫出上面的 callback() 方法,,然後確保用外網能訪問進來這個方法,關於外網,看下面踩坑3.
外網沒問題了,能訪問通這個方法了,就把這個方法路徑,填寫到註冊回調的 Url 參數位置去,,就可以了。 -
踩坑3:
開發時候,一般要訪問外網,我們都要藉助內網穿透工具,正巧,釘釘官網提供的有。。。結果用了半天發現這個工具不好用。。。換工具吧:內網穿透工具
如上:基本都能梳理通整個邏輯了吧????