开发者后台配置
1.登录开发者后台并配置
首先需要登录开发者后台,配置企业内部应用,如图
可以点击【返回旧版】,个人比较习惯了旧版的操作风格,旧版风格如下
2.创建应用
选中【H5微应用】,点击【创建应用】
应用创建完成之后可以看到如图
记录下对应的AgentId、AppKey、AppSecret,上面步骤都操作完成之后需要开启对应权限,由于我们需要获取部门及人员信息,因此需要开启对应权限,点击【权限管理】选择需要的权限点击【申请权限】即可,如图
java开发阶段
1.所需钉钉接口文档
完成了【开发者后台】配置后,就可以进入开发阶段了,首先确定需要两个接口,文档为获取部门列表和获取部门用户详情,仔细阅读完文档后进入后面操作。
2.springboot项目配置类
添加配置类DingDingProperties.java读取配置信息,代码中给出的对应key信息为默认信息,后面可通过配置文件设置
package com.dongao.project.config.properties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @ClassName:DingDingProperties * @author:dongao * @date 2022/1/27 10:23 */ @Component @ConfigurationProperties(prefix = "dingtalk.msg") public class DingDingProperties { public String agentId = "1111111"; public String appKey = "dingbmxxxggddfdf"; public String appSecret = "KIy8DasdfghbbdhghdvgfdfC5gztU-m1nFYQfiUUil-6pnHJN-wU_Xv"; public String getAgentId() { return agentId; } public void setAgentId(String agentId) { this.agentId = agentId; } public String getAppKey() { return appKey; } public void setAppKey(String appKey) { this.appKey = appKey; } public String getAppSecret() { return appSecret; } public void setAppSecret(String appSecret) { this.appSecret = appSecret; } }
代码中的配置参数可以通过application.properties文件进行更改
dingtalk.msg.agentId=12345345 dingtalk.msg.appKey=fggsfhnhhjsdfdsf dingtalk.msg.appSecret=234dfgfhbvbhsdfdsgsf-fgdfhghbbdhjj-6pnHJN-wU_Xv
3.pom.xml
pom.xml文件引入钉钉jar包依赖
<dependency> <groupId>com.aliyun</groupId> <artifactId>alibaba-dingtalk-service-sdk</artifactId> <version>2.0.0</version> </dependency>
4.编写接口及实现类
编写钉钉接口IDingDingService.java,这里面也包括了发送钉钉消息相关的内容,此文可以忽略,只关注获取钉钉部门及部门下人员信息即可
package com.dongao.project.common.dingding; import com.dingtalk.api.response.*; import com.taobao.api.ApiException; import org.springframework.web.multipart.MultipartFile; /** * Created by nao'nao on 2020/3/31. * @author dingding * 工作通知消息的发送限制 *(1)企业开发者每分钟最多可调用接口1500次,ISV开发者每分钟最多可调用接口1000次 *(2)企业发送消息单次最多只能给5000人发送,ISV发送消息单次最多能给1000人发送 *(3)给同一员工发送内容相同的消息,一天只能发一次 *(4)企业发送每个员工每天最多可发送500条,ISV方式最多可发送50条 *(5)企业/ISV发送消息时每分钟最多只能有5000人可以接收到消息 */ public interface IDingDingService { /** * 调用钉钉上传文件 * (1) 图片(image):1MB,支持JPG格式 * (2)语音(voice):2MB,播放长度不超过60s,AMR格式 * (3)普通文件(file):10MB * @param type 文件类型 image file (voice暂不支持) * @param file 文件 * @return 返回上传成功对象 OapiMediaUploadResponse */ OapiMediaUploadResponse uploadMedia(String type, MultipartFile file); /** * 根据unionId获取userId * @param unionId 当前钉钉用户在当前企业下的唯一识别码 * @return */ OapiUserGetUseridByUnionidResponse getUserIdByUnionId(String unionId); /** * 根据unionId获取userId * @param unionId 当前钉钉用户在当前企业下的唯一识别码 * @return */ OapiUserGetbyunionidResponse getUserIdByUnionIdV2(String unionId); /** * 根据手机号获取userid * @param phone * @return */ OapiV2UserGetbymobileResponse getUserIdByPhone(String phone); /** * 发送消息调用 * @param msgInfo 发送消息的内容对象 * @return AjaxResult */ int sendMessage(MsgInfo msgInfo); /** * 获取部门列表 * @param accessToken * @param deptId * @return */ OapiV2DepartmentListsubResponse getDeptList(String accessToken, Long deptId) throws Exception; /** * 获取部门下用户信息列表 * @param accessToken * @param deptId * @param cursor * @param size * @return * @throws Exception */ OapiV2UserListResponse getUserListByDeptId(String accessToken, Long deptId, Long cursor, Long size) throws Exception; /** * 获取企业凭证 access_token * @return * @throws ApiException */ String getAccessToken() throws ApiException; /** * 获取部门详情 * @param accessToken * @param deptId * @return * @throws Exception */ OapiV2DepartmentGetResponse getDeptDetail(String accessToken, Long deptId) throws Exception; }
实现类DingDingServiceImpl.java
package com.dongao.project.common.dingding; import com.dingtalk.api.DefaultDingTalkClient; import com.dingtalk.api.DingTalkClient; import com.dingtalk.api.request.*; import com.dingtalk.api.response.*; import com.dongao.project.common.constants.Constants; import com.dongao.project.config.properties.DingDingProperties; import com.dongao.project.keymsghistory.domain.KeyMsgHistory; import com.dongao.project.keymsghistory.service.IKeyMsgHistoryService; import com.dongao.project.sysuser.service.ISysUserService; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.text.Convert; import com.taobao.api.ApiException; import com.taobao.api.FileItem; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.util.Objects; /** * Created by nao'nao on 2020/3/31. * @author dingding */ @Service public class DingDingServiceImpl implements IDingDingService { @Autowired private DingDingProperties dingDingProperties; @Autowired private ISysUserService sysUserService; @Autowired private IKeyMsgHistoryService msgHistoryService; /** * 调用钉钉发送工作通知消息 * @param useridList 员工在当前开发者企业账号范围内的userid * @param msgTemplet 消息模板 * @return 异步发送消息返回发送任务对象 OapiMessageCorpconversationAsyncsendV2Response */ private OapiMessageCorpconversationAsyncsendV2Response sendWorkMsg(String useridList, MsgTemplet msgTemplet) { try { //给钉钉用户发送工作通知消息 OapiMessageCorpconversationAsyncsendV2Response rsp = send(useridList, msgTemplet); return rsp; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 给钉钉用户发送工作通知消息 * @param useridList 接收者的用户userid列表,最大用户列表长度:100 zhangsan,lisi * @param msgTemplet 消息内容 * @return OapiMessageCorpconversationAsyncsendV2Response */ private OapiMessageCorpconversationAsyncsendV2Response send(String useridList, MsgTemplet msgTemplet) { //获取企业认证 try { String accessToken = getAccessToken(); //给钉钉用户发送工作通知消息 DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2"); OapiMessageCorpconversationAsyncsendV2Request req = new OapiMessageCorpconversationAsyncsendV2Request(); req.setAgentId(Long.parseLong(dingDingProperties.getAgentId())); req.setUseridList(useridList); //部门id串 ,分隔 ///req2.setDeptIdList("1"); //是否发送给企业全部用户 ///req2.setToAllUser(false); OapiMessageCorpconversationAsyncsendV2Request.Msg obj1 = new OapiMessageCorpconversationAsyncsendV2Request.Msg(); if (Constants.MsgType.TEXT.getValue().equals(msgTemplet.getTempletType())) { //发送文本消息 obj1.setMsgtype(Constants.MsgType.TEXT.getValue()); OapiMessageCorpconversationAsyncsendV2Request.Text obj2 = new OapiMessageCorpconversationAsyncsendV2Request.Text(); obj2.setContent(msgTemplet.getContent()); obj1.setText(obj2); }else if (Constants.MsgType.IMAGE.getValue().equals(msgTemplet.getTempletType())) { //发送图片信息 content即为图片media_id obj1.setMsgtype(Constants.MsgType.IMAGE.getValue()); OapiMessageCorpconversationAsyncsendV2Request.Image obj2 = new OapiMessageCorpconversationAsyncsendV2Request.Image(); obj2.setMediaId(msgTemplet.getContent()); obj1.setImage(obj2); }else if (Constants.MsgType.FILE.getValue().equals(msgTemplet.getTempletType())) { //发送文件信息 content即为图片media_id obj1.setMsgtype(Constants.MsgType.FILE.getValue()); OapiMessageCorpconversationAsyncsendV2Request.File obj2 = new OapiMessageCorpconversationAsyncsendV2Request.File(); obj2.setMediaId(msgTemplet.getContent()); obj1.setFile(obj2); }else if (Constants.MsgType.LINK.getValue().equals(msgTemplet.getTempletType())) { obj1.setMsgtype(Constants.MsgType.LINK.getValue()); OapiMessageCorpconversationAsyncsendV2Request.Link obj2 = new OapiMessageCorpconversationAsyncsendV2Request.Link(); obj2.setPicUrl(msgTemplet.getPicUrl()); obj2.setMessageUrl(msgTemplet.getMessageUrl()); obj2.setText(msgTemplet.getContent()); obj2.setTitle(msgTemplet.getTitle()); obj1.setLink(obj2); }else if (Constants.MsgType.MARKDOWN.getValue().equals(msgTemplet.getTempletType())) { obj1.setMsgtype(Constants.MsgType.MARKDOWN.getValue()); OapiMessageCorpconversationAsyncsendV2Request.Markdown obj2 = new OapiMessageCorpconversationAsyncsendV2Request.Markdown(); obj2.setText(msgTemplet.getContent()); obj2.setTitle(msgTemplet.getTitle()); obj1.setMarkdown(obj2); } req.setMsg(obj1); OapiMessageCorpconversationAsyncsendV2Response rsp = client.execute(req, accessToken); return rsp; } catch (ApiException e) { e.printStackTrace(); } return null; } /** * 获取企业凭证 access_token */ @Override public String getAccessToken() throws ApiException { //获取企业凭证 access_token 正常情况下access_token有效期为7200秒,有效期内重复获取返回相同结果,并自动续期。 DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken"); OapiGettokenRequest req = new OapiGettokenRequest(); req.setAppkey(dingDingProperties.getAppKey()); req.setAppsecret(dingDingProperties.getAppSecret()); req.setHttpMethod("GET"); OapiGettokenResponse rsp = client.execute(req); return rsp.getAccessToken(); } /** * 调用钉钉上传文件 * (1) 图片(image):1MB,支持JPG格式 * (2)语音(voice):2MB,播放长度不超过60s,AMR格式 * (3)普通文件(file):10MB * @param type 文件类型 image file (voice暂不支持) * @param file 文件 * @return */ @Override public OapiMediaUploadResponse uploadMedia(String type, MultipartFile file) { try { //获取企业凭证 access_token String accessToken = getAccessToken(); DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/media/upload"); OapiMediaUploadRequest req = new OapiMediaUploadRequest(); req.setType(type); req.setMedia(new FileItem(file.getOriginalFilename(),file.getInputStream())); OapiMediaUploadResponse rsp = client.execute(req, accessToken); return rsp; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 根据unionId获取userId * @param unionId 当前钉钉用户在当前企业下的唯一识别码 * @return */ @Override public OapiUserGetUseridByUnionidResponse getUserIdByUnionId(String unionId) { try { String accessToken = getAccessToken(); //根据unionId获取userId DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/getUseridByUnionid"); OapiUserGetUseridByUnionidRequest req = new OapiUserGetUseridByUnionidRequest(); req.setUnionid(unionId); req.setHttpMethod("GET"); OapiUserGetUseridByUnionidResponse rsp = client.execute(req, accessToken); return rsp; } catch (ApiException e) { e.printStackTrace(); } return null; } /** * 根据unionId获取userId 2.0版本 * @param unionId 当前钉钉用户在当前企业下的唯一识别码 * @return */ @Override public OapiUserGetbyunionidResponse getUserIdByUnionIdV2(String unionId) { try { String accessToken = getAccessToken(); //根据unionId获取userId DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid"); OapiUserGetbyunionidRequest req = new OapiUserGetbyunionidRequest(); req.setUnionid(unionId); OapiUserGetbyunionidResponse rsp = client.execute(req, accessToken); return rsp; } catch (ApiException e) { e.printStackTrace(); } return null; } /** * 根据手机号获取userid * @param phone * @return */ @Override public OapiV2UserGetbymobileResponse getUserIdByPhone(String phone){ try { String accessToken = getAccessToken(); DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/getbymobile"); OapiV2UserGetbymobileRequest req = new OapiV2UserGetbymobileRequest(); req.setMobile(phone); OapiV2UserGetbymobileResponse rsp = client.execute(req, accessToken); return rsp; } catch (ApiException e) { e.printStackTrace(); } return null; } /** * 发送消息调用 * @param msgInfo 发送消息的内容对象 * @return AjaxResult */ @Override public int sendMessage(MsgInfo msgInfo) { String userIdsStr = msgInfo.getUserIds(); if (msgInfo != null && StringUtils.isNotEmpty(userIdsStr)) { //获取用户userId对应的钉钉平台用户userid String[] userIds = Convert.toStrArray(userIdsStr); String useridList = sysUserService.selectDingdingUseridList(userIds); if (StringUtils.isNotEmpty(useridList)) { //获取消息模板 MsgTemplet msgTemplet = msgInfo.getMsgTemplet(); if (msgTemplet != null) { OapiMessageCorpconversationAsyncsendV2Response rsp = sendWorkMsg(useridList, msgTemplet); //插入消息发送记录表 KeyMsgHistory msgHistory = new KeyMsgHistory(); for (String userId:userIds) { msgHistory.setUserId(Long.parseLong(userId)); if (Objects.nonNull(rsp)){ msgHistory.setErrCode(rsp.getErrcode()); msgHistory.setErrMsg(rsp.getErrmsg()); msgHistory.setTaskId(rsp.getTaskId()); } msgHistory.setContent(msgTemplet.getContent()); msgHistory.setType(msgTemplet.getTempletType()); msgHistoryService.insertKeyMsgHistory(msgHistory); } if (Objects.nonNull(rsp) && rsp.isSuccess()) { //success return 1; } } } } return 0; } /** * 获取部门列表 * @throws Exception */ @Override public OapiV2DepartmentListsubResponse getDeptList(String accessToken, Long deptId) throws Exception { try { if (StringUtils.isEmpty(accessToken)) { accessToken = getAccessToken(); } DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub"); OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest(); if (deptId != null) { req.setDeptId(deptId); } OapiV2DepartmentListsubResponse rsp = client.execute(req, accessToken); return rsp; } catch (ApiException e) { e.printStackTrace(); return null; } } /** * 获取部门下用户信息列表 * @throws Exception */ @Override public OapiV2UserListResponse getUserListByDeptId(String accessToken, Long deptId, Long cursor, Long size) throws Exception { try { if (StringUtils.isEmpty(accessToken)) { accessToken = getAccessToken(); } DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/list"); OapiV2UserListRequest req = new OapiV2UserListRequest(); req.setDeptId(deptId); req.setCursor(cursor); req.setSize(size); OapiV2UserListResponse rsp = client.execute(req, accessToken); return rsp; } catch (ApiException e) { e.printStackTrace(); return null; } } /** * 获取部门详情 * @param accessToken * @param deptId * @return * @throws Exception */ @Override public OapiV2DepartmentGetResponse getDeptDetail(String accessToken, Long deptId) throws Exception { try { if (StringUtils.isEmpty(accessToken)) { accessToken = getAccessToken(); } DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/get"); OapiV2DepartmentGetRequest req = new OapiV2DepartmentGetRequest(); req.setDeptId(deptId); OapiV2DepartmentGetResponse rsp = client.execute(req, accessToken); return rsp; } catch (ApiException e) { e.printStackTrace(); return null; } } }
5.获取钉钉部门及人员并保存
获取钉钉部门及部门下人员java类主要方法如下
/** * 保存钉钉部门人员信息 * @param param * @return */ @Override public AjaxResult saveDingDingDeptListV3(String param) { //逐级获取部门信息列表并保存 AjaxResult ajaxResult = new AjaxResult(); ajaxResult.setMsg("成功!"); try { String accessToken = dingDingService.getAccessToken(); if (StringUtils.isNotEmpty(param)) { //获取多个部门的部门信息 String[] split = param.split(","); for (String s : split) { //获取叶子部门列表 and 获取部门下人员信息 saveDeptListV3(ajaxResult, accessToken, Long.parseLong(s)); } }else { //获取根部门列表 and 获取部门下人员信息 saveDeptListV3(ajaxResult, accessToken, null); } }catch (Exception e) { e.printStackTrace(); } return ajaxResult; } private void saveDeptListV3(AjaxResult ajaxResult, String accessToken, Long reqDeptId) throws Exception { //保存当前部门下的人员信息 saveUserListV3(ajaxResult, accessToken, reqDeptId); //获取部门列表 OapiV2DepartmentListsubResponse deptrsp = dingDingService.getDeptList(accessToken, reqDeptId); if (deptrsp.isSuccess()) { List<OapiV2DepartmentListsubResponse.DeptBaseResponse> result = deptrsp.getResult(); if (CollectionUtils.isNotEmpty(result)) { SysDept sysDept = new SysDept(); for (OapiV2DepartmentListsubResponse.DeptBaseResponse dept : result) { Long deptId = dept.getDeptId(); if (deptId > 0) { //判定当前部门是否已存在,存在则不再新增 SysDept sysdept = sysDeptMapper.selectSysDeptByDeptId(deptId); if (sysdept == null) { sysDept.setDeptId(deptId); sysDept.setDeptName(dept.getName()); sysDept.setParentId(dept.getParentId()); sysDept.setCreateBy("定时任务拉取"); sysDept.setCreateTime(new Date()); //保存部门信息 sysDeptMapper.insertSysDept(sysDept); } } //继续获取当前部门下的子级部门 saveDeptListV3(ajaxResult, accessToken, deptId); } }else { ajaxResult.setMsg("当前部门已是叶子部门"); } }else { ajaxResult.setMsg("获取部门列表API调用失败"); } } private void saveUserListV3(AjaxResult ajaxResult, String accessToken, Long reqDeptId) throws Exception { //获取当前部门下的人员信息并保存 Boolean hasMore = false; OapiV2UserListResponse userrsp = dingDingService.getUserListByDeptId(accessToken, reqDeptId, 0L, 10L); if (userrsp.isSuccess()) { OapiV2UserListResponse.PageResult result = userrsp.getResult(); List<OapiV2UserListResponse.ListUserResponse> list = result.getList(); if (CollectionUtils.isNotEmpty(list)) { saveDingDingUserV3(list); //继续 hasMore = result.getHasMore(); while (hasMore) { userrsp = dingDingService.getUserListByDeptId(accessToken, reqDeptId, result.getNextCursor(), 10L); if (userrsp.isSuccess()) { result = userrsp.getResult(); list = result.getList(); saveDingDingUserV3(list); hasMore = result.getHasMore(); } } }else { ajaxResult.setMsg("当前部门下无人员信息"); } }else { ajaxResult.setMsg("获取部门人员列表API调用失败"); } } private void saveDingDingUserV3(List<OapiV2UserListResponse.ListUserResponse> list) { SysUser sysUser = new SysUser(); for (OapiV2UserListResponse.ListUserResponse user : list) { //判断是否已经插入过 String userid = user.getUserid(); int count = sysUserMapper.countSysUserByDingDingUserId(userid); if (count == 0) { sysUser.setDingdingUserid(user.getUserid()); sysUser.setAvatar(user.getAvatar()); sysUser.setUnionId(user.getUnionid()); String userName = user.getName(); sysUser.setUserName(userName); //中文转拼音作为loginName String loginName = PinYinUtils.getPingYin(userName); sysUser.setLoginName(loginName); List<Long> deptIdList = user.getDeptIdList(); if (CollectionUtils.isNotEmpty(deptIdList)) { for (Long deptid : deptIdList) { sysUser.setDeptId(deptid); sysUser.setCreateBy("定时任务拉取"); sysUser.setCreateTime(new Date()); sysUserMapper.insertSysUser(sysUser); } } } } }
6.补充
在获取钉钉部门及人员信息时用到了汉字转拼音jar,代码需要补充如下
pom.xml引入jar包
<dependency> <groupId>com.belerweb</groupId> <artifactId>pinyin4j</artifactId> <version>2.5.0</version> </dependency>
添加工具类PinYinUtils.java
package com.dongao.project.utils; import net.sourceforge.pinyin4j.PinyinHelper; import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType; import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; /** * @ClassName:PinYinUtils * @author:dongao * @date 2022/2/10 14:05 */ public class PinYinUtils { /** * 中文转拼音 * @param inputStr * @return */ public static String getPingYin(String inputStr) { if (inputStr == null || "".equals(inputStr)) { return ""; } HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); //UPPERCASE 大写 LOWERCASE 小写 format.setCaseType(HanyuPinyinCaseType.LOWERCASE); //WITH_TONE_NUMBER 音标用数字 WITHOUT_TONE 无音标 WITH_TONE_MARK 直接用音标 format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); //WITH_U_AND_COLON 用u表示ü WITH_V 用v表示ü WITH_U_UNICODE 用ü表示ü format.setVCharType(HanyuPinyinVCharType.WITH_V); StringBuilder pYStr = new StringBuilder(); char[] input = inputStr.trim().toCharArray(); try { for (int i = 0; i < input.length; i++) { if (Character.toString(input[i]).matches("[\\u4E00-\\u9FA5]+")) { pYStr.append(PinyinHelper.toHanyuPinyinStringArray(input[i], format)[0]); } else if (!(input[i] == ' ')) { //过滤空格 pYStr.append(input[i]); } } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } return pYStr.toString(); } }
总结:通过以上步骤即可以获取钉钉下部门及人员信息,sql表语句此处没有列出是大家可以自行根据上面set字段进行创建表即可