钉钉通讯录同步二开案例

一、背景
目前钉钉支持手机号或者邮箱同步钉钉通讯录,有些客户需要使用工号同步,或者客户使用了企业专属账号,苍穹当前版本产品接口不支持同步(v6.0.6版本以上支持),则需要现场二开解决。
二、方案
新增一个调度任务,在调度中调用钉钉的接口进行通讯录同步。
具体步骤
1. 获取token
根据配置好的应用信息获取token,方法如下:
请求方式:GET
请求地址:https://oapi.dingtalk.com/gettoken ?appkey=xxx&appsecret=xxx
请求参数:
appkey | String | 是 | dingeqqpkv3xxxx | 应用的唯一标识key。 |
appsecret | String | 是 | GT-lsu-taDAsTsxxxx | 应用的密钥。AppKey和AppSecret可在钉钉开发者后台 的应用详情页面获取。 |
2. 获取授权部门
在钉钉管理后台【权限管理】中需要配置授权部门,如果调用接口获取非授权部门的人员信息则会提示无权限,所以需要主动获取授权部门。
请求方式:GET
请求地址:https://oapi.dingtalk.com/auth/scopes ? access_token =xxxxx
请求参数:
access_token | String | 是 | 6ed1bxxx | 调用该接口的应用凭证。 |
3. 获取授权部门及其子部门
请求方式:GET
请求地址:
https://oapi.dingtalk.com/department/list? access_token =xxx& fetch_child =true&id=xxx
请求参数:
access_token | String | 是 | 6ed1bxxx | 调用服务端API的应用凭证。 |
lang | String | 否 | zh_CN | 通讯录语言,默认zh_CN。 |
fetch_child | Boolean | 否 | true | 是否递归部门的全部子部门。 |
id | String | 否 | 1 | 父部门ID。 如果不传,默认部门为根部门,根部门ID为1。 |
4. 获取部门下的人员信息
请求方式:POST
请求地址:https://oapi.dingtalk.com/topapi/v2/user/list ? access_token =xxxx
请求参数:
access_token | String | 是 | be3Fxxxx | 调用该接口的应用凭证。 |
Body参数:
dept_id | Number | 是 | 10 | 部门ID,可调用获取部门列表 获取,如果是根部门,该参数传1。 |
cursor | Number | 是 | 0 | 分页查询的游标,最开始传0,后续传返回参数中的next_cursor值。 |
size | Number | 是 | 10 | 分页大小。 |
order_field | String | 否 | modify_desc | 部门成员的排序规则,默认不传是按自定义排序(custom): entry_asc:代表按照进入部门的时间升序 entry_desc:代表按照进入部门的时间降序 modify_asc:代表按照部门信息修改时间升序 modify_desc:代表按照部门信息修改时间降序 custom:代表用户定义(未定义时按照拼音)排序 |
contain_access_limit | Boolean | 否 | false | 是否返回访问受限的员工: true:返回 false:不返回 |
language | String | 否 | zh_CN | 通讯录语言,取值。 |
5. 根据相关字段(如手机号或者邮箱)与苍穹(星瀚)人员信息进行匹配
6. 将钉钉人员信息保存在映射表中
参考代码:
package hnzb.sys.base.plugin.task;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import kd.bos.context.RequestContext;
import kd.bos.dataentity.utils.ObjectUtils;
import kd.bos.dd.service.DingDingServiceHelper;
import kd.bos.exception.KDException;
import kd.bos.lang.Lang;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.org.utils.Consts;
import kd.bos.schedule.executor.AbstractTask;
import kd.bos.sec.user.task.SynUserTypeEnum;
import kd.bos.sec.user.utils.UserOperationUtils;
import kd.bos.util.HttpClientUtils;
import kd.bos.util.StringUtils;
import kd.sdk.plugin.Plugin;
import java.io.IOException;
import java.util.*;
/**
* @BelongsProject: hnzb-cosmic
* @BelongsPackage: hnzb.sys.base.plugin.task
* @Author:
* @CreateTime: 2023-12-21 17:37
* @Description: TODO 查询系统中指定日期修改的组织单元、人员、岗位,然后同步到主数据系统
* @Version: 1.0
* @PageNumber:
*/
public class SynDingUserToUserMappingTask extends AbstractTask {
private static Log logger = LogFactory.getLog(SynDingUserToUserMappingTask.class);
@Override
public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
synDing();
}
/**
* 钉钉openid同步
*/
public static void synDing() {
String accessToken = DingDingServiceHelper.getAccess_token();
logger.info("authScope" + accessToken);
if (kd.bos.util.StringUtils.isEmpty(accessToken)) {
return;
}
Map<String, List<String>> authScope = getAuthScope(accessToken);
logger.info("authScope" + authScope);
//如果设置了权限范围 则只查询有权的组织及人员
if (isAuthScope(authScope)) {
List<String> authedUser = authScope.get("authed_user");
logger.info("authedUser" + authedUser);
int userSize = 0;
int deptSize = 0;
//授权人员
if (null != authedUser && !authedUser.isEmpty()) {
List<Map<String, String>> userList = getAuthScopeUsers(authedUser, accessToken);
logger.info("userList" + userList);
UserOperationUtils.saveIMMapping(userList, "mobile", Consts.PHONE, SynUserTypeEnum.DINGDING.getValue());
userSize = authedUser.size();
}
List<String> authedDept = authScope.get("authed_dept");
logger.info("authed_dept" + authedDept);
int deptCount = 0;
//授权部门
if (null != authedDept && !authedDept.isEmpty()) {
//获取子部门
List<String> allDept = getChildrenList(accessToken,authedDept);
authedDept.addAll(allDept);
deptSize = authedDept.size();
deptCount = saveUserMappingByDept(accessToken, authedDept, imTypeFieldName, sysFieldName);
logger.info("deptCount" + deptCount);
}
return;
}
List<String> deptList = DingDingServiceHelper.getDeptList(accessToken);
if (deptList == null || deptList.isEmpty()) {
return;
}
saveUserMappingByDept(accessToken, deptList);
}
private static List<String> getChildrenList(String accessToken, List<String> authedDept) {
List<String> allDept = new ArrayList<>(authedDept.size());
for (String deptId : authedDept) {
List<String> deptList = getDeptList(accessToken,deptId);
allDept.addAll(deptList);
}
return allDept;
}
private static int saveUserMappingByDept(String accessToken, List<String> deptList) {
int completedCount = 1;
for (String deptId : deptList) {
long offset = 0;
long size = 100;
while (true) {
List<Map<String, String>> userList = getDeptUserList(accessToken,
Long.parseLong(deptId), offset, size, null);
logger.error("saveUserMappingByDept**userList" + userList);
if (userList == null || userList.isEmpty()) {
break;
}
boolean mobile = UserOperationUtils.saveIMMapping(userList, "mobile", Consts.PHONE, SynUserTypeEnum.DINGDING.getValue());
logger.error("saveUserMappingByDept**mobile" + mobile);
if (userList.size() < 100) {
break;
}
offset += 100;
}
completedCount++;
}
return completedCount;
}
private static boolean isAuthScope(Map<String, List<String>> authScope) {
//授权范围为空
if (authScope.isEmpty()) {
logger.error("authScope is empty");
return false;
}
//授权范围为全部员工
List<String> authedDept = authScope.get("authed_dept");
if (null != authedDept && !authedDept.isEmpty()) {
String dept = authedDept.get(0);
if ("1".equals(dept)) {
logger.info("authed_dept : 1");
return false;
}
}
return true;
}
/**
* 获取钉钉应用的授权范围
*
* @param token
* @return
*/
public static Map<String, List<String>> getAuthScope(String token) {
String ddHost = "https://oapi.dingtalk.com";
StringBuilder url = new StringBuilder(ddHost).append("/auth/scopes?access_token=");
url.append(token);
try {
String data = HttpClientUtils.get(url.toString());
HashMap<String, Object> map = JSON.parseObject(data, HashMap.class);
//权限范围
Map<String, Object> authOrgScopes = (Map<String, Object>) map.get("auth_org_scopes");
//授权人员
JSONArray authed_user = (JSONArray) authOrgScopes.get("authed_user");
//授权部门
JSONArray authed_dept = (JSONArray) authOrgScopes.get("authed_dept");
HashMap<String, List<String>> dataMap = new HashMap<>(2);
if (authed_user != null) {
List<String> userList = authed_user.toJavaList(String.class);
dataMap.put("authed_user", userList);
}
if (authed_dept != null) {
List<String> deptList = authed_dept.toJavaList(String.class);
dataMap.put("authed_dept", deptList);
}
return dataMap;
} catch (Exception e) {
logger.error(e);
}
return Collections.emptyMap();
}
/**
* 根据授权范围获取人员信息
*
* @param authedUser 授权范围:授权人员
* @param accessToken
* @return
*/
public static List<Map<String, String>> getAuthScopeUsers(List<String> authedUser, String accessToken) {
List<Map<String, String>> userList = new ArrayList<>(16);
//1.获取授权人员信息
String ddHost = "https://oapi.dingtalk.com";
StringBuilder url = new StringBuilder(ddHost).append("/topapi/v2/user/get?access_token=");
url.append(accessToken);
HashMap<String, Object> params = new HashMap<>(2);
params.put("language", Lang.get().name());
try {
for (String userId : authedUser) {
钉钉通讯录同步二开案例
声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。如若本站内容侵犯了原著者的合法权益,可联系本站删除。



