AI接入逻辑完善
This commit is contained in:
26
pom.xml
26
pom.xml
@@ -352,6 +352,32 @@
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Markdown -> HTML -->
|
||||
<dependency>
|
||||
<groupId>org.commonmark</groupId>
|
||||
<artifactId>commonmark</artifactId>
|
||||
<version>0.22.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- HTML -> PDF -->
|
||||
<dependency>
|
||||
<groupId>com.openhtmltopdf</groupId>
|
||||
<artifactId>openhtmltopdf-pdfbox</artifactId>
|
||||
<version>1.0.10</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 可选:支持更复杂 CSS -->
|
||||
<dependency>
|
||||
<groupId>com.openhtmltopdf</groupId>
|
||||
<artifactId>openhtmltopdf-slf4j</artifactId>
|
||||
<version>1.0.10</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.vetti.hotake.domain.dto.HotakeJobDescriptionGeneratorDto;
|
||||
import com.vetti.hotake.domain.vo.HotakeInitialScreeningQuestionsVo;
|
||||
import com.vetti.hotake.domain.vo.HotakeJobDescriptionGeneratorVo;
|
||||
import com.vetti.hotake.domain.vo.HotakeResumeJobMatchingScoreVo;
|
||||
import com.vetti.hotake.domain.vo.HotakeWebInfoExtractVo;
|
||||
import com.vetti.hotake.service.IHotakeAiCommonToolsService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
@@ -56,7 +57,7 @@ public class HotakeAiCommonToolsController extends BaseController {
|
||||
* 简历岗位匹配度评分
|
||||
*/
|
||||
@ApiOperation("简历岗位匹配度评分")
|
||||
@GetMapping(value = "/resumeJobMatchingScore")
|
||||
@PostMapping(value = "/resumeJobMatchingScore")
|
||||
public R<?> handleResumeJobMatchingScore(@RequestBody HotakeResumeJobMatchingScoreVo scoreVo)
|
||||
{
|
||||
return R.ok(hotakeAiCommonToolsService.getResumeJobMatchingScore(scoreVo));
|
||||
@@ -82,4 +83,56 @@ public class HotakeAiCommonToolsController extends BaseController {
|
||||
return R.ok(hotakeAiCommonToolsService.getInitialQuestionEliminationScore(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理分析附件结果
|
||||
*/
|
||||
@ApiOperation("处理分析附件结果")
|
||||
@GetMapping(value = "/analyzedAttachment")
|
||||
public R<?> handleAnalyzedAttachment()
|
||||
{
|
||||
return R.ok(hotakeAiCommonToolsService.handleAnalyzedAttachment(""));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理附件分析结果合并信息
|
||||
*/
|
||||
@ApiOperation("处理附件分析结果合并信息")
|
||||
@GetMapping(value = "/attachmentResultMerging")
|
||||
public R<?> handleAttachmentResultMerging()
|
||||
{
|
||||
return R.ok(hotakeAiCommonToolsService.handleAttachmentResultMerging("",""));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成最终的Markdown格式简历
|
||||
*/
|
||||
@ApiOperation("生成最终的Markdown格式简历")
|
||||
@GetMapping(value = "/generateMarkdown")
|
||||
public R<?> handleGenerateMarkdown()
|
||||
{
|
||||
return R.ok(hotakeAiCommonToolsService.handleGenerateMarkdown(""));
|
||||
}
|
||||
|
||||
/**
|
||||
* 网站内容抓取
|
||||
*/
|
||||
@ApiOperation("网站内容抓取")
|
||||
@GetMapping(value = "/webContentScraping")
|
||||
public R<?> handleWebContentScraping()
|
||||
{
|
||||
return R.ok(hotakeAiCommonToolsService.handleWebContentScraping(""));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 网站AI信息提取(使用提示词)
|
||||
*/
|
||||
@ApiOperation("网站AI信息提取(使用提示词)")
|
||||
@PostMapping(value = "/webInfoExtract")
|
||||
public R<?> handleWebInfoExtract(@RequestBody HotakeWebInfoExtractVo webInfoExtractVo)
|
||||
{
|
||||
hotakeAiCommonToolsService.handleWebInfoExtract(webInfoExtractVo);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -111,4 +111,15 @@ public class HotakeCvInfoController extends BaseController
|
||||
return R.ok(hotakeCvInfoService.handleCvAnalysis(hotakeCvInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据其他的附件信息生成个人简历
|
||||
*/
|
||||
@ApiOperation("根据其他的附件信息生成个人简历")
|
||||
@Log(title = "根据其他的附件信息生成个人简历", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/generateResume")
|
||||
public R<HotakeCvInfo> handleGenerateResume()
|
||||
{
|
||||
return R.ok(hotakeCvInfoService.getGenerateResume());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -178,6 +178,10 @@ chatGpt:
|
||||
modelRoleCv: ft:gpt-4o-mini-2024-07-18:vetti:resume-scoring-v2:CnbgEHQQ
|
||||
modelCvJx: gpt-4o-mini
|
||||
modelCbqpf: gpt-4o-mini
|
||||
modelAafx: gpt-4o-mini
|
||||
modelAaHb: gpt-4o-mini
|
||||
modelMarkdown: gpt-4o-mini
|
||||
modelWebAiTq: gpt-4o-mini
|
||||
role: system
|
||||
|
||||
http:
|
||||
|
||||
@@ -178,6 +178,10 @@ chatGpt:
|
||||
modelRoleCv: ft:gpt-4o-mini-2024-07-18:vetti:resume-scoring-v2:CnbgEHQQ
|
||||
modelCvJx: gpt-4o-mini
|
||||
modelCbqpf: gpt-4o-mini
|
||||
modelAafx: gpt-4o-mini
|
||||
modelAaHb: gpt-4o-mini
|
||||
modelMarkdown: gpt-4o-mini
|
||||
modelWebAiTq: gpt-4o-mini
|
||||
role: system
|
||||
|
||||
http:
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
<artifactId>swagger-models</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
@@ -210,6 +216,27 @@
|
||||
<artifactId>jsoup</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Markdown -> HTML -->
|
||||
<dependency>
|
||||
<groupId>org.commonmark</groupId>
|
||||
<artifactId>commonmark</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- HTML -> PDF -->
|
||||
<dependency>
|
||||
<groupId>com.openhtmltopdf</groupId>
|
||||
<artifactId>openhtmltopdf-pdfbox</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 可选:支持更复杂 CSS -->
|
||||
<dependency>
|
||||
<groupId>com.openhtmltopdf</groupId>
|
||||
<artifactId>openhtmltopdf-slf4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -57,6 +57,18 @@ public class ChatGPTClient {
|
||||
@Value("${chatGpt.modelCbqpf}")
|
||||
private String modelCbqpf;
|
||||
|
||||
@Value("${chatGpt.modelAafx}")
|
||||
private String modelAnalyzedAttachment;
|
||||
|
||||
@Value("${chatGpt.modelAaHb}")
|
||||
private String modelAaHb;
|
||||
|
||||
@Value("${chatGpt.modelMarkdown}")
|
||||
private String modelMarkdown;
|
||||
|
||||
@Value("${chatGpt.modelWebAiTq}")
|
||||
private String modelWebAiTq;
|
||||
|
||||
@Value("${chatGpt.role}")
|
||||
private String role;
|
||||
|
||||
@@ -93,9 +105,22 @@ public class ChatGPTClient {
|
||||
}else if("ROLECV".equals(type)){
|
||||
resultText = sendMessage(promptText, modelRoleCv,objectMapper,client,role);
|
||||
}else if("CVJX".equals(type)){
|
||||
//简历解析
|
||||
resultText = sendMessage(promptText, modelCvJx,objectMapper,client,role);
|
||||
}else if("CBQPF".equals(type)){
|
||||
resultText = sendMessage(promptText, modelCbqpf,objectMapper,client,role);
|
||||
}else if("AAFX".equals(type)){
|
||||
//附件分析
|
||||
resultText = sendMessage(promptText, modelAnalyzedAttachment,objectMapper,client,role);
|
||||
}else if("AAHB".equals(type)){
|
||||
//附件分析信息合并
|
||||
resultText = sendMessage(promptText, modelAaHb,objectMapper,client,role);
|
||||
}else if("MARKDOWN".equals(type)){
|
||||
//生成最终的Markdown格式简历
|
||||
resultText = sendMessage(promptText, modelMarkdown,objectMapper,client,role);
|
||||
}else if("WEBAITQ".equals(type)){
|
||||
//网站AI信息提取
|
||||
resultText = sendMessage(promptText, modelWebAiTq,objectMapper,client,role);
|
||||
}else {
|
||||
resultText = sendMessage(promptText, modelQuestion,objectMapper,client,role);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
package com.vetti.common.utils;
|
||||
|
||||
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class HtmlToPdfUtil {
|
||||
|
||||
public static void htmlToPdf(String html, String outputPath) throws Exception {
|
||||
try (OutputStream os = new FileOutputStream(outputPath)) {
|
||||
PdfRendererBuilder builder = new PdfRendererBuilder();
|
||||
builder.useFastMode();
|
||||
builder.withHtmlContent(html, null);
|
||||
builder.toStream(os);
|
||||
builder.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.vetti.common.utils;
|
||||
|
||||
public class MarkdownToPdfExample {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String markdown = "markdown\n" +
|
||||
"# ACE AMIO\n" +
|
||||
"\n" +
|
||||
"## Personal Information\n" +
|
||||
"- **Phone**: (+61) 475 185 310\n" +
|
||||
"- **Email**: ecaamio@gmail.com\n" +
|
||||
"- **Address**: Marsden Park, New South Wales 2765\n" +
|
||||
"\n" +
|
||||
"## Professional Summary\n" +
|
||||
"Experienced Quantity Surveyor with over 20 years in the construction and engineering industry, skilled in cost estimation, procurement, and project management. Proven ability to liaise effectively with clients, subcontractors, and suppliers to ensure project success. Proficient in using industry-standard software tools and a strong understanding of construction processes.\n" +
|
||||
"\n" +
|
||||
"## Work Experience\n" +
|
||||
"\n" +
|
||||
"### Quantity Surveyor (Pre-Contracts Team) | Ertech Pty Ltd\n" +
|
||||
"**Employment Period**: April 2023 – December 2024\n" +
|
||||
"- Prepared procurement lists, Request for Quotation letters, and tender documents to streamline project initiation.\n" +
|
||||
"- Liaised with suppliers and subcontractors for quotations using Oracle Preconstruction and OneDrive, enhancing procurement efficiency.\n" +
|
||||
"- Accurately measured quantities using Bluebeam, ensuring precise project estimations.\n" +
|
||||
"- Assisted the project team in Cost to Completion tasks, contributing to budget adherence.\n" +
|
||||
"- Maintained a comprehensive directory of suppliers and subcontractors, improving sourcing efficiency.\n" +
|
||||
"\n" +
|
||||
"### Estimator (Cost Engineering Team) | MIEngineers (Leckring Pty Ltd)\n" +
|
||||
"**Employment Period**: December 2017 – March 2023\n" +
|
||||
"- Communicated with suppliers and subcontractors for materials quotations, comparing and analyzing tenders to identify the best value options.\n" +
|
||||
"- Engaged with clients for additional information and clarification, ensuring all project requirements were met.\n" +
|
||||
"- Measured quantities accurately using On-Screen Takeoff and Bluebeam, supporting detailed costing.\n" +
|
||||
"- Determined pay items and prepared a Schedule of Rates, facilitating clear financial planning.\n" +
|
||||
"- Developed first principles estimates using Benchmark Estimating Software, enhancing accuracy in financial proposals.\n" +
|
||||
"\n" +
|
||||
"### Senior Quantity Surveyor (Project / Site Team) | BEWG International Pte. Ltd.\n" +
|
||||
"**Employment Period**: May 2015 – June 2016\n" +
|
||||
"- Liaised with subcontractors for quotations, analyzed tenders, and conducted interviews, driving competitive pricing.\n" +
|
||||
"- Prepared evaluation forms for subcontractors, ensuring compliance with project standards and management approval.\n" +
|
||||
"- Developed subcontract documents, including bills of quantities, to clarify project expectations.\n" +
|
||||
"- Conducted site visits to monitor work progress and ensure alignment with project schedules.\n" +
|
||||
"- Verified the accuracy of EPC contractor and subcontractor payment applications, maintaining financial integrity.\n" +
|
||||
"\n" +
|
||||
"### Quantity Surveyor (Project / Site Team) | Koh Brothers Building & Civil Engineering Contractor (Pte.) Ltd\n" +
|
||||
"**Employment Period**: June 2010 – August 2014\n" +
|
||||
"- Coordinated with subcontractors for quotations and analyzed tenders, streamlining procurement processes.\n" +
|
||||
"- Prepared evaluation forms for subcontractors, supporting informed decision-making.\n" +
|
||||
"- Developed subcontract documents, including bills of quantities, to facilitate project execution.\n" +
|
||||
"- Regularly visited sites to communicate with the site team and monitor subcontract performance.\n" +
|
||||
"\n" +
|
||||
"### Quantity Surveyor (Project / Site Team) | Shimizu Corporation\n" +
|
||||
"**Employment Period**: June 2007 – June 2008\n" +
|
||||
"- Liaised with subcontractors for quotations and analyzed tenders to ensure competitive pricing.\n" +
|
||||
"- Collaborated with the planning department and subcontractors for necessary drawings, supporting project clarity.\n" +
|
||||
"- Conducted daily site visits to monitor progress and maintain communication with the site team.\n" +
|
||||
"- Prepared and submitted progress and variation claims, ensuring accurate financial reporting.\n" +
|
||||
"\n" +
|
||||
"### Quantity Surveyor (Building Materials Group Tender Department) | Hong Leong Asia Ltd\n" +
|
||||
"**Employment Period**: March 2006 – June 2007\n" +
|
||||
"- Engaged with clients to gather necessary project documents, ensuring all requirements were addressed.\n" +
|
||||
"- Measured quantities for individual projects, providing accurate estimates for bidding.\n" +
|
||||
"- Prepared client quotations and verified quantities for projects nearing completion, ensuring financial accuracy.\n" +
|
||||
"\n" +
|
||||
"### Site Engineer cum Quantity Surveyor | Floor Finish – Floor Level (FF-FL), Inc.\n" +
|
||||
"**Employment Period**: August 2003 – December 2005\n" +
|
||||
"- Supervised civil and structural works, ensuring adherence to project specifications.\n" +
|
||||
"- Prepared activity reports and site diaries to document progress.\n" +
|
||||
"- Coordinated with the main contractor to align project activities.\n" +
|
||||
"\n" +
|
||||
"## Education\n" +
|
||||
"\n" +
|
||||
"### Bachelor of Science in Civil Engineering | Mapua Institute of Technology, Philippines\n" +
|
||||
"**Graduation Date**: March 2002\n" +
|
||||
"\n" +
|
||||
"## Skills & Certifications\n" +
|
||||
"\n" +
|
||||
"### Professional Skills\n" +
|
||||
"- Microsoft Office: Advanced\n" +
|
||||
"- MS Project: Advanced\n" +
|
||||
"- Primavera 6.0: Intermediate\n" +
|
||||
"- AutoCAD: Intermediate\n" +
|
||||
"- Adobe Acrobat: Advanced\n" +
|
||||
"- Bluebeam: Advanced\n" +
|
||||
"- On-Screen Takeoff: Advanced\n" +
|
||||
"- Benchmark Estimating Software: Advanced\n" +
|
||||
"- COINS: Intermediate\n" +
|
||||
"- Xero: Intermediate\n" +
|
||||
"- 12d Model: Intermediate\n" +
|
||||
"- OpenRoads Designer: Intermediate\n" +
|
||||
"- Foxit: Advanced\n" +
|
||||
"\n" +
|
||||
"### Professional Certifications\n" +
|
||||
"- General Safety Induction Course – White Card (TAFE Queensland, Australia) | February 2017\n" +
|
||||
"- CostX 6.0 Introductory Training (Exactal Technologies Pty Ltd, Australia) | October 2016\n" +
|
||||
"\n" +
|
||||
"## Additional Information\n" +
|
||||
"\n" +
|
||||
"### Professional Affiliations\n" +
|
||||
"- Australian Cost Engineering Society | 2022 – Present\n" +
|
||||
"- Registered Professional Civil Engineer (Philippines) | 2003 – Present\n" +
|
||||
"- Philippine Institute of Civil Engineers | 2000 – Present\n" +
|
||||
"\n" +
|
||||
"### References\n" +
|
||||
"- **Sri Somanchi** \n" +
|
||||
" Position: Business Development Manager \n" +
|
||||
" Company: Abergeldie Complex Infrastructure \n" +
|
||||
" Phone: (+61) 413 334 849 \n" +
|
||||
" Address: 5 George Young Street, Regents Park NSW 2143 \n" +
|
||||
"\n" +
|
||||
"- **Jensen Enriquez** \n" +
|
||||
" Position: Senior Estimator \n" +
|
||||
" Company: Melhem Civil Pty Ltd \n" +
|
||||
" Phone: (+61) 420 395 454 \n" +
|
||||
" Address: 4/18-30 Pindari Road, Peakhurst Heights NSW 2210 ";
|
||||
|
||||
String html = MarkdownUtil.markdownToHtml(markdown);
|
||||
|
||||
// 可注入 CSS
|
||||
html = """
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: SimSun; }
|
||||
h1 { color: #2c3e50; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
""" + html + """
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
|
||||
HtmlToPdfUtil.htmlToPdf(html, "/Users/wangxiangshun/Desktop/临时文件/110/output.pdf");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.vetti.common.utils;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
import org.commonmark.parser.Parser;
|
||||
import org.commonmark.renderer.html.HtmlRenderer;
|
||||
|
||||
public class MarkdownUtil {
|
||||
|
||||
public static String markdownToHtml(String markdown) {
|
||||
Parser parser = Parser.builder().build();
|
||||
Node document = parser.parse(markdown);
|
||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
||||
return renderer.render(document);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.vetti.common.utils;
|
||||
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
public class MultipartFileUtil {
|
||||
|
||||
public static MultipartFile fileToMultipartFile(File file) throws Exception {
|
||||
try (FileInputStream inputStream = new FileInputStream(file)) {
|
||||
return new MockMultipartFile(
|
||||
"file", // 表单字段名
|
||||
file.getName(), // 原始文件名
|
||||
"application/octet-stream", // Content-Type
|
||||
inputStream
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.vetti.common.utils.html;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.nodes.TextNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* OkHttp第三方库读取网页HTML内容
|
||||
*/
|
||||
public class ReadHtmlByOkHttp {
|
||||
|
||||
// 预编译空白清理正则
|
||||
private static final Pattern BLANK_LINE_PATTERN = Pattern.compile("\\n\\s*\\n");
|
||||
private static final Pattern MULTIPLE_SPACE_PATTERN = Pattern.compile("\\s+");
|
||||
|
||||
|
||||
// 全局OkHttpClient实例(推荐单例,避免重复创建连接池)
|
||||
private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder()
|
||||
.connectTimeout(5, TimeUnit.SECONDS) // 连接超时
|
||||
.readTimeout(5, TimeUnit.SECONDS) // 读取超时
|
||||
.writeTimeout(5, TimeUnit.SECONDS) // 写入超时
|
||||
.build();
|
||||
|
||||
public static void main(String[] args) {
|
||||
String websiteUrl = "https://www.shearwatercn.com/";
|
||||
try {
|
||||
String htmlContent = getHtmlContentByOkHttp(websiteUrl);
|
||||
String resultStr = removeHtmlTagAndAttrByJsoup(htmlContent);
|
||||
String resultStr1 = removeBlankLineAndSpace(resultStr);
|
||||
System.out.println("读取到的HTML内容:\n" + resultStr1);
|
||||
} catch (IOException e) {
|
||||
System.err.println("读取HTML失败:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OkHttp获取网页HTML内容
|
||||
* @param urlStr 网站链接
|
||||
* @return 网页HTML字符串
|
||||
* @throws IOException 网络IO异常
|
||||
*/
|
||||
public static String getHtmlContentByOkHttp(String urlStr) throws IOException {
|
||||
// 1. 构建GET请求
|
||||
Request request = new Request.Builder()
|
||||
.url(urlStr)
|
||||
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
|
||||
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||
.header("Referer", urlStr)
|
||||
.get() // 默认就是GET请求,可省略
|
||||
.build();
|
||||
|
||||
// 2. 执行请求并获取响应(try-with-resources自动关闭Response)
|
||||
try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
|
||||
// 3. 检查响应是否成功
|
||||
if (!response.isSuccessful()) {
|
||||
throw new IOException("请求失败,响应码:" + response.code());
|
||||
}
|
||||
|
||||
// 4. 读取响应体内容(string()方法默认使用UTF-8编码,避免乱码)
|
||||
return response.body() != null ? response.body().string() : "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSoup仅去除HTML标签及属性,保留原始文本(不修改转义字符、空白字符)
|
||||
* @param htmlContent 原始HTML字符串
|
||||
* @return 剥离标签和属性后的文本
|
||||
*/
|
||||
public static String removeHtmlTagAndAttrByJsoup(String htmlContent) {
|
||||
if (htmlContent == null || htmlContent.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 步骤1:解析HTML为Document对象(自动修复畸形HTML标签)
|
||||
Document document = Jsoup.parse(htmlContent);
|
||||
// 可选:删除HTML注释(若需保留注释可注释此行)
|
||||
// Elements comments = document.select("comment()");
|
||||
// comments.remove();
|
||||
|
||||
// 步骤2:递归遍历所有节点,仅提取文本节点的内容
|
||||
StringBuilder textBuilder = new StringBuilder();
|
||||
traverseNodes(document.body(), textBuilder);
|
||||
|
||||
return textBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归遍历节点,提取纯文本(跳过标签和属性,仅保留文本节点内容)
|
||||
*/
|
||||
private static void traverseNodes(Node node, StringBuilder textBuilder) {
|
||||
// 若是文本节点,直接追加内容(保留原始格式)
|
||||
if (node instanceof TextNode) {
|
||||
TextNode textNode = (TextNode) node;
|
||||
textBuilder.append(textNode.getWholeText()); // getWholeText()保留原始文本,不做任何处理
|
||||
return;
|
||||
}
|
||||
|
||||
// 若是元素节点(标签),跳过标签本身和属性,递归遍历子节点
|
||||
if (node instanceof Element) {
|
||||
for (Node childNode : node.childNodes()) {
|
||||
traverseNodes(childNode, textBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除空白行和多余空格,生成整洁文本
|
||||
*/
|
||||
public static String removeBlankLineAndSpace(String text) {
|
||||
if (text == null || text.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 步骤1:清理空白行(多次替换确保所有连续空白行都被清理)
|
||||
String noBlankLine = BLANK_LINE_PATTERN.matcher(text).replaceAll("\n");
|
||||
while (BLANK_LINE_PATTERN.matcher(noBlankLine).find()) {
|
||||
noBlankLine = BLANK_LINE_PATTERN.matcher(noBlankLine).replaceAll("\n");
|
||||
}
|
||||
|
||||
// 步骤2:合并多余空格为单个空格
|
||||
String noMultipleSpace = MULTIPLE_SPACE_PATTERN.matcher(noBlankLine).replaceAll(" ");
|
||||
|
||||
// 步骤3:去除首尾空白
|
||||
return noMultipleSpace.trim();
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,9 @@ public class HotakeCvInfo extends BaseEntity
|
||||
@ApiModelProperty("简历分析结果数据存储")
|
||||
private String cvOptimizeJson;
|
||||
|
||||
@ApiModelProperty("分析附件结果Json")
|
||||
private String analyzedAttachmentJson;
|
||||
|
||||
@ApiModelProperty("文本修正的数量")
|
||||
private Integer textCorrectionsNums;
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.vetti.hotake.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 网站信息提取 请求对象
|
||||
*
|
||||
* @author ID
|
||||
* @date 2025-09-06
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class HotakeWebInfoExtractVo {
|
||||
|
||||
@ApiModelProperty("网站地址")
|
||||
private String webUrl;
|
||||
|
||||
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.vetti.hotake.domain.dto.HotakeJobDescriptionGeneratorDto;
|
||||
import com.vetti.hotake.domain.vo.HotakeInitialQuestionEliminationScoreVo;
|
||||
import com.vetti.hotake.domain.vo.HotakeInitialScreeningQuestionsVo;
|
||||
import com.vetti.hotake.domain.vo.HotakeResumeJobMatchingScoreVo;
|
||||
import com.vetti.hotake.domain.vo.HotakeWebInfoExtractVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,14 +26,11 @@ public interface IHotakeAiCommonToolsService {
|
||||
**/
|
||||
public HotakeJobDescriptionGeneratorDto getJobDescriptionGenerator(Long roleId);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 初筛问题生成
|
||||
**/
|
||||
public List<HotakeInitialScreeningQuestionsInfo> getInitialScreeningQuestionsGenerator(HotakeInitialScreeningQuestionsVo questionsVo);
|
||||
|
||||
|
||||
/**
|
||||
* 简历岗位匹配度评分
|
||||
**/
|
||||
@@ -43,10 +41,57 @@ public interface IHotakeAiCommonToolsService {
|
||||
**/
|
||||
public HotakeCvOptimizeDto getResumeAnalysisOptimizer(String cvConnect);
|
||||
|
||||
|
||||
/**
|
||||
* 初步筛选问题淘汰评分
|
||||
**/
|
||||
public HotakeInitialQuestionEliminationScoreDto getInitialQuestionEliminationScore(HotakeInitialQuestionEliminationScoreVo questionEliminationScoreVo);
|
||||
|
||||
/**
|
||||
* 处理分析附件结果
|
||||
* @param connect
|
||||
* @return
|
||||
*/
|
||||
public String handleAnalyzedAttachment(String connect);
|
||||
|
||||
|
||||
/**
|
||||
* 处理附件分析结果合并信息
|
||||
* @param basicInformation 基础信息
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
public String handleAttachmentResultMerging(String basicInformation,String attachmentContent);
|
||||
|
||||
|
||||
/**
|
||||
* 生成最终的Markdown格式简历
|
||||
* @param markdown
|
||||
* @return
|
||||
*/
|
||||
public String handleGenerateMarkdown(String markdown);
|
||||
|
||||
/**
|
||||
* 网站内容抓取
|
||||
* @param webUrl
|
||||
* @return
|
||||
*/
|
||||
public String handleWebContentScraping(String webUrl);
|
||||
|
||||
/**
|
||||
* 网站AI信息提取(使用提示词)
|
||||
* @param webInfoExtractVo 网站提取对象
|
||||
* @return
|
||||
*/
|
||||
public String handleWebInfoExtract(HotakeWebInfoExtractVo webInfoExtractVo);
|
||||
|
||||
/**
|
||||
* 网站信息增强处理
|
||||
* @param webContent 网站内容
|
||||
* @return
|
||||
*/
|
||||
public String handleWebInfoEnhancedProcessing(String webContent);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -87,4 +87,13 @@ public interface IHotakeCvInfoService
|
||||
public HotakeCvInfo handleCvAnalysis(HotakeCvInfo hotakeCvInfo);
|
||||
|
||||
|
||||
/**
|
||||
* 根据其他的附件信息生成个人简历
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public HotakeCvInfo getGenerateResume();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@ package com.vetti.hotake.service.impl;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.vetti.common.ai.gpt.ChatGPTClient;
|
||||
import com.vetti.common.constant.AiCommonPromptConstants;
|
||||
import com.vetti.common.core.domain.entity.SysUser;
|
||||
import com.vetti.common.core.service.BaseServiceImpl;
|
||||
import com.vetti.common.utils.SecurityUtils;
|
||||
import com.vetti.common.utils.html.ReadHtmlByOkHttp;
|
||||
import com.vetti.hotake.domain.HotakeInitScreQuestionsReplyRecordInfo;
|
||||
import com.vetti.hotake.domain.HotakeInitialScreeningQuestionsInfo;
|
||||
import com.vetti.hotake.domain.HotakeRolesInfo;
|
||||
@@ -18,14 +22,18 @@ import com.vetti.hotake.domain.dto.roleDto.ResponsibilitiesDto;
|
||||
import com.vetti.hotake.domain.vo.HotakeInitialQuestionEliminationScoreVo;
|
||||
import com.vetti.hotake.domain.vo.HotakeInitialScreeningQuestionsVo;
|
||||
import com.vetti.hotake.domain.vo.HotakeResumeJobMatchingScoreVo;
|
||||
import com.vetti.hotake.domain.vo.HotakeWebInfoExtractVo;
|
||||
import com.vetti.hotake.mapper.HotakeInitialScreeningQuestionsInfoMapper;
|
||||
import com.vetti.hotake.mapper.HotakeRolesApplyInfoMapper;
|
||||
import com.vetti.hotake.mapper.HotakeRolesInfoMapper;
|
||||
import com.vetti.hotake.service.IHotakeAiCommonToolsService;
|
||||
import com.vetti.system.mapper.SysUserMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.catalina.security.SecurityUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -52,6 +60,9 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
|
||||
@Autowired
|
||||
private ChatGPTClient chatGPTClient;
|
||||
|
||||
@Autowired
|
||||
private SysUserMapper userMapper;
|
||||
|
||||
|
||||
/**
|
||||
* 职位描述生成器
|
||||
@@ -65,7 +76,7 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
|
||||
HotakeJobDescriptionGeneratorDto generatorDto = new HotakeJobDescriptionGeneratorDto();
|
||||
//根据岗位ID查询岗位基本信息
|
||||
HotakeRolesInfo rolesInfo = hotakeRolesInfoMapper.selectHotakeRolesInfoById(roleId);
|
||||
String prompt = initializationJobDescriptionPrompt(rolesInfo.getRoleName(),rolesInfo.getRoleType(),
|
||||
String prompt = AiCommonPromptConstants.initializationJobDescriptionPrompt(rolesInfo.getRoleName(),rolesInfo.getRoleType(),
|
||||
rolesInfo.getRequiredSkillsJson());
|
||||
List<Map<String, String>> list = new LinkedList();
|
||||
Map<String, String> mapEntity = new HashMap<>();
|
||||
@@ -133,7 +144,7 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
|
||||
List<HotakeInitialScreeningQuestionsInfo> questionsInfoList = new ArrayList<>();
|
||||
//根据岗位ID查询岗位基本信息
|
||||
HotakeRolesInfo rolesInfo = hotakeRolesInfoMapper.selectHotakeRolesInfoById(questionsVo.getRoleId());
|
||||
String prompt = initializationInitialScreeningQuestionsPrompt(rolesInfo.getRoleName(),rolesInfo.getAboutRole(),
|
||||
String prompt = AiCommonPromptConstants.initializationInitialScreeningQuestionsPrompt(rolesInfo.getRoleName(),rolesInfo.getAboutRole(),
|
||||
rolesInfo.getResponsibilities());
|
||||
List<Map<String, String>> list = new LinkedList();
|
||||
Map<String, String> mapEntity = new HashMap<>();
|
||||
@@ -180,7 +191,7 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
|
||||
@Override
|
||||
public String getResumeJobMatchingScore(HotakeResumeJobMatchingScoreVo scoreVo) {
|
||||
//查询岗位信息
|
||||
String prompt = initializationResumeJobMatchingScorePrompt(scoreVo.getJobTitle(),scoreVo.getJobDescription(),
|
||||
String prompt = AiCommonPromptConstants.initializationResumeJobMatchingScorePrompt(scoreVo.getJobTitle(),scoreVo.getJobDescription(),
|
||||
scoreVo.getKeyResponsibilities(),scoreVo.getJobRequirements(),scoreVo.getPersonalInformation(),
|
||||
scoreVo.getWorkHistory(),scoreVo.getProjectExperience(),scoreVo.getSkillsList(),
|
||||
scoreVo.getEducationalBackground(),scoreVo.getOtherRelevantInformation());
|
||||
@@ -205,7 +216,7 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
|
||||
@Override
|
||||
public HotakeCvOptimizeDto getResumeAnalysisOptimizer(String cvConnect) {
|
||||
//传输简历内容,返回简历分析结果
|
||||
String prompt = initializationResumeAnalysisOptimizerPrompt();
|
||||
String prompt = AiCommonPromptConstants.initializationResumeAnalysisOptimizerPrompt();
|
||||
List<Map<String, String>> list = new LinkedList();
|
||||
Map<String, String> mapEntity = new HashMap<>();
|
||||
mapEntity.put("role", "system");
|
||||
@@ -435,7 +446,7 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
|
||||
List<HotakeInitialScreeningQuestionsInfo> screeningQuestionsInfoList = hotakeInitialScreeningQuestionsInfoMapper.selectHotakeInitialScreeningQuestionsInfoList(queryQuestionsInfo);
|
||||
|
||||
//提示词处理
|
||||
String prompt = initializationQuestionEliminationScorePrompt(questionEliminationScoreVo.getRoleName());
|
||||
String prompt = AiCommonPromptConstants.initializationQuestionEliminationScorePrompt(questionEliminationScoreVo.getRoleName());
|
||||
List<Map<String, String>> list = new LinkedList();
|
||||
Map<String, String> mapEntity = new HashMap<>();
|
||||
mapEntity.put("role", "system");
|
||||
@@ -467,680 +478,171 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
|
||||
return dto;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化职位描述生成器提示词
|
||||
* @param jobTitle Specific position 岗位
|
||||
* @param industry Industry sector 行业领域
|
||||
* @param coreRequirements Key skills or experience requirements 所需技能或经验
|
||||
* 处理分析附件结果
|
||||
* @param connect
|
||||
* @return
|
||||
*/
|
||||
private String initializationJobDescriptionPrompt(String jobTitle,String industry,String coreRequirements){
|
||||
String prompt = "You are a senior HR expert and recruitment copywriter specializing in optimizing and generating attractive job descriptions.\n" +
|
||||
"\n" +
|
||||
"Core Task:\n" +
|
||||
"Generate standardized, professional, and candidate-attractive job description copy based on user-provided \"job title, industry, and core requirements.\"\n" +
|
||||
"\n" +
|
||||
"Input Format:\n" +
|
||||
"- Job Title: ["+jobTitle+"]\n" +
|
||||
"- Industry: ["+industry+"]\n" +
|
||||
"- Core Requirements: ["+coreRequirements+"]\n" +
|
||||
"\n" +
|
||||
"Output Structure:\n" +
|
||||
"1. Job Description\n" +
|
||||
"2. Key Responsibilities\n" +
|
||||
"3. Requirements\n" +
|
||||
"\n" +
|
||||
"Writing Principles:\n" +
|
||||
"1. Professional yet approachable language, avoiding overly formal or rigid tone\n" +
|
||||
"2. Highlight career development prospects and value\n" +
|
||||
"3. Specific and clear requirements, avoiding vague statements\n" +
|
||||
"4. Reflect company culture and team atmosphere\n" +
|
||||
"5. Use industry-standard terminology to ensure professionalism\n" +
|
||||
"\n" +
|
||||
"Job Description Guidelines:\n" +
|
||||
"- Concisely summarize the core value and significance of the position\n" +
|
||||
"- Highlight the position's importance within the company/team\n" +
|
||||
"- Emphasize career development opportunities\n" +
|
||||
"- Keep length between 100-150 words\n" +
|
||||
"\n" +
|
||||
"Key Responsibilities Guidelines:\n" +
|
||||
"- List 5-8 specific responsibilities\n" +
|
||||
"- Arrange by importance\n" +
|
||||
"- Start with action verbs, be specific\n" +
|
||||
"- Reflect work challenges and growth opportunities\n" +
|
||||
"\n" +
|
||||
"Requirements Guidelines:\n" +
|
||||
"- Separate into essential requirements and preferred qualifications\n" +
|
||||
"- Include four dimensions: education, experience, skills, and qualities\n" +
|
||||
"- Reasonable requirements, avoid excessive barriers\n" +
|
||||
"- Reflect position-specific needs\n" +
|
||||
"\n" +
|
||||
"Target Industry Adaptation:\n" +
|
||||
"\n" +
|
||||
"### Construction Industry\n" +
|
||||
"- Core Keywords: Safety first, quality control, project management, teamwork\n" +
|
||||
"- Key Requirements: Safety awareness, field experience, technical qualifications, stress resistance\n" +
|
||||
"- Development Path: Technical specialization, project management, safety management, quality control\n" +
|
||||
"\n" +
|
||||
"### Logistics Industry\n" +
|
||||
"- Core Keywords: Efficiency optimization, cost control, supply chain management, customer service\n" +
|
||||
"- Key Requirements: Time management, communication coordination, problem solving, strong responsibility\n" +
|
||||
"- Development Path: Operations optimization, management advancement, professional certification, cross-departmental development\n" +
|
||||
"\n" +
|
||||
"### Manufacturing Industry\n" +
|
||||
"- Core Keywords: Production efficiency, quality standards, safe operations, continuous improvement\n" +
|
||||
"- Key Requirements: Technical skills, safety awareness, teamwork, learning ability\n" +
|
||||
"- Development Path: Skill enhancement, team leader, supervisor, professional technical route\n" +
|
||||
"\n" +
|
||||
"### Hospitality Industry\n" +
|
||||
"- Core Keywords: Customer service, teamwork, flexibility, professional image\n" +
|
||||
"- Key Requirements: Service mindset, communication skills, stress resistance, language abilities\n" +
|
||||
"- Development Path: Service specialization, management training, department supervisor, hotel management\n" +
|
||||
"\n" +
|
||||
"### Mining Industry\n" +
|
||||
"- Core Keywords: Safe operations, environmental protection, technical standards, teamwork\n" +
|
||||
"- Key Requirements: Safety awareness, physical requirements, technical qualifications, emergency response\n" +
|
||||
"- Development Path: Technical certification, safety management, equipment expertise, field management\n" +
|
||||
"\n" +
|
||||
"Language Style:\n" +
|
||||
"- Practical and down-to-earth, close to frontline work\n" +
|
||||
"- Emphasize the importance of safety and quality\n" +
|
||||
"- Highlight career development and skill enhancement opportunities\n" +
|
||||
"- Emphasize teamwork and responsibility\n" +
|
||||
"- Avoid overly technical expressions, use clear and understandable language\n" +
|
||||
"\n" +
|
||||
"Output Format Requirements:\n" +
|
||||
"Must return standard JSON format with the following structure:\n" +
|
||||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
" \"job_description\": {\n" +
|
||||
" \"overview\": \"Concise description of position value and significance\",\n" +
|
||||
" \"development_prospects\": \"Career development prospects description\"\n" +
|
||||
" },\n" +
|
||||
" \"key_responsibilities\": [\n" +
|
||||
" {\n" +
|
||||
" \"id\": 1,\n" +
|
||||
" \"responsibility\": \"Specific responsibility description\",\n" +
|
||||
" \"priority\": \"high|medium|low\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"requirements\": {\n" +
|
||||
" \"essential\": [\n" +
|
||||
" {\n" +
|
||||
" \"category\": \"education|experience|skills|qualities\",\n" +
|
||||
" \"requirement\": \"Specific requirement description\",\n" +
|
||||
" \"details\": \"Detailed explanation\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"preferred\": [\n" +
|
||||
" {\n" +
|
||||
" \"category\": \"education|experience|skills|qualities\", \n" +
|
||||
" \"requirement\": \"Preferred condition description\",\n" +
|
||||
" \"details\": \"Detailed explanation\"\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"company_culture\": \"Company culture and team atmosphere description\",\n" +
|
||||
" \"industry_focus\": \"Industry characteristics and focus\",\n" +
|
||||
" \"total_responsibilities\": 6,\n" +
|
||||
" \"total_essential_requirements\": 4,\n" +
|
||||
" \"total_preferred_requirements\": 3\n" +
|
||||
"}\n" +
|
||||
"```\n" +
|
||||
"\n" +
|
||||
"Important Notes:\n" +
|
||||
"- Ensure content complies with labor laws\n" +
|
||||
"- Avoid discriminatory language\n" +
|
||||
"- Maintain content timeliness and accuracy\n" +
|
||||
"- Adjust requirement difficulty based on different levels";
|
||||
|
||||
|
||||
return prompt;
|
||||
@Override
|
||||
public String handleAnalyzedAttachment(String connect) {
|
||||
//处理提示词数据
|
||||
String prompt = AiCommonPromptConstants.initializationAnalyzedAttachmentPrompt();
|
||||
List<Map<String, String>> list = new LinkedList();
|
||||
Map<String, String> mapEntity = new HashMap<>();
|
||||
mapEntity.put("role", "system");
|
||||
mapEntity.put("content",prompt);
|
||||
list.add(mapEntity);
|
||||
Map<String, String> mapUserEntity = new HashMap<>();
|
||||
mapUserEntity.put("role", "user");
|
||||
mapUserEntity.put("content",connect);
|
||||
list.add(mapUserEntity);
|
||||
String promptJson = JSONUtil.toJsonStr(list);
|
||||
String resultStr = chatGPTClient.handleAiChat(promptJson,"AAFX");
|
||||
String resultJson = resultStr.replaceAll("```json","").replaceAll("```","");
|
||||
return resultJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化初筛问题生成提示词
|
||||
* @param jobTitle 岗位
|
||||
* @param jobRequirements 职位要求
|
||||
* @param screeningFocus 筛选重点
|
||||
* 处理附件分析结果合并信息
|
||||
* @param basicInformation 基础信息
|
||||
* @param attachmentContent 所有附件信息
|
||||
* @return
|
||||
*/
|
||||
private String initializationInitialScreeningQuestionsPrompt(String jobTitle,String jobRequirements,
|
||||
String screeningFocus){
|
||||
String prompt = "You are an experienced recruitment expert specializing in designing effective screening questions to quickly identify suitable candidates.\n" +
|
||||
"\n" +
|
||||
"Core Task:\n" +
|
||||
"Generate targeted and easily assessable screening questions based on job requirements and specified question types to help HR quickly filter candidates.\n" +
|
||||
"\n" +
|
||||
"Input Information:\n" +
|
||||
"- Job Title: ["+jobTitle+"]\n" +
|
||||
"- Job Requirements: ["+jobRequirements+"]\n" +
|
||||
"- Screening Focus: ["+screeningFocus+"]\n" +
|
||||
"- Question Type: [Specified question type to generate]\n" +
|
||||
"\n" +
|
||||
"Supported Question Types:\n" +
|
||||
"\n" +
|
||||
"### 1. Single Choice (Radio) - Single Selection\n" +
|
||||
"- Use Cases: Years of experience, education level, work status, etc.\n" +
|
||||
"- Features: Only one option can be selected, convenient for quick screening\n" +
|
||||
"- Examples: Work experience years, highest education, current work status\n" +
|
||||
"\n" +
|
||||
"### 2. Multiple Choices (Checkbox) - Multiple Selection\n" +
|
||||
"- Use Cases: Skills mastery, tool usage, certifications, etc.\n" +
|
||||
"- Features: Multiple options can be selected, comprehensive understanding of candidate abilities\n" +
|
||||
"- Examples: Programming languages mastered, tools used, certifications held\n" +
|
||||
"\n" +
|
||||
"### 3. Rating Scale (1-5) - Rating Questions\n" +
|
||||
"- Use Cases: Skill proficiency, experience richness, self-assessment, etc.\n" +
|
||||
"- Features: 1-5 point scale, convenient for quantitative comparison\n" +
|
||||
"- Examples: Skill proficiency level, project experience richness, communication ability self-assessment\n" +
|
||||
"\n" +
|
||||
"### 4. Short Text Answer - Open-ended Questions\n" +
|
||||
"- Use Cases: Project experience description, career planning, reasons for leaving, etc.\n" +
|
||||
"- Features: Requires text description, in-depth understanding of candidate situation\n" +
|
||||
"- Examples: Project experience description, career development planning, work motivation\n" +
|
||||
"\n" +
|
||||
"Question Design Principles:\n" +
|
||||
"1. Generate questions in the specified format according to the designated question type\n" +
|
||||
"2. Questions should be specific and clear, avoiding vague statements\n" +
|
||||
"3. Easy to quantify and compare for assessment\n" +
|
||||
"4. Quickly identify candidates who don't meet basic requirements\n" +
|
||||
"5. Cover key dimensions: skills, experience, attitude, etc.\n" +
|
||||
"6. Limit to 5-8 questions to avoid excessive length\n" +
|
||||
"\n" +
|
||||
"Question Type Selection Guidelines:\n" +
|
||||
"- Basic qualification screening: Prioritize Single Choice\n" +
|
||||
"- Skills and abilities assessment: Use Multiple Choices or Rating Scale\n" +
|
||||
"- In-depth experience understanding: Use Short Text Answer\n" +
|
||||
"- Degree assessment: Use Rating Scale\n" +
|
||||
"\n" +
|
||||
"Industry-Specific Questions:\n" +
|
||||
"\n" +
|
||||
"### Technology Industry\n" +
|
||||
"- Data sensitivity, user thinking, rapid learning ability\n" +
|
||||
"- Attention to and learning capability for new technologies\n" +
|
||||
"\n" +
|
||||
"### Sales Positions\n" +
|
||||
"- Sales performance, client maintenance, stress resistance\n" +
|
||||
"- Communication skills and negotiation experience\n" +
|
||||
"\n" +
|
||||
"### Technical Positions\n" +
|
||||
"- Programming languages, project experience, technical depth\n" +
|
||||
"- Ability and methods for learning new technologies\n" +
|
||||
"\n" +
|
||||
"### Operations Positions\n" +
|
||||
"- Data analysis capability, event planning experience\n" +
|
||||
"- Understanding of user growth and retention\n" +
|
||||
"\n" +
|
||||
"Output Format Requirements:\n" +
|
||||
"Must return standard JSON format with the following structure:\n" +
|
||||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
" \"questions\": [\n" +
|
||||
" {\n" +
|
||||
" \"id\": 1,\n" +
|
||||
" \"type\": \"single_choice|multiple_choices|rating_scale|short_text\",\n" +
|
||||
" \"question\": \"Question content\",\n" +
|
||||
" \"options\": [\"Option 1\", \"Option 2\", \"Option 3\"], // Only for single_choice and multiple_choices\n" +
|
||||
" \"scale\": {\n" +
|
||||
" \"min\": 1,\n" +
|
||||
" \"max\": 5,\n" +
|
||||
" \"labels\": {\n" +
|
||||
" \"1\": \"Not familiar at all\",\n" +
|
||||
" \"2\": \"Slightly familiar\", \n" +
|
||||
" \"3\": \"Moderately familiar\",\n" +
|
||||
" \"4\": \"Quite familiar\",\n" +
|
||||
" \"5\": \"Very familiar\"\n" +
|
||||
" }\n" +
|
||||
" }, // Only for rating_scale\n" +
|
||||
" \"placeholder\": \"Please briefly describe...\", // Only for short_text\n" +
|
||||
" \"required\": true,\n" +
|
||||
" \"category\": \"basic_qualification|skills_assessment|experience_verification|motivation_attitude|practical_conditions\",\n" +
|
||||
" \"scoring_criteria\": \"Scoring criteria description\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"screening_guidelines\": {\n" +
|
||||
" \"must_meet_conditions\": [\"Must-meet condition 1\", \"Must-meet condition 2\"],\n" +
|
||||
" \"bonus_points\": [\"Bonus point 1\", \"Bonus point 2\"],\n" +
|
||||
" \"red_flags\": [\"Red flag 1\", \"Red flag 2\"]\n" +
|
||||
" },\n" +
|
||||
" \"total_questions\": 5\n" +
|
||||
"}\n" +
|
||||
"```\n" +
|
||||
"\n" +
|
||||
"Important Notes:\n" +
|
||||
"1. Questions must be legal and compliant, avoiding personal privacy\n" +
|
||||
"2. Avoid discriminatory questions (age, gender, marital status, etc.)\n" +
|
||||
"3. Questions should be directly related to the position\n" +
|
||||
"4. Maintain objectivity and fairness in questions\n" +
|
||||
"5. Consider candidate response time, avoid overly complex questions\n" +
|
||||
"\n" +
|
||||
"Quality Checklist:\n" +
|
||||
"□ Are questions directly related to job requirements?\n" +
|
||||
"□ Can they effectively distinguish suitable from unsuitable candidates?\n" +
|
||||
"□ Are question statements clear and understandable?\n" +
|
||||
"□ Have discriminatory contents been avoided?\n" +
|
||||
"□ Are assessment criteria clear?";
|
||||
|
||||
|
||||
return prompt;
|
||||
@Override
|
||||
public String handleAttachmentResultMerging(String basicInformation, String attachmentContent) {
|
||||
//处理提示词数据
|
||||
String prompt = AiCommonPromptConstants.initializationAttachmentResultMergingPrompt(basicInformation,attachmentContent);
|
||||
List<Map<String, String>> list = new LinkedList();
|
||||
Map<String, String> mapEntity = new HashMap<>();
|
||||
mapEntity.put("role", "system");
|
||||
mapEntity.put("content",prompt);
|
||||
list.add(mapEntity);
|
||||
Map<String, String> mapUserEntity = new HashMap<>();
|
||||
mapUserEntity.put("role", "user");
|
||||
mapUserEntity.put("content","基本信息:"+basicInformation+"\\n分析结果:"+attachmentContent);
|
||||
list.add(mapUserEntity);
|
||||
String promptJson = JSONUtil.toJsonStr(list);
|
||||
String resultStr = chatGPTClient.handleAiChat(promptJson,"AAHB");
|
||||
String resultJson = resultStr.replaceAll("```json","").replaceAll("```","");
|
||||
log.info("处理附件分析结果合并信息:{}",resultJson);
|
||||
return resultJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化简历岗位匹配度评分提示词
|
||||
*
|
||||
* @param jobTitle 岗位
|
||||
* @param jobDescription 岗位描述
|
||||
* @param keyResponsibilities 主要职责
|
||||
* @param jobRequirements 任职要求
|
||||
* @param personalInformation 个人信息
|
||||
* @param workHistory 工作经历
|
||||
* @param projectExperience 项目经验
|
||||
* @param skillsList 技能清单
|
||||
* @param educationalBackground 教育背景
|
||||
* @param otherRelevantInformation 其他相关信息
|
||||
* 生成最终的Markdown格式简历
|
||||
* @param markdown
|
||||
* @return
|
||||
*/
|
||||
private String initializationResumeJobMatchingScorePrompt(String jobTitle,String jobDescription,
|
||||
String keyResponsibilities,String jobRequirements,
|
||||
String personalInformation,String workHistory,
|
||||
String projectExperience,String skillsList,
|
||||
String educationalBackground,String otherRelevantInformation){
|
||||
String prompt = "You are a professional resume analysis expert and recruitment consultant, specializing in evaluating the match between candidate resumes and specific job positions.\n" +
|
||||
"\n" +
|
||||
"Core Task:\n" +
|
||||
"Comprehensively analyze candidate resumes and target job requirements to provide objective and accurate job fit scoring and detailed analysis reports.\n" +
|
||||
"\n" +
|
||||
"Input Information:\n" +
|
||||
"1. Job Information:\n" +
|
||||
" - Job Title:["+jobTitle+"]\n" +
|
||||
" - Job Description:["+jobDescription+"]\n" +
|
||||
" - Key Responsibilities:["+keyResponsibilities+"]\n" +
|
||||
" - Requirements (essential and preferred qualifications):["+jobRequirements+"]\n" +
|
||||
"\n" +
|
||||
"2. Candidate Resume:\n" +
|
||||
" - Personal information (work experience, educational background, etc.):["+personalInformation+"]\n" +
|
||||
" - Work History:["+workHistory+"]\n" +
|
||||
" - Project Experience:["+projectExperience+"]\n" +
|
||||
" - Skills List:["+skillsList+"]\n" +
|
||||
" - Educational Background:["+educationalBackground+"]\n" +
|
||||
" - Other relevant information:["+otherRelevantInformation+"]\n" +
|
||||
"\n" +
|
||||
"Scoring Dimensions (Total 100 points):\n" +
|
||||
"\n" +
|
||||
"### 1. Work Experience Match (30 points)\n" +
|
||||
"- Relevant industry experience: 10 points\n" +
|
||||
"- Similar position experience: 15 points\n" +
|
||||
"- Work years alignment: 5 points\n" +
|
||||
"\n" +
|
||||
"Scoring Standards:\n" +
|
||||
"- Perfect match: Full points\n" +
|
||||
"- Highly relevant: 80-90%\n" +
|
||||
"- Partially relevant: 60-79%\n" +
|
||||
"- Slightly relevant: 40-59%\n" +
|
||||
"- Not relevant: 0-39%\n" +
|
||||
"\n" +
|
||||
"### 2. Skills and Abilities Match (25 points)\n" +
|
||||
"- Professional skills: 15 points\n" +
|
||||
"- Soft skills: 10 points\n" +
|
||||
"\n" +
|
||||
"Scoring Focus:\n" +
|
||||
"- Mastery level of essential skills\n" +
|
||||
"- Coverage of preferred skills\n" +
|
||||
"- Skill depth and breadth\n" +
|
||||
"- Demonstration of learning ability\n" +
|
||||
"\n" +
|
||||
"### 3. Educational Background Match (15 points)\n" +
|
||||
"- Education level: 8 points\n" +
|
||||
"- Major relevance: 7 points\n" +
|
||||
"\n" +
|
||||
"Considerations:\n" +
|
||||
"- Whether education level meets requirements\n" +
|
||||
"- Degree of major background relevance\n" +
|
||||
"- Continuous learning and professional development\n" +
|
||||
"\n" +
|
||||
"### 4. Project Experience Match (20 points)\n" +
|
||||
"- Project scale and complexity: 10 points\n" +
|
||||
"- Project outcomes and impact: 10 points\n" +
|
||||
"\n" +
|
||||
"Assessment Points:\n" +
|
||||
"- Relevance of projects to target position\n" +
|
||||
"- Role and responsibilities in projects\n" +
|
||||
"- Quantified project achievements\n" +
|
||||
"- Demonstration of problem-solving abilities\n" +
|
||||
"\n" +
|
||||
"### 5. Overall Quality Assessment (10 points)\n" +
|
||||
"- Career development trajectory: 5 points\n" +
|
||||
"- Stability and growth potential: 5 points\n" +
|
||||
"\n" +
|
||||
"Considerations:\n" +
|
||||
"- Logic and coherence of career development\n" +
|
||||
"- Work stability (job-hopping frequency and reasons)\n" +
|
||||
"- Growth potential and learning ability\n" +
|
||||
"- Clarity of career planning\n" +
|
||||
"\n" +
|
||||
"Output Format Requirements:\n" +
|
||||
"Must return standard JSON format with the following structure:\n" +
|
||||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
" \"overall_score\": {\n" +
|
||||
" \"total_score\": 85,\n" +
|
||||
" \"max_score\": 100,\n" +
|
||||
" \"match_level\": \"excellent|good|average|poor\",\n" +
|
||||
" \"recommendation\": \"strongly_recommend|recommend|consider_carefully|not_recommend\"\n" +
|
||||
" },\n" +
|
||||
" \"detailed_scores\": {\n" +
|
||||
" \"work_experience\": {\n" +
|
||||
" \"total\": 25,\n" +
|
||||
" \"max\": 30,\n" +
|
||||
" \"breakdown\": {\n" +
|
||||
" \"industry_experience\": {\"score\": 8, \"max\": 10, \"analysis\": \"Analysis explanation\"},\n" +
|
||||
" \"similar_position\": {\"score\": 12, \"max\": 15, \"analysis\": \"Analysis explanation\"},\n" +
|
||||
" \"work_years\": {\"score\": 5, \"max\": 5, \"analysis\": \"Analysis explanation\"}\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"skills_abilities\": {\n" +
|
||||
" \"total\": 20,\n" +
|
||||
" \"max\": 25,\n" +
|
||||
" \"breakdown\": {\n" +
|
||||
" \"professional_skills\": {\"score\": 12, \"max\": 15, \"matching_skills\": [\"Skill 1\", \"Skill 2\"], \"missing_skills\": [\"Missing skill 1\"]},\n" +
|
||||
" \"soft_skills\": {\"score\": 8, \"max\": 10, \"analysis\": \"Analysis explanation\"}\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"education\": {\n" +
|
||||
" \"total\": 12,\n" +
|
||||
" \"max\": 15,\n" +
|
||||
" \"breakdown\": {\n" +
|
||||
" \"education_level\": {\"score\": 6, \"max\": 8, \"analysis\": \"Analysis explanation\"},\n" +
|
||||
" \"major_relevance\": {\"score\": 6, \"max\": 7, \"analysis\": \"Analysis explanation\"}\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"project_experience\": {\n" +
|
||||
" \"total\": 18,\n" +
|
||||
" \"max\": 20,\n" +
|
||||
" \"relevant_projects\": [\"Project 1\", \"Project 2\"],\n" +
|
||||
" \"achievement_highlights\": [\"Achievement 1\", \"Achievement 2\"],\n" +
|
||||
" \"analysis\": \"Project experience analysis\"\n" +
|
||||
" },\n" +
|
||||
" \"overall_quality\": {\n" +
|
||||
" \"total\": 8,\n" +
|
||||
" \"max\": 10,\n" +
|
||||
" \"breakdown\": {\n" +
|
||||
" \"career_trajectory\": {\"score\": 4, \"max\": 5, \"analysis\": \"Analysis explanation\"},\n" +
|
||||
" \"stability_growth\": {\"score\": 4, \"max\": 5, \"analysis\": \"Analysis explanation\"}\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"match_highlights\": [\n" +
|
||||
" \"Outstanding matching advantage 1\",\n" +
|
||||
" \"Outstanding matching advantage 2\",\n" +
|
||||
" \"Outstanding matching advantage 3\"\n" +
|
||||
" ],\n" +
|
||||
" \"risk_points\": [\n" +
|
||||
" \"Issue requiring attention 1\",\n" +
|
||||
" \"Issue requiring attention 2\"\n" +
|
||||
" ],\n" +
|
||||
" \"interview_recommendations\": {\n" +
|
||||
" \"key_assessment_areas\": [\"Key assessment area 1\", \"Key assessment area 2\"],\n" +
|
||||
" \"critical_questions\": [\"Suggested interview question 1\", \"Suggested interview question 2\"],\n" +
|
||||
" \"skill_verification\": [\"Skill to verify 1\", \"Skill to verify 2\"]\n" +
|
||||
" },\n" +
|
||||
" \"hiring_recommendation\": {\n" +
|
||||
" \"level\": \"strongly_recommend|recommend|consider_carefully|not_recommend\",\n" +
|
||||
" \"reasoning\": \"Brief explanation of recommendation\"\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"```\n" +
|
||||
"\n" +
|
||||
"Scoring Level Descriptions:\n" +
|
||||
"- 90-100 points: Excellent match, strongly recommend\n" +
|
||||
"- 80-89 points: Good match, recommend for interview\n" +
|
||||
"- 70-79 points: Average match, consider for interview\n" +
|
||||
"- 60-69 points: Low match, consider carefully\n" +
|
||||
"- Below 60 points: Poor match, not recommended\n" +
|
||||
"\n" +
|
||||
"Important Notes:\n" +
|
||||
"1. Maintain objectivity and fairness, base assessment on facts\n" +
|
||||
"2. Avoid personal bias and discrimination\n" +
|
||||
"3. Consider candidate's growth potential\n" +
|
||||
"4. Combine company culture and team needs\n" +
|
||||
"5. Provide constructive analysis and recommendations\n" +
|
||||
"\n" +
|
||||
"Quality Check:\n" +
|
||||
"□ Is the scoring well-supported by evidence?\n" +
|
||||
"□ Is the analysis objective and comprehensive?\n" +
|
||||
"□ Are all key dimensions considered?\n" +
|
||||
"□ Are recommendations actionable?\n" +
|
||||
"□ Have discriminatory evaluations been avoided?";
|
||||
|
||||
|
||||
return prompt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化简历优化分析器提示词
|
||||
* @return
|
||||
*/
|
||||
private String initializationResumeAnalysisOptimizerPrompt(){
|
||||
String prompt = "You are a professional resume analysis and optimization expert, specializing in providing resume improvement recommendations for traditional industries including construction, logistics, manufacturing, hospitality, and mining.\n" +
|
||||
"\n" +
|
||||
"## Core Mission\n" +
|
||||
"Analyze uploaded resumes, identify issues, and provide optimization recommendations to help candidates improve resume quality and job search success rates.\n" +
|
||||
"\n" +
|
||||
"## Analysis Dimensions\n" +
|
||||
"\n" +
|
||||
"### 1. Text Correction Analysis\n" +
|
||||
"Check for the following issues:\n" +
|
||||
"- **Spelling Errors**: Identify word spelling mistakes\n" +
|
||||
"- **Grammar Errors**: Sentence structure, tense, and grammatical issues\n" +
|
||||
"- **Punctuation**: Improper or missing punctuation usage\n" +
|
||||
"- **Format Consistency**: Date formats, capitalization, abbreviation uniformity\n" +
|
||||
"- **Professional Terminology**: Accuracy of industry-specific terms\n" +
|
||||
"\n" +
|
||||
"### 2. Logic Correction Analysis\n" +
|
||||
"Check for the following issues:\n" +
|
||||
"- **Timeline Conflicts**: Overlapping or illogical dates in work/education history\n" +
|
||||
"- **Career Progression Logic**: Whether promotion paths are reasonable\n" +
|
||||
"- **Skill Matching**: Whether claimed skills match work experience\n" +
|
||||
"- **Experience Consistency**: Whether descriptions across sections are consistent\n" +
|
||||
"- **Gap Explanations**: Whether employment gaps need clarification\n" +
|
||||
"\n" +
|
||||
"### 3. Content Optimization Recommendations\n" +
|
||||
"Provide the following optimization suggestions:\n" +
|
||||
"- **Personal Summary Optimization**: Highlight core strengths and value proposition\n" +
|
||||
"- **Work Experience Optimization**: Use quantified data and specific achievements\n" +
|
||||
"- **Skills Display Optimization**: Organize by importance and relevance\n" +
|
||||
"- **Keyword Optimization**: Add industry-relevant keywords\n" +
|
||||
"- **Structure Optimization**: Improve resume layout and information hierarchy\n" +
|
||||
"\n" +
|
||||
"## Traditional Industry Focus\n" +
|
||||
"\n" +
|
||||
"### Construction Industry Priorities\n" +
|
||||
"- Safety qualifications and certifications\n" +
|
||||
"- Project scale and complexity\n" +
|
||||
"- Site management experience\n" +
|
||||
"- Quality control capabilities\n" +
|
||||
"\n" +
|
||||
"### Logistics Industry Priorities\n" +
|
||||
"- Transportation efficiency optimization\n" +
|
||||
"- Inventory management experience\n" +
|
||||
"- Customer service capabilities\n" +
|
||||
"- Cost control achievements\n" +
|
||||
"\n" +
|
||||
"### Manufacturing Industry Priorities\n" +
|
||||
"- Production efficiency improvements\n" +
|
||||
"- Quality management systems\n" +
|
||||
"- Equipment operation skills\n" +
|
||||
"- Safety operation records\n" +
|
||||
"\n" +
|
||||
"### Hospitality Industry Priorities\n" +
|
||||
"- Customer service experience\n" +
|
||||
"- Multilingual capabilities\n" +
|
||||
"- Team collaboration skills\n" +
|
||||
"- Sales performance\n" +
|
||||
"\n" +
|
||||
"### Mining Industry Priorities\n" +
|
||||
"- Safety operation qualifications\n" +
|
||||
"- Equipment operation experience\n" +
|
||||
"- Environmental adaptability\n" +
|
||||
"- Technical compliance adherence\n" +
|
||||
"\n" +
|
||||
"## Output Format\n" +
|
||||
"\n" +
|
||||
"Please strictly follow this JSON format for analysis results:\n" +
|
||||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
" \"analysis_summary\": {\n" +
|
||||
" \"overall_score\": 85,\n" +
|
||||
" \"main_strengths\": [\"Specific strength 1\", \"Specific strength 2\"],\n" +
|
||||
" \"critical_issues\": [\"Critical issue 1\", \"Critical issue 2\"],\n" +
|
||||
" \"improvement_potential\": \"high|medium|low\"\n" +
|
||||
" },\n" +
|
||||
" \"text_corrections\": {\n" +
|
||||
" \"spelling_errors\": [\n" +
|
||||
" {\n" +
|
||||
" \"original\": \"Incorrect text\",\n" +
|
||||
" \"corrected\": \"Correct text\",\n" +
|
||||
" \"location\": \"Personal summary line 2\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"grammar_errors\": [\n" +
|
||||
" {\n" +
|
||||
" \"original\": \"Grammatically incorrect sentence\",\n" +
|
||||
" \"corrected\": \"Corrected sentence\",\n" +
|
||||
" \"location\": \"Work experience item 1\",\n" +
|
||||
" \"explanation\": \"Tense inconsistency\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"formatting_issues\": [\n" +
|
||||
" {\n" +
|
||||
" \"issue\": \"Inconsistent date format\",\n" +
|
||||
" \"suggestion\": \"Use consistent MM/YYYY format\",\n" +
|
||||
" \"locations\": [\"Work Experience\", \"Education\"]\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"logic_corrections\": {\n" +
|
||||
" \"timeline_conflicts\": [\n" +
|
||||
" {\n" +
|
||||
" \"conflict\": \"Overlapping work periods\",\n" +
|
||||
" \"details\": \"2020-2022 at Company A, 2021-2023 at Company B\",\n" +
|
||||
" \"suggestion\": \"Verify and correct timeline, or explain part-time situation\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"career_progression\": [\n" +
|
||||
" {\n" +
|
||||
" \"issue\": \"Position regression\",\n" +
|
||||
" \"details\": \"From Manager to Assistant\",\n" +
|
||||
" \"suggestion\": \"Explain reason for position change or adjust description\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"skill_experience_mismatch\": [\n" +
|
||||
" {\n" +
|
||||
" \"skill\": \"Project Management\",\n" +
|
||||
" \"issue\": \"Claims 5 years experience but work history shows only 2 years\",\n" +
|
||||
" \"suggestion\": \"Adjust skill description or add relevant experience\"\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"optimization_suggestions\": {\n" +
|
||||
" \"personal_summary\": {\n" +
|
||||
" \"current_issues\": [\"Too generic\", \"Lacks quantified data\"],\n" +
|
||||
" \"optimized_version\": \"Experienced construction project manager with 8 years in residential and commercial projects, successfully managing projects worth over $5M, specializing in cost control and quality management, certified PMP with White Card qualification.\",\n" +
|
||||
" \"key_improvements\": [\"Add specific numbers\", \"Highlight core qualifications\", \"Emphasize industry experience\"]\n" +
|
||||
" },\n" +
|
||||
" \"work_experience\": [\n" +
|
||||
" {\n" +
|
||||
" \"position\": \"Project Manager - ABC Construction\",\n" +
|
||||
" \"current_description\": \"Responsible for project management work\",\n" +
|
||||
" \"optimized_description\": \"• Managed 3 concurrent residential projects worth $2.8M, achieving 100% on-time, on-budget completion rate\\n• Coordinated 15-person team including engineers, contractors, and suppliers\\n• Implemented quality control processes, reducing rework by 25%\",\n" +
|
||||
" \"improvements\": [\"Quantify achievements\", \"Specific responsibilities\", \"Highlight accomplishments\"]\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"skills_section\": {\n" +
|
||||
" \"current_issues\": [\"Poor skill ordering\", \"Missing industry keywords\"],\n" +
|
||||
" \"suggested_structure\": {\n" +
|
||||
" \"core_skills\": [\"Project Management\", \"Quality Control\", \"Cost Management\", \"Team Leadership\"],\n" +
|
||||
" \"technical_skills\": [\"AutoCAD\", \"MS Project\", \"Building Codes\", \"Safety Management\"],\n" +
|
||||
" \"certifications\": [\"PMP\", \"White Card\", \"First Aid\", \"WHS Certificate\"]\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"keyword_optimization\": {\n" +
|
||||
" \"missing_keywords\": [\"Construction Management\", \"Site Supervision\", \"Safety Compliance\", \"Budget Control\"],\n" +
|
||||
" \"industry_terms\": [\"Residential Construction\", \"Commercial Projects\", \"Subcontractor Management\", \"Quality Assurance\"],\n" +
|
||||
" \"ats_keywords\": [\"Project Delivery\", \"Cost Effectiveness\", \"Team Collaboration\", \"Customer Satisfaction\"]\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"action_plan\": {\n" +
|
||||
" \"immediate_fixes\": [\n" +
|
||||
" \"Correct all spelling and grammar errors\",\n" +
|
||||
" \"Standardize dates and formatting\",\n" +
|
||||
" \"Resolve timeline conflicts\"\n" +
|
||||
" ],\n" +
|
||||
" \"content_improvements\": [\n" +
|
||||
" \"Rewrite personal summary to highlight core value\",\n" +
|
||||
" \"Add quantified achievements to each work experience\",\n" +
|
||||
" \"Reorganize skills section\"\n" +
|
||||
" ],\n" +
|
||||
" \"strategic_enhancements\": [\n" +
|
||||
" \"Add industry-relevant keywords\",\n" +
|
||||
" \"Highlight safety and compliance experience\",\n" +
|
||||
" \"Emphasize project management achievements\"\n" +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"estimated_improvement\": {\n" +
|
||||
" \"current_ats_score\": 65,\n" +
|
||||
" \"potential_ats_score\": 85,\n" +
|
||||
" \"recruiter_appeal\": \"Significantly improved\",\n" +
|
||||
" \"interview_probability\": \"+40%\"\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"```\n" +
|
||||
"\n" +
|
||||
"## Analysis Principles\n" +
|
||||
"\n" +
|
||||
"1. **Constructive**: All suggestions should be actionable improvement recommendations\n" +
|
||||
"2. **Specific**: Provide concrete modification examples, not just general advice\n" +
|
||||
"3. **Industry-Relevant**: Offer targeted suggestions based on target industry characteristics\n" +
|
||||
"4. **Balanced**: Point out issues while also acknowledging strengths\n" +
|
||||
"5. **Practical**: Consider actual needs of ATS systems and recruiters\n" +
|
||||
"\n" +
|
||||
"## Important Notes\n" +
|
||||
"\n" +
|
||||
"- Maintain professional and encouraging tone\n" +
|
||||
"- Avoid overly harsh criticism\n" +
|
||||
"- Provide feasible solutions\n" +
|
||||
"- Consider candidates of different experience levels\n" +
|
||||
"- Respect personal privacy, don't request sensitive information";
|
||||
|
||||
return prompt;
|
||||
@Override
|
||||
public String handleGenerateMarkdown(String markdown) {
|
||||
String prompt = AiCommonPromptConstants.initializationGenerateMarkdownPrompt();
|
||||
List<Map<String, String>> list = new LinkedList();
|
||||
Map<String, String> mapEntity = new HashMap<>();
|
||||
mapEntity.put("role", "system");
|
||||
mapEntity.put("content",prompt);
|
||||
list.add(mapEntity);
|
||||
Map<String, String> mapUserEntity = new HashMap<>();
|
||||
mapUserEntity.put("role", "user");
|
||||
mapUserEntity.put("content",markdown);
|
||||
list.add(mapUserEntity);
|
||||
String promptJson = JSONUtil.toJsonStr(list);
|
||||
String resultStr = chatGPTClient.handleAiChat(promptJson,"MARKDOWN");
|
||||
String resultJson = resultStr.replaceAll("```json","").replaceAll("```","");
|
||||
log.info("处理附件分析结果合并信息:{}",resultJson);
|
||||
return resultJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化初步筛选问题淘汰评分提示词
|
||||
* 网站内容抓取
|
||||
* @param webUrl 网站链接地址
|
||||
* @return
|
||||
*/
|
||||
private String initializationQuestionEliminationScorePrompt(String roleName){
|
||||
|
||||
String prompt = "你是资深的面试专家和职业顾问,专注于为建筑、物流、制造、酒店、矿业等传统行业的求职者初步筛选面试回答进行打分,分数区间为0到100分。\n" +
|
||||
"\n" +
|
||||
"##核心任务\n" +
|
||||
"根据所提供的问题与答案,结合所申请的岗位,进行最终的打分与评价说明.\n" +
|
||||
"\n" +
|
||||
"##输入项参数\n" +
|
||||
"岗位:[]\n" +
|
||||
"\n" +
|
||||
"##返回的结构\n" +
|
||||
"\n" +
|
||||
"{ \\\"score\\\": \\\"\\\", \\\"evaluate\\\": \\\"\\\"}。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息,score为分数,evaluate为评价说明(返回英文)";
|
||||
|
||||
return prompt;
|
||||
@Override
|
||||
public String handleWebContentScraping(String webUrl) {
|
||||
//网站内容抓取
|
||||
try {
|
||||
String htmlContent = ReadHtmlByOkHttp.getHtmlContentByOkHttp(webUrl);
|
||||
String resultStr = ReadHtmlByOkHttp.removeHtmlTagAndAttrByJsoup(htmlContent);
|
||||
String resultData = ReadHtmlByOkHttp.removeBlankLineAndSpace(resultStr);
|
||||
return resultData;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 网站AI信息提取(使用提示词)
|
||||
* @param webInfoExtractVo 网站提取对象
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String handleWebInfoExtract(HotakeWebInfoExtractVo webInfoExtractVo) {
|
||||
//获取
|
||||
String webContent = handleWebContentScraping(webInfoExtractVo.getWebUrl());
|
||||
|
||||
String prompt = AiCommonPromptConstants.initializationWebInfoExtractPrompt();
|
||||
String userPrompt = "Please analyze the following company website:\n" +
|
||||
"\n" +
|
||||
"URL: "+webInfoExtractVo.getWebUrl()+"\n" +
|
||||
"\n" +
|
||||
"Content:\n" +
|
||||
webContent;
|
||||
List<Map<String, String>> list = new LinkedList();
|
||||
Map<String, String> mapEntity = new HashMap<>();
|
||||
mapEntity.put("role", "system");
|
||||
mapEntity.put("content",prompt);
|
||||
list.add(mapEntity);
|
||||
Map<String, String> mapUserEntity = new HashMap<>();
|
||||
mapUserEntity.put("role", "user");
|
||||
mapUserEntity.put("content",userPrompt);
|
||||
list.add(mapUserEntity);
|
||||
String promptJson = JSONUtil.toJsonStr(list);
|
||||
String resultStr = chatGPTClient.handleAiChat(promptJson,"WEBAITQ");
|
||||
String resultJson = resultStr.replaceAll("```json","").replaceAll("```","");
|
||||
log.info("网站AI信息提取:{}",resultJson);
|
||||
//直接更新个人信息中的企业名和地址
|
||||
Map dataMap = JSONUtil.toBean(resultJson,Map.class);
|
||||
Map companyOverviewMap = (Map)dataMap.get("company_overview");
|
||||
String companyName = companyOverviewMap.get("company_name").toString();
|
||||
Map contactInfoMap = (Map)companyOverviewMap.get("contact_info");
|
||||
String location = contactInfoMap.get("address").toString();
|
||||
|
||||
SysUser user = userMapper.selectUserById(SecurityUtils.getUserId());
|
||||
user.setCompanyName(companyName);
|
||||
user.setLocation(location);
|
||||
userMapper.updateUser(user);
|
||||
// String resultData = handleWebInfoEnhancedProcessing(resultJson);
|
||||
return resultJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 网站信息增强处理
|
||||
* @param webUlr
|
||||
* @param webContent 网站内容
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String handleWebInfoEnhancedProcessing(String webContent) {
|
||||
|
||||
List<Map<String, String>> list = new LinkedList();
|
||||
Map<String, String> mapEntity = new HashMap<>();
|
||||
mapEntity.put("role", "system");
|
||||
mapEntity.put("content","You are a professional HR consultant...\n" +
|
||||
"Please enhance and structure the company information...");
|
||||
list.add(mapEntity);
|
||||
Map<String, String> mapUserEntity = new HashMap<>();
|
||||
mapUserEntity.put("role", "user");
|
||||
mapUserEntity.put("content","Please enhance the following company information:\n" +webContent);
|
||||
list.add(mapUserEntity);
|
||||
String promptJson = JSONUtil.toJsonStr(list);
|
||||
String resultStr = chatGPTClient.handleAiChat(promptJson,"WEBAITQ");
|
||||
String resultJson = resultStr.replaceAll("```json","").replaceAll("```","");
|
||||
log.info("网站信息增强处理:{}",resultJson);
|
||||
|
||||
return resultJson;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,33 +6,39 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.MD5;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.vetti.common.ai.gpt.ChatGPTClient;
|
||||
import com.vetti.common.config.RuoYiConfig;
|
||||
import com.vetti.common.core.service.BaseServiceImpl;
|
||||
import com.vetti.common.enums.FillTypeEnum;
|
||||
import com.vetti.common.enums.MinioBucketNameEnum;
|
||||
import com.vetti.common.utils.HtmlToPdfUtil;
|
||||
import com.vetti.common.utils.MarkdownUtil;
|
||||
import com.vetti.common.utils.MultipartFileUtil;
|
||||
import com.vetti.common.utils.SecurityUtils;
|
||||
import com.vetti.common.utils.readFile.FileContentUtil;
|
||||
import com.vetti.hotake.domain.HotakeCvInfo;
|
||||
import com.vetti.hotake.domain.HotakeProblemBaseInfo;
|
||||
import com.vetti.hotake.domain.HotakeSysFile;
|
||||
import com.vetti.hotake.domain.dto.HotakeCvInfoDto;
|
||||
import com.vetti.hotake.domain.dto.HotakeCvOptimizeDto;
|
||||
import com.vetti.hotake.domain.dto.VcDto.*;
|
||||
import com.vetti.hotake.domain.vo.HotakeSysFileVo;
|
||||
import com.vetti.hotake.mapper.HotakeCvInfoMapper;
|
||||
import com.vetti.hotake.service.IHotakeAiCommonToolsService;
|
||||
import com.vetti.hotake.service.IHotakeCvInfoService;
|
||||
import com.vetti.hotake.service.IHotakeProblemBaseInfoService;
|
||||
import com.vetti.hotake.service.IHotakeSysFileService;
|
||||
import io.minio.GetObjectArgs;
|
||||
import io.minio.MinioClient;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 简历信息Service业务层处理
|
||||
@@ -45,6 +51,9 @@ import java.util.Map;
|
||||
@Service
|
||||
public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeCvInfoService {
|
||||
|
||||
// 系统目录
|
||||
private static final String SYSTEM_DIR = "/system_files/";
|
||||
|
||||
/**
|
||||
* 问题前缀
|
||||
*/
|
||||
@@ -65,6 +74,9 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
|
||||
@Autowired
|
||||
private IHotakeAiCommonToolsService aiCommonToolsService;
|
||||
|
||||
@Autowired
|
||||
private IHotakeSysFileService sysFileService;
|
||||
|
||||
/**
|
||||
* 查询简历信息
|
||||
*
|
||||
@@ -134,14 +146,21 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public HotakeCvInfo insertHotakeCvInfo(HotakeCvInfo hotakeCvInfo) {
|
||||
//删除原先的CV简历信息
|
||||
hotakeCvInfoMapper.deleteHotakeCvInfoByUserIdAndType(SecurityUtils.getUserId(),"cv");
|
||||
if("cv".equals(hotakeCvInfo.getCvFileType())){
|
||||
//删除原先的CV简历信息
|
||||
hotakeCvInfoMapper.deleteHotakeCvInfoByUserIdAndType(SecurityUtils.getUserId(),"cv");
|
||||
}
|
||||
fill(FillTypeEnum.INSERT.getCode(), hotakeCvInfo);
|
||||
String fileSuffix = FileUtil.getSuffix(hotakeCvInfo.getCvUrl());
|
||||
if (StrUtil.isNotEmpty(fileSuffix)) {
|
||||
fileSuffix = fileSuffix.toLowerCase();
|
||||
}
|
||||
hotakeCvInfo.setCvFileSuffix(fileSuffix);
|
||||
if(!"cv".equals(hotakeCvInfo.getCvFileType())){
|
||||
//对附件进行基础数据的解析
|
||||
String resultStr = handleAnalyzedAttachment(hotakeCvInfo.getCvUrl(),fileSuffix);
|
||||
hotakeCvInfo.setAnalyzedAttachmentJson(resultStr);
|
||||
}
|
||||
hotakeCvInfoMapper.insertHotakeCvInfo(hotakeCvInfo);
|
||||
return hotakeCvInfo;
|
||||
}
|
||||
@@ -331,6 +350,168 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
|
||||
return cvInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据其他的附件信息生成个人简历
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public HotakeCvInfo getGenerateResume() {
|
||||
//根据候选人登陆信息查询所有的附件信息
|
||||
HotakeCvInfo queryCv = new HotakeCvInfo();
|
||||
queryCv.setUserId(SecurityUtils.getUserId());
|
||||
List<HotakeCvInfo> cvInfoList = hotakeCvInfoMapper.selectHotakeCvInfoList(queryCv);
|
||||
|
||||
//基础数据信息,从推荐信中过去
|
||||
String basicInformation = "";
|
||||
//其他的附件信息集合
|
||||
String attachmentContent = "";
|
||||
if(CollectionUtil.isNotEmpty(cvInfoList)){
|
||||
List<HotakeCvInfo> cvInfos = cvInfoList.stream().filter(e->"letter".equals(e.getCvFileType())).toList();
|
||||
if(CollectionUtil.isNotEmpty(cvInfos)){
|
||||
basicInformation = cvInfos.get(0).getAnalyzedAttachmentJson();
|
||||
}
|
||||
List<HotakeCvInfo> cvInfoOnes = cvInfoList.stream().filter(e->!"letter".equals(e.getCvFileType())
|
||||
&& "cv".equals(e.getCvFileType())).toList();
|
||||
if(CollectionUtil.isNotEmpty(cvInfoOnes)){
|
||||
List<Map> attachmentContentList = new ArrayList<>();
|
||||
for (HotakeCvInfo hotakeCvInfo : cvInfoOnes) {
|
||||
if(StrUtil.isNotEmpty(hotakeCvInfo.getAnalyzedAttachmentJson())){
|
||||
Map map = JSONUtil.toBean(hotakeCvInfo.getAnalyzedAttachmentJson(), Map.class);
|
||||
attachmentContentList.add(map);
|
||||
}
|
||||
}
|
||||
if(CollectionUtil.isNotEmpty(attachmentContentList)){
|
||||
attachmentContent = JSONUtil.toJsonStr(attachmentContentList);
|
||||
}
|
||||
}
|
||||
}
|
||||
String cvData = aiCommonToolsService.handleAttachmentResultMerging(basicInformation,attachmentContent);
|
||||
//进行简历数据组合
|
||||
Map dataMap = JSONUtil.toBean(cvData, Map.class);
|
||||
HotakeCvInfoDto cvInfoDto = new HotakeCvInfoDto();
|
||||
//个人基本信息
|
||||
Map personalInfoMap = (Map)dataMap.get("personal_info");
|
||||
cvInfoDto.setName(personalInfoMap.get("name").toString());
|
||||
Map contactDetailsMap = (Map)personalInfoMap.get("contact_details");
|
||||
cvInfoDto.setPhone(contactDetailsMap.get("phone").toString());
|
||||
cvInfoDto.setEmail(contactDetailsMap.get("email").toString());
|
||||
cvInfoDto.setLocation(contactDetailsMap.get("address").toString());
|
||||
//个人介绍
|
||||
cvInfoDto.setAbout(dataMap.get("professional_summary").toString());
|
||||
|
||||
//技能工具
|
||||
Map skillsCertificatesMap = (Map)dataMap.get("skills_certificates");
|
||||
List<String> skillsList = (List<String>)skillsCertificatesMap.get("skills");
|
||||
//技能工具
|
||||
List<VcSkillsToolsDto> skillsTools = new ArrayList<>();
|
||||
if(CollectionUtil.isNotEmpty(skillsList)){
|
||||
for (String skill : skillsList) {
|
||||
VcSkillsToolsDto toolsDto = new VcSkillsToolsDto();
|
||||
toolsDto.setContent(skill);
|
||||
skillsTools.add(toolsDto);
|
||||
}
|
||||
}
|
||||
cvInfoDto.setSkillsTools(skillsTools);
|
||||
//工作经验集合
|
||||
List<VcExperienceDto> experience = new ArrayList<>();
|
||||
List<Map> workExperienceMapList = (List<Map>)dataMap.get("work_experience");
|
||||
if (CollectionUtil.isNotEmpty(workExperienceMapList)) {
|
||||
for (Map map : workExperienceMapList) {
|
||||
VcExperienceDto experienceDto = new VcExperienceDto();
|
||||
|
||||
experienceDto.setCompany(map.get("company").toString());
|
||||
experienceDto.setTitle(map.get("position").toString());
|
||||
String time_period = map.get("time_period").toString();
|
||||
if(StrUtil.isNotEmpty(time_period)){
|
||||
String[] times = time_period.split("–");
|
||||
experienceDto.setDurationStart(times[0]);
|
||||
if(times.length > 1){
|
||||
experienceDto.setDurationEnd(times[1]);
|
||||
}
|
||||
}
|
||||
List<VcExperienceDescriptionDto> description = new ArrayList<>();
|
||||
List<String> responsibilitiesList = (List<String>)map.get("responsibilities");
|
||||
if(CollectionUtil.isNotEmpty(responsibilitiesList)){
|
||||
for (String responsibility : responsibilitiesList) {
|
||||
VcExperienceDescriptionDto descriptionDto = new VcExperienceDescriptionDto();
|
||||
descriptionDto.setContent(responsibility);
|
||||
description.add(descriptionDto);
|
||||
}
|
||||
experienceDto.setDescription(description);
|
||||
}
|
||||
experience.add(experienceDto);
|
||||
}
|
||||
cvInfoDto.setExperience(experience);
|
||||
}
|
||||
//教育经历
|
||||
List<VcEducationDto> educationList = new ArrayList<>();
|
||||
List<Map> educationMapList = (List<Map>)dataMap.get("education");
|
||||
if(CollectionUtil.isNotEmpty(educationMapList)){
|
||||
for(Map map : educationMapList){
|
||||
VcEducationDto educationDto = new VcEducationDto();
|
||||
educationDto.setDegree(map.get("degree").toString());
|
||||
educationDto.setInstitution(map.get("school").toString());
|
||||
String time_period = map.get("time_period").toString();
|
||||
if(StrUtil.isNotEmpty(time_period)){
|
||||
String[] times = time_period.split("–");
|
||||
educationDto.setDurationStart(times[0]);
|
||||
if(times.length > 1){
|
||||
educationDto.setDurationEnd(times[1]);
|
||||
}
|
||||
}
|
||||
educationList.add(educationDto);
|
||||
}
|
||||
}
|
||||
cvInfoDto.setEducation(educationList);
|
||||
//创建简历信息
|
||||
HotakeCvInfo cvInfo = new HotakeCvInfo();
|
||||
cvInfo.setUserId(SecurityUtils.getUserId());
|
||||
cvInfo.setCvInfoDto(cvInfoDto);
|
||||
cvInfo.setCvTemplateJson(JSONUtil.toJsonStr(cvInfoDto));
|
||||
cvInfo.setCvFileType("cv");
|
||||
cvInfo.setCvFileSuffix("pdf");
|
||||
cvInfo.setStatus("1");
|
||||
//生成简历PDF数据
|
||||
String markdown = aiCommonToolsService.handleGenerateMarkdown(cvData);
|
||||
markdown = markdown.replaceAll("markdown","");
|
||||
try {
|
||||
String html = MarkdownUtil.markdownToHtml(markdown);
|
||||
// 可注入 CSS
|
||||
html = """
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: SimSun; }
|
||||
h1 { color: #2c3e50; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
""" + html + """
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
//生成PDF文件
|
||||
String resultFileName = SecurityUtils.getUsername() + "_" + System.currentTimeMillis() + ".pdf";
|
||||
cvInfo.setCvName(resultFileName);
|
||||
String resultPathUrl = RuoYiConfig.getProfile() + SYSTEM_DIR + resultFileName;
|
||||
HtmlToPdfUtil.htmlToPdf(html, resultPathUrl);
|
||||
//上传到minio中
|
||||
File pdf = new File(resultPathUrl);
|
||||
MultipartFile multipartFile =
|
||||
MultipartFileUtil.fileToMultipartFile(pdf);
|
||||
HotakeSysFileVo fileVo = new HotakeSysFileVo();
|
||||
fileVo.setMinioBucketName("cv-fs");
|
||||
HotakeSysFile sysFile = sysFileService.insertHotakeSysFile(multipartFile,fileVo);
|
||||
cvInfo.setFileSizeShow(sysFile.getFileSizeShow());
|
||||
cvInfo.setCvUrl(sysFile.getStoragePath());
|
||||
//创建简历对象数据
|
||||
insertHotakeCvInfo(cvInfo);
|
||||
//保存简历PDF附件数据
|
||||
// log.info("Markdown数据为:{}",markdown);
|
||||
}catch (Exception e){}
|
||||
return cvInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 简历解析数据
|
||||
@@ -509,6 +690,28 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
|
||||
System.out.println(strs[0].replaceAll("Score:","").trim().split("/")[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理分析附件返回对应的json 内容
|
||||
* @return
|
||||
*/
|
||||
private String handleAnalyzedAttachment(String fileUrl,String fileSuffix){
|
||||
log.info("开始处理附件,对附件进行解析");
|
||||
try {
|
||||
InputStream inputStream = minioClient.getObject(
|
||||
GetObjectArgs.builder()
|
||||
.bucket(MinioBucketNameEnum.CV.getCode())
|
||||
.object(fileUrl)
|
||||
.build());
|
||||
String contents = FileContentUtil.readFileContent(inputStream, fileSuffix);
|
||||
String resultStr = aiCommonToolsService.handleAnalyzedAttachment(contents);
|
||||
log.info("返回的分析附件结果数据为:{}",resultStr);
|
||||
return resultStr;
|
||||
}catch (Exception e){}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="aiMatchScorePercentage" column="ai_match_score_percentage" />
|
||||
|
||||
<result property="cvOptimizeJson" column="cv_optimize_json" />
|
||||
|
||||
<result property="analyzedAttachmentJson" column="analyzed_attachment_json" />
|
||||
|
||||
<result property="delFlag" column="del_flag" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
@@ -32,7 +35,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<sql id="selectHotakeCvInfoVo">
|
||||
select id, user_id, cv_name, cv_file_type, cv_url,file_size_show,cv_file_suffix,
|
||||
status,cv_template_json,cv_score,cv_md5,experience,ai_match_score,ai_match_score_percentage,cv_optimize_json,
|
||||
status,cv_template_json,cv_score,cv_md5,experience,ai_match_score,ai_match_score_percentage,cv_optimize_json,analyzed_attachment_json,
|
||||
del_flag, create_by, create_time, update_by, update_time, remark from hotake_cv_info
|
||||
</sql>
|
||||
|
||||
@@ -73,6 +76,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="aiMatchScorePercentage != null">ai_match_score_percentage,</if>
|
||||
<if test="cvOptimizeJson != null">cv_optimize_json,</if>
|
||||
|
||||
<if test="analyzedAttachmentJson != null">analyzed_attachment_json,</if>
|
||||
|
||||
<if test="delFlag != null">del_flag,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
@@ -97,6 +102,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="aiMatchScorePercentage != null">#{aiMatchScorePercentage},</if>
|
||||
<if test="cvOptimizeJson != null">#{cvOptimizeJson},</if>
|
||||
|
||||
<if test="analyzedAttachmentJson != null">#{analyzedAttachmentJson},</if>
|
||||
|
||||
<if test="delFlag != null">#{delFlag},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
@@ -126,6 +133,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<if test="cvOptimizeJson != null">cv_optimize_json = #{cvOptimizeJson},</if>
|
||||
|
||||
<if test="analyzedAttachmentJson != null">analyzed_attachment_json = #{analyzedAttachmentJson},</if>
|
||||
|
||||
<if test="delFlag != null">del_flag = #{delFlag},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
|
||||
Reference in New Issue
Block a user