1. 消息对象定义
1.1 通用消息对象定义
package com.yj.notice.message;
import com.yj.commons.tools.utils.DateUtils;
import com.yj.commons.tools.utils.StringUtil;
import com.yj.notice.costant.NoticeMethodEnum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;/**
* @author : dyg
* @className : SmsMessageDTO
* @description : SMS短信平台 实体类
* @date : 2023/8/30 10:35
*/
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class NoticeMessage implements Serializable {
private static final long serialVersionUID = 5081758462088563857L; /** * 消息类型 **/ private String messageType;
/**
* 通知消息类型
*/
protected NoticeMethodEnum noticeMethod;
/**
* 接收者-手机号码
*/
protected String receiverPhone;
protected String receiverId;
/**
* 接收者-用户名
*/
protected String receiverUserName;
/**
* 消息内容
*/
protected String content;
/**
* 消息内容
*/
protected String title;
/**
* 消息时间
*/
protected String time;
/**
* 发送结果
*/
protected String result;
/**
* 错误信息
*/
protected String error;
public String wrapperMessage() {
StringBuilder sub = new StringBuilder();
String time = StringUtil.isEmpty(this.getTime()) ? format(new Date(),"yyyy-MM-dd HH:mm:ss") : this.getTime();
sub.append("消息标题: ").append(this.getTitle()).append("\n" )
.append("消息内容: ").append(this.getContent()).append("\n")
.append("消息时间: ").append(time).append("\n" );
return sub.toString();
}
public static String format(Date date, String pattern) { if (date != null) { SimpleDateFormat df = new SimpleDateFormat(pattern); return df.format(date); } return null; }
}
1.2 告警消息对象定义
package com.yj.notice.message;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author : dyg
* @className : AlarmMessage
* @description : 告警信息
* @date : 2023/9/5 11:19
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AlarmMessage extends NoticeMessage implements Serializable {
private static final long serialVersionUID = -8390792683552907827L;
/**
* 告警级别
*/
protected String level;
/**
* 告警类型
*/
protected String type;
/**
* 告警备注
*/
protected String remark;
public String wrapperMessage() {
if(StringUtil.isEmpty(this.getTime()) && StringUtil.isEmpty(this.getLevel()) && StringUtil.isEmpty(this.getType())&& StringUtil.isEmpty(this.getRemark())) {
return this.getContent();
}
StringBuilder sub = new StringBuilder();
sub.append("告警时间: ").append(this.getTime()).append("\n")
.append("告警级别: ").append(this.getLevel()).append("\n")
.append("告警类别: ").append(this.getType()).append("\n")
.append("告警消息: ").append(this.getContent()).append("\n")
.append("备注信息: ").append(this.getRemark());
return sub.toString();
}
}
1.3 邮件消息对象定义
package com.yj.notice.message;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.File;
import java.io.Serializable;
/**
* @author : dyg
* @className : MailMessage
* @description : 描述说明该类的功能
* @date : 2023/9/5 14:33
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MailMessage extends AlarmMessage implements Serializable {
private static final long serialVersionUID = 4370431527898082801L;
/**
* 附件
*/
private File attachFile;
/**
* html内容
*/
private String html;
}
1.4 消息发送类型枚举定义
package com.yj.notice.costant;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 通知方式
*
* @author changXT
* @date 2022-8-10
**/
@AllArgsConstructor
@Getter
public enum NoticeMethodEnum {
WECHAT("微信", "WECHAT"),
EMAIL("邮件", "EMAIL"),
TENCENT_SMS("腾讯短信", "TENCENT_SMS"),
IN_MSG("站内消息", "IN_MSG"),
DINGTALK("钉钉", "DINGTALK");
private String cnName;
private String name;
public static NoticeMethodEnum getEnum(String name) {
for (NoticeMethodEnum noticeMethodEnum : NoticeMethodEnum.values()) {
if (name.equals(noticeMethodEnum.name())) {
return noticeMethodEnum;
}
}
return null;
}
}
2. 消息发送接口定义
2.1 消息发送接口定义
package com.yj.notice.service;
import com.yj.notice.MessageSenderManager;
import com.yj.notice.message.NoticeMessage;
import org.springframework.beans.factory.InitializingBean;
/**
* @author : dyg
* @className : MessageService
* @description : 消息发送
* @date : 2023/8/30 10:34
*/
public interface MessageService
/**
* 发送消息
* @param message
* @return
*/
String send(NoticeMessage message);
/**
* 获取发送方法
*
* @return 发送方法
*/
String getNoticeMethod();
@Override
default void afterPropertiesSet() {
MessageSenderManager.registrySender(getNoticeMethod(), this);
}
}
2.2 消息发送收集器定义
package com.yj.notice;
import com.yj.notice.message.NoticeMessage;
import com.yj.notice.costant.NoticeMethodEnum;
import com.yj.notice.service.MessageService;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
/**
* 消息发送者管理器
*
* @author donglanlan
* @date 2021/7/22 2:33 下午
**/
@Component
public class MessageSenderManager {
private static final ConcurrentHashMap
new ConcurrentHashMap<>();
public static void registrySender(String sendMethod, MessageService extends NoticeMessage> messageSender) {
SENDER_MAP.put(sendMethod, messageSender);
}
public MessageService getMessageSender(NoticeMessage messsage) {
if (messsage.getNoticeMethod() == null) {
throw new RuntimeException("没有指定消息的发送方式!");
}
return getMessageSender(messsage.getNoticeMethod());
}
public MessageService getMessageSender(NoticeMethodEnum methodEnum) {
return SENDER_MAP.get(methodEnum.getName());
}
}
3. 微信公众号消息发送实现类
3.1 实现类定义
package com.yj.notice.service.impl;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.yj.commons.tools.utils.DateUtils;
import com.yj.commons.tools.utils.JsonUtil;
import com.yj.commons.tools.utils.StringUtil;
import com.yj.notice.message.NoticeMessage;
import com.yj.notice.costant.NoticeMethodEnum;
import com.yj.notice.message.AlarmMessage;
import com.yj.notice.service.MessageService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.*;
import static com.yj.commons.tools.utils.DateUtils.DATE_TIME_PATTERN;
/**
* @author : dyg
* @className : WxMessageServiceImpl
* @description : 描述说明该类的功能
* @date : 2023/9/5 9:35
*/
@Service("WechatMessageService")
@Slf4j
@Data
public class WechatMessageServiceImpl implements MessageService {
/**
* 账号app_id
*/
@Value(value = "${notice.wechat.bk_app_id}")
private String bkAppKey;
/**
* 账户密钥
*/
@Value(value = "${notice.wechat.bk_app_secret}")
private String bkAppSecret;
/**
* 告警消息模板id
*/
@Value(value = "${notice.wechat.alarm_template_id}")
private String alarmTemplateId;
/**
* 普通消息模板id
*/
@Value(value = "${notice.wechat.common_template_id}")
private String commonTemplateId;
/**
* 获取token
* "+ appId +"&secret=" + appIdSecret
*/
@Value(value = "${notice.wechat.token_uri}")
private String tokenUri;
/**
* + accessToken;
*/
@Value(value = "${notice.wechat.user_list_uri}")
private String userListUri;
/**
* + accessToken;
*/
@Value(value = "${notice.wechat.send_message_uri}")
private String sendMessageUri;
@Autowired
RestTemplate restTemplate;
/**
* 用户token
*/
private String enterpriseToken = null;
private Long tokenFreshTimeSt = 0L;
@Override
public String send(NoticeMessage message) {
String result = null;
try {
result = this.sendMessage(message);
}catch (Exception e){
log.error(e.getMessage());
result = e.getMessage()+ " " + e;
}
if (com.yj.cmp.commons.util.StringUtil.isNotEmpty(result)) {
log.error(result);
} else {
result = "发送成功";
}
return result;
}
@Override
public String getNoticeMethod() {
return NoticeMethodEnum.WECHAT.getName();
}
/**
* 获取或者刷新token
*/
private void getOrRefreshToken() {
try {
String requestUrl = this.tokenUri + this.bkAppKey +"&secret=" + this.bkAppSecret;
String res = HttpUtil.get(requestUrl);
JSONObject jsonObject = JSONObject.parseObject(res);
String accessToken = jsonObject.getString("access_token");
this.enterpriseToken = accessToken;
this.tokenFreshTimeSt = System.currentTimeMillis()/1000;
} catch (Exception e) {
log.error("---获取token出现异常{} {} ",e.getMessage(),e);
}
}
/**
* 获取用户列表openid
*/
public void getUserList(){
RestTemplate restTemplate = new RestTemplate();
String requestUrl = this.userListUri+ this.enterpriseToken;
ResponseEntity
log.info("结果是: {}",response.getBody());
com.alibaba.fastjson.JSONObject result = com.alibaba.fastjson.JSONObject.parseObject(response.getBody());
com.alibaba.fastjson.JSONArray openIdJsonArray = result.getJSONObject("data").getJSONArray("openid");
Iterator iterator = openIdJsonArray.iterator();
if (iterator.hasNext()){
log.debug("用户openid:"+iterator.next());
}
}
@Data
public class WeChatTemplateMsg {
/**
* 消息
*/
private String value;
/**
* 消息颜色
*/
private String color;
public WeChatTemplateMsg(String value) {
this.value = value;
this.color = "#173177";
}
public WeChatTemplateMsg(String value, String color) {
this.value = value;
this.color = color;
}
}
/**
* 获取用户id
* @param alarmMessage
* @return
*/
private JSONObject getWechatUserId(NoticeMessage alarmMessage){
JSONObject result = new JSONObject();
String openId = alarmMessage.getReceiverId();
if(StringUtil.isEmpty(openId)){
// todo 根据手机号码获取微信id --对应数据估计得手动维护
String receiverPhone = alarmMessage.getReceiverPhone();
if(StringUtil.isEmpty(receiverPhone)){
result.put("message","消息接收者手机号码+微信id都为空,消息无法发送");
}
openId = getWechatUserId(receiverPhone);
}
result.put("userId",openId);
return result;
}
/**
* 发送消息
* @param noticeMessage
* @return
*/
public String sendMessage(NoticeMessage noticeMessage){
String result = null;
// 模板参数
Map
// openId代表一个唯一微信用户,即微信消息的接收人
JSONObject wechatUserId = getWechatUserId(noticeMessage);
String openId = null;
if(wechatUserId.containsKey("message")){
String message = wechatUserId.getString("message");
log.error(message);
return message;
}else{
Object userId = wechatUserId.get("userId");
if(Objects.isNull(userId)){
String message = "未能根据手机号码"+noticeMessage.getReceiverPhone()+"成功获取用户的微信id";
return message;
}
openId = wechatUserId.getString("userId");
}
// 公众号的模板id(也有相应的接口可以查询到)
validateToken();
String requestUrl = this.sendMessageUri + this.enterpriseToken;
//拼接base参数
Map
sendBody.put("touser", openId);
sendBody.put("data", sendMag);
if(noticeMessage instanceof AlarmMessage){
AlarmMessage alarmMessage = (AlarmMessage)noticeMessage;
sendMag.put("message", new WeChatTemplateMsg(alarmMessage.getContent()));
sendMag.put("time",new WeChatTemplateMsg(alarmMessage.getTime()));
sendMag.put("level",new WeChatTemplateMsg(alarmMessage.getLevel(),"#FF69B4" ));
sendMag.put("type",new WeChatTemplateMsg(alarmMessage.getType() ,"#173177"));
sendMag.put("remark",new WeChatTemplateMsg(alarmMessage.getRemark(),"#173177"));
sendBody.put("template_id", this.alarmTemplateId);
}else{
sendMag.put("content", new WeChatTemplateMsg(noticeMessage.getContent()));
sendMag.put("title",new WeChatTemplateMsg(noticeMessage.getTitle()));
String time = StringUtil.isEmpty(noticeMessage.getTime()) ? DateUtils.format(new Date(),DATE_TIME_PATTERN) : noticeMessage.getTime();
sendMag.put("time",new WeChatTemplateMsg(time,"#FF69B4"));
sendBody.put("template_id", this.commonTemplateId);
}
try {
// 1.创建httpclient对象
CloseableHttpClient client = HttpClients.createDefault();
// 2.创建post对象
HttpPost post = new HttpPost(requestUrl);
StringEntity postingString = new StringEntity(JsonUtil.entityToString(sendBody), "utf-8");
post.setEntity(postingString);
// 3.执行post方法:得到结果
CloseableHttpResponse response = client.execute(post);
// 4.处理结果
// 1.得到状态码
int statusCode = response.getStatusLine().getStatusCode();
log.info("----http code : {}", statusCode);
if (statusCode == 200) {
// 2.得到实体内容
org.apache.http.HttpEntity entity = response.getEntity();
String content = EntityUtils.toString(entity, "utf-8");
JSONObject jsonObject = JsonUtil.StringToEntity(content, JSONObject.class);
String messageCode = jsonObject.getString("errcode");
String msgId = jsonObject.getString("msgid");
result = "messageCode : " + messageCode + ", msgId: " +msgId;
}
// 5.关闭连接
client.close();
} catch (IOException e) {
log.error("------exception : {} {} ",e.getMessage(),e);
}
return result;
}
/**
* 根据手机号码获取用户id
* @param receiverPhone
* @return
*/
private String getWechatUserId(String receiverPhone) {
String userId = null;
return userId;
}
/**
* 验证并刷新token
*/
private void validateToken() {
if (com.yj.cmp.commons.util.StringUtil.isEmpty(this.enterpriseToken)) {
this.getOrRefreshToken();
}
Long now = System.currentTimeMillis()/1000;
if(this.tokenFreshTimeSt == 0L){
this.getOrRefreshToken();
}else{
Long diff = (now - tokenFreshTimeSt)/60;
if(diff > 600){
// 超过十分钟重新获取一下
this.getOrRefreshToken();
}
}
}
}
3.2 配置参数
# 消息推送相关
notice:
# 微信消息相关
wechat:
bk_app_id: xxx
bk_app_secret: xxx
alarm_template_id: xxx
common_template_id: xxxxxh
token_uri: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=
user_list_uri: https://api.weixin.qq.com/cgi-bin/user/get?access_token=
send_message_uri: https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=
3.3 消息模板
4. 钉钉群消息发送实现类
4.1 引入依赖
4.2 实现类定义
package com.yj.notice.service.impl;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.yj.cmp.commons.util.StringUtil;
import com.yj.commons.tools.utils.JsonUtil;
import com.yj.notice.message.AlarmMessage;
import com.yj.notice.message.NoticeMessage;
import com.yj.notice.costant.NoticeMethodEnum;
import com.yj.notice.service.MessageService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.aliyun.dingtalkrobot_1_0.Client;
import com.aliyun.dingtalkrobot_1_0.models.BatchSendOTOHeaders;
import com.aliyun.dingtalkrobot_1_0.models.BatchSendOTORequest;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.Common;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.request.OapiV2UserGetbymobileRequest;
import com.dingtalk.api.response.OapiV2UserGetbymobileResponse;
import java.util.Arrays;
import java.util.Map;
/**
* @author : dyg
* @className : DingMessageServiceImpl
* @description : 钉钉消息发送
* @date : 2023/9/4 17:14
*/
@Service("DingTalkMessageService")
@Slf4j
@Data
public class DingTalkMessageServiceImpl implements MessageService {
@Value(value = "${notice.dingtalk.token_url}")
private String tokenUrl;
@Value(value = "${notice.dingtalk.get_user_by_phone_url}")
private String getUserByPhoneUrl;
@Value(value = "${notice.dingtalk.bk_app_key}")
private String bkAppKey;
@Value(value = "${notice.dingtalk.bk_app_secret}")
private String bkAppSecret;
/**
* token 数据
*/
private String enterpriseToken = "";
/**
* 获取/刷新token时间戳
*/
private Long tokenFreshTimeSt = 0L;
@Override
public String send(NoticeMessage message) {
String result = null;
try {
String receiverId = message.getReceiverId();
if (StringUtil.isNotEmpty(receiverId)) {
// 用户id不为空根据id发送
result = this.sendDingTalkNotify(receiverId, message);
} else {
// 否则根据手机号码查询用户
String receiverPhone = message.getReceiverPhone();
if (StringUtil.isNotEmpty(receiverPhone)) {
receiverId = this.getDingdingUserIdByPhone(receiverPhone);
if (StringUtil.isNotEmpty(receiverId)) {
result = this.sendDingTalkNotify(receiverId, message);
} else {
result = "根据接收者用户手机号码获取的用户id都为空,无法完成消息发送需求";
}
} else {
result = "接收者用户id和手机号码都为空,无法完成消息发送需求";
}
}
}catch (Exception e){
log.error(e.getMessage());
result = e.getMessage()+ " " + e;
}
if (StringUtil.isNotEmpty(result)) {
log.error(result);
} else {
result = "发送成功";
}
return result;
}
@Override
public String getNoticeMethod() {
return NoticeMethodEnum.DINGTALK.getName();
}
/**
* 使用 Token 初始化账号Client
*
* @return Client
* @throws Exception
*/
private Client createClient() throws Exception {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
return new Client(config);
}
/**
* 获取token,每两小时失效
*/
private void getOrRefreshToken() {
try {
DingTalkClient client = new DefaultDingTalkClient(this.tokenUrl);
OapiGettokenRequest req = new OapiGettokenRequest();
req.setAppkey(this.bkAppKey);
req.setAppsecret(this.bkAppSecret);
req.setHttpMethod("GET");
OapiGettokenResponse rsp = client.execute(req);
log.info("token:" + rsp.getBody());
Map json = JsonUtil.StringToEntity(rsp.getBody(), Map.class);
this.enterpriseToken = json.get("access_token").toString();
this.tokenFreshTimeSt = System.currentTimeMillis()/1000;
} catch (Exception e) {
log.error("---获取token出现异常{} {} ",e.getMessage(),e);
}
}
/**
* 根据手机号码获取钉钉用户id
* -- 备注: 必须将用户拉入组织/群中之后才可以根据手机号码获取用户
* @param phone 用户手机号
* @return void
*/
private String getDingdingUserIdByPhone(String phone) {
try {
if (StringUtil.isEmpty(this.enterpriseToken)) {
this.getOrRefreshToken();
}
DingTalkClient client = new DefaultDingTalkClient(getUserByPhoneUrl);
OapiV2UserGetbymobileRequest req = new OapiV2UserGetbymobileRequest();
req.setMobile(phone);
OapiV2UserGetbymobileResponse rsp = client.execute(req, this.enterpriseToken);
if (rsp != null) {
OapiV2UserGetbymobileResponse.UserGetByMobileResponse result = rsp.getResult();
return result == null ? null : result.getUserid();
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 发送钉钉消息
* @param dingUserId 钉钉用户id
* @param message 发送消息
* @return void
*/
public String sendDingTalkNotify(String dingUserId, NoticeMessage message) throws Exception {
String result = null;
validateToken();
Client client = createClient();
BatchSendOTOHeaders batchSendOTOHeaders = new BatchSendOTOHeaders();
batchSendOTOHeaders.xAcsDingtalkAccessToken = this.enterpriseToken;
String content = null;
if(message instanceof AlarmMessage){
AlarmMessage alarmMessage = (AlarmMessage)message;
content = alarmMessage.wrapperMessage();
}else{
content = message.wrapperMessage();
}
String finalContent = "{\"content\": \"" + content + "\"}";
BatchSendOTORequest batchSendOTORequest = new BatchSendOTORequest()
.setRobotCode(this.bkAppKey)//机器人appkey
.setUserIds(Arrays.asList(dingUserId))
.setMsgKey("officialTextMsg")
.setMsgParam(finalContent);
try {
client.batchSendOTOWithOptions(batchSendOTORequest, batchSendOTOHeaders, new RuntimeOptions());
} catch (TeaException err) {
if (!Common.empty(err.code) && !Common.empty(err.message))
// err 中含有 code 和 message 属性,可帮助开发定位问题
log.error(err.code + ":" + err.message);
} catch (Exception e) {
TeaException err = new TeaException(e.getMessage(), e);
if (!Common.empty(err.code) && !Common.empty(err.message))
// err中含有code和message 属性,可帮助开发定位问题
log.error(err.code + ":" + err.message);
result = err.code + ":" + err.message;
}
return result;
}
/**
* 验证并刷新token
*/
private void validateToken() {
if (StringUtil.isEmpty(this.enterpriseToken)) {
this.getOrRefreshToken();
}
Long now = System.currentTimeMillis()/1000;
if(this.tokenFreshTimeSt == 0L){
this.getOrRefreshToken();
}else{
Long diff = (now - tokenFreshTimeSt)/60;
if(diff > 600){
// 超过十分钟重新获取一下
this.getOrRefreshToken();
}
}
}
}
4.3 配置参数
# 消息推送相关
notice:
# 钉钉消息相关
dingtalk:
bk_app_key: xxx
bk_app_secret: xxxx
token_url: https://oapi.dingtalk.com/gettoken
get_user_by_phone_url: https://oapi.dingtalk.com/topapi/v2/user/getbymobile
4.4 创建应用获取appKey+appSecret
1. 使用钉钉账户作为管理员创建一个组织/群
2. 登录钉钉后台管理页面,工作台--->应用管理--->创建应用(委托服务商开发), 创建一个H5微应用
3. 登录钉钉后台管理页面,工作台--->应用管理--->点击应用, 查看 凭证与基础信息 获取配置文件中所需要的appKey/appSecret
4. 为当前应用创建机器人,工作台--->应用管理--->点击应用-->添加应用能力--->机器人添加
5. 为应用申请相应的权限,例如根据手机号码获取用户,企业内机器人发送消息权限
6. 将当前应用发布 ,当添加完毕机器人+权限之后,当前应用显示 开发中,如下图所示,需要在版本与管理中创建一个发布版本(只有发布成功之后才可以使用)
7. 将需要接收消息的钉钉用户拉入群组
5. 邮件发送实现类
5.1 引入依赖
5.2 定义实现类import com.yj.commons.tools.utils.JsonUtil;
import com.yj.commons.tools.utils.StringUtil;
import com.yj.notice.message.MailMessage;
import com.yj.notice.message.NoticeMessage;
import com.yj.notice.costant.NoticeMethodEnum;
import com.yj.notice.service.MessageService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.internet.MimeMessage;
import java.io.File;
/**
* @author : dyg
* @className : MailMessageServiceImpl
* @description : 邮件发送器
* @date : 2023/9/5 14:31
*/
@Service("MailMessageService")
@Slf4j
@Data
public class MailMessageServiceImpl implements MessageService {
@Value("${spring.mail.username}")
private String from;
@Autowired
private JavaMailSender mailSender;
@Override
public String getNoticeMethod() {
return NoticeMethodEnum.EMAIL.getName();
}
/**
* 发送邮件
* @param message
* @return
*/
@Override
public String send(NoticeMessage message) {
String result = null;
try{
if(message instanceof MailMessage){
MailMessage mailMessage = (MailMessage)message;
File attachFile = mailMessage.getAttachFile();
String html = mailMessage.getHtml();
if(null != attachFile){
// 带附件的邮件
result = sendAttachFileMail(mailMessage,attachFile);
}else{
if(StringUtil.isNotEmpty(html)){
// html内容的邮件
result = sendHtmlMail(mailMessage,html);
}else{
// 普通邮件
result = sendSimpleMail(mailMessage);
}
}
}
if(StringUtil.isNotEmpty(result)){
log.error("发送邮件: {} 发生错误: {}", JsonUtil.entityToString(message),result);
}
}catch (Exception e){
log.error(e.getMessage());
result = e.getMessage();
}
return result;
}
/**
* 带附件的邮件发送
* @param mailMessage
* @param attachFile
* @return
*/
private String sendAttachFileMail(MailMessage mailMessage,File attachFile) {
String result = null;
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
//邮件发送人
messageHelper.setFrom(from);
//邮件接收人
messageHelper.setTo(mailMessage.getReceiverId());
//邮件主题
message.setSubject(mailMessage.getTitle());
//邮件内容
messageHelper.setText(mailMessage.wrapperMessage());
//添加附件
messageHelper.addAttachment(attachFile.getName(), attachFile);
//发送
mailSender.send(message);
}catch(Exception e) {
log.error("发送附件邮件报错: {} {}",e.getMessage(),e);
result = e.getMessage();
}
return result;
}
/**
* html内容的邮件发送
* @param mailMessage
* @param html
* @return
*/
private String sendHtmlMail(MailMessage mailMessage, String html) {
String result = null;
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
//邮件发送人
messageHelper.setFrom(from);
//邮件接收人
messageHelper.setTo(mailMessage.getReceiverId());
//邮件主题
message.setSubject(mailMessage.getTitle());
//邮件内容
messageHelper.setText(html,true);
//发送
mailSender.send(message);
}catch(Exception e) {
log.error("发送html内容邮件报错: {} {}",e.getMessage(),e);
result = e.getMessage();
}
return result;
}
/**
* 普通邮件发送
* @param mailMessage
* @return
*/
private String sendSimpleMail(MailMessage mailMessage) {
String result = null;
try {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(mailMessage.getReceiverId());
message.setCc(from);
message.setSubject(mailMessage.getTitle());
message.setText(mailMessage.wrapperMessage());
mailSender.send(message);
}catch(Exception e) {
log.error("发送普通邮件报错: {} {}",e.getMessage(),e);
result = e.getMessage();
}
return result;
}
}
5.3 配置参数
spring:
# 邮件相关
mail:
host: smtp.qq.com
port: 465
username: xxx@qq.com
password: xxx
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
6. 腾讯短信发送实现类
6.1 引入依赖
6.2 定义实现类
package com.yj.notice.service.impl;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.yj.cmp.commons.json.JsonUtil;
import com.yj.notice.costant.NoticeMethodEnum;
import com.yj.notice.message.NoticeMessage;
import com.yj.notice.service.MessageService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* @author : dyg
* @className : TencentMessageServiceImpl
* @description : 描述说明该类的功能
* @date : 2024/4/16 10:10
*/
@Service("TencentMessageService")
@Slf4j
@Data
public class TencentMessageServiceImpl implements MessageService {
/**
* 短信发送API
*/
private final String SMS_API = "sms.tencentcloudapi.com";
private final String SMS_ACTION = "SendSms";
private final String SMS_VERSION = "2021-01-11";
private final String SMS_REGION= "ap-beijing";
/**
* 短信客户端
*/
private SmsClient smsClient;
/**
* 短信发送AppId
*/
@Value(value = "${notice.tencent.appId}")
private String smsSdkAppId;
/**
* 短信发送模板Id
*/
@Value(value = "${notice.tencent.templateId}")
private String smsTemplateId ;
/**
* 腾讯云认证信息
*/
@Value(value = "${notice.tencent.secretId}")
private String secretId ;
@Value(value = "${notice.tencent.secretKey}")
private String secretKey ;
/**
* 获取客户端
* @return
*/
public SmsClient getSmsClient() {
synchronized (this){
if(null == smsClient){
Credential credential = new Credential(secretId,secretKey);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
// 推荐使用北极星,相关指引可访问如下链接
// https://git.woa.com/tencentcloud-internal/tencentcloud-sdk-java#%E5%8C%97%E6%9E%81%E6%98%9F
httpProfile.setEndpoint(SMS_API);
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
smsClient = new SmsClient(credential,SMS_REGION,clientProfile);
}
}
return smsClient;
}
@Override
public String send(NoticeMessage message) {
String result = null;
SendSmsRequest sendSmsRequest = new SendSmsRequest();
sendSmsRequest.setSmsSdkAppId(smsSdkAppId);
sendSmsRequest.setPhoneNumberSet(new String[]{message.getReceiverPhone()});
sendSmsRequest.setSignName("腾讯云");
sendSmsRequest.setTemplateId(smsTemplateId);
sendSmsRequest.setTemplateParamSet(new String[]{message.getTitle(),message.getContent(),message.getTime()});
try {
SendSmsResponse sendSmsResponse = smsClient.SendSms(sendSmsRequest);
result = "发送腾讯短信平台结果: "+ JsonUtil.objectToJson(sendSmsResponse);
} catch (TencentCloudSDKException e) {
log.error(e.getMessage(),e);
result ="发送腾讯短信平台失败: "+ e.getMessage()+ " " + e;
}
return result;
}
@Override
public String getNoticeMethod() {
return NoticeMethodEnum.TENCENT_SMS.getName();
}
}
6.3 配置参数
# 消息推送相关
notice:
# 腾讯短信配置
tencent:
appId: xx
templateId: xx
secretId: xx
secretKey: xx
7. 系统内部消息发送实现类
7.1 系统内部消息表定义
-- 系统用户-站内消息表
DROP TABLE IF EXISTS `system_user_message`;
create table `system_user_message` (
`id` int(16) NOT NULL AUTO_INCREMENT,
`message_type` varchar(200) COMMENT '消息类型 public-公告 private-个人消息 ticket-工单消息',
`source_msg_id` int(16) COMMENT '消息id' ,
`title` varchar(200) COMMENT '标题',
`content` text COMMENT '内容',
`receiver_id` varchar(200) COMMENT '接收者id 用户id',
`send_time` datetime NOT NULL default NOW() COMMENT '发送时间',
`bus_type` varchar(200) COMMENT '业务类型',
`is_read` int(1) default 0 COMMENT '是否已读 0-no 1-yes',
`read_time` datetime COMMENT '读取时间',
`properties` varchar(1000) comment '其他数据',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '系统用户-站内消息表';
6.2 系统内部消息实体定义
package com.yj.notice.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yj.notice.costant.MessageSource;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* @author : dyg
* @className : SystemMessage
* @description : 内部通知消息
* @date : 2023/9/7 10:04
*/
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
@TableName("system_user_message")
public class SystemUserMessage implements Serializable {
private static final long serialVersionUID = 228169618996650529L;
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 消息类型
* {@link MessageSource}
*/
@TableField("message_type")
private String messageType;
/**
* 源头消息id
* public-公告 yg_alarm-云管告警 cloud_alarm-插件告警 使用到
* {@link MessageSource}
*/
@TableField("source_msg_id")
private Integer sourceMsgId;
/**
* 消息标题
*/
@TableField("title")
private String title;
/**
*消息内容
*/
@TableField("content")
private String content;
/**
* 接收者id
*/
@TableField("receiver_id")
private String receiverId;
/**
* 发送时间
*/
@TableField("send_time")
private Date sendTime;
/**
* 消息业务类型
*/
@TableField("bus_type")
private String busType;
/**
* 是否已读
* 0-no 1-yes
*/
@TableField("is_read")
private Integer isRead;
/**
* 读取时间
*/
@TableField("read_time")
private Date readTime;
@TableField("properties")
private String properties;
}
7.3 实现类定义
package com.yj.notice.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yj.cmp.bss.constant.RoleGroup;
import com.yj.cmp.bss.entity.APIPermission;
import com.yj.cmp.bss.entity.Audit;
import com.yj.cmp.bss.entity.Request;
import com.yj.cmp.bss.entity.User;
import com.yj.cmp.bss.service.RequestService;
import com.yj.cmp.bss.service.UserService;
import com.yj.cmp.commons.constant.BssConstant;
import com.yj.commons.security.user.SecurityUser;
import com.yj.commons.security.user.UserDetail;
import com.yj.commons.tools.page.PageData;
import com.yj.commons.tools.utils.Result;
import com.yj.commons.tools.utils.StringUtil;
import com.yj.helper.user.UserHelper;
import com.yj.notice.costant.MessageSource;
import com.yj.notice.costant.NoticeMethodEnum;
import com.yj.notice.dao.SystemUserMessageDao;
import com.yj.notice.entity.SystemUserMessage;
import com.yj.notice.entity.PublicSystemNotice;
import com.yj.notice.message.NoticeMessage;
import com.yj.notice.service.MessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author : dyg
* @className : NoticeInnerMessageServiceImpl
* @description : 描述说明该类的功能
* @date : 2023/9/7 10:42
*/
@Service
@Slf4j
public class SystemUserMessageServiceImpl extends ServiceImpl
@Override
public String send(NoticeMessage message) {
SystemUserMessage build = SystemUserMessage.builder().messageType(message.getMessageType())
.title(message.getTitle())
.content(message.getContent())
.receiverId(message.getReceiverId())
.sendTime(new Date())
.busType(message.getMessageType())
.isRead(0)
.build();
this.save(build);
return "success";
}
@Override
public String getNoticeMethod() {
return NoticeMethodEnum.IN_MSG.getName();
}
}
8. 消息推送测试
8.1 钉钉推送测试
package com.yj.notice.demo;
import com.aliyun.dingtalkrobot_1_0.Client;
import com.aliyun.dingtalkrobot_1_0.models.BatchSendOTOHeaders;
import com.aliyun.dingtalkrobot_1_0.models.BatchSendOTORequest;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.Common;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;
import com.taobao.api.ApiException;
import com.yj.commons.tools.utils.JsonUtil;
import com.yj.commons.tools.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Slf4j
public class DingTalkMessageSend {
// 企业凭证,两小时一更新
private static String enterpriseToken = "";
// 应用凭证
private static String appkey = "xxxx";
private static String appsecret = "xxxx";
// 获取token,每两小时失效
public static void getToken() {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
OapiGettokenRequest req = new OapiGettokenRequest();
req.setAppkey(appkey);
req.setAppsecret(appsecret);
req.setHttpMethod("GET");
OapiGettokenResponse rsp = client.execute(req);
log.info("token:" + rsp.getBody());
Map json = JsonUtil.StringToEntity(rsp.getBody(), Map.class);
enterpriseToken = json.get("access_token").toString();
} catch (ApiException e) {
e.printStackTrace();
}
}
/**
* 使用 Token 初始化账号Client
*
* @return Client
* @throws Exception
*/
private static Client createClient() throws Exception {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
return new Client(config);
}
/**
* 发送钉钉消息
*
* @param dingUserId 钉钉用户id
* @param content 发送消息内容
* @return void
*/
public static void createDingNotify(String dingUserId, String content) throws Exception {
if (StringUtil.isEmpty(enterpriseToken)) {
getToken();
}
Client client = createClient();
BatchSendOTOHeaders batchSendOTOHeaders = new BatchSendOTOHeaders();
batchSendOTOHeaders.xAcsDingtalkAccessToken = enterpriseToken;
content = "{\"content\": \"" + content + "\"}";
BatchSendOTORequest batchSendOTORequest = new BatchSendOTORequest()
.setRobotCode(appkey)//机器人appkey
.setUserIds(Arrays.asList(dingUserId))
.setMsgKey("officialTextMsg")
.setMsgParam(content);
try {
client.batchSendOTOWithOptions(batchSendOTORequest, batchSendOTOHeaders, new RuntimeOptions());
} catch (TeaException err) {
if (!Common.empty(err.code) && !Common.empty(err.message))
// err 中含有 code 和 message 属性,可帮助开发定位问题
log.error(err.code + ":" + err.message);
// 企业凭证enterpriseToken不合法导致出错时获取新企业凭证并重试
if (err.code.equals("InvalidAuthentication")) {
getToken();
}
} catch (Exception e) {
TeaException err = new TeaException(e.getMessage(), e);
if (!Common.empty(err.code) && !Common.empty(err.message))
// err中含有code和message 属性,可帮助开发定位问题
log.error(err.code + ":" + err.message);
}
}
/**
* 发送钉钉消息
*
* @param phone 用户手机号
* @return void
*/
public static String getDingdingUserIdByPhone(String phone) {
try {
if (StringUtil.isEmpty(enterpriseToken)) {
getToken();
}
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/getbymobile");
OapiV2UserGetbymobileRequest req = new OapiV2UserGetbymobileRequest();
req.setMobile(phone);
OapiV2UserGetbymobileResponse rsp = client.execute(req, enterpriseToken);
if (rsp != null) {
OapiV2UserGetbymobileResponse.UserGetByMobileResponse result = rsp.getResult();
return result == null ? null : result.getUserid();
}
} catch (ApiException e) {
log.error(e.getErrMsg(), e);
}
return null;
}
/**
* 获取组内用户列表
*
* @return void
*/
public static void getUserList( ) {
try {
if (StringUtil.isEmpty(enterpriseToken)) {
getToken();
}
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/listid");
OapiUserListRequest req = new OapiUserListRequest();
OapiUserListResponse rsp = client.execute(req, enterpriseToken);
if (rsp != null) {
List
userlist.stream().forEach(System.out::println);
}
} catch (ApiException e) {
log.error(e.getErrMsg(), e);
}
}
/**
* 获取组内用户列表
*
* @return void
*/
public static void getDepartmentList( ) {
try {
if (StringUtil.isEmpty(enterpriseToken)) {
getToken();
}
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub");
OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
OapiV2DepartmentListsubResponse rsp = client.execute(req, enterpriseToken);
if (rsp != null) {
List
result.stream().forEach(System.out::println);
}
} catch (ApiException e) {
log.error(e.getErrMsg(), e);
}
}
public static void main(String[] args) throws Exception{
String dingdingUserIdByPhone = getDingdingUserIdByPhone("17352253381");
System.out.println(dingdingUserIdByPhone);
createDingNotify(dingdingUserIdByPhone,"告警时间: 2023-09-05 16:33:36\n告警级别: 中级\n告警类别: 虚拟机\n告警消息: 虚拟机告警信息\n备注: 虚拟机告警信息");
getDepartmentList();
getUserList();
}
}
8.2 微信推送测试
package com.yj.notice.demo;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.yj.commons.tools.utils.DateUtils;
import com.yj.commons.tools.utils.JsonUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import static com.yj.commons.tools.utils.DateUtils.DATE_TIME_PATTERN;
/**
* @author : dyg
* @className : WechatMessageSend
* @description : 描述说明该类的功能
* @date : 2023/9/5 10:01
*/
@Slf4j
public class WechatMessageSend {
@Data
public static class WeChatTemplateMsg implements Serializable {
/**
* 消息
*/
private String value;
/**
* 消息颜色
*/
private String color;
public WeChatTemplateMsg(String value) {
this.value = value;
this.color = "#173177";
}
public WeChatTemplateMsg(String value, String color) {
this.value = value;
this.color = color;
}
}
public static String getAccessToken(){
String appId = "xxxx";
String appIdSecret = "xxxx";
String requestUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ appId +"&secret=" + appIdSecret;
String res = HttpUtil.get(requestUrl);
JSONObject jsonObject = JSONObject.parseObject(res);
String accessToken = jsonObject.getString("access_token");
log.info("accessToken:{}", accessToken);
return accessToken;
}
public static void getUserList(){
RestTemplate restTemplate = new RestTemplate();
String accessToken = getAccessToken();
String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/get?access_token="+ accessToken;
ResponseEntity
log.info("结果是: {}",response.getBody());
JSONObject result = JSONObject.parseObject(response.getBody());
com.alibaba.fastjson.JSONArray openIdJsonArray = result.getJSONObject("data").getJSONArray("openid");
Iterator iterator = openIdJsonArray.iterator();
if (iterator.hasNext()){
String userId = iterator.next().toString();
log.debug("用户openid:"+userId);
String userDetailUri = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+accessToken+"&openid="+userId+"&lang=zh_CN";
ResponseEntity
log.info("结果是: {}",response2.getBody());
JSONObject result2 = JSONObject.parseObject(response2.getBody());
log.info("用户详细信息: {}",result2.toJSONString());
}
}
public static void sendMessage(String type){
// 模板参数
Map
// openId代表一个唯一微信用户,即微信消息的接收人
String openId = "okUjK6P908Zwjc8BOSdinAO8iG5o";
// 公众号的模板id(也有相应的接口可以查询到)
String alarmTemplateId = "mfriJWNssZYtIbpfzxH-4FlxgK4ZQ0ID_xNvEO0xDOY";
String commonTemplateId = "q7bSOYPPDHn-Lz_vTShzsbU4WzKX3lH9nxuasnngj8A";
// 微信的基础accessToken
String accessToken = getAccessToken();
String requestUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken;
//消息主题显示相关map
//根据自己的模板定义内容和颜色
//拼接base参数
Map
sendBody.put("touser", openId); // openId
// sendBody.put("url", "https://www.baidu.com"); //跳转网页url
sendBody.put("data", sendMag); // 模板参数
if("alarm".equals(type)){
sendMag.put("message", new WeChatTemplateMsg("虚拟机-102.23.26.36 CPU使用占比超过80%"));
sendMag.put("time",new WeChatTemplateMsg(DateUtils.format(new Date(),DATE_TIME_PATTERN),"#173177"));
sendMag.put("level",new WeChatTemplateMsg("中等","#FF69B4" ));
sendMag.put("type",new WeChatTemplateMsg("虚拟机CPU" ,"#173177"));
sendMag.put("remark",new WeChatTemplateMsg("虚拟机CPU使用触发告警","#173177"));
sendBody.put("template_id", alarmTemplateId); // 模板Id
}else{
sendMag.put("content", new WeChatTemplateMsg("根据兰州市气象局发布的最新天气预警信息,未来8-24小时内,城关区、七里河区、西固区、安宁区、榆中县、皋兰县大部将有依次明显的降水活动,并伴有短时5-6级大风,请注意及时关注最新天气情况!"));
sendMag.put("title",new WeChatTemplateMsg("暴雨蓝色预警"));
sendMag.put("time",new WeChatTemplateMsg(DateUtils.format(new Date(),DATE_TIME_PATTERN),"#FF69B4"));
sendBody.put("template_id", commonTemplateId); // 模板Id
}
try {
// 1.创建httpclient对象
CloseableHttpClient client = HttpClients.createDefault();
// 2.创建post对象
HttpPost post = new HttpPost(requestUrl);
StringEntity postingString = new StringEntity(JsonUtil.entityToString(sendBody), "utf-8");
post.setEntity(postingString);
// 3.执行post方法:得到结果
CloseableHttpResponse response = client.execute(post);
// 4.处理结果
// 1.得到状态码
int statusCode = response.getStatusLine().getStatusCode();
log.info("----http code : {}", statusCode);
if (statusCode == 200) {
// 2.得到实体内容
org.apache.http.HttpEntity entity = response.getEntity();
String content = EntityUtils.toString(entity, "utf-8");
System.out.println(content);
}
// 5.关闭连接
client.close();
} catch (IOException e) {
log.error("------exception : {} {} ",e.getMessage(),e);
}
}
public static void main(String[] args) {
// getAccessToken();
// getUserList();
sendMessage("a");
}
}