本文探讨了如何在ABP框架中使用TPL文件将HTML模板转换为PDF文档.了解这种强大的方法如何简化文档创建,使开发人员能够高效地生成高质量的 PDF。和我一起演练利用 TPL 文件增强应用程序文档生成功能的步骤和最佳实践。
我们的系统中有广告。当我们的广告获得管理员的批准后,我们将发送已批准的广告报告并发送邮件以获取信息。
**注意:**我将使用 Puppeteer Sharp 库生成 pdf。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ L "AdvertApprovedReport" }}</title>
</head>
<body>
<div class="container">
<div class="report-header">
<div class="report-to">
<strong>{{ L "ReportTo" }}</strong><br>
{{ model.warehouse_name }}<br>
{{ model.warehouse_postal_code }}, {{ model.warehouse_place }}<br>
{{ model.warehouse_country }}
</div>
<div class="report-info">
<div><strong>{{ L "AdvertCode" }}:</strong> {{ model.advert_code }}</div>
<div><strong>{{ L "ApproveDate" }}:</strong> {{ model.approve_date }}</div>
</div>
</div>
<h4>{{ L "AdvertItems" }}</h4>
<table>
<thead>
<tr>
<th>{{ L "ProductName" }}</th>
<th>{{ L "UnitPrice" }}</th>
<th>{{ L "Quantity" }}</th>
<th>{{ L "TotalCost" }}</th>
</tr>
</thead>
<tbody>
{{ for item in model.advert_items }}
<tr>
<td>{{ item.product_name }}</td>
<td> €{{ item.unit_price }}</td>
<td>{{ item.quantity }}</td>
<td> €{{ item.total_cost }}</td>
</tr>
{{ end }}
</tbody>
</table>
<h4>{{ L "ApprovedAdvertItems" }}</h4>
<table>
<thead>
<tr>
<th>{{ L "ProductName" }}</th>
<th>{{ L "UnitPrice" }}</th>
<th>{{ L "Quantity" }}</th>
<th>{{ L "TotalCost" }}</th>
</tr>
</thead>
<tbody>
{{ for item in model.approved_advert_items }}
<tr>
<td>{{ item.product_name }}</td>
<td> €{{ item.unit_price }}</td>
<td>{{ item.quantity }}</td>
<td> €{{ item.total_cost }}</td>
</tr>
{{ end }}
</tbody>
</table>
<div class="total">{{ L "Subtotal" }}: €{{ model.subtotal }}</div>
<div class="total">(-) {{ L "FreightPrice" }}: €{{ model.freight_price }}</div>
<div class="total">(-) {{ L "ServiceFee" }}: €{{ model.service_fee }}</div>
<div class="total"></div>
<div class="expected-total">{{ L "ExpectedTotal" }}: €{{ model.expected_total }}</div>
</div>
</body>
</html>
我们需要在派生自 TemplateDefinitionProvider 的类中声明这一点。
public class AdvertEmailTemplateDefinitionProvider : TemplateDefinitionProvider
{
public override void Define(ITemplateDefinitionContext context)
{
context.Add(
new TemplateDefinition(
AdvertEmailTemplates.AdvertApprovedReportBody,
displayName: LocalizableString.Create\<KhanPalettenResource>(
$"TextTemplate:{AdvertEmailTemplates.AdvertApprovedReport}"
),
layout: StandardEmailTemplates.Layout,
localizationResource: typeof(KhanPalettenResource)
).WithVirtualFilePath("/Adverts/Templates/AdvertApprovedReport.tpl", true)
);
}
}
public class AdvertEmailTemplates
{
public const string AdvertApprovedReport = "AdvertApprovedReport";
}
public async Task SendAdvertReportAsync(
Advert advert,
List<string> customerMails,
List<string> adminMails
)
{
var attachmentModel = new
{
warehouse_name = advert?.Warehouse?.Name ?? string.Empty,
warehouse_postal_code = advert?.Warehouse?.PostalCode?.Code ?? string.Empty,
warehouse_place = advert?.Warehouse?.Place?.Name ?? string.Empty,
warehouse_country = advert?.Warehouse?.Country?.Name ?? string.Empty,
advert_code = advert?.TransactionNumber ?? string.Empty,
approve_date = DateTime.Now.ToString("dd.MM.yyyy HH:mm"),
advert_items = advert?.Items?.Select(x => new
{
product_name = x?.Product?.Name ?? string.Empty,
quantity = x?.Quantity ?? 0,
total_cost = x?.TotalCost ?? 0m,
unit_price = x?.UnitPrice ?? 0m,
}) ?? Enumerable.Empty<object>(),
approved_advert_items = advert?.ApprovedItems?.Select(x => new
{
product_name = x?.Product?.Name ?? string.Empty,
quantity = x?.Quantity ?? 0,
total_cost = x?.TotalCost ?? 0m,
unit_price = x?.UnitPrice ?? 0m,
}) ?? Enumerable.Empty<object>(),
subtotal = advert?.ApprovedItems?.Sum(x => x.TotalCost) ?? 0m,
freight_price = advert?.TransportationPrice?.Price ?? 0m,
service_fee = advert?.ApprovedItems?.Sum(x =>
x.Quantity * x.ServiceFees.Sum(fee => fee.ServicePrice.Price)
) ?? 0m,
expected_total = (advert?.ApprovedItems?.Sum(x => x.TotalCost) ?? 0m)
- (advert?.TransportationPrice?.Price ?? 0m)
- (
advert?.ApprovedItems?.Sum(x =>
x.Quantity * x.ServiceFees.Sum(fee => fee.ServicePrice.Price)
) ?? 0m
)
};
var emailAttachmentContent = await _templateRenderer.RenderAsync(
AdvertEmailTemplates.AdvertApprovedReport,
attachmentModel
);
byte[] pdfBytes = await _pdfService.ConvertHtmlToPdf(emailAttachmentContent);
var mailMessage = new MailMessage
{
Subject = _stringLocalizer["AdvertApprovedReport"],
IsBodyHtml = true,
Body = emailBodyContent,
};
foreach (var customer in customerMails)
{
mailMessage.To.Add(customer);
}
// from our code the scenario was like this:)
foreach (var admin in adminMails)
{
mailMessage.Bcc.Add(admin);
}
mailMessage.Attachments.Add(
new Attachment(new MemoryStream(pdfBytes), "AdvertApprovedReport.pdf", "application/pdf")
);
await _emailSender.SendAsync(mailMessage);
}
public class SendAdvertApprovedInfoLinkJob
: AsyncBackgroundJob<SendAdvertApprovedInfoLinkArgs>,
ITransientDependency
{
private readonly IAdvertRepository _advertRepository;
private readonly IAdvertEmailer _advertEmailer;
public SendAdvertApprovedInfoLinkJob(
IAdvertRepository advertRepository,
IAdvertEmailer advertEmailer
)
{
_advertRepository = advertRepository;
_advertEmailer = advertEmailer;
}
public override async Task ExecuteAsync(SendAdvertApprovedInfoLinkArgs args)
{
var advert = await _advertRepository.GetAsync(args.AdvertId);
var ownerEmail = advert
.Company.Members.Where(x => x.IsOwner)
.Select(x => x.User.Email)
.FirstOrDefault();
Check.NotNullOrEmpty(ownerEmail, nameof(ownerEmail));
await _advertEmailer.SendAdvertApprovedInfoLinkAsync(ownerEmail, args.TransactionNumber);
}
}
public class PdfService : IPdfService
{
public async Task<byte[]> ConvertHtmlToPdf(string htmlContent)
{
await new BrowserFetcher().DownloadAsync();
using (
var browser = await Puppeteer.LaunchAsync(
new LaunchOptions { Headless = true, Args = ["--no-sandbox"] }
)
)
using (var page = await browser.NewPageAsync())
{
await page.SetContentAsync(htmlContent);
var pdfBytes = await page.PdfDataAsync();
return pdfBytes;
}
}
}
[HttpPut("/api/app/advert-approval/{id}/{approvedItemId}")]
public async Task<AdvertDto> UpdateAsync(
Guid id,
long approvedItemId,
UpdateAdvertItemDto input
)
{
var advert = await _advertRepository.GetAsync(id);
var item = advert.GetApprovedItem(approvedItemId);
item.SetQuantity(input.Quantity);
var advertManager = GetAdvertManager(advert.AppModule);
await advertManager.CalculateTotalCost(advert);
await _advertRepository.UpdateAsync(advert);
await _backgroundJobManager.EnqueueAsync(new SendAdvertReportArgs(advert.Id));
return ObjectMapper.MapToDto(advert);
}
在这里,当我们更新广告状态时,我们称之为电子邮件发送部分。
这就是我要分享的关于这个话题的全部内容。我希望这篇文章对每个人都有信息和富有成效。