Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[task#736]Add enterprise WeChat application notifications for alerts #844

Merged
merged 3 commits into from
Apr 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ public class NoticeReceiver {
@NotNull
private String name;

@Schema(title = "Notification information method: 0-SMS 1-Email 2-webhook 3-WeChat Official Account 4-Enterprise WeChat Robot 5-DingTalk Robot 6-FeiShu Robot 7-Telegram Bot 8-SlackWebHook 9-Discord Bot",
description = "通知信息方式: 0-手机短信 1-邮箱 2-webhook 3-微信公众号 4-企业微信机器人 5-钉钉机器人 6-飞书机器人 7-Telegram机器人 8-SlackWebHook 9-Discord机器人",
@Schema(title = "Notification information method: 0-SMS 1-Email 2-webhook 3-WeChat Official Account 4-Enterprise WeChat Robot 5-DingTalk Robot 6-FeiShu Robot 7-Telegram Bot 8-SlackWebHook 9-Discord Bot 10-Enterprise WeChat app message",
description = "通知信息方式: 0-手机短信 1-邮箱 2-webhook 3-微信公众号 4-企业微信机器人 5-钉钉机器人 6-飞书机器人 7-Telegram机器人 8-SlackWebHook 9-Discord机器人 10-企业微信-应用消息",
accessMode = READ_WRITE)
@Min(0)
@NotNull
Expand Down Expand Up @@ -120,6 +120,21 @@ public class NoticeReceiver {
@Length(max = 300)
private String slackWebHookUrl;

@Schema(title = "Enterprise weChat message: The notification method is valid for Enterprise WeChat app message",
description = "企业信息 : 通知方式为Enterprise WeChat app message有效",
example = "ww1a603432123d0dc1", accessMode = READ_WRITE)
private String corpId;

@Schema(title = "Enterprise weChat appId: The notification method is valid for Enterprise WeChat app message",
description = "企业微信应用id : 通知方式为Enterprise WeChat app message有效",
example = "1000001", accessMode = READ_WRITE)
private Integer agentId;

@Schema(title = "Enterprise weChat secret: The notification method is valid for Enterprise WeChat app message",
description = "企业微信应用secret : 通知方式为Enterprise WeChat app message有效",
example = "oUydwn92ey0lnuY02MixNa57eNK-20dJn5NEOG-u2uE", accessMode = READ_WRITE)
private String appSecret;

@Schema(title = "Discord channel id: The notification method is valid for Discord",
description = "Discord 频道id: 通知方式为Discord有效",
example = "1065303416030642266", accessMode = READ_WRITE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.dromara.hertzbeat.manager.component.alerter.impl;

import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hertzbeat.common.entity.alerter.Alert;
import org.dromara.hertzbeat.common.entity.manager.NoticeReceiver;
import org.dromara.hertzbeat.manager.component.alerter.AlertNotifyHandler;
import org.dromara.hertzbeat.manager.pojo.dto.WeChatAppDTO;
import org.dromara.hertzbeat.manager.pojo.dto.WeChatAppReq;
import org.dromara.hertzbeat.manager.support.exception.AlertNoticeException;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.Objects;

/**
* @description:
* @author: hdd
* @create: 2023/04/04
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class WeChatAppAlertNotifyHandlerImpl implements AlertNotifyHandler {

/**
* send weChat app message url
*/
private static final String APP_MESSAGE_URL = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s";

/**
* get access_token url
*/
private static final String SECRET_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s";

/**
* 应用消息发送对象
*/
private static final String DEFAULT_ALL = "@all";

/**
* send message type
*/
private static final String DEFAULT_TYPE = "text";

private final RestTemplate restTemplate;

@Override
public void send(NoticeReceiver receiver, Alert alert) throws AlertNoticeException {
String corpId = receiver.getCorpId();
Integer agentId = receiver.getAgentId();
String appSecret = receiver.getAppSecret();

try {
ResponseEntity<WeChatAppReq> entityResponse = restTemplate.getForEntity(String.format(SECRET_URL, corpId, appSecret), WeChatAppReq.class);
if (Objects.nonNull(entityResponse.getBody())) {
String accessToken = entityResponse.getBody().getAccessToken();
WeChatAppDTO.TextDTO textDTO = new WeChatAppDTO.TextDTO();
textDTO.setContent(JSON.toJSONString(alert));
WeChatAppDTO weChatAppDTO = WeChatAppDTO.builder()
.toUser(DEFAULT_ALL)
.msgType(DEFAULT_TYPE)
.agentId(agentId)
.text(textDTO)
.build();
ResponseEntity<WeChatAppReq> response = restTemplate.postForEntity(String.format(APP_MESSAGE_URL, accessToken), weChatAppDTO, WeChatAppReq.class);
if (Objects.nonNull(response.getBody()) && !Objects.equals(response.getBody().getErrCode(), 0)) {
log.warn("Send Enterprise WeChat App Error: {} Failed: {}", receiver.getHookUrl(), response.getBody().getErrMsg());
throw new AlertNoticeException("Http StatusCode " + response.getStatusCode());
}
}
} catch (Exception e) {
throw new AlertNoticeException("[WebHook Notify Error] " + e.getMessage());
}
}

@Override
public byte type() {
return 10;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.dromara.hertzbeat.manager.pojo.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* @description:
* @author: hdd
* @create: 2023/04/05
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class WeChatAppDTO {

@JsonProperty(value = "touser")
private String toUser;

@JsonProperty(value = "toparty")
private String toParty;

@JsonProperty(value = "totag")
private String toTag;

@JsonProperty(value = "msgtype")
private String msgType;

@JsonProperty(value = "agentid")
private Integer agentId;


private TextDTO text;


@Data
public static class TextDTO {
/**
* 消息内容
*/
private String content;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.dromara.hertzbeat.manager.pojo.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* @description:
* @author: hdd
* @create: 2023/04/05
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WeChatAppReq {

@JsonProperty(value = "errcode")
private Integer errCode;

@JsonProperty(value = "errmsg")
private String errMsg;

@JsonProperty(value = "access_token")
private String accessToken;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.dromara.hertzbeat.manager.component.alerter.impl;

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.web.client.RestTemplate;

/**
* @description:
* @author: hdd
* @create: 2023/04/05
*/
public class WeChatAppAlertNotifyHandlerImplTest {


@InjectMocks
private WeChatAppAlertNotifyHandlerImpl weChatAppAlertNotifyHandler;

@Mock
private RestTemplate restTemplate;


}
5 changes: 4 additions & 1 deletion web-app/src/app/pojo/NoticeReceiver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export class NoticeReceiver {
id!: number;
name!: string;
// 通知信息方式: 0-手机短信 1-邮箱 2-webhook 3-微信公众号 4-企业微信机器人 5-钉钉机器人 6-飞书机器人 7-Telegram机器人 8-SlackWebHook 9-Discord机器人
// 通知信息方式: 0-手机短信 1-邮箱 2-webhook 3-微信公众号 4-企业微信机器人 5-钉钉机器人 6-飞书机器人 7-Telegram机器人 8-SlackWebHook 9-Discord机器人 10-企业微信应用消息
type: number = 1;
phone!: string;
email!: string;
Expand All @@ -13,6 +13,9 @@ export class NoticeReceiver {
hookUrl!: string;
wechatId!: string;
accessToken!: string;
corpId!: string;
agentId!: number;
appSecret!: string;
creator!: string;
modifier!: string;
gmtCreate!: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@
<i nz-icon nzType="notification" nzTheme="outline"></i>
<span>{{ 'alert.notice.type.discord' | i18n }}</span>
</nz-tag>
<nz-tag *ngIf="data.type == 10" nzColor="orange">
<i nz-icon nzType="notification" nzTheme="outline"></i>
<span>{{ 'alert.notice.type.weChatApp' | i18n }}</span>
</nz-tag>
</td>
<td nzAlign="center">
<span *ngIf="data.type == 0">{{ data.phone }}</span>
Expand All @@ -106,6 +110,7 @@
<span *ngIf="data.type == 7">{{ data.tgBotToken }}<br />{{ data.tgUserId }}</span>
<span *ngIf="data.type == 8">{{ data.slackWebHookUrl }}</span>
<span *ngIf="data.type == 9">{{ data.discordChannelId }}<br />{{ data.discordBotToken }}</span>
<span *ngIf="data.type == 10">{{ data.corpId }}<br />{{ data.agentId }}<br />{{ data.appSecret }}</span>
</td>
<td nzAlign="center">{{ (data.gmtUpdate ? data.gmtUpdate : data.gmtCreate) | date : 'YYYY-MM-dd HH:mm:ss' }}</td>
<td nzAlign="center">
Expand Down Expand Up @@ -236,6 +241,7 @@
<nz-option [nzValue]="5" [nzLabel]="'alert.notice.type.ding' | i18n"></nz-option>
<nz-option [nzValue]="6" [nzLabel]="'alert.notice.type.fei-shu' | i18n"></nz-option>
<nz-option [nzValue]="7" [nzLabel]="'alert.notice.type.telegram-bot' | i18n"></nz-option>
<nz-option [nzValue]="10" [nzLabel]="'alert.notice.type.weChatApp' | i18n"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
Expand Down Expand Up @@ -329,6 +335,30 @@
<input [(ngModel)]="receiver.discordChannelId" nz-input [required]="receiver.type === 9" name="discordChannelId" type="text" />
</nz-form-control>
</nz-form-item>
<nz-form-item *ngIf="receiver.type === 10">
<nz-form-label [nzSpan]="7" nzFor="corpId" [nzRequired]="receiver.type === 10">
{{ 'alert.notice.type.weChatApp-corpId' | i18n }}
</nz-form-label>
<nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | i18n">
<input [(ngModel)]="receiver.wechatId" nz-input [required]="receiver.type === 10" name="corpId" type="text" />
</nz-form-control>
</nz-form-item>
<nz-form-item *ngIf="receiver.type === 10">
<nz-form-label [nzSpan]="7" nzFor="agentId" [nzRequired]="receiver.type === 10">
{{ 'alert.notice.type.weChatApp-agentId' | i18n }}
</nz-form-label>
<nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | i18n">
<input [(ngModel)]="receiver.agentId" nz-input [required]="receiver.type === 10" name="agentId" type="text" />
</nz-form-control>
</nz-form-item>
<nz-form-item *ngIf="receiver.type === 10">
<nz-form-label [nzSpan]="7" nzFor="appSecret" [nzRequired]="receiver.type === 10">
{{ 'alert.notice.type.weChatApp-appSecret' | i18n }}
</nz-form-label>
<nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | i18n">
<input [(ngModel)]="receiver.appSecret" nz-input [required]="receiver.type === 10" name="appSecret" type="text" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzOffset]="7" [nzSpan]="12">
<button nz-button nzDanger nzType="primary" [nzLoading]="isSendTestButtonLoading" (click)="onSendAlertTestMsg()">
Expand Down
4 changes: 4 additions & 0 deletions web-app/src/assets/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@
"alert.notice.type.discord": "Discord Bot",
"alert.notice.type.discord-bot-token": "Discord Bot Token",
"alert.notice.type.discord-channel-id": "Discord Channel ID",
"alert.notice.type.weChatApp": "Enterprise WeChat app",
"alert.notice.type.weChatApp-corpId": "Enterprise WeChat ID",
"alert.notice.type.weChatApp-agentId": "Enterprise App ID",
"alert.notice.type.weChatApp-appSecret": "Enterprise App Secret",
"alert.notice.rule": "Alert Notice Policy",
"alert.notice.rule.new": "New Notice Policy",
"alert.notice.rule.edit": "Edit Notice Policy",
Expand Down
4 changes: 4 additions & 0 deletions web-app/src/assets/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@
"alert.notice.type.discord": "Discord机器人",
"alert.notice.type.discord-bot-token": "Discord Bot Token",
"alert.notice.type.discord-channel-id": "Discord频道ID",
"alert.notice.type.weChatApp": "企业微信应用",
"alert.notice.type.weChatApp-corpId": "企业id",
"alert.notice.type.weChatApp-agentId": "企业应用id",
"alert.notice.type.weChatApp-appSecret": "企业应用secret",
"alert.notice.rule": "告警通知策略",
"alert.notice.rule.new": "新增通知策略",
"alert.notice.rule.edit": "编辑通知策略",
Expand Down
4 changes: 4 additions & 0 deletions web-app/src/assets/i18n/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@
"alert.notice.type.discord": "Discord機器人",
"alert.notice.type.discord-bot-token": "Discord Bot Token",
"alert.notice.type.discord-channel-id": "Discord頻道ID",
"alert.notice.type.weChatApp": "企業微信應用",
"alert.notice.type.weChatApp-corpId": "企業id",
"alert.notice.type.weChatApp-agentId": "企業應用id",
"alert.notice.type.weChatApp-appSecret": "企業應用secret",
"alert.notice.rule": "告警通知策略",
"alert.notice.rule.new": "新增通知策略",
"alert.notice.rule.edit": "編輯通知策略",
Expand Down