Merge branch 'refs/heads/master' into test

This commit is contained in:
zk
2026-08-12 16:59:33 +08:00
2 changed files with 43 additions and 5 deletions
@@ -10,6 +10,8 @@ import org.jiayunet.tool.UserSecurityTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.time.Instant;
import java.util.List;
@@ -369,12 +371,41 @@ public class UserProfileService {
// ==================== 从简历同步到个人资料 ====================
/**
* 根据简历ID同步更新个人资料
* 根据简历ID同步更新个人资料AI分析同步执行,阻塞至分析完成)
* <p>1. 校验简历归属 2. 读取简历主表+6子表 3. 事务内覆盖写入Profile主表+6子表 4. 同步执行一次AI分析</p>
* <p>适用于前端需要拿到分析结果的场景(如简历上传解析后立即展示)</p>
*/
@Transactional(rollbackFor = Exception.class)
public void syncProfileFromResume(Long resumeId) {
Long userId = UserSecurityTool.getUserId();
copyResumeToProfile(userId, resumeId);
// 事务内所有数据写入完成,同步执行AI分析并等待完成
userProfileAnalyzeService.analyzeUserProfileSync(userId);
}
/**
* 根据简历ID同步更新个人资料(AI分析异步执行,立即返回)
* <p>1. 校验简历归属 2. 读取简历主表+6子表 3. 事务内覆盖写入Profile主表+6子表 4. 事务提交后异步触发AI分析</p>
* <p>适用于同步只是副作用、不需要等待分析结果的场景(如设置默认简历)</p>
*/
@Transactional(rollbackFor = Exception.class)
public void syncProfileFromResumeAsync(Long resumeId) {
Long userId = UserSecurityTool.getUserId();
copyResumeToProfile(userId, resumeId);
// AI分析必须等事务提交后再触发,否则异步线程读不到本次写入的数据
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
userProfileAnalyzeService.analyzeUserProfile(userId);
}
});
}
/**
* 将简历数据覆盖写入个人资料(主表+6子表),不触发AI分析
* <p>由 syncProfileFromResume / syncProfileFromResumeAsync 复用,须在事务内调用</p>
*/
private void copyResumeToProfile(Long userId, Long resumeId) {
// 1. 校验简历归属当前用户
UserResume resume = userResumeMapper.selectById(resumeId);
@@ -557,9 +588,6 @@ public class UserProfileService {
}).collect(Collectors.toList());
organizationMapper.batchInsert(profileOrganizationList);
}
// 10. 事务内所有数据写入完成,同步执行AI分析并等待完成
userProfileAnalyzeService.analyzeUserProfileSync(userId);
}
// ==================== 从个人资料生成简历 ====================
@@ -38,6 +38,9 @@ public class UserResumeService {
@Autowired
private MemberUserMapper memberUserMapper;
@Autowired
private UserProfileService userProfileService;
@Value("${app.resume.max-count:5}")
private int maxResumeCount;
@@ -142,6 +145,10 @@ public class UserResumeService {
.set(UserResume::getHobbies, resume.getHobbies())
.set(UserResume::getLanguageSkills, resume.getLanguageSkills())
.set(UserResume::getUpdateTime, now));
// 默认简历即用户主简历,内容变更后同步覆盖个人资料;AI提取异步执行,不阻塞本次请求
if (Integer.valueOf(1).equals(existing.getIsDefault())) {
userProfileService.syncProfileFromResumeAsync(resumeId);
}
return resumeId;
}
@@ -742,7 +749,8 @@ public class UserResumeService {
/**
* 设置默认简历
* <p>1. 查询用户所有简历,无简历则报错 2. 校验resumeId存在于用户简历中 3. 全部置为非默认,目标简历置为默认</p>
* <p>1. 查询用户所有简历,无简历则报错 2. 校验resumeId存在于用户简历中 3. 全部置为非默认,目标简历置为默认
* 4. 将该简历同步到个人资料,AI提取在事务提交后异步执行</p>
*/
@Transactional(rollbackFor = Exception.class)
public void setDefaultResume(Long resumeId) {
@@ -759,6 +767,8 @@ public class UserResumeService {
userResumeMapper.update(null, new LambdaUpdateWrapper<UserResume>().eq(UserResume::getUserId, userId).set(UserResume::getIsDefault, 0));
// 目标简历置为默认
userResumeMapper.update(null, new LambdaUpdateWrapper<UserResume>().eq(UserResume::getId, resumeId).set(UserResume::getIsDefault, 1));
// 默认简历即用户主简历,同步覆盖个人资料;AI提取异步执行,不阻塞本次请求
userProfileService.syncProfileFromResumeAsync(resumeId);
}
/**