基于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;
}

}