AnCao/internal/handlers/wrong_question_handler.go
yanlongqi 6082ca0bf3 添加错题删除功能和优化错题列表
**新增功能**:
- 新增删除单个错题的接口 (DELETE /api/wrong-questions/:id)
- 错题列表每个项添加删除按钮
- 支持单独删除不想要的错题记录

**技术实现**:
- 后端: 实现DeleteWrongQuestion处理器,确保用户只能删除自己的错题
- 前端: 添加deleteWrongQuestion API 调用
- UI: 在List.Item中添加actions属性,显示删除按钮
- 删除时显示二次确认弹窗

**注意事项**:
- 删除需要确认,防止误操作
- 删除成功后自动刷新列表
- 仅显示操作用户自己的错题

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 15:42:59 +08:00

304 lines
7.2 KiB
Go

package handlers
import (
"ankao/internal/database"
"ankao/internal/models"
"encoding/json"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// GetWrongQuestions 获取错题列表
func GetWrongQuestions(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "未登录",
})
return
}
db := database.GetDB()
var wrongQuestions []models.WrongQuestion
// 查询参数
isMastered := c.Query("is_mastered") // "true" 或 "false"
questionType := c.Query("type") // 题型筛选
query := db.Where("user_id = ?", userID).Preload("PracticeQuestion")
// 筛选是否已掌握
if isMastered == "true" {
query = query.Where("is_mastered = ?", true)
} else if isMastered == "false" {
query = query.Where("is_mastered = ?", false)
}
// 按最后错误时间倒序
if err := query.Order("last_wrong_time DESC").Find(&wrongQuestions).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "查询失败",
})
return
}
// 转换为DTO
dtos := make([]models.WrongQuestionDTO, 0, len(wrongQuestions))
for _, wq := range wrongQuestions {
// 题型筛选
if questionType != "" && mapBackendToFrontendType(wq.PracticeQuestion.Type) != questionType {
continue
}
// 解析答案
var wrongAnswer, correctAnswer interface{}
json.Unmarshal([]byte(wq.WrongAnswer), &wrongAnswer)
json.Unmarshal([]byte(wq.CorrectAnswer), &correctAnswer)
dto := models.WrongQuestionDTO{
ID: wq.ID,
QuestionID: wq.QuestionID,
Question: convertToDTO(wq.PracticeQuestion),
WrongAnswer: wrongAnswer,
CorrectAnswer: correctAnswer,
WrongCount: wq.WrongCount,
LastWrongTime: wq.LastWrongTime,
IsMastered: wq.IsMastered,
}
dtos = append(dtos, dto)
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": dtos,
"total": len(dtos),
})
}
// GetWrongQuestionStats 获取错题统计
func GetWrongQuestionStats(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "未登录",
})
return
}
db := database.GetDB()
var wrongQuestions []models.WrongQuestion
if err := db.Where("user_id = ?", userID).Preload("PracticeQuestion").Find(&wrongQuestions).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "查询失败",
})
return
}
stats := models.WrongQuestionStats{
TotalWrong: len(wrongQuestions),
Mastered: 0,
NotMastered: 0,
TypeStats: make(map[string]int),
CategoryStats: make(map[string]int),
}
for _, wq := range wrongQuestions {
if wq.IsMastered {
stats.Mastered++
} else {
stats.NotMastered++
}
// 统计题型
frontendType := mapBackendToFrontendType(wq.PracticeQuestion.Type)
stats.TypeStats[frontendType]++
// 统计分类
stats.CategoryStats[wq.PracticeQuestion.TypeName]++
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": stats,
})
}
// MarkWrongQuestionMastered 标记错题为已掌握
func MarkWrongQuestionMastered(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "未登录",
})
return
}
wrongQuestionID := c.Param("id")
db := database.GetDB()
var wrongQuestion models.WrongQuestion
if err := db.Where("id = ? AND user_id = ?", wrongQuestionID, userID).First(&wrongQuestion).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"success": false,
"message": "错题不存在",
})
return
}
wrongQuestion.IsMastered = true
if err := db.Save(&wrongQuestion).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "更新失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "已标记为掌握",
})
}
// ClearWrongQuestions 清空错题本
func ClearWrongQuestions(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "未登录",
})
return
}
db := database.GetDB()
// 删除用户所有错题记录
if err := db.Where("user_id = ?", userID).Delete(&models.WrongQuestion{}).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "清空失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "错题本已清空",
})
}
// recordWrongQuestion 记录错题(内部函数,在答题错误时调用)
func recordWrongQuestion(userID, questionID uint, userAnswer, correctAnswer interface{}) error {
db := database.GetDB()
// 将答案序列化为JSON
wrongAnswerJSON, _ := json.Marshal(userAnswer)
correctAnswerJSON, _ := json.Marshal(correctAnswer)
// 查找是否已存在该错题
var existingWrong models.WrongQuestion
result := db.Where("user_id = ? AND question_id = ?", userID, questionID).First(&existingWrong)
if result.Error == nil {
// 已存在,更新错误次数和时间
existingWrong.WrongCount++
existingWrong.LastWrongTime = time.Now()
existingWrong.WrongAnswer = string(wrongAnswerJSON)
existingWrong.CorrectAnswer = string(correctAnswerJSON)
existingWrong.IsMastered = false // 重新标记为未掌握
return db.Save(&existingWrong).Error
}
// 不存在,创建新记录
newWrong := models.WrongQuestion{
UserID: userID,
QuestionID: questionID,
WrongAnswer: string(wrongAnswerJSON),
CorrectAnswer: string(correctAnswerJSON),
WrongCount: 1,
LastWrongTime: time.Now(),
IsMastered: false,
}
return db.Create(&newWrong).Error
}
// GetRandomWrongQuestion 获取随机错题进行练习
func GetRandomWrongQuestion(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "未登录",
})
return
}
db := database.GetDB()
var wrongQuestion models.WrongQuestion
// 随机获取一个错题
if err := db.Where("user_id = ?", userID).Order("RANDOM()").Preload("PracticeQuestion").First(&wrongQuestion).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"success": false,
"message": "暂无错题",
})
return
}
// 转换为DTO返回
dto := convertToDTO(wrongQuestion.PracticeQuestion)
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": dto,
})
}
// DeleteWrongQuestion 删除单个错题
func DeleteWrongQuestion(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "未登录",
})
return
}
wrongQuestionID := c.Param("id")
db := database.GetDB()
// 删除错题(确保只能删除自己的错题)
result := db.Where("id = ? AND user_id = ?", wrongQuestionID, userID).Delete(&models.WrongQuestion{})
if result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "删除失败",
})
return
}
if result.RowsAffected == 0 {
c.JSON(http.StatusNotFound, gin.H{
"success": false,
"message": "错题不存在",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "已删除",
})
}