企業內部應用對接釘釘 -- 釘釘回調

記錄自己踩得坑,希望可以幫助更多人。

首先,想說釘釘官方文檔寫的,,也挺全,也不全,內容也挺豐富,有寫東西吧,也真不好找,內容比較散。。。

言歸正傳。。。
整個流程,依舊先閱讀官網:https://ding-doc.dingtalk.com/doc#/serverapi2/pwz3r5

  1. 第一步:註冊業務事件回調接口,主要目的就是,給釘釘一個接口,這個接口作用是:如有回調發生,釘釘就會給這個接口返回變更的數據,這個接口就是這一步驟中傳入的參數url 【具體url如何寫,後面文中會說】
  2. 第二步:解讀下這個流程:
    在這裏插入圖片描述
    這個流程意思:第一步中填入的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:
    開發時候,一般要訪問外網,我們都要藉助內網穿透工具,正巧,釘釘官網提供的有。。。結果用了半天發現這個工具不好用。。。換工具吧:內網穿透工具

如上:基本都能梳理通整個邏輯了吧????

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章