fix(aihr): validate candidate links

This commit is contained in:
2026-07-14 10:46:39 +08:00
parent 92afb51902
commit 94a3d0fe5b
4 changed files with 41 additions and 0 deletions
@@ -238,6 +238,9 @@ public class AihrInterviewService {
if (current != null && !extPartyId.equals(current.extPartyId())) {
throw new IllegalArgumentException("候选人已关联其他员工主体,请先由HR核对后处理");
}
if (current == null && !candidateExists(candidateId)) {
throw new IllegalArgumentException("候选人不存在,请先创建或完成一次面试记录");
}
jdbcTemplate.update("""
INSERT INTO aihr_candidate_employee_link
(tenant_id, candidate_id, ext_party_id, status, linked_by, linked_time, create_time, update_time)
@@ -248,6 +251,25 @@ public class AihrInterviewService {
return candidateLink(candidateId);
}
private boolean candidateExists(String candidateId) {
if (candidates.containsKey(candidateId)
|| sessions.values().stream().anyMatch(session -> candidateId.equals(session.candidate().id()))) {
return true;
}
try {
ensureResultTable();
Integer count = jdbcTemplate.queryForObject("""
SELECT COUNT(*)
FROM aihr_interview_result
WHERE tenant_id = ? AND candidate_id = ?
""", Integer.class, tenantId(), candidateId);
return count != null && count > 0;
} catch (DataAccessException e) {
log.warn("resolve interview candidate failed(处理错误已隐藏)");
return false;
}
}
private RecordResponse mapRecord(ResultSet rs, int rowNum) throws SQLException {
var finishedTime = rs.getTimestamp("finished_time");
Integer aiScore = rs.getInt("total_score");
@@ -103,6 +103,23 @@ class AihrInterviewServiceTest {
verify(jdbcTemplate).update(anyString(), eq("000000"), eq("candidate-3"), eq("employee-3"), eq("hr"));
}
@Test
@Tag("dev")
void linkCandidateRejectsUnknownCandidate() {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
AihrInterviewService service = spy(new AihrInterviewService(new ObjectMapper(), null, jdbcTemplate));
doReturn(null).when(service).candidateLink("candidate-unknown");
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("000000"), eq("employee-5"))).thenReturn(1);
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("000000"), eq("candidate-unknown"))).thenReturn(0);
IllegalArgumentException error = assertThrows(IllegalArgumentException.class, () -> service.linkCandidate(
new CandidateLinkRequest("candidate-unknown", "employee-5"), "hr"
));
assertEquals("候选人不存在,请先创建或完成一次面试记录", error.getMessage());
verify(jdbcTemplate, never()).update(anyString(), eq("000000"), eq("candidate-unknown"), eq("employee-5"), eq("hr"));
}
@Test
@Tag("dev")
void interviewRecordsAreScopedToCurrentTenant() {