Skip to content

Commit a1e986b

Browse files
committed
fix: enhance notification system reliability
- Fix: Trigger notification when Agent and Monitor recover from offline status - Fix: Prevent duplicate 'Down' notifications in Monitor task - Fix: Use 'Asia/Shanghai' timezone for notification time - Feat: Add status emojis (🟢/🔴) to Agent and Monitor notifications
1 parent 4803321 commit a1e986b

File tree

4 files changed

+206
-100
lines changed

4 files changed

+206
-100
lines changed

backend/src/jobs/agent-task.ts

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,11 @@ async function handleAgentOfflineNotification(
109109
name: agentName,
110110
status: "offline",
111111
previous_status: "online", // 添加previous_status变量
112-
time: new Date().toLocaleString("zh-CN"),
112+
time: new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
113113
hostname: agent.hostname || "未知",
114114
ip_addresses: getFormattedIPAddresses(agent.ip_addresses),
115115
os: agent.os || "未知",
116-
error: "客户端连接超时",
116+
error: "客户端连接超时 🔴",
117117
details: `主机名: ${
118118
agent.hostname || "未知"
119119
}\nIP地址: ${getFormattedIPAddresses(
@@ -145,6 +145,85 @@ async function handleAgentOfflineNotification(
145145
}
146146
}
147147

148+
/**
149+
* 处理客户端上线通知
150+
* @param env 环境变量
151+
* @param agentId 客户端ID
152+
* @param agentName 客户端名称
153+
* @param userId 用户ID
154+
*/
155+
export async function handleAgentOnlineNotification(
156+
env: any,
157+
agentId: number,
158+
agentName: string,
159+
userId: number
160+
) {
161+
try {
162+
// 检查是否需要发送通知
163+
// 注意:这里状态是从 offline 变为 online
164+
const notificationCheck = await shouldSendNotification(
165+
userId,
166+
"agent",
167+
agentId,
168+
"offline", // 上一个状态
169+
"online" // 当前状态
170+
);
171+
172+
if (
173+
!notificationCheck.shouldSend ||
174+
notificationCheck.channels.length === 0
175+
) {
176+
return;
177+
}
178+
179+
console.log(`客户端 ${agentName} (ID: ${agentId}) 已恢复上线,正在发送通知...`);
180+
181+
// 获取客户端完整信息
182+
const agent = await getAgentById(agentId);
183+
if (!agent) {
184+
console.error(`找不到客户端数据 (ID: ${agentId})`);
185+
return;
186+
}
187+
188+
// 准备通知变量
189+
const variables = {
190+
name: agentName,
191+
status: "online",
192+
previous_status: "offline",
193+
time: new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
194+
hostname: agent.hostname || "未知",
195+
ip_addresses: getFormattedIPAddresses(agent.ip_addresses),
196+
os: agent.os || "未知",
197+
error: "客户端连接已恢复 🟢",
198+
details: `主机名: ${
199+
agent.hostname || "未知"
200+
}\nIP地址: ${getFormattedIPAddresses(
201+
agent.ip_addresses
202+
)}\n操作系统: ${agent.os || "未知"}\n恢复时间: ${new Date().toLocaleString("zh-CN")}`,
203+
};
204+
205+
// 发送通知
206+
const notificationResult = await sendNotification(
207+
"agent",
208+
agentId,
209+
variables,
210+
notificationCheck.channels,
211+
userId
212+
);
213+
214+
if (notificationResult.success) {
215+
console.log(`客户端 ${agentName} (ID: ${agentId}) 上线通知发送成功`);
216+
} else {
217+
console.error(`客户端 ${agentName} (ID: ${agentId}) 上线通知发送失败`);
218+
}
219+
} catch (error) {
220+
console.error(
221+
`处理客户端上线通知时出错 (${agentName}, ID: ${agentId}):`,
222+
error
223+
);
224+
}
225+
}
226+
148227
/**
149228
* 处理客户端阈值超出通知
150229
* 此函数可以单独调用,也可以在客户端上报数据时触发
@@ -261,7 +340,7 @@ export async function handleAgentThresholdNotification(
261340
name: agent.name,
262341
status: `${metricName}告警`,
263342
previous_status: "normal", // 添加previous_status变量
264-
time: new Date().toLocaleString("zh-CN"),
343+
time: new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
265344
hostname: agent.hostname || "未知",
266345
ip_addresses: getFormattedIPAddresses(agent.ip_addresses),
267346
os: agent.os || "未知",

backend/src/jobs/monitor-task.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,22 @@ async function handleMonitorNotification(
6060
try {
6161
console.log(`======= 通知检查开始 =======`);
6262
console.log(`监控: ${monitor.name} (ID: ${monitor.id})`);
63-
console.log(
64-
`上一状态: ${checkResult.previous_status}, 当前状态: ${checkResult.status}`
65-
);
63+
// console.log(
64+
// `上一状态: ${checkResult.previous_status}, 当前状态: ${checkResult.status}`
65+
// );
6666

67-
// 如果监控状态没有变化,不需要继续处理
68-
if (checkResult.status === checkResult.previous_status) {
69-
console.log(`状态未变化,不发送通知`);
67+
// 如果监控状态没有变化,不需要继续处理,使用 monitor.status (数据库里的最新状态) 与刚才检查到的状态 (checkResult.status)
68+
if (monitor.status === checkResult.status) {
69+
console.log(`[Monitor] ${monitor.name} 状态未变化 (${monitor.status}),忽略通知`);
7070
return;
7171
}
7272

73+
// 定义当前状态和前一个状态
74+
const currentStatus = checkResult.status;
75+
const previousStatus = monitor.status || "unknown"; // 使用 monitor.status 作为前一个状态
76+
7377
console.log(
74-
`状态已变化: ${checkResult.previous_status} -> ${checkResult.status}`
78+
`状态已变化: ${previousStatus} -> ${currentStatus}`
7579
);
7680

7781
// 检查是否需要发送通知
@@ -80,8 +84,8 @@ async function handleMonitorNotification(
8084
monitor.created_by, // 修复: 传入 userId
8185
"monitor",
8286
monitor.id,
83-
checkResult.previous_status,
84-
checkResult.status
87+
previousStatus,
88+
currentStatus
8589
);
8690

8791
console.log(
@@ -105,19 +109,28 @@ async function handleMonitorNotification(
105109
);
106110
console.log(`通知渠道: ${JSON.stringify(notificationCheck.channels)}`);
107111

112+
// 信息添加红绿灯
113+
let errorMsg = checkResult.error || "无";
114+
if (currentStatus === "up") {
115+
errorMsg = "服务已恢复访问 🟢";
116+
}
117+
else if (currentStatus === "down") {
118+
errorMsg = `${checkResult.error || "服务无法访问"} 🔴`;
119+
}
120+
108121
// 准备通知变量
109122
const variables = {
110123
name: monitor.name,
111-
status: checkResult.status,
112-
previous_status: checkResult.previous_status || "未知",
113-
time: new Date().toLocaleString("zh-CN"),
124+
status: currentStatus,
125+
previous_status: previousStatus,
126+
time: new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
114127
url: monitor.url,
115128
response_time: `${checkResult.responseTime}ms`,
116129
status_code: checkResult.statusCode
117130
? checkResult.statusCode.toString()
118131
: "无",
119132
expected_status: monitor.expected_status.toString(),
120-
error: checkResult.error || "无",
133+
error: errorMsg,
121134
details: `URL: ${monitor.url}\n响应时间: ${
122135
checkResult.responseTime
123136
}ms\n状态码: ${checkResult.statusCode || "无"}\n错误信息: ${

backend/src/services/AgentService.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Bindings } from "../models/db";
22
import type { Agent } from "../models/agent";
33
import * as AgentRepository from "../repositories";
44
import { generateToken, verifyToken } from "../utils/jwt";
5-
import { handleAgentThresholdNotification } from "../jobs/agent-task";
5+
import { handleAgentThresholdNotification, handleAgentOnlineNotification } from "../jobs/agent-task"; // 引入上线通知处理函数
66

77
/**
88
* 获取所有客户端
@@ -305,6 +305,19 @@ export async function updateAgentStatusService(status: any) {
305305
// 通过token查找客户端
306306
const agent = await AgentRepository.getAgentByToken(norlmalInfo.token);
307307

308+
if (!agent) {
309+
throw new Error("找不到对应的Agent");
310+
}
311+
312+
// 检测上线状态
313+
if (agent.status !== "active") {
314+
console.log(`[AgentService] 客户端 ${agent.name} (ID: ${agent.id}) 恢复上线,触发通知...`);
315+
316+
// 异步触发上线通知,不阻塞主流程
317+
handleAgentOnlineNotification({}, agent.id, agent.name, agent.created_by)
318+
.catch(err => console.error(`[AgentService] 触发上线通知失败:`, err));
319+
}
320+
308321
if (
309322
agent.status != "active" ||
310323
agent.hostname != norlmalInfo.hostname ||

0 commit comments

Comments
 (0)