Files

96 lines
3.9 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Common.Notify;
using Model.Entity.Inspection;
using Service.Interface;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace Service.Implement
{
/// <summary>
/// 钉钉群机器人通知发送器(自定义机器人 Webhook,支持加签校验)
/// 报文:msgtype=markdownat.atMobiles 支持 @手机号;
/// 加签:URL 追加 timestamp(毫秒)+sign(算法见 ImWebhookSigner.DingTalkSign
/// </summary>
public class DingTalkNotifier : IAlertNotifier
{
private readonly HttpClient _httpClient;
private readonly ILogger<DingTalkNotifier> _logger;
public DingTalkNotifier(HttpClient httpClient, ILogger<DingTalkNotifier> logger)
{
_httpClient = httpClient;
_logger = logger;
}
public NotifyChannelTypeEnum ChannelType => NotifyChannelTypeEnum.DingTalk;
public async Task<NotifyResult> SendAsync(AlertNotifyMessage message, string webhookUrl, string? secret, CancellationToken ct = default)
{
try
{
string url = webhookUrl;
// 加签:URL 追加 timestamp 与 sign 参数
if (!string.IsNullOrWhiteSpace(secret))
{
long timestampMs = DateTimeOffset.Now.ToUnixTimeMilliseconds();
string sign = ImWebhookSigner.DingTalkSign(timestampMs, secret);
url += $"{(url.Contains('?') ? "&" : "?")}timestamp={timestampMs}&sign={sign}";
}
// markdown 正文中 @ 手机号需同时出现在 atMobiles 与正文里才会真正提醒
var text = new StringBuilder();
text.AppendLine($"### {message.Title}");
text.AppendLine(message.Content);
foreach (var mobile in message.AtMobiles)
{
text.Append($"@{mobile} ");
}
var body = new Dictionary<string, object>
{
["msgtype"] = "markdown",
["markdown"] = new Dictionary<string, object>
{
["title"] = message.Title,
["text"] = text.ToString()
},
["at"] = new Dictionary<string, object>
{
["atMobiles"] = message.AtMobiles,
["isAtAll"] = false
}
};
string json = JsonSerializer.Serialize(body);
_logger.LogInformation("钉钉推送 atMobiles=[{At}]", string.Join(",", message.AtMobiles));
using var content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await _httpClient.PostAsync(url, content, ct);
string respText = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
{
return NotifyResult.Fail($"HTTP {(int)response.StatusCode}: {Truncate(respText)}");
}
// 钉钉返回 {"errcode":0,"errmsg":"ok"}
using var doc = JsonDocument.Parse(respText);
bool ok = doc.RootElement.TryGetProperty("errcode", out var errcode) && errcode.GetInt32() == 0;
return ok ? NotifyResult.Ok() : NotifyResult.Fail(Truncate(respText));
}
catch (Exception ex)
{
return NotifyResult.Fail($"钉钉推送异常: {NotifyError.Describe(ex)}");
}
}
private static string Truncate(string s, int max = 200) =>
string.IsNullOrEmpty(s) || s.Length <= max ? s : s.Substring(0, max) + "...";
}
}