一 建立机器人步骤
进入群设置->点击智能群助手->点击添加机器人
点击添加机器人->点击自定义->点击添加
重点牢记(秘钥、Webhook地址、access_access_token代码中会用到,需保存好):填写机器人信息,选择数据传输安全方式->获取机器请求地址
二 机器人同步异常信息到钉钉指定群
如下为service所有代码,从execute(WarnContent content)实现类开始阅读即可,content后面全局异常调用会传入。
http主要的maven依赖org.apache.httpcomponents httpclient 4.5.7 org.apache.httpcomponents httpcore 4.4.11
@Component
@Slf4j
public class DingTalkExceptionWarnImpl implements ExceptionWarn {
/**
* 执行同步钉钉任务
* @param content
*/
@Override
public void execute(WarnContent content) {
//数据安全加密秘钥
String secret="SECad6f95819fcfd1620cb267d9850d0336b9e8e046234f5c3e7ef48c4b4dae7543";
//请求通行认证
String accessToken="8a9fcc774fe636f73c486c0d400b96a74d546d3d32f0f62abfef666b43609ec2";
try {
long timestamp = System.currentTimeMillis();
String stringToSign = timestamp + "\n" +secret;
Mac mac = Mac.getInstance("HmacSHA256");
String charsetName = "UTF-8";
mac.init(new SecretKeySpec(secret.getBytes(charsetName), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes(charsetName));
//签名
String signature = new String(Base64.encodeBase64(signData));
Map headers = Maps.newHashMap();
headers.put("Content-Type", "application/json");
Map querys = Maps.newHashMap();
querys.put("access_token", accessToken);
querys.put("timestamp", timestamp + "");
querys.put("sign", signature);
HttpResponse response = doPost(
"https://oapi.dingtalk.com", "/robot/send", headers, querys, dingTalkFormat(content));
WarnResult res = getResult(response, WarnResult.class);
if (res.getErrcode() != 0) {
log.error(res.getErrcode() + "", res.getErrmsg());
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
/**
* 转换钉钉格式
*/
private String dingTalkFormat(WarnContent content) {
String title = content.getTitle();
content.setTitle("[dev 1.0][zjmProject]" + title);
Map text = Maps.newHashMap();
text.put("content", content.getTitle() + "\n" + content.getText());
Map data = Maps.newLinkedHashMap();
data.put("msgtype", "text");
data.put("text", text);
Map at = new HashMap<>();
at.put("isAtAll", true);
data.put("at", at);
return JSON.toJSONString(data);
}
/**
* 发送post请求
*/
private static HttpResponse doPost(String host, String path,
Map headers,
Map querys,
String body)
throws Exception {
HttpClient httpClient = wrapClient(host,path);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
if(headers != null) {
for (Map.Entry e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
}
if (StringUtils.isNotBlank(body)) {
request.setEntity(new StringEntity(body, "utf-8"));
}
return httpClient.execute(request);
}
/**
* 获取结果
*/
private static T getResult(HttpResponse httpResponse, Class cls) throws IOException {
return JSON.parseObject(getString(httpResponse), cls);
}
/**
* 将结果转换成string
*/
private static String getString(HttpResponse httpResponse) throws IOException {
HttpEntity entity = httpResponse.getEntity();
String resp = EntityUtils.toString(entity, "UTF-8");
EntityUtils.consume(entity);
return resp;
}
/**
* 获取 HttpClient
*/
private static HttpClient wrapClient(String host,String path) {
HttpClient httpClient = HttpClientBuilder.create().build();
if (host != null && host.startsWith("https://")) {
return sslClient();
}else if (StringUtils.isBlank(host) && path != null && path.startsWith("https://")) {
return sslClient();
}
return httpClient;
}
/**
* 创建URL
*/
private static String buildUrl(String host, String path, Map querys) throws UnsupportedEncodingException {
StringBuilder sbUrl = new StringBuilder();
if (!StringUtils.isBlank(host)) {
sbUrl.append(host);
}
if (!StringUtils.isBlank(path)) {
sbUrl.append(path);
}
if (null != querys) {
StringBuilder sbQuery = new StringBuilder();
for (Map.Entry query : querys.entrySet()) {
if (0 < sbQuery.length()) {
sbQuery.append("&");
}
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
sbQuery.append(query.getValue());
}
if (!StringUtils.isBlank(query.getKey())) {
sbQuery.append(query.getKey());
if (!StringUtils.isBlank(query.getValue())) {
sbQuery.append("=");
sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
}
}
}
if (0 < sbQuery.length()) {
sbUrl.append("?").append(sbQuery);
}
}
return sbUrl.toString();
}
/**
* 在调用SSL之前需要重写验证方法,取消检测SSL
* 创建ConnectionManager,添加Connection配置信息
* @return HttpClient 支持https
*/
private static HttpClient sslClient() {
try {
// 在调用SSL之前需要重写验证方法,取消检测SSL
X509TrustManager trustManager = new X509TrustManager() {
@Override public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override public void checkClientTrusted(X509Certificate[] xcs, String str) {}
@Override public void checkServerTrusted(X509Certificate[] xcs, String str) {}
};
SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
ctx.init(null, new TrustManager[] { trustManager }, null);
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
// 创建Registry
RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT)
.setExpectContinueEnabled(Boolean.TRUE).setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM,AuthSchemes.DIGEST))
.setProxyPreferredAuthSchemes(Collections.singletonList(AuthSchemes.BASIC)).build();
Registry socketFactoryRegistry = RegistryBuilder.create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https",socketFactory).build();
// 创建ConnectionManager,添加Connection配置信息
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
CloseableHttpClient closeableHttpClient = HttpClients.custom().setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig).build();
return closeableHttpClient;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
三 全局异常捕获并同步到钉钉机器人
如下为全局异常同步service所有代码,至此项目中所有的运行时异常会同步到钉钉群中
@Slf4j
@RestControllerAdvice
public class GlobalException {
@Autowired
private ExceptionWarn exceptionWarn;
/**
* 运行时异常全局捕获,
项目中所有运行时异常会同步到此方法
*/
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public void handle(Exception e) {
log.error("GlobalException handle() failed,error message {}", e.getMessage(), e);
doWarn(e);
}
/**
* 钉钉机器人同步
*/
private void doWarn(Exception e) {
try {
//调用钉钉机器人传递异常信息
exceptionWarn.execute(WarnContent.builder().title(e.getMessage()).text(getStackTraceAsString(e)).build());
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
}
/**
* 转换栈追踪信息
*/
private static String getStackTraceAsString(Throwable ex) {
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
}
四 两个代码额外的bean补充
@Data
@Builder
/**
* 异常内容
*/
public class WarnContent implements Serializable {
/**
* 标题
*/
private String title;
/**
* 内容
*/
private String text;
}
/**
* 提醒结果
*/
public class WarnResult implements Serializable {
/**
* 异常代码, 0 正常 其他正常
*/
private Integer errcode;
private String errmsg;
public Integer getErrcode() {
return errcode;
}
public void setErrcode(Integer errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
}
钉钉收到异常信息展示类似如下: