feat(aihr): map supervisor phone to org scope
This commit is contained in:
+47
-5
@@ -280,13 +280,14 @@ public class AihrOrgSyncService {
|
||||
}
|
||||
jdbcTemplate.batchUpdate("""
|
||||
insert into aihr_org_snapshot
|
||||
(tenant_id, project_code, project_name, dept_name, ext_party_id, person_name,
|
||||
(tenant_id, project_code, project_name, dept_name, ext_party_id, person_phone, person_name,
|
||||
position_name, position_level, employment_status, snapshot_date, create_time)
|
||||
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, now())
|
||||
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, now())
|
||||
on duplicate key update
|
||||
project_code = values(project_code),
|
||||
project_name = values(project_name),
|
||||
dept_name = values(dept_name),
|
||||
person_phone = values(person_phone),
|
||||
person_name = values(person_name),
|
||||
position_name = values(position_name),
|
||||
position_level = values(position_level),
|
||||
@@ -320,16 +321,18 @@ public class AihrOrgSyncService {
|
||||
deptName,
|
||||
"组织架构"
|
||||
);
|
||||
String positionName = firstNonBlank(text(employee, "position_name", "positionName", "job_title", "jobTitle", "title"), "员工");
|
||||
String positionName = firstNonBlank(text(employee, "position_name", "positionName", "job_title", "jobTitle",
|
||||
"post_name", "postName", "job_name", "jobName", "role_name", "roleName", "position", "title"), "员工");
|
||||
String positionLevel = firstNonBlank(text(employee, "position_level", "positionLevel", "job_level", "jobLevel"), level(positionName));
|
||||
return new OrgRow(
|
||||
projectCode,
|
||||
projectName,
|
||||
deptName,
|
||||
extPartyId,
|
||||
phone(employee),
|
||||
firstNonBlank(text(employee, "name", "person_name", "personName", "employee_name", "employeeName"), "-"),
|
||||
positionName,
|
||||
positionLevel,
|
||||
positionLevel(positionLevel, positionName),
|
||||
status(text(employee, "status", "employment_status", "employmentStatus")),
|
||||
LocalDate.now()
|
||||
);
|
||||
@@ -437,11 +440,35 @@ public class AihrOrgSyncService {
|
||||
if (count == null || count == 0) {
|
||||
throw new IllegalArgumentException("请先执行 aihr_org_snapshot_mysql8.sql 初始化组织人员快照表");
|
||||
}
|
||||
ensureSnapshotColumn("person_phone", "ALTER TABLE aihr_org_snapshot ADD COLUMN `person_phone` varchar(30) DEFAULT NULL COMMENT '手机号(仅用于登录身份映射)' AFTER `ext_party_id`");
|
||||
ensureSnapshotIndex("idx_aihr_org_phone", "ALTER TABLE aihr_org_snapshot ADD KEY `idx_aihr_org_phone` (`tenant_id`, `person_phone`)");
|
||||
} catch (DataAccessException e) {
|
||||
throw new IllegalStateException("组织人员快照表检查失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureSnapshotColumn(String column, String ddl) {
|
||||
Integer exists = jdbcTemplate.queryForObject("""
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'aihr_org_snapshot' AND COLUMN_NAME = ?
|
||||
""", Integer.class, column);
|
||||
if (exists == null || exists == 0) {
|
||||
jdbcTemplate.execute(ddl);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureSnapshotIndex(String index, String ddl) {
|
||||
Integer exists = jdbcTemplate.queryForObject("""
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'aihr_org_snapshot' AND INDEX_NAME = ?
|
||||
""", Integer.class, index);
|
||||
if (exists == null || exists == 0) {
|
||||
jdbcTemplate.execute(ddl);
|
||||
}
|
||||
}
|
||||
|
||||
private static String status(String value) {
|
||||
String text = clean(value).toLowerCase();
|
||||
if (text.isBlank() || text.equals("1") || text.equals("active") || text.equals("enabled")
|
||||
@@ -466,6 +493,20 @@ public class AihrOrgSyncService {
|
||||
return "一线";
|
||||
}
|
||||
|
||||
private static String positionLevel(String rawLevel, String positionName) {
|
||||
String inferred = level(positionName);
|
||||
String level = clean(rawLevel);
|
||||
if (level.isBlank() || (("一线".equals(level) || "员工".equals(level)) && !"一线".equals(inferred))) {
|
||||
return inferred;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
private static String phone(JsonNode employee) {
|
||||
return text(employee, "phone", "mobile", "mobile_phone", "mobilePhone", "contact_phone",
|
||||
"contactPhone", "phonenumber", "telephone", "tel");
|
||||
}
|
||||
|
||||
private static String text(JsonNode node, String... fields) {
|
||||
for (String field : fields) {
|
||||
JsonNode child = node.path(field);
|
||||
@@ -564,6 +605,7 @@ public class AihrOrgSyncService {
|
||||
String projectName,
|
||||
String deptName,
|
||||
String extPartyId,
|
||||
String personPhone,
|
||||
String personName,
|
||||
String positionName,
|
||||
String positionLevel,
|
||||
@@ -572,7 +614,7 @@ public class AihrOrgSyncService {
|
||||
) {
|
||||
Object[] args() {
|
||||
return new Object[] {
|
||||
TENANT_ID, projectCode, projectName, deptName, extPartyId, personName,
|
||||
TENANT_ID, projectCode, projectName, deptName, extPartyId, personPhone, personName,
|
||||
positionName, positionLevel, employmentStatus, snapshotDate
|
||||
};
|
||||
}
|
||||
|
||||
+21
-2
@@ -1366,12 +1366,22 @@ public class AihrPracticeSeedService {
|
||||
if (owner.isEmpty() || !orgSnapshotTableExists()) {
|
||||
return TeamScope.unscoped();
|
||||
}
|
||||
boolean hasPhone = orgSnapshotColumnExists("person_phone");
|
||||
List<Object> ownerArgs = new ArrayList<>();
|
||||
ownerArgs.add(TENANT_ID);
|
||||
ownerArgs.add(owner);
|
||||
String ownerWhere = "ext_party_id = ?";
|
||||
if (hasPhone) {
|
||||
ownerWhere = "(ext_party_id = ? OR person_phone = ?)";
|
||||
ownerArgs.add(owner);
|
||||
}
|
||||
List<OrgScopeRow> owners = jdbcTemplate.query("""
|
||||
SELECT project_code, position_level
|
||||
FROM aihr_org_snapshot
|
||||
WHERE tenant_id = ? AND ext_party_id = ? AND employment_status = 'active'
|
||||
WHERE tenant_id = ? AND """ + ownerWhere + """
|
||||
AND employment_status = 'active'
|
||||
LIMIT 1
|
||||
""", (rs, rowNum) -> new OrgScopeRow(rs.getString("project_code"), rs.getString("position_level")), TENANT_ID, owner);
|
||||
""", (rs, rowNum) -> new OrgScopeRow(rs.getString("project_code"), rs.getString("position_level")), ownerArgs.toArray());
|
||||
if (owners.isEmpty()) {
|
||||
return TeamScope.unscoped();
|
||||
}
|
||||
@@ -1397,6 +1407,15 @@ public class AihrPracticeSeedService {
|
||||
return count != null && count > 0;
|
||||
}
|
||||
|
||||
private boolean orgSnapshotColumnExists(String column) {
|
||||
Integer count = jdbcTemplate.queryForObject("""
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'aihr_org_snapshot' AND COLUMN_NAME = ?
|
||||
""", Integer.class, column);
|
||||
return count != null && count > 0;
|
||||
}
|
||||
|
||||
private static boolean canSeeProject(String positionLevel) {
|
||||
return "主管".equals(positionLevel) || "项目经理".equals(positionLevel);
|
||||
}
|
||||
|
||||
+17
@@ -93,6 +93,19 @@ public class AihrPracticeSeedServiceTest {
|
||||
assertEquals(List.of("000000", "SUP-1", 5), jdbcTemplate.reviewArgs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pendingReviewRecordsResolvesSupervisorPhone() {
|
||||
PracticeReviewJdbcTemplate jdbcTemplate = new PracticeReviewJdbcTemplate("主管", List.of("SUP-1", "EMP-1"));
|
||||
AihrPracticeSeedService service = new AihrPracticeSeedService(new ObjectMapper(), jdbcTemplate, null, null);
|
||||
|
||||
service.pendingReviewRecords("13800000001", "", "", 5);
|
||||
|
||||
assertTrue(jdbcTemplate.ownerSql.contains("person_phone = ?"));
|
||||
assertEquals(List.of("000000", "13800000001", "13800000001"), jdbcTemplate.ownerArgs);
|
||||
assertTrue(jdbcTemplate.reviewSql.contains("ext_party_id IN (?,?)"));
|
||||
assertEquals(List.of("000000", "SUP-1", "EMP-1", 5), jdbcTemplate.reviewArgs);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static <T> List<T> mapOne(InvocationOnMock invocation, Map<String, String> values) throws SQLException {
|
||||
RowMapper mapper = invocation.getArgument(1);
|
||||
@@ -116,6 +129,8 @@ public class AihrPracticeSeedServiceTest {
|
||||
private static final class PracticeReviewJdbcTemplate extends JdbcTemplate {
|
||||
private final String ownerLevel;
|
||||
private final List<String> scopeIds;
|
||||
private String ownerSql = "";
|
||||
private List<Object> ownerArgs = List.of();
|
||||
private String reviewSql = "";
|
||||
private List<Object> reviewArgs = List.of();
|
||||
|
||||
@@ -148,6 +163,8 @@ public class AihrPracticeSeedServiceTest {
|
||||
return List.of();
|
||||
}
|
||||
if (sql.contains("SELECT project_code") && sql.contains("LIMIT 1")) {
|
||||
ownerSql = sql;
|
||||
ownerArgs = List.of(args);
|
||||
return mapRows(rowMapper, List.of(Map.of(
|
||||
"project_code", "P1",
|
||||
"position_level", ownerLevel
|
||||
|
||||
@@ -11,6 +11,7 @@ CREATE TABLE `aihr_org_snapshot` (
|
||||
`project_name` varchar(100) NOT NULL COMMENT '项目名称',
|
||||
`dept_name` varchar(100) DEFAULT NULL COMMENT '部门/条线',
|
||||
`ext_party_id` varchar(100) NOT NULL COMMENT '外部主体ID(只存ID,不回写外部系统)',
|
||||
`person_phone` varchar(30) DEFAULT NULL COMMENT '手机号(仅用于登录身份映射)',
|
||||
`person_name` varchar(100) DEFAULT NULL COMMENT '姓名(Demo 快照允许,生产走脱敏闸门G3)',
|
||||
`position_name` varchar(100) DEFAULT NULL COMMENT '岗位',
|
||||
`position_level` varchar(30) DEFAULT NULL COMMENT '岗位层级(一线/主管/项目经理)',
|
||||
@@ -19,6 +20,7 @@ CREATE TABLE `aihr_org_snapshot` (
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_aihr_org_party` (`tenant_id`, `ext_party_id`),
|
||||
KEY `idx_aihr_org_phone` (`tenant_id`, `person_phone`),
|
||||
KEY `idx_aihr_org_project` (`tenant_id`, `project_code`, `position_level`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='组织人员静态快照(Demo)';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user