using System.Net.Http; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; namespace Admin.NET.Plugin.ApprovalFlow.Service; /// /// 钉钉推送(P4-16 - 首版 Webhook 机制) /// 通过配置的群机器人 Webhook URL(含 access_token)推送文本消息。 /// 不做 DingTalk 应用消息(需要 DingId 与 AgentId,留待后续 P5 补齐 SysUser.DingId 字段)。 /// 若配置了 Secret 则按钉钉官方规范加签发送。 /// public class DingTalkNotifyPusher : INotifyPusher, ITransient { public string Channel => "DingTalk"; private static readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(5) }; public bool IsEnabled(NotifyChannelConfig cfg) => cfg?.DingTalk == true && !string.IsNullOrWhiteSpace(cfg.DingTalkWebhookUrl); public async Task PushAsync(List userIds, FlowNotification notification) { var cfg = App.GetConfig("ApprovalFlow:Notify"); var url = cfg?.DingTalkWebhookUrl; if (string.IsNullOrWhiteSpace(url)) return FlowNotifyPushResult.Ok(0); if (!string.IsNullOrWhiteSpace(cfg.DingTalkSecret)) { var ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); var stringToSign = $"{ts}\n{cfg.DingTalkSecret}"; using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(cfg.DingTalkSecret)); var sign = Uri.EscapeDataString( Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)))); url += $"×tamp={ts}&sign={sign}"; } var content = $"【{notification.Title}】\n{notification.Content}\n实例: {notification.InstanceId}"; var payload = new { msgtype = "text", text = new { content } }; try { var resp = await _httpClient.PostAsJsonAsync(url, payload); var body = await resp.Content.ReadAsStringAsync(); if (!resp.IsSuccessStatusCode) return FlowNotifyPushResult.Fail($"HTTP {(int)resp.StatusCode}: {body}"); if (body.Contains("\"errcode\":0") || body.Contains("errcode\": 0")) return FlowNotifyPushResult.Ok(userIds.Count); return FlowNotifyPushResult.Fail($"DingTalk resp: {body}"); } catch (Exception ex) { return FlowNotifyPushResult.Fail(ex.Message); } } }