业务逻辑修改以及完善

This commit is contained in:
2026-01-11 23:31:20 +08:00
parent 6edf7a4958
commit 85294a917a
17 changed files with 1014 additions and 378 deletions

View File

@@ -2,6 +2,7 @@ package com.vetti.hotake.service;
import com.vetti.hotake.domain.HotakeAiInterviewQuestionsInfo;
import com.vetti.hotake.domain.HotakeInitialScreeningQuestionsInfo;
import com.vetti.hotake.domain.HotakeRolesInfo;
import com.vetti.hotake.domain.dto.*;
import com.vetti.hotake.domain.vo.*;
@@ -111,4 +112,20 @@ public interface IHotakeAiCommonToolsService {
public HotakeRolesInfoDto getRoleLinkAnalysis(HotakeRoleLinkAnalysisVo roleLinkAnalysisVo);
/**
* 招聘链接信息分析补全(按照步骤生成)
* @param rolesInfoDto 岗位信息
* @return
*/
public HotakeRolesInfoDto getRoleInfoAnalysisSetUp(HotakeRolesInfoDto rolesInfoDto);
/**
* AI面试问题生成
* @param rolesInfo 岗位信息
* @return
*/
public String getAiInterviewQuestions(HotakeRolesInfo rolesInfo);
}

View File

