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.Info.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.Interface; using DS.WMS.Core.Op.Interface.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 HtmlAgilityPack; using Mapster; using Masuit.Tools; using Masuit.Tools.Systems; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using RazorEngineCore; namespace DS.WMS.Core.Op.Method.TaskInteraction { /// /// 业务邮件发送服务 /// public class MailService : ServiceBase { static readonly ConcurrentDictionary TemplateCache = new ConcurrentDictionary(); ITaskMailService service; ISeaExportService seService; IConfiguration config; ApiFox Api = DefaultActionExecutor.Api; /// /// 初始化 /// /// public MailService(IServiceProvider provider) : base(provider) { service = provider.GetRequiredService(); seService = provider.GetRequiredService(); config = provider.GetRequiredService(); } private async Task RenderTemplateAsync(string name, string templateText, object model, RazorEngine? 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); } /// /// 根据配置发送邮件 /// /// 邮件配置 /// 业务ID /// 业务类型 /// public async Task SendAsync(BusinessTaskMail mailConfig, long businessId, BusinessType businessType = BusinessType.OceanShippingExport) { ArgumentNullException.ThrowIfNull(mailConfig, nameof(mailConfig)); var result = seService.GetSeaExportInfo(businessId.ToString()); if (!result.Succeeded || result.Data == null) return DataResult.Failed($"未能获取Id={businessId}的{businessType.GetDescription()}数据"); string title, content = string.Empty; var templateModel = new MailTemplateModel { Primary = result.Data.Adapt() }; 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})时出错,请检查模板是否有语法错误"); } //设置发件人 long senderId = 0; if (mailConfig.Sender.IsSale) senderId = templateModel.Primary.SaleId; else if (mailConfig.Sender.IsOperator) senderId = templateModel.Primary.OperatorId; else if (mailConfig.Sender.IsCustomerService) senderId = templateModel.Primary.CustomerService; else if (mailConfig.Sender.IsVouchingClerk) senderId = templateModel.Primary.Doc; var sender = await Db.Queryable().Where(x => x.Id == senderId).Select(x => new { x.UserName, x.SignatureHtml }).FirstAsync(); if (sender == null) return DataResult.Failed("未设置发件人"); var senderConfig = await TenantDb.Queryable().FirstAsync(x => x.CreateBy == senderId); if (senderConfig == null) return DataResult.Failed($"发件人用户:{sender.UserName} 未设置SMTP发件信息"); //插入发件人签名 var htmlDoc = new HtmlDocument(); htmlDoc.LoadHtml(content); var node = htmlDoc.GetElementbyId("sign"); if (node != null) node.InnerHtml = sender.SignatureHtml; using (StringWriter writer = new()) { htmlDoc.Save(writer); content = writer.ToString(); writer.Close(); } //设置抄送人 List ccIds = []; List ccList = []; if (mailConfig.CC.IsSale) ccIds.Add(templateModel.Primary.SaleId); else if (mailConfig.CC.IsOperator) ccIds.Add(templateModel.Primary.OperatorId); else if (mailConfig.CC.IsCustomerService) ccIds.Add(templateModel.Primary.CustomerService); else if (mailConfig.CC.IsVouchingClerk) ccIds.Add(templateModel.Primary.Doc); if (ccIds.Count > 0) ccList = await Db.Queryable().Where(x => ccIds.Contains(x.Id)).Select(x => x.Email).ToListAsync(); //设置收件人 List receiverIds = []; if (mailConfig.Receiver.IsCarrier) receiverIds.Add(templateModel.Primary.CarrierId); if (mailConfig.Receiver.IsBooking) receiverIds.Add(templateModel.Primary.ForwarderId); if (mailConfig.Receiver.IsYard) receiverIds.Add(templateModel.Primary.YardId); if (mailConfig.Receiver.IsTruck) receiverIds.Add(templateModel.Primary.TruckerId); if (mailConfig.Receiver.IsController) receiverIds.Add(templateModel.Primary.CustomerId); var receiverList = await TenantDb.Queryable().Where(x => receiverIds.Contains(x.Id) && x.Email != null && x.Email != string.Empty) .Select(x => new { x.ShortName, x.EnShortName, x.Email }).ToListAsync(); var attachmentList = mailConfig.Attachments == null ? [] : new List>(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) { var req = new OpenPrintReq { ParamJsonStr = JsonConvert.SerializeObject(new { Id = businessId }), PrintType = ((int)FileFormat.PDF).ToString(), TemplateId = item.TemplateId, TenantId = tenantId }; var reqResult = await Api.PostAsync(requestUrl, req); if (!reqResult.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.Succeeded) return DataResult.Failed($"未能获取打印API生成的文件,附件地址:{url}"); string fileName = item.FileName.IsNullOrEmpty() ? Path.GetFileName(reqResult.Data.Data) : item.FileName; var bytes = await fileResult.Data.Content.ReadAsByteArrayAsync(); string base64Str = Convert.ToBase64String(bytes); attachmentList.Add(new Tuple(fileName, base64Str)); } } dynamic[] mailParams = [new { SendTo = string.Join(",", receiverList.Select(x => x.Email)), Title = title, Body = content, CCTo = string.Join(",", ccList), ShowName = senderConfig.ShowName.IsNullOrEmpty() ? sender.UserName : 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.Data.IsSuccessStatusCode) { return DataResult.Successed($"已发邮件(委托单:{templateModel.Primary.CustomerNo}),收件人:" + string.Join(",", receiverList.Select(x => x.Email)) + "发件人:" + senderConfig.MailAccount); } return DataResult.Failed($"邮件发送失败,API返回状态为:{(int)mailResult.Data.StatusCode} {mailResult.Data.StatusCode}" + "(委托单:{templateModel.Primary.CustomerNo}),收件人:" + string.Join(",", receiverList.Select(x => x.Email)) + "发件人:" + senderConfig.MailAccount); } catch (Exception ex) { await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.HttpRequestFailed)); } } } }