You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
12 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Collections.Concurrent;
using DS.Module.Core;
using DS.Module.Core.Data;
using DS.Module.PrintModule;
using DS.WMS.Core.Code.Entity;
using DS.WMS.Core.Op.Dtos.TaskInteraction;
using DS.WMS.Core.Op.Entity;
using DS.WMS.Core.Op.Entity.TaskInteraction;
using DS.WMS.Core.Op.Method.TaskInteraction.ActionExecutor;
using DS.WMS.Core.Op.Method.TaskInteraction.ActionExecutor.Booking;
using DS.WMS.Core.Sys.Entity;
using Fasterflect;
using HtmlAgilityPack;
using Masuit.Tools;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using RazorEngineCore;
namespace DS.WMS.Core.Op.Method.TaskInteraction
{
/// <summary>
/// 业务邮件发送服务
/// </summary>
public class MailService : ServiceBase
{
static readonly ConcurrentDictionary<int, IRazorEngineCompiledTemplate> TemplateCache =
new ConcurrentDictionary<int, IRazorEngineCompiledTemplate>();
IConfiguration config;
ApiFox Api = DefaultActionExecutor.Api;
/// <summary>
/// 初始化
/// </summary>
/// <param name="provider"></param>
public MailService(IServiceProvider provider) : base(provider)
{
config = provider.GetRequiredService<IConfiguration>();
TenantDb.QueryFilter.Clear<IOrgId>();
}
static async Task<string> RenderTemplateAsync(string name, string templateText, object model, IRazorEngine? razorEngine = null)
{
int hashCode = name.GetHashCode();
IRazorEngineCompiledTemplate compiledTemplate = TemplateCache.GetOrAdd(hashCode, i =>
{
var engine = razorEngine ?? new RazorEngine();
return engine.Compile(templateText);
});
return await compiledTemplate.RunAsync(model);
}
/// <summary>
/// 根据配置发送邮件
/// </summary>
/// <param name="mailConfig">邮件配置</param>
/// <param name="templateModel">模板数据</param>
/// <returns></returns>
public async Task<DataResult> SendAsync(BusinessTaskMail mailConfig, MailTemplateModel templateModel)
{
ArgumentNullException.ThrowIfNull(mailConfig, nameof(mailConfig));
ArgumentNullException.ThrowIfNull(templateModel, nameof(templateModel));
var order = await TenantDb.Queryable<SeaExport>().Where(x => x.Id == templateModel.BusinessId)
.Select(x => new
{
x.CustomerNo,
//
x.SaleId,
x.OperatorId,
x.CustomerService,
x.Doc,
//
x.CarrierId,
x.ForwarderId,
x.YardId,
x.TruckerId,
x.CustomerId
}).FirstAsync();
if (order == null)
return DataResult.Failed($"未能获取订单({templateModel.BusinessId})数据");
//设置发件人
if (mailConfig.Sender == null)
return DataResult.Failed("未设置发件人");
long senderId = 0;
if (mailConfig.Sender.IsSale)
senderId = order.SaleId;
else if (mailConfig.Sender.IsOperator)
senderId = order.OperatorId;
else if (mailConfig.Sender.IsCustomerService)
senderId = order.CustomerService;
else if (mailConfig.Sender.IsVouchingClerk)
senderId = order.Doc;
templateModel.Sender = await Db.Queryable<SysUser>().Where(x => x.Id == senderId).Select(x => new MailSender
{
DisplayName = x.UserName,
Phone = x.Phone,
SignatureHtml = x.SignatureHtml
}).FirstAsync();
if (templateModel.Sender == null)
return DataResult.Failed("邮件模板未设置发件人");
var senderConfig = await TenantDb.Queryable<CodeUserEmail>().FirstAsync(x => x.CreateBy == senderId);
if (senderConfig == null)
return DataResult.Failed($"发件人用户:{templateModel.Sender.DisplayName} 未设置SMTP发件信息");
templateModel.Sender.MailAddress = senderConfig.MailAccount;
List<string> ccList = [];
if (mailConfig.CC != null)
{
//设置抄送人
List<long> ccIds = [];
if (mailConfig.CC.IsSale)
ccIds.Add(order.SaleId);
else if (mailConfig.CC.IsOperator)
ccIds.Add(order.OperatorId);
else if (mailConfig.CC.IsCustomerService)
ccIds.Add(order.CustomerService);
else if (mailConfig.CC.IsVouchingClerk)
ccIds.Add(order.Doc);
if (ccIds.Count > 0)
ccList = await Db.Queryable<SysUser>().Where(x => ccIds.Contains(x.Id)).Select(x => x.Email).ToListAsync();
}
//设置收件人
if (mailConfig.Receiver == null)
return DataResult.Failed("邮件模板未设置收件人");
List<string> receiverTypes = [];
if (mailConfig.Receiver.IsCarrier)
receiverTypes.Add("carrier");
if (mailConfig.Receiver.IsBooking)
receiverTypes.Add("booking");
if (mailConfig.Receiver.IsYard)
receiverTypes.Add("yard");
if (mailConfig.Receiver.IsTruck)
receiverTypes.Add("truck");
if (mailConfig.Receiver.IsController)
receiverTypes.Add("controller");
templateModel.Receivers = await TenantDb.Queryable<BusinessOrderContact>().Where(x => receiverTypes.Contains(x.CustomerType)
&& x.Email != null && x.Email != string.Empty && x.BusinessId == templateModel.BusinessId && x.BusinessType == templateModel.BusinessType)
.Select(x => new MailReceiver { DisplayName = x.Name, MailAddress = x.Email }).ToListAsync();
string title, content = string.Empty;
var razorEngine = new RazorEngine();
try
{
string key1 = mailConfig.Id + "-" + nameof(mailConfig.Title);
title = await RenderTemplateAsync(key1, mailConfig.Title, templateModel, razorEngine);
string key2 = mailConfig.Id + "-" + nameof(mailConfig.Content);
content = await RenderTemplateAsync(key2, mailConfig.Content, templateModel, razorEngine);
}
catch (Exception ex)
{
await ex.LogAsync(Db);
return DataResult.Failed($"渲染邮件模板({mailConfig.Id})时出错,请检查模板是否有语法错误");
}
//插入发件人签名
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(content);
var node = htmlDoc.GetElementbyId("sign");
if (node != null)
node.InnerHtml = templateModel.Sender.SignatureHtml;
using (StringWriter writer = new())
{
htmlDoc.Save(writer);
content = writer.ToString();
writer.Close();
}
var attachmentList = mailConfig.Attachments == null ? [] : new List<Tuple<string, string>>(mailConfig.Attachments.Count);
try
{
//生成模板附件
if (mailConfig.Attachments?.Count > 0)
{
if (Api.DefaultHeaders.Contains("Authorization"))
Api.DefaultHeaders.Remove("Authorization");
Api.DefaultHeaders.Add("Authorization", "Bearer " + User.GetToken());
long tenantId = long.Parse(User.TenantId);
string requestUrl = config["TaskMail:FileBaseUrl"] + config["TaskMail:SQLPrint"];
foreach (var item in mailConfig.Attachments)
{
object? obj = PropertyExtensions.GetPropertyValue(templateModel, nameof(MailTemplateModel.Primary), Flags.InstancePublic);
var req = new OpenPrintReq
{
ParamJsonStr = JsonConvert.SerializeObject(obj),
PrintType = ((int)FileFormat.PDF).ToString(),
TemplateId = item.TemplateId,
TenantId = tenantId
};
var reqResult = await Api.PostAsync<PrintResult>(requestUrl, req);
if (!reqResult.Data.Succeeded)
return DataResult.Failed($"未能获取打印API生成的文件请求地址{requestUrl}");
string url = config["TaskMail:FileBaseUrl"] + @"/PrintTempFile/" + reqResult.Data.Data;
var fileResult = await Api.SendRequestAsync(HttpMethod.Get, url);
if (!fileResult.IsSuccessStatusCode)
return DataResult.Failed($"未能获取打印API生成的文件附件地址{url}");
string? fileName = item.FileName.IsNullOrEmpty() ? Path.GetFileName(reqResult.Data.Data) : item.FileName;
var byteArray = await fileResult.Content.ReadAsByteArrayAsync();
string base64Str = Convert.ToBase64String(byteArray);
attachmentList.Add(new Tuple<string, string>(fileName, base64Str));
}
}
//手工附件
if (templateModel.FileAttachments?.Count > 0)
{
foreach (var item in templateModel.FileAttachments)
{
FileInfo file = new FileInfo(item.FilePath);
if (!file.Exists)
return DataResult.Failed($"参数设置的附件路径:{file.FullName} 不存在");
using (var stream = file.OpenRead())
{
var byteArray = stream.ToArray();
string base64Str = Convert.ToBase64String(byteArray);
attachmentList.Add(new Tuple<string, string>(item.DisplayName ?? file.Name, base64Str));
}
}
}
string sendTo = string.Join(",", templateModel.Receivers.Select(x => x.MailAddress));
dynamic[] mailParams = [new
{
SendTo = sendTo,
Title = title,
Body = content,
CCTo = string.Join(",", ccList),
ShowName = senderConfig.ShowName.IsNullOrEmpty() ? templateModel.Sender.DisplayName : senderConfig.ShowName,
Account = senderConfig.MailAccount,
senderConfig.Password,
Server = senderConfig.SmtpServer,
Port = senderConfig.SmtpPort.GetValueOrDefault(),
UseSSL = senderConfig.SmtpSSL.GetValueOrDefault(),
Attaches = attachmentList?.Select(x => new
{
AttachName = x.Item1,
AttachContent = x.Item2
}).ToList()
}];
var mailResult = await Api.SendRequestAsync(HttpMethod.Post, config["TaskMail:MailApiUrl"], mailParams);
if (mailResult.IsSuccessStatusCode)
{
return DataResult.Successed($"已发邮件(委托单:{order.CustomerNo}),收件人:"
+ string.Join(",", sendTo) + "发件人:" + senderConfig.MailAccount);
}
return DataResult.Failed($"邮件发送失败API返回状态为{(int)mailResult.StatusCode} {mailResult.StatusCode}"
+ $"(委托单:{order.CustomerNo}),收件人:" + string.Join(",", sendTo) + "发件人:" + senderConfig.MailAccount);
}
catch (Exception ex)
{
await ex.LogAsync(Db);
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.HttpRequestFailed));
}
}
}
}