DingTalkNotifyPusher.cs 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. using System.Net.Http;
  2. using System.Net.Http.Json;
  3. using System.Security.Cryptography;
  4. using System.Text;
  5. namespace Admin.NET.Plugin.ApprovalFlow.Service;
  6. /// <summary>
  7. /// 钉钉推送(P4-16 - 首版 Webhook 机制)
  8. /// 通过配置的群机器人 Webhook URL(含 access_token)推送文本消息。
  9. /// 不做 DingTalk 应用消息(需要 DingId 与 AgentId,留待后续 P5 补齐 SysUser.DingId 字段)。
  10. /// 若配置了 Secret 则按钉钉官方规范加签发送。
  11. /// </summary>
  12. public class DingTalkNotifyPusher : INotifyPusher, ITransient
  13. {
  14. public string Channel => "DingTalk";
  15. private static readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(5) };
  16. public bool IsEnabled(NotifyChannelConfig cfg) =>
  17. cfg?.DingTalk == true && !string.IsNullOrWhiteSpace(cfg.DingTalkWebhookUrl);
  18. public async Task<FlowNotifyPushResult> PushAsync(List<long> userIds, FlowNotification notification)
  19. {
  20. var cfg = App.GetConfig<NotifyChannelConfig>("ApprovalFlow:Notify");
  21. var url = cfg?.DingTalkWebhookUrl;
  22. if (string.IsNullOrWhiteSpace(url)) return FlowNotifyPushResult.Ok(0);
  23. if (!string.IsNullOrWhiteSpace(cfg.DingTalkSecret))
  24. {
  25. var ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  26. var stringToSign = $"{ts}\n{cfg.DingTalkSecret}";
  27. using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(cfg.DingTalkSecret));
  28. var sign = Uri.EscapeDataString(
  29. Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign))));
  30. url += $"&timestamp={ts}&sign={sign}";
  31. }
  32. var content = $"【{notification.Title}】\n{notification.Content}\n实例: {notification.InstanceId}";
  33. var payload = new { msgtype = "text", text = new { content } };
  34. try
  35. {
  36. var resp = await _httpClient.PostAsJsonAsync(url, payload);
  37. var body = await resp.Content.ReadAsStringAsync();
  38. if (!resp.IsSuccessStatusCode)
  39. return FlowNotifyPushResult.Fail($"HTTP {(int)resp.StatusCode}: {body}");
  40. if (body.Contains("\"errcode\":0") || body.Contains("errcode\": 0"))
  41. return FlowNotifyPushResult.Ok(userIds.Count);
  42. return FlowNotifyPushResult.Fail($"DingTalk resp: {body}");
  43. }
  44. catch (Exception ex)
  45. {
  46. return FlowNotifyPushResult.Fail(ex.Message);
  47. }
  48. }
  49. }