之前有集成過釘釘群機(jī)器人??報(bào)警慧库,這次主要是SpringBoot集成企業(yè)微信群機(jī)器人報(bào)警。
聲明:
1.本篇文章不涉及任何業(yè)務(wù)數(shù)據(jù)
2.禁止轉(zhuǎn)載2鍪取F氚濉!禁止轉(zhuǎn)載8鸸健8誓ァ!禁止轉(zhuǎn)載C型!<糜摺!
相關(guān)文檔:
企業(yè)微信群機(jī)器人配置說(shuō)明
1. 配置
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @descriptions: 配置參數(shù)
* @author: xucl
*/
@Component
@Getter
@Setter
@ConfigurationProperties(prefix = "notice")
public class NoticeProperties {
private String wechatKey;
private List<String> phoneList;
}
這個(gè)key的獲取方式:群機(jī)器人配置說(shuō)明
yaml文件配置數(shù)據(jù):
notice:
####### 企業(yè)微信群機(jī)器人key
wechat-key: xxxxxxxxx-xxx-xxx-xxxx-xxxxxxxxxx
####### 需要@的群成員手機(jī)號(hào)
phone-list:
1.2 Http客戶端配置
這里用的Forest 莺债,感覺還挺強(qiáng)大靈活 官方文檔吗冤,如果你喜歡使用httpClient或者okHttp建議你看看forest ;如果你更喜歡RestTemplate九府,那就使用RestTemplate。
POM依賴
<!-- 輕量級(jí)HTTP客戶端框架 -->
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
yaml文件配置
日志打開關(guān)閉請(qǐng)參考自己的業(yè)務(wù)需要
## 輕量級(jí)HTTP客戶端框架forest
forest:
# 配置底層API為 okhttp3
backend: okhttp3
# 連接池最大連接數(shù)覆致,默認(rèn)值為500
max-connections: 1000
# 每個(gè)路由的最大連接數(shù)侄旬,默認(rèn)值為500
max-route-connections: 500
# 請(qǐng)求超時(shí)時(shí)間,單位為毫秒, 默認(rèn)值為3000
timeout: 3000
# 連接超時(shí)時(shí)間煌妈,單位為毫秒, 默認(rèn)值為2000
connect-timeout: 3000
# 請(qǐng)求失敗后重試次數(shù)儡羔,默認(rèn)為0次不重試
retry-count: 1
# 單向驗(yàn)證的HTTPS的默認(rèn)SSL協(xié)議,默認(rèn)為SSLv3
ssl-protocol: SSLv3
# 打開或關(guān)閉日志璧诵,默認(rèn)為true
logEnabled: true
# 打開/關(guān)閉Forest請(qǐng)求日志(默認(rèn)為 true)
log-request: true
# 打開/關(guān)閉Forest響應(yīng)狀態(tài)日志(默認(rèn)為 true)
log-response-status: true
# 打開/關(guān)閉Forest響應(yīng)內(nèi)容日志(默認(rèn)為 false)
log-response-content: true
發(fā)送Http
使用前請(qǐng)注意
在 Spring Boot 項(xiàng)目中調(diào)用接口
只要在Spring Boot的配置類或者啟動(dòng)類上加上@ForestScan注解汰蜘,并在basePackages屬性里填上遠(yuǎn)程>接口的所在的包名,加入@ForestScan
src/main/java/MyApp.java
@SpringBootApplication
@Configuration
@ForestScan(basePackages = "com.yoursite.client")
public class MyApp {
...
}
Forest 會(huì)掃描@ForestScan注解中basePackages屬性指定的包下面所有的接口之宿,然后會(huì)將符合條件的接口進(jìn)行動(dòng)態(tài)代理并注入到 Spring 的上下文中族操。
友情提示:
1.5.1以后版本可以跳過此步,不需要 @ForestScan 注解來(lái)指定掃描的包范圍
發(fā)送請(qǐng)求的客戶端
public interface NoticeClient {
/**
* 企業(yè)微信機(jī)器人 發(fā)送 https 請(qǐng)求
*
* @param keyValue
* @return
*/
@Post(
url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={keyValue}",
headers = {
"Accept-Charset: utf-8",
"Content-Type: application/json"
},
dataType = "json"
)
ForestResponse<JsonObject> weChatNotice(@Var("keyValue") String keyValue, @JSONBody Map<String, Object> body );
}
2.定義服務(wù)
2.1 接口
/**
* @descriptions: 消息通知接口
* @author: xucl
*/
public interface NoticeService {
/**
* 發(fā)送錯(cuò)誤信息至群機(jī)器人
* @param throwable
* @param msg
*/
void sendError(Throwable throwable,String msg);
/**
* 發(fā)送文本信息至群機(jī)器人
* @param msg
*/
void sendByMd(String msg);
/**
* 發(fā)送md至群機(jī)器人
* @param msg 文本消息
* @param isAtALL 是否@所有人 true是 false否
*/
void sendByText(String msg,boolean isAtALL);
}
2.2 工具實(shí)現(xiàn)
注意:這里的包名(com.github)填自己項(xiàng)目?jī)?nèi)的包名
import com.dtflys.forest.http.ForestResponse;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* @descriptions: 通知實(shí)現(xiàn)
* @author: xucl
*/
@Service
@Slf4j
public class NoticeServiceImpl implements NoticeService {
@Value("${spring.application.name}")
private String appName;
@Value("${spring.profiles.active}")
private String env;
@Autowired
private NoticeClient noticeClient;
@Autowired
private NoticeProperties noticeProperties;
/**
* 發(fā)送錯(cuò)誤信息至群機(jī)器人
*
* @param throwable
* @param msg
*/
@Override
public void sendError(Throwable throwable, String msg) {
String errorClassName = throwable.getClass().getSimpleName();
if (StringUtils.isBlank(msg)){
msg = throwable.getMessage() == null ? "出現(xiàn)null值" : throwable.getMessage();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
throwable.printStackTrace(new PrintStream(baos));
String bodyStr = StringComUtils.limitStrNone(regexThrowableStr(baos.toString()),450);
String md = getMdByTemplate(appName, env, errorClassName, msg, bodyStr);
sendByMd(md);
}
/**
* 發(fā)送文本信息至群機(jī)器人
*
* @param msg
*/
@Override
public void sendByMd(String msg) {
try {
Map<String, Object> params = buildMdParams(msg);
ForestResponse<JsonObject> response = noticeClient.weChatNotice(noticeProperties.getWechatKey(), params);
log.debug("WeChatRobo-Send Error:{} Status:{}",response.isError(),response.getStatusCode());
} catch (Exception e) {
log.error("WeChatRobot-發(fā)送文本消息異常 body:{}",msg,e);
}
}
/**
* 發(fā)送md至群機(jī)器人
*
* @param msg
*/
@Override
public void sendByText(String msg,boolean isAtALL) {
try {
Map<String, Object> params = buildTextParams(msg,noticeProperties.getPhoneList(),isAtALL);
ForestResponse<JsonObject> response = noticeClient.weChatNotice(noticeProperties.getWechatKey(), params);
log.debug("WeChatRobo-Send Error:{} Status:{}",response.isError(),response.getStatusCode());
} catch (Exception e) {
log.error("WeChatRobot-發(fā)送文本消息異常 body:{}",msg,e);
}
}
/**
* 構(gòu)建發(fā)送文本消息格式的參數(shù)
* @param phoneList @群用戶
* @param isAtALL 是否@所有人
* @return
*/
private Map<String,Object> buildTextParams(String text, List<String> phoneList, boolean isAtALL){
Map<String,Object> params = new HashMap<>();
Map<String,Object> data = new HashMap<>();
data.put("content",text);
if (isAtALL){
phoneList.add("@all");
}
if (CollectionUtils.isNotEmpty(phoneList)){
data.put("mentioned_mobile_list", phoneList);
}
params.put("msgtype","text");
params.put("text",data);
return params;
}
/**
* 構(gòu)建發(fā)送markdown消息格式的參數(shù)
*
* @param md
* @return
*/
private Map<String,Object> buildMdParams(String md){
Map<String,Object> params = new HashMap<>();
Map<String,Object> data = new HashMap<>();
data.put("content",md);
params.put("msgtype","markdown");
params.put("markdown",data);
return params;
}
private String regexThrowableStr(String str){
try {
// 注意:這里的包名(com.github)填自己項(xiàng)目?jī)?nèi)的包名
String pattern = "(com)(\\.)(github)(.{10,200})(\\))";
Pattern r = Pattern.compile(pattern);
Matcher m=r.matcher(str);
List<String> list = new ArrayList<>();
while (m.find()) {
list.add(m.group());
}
if (CollectionUtils.isEmpty(list)){
return str;
}
String s = list.stream().collect(Collectors.joining("\n"));
return s;
} catch (Exception e) {
return str;
}
}
private String getMdByTemplate(String appName,String env,String errorClassName,String msg,String bodyStr){
String titleTpl = "### 異常告警通知\n#### 應(yīng)用:%s\n#### 環(huán)境:<font color=\"info\">%s</font>\n##### 異常:<font color=\"warning\">%s</font>\n";
String bodyTpl = "\nMsg:%s\nDetail:\n>%s";
String footerTpl = "\n<font color=\"comment\">%s</font>";
String title = String.format(titleTpl, appName, env, errorClassName);
String body = String.format(bodyTpl, msg,bodyStr);
String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));
String footer = String.format(footerTpl, dateStr);
return title.concat(body).concat(footer);
}
}
使用到的工具方法:
/**
* 限制文本描述
*
* @param content 內(nèi)容或問題
* @param charNumber 長(zhǎng)度
* @return
*/
public static String limitStrNone(String content ,int charNumber){
if (StringUtils.isNotBlank(content)){
if (content.length() > charNumber){
String substring = content.substring(0, charNumber);
return substring;
}else {
return content;
}
}
return "";
}
最終效果: