fix(aihr): scope candidate links to tenant

This commit is contained in:
2026-07-14 06:31:04 +08:00
parent e332efb731
commit 0f647f8086
4 changed files with 24 additions and 19 deletions
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.aihr.domain.AihrInterviewDto.AnswerRequest;
import org.dromara.aihr.domain.AihrInterviewDto.AnswerResponse;
import org.dromara.aihr.domain.AihrInterviewDto.CandidateLinkRequest;
@@ -141,9 +142,9 @@ public class AihrInterviewService {
return jdbcTemplate.query("""
SELECT candidate_id, ext_party_id, status, linked_time
FROM aihr_candidate_employee_link
WHERE tenant_id = '000000' AND candidate_id = ?
WHERE tenant_id = ? AND candidate_id = ?
LIMIT 1
""", this::mapCandidateLink, normalizedCandidateId).stream().findFirst().orElse(null);
""", this::mapCandidateLink, tenantId(), normalizedCandidateId).stream().findFirst().orElse(null);
}
public CandidateLinkResponse linkCandidate(CandidateLinkRequest request, String operator) {
@@ -159,19 +160,19 @@ public class AihrInterviewService {
Integer activeEmployee = jdbcTemplate.queryForObject("""
SELECT COUNT(*)
FROM aihr_org_snapshot
WHERE tenant_id = '000000'
WHERE tenant_id = ?
AND employment_status = 'active'
AND ext_party_id = ?
""", Integer.class, extPartyId);
""", Integer.class, tenantId(), extPartyId);
if (activeEmployee == null || activeEmployee == 0) {
throw new IllegalArgumentException("员工主体不存在或已离职,请先同步在职组织快照");
}
CandidateLinkResponse existingPartyLink = jdbcTemplate.query("""
SELECT candidate_id, ext_party_id, status, linked_time
FROM aihr_candidate_employee_link
WHERE tenant_id = '000000' AND ext_party_id = ?
WHERE tenant_id = ? AND ext_party_id = ?
LIMIT 1
""", this::mapCandidateLink, extPartyId).stream().findFirst().orElse(null);
""", this::mapCandidateLink, tenantId(), extPartyId).stream().findFirst().orElse(null);
if (existingPartyLink != null && !candidateId.equals(existingPartyLink.candidateId())) {
throw new IllegalArgumentException("员工主体已关联其他候选人,请先由HR核对后处理");
}
@@ -182,10 +183,10 @@ public class AihrInterviewService {
jdbcTemplate.update("""
INSERT INTO aihr_candidate_employee_link
(tenant_id, candidate_id, ext_party_id, status, linked_by, linked_time, create_time, update_time)
VALUES ('000000', ?, ?, '已关联', ?, now(), now(), now())
VALUES (?, ?, ?, '已关联', ?, now(), now(), now())
ON DUPLICATE KEY UPDATE
status = '已关联', linked_by = VALUES(linked_by), linked_time = now(), update_time = now()
""", candidateId, extPartyId, firstNonBlank(operator, "unknown"));
""", tenantId(), candidateId, extPartyId, firstNonBlank(operator, "unknown"));
return candidateLink(candidateId);
}
@@ -211,6 +212,10 @@ public class AihrInterviewService {
);
}
private String tenantId() {
return firstNonBlank(LoginHelper.getTenantId(), "000000");
}
private void saveResult(String sessionId, InterviewSession session, ScoreResult score) {
ensureResultTable();
jdbcTemplate.update("""
@@ -27,14 +27,14 @@ class AihrInterviewServiceTest {
void linkCandidateRejectsMissingOrInactiveEmployee() {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
AihrInterviewService service = new AihrInterviewService(new ObjectMapper(), null, jdbcTemplate);
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("employee-inactive"))).thenReturn(0);
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("000000"), eq("employee-inactive"))).thenReturn(0);
IllegalArgumentException error = assertThrows(IllegalArgumentException.class, () -> service.linkCandidate(
new CandidateLinkRequest("candidate-1", "employee-inactive"), "hr"
));
assertEquals("员工主体不存在或已离职,请先同步在职组织快照", error.getMessage());
verify(jdbcTemplate, never()).update(anyString(), eq("candidate-1"), eq("employee-inactive"), eq("hr"));
verify(jdbcTemplate, never()).update(anyString(), eq("000000"), eq("candidate-1"), eq("employee-inactive"), eq("hr"));
}
@Test
@@ -44,14 +44,14 @@ class AihrInterviewServiceTest {
AihrInterviewService service = spy(new AihrInterviewService(new ObjectMapper(), null, jdbcTemplate));
CandidateLinkResponse current = new CandidateLinkResponse("candidate-2", "employee-old", "已关联", "07-14 06:00");
doReturn(current).when(service).candidateLink("candidate-2");
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("employee-new"))).thenReturn(1);
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("000000"), eq("employee-new"))).thenReturn(1);
IllegalArgumentException error = assertThrows(IllegalArgumentException.class, () -> service.linkCandidate(
new CandidateLinkRequest("candidate-2", "employee-new"), "hr"
));
assertEquals("候选人已关联其他员工主体,请先由HR核对后处理", error.getMessage());
verify(jdbcTemplate, never()).update(anyString(), eq("candidate-2"), eq("employee-new"), eq("hr"));
verify(jdbcTemplate, never()).update(anyString(), eq("000000"), eq("candidate-2"), eq("employee-new"), eq("hr"));
}
@Test
@@ -61,8 +61,8 @@ class AihrInterviewServiceTest {
AihrInterviewService service = spy(new AihrInterviewService(new ObjectMapper(), null, jdbcTemplate));
CandidateLinkResponse existing = new CandidateLinkResponse("candidate-old", "employee-4", "已关联", "07-14 06:00");
doReturn(null).when(service).candidateLink("candidate-4");
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("employee-4"))).thenReturn(1);
when(jdbcTemplate.query(anyString(), org.mockito.ArgumentMatchers.<RowMapper<CandidateLinkResponse>>any(), eq("employee-4")))
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("000000"), eq("employee-4"))).thenReturn(1);
when(jdbcTemplate.query(anyString(), org.mockito.ArgumentMatchers.<RowMapper<CandidateLinkResponse>>any(), eq("000000"), eq("employee-4")))
.thenReturn(java.util.List.of(existing));
IllegalArgumentException error = assertThrows(IllegalArgumentException.class, () -> service.linkCandidate(
@@ -70,7 +70,7 @@ class AihrInterviewServiceTest {
));
assertEquals("员工主体已关联其他候选人,请先由HR核对后处理", error.getMessage());
verify(jdbcTemplate, never()).update(anyString(), eq("candidate-4"), eq("employee-4"), eq("hr"));
verify(jdbcTemplate, never()).update(anyString(), eq("000000"), eq("candidate-4"), eq("employee-4"), eq("hr"));
}
@Test
@@ -80,13 +80,13 @@ class AihrInterviewServiceTest {
AihrInterviewService service = spy(new AihrInterviewService(new ObjectMapper(), null, jdbcTemplate));
CandidateLinkResponse linked = new CandidateLinkResponse("candidate-3", "employee-3", "已关联", "07-14 06:10");
doReturn(linked).when(service).candidateLink("candidate-3");
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("employee-3"))).thenReturn(1);
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), eq("000000"), eq("employee-3"))).thenReturn(1);
CandidateLinkResponse result = service.linkCandidate(
new CandidateLinkRequest("candidate-3", "employee-3"), "hr"
);
assertSame(linked, result);
verify(jdbcTemplate).update(anyString(), eq("candidate-3"), eq("employee-3"), eq("hr"));
verify(jdbcTemplate).update(anyString(), eq("000000"), eq("candidate-3"), eq("employee-3"), eq("hr"));
}
}