本文目录
2.3 步骤三、添加配置文件application-local.yml
另外实现一种方式,非常简单,代码粘下来就可以用:【Java用法】使用Java开发连接钉钉应用实现钉钉通知的功能
一、项目背景
项目架构是:Springboot (2.0.0.RELEASE) + maven (2.18.1) + mybatis-plus (3.1.1) + jdk1.8
前几天在钉钉创建了一个H5微应用,上线运行几天之后,又增加了新的需求,即当有人评论你的日报时,给你发送钉钉通知提醒一下,今天又通过另一种方式实现了钉钉通知的功能。
二、详细步骤
2.1 步骤一:添加依赖
<!-- 钉钉SDK -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
<version>1.0.1</version>
</dependency>
2.2 步骤二、添加使用的工具类
参考本文拓展里的内容
2.3 步骤三、添加配置文件application-local.yml
2.4 步骤四、在业务中添加发送钉钉通知的代码
@Override
public JsonResult addComment(User user, DailyJobComment dailyJobComment) {
if (user == null) {
log.error("E|DailyCommentServiceImpl|addComment()|新增日报评论时,获取当前登录人失败!");
return JsonResult.fail("获取当前登录人失败!");
}
String userName = user.getUserName();
String trueName = user.getTrueName();
dailyJobComment.setCreateUser(userName);
dailyJobComment.setCreateName(trueName);
dailyJobComment.setCreateTime(new Date());
int insert = dailyCommentMapper.insert(dailyJobComment);
if (insert > 0) {
log.info("E|DailyCommentServiceImpl|addComment()|新增日报评论成功,当前登录人 trueName = {}", trueName);
this.executeDingInform(dailyJobComment);
return JsonResult.ok("新增日报评论成功");
}
return JsonResult.fail("新增日报评论失败!");
}
/**
* 执行钉钉通知
*
* @param dailyJobComment 日报评论内容
*/
private void executeDingInform(DailyJobComment dailyJobComment) {
// 获取钉钉用户id
Integer jobId = dailyJobComment.getJobId();
DailyJob dailyJob = dailyJobMapper.selectById(jobId);
DailyViewUser dailyViewUser = dailyViewUserMapper.getDailyViewUserByUsername(dailyJob.getCreateUser());
String userName = dailyViewUser.getUserName();
String dingUserId = dailyViewUser.getDingUserId();
String createName = dailyJobComment.getCreateName();
// 评论创建时间
Date createTime = dailyJobComment.getCreateTime();
ThreadUtil.excAsync(new Runnable() {
@Override
public void run() {
DingTalkMsg dingTalkMsg = new DingTalkMsg();
dingTalkMsg.setAuthor(createName + " " + DateUtil.format(createTime, "yyyy年MM月dd日 HH:mm:ss"));
dingTalkMsg.setContent("");
dingTalkMsg.setTitle("日报评论通知");
dingTalkMsg.setMsgType("oa");
dingTalkMsg.setUserList(Objects.isNull(dingUserId) ? userName : dingUserId);
dingTalkMsg.setFromList(getFormList());
dingTalkWorkNotice.sendDingMsg(dingTalkMsg);
}
private List<OapiMessageCorpconversationAsyncsendV2Request.Form> getFormList() {
List<OapiMessageCorpconversationAsyncsendV2Request.Form> formList = DingDingMsg.create("【" + createName + "】评论了你【",
DateUtil.format(dailyJob.getCreateTime(), "yyyy-MM-dd HH:mm:ss") + "】创建的日报")
.builder("评论内容:", dailyJobComment.getContent())
.builder("评论时间:", DateUtil.format(createTime, "yyyy-MM-dd HH:mm:ss"))
.collect();
log.info("E|DailyCommentServiceImpl|getDingInformContent()|新增日报评论成功时,封装钉钉通知内容 = {}", formList);
return formList;
}
}, true);
}
三、最终效果
实现的最终效果是,当有人评论了你的日报时,钉钉客户端就会向你推送一条消息,至于推送的内容,可以自己定制的。以下就是我本次实现的最终效果。
四、拓展文件
2.2 步骤二中使用的工具类有以下五个(DingTalkMsg.class,DingTalkParams.class,DingDingMsg.class,DingTalkUrlConstants.class,DingTalkWorkNotice.class)
package com.iot.daily.dingding.domain;
import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* DingTalkMsg.java此类用于
*
* @author: hjm
* @date: 2021/4/14
* @remark:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DingTalkMsg {
@ApiModelProperty(value = "消息类型")
private String msgType;
@ApiModelProperty(value = "通知标题")
private String title;
@ApiModelProperty(value = "通知内容")
private String content;
@ApiModelProperty(value = "通知人员dingUserId列表")
private String userList;
@ApiModelProperty(value = "消息链接地址")
private String url;
@ApiModelProperty(value = "OA Form 表单内容")
private List<OapiMessageCorpconversationAsyncsendV2Request.Form> fromList;
@ApiModelProperty(value = "提醒人")
private String author;
}
package com.iot.daily.dingding.domain;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* @author openapi@dingtalk
* @date 2020/2/4
*/
@Configuration
@Data
public class DingTalkParams {
@Value("${dingtalk.app_key}")
private String appKey;
@Value("${dingtalk.app_secret}")
private String appSecret;
@Value("${dingtalk.agent_id}")
private long agentId;
@Value("${dingtalk.corp_id}")
private String corpId;
@Value("${dingtalk.is_open}")
private String isOpen;
}
package com.iot.daily.dingding.util;
import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
import java.util.ArrayList;
import java.util.List;
/**
* 构建钉钉发送消息
*
* @author hjm
* @date 2019-04-24
*/
public class DingDingMsg {
private List<OapiMessageCorpconversationAsyncsendV2Request.Form> mapList = new ArrayList<>();
private DingDingMsg() {
}
public static DingDingMsg create(String key, String value) {
return (new DingDingMsg()).builder(key, value);
}
public DingDingMsg builder(String key, String value) {
OapiMessageCorpconversationAsyncsendV2Request.Form form = new OapiMessageCorpconversationAsyncsendV2Request.Form();
form.setKey(key);
form.setValue(value);
mapList.add(form);
return this;
}
public List<OapiMessageCorpconversationAsyncsendV2Request.Form> collect() {
return this.mapList;
}
}
package com.iot.daily.common.constant;
/**
* <p>DingTalkUrlConstants 此类用于:调用钉钉API路径URL</p>
* <p>@author:hjm</p>
* <p>@date:2021年06月08日 11:20</p>
* <p>@remark:</p>
*/
public class DingTalkUrlConstants {
private static final String HOST = "https://oapi.dingtalk.com";
/**
* 钉钉网关 gettoken 地址
*/
public static final String URL_GET_TOKEN = HOST + "/gettoken";
/**
* 获取 jsapi_ticket 地址
*/
public static final String URL_GET_JSTICKET = HOST + "/get_jsapi_ticket";
/**
* 发送钉钉工作通知接口URL
*/
public static final String URL_SEND_WORK_NOTICE = HOST + "/topapi/message/corpconversation/asyncsend_v2";
/**
* 获取用户在企业内 userId 的接口URL
*/
public static final String URL_GET_USER_INFO = HOST + "/user/getuserinfo";
/**
* 获取用户姓名的接口URL
*/
public static final String URL_USER_GET = HOST + "/user/get";
/**
* 获取部门列表接口URL
*/
public static final String URL_DEPARTMENT_LIST = HOST + "/department/list";
/**
* 获取部门用户接口URL
*/
public static final String URL_USER_SIMPLELIST = HOST + "/user/simplelist";
}
package com.iot.daily.dingding.util;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGetJsapiTicketRequest;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
import com.dingtalk.api.response.OapiGetJsapiTicketResponse;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response;
import com.taobao.api.ApiException;
import com.iot.daily.common.constant.CommonConstants;
import com.iot.daily.common.constant.DingTalkUrlConstants;
import com.iot.daily.common.util.cache.RedisCache;
import com.iot.daily.dingding.domain.DingTalkMsg;
import com.iot.daily.dingding.domain.DingTalkParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* DingTalkWorkNotice.java此类用于钉钉自定义机器人
*
* @author:hjm
* @date:2021年4月7日
* @remark:
*/
@Slf4j
@Component
public class DingTalkWorkNotice {
/**
* 缓存时间:一小时50分钟
*/
private static final long CACHE_TTL = 60 * 55 * 2 * 1000;
@Resource
private DingTalkParams dingTalkParams;
@Resource
private RedisCache cache;
private final String IS_OPEN = "true";
private final String MSG_TYPE_OA = "oa";
private final String MSG_TYPE_LINK = "link";
private final String MSG_TYPE_ACTION_CARD = "action_card";
/**
* 向钉钉发送消息
*
* @param dingTalkMsg 实例对象
*/
public void sendDingMsg(DingTalkMsg dingTalkMsg) {
String msgType = dingTalkMsg.getMsgType();
String title = dingTalkMsg.getTitle();
String content = dingTalkMsg.getContent();
String userList = dingTalkMsg.getUserList();
String url = dingTalkMsg.getUrl();
List<OapiMessageCorpconversationAsyncsendV2Request.Form> fromList = dingTalkMsg.getFromList();
String author = dingTalkMsg.getAuthor();
DingTalkClient client = new DefaultDingTalkClient(DingTalkUrlConstants.URL_SEND_WORK_NOTICE);
OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
request.setAgentId(dingTalkParams.getAgentId());
request.setUseridList(userList);
request.setToAllUser(false);
OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
// 消息类型
switch (msgType) {
case MSG_TYPE_OA:
msg.setOa(new OapiMessageCorpconversationAsyncsendV2Request.OA());
msg.getOa().setHead(new OapiMessageCorpconversationAsyncsendV2Request.Head());
msg.getOa().getHead().setText("head");
msg.getOa().setBody(new OapiMessageCorpconversationAsyncsendV2Request.Body());
msg.getOa().getBody().setTitle(title);
msg.getOa().getBody().setContent(content);
msg.getOa().getBody().setForm(fromList);
msg.getOa().getBody().setAuthor(author);
break;
case MSG_TYPE_LINK:
msg.setLink(new OapiMessageCorpconversationAsyncsendV2Request.Link());
msg.getLink().setTitle(title);
msg.getLink().setText(content);
msg.getLink().setMessageUrl(url);
msg.getLink().setPicUrl("test");
break;
case MSG_TYPE_ACTION_CARD:
msg.setActionCard(new OapiMessageCorpconversationAsyncsendV2Request.ActionCard());
msg.getActionCard().setTitle(title);
msg.getActionCard().setMarkdown(content);
msg.getActionCard().setSingleTitle("查看详情");
msg.getActionCard().setSingleUrl(url);
break;
default:
break;
}
msg.setMsgtype(msgType);
request.setMsg(msg);
OapiMessageCorpconversationAsyncsendV2Response rsp;
try {
// 是否开启钉钉提醒
if (StrUtil.equals(dingTalkParams.getIsOpen(), IS_OPEN)) {
String accessToken = getAccessToken();
rsp = client.execute(request, accessToken);
log.info(rsp.getBody());
}
} catch (ApiException e) {
log.error("发送钉钉工作通知异常:", e);
}
}
/**
* 在此方法中,为了避免频繁获取access_token,
* 在距离上一次获取access_token时间在两个小时之内的情况,
* 将直接从持久化存储中读取access_token
* <p>
* 因为access_token和jsapi_ticket的过期时间都是7200秒
* 所以在获取access_token的同时也去获取了jsapi_ticket
* 注:jsapi_ticket是在前端页面JSAPI做权限验证配置的时候需要使用的
* 具体信息请查看开发者文档--权限验证配置
*
* @return accessToken 或错误信息
*/
public String getAccessToken() throws ApiException {
String redisKey = CommonConstants.DAILY_DING_ACCESS_TOKEN + dingTalkParams.getAppKey();
// 从持久化存储中读取
String accessToken = getFromCache(redisKey, "access_token");
if (accessToken != null) {
return accessToken;
}
DefaultDingTalkClient client = new DefaultDingTalkClient(DingTalkUrlConstants.URL_GET_TOKEN);
OapiGettokenRequest request = new OapiGettokenRequest();
OapiGettokenResponse response;
request.setAppkey(dingTalkParams.getAppKey());
request.setAppsecret(dingTalkParams.getAppSecret());
request.setHttpMethod("GET");
try {
response = client.execute(request);
} catch (ApiException e) {
log.error("获取access_token失败异常:{}", e);
throw e;
}
accessToken = response.getAccessToken();
// 持久化accessToken
putToCache(redisKey, "access_token", accessToken);
return accessToken;
}
/**
* 获取JSTicket, 用于js的签名计算
* 正常的情况下,jsapi_ticket的有效期为7200秒,所以开发者需要在某个地方设计一个定时器,定期去更新jsapi_ticket
*
* @return jsTicket或错误信息
*/
public String getJsTicket() throws ApiException {
// 从持久化存储中读取
String ticket = getFromCache("jsticket", "ticket");
if (ticket != null) {
return ticket;
}
String accessToken = getAccessToken();
DefaultDingTalkClient client = new DefaultDingTalkClient(DingTalkUrlConstants.URL_GET_JSTICKET);
OapiGetJsapiTicketRequest request = new OapiGetJsapiTicketRequest();
OapiGetJsapiTicketResponse response;
request.setHttpMethod("GET");
try {
response = client.execute(request, accessToken);
} catch (ApiException e) {
log.error("获取jsticket失败异常:{}", e);
throw e;
}
ticket = response.getTicket();
// 持久化jsticket
putToCache("jsticket", "ticket", ticket);
return ticket;
}
/**
* 模拟从持久化存储中获取token并检查是否已过期
*
* @param section 存储key
* @param field token字段名
* @return token值 或 null (过期或未查到)
*/
private String getFromCache(String section, String field) {
String beginTimeKey = "begin_time";
String value = cache.get(section);
JSONObject o = JSONObject.parseObject(value);
if (o != null && System.currentTimeMillis() - o.getLong(beginTimeKey) <= CACHE_TTL) {
return o.getString(field);
}
return null;
}
private void putToCache(String section, String field, String value) {
JSONObject fieldObj = new JSONObject(2);
fieldObj.put(field, value);
fieldObj.put("begin_time", System.currentTimeMillis());
cache.set(section, fieldObj.toJSONString());
}
}
完结!