实现共享试卷考试记录查看功能

功能说明:
- 查看共享试卷的考试记录时,显示所有参与用户的成绩
- 按分数从高到低排序,方便查看排行
- 显示每个用户的头像、昵称和考试详情

实现细节:
1. 后端:修改 GetExamRecordList 接口
   - 指定试卷ID时自动识别共享关系
   - 查询原始试卷+所有分享副本的考试记录
   - 返回用户信息(username, nickname, avatar)
   - 按分数降序排序

2. 前端:更新考试记录展示
   - 显示用户头像和昵称
   - 保持原有的考试详情信息
   - 适配共享和非共享两种场景

修改文件:
- internal/handlers/exam_handler.go: 重构GetExamRecordList逻辑
- web/src/types/exam.ts: ExamRecord添加user字段
- 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:
燕陇琪 2025-12-01 21:34:20 +08:00
parent 03f3e14f6e
commit ccc77beef8
3 changed files with 71 additions and 4 deletions

View File

@ -641,20 +641,73 @@ func GetExamRecordList(c *gin.Context) {
examIDStr := c.Query("exam_id") examIDStr := c.Query("exam_id")
db := database.GetDB() db := database.GetDB()
query := db.Where("user_id = ?", userID)
// 如果指定了试卷ID,只查询该试卷的记录 // 如果指定了试卷ID,需要判断是否为共享试卷
if examIDStr != "" { if examIDStr != "" {
examID, err := strconv.ParseUint(examIDStr, 10, 32) examID, err := strconv.ParseUint(examIDStr, 10, 32)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的试卷ID"}) c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的试卷ID"})
return return
} }
query = query.Where("exam_id = ?", examID)
// 查询试卷信息
var exam models.Exam
if err := db.Where("id = ? AND user_id = ?", examID, userID).First(&exam).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "试卷不存在或无权限"})
return
}
// 获取所有相关试卷ID包括原始试卷和所有分享副本
var relatedExamIDs []uint
relatedExamIDs = append(relatedExamIDs, uint(examID))
// 如果这是被分享的试卷,找到原始试卷
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 records []models.ExamRecord
if err := db.Where("exam_id IN ? AND status = ?", relatedExamIDs, "graded").
Preload("Exam").
Preload("User", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "username", "nickname", "avatar")
}).
Order("score DESC, created_at DESC").
Find(&records).Error; err != nil {
log.Printf("查询考试记录失败: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "查询考试记录失败"})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": records,
})
return
} }
// 如果没有指定试卷ID只返回当前用户的记录
var records []models.ExamRecord var records []models.ExamRecord
if err := query.Preload("Exam"). if err := db.Where("user_id = ?", userID).
Preload("Exam").
Order("created_at DESC"). Order("created_at DESC").
Find(&records).Error; err != nil { Find(&records).Error; err != nil {
log.Printf("查询考试记录失败: %v", err) log.Printf("查询考试记录失败: %v", err)

View File

@ -443,6 +443,14 @@ const ExamManagement: React.FC = () => {
style={{ marginBottom: 16 }} style={{ marginBottom: 16 }}
size="small" size="small"
> >
{record.user && (
<div style={{ marginBottom: 12, display: 'flex', alignItems: 'center', gap: 8 }}>
<Avatar src={record.user.avatar} icon={<UserOutlined />} />
<span style={{ fontWeight: 'bold' }}>
{record.user.nickname || record.user.username}
</span>
</div>
)}
<Descriptions column={1} size="small"> <Descriptions column={1} size="small">
<Descriptions.Item label="状态"> <Descriptions.Item label="状态">
{record.status === 'in_progress' && <Tag color="processing"></Tag>} {record.status === 'in_progress' && <Tag color="processing"></Tag>}

View File

@ -61,6 +61,12 @@ export interface ExamRecord {
status: 'in_progress' | 'submitted' | 'graded' status: 'in_progress' | 'submitted' | 'graded'
is_passed: boolean is_passed: boolean
exam?: ExamModel exam?: ExamModel
user?: { // 用户信息(共享试卷时返回)
id: number
username: string
nickname: string
avatar: string
}
created_at: string created_at: string
updated_at: string updated_at: string
} }