html 转pdf

简历下载
master
xiaochanghai 7 months ago
parent 408ff314d5
commit 0dbdc8a937
  1. 13
      Tiobon.Core.Api/Controllers/Ghrh/Ghrh_ResumeController.cs
  2. 17
      Tiobon.Core.Api/Program.cs
  3. 7
      Tiobon.Core.Api/Tiobon.Core.xml
  4. 74
      Tiobon.Core.Api/Views/Ghrh_ResumeTemplatePreview/Index.cshtml
  5. 3
      Tiobon.Core.Api/appsettings.json
  6. 82
      Tiobon.Core.Common/Helper/HttpHelper.cs
  7. 1
      Tiobon.Core.IServices/Ghrh/IGhrh_ResumeServices.cs
  8. 149
      Tiobon.Core.Services/Ghrh/Ghrh_ResumeServices.cs
  9. 1
      Tiobon.Core.Services/Tiobon.Core.Services.csproj
  10. 9
      Tiobon.Core/Tiobon.Core.xml

@ -437,4 +437,17 @@ public class Ghrh_ResumeController : BaseController<IGhrh_ResumeServices, Ghrh_R
return await _service.ApplyOfferApproval(ids);
}
#endregion
#region 简历下载
/// <summary>
/// 简历下载
/// </summary>
/// <param name="ids">简历ID</param>
/// <returns></returns>
[HttpPost, Route("Download")]
public async Task<ServiceResult<string>> Download([FromBody] List<long> ids)
{
return await _service.Download(ids);
}
#endregion
}

@ -19,6 +19,9 @@ using Tiobon.Core.Extensions.ServiceExtensions;
using Tiobon.Core.Filter;
using Tiobon.Core.Hubs;
using Tiobon.Core.Serilog.Utility;
using DinkToPdf.Contracts;
using DinkToPdf;
using Microsoft.Extensions.FileProviders;
var builder = WebApplication.CreateBuilder(args);
@ -116,6 +119,9 @@ builder.Services.AddEventBusSetup();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddMvc();
// DinkToPdf注入
builder.Services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools()));
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
@ -166,6 +172,17 @@ app.UseSerilogRequestLogging(options =>
options.GetLevel = SerilogRequestUtility.GetRequestLevel;
options.EnrichDiagnosticContext = SerilogRequestUtility.EnrichFromRequest;
});
//string fileroot = Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot/pdf_files");
//if (Directory.Exists(fileroot))
//{
// app.UseStaticFiles(new StaticFileOptions()
// {
// FileProvider = new PhysicalFileProvider(fileroot),
// RequestPath = new PathString("/pdf_files")
// });
//}
app.UseRouting();
if (builder.Configuration.GetValue<bool>("AppSettings:UseLoadTest"))

@ -1479,6 +1479,13 @@
<param name="ids">简历ID</param>
<returns></returns>
</member>
<member name="M:Tiobon.Core.Api.Controllers.Ghrh_ResumeController.Download(System.Collections.Generic.List{System.Int64})">
<summary>
简历下载
</summary>
<param name="ids">简历ID</param>
<returns></returns>
</member>
<member name="T:Tiobon.Core.Api.Controllers.Ghrh_ResumeEduBGController">
<summary>
教育背景(Controller)

