feat(aihr): flag masked org phones in precheck

This commit is contained in:
2026-07-09 22:06:59 +08:00
parent 3b034f6903
commit cc3581a27a
6 changed files with 40 additions and 5 deletions
@@ -28,6 +28,7 @@ public final class AihrOrgSyncDto {
int syncedCount,
int skippedCount,
int phoneLinked,
int maskedPhone,
int suspectText,
List<String> warnings
) {
@@ -42,6 +42,9 @@ public class AihrOrgSyncService {
private static final int MAX_PAGE_SIZE = 500;
private static final int DEFAULT_MAX_PAGES = 200;
private static final Duration HTTP_TIMEOUT = Duration.ofSeconds(30);
private static final String[] PHONE_FIELDS = {"phone", "phone_number", "phoneNumber", "mobile", "mobile_phone",
"mobilePhone", "mobile_tel", "mobileTel", "cellphone", "cellPhone", "contact_phone",
"contactPhone", "contact_mobile", "contactMobile", "phonenumber", "telephone", "tel", "手机号", "手机", "联系电话"};
private final ObjectMapper objectMapper;
private final JdbcTemplate jdbcTemplate;
@@ -85,11 +88,15 @@ public class AihrOrgSyncService {
List<OrgRow> rows = new ArrayList<>();
int skipped = 0;
int maskedPhone = 0;
for (JsonNode employee : employeeItems) {
OrgRow row = orgRow(employee, companies, departments);
if (row == null) {
skipped++;
} else {
if (row.personPhone().isBlank() && hasUnusablePhoneCandidate(employee)) {
maskedPhone++;
}
rows.add(row);
}
}
@@ -101,6 +108,9 @@ public class AihrOrgSyncService {
if (suspectText > 0) {
warnings.add("外部员工快照存在 " + suspectText + " 条疑似乱码字段,请检查开放接口编码或导入链路");
}
if (maskedPhone > 0) {
warnings.add("外部员工快照存在 " + maskedPhone + " 条脱敏/不可用手机号,不能用于移动端正式登录映射");
}
if (!dryRun && replaceExisting && rows.isEmpty()) {
throw new IllegalArgumentException("外部员工快照为空,已阻止覆盖本地组织人员快照");
}
@@ -118,6 +128,7 @@ public class AihrOrgSyncService {
dryRun ? 0 : rows.size(),
skipped,
phoneLinked,
maskedPhone,
suspectText,
List.copyOf(warnings)
);
@@ -525,10 +536,7 @@ public class AihrOrgSyncService {
}
private static String phone(JsonNode employee) {
String[] fields = {"phone", "phone_number", "phoneNumber", "mobile", "mobile_phone",
"mobilePhone", "mobile_tel", "mobileTel", "cellphone", "cellPhone", "contact_phone",
"contactPhone", "contact_mobile", "contactMobile", "phonenumber", "telephone", "tel", "手机号", "手机", "联系电话"};
for (String field : fields) {
for (String field : PHONE_FIELDS) {
String normalized = normalizeMobilePhone(text(employee, field));
if (!normalized.isBlank()) {
return normalized;
@@ -545,6 +553,16 @@ public class AihrOrgSyncService {
return digits.matches("1[3-9]\\d{9}") ? digits : "";
}
static boolean hasUnusablePhoneCandidate(JsonNode employee) {
for (String field : PHONE_FIELDS) {
String value = text(employee, field);
if (!value.isBlank() && normalizeMobilePhone(value).isBlank()) {
return true;
}
}
return false;
}
private static boolean hasSuspectText(OrgRow row) {
return hasQuestionMark(row.projectName())
|| hasQuestionMark(row.deptName())
@@ -1,9 +1,12 @@
package org.dromara.aihr.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Tag("dev")
public class AihrOrgSyncServiceTest {
@@ -14,4 +17,13 @@ public class AihrOrgSyncServiceTest {
assertEquals("", AihrOrgSyncService.normalizeMobilePhone("139****1111"));
assertEquals("", AihrOrgSyncService.normalizeMobilePhone("025-88888888"));
}
@Test
public void unusablePhoneCandidateDetectsMaskedPhone() throws Exception {
ObjectMapper mapper = new ObjectMapper();
assertTrue(AihrOrgSyncService.hasUnusablePhoneCandidate(mapper.readTree("{\"phone\":\"139****1111\"}")));
assertFalse(AihrOrgSyncService.hasUnusablePhoneCandidate(mapper.readTree("{\"phone\":\"13900001111\"}")));
assertFalse(AihrOrgSyncService.hasUnusablePhoneCandidate(mapper.readTree("{\"name\":\"张三\"}")));
}
}