消息推送改为DINGDING

This commit is contained in:
JiyangTang 2023-06-27 12:53:25 +08:00
parent 03a70aeb53
commit f8f94bcc97
11 changed files with 441 additions and 8 deletions

View File

@ -79,11 +79,7 @@
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dingtalk</artifactId>
<version>2.0.20</version>
</dependency>
<!-- Sa-Token 权限认证在线文档https://sa-token.cc --> <!-- Sa-Token 权限认证在线文档https://sa-token.cc -->
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
@ -103,7 +99,11 @@
<artifactId>forest-spring-boot-starter</artifactId> <artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.26</version> <version>1.5.26</version>
</dependency> </dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -2,15 +2,18 @@ package top.baogutang.admin.schedule;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.TypeReference;
import com.zjiecode.wxpusher.client.bean.Message; import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import top.baogutang.admin.domain.EdgeXAnnouncementsDto; import top.baogutang.admin.domain.EdgeXAnnouncementsDto;
import top.baogutang.admin.services.IWxMsgPushService; import top.baogutang.admin.services.IWxMsgPushService;
import top.baogutang.admin.utils.DingTalkMsgPushUtils;
import top.baogutang.common.constants.DingTalkMsgTypeEnum;
import top.baogutang.common.domain.Page; import top.baogutang.common.domain.Page;
import top.baogutang.common.domain.Results; import top.baogutang.common.domain.Results;
import top.baogutang.common.properties.WxMsgPushProperties; import top.baogutang.common.properties.WxMsgPushProperties;
@ -41,6 +44,12 @@ public class EdgeXNoticeSchedule {
@Resource @Resource
private RedisTemplate<String, Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@Resource
private DingTalkMsgPushUtils dingTalkMsgPushUtils;
@Value("${dingtalk.baogutang.agentId}")
private String agentId;
private static final String REQUEST_URL = "https://art-api.edge-x.cn/api/v1/art/announcements?pageNum=1&pageSize=1"; private static final String REQUEST_URL = "https://art-api.edge-x.cn/api/v1/art/announcements?pageNum=1&pageSize=1";
/** /**
@ -65,7 +74,27 @@ public class EdgeXNoticeSchedule {
Boolean result = redisTemplate.opsForValue().setIfAbsent(cacheKey, 1, 5, TimeUnit.DAYS); Boolean result = redisTemplate.opsForValue().setIfAbsent(cacheKey, 1, 5, TimeUnit.DAYS);
if (Boolean.TRUE.equals(result)) { if (Boolean.TRUE.equals(result)) {
wxMsgPushService.msgPush(Message.CONTENT_TYPE_HTML, announcementsDto.getTitle(), announcementsDto.getContent(), wxMsgPushProperties.getTopicIds()); dingTalkMsgPushUtils.dingTalkMsgPush(agentId, Boolean.TRUE, null, null, this.geneMsg(announcementsDto));
// wxMsgPushService.msgPush(Message.CONTENT_TYPE_HTML, announcementsDto.getTitle(), announcementsDto.getContent(), wxMsgPushProperties.getTopicIds());
} }
} }
private OapiMessageCorpconversationAsyncsendV2Request.Msg geneMsg(EdgeXAnnouncementsDto announcementsDto) {
// 读取消息文件
OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
msg.setMsgtype(DingTalkMsgTypeEnum.ACTION_CARD.getType());
OapiMessageCorpconversationAsyncsendV2Request.ActionCard actionCard = new OapiMessageCorpconversationAsyncsendV2Request.ActionCard();
// 透出到会话列表和通知的文案
actionCard.setTitle(announcementsDto.getTitle());
// 支持markdown格式的正文内容
String title = "# " + announcementsDto.getTitle() + "+\n\n";
String image = "![](" + announcementsDto.getCover() + ")";
actionCard.setMarkdown(title + image);
// 跳转按钮展示
actionCard.setSingleTitle("查看详情");
// 跳转URL
actionCard.setSingleUrl("https://app.edge-x.cn/#/noticeDetail?noticeId=" + announcementsDto.getId());
msg.setActionCard(actionCard);
return msg;
}
} }

View File

@ -0,0 +1,122 @@
package top.baogutang.admin.utils;
import com.alibaba.fastjson.JSON;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import top.baogutang.common.exceptions.BusinessException;
import top.baogutang.common.properties.DingTalkConfigFactory;
import top.baogutang.common.properties.DingTalkConfigStrategy;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static top.baogutang.common.constants.CacheConstant.PREFIX_DING_TALK_ACCESS_TOKEN;
import static top.baogutang.common.constants.DingTalkServerConstants.ACC_TOKEN;
import static top.baogutang.common.constants.DingTalkServerConstants.CORP_CONVERSATION_ASYNC_SEND_V2;
/**
* @description: 钉钉消息推送工具类
* @author: nikooh
* @date: 2023/04/03 : 16:29
*/
@Slf4j
@Component
public class DingTalkMsgPushUtils {
@Resource
private DingTalkConfigFactory dingTalkConfigFactory;
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 钉钉消息推送 {https://open.dingtalk.com/document/orgapp/work-notice-option}
*
* @param agentId 应用ID
* @param deptIdList 消息接收部门ID集合
* @param userIdList 消息接收用户ID集合
* @param message 具体消息内容
* @return 消息ID
*/
public Long dingTalkMsgPush(String agentId, Boolean toAllUser, List<String> deptIdList, List<String> userIdList, OapiMessageCorpconversationAsyncsendV2Request.Msg message) {
// 1.获取消息所属应用及应用token
String accessToken = this.getAccessToken(agentId);
if (StringUtils.isBlank(accessToken)) {
throw new BusinessException("消息发送失败->未能成功生成应用token");
}
// 2.组装dingTalk请求参数
OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
request.setAgentId(Long.parseLong(agentId));
request.setToAllUser(toAllUser);
if (CollectionUtils.isNotEmpty(deptIdList)) {
request.setDeptIdList(StringUtils.join(deptIdList.toArray(), ","));
}
if (CollectionUtils.isNotEmpty(userIdList)) {
request.setUseridList(StringUtils.join(userIdList.toArray(), ","));
}
request.setMsg(message);
// 3.请求钉钉发送消息
DingTalkClient client = new DefaultDingTalkClient(CORP_CONVERSATION_ASYNC_SEND_V2);
OapiMessageCorpconversationAsyncsendV2Response response = null;
try {
response = client.execute(request, accessToken);
log.info(">>>>>>>>>>request dingTalk send msg res:{}<<<<<<<<<<", JSON.toJSONString(response));
return response.getTaskId();
} catch (Exception e) {
log.error(">>>>>>>>>>request dingTalk send msg error:{}<<<<<<<<<<", e.getMessage(), e);
return null;
}
}
/**
* 获取钉钉应用accessToken
*
* @param agentId 应用ID
* @return token
*/
public String getAccessToken(String agentId) {
DingTalkConfigStrategy config = dingTalkConfigFactory.getConfigByAgentId(agentId);
String appKey = config.getAppKey();
String appSecret = config.getAppSecret();
String cacheKey = PREFIX_DING_TALK_ACCESS_TOKEN + appKey;
Object accessTokenObj = redisTemplate.opsForValue().get(cacheKey);
if (Objects.nonNull(accessTokenObj)) {
return (String) accessTokenObj;
}
// 缓存不存在生成token
DingTalkClient client = new DefaultDingTalkClient(ACC_TOKEN);
OapiGettokenRequest request = new OapiGettokenRequest();
request.setAppkey(appKey);
request.setAppsecret(appSecret);
request.setHttpMethod("GET");
OapiGettokenResponse response;
try {
response = client.execute(request);
log.info(">>>>>>>>>>request dingTalk gene accessToken,request:[{}] response:[{}]<<<<<<<<<<", JSON.toJSONString(request), JSON.toJSONString(response));
if (Objects.nonNull(response) && Objects.nonNull(response.getAccessToken())) {
redisTemplate.opsForValue().set(cacheKey, response.getAccessToken(), 7000L, TimeUnit.SECONDS);
return response.getAccessToken();
}
} catch (Exception e) {
log.error(">>>>>>>>>>request dingTalk gene accessToken error! request:{},errorMsg:{}<<<<<<<<<<", JSON.toJSONString(request), e.getMessage(), e);
}
return null;
}
}

View File

@ -144,6 +144,11 @@
<artifactId>hutool-all</artifactId> <artifactId>hutool-all</artifactId>
<version>5.7.4</version> <version>5.7.4</version>
</dependency> </dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dingtalk</artifactId>
<version>2.0.14</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -34,4 +34,9 @@ public class CacheConstant {
public static final Integer ENABLE = 0; public static final Integer ENABLE = 0;
/**
* 钉钉应用access_token缓存KEY前缀
*/
public static final String PREFIX_DING_TALK_ACCESS_TOKEN = "top:baogutang:dingtalk:access_token:";
} }

View File

@ -0,0 +1,62 @@
package top.baogutang.common.constants;
/**
* @description: 钉钉消息类型枚举
* @author: nikooh
* @date: 2023/04/07 : 13:41
*/
public enum DingTalkMsgTypeEnum {
/**
* 文本消息
*/
TEXT("text"),
/**
* 图片消息
*/
IMAGE("image"),
/**
* 语音消息
*/
VOICE("voice"),
/**
* 文件消息
*/
FILE("file"),
/**
* 链接消息
*/
LINK("link"),
/**
* OA消息
*/
OA("oa"),
/**
* markdown消息
*/
MARKDOWN("markdown"),
/**
* 卡片消息
*/
ACTION_CARD("action_card"),
;
private final String type;
DingTalkMsgTypeEnum(String type) {
this.type = type;
}
public String getType() {
return type;
}
}

View File

@ -0,0 +1,53 @@
package top.baogutang.common.constants;
/**
* @description: 钉钉服务相关常量
* @author: nikooh
* @date: 2023/06/21 : 16:53
*/
public class DingTalkServerConstants {
private DingTalkServerConstants() {
// private constructor
}
/**
* token
*/
public static final String ACC_TOKEN = "https://oapi.dingtalk.com/gettoken";
/**
* 群组创建
*/
public static final String CHAT_CREATE = "https://oapi.dingtalk.com/chat/create";
/**
* 群组修改
*/
public static final String CHAT_UPDATE = "https://oapi.dingtalk.com/chat/update";
/**
* 工作通知
*/
public static final String CORP_CONVERSATION_ASYNC_SEND_V2 = "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2";
/**
* 部门下员工
*/
public static final String USER_LIST = "https://oapi.dingtalk.com/topapi/v2/user/list";
/**
* 部门列表
*/
public static final String DEPARTMENT_LIST = "https://oapi.dingtalk.com/department/list";
/**
* 用户详情
*/
public static final String USER_INFO = "https://oapi.dingtalk.com/user/getuserinfo";
/**
* 手机号搜索userId
*/
public static final String USER_ID_BY_MOBILE = "https://oapi.dingtalk.com/topapi/v2/user/getbymobile";
}

View File

@ -0,0 +1,54 @@
package top.baogutang.common.properties;
import org.springframework.stereotype.Component;
/**
* @author niko
* @version 0.1.0
* @create 2023/3/31 19:25
* @since 0.1.0
**/
@Component
public class BaoGuTangConfigStrategy implements DingTalkConfigStrategy {
private String agentId;
private String appKey;
private String appSecret;
private String appCorpId;
private String appSource;
public BaoGuTangConfigStrategy() {
}
public BaoGuTangConfigStrategy(String agentId, String appKey, String appSecret, String appCorpId, String appSource) {
this.agentId = agentId;
this.appKey = appKey;
this.appSecret = appSecret;
this.appCorpId = appCorpId;
this.appSource = appSource;
}
@Override
public String getAgentId() {
return agentId;
}
@Override
public String getAppKey() {
return appKey;
}
@Override
public String getAppSecret() {
return appSecret;
}
@Override
public String getAppCorpId() {
return appCorpId;
}
@Override
public String getAppSource() {
return appSource;
}
}

View File

@ -0,0 +1,36 @@
package top.baogutang.common.properties;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author niko
* @version 0.1.0
* @create 2023/3/31 17:15
* @since 0.1.0
**/
@Component
@ConfigurationProperties(prefix = "dingtalk")
@Getter
@Setter
public class DingTalkAppConfig {
private BaoGuTangProperties baoGuTang = new BaoGuTangProperties();
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public static class BaoGuTangProperties {
private String agentId;
private String appKey;
private String appSecret;
private String appCorpId;
}
}

View File

@ -0,0 +1,46 @@
package top.baogutang.common.properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author niko
* @version 0.1.0
* @create 2023/3/31 19:25
* @since 0.1.0
**/
@Component
public class DingTalkConfigFactory {
public static final String BAO_GU_TANG = "BAO_GU_TANG";
private Map<String, DingTalkConfigStrategy> configMap;
@Qualifier
public DingTalkAppConfig dingTalkAppConfig;
@Autowired
public DingTalkConfigFactory(DingTalkAppConfig dingTalkAppConfig) {
this.dingTalkAppConfig = dingTalkAppConfig;
this.configMap = new HashMap<>();
this.configMap.put(BAO_GU_TANG, new BaoGuTangConfigStrategy(dingTalkAppConfig.getBaoGuTang().getAgentId(),
dingTalkAppConfig.getBaoGuTang().getAppKey(), dingTalkAppConfig.getBaoGuTang().getAppSecret(),
dingTalkAppConfig.getBaoGuTang().getAppCorpId(), BAO_GU_TANG));
}
public DingTalkConfigStrategy getConfig(String appType) {
return configMap.get(appType);
}
public DingTalkConfigStrategy getConfigByAgentId(String agentId) {
if (Objects.equals(agentId, dingTalkAppConfig.getBaoGuTang().getAgentId())) {
return getConfig(BAO_GU_TANG);
}
return null;
}
}

View File

@ -0,0 +1,21 @@
package top.baogutang.common.properties;
/**
* @author niko
* @version 0.1.0
* @create 2023/3/31 19:23
* @since 0.1.0
**/
public interface DingTalkConfigStrategy {
String getAgentId();
String getAppKey();
String getAppSecret();
String getAppCorpId();
String getAppSource();
}