From bc9694fbecb513b2b8847ce221785ba226750b5d Mon Sep 17 00:00:00 2001 From: zk Date: Tue, 12 May 2026 09:57:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E7=AE=80=E5=8E=86ID=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E6=9B=B4=E6=96=B0=E4=B8=AA=E4=BA=BA=E8=B5=84=E6=96=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/UserProfileController.java | 8 + .../jiayunet/service/UserProfileService.java | 204 ++++++++++++++++++ 2 files changed, 212 insertions(+) diff --git a/client-api/src/main/java/org/jiayunet/controller/UserProfileController.java b/client-api/src/main/java/org/jiayunet/controller/UserProfileController.java index 9a791a5..88d8295 100644 --- a/client-api/src/main/java/org/jiayunet/controller/UserProfileController.java +++ b/client-api/src/main/java/org/jiayunet/controller/UserProfileController.java @@ -53,6 +53,14 @@ public class UserProfileController { userProfileService.saveProfile(po); } + /** + * 根据简历ID同步更新个人资料(主表+5子表全量覆盖,触发一次异步AI分析) + */ + @PostMapping("/syncFromResume") + public void syncFromResume(@RequestParam Long resumeId) { + userProfileService.syncProfileFromResume(resumeId); + } + // ==================== 教育经历 ==================== /** diff --git a/client-api/src/main/java/org/jiayunet/service/UserProfileService.java b/client-api/src/main/java/org/jiayunet/service/UserProfileService.java index 4b94258..8b0560a 100644 --- a/client-api/src/main/java/org/jiayunet/service/UserProfileService.java +++ b/client-api/src/main/java/org/jiayunet/service/UserProfileService.java @@ -2,6 +2,8 @@ package org.jiayunet.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.extern.slf4j.Slf4j; +import org.jiayunet.exception.BusinessException; +import org.jiayunet.exception.BusinessExpCodeEnum; import org.jiayunet.mapper.*; import org.jiayunet.pojo.po.*; import org.jiayunet.tool.UserSecurityTool; @@ -11,6 +13,8 @@ import org.springframework.transaction.annotation.Transactional; import java.time.Instant; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.IntStream; /** @@ -47,6 +51,34 @@ public class UserProfileService { @Autowired private org.jiayunet.service.UserProfileAnalyzeService userProfileAnalyzeService; + @Autowired + private UserResumeMapper userResumeMapper; + + @Autowired + private UserResumeEducationMapper resumeEducationMapper; + + @Autowired + private UserResumeWorkMapper resumeWorkMapper; + + @Autowired + private UserResumeInternshipMapper resumeInternshipMapper; + + @Autowired + private UserResumeProjectMapper resumeProjectMapper; + + @Autowired + private UserResumeCompetitionMapper resumeCompetitionMapper; + + /** 学历文本→枚举映射 */ + private static final Map DEGREE_MAP = Map.of( + "大专", 1, "本科", 2, "硕士", 3, "博士", 4 + ); + + /** 学习形式文本→枚举映射 */ + private static final Map STUDY_TYPE_MAP = Map.of( + "全日制", 0, "非全日制", 1 + ); + // ==================== 主表 ==================== /** 查询当前用户个人资料 */ @@ -265,6 +297,178 @@ public class UserProfileService { userProfileAnalyzeService.analyzeUserProfile(userId); } + // ==================== 从简历同步到个人资料 ==================== + + /** + * 根据简历ID同步更新个人资料 + *

1. 校验简历归属 2. 读取简历主表+5子表 3. 事务内覆盖写入Profile主表+5子表 4. 触发一次异步AI分析

