398 lines
18 KiB
C#
398 lines
18 KiB
C#
using Microsoft.Extensions.Logging;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Text;
|
||
using WMS.Web.Domain.IService;
|
||
using Aliyun.Acs.Core;
|
||
using Aliyun.Acs.Core.Profile;
|
||
using Aliyun.Acs.Dysmsapi.Model.V20170525;
|
||
using Aliyun.Acs.Core.Exceptions;
|
||
using WMS.Web.Core.Internal.Results;
|
||
using System.Threading.Tasks;
|
||
using WMS.Web.Domain.Options;
|
||
using Microsoft.Extensions.Options;
|
||
using MimeKit;
|
||
using MailKit.Net.Smtp;
|
||
using MailKit.Security;
|
||
using Newtonsoft.Json;
|
||
using WMS.Web.Domain.Infrastructure;
|
||
using WMS.Web.Core.Dto;
|
||
using System.Linq;
|
||
using WMS.Web.Domain.IService.Public;
|
||
using WMS.Web.Core.Dto.Erp;
|
||
using WMS.Web.Domain.Entitys;
|
||
|
||
namespace WMS.Web.Domain.Services
|
||
{
|
||
/// <summary>
|
||
/// 阿里云短息服务
|
||
/// </summary>
|
||
public class SendMessageService : ISendMessageService
|
||
{
|
||
private readonly ILogger<SendMessageService> _logger;
|
||
private readonly IClientProfile profile;
|
||
private readonly DefaultAcsClient client;
|
||
private readonly EmailOptions _emailOptions;
|
||
private readonly SmsOptions _smsOptions;
|
||
private readonly IInStockRepositories _inStockRepositories;
|
||
private readonly IInStockTaskRepositories _inStockTaskRepositories;
|
||
private readonly IErpBasicDataExtendService _erpBasicDataExtendService;
|
||
private readonly ISubscribeNotificationRepositories _subscribeNotificationRepositories;
|
||
private readonly IErpService _erpService;
|
||
|
||
public SendMessageService(ILogger<SendMessageService> logger,
|
||
IOptions<EmailOptions> emailOptions,
|
||
IOptions<SmsOptions> smsOptions,
|
||
IInStockRepositories inStockRepositories,
|
||
IErpService erpService,
|
||
IErpBasicDataExtendService erpBasicDataExtendService,
|
||
IInStockTaskRepositories inStockTaskRepositories,
|
||
ISubscribeNotificationRepositories subscribeNotificationRepositories)
|
||
{
|
||
_logger = logger;
|
||
_erpService = erpService;
|
||
_inStockRepositories = inStockRepositories;
|
||
_erpBasicDataExtendService = erpBasicDataExtendService;
|
||
_inStockTaskRepositories = inStockTaskRepositories;
|
||
_subscribeNotificationRepositories = subscribeNotificationRepositories;
|
||
_emailOptions = emailOptions?.Value;
|
||
_smsOptions = smsOptions?.Value;
|
||
profile = DefaultProfile.GetProfile("cn-hangzhou", _smsOptions.AccessKeyId, _smsOptions.AccessKeySecret);
|
||
client = new DefaultAcsClient(profile);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 短信发送
|
||
/// </summary>
|
||
/// <param name="phoneNumbers"></param>
|
||
/// <param name="content"></param>
|
||
/// <returns></returns>
|
||
public Result SendSms(string phoneNumbers, string content)
|
||
{
|
||
// 构造短信请求
|
||
SendSmsRequest request = new SendSmsRequest();
|
||
request.PhoneNumbers = phoneNumbers; // 目标手机号
|
||
request.SignName = _smsOptions.SignName; // 短信签名
|
||
//request.SignName = "深圳市元创时代"; // 短信签名
|
||
request.TemplateCode = _smsOptions.TemplateCode; // 短信模板编号
|
||
request.TemplateParam = "{\"material\":\"" + content + "\"}"; // 模板参数,根据实际情况填写
|
||
|
||
try
|
||
{
|
||
// 发送短信
|
||
SendSmsResponse response = client.GetAcsResponse(request);
|
||
//isv.PARAM_LENGTH_LIMIT
|
||
if (response.Code == "OK")
|
||
{
|
||
_logger.LogInformation($"短信发送消息:成功->短信签名:{_smsOptions.SignName}->手机号码:" + phoneNumbers + " 内容:" + content);
|
||
return Result.ReSuccess();
|
||
}
|
||
else
|
||
{
|
||
_logger.LogInformation($"短信发送消息:失败->原因:{response.Message}->短信签名:{_smsOptions.SignName}->手机号码:" + phoneNumbers + " 内容:" + content);
|
||
return Result.ReFailure(response.Code, 6000);
|
||
}
|
||
|
||
|
||
}
|
||
catch (ServerException e)
|
||
{
|
||
_logger.LogInformation($"短信发送消息:Server失败:{e.ErrorMessage}->手机号码:" + phoneNumbers + " 内容:" + content);
|
||
return Result.ReFailure(e.ErrorMessage, 6000);
|
||
}
|
||
catch (ClientException e)
|
||
{
|
||
_logger.LogInformation($"短信发送消息:Client失败:{e.ErrorMessage}->手机号码:" + phoneNumbers + " 内容:" + content);
|
||
return Result.ReFailure(e.ErrorMessage, 6000);
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 邮箱发送消息
|
||
/// </summary>
|
||
/// <param name="toMailList"></param>
|
||
/// <param name="textBody"></param>
|
||
/// <returns></returns>
|
||
public async Task<Result> SendEmail(string mailAddress, string textBody, string coustomerName)
|
||
{
|
||
// 创建 MimeMessage 实例
|
||
MimeMessage message = new MimeMessage();
|
||
//message.From.Add(new MailboxAddress( _emailOptions.SenderName, _emailOptions.SenderEmail)); // 设置发件人姓名和邮箱地址
|
||
message.From.Add(new MailboxAddress("深圳市元创时代", _emailOptions.SenderEmail)); // 设置发件人姓名和邮箱地址
|
||
message.To.Add(new MailboxAddress(coustomerName, mailAddress)); // 设置收件人姓名和邮箱地址
|
||
//message.To.AddRange(toMailList);
|
||
//message.Subject = _emailOptions.SendTitle+$"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}"; // 设置邮件主题
|
||
message.Subject = $"【深圳市元创时代】-产品入库通知{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}"; // 设置邮件主题
|
||
|
||
// 创建邮件正文
|
||
BodyBuilder bodyBuilder = new BodyBuilder();
|
||
//bodyBuilder.TextBody = textBody; // 设置纯文本内容
|
||
bodyBuilder.HtmlBody = $"<p>尊敬的{coustomerName},您好,您的以下产品已入库/可安排发货:\r\n<p><p>{textBody}</p>"; // 设置 HTML 内容
|
||
message.Body = bodyBuilder.ToMessageBody();
|
||
|
||
try
|
||
{
|
||
// 连接 SMTP 服务器并发送邮件
|
||
using (SmtpClient client = new SmtpClient())
|
||
{
|
||
await client.ConnectAsync(_emailOptions.SmtpServer, _emailOptions.SmtpPort, SecureSocketOptions.StartTls); // 连接 SMTP 服务器
|
||
await client.AuthenticateAsync(_emailOptions.SenderEmail, _emailOptions.SenderEmailPwd); // 进行身份验证
|
||
await client.SendAsync(message); // 发送邮件
|
||
await client.DisconnectAsync(true); // 断开连接
|
||
}
|
||
_logger.LogInformation("邮箱发送消息:成功->收件方:" + mailAddress + " 内容:" + textBody);
|
||
return Result.ReSuccess();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogInformation("邮箱发送消息:失败->收件方:" + mailAddress + " 内容:" + textBody);
|
||
return Result.ReFailure("邮箱发送:失败->收件方:"+mailAddress+",错误内容:" + ex.Message, 50006);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取数据
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<(List<SendDataDto> sendDataList, List<InStockDetails> updateDetails)> GetSendContent()
|
||
{
|
||
//1.获取物料集合和组织集合和供应商的集合
|
||
var materials = new List<ErpMaterialDto>();
|
||
var materials_result = await _erpService.BillQueryForMaterial();
|
||
if (materials_result.IsSuccess)
|
||
materials = materials_result.Data.ToList();
|
||
|
||
//客户
|
||
var customerList = await _subscribeNotificationRepositories.GetList();
|
||
//要处理发送的明细
|
||
//var notSendDetails = await _inStockTaskRepositories.GetNotSendErpDetails();
|
||
var notSendDetails = await _inStockRepositories.GetNotSendDetails();
|
||
_logger.LogInformation("发送消息:要处理发送的明细->:" + JsonConvert.SerializeObject(notSendDetails));
|
||
|
||
var sendDataList = new List<SendDataDto>();
|
||
if (customerList != null && customerList.Count != 0)
|
||
{
|
||
if (notSendDetails != null && notSendDetails.Count != 0)
|
||
{
|
||
//var taskIds = notSendDetails.GroupBy(x => x.Fid).Select(x => x.Key).ToList();
|
||
|
||
//var instockTotalDetails = await _inStockRepositories.GetInStockTotalDetails(taskIds);
|
||
var instockTotalDetails = notSendDetails.GroupBy(x => new { x.CustomerCode, x.MaterialNumber }).Select(x => new
|
||
{
|
||
CustomerCode = x.Key.CustomerCode,
|
||
MaterialNumber = x.Key.MaterialNumber,
|
||
TotalQty = x.Sum(t => t.Qty)
|
||
}).ToList();
|
||
|
||
foreach (var item in customerList)
|
||
{
|
||
//var current_notSendDetails = notSendDetails.Where(x => x.CustomerCode == item.CustomerNumber).ToList();
|
||
//var current_notSendDetails = instockTotalDetails.Where(x => x.CustomerCode == item.CustomerNumber).ToList();
|
||
var current_notSendDetails = instockTotalDetails.Where(x => x.CustomerCode == item.CustomerNumber).ToList();
|
||
if (current_notSendDetails != null && current_notSendDetails.Count != 0)
|
||
{
|
||
var data = new SendDataDto();
|
||
data.CustomerCode = item.CustomerNumber;
|
||
data.CustomerName = item.CustomerName;
|
||
data.PhoneNumbers = string.Join(",", item.Telephones);
|
||
data.Emails = string.Join(",", item.Emails);
|
||
foreach (var ema in item.Emails)
|
||
{
|
||
if (!string.IsNullOrEmpty(ema))
|
||
data.EmailList.Add(new MailAddress(item.CustomerName, ema));
|
||
}
|
||
|
||
foreach (var current_det in current_notSendDetails)
|
||
{
|
||
var det = new SendDataDetailsDto();
|
||
//det.TaskDetailId = current_det.Id;
|
||
det.MaterialName = _erpBasicDataExtendService.GetMaterialName(materials, current_det.MaterialNumber);
|
||
det.Specifications = _erpBasicDataExtendService.GetMaterialSpecifications(materials, current_det.MaterialNumber);
|
||
det.MaterialNumber = current_det.MaterialNumber;
|
||
//det.Qty = instockTotalDetails.Where(x => x.TaskId == current_det.Fid && x.MaterialNumber == current_det.MaterialNumber).Sum(x => x.Qty);
|
||
det.Qty = current_det.TotalQty;
|
||
data.Details.Add(det);
|
||
//if (det.Qty > 0)
|
||
// data.Details.Add(det);
|
||
}
|
||
if (data.Details.Count != 0)
|
||
sendDataList.Add(data);
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
return (sendDataList, notSendDetails);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<Result> Execute()
|
||
{
|
||
try
|
||
{
|
||
var update_DetailsIds = new List<int>();
|
||
var update_InstockIds = new List<int>();
|
||
|
||
var (sendContentList,updateList) = await this.GetSendContent();
|
||
|
||
if (sendContentList != null && sendContentList.Count != 0)
|
||
{
|
||
|
||
foreach (var item in sendContentList)
|
||
{
|
||
|
||
var content = "";
|
||
int i = 1;
|
||
foreach (var det in item.Details)
|
||
{
|
||
if (i == item.Details.Count)
|
||
content = content+ det.Specifications + " 数量" + Convert.ToInt32(det.Qty);
|
||
else
|
||
content = content + det.Specifications + " 数量" + Convert.ToInt32(det.Qty) + ",";
|
||
i = i + 1;
|
||
|
||
}
|
||
foreach (var email in item.EmailList)
|
||
{
|
||
//邮箱
|
||
await this.SendEmail(email.Email, content, email.Name);
|
||
}
|
||
|
||
//短信
|
||
var sms_result = this.SendSms(item.PhoneNumbers, content);
|
||
//内容过长-分段发送
|
||
if (!sms_result.IsSuccess && sms_result.Message == "isv.PARAM_LENGTH_LIMIT")
|
||
{
|
||
//分段发送:以字数480长度分段
|
||
//var contentParts = this.SplitContent(content, 480);
|
||
//foreach (var partContent in contentParts)
|
||
//{
|
||
// this.SendSms(item.PhoneNumbers, partContent);
|
||
//}
|
||
|
||
//1.先合并属性:规格和数量
|
||
var materials= this.MergeProperties(item.Details);
|
||
//2.按450个字分组成多个字符串集合
|
||
var contentParts = this.MergeStrings(materials);
|
||
foreach (var partContent in contentParts)
|
||
{
|
||
this.SendSms(item.PhoneNumbers, partContent);
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
update_DetailsIds = updateList.GroupBy(x=>x.Id).Select(x => x.Key).ToList();
|
||
//var inStockDetails = await _inStockRepositories.GetDetailsList(update_DetailsIds);
|
||
//if (inStockDetails != null && inStockDetails.Count != 0)
|
||
//{
|
||
// inStockDetails.ForEach(x => { x.IsHasSend = true; });
|
||
|
||
// var update_Result = await _inStockRepositories.UpdateRange(inStockDetails);
|
||
// if (!update_Result)
|
||
// {
|
||
// _logger.LogInformation("发送消息:失败-修改入库明细->:" + JsonConvert.SerializeObject(inStockDetails));
|
||
// }
|
||
// else
|
||
// {
|
||
// _logger.LogInformation("发送消息:成功-修改入库明细->:" + JsonConvert.SerializeObject(inStockDetails));
|
||
// }
|
||
//}
|
||
|
||
update_InstockIds = updateList.GroupBy(x => x.Fid).Select(x => x.Key).ToList();
|
||
var inStocks = await _inStockRepositories.GetList(update_InstockIds);
|
||
if (inStocks != null && inStocks.Count != 0)
|
||
{
|
||
inStocks.SelectMany(x => x.Details).Where(x=> update_DetailsIds.Contains(x.Id)).ToList().ForEach(x => { x.IsHasSend = true; });
|
||
var isSuccess = await _inStockRepositories.UpdateRange(inStocks);
|
||
if (!isSuccess)
|
||
{
|
||
_logger.LogInformation("发送消息:失败-修改入库明细->:" + JsonConvert.SerializeObject(inStocks.SelectMany(x => x.Details).Where(x => update_DetailsIds.Contains(x.Id)).ToList()));
|
||
}
|
||
else
|
||
{
|
||
_logger.LogInformation("发送消息:成功-修改入库明细->:" + JsonConvert.SerializeObject(inStocks.SelectMany(x => x.Details).Where(x => update_DetailsIds.Contains(x.Id)).ToList()));
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
else
|
||
_logger.LogInformation("发送消息:暂无可发送的通知明细");
|
||
|
||
|
||
return Result.ReSuccess();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return Result.ReFailure("发送失败:" + ex.Message, 50006);
|
||
}
|
||
|
||
}
|
||
|
||
// 将消息分割成多个部分
|
||
public List<string> SplitContent(string content, int maxLength)
|
||
{
|
||
|
||
List<string> parts = new List<string>();
|
||
for (int i = 0; i < content.Length; i += maxLength)
|
||
{
|
||
parts.Add(content.Substring(i, Math.Min(maxLength, content.Length - i)));
|
||
}
|
||
return parts;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 合并属性
|
||
/// </summary>
|
||
/// <param details=""></param>
|
||
/// <returns></returns>
|
||
public List<string> MergeProperties(List<SendDataDetailsDto> details)
|
||
{
|
||
List<string> result = new List<string>();
|
||
foreach (var det in details)
|
||
{
|
||
result.Add($"{det.Specifications} 数量{Convert.ToInt32(det.Qty)}");
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按450个字开始分组
|
||
/// </summary>
|
||
/// <param name="strings"></param>
|
||
/// <returns></returns>
|
||
public List<string> MergeStrings(List<string> strings)
|
||
{
|
||
List<string> result = new List<string>();
|
||
StringBuilder currentGroup = new StringBuilder();
|
||
|
||
foreach (var str in strings)
|
||
{
|
||
if (currentGroup.Length + str.Length > 450)
|
||
{
|
||
// 当前字符串超过50个字符,将当前分组加入结果集并重置currentGroup
|
||
result.Add(currentGroup.ToString());
|
||
currentGroup.Clear();
|
||
}
|
||
|
||
// 添加当前字符串到当前分组
|
||
currentGroup.Append(str);
|
||
}
|
||
|
||
// 添加最后一个分组
|
||
if (currentGroup.Length > 0)
|
||
{
|
||
result.Add(currentGroup.ToString());
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
}
|