@ -1,6 +1,8 @@
@using Tiobon.Core.Model.ViewModels.Extend
@{
Layout = "~/Views/Shared/_Layout.cshtml";
string frontUrl = AppSettings.app(["Startup", "FrontUrl"]);
List<ResumeFormColumn> Columns = ViewBag.Columns;
Ghrh_ResumeDto entity = ViewBag.entity;
ViewData["Title"] = entity.StaffName;
@ -17,7 +19,7 @@
color: #000000d9;
font-size: 14px;
font-family: Noto Sans SC, PingFang SC, tahoma, arial, Hiragino Sans GB, Hiragino Sans GB W3, Microsoft Yahei, STHeitiSC-Light, Helvetica-Light, sans-serif !important;
page-break-before: always;
page-break-before: always;
}
.ghr-preview-staff-info .pageContainer {
@ -35,10 +37,10 @@
}
.pageContainer .a4page.displayMode {
margin: 10px auto;
margin: 20px auto;
/* border: 1px #d3d3d3 solid;
box-shadow: 0 0 5px #0000001a; */
display: flex;
box-shadow: 0 0 5px #0000001a; */
/* display: flex; */
flex-wrap: wrap;
}
@ -131,11 +133,11 @@
if (item.tabKey == "Photo")
{
<div class="a4page displayMode page-break-inside" id="pageIndex-0">
<main class="ghr-preview-staff-base-info" id="info">
<div class="ghr-preview-staff-base-info" id="info">
<div class="staffPhoto">
@if (!string.IsNullOrEmpty(entity.PhotoUrl))
{
<img src="http://g.tiobon.com/@(entity.PhotoUrl)" alt="">
<img src="@frontUrl@entity.PhotoUrl" alt="">
}
</div>
<div class="staffBaseInfo">
@ -143,19 +145,19 @@
<div class="staffEName">@(entity.ApplyStatusLabel)&nbsp;</div>
<div class="staffHumanResourcesMsg">经验:@(entity.WorkYears)年工作经验&nbsp; <span><span>|</span><span>年龄:@(entity.Age)</span> &nbsp; </span><span><span>|</span><span>学历: @(entity.EduDegreeLabel)</span> &nbsp; </span></div>
</div>
</main>
</div>
</div>
}
else if (item.tabKey == "Base")
{
<div class="a4page displayMode page-break-inside" id="pageIndex-0">
<main class="ghr-preview-staff-resume-title" id="Base">
<div class="ghr-preview-staff-resume-title" id="Base" style="float:left;">
@item.tabName
</main>
</div>
@if (item.children != null && item.children.Any())
@foreach (var children in item.children)
{
<main class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="width:@(children.label=="备注"?"100%": "50%")">
<div class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="float:left;width:@(children.label=="备注"?"100%": "50%")">
<div class="label">@(children.label):</div>
<div class="itemValue">
@{
@ -168,7 +170,7 @@
}
}
</div>
</main>
</div>
}
</div>
@ -180,9 +182,9 @@
<div class="a4page displayMode page-break-inside" id="pageIndex-0">
@if (FamilyDic.Count > 0)
{
<main class="ghr-preview-staff-resume-title" id="Base">
<div class="ghr-preview-staff-resume-title" id="Base" style="float:left;">
@item.tabName
</main>
</div>
}
@for (var i = 0; i < FamilyDic.Count; i++)
{
@ -194,7 +196,7 @@
{
@foreach (var children in item.children)
{
<main class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="width:@(children.label=="备注"?"100%": "50%")">
<div class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="float:left;width:@(children.label=="备注"?"100%": "50%")">
<div class="label">@(children.label):</div>
<div class="itemValue">
@{
@ -207,7 +209,7 @@
}
}
</div>
</main>
</div>
}
}
}
@ -220,9 +222,9 @@
@{
if (EducationDic.Count > 0)
{
<main class="ghr-preview-staff-resume-title" id="Base">
<div class="ghr-preview-staff-resume-title" id="Base" style="float:left;">
@item.tabName
</main>
</div>
}
for (var i = 0; i < EducationDic.Count; i++)
{
@ -233,7 +235,7 @@
if (item.children != null && item.children.Any())
foreach (var children in item.children)
{
<main class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="width:@(children.label=="备注"?"100%": "50%")">
<div class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="float:left;width:@(children.label=="备注"?"100%": "50%")">
<div class="label">@(children.label):</div>
<div class="itemValue">
@{
@ -243,7 +245,7 @@
}
}
</div>
</main>
</div>
}
}
}
@ -255,9 +257,9 @@
@{
if (WorkExpDic.Count > 0)
{
<main class="ghr-preview-staff-resume-title" id="Base">
<div class="ghr-preview-staff-resume-title" id="Base" style="float:left;">
@item.tabName
</main>
</div>
}
for (var i = 0; i < WorkExpDic.Count; i++)
{
@ -268,7 +270,7 @@
if (item.children != null && item.children.Any())
foreach (var children in item.children)
{
<main class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="width:@(children.label=="备注"?"100%": "50%")">
<div class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="float:left;width:@(children.label=="备注"?"100%": "50%")">
<div class="label">@(children.label):</div>
<div class="itemValue">
@{
@ -278,7 +280,7 @@
}
}
</div>
</main>
</div>
}
}
}
@ -291,9 +293,9 @@
@{
if (LicenceDic.Count > 0)
{
<main class="ghr-preview-staff-resume-title" id="Base">
<div class="ghr-preview-staff-resume-title" id="Base" style="float:left;">
@item.tabName
</main>
</div>
}
for (var i = 0; i < LicenceDic.Count; i++)
{
@ -304,7 +306,7 @@
if (item.children != null && item.children.Any())
foreach (var children in item.children)
{
<main class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="width:@(children.label=="备注"?"100%": "50%")">
<div class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="float:left;width:@(children.label=="备注"?"100%": "50%")">
<div class="label">@(children.label):</div>
<div class="itemValue">
@{
@ -314,7 +316,7 @@
}
}
</div>
</main>
</div>
}
}
}
@ -326,9 +328,9 @@
@{
if (TrainingDic.Count > 0)
{
<main class="ghr-preview-staff-resume-title" id="Base">
<div class="ghr-preview-staff-resume-title" id="Base" style="float:left;">
@item.tabName
</main>
</div>
}
for (var i = 0; i < TrainingDic.Count; i++)
{
@ -339,7 +341,7 @@
if (item.children != null && item.children.Any())
foreach (var children in item.children)
{
<main class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="width:@(children.label=="培训说明"?"100%": "50%")">
<div class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="float:left;width:@(children.label=="培训说明"?"100%": "50%")">
<div class="label">@(children.label):</div>
<div class="itemValue">
@{
@ -349,7 +351,7 @@
}
}
</div>
</main>
</div>
}
}
}
@ -362,9 +364,9 @@
var ii = 0;
if (item.children != null && item.children.Any())
{
<main class="ghr-preview-staff-resume-title" id="Base">
<div class="ghr-preview-staff-resume-title" id="Base" style="float:left;">
@item.tabName
</main>
</div>
}
if (item.children != null && item.children.Any())
@ -373,9 +375,9 @@
if (ii > 0)
{
<div style="margin-top:20px;width:100%"></div>
<div style="margin-top:20px;width:100%;float:left;"></div>
}
<main class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="display: flex;width:80%">
<div class="ghr-preview-staff-resume-block-item" id="Base-0-StaffName" style="display: flex;width:80%">
<div class="itemValue" style="width:80%">@(ii + 1).&nbsp;@(children.placeholder):</div>
<div class="label" style="width:20%">
@{
@ -390,7 +392,7 @@
}
}
</div>
</main>
</div>
}
}
</div>

