77 lines
3.0 KiB
TypeScript
77 lines
3.0 KiB
TypeScript
// src/lib/monitor/wechat-pusher.ts
|
||
import type { MonitorConfig } from './types'
|
||
import { formatBeijingTime } from './types'
|
||
|
||
const logger = {
|
||
info: (msg: string) => console.log(`[Worker] ${formatBeijingTime()} INFO ${msg}`),
|
||
error: (msg: string) => console.error(`[Worker] ${formatBeijingTime()} ERROR ${msg}`),
|
||
}
|
||
|
||
export class WeChatPusher {
|
||
async pushText(text: string, webhookUrl: string): Promise<boolean> {
|
||
if (!webhookUrl) { logger.error('Webhook URL not configured'); return false }
|
||
try {
|
||
const response = await fetch(webhookUrl, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ msgtype: 'text', text: { content: text } }),
|
||
signal: AbortSignal.timeout(5_000),
|
||
})
|
||
if (!response.ok) { logger.error(`Webhook HTTP ${response.status}`); return false }
|
||
const result = await response.json() as { errcode?: number; errmsg?: string }
|
||
if (result.errcode === 0) { logger.info('WeChat message sent'); return true }
|
||
logger.error(`WeChat error: ${result.errmsg}`)
|
||
return false
|
||
} catch (e) {
|
||
logger.error(`Webhook failed: ${e instanceof Error ? e.message : e}`)
|
||
return false
|
||
}
|
||
}
|
||
|
||
formatAvailabilityMessage(
|
||
deadlines: Record<string, { deadline: string; hours: number }>,
|
||
faultInfo: { server_ip: string | null; server_sn: string | null; fault_time: string | null },
|
||
isOemDiag: boolean,
|
||
oemDeadline: string | null,
|
||
orderNumber: string | null,
|
||
rackPosition: string | null,
|
||
faultDetail: string | null,
|
||
): string {
|
||
let msg = ''
|
||
const typeLabel = isOemDiag ? 'OEM诊断' : 'OEM维修'
|
||
msg += `【服务器故障单】 - ${typeLabel}\n\n`
|
||
if (orderNumber) msg += `工单号:${orderNumber}\n`
|
||
msg += `服务器IP:${faultInfo.server_ip || '未知'}\n`
|
||
msg += `服务器SN:${faultInfo.server_sn || '未知'}\n`
|
||
msg += `机架位置:${rackPosition || '未知'}\n`
|
||
if (faultInfo.fault_time) msg += `故障时间:${faultInfo.fault_time}\n`
|
||
if (faultDetail) msg += `故障信息:${faultDetail}\n`
|
||
|
||
if (isOemDiag && oemDeadline) {
|
||
msg += `到期时间:${oemDeadline}\n`
|
||
} else if (!isOemDiag) {
|
||
msg += '\n📊 服务可用性各档位截止时间:\n\n'
|
||
const tierDesc: Record<string, string> = {
|
||
'第一档': '99%(含)以上(不扣费)',
|
||
'第二档': '99%-97%(含)(扣10%)',
|
||
'第三档': '97%-95%(含)(扣25%)',
|
||
'第四档': '95%-90%(含)(扣50%)',
|
||
'第五档': '90%(不含)以下(扣100%)',
|
||
}
|
||
const emojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣']
|
||
let i = 0
|
||
for (const [tier, desc] of Object.entries(tierDesc)) {
|
||
const d = deadlines[tier]
|
||
if (d) {
|
||
msg += `${emojis[i]} ${desc}\n`
|
||
msg += ` 截止时间:${d.deadline}`
|
||
if (d.hours > 0) msg += `(${d.hours}小时)`
|
||
msg += '\n\n'
|
||
}
|
||
i++
|
||
}
|
||
}
|
||
return msg.trim()
|
||
}
|
||
}
|