+ */ + @Transactional(rollbackFor = Exception.class) + public void syncProfileFromResume(Long resumeId) { + Long userId = UserSecurityTool.getUserId(); + + // 1. 校验简历归属当前用户 + UserResume resume = userResumeMapper.selectById(resumeId); + if (resume == null || !resume.getUserId().equals(userId)) { + throw new BusinessException(BusinessExpCodeEnum.PERMISSION_DENIED, "简历不存在或无权操作"); + } + + // 2. 读取简历5张子表 + List resumeEducationList = resumeEducationMapper.selectList( + new LambdaQueryWrapper().eq(UserResumeEducation::getResumeId, resumeId).orderByAsc(UserResumeEducation::getSortOrder)); + List resumeWorkList = resumeWorkMapper.selectList( + new LambdaQueryWrapper().eq(UserResumeWork::getResumeId, resumeId).orderByAsc(UserResumeWork::getSortOrder)); + List resumeInternshipList = resumeInternshipMapper.selectList( + new LambdaQueryWrapper().eq(UserResumeInternship::getResumeId, resumeId).orderByAsc(UserResumeInternship::getSortOrder)); + List resumeProjectList = resumeProjectMapper.selectList( + new LambdaQueryWrapper().eq(UserResumeProject::getResumeId, resumeId).orderByAsc(UserResumeProject::getSortOrder)); + List resumeCompetitionList = resumeCompetitionMapper.selectList( + new LambdaQueryWrapper().eq(UserResumeCompetition::getResumeId, resumeId).orderByAsc(UserResumeCompetition::getSortOrder)); + + Instant now = Instant.now(); + + // 3. 同步主表(upsert,只更新共有字段,保留Profile独有字段原值) + UserProfile existing = userProfileMapper.selectOne( + new LambdaQueryWrapper().eq(UserProfile::getUserId, userId)); + Long profileId; + if (existing != null) { + profileId = existing.getId(); + existing.setName(resume.getName()); + existing.setEmail(resume.getEmail()); + existing.setMobileNumber(resume.getMobileNumber()); + existing.setWechatNumber(resume.getWechatNumber()); + existing.setPortfolioUrl(resume.getPortfolioUrl()); + existing.setSkills(resume.getSkills()); + existing.setCertificates(resume.getCertificates()); + existing.setUpdateTime(now); + userProfileMapper.updateById(existing); + } else { + UserProfile profile = new UserProfile(); + profile.setUserId(userId); + profile.setName(resume.getName()); + profile.setEmail(resume.getEmail()); + profile.setMobileNumber(resume.getMobileNumber()); + profile.setWechatNumber(resume.getWechatNumber()); + profile.setPortfolioUrl(resume.getPortfolioUrl()); + profile.setSkills(resume.getSkills()); + profile.setCertificates(resume.getCertificates()); + profile.setCreateTime(now); + profile.setUpdateTime(now); + userProfileMapper.insert(profile); + profileId = profile.getId(); + } + + // 4. 同步教育经历(先删后插,含degree/studyType映射) + educationMapper.delete(new LambdaQueryWrapper().eq(UserProfileEducation::getUserId, userId)); + if (!resumeEducationList.isEmpty()) { + List profileEducationList = IntStream.range(0, resumeEducationList.size()).mapToObj(i -> { + UserResumeEducation src = resumeEducationList.get(i); + UserProfileEducation dest = new UserProfileEducation(); + dest.setProfileId(profileId); + dest.setUserId(userId); + dest.setSchool(src.getSchool()); + dest.setMajor(src.getMajor()); + dest.setDegree(DEGREE_MAP.get(src.getDegree())); + dest.setStudyType(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()); + educationMapper.batchInsert(profileEducationList); + } + + // 5. 同步工作经历 + workMapper.delete(new LambdaQueryWrapper().eq(UserProfileWork::getUserId, userId)); + if (!resumeWorkList.isEmpty()) { + List profileWorkList = IntStream.range(0, resumeWorkList.size()).mapToObj(i -> { + UserResumeWork src = resumeWorkList.get(i); + UserProfileWork dest = new UserProfileWork(); + dest.setProfileId(profileId); + 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()); + workMapper.batchInsert(profileWorkList); + } + + // 6. 同步实习经历 + internshipMapper.delete(new LambdaQueryWrapper().eq(UserProfileInternship::getUserId, userId)); + if (!resumeInternshipList.isEmpty()) { + List profileInternshipList = IntStream.range(0, resumeInternshipList.size()).mapToObj(i -> { + UserResumeInternship src = resumeInternshipList.get(i); + UserProfileInternship dest = new UserProfileInternship(); + dest.setProfileId(profileId); + 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()); + internshipMapper.batchInsert(profileInternshipList); + } + + // 7. 同步项目经历 + projectMapper.delete(new LambdaQueryWrapper().eq(UserProfileProject::getUserId, userId)); + if (!resumeProjectList.isEmpty()) { + List profileProjectList = IntStream.range(0, resumeProjectList.size()).mapToObj(i -> { + UserResumeProject src = resumeProjectList.get(i); + UserProfileProject dest = new UserProfileProject(); + dest.setProfileId(profileId); + 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()); + projectMapper.batchInsert(profileProjectList); + } + + // 8. 同步竞赛经历 + competitionMapper.delete(new LambdaQueryWrapper().eq(UserProfileCompetition::getUserId, userId)); + if (!resumeCompetitionList.isEmpty()) { + List profileCompetitionList = IntStream.range(0, resumeCompetitionList.size()).mapToObj(i -> { + UserResumeCompetition src = resumeCompetitionList.get(i); + UserProfileCompetition dest = new UserProfileCompetition(); + dest.setProfileId(profileId); + 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()); + competitionMapper.batchInsert(profileCompetitionList); + } + + // 9. 事务内所有数据写入完成,触发一次异步AI分析 + userProfileAnalyzeService.analyzeUserProfile(userId); + } + // ==================== 内部方法 ==================== /**