简历以及招聘人基础逻辑修改

This commit is contained in:
2025-12-09 20:09:46 +08:00
parent 8d76eda22f
commit 65255bafb9
18 changed files with 635 additions and 64 deletions

View File

@@ -0,0 +1,49 @@
package com.vetti.hotake.domain;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.annotations.ApiModelProperty;
import com.vetti.common.annotation.Excel;
import com.vetti.common.core.domain.BaseEntity;
/**
* 候选人面试记录信息对象 hotake_candidate_interview_record_info
*
* @author wangxiangshun
* @date 2025-12-08
*/
@Data
@Accessors(chain = true)
public class HotakeCandidateInterviewRecordInfo extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
@ApiModelProperty("主键ID")
private Long id;
/** 用户ID */
@ApiModelProperty("用户ID")
private Long userId;
/** 简历ID */
@ApiModelProperty("简历ID")
private Long cvId;
/** 招聘人ID */
@ApiModelProperty("招聘人ID")
private Long recruiterId;
/** 候选人状态Hot、Warm、Cold、Pending */
@ApiModelProperty("候选人状态Hot、Warm、Cold、Pending")
private String candidateStatus;
/** 当前阶段 */
@ApiModelProperty("当前阶段(Applied、Shortlisted、Interview、Offer、Hired)")
private String stage;
/** 最后联系时间 */
@ApiModelProperty("最后联系时间")
private String lastContact;
}

View File

@@ -62,6 +62,15 @@ public class HotakeCvInfo extends BaseEntity
@ApiModelProperty("简历内容MD5 Hash")
private String cvMd5;
@ApiModelProperty("工作经验")
private String experience;
@ApiModelProperty("AI评分")
private String aiMatchScore;
@ApiModelProperty("AI评分百分比")
private String aiMatchScorePercentage;
@ApiModelProperty("简历详细信息-固定模版")
private HotakeCvInfoDto cvInfoDto;

View File

@@ -0,0 +1,69 @@
package com.vetti.hotake.mapper;
import java.util.List;
import com.vetti.hotake.domain.HotakeCandidateInterviewRecordInfo;
/**
* 候选人面试记录信息Mapper接口
*
* @author wangxiangshun
* @date 2025-12-08
*/
public interface HotakeCandidateInterviewRecordInfoMapper
{
/**
* 查询候选人面试记录信息
*
* @param id 候选人面试记录信息主键
* @return 候选人面试记录信息
*/
public HotakeCandidateInterviewRecordInfo selectHotakeCandidateInterviewRecordInfoById(Long id);
/**
* 查询候选人面试记录信息列表
*
* @param hotakeCandidateInterviewRecordInfo 候选人面试记录信息
* @return 候选人面试记录信息集合
*/
public List<HotakeCandidateInterviewRecordInfo> selectHotakeCandidateInterviewRecordInfoList(HotakeCandidateInterviewRecordInfo hotakeCandidateInterviewRecordInfo);
/**
* 新增候选人面试记录信息
*
* @param hotakeCandidateInterviewRecordInfo 候选人面试记录信息
* @return 结果
*/
public int insertHotakeCandidateInterviewRecordInfo(HotakeCandidateInterviewRecordInfo hotakeCandidateInterviewRecordInfo);
/**
* 修改候选人面试记录信息
*
* @param hotakeCandidateInterviewRecordInfo 候选人面试记录信息
* @return 结果
*/
public int updateHotakeCandidateInterviewRecordInfo(HotakeCandidateInterviewRecordInfo hotakeCandidateInterviewRecordInfo);
/**
* 删除候选人面试记录信息
*
* @param id 候选人面试记录信息主键
* @return 结果
*/
public int deleteHotakeCandidateInterviewRecordInfoById(Long id);
/**
* 批量删除候选人面试记录信息
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteHotakeCandidateInterviewRecordInfoByIds(Long[] ids);
/**
* 批量新增候选人面试记录信息
*
* @param hotakeCandidateInterviewRecordInfoList 候选人面试记录信息列表
* @return 结果
*/
public int batchInsertHotakeCandidateInterviewRecordInfo(List<HotakeCandidateInterviewRecordInfo> hotakeCandidateInterviewRecordInfoList);
}

View File

@@ -2,6 +2,7 @@ package com.vetti.hotake.mapper;
import java.util.List;
import com.vetti.hotake.domain.HotakeCvInfo;
import org.apache.ibatis.annotations.Param;
/**
* 简历信息Mapper接口
@@ -66,4 +67,13 @@ public interface HotakeCvInfoMapper
*/
public int batchInsertHotakeCvInfo(List<HotakeCvInfo> hotakeCvInfoList);
/**
* 删除简历信息
*
* @param userId 用户信息ID
* @return 结果
*/
public int deleteHotakeCvInfoByUserIdAndType(@Param("userId") Long userId,@Param("cvFileType") String cvFileType);
}

View File

@@ -0,0 +1,70 @@
package com.vetti.hotake.service;
import java.util.List;
import com.vetti.hotake.domain.HotakeCandidateInterviewRecordInfo;
/**
* 候选人面试记录信息Service接口
*
* @author wangxiangshun
* @date 2025-12-08
*/
public interface IHotakeCandidateInterviewRecordInfoService
{
/**
* 查询候选人面试记录信息
*
* @param id 候选人面试记录信息主键
* @return 候选人面试记录信息
*/
public HotakeCandidateInterviewRecordInfo selectHotakeCandidateInterviewRecordInfoById(Long id);
/**
* 查询候选人面试记录信息列表
*
* @param hotakeCandidateInterviewRecordInfo 候选人面试记录信息
* @return 候选人面试记录信息集合
*/
public List<HotakeCandidateInterviewRecordInfo> selectHotakeCandidateInterviewRecordInfoList(HotakeCandidateInterviewRecordInfo hotakeCandidateInterviewRecordInfo);
/**
* 新增候选人面试记录信息
*
* @param hotakeCandidateInterviewRecordInfo 候选人面试记录信息
* @return 结果
*/
public int insertHotakeCandidateInterviewRecordInfo(HotakeCandidateInterviewRecordInfo hotakeCandidateInterviewRecordInfo);
/**
* 修改候选人面试记录信息
*
* @param hotakeCandidateInterviewRecordInfo 候选人面试记录信息
* @return 结果
*/
public int updateHotakeCandidateInterviewRecordInfo(HotakeCandidateInterviewRecordInfo hotakeCandidateInterviewRecordInfo);
/**
* 批量删除候选人面试记录信息
*
* @param ids 需要删除的候选人面试记录信息主键集合
* @return 结果
*/
public int deleteHotakeCandidateInterviewRecordInfoByIds(Long[] ids);
/**
* 删除候选人面试记录信息信息
*
* @param id 候选人面试记录信息主键
* @return 结果
*/
public int deleteHotakeCandidateInterviewRecordInfoById(Long id);
/**
* 批量新增候选人面试记录信息
*
* @param hotakeCandidateInterviewRecordInfoList 候选人面试记录信息列表
* @return 结果
*/
public int batchInsertHotakeCandidateInterviewRecordInfo(List<HotakeCandidateInterviewRecordInfo> hotakeCandidateInterviewRecordInfoList);
}

View File

@@ -0,0 +1,116 @@
package com.vetti.hotake.service.impl;
import java.util.List;
import com.vetti.common.core.service.BaseServiceImpl;
import com.vetti.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.vetti.hotake.mapper.HotakeCandidateInterviewRecordInfoMapper;
import com.vetti.hotake.domain.HotakeCandidateInterviewRecordInfo;
import com.vetti.hotake.service.IHotakeCandidateInterviewRecordInfoService;
/**
* 候选人面试记录信息Service业务层处理
*
* @author wangxiangshun
* @date 2025-12-08
*/
@SuppressWarnings("all")
@Service
public class HotakeCandidateInterviewRecordInfoServiceImpl extends BaseServiceImpl implements IHotakeCandidateInterviewRecordInfoService
{
@Autowired
private HotakeCandidateInterviewRecordInfoMapper hotakeCandidateInterviewRecordInfoMapper;
/**
* 查询候选人面试记录信息
*
* @param id 候选人面试记录信息主键
* @return 候选人面试记录信息
*/
@Transactional(readOnly = true)
@Override
public HotakeCandidateInterviewRecordInfo selectHotakeCandidateInterviewRecordInfoById(Long id)
{
return hotakeCandidateInterviewRecordInfoMapper.selectHotakeCandidateInterviewRecordInfoById(id);
}
/**
* 查询候选人面试记录信息列表
*
* @param hotakeCandidateInterviewRecordInfo 候选人面试记录信息
* @return 候选人面试记录信息
*/
@Transactional(readOnly = true)
@Override
public List<HotakeCandidateInterviewRecordInfo> selectHotakeCandidateInterviewRecordInfoList(HotakeCandidateInterviewRecordInfo hotakeCandidateInterviewRecordInfo)
{
return hotakeCandidateInterviewRecordInfoMapper.selectHotakeCandidateInterviewRecordInfoList(hotakeCandidateInterviewRecordInfo);
}
/**
* 新增候选人面试记录信息
*
* @param hotakeCandidateInterviewRecordInfo 候选人面试记录信息
* @return 结果
*/
@Transactional(rollbackFor=Exception.class)
@Override
public int insertHotakeCandidateInterviewRecordInfo(HotakeCandidateInterviewRecordInfo hotakeCandidateInterviewRecordInfo)
{
return hotakeCandidateInterviewRecordInfoMapper.insertHotakeCandidateInterviewRecordInfo(hotakeCandidateInterviewRecordInfo);
}
/**
* 修改候选人面试记录信息
*
* @param hotakeCandidateInterviewRecordInfo 候选人面试记录信息
* @return 结果
*/
@Transactional(rollbackFor=Exception.class)
@Override
public int updateHotakeCandidateInterviewRecordInfo(HotakeCandidateInterviewRecordInfo hotakeCandidateInterviewRecordInfo)
{
return hotakeCandidateInterviewRecordInfoMapper.updateHotakeCandidateInterviewRecordInfo(hotakeCandidateInterviewRecordInfo);
}
/**
* 批量删除候选人面试记录信息
*
* @param ids 需要删除的候选人面试记录信息主键
* @return 结果
*/
@Transactional(rollbackFor=Exception.class)
@Override
public int deleteHotakeCandidateInterviewRecordInfoByIds(Long[] ids)
{
return hotakeCandidateInterviewRecordInfoMapper.deleteHotakeCandidateInterviewRecordInfoByIds(ids);
}
/**
* 删除候选人面试记录信息信息
*
* @param id 候选人面试记录信息主键
* @return 结果
*/
@Transactional(rollbackFor=Exception.class)
@Override
public int deleteHotakeCandidateInterviewRecordInfoById(Long id)
{
return hotakeCandidateInterviewRecordInfoMapper.deleteHotakeCandidateInterviewRecordInfoById(id);
}
/**
* 批量新增候选人面试记录信息
*
* @param hotakeCandidateInterviewRecordInfoList 候选人面试记录信息列表
* @return 结果
*/
@Transactional(rollbackFor=Exception.class)
@Override
public int batchInsertHotakeCandidateInterviewRecordInfo(List<HotakeCandidateInterviewRecordInfo> hotakeCandidateInterviewRecordInfoList){
return hotakeCandidateInterviewRecordInfoMapper.batchInsertHotakeCandidateInterviewRecordInfo(hotakeCandidateInterviewRecordInfoList);
}
}

View File

@@ -112,6 +112,8 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
@Transactional(rollbackFor = Exception.class)
@Override
public HotakeCvInfo insertHotakeCvInfo(HotakeCvInfo hotakeCvInfo) {
//删除原先的CV简历信息
hotakeCvInfoMapper.deleteHotakeCvInfoByUserIdAndType(SecurityUtils.getUserId(),"cv");
fill(FillTypeEnum.INSERT.getCode(), hotakeCvInfo);
String fileSuffix = FileUtil.getSuffix(hotakeCvInfo.getCvUrl());
if (StrUtil.isNotEmpty(fileSuffix)) {
@@ -247,12 +249,14 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
.object(cvInfo.getCvUrl())
.build());
String contents = FileContentUtil.readFileContent(inputStream, cvInfo.getCvFileSuffix());
log.info("简历信息:{}",contents);
//验证文件内容是否改变,如果未改变不进行模型解析直接返回原有结果
String md5Hash = MD5.create().digestHex16(contents);
if(StrUtil.isNotEmpty(md5Hash) && md5Hash.equals(cvInfo.getCvMd5())){
//直接返回简历结果
return cvInfo;
}
cvInfo.setCvMd5(md5Hash);
//生成简历模版数据信息
HotakeCvInfoDto cvInfoDto = handleAnalysisCvInfo(contents);
cvInfo.setCvInfoDto(cvInfoDto);
@@ -285,7 +289,7 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
List<Map<String,String>> list = new LinkedList();
Map<String,String> entity = new HashMap<>();
entity.put("role","user");
entity.put("content","从下面提供的文本中提取所有能识别到的简历信息只提取原文中存在的内容不要补充、不推测、不总结。需要提取的字段包括name姓名或人名、phone电话号码、email电子邮件地址、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\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息不要解释说明不要改写内容。以下为待处理文本"+contents);
entity.put("content","从下面提供的文本中提取所有能识别到的简历信息只提取原文中存在的内容不要补充、不推测、不总结。需要提取的字段包括name姓名或人名、phone电话号码、email电子邮件地址、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\\\": \\\"\\\", \\\"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");