fix(aihr): restore org snapshot anomaly counts

This commit is contained in:
2026-07-14 09:55:26 +08:00
parent 829ce331f6
commit 91bd28811d
4 changed files with 55 additions and 4 deletions
@@ -187,10 +187,10 @@ public class AihrOrgSyncService {
select count(*) from aihr_org_snapshot select count(*) from aihr_org_snapshot
""" + where + """ """ + where + """
and ( and (
person_name like '%?%' instr(coalesce(person_name, ''), '?') > 0
or project_name like '%?%' or instr(coalesce(project_name, ''), '?') > 0
or dept_name like '%?%' or instr(coalesce(dept_name, ''), '?') > 0
or position_name like '%?%' or instr(coalesce(position_name, ''), '?') > 0
) )
""", args); """, args);
List<Object> rowArgs = new ArrayList<>(args); List<Object> rowArgs = new ArrayList<>(args);
@@ -6,11 +6,13 @@ import org.dromara.aihr.domain.AihrOrgSyncDto.SyncRequest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tag;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -54,6 +56,21 @@ public class AihrOrgSyncServiceTest {
assertFalse(AihrOrgSyncService.hasUnusablePhoneCandidate(mapper.readTree("{\"name\":\"张三\"}"))); assertFalse(AihrOrgSyncService.hasUnusablePhoneCandidate(mapper.readTree("{\"name\":\"张三\"}")));
} }
@Test
public void snapshotSuspectTextQuerySearchesLiteralQuestionMarks() {
SnapshotJdbcTemplate jdbcTemplate = new SnapshotJdbcTemplate();
AihrOrgSyncService service = new AihrOrgSyncService(new ObjectMapper(), jdbcTemplate, mock(TransactionTemplate.class));
service.snapshot(null, null, null, null, 1, 20, null);
String suspectQuery = jdbcTemplate.queries.stream()
.filter(sql -> sql.contains("coalesce(person_name"))
.findFirst()
.orElseThrow();
assertTrue(suspectQuery.contains("instr(coalesce(person_name, ''), '?') > 0"));
assertFalse(suspectQuery.contains("like '%?%'"));
}
@Test @Test
public void dryRunNeverTouchesSnapshotSchemaWhenExternalFetchFails() { public void dryRunNeverTouchesSnapshotSchemaWhenExternalFetchFails() {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class); JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
@@ -110,4 +127,31 @@ public class AihrOrgSyncServiceTest {
server.stop(0); server.stop(0);
} }
} }
private static final class SnapshotJdbcTemplate extends JdbcTemplate {
private final List<String> queries = new ArrayList<>();
@Override
public <T> T queryForObject(String sql, Class<T> requiredType) {
queries.add(sql);
return requiredType.cast(1);
}
@Override
public <T> T queryForObject(String sql, Class<T> requiredType, Object... args) {
queries.add(sql);
return requiredType.cast(0);
}
@Override
public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args) {
queries.add(sql);
return List.of();
}
@Override
public void execute(String sql) {
// Schema setup is irrelevant for this query contract test.
}
}
} }
+6
View File
@@ -80,6 +80,12 @@
4. 在业务提供一期视频样片及字段口径后,继续扩案例详情数据契约;当前原始音频回放已完成,但不替代视频、学习点和主管点评。 4. 在业务提供一期视频样片及字段口径后,继续扩案例详情数据契约;当前原始音频回放已完成,但不替代视频、学习点和主管点评。
5. 用正式试点窗口跑 BRD L1/M5,而不是继续增加视觉页面。 5. 用正式试点窗口跑 BRD L1/M5,而不是继续增加视觉页面。
## 2026-07-14 增量:组织快照异常统计修复
- 发现 `AihrOrgSyncService.snapshot()` 的疑似乱码统计把问号写成 `like '%?%'`,问号位于 SQL 字符串字面量内,未形成有效的参数或字符匹配,导致管理端组织快照的异常计数可能恒为 0。
- 已改为 MySQL `instr(coalesce(field, ''), '?') > 0`,继续沿用当前租户和筛选条件,并补回归测试与 `demo-check` marker;本地 `ry-vue` 直接查询得到 `3473` 条快照、`8` 条疑似异常文本,与修复后的统计口径一致。
- 该修复只恢复异常计数的真实性,不代表外部组织快照已经具备完整手机号映射;正式主管身份和项目范围仍需真实组织同步数据。
## 5. 每批完成定义 ## 5. 每批完成定义
- 有明确 BRD 条款、代码路径和数据证据。 - 有明确 BRD 条款、代码路径和数据证据。
+1
View File
@@ -455,6 +455,7 @@ contains mobile-uni/src/pages/supervisor/review/index.vue "人工贡献分"
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "/practice/reviews/{id}/reviewed" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "/practice/reviews/{id}/reviewed"
contains mobile-uni/src/pages/supervisor/review/index.vue "标记已复盘" contains mobile-uni/src/pages/supervisor/review/index.vue "标记已复盘"
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "source = 'retry'" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "source = 'retry'"
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java "instr(coalesce(person_name, '')"
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "maskSensitiveText" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "maskSensitiveText"
contains mobile-uni/src/services/practice.ts "/api/aihr/mobile/practice/assignments" contains mobile-uni/src/services/practice.ts "/api/aihr/mobile/practice/assignments"
contains frontend/src/api/aihr/case.ts "/api/knowledge/case/upload" contains frontend/src/api/aihr/case.ts "/api/knowledge/case/upload"