|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|