基于springboot项目的钉钉消息发送
此功能开发完成后已距当下十月之久,目前整理来方便以后查阅,此处仅做记录之用,他人亦可作为参考
MsgInfo.java
package com.dongao.project.common.dingding; import com.dongao.project.msgtemplet.domain.MsgTemplet; /** * Created by nao'nao on 2020/4/2. */ public class MsgInfo { private static final long serialVersionUID = 1L; /**参数1 可选*/ private String param1; /**参数2 可选*/ private String param2; /**参数3 可选*/ private String param3; /**参数4 可选*/ private String param4; /**参数5 可选*/ private String param5; /**参数6 可选*/ private String param6; /**参数7 可选*/ private String param7; /**参数8 可选*/ private String param8; /**参数9 可选*/ private String param9; /**参数10 可选*/ private String param10; /** 线索id 必选*/ private String clueId; /**消息对象 必选*/ private MsgTemplet msgTemplet; /**必选*/ private String userIds; public String getParam1() { return param1; } public void setParam1(String param1) { this.param1 = param1; } public String getParam2() { return param2; } public void setParam2(String param2) { this.param2 = param2; } public String getParam3() { return param3; } public void setParam3(String param3) { this.param3 = param3; } public String getParam4() { return param4; } public void setParam4(String param4) { this.param4 = param4; } public String getParam5() { return param5; } public void setParam5(String param5) { this.param5 = param5; } public String getParam6() { return param6; } public void setParam6(String param6) { this.param6 = param6; } public String getParam7() { return param7; } public void setParam7(String param7) { this.param7 = param7; } public String getParam8() { return param8; } public void setParam8(String param8) { this.param8 = param8; } public String getParam9() { return param9; } public void setParam9(String param9) { this.param9 = param9; } public String getParam10() { return param10; } public void setParam10(String param10) { this.param10 = param10; } public String getClueId() { return clueId; } public void setClueId(String clueId) { this.clueId = clueId; } public MsgTemplet getMsgTemplet() { return msgTemplet; } public void setMsgTemplet(MsgTemplet msgTemplet) { this.msgTemplet = msgTemplet; } public String getUserIds() { return userIds; } public void setUserIds(String userIds) { this.userIds = userIds; } }
IDingDingService.java
package com.dongao.project.common.dingding; import com.dingtalk.api.response.OapiMediaUploadResponse; import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response; import com.dingtalk.api.response.OapiUserGetUseridByUnionidResponse; import com.ruoyi.framework.web.domain.AjaxResult; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; /** * 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 */ public OapiMediaUploadResponse uploadMedia(String type, MultipartFile file); /** * 发送消息调用 * @param msgInfo 发送消息的内容对象 * @return AjaxResult */ public AjaxResult sendMessage(MsgInfo msgInfo); /** * 根据unionId获取userId * @param unionId 当前钉钉用户在当前企业下的唯一识别码 * @return */ public OapiUserGetUseridByUnionidResponse getUserIdByUnionId(String unionId); }
DingDingServiceImpl.java
package com.dongao.project.common.dingding; import com.dingtalk.api.DefaultDingTalkClient; import com.dingtalk.api.DingTalkClient; import com.dingtalk.api.request.OapiGettokenRequest; import com.dingtalk.api.request.OapiMediaUploadRequest; import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request; import com.dingtalk.api.request.OapiUserGetUseridByUnionidRequest; import com.dingtalk.api.response.OapiGettokenResponse; import com.dingtalk.api.response.OapiMediaUploadResponse; import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response; import com.dingtalk.api.response.OapiUserGetUseridByUnionidResponse; import com.dongao.project.common.constants.Constants; import com.dongao.project.config.ConstantConfig; import com.dongao.project.msghistory.domain.MsgHistory; import com.dongao.project.msghistory.service.IMsgHistoryService; import com.dongao.project.msgtemplet.domain.MsgTemplet; import com.dongao.project.sys.service.SysService; import com.ruoyi.common.utils.Md5Utils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.text.Convert; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.project.system.config.service.IConfigService; 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; /** * Created by nao'nao on 2020/3/31. * @author dingding */ @Service public class DingDingServiceImpl implements IDingDingService { @Autowired private IMsgHistoryService msgHistoryService; @Autowired private SysService sysService; @Autowired private IConfigService configService; /** * 调用钉钉发送工作通知消息 * @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(ConstantConfig.dingtalkMsgAgentId)); 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 */ private String getAccessToken() throws ApiException { //获取企业凭证 access_token 正常情况下access_token有效期为7200秒,有效期内重复获取返回相同结果,并自动续期。 DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken"); OapiGettokenRequest req = new OapiGettokenRequest(); req.setAppkey(ConstantConfig.dingtalkMsgAppKey); req.setAppsecret(ConstantConfig.dingtalkMsgAppSecret); 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; } /** * 发送消息调用 * @param msgInfo 发送消息的内容对象 * @return AjaxResult */ @Override public AjaxResult sendMessage(MsgInfo msgInfo) { String dingSwitch=configService.selectConfigByKey("ding.ding.switch"); if(!dingSwitch.equals("on")){ return AjaxResult.success("钉钉消息开关没有打开无法推送!"); } String userIdsStr = msgInfo.getUserIds(); if (msgInfo != null && StringUtils.isNotEmpty(userIdsStr)) { //获取用户userId对应的钉钉平台用户userid String[] userIds = Convert.toStrArray(userIdsStr); String useridList = sysService.selectDingdingUseridList(userIds); if (StringUtils.isNotEmpty(useridList)) { //获取消息模板 MsgTemplet msgTemplet = msgInfo.getMsgTemplet(); if (msgTemplet != null) { String content = msgTemplet.getContent(); //替换content中特定字段值 content = getContent(msgInfo, content); msgTemplet.setContent(content); //给跳转链接增加免密参数 String messageUrl = msgTemplet.getMessageUrl(); String projectUrl = msgTemplet.getProjectUrl(); if (StringUtils.isNotEmpty(projectUrl) && StringUtils.isNotEmpty(messageUrl)) { //加密 String sign = Md5Utils.hash(Constants.LOGIN_KEY + userIdsStr); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(projectUrl); stringBuilder.append("/loginFree?userId="); stringBuilder.append(userIdsStr); stringBuilder.append("&sign="); stringBuilder.append(sign); stringBuilder.append("&clueId="); stringBuilder.append(msgInfo.getClueId()); stringBuilder.append("&redirect_url="); stringBuilder.append(messageUrl); msgTemplet.setMessageUrl(stringBuilder.toString()); } //如果是markdown消息,链接不为空需要拼链接 if (Constants.MsgType.MARKDOWN.getValue().equals(msgTemplet.getTempletType()) && msgTemplet.getContent().contains("]") && StringUtils.isNotEmpty(msgTemplet.getMessageUrl())) { content = content.replace("]","]("+msgTemplet.getMessageUrl()+")"); msgTemplet.setContent(content); } OapiMessageCorpconversationAsyncsendV2Response rsp = sendWorkMsg(useridList, msgTemplet); // 2020-07-03 fdh 钉钉服务调取失败处理 if( rsp == null ){ return AjaxResult.error("钉钉推送消息失败"); } //插入消息发送记录表 MsgHistory msgHistory = new MsgHistory(); for (String userId:userIds) { msgHistory.setUserId(Long.parseLong(userId)); msgHistory.setErrCode(rsp.getErrcode()); msgHistory.setErrMsg(rsp.getErrmsg()); msgHistory.setContent(content); msgHistory.setTaskId(rsp.getTaskId()); msgHistory.setType(msgTemplet.getTempletType()); msgHistoryService.insertMsgHistory(msgHistory); } if (rsp.isSuccess()) { //success return AjaxResult.success("钉钉工作通知消息发送成功!"); }else { //error return AjaxResult.error(rsp.getErrmsg()); } }else { return AjaxResult.error("暂无消息模板,请添加后操作!"); } }else { return AjaxResult.error("用户不存在或者未绑定钉钉账号!"); } }else { return AjaxResult.error(); } } /** * 替换content中特定字段值 * @param msgInfo * @param content * @return */ private String getContent(MsgInfo msgInfo, String content) { //替换可选参数 content = content.replace("@param1@", msgInfo.getParam1() == null ? "" : msgInfo.getParam1()); content = content.replace("@param2@",msgInfo.getParam2() == null?"":msgInfo.getParam2()); content = content.replace("@param3@",msgInfo.getParam3() == null?"":msgInfo.getParam3()); content = content.replace("@param4@",msgInfo.getParam4() == null?"":msgInfo.getParam4()); content = content.replace("@param5@",msgInfo.getParam5() == null?"":msgInfo.getParam5()); content = content.replace("@param6@",msgInfo.getParam6() == null?"":msgInfo.getParam6()); content = content.replace("@param7@",msgInfo.getParam7() == null?"":msgInfo.getParam7()); content = content.replace("@param8@",msgInfo.getParam8() == null?"":msgInfo.getParam8()); content = content.replace("@param9@",msgInfo.getParam9() == null?"":msgInfo.getParam9()); content = content.replace("@param10@",msgInfo.getParam10() == null?"":msgInfo.getParam10()); return content; } /** * 根据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; } }