diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/SecurityConstants.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/SecurityConstants.java index e7f4cda77..0cc029d39 100644 --- a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/SecurityConstants.java +++ b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/SecurityConstants.java @@ -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} 加密的特征码 */ diff --git a/pigx-upms/pigx-upms-biz/pom.xml b/pigx-upms/pigx-upms-biz/pom.xml index 44bca5af8..740f16112 100644 --- a/pigx-upms/pigx-upms-biz/pom.xml +++ b/pigx-upms/pigx-upms-biz/pom.xml @@ -128,11 +128,18 @@ cas-client-core ${cas.sdk.version} + com.aliyun dingtalk ${dingtalk.version} + + + com.aliyun + alibaba-dingtalk-service-sdk + ${dingtalk.old.version} + org.springframework.boot diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/ConnectController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/ConnectController.java index 445268b61..ec9add850 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/ConnectController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/ConnectController.java @@ -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()); } /** diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/ConnectService.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/ConnectService.java index faedf2fb6..c0260fac0 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/ConnectService.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/ConnectService.java @@ -3,9 +3,24 @@ package com.pig4cloud.pigx.admin.service; /** * @author lengleng * @date 2022/4/22 - * + *

* 互联平台 */ public interface ConnectService { + /** + * 同步钉钉角色 + */ + Boolean syncDingRole(); + + /** + * 同步钉钉部门 + */ + Boolean syncDingDept(); + + /** + * 同步钉钉用户 + */ + Boolean syncDingUser(Long deptId); + } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java index 1a8f54aba..2b8da301e 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java @@ -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; + /** * 互联平台实现 - * + *

* 钉钉: 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.lambdaQuery().eq(SysSocialDetails::getType, "ding")); @@ -54,4 +83,204 @@ public class ConnectServiceImpl implements ConnectService { return new com.aliyun.dingtalkoauth2_1_0.Client(config); } + /** + * 同步钉钉角色 + * + * @see + */ + @SneakyThrows + @Override + public Boolean syncDingRole() { + // 分页偏移量 + long offset = 0L; + // 是否还有非同步的数据 + boolean hasMore; + // 数据库中现有的角色 + List roleList = sysRoleService.list(Wrappers.emptyWrapper()); + // 钉钉同步过来的角色 + List 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 list = resultStr.get("list", List.class); + + if (CollectionUtils.isNotEmpty(list)) { + list.forEach(item -> { + List roles = (List) item.get("roles"); + if (CollectionUtils.isNotEmpty(roles)) { + // 过滤出数据库不存在的角色 + List 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 + */ + @Override + public Boolean syncDingDept() { + OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest(); + req.setDeptId(1L); + req.setLanguage("zh_CN"); + + // 数据库中现有的部门 + List deptList = sysDeptService.list(Wrappers.emptyWrapper()); + + // 查询根部门下的所有部门 + List insertDept = queryChildDept(req, getDingAccessToken(), deptList); + + // 去重 + List syncDept = insertDept.stream().distinct().collect(Collectors.toList()); + + // 保存部门以及部门关系 + if (CollectionUtils.isNotEmpty(syncDept)) { + syncDept.forEach(sysDeptService::saveDept); + } + + return Boolean.TRUE; + } + + /** + * 查询当前部门下的所有部门 + */ + @SneakyThrows + private List queryChildDept(OapiV2DepartmentListsubRequest req, String token, List deptList) { + List 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 resultList = result.get("result", List.class); + + if (CollectionUtils.isNotEmpty(resultList)) { + // 过滤出数据库存在的部门 + List 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 childDept = queryChildDept(request, token, deptList); + sysDepts.addAll(childDept); + }); + } + } + + return sysDepts; + } + + /** + * 同步部门下的钉钉用户 + * + * @see + * @see + */ + @SneakyThrows + @Override + public Boolean syncDingUser(Long deptId) { + // 数据库中现有用户 + List 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 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; + } + } diff --git a/pom.xml b/pom.xml index bf21ae7ce..033360534 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ 1.2.7 3.6.4 1.3.40 + 2.0.0 3.0.0 2.0.9 2.3