fix(mobile): avoid seeded empty competency

This commit is contained in:
2026-07-09 15:15:14 +08:00
parent 25d3d1b8d5
commit aa547ef365
3 changed files with 52 additions and 5 deletions
@@ -765,11 +765,11 @@ public class AihrPracticeSeedService {
List<CompetencySnapshot> rows = jdbcTemplate.query("""
SELECT
COUNT(*) completed,
COALESCE(ROUND(AVG(total_score)), 82) score,
COALESCE(ROUND(AVG(total_score)), 0) score,
SUM(CASE WHEN status = '待复盘' THEN 1 ELSE 0 END) pending_review,
COALESCE(ROUND(AVG(dim_compliance)), 82) compliance,
COALESCE(ROUND(AVG(dim_communication)), 82) communication,
COALESCE(ROUND(AVG(dim_emotion)), 82) emotion,
COALESCE(ROUND(AVG(dim_compliance)), 0) compliance,
COALESCE(ROUND(AVG(dim_communication)), 0) communication,
COALESCE(ROUND(AVG(dim_emotion)), 0) emotion,
COALESCE(SUM(incentive_point), 0) incentive_points
FROM aihr_practice_session
WHERE tenant_id = ? AND mode = 'mobile' AND (? = '' OR ext_party_id = ?)
@@ -782,7 +782,7 @@ public class AihrPracticeSeedService {
rs.getInt("emotion"),
rs.getInt("incentive_points")
), TENANT_ID, party, party);
CompetencySnapshot snapshot = rows.isEmpty() ? new CompetencySnapshot(0, 82, 0, 82, 82, 82, 0) : rows.get(0);
CompetencySnapshot snapshot = rows.isEmpty() ? new CompetencySnapshot(0, 0, 0, 0, 0, 0, 0) : rows.get(0);
int trainingMinutes = snapshot.completed() * 8;
int contribution = snapshot.completed() == 0 && snapshot.incentivePoints() == 0
? 0
@@ -835,6 +835,9 @@ public class AihrPracticeSeedService {
}
private String aiLevel(int completed, int score) {
if (completed <= 0) {
return "未评级";
}
if (completed >= 20 && score >= 90) {
return "高级";
}
@@ -845,6 +848,13 @@ public class AihrPracticeSeedService {
}
private List<GrowthStageResponse> growthPath(int completed, int score) {
if (completed <= 0) {
return List.of(
new GrowthStageResponse("初级", 0, "待训练", "完成3次基础场景并保持80分以上"),
new GrowthStageResponse("中级", 0, "待积累", "完成10次训练且测评分达到85"),
new GrowthStageResponse("高级", 0, "待积累", "完成20次训练、主管复盘稳定通过")
);
}
return List.of(
new GrowthStageResponse("初级", Math.min(100, Math.max(30, completed * 10)), completed >= 3 ? "已达成" : "进行中", "完成3次基础场景并保持80分以上"),
new GrowthStageResponse("中级", Math.min(100, Math.max(0, completed * 6 + Math.max(0, score - 75))), completed >= 10 && score >= 85 ? "已达成" : "待积累", "完成10次训练且测评分达到85"),
@@ -1,6 +1,7 @@
package org.dromara.aihr.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.aihr.domain.AihrPracticeDto.CompetencyResponse;
import org.dromara.aihr.domain.AihrPracticeDto.PracticeAssignmentRequest;
import org.dromara.common.core.exception.ServiceException;
import org.junit.jupiter.api.Tag;
@@ -106,6 +107,35 @@ public class AihrPracticeSeedServiceTest {
assertEquals(List.of("000000", "SUP-1", "EMP-1", 5), jdbcTemplate.reviewArgs);
}
@Test
public void competencyDoesNotSeedScoreForEmptyEmployee() {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
when(jdbcTemplate.queryForObject(ArgumentMatchers.anyString(), eq(Integer.class), ArgumentMatchers.any(), ArgumentMatchers.any()))
.thenReturn(1);
when(jdbcTemplate.query(contains("COUNT(*) completed"), any(RowMapper.class), eq("000000"), eq("13900000999"), eq("13900000999")))
.thenAnswer(invocation -> mapOne(invocation, Map.of(
"completed", "0",
"score", "0",
"pending_review", "0",
"compliance", "0",
"communication", "0",
"emotion", "0",
"incentive_points", "0"
)));
AihrPracticeSeedService service = new AihrPracticeSeedService(new ObjectMapper(), jdbcTemplate, null, null);
CompetencyResponse profile = service.competency("13900000999");
assertEquals(0, profile.score());
assertEquals(0, profile.assessmentScore());
assertEquals(0, profile.completed());
assertEquals(0, profile.trainingMinutes());
assertEquals(0, profile.contribution());
assertEquals("未评级", profile.aiLevel());
assertTrue(profile.dimensions().stream().allMatch(item -> Integer.valueOf(0).equals(item.value())));
assertTrue(profile.growthPath().stream().allMatch(item -> Integer.valueOf(0).equals(item.progress())));
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static <T> List<T> mapOne(InvocationOnMock invocation, Map<String, String> values) throws SQLException {
RowMapper mapper = invocation.getArgument(1);
@@ -120,6 +150,12 @@ public class AihrPracticeSeedServiceTest {
ResultSet resultSet = mock(ResultSet.class);
for (Map.Entry<String, String> entry : values.entrySet()) {
when(resultSet.getString(entry.getKey())).thenReturn(entry.getValue());
try {
int intValue = Integer.parseInt(entry.getValue());
when(resultSet.getInt(entry.getKey())).thenReturn(intValue);
} catch (NumberFormatException ignored) {
// String-only test column.
}
}
mapped.add((T) mapper.mapRow(resultSet, index++));
}
@@ -149,6 +149,7 @@ mobile-uni/
- 员工端每日三题排序已改为按题目生成顺序展示: 后端 assignments 查询对 `source=daily` 按 ID 升序返回,不再把第 3 题排在最前。
- 员工端专项训练已补完成回写: 主管指派返回真实专项 ID,员工从专项入口完成对练后对应 `aihr_practice_assignment` 会置为已完成,不再长期卡在“待训练”。
- 员工端成长证据贡献度已收紧: 无训练记录且无主管人工加分时贡献度为 0,不再给空用户默认 20 分。
- 员工端空画像已收紧: 无移动端对练记录时综合能力、维度分、测评分和成长路径均为 0,AI 等级为“未评级”,不再沿用演示 seed 的 82 分/初级/30%。
- 员工端训练前预习卡已接入: 新 uni-app 对练页复用 `/api/train/practice/scenarios/{id}/prep-card`,开始前展示要点、红线和可说话术。
- 员工端训练前预习卡与页头已收敛展示: 隐藏内部 promptVersion,训练卡标题跟随当前专项场景,页头不再固定写“投诉接待”。
- 员工端训练中已接 SOP 求助: 新 uni-app 对练页复用 `/api/knowledge/search`,展示正式检索答案和引用,失败明确报错不兜假答案。