添加共享试卷参与人数统计功能
功能说明: - 后端:GetExamList 接口新增 participant_count 字段 - 前端:试卷卡片显示"X 人参与"标签 - 统计逻辑:计算原始试卷和所有分享副本的不同用户数 实现细节: - 自动识别原始试卷和分享副本 - 统计所有已完成考试的不同用户 - 使用团队图标(TeamOutlined)展示参与人数 修改文件: - internal/handlers/exam_handler.go: 添加参与人数统计逻辑 - web/src/types/exam.ts: 更新类型定义 - web/src/pages/ExamManagement.tsx: 显示参与人数标签 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3704d52a26
commit
03f3e14f6e
@ -169,10 +169,11 @@ func GetExamList(c *gin.Context) {
|
||||
type ExamWithStats struct {
|
||||
models.Exam
|
||||
QuestionCount int `json:"question_count"`
|
||||
AttemptCount int `json:"attempt_count"` // 考试次数
|
||||
BestScore float64 `json:"best_score"` // 最高分
|
||||
AttemptCount int `json:"attempt_count"` // 考试次数(当前用户)
|
||||
BestScore float64 `json:"best_score"` // 最高分(当前用户)
|
||||
HasInProgressExam bool `json:"has_in_progress_exam"` // 是否有进行中的考试
|
||||
InProgressRecordID uint `json:"in_progress_record_id,omitempty"` // 进行中的考试记录ID
|
||||
ParticipantCount int `json:"participant_count"` // 共享试卷的参与人数(所有用户)
|
||||
}
|
||||
|
||||
result := make([]ExamWithStats, 0, len(exams))
|
||||
@ -184,12 +185,12 @@ func GetExamList(c *gin.Context) {
|
||||
QuestionCount: len(questionIDs),
|
||||
}
|
||||
|
||||
// 查询该试卷的考试记录统计
|
||||
// 查询该试卷的考试记录统计(当前用户)
|
||||
var count int64
|
||||
db.Model(&models.ExamRecord{}).Where("exam_id = ? AND user_id = ?", exam.ID, userID).Count(&count)
|
||||
stats.AttemptCount = int(count)
|
||||
|
||||
// 查询最高分
|
||||
// 查询最高分(当前用户)
|
||||
var record models.ExamRecord
|
||||
if err := db.Where("exam_id = ? AND user_id = ?", exam.ID, userID).
|
||||
Order("score DESC").
|
||||
@ -206,6 +207,41 @@ func GetExamList(c *gin.Context) {
|
||||
stats.InProgressRecordID = inProgressRecord.ID
|
||||
}
|
||||
|
||||
// 计算共享试卷的参与人数
|
||||
// 获取所有相关试卷ID(包括原始试卷和所有分享副本)
|
||||
var relatedExamIDs []uint
|
||||
relatedExamIDs = append(relatedExamIDs, exam.ID)
|
||||
|
||||
// 如果这是被分享的试卷,找到原始试卷
|
||||
if exam.IsShared && exam.SharedByID != nil {
|
||||
var originalExam models.Exam
|
||||
if err := db.Where("user_id = ? AND question_ids = ? AND deleted_at IS NULL",
|
||||
*exam.SharedByID, exam.QuestionIDs).First(&originalExam).Error; err == nil {
|
||||
relatedExamIDs = append(relatedExamIDs, originalExam.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// 查找所有基于该试卷的分享副本
|
||||
var sharedExams []models.Exam
|
||||
sharedByID := exam.UserID
|
||||
if exam.IsShared && exam.SharedByID != nil {
|
||||
sharedByID = *exam.SharedByID
|
||||
}
|
||||
if err := db.Where("shared_by_id = ? AND question_ids = ? AND deleted_at IS NULL",
|
||||
sharedByID, exam.QuestionIDs).Find(&sharedExams).Error; err == nil {
|
||||
for _, se := range sharedExams {
|
||||
relatedExamIDs = append(relatedExamIDs, se.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// 统计所有相关试卷的已完成考试的不同用户数
|
||||
var participantCount int64
|
||||
db.Model(&models.ExamRecord{}).
|
||||
Where("exam_id IN ? AND status = ?", relatedExamIDs, "graded").
|
||||
Distinct("user_id").
|
||||
Count(&participantCount)
|
||||
stats.ParticipantCount = int(participantCount)
|
||||
|
||||
result = append(result, stats)
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,8 @@ import {
|
||||
PrinterOutlined,
|
||||
ArrowLeftOutlined,
|
||||
ShareAltOutlined,
|
||||
UserOutlined
|
||||
UserOutlined,
|
||||
TeamOutlined
|
||||
} from '@ant-design/icons'
|
||||
import * as examApi from '../api/exam'
|
||||
import styles from './ExamManagement.module.less'
|
||||
@ -46,6 +47,7 @@ interface ExamListItem {
|
||||
best_score: number
|
||||
has_in_progress_exam: boolean
|
||||
in_progress_record_id?: number
|
||||
participant_count: number // 共享试卷的参与人数
|
||||
created_at: string
|
||||
is_shared?: boolean
|
||||
shared_by?: {
|
||||
@ -396,6 +398,14 @@ const ExamManagement: React.FC = () => {
|
||||
<span>考试次数 {exam.attempt_count}</span>
|
||||
</Tag>
|
||||
</div>
|
||||
{exam.participant_count > 0 && (
|
||||
<div className={styles.statItem}>
|
||||
<Tag color="blue" className={styles.participantTag}>
|
||||
<TeamOutlined />
|
||||
<span>{exam.participant_count} 人参与</span>
|
||||
</Tag>
|
||||
</div>
|
||||
)}
|
||||
{exam.has_in_progress_exam && (
|
||||
<div className={styles.statItem}>
|
||||
<Tag color="processing" className={styles.progressTag}>
|
||||
|
||||
@ -98,6 +98,14 @@ export type ExamListResponse = Array<{
|
||||
best_score: number
|
||||
has_in_progress_exam: boolean
|
||||
in_progress_record_id?: number
|
||||
participant_count: number // 共享试卷的参与人数
|
||||
is_shared: boolean // 是否为分享的试卷
|
||||
shared_by_id?: number // 分享人ID
|
||||
shared_by?: { // 分享人信息
|
||||
id: number
|
||||
username: string
|
||||
nickname: string
|
||||
}
|
||||
created_at: string
|
||||
}>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user