diff --git a/Tiobon.Core.Api/Controllers/Ghrh/Ghrh_ResumeController.cs b/Tiobon.Core.Api/Controllers/Ghrh/Ghrh_ResumeController.cs index 3fa48638..943eb78b 100644 --- a/Tiobon.Core.Api/Controllers/Ghrh/Ghrh_ResumeController.cs +++ b/Tiobon.Core.Api/Controllers/Ghrh/Ghrh_ResumeController.cs @@ -437,4 +437,17 @@ public class Ghrh_ResumeController : BaseController + /// 简历下载 + /// + /// 简历ID + /// + [HttpPost, Route("Download")] + public async Task> Download([FromBody] List ids) + { + return await _service.Download(ids); + } + #endregion } \ No newline at end of file diff --git a/Tiobon.Core.Api/Program.cs b/Tiobon.Core.Api/Program.cs index e663c164..4dc0d9c7 100644 --- a/Tiobon.Core.Api/Program.cs +++ b/Tiobon.Core.Api/Program.cs @@ -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()); 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("AppSettings:UseLoadTest")) diff --git a/Tiobon.Core.Api/Tiobon.Core.xml b/Tiobon.Core.Api/Tiobon.Core.xml index 68ce038e..488f4c95 100644 --- a/Tiobon.Core.Api/Tiobon.Core.xml +++ b/Tiobon.Core.Api/Tiobon.Core.xml @@ -1479,6 +1479,13 @@ 简历ID + + + 简历下载 + + 简历ID + + 教育背景(Controller) diff --git a/Tiobon.Core.Api/Views/Ghrh_ResumeTemplatePreview/Index.cshtml b/Tiobon.Core.Api/Views/Ghrh_ResumeTemplatePreview/Index.cshtml index d9720d8b..c46a1d03 100644 --- a/Tiobon.Core.Api/Views/Ghrh_ResumeTemplatePreview/Index.cshtml +++ b/Tiobon.Core.Api/Views/Ghrh_ResumeTemplatePreview/Index.cshtml @@ -1,6 +1,8 @@ @using Tiobon.Core.Model.ViewModels.Extend @{ Layout = "~/Views/Shared/_Layout.cshtml"; + + string frontUrl = AppSettings.app(["Startup", "FrontUrl"]); List 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") {
-
+
@if (!string.IsNullOrEmpty(entity.PhotoUrl)) { - + }
@@ -143,19 +145,19 @@
@(entity.ApplyStatusLabel) 
经验:@(entity.WorkYears)年工作经验   | 年龄:@(entity.Age)    | 学历: @(entity.EduDegreeLabel)  
-
+
} else if (item.tabKey == "Base") {
-
+
@item.tabName -
+
@if (item.children != null && item.children.Any()) @foreach (var children in item.children) { -
+
@(children.label):
@{ @@ -168,7 +170,7 @@ } }
-
+ } @@ -180,9 +182,9 @@
@if (FamilyDic.Count > 0) { -
+
@item.tabName -
+
} @for (var i = 0; i < FamilyDic.Count; i++) { @@ -194,7 +196,7 @@ { @foreach (var children in item.children) { -
+
@(children.label):
@{ @@ -207,7 +209,7 @@ } }
-
+ } } } @@ -220,9 +222,9 @@ @{ if (EducationDic.Count > 0) { -
+
@item.tabName -
+ } 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) { -
+
@(children.label):
@{ @@ -243,7 +245,7 @@ } }
-
+ } } } @@ -255,9 +257,9 @@ @{ if (WorkExpDic.Count > 0) { -
+
@item.tabName -
+ } 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) { -
+
@(children.label):
@{ @@ -278,7 +280,7 @@ } }
-
+ } } } @@ -291,9 +293,9 @@ @{ if (LicenceDic.Count > 0) { -
+
@item.tabName -
+ } 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) { -
+
@(children.label):
@{ @@ -314,7 +316,7 @@ } }
-
+ } } } @@ -326,9 +328,9 @@ @{ if (TrainingDic.Count > 0) { -
+
@item.tabName -
+ } 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) { -
+
@(children.label):
@{ @@ -349,7 +351,7 @@ } }
-
+ } } } @@ -362,9 +364,9 @@ var ii = 0; if (item.children != null && item.children.Any()) { -
+
@item.tabName -
+ } if (item.children != null && item.children.Any()) @@ -373,9 +375,9 @@ if (ii > 0) { -
+
} -
+
@(ii + 1). @(children.placeholder):
@{ @@ -390,7 +392,7 @@ } }
-
+ } } diff --git a/Tiobon.Core.Api/appsettings.json b/Tiobon.Core.Api/appsettings.json index e1b58682..c67ffd5f 100644 --- a/Tiobon.Core.Api/appsettings.json +++ b/Tiobon.Core.Api/appsettings.json @@ -230,7 +230,8 @@ }, "Nacos": { "Enabled": false //Nacos注册中心 - } + }, + "FrontUrl": "https://g.tiobon.com" }, "Middleware": { "RequestResponseLog": { diff --git a/Tiobon.Core.Common/Helper/HttpHelper.cs b/Tiobon.Core.Common/Helper/HttpHelper.cs index c20af6a1..53fc867b 100644 --- a/Tiobon.Core.Common/Helper/HttpHelper.cs +++ b/Tiobon.Core.Common/Helper/HttpHelper.cs @@ -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; + +/// +/// httpclinet请求方式,请尽量使用IHttpClientFactory方式 +/// +public class HttpHelper { - /// - /// httpclinet请求方式,请尽量使用IHttpClientFactory方式 - /// - public class HttpHelper - { - public static readonly HttpClient Httpclient = new HttpClient(); + public static readonly HttpClient Httpclient = new HttpClient(); - public static async Task GetAsync(string serviceAddress) + public static async Task 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 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 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; } } \ No newline at end of file diff --git a/Tiobon.Core.IServices/Ghrh/IGhrh_ResumeServices.cs b/Tiobon.Core.IServices/Ghrh/IGhrh_ResumeServices.cs index 98b8905b..3d99952c 100644 --- a/Tiobon.Core.IServices/Ghrh/IGhrh_ResumeServices.cs +++ b/Tiobon.Core.IServices/Ghrh/IGhrh_ResumeServices.cs @@ -69,4 +69,5 @@ public interface IGhrh_ResumeServices : IBaseServices ApplyOfferApproval(List ids); Task OverTimeDeleteResume(); + Task> Download(List ids); } \ No newline at end of file diff --git a/Tiobon.Core.Services/Ghrh/Ghrh_ResumeServices.cs b/Tiobon.Core.Services/Ghrh/Ghrh_ResumeServices.cs index eed9b4e6..00efb6bb 100644 --- a/Tiobon.Core.Services/Ghrh/Ghrh_ResumeServices.cs +++ b/Tiobon.Core.Services/Ghrh/Ghrh_ResumeServices.cs @@ -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 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 dal, IGhrh_ResumeEduBGServices ghre_ResumeEduBGServices, @@ -50,7 +60,9 @@ public class Ghrh_ResumeServices : BaseServices @@ -2364,6 +2379,136 @@ WHERE A.IsEnable = 1 AND C.IsEnable = 1"; } #endregion + #region 简历下载 + public async Task> Download(List 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 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.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 通用方法 /// /// 记录日志 diff --git a/Tiobon.Core.Services/Tiobon.Core.Services.csproj b/Tiobon.Core.Services/Tiobon.Core.Services.csproj index d92bb106..1c210dda 100644 --- a/Tiobon.Core.Services/Tiobon.Core.Services.csproj +++ b/Tiobon.Core.Services/Tiobon.Core.Services.csproj @@ -22,6 +22,7 @@ + diff --git a/Tiobon.Core/Tiobon.Core.xml b/Tiobon.Core/Tiobon.Core.xml index 3f902792..488f4c95 100644 --- a/Tiobon.Core/Tiobon.Core.xml +++ b/Tiobon.Core/Tiobon.Core.xml @@ -1479,6 +1479,13 @@ 简历ID + + + 简历下载 + + 简历ID + + 教育背景(Controller) @@ -1578,7 +1585,7 @@ - + Index