@@ -1,5 +1,6 @@
package com.vetti.hotake.service.impl ;
import cn.hutool.core.collection.CollectionUtil ;
import cn.hutool.core.io.FileUtil ;
import cn.hutool.core.util.StrUtil ;
import cn.hutool.json.JSONUtil ;
@@ -7,7 +8,6 @@ import com.vetti.common.ai.gpt.ChatGPTClient;
import com.vetti.common.core.service.BaseServiceImpl ;
import com.vetti.common.enums.FillTypeEnum ;
import com.vetti.common.enums.MinioBucketNameEnum ;
import com.vetti.common.utils.DateUtils ;
import com.vetti.common.utils.SecurityUtils ;
import com.vetti.common.utils.readFile.FileContentUtil ;
import com.vetti.hotake.domain.HotakeCvInfo ;
@@ -31,22 +31,20 @@ import java.util.Map;
/**
* 简历信息Service业务层处理
*
*
* @author wangxiangshun
* @date 2025-11-02
*/
@Slf4j
@SuppressWarnings ( " all " )
@Service
public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeCvInfoService
{
public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeCvInfoService {
/**
* 问题前缀
*/
private final String QUESTION_PREFIX = " Question: " ;
@Autowired
private HotakeCvInfoMapper hotakeCvInfoMapper ;
@@ -59,116 +57,111 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
@Autowired
private IHotakeProblemBaseInfoService hotakeProblemBaseInfoService ;
/**
* 查询简历信息
*
*
* @param id 简历信息主键
* @return 简历信息
*/
@Transactional ( readOnly = true )
@Override
public HotakeCvInfo selectHotakeCvInfoById ( Long id )
{
return hotakeCvInfoMapper . selectHotakeCvInfoById ( id ) ;
public HotakeCvInfo selectHotakeCvInfoById ( Long id ) {
HotakeCvInfo cvInfo = hotakeCvInfoMapper . selectHotakeCvInfoById ( id ) ;
HotakeCvInfoDto cvInfoDto = JSONUtil . toBean ( cvInfo . getCvTemplateJson ( ) , HotakeCvInfoDto . class ) ;
cvInfo . setCvInfoDto ( cvInfoDto ) ;
HotakeProblemBaseInfo query = new HotakeProblemBaseInfo ( ) ;
query . setCvId ( id ) ;
List < HotakeProblemBaseInfo > problemBaseInfoList = hotakeProblemBaseInfoService . selectHotakeProblemBaseInfoList ( query ) ;
if ( CollectionUtil . isNotEmpty ( problemBaseInfoList ) ) {
cvInfo . setProblemBaseInfo ( problemBaseInfoList . get ( 0 ) ) ;
}
return cvInfo ;
}
/**
* 查询简历信息列表
*
*
* @param hotakeCvInfo 简历信息
* @return 简历信息
*/
@Transactional ( readOnly = true )
@Override
public List < HotakeCvInfo > selectHotakeCvInfoList ( HotakeCvInfo hotakeCvInfo )
{
return hotakeCvInfoMapper . selectHotakeCvInfoList ( hotakeCvInfo ) ;
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 ) ;
cvInfo . setCvInfoDto ( cvInfoDto ) ;
//返回问题记录
}
}
return cvInfoList ;
}
/**
* 新增简历信息
*
*
* @param hotakeCvInfo 简历信息
* @return 结果
*/
@Transactional ( rollbackFor = Exception . class )
@Transactional ( rollbackFor = Exception . class )
@Override
public HotakeCvInfo insertHotakeCvInfo ( HotakeCvInfo hotakeCvInfo )
{
fill ( FillTypeEnum . INSERT . getCode ( ) , hotakeCvInfo ) ;
hotakeCvInfoMapper . insertHotakeCvInfo ( hotakeCvInfo ) ;
//获取文件后缀
public HotakeCvInfo insertHotakeCvInfo ( HotakeCvInfo hotakeCvInfo ) {
fill ( FillTypeEnum . INSERT . getCode ( ) , hotakeCvInfo ) ;
String fileSuffix = FileUtil . getSuffix ( hotakeCvInfo . getCvUrl ( ) ) ;
if ( StrUtil . isNotEmpty ( fileSuffix ) ) {
if ( StrUtil . isNotEmpty ( fileSuffix ) ) {
fileSuffix = fileSuffix . toLowerCase ( ) ;
}
hotakeCvInfo . setCvFileSuffix ( fileSuffix ) ;
if ( " cv " . equals ( hotakeCvInfo . getCvFileType ( ) ) ) {
try {
InputStream inputStream = minioClient . getObject (
GetObjectArgs . builder ( )
. bucket ( MinioBucketNameEnum . CV . getCode ( ) )
. object ( hotakeCvInfo . getCvUrl ( ) )
. build ( ) ) ;
String contents = FileContentUtil . readFileContent ( inputStream , hotakeCvInfo . getCvFileSuffix ( ) ) ;
//生成简历模版数据信息
HotakeCvInfoDto cvInfoDto = handleAnalysisCvInfo ( contents ) ;
hotakeCvInfo . setCvInfoDto ( cvInfoDto ) ;
//对简历数据进行处理生成相应的题库数据
HotakeProblemBaseInfo problemBaseInfo = handleHotakeCvInfo ( cvInfoDto ) ;
hotakeCvInfo . setProblemBaseInfo ( problemBaseInfo ) ;
//生成对应的简历评分
handleHotakeCvInfoScore ( cvInfoDto ) ;
} catch ( Exception e ) {
}
}
hotakeCvInfoMapper . insertHotakeCvInfo ( hotakeCvInfo ) ;
return hotakeCvInfo ;
}
/**
* 处理简历信息
*
* @param hotakeCvInfo 简历信息
* @return
*/
@Override
public HotakeProblemBaseInfo handleHotakeCvInfo ( HotakeCvInfoDto cvInfoDto ) {
public HotakeProblemBaseInfo handleHotakeCvInfo ( HotakeCvInfoDto cvInfoDto , Long cvId ) {
log . info ( " 处理简历信息 " ) ;
HotakeProblemBaseInfo problemBaseInfo = new HotakeProblemBaseInfo ( ) ;
try {
try {
//调用AI大模型
List < Map < String , String > > list = new LinkedList ( ) ;
Map < String , String > mapEntity = new HashMap < > ( ) ;
mapEntity . put ( " role " , " system " ) ;
mapEntity . put ( " content " , " You are an experienced interviewer for construction management positions. Generate targeted interview questions based on candidate resumes. " ) ;
List < Map < String , String > > list = new LinkedList ( ) ;
Map < String , String > mapEntity = new HashMap < > ( ) ;
mapEntity . put ( " role " , " system " ) ;
mapEntity . put ( " content " , " You are an experienced interviewer for construction management positions. Generate targeted interview questions based on candidate resumes. " ) ;
list . add ( mapEntity ) ;
Map < String , String > mapEntityOne = new HashMap < > ( ) ;
mapEntityOne . put ( " role " , " user " ) ;
mapEntityOne . put ( " content " , " Position: Construction Labourer " +
" \\ nCandidate: " + cvInfoDto . getName ( ) +
" \\ nKey Skills: " + JSONUtil . toJsonStr ( cvInfoDto . getSkillsToolsList ( ) ) +
" \\ nExperience: " + JSONUtil . toJsonStr ( cvInfoDto . getExperienceList ( ) ) +
" \\ nStrengths: Professional certifications, Relevant experience " +
" \\ nConcerns: " + JSONUtil . toJsonStr ( cvInfoDto . getAbout ( ) ) ) ;
Map < String , String > mapEntityOne = new HashMap < > ( ) ;
mapEntityOne . put ( " role " , " user " ) ;
mapEntityOne . put ( " content " , " Position: Construction Labourer " +
" \\ nCandidate: " + cvInfoDto . getName ( ) +
" \\ nKey Skills: " + JSONUtil . toJsonStr ( cvInfoDto . getSkillsToolsList ( ) ) +
" \\ nExperience: " + JSONUtil . toJsonStr ( cvInfoDto . getExperienceList ( ) ) +
" \\ nStrengths: Professional certifications, Relevant experience " +
" \\ nConcerns: " + JSONUtil . toJsonStr ( cvInfoDto . getAbout ( ) ) ) ;
list . add ( mapEntityOne ) ;
String promptText = JSONUtil . toJsonStr ( list ) ;
String resultMsg = chatGPTClient . handleAiChat ( promptText , " CV " ) ;
log . info ( " 返回简历问题数据:{} " , resultMsg ) ;
resultMsg = resultMsg . replaceAll ( " \ n " , " #AA# " ) ;
String resultMsg = chatGPTClient . handleAiChat ( promptText , " CV " ) ;
log . info ( " 返回简历问题数据:{} " , resultMsg ) ;
resultMsg = resultMsg . replaceAll ( " \ n " , " #AA# " ) ;
String question = " " ;
Integer questionNums = 0 ;
if ( StrUtil . isNotEmpty ( resultMsg ) ) {
resultMsg = resultMsg . replaceAll ( QUESTION_PREFIX , " " ) ;
if ( StrUtil . isNotEmpty ( resultMsg ) ) {
resultMsg = resultMsg . replaceAll ( QUESTION_PREFIX , " " ) ;
questionNums = resultMsg . split ( " #AA# " ) . length ;
}
//生成预设问题记录
//先删除该用户的预设问题
hotakeProblemBaseInfoService . deleteHotakeProblemBaseInfoByUserId ( SecurityUtils . getUserId ( ) ) ;
hotakeProblemBaseInfoService . deleteHotakeProblemBaseInfoByUserId ( SecurityUtils . getUserId ( ) , cvId );
problemBaseInfo . setUserId ( SecurityUtils . getUserId ( ) ) ;
problemBaseInfo . setContents ( resultMsg ) ;
problemBaseInfo . setQuestionNums ( questionNums ) ;
problemBaseInfo . setCvId ( cvId ) ;
hotakeProblemBaseInfoService . insertHotakeProblemBaseInfo ( problemBaseInfo ) ;
} catch ( Exception e ) {
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
return problemBaseInfo ;
@@ -177,75 +170,114 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
/**
* 修改简历信息
*
*
* @param hotakeCvInfo 简历信息
* @return 结果
*/
@Transactional ( rollbackFor = Exception . class )
@Transactional ( rollbackFor = Exception . class )
@Override
public HotakeCvInfo updateHotakeCvInfo ( HotakeCvInfo hotakeCvInfo )
{
hotakeCvInfo . setUpdateTime ( DateUtils . getNowDate ( ) ) ;
public HotakeCvInfo updateHotakeCvInfo ( HotakeCvInfo hotakeCvInfo ) {
fill ( FillTypeEnum . UPDATE . getCode ( ) , hotakeCvInfo ) ;
hotakeCvInfo . setCvTemplateJson ( JSONUtil . toJsonStr ( hotakeCvInfo . getCvInfoDto ( ) ) ) ;
hotakeCvInfoMapper . updateHotakeCvInfo ( hotakeCvInfo ) ;
return hotakeCvInfo ;
}
/**
* 批量删除简历信息
*
*
* @param ids 需要删除的简历信息主键
* @return 结果
*/
@Transactional ( rollbackFor = Exception . class )
@Transactional ( rollbackFor = Exception . class )
@Override
public int deleteHotakeCvInfoByIds ( Long [ ] ids )
{
public int deleteHotakeCvInfoByIds ( Long [ ] ids ) {
return hotakeCvInfoMapper . deleteHotakeCvInfoByIds ( ids ) ;
}
/**
* 删除简历信息信息
*
*
* @param id 简历信息主键
* @return 结果
*/
@Transactional ( rollbackFor = Exception . class )
@Transactional ( rollbackFor = Exception . class )
@Override
public int deleteHotakeCvInfoById ( Long id )
{
public int deleteHotakeCvInfoById ( Long id ) {
return hotakeCvInfoMapper . deleteHotakeCvInfoById ( id ) ;
}
/**
* 批量新增简历信息
*
* @param hotakeCvInfoList 简历信息列表
* @return 结果
*/
@Transactional ( rollbackFor = Exception . class )
@Transactional ( rollbackFor = Exception . class )
@Override
public int batchInsertHotakeCvInfo ( List < HotakeCvInfo > hotakeCvInfoList ) {
public int batchInsertHotakeCvInfo ( List < HotakeCvInfo > hotakeCvInfoList ) {
return hotakeCvInfoMapper . batchInsertHotakeCvInfo ( hotakeCvInfoList ) ;
}
/**
* 简历解析以及生成问题和评分
* @param hotakeCvInfo 简历信息
* @return
*/
@Override
public HotakeCvInfo handleCvAnalysis ( HotakeCvInfo hotakeCvInfo ) {
HotakeCvInfo cvInfo = selectHotakeCvInfoById ( hotakeCvInfo . getId ( ) ) ;
if ( " cv " . equals ( cvInfo . getCvFileType ( ) ) ) {
log . info ( " 开始处理简历 " ) ;
try {
InputStream inputStream = minioClient . getObject (
GetObjectArgs . builder ( )
. bucket ( MinioBucketNameEnum . CV . getCode ( ) )
. object ( cvInfo . getCvUrl ( ) )
. build ( ) ) ;
String contents = FileContentUtil . readFileContent ( inputStream , cvInfo . getCvFileSuffix ( ) ) ;
//生成简历模版数据信息
HotakeCvInfoDto cvInfoDto = handleAnalysisCvInfo ( contents ) ;
cvInfo . setCvInfoDto ( cvInfoDto ) ;
//对简历数据进行处理生成相应的题库数据
HotakeProblemBaseInfo problemBaseInfo = handleHotakeCvInfo ( cvInfoDto , hotakeCvInfo . getId ( ) ) ;
cvInfo . setProblemBaseInfo ( problemBaseInfo ) ;
//生成对应的简历评分
String resultMsg = handleHotakeCvInfoScore ( cvInfoDto ) ;
cvInfo . setScore ( resultMsg ) ;
cvInfo . setCvTemplateJson ( JSONUtil . toJsonStr ( cvInfoDto ) ) ;
fill ( FillTypeEnum . UPDATE . getCode ( ) , cvInfo ) ;
hotakeCvInfoMapper . updateHotakeCvInfo ( cvInfo ) ;
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
}
return cvInfo ;
}
/**
* 简历解析数据
*
* @param contents
* @return
*/
private HotakeCvInfoDto handleAnalysisCvInfo ( String contents ) {
//根据AI做
String pom = " [ \ n " +
" { \ n " +
" \" role \" : \" user \" , \ n " +
" \" content \" : \" 从下面提供的文本中提取所有能识别到的简历信息, 只提取原文中存在的内容, 不要补充、不推测、不总结。需要提取的字段包括: name( 姓名) 、phone( 电话号码) 、email( 电子邮件地址) 、links( 所有链接地址) 、about( 关于我/自我介绍) 、skills_tools( 关键资格( 许可证、注册/会员资格、认证) ) 、languages( 语言能力) 、experience( 工作经历(除了title、company、location、duration其他的都放到描述里面)) 、education( 教育经历) 。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\ \" name \\ \" : \\ \" \\ \" , \\ \" phone \\ \" : \\ \" \\ \" , \\ \" email \\ \" : \\ \" \\ \" , \\ \" links \\ \" : [], \\ \" about \\ \" : \\ \" \\ \" , \\ \" skills_tools \\ \" : [], \\ \" languages \\ \" : [], \\ \" experience \\ \" : [{ \\ \" title \\ \" : \\ \" \\ \" , \\ \" company \\ \" : \\ \" \\ \" , \\ \" location \\ \" : \\ \" \\ \" , \\ \" duration \\ \" : \\ \" \\ \" , \\ \" description \\ \" : []}], \\ \" education \\ \" : [{ \\ \" degree \\ \" : \\ \" \\ \" , \\ \" institution \\ \" : \\ \" \\ \" , \\ \" date \\ \" : \\ \" \\ \" }] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息, 不要解释说明, 不要改写内容。以下为待处理文本: " + contents +
" } \ n " +
" ] " ;
String resultCv = chatGPTClient . handleAiChat ( pom , " JX " ) ;
try {
HotakeCvInfoDto cvInfoDto = JSONUtil . toBean ( resultCv , HotakeCvInfoDto . class ) ;
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( 电话号码) 、position( 岗位) 、location( 地点) 、email( 电子邮件地址) 、links( 所有链接地址) 、about( 关于我/自我介绍) 、skills_tools( 关键资格( 许可证、注册/会员资格、认证) ) 、languages( 语言能力) 、experience( 工作经历(除了title、company、location、duration其他的都放到描述里面)) 、education( 教育经历) 。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\ \" name \\ \" : \\ \" \\ \" , \\ \" phone \\ \" : \\ \" \\ \" , \\ \" position \\ \" : \\ \" \\ \" , \\ \" location \\ \" : \\ \" \\ \" , \\ \" email \\ \" : \\ \" \\ \" , \\ \" links \\ \" : [], \\ \" about \\ \" : \\ \" \\ \" , \\ \" skills_tools \\ \" : [], \\ \" languages \\ \" : [], \\ \" experience \\ \" : [{ \\ \" title \\ \" : \\ \" \\ \" , \\ \" company \\ \" : \\ \" \\ \" , \\ \" location \\ \" : \\ \" \\ \" , \\ \" duration \\ \" : \\ \" \\ \" , \\ \" description \\ \" : []}], \\ \" education \\ \" : [{ \\ \" degree \\ \" : \\ \" \\ \" , \\ \" institution \\ \" : \\ \" \\ \" , \\ \" date \\ \" : \\ \" \\ \" }] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息, 不要解释说明, 不要改写内容。以下为待处理文本: " + contents ) ;
//根据AI做
list . add ( entity ) ;
String resultCv = chatGPTClient . handleAiChat ( JSONUtil . toJsonS tr ( list ) , " JX " ) ;
log . info ( " 开始返回简历解析结果:{} " , resultCv ) ;
HotakeCvInfoDto cvInfoDto = JSONUtil . toBean ( resultCv , HotakeCvInfoDto . class ) ;
return cvInfoDto ;
} catch ( Exception e ) {
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
return new HotakeCvInfoDto ( ) ;
}
@@ -253,36 +285,36 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
/**
* 处理简历信息
*
* @param hotakeCvInfo 简历信息
* @return
*/
private HotakeProblemBaseInfo handleHotakeCvInfoScore ( HotakeCvInfoDto cvInfoDto ) {
HotakeProblemBaseInfo problemBaseInfo = new HotakeProblemBaseInfo ( ) ;
try {
private String handleHotakeCvInfoScore ( HotakeCvInfoDto cvInfoDto ) {
String resultMsg = " " ;
try {
//调用AI大模型
List < Map < String , String > > list = new LinkedList ( ) ;
Map < String , String > mapEntity = new HashMap < > ( ) ;
mapEntity . put ( " role " , " system " ) ;
mapEntity . put ( " content " , " You are a construction industry HR expert. Evaluate resumes and provide scores and recommendations for construction management positions. " ) ;
List < Map < String , String > > list = new LinkedList ( ) ;
Map < String , String > mapEntity = new HashMap < > ( ) ;
mapEntity . put ( " role " , " system " ) ;
mapEntity . put ( " content " , " You are a construction industry HR expert. Evaluate resumes and provide scores and recommendations for construction management positions. " ) ;
list . add ( mapEntity ) ;
Map < String , String > mapEntityOne = new HashMap < > ( ) ;
mapEntityOne . put ( " role " , " user " ) ;
mapEntityOne . put ( " content " , " Position: Construction Labourer " +
" \\ nCandidate: " + cvInfoDto . getName ( ) +
" \\ nResume Summary: " + JSONUtil . toJsonStr ( cvInfoDto . getAbout ( ) ) +
" \\ nSkills: " + JSONUtil . toJsonStr ( cvInfoDto . getSkillsToolsList ( ) ) +
" \\ nExperience: " + JSONUtil . toJsonStr ( cvInfoDto . getExperienceList ( ) ) +
" \\ nEducation: " + JSONUtil . toJsonStr ( cvInfoDto . getEducationList ( ) ) +
" \\ nCertifications: " + JSONUtil . toJsonStr ( cvInfoDto . getSkillsToolsList ( ) ) ) ;
Map < String , String > mapEntityOne = new HashMap < > ( ) ;
mapEntityOne . put ( " role " , " user " ) ;
mapEntityOne . put ( " content " , " Position: Construction Labourer " +
" \\ nCandidate: " + cvInfoDto . getName ( ) +
" \\ nResume Summary: " + JSONUtil . toJsonStr ( cvInfoDto . getAbout ( ) ) +
" \\ nSkills: " + JSONUtil . toJsonStr ( cvInfoDto . getSkillsToolsList ( ) ) +
" \\ nExperience: " + JSONUtil . toJsonStr ( cvInfoDto . getExperienceList ( ) ) +
" \\ nEducation: " + JSONUtil . toJsonStr ( cvInfoDto . getEducationList ( ) ) +
" \\ nCertifications: " + JSONUtil . toJsonStr ( cvInfoDto . getSkillsToolsList ( ) ) ) ;
list . add ( mapEntityOne ) ;
String promptText = JSONUtil . toJsonStr ( list ) ;
String resultMsg = chatGPTClient . handleAiChat ( promptText , " CV " ) ;
log . info ( " 返回简历评分数据:{} " , resultMsg ) ;
} catch ( Exception e ) {
resultMsg = chatGPTClient . handleAiChat ( promptText , " CV " ) ;
log . info ( " 返回简历评分数据:{} " , resultMsg ) ;
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
return problemBaseInfo ;
return resultMsg ;
}
}