统计面板继续迭代
This commit is contained in:
@@ -55,6 +55,54 @@ export interface ActiveTaskStat {
|
||||
}
|
||||
|
||||
export class ExamTaskModel {
|
||||
private static buildActiveTaskStat(input: {
|
||||
taskId: string;
|
||||
taskName: string;
|
||||
subjectName: string;
|
||||
totalScore: number;
|
||||
startAt: string;
|
||||
endAt: string;
|
||||
report: TaskReport;
|
||||
}): ActiveTaskStat {
|
||||
const { report } = input;
|
||||
|
||||
const completionRate =
|
||||
report.totalUsers > 0
|
||||
? Math.round((report.completedUsers / report.totalUsers) * 100)
|
||||
: 0;
|
||||
|
||||
const passingUsers = report.details.filter((d) => {
|
||||
if (d.score === null) return false;
|
||||
return d.score / input.totalScore >= 0.6;
|
||||
}).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;
|
||||
}).length;
|
||||
|
||||
const excellentRate =
|
||||
report.totalUsers > 0
|
||||
? Math.round((excellentUsers / report.totalUsers) * 100)
|
||||
: 0;
|
||||
|
||||
return {
|
||||
taskId: input.taskId,
|
||||
taskName: input.taskName,
|
||||
subjectName: input.subjectName,
|
||||
totalUsers: report.totalUsers,
|
||||
completedUsers: report.completedUsers,
|
||||
completionRate,
|
||||
passRate,
|
||||
excellentRate,
|
||||
startAt: input.startAt,
|
||||
endAt: input.endAt,
|
||||
};
|
||||
}
|
||||
|
||||
static async findAll(): Promise<(TaskWithSubject & {
|
||||
completedUsers: number;
|
||||
passRate: number;
|
||||
@@ -178,6 +226,98 @@ export class ExamTaskModel {
|
||||
return stats;
|
||||
}
|
||||
|
||||
static async getHistoryTasksWithStatsPaged(
|
||||
page: number,
|
||||
limit: number,
|
||||
): Promise<{ data: ActiveTaskStat[]; total: number }> {
|
||||
const now = new Date().toISOString();
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const totalRow = await get(
|
||||
`SELECT COUNT(*) as total FROM exam_tasks t WHERE t.end_at < ?`,
|
||||
[now],
|
||||
);
|
||||
const total = Number(totalRow?.total || 0);
|
||||
|
||||
const tasks = await all(
|
||||
`
|
||||
SELECT
|
||||
t.id, t.name as taskName, s.name as subjectName, s.total_score as totalScore,
|
||||
t.start_at as startAt, t.end_at as endAt
|
||||
FROM exam_tasks t
|
||||
JOIN exam_subjects s ON t.subject_id = s.id
|
||||
WHERE t.end_at < ?
|
||||
ORDER BY t.end_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`,
|
||||
[now, limit, offset],
|
||||
);
|
||||
|
||||
const data: ActiveTaskStat[] = [];
|
||||
for (const task of tasks) {
|
||||
const report = await this.getReport(task.id);
|
||||
data.push(
|
||||
this.buildActiveTaskStat({
|
||||
taskId: task.id,
|
||||
taskName: task.taskName,
|
||||
subjectName: task.subjectName,
|
||||
totalScore: Number(task.totalScore) || 0,
|
||||
startAt: task.startAt,
|
||||
endAt: task.endAt,
|
||||
report,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return { data, total };
|
||||
}
|
||||
|
||||
static async getUpcomingTasksWithStatsPaged(
|
||||
page: number,
|
||||
limit: number,
|
||||
): Promise<{ data: ActiveTaskStat[]; total: number }> {
|
||||
const now = new Date().toISOString();
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const totalRow = await get(
|
||||
`SELECT COUNT(*) as total FROM exam_tasks t WHERE t.start_at > ?`,
|
||||
[now],
|
||||
);
|
||||
const total = Number(totalRow?.total || 0);
|
||||
|
||||
const tasks = await all(
|
||||
`
|
||||
SELECT
|
||||
t.id, t.name as taskName, s.name as subjectName, s.total_score as totalScore,
|
||||
t.start_at as startAt, t.end_at as endAt
|
||||
FROM exam_tasks t
|
||||
JOIN exam_subjects s ON t.subject_id = s.id
|
||||
WHERE t.start_at > ?
|
||||
ORDER BY t.start_at ASC
|
||||
LIMIT ? OFFSET ?
|
||||
`,
|
||||
[now, limit, offset],
|
||||
);
|
||||
|
||||
const data: ActiveTaskStat[] = [];
|
||||
for (const task of tasks) {
|
||||
const report = await this.getReport(task.id);
|
||||
data.push(
|
||||
this.buildActiveTaskStat({
|
||||
taskId: task.id,
|
||||
taskName: task.taskName,
|
||||
subjectName: task.subjectName,
|
||||
totalScore: Number(task.totalScore) || 0,
|
||||
startAt: task.startAt,
|
||||
endAt: task.endAt,
|
||||
report,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return { data, total };
|
||||
}
|
||||
|
||||
static async findById(id: string): Promise<ExamTask | null> {
|
||||
const sql = `SELECT id, name, subject_id as subjectId, start_at as startAt, end_at as endAt, created_at as createdAt, selection_config as selectionConfig FROM exam_tasks WHERE id = ?`;
|
||||
const row = await get(sql, [id]);
|
||||
@@ -505,4 +645,4 @@ export class ExamTaskModel {
|
||||
timeLimitMinutes: row.timeLimitMinutes
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user