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