FlowNotifyService.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. using Admin.NET.Core.Service;
  2. using Microsoft.AspNetCore.SignalR;
  3. namespace Admin.NET.Plugin.ApprovalFlow.Service;
  4. /// <summary>
  5. /// 流程通知服务 — 按 JSON 配置分渠道调度
  6. /// </summary>
  7. public class FlowNotifyService : ITransient
  8. {
  9. private readonly IHubContext<OnlineUserHub, IOnlineUserHub> _hubContext;
  10. private readonly SysCacheService _cacheService;
  11. public FlowNotifyService(
  12. IHubContext<OnlineUserHub, IOnlineUserHub> hubContext,
  13. SysCacheService cacheService)
  14. {
  15. _hubContext = hubContext;
  16. _cacheService = cacheService;
  17. }
  18. public async Task NotifyUsers(List<long> userIds, FlowNotification notification)
  19. {
  20. if (userIds.Count == 0) return;
  21. var cfg = App.GetConfig<NotifyChannelConfig>("ApprovalFlow:Notify");
  22. if (cfg?.SignalR != false)
  23. await SendSignalR(userIds, notification);
  24. if (cfg?.DingTalk == true)
  25. await SendDingTalk(userIds, notification);
  26. if (cfg?.WorkWeixin == true)
  27. await SendWorkWeixin(userIds, notification);
  28. if (cfg?.Email == true)
  29. await SendEmail(userIds, notification);
  30. if (cfg?.Sms == true)
  31. await SendSms(userIds, notification);
  32. }
  33. public async Task NotifyUrge(List<long> userIds, long instanceId, string title)
  34. {
  35. await NotifyUsers(userIds, new FlowNotification
  36. {
  37. Type = FlowNotificationTypeEnum.Urge,
  38. InstanceId = instanceId,
  39. Title = $"【催办】{title}",
  40. Content = "流程发起人催促您尽快审批,请及时处理。",
  41. });
  42. }
  43. public async Task NotifyNewTask(List<long> userIds, long instanceId, string title, string? nodeName)
  44. {
  45. await NotifyUsers(userIds, new FlowNotification
  46. {
  47. Type = FlowNotificationTypeEnum.NewTask,
  48. InstanceId = instanceId,
  49. Title = $"【待审批】{title}",
  50. Content = $"您有一条新的审批任务({nodeName}),请及时处理。",
  51. });
  52. }
  53. public async Task NotifyFlowCompleted(long initiatorId, long instanceId, string title, FlowInstanceStatusEnum finalStatus)
  54. {
  55. var statusText = finalStatus == FlowInstanceStatusEnum.Approved ? "已通过" : "已拒绝";
  56. await NotifyUsers(new List<long> { initiatorId }, new FlowNotification
  57. {
  58. Type = FlowNotificationTypeEnum.FlowCompleted,
  59. InstanceId = instanceId,
  60. Title = $"【审批{statusText}】{title}",
  61. Content = $"您发起的审批流程已{statusText}。",
  62. });
  63. }
  64. /// <summary>
  65. /// 转办通知 — 通知被转办人
  66. /// </summary>
  67. public async Task NotifyTransferred(long targetUserId, long instanceId, string title, string? fromName)
  68. {
  69. await NotifyUsers(new List<long> { targetUserId }, new FlowNotification
  70. {
  71. Type = FlowNotificationTypeEnum.Transferred,
  72. InstanceId = instanceId,
  73. Title = $"【转办】{title}",
  74. Content = $"{fromName} 将一条审批任务转交给您,请及时处理。",
  75. });
  76. }
  77. /// <summary>
  78. /// 退回通知 — 通知被退回节点的审批人
  79. /// </summary>
  80. public async Task NotifyReturned(List<long> userIds, long instanceId, string title, string? returnedBy)
  81. {
  82. await NotifyUsers(userIds, new FlowNotification
  83. {
  84. Type = FlowNotificationTypeEnum.Returned,
  85. InstanceId = instanceId,
  86. Title = $"【退回】{title}",
  87. Content = $"{returnedBy} 已退回该审批,请重新审核。",
  88. });
  89. }
  90. /// <summary>
  91. /// 加签通知 — 通知被加签人
  92. /// </summary>
  93. public async Task NotifyAddSign(long targetUserId, long instanceId, string title, string? fromName)
  94. {
  95. await NotifyUsers(new List<long> { targetUserId }, new FlowNotification
  96. {
  97. Type = FlowNotificationTypeEnum.AddSign,
  98. InstanceId = instanceId,
  99. Title = $"【加签】{title}",
  100. Content = $"{fromName} 邀请您参与审批,请及时处理。",
  101. });
  102. }
  103. /// <summary>
  104. /// 升级通知 — 通知升级目标人
  105. /// </summary>
  106. public async Task NotifyEscalated(List<long> targetUserIds, long instanceId, string title, string? fromName, string? nodeName)
  107. {
  108. await NotifyUsers(targetUserIds, new FlowNotification
  109. {
  110. Type = FlowNotificationTypeEnum.Escalated,
  111. InstanceId = instanceId,
  112. Title = $"【升级】{title}",
  113. Content = $"{fromName} 将审批任务({nodeName})升级给您,请及时处理。",
  114. });
  115. }
  116. /// <summary>
  117. /// 超时提醒通知
  118. /// </summary>
  119. public async Task NotifyTimeout(List<long> userIds, long instanceId, string title)
  120. {
  121. await NotifyUsers(userIds, new FlowNotification
  122. {
  123. Type = FlowNotificationTypeEnum.Timeout,
  124. InstanceId = instanceId,
  125. Title = $"【超时提醒】{title}",
  126. Content = "您有一条审批任务已超时,请尽快处理。",
  127. });
  128. }
  129. /// <summary>
  130. /// 撤回通知 — 通知被取消的审批人
  131. /// </summary>
  132. public async Task NotifyWithdrawn(List<long> userIds, long instanceId, string title, string? initiatorName)
  133. {
  134. await NotifyUsers(userIds, new FlowNotification
  135. {
  136. Type = FlowNotificationTypeEnum.Withdrawn,
  137. InstanceId = instanceId,
  138. Title = $"【已撤回】{title}",
  139. Content = $"{initiatorName} 已撤回该审批流程。",
  140. });
  141. }
  142. // ═══════════════════════════════════════════
  143. // 各渠道发送实现
  144. // ═══════════════════════════════════════════
  145. private async Task SendSignalR(List<long> userIds, FlowNotification notification)
  146. {
  147. var onlineUsers = _cacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline);
  148. var connectionIds = onlineUsers
  149. .Where(u => userIds.Contains(u.Value.UserId))
  150. .Select(u => u.Value.ConnectionId)
  151. .ToList();
  152. if (connectionIds.Count == 0) return;
  153. await _hubContext.Clients.Clients(connectionIds).ReceiveMessage(new
  154. {
  155. title = notification.Title,
  156. message = notification.Content,
  157. type = notification.Type.ToString(),
  158. instanceId = notification.InstanceId,
  159. });
  160. }
  161. private Task SendDingTalk(List<long> userIds, FlowNotification notification)
  162. {
  163. // TODO: 接入 DingTalk 开放平台 API
  164. return Task.CompletedTask;
  165. }
  166. private Task SendWorkWeixin(List<long> userIds, FlowNotification notification)
  167. {
  168. // TODO: 接入企业微信消息推送 API
  169. return Task.CompletedTask;
  170. }
  171. private Task SendEmail(List<long> userIds, FlowNotification notification)
  172. {
  173. // TODO: 接入 SMTP / 邮件服务
  174. return Task.CompletedTask;
  175. }
  176. private Task SendSms(List<long> userIds, FlowNotification notification)
  177. {
  178. // TODO: 接入短信服务
  179. return Task.CompletedTask;
  180. }
  181. }
  182. public class FlowNotification
  183. {
  184. public FlowNotificationTypeEnum Type { get; set; }
  185. public long InstanceId { get; set; }
  186. public string Title { get; set; } = "";
  187. public string Content { get; set; } = "";
  188. }
  189. /// <summary>
  190. /// 通知类型
  191. /// </summary>
  192. public enum FlowNotificationTypeEnum
  193. {
  194. NewTask,
  195. Urge,
  196. FlowCompleted,
  197. Transferred,
  198. Returned,
  199. AddSign,
  200. Withdrawn,
  201. Escalated,
  202. Timeout,
  203. }
  204. /// <summary>
  205. /// 通知渠道配置(来自 ApprovalFlow:Notify JSON)
  206. /// </summary>
  207. public class NotifyChannelConfig
  208. {
  209. public bool SignalR { get; set; } = true;
  210. public bool DingTalk { get; set; }
  211. public bool WorkWeixin { get; set; }
  212. public bool Email { get; set; }
  213. public bool Sms { get; set; }
  214. }