feat(ops): WeCom webhook alerts for pay/redeem anomalies

Add outbound group-bot alerts, rate/amount rules, stuck-order cron, HQ test button, and health db/redis checks.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-02 16:01:30 +08:00
parent cb6ecaaba6
commit 3328c52cb4
27 changed files with 1036 additions and 103 deletions
@@ -0,0 +1,48 @@
import { Injectable, Logger } from '@nestjs/common';
/**
* 企业微信群机器人 Webhook 出站(单向告警,与智能机器人长连接无关)。
* @see https://developer.work.weixin.qq.com/document/path/91770
*/
@Injectable()
export class WecomWebhookAlertService {
private readonly logger = new Logger(WecomWebhookAlertService.name);
isEnabled(): boolean {
return (
process.env.WECOM_ALERT_ENABLED === 'true' &&
!!(process.env.WECOM_ALERT_WEBHOOK_URL || '').trim()
);
}
async sendMarkdown(content: string): Promise<boolean> {
if (!this.isEnabled()) return false;
const url = (process.env.WECOM_ALERT_WEBHOOK_URL || '').trim();
try {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
msgtype: 'markdown',
markdown: { content: content.slice(0, 4000) },
}),
});
const data = (await res.json().catch(() => ({}))) as {
errcode?: number;
errmsg?: string;
};
if (!res.ok || (data.errcode != null && data.errcode !== 0)) {
this.logger.warn(
`wecom webhook failed: HTTP ${res.status} errcode=${data.errcode} ${data.errmsg ?? ''}`,
);
return false;
}
return true;
} catch (e) {
this.logger.warn(
`wecom webhook network error: ${e instanceof Error ? e.message : String(e)}`,
);
return false;
}
}
}