✨ 钉钉导入角色、部门、用户

This commit is contained in:
Fxz committed 2022-04-23 18:28:34 +08:00
1 parent aa17bb3924
commit e69996e39d
6 files changed
+287 -9

No files matched your search

@@ -139,6 +139,26 @@ public interface SecurityConstants {
*/
String OSC_USER_INFO_URL = "https://www.oschina.net/action/openapi/user?access_token=%s&dataType=json";
/**
* 钉钉同步角色列表
*/
String DING_OLD_ROLE_URL = "https://oapi.dingtalk.com/topapi/role/list";
/**
* 钉钉同步部门列表
*/
String DING_OLD_DEPT_URL = "https://oapi.dingtalk.com/topapi/v2/department/listsub";
/**
* 钉钉部门用户id列表
*/
String DING_DEPT_USERIDS_URL = "https://oapi.dingtalk.com/topapi/user/listid";
/**
* 钉钉用户详情
*/
String DING_USER_INFO_URL = "https://oapi.dingtalk.com/topapi/v2/user/get";
/**
* {bcrypt} 加密的特征码
*/
+7
View File
@@ -128,11 +128,18 @@
<artifactId>cas-client-core</artifactId>
<version>${cas.sdk.version}</version>
</dependency>
<!--新版api-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dingtalk</artifactId>
<version>${dingtalk.version}</version>
</dependency>
<!--旧版api,新版api未包含全部的服务端API的产品能力-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
<version>${dingtalk.old.version}</version>
</dependency>
<!--web 模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -1,8 +1,11 @@
package com.pig4cloud.pigx.admin.controller;
import com.pig4cloud.pigx.admin.service.ConnectService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -13,19 +16,22 @@ import org.springframework.web.bind.annotation.RestController;
* @author lengleng
* @date 2022/4/22
*/
@Inner(value = false)
@RestController
@RequiredArgsConstructor
@RequestMapping("/connect")
@Api(value = "connect", tags = "开放互联")
public class ConnectController {
private final ConnectService connectService;
/**
* 同步钉钉用户
* @return
*/
@PostMapping("/sync/ding/user")
public R syncUser() {
return R.ok();
@PostMapping("/sync/ding/user/{deptId}")
public R syncUser(@PathVariable("deptId") Long deptId) {
return R.ok(connectService.syncDingUser(deptId));
}
/**
@@ -34,7 +40,7 @@ public class ConnectController {
*/
@PostMapping("/sync/ding/role")
public R syncRole() {
return R.ok();
return R.ok(connectService.syncDingRole());
}
/**
@@ -43,7 +49,7 @@ public class ConnectController {
*/
@PostMapping("/sync/ding/dept")
public R syncDept() {
return R.ok();
return R.ok(connectService.syncDingDept());
}
/**
@@ -3,9 +3,24 @@ package com.pig4cloud.pigx.admin.service;
/**
* @author lengleng
* @date 2022/4/22
*
* <p>
* 互联平台
*/
public interface ConnectService {
/**
* 同步钉钉角色
*/
Boolean syncDingRole();
/**
* 同步钉钉部门
*/
Boolean syncDingDept();
/**
* 同步钉钉用户
*/
Boolean syncDingUser(Long deptId);
}
@@ -1,25 +1,46 @@
package com.pig4cloud.pigx.admin.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
import com.aliyun.teaopenapi.models.Config;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pig4cloud.pigx.admin.api.entity.SysSocialDetails;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRoleListRequest;
import com.dingtalk.api.request.OapiUserListidRequest;
import com.dingtalk.api.request.OapiV2DepartmentListsubRequest;
import com.dingtalk.api.request.OapiV2UserGetRequest;
import com.dingtalk.api.response.OapiRoleListResponse;
import com.dingtalk.api.response.OapiUserListidResponse;
import com.dingtalk.api.response.OapiV2DepartmentListsubResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.pig4cloud.pigx.admin.api.entity.*;
import com.pig4cloud.pigx.admin.mapper.SysSocialDetailsMapper;
import com.pig4cloud.pigx.admin.service.ConnectService;
import com.pig4cloud.pigx.admin.service.*;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 互联平台实现
*
* <p>
* 钉钉: https://open-dev.dingtalk.com/apiExplorer
*
* @author lengleng
* @date 2022/4/22
*/
@SuppressWarnings("unchecked")
@Slf4j
@Service
@RequiredArgsConstructor
@@ -27,6 +48,14 @@ public class ConnectServiceImpl implements ConnectService {
private final SysSocialDetailsMapper sysSocialDetailsMapper;
private final SysRoleService sysRoleService;
private final SysDeptService sysDeptService;
private final SysUserService sysUserService;
private final SysUserRoleService sysUserRoleService;
private String getDingAccessToken() {
SysSocialDetails dingTalk = sysSocialDetailsMapper
.selectOne(Wrappers.<SysSocialDetails>lambdaQuery().eq(SysSocialDetails::getType, "ding"));
@@ -54,4 +83,204 @@ public class ConnectServiceImpl implements ConnectService {
return new com.aliyun.dingtalkoauth2_1_0.Client(config);
}
/**
* 同步钉钉角色
*
* @see <a href=
* "https://open.dingtalk.com/document/orgapp-server/obtains-a-list-of-enterprise-roles"/>
*/
@SneakyThrows
@Override
public Boolean syncDingRole() {
// 分页偏移量
long offset = 0L;
// 是否还有非同步的数据
boolean hasMore;
// 数据库中现有的角色
List<SysRole> roleList = sysRoleService.list(Wrappers.emptyWrapper());
// 钉钉同步过来的角色
List<SysRole> insertRole = new LinkedList<>();
do {
DingTalkClient client = new DefaultDingTalkClient(SecurityConstants.DING_OLD_ROLE_URL);
OapiRoleListRequest req = new OapiRoleListRequest();
// 分页查询,默认分页20条
req.setSize(2L);
req.setOffset(offset);
OapiRoleListResponse rsp = client.execute(req, getDingAccessToken());
offset += req.getSize();
JSONObject result = JSONUtil.parseObj(rsp.getBody());
JSONObject resultStr = result.getJSONObject("result");
hasMore = resultStr.get("hasMore", Boolean.class);
List<JSONObject> list = resultStr.get("list", List.class);
if (CollectionUtils.isNotEmpty(list)) {
list.forEach(item -> {
List<JSONObject> roles = (List<JSONObject>) item.get("roles");
if (CollectionUtils.isNotEmpty(roles)) {
// 过滤出数据库不存在的角色
List<SysRole> sysRoles = roles.stream().filter(
role -> roleList.stream().noneMatch(db -> db.getRoleName().equals(role.get("name"))))
.map(role -> {
SysRole sysRole = new SysRole();
sysRole.setRoleId(Convert.toLong(role.get("id")));
sysRole.setRoleName(Convert.toStr(role.get("name")));
return sysRole;
}).collect(Collectors.toList());
insertRole.addAll(sysRoles);
}
});
}
}
while (hasMore);
// 保存到数据库中
return sysRoleService.saveBatch(insertRole);
}
/**
* 同步钉钉部门
*
* @see <a href=
* "https://open.dingtalk.com/document/orgapp-server/obtain-the-department-list-v2"/>
*/
@Override
public Boolean syncDingDept() {
OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
req.setDeptId(1L);
req.setLanguage("zh_CN");
// 数据库中现有的部门
List<SysDept> deptList = sysDeptService.list(Wrappers.emptyWrapper());
// 查询根部门下的所有部门
List<SysDept> insertDept = queryChildDept(req, getDingAccessToken(), deptList);
// 去重
List<SysDept> syncDept = insertDept.stream().distinct().collect(Collectors.toList());
// 保存部门以及部门关系
if (CollectionUtils.isNotEmpty(syncDept)) {
syncDept.forEach(sysDeptService::saveDept);
}
return Boolean.TRUE;
}
/**
* 查询当前部门下的所有部门
*/
@SneakyThrows
private List<SysDept> queryChildDept(OapiV2DepartmentListsubRequest req, String token, List<SysDept> deptList) {
List<SysDept> sysDepts = new LinkedList<>();
// 调用Api查询
DingTalkClient client = new DefaultDingTalkClient(SecurityConstants.DING_OLD_DEPT_URL);
OapiV2DepartmentListsubResponse rsp = client.execute(req, token);
JSONObject result = JSONUtil.parseObj(rsp.getBody());
List<JSONObject> resultList = result.get("result", List.class);
if (CollectionUtils.isNotEmpty(resultList)) {
// 过滤出数据库存在的部门
List<SysDept> depts = resultList.stream()
.filter(dept -> deptList.stream().noneMatch(sysDept -> sysDept.getName().equals(dept.get("name"))))
.map(dept -> {
SysDept sysDept = new SysDept();
sysDept.setName(Convert.toStr(dept.get("name")));
sysDept.setDeptId(Convert.toLong(dept.get("dept_id")));
sysDept.setParentId(Convert.toLong(dept.get("parent_id")));
return sysDept;
}).collect(Collectors.toList());
// 递归查询下一级
if (CollectionUtils.isNotEmpty(depts)) {
sysDepts.addAll(depts);
depts.forEach(dept -> {
OapiV2DepartmentListsubRequest request = new OapiV2DepartmentListsubRequest();
request.setDeptId(dept.getDeptId());
request.setLanguage("zh_CN");
List<SysDept> childDept = queryChildDept(request, token, deptList);
sysDepts.addAll(childDept);
});
}
}
return sysDepts;
}
/**
* 同步部门下的钉钉用户
*
* @see <a href=
* "https://open.dingtalk.com/document/isvapp-server/query-the-list-of-department-userids"/>
* @see <a href=
* "https://open.dingtalk.com/document/isvapp-server/query-user-details"/>
*/
@SneakyThrows
@Override
public Boolean syncDingUser(Long deptId) {
// 数据库中现有用户
List<SysUser> users = sysUserService.list(Wrappers.emptyWrapper());
String token = getDingAccessToken();
// 查询部门下的用户id列表
DingTalkClient client = new DefaultDingTalkClient(SecurityConstants.DING_DEPT_USERIDS_URL);
OapiUserListidRequest req = new OapiUserListidRequest();
req.setDeptId(deptId);
OapiUserListidResponse rsp = client.execute(req, token);
JSONObject jsonObject = JSONUtil.parseObj(rsp.getBody());
JSONObject result = jsonObject.getJSONObject("result");
List<String> useridList = result.get("userid_list", List.class);
// 查询用户详情
if (CollectionUtils.isNotEmpty(useridList)) {
for (String userid : useridList) {
DingTalkClient userClient = new DefaultDingTalkClient(SecurityConstants.DING_USER_INFO_URL);
OapiV2UserGetRequest userGetRequest = new OapiV2UserGetRequest();
userGetRequest.setUserid(userid);
OapiV2UserGetResponse userGetResponse = userClient.execute(userGetRequest, token);
JSONObject userResult = JSONUtil.parseObj(userGetResponse.getBody());
JSONObject userObj = JSONUtil.parseObj(userResult.get("result"));
boolean exist = users.stream().anyMatch(user -> user.getPhone().equals(userObj.getStr("mobile")));
if (!exist) {
// 用户部门信息
JSONArray deptOrders = userObj.getJSONArray("dept_order_list");
JSONObject deptInfo = (JSONObject) deptOrders.get(0);
// 用户信息
SysUser sysUser = new SysUser();
sysUser.setDeptId(Convert.toLong(deptInfo.get("dept_id")));
sysUser.setAvatar(userObj.getStr("avatar"));
sysUser.setUsername(userObj.getStr("name"));
sysUser.setPhone(userObj.getStr("mobile"));
sysUser.setNickname(userObj.getStr("nickname"));
sysUser.setEmail(userObj.getStr("email"));
sysUserService.save(sysUser);
// 用户角色信息
JSONArray roleList = userObj.getJSONArray("role_list");
if (roleList != null && roleList.size() > 0) {
JSONObject roleInfo = (JSONObject) roleList.get(0);
Long userRoleId = Convert.toLong(roleInfo.get("id"));
SysUserRole sysUserRole = new SysUserRole();
sysUserRole.setUserId(sysUser.getUserId());
sysUserRole.setRoleId(userRoleId);
sysUserRoleService.save(sysUserRole);
}
}
}
}
return Boolean.TRUE;
}
}
+1
View File
@@ -46,6 +46,7 @@
<captcha.version>1.2.7</captcha.version>
<cas.sdk.version>3.6.4</cas.sdk.version>
<dingtalk.version>1.3.40</dingtalk.version>
<dingtalk.old.version>2.0.0</dingtalk.old.version>
<swagger.fox.version>3.0.0</swagger.fox.version>
<knife4j.version>2.0.9</knife4j.version>
<velocity.version>2.3</velocity.version>