AI接入逻辑完善

This commit is contained in:
2026-01-06 19:37:10 +08:00
parent 5663e8abfd
commit 7114a5f625
20 changed files with 2193 additions and 672 deletions

View File

@@ -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

View File

@@ -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();
}
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}
}

View File

@@ -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
);
}
}
}

View File

@@ -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();
}
}