|
@@ -0,0 +1,458 @@
|
|
|
|
+using System.Net;
|
|
|
|
+using System.Text.RegularExpressions;
|
|
|
|
+using System.Xml;
|
|
|
|
+using MapsterMapper;
|
|
|
|
+using MediatR;
|
|
|
|
+using Microsoft.Extensions.Configuration;
|
|
|
|
+using Push.YiBin.Share.Dtos;
|
|
|
|
+using Push.YiBin.Share.Dtos.FWMessage;
|
|
|
|
+using Push.YiBin.Share.Enums;
|
|
|
|
+using XF.Domain.Cache;
|
|
|
|
+using XF.Domain.Dependency;
|
|
|
|
+using XF.Domain.Exceptions;
|
|
|
|
+
|
|
|
|
+namespace Push.YiBin;
|
|
|
|
+
|
|
|
|
+/// <summary>
|
|
|
|
+///
|
|
|
|
+/// </summary>
|
|
|
|
+public class PushDomainService : IPushDomainService, IScopeDependency
|
|
|
|
+{
|
|
|
|
+ #region 注入
|
|
|
|
+
|
|
|
|
+ private readonly IRepository<Message> _messageRepository;
|
|
|
|
+ private readonly IMapper _mapper;
|
|
|
|
+ private readonly IHttpClientFactory _httpClientFactory;
|
|
|
|
+ private readonly IConfiguration _config;
|
|
|
|
+ private readonly SmsAccountInfo? accountInfo = null;
|
|
|
|
+ private readonly IMediator _mediator;
|
|
|
|
+ private readonly ITypedCache<CacheWaitSendId> _cacheWaitSendId;
|
|
|
|
+ private readonly IRepository<MessageTemplate> _messageTemplateRepository;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ ///
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="messageRepository"></param>
|
|
|
|
+ /// <param name="httpClientFactory"></param>
|
|
|
|
+ /// <param name="mapper"></param>
|
|
|
|
+ /// <param name="config"></param>
|
|
|
|
+ /// <param name="mediator"></param>
|
|
|
|
+ /// <param name="cacheWaitSendId"></param>
|
|
|
|
+ public PushDomainService(IRepository<Message> messageRepository, IHttpClientFactory httpClientFactory
|
|
|
|
+ , IMapper mapper, IConfiguration config, IMediator mediator,
|
|
|
|
+ ITypedCache<CacheWaitSendId> cacheWaitSendId,
|
|
|
|
+ IRepository<MessageTemplate> messageTemplateRepository)
|
|
|
|
+ {
|
|
|
|
+ _messageRepository = messageRepository;
|
|
|
|
+ _httpClientFactory = httpClientFactory;
|
|
|
|
+ _mapper = mapper;
|
|
|
|
+ _config = config;
|
|
|
|
+ accountInfo = _config.GetSection("SmsAccountInfo").Get<SmsAccountInfo>();
|
|
|
|
+ _mediator = mediator;
|
|
|
|
+ _cacheWaitSendId = cacheWaitSendId;
|
|
|
|
+ _messageTemplateRepository = messageTemplateRepository;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ #region 短信发送
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 短信发送
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="messageDto"></param>
|
|
|
|
+ /// <param name="cancellation"></param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ public async Task PushAsync(MessageDto messageDto, CancellationToken cancellation)
|
|
|
|
+ {
|
|
|
|
+ if (messageDto == null)
|
|
|
|
+ throw UserFriendlyException.SameMessage("消息不能为空!");
|
|
|
|
+
|
|
|
|
+ if (string.IsNullOrEmpty(messageDto.Template) && string.IsNullOrEmpty(messageDto.Content))
|
|
|
|
+ throw UserFriendlyException.SameMessage("模板和内容不能同时为空!");
|
|
|
|
+
|
|
|
|
+ Message message = new();
|
|
|
|
+ int WaitSendId = 0;
|
|
|
|
+ var sendData = await _messageRepository.GetAsync(p =>
|
|
|
|
+ p.ExternalId == messageDto.ExternalId &&
|
|
|
|
+ p.TelNumber == messageDto.TelNumber &&
|
|
|
|
+ p.Content == messageDto.Content);
|
|
|
|
+ if (sendData != null) //处理重复数据
|
|
|
|
+ {
|
|
|
|
+ WaitSendId = sendData.WaitSendId;
|
|
|
|
+ messageDto = _mapper.Map<MessageDto>(sendData);
|
|
|
|
+ message = sendData;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ #region 替换模板内容
|
|
|
|
+
|
|
|
|
+ string reason = null;
|
|
|
|
+ //如果模板为空,参数为空则直接用短信内容
|
|
|
|
+ if (!string.IsNullOrEmpty(messageDto.Template) && messageDto.Params.Count > 0)
|
|
|
|
+ {
|
|
|
|
+ //查询模板信息
|
|
|
|
+ var messageTemplate =
|
|
|
|
+ await _messageTemplateRepository.GetAsync(p => p.Code == messageDto.Template, cancellation);
|
|
|
|
+ if (messageTemplate == null || string.IsNullOrEmpty(messageTemplate.Content))
|
|
|
|
+ throw UserFriendlyException.SameMessage("未找到模板短信!");
|
|
|
|
+
|
|
|
|
+ string Template = messageTemplate.Content;
|
|
|
|
+ //正则查询模板中需要替换的内容
|
|
|
|
+ Regex regex = new(@"\{[a-zA-Z0-9]{1,}\}");
|
|
|
|
+ var matches = regex.Matches(Template);
|
|
|
|
+ if (matches != null && matches.Count != messageDto.Params.Count)
|
|
|
|
+ //参数与需要替换的字符数不匹配
|
|
|
|
+ throw UserFriendlyException.SameMessage("模板需要参数与实际传递参数个数不匹配!");
|
|
|
|
+ //reason = "模板需要参数与实际传递参数个数不匹配!";
|
|
|
|
+
|
|
|
|
+ //根据正则查询出来的匹配项替换内容
|
|
|
|
+ for (int i = 0; i < matches.Count; i++)
|
|
|
|
+ Template = Template.Replace(matches[i].ToString(), messageDto.Params[i]);
|
|
|
|
+
|
|
|
|
+ messageDto.Content = Template;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ message = _mapper.Map<Message>(messageDto);
|
|
|
|
+ WaitSendId = GenerateWaitSendId();
|
|
|
|
+ message.WaitSendId = WaitSendId;
|
|
|
|
+ message.Reason = reason;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //调用短信发送业务
|
|
|
|
+ var strResult = await SmsSend(messageDto, WaitSendId);
|
|
|
|
+
|
|
|
|
+ //处理返回值
|
|
|
|
+ if (true == strResult.Contains("ok,")) // ok,35797257
|
|
|
|
+ {
|
|
|
|
+ string[] strArray = strResult.Split(',');
|
|
|
|
+ if (2 == strArray.Length)
|
|
|
|
+ {
|
|
|
|
+ // 短信服务中心返回 IFSFWID,用于短信发送状态接收时匹配
|
|
|
|
+ message.SmsWaitSendingId = strArray[1];
|
|
|
|
+ message.SendState = ESendState.Sending;
|
|
|
|
+ message.Status = EPushStatus.Success;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ message.Reason = strResult;
|
|
|
|
+ message.Status = EPushStatus.Failed;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (sendData != null)
|
|
|
|
+ await _messageRepository.UpdateAsync(message); //跟新本地数据库
|
|
|
|
+ else
|
|
|
|
+ await _messageRepository.AddAsync(message); //写入本地数据库
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region 查询账号短信剩余量
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 查询账号短信剩余量
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ public async Task<string> GetAccountNum()
|
|
|
|
+ {
|
|
|
|
+ // 账户,密码
|
|
|
|
+ string strAcceptContent = $"{accountInfo.AccountUser},{accountInfo.AccountPwd}";
|
|
|
|
+ // 参数
|
|
|
|
+ Dictionary<string, string> dicParam = new()
|
|
|
|
+ {
|
|
|
|
+ { "AcceptType", "fwAccountNum" },
|
|
|
|
+ { "AcceptContent", strAcceptContent }
|
|
|
|
+ };
|
|
|
|
+ // 将参数转化为HttpContent
|
|
|
|
+ HttpContent content = new FormUrlEncodedContent(dicParam);
|
|
|
|
+ // ok,9
|
|
|
|
+ // error,查询异常
|
|
|
|
+ string strResult = await PostHelper(accountInfo.MessageServerUrl, content);
|
|
|
|
+ var strArray = strResult.Split(",");
|
|
|
|
+ if (2 == strArray.Length)
|
|
|
|
+ {
|
|
|
|
+ strResult = strArray[1];
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ strResult = "查询异常";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return strResult;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region 短信发送状态回调接口
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 短信发送状态回调接口
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="receiveMessageDto"></param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ public async Task<string> ReceiveObtain(ReceiveMessageDto receiveMessageDto)
|
|
|
|
+ {
|
|
|
|
+ string strResult = "error,缺少参数";
|
|
|
|
+ if (receiveMessageDto.account != accountInfo.ReturnAccountUser ||
|
|
|
|
+ receiveMessageDto.pswd != accountInfo.ReturnAccountPwd)
|
|
|
|
+ {
|
|
|
|
+ strResult = "error,参数错误";
|
|
|
|
+ return strResult;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (true == string.IsNullOrEmpty(receiveMessageDto.account) || true == string.IsNullOrEmpty(receiveMessageDto
|
|
|
|
+ .pswd)
|
|
|
|
+ || true == string.IsNullOrEmpty(receiveMessageDto
|
|
|
|
+ .msgid) || true ==
|
|
|
|
+ string.IsNullOrEmpty(receiveMessageDto.sendtime)
|
|
|
|
+ || receiveMessageDto.msgcount <= 0 ||
|
|
|
|
+ receiveMessageDto.state < 0)
|
|
|
|
+ {
|
|
|
|
+ strResult = "error,参数错误";
|
|
|
|
+ return strResult;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //修改数据
|
|
|
|
+ var data = await _messageRepository.GetAsync(p => p.SmsWaitSendingId == receiveMessageDto.msgid);
|
|
|
|
+ if (data != null)
|
|
|
|
+ {
|
|
|
|
+ data.SendTime = Convert.ToDateTime(receiveMessageDto.sendtime);
|
|
|
|
+ data.MsgCount = receiveMessageDto.msgcount;
|
|
|
|
+ data.SendState = (ESendState)receiveMessageDto.state;
|
|
|
|
+ data.SmsSendingCompletedId = receiveMessageDto.sfid + "";
|
|
|
|
+ data.Reason = receiveMessageDto.errormsg;
|
|
|
|
+ await _messageRepository.UpdateAsync(data);
|
|
|
|
+ // 成功返回值必须是ok
|
|
|
|
+ strResult = "ok";
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ strResult = "error,调用失败";
|
|
|
|
+
|
|
|
|
+ return strResult;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region 短信接收
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 短信接收
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="receiveMessageDto"></param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ public async Task<string> ReceiveSms(ReceiveMessageDto receiveMessageDto)
|
|
|
|
+ {
|
|
|
|
+ string strResult = "error,缺少参数";
|
|
|
|
+ if (receiveMessageDto.account != accountInfo.ReturnAccountUser ||
|
|
|
|
+ receiveMessageDto.pswd != accountInfo.ReturnAccountPwd)
|
|
|
|
+ {
|
|
|
|
+ strResult = "error,参数错误";
|
|
|
|
+ return strResult;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (true == string.IsNullOrEmpty(receiveMessageDto.account) || true == string.IsNullOrEmpty(receiveMessageDto
|
|
|
|
+ .pswd)
|
|
|
|
+ || true == string.IsNullOrEmpty(receiveMessageDto
|
|
|
|
+ .msg) || true ==
|
|
|
|
+ string.IsNullOrEmpty(receiveMessageDto.mobile)
|
|
|
|
+ || true == string.IsNullOrEmpty(receiveMessageDto
|
|
|
|
+ .motime))
|
|
|
|
+ {
|
|
|
|
+ strResult = "error,参数错误";
|
|
|
|
+ return strResult;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var data = await _messageRepository.GetAsync(p =>
|
|
|
|
+ p.TelNumber == receiveMessageDto.mobile && p.IsSmsReply == false && p.SmsReplyTime == null);
|
|
|
|
+ if (data != null)
|
|
|
|
+ {
|
|
|
|
+ data.IsSmsReply = true;
|
|
|
|
+ data.SmsReplyTime = Convert.ToDateTime(receiveMessageDto.motime);
|
|
|
|
+ data.SmsReplyContent = receiveMessageDto.msg;
|
|
|
|
+ await _messageRepository.UpdateAsync(data);
|
|
|
|
+
|
|
|
|
+ //消息处理 todo
|
|
|
|
+ //await _mediator.Publish(new PushMessageNotify(_mapper.Map<PushMessageNotifyDto>(data)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 成功返回值必须是ok
|
|
|
|
+ strResult = "ok";
|
|
|
|
+
|
|
|
|
+ return strResult;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region 私有方法
|
|
|
|
+
|
|
|
|
+ #region 短信发送
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 短信发送
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ private async Task<string> SmsSend(MessageDto messageDto, int WaitSendId)
|
|
|
|
+ {
|
|
|
|
+ string strResult;
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ SendSmsModelDto tSms = new()
|
|
|
|
+ {
|
|
|
|
+ // 业务系统短信ID
|
|
|
|
+ nWaitID = WaitSendId,
|
|
|
|
+ // 短信类型1:默认2:营销
|
|
|
|
+ strType = "1",
|
|
|
|
+ // 业务系统工单ID
|
|
|
|
+ nFlowID = 0,
|
|
|
|
+ // 姓名
|
|
|
|
+ strPhoneName = messageDto.Name,
|
|
|
|
+ // 手机号码
|
|
|
|
+ strPhoneNumber = messageDto.TelNumber,
|
|
|
|
+ // 短信内容
|
|
|
|
+ strPhoneConnent = messageDto.Content,
|
|
|
|
+ // 添加时间
|
|
|
|
+ dAddTime = DateTime.Now,
|
|
|
|
+ strPhoneNumberAll = "",
|
|
|
|
+ strRemark = "",
|
|
|
|
+ // 业务系统添加人ID
|
|
|
|
+ nUserID = 0,
|
|
|
|
+ // 业务系统添加人姓名
|
|
|
|
+ strUserName = "",
|
|
|
|
+ // 业务系统添加部门ID
|
|
|
|
+ nBMID = 0,
|
|
|
|
+ // 业务系统添加部门名称
|
|
|
|
+ strBMName = "",
|
|
|
|
+ // 短信系统账号
|
|
|
|
+ strAccountUser = accountInfo.AccountUser,
|
|
|
|
+ // 短信系统密码
|
|
|
|
+ strAccountPwd = accountInfo.AccountPwd
|
|
|
|
+ };
|
|
|
|
+ // 序列化
|
|
|
|
+ string strSendJson =
|
|
|
|
+ Newtonsoft.Json.JsonConvert.SerializeObject(tSms); //System.Text.Json.JsonSerializer.Serialize(tSms);//
|
|
|
|
+
|
|
|
|
+ // 参数加密
|
|
|
|
+ string strKey = await FwEncrypt(strSendJson);
|
|
|
|
+ // 短信发送参数
|
|
|
|
+ Dictionary<string, string> dicParam = new Dictionary<string, string>
|
|
|
|
+ {
|
|
|
|
+ { "AcceptType", "fwSms" },
|
|
|
|
+ { "AcceptContent", strKey }
|
|
|
|
+ };
|
|
|
|
+ // 将参数转化为HttpContent
|
|
|
|
+ HttpContent content = new FormUrlEncodedContent(dicParam);
|
|
|
|
+ strResult = await PostHelper(accountInfo.MessageServerUrl, content);
|
|
|
|
+ }
|
|
|
|
+ catch (Exception ex)
|
|
|
|
+ {
|
|
|
|
+ strResult = ex.Message;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return strResult;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region 参数加密
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 参数加密
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="planText"></param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ private async Task<string> FwEncrypt(string planText)
|
|
|
|
+ {
|
|
|
|
+ // 参数
|
|
|
|
+ Dictionary<string, string> dicParam = new Dictionary<string, string>();
|
|
|
|
+ dicParam.Add("AcceptType", "fwEncrypt");
|
|
|
|
+ dicParam.Add("AcceptContent", planText);
|
|
|
|
+ // 将参数转化为HttpContent
|
|
|
|
+ HttpContent content = new FormUrlEncodedContent(dicParam);
|
|
|
|
+ string strResult = await PostHelper(accountInfo.MessageServerUrl, content);
|
|
|
|
+ string strValue = "";
|
|
|
|
+ // ok,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
|
|
+ if (true == strResult.Contains("ok,"))
|
|
|
|
+ {
|
|
|
|
+ string[] strArray = strResult.Split(',');
|
|
|
|
+ if (2 == strArray.Length)
|
|
|
|
+ {
|
|
|
|
+ strValue = strArray[1];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return strValue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region http请求
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 封装使用HttpClient调用WebService
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="url">URL地址</param>
|
|
|
|
+ /// <param name="content">参数</param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ private async Task<string> PostHelper(string url, HttpContent content)
|
|
|
|
+ {
|
|
|
|
+ var result = string.Empty;
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ using (var client = _httpClientFactory.CreateClient())
|
|
|
|
+ using (var response = await client.PostAsync(url, content))
|
|
|
|
+ {
|
|
|
|
+ if (response.StatusCode == HttpStatusCode.OK)
|
|
|
|
+ {
|
|
|
|
+ result = await response.Content.ReadAsStringAsync();
|
|
|
|
+ XmlDocument doc = new XmlDocument();
|
|
|
|
+ doc.LoadXml(result);
|
|
|
|
+ result = doc.InnerText;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ catch (Exception ex)
|
|
|
|
+ {
|
|
|
|
+ result = ex.Message;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region 生成待发短信ID缓存
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 生成待发短信ID缓存
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ private int GenerateWaitSendId()
|
|
|
|
+ {
|
|
|
|
+ var cacheKey = "PushMessageWaitSendId";
|
|
|
|
+ var cacheWaitSend = _cacheWaitSendId.GetOrSet(cacheKey, f =>
|
|
|
|
+ {
|
|
|
|
+ var waitSendId = _messageRepository.Queryable()
|
|
|
|
+ .MaxAsync(p => p.WaitSendId)
|
|
|
|
+ .GetAwaiter()
|
|
|
|
+ .GetResult();
|
|
|
|
+ return new CacheWaitSendId { WaitSendId = waitSendId };
|
|
|
|
+ }, TimeSpan.FromDays(1));
|
|
|
|
+ cacheWaitSend!.WaitSendId += 1;
|
|
|
|
+
|
|
|
|
+ _cacheWaitSendId.Set(cacheKey, cacheWaitSend);
|
|
|
|
+ return cacheWaitSend.WaitSendId;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public class CacheWaitSendId
|
|
|
|
+ {
|
|
|
|
+ public int WaitSendId { get; set; }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+}
|