记录自己踩得坑,希望可以帮助更多人。
首先,想说钉钉官方文档写的,,也挺全,也不全,内容也挺丰富,有写东西吧,也真不好找,内容比较散。。。
言归正传。。。
整个流程,依旧先阅读官网:https://ding-doc.dingtalk.com/doc#/serverapi2/pwz3r5
下面就是疑难杂症。。。给出代码:
回调事件通用类:
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:
开发时候,一般要访问外网,我们都要借助内网穿透工具,正巧,钉钉官网提供的有。。。结果用了半天发现这个工具不好用。。。换工具吧:内网穿透工具
如上:基本都能梳理通整个逻辑了吧????