feat: 添加得分占比计算功能;优化时间格式化工具函数;更新考试记录和用户任务页面以显示得分占比;新增得分占比测试用例;修复时间解析逻辑

This commit is contained in:
2025-12-30 15:26:53 +08:00
parent 8cd6950631
commit 1822d8b4da
18 changed files with 347 additions and 68 deletions

View File

@@ -46,6 +46,7 @@ export interface TaskReport {
userName: string;
userPhone: string;
score: number | null;
scorePercentage: number | null;
completedAt: string | null;
}>;
}
@@ -81,16 +82,16 @@ export class ExamTaskModel {
: 0;
const passingUsers = report.details.filter((d) => {
if (d.score === null) return false;
return d.score / input.totalScore >= 0.6;
if (d.scorePercentage === null) return false;
return d.scorePercentage >= 60;
}).length;
const passRate =
report.totalUsers > 0 ? Math.round((passingUsers / report.totalUsers) * 100) : 0;
const excellentUsers = report.details.filter((d) => {
if (d.score === null) return false;
return d.score / input.totalScore >= 0.8;
if (d.scorePercentage === null) return false;
return d.scorePercentage >= 80;
}).length;
const excellentRate =
@@ -142,20 +143,20 @@ export class ExamTaskModel {
// 获取该任务的详细报表数据
const report = await this.getReport(task.id);
// 计算合格率(得分60%以上)
// 计算合格率(得分占比60%以上)
const passingUsers = report.details.filter((d: any) => {
if (d.score === null) return false;
return (d.score / task.totalScore) >= 0.6;
if (d.scorePercentage === null) return false;
return d.scorePercentage >= 60;
}).length;
const passRate = report.totalUsers > 0
? Math.round((passingUsers / report.totalUsers) * 100)
: 0;
// 计算优秀率(得分80%以上)
// 计算优秀率(得分占比80%以上)
const excellentUsers = report.details.filter((d: any) => {
if (d.score === null) return false;
return (d.score / task.totalScore) >= 0.8;
if (d.scorePercentage === null) return false;
return d.scorePercentage >= 80;
}).length;
const excellentRate = report.totalUsers > 0
@@ -198,20 +199,20 @@ export class ExamTaskModel {
? Math.round((report.completedUsers / report.totalUsers) * 100)
: 0;
// 4. 计算合格率(得分60%以上)
// 4. 计算合格率(得分占比60%以上)
const passingUsers = report.details.filter(d => {
if (d.score === null) return false;
return (d.score / task.totalScore) >= 0.6;
if (d.scorePercentage === null) return false;
return d.scorePercentage >= 60;
}).length;
const passRate = report.totalUsers > 0
? Math.round((passingUsers / report.totalUsers) * 100)
: 0;
// 5. 计算优秀率(得分80%以上)
// 5. 计算优秀率(得分占比80%以上)
const excellentUsers = report.details.filter(d => {
if (d.score === null) return false;
return (d.score / task.totalScore) >= 0.8;
if (d.scorePercentage === null) return false;
return d.scorePercentage >= 80;
}).length;
const excellentRate = report.totalUsers > 0
@@ -509,16 +510,39 @@ export class ExamTaskModel {
const subject = await import('./examSubject').then(({ ExamSubjectModel }) => ExamSubjectModel.findById(task.subjectId));
if (!subject) throw new Error('科目不存在');
// 注意:同一用户可能多次参加同一任务考试,这里取“得分占比最高,其次完成时间最新”的那一次。
// 得分占比优先用 quiz_answers/题目分值重算(兼容旧记录),否则回退到 quiz_records.score_percentage。
const sqlUsers = `
WITH best_records AS (
SELECT
r.*,
ROW_NUMBER() OVER (
PARTITION BY r.user_id, r.task_id
ORDER BY COALESCE(r.score_percentage, -1) DESC, r.created_at DESC
) as rn
FROM quiz_records r
WHERE r.task_id = ?
),
totals AS (
SELECT qa.record_id as recordId, SUM(q.score) as totalPossibleScore
FROM quiz_answers qa
JOIN questions q ON q.id = qa.question_id
GROUP BY qa.record_id
)
SELECT
u.id as userId,
u.name as userName,
u.phone as userPhone,
qr.total_score as score,
qr.created_at as completedAt
br.total_score as score,
br.created_at as completedAt,
CASE
WHEN totals.totalPossibleScore > 0 THEN (br.total_score * 1.0 / totals.totalPossibleScore) * 100
ELSE br.score_percentage
END as scorePercentage
FROM exam_task_users etu
JOIN users u ON etu.user_id = u.id
LEFT JOIN quiz_records qr ON u.id = qr.user_id AND qr.task_id = ?
LEFT JOIN best_records br ON br.user_id = u.id AND br.rn = 1
LEFT JOIN totals ON totals.recordId = br.id
WHERE etu.task_id = ?
`;
@@ -529,6 +553,7 @@ export class ExamTaskModel {
userName: r.userName,
userPhone: r.userPhone,
score: r.score !== null ? r.score : null,
scorePercentage: r.scorePercentage !== null ? Number(r.scorePercentage) : null,
completedAt: r.completedAt || null
}));