From 009d839dceb0ccf08e1a33d434dd80ea16445432 Mon Sep 17 00:00:00 2001 From: wangxiangshun Date: Fri, 9 Jan 2026 16:50:28 +0800 Subject: [PATCH] =?UTF-8?q?AI=20=E7=94=9F=E6=88=90=E5=99=A8=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 9 +- .../ai/HotakeAiCommonToolsController.java | 32 +- .../src/main/resources/application-druid.yml | 5 +- .../target/classes/application-druid.yml | 5 +- vetti-common/pom.xml | 5 + .../vetti/common/ai/gpt/ChatGPTClient.java | 19 ++ .../constant/AiCommonPromptConstants.java | 304 ++++++++++++++++++ .../common/utils/html/ReadHtmlByOkHttp.java | 47 ++- .../dto/HotakeWorkExperienceGeneratorDto.java | 22 ++ .../domain/vo/HotakeRoleLinkAnalysisVo.java | 20 ++ .../vo/HotakeWorkExperienceGeneratorVo.java | 34 ++ .../service/IHotakeAiCommonToolsService.java | 21 +- .../impl/HotakeAiCommonToolsServiceImpl.java | 95 +++++- 13 files changed, 587 insertions(+), 31 deletions(-) create mode 100644 vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeWorkExperienceGeneratorDto.java create mode 100644 vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeRoleLinkAnalysisVo.java create mode 100644 vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeWorkExperienceGeneratorVo.java diff --git a/pom.xml b/pom.xml index 3e6f7a1..82f1379 100644 --- a/pom.xml +++ b/pom.xml @@ -373,10 +373,11 @@ 1.0.10 - - - - + + net.sourceforge.htmlunit + htmlunit + 2.70.0 + diff --git a/vetti-admin/src/main/java/com/vetti/web/controller/ai/HotakeAiCommonToolsController.java b/vetti-admin/src/main/java/com/vetti/web/controller/ai/HotakeAiCommonToolsController.java index 8c7a112..a5efb54 100644 --- a/vetti-admin/src/main/java/com/vetti/web/controller/ai/HotakeAiCommonToolsController.java +++ b/vetti-admin/src/main/java/com/vetti/web/controller/ai/HotakeAiCommonToolsController.java @@ -3,10 +3,7 @@ package com.vetti.web.controller.ai; import com.vetti.common.core.controller.BaseController; import com.vetti.common.core.domain.R; import com.vetti.hotake.domain.HotakeInitialScreeningQuestionsInfo; -import com.vetti.hotake.domain.dto.HotakeCvOptimizeDto; -import com.vetti.hotake.domain.dto.HotakeInitialQuestionEliminationScoreDto; -import com.vetti.hotake.domain.dto.HotakeJobDescriptionGeneratorDto; -import com.vetti.hotake.domain.dto.HotakePersonalProfileGeneratorDto; +import com.vetti.hotake.domain.dto.*; import com.vetti.hotake.domain.vo.*; import com.vetti.hotake.service.IHotakeAiCommonToolsService; import io.swagger.annotations.Api; @@ -115,10 +112,10 @@ public class HotakeAiCommonToolsController extends BaseController { * 网站内容抓取 */ @ApiOperation("网站内容抓取") - @GetMapping(value = "/webContentScraping") - public R handleWebContentScraping() + @PostMapping(value = "/webContentScraping") + public R handleWebContentScraping(@RequestBody HotakeWebInfoExtractVo webInfoExtractVo) { - return R.ok(hotakeAiCommonToolsService.handleWebContentScraping("")); + return R.ok(hotakeAiCommonToolsService.handleWebContentScraping(webInfoExtractVo.getWebUrl())); } @@ -143,4 +140,25 @@ public class HotakeAiCommonToolsController extends BaseController { return R.ok(hotakeAiCommonToolsService.getPersonalProfileGenerator(personalProfileGeneratorVo)); } + /** + * 工作经验生成器 + */ + @ApiOperation("工作经验生成器") + @PostMapping(value = "/workExperienceGenerator") + public R handleWorkExperienceGenerator(@RequestBody HotakeWorkExperienceGeneratorVo workExperienceGeneratorVo) + { + return R.ok(hotakeAiCommonToolsService.getWorkExperienceGenerator(workExperienceGeneratorVo)); + } + + /** + * 招聘链接信息分析补全(使用提示词) + */ + @ApiOperation("招聘链接信息分析补全(使用提示词)") + @PostMapping(value = "/roleLinkAnalysis") + public R handleRoleLinkAnalysis(@RequestBody HotakeRoleLinkAnalysisVo roleLinkAnalysisVo) + { + hotakeAiCommonToolsService.getRoleLinkAnalysis(roleLinkAnalysisVo); + return R.ok(); + } + } diff --git a/vetti-admin/src/main/resources/application-druid.yml b/vetti-admin/src/main/resources/application-druid.yml index c68530f..1a42d40 100644 --- a/vetti-admin/src/main/resources/application-druid.yml +++ b/vetti-admin/src/main/resources/application-druid.yml @@ -2,7 +2,7 @@ # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 - port: 6789 + port: 8080 servlet: # 应用的访问路径 context-path: / @@ -182,6 +182,9 @@ chatGpt: modelAaHb: gpt-4o-mini modelMarkdown: gpt-4o-mini modelWebAiTq: gpt-4o-mini + modelPpg: gpt-4o-mini + modelRLinkAl: gpt-4o-mini + modelRLinkAl_1: gpt-4o-mini role: system http: diff --git a/vetti-admin/target/classes/application-druid.yml b/vetti-admin/target/classes/application-druid.yml index 95c1e11..1a42d40 100644 --- a/vetti-admin/target/classes/application-druid.yml +++ b/vetti-admin/target/classes/application-druid.yml @@ -117,7 +117,7 @@ spring: max-wait: -1ms fs: minio: - endpoint: http://vetti.hotake.cn:9000 # MinIO 服务地址 + endpoint: https://vetti.hotake.cn:9000 # MinIO 服务地址 access-key: minioadmin # 访问密钥(替换为你的 Access Key) secret-key: minioadmin # 密钥(替换为你的 Secret Key) max-file-size: 104857600 #字节(Byte) @@ -182,6 +182,9 @@ chatGpt: modelAaHb: gpt-4o-mini modelMarkdown: gpt-4o-mini modelWebAiTq: gpt-4o-mini + modelPpg: gpt-4o-mini + modelRLinkAl: gpt-4o-mini + modelRLinkAl_1: gpt-4o-mini role: system http: diff --git a/vetti-common/pom.xml b/vetti-common/pom.xml index e556d71..91298fb 100644 --- a/vetti-common/pom.xml +++ b/vetti-common/pom.xml @@ -236,6 +236,11 @@ + + net.sourceforge.htmlunit + htmlunit + + diff --git a/vetti-common/src/main/java/com/vetti/common/ai/gpt/ChatGPTClient.java b/vetti-common/src/main/java/com/vetti/common/ai/gpt/ChatGPTClient.java index 4010050..2f1ebfb 100644 --- a/vetti-common/src/main/java/com/vetti/common/ai/gpt/ChatGPTClient.java +++ b/vetti-common/src/main/java/com/vetti/common/ai/gpt/ChatGPTClient.java @@ -69,6 +69,16 @@ public class ChatGPTClient { @Value("${chatGpt.modelWebAiTq}") private String modelWebAiTq; + @Value("${chatGpt.modelPpg}") + private String modelPpg; + + @Value("${chatGpt.modelRLinkAl}") + private String modelRLinkAl; + + + @Value("${chatGpt.modelRLinkAl_1}") + private String modelRLinkAl_1; + @Value("${chatGpt.role}") private String role; @@ -121,6 +131,15 @@ public class ChatGPTClient { }else if("WEBAITQ".equals(type)){ //网站AI信息提取 resultText = sendMessage(promptText, modelWebAiTq,objectMapper,client,role); + }else if("PPG".equals(type)){ + //个人简介生成器 + resultText = sendMessage(promptText, modelPpg,objectMapper,client,role); + }else if("RLINKAL".equals(type)){ + //招聘链接信息分析补全-第一阶段 + resultText = sendMessage(promptText, modelRLinkAl,objectMapper,client,role); + }else if("RLINKAL_1".equals(type)){ + //招聘链接信息分析补全-第二阶段 + resultText = sendMessage(promptText, modelRLinkAl_1,objectMapper,client,role); }else { resultText = sendMessage(promptText, modelQuestion,objectMapper,client,role); } diff --git a/vetti-common/src/main/java/com/vetti/common/constant/AiCommonPromptConstants.java b/vetti-common/src/main/java/com/vetti/common/constant/AiCommonPromptConstants.java index 34e93a2..5d709df 100644 --- a/vetti-common/src/main/java/com/vetti/common/constant/AiCommonPromptConstants.java +++ b/vetti-common/src/main/java/com/vetti/common/constant/AiCommonPromptConstants.java @@ -1520,4 +1520,308 @@ public class AiCommonPromptConstants { return promptStr; } + + /** + * 初始化工作经验生成器提示词 + * @return + */ + public static String initializationWorkExperienceGeneratorPrompt(){ + String promptStr = "You are a senior resume writing expert and career consultant, specializing in helping job seekers create professional, compelling work experience descriptions.\n" + + "\n" + + "## Core Mission\n" + + "Based on the job title, company name, and work period provided by the user, generate a professional, detailed, and attractive work experience description that highlights the candidate's value and achievements.\n" + + "\n" + + "## Generation Principles\n" + + "\n" + + "### 1. Professionalism Principle\n" + + "- Use professional business language and industry terminology\n" + + "- Avoid overly casual or informal expressions\n" + + "- Ensure content follows resume writing best practices\n" + + "\n" + + "### 2. Results-Oriented Principle\n" + + "- Focus on work outcomes and performance\n" + + "- Use specific data and metrics (reasonable estimates)\n" + + "- Demonstrate value contribution to company and team\n" + + "\n" + + "### 3. Skills Showcase Principle\n" + + "- Highlight core skills relevant to the position\n" + + "- Show combination of technical and soft skills\n" + + "- Demonstrate learning ability and adaptability\n" + + "\n" + + "### 4. Clear Logic Principle\n" + + "- Organize content by importance and logical order\n" + + "- Use clear structure and hierarchy\n" + + "- Ensure content coherence and readability\n" + + "\n" + + "## Content Requirements\n" + + "\n" + + "### 1. Job Responsibilities Description\n" + + "- Clearly describe main job responsibilities and scope\n" + + "- Highlight core business and key tasks\n" + + "- Demonstrate position importance and impact\n" + + "\n" + + "### 2. Key Achievements Listing\n" + + "- List 3-5 specific work achievements\n" + + "- Use quantified data to support achievements (reasonable estimates)\n" + + "- Highlight problem-solving and innovation capabilities\n" + + "\n" + + "### 3. Skills and Tools\n" + + "- List professional skills and tools used\n" + + "- Include both technical and management skills\n" + + "- Show alignment with industry development\n" + + "\n" + + "### 4. Team Collaboration and Leadership\n" + + "- Showcase teamwork and communication abilities\n" + + "- Highlight leadership and management experience (if applicable)\n" + + "- Demonstrate cross-functional collaboration skills\n" + + "\n" + + "## Industry Adaptation\n" + + "\n" + + "### Technology Industry\n" + + "- Emphasize tech stack and development experience\n" + + "- Highlight project management and architecture design\n" + + "- Show innovation and technical leadership\n" + + "\n" + + "### Financial Industry\n" + + "- Focus on risk control and compliance experience\n" + + "- Highlight data analysis and decision support\n" + + "- Show customer service and business growth\n" + + "\n" + + "### Manufacturing Industry\n" + + "- Emphasize production efficiency and quality management\n" + + "- Highlight process optimization and cost control\n" + + "- Show safety management and team leadership\n" + + "\n" + + "### Service Industry\n" + + "- Focus on customer satisfaction and service quality\n" + + "- Highlight sales performance and market expansion\n" + + "- Show communication skills and problem-solving\n" + + "\n" + + "### Education Industry\n" + + "- Emphasize teaching outcomes and student development\n" + + "- Highlight curriculum design and teaching innovation\n" + + "- Show teamwork and continuous learning\n" + + "\n" + + "## Output Format Requirements\n" + + "\n" + + "Please return the work experience description in JSON format with the following structure:\n" + + "\n" + + "```json\n" + + "{\n" + + " \"work_experience\": {\n" + + " \"job_title\": \"Job Title\",\n" + + " \"company_name\": \"Company Name\",\n" + + " \"work_period\": \"Work Period\",\n" + + " \"company_description\": \"Company brief description (1-2 sentences)\",\n" + + " \"position_summary\": \"Position overview (2-3 sentences highlighting position importance and main responsibilities)\",\n" + + " \"key_responsibilities\": [\n" + + " \"Main responsibility 1 (specific description of work content and scope)\",\n" + + " \"Main responsibility 2 (highlighting core business and key tasks)\",\n" + + " \"Main responsibility 3 (demonstrating position impact and value)\"\n" + + " ],\n" + + " \"key_achievements\": [\n" + + " {\n" + + " \"achievement\": \"Key achievement 1\",\n" + + " \"description\": \"Detailed description of achievement content and impact\",\n" + + " \"metrics\": \"Quantified metrics (if applicable)\"\n" + + " },\n" + + " {\n" + + " \"achievement\": \"Key achievement 2\",\n" + + " \"description\": \"Detailed description of achievement content and impact\",\n" + + " \"metrics\": \"Quantified metrics (if applicable)\"\n" + + " },\n" + + " {\n" + + " \"achievement\": \"Key achievement 3\",\n" + + " \"description\": \"Detailed description of achievement content and impact\",\n" + + " \"metrics\": \"Quantified metrics (if applicable)\"\n" + + " }\n" + + " ],\n" + + " \"skills_used\": [\n" + + " \"Core skill 1\",\n" + + " \"Core skill 2\",\n" + + " \"Core skill 3\",\n" + + " \"Core skill 4\",\n" + + " \"Core skill 5\"\n" + + " ],\n" + + " \"tools_technologies\": [\n" + + " \"Tool/Technology 1\",\n" + + " \"Tool/Technology 2\",\n" + + " \"Tool/Technology 3\",\n" + + " \"Tool/Technology 4\"\n" + + " ],\n" + + " \"team_collaboration\": \"Team collaboration and leadership experience description (2-3 sentences)\",\n" + + " \"professional_growth\": \"Professional growth and learning gains (2-3 sentences)\",\n" + + " \"formatted_description\": \"Complete work experience description (integrating all above information, suitable for direct resume use)\"\n" + + " },\n" + + " \"writing_tips\": {\n" + + " \"strengths_highlighted\": [\"Highlighted strength 1\", \"Highlighted strength 2\", \"Highlighted strength 3\"],\n" + + " \"customization_suggestions\": [\"Customization suggestion 1\", \"Customization suggestion 2\"],\n" + + " \"interview_talking_points\": [\"Interview point 1\", \"Interview point 2\", \"Interview point 3\"]\n" + + " }\n" + + "}\n" + + "```\n" + + "\n" + + "## Quality Standards\n" + + "\n" + + "### 1. Content Quality\n" + + "- Information accurate and industry-appropriate\n" + + "- Descriptions specific and persuasive\n" + + "- Highlight candidate's unique value\n" + + "\n" + + "### 2. Language Quality\n" + + "- Use professional and fluent English expression\n" + + "- Avoid grammatical errors and inappropriate expressions\n" + + "- Maintain consistent language style\n" + + "\n" + + "### 3. Structural Quality\n" + + "- Clear logic and distinct hierarchy\n" + + "- Prominent focus and easy to read\n" + + "- Standard format and convenient to use\n" + + "\n" + + "### 4. Practicality\n" + + "- Content suitable for resume use\n" + + "- Provide interview preparation points\n" + + "- Offer personalized suggestions\n" + + "\n" + + "## Important Notes\n" + + "\n" + + "1. **Authenticity**: Although AI-generated, ensure content is reasonable and credible, avoid exaggeration or false information\n" + + "2. **Personalization**: Adjust content style and focus based on different positions and company characteristics\n" + + "3. **Timeliness**: Consider industry development and technological changes during the work period\n" + + "4. **Completeness**: Ensure all JSON fields have reasonable content, no empty values\n" + + "5. **Usability**: Generated content should be directly usable for resumes, or ready with minor adjustments\n" + + "\n" + + "Please generate a high-quality work experience description based on the job information provided by the user."; + + return promptStr; + } + + /** + * 初始化招聘链接信息分析补全提示词 + * @return + */ + public static String initializationRoleLinkAnalysisPrompt(){ + String promptStr = "你是一位专业的招聘信息补全专家,专门根据已提取的岗位信息进行AI智能补全,严格遵守业务约束规则生成标准API格式响应。\n" + + "\n" + + "## 核心任务\n" + + "根据用户提供的「已提取岗位信息」,生成完整的、符合业务约束规则的标准API格式岗位数据。\n" + + "\n" + + "## 输入信息说明\n" + + "用户将提供一个JSON对象,包含从招聘页面提取的基础信息,其中:\n" + + "- **有值的字段**:表示从页面准确提取的信息,请保留原值\n" + + "- **null值字段**:表示页面未找到的信息,需要AI智能补全\n" + + "- **非标准格式字段**:需要转换为业务规则要求的标准格式\n" + + "\n" + + "## 信息处理原则\n" + + "1. **保留准确信息**:对于已从页面准确提取的信息(如jobTitle、companyName、location),请直接保留原值\n" + + "2. **格式标准化**:对于格式不标准的信息,请转换为业务规则要求的标准格式\n" + + " - `experience: \"3-5年工作经验\"` → `jobExperience: \"2-3 Years\"`\n" + + " - `education: \"本科及以上学历\"` → 标准教育要求格式\n" + + " - `skills: [\"Java\", \"Spring\"]` → 规则允许的技能格式\n" + + " - `salaryRange: \"15K-25K\"` → `salaryStart: 90000, salaryEnd: 150000`\n" + + "3. **智能补全**:对于标记为null的缺失信息,请根据岗位特点和行业标准进行合理补全\n" + + "4. **严格合规**:所有字段必须遵守业务约束规则表,禁止生成规则外的值\n" + + "\n" + + "## 业务约束规则表\n" + + "| 字段名 | 约束类型 | 业务约束值/规则 |\n" + + "|----------------------------|----------------|--------------------------------------------------------------------------------|\n" + + "| jobType | 可选值集合 | 仅允许:Full-time/Part-time/Contract/Temporary/Internship |\n" + + "| jobLevel | 可选值集合 | 仅允许:Entry/Junior/Mid/Senior/Lead/Manager/Director |\n" + + "| jobExperience | 文本输入 | 自由文本描述工作经验要求,如\"3-5年相关工作经验\"、\"2年以上Java开发经验\"等 |\n" + + "| locationType | 可选值集合 | 仅允许:On-site/Remote/Hybrid |\n" + + "| status | 可选值集合 | 仅允许:pause/archived/open/editing |\n" + + "| dataType | 可选值集合 | 仅允许:release/draft/template |\n" + + "| salaryStart/salaryEnd | 数值范围+类型 | 类型:数字(BigDecimal);范围:salaryStart≥50000,salaryEnd≤200000,且salaryEnd≥salaryStart |\n" + + "| descriptionTone | 可选值集合 | 仅允许:professional/friendly/innovative/dynamic(四选一,数组格式) |\n" + + "| aboutRole | 文本输入 | 关于职位的详细描述,包含岗位背景、重要性、发展前景等 |\n" + + "| responsibilities | 文本输入 | 详细的岗位职责描述,包含主要工作内容和具体任务 |\n" + + "| roleBenefitsList | 结构+取值规则 | 1. 结构:数组,每个元素仅含 keyValue 字段;
2. 取值:自由文本,如\"五险一金\"、\"年终奖金\"、\"带薪年假\"等;
3. 数量:3-8项 |\n" + + "| requiredSkillsList | 结构+取值规则 | 1. 结构:数组,每个元素仅含 keyValue 字段;
2. 取值:必须是\"Angular 4+ & RxJS\"/\"Financial Services Industry Experience\"/\"Health & Travel Account Modification\"/\"Information Technology\";
3. 数量:1-5项 |\n" + + "| niceToHaveSkillsList | 结构+取值规则 | 1. 结构:数组,每个元素仅含 keyValue 字段;
2. 取值:必须是\"Financial Services Industry Experience\"/\"Health & Travel Account Modification\"/\"Information Technology\";
3. 数量:0-3项 |\n" + + "| educationRequirements | 结构+可选值 | 1. 结构:对象,含 academicMajor、degree;
2. academicMajor:仅允许\"Computer Science\";
3. degree:仅允许\"Bachelor's Degree\"/\"Bachelor's degree or equivalent\" |\n" + + "| certificationsLicensesList | 结构+取值规则 | 1. 结构:数组,每个元素含 type、val;
2. type:仅允许\"standard\"/\"customize\";
3. val(standard):仅允许\"AWS Certified Solutions Architect\"/\"CISSP Certified Information Systems Security Professional\"/\"PMP Certified\";
4. 数量:0-3项 |\n" + + "\n" + + "## 核心要求\n" + + "1. 所有字段必须遵守「业务约束规则表」,禁止生成规则外的值;\n" + + "2. 数值字段必须匹配类型(如salaryStart为数字,非字符串);\n" + + "3. 必须包含requiredSkillsList、niceToHaveSkillsList、educationRequirements、certificationsLicensesList、descriptionTone、aboutRole、responsibilities、roleBenefitsList八个核心字段,不得遗漏;\n" + + "4. 去掉以下字段:createTime、updateTime、uuid、sysUserType、speechSpeed、acceptEquivalentWorkFlag(生成结果中不得出现这些字段);\n" + + "5. requiredSkillsList/niceToHaveSkillsList/educationRequirements/certificationsLicensesList的取值必须完全匹配业务约束规则;\n" + + "6. jobExperience字段为自由文本输入,不限制于预定义选项;\n" + + "7. descriptionTone必须从四个选项中选择一个,以数组格式返回;\n" + + "8. aboutRole和responsibilities为文本字段,需要详细描述;\n" + + "9. roleBenefitsList为数组格式,包含3-8项岗位福利。\n" + + "\n" + + "## API响应格式要求\n" + + "- 顶层字段:code(固定0)、data(HotakeRolesInfoDtoRes)、message(空字符串)、timestamp(当前时间,格式YYYY-MM-DDTHH:MM:SS);\n" + + "- 严格排除:createTime、updateTime、uuid、sysUserType、speechSpeed、acceptEquivalentWorkFlag;\n" + + "- 严格匹配字段类型(如数组字段不得为单个值,数字字段不得为字符串)。\n" + + "\n" + + "## 输出格式\n" + + "请以JSON格式返回补全结果,确保:\n" + + "1. 结构完整,包含所有必需字段\n" + + "2. 数据类型正确(数字、字符串、数组、对象)\n" + + "3. 取值符合业务约束规则\n" + + "4. 不包含禁止字段\n" + + "\n" + + "## 示例输出格式\n" + + "```json\n" + + "{\n" + + " \"code\": 0,\n" + + " \"data\": {\n" + + " \"jobTitle\": \"岗位名称\",\n" + + " \"companyName\": \"公司名称\",\n" + + " \"location\": \"工作地点\",\n" + + " \"salaryStart\": 80000,\n" + + " \"salaryEnd\": 120000,\n" + + " \"jobType\": \"Full-time\",\n" + + " \"jobLevel\": \"Senior\",\n" + + " \"jobExperience\": \"3-5年相关工作经验,熟悉Java开发\",\n" + + " \"locationType\": \"On-site\",\n" + + " \"status\": \"open\",\n" + + " \"dataType\": \"release\",\n" + + " \"descriptionTone\": [\"professional\"],\n" + + " \"aboutRole\": \"这是一个关键的技术岗位,负责公司核心业务系统的开发和维护。该职位将直接参与产品架构设计,是技术团队的重要组成部分,具有良好的职业发展前景。\",\n" + + " \"responsibilities\": \"1. 负责后端系统的设计、开发和维护;2. 参与系统架构设计和技术方案制定;3. 编写高质量、可维护的代码;4. 与前端团队协作完成产品功能;5. 参与代码审查和技术分享;6. 解决系统性能和稳定性问题。\",\n" + + " \"roleBenefitsList\": [\n" + + " {\"keyValue\": \"五险一金\"},\n" + + " {\"keyValue\": \"年终奖金\"},\n" + + " {\"keyValue\": \"带薪年假\"},\n" + + " {\"keyValue\": \"弹性工作时间\"},\n" + + " {\"keyValue\": \"技能培训\"},\n" + + " {\"keyValue\": \"团队建设活动\"}\n" + + " ],\n" + + " \"requiredSkillsList\": [\n" + + " {\"keyValue\": \"Information Technology\"},\n" + + " {\"keyValue\": \"Angular 4+ & RxJS\"}\n" + + " ],\n" + + " \"niceToHaveSkillsList\": [\n" + + " {\"keyValue\": \"Financial Services Industry Experience\"}\n" + + " ],\n" + + " \"educationRequirements\": {\n" + + " \"academicMajor\": \"Computer Science\",\n" + + " \"degree\": \"Bachelor's Degree\"\n" + + " },\n" + + " \"certificationsLicensesList\": [\n" + + " {\n" + + " \"type\": \"standard\",\n" + + " \"val\": \"PMP Certified\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"message\": \"\",\n" + + " \"timestamp\": \"2023-10-01T12:00:00\"\n" + + "}\n" + + "```\n" + + "\n" + + "## 质量要求\n" + + "1. 信息保留性:准确保留第一阶段提取的有效信息\n" + + "2. 补全合理性:AI补全的信息应符合行业标准和岗位特点\n" + + "3. 规则严格性:100%遵守业务约束规则,不得有任何例外\n" + + "4. 格式标准性:严格按照API格式要求输出\n" + + "5. 完整性验证:确保所有必需字段都已包含"; + + return promptStr; + } + } diff --git a/vetti-common/src/main/java/com/vetti/common/utils/html/ReadHtmlByOkHttp.java b/vetti-common/src/main/java/com/vetti/common/utils/html/ReadHtmlByOkHttp.java index 32058b0..de74757 100644 --- a/vetti-common/src/main/java/com/vetti/common/utils/html/ReadHtmlByOkHttp.java +++ b/vetti-common/src/main/java/com/vetti/common/utils/html/ReadHtmlByOkHttp.java @@ -13,6 +13,10 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + + /** * OkHttp第三方库读取网页HTML内容 */ @@ -30,19 +34,6 @@ public class ReadHtmlByOkHttp { .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 网站链接 @@ -133,4 +124,34 @@ public class ReadHtmlByOkHttp { // 步骤3:去除首尾空白 return noMultipleSpace.trim(); } + + + public static void main(String[] args) { + // 1. 初始化 HTMLUnit 客户端(启用 JavaScript,模拟 Chrome) + try (WebClient webClient = new WebClient()) { + // 关键:启用 JavaScript(Vue 依赖 JS 渲染) + webClient.getOptions().setJavaScriptEnabled(true); + // 禁用 CSS(无需渲染样式,提升速度) + webClient.getOptions().setCssEnabled(false); + // 忽略 JS 错误(避免页面 JS 报错中断执行) + webClient.getOptions().setThrowExceptionOnScriptError(false); + // 设置超时时间 + webClient.getOptions().setTimeout(15000); + + // 2. 加载页面并等待 JS 渲染 + String url = "https://vetti.hotake.cn/#/jobs/job/detail?jobId=126"; + HtmlPage page = webClient.getPage(url); + // 等待 Vue 数据渲染(给足够时间执行 JS) + webClient.waitForBackgroundJavaScript(5000); + + // 3. 提取页面纯文本 + String pageText = page.getTextContent(); + System.out.println("=== HTMLUnit 提取的页面文本 ==="); + System.out.println(pageText); + + } catch (Exception e) { + System.out.println("提取失败:" + e.getMessage()); + e.printStackTrace(); + } + } } diff --git a/vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeWorkExperienceGeneratorDto.java b/vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeWorkExperienceGeneratorDto.java new file mode 100644 index 0000000..eba2a29 --- /dev/null +++ b/vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeWorkExperienceGeneratorDto.java @@ -0,0 +1,22 @@ +package com.vetti.hotake.domain.dto; + +import com.vetti.hotake.domain.dto.VcDto.VcExperienceDescriptionDto; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 工作经验生成器 返回对象 + * + * @author wangxiangshun + * @date 2025-11-30 + */ +@Data +@Accessors(chain = true) +public class HotakeWorkExperienceGeneratorDto { + + @ApiModelProperty("描述集合") + private List description; +} diff --git a/vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeRoleLinkAnalysisVo.java b/vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeRoleLinkAnalysisVo.java new file mode 100644 index 0000000..27c1d5d --- /dev/null +++ b/vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeRoleLinkAnalysisVo.java @@ -0,0 +1,20 @@ +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 HotakeRoleLinkAnalysisVo { + + @ApiModelProperty("岗位网站地址") + private String roleUrl; + +} diff --git a/vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeWorkExperienceGeneratorVo.java b/vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeWorkExperienceGeneratorVo.java new file mode 100644 index 0000000..073421d --- /dev/null +++ b/vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeWorkExperienceGeneratorVo.java @@ -0,0 +1,34 @@ +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 HotakeWorkExperienceGeneratorVo { + + @ApiModelProperty("标题") + private String title; + + @ApiModelProperty("公司") + private String company; + + @ApiModelProperty("地点") + private String location; + + @ApiModelProperty("开始时间") + private String durationStart; + + @ApiModelProperty("结束时间") + private String durationEnd; + + + +} diff --git a/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeAiCommonToolsService.java b/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeAiCommonToolsService.java index 2e4b944..cc3dac3 100644 --- a/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeAiCommonToolsService.java +++ b/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeAiCommonToolsService.java @@ -2,10 +2,7 @@ package com.vetti.hotake.service; import com.vetti.hotake.domain.HotakeAiInterviewQuestionsInfo; import com.vetti.hotake.domain.HotakeInitialScreeningQuestionsInfo; -import com.vetti.hotake.domain.dto.HotakeCvOptimizeDto; -import com.vetti.hotake.domain.dto.HotakeInitialQuestionEliminationScoreDto; -import com.vetti.hotake.domain.dto.HotakeJobDescriptionGeneratorDto; -import com.vetti.hotake.domain.dto.HotakePersonalProfileGeneratorDto; +import com.vetti.hotake.domain.dto.*; import com.vetti.hotake.domain.vo.*; import java.util.List; @@ -98,4 +95,20 @@ public interface IHotakeAiCommonToolsService { public HotakePersonalProfileGeneratorDto getPersonalProfileGenerator(HotakePersonalProfileGeneratorVo personalProfileGeneratorVo); + /** + * 工作经验生成器 + * @param workExperienceGeneratorVo 工作信息 + * @return + */ + public HotakeWorkExperienceGeneratorDto getWorkExperienceGenerator(HotakeWorkExperienceGeneratorVo workExperienceGeneratorVo); + + + /** + * 招聘链接信息分析补全 + * @param roleLinkAnalysisVo 岗位链接对象 + * @return + */ + public String getRoleLinkAnalysis(HotakeRoleLinkAnalysisVo roleLinkAnalysisVo); + + } diff --git a/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeAiCommonToolsServiceImpl.java b/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeAiCommonToolsServiceImpl.java index a021e3c..941367a 100644 --- a/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeAiCommonToolsServiceImpl.java +++ b/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeAiCommonToolsServiceImpl.java @@ -674,7 +674,7 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I mapUserEntity.put("content",userPrompt); list.add(mapUserEntity); String promptJson = JSONUtil.toJsonStr(list); - String resultStr = chatGPTClient.handleAiChat(promptJson,"WEBAITQ"); + String resultStr = chatGPTClient.handleAiChat(promptJson,"PPG"); String resultJson = resultStr.replaceAll("```json","").replaceAll("```",""); log.info("个人简介生成器:{}",resultJson); @@ -686,5 +686,98 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I return generatorDto; } + /** + * 工作经验生成器 + * @param workExperienceGeneratorVo 工作信息 + * @return + */ + @Override + public HotakeWorkExperienceGeneratorDto getWorkExperienceGenerator(HotakeWorkExperienceGeneratorVo workExperienceGeneratorVo) { + String prompt = AiCommonPromptConstants.initializationWorkExperienceGeneratorPrompt(); + + String candidateText = " Job Title: "+workExperienceGeneratorVo.getTitle()+" Company Name: "+workExperienceGeneratorVo.getCompany()+" Work Period: "+workExperienceGeneratorVo.getDurationStart()+"-"+workExperienceGeneratorVo.getDurationEnd() + +"Additional Info: "; + + String userPrompt = "Please generate a professional work experience description based on the following information:\\n\\n"+candidateText; + List> list = new LinkedList(); + Map mapEntity = new HashMap<>(); + mapEntity.put("role", "system"); + mapEntity.put("content",prompt); + list.add(mapEntity); + Map mapUserEntity = new HashMap<>(); + mapUserEntity.put("role", "user"); + mapUserEntity.put("content",userPrompt); + list.add(mapUserEntity); + String promptJson = JSONUtil.toJsonStr(list); + String resultStr = chatGPTClient.handleAiChat(promptJson,"PPG"); + String resultJson = resultStr.replaceAll("```json","").replaceAll("```",""); + log.info("工作经验生成器:{}",resultJson); + + Map dataMap = JSONUtil.toBean(resultJson,Map.class); + HotakeWorkExperienceGeneratorDto generatorDto = new HotakeWorkExperienceGeneratorDto(); + List description = new ArrayList<>(); + + Map workExperienceMap = (Map)dataMap.get("work_experience"); + + VcExperienceDescriptionDto descriptionDto = new VcExperienceDescriptionDto(); + descriptionDto.setContent(workExperienceMap.get("position_summary").toString()); + description.add(descriptionDto); + + List keyResponsibilitiesList = (List)workExperienceMap.get("key_responsibilities"); + if(CollectionUtil.isNotEmpty(keyResponsibilitiesList)){ + for (String Str : keyResponsibilitiesList){ + VcExperienceDescriptionDto descriptionDto1 = new VcExperienceDescriptionDto(); + descriptionDto1.setContent(Str); + description.add(descriptionDto1); + } + } + generatorDto.setDescription(description); + return generatorDto; + } + + /** + * 招聘链接信息分析补全 + * @param roleLinkAnalysisVo 岗位链接对象 + * @return + */ + @Override + public String getRoleLinkAnalysis(HotakeRoleLinkAnalysisVo roleLinkAnalysisVo) { + //获取 + String webContent = handleWebContentScraping(roleLinkAnalysisVo.getRoleUrl()); + + String prompt = AiCommonPromptConstants.initializationRoleLinkAnalysisPrompt(); + String userPrompt = "请分析以下招聘页面并提取岗位信息:\\n\\n网址: "+roleLinkAnalysisVo.getRoleUrl()+"\\n\\n内容:\\n" +webContent; + List> list = new LinkedList(); + Map mapEntity = new HashMap<>(); + mapEntity.put("role", "system"); + mapEntity.put("content",prompt); + list.add(mapEntity); + Map mapUserEntity = new HashMap<>(); + mapUserEntity.put("role", "user"); + mapUserEntity.put("content",userPrompt); + list.add(mapUserEntity); + String promptJson = JSONUtil.toJsonStr(list); + String resultStr = chatGPTClient.handleAiChat(promptJson,"RLINKAL"); + String resultJson = resultStr.replaceAll("```json","").replaceAll("```",""); + log.info("招聘链接信息提取:{}",resultJson); + //处理岗位信息补充 + String userPrompt_1 = "请根据以下提取的岗位信息生成完整的API格式数据:\\n\\n" +resultJson; + List> listOne = new LinkedList(); + Map mapEntityOne = new HashMap<>(); + mapEntityOne.put("role", "system"); + mapEntityOne.put("content",prompt); + listOne.add(mapEntityOne); + Map mapUserEntityOne = new HashMap<>(); + mapUserEntityOne.put("role", "user"); + mapUserEntityOne.put("content",userPrompt_1); + listOne.add(mapUserEntityOne); + String promptJsonOne = JSONUtil.toJsonStr(listOne); + String resultStrOne = chatGPTClient.handleAiChat(promptJsonOne,"RLINKAL"); + String resultJsonOne = resultStrOne.replaceAll("```json","").replaceAll("```",""); + log.info("招聘链接信息补全:{}",resultJsonOne); + + return resultJsonOne; + } + }