package handler import ( "log" "net/http" "strconv" "time" "github.com/gin-gonic/gin" "github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/services" "github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/models" "github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/common" ) type VocabularyHandler struct { vocabularyService *services.VocabularyService } func NewVocabularyHandler(vocabularyService *services.VocabularyService) *VocabularyHandler { return &VocabularyHandler{vocabularyService: vocabularyService} } // GetCategories 获取词汇分类列表 func (h *VocabularyHandler) GetCategories(c *gin.Context) { level := c.Query("level") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) categories, err := h.vocabularyService.GetCategories(page, pageSize, level) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"categories": categories}) } // CreateCategory 创建词汇分类 func (h *VocabularyHandler) CreateCategory(c *gin.Context) { var category models.VocabularyCategory if err := c.ShouldBindJSON(&category); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 将模型转换为服务所需参数 desc := "" if category.Description != nil { desc = *category.Description } createdCategory, err := h.vocabularyService.CreateCategory(category.Name, desc, category.Level) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"category": createdCategory}) } // GetVocabulariesByCategory 根据分类获取词汇列表 func (h *VocabularyHandler) GetVocabulariesByCategory(c *gin.Context) { categoryID := c.Param("id") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) level := c.Query("level") vocabularies, err := h.vocabularyService.GetVocabulariesByCategory(categoryID, page, pageSize, level) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"vocabularies": vocabularies}) } // GetVocabularyByID 获取词汇详情 func (h *VocabularyHandler) GetVocabularyByID(c *gin.Context) { vocabularyID := c.Param("id") vocabulary, err := h.vocabularyService.GetVocabularyByID(vocabularyID) if err != nil { if common.IsBusinessError(err) { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusOK, gin.H{"vocabulary": vocabulary}) } // SearchVocabularies 搜索词汇 func (h *VocabularyHandler) SearchVocabularies(c *gin.Context) { keyword := c.Query("keyword") level := c.Query("level") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) vocabularies, err := h.vocabularyService.SearchVocabularies(keyword, level, page, pageSize) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"vocabularies": vocabularies}) } // GetUserVocabularyProgress 获取用户词汇学习进度 func (h *VocabularyHandler) GetUserVocabularyProgress(c *gin.Context) { userID := c.GetInt64("user_id") vocabularyID := c.Param("id") progress, err := h.vocabularyService.GetUserVocabularyProgress(userID, vocabularyID) if err != nil { if common.IsBusinessError(err) { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusOK, gin.H{"progress": progress}) } // UpdateUserVocabularyProgress 更新用户词汇学习进度 func (h *VocabularyHandler) UpdateUserVocabularyProgress(c *gin.Context) { userID := c.GetInt64("user_id") vocabularyID := c.Param("id") var updates map[string]interface{} if err := c.ShouldBindJSON(&updates); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } progress, err := h.vocabularyService.UpdateUserVocabularyProgress(userID, vocabularyID, updates) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"progress": progress}) } // GetUserVocabularyStats 获取用户词汇学习统计 func (h *VocabularyHandler) GetUserVocabularyStats(c *gin.Context) { userID := c.GetInt64("user_id") stats, err := h.vocabularyService.GetUserVocabularyStats(userID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"stats": stats}) } // UpdateCategory 更新词汇分类 func (h *VocabularyHandler) UpdateCategory(c *gin.Context) { categoryID := c.Param("id") if categoryID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "分类ID不能为空"}) return } var updates map[string]interface{} if err := c.ShouldBindJSON(&updates); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } category, err := h.vocabularyService.UpdateCategory(categoryID, updates) if err != nil { if common.IsBusinessError(err) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusOK, gin.H{"category": category}) } // DeleteCategory 删除词汇分类 func (h *VocabularyHandler) DeleteCategory(c *gin.Context) { categoryID := c.Param("id") if categoryID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "分类ID不能为空"}) return } if err := h.vocabularyService.DeleteCategory(categoryID); err != nil { if common.IsBusinessError(err) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusOK, gin.H{"message": "分类已删除"}) } // CreateVocabulary 创建词汇 func (h *VocabularyHandler) CreateVocabulary(c *gin.Context) { type reqBody struct { Word string `json:"word"` Phonetic string `json:"phonetic"` Level string `json:"level"` Frequency int `json:"frequency"` CategoryID string `json:"category_id"` Definitions []string `json:"definitions"` Examples []string `json:"examples"` Images []string `json:"images"` } var req reqBody if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } vocab, err := h.vocabularyService.CreateVocabulary( req.Word, req.Phonetic, req.Level, req.Frequency, req.CategoryID, req.Definitions, req.Examples, req.Images, ) if err != nil { if common.IsBusinessError(err) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusCreated, gin.H{"vocabulary": vocab}) } // GetDailyVocabularyStats 获取每日学习单词统计 func (h *VocabularyHandler) GetDailyVocabularyStats(c *gin.Context) { userID := c.GetInt64("user_id") userIDStr := strconv.FormatInt(userID, 10) stats, err := h.vocabularyService.GetDailyStats(userIDStr) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"stats": stats}) } // CreateVocabularyTest 创建词汇测试 func (h *VocabularyHandler) CreateVocabularyTest(c *gin.Context) { type reqBody struct { TestType string `json:"test_type"` Level string `json:"level"` TotalWords int `json:"total_words"` } var req reqBody if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } userID := c.GetInt64("user_id") test, err := h.vocabularyService.CreateVocabularyTest(userID, req.TestType, req.Level, req.TotalWords) if err != nil { if common.IsBusinessError(err) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusCreated, gin.H{"test": test}) } // GetVocabularyTest 获取词汇测试 func (h *VocabularyHandler) GetVocabularyTest(c *gin.Context) { testID := c.Param("id") if testID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "测试ID不能为空"}) return } test, err := h.vocabularyService.GetVocabularyTest(testID) if err != nil { if common.IsBusinessError(err) { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusOK, gin.H{"test": test}) } // UpdateVocabularyTestResult 更新词汇测试结果 func (h *VocabularyHandler) UpdateVocabularyTestResult(c *gin.Context) { testID := c.Param("id") if testID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "测试ID不能为空"}) return } type reqBody struct { CorrectWords int `json:"correct_words"` Score float64 `json:"score"` Duration int `json:"duration"` } var req reqBody if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := h.vocabularyService.UpdateVocabularyTestResult(testID, req.CorrectWords, req.Score, req.Duration); err != nil { if common.IsBusinessError(err) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusOK, gin.H{"message": "测试结果更新成功"}) } // GetTodayStudyWords 获取今日学习单词 func (h *VocabularyHandler) GetTodayStudyWords(c *gin.Context) { userID := c.GetInt64("user_id") limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) words, err := h.vocabularyService.GetTodayStudyWords(userID, limit) if err != nil { common.ErrorResponse(c, 500, "获取今日单词失败") return } // 确保返回空数组而不是null if words == nil { words = []map[string]interface{}{} } common.SuccessResponse(c, gin.H{"words": words}) } // GetStudyStatistics 获取学习统计 func (h *VocabularyHandler) GetStudyStatistics(c *gin.Context) { userID := c.GetInt64("user_id") date := c.Query("date") stats, err := h.vocabularyService.GetStudyStatistics(userID, date) if err != nil { common.ErrorResponse(c, 500, "获取学习统计失败") return } common.SuccessResponse(c, stats) } // GetStudyStatisticsHistory 获取学习统计历史 func (h *VocabularyHandler) GetStudyStatisticsHistory(c *gin.Context) { userID := c.GetInt64("user_id") startDate := c.Query("startDate") endDate := c.Query("endDate") history, err := h.vocabularyService.GetStudyStatisticsHistory(userID, startDate, endDate) if err != nil { log.Printf("[ERROR] GetStudyStatisticsHistory failed: userID=%d, startDate=%s, endDate=%s, error=%v", userID, startDate, endDate, err) common.ErrorResponse(c, 500, "获取学习历史失败") return } common.SuccessResponse(c, history) } // GetSystemVocabularyBooks 获取系统词汇书列表 func (h *VocabularyHandler) GetSystemVocabularyBooks(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) category := c.Query("category") books, total, err := h.vocabularyService.GetSystemVocabularyBooks(page, limit, category) if err != nil { common.ErrorResponse(c, 500, "获取系统词汇书失败") return } totalPages := (int(total) + limit - 1) / limit pagination := &common.Pagination{ Page: page, PageSize: limit, Total: int(total), TotalPages: totalPages, } common.PaginationSuccessResponse(c, books, pagination) } // GetVocabularyBookCategories 获取词汇书分类列表 func (h *VocabularyHandler) GetVocabularyBookCategories(c *gin.Context) { categories, err := h.vocabularyService.GetVocabularyBookCategories() if err != nil { common.ErrorResponse(c, 500, "获取词汇书分类失败") return } common.SuccessResponse(c, categories) } // GetVocabularyBookProgress 获取词汇书学习进度 func (h *VocabularyHandler) GetVocabularyBookProgress(c *gin.Context) { bookID := c.Param("id") if bookID == "" { common.ErrorResponse(c, 400, "词汇书ID不能为空") return } userID := c.GetInt64("user_id") progress, err := h.vocabularyService.GetVocabularyBookProgress(userID, bookID) if err != nil { // 如果没有进度记录,返回默认进度 now := time.Now() defaultProgress := &models.UserVocabularyBookProgress{ ID: 0, UserID: userID, BookID: bookID, LearnedWords: 0, MasteredWords: 0, ProgressPercentage: 0.0, StreakDays: 0, TotalStudyDays: 0, AverageDailyWords: 0.0, EstimatedCompletionDate: nil, IsCompleted: false, CompletedAt: nil, StartedAt: now, LastStudiedAt: now, } common.SuccessResponse(c, defaultProgress) return } common.SuccessResponse(c, progress) } // GetVocabularyBookWords 获取词汇书单词列表 func (h *VocabularyHandler) GetVocabularyBookWords(c *gin.Context) { bookID := c.Param("id") if bookID == "" { common.ErrorResponse(c, 400, "词汇书ID不能为空") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "100")) words, total, err := h.vocabularyService.GetVocabularyBookWords(bookID, page, limit) if err != nil { common.ErrorResponse(c, 500, "获取词汇书单词失败") return } totalPages := (int(total) + limit - 1) / limit pagination := &common.Pagination{ Page: page, PageSize: limit, Total: int(total), TotalPages: totalPages, } common.PaginationSuccessResponse(c, words, pagination) } // GetUserWordProgress 获取用户单词学习进度 func (h *VocabularyHandler) GetUserWordProgress(c *gin.Context) { userID := c.GetInt64("user_id") wordIDStr := c.Param("id") wordID, err := strconv.ParseInt(wordIDStr, 10, 64) if err != nil { common.ErrorResponse(c, 400, "无效的单词ID") return } // 查询用户单词进度 progress, err := h.vocabularyService.GetUserWordProgress(userID, wordID) if err != nil { common.ErrorResponse(c, 500, "获取单词进度失败") return } common.SuccessResponse(c, progress) } // UpdateUserWordProgress 更新用户单词学习进度 func (h *VocabularyHandler) UpdateUserWordProgress(c *gin.Context) { userID := c.GetInt64("user_id") wordIDStr := c.Param("id") wordID, err := strconv.ParseInt(wordIDStr, 10, 64) if err != nil { common.ErrorResponse(c, 400, "无效的单词ID") return } var req struct { Status string `json:"status"` IsCorrect *bool `json:"isCorrect"` } if err := c.ShouldBindJSON(&req); err != nil { common.ErrorResponse(c, 400, "无效的请求参数") return } // 更新单词进度 progress, err := h.vocabularyService.UpdateUserWordProgress(userID, wordID, req.Status, req.IsCorrect) if err != nil { common.ErrorResponse(c, 500, "更新单词进度失败") return } common.SuccessResponse(c, progress) }