@@ -1,12 +1,15 @@
package com.vetti.hotake.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
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.enums.FillTypeEnum;
import com.vetti.common.enums.RoleBenefitsEnum;
import com.vetti.common.utils.SecurityUtils;
import com.vetti.common.utils.html.ReadHtmlByOkHttp;
import com.vetti.hotake.domain.HotakeInitScreQuestionsReplyRecordInfo;
@@ -765,7 +768,7 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
String resultJson = "{\"jobTitle\":\"Senior Software Engineer\",\"companyName\":\"Tech Innovation Corp\",\"jobType\":null}";
log.info("招聘链接信息提取:{}",resultJson);
//处理岗位信息补充
String userPrompt_1 = "请根据以下提取的岗位信息生成完整的API格式数据\\n\\n" +resultJson;
String userPrompt_1 = "Please generate complete API-formatted data based on the extracted job information below\\n\\n" +resultJson;
List<Map<String, String>> listOne = new LinkedList();
Map<String, String> mapEntityOne = new HashMap<>();
mapEntityOne.put("role", "system");
@@ -798,25 +801,192 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
hotakeRolesInfoDto.setResponsibilities(dataMap.get("responsibilities").toString());
//岗位所需技能信息
List<RequiredSkillsDto> requiredSkillsList = (List<RequiredSkillsDto>)dataMap.get("requiredSkills");
List<RequiredSkillsDto> requiredSkillsList = (List<RequiredSkillsDto>)dataMap.get("requiredSkillsList");
hotakeRolesInfoDto.setRequiredSkillsList(requiredSkillsList);
hotakeRolesInfoDto.setRequiredSkillsJson(JSONUtil.toJsonStr(requiredSkillsList));
List<NiceToHaveSkillsDto> niceToHaveSkillsList = (List<NiceToHaveSkillsDto>)dataMap.get("niceToHaveSkillsList");
hotakeRolesInfoDto.setNiceToHaveSkillsList(niceToHaveSkillsList);
List<RoleBenefitsDto> roleBenefitsList = (List<RoleBenefitsDto>)dataMap.get("roleBenefitsList");
hotakeRolesInfoDto.setNiceToHaveSkillsJson(JSONUtil.toJsonStr(niceToHaveSkillsList));
List<Map> roleBenefitsMapList = (List<Map>)dataMap.get("roleBenefitsList");
List<RoleBenefitsDto> roleBenefitsList = new ArrayList<>();
if(CollectionUtil.isNotEmpty(roleBenefitsMapList)){
for(Map dto : roleBenefitsMapList){
//福利转换
RoleBenefitsDto dto1 = new RoleBenefitsDto();
if(ObjectUtil.isNotEmpty(dto.get("keyValue"))){
RoleBenefitsEnum benefitsEnum = RoleBenefitsEnum.getByCode(dto.get("keyValue").toString());
if(benefitsEnum != null){
dto1.setKeyValue(benefitsEnum.getCode());
roleBenefitsList.add(dto1);
}
}
}
}
hotakeRolesInfoDto.setRoleBenefitsList(roleBenefitsList);
hotakeRolesInfoDto.setRoleBenefitsJson(JSONUtil.toJsonStr(roleBenefitsList));
Map educationRequirementsMap = (Map)dataMap.get("educationRequirements");
EducationRequirementsDto educationRequirements = new EducationRequirementsDto();
educationRequirements.setAcademicMajor(educationRequirementsMap.get("academicMajor").toString());
educationRequirements.setDegree(educationRequirementsMap.get("degree").toString());
hotakeRolesInfoDto.setEducationRequirements(educationRequirements);
hotakeRolesInfoDto.setEducationRequirementsJson(JSONUtil.toJsonStr(educationRequirements));
List<CertificationsLicensesDto> certificationsLicensesList = (List<CertificationsLicensesDto>)dataMap.get("certificationsLicensesList");
hotakeRolesInfoDto.setCertificationsLicensesList(certificationsLicensesList);
hotakeRolesInfoDto.setCertificationsLicensesJson(JSONUtil.toJsonStr(certificationsLicensesList));
fill(FillTypeEnum.INSERT.getCode(),hotakeRolesInfoDto);
hotakeRolesInfoMapper.insertHotakeRolesInfo(hotakeRolesInfoDto);
return hotakeRolesInfoDto;
}
/**
* 招聘链接信息分析补全
* @param roleLinkAnalysisVo 岗位链接对象
* @return
*/
@Override
public HotakeRolesInfoDto getRoleInfoAnalysisSetUp(HotakeRolesInfoDto rolesInfoDto) {
String prompt = AiCommonPromptConstants.initializationRoleLinkAnalysisPrompt();
String resultJson = "{\n" +
" \"jobTitle\": "+rolesInfoDto.getRoleName()+",\n" +
" \"companyName\": "+rolesInfoDto.getCompanyName()+",\n" +
" \"location\": "+rolesInfoDto.getLocations()+",\n" +
" \"salaryRange\": "+rolesInfoDto.getSalaryStart()+"-"+rolesInfoDto.getSalaryEnd()+",\n" +
" \"jobType\": "+rolesInfoDto.getJobType()+",\n" +
" \"experience\": "+rolesInfoDto.getJobExperience()+",\n" +
" \"education\": "+JSONUtil.toJsonStr(rolesInfoDto.getEducationRequirements())+",\n" +
" \"skills\": "+JSONUtil.toJsonStr(rolesInfoDto.getRequiredSkillsList())+",\n" +
" \"description\": "+rolesInfoDto.getAboutRole()+"\n" +
"}";
log.info("招聘链接信息提取:{}",resultJson);
//处理岗位信息补充
String userPrompt_1 = "Please generate complete API-formatted data based on the extracted job information below\\n\\n" +resultJson;
List<Map<String, String>> listOne = new LinkedList();
Map<String, String> mapEntityOne = new HashMap<>();
mapEntityOne.put("role", "system");
mapEntityOne.put("content",prompt);
listOne.add(mapEntityOne);
Map<String, String> 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);
HotakeRolesInfoDto hotakeRolesInfoDto = new HotakeRolesInfoDto();
Map resultMap = JSONUtil.toBean(resultJsonOne,Map.class);
Map dataMap = (Map)resultMap.get("data");
hotakeRolesInfoDto.setRoleName(dataMap.get("jobTitle").toString());
hotakeRolesInfoDto.setCompanyName(dataMap.get("companyName").toString());
hotakeRolesInfoDto.setLocations(dataMap.get("location").toString());
hotakeRolesInfoDto.setSalaryStart(new BigDecimal(dataMap.get("salaryStart").toString()));
hotakeRolesInfoDto.setSalaryEnd(new BigDecimal(dataMap.get("salaryEnd").toString()));
hotakeRolesInfoDto.setJobType(dataMap.get("jobType").toString().toLowerCase());
hotakeRolesInfoDto.setJobLevel(dataMap.get("jobLevel").toString().toLowerCase());
hotakeRolesInfoDto.setJobExperience("5");
hotakeRolesInfoDto.setLocationType(dataMap.get("locationType").toString().toLowerCase());
hotakeRolesInfoDto.setAboutRole(dataMap.get("aboutRole").toString());
hotakeRolesInfoDto.setResponsibilities(dataMap.get("responsibilities").toString());
//岗位所需技能信息
List<RequiredSkillsDto> requiredSkillsList = (List<RequiredSkillsDto>)dataMap.get("requiredSkillsList");
hotakeRolesInfoDto.setRequiredSkillsList(requiredSkillsList);
hotakeRolesInfoDto.setRequiredSkillsJson(JSONUtil.toJsonStr(requiredSkillsList));
List<NiceToHaveSkillsDto> niceToHaveSkillsList = (List<NiceToHaveSkillsDto>)dataMap.get("niceToHaveSkillsList");
hotakeRolesInfoDto.setNiceToHaveSkillsList(niceToHaveSkillsList);
hotakeRolesInfoDto.setNiceToHaveSkillsJson(JSONUtil.toJsonStr(niceToHaveSkillsList));
List<Map> roleBenefitsMapList = (List<Map>)dataMap.get("roleBenefitsList");
List<RoleBenefitsDto> roleBenefitsList = new ArrayList<>();
if(CollectionUtil.isNotEmpty(roleBenefitsMapList)){
for(Map dto : roleBenefitsMapList){
//福利转换
RoleBenefitsDto dto1 = new RoleBenefitsDto();
if(ObjectUtil.isNotEmpty(dto.get("keyValue"))){
RoleBenefitsEnum benefitsEnum = RoleBenefitsEnum.getByCode(dto.get("keyValue").toString());
if(benefitsEnum != null){
dto1.setKeyValue(benefitsEnum.getCode());
roleBenefitsList.add(dto1);
}
}
}
}
hotakeRolesInfoDto.setRoleBenefitsList(roleBenefitsList);
hotakeRolesInfoDto.setRoleBenefitsJson(JSONUtil.toJsonStr(roleBenefitsList));
Map educationRequirementsMap = (Map)dataMap.get("educationRequirements");
EducationRequirementsDto educationRequirements = new EducationRequirementsDto();
educationRequirements.setAcademicMajor(educationRequirementsMap.get("academicMajor").toString());
educationRequirements.setDegree(educationRequirementsMap.get("degree").toString());
hotakeRolesInfoDto.setEducationRequirements(educationRequirements);
hotakeRolesInfoDto.setEducationRequirementsJson(JSONUtil.toJsonStr(educationRequirements));
List<CertificationsLicensesDto> certificationsLicensesList = (List<CertificationsLicensesDto>)dataMap.get("certificationsLicensesList");
hotakeRolesInfoDto.setCertificationsLicensesList(certificationsLicensesList);
hotakeRolesInfoDto.setCertificationsLicensesJson(JSONUtil.toJsonStr(certificationsLicensesList));
return hotakeRolesInfoDto;
}
/**
* AI面试问题生成
* @param rolesInfo 岗位信息
* @return
*/
@Override
public String getAiInterviewQuestions(HotakeRolesInfo rolesInfo) {
String prompt = AiCommonPromptConstants.initializationAiInterviewQuestionsPrompt();
String resultJson = "Please generate AI interview questions based on the following job description\n" +
"\n" +
"**Job Information**\n" +
"职位名称:【】\n" +
"技术要求:【】\n" +
"经验要求:【】\n" +
"面试时长:【】\n" +
"公司文化:【】\n" +
"特殊要求:【】\n" +
"`;";
log.info("招聘链接信息提取:{}",resultJson);
//处理岗位信息补充
String userPrompt_1 = "Please generate complete API-formatted data based on the extracted job information below\\n\\n" +resultJson;
List<Map<String, String>> listOne = new LinkedList();
Map<String, String> mapEntityOne = new HashMap<>();
mapEntityOne.put("role", "system");
mapEntityOne.put("content",prompt);
listOne.add(mapEntityOne);
Map<String, String> 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 "";
}
}