@ -230,7 +230,8 @@
},
"Nacos": {
"Enabled": false //Nacos
}
},
"FrontUrl": "https://g.tiobon.com"
},
"Middleware": {
"RequestResponseLog": {

@ -1,58 +1,54 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Net.Http.Headers;
namespace Tiobon.Core.Common.Helper
namespace Tiobon.Core.Common.Helper;
/// <summary>
/// httpclinet请求方式,请尽量使用IHttpClientFactory方式
/// </summary>
public class HttpHelper
{
/// <summary>
/// httpclinet请求方式,请尽量使用IHttpClientFactory方式
/// </summary>
public class HttpHelper
{
public static readonly HttpClient Httpclient = new HttpClient();
public static readonly HttpClient Httpclient = new HttpClient();
public static async Task<string> GetAsync(string serviceAddress)
public static async Task<string> GetAsync(string serviceAddress)
{
try
{
try
{
string result = string.Empty;
Uri getUrl = new Uri(serviceAddress);
Httpclient.Timeout = new TimeSpan(0, 0, 60);
result = await Httpclient.GetAsync(serviceAddress).Result.Content.ReadAsStringAsync();
return result;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return null;
string result = string.Empty;
Uri getUrl = new Uri(serviceAddress);
Httpclient.Timeout = new TimeSpan(0, 0, 60);
result = await Httpclient.GetAsync(serviceAddress).Result.Content.ReadAsStringAsync();
return result;
}
public static async Task<string> PostAsync(string serviceAddress, string requestJson = null)
catch (Exception e)
{
try
{
string result = string.Empty;
Uri postUrl = new Uri(serviceAddress);
Console.WriteLine(e.Message);
}
using (HttpContent httpContent = new StringContent(requestJson))
{
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return null;
}
Httpclient.Timeout = new TimeSpan(0, 0, 60);
result = await Httpclient.PostAsync(serviceAddress, httpContent).Result.Content.ReadAsStringAsync();
}
public static async Task<string> PostAsync(string serviceAddress, string requestJson = null)
{
try
{
string result = string.Empty;
Uri postUrl = new Uri(serviceAddress);
return result;
}
catch (Exception e)
using (HttpContent httpContent = new StringContent(requestJson))
{
Console.WriteLine(e.Message);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
Httpclient.Timeout = new TimeSpan(0, 0, 60);
result = await Httpclient.PostAsync(serviceAddress, httpContent).Result.Content.ReadAsStringAsync();
}
return null;
return result;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return null;
}
}

@ -69,4 +69,5 @@ public interface IGhrh_ResumeServices : IBaseServices<Ghrh_Resume, Ghrh_ResumeDt
Task<ServiceResult> ApplyOfferApproval(List<long> ids);
Task<ServiceResult> OverTimeDeleteResume();
Task<ServiceResult<string>> Download(List<long> ids);
}

@ -1,4 +1,11 @@
using Org.BouncyCastle.Utilities;
using DinkToPdf;
using DinkToPdf.Contracts;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using MySqlX.XDevAPI.Common;
using Org.BouncyCastle.Utilities;
using SharpCompress.Common;
using System.IO.Compression;
using static Tiobon.Core.Model.Consts;
namespace Tiobon.Core.Services;
@ -38,6 +45,9 @@ public class Ghrh_ResumeServices : BaseServices<Ghrh_Resume, Ghrh_ResumeDto, Ins
/// </summary>
private readonly IGhrh_InterviewLogServices _ghrh_InterviewLogServices;
private readonly IGhrh_HumanRequestServices _ghrh_HumanRequestServices;
private readonly IWebHostEnvironment Env;
private readonly IConverter _converter;
private readonly IWebHostEnvironment _hostingEnvironment;
public Ghrh_ResumeServices(ICaching caching,
IBaseRepository<Ghrh_Resume> dal,
IGhrh_ResumeEduBGServices ghre_ResumeEduBGServices,
@ -50,7 +60,9 @@ public class Ghrh_ResumeServices : BaseServices<Ghrh_Resume, Ghrh_ResumeDto, Ins
IGhrh_InterviewRecordServices ghre_InterviewRecordServices,
IGhrh_InterviewLogServices ghrh_InterviewLogServices,
IGhrh_HumanRequestServices ghrh_HumanRequestServices,
IGhrh_ResumeWorkExpServices ghre_ResumeWorkExpServices)
IConverter converter,
IWebHostEnvironment hostingEnvironment,
IGhrh_ResumeWorkExpServices ghre_ResumeWorkExpServices, IWebHostEnvironment env)
{
this._dal = dal;
base.BaseDal = dal;
@ -66,6 +78,9 @@ public class Ghrh_ResumeServices : BaseServices<Ghrh_Resume, Ghrh_ResumeDto, Ins
_ghrh_ResumeWorkExpServices = ghre_ResumeWorkExpServices;
_ghrh_InterviewLogServices = ghrh_InterviewLogServices;
_ghrh_HumanRequestServices = ghrh_HumanRequestServices;
Env = env;
_converter = converter;
_hostingEnvironment = hostingEnvironment;
}
/// <summary>
@ -2364,6 +2379,136 @@ WHERE A.IsEnable = 1 AND C.IsEnable = 1";
}
#endregion
#region 简历下载
public async Task<ServiceResult<string>> Download(List<long> ids)
{
string frontUrl = AppSettings.app(["Startup", "FrontUrl"]);
string path = string.Empty;
frontUrl += "/Advanced";
if (Env.IsDevelopment())
frontUrl = "http://localhost:9292";
//}
if (ids.Count == 1)
{
var entity = await base.QueryById(ids[0]);
var returnData = await HttpHelper.GetAsync(frontUrl + "/api/Ghrh_ResumeTemplatePreview/" + ids[0]);
var globalSettings = new GlobalSettings
{
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
DocumentTitle = "PDF Report",
};
var objectSettings = new ObjectSettings
{
PagesCount = true,
HtmlContent = returnData,
WebSettings = { DefaultEncoding = "utf-8" },
};
var pdf = new HtmlToPdfDocument()
{
GlobalSettings = globalSettings,
Objects = { objectSettings }
};
var fileBytes = _converter.Convert(pdf);
string pathHeader = "wwwroot/files/pdf_files";
if (!Directory.Exists(pathHeader))
Directory.CreateDirectory(pathHeader);
var ms = new MemoryStream(fileBytes);
var file = new FormFile(ms, 0, ms.Length, Path.GetFileNameWithoutExtension(pathHeader), Path.GetFileName(pathHeader));
var fileName = entity.StaffName + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".pdf";
await using (var fs = System.IO.File.Create(pathHeader + "/" + fileName))
{
await file.CopyToAsync(fs);
fs.Flush();
}
path = "/Advanced/files/pdf_files/" + fileName;
}
else
{
var files = new List<string>();
string pathHeader = "/files/pdf_files/" + SnowFlakeSingle.Instance.NextId() + "/";
if (!Directory.Exists("wwwroot" + pathHeader))
Directory.CreateDirectory("wwwroot" + pathHeader);
for (int i = 0; i < ids.Count; i++)
{
var entity = await base.QueryById(ids[i]);
var returnData = await HttpHelper.GetAsync(frontUrl + "/api/Ghrh_ResumeTemplatePreview/" + ids[0]);
var globalSettings = new GlobalSettings
{
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
DocumentTitle = "PDF Report",
};
var objectSettings = new ObjectSettings
{
PagesCount = true,
HtmlContent = returnData,
WebSettings = { DefaultEncoding = "utf-8" },
};
var pdf = new HtmlToPdfDocument()
{
GlobalSettings = globalSettings,
Objects = { objectSettings }
};
var fileBytes = _converter.Convert(pdf);
var fileName = pathHeader + entity.StaffName + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".pdf";
files.Add(_hostingEnvironment.WebRootPath + fileName);
var ms = new MemoryStream(fileBytes);
var file = new FormFile(ms, 0, ms.Length, Path.GetFileNameWithoutExtension("wwwroot" + pathHeader), Path.GetFileName("wwwroot" + pathHeader));
await using (var fs = System.IO.File.Create("wwwroot" + fileName))
{
await file.CopyToAsync(fs);
fs.Flush();
}
}
path = $"/files/pdf_files/简历_{DateTime.Now.ToString("yyyyMMddHHmmss")}.zip";
CreateZip($"{_hostingEnvironment.WebRootPath}{path}", files.ToArray());
FileHelper.DeleteFolder($"{_hostingEnvironment.WebRootPath}{pathHeader}");
path = "/Advanced" + path;
}
return ServiceResult<string>.OprateSuccess(path);
}
public static void CreateZip(string zipPath, string[] filesToZip)
{
if (File.Exists(zipPath))
{
throw new IOException("File already exists.");
}
using (FileStream zipFile = new FileStream(zipPath, FileMode.Create))
{
using (ZipArchive zipArchive = new ZipArchive(zipFile, ZipArchiveMode.Create))
{
foreach (string file in filesToZip)
{
if (File.Exists(file))
{
ZipArchiveEntry entry = zipArchive.CreateEntryFromFile(file, Path.GetFileName(file));
}
}
}
}
}
#endregion
#region 通用方法
/// <summary>
/// 记录日志

@ -22,6 +22,7 @@
<ItemGroup>
<PackageReference Include="AgileObjects.AgileMapper" Version="1.8.1" />
<PackageReference Include="Haukcode.DinkToPdf" Version="1.1.2" />
</ItemGroup>

@ -1479,6 +1479,13 @@
<param name="ids">简历ID</param>
<returns></returns>
</member>
<member name="M:Tiobon.Core.Api.Controllers.Ghrh_ResumeController.Download(System.Collections.Generic.List{System.Int64})">
<summary>
简历下载
</summary>
<param name="ids">简历ID</param>
<returns></returns>
</member>
<member name="T:Tiobon.Core.Api.Controllers.Ghrh_ResumeEduBGController">
<summary>
教育背景(Controller)
@ -1578,7 +1585,7 @@
<param name="filter"></param>
<returns></returns>
</member>
<member name="M:Tiobon.Core.Api.Controllers.Ghrh_ResumeTemplatePreviewController.Index">
<member name="M:Tiobon.Core.Api.Controllers.Ghrh_ResumeTemplatePreviewController.Index(System.Int64)">
<summary>
Index
</summary>

Loading…
Cancel
Save