From 1f2d0584e27b1f9b75a4c50012876d14974f439c Mon Sep 17 00:00:00 2001 From: xiang2lin <251481237@qq.com> Date: Thu, 5 Sep 2024 16:00:23 +0800 Subject: [PATCH] =?UTF-8?q?=E9=92=89=E9=92=89=E6=8E=A5=E5=8F=A3=202024?= =?UTF-8?q?=E5=B9=B49=E6=9C=885=E6=97=A516:00:12=20xel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 18 +- .../frame/dingtalk/enums/OrgEventEnum.java | 48 +++++ .../dingtalk/service/IDingTalkExtService.java | 32 ++++ .../dingtalk/service/IDingTalkService.java | 66 +++++++ .../service/impl/DingTalkExtServiceImpl.java | 100 +++++++++++ .../service/impl/DingTalkServiceImpl.java | 164 ++++++++++++++++++ .../dingtalk/util/DingTalkAccessToken.java | 103 +++++++++++ 7 files changed, 530 insertions(+), 1 deletion(-) create mode 100644 service/src/main/java/com/hzya/frame/dingtalk/enums/OrgEventEnum.java create mode 100644 service/src/main/java/com/hzya/frame/dingtalk/service/IDingTalkExtService.java create mode 100644 service/src/main/java/com/hzya/frame/dingtalk/service/IDingTalkService.java create mode 100644 service/src/main/java/com/hzya/frame/dingtalk/service/impl/DingTalkExtServiceImpl.java create mode 100644 service/src/main/java/com/hzya/frame/dingtalk/service/impl/DingTalkServiceImpl.java create mode 100644 service/src/main/java/com/hzya/frame/dingtalk/util/DingTalkAccessToken.java diff --git a/pom.xml b/pom.xml index 73a24eaf..d7c2e49a 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,8 @@ 8.0.3 42.2.6 1.7.4 - + 1.3.7 + 2.1.46 @@ -383,6 +384,21 @@ org.springframework.boot spring-boot-starter-data-mongodb + + com.dingtalk.open + app-stream-client + ${dingtalk-stream-sdk.version} + + + com.aliyun + dingtalk + ${dingtalk-sdk.version} + + + com.aliyun + alibaba-dingtalk-service-sdk + 2.0.0 + diff --git a/service/src/main/java/com/hzya/frame/dingtalk/enums/OrgEventEnum.java b/service/src/main/java/com/hzya/frame/dingtalk/enums/OrgEventEnum.java new file mode 100644 index 00000000..f01431f0 --- /dev/null +++ b/service/src/main/java/com/hzya/frame/dingtalk/enums/OrgEventEnum.java @@ -0,0 +1,48 @@ +package com.hzya.frame.dingtalk.enums; + +/** + * @Description 通讯录事件类型 + * @Author xiangerlin + * @Date 2024/8/27 15:58 + **/ +public enum OrgEventEnum { + USER_ADD_ORG("user_add_org","通讯录用户新增"), + USER_MODIFY_ORG("user_modify_org","通讯录用户更改"), + USER_LEAVE_ORG("user_leave_org","通讯录用户离职"), + USER_ACTIVE_ORG("user_active_org","加入企业后用户激活"), + ORG_DEPT_CREATE("org_dept_create","通讯录企业部门创建"), + ORG_DEPT_MODIFY("org_dept_modify","通讯录企业部门更改"), + ORG_DEPT_REMOVE("org_dept_remove","通讯录企业部门删除"), + ; + + + private String code; + private String explain; + + OrgEventEnum(String code, String explain) { + this.code = code; + this.explain = explain; + } + + public String getCode() { + return code; + } + + public String getExplain() { + return explain; + } + + /** + * 根据code获取事件类型 + * @param code + * @return + */ + public static OrgEventEnum getByCode(String code){ + for (OrgEventEnum org : OrgEventEnum.values()) { + if (org.getCode().equals(code)){ + return org; + } + } + return null; + } +} diff --git a/service/src/main/java/com/hzya/frame/dingtalk/service/IDingTalkExtService.java b/service/src/main/java/com/hzya/frame/dingtalk/service/IDingTalkExtService.java new file mode 100644 index 00000000..ffbba432 --- /dev/null +++ b/service/src/main/java/com/hzya/frame/dingtalk/service/IDingTalkExtService.java @@ -0,0 +1,32 @@ +package com.hzya.frame.dingtalk.service; + +import com.alibaba.fastjson.JSONObject; +import com.hzya.frame.sysnew.application.entity.SysApplicationEntity; +import com.hzya.frame.sysnew.application.entity.SysExtensionApiEntity; + +/** + * @Description 钉钉集成扩展类 + * @Author xiangerlin + * @Date 2024/8/28 14:25 + **/ +public interface IDingTalkExtService { + + /** + * 调用这个方法初始化钉钉参数 + * @param entity + * @return + */ + SysExtensionApiEntity init(SysExtensionApiEntity entity); + + /** + * 查询配置在应用上的钉钉参数 + * @param sysApplication + * @return + */ + JSONObject getDingTalkConfig(SysApplicationEntity sysApplication); + + /** + * 清空配置缓存 + */ + void clearDingTalkConfigCatch(); +} diff --git a/service/src/main/java/com/hzya/frame/dingtalk/service/IDingTalkService.java b/service/src/main/java/com/hzya/frame/dingtalk/service/IDingTalkService.java new file mode 100644 index 00000000..8429bc1c --- /dev/null +++ b/service/src/main/java/com/hzya/frame/dingtalk/service/IDingTalkService.java @@ -0,0 +1,66 @@ +package com.hzya.frame.dingtalk.service; + +import com.dingtalk.api.request.OapiV2UserListRequest; +import com.dingtalk.api.response.OapiV2DepartmentGetResponse; +import com.dingtalk.api.response.OapiV2DepartmentListsubResponse; +import com.dingtalk.api.response.OapiV2UserGetResponse; +import com.dingtalk.api.response.OapiV2UserListResponse; + +import java.util.List; + +/** + * @Description 钉钉service + * @Author xiangerlin + * @Date 2024/8/27 16:17 + **/ +public interface IDingTalkService { + + /** + * 根据userid获取用户详情 + * @param userId 钉钉userid + * @param appKey + * @param appSecret + * @return + */ + OapiV2UserGetResponse.UserGetResponse getUserById(String userId,String appKey,String appSecret); + + /** + * 根据userid获取用户详情 + * @param userId + * @return + */ + OapiV2UserGetResponse.UserGetResponse getUserById(String userId); + + /** + * 获取部门用户列表 + * @param req 请求参数 + * @param appKey + * @param appSecret + * @return + */ + OapiV2UserListResponse.PageResult getUserListByDeptId(OapiV2UserListRequest req, String appKey, String appSecret); + /** + * 根据部门id获取部门详情 + * @param deptId 钉钉部门id + * @param appKey + * @param appSecret + * @return + */ + OapiV2DepartmentGetResponse.DeptGetResponse getDeptById(Long deptId,String appKey,String appSecret); + + /** + * 根据部门id获取部门详情 + * @param deptId + * @return + */ + OapiV2DepartmentGetResponse.DeptGetResponse getDeptById(Long deptId); + + /** + * 获取部门列表,此接口只会返回下一级部门信息 + * @param deptId 部门id,如果不传则查询一级部门 + * @param appKey + * @param appSecret + * @return + */ + List getDeptList(Long deptId,String appKey,String appSecret); +} diff --git a/service/src/main/java/com/hzya/frame/dingtalk/service/impl/DingTalkExtServiceImpl.java b/service/src/main/java/com/hzya/frame/dingtalk/service/impl/DingTalkExtServiceImpl.java new file mode 100644 index 00000000..61dc97fa --- /dev/null +++ b/service/src/main/java/com/hzya/frame/dingtalk/service/impl/DingTalkExtServiceImpl.java @@ -0,0 +1,100 @@ +package com.hzya.frame.dingtalk.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.hzya.frame.dingtalk.service.IDingTalkExtService; +import com.hzya.frame.dingtalk.util.DingTalkAccessToken; +import com.hzya.frame.sysnew.application.api.entity.SysApplicationApiEntity; +import com.hzya.frame.sysnew.application.apiPara.dao.ISysApplicationApiParaDao; +import com.hzya.frame.sysnew.application.apiPara.entity.SysApplicationApiParaEntity; +import com.hzya.frame.sysnew.application.apiPara.service.ISysApplicationApiParaService; +import com.hzya.frame.sysnew.application.entity.SysApplicationEntity; +import com.hzya.frame.sysnew.application.entity.SysExtensionApiEntity; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @Description 钉钉集成扩展类 + * @Author xiangerlin + * @Date 2024/8/28 14:25 + **/ +@Service +public class DingTalkExtServiceImpl implements IDingTalkExtService { + + + @Resource + private ISysApplicationApiParaDao sysApplicationApiParaDao; + private final ConcurrentHashMap dingTalkMap = new ConcurrentHashMap<>(); + /** + * 调用这个方法初始化钉钉参数 + * + * @param entity + * @return + */ + @Override + public SysExtensionApiEntity init(SysExtensionApiEntity entity) { + Map headers = entity.getHeaders(); + if (null == headers){ + headers = new HashMap<>(); + } + SysApplicationEntity receiveApp = entity.getReceiveApp(); + //查询应用上配置的参数 + JSONObject dingTalkConfig = getDingTalkConfig(receiveApp); + //给token赋值 + entity.setQuerys("access_token="+DingTalkAccessToken.getAccessToken(dingTalkConfig.getString("appKey"),dingTalkConfig.getString("appSecret"))); + return entity; + } + + /** + * 查询配置在应用上的钉钉参数 + * + * @param sysApplication + * @return + */ + @Override + public JSONObject getDingTalkConfig(SysApplicationEntity sysApplication) { + if (null != sysApplication && StrUtil.isNotEmpty(sysApplication.getId()) && null != sysApplication.getAppId()){ + JSONObject jsonObject = new JSONObject(); + String key = sysApplication.getAppId()+"dingTalk"; + if (null != dingTalkMap.get(key)){ + return dingTalkMap.get(key); + }else { + //查询应用上配置的参数 + SysApplicationApiParaEntity paraEntity = new SysApplicationApiParaEntity(); + paraEntity.setAppId(sysApplication.getId()); + List paraList = sysApplicationApiParaDao.query(paraEntity); + if (CollectionUtils.isNotEmpty(paraList)) { + List appKeyList = paraList.stream().filter(p -> p.getInterfaceKey().equals("appKey")).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(appKeyList)) { + jsonObject.put("appKey", appKeyList.get(0).getInterfaceValue()); + } + List appSecretList = paraList.stream().filter(p -> p.getInterfaceKey().equals("appSecret")).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(appSecretList)) { + jsonObject.put("appSecret", appSecretList.get(0).getInterfaceValue()); + } + dingTalkMap.put(key,jsonObject); + return dingTalkMap.get(key); + } + } + } + return null; + } + + /** + * 清空配置缓存 + */ + @Override + public void clearDingTalkConfigCatch() { + dingTalkMap.clear(); + } +} diff --git a/service/src/main/java/com/hzya/frame/dingtalk/service/impl/DingTalkServiceImpl.java b/service/src/main/java/com/hzya/frame/dingtalk/service/impl/DingTalkServiceImpl.java new file mode 100644 index 00000000..89b4bd3e --- /dev/null +++ b/service/src/main/java/com/hzya/frame/dingtalk/service/impl/DingTalkServiceImpl.java @@ -0,0 +1,164 @@ +package com.hzya.frame.dingtalk.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.dingtalk.api.DefaultDingTalkClient; +import com.dingtalk.api.DingTalkClient; +import com.dingtalk.api.request.OapiV2DepartmentGetRequest; +import com.dingtalk.api.request.OapiV2DepartmentListsubRequest; +import com.dingtalk.api.request.OapiV2UserGetRequest; +import com.dingtalk.api.request.OapiV2UserListRequest; +import com.dingtalk.api.response.OapiV2DepartmentGetResponse; +import com.dingtalk.api.response.OapiV2DepartmentListsubResponse; +import com.dingtalk.api.response.OapiV2UserGetResponse; +import com.dingtalk.api.response.OapiV2UserListResponse; +import com.hzya.frame.dingtalk.service.IDingTalkService; +import com.hzya.frame.dingtalk.util.DingTalkAccessToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @Description 钉钉service + * @Author xiangerlin + * @Date 2024/8/27 16:17 + **/ +@Service +public class DingTalkServiceImpl implements IDingTalkService { + Logger logger = LoggerFactory.getLogger(getClass()); + @Value("${dingtalk.appKey:}") + private String dAppKey; + @Value("${dingtalk.appSecret:}") + private String dAppSecret; + + + /** + * 根据userid获取用户详情 + * + * @param userId 钉钉userid + * @param appKey + * @param appSecret + * @return + */ + @Override + public OapiV2UserGetResponse.UserGetResponse getUserById(String userId, String appKey, String appSecret) { + DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get"); + OapiV2UserGetRequest req = new OapiV2UserGetRequest(); + req.setUserid(userId); + req.setLanguage("zh_CN"); + try { + OapiV2UserGetResponse rsp = client.execute(req, DingTalkAccessToken.getAccessToken(appKey,appSecret)); + if (rsp.isSuccess()){ + OapiV2UserGetResponse.UserGetResponse result = rsp.getResult(); + String s = JSONObject.toJSONString(result); + logger.info("人员详情信息:{}",s); + return result; + } + }catch (Exception e){ + logger.error("根据部门id获取钉钉用户详情出错:{}",e); + } + return null; + } + + /** + * 根据userid获取用户详情 + * + * @param userId + * @return + */ + @Override + public OapiV2UserGetResponse.UserGetResponse getUserById(String userId) { + return getUserById(userId,dAppKey,dAppSecret); + } + + /** + * 获取部门用户列表 + * + * @param req 请求参数 + * @param appKey + * @param appSecret + * @return + */ + @Override + public OapiV2UserListResponse.PageResult getUserListByDeptId(OapiV2UserListRequest req, String appKey, String appSecret) { + try { + DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/list"); + req.setSize(100L);//每页最大只能查100条 + req.setOrderField("modify_desc"); + req.setContainAccessLimit(false); + req.setLanguage("zh_CN"); + OapiV2UserListResponse rsp = client.execute(req, DingTalkAccessToken.getAccessToken(appKey,appSecret)); + OapiV2UserListResponse.PageResult result = rsp.getResult(); + return result; + }catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + /** + * 根据部门id获取部门详情 + * + * @param deptId 钉钉部门id + * @param appKey + * @param appSecret + * @return + */ + @Override + public OapiV2DepartmentGetResponse.DeptGetResponse getDeptById(Long deptId, String appKey, String appSecret) { + DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/get"); + OapiV2DepartmentGetRequest req = new OapiV2DepartmentGetRequest(); + req.setDeptId(deptId); + req.setLanguage("zh_CN"); + try { + OapiV2DepartmentGetResponse rsp = client.execute(req, DingTalkAccessToken.getAccessToken(appKey,appSecret)); + if (rsp.isSuccess()){ + OapiV2DepartmentGetResponse.DeptGetResponse result = rsp.getResult(); + String s = JSONObject.toJSONString(result); + logger.info("部门详情信息:{}",s); + return result; + } + }catch(Exception e){ + logger.error("根据部门id获取钉钉部门出错:{}",e); + } + return null; + } + + /** + * 根据部门id获取部门详情 + * + * @param deptId + * @return + */ + @Override + public OapiV2DepartmentGetResponse.DeptGetResponse getDeptById(Long deptId) { + return getDeptById(deptId,dAppKey,dAppSecret); + } + + /** + * 获取部门列表,此接口只会返回下一级部门信息 + * @param deptId 部门id,如果不传则查询一级部门 + * @param appKey + * @param appSecret + * @return + */ + @Override + public List getDeptList(Long deptId,String appKey,String appSecret) { + try { + DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub"); + OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest(); + req.setDeptId(deptId); + req.setLanguage("zh_CN"); + OapiV2DepartmentListsubResponse rsp = client.execute(req, DingTalkAccessToken.getAccessToken(appKey,appSecret)); + if (rsp.isSuccess()){ + List result = rsp.getResult(); + return result; + } + }catch (Exception e){ + logger.error("获取部门列表接口出错:{}",e); + } + return null; + } +} diff --git a/service/src/main/java/com/hzya/frame/dingtalk/util/DingTalkAccessToken.java b/service/src/main/java/com/hzya/frame/dingtalk/util/DingTalkAccessToken.java new file mode 100644 index 00000000..eb951180 --- /dev/null +++ b/service/src/main/java/com/hzya/frame/dingtalk/util/DingTalkAccessToken.java @@ -0,0 +1,103 @@ +package com.hzya.frame.dingtalk.util; + +import cn.hutool.core.util.StrUtil; +import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse; +import com.aliyun.tea.TeaException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; + +import java.time.Instant; + +/** + * @Description 钉钉获取accessToken + * @Author xiangerlin + * @Date 2024/8/27 14:05 + **/ +public class DingTalkAccessToken { + static Logger logger = LoggerFactory.getLogger(DingTalkAccessToken.class); + //token + private static String accessToken; + //过期时间 + private static Instant expireTime; + private static final Long CACHE_EXPIRY_TIME = 7000L; // 缓存有效时间(秒) + //应用key + private static String appKey; + //应用密钥 + private static String appSecret; + @Value("${dingtalk.appKey:}") + public static void setAppKey(String appKey) { + DingTalkAccessToken.appKey = appKey; + } + @Value("${dingtalk.appSecret:}") + public static void setAppSecret(String appSecret) { + DingTalkAccessToken.appSecret = appSecret; + } + + /** + * 获取token + * @return + */ + public static String getAccessToken(){ + return getAccessToken(appKey,appSecret); + } + /** + * 获取accessToken + * + * @param appKey + * @param appSecret + * @return + */ + public static String getAccessToken(String appKey,String appSecret) { + //判断是否过期 如果没过期直接返回 + if (null != accessToken && expireTime != null && Instant.now().isBefore(expireTime)) { + return accessToken; + } + //获取新的accessToken + accessToken = fetchNewAccessToken(appKey,appSecret); + //过期时间设置成当前事件+7000s,预留200s的时间 + expireTime = Instant.now().plusSeconds(CACHE_EXPIRY_TIME); + return accessToken; + } + + /** + * 获取新的accessToken + * + * @return + */ + private static String fetchNewAccessToken(String appKey,String appSecret) { + try { + //查询应用上配置的钉钉信息 + if (StrUtil.isNotEmpty(appKey) && StrUtil.isNotEmpty(appSecret)) { + //查询应用上的信息 + com.aliyun.dingtalkoauth2_1_0.Client client = DingTalkAccessToken.createClient(); + com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest getAccessTokenRequest = new com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest() + .setAppKey(appKey) + .setAppSecret(appSecret); + GetAccessTokenResponse accessToken = client.getAccessToken(getAccessTokenRequest); + String accessToken1 = accessToken.getBody().getAccessToken(); + return accessToken1; + } + } catch (Exception _err) { + TeaException err = new TeaException(_err.getMessage(), _err); + if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) { + // err 中含有 code 和 message 属性,可帮助开发定位问题 + } + logger.error("获取钉钉token出错:{}", _err); + } + return null; + } + + /** + * 使用 Token 初始化账号Client + * + * @return Client + * @throws Exception + */ + private static com.aliyun.dingtalkoauth2_1_0.Client createClient() throws Exception { + com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config(); + config.protocol = "https"; + config.regionId = "central"; + return new com.aliyun.dingtalkoauth2_1_0.Client(config); + } +}