View File

@@ -10,10 +10,7 @@ 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.*;
import com.vetti.common.utils.readFile.FileContentUtil;
import com.vetti.hotake.domain.HotakeCvInfo;
import com.vetti.hotake.domain.HotakeProblemBaseInfo;
@@ -87,23 +84,23 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
@Override
public HotakeCvInfo selectHotakeCvInfoById(Long id) {
HotakeCvInfo cvInfo = hotakeCvInfoMapper.selectHotakeCvInfoById(id);
if(StrUtil.isNotEmpty(cvInfo.getCvTemplateJson())){
HotakeCvInfoDto cvInfoDto = JSONUtil.toBean(cvInfo.getCvTemplateJson(),HotakeCvInfoDto.class);
if (StrUtil.isNotEmpty(cvInfo.getCvTemplateJson())) {
HotakeCvInfoDto cvInfoDto = JSONUtil.toBean(cvInfo.getCvTemplateJson(), HotakeCvInfoDto.class);
cvInfo.setCvInfoDto(cvInfoDto);
}
if(StrUtil.isNotEmpty(cvInfo.getCvOptimizeJson())){
if (StrUtil.isNotEmpty(cvInfo.getCvOptimizeJson())) {
//解析优化简历
HotakeCvOptimizeDto cvOptimizeDto = JSONUtil.toBean(cvInfo.getCvOptimizeJson(),HotakeCvOptimizeDto.class);
if(cvOptimizeDto != null){
HotakeCvOptimizeDto cvOptimizeDto = JSONUtil.toBean(cvInfo.getCvOptimizeJson(), HotakeCvOptimizeDto.class);
if (cvOptimizeDto != null) {
cvInfo.setCvOptimizeDto(cvOptimizeDto);
if(cvOptimizeDto.getTextCorrections() != null && CollectionUtil.isNotEmpty(cvOptimizeDto.getTextCorrections().getFormattingIssues())){
if (cvOptimizeDto.getTextCorrections() != null && CollectionUtil.isNotEmpty(cvOptimizeDto.getTextCorrections().getFormattingIssues())) {
cvInfo.setTextCorrectionsNums(cvOptimizeDto.getTextCorrections().getFormattingIssues().size());
}else{
} else {
cvInfo.setTextCorrectionsNums(0);
}
if(cvOptimizeDto.getLogicCorrections() != null && CollectionUtil.isNotEmpty(cvOptimizeDto.getLogicCorrections().getCareerProgressionDtos())){
if (cvOptimizeDto.getLogicCorrections() != null && CollectionUtil.isNotEmpty(cvOptimizeDto.getLogicCorrections().getCareerProgressionDtos())) {
cvInfo.setLogicCorrectionsNum(cvOptimizeDto.getTextCorrections().getFormattingIssues().size());
}else{
} else {
cvInfo.setLogicCorrectionsNum(0);
}
}
@@ -111,7 +108,7 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
HotakeProblemBaseInfo query = new HotakeProblemBaseInfo();
query.setCvId(id);
List<HotakeProblemBaseInfo> problemBaseInfoList = hotakeProblemBaseInfoService.selectHotakeProblemBaseInfoList(query);
if(CollectionUtil.isNotEmpty(problemBaseInfoList)){
if (CollectionUtil.isNotEmpty(problemBaseInfoList)) {
cvInfo.setProblemBaseInfo(problemBaseInfoList.get(0));
}
return cvInfo;
@@ -127,9 +124,9 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
@Override
public List<HotakeCvInfo> selectHotakeCvInfoList(HotakeCvInfo hotakeCvInfo) {
List<HotakeCvInfo> cvInfoList = hotakeCvInfoMapper.selectHotakeCvInfoList(hotakeCvInfo);
if(CollectionUtil.isNotEmpty(cvInfoList)){
for(HotakeCvInfo cvInfo : cvInfoList){
HotakeCvInfoDto cvInfoDto = JSONUtil.toBean(cvInfo.getCvTemplateJson(),HotakeCvInfoDto.class);
if (CollectionUtil.isNotEmpty(cvInfoList)) {
for (HotakeCvInfo cvInfo : cvInfoList) {
HotakeCvInfoDto cvInfoDto = JSONUtil.toBean(cvInfo.getCvTemplateJson(), HotakeCvInfoDto.class);
cvInfo.setCvInfoDto(cvInfoDto);
//返回问题记录
}
@@ -146,9 +143,9 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
@Transactional(rollbackFor = Exception.class)
@Override
public HotakeCvInfo insertHotakeCvInfo(HotakeCvInfo hotakeCvInfo) {
if("cv".equals(hotakeCvInfo.getCvFileType())){
if ("cv".equals(hotakeCvInfo.getCvFileType())) {
//删除原先的CV简历信息
hotakeCvInfoMapper.deleteHotakeCvInfoByUserIdAndType(SecurityUtils.getUserId(),"cv");
hotakeCvInfoMapper.deleteHotakeCvInfoByUserIdAndType(SecurityUtils.getUserId(), "cv");
}
fill(FillTypeEnum.INSERT.getCode(), hotakeCvInfo);
String fileSuffix = FileUtil.getSuffix(hotakeCvInfo.getCvUrl());
@@ -156,9 +153,9 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
fileSuffix = fileSuffix.toLowerCase();
}
hotakeCvInfo.setCvFileSuffix(fileSuffix);
if(!"cv".equals(hotakeCvInfo.getCvFileType())){
if (!"cv".equals(hotakeCvInfo.getCvFileType())) {
//对附件进行基础数据的解析
String resultStr = handleAnalyzedAttachment(hotakeCvInfo.getCvUrl(),fileSuffix);
String resultStr = handleAnalyzedAttachment(hotakeCvInfo.getCvUrl(), fileSuffix);
hotakeCvInfo.setAnalyzedAttachmentJson(resultStr);
}
hotakeCvInfoMapper.insertHotakeCvInfo(hotakeCvInfo);
@@ -172,7 +169,7 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
* @return
*/
@Override
public HotakeProblemBaseInfo handleHotakeCvInfo(HotakeCvInfoDto cvInfoDto,Long cvId) {
public HotakeProblemBaseInfo handleHotakeCvInfo(HotakeCvInfoDto cvInfoDto, Long cvId) {
log.info("处理简历信息");
HotakeProblemBaseInfo problemBaseInfo = new HotakeProblemBaseInfo();
try {
@@ -203,7 +200,7 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
}
//生成预设问题记录
//先删除该用户的预设问题
hotakeProblemBaseInfoService.deleteHotakeProblemBaseInfoByUserId(SecurityUtils.getUserId(),cvId);
hotakeProblemBaseInfoService.deleteHotakeProblemBaseInfoByUserId(SecurityUtils.getUserId(), cvId);
problemBaseInfo.setUserId(SecurityUtils.getUserId());
problemBaseInfo.setContents(resultMsg);
problemBaseInfo.setQuestionNums(questionNums);
@@ -231,7 +228,6 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
//修改的时候重新生成问题和评分
hotakeCvInfoMapper.updateHotakeCvInfo(hotakeCvInfo);
return hotakeCvInfo;
@@ -275,6 +271,7 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
/**
* 简历解析以及生成问题和评分
*
* @param hotakeCvInfo 简历信息
* @return
*/
@@ -290,10 +287,10 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
.object(cvInfo.getCvUrl())
.build());
String contents = FileContentUtil.readFileContent(inputStream, cvInfo.getCvFileSuffix());
log.info("简历信息:{}",contents);
log.info("简历信息:{}", contents);
//验证文件内容是否改变,如果未改变不进行模型解析直接返回原有结果
String md5Hash = MD5.create().digestHex16(contents);
if(StrUtil.isNotEmpty(md5Hash) && md5Hash.equals(cvInfo.getCvMd5())){
if (StrUtil.isNotEmpty(md5Hash) && md5Hash.equals(cvInfo.getCvMd5())) {
//直接返回简历结果
return cvInfo;
}
@@ -302,21 +299,21 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
HotakeCvInfoDto cvInfoDto = handleAnalysisCvInfo(contents);
cvInfo.setCvInfoDto(cvInfoDto);
//对简历数据进行处理生成相应的题库数据
HotakeProblemBaseInfo problemBaseInfo = handleHotakeCvInfo(cvInfoDto,hotakeCvInfo.getId());
HotakeProblemBaseInfo problemBaseInfo = handleHotakeCvInfo(cvInfoDto, hotakeCvInfo.getId());
cvInfo.setProblemBaseInfo(problemBaseInfo);
//解析优化简历
HotakeCvOptimizeDto cvOptimizeDto = aiCommonToolsService.getResumeAnalysisOptimizer(contents);
HotakeCvOptimizeDto cvOptimizeDto = aiCommonToolsService.getResumeAnalysisOptimizer(contents);
if(cvOptimizeDto != null){
if (cvOptimizeDto != null) {
cvInfo.setCvOptimizeJson(JSONUtil.toJsonStr(cvOptimizeDto));
if(cvOptimizeDto.getTextCorrections() != null && CollectionUtil.isNotEmpty(cvOptimizeDto.getTextCorrections().getFormattingIssues())){
if (cvOptimizeDto.getTextCorrections() != null && CollectionUtil.isNotEmpty(cvOptimizeDto.getTextCorrections().getFormattingIssues())) {
cvInfo.setTextCorrectionsNums(cvOptimizeDto.getTextCorrections().getFormattingIssues().size());
}else{
} else {
cvInfo.setTextCorrectionsNums(0);
}
if(cvOptimizeDto.getLogicCorrections() != null && CollectionUtil.isNotEmpty(cvOptimizeDto.getLogicCorrections().getCareerProgressionDtos())){
if (cvOptimizeDto.getLogicCorrections() != null && CollectionUtil.isNotEmpty(cvOptimizeDto.getLogicCorrections().getCareerProgressionDtos())) {
cvInfo.setLogicCorrectionsNum(cvOptimizeDto.getTextCorrections().getFormattingIssues().size());
}else{
} else {
cvInfo.setLogicCorrectionsNum(0);
}
cvInfo.setCvOptimizeDto(cvOptimizeDto);
@@ -352,6 +349,7 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
/**
* 根据其他的附件信息生成个人简历
*
* @return
*/
@Override
@@ -365,73 +363,104 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
String basicInformation = "";
//其他的附件信息集合
String attachmentContent = "";
if(CollectionUtil.isNotEmpty(cvInfoList)){
List<HotakeCvInfo> cvInfos = cvInfoList.stream().filter(e->"letter".equals(e.getCvFileType())).toList();
if(CollectionUtil.isNotEmpty(cvInfos)){
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<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())){
if (StrUtil.isNotEmpty(hotakeCvInfo.getAnalyzedAttachmentJson())) {
Map map = JSONUtil.toBean(hotakeCvInfo.getAnalyzedAttachmentJson(), Map.class);
attachmentContentList.add(map);
}
}
if(CollectionUtil.isNotEmpty(attachmentContentList)){
if (CollectionUtil.isNotEmpty(attachmentContentList)) {
attachmentContent = JSONUtil.toJsonStr(attachmentContentList);
}
}
}
String cvData = aiCommonToolsService.handleAttachmentResultMerging(basicInformation,attachmentContent);
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);
Map personalInfoMap = (Map) dataMap.get("personal_info");
if (personalInfoMap != null) {
cvInfoDto.setName(StringUtils.getObjectStr(personalInfoMap.get("full_name")));
Map contactDetailsMap = (Map) personalInfoMap.get("contact_details");
if (contactDetailsMap != null) {
cvInfoDto.setPhone(StringUtils.getObjectStr(contactDetailsMap.get("phone")));
cvInfoDto.setEmail(StringUtils.getObjectStr(contactDetailsMap.get("email")));
cvInfoDto.setLocation(StringUtils.getObjectStr(contactDetailsMap.get("address")));
} else {
cvInfoDto.setPhone(StringUtils.getObjectStr(personalInfoMap.get("phone")));
cvInfoDto.setEmail(StringUtils.getObjectStr(personalInfoMap.get("email")));
cvInfoDto.setLocation(StringUtils.getObjectStr(personalInfoMap.get("address")));
}
}
cvInfoDto.setSkillsTools(skillsTools);
//个人介绍
cvInfoDto.setAbout(StringUtils.getObjectStr(dataMap.get("professional_summary")));
try{
//技能工具
List<Map> skillsList = (List<Map>) dataMap.get("skills_certificates");
if(CollectionUtil.isNotEmpty(skillsList)){
Map map = skillsList.get(0);
if(map != null){
List<String> skillsAllList = (List<String>) map.get("skills");
//技能工具
List<VcSkillsToolsDto> skillsTools = new ArrayList<>();
if (CollectionUtil.isNotEmpty(skillsAllList)) {
for (String skill : skillsAllList) {
VcSkillsToolsDto toolsDto = new VcSkillsToolsDto();
toolsDto.setContent(skill);
skillsTools.add(toolsDto);
}
}
cvInfoDto.setSkillsTools(skillsTools);
}
}
}catch (Exception e){
//技能工具
List<String> skillsList = (List<String>) dataMap.get("skills_certificates");
if(CollectionUtil.isNotEmpty(skillsList)){
//技能工具
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");
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]);
}
}
experienceDto.setCompany(StringUtils.getObjectStr(map.get("company")));
experienceDto.setTitle(StringUtils.getObjectStr(map.get("job_title")));
experienceDto.setLocation(StringUtils.getObjectStr(map.get("location")));
String startDate = StringUtils.getObjectStr(map.get("start_date"));
experienceDto.setDurationStart(startDate);
String endDate = StringUtils.getObjectStr(map.get("end_date"));
experienceDto.setDurationEnd(endDate);
List<VcExperienceDescriptionDto> description = new ArrayList<>();
List<String> responsibilitiesList = (List<String>)map.get("responsibilities");
if(CollectionUtil.isNotEmpty(responsibilitiesList)){
List<String> responsibilitiesList = (List<String>) map.get("responsibilities");
if (CollectionUtil.isNotEmpty(responsibilitiesList)) {
for (String responsibility : responsibilitiesList) {
VcExperienceDescriptionDto descriptionDto = new VcExperienceDescriptionDto();
descriptionDto.setContent(responsibility);
@@ -445,17 +474,17 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
}
//教育经历
List<VcEducationDto> educationList = new ArrayList<>();
List<Map> educationMapList = (List<Map>)dataMap.get("education");
if(CollectionUtil.isNotEmpty(educationMapList)){
for(Map map : educationMapList){
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)){
educationDto.setDegree(StringUtils.getObjectStr(map.get("degree")));
educationDto.setInstitution(StringUtils.getObjectStr(map.get("institution")));
String time_period = StringUtils.getObjectStr(map.get("graduation_date"));
if (StrUtil.isNotEmpty(time_period)) {
String[] times = time_period.split("");
educationDto.setDurationStart(times[0]);
if(times.length > 1){
if (times.length > 1) {
educationDto.setDurationEnd(times[1]);
}
}
@@ -473,23 +502,23 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
cvInfo.setStatus("1");
//生成简历PDF数据
String markdown = aiCommonToolsService.handleGenerateMarkdown(cvData);
markdown = markdown.replaceAll("markdown","");
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>
""";
<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);
@@ -501,14 +530,15 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
MultipartFileUtil.fileToMultipartFile(pdf);
HotakeSysFileVo fileVo = new HotakeSysFileVo();
fileVo.setMinioBucketName("cv-fs");
HotakeSysFile sysFile = sysFileService.insertHotakeSysFile(multipartFile,fileVo);
HotakeSysFile sysFile = sysFileService.insertHotakeSysFile(multipartFile, fileVo);
cvInfo.setFileSizeShow(sysFile.getFileSizeShow());
cvInfo.setCvUrl(sysFile.getStoragePath());
//创建简历对象数据
insertHotakeCvInfo(cvInfo);
//保存简历PDF附件数据
// log.info("Markdown数据为:{}",markdown);
}catch (Exception e){}
} catch (Exception e) {
}
return cvInfo;
}
@@ -522,81 +552,81 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
private HotakeCvInfoDto handleAnalysisCvInfo(String contents) {
log.info("开始简历解析");
try {
List<Map<String,String>> list = new LinkedList();
Map<String,String> entity = new HashMap<>();
entity.put("role","user");
entity.put("content","从下面提供的文本中提取所有能识别到的简历信息只提取原文中存在的内容不要补充、不推测、不总结。需要提取的字段包括name姓名或人名、phone电话号码、email电子邮件地址、experienceYear根据工作经验计算出来的工作年限、position岗位或者简历中自己期望的职位等、location地点或者地址、家庭住址、links所有链接地址、currentWork(当前工作公司)、about关于我/自我介绍、skills_tools关键资格许可证、注册/会员资格、认证、languages语言能力,主要就是Languages下面的语言、experience工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd)、education教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息不要解释说明不要改写内容。以下为待处理文本"+contents);
List<Map<String, String>> list = new LinkedList();
Map<String, String> entity = new HashMap<>();
entity.put("role", "user");
entity.put("content", "从下面提供的文本中提取所有能识别到的简历信息只提取原文中存在的内容不要补充、不推测、不总结。需要提取的字段包括name姓名或人名、phone电话号码、email电子邮件地址、experienceYear根据工作经验计算出来的工作年限、position岗位或者简历中自己期望的职位等、location地点或者地址、家庭住址、links所有链接地址、currentWork(当前工作公司)、about关于我/自我介绍、skills_tools关键资格许可证、注册/会员资格、认证、languages语言能力,主要就是Languages下面的语言、experience工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd)、education教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息不要解释说明不要改写内容。以下为待处理文本" + contents);
//根据AI做
list.add(entity);
String resultCv = chatGPTClient.handleAiChat(JSONUtil.toJsonStr(list), "JX");
log.info("开始返回简历解析结果:{}", resultCv);
HotakeCvInfoDto cvInfoDto = JSONUtil.toBean(resultCv, HotakeCvInfoDto.class);
//数据添加默认值
if(cvInfoDto != null){
if(StrUtil.isEmpty(cvInfoDto.getName())){
if (cvInfoDto != null) {
if (StrUtil.isEmpty(cvInfoDto.getName())) {
cvInfoDto.setName("-");
}
if(StrUtil.isEmpty(cvInfoDto.getPhone())){
if (StrUtil.isEmpty(cvInfoDto.getPhone())) {
cvInfoDto.setPhone("-");
}
if(StrUtil.isEmpty(cvInfoDto.getEmail())){
if (StrUtil.isEmpty(cvInfoDto.getEmail())) {
cvInfoDto.setEmail("-");
}
if(StrUtil.isEmpty(cvInfoDto.getPosition())){
if (StrUtil.isEmpty(cvInfoDto.getPosition())) {
cvInfoDto.setPosition("-");
}
if(StrUtil.isEmpty(cvInfoDto.getLocation())){
if (StrUtil.isEmpty(cvInfoDto.getLocation())) {
cvInfoDto.setLocation("-");
}
if(StrUtil.isEmpty(cvInfoDto.getAbout())){
if (StrUtil.isEmpty(cvInfoDto.getAbout())) {
cvInfoDto.setAbout("-");
}
if (StrUtil.isEmpty(cvInfoDto.getCurrentWork())){
if (StrUtil.isEmpty(cvInfoDto.getCurrentWork())) {
cvInfoDto.setCurrentWork("-");
}
if(CollectionUtil.isNotEmpty(cvInfoDto.getSkillsTools())){
for (VcSkillsToolsDto toolsDto :cvInfoDto.getSkillsTools()) {
if(StrUtil.isEmpty(toolsDto.getContent())){
if (CollectionUtil.isNotEmpty(cvInfoDto.getSkillsTools())) {
for (VcSkillsToolsDto toolsDto : cvInfoDto.getSkillsTools()) {
if (StrUtil.isEmpty(toolsDto.getContent())) {
toolsDto.setContent("-");
}
}
}
if(CollectionUtil.isNotEmpty(cvInfoDto.getLinks())){
for (VcLinksDto linksDto :cvInfoDto.getLinks()) {
if(StrUtil.isEmpty(linksDto.getContent())){
if (CollectionUtil.isNotEmpty(cvInfoDto.getLinks())) {
for (VcLinksDto linksDto : cvInfoDto.getLinks()) {
if (StrUtil.isEmpty(linksDto.getContent())) {
linksDto.setContent("-");
}
}
}
if(CollectionUtil.isNotEmpty(cvInfoDto.getLanguages())){
for (VcLanguagesDto languagesDto :cvInfoDto.getLanguages()) {
if(StrUtil.isEmpty(languagesDto.getContent())){
if (CollectionUtil.isNotEmpty(cvInfoDto.getLanguages())) {
for (VcLanguagesDto languagesDto : cvInfoDto.getLanguages()) {
if (StrUtil.isEmpty(languagesDto.getContent())) {
languagesDto.setContent("-");
}
}
}
if(CollectionUtil.isNotEmpty(cvInfoDto.getExperience())){
for (VcExperienceDto experienceDto :cvInfoDto.getExperience()) {
if (StrUtil.isEmpty(experienceDto.getTitle())){
if (CollectionUtil.isNotEmpty(cvInfoDto.getExperience())) {
for (VcExperienceDto experienceDto : cvInfoDto.getExperience()) {
if (StrUtil.isEmpty(experienceDto.getTitle())) {
experienceDto.setTitle("-");
}
if(StrUtil.isEmpty(experienceDto.getCompany())){
if (StrUtil.isEmpty(experienceDto.getCompany())) {
experienceDto.setCompany("-");
}
if(StrUtil.isEmpty(experienceDto.getLocation())){
if (StrUtil.isEmpty(experienceDto.getLocation())) {
experienceDto.setLocation("-");
}
if(StrUtil.isEmpty(experienceDto.getDurationStart())){
if (StrUtil.isEmpty(experienceDto.getDurationStart())) {
experienceDto.setDurationStart("-");
}
if(StrUtil.isEmpty(experienceDto.getDurationEnd())){
if (StrUtil.isEmpty(experienceDto.getDurationEnd())) {
experienceDto.setDurationEnd("-");
}
if (CollectionUtil.isNotEmpty(experienceDto.getDescription())){
for(VcExperienceDescriptionDto descriptionDto :experienceDto.getDescription()){
if(StrUtil.isEmpty(descriptionDto.getContent())){
if (CollectionUtil.isNotEmpty(experienceDto.getDescription())) {
for (VcExperienceDescriptionDto descriptionDto : experienceDto.getDescription()) {
if (StrUtil.isEmpty(descriptionDto.getContent())) {
descriptionDto.setContent("-");
}
@@ -605,31 +635,31 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
}
}
if(CollectionUtil.isNotEmpty(cvInfoDto.getEducation())){
for (VcEducationDto educationDto :cvInfoDto.getEducation()) {
if (StrUtil.isEmpty(educationDto.getDegree())){
if (CollectionUtil.isNotEmpty(cvInfoDto.getEducation())) {
for (VcEducationDto educationDto : cvInfoDto.getEducation()) {
if (StrUtil.isEmpty(educationDto.getDegree())) {
educationDto.setDegree("-");
}
if(StrUtil.isEmpty(educationDto.getInstitution())){
if (StrUtil.isEmpty(educationDto.getInstitution())) {
educationDto.setInstitution("-");
}
if(StrUtil.isEmpty(educationDto.getDurationStart())){
if (StrUtil.isEmpty(educationDto.getDurationStart())) {
educationDto.setDurationStart("-");
}
if(StrUtil.isEmpty(educationDto.getDurationEnd())){
if (StrUtil.isEmpty(educationDto.getDurationEnd())) {
educationDto.setDurationEnd("-");
}
if(educationDto.getCertificate() != null){
if(StrUtil.isEmpty(educationDto.getCertificate().getFileName())){
if (educationDto.getCertificate() != null) {
if (StrUtil.isEmpty(educationDto.getCertificate().getFileName())) {
educationDto.getCertificate().setFileName("-");
}
if (StrUtil.isEmpty(educationDto.getCertificate().getFileSuffix())){
if (StrUtil.isEmpty(educationDto.getCertificate().getFileSuffix())) {
educationDto.getCertificate().setFileSuffix("-");
}
if (StrUtil.isEmpty(educationDto.getCertificate().getFileUrl())){
if (StrUtil.isEmpty(educationDto.getCertificate().getFileUrl())) {
educationDto.getCertificate().setFileUrl("-");
}
if (StrUtil.isEmpty(educationDto.getCertificate().getFileSizeShow())){
if (StrUtil.isEmpty(educationDto.getCertificate().getFileSizeShow())) {
educationDto.getCertificate().setFileSizeShow("-");
}
}
@@ -680,21 +710,22 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
}
public static void main(String[] args){
public static void main(String[] args) {
String str = "Score: 4.8/5\n" +
"Recommendation: Highly recommended candidate - strong match for the role\n" +
"Strengths: Extensive relevant experience, Professional certifications, Strong skill set alignment\n" +
"Concerns: -";
String[] strs = str.split("\n");
System.out.println(strs[0].replaceAll("Score:","").trim().split("/")[0]);
System.out.println(strs[0].replaceAll("Score:", "").trim().split("/")[0]);
}
/**
* 处理分析附件返回对应的json 内容
*
* @return
*/
private String handleAnalyzedAttachment(String fileUrl,String fileSuffix){
private String handleAnalyzedAttachment(String fileUrl, String fileSuffix) {
log.info("开始处理附件,对附件进行解析");
try {
InputStream inputStream = minioClient.getObject(
@@ -704,14 +735,12 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
.build());
String contents = FileContentUtil.readFileContent(inputStream, fileSuffix);
String resultStr = aiCommonToolsService.handleAnalyzedAttachment(contents);
log.info("返回的分析附件结果数据为:{}",resultStr);
log.info("返回的分析附件结果数据为:{}", resultStr);
return resultStr;
}catch (Exception e){}
} catch (Exception e) {
}
return "";
}
}