添加接口 跟据个人信息创建简历

This commit is contained in:
zk
2026-07-02 14:30:17 +08:00
parent a915d709f4
commit 38cd49d292
2 changed files with 174 additions and 0 deletions
@@ -61,6 +61,14 @@ public class UserProfileController {
userProfileService.syncProfileFromResume(resumeId);
}
/**
* 根据当前用户个人资料创建一份新简历(名"个人资料简历"),返回新简历ID
*/
@PostMapping("/createResume")
public Long createResumeFromProfile() {
return userProfileService.createResumeFromProfile();
}
// ==================== 教育经历 ====================
/**
@@ -80,6 +80,16 @@ public class UserProfileService {
"全日制", 0, "非全日制", 1
);
/** 学历枚举→文本映射(个人资料→简历反向映射) */
private static final Map<Integer, String> REVERSE_DEGREE_MAP = Map.of(
1, "大专", 2, "本科", 3, "硕士", 4, "博士"
);
/** 学习形式枚举→文本映射(个人资料→简历反向映射) */
private static final Map<Integer, String> REVERSE_STUDY_TYPE_MAP = Map.of(
0, "全日制", 1, "非全日制"
);
// ==================== 主表 ====================
/** 查询当前用户个人资料 */
@@ -480,6 +490,162 @@ public class UserProfileService {
userProfileAnalyzeService.analyzeUserProfile(userId);
}
// ==================== 从个人资料生成简历 ====================
/**
* 根据当前用户的个人资料创建一份新简历
* <p>syncProfileFromResume 的反向操作:读取个人资料主表+5子表,新建一份简历(名"个人资料简历")及对应子表,返回新简历ID</p>
* <p>1. 校验个人资料存在 2. 读取个人资料5子表 3. 新建简历主表(复制共有字段,简历独有字段targetPosition/avatarUrl/city/summary留空)
* 4. 转换并批量插入5张简历子表(含degree/studyType反向映射) 5. 返回新简历ID</p>
* <p>注意:这是创建新简历(非覆盖),不删除已有简历;简历侧无AI分析,故不触发分析</p>
*/
@Transactional(rollbackFor = Exception.class)
public Long createResumeFromProfile() {
Long userId = UserSecurityTool.getUserId();
// 1. 校验个人资料存在
UserProfile profile = userProfileMapper.selectOne(
new LambdaQueryWrapper<UserProfile>().eq(UserProfile::getUserId, userId));
if (profile == null) {
throw new BusinessException(BusinessExpCodeEnum.DATA_NOT_EXIST, "个人资料不存在");
}
Long profileId = profile.getId();
// 2. 读取个人资料5张子表
List<UserProfileEducation> profileEducationList = educationMapper.selectList(new LambdaQueryWrapper<UserProfileEducation>().eq(UserProfileEducation::getProfileId, profileId).orderByAsc(UserProfileEducation::getSortOrder));
List<UserProfileWork> profileWorkList = workMapper.selectList(new LambdaQueryWrapper<UserProfileWork>().eq(UserProfileWork::getProfileId, profileId).orderByAsc(UserProfileWork::getSortOrder));
List<UserProfileInternship> profileInternshipList = internshipMapper.selectList(new LambdaQueryWrapper<UserProfileInternship>().eq(UserProfileInternship::getProfileId, profileId).orderByAsc(UserProfileInternship::getSortOrder));
List<UserProfileProject> profileProjectList = projectMapper.selectList(new LambdaQueryWrapper<UserProfileProject>().eq(UserProfileProject::getProfileId, profileId).orderByAsc(UserProfileProject::getSortOrder));
List<UserProfileCompetition> profileCompetitionList = competitionMapper.selectList(new LambdaQueryWrapper<UserProfileCompetition>().eq(UserProfileCompetition::getProfileId, profileId).orderByAsc(UserProfileCompetition::getSortOrder));
Instant now = Instant.now();
// 3. 新建简历主表(复制共有字段,简历独有字段targetPosition/avatarUrl/city/summary留空)
UserResume resume = new UserResume();
resume.setUserId(userId);
resume.setResumeName("个人资料简历");
resume.setName(profile.getName());
resume.setEmail(profile.getEmail());
resume.setMobileNumber(profile.getMobileNumber());
resume.setWechatNumber(profile.getWechatNumber());
resume.setPortfolioUrl(profile.getPortfolioUrl());
resume.setSkills(profile.getSkills());
resume.setCertificates(profile.getCertificates());
// 无简历时新建的这份自动设为默认,与 UserResumeService.saveResume 逻辑一致
boolean hasResume = userResumeMapper.selectCount(new LambdaQueryWrapper<UserResume>().eq(UserResume::getUserId, userId)) > 0;
resume.setIsDefault(hasResume ? 0 : 1);
resume.setSortOrder(0);
resume.setCreateTime(now);
resume.setUpdateTime(now);
userResumeMapper.insert(resume);
Long resumeId = resume.getId();
// 4. 转换并插入简历教育经历(含degree/studyType枚举→文本反向映射)
if (!profileEducationList.isEmpty()) {
List<UserResumeEducation> resumeEducationList = IntStream.range(0, profileEducationList.size()).mapToObj(i -> {
UserProfileEducation src = profileEducationList.get(i);
UserResumeEducation dest = new UserResumeEducation();
dest.setResumeId(resumeId);
dest.setUserId(userId);
dest.setSchool(src.getSchool());
dest.setMajor(src.getMajor());
dest.setDegree(Objects.isNull(src.getDegree()) ? null : REVERSE_DEGREE_MAP.get(src.getDegree()));
dest.setStudyType(Objects.isNull(src.getStudyType()) ? null : REVERSE_STUDY_TYPE_MAP.get(src.getStudyType()));
dest.setStartDate(src.getStartDate());
dest.setEndDate(src.getEndDate());
dest.setDescription(src.getDescription());
dest.setSortOrder(i);
dest.setCreateTime(now);
dest.setUpdateTime(now);
return dest;
}).collect(Collectors.toList());
resumeEducationMapper.batchInsert(resumeEducationList);
}
// 5. 转换并插入简历工作经历
if (!profileWorkList.isEmpty()) {
List<UserResumeWork> resumeWorkList = IntStream.range(0, profileWorkList.size()).mapToObj(i -> {
UserProfileWork src = profileWorkList.get(i);
UserResumeWork dest = new UserResumeWork();
dest.setResumeId(resumeId);
dest.setUserId(userId);
dest.setCompanyName(src.getCompanyName());
dest.setPosition(src.getPosition());
dest.setStartDate(src.getStartDate());
dest.setEndDate(src.getEndDate());
dest.setDescription(src.getDescription());
dest.setSortOrder(i);
dest.setCreateTime(now);
dest.setUpdateTime(now);
return dest;
}).collect(Collectors.toList());
resumeWorkMapper.batchInsert(resumeWorkList);
}
// 6. 转换并插入简历实习经历
if (!profileInternshipList.isEmpty()) {
List<UserResumeInternship> resumeInternshipList = IntStream.range(0, profileInternshipList.size()).mapToObj(i -> {
UserProfileInternship src = profileInternshipList.get(i);
UserResumeInternship dest = new UserResumeInternship();
dest.setResumeId(resumeId);
dest.setUserId(userId);
dest.setCompanyName(src.getCompanyName());
dest.setPosition(src.getPosition());
dest.setStartDate(src.getStartDate());
dest.setEndDate(src.getEndDate());
dest.setDescription(src.getDescription());
dest.setSortOrder(i);
dest.setCreateTime(now);
dest.setUpdateTime(now);
return dest;
}).collect(Collectors.toList());
resumeInternshipMapper.batchInsert(resumeInternshipList);
}
// 7. 转换并插入简历项目经历
if (!profileProjectList.isEmpty()) {
List<UserResumeProject> resumeProjectList = IntStream.range(0, profileProjectList.size()).mapToObj(i -> {
UserProfileProject src = profileProjectList.get(i);
UserResumeProject dest = new UserResumeProject();
dest.setResumeId(resumeId);
dest.setUserId(userId);
dest.setCompanyName(src.getCompanyName());
dest.setProjectName(src.getProjectName());
dest.setRole(src.getRole());
dest.setStartDate(src.getStartDate());
dest.setEndDate(src.getEndDate());
dest.setDescription(src.getDescription());
dest.setSortOrder(i);
dest.setCreateTime(now);
dest.setUpdateTime(now);
return dest;
}).collect(Collectors.toList());
resumeProjectMapper.batchInsert(resumeProjectList);
}
// 8. 转换并插入简历竞赛经历
if (!profileCompetitionList.isEmpty()) {
List<UserResumeCompetition> resumeCompetitionList = IntStream.range(0, profileCompetitionList.size()).mapToObj(i -> {
UserProfileCompetition src = profileCompetitionList.get(i);
UserResumeCompetition dest = new UserResumeCompetition();
dest.setResumeId(resumeId);
dest.setUserId(userId);
dest.setCompetitionName(src.getCompetitionName());
dest.setAward(src.getAward());
dest.setAwardDate(src.getAwardDate());
dest.setDescription(src.getDescription());
dest.setSortOrder(i);
dest.setCreateTime(now);
dest.setUpdateTime(now);
return dest;
}).collect(Collectors.toList());
resumeCompetitionMapper.batchInsert(resumeCompetitionList);
}
// 9. 返回新简历ID
return resumeId;
}
// ==================== 内部方法 ====================
/**