feat(aihr): add supervisor mistake aggregation

This commit is contained in:
2026-07-14 04:27:10 +08:00
parent 37ddb307cf
commit cd2c7fdd45
8 changed files with 185 additions and 7 deletions
@@ -162,6 +162,17 @@ public final class AihrPracticeDto {
) {
}
public record MistakeAggregateResponse(
String scenarioId,
String scenarioName,
String cause,
Integer count,
Integer affectedPeople,
Integer averageScore,
String latestTime
) {
}
public record DialogueResponse(String role, String label, String text, String audioUrl, Long audioOssId, Integer emotion, Integer trust, Boolean redFlag, String coachHint) {
public DialogueResponse(String role, String label, String text) {
this(role, label, text, "", null, null, null, null, "");
@@ -260,7 +271,8 @@ public final class AihrPracticeDto {
public record PracticeTeamResponse(
List<TeamMemberResponse> members,
List<RecordResponse> records,
List<PracticeAssignmentResponse> assignments
List<PracticeAssignmentResponse> assignments,
List<MistakeAggregateResponse> mistakes
) {
}
@@ -26,6 +26,7 @@ import org.dromara.aihr.domain.AihrPracticeDto.PromptTemplateResponse;
import org.dromara.aihr.domain.AihrPracticeDto.PromotionEvidenceResponse;
import org.dromara.aihr.domain.AihrPracticeDto.RecordResponse;
import org.dromara.aihr.domain.AihrPracticeDto.MistakeResponse;
import org.dromara.aihr.domain.AihrPracticeDto.MistakeAggregateResponse;
import org.dromara.aihr.domain.AihrPracticeDto.ReviewAdviceRequest;
import org.dromara.aihr.domain.AihrPracticeDto.ReviewDetailResponse;
import org.dromara.aihr.domain.AihrPracticeDto.RubricDimensionResponse;
@@ -799,12 +800,13 @@ public class AihrPracticeSeedService {
TeamScope scope = teamScope(supervisorExtPartyId);
int safeLimit = normalizeTeamLimit(limit);
if (scope.scoped() && scope.extPartyIds().isEmpty()) {
return new PracticeTeamResponse(List.of(), List.of(), List.of());
return new PracticeTeamResponse(List.of(), List.of(), List.of(), List.of());
}
return new PracticeTeamResponse(
practiceTeamMembers(scope),
practiceTeamRecords(scope, safeLimit),
practiceTeamAssignments(scope, safeLimit)
practiceTeamAssignments(scope, safeLimit),
practiceTeamMistakes(scope, safeLimit)
);
}
@@ -920,6 +922,57 @@ public class AihrPracticeSeedService {
return jdbcTemplate.query(sql, this::mapAssignment, args.toArray());
}
private List<MistakeAggregateResponse> practiceTeamMistakes(TeamScope scope, int limit) {
ensurePracticeTable();
List<Object> args = new ArrayList<>();
args.add(TENANT_ID);
StringBuilder where = new StringBuilder("WHERE tenant_id = ? AND mode = 'mobile' AND finished_time IS NOT NULL");
if (scope.scoped()) {
where.append(" AND ext_party_id IN (").append(inClause(scope.extPartyIds().size())).append(")");
args.addAll(scope.extPartyIds());
}
args.add(limit);
String sql = """
SELECT scenario_id,
scenario_name,
cause,
COUNT(*) AS mistake_count,
COUNT(DISTINCT NULLIF(ext_party_id, '')) AS affected_people,
ROUND(AVG(COALESCE(total_score, 0))) AS average_score,
MAX(finished_time) AS latest_time
FROM (
SELECT scenario_id,
COALESCE(NULLIF(scenario_name, ''), '场景训练') AS scenario_name,
ext_party_id,
total_score,
finished_time,
CASE
WHEN dialogue_json LIKE '%%"redFlag":true%%' THEN '红线话术'
WHEN COALESCE(dim_emotion, 100) < 60 THEN '情绪承接'
WHEN COALESCE(dim_compliance, 100) < 60 THEN '流程合规'
ELSE '综合得分不足'
END AS cause
FROM aihr_practice_session
%s
AND (total_score < 80 OR dialogue_json LIKE '%%"redFlag":true%%')
) mistake_rows
GROUP BY scenario_id, scenario_name, cause
ORDER BY mistake_count DESC, latest_time DESC, scenario_name, cause
LIMIT ?
""".formatted(where);
return jdbcTemplate.query(sql, (rs, rowNum) -> new MistakeAggregateResponse(
rs.getString("scenario_id"),
firstNonBlank(rs.getString("scenario_name"), "场景训练"),
firstNonBlank(rs.getString("cause"), "综合得分不足"),
rs.getInt("mistake_count"),
rs.getInt("affected_people"),
rs.getInt("average_score"),
rs.getTimestamp("latest_time") == null
? ""
: rs.getTimestamp("latest_time").toLocalDateTime().format(TIME_FORMATTER)
), args.toArray());
}
public List<RecordResponse> pendingReviewRecords(int limit) {
return pendingReviewRecords("", "", "", limit);
}