fix(aihr): persist daily drill scores
This commit is contained in:
+1
@@ -262,6 +262,7 @@ public final class AihrPracticeDto {
|
||||
String userAnswer,
|
||||
String feedback,
|
||||
Boolean correct,
|
||||
Integer score,
|
||||
String audioUrl,
|
||||
Long audioOssId
|
||||
) {
|
||||
|
||||
+11
-6
@@ -993,7 +993,7 @@ public class AihrPracticeSeedService {
|
||||
args.add(limit);
|
||||
String sql = """
|
||||
SELECT id, ext_party_id, trainee_name, scenario_id, scenario_name, source, reason, status, create_time,
|
||||
answer_text, answer_audio_url, answer_audio_oss_id, feedback, correct
|
||||
answer_text, answer_audio_url, answer_audio_oss_id, feedback, correct, score
|
||||
FROM aihr_practice_assignment
|
||||
%s
|
||||
ORDER BY create_time DESC, id DESC
|
||||
@@ -1716,7 +1716,7 @@ public class AihrPracticeSeedService {
|
||||
ensureDailyDrills(party);
|
||||
return jdbcTemplate.query("""
|
||||
SELECT id, ext_party_id, trainee_name, scenario_id, scenario_name, source, reason, status, create_time,
|
||||
answer_text, answer_audio_url, answer_audio_oss_id, feedback, correct
|
||||
answer_text, answer_audio_url, answer_audio_oss_id, feedback, correct, score
|
||||
FROM aihr_practice_assignment
|
||||
WHERE tenant_id = ?
|
||||
AND (? = '' OR ext_party_id = ?)
|
||||
@@ -1754,7 +1754,7 @@ public class AihrPracticeSeedService {
|
||||
args.add(normalizeLimit(limit));
|
||||
return jdbcTemplate.query("""
|
||||
SELECT id, ext_party_id, trainee_name, scenario_id, scenario_name, source, reason, status, create_time,
|
||||
answer_text, answer_audio_url, answer_audio_oss_id, feedback, correct
|
||||
answer_text, answer_audio_url, answer_audio_oss_id, feedback, correct, score
|
||||
FROM aihr_practice_assignment
|
||||
WHERE tenant_id = ?
|
||||
AND ext_party_id IN (%s)
|
||||
@@ -1857,13 +1857,13 @@ public class AihrPracticeSeedService {
|
||||
""", tenantId(), extPartyId, traineeName, scenario.id(), firstNonBlank(request == null ? null : request.scenarioName(), scenario.name()), source, reason, createdAt, createdAt);
|
||||
List<PracticeAssignmentResponse> rows = jdbcTemplate.query("""
|
||||
SELECT id, ext_party_id, trainee_name, scenario_id, scenario_name, source, reason, status, create_time,
|
||||
answer_text, answer_audio_url, answer_audio_oss_id, feedback, correct
|
||||
answer_text, answer_audio_url, answer_audio_oss_id, feedback, correct, score
|
||||
FROM aihr_practice_assignment
|
||||
WHERE tenant_id = ? AND ext_party_id = ? AND scenario_id = ? AND source = ? AND reason = ?
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
""", this::mapAssignment, tenantId(), extPartyId, scenario.id(), source, reason);
|
||||
return rows.isEmpty() ? new PracticeAssignmentResponse(null, extPartyId, traineeName, scenario.id(), scenario.name(), source, reason, "待训练", now.format(TIME_FORMATTER), null, null, null, null, null, null, null) : rows.get(0);
|
||||
return rows.isEmpty() ? new PracticeAssignmentResponse(null, extPartyId, traineeName, scenario.id(), scenario.name(), source, reason, "待训练", now.format(TIME_FORMATTER), null, null, null, null, null, null, null, null) : rows.get(0);
|
||||
}
|
||||
|
||||
public PracticeAssignmentResponse createAssignment(PracticeAssignmentRequest request, String supervisorExtPartyId) {
|
||||
@@ -1935,6 +1935,7 @@ public class AihrPracticeSeedService {
|
||||
answer_audio_oss_id = ?,
|
||||
feedback = ?,
|
||||
correct = ?,
|
||||
score = ?,
|
||||
finished_time = ?,
|
||||
update_time = ?
|
||||
WHERE tenant_id = ? AND id = ? AND source = 'daily'
|
||||
@@ -1946,6 +1947,7 @@ public class AihrPracticeSeedService {
|
||||
audioOssId,
|
||||
score.feedback(),
|
||||
score.correct() ? 1 : 0,
|
||||
score.score(),
|
||||
Timestamp.valueOf(now),
|
||||
Timestamp.valueOf(now),
|
||||
tenantId(),
|
||||
@@ -2509,6 +2511,7 @@ public class AihrPracticeSeedService {
|
||||
rs.getString("answer_text"),
|
||||
"daily".equals(source) ? stripReferenceAnswer(rs.getString("feedback")) : rs.getString("feedback"),
|
||||
correctValue == null ? null : rs.getBoolean("correct"),
|
||||
rs.getObject("score") == null ? null : rs.getInt("score"),
|
||||
rs.getString("answer_audio_url"),
|
||||
rs.getObject("answer_audio_oss_id") == null ? null : rs.getLong("answer_audio_oss_id")
|
||||
);
|
||||
@@ -3315,6 +3318,7 @@ public class AihrPracticeSeedService {
|
||||
`answer_audio_oss_id` bigint DEFAULT NULL COMMENT '每日三题语音OSS编号',
|
||||
`feedback` varchar(1000) DEFAULT NULL COMMENT '每日三题反馈',
|
||||
`correct` tinyint DEFAULT NULL COMMENT '是否答对',
|
||||
`score` int DEFAULT NULL COMMENT '每日三题得分',
|
||||
`finished_time` datetime DEFAULT NULL COMMENT '完成时间',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
@@ -3328,7 +3332,8 @@ public class AihrPracticeSeedService {
|
||||
ensureColumn("aihr_practice_assignment", "answer_audio_oss_id", "ALTER TABLE aihr_practice_assignment ADD COLUMN `answer_audio_oss_id` bigint DEFAULT NULL COMMENT '每日三题语音OSS编号' AFTER `answer_audio_url`");
|
||||
ensureColumn("aihr_practice_assignment", "feedback", "ALTER TABLE aihr_practice_assignment ADD COLUMN `feedback` varchar(1000) DEFAULT NULL COMMENT '每日三题反馈' AFTER `answer_audio_oss_id`");
|
||||
ensureColumn("aihr_practice_assignment", "correct", "ALTER TABLE aihr_practice_assignment ADD COLUMN `correct` tinyint DEFAULT NULL COMMENT '是否答对' AFTER `feedback`");
|
||||
ensureColumn("aihr_practice_assignment", "finished_time", "ALTER TABLE aihr_practice_assignment ADD COLUMN `finished_time` datetime DEFAULT NULL COMMENT '完成时间' AFTER `correct`");
|
||||
ensureColumn("aihr_practice_assignment", "score", "ALTER TABLE aihr_practice_assignment ADD COLUMN `score` int DEFAULT NULL COMMENT '每日三题得分' AFTER `correct`");
|
||||
ensureColumn("aihr_practice_assignment", "finished_time", "ALTER TABLE aihr_practice_assignment ADD COLUMN `finished_time` datetime DEFAULT NULL COMMENT '完成时间' AFTER `score`");
|
||||
assignmentTableReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -135,6 +135,20 @@ public class AihrPracticeSeedServiceTest {
|
||||
assertTrue(method.contains("if (updated == 0)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dailyDrillScoreSurvivesAssignmentRefresh() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java"));
|
||||
int answerStart = source.indexOf("public DailyDrillAnswerResponse answerDailyDrill");
|
||||
int answerEnd = source.indexOf("public List<PromptTemplateResponse> promptTemplates", answerStart);
|
||||
assertTrue(answerStart >= 0 && answerEnd > answerStart);
|
||||
String answerMethod = source.substring(answerStart, answerEnd);
|
||||
assertTrue(answerMethod.contains("score = ?"));
|
||||
assertTrue(answerMethod.contains("score.score()"));
|
||||
assertTrue(source.contains("feedback, correct, score"));
|
||||
assertTrue(source.contains("rs.getObject(\"score\")"));
|
||||
assertTrue(source.contains("ADD COLUMN `score` int"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mobileAssignmentStartValidatesOwnerStatusAndScenario() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java"));
|
||||
|
||||
@@ -103,6 +103,7 @@ CREATE TABLE IF NOT EXISTS `aihr_practice_assignment` (
|
||||
`answer_audio_oss_id` bigint DEFAULT NULL COMMENT '每日三题语音OSS编号',
|
||||
`feedback` varchar(1000) DEFAULT NULL COMMENT '每日三题反馈',
|
||||
`correct` tinyint DEFAULT NULL COMMENT '是否答对',
|
||||
`score` int DEFAULT NULL COMMENT '每日三题得分',
|
||||
`finished_time` datetime DEFAULT NULL COMMENT '完成时间',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
|
||||
@@ -193,4 +193,5 @@
|
||||
- 2026-07-14 BRD 案例提交能力边界复核:移动端案例页原先对所有登录员工展示“提交素材”表单,但后端仅允许主管/项目负责人提交,普通员工点击后才得到权限错误;现新增只读 `GET /api/knowledge/case/capabilities`,复用服务端唯一贡献者判定,员工端仅在 `canSubmit=true` 时展示提交入口,其他员工显示案例学习说明。后端权限仍保留为最终防线。
|
||||
- 2026-07-14 线上只读发布差异:生产根站和 `/h5/` 返回 `200`,生产仍加载 H5 `assets/index-D4-NrEpb.js`;本地 `b8be1bb6` 构建已生成 `mobile-uni/dist/build/h5/assets/index-u8NWYNWX.js`,案例状态过滤和提交能力入口尚未发布。本轮未执行生产静态资源同步、后端重启或业务数据写入。
|
||||
- 2026-07-14 BRD 4.7 SOP 指标口径复核:正式试点的 SOP 可用率与待评审数现只统计评审表中 `category='sop'` 或名称包含 `SOP` 的记录,并继续按租户和试点日期窗口过滤;培训类或其他知识问答不会混入正式 SOP 分母。后端导出、严格预检和单测夹具已统一该条件;岗位与住宅项目的正式适用性仍需组织/HR 规则输入,不能仅靠分类字符串替代。
|
||||
- 2026-07-14 BRD 4.4 每日三题结果留痕补强:每日三题提交返回的分数此前只存在响应内,刷新任务列表后无法恢复数值分数;现新增 `aihr_practice_assignment.score`,提交、查询、员工学习页均保留“得分 + 反馈”。当前得分仍来自可解释关键词规则兜底,不把它宣称为语义级 AI 评分;后续若启用模型评估,需单独补评分模式、成本闸门和人工抽检证据。
|
||||
- 2026-07-14 BRD 4.4 每日一练评分能力复核:当前 `scoreDailyDrill` 使用参考答案关键词命中和长度规则即时反馈,属于可解释的规则兜底,不等同于 BRD 所称的语义级“AI 秒评”;正式升级前需确认模型、成本闸门、评分维度和人工抽检样本,当前不把规则分数宣称为 AI 评测结果。
|
||||
|
||||
@@ -363,6 +363,10 @@ const roleLabel = (role: PracticeRole) => {
|
||||
|
||||
const assignmentDesc = (item: PracticeAssignment) => {
|
||||
if (item.source === 'daily' && item.status !== '已完成') return '提交后显示反馈与参考答案';
|
||||
if (item.source === 'daily' && item.status === '已完成') {
|
||||
const score = typeof item.score === 'number' ? `得分 ${item.score} 分` : '';
|
||||
return [score, item.feedback || item.reason].filter(Boolean).join(' · ');
|
||||
}
|
||||
return item.feedback || item.reason;
|
||||
};
|
||||
|
||||
|
||||
@@ -301,6 +301,7 @@ export interface PracticeAssignment {
|
||||
userAnswer?: string;
|
||||
feedback?: string;
|
||||
correct?: boolean | null;
|
||||
score?: number | null;
|
||||
audioUrl?: string;
|
||||
audioOssId?: number | string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user