fix(aihr): prevent duplicate employee principal links
This commit is contained in:
+20
-7
@@ -143,12 +143,7 @@ public class AihrInterviewService {
|
||||
FROM aihr_candidate_employee_link
|
||||
WHERE tenant_id = '000000' AND candidate_id = ?
|
||||
LIMIT 1
|
||||
""", (rs, rowNum) -> new CandidateLinkResponse(
|
||||
rs.getString("candidate_id"),
|
||||
rs.getString("ext_party_id"),
|
||||
rs.getString("status"),
|
||||
rs.getTimestamp("linked_time") == null ? "" : rs.getTimestamp("linked_time").toLocalDateTime().format(TIME_FORMAT)
|
||||
), normalizedCandidateId).stream().findFirst().orElse(null);
|
||||
""", this::mapCandidateLink, normalizedCandidateId).stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public CandidateLinkResponse linkCandidate(CandidateLinkRequest request, String operator) {
|
||||
@@ -171,6 +166,15 @@ public class AihrInterviewService {
|
||||
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 = ?
|
||||
LIMIT 1
|
||||
""", this::mapCandidateLink, extPartyId).stream().findFirst().orElse(null);
|
||||
if (existingPartyLink != null && !candidateId.equals(existingPartyLink.candidateId())) {
|
||||
throw new IllegalArgumentException("员工主体已关联其他候选人,请先由HR核对后处理");
|
||||
}
|
||||
CandidateLinkResponse current = candidateLink(candidateId);
|
||||
if (current != null && !extPartyId.equals(current.extPartyId())) {
|
||||
throw new IllegalArgumentException("候选人已关联其他员工主体,请先由HR核对后处理");
|
||||
@@ -198,6 +202,15 @@ public class AihrInterviewService {
|
||||
);
|
||||
}
|
||||
|
||||
private CandidateLinkResponse mapCandidateLink(ResultSet rs, int rowNum) throws SQLException {
|
||||
return new CandidateLinkResponse(
|
||||
rs.getString("candidate_id"),
|
||||
rs.getString("ext_party_id"),
|
||||
rs.getString("status"),
|
||||
rs.getTimestamp("linked_time") == null ? "" : rs.getTimestamp("linked_time").toLocalDateTime().format(TIME_FORMAT)
|
||||
);
|
||||
}
|
||||
|
||||
private void saveResult(String sessionId, InterviewSession session, ScoreResult score) {
|
||||
ensureResultTable();
|
||||
jdbcTemplate.update("""
|
||||
@@ -303,7 +316,7 @@ public class AihrInterviewService {
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_aihr_candidate_employee_link_candidate` (`tenant_id`, `candidate_id`),
|
||||
KEY `idx_aihr_candidate_employee_link_party` (`tenant_id`, `ext_party_id`)
|
||||
UNIQUE KEY `uk_aihr_candidate_employee_link_party` (`tenant_id`, `ext_party_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI HR 候选人与员工主体关联';
|
||||
""");
|
||||
candidateLinkTableReady = true;
|
||||
|
||||
+20
@@ -6,6 +6,7 @@ import org.dromara.aihr.domain.AihrInterviewDto.CandidateLinkResponse;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
@@ -53,6 +54,25 @@ class AihrInterviewServiceTest {
|
||||
verify(jdbcTemplate, never()).update(anyString(), eq("candidate-2"), eq("employee-new"), eq("hr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("dev")
|
||||
void linkCandidateRejectsAnEmployeeAlreadyLinkedToAnotherCandidate() {
|
||||
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
|
||||
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")))
|
||||
.thenReturn(java.util.List.of(existing));
|
||||
|
||||
IllegalArgumentException error = assertThrows(IllegalArgumentException.class, () -> service.linkCandidate(
|
||||
new CandidateLinkRequest("candidate-4", "employee-4"), "hr"
|
||||
));
|
||||
|
||||
assertEquals("员工主体已关联其他候选人,请先由HR核对后处理", error.getMessage());
|
||||
verify(jdbcTemplate, never()).update(anyString(), eq("candidate-4"), eq("employee-4"), eq("hr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("dev")
|
||||
void linkCandidateIsIdempotentForTheSameEmployee() {
|
||||
|
||||
Reference in New Issue
Block a user