PushDomainService.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. using DotNetCore.CAP;
  2. using Hotline.Share.Dtos.Push;
  3. using Hotline.Share.Dtos.SendSms;
  4. using Hotline.Share.Enums.Push;
  5. using MapsterMapper;
  6. using MediatR;
  7. using Microsoft.AspNetCore.Http;
  8. using Microsoft.Extensions.Logging;
  9. using Microsoft.Extensions.Options;
  10. using System.Net;
  11. using System.Net.Http.Json;
  12. using System.Text.RegularExpressions;
  13. using System.Xml;
  14. using XF.Domain.Dependency;
  15. using XF.Domain.Exceptions;
  16. using XF.Domain.Repository;
  17. namespace Hotline.Push.FWMessage;
  18. /// <summary>
  19. ///
  20. /// </summary>
  21. public class PushDomainService : IPushDomainService, IScopeDependency
  22. {
  23. #region 注入
  24. private readonly IRepository<Message> _messageRepository;
  25. private readonly IMapper _mapper;
  26. private readonly IMediator _mediator;
  27. private readonly IRepository<MessageTemplate> _messageTemplateRepository;
  28. private readonly ICapPublisher _capPublisher;
  29. private readonly IHttpClientFactory _httpClientFactory;
  30. private readonly ILogger<PushDomainService> _logger;
  31. private readonly IOptionsSnapshot<SendSmsConfiguration> _sendSmsConfiguration;
  32. /// <summary>
  33. ///
  34. /// </summary>
  35. /// <param name="messageRepository"></param>
  36. /// <param name="mapper"></param>
  37. /// <param name="mediator"></param>
  38. /// <param name="messageTemplateRepository"></param>
  39. /// <param name="capPublisher"></param>
  40. /// <param name="httpClientFactory"></param>
  41. /// <param name="logger"></param>
  42. /// <param name="sendSmsConfiguration"></param>
  43. public PushDomainService(IRepository<Message> messageRepository,
  44. IMapper mapper,
  45. IMediator mediator,
  46. IRepository<MessageTemplate> messageTemplateRepository,
  47. ICapPublisher capPublisher,
  48. IHttpClientFactory httpClientFactory,
  49. ILogger<PushDomainService> logger,
  50. IOptionsSnapshot<SendSmsConfiguration> sendSmsConfiguration
  51. )
  52. {
  53. _messageRepository = messageRepository;
  54. _mapper = mapper;
  55. _mediator = mediator;
  56. _messageTemplateRepository = messageTemplateRepository;
  57. _capPublisher = capPublisher;
  58. _httpClientFactory = httpClientFactory;
  59. _logger = logger;
  60. _sendSmsConfiguration = sendSmsConfiguration;
  61. }
  62. #endregion
  63. /// <summary>
  64. /// 短信发送
  65. /// </summary>
  66. /// <param name="messageDto"></param>
  67. /// <param name="cancellation"></param>
  68. /// <returns></returns>
  69. public async Task PushMsgAsync(MessageDto messageDto, CancellationToken cancellation)
  70. {
  71. if (messageDto == null)
  72. throw UserFriendlyException.SameMessage("消息不能为空!");
  73. if (string.IsNullOrEmpty(messageDto.TemplateCode) && string.IsNullOrEmpty(messageDto.Content))
  74. throw UserFriendlyException.SameMessage("模板和内容不能同时为空!");
  75. #region 替换模板内容
  76. //如果模板为空,参数为空则直接用短信内容
  77. if (!string.IsNullOrEmpty(messageDto.TemplateCode))
  78. {
  79. //查询模板信息
  80. var messageTemplate = await _messageTemplateRepository.GetAsync(p => p.Code == messageDto.TemplateCode, cancellation);
  81. if (messageTemplate == null || string.IsNullOrEmpty(messageTemplate.Content))
  82. throw UserFriendlyException.SameMessage("未找到模板短信!");
  83. string Template = messageTemplate.Content;
  84. if (messageDto.Params != null && messageDto.Params.Count > 0)
  85. {
  86. //正则查询模板中需要替换的内容
  87. Regex regex = new(@"\{[a-zA-Z0-9]{1,}\}");
  88. var matches = regex.Matches(Template);
  89. if (matches != null && matches.Count != messageDto.Params.Count)
  90. //参数与需要替换的字符数不匹配
  91. throw UserFriendlyException.SameMessage("模板需要参数与实际传递参数个数不匹配!");
  92. //reason = "模板需要参数与实际传递参数个数不匹配!";
  93. //根据正则查询出来的匹配项替换内容
  94. for (int i = 0; i < matches.Count; i++)
  95. Template = Template.Replace(matches[i].ToString(), messageDto.Params[i]);
  96. }
  97. messageDto.Content = Template;
  98. }
  99. #endregion
  100. var message = _mapper.Map<Message>(messageDto);
  101. var id = await _messageRepository.AddAsync(message, cancellation);//写入本地数据库
  102. PushMessageDto pushMessage = new()
  103. {
  104. ClientId = "Hotline",
  105. ExternalId = id,
  106. Content = message.Content,
  107. Remark = message.Remark,
  108. Name = message.Name,
  109. TelNumber = message.TelNumber
  110. };
  111. //消息队列
  112. await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.SendSms, pushMessage, cancellationToken: cancellation);
  113. ////http请求
  114. //var result = await ExecuteAsync<PushMessageDto, SendSmsReponse>(_sendSmsConfiguration.Value.Url, pushMessage, cancellationToken: cancellation);
  115. //if (result != null && result.code == 0)
  116. //{
  117. // message.Status = EPushStatus.Success;
  118. // message.SendState = ESendState.Sending;
  119. // await _messageRepository.UpdateAsync(message, cancellation);
  120. //}
  121. }
  122. /// <summary>
  123. /// 修改短信推送状态
  124. /// </summary>
  125. /// <param name="dto"></param>
  126. /// <param name="cancellation"></param>
  127. /// <returns></returns>
  128. public async Task PushMsgUpdateStateAsync(PushReceiveMessageDto dto, CancellationToken cancellation)
  129. {
  130. var data = await _messageRepository.GetAsync(p => p.Id == dto.ExternalId, cancellation);
  131. if (data != null)
  132. {
  133. if (dto.Type == "0")
  134. {
  135. if (dto.SendState == ESendState.Sending)
  136. {
  137. data.SmsWaitSendingId = dto.SmsWaitSendingId;
  138. data.SendState = ESendState.Sending;
  139. data.Status = EPushStatus.Success;
  140. }
  141. else
  142. data.Status = EPushStatus.Failed;
  143. }
  144. else if (dto.Type == "1")
  145. {
  146. data.SendTime = Convert.ToDateTime(dto.SendTime);
  147. data.SendState = dto.SendState;
  148. data.MsgCount = dto.MsgCount;
  149. data.SmsSendingCompletedId = dto.SmsSendingCompletedId;
  150. }
  151. else if (dto.Type == "2")
  152. {
  153. data.IsSmsReply = dto.IsSmsReply;
  154. data.SmsReplyTime = Convert.ToDateTime(dto.SmsReplyTime);
  155. data.SmsReplyContent = dto.SmsReplyContent;
  156. }
  157. data.Reason = dto.Reason;
  158. await _messageRepository.UpdateAsync(data, cancellation);
  159. }
  160. }
  161. /// <summary>
  162. /// 修改短信推送状态
  163. /// </summary>
  164. /// <param name="dto"></param>
  165. /// <param name="cancellation"></param>
  166. /// <returns></returns>
  167. public async Task PushMsgUpdateStateNewAsync(PushReceiveMessageDto dto, CancellationToken cancellation)
  168. {
  169. var data = await _messageRepository.GetAsync(p => p.Id == dto.ExternalId, cancellation);
  170. if (data != null)
  171. {
  172. data.SmsWaitSendingId = dto.SmsWaitSendingId;
  173. data.Status = EPushStatus.Success;
  174. data.SendTime = Convert.ToDateTime(dto.SendTime);
  175. data.SendState = dto.SendState;
  176. data.MsgCount = dto.MsgCount;
  177. data.SmsSendingCompletedId = dto.SmsSendingCompletedId;
  178. data.IsSmsReply = dto.IsSmsReply;
  179. data.SmsReplyTime = Convert.ToDateTime(dto.SmsReplyTime);
  180. data.SmsReplyContent = dto.SmsReplyContent;
  181. data.Reason = dto.Reason;
  182. await _messageRepository.UpdateAsync(data, cancellation);
  183. }
  184. }
  185. /// <summary>
  186. ///
  187. /// </summary>
  188. /// <typeparam name="TRequest"></typeparam>
  189. /// <typeparam name="TResponse"></typeparam>
  190. /// <param name="path"></param>
  191. /// <param name="request"></param>
  192. /// <param name="clientInitialize"></param>
  193. /// <param name="cancellationToken"></param>
  194. /// <returns></returns>
  195. private async Task<TResponse?> ExecuteAsync<TRequest, TResponse>(string path, TRequest request,
  196. Action<HttpClient>? clientInitialize = null, CancellationToken cancellationToken = default)
  197. {
  198. var client = _httpClientFactory.CreateClient("sendwaitsms");
  199. clientInitialize?.Invoke(client);
  200. using var responseMessage = await client.PostAsJsonAsync(path, request, cancellationToken);
  201. responseMessage.EnsureSuccessStatusCode();
  202. var result = await responseMessage.Content.ReadFromJsonAsync<TResponse>(cancellationToken: cancellationToken);
  203. return result;
  204. }
  205. #region http请求
  206. /// <summary>
  207. /// 封装使用HttpClient调用WebService
  208. /// </summary>
  209. /// <param name="url">URL地址</param>
  210. /// <param name="content">参数</param>
  211. /// <returns></returns>
  212. private async Task<string> PostHelper(string url, HttpContent content)
  213. {
  214. _logger.LogInformation($"准备推送短信, {nameof(PostHelper)}");
  215. var result = string.Empty;
  216. try
  217. {
  218. using var client = _httpClientFactory.CreateClient();
  219. using (var response = await client.PostAsync(url, content))
  220. {
  221. if (response.StatusCode == HttpStatusCode.OK)
  222. {
  223. result = await response.Content.ReadAsStringAsync();
  224. XmlDocument doc = new XmlDocument();
  225. doc.LoadXml(result);
  226. result = doc.InnerText;
  227. }
  228. }
  229. }
  230. catch (Exception ex)
  231. {
  232. _logger.LogError(ex.InnerException?.Message);
  233. result = ex.Message;
  234. }
  235. _logger.LogInformation($"推送响应:{result}");
  236. return result;
  237. }
  238. #endregion
  239. }