Files
ai_english/serve/internal/services/word_book_service.go
2025-11-17 14:09:17 +08:00

238 lines
6.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package services
import (
"time"
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/models"
"gorm.io/gorm"
)
// WordBookService 生词本服务
type WordBookService struct {
db *gorm.DB
}
func NewWordBookService(db *gorm.DB) *WordBookService {
return &WordBookService{db: db}
}
// ToggleFavorite 切换单词收藏状态
func (s *WordBookService) ToggleFavorite(userID, wordID int64) (bool, error) {
var progress models.UserWordProgress
err := s.db.Where("user_id = ? AND vocabulary_id = ?", userID, wordID).First(&progress).Error
if err == gorm.ErrRecordNotFound {
// 如果没有进度记录,创建一个并设置为收藏
now := time.Now()
progress = models.UserWordProgress{
UserID: userID,
VocabularyID: wordID,
Status: "not_started",
StudyCount: 0,
CorrectCount: 0,
WrongCount: 0,
Proficiency: 0,
ReviewInterval: 1,
FirstStudiedAt: now,
LastStudiedAt: now,
}
// 通过GORM钩子设置IsFavorite因为是bool类型的零值问题
if err := s.db.Create(&progress).Error; err != nil {
return false, err
}
// 更新IsFavorite为true
if err := s.db.Model(&progress).Update("is_favorite", true).Error; err != nil {
return false, err
}
return true, nil
} else if err != nil {
return false, err
}
// 切换收藏状态
newStatus := !progress.IsFavorite
if err := s.db.Model(&progress).Update("is_favorite", newStatus).Error; err != nil {
return false, err
}
return newStatus, nil
}
// GetFavoriteWords 获取生词本列表(带分页)
func (s *WordBookService) GetFavoriteWords(userID int64, page, pageSize int, sortBy, order string) ([]map[string]interface{}, int64, error) {
var total int64
// 统计总数
s.db.Model(&models.UserWordProgress{}).
Where("user_id = ? AND is_favorite = ?", userID, true).
Count(&total)
if total == 0 {
return []map[string]interface{}{}, 0, nil
}
// 构建排序字段
orderClause := "uwp.created_at DESC"
switch sortBy {
case "proficiency":
orderClause = "uwp.proficiency " + order
case "word":
orderClause = "v.word " + order
case "created_at":
orderClause = "uwp.created_at " + order
}
// 查询收藏的单词及其详情
var results []map[string]interface{}
offset := (page - 1) * pageSize
err := s.db.Raw(`
SELECT
v.id,
v.word,
v.phonetic,
v.audio_url,
v.level,
uwp.proficiency,
uwp.study_count,
uwp.status,
uwp.next_review_at,
uwp.created_at as favorited_at,
GROUP_CONCAT(DISTINCT vd.definition_cn SEPARATOR '; ') as definitions,
GROUP_CONCAT(DISTINCT vd.part_of_speech SEPARATOR ', ') as parts_of_speech
FROM ai_user_word_progress uwp
INNER JOIN ai_vocabulary v ON v.id = uwp.vocabulary_id
LEFT JOIN ai_vocabulary_definitions vd ON vd.vocabulary_id = v.id
WHERE uwp.user_id = ? AND uwp.is_favorite = true
GROUP BY v.id, v.word, v.phonetic, v.audio_url, v.level,
uwp.proficiency, uwp.study_count, uwp.status, uwp.next_review_at, uwp.created_at
ORDER BY `+orderClause+`
LIMIT ? OFFSET ?
`, userID, pageSize, offset).Scan(&results).Error
if err != nil {
return nil, 0, err
}
return results, total, nil
}
// GetFavoriteWordsByBook 获取指定词汇书的生词本
func (s *WordBookService) GetFavoriteWordsByBook(userID int64, bookID string) ([]map[string]interface{}, error) {
var results []map[string]interface{}
err := s.db.Raw(`
SELECT
v.id,
v.word,
v.phonetic,
v.audio_url,
v.level,
uwp.proficiency,
uwp.study_count,
uwp.status,
GROUP_CONCAT(DISTINCT vd.definition_cn SEPARATOR '; ') as definitions,
GROUP_CONCAT(DISTINCT vd.part_of_speech SEPARATOR ', ') as parts_of_speech
FROM ai_user_word_progress uwp
INNER JOIN ai_vocabulary v ON v.id = uwp.vocabulary_id
INNER JOIN ai_vocabulary_book_words vbw ON CAST(vbw.vocabulary_id AS UNSIGNED) = v.id
LEFT JOIN ai_vocabulary_definitions vd ON vd.vocabulary_id = v.id
WHERE uwp.user_id = ? AND uwp.is_favorite = true AND vbw.book_id = ?
GROUP BY v.id, v.word, v.phonetic, v.audio_url, v.level,
uwp.proficiency, uwp.study_count, uwp.status
ORDER BY uwp.created_at DESC
`, userID, bookID).Scan(&results).Error
if err != nil {
return nil, err
}
return results, nil
}
// GetFavoriteStats 获取生词本统计信息
func (s *WordBookService) GetFavoriteStats(userID int64) (map[string]interface{}, error) {
var stats struct {
TotalWords int64
MasteredWords int64
ReviewingWords int64
LearningWords int64
AvgProficiency float64
NeedReviewToday int64
}
// 统计总数和各状态数量
s.db.Raw(`
SELECT
COUNT(*) as total_words,
SUM(CASE WHEN status = 'mastered' THEN 1 ELSE 0 END) as mastered_words,
SUM(CASE WHEN status = 'reviewing' THEN 1 ELSE 0 END) as reviewing_words,
SUM(CASE WHEN status = 'learning' THEN 1 ELSE 0 END) as learning_words,
AVG(proficiency) as avg_proficiency,
SUM(CASE WHEN next_review_at IS NOT NULL AND next_review_at <= NOW() THEN 1 ELSE 0 END) as need_review_today
FROM ai_user_word_progress
WHERE user_id = ? AND is_favorite = true
`, userID).Scan(&stats)
return map[string]interface{}{
"total_words": stats.TotalWords,
"mastered_words": stats.MasteredWords,
"reviewing_words": stats.ReviewingWords,
"learning_words": stats.LearningWords,
"avg_proficiency": stats.AvgProficiency,
"need_review_today": stats.NeedReviewToday,
}, nil
}
// BatchAddToFavorite 批量添加到生词本
func (s *WordBookService) BatchAddToFavorite(userID int64, wordIDs []int64) (int, error) {
count := 0
now := time.Now()
for _, wordID := range wordIDs {
var progress models.UserWordProgress
err := s.db.Where("user_id = ? AND vocabulary_id = ?", userID, wordID).First(&progress).Error
if err == gorm.ErrRecordNotFound {
// 创建新记录
progress = models.UserWordProgress{
UserID: userID,
VocabularyID: wordID,
Status: "not_started",
StudyCount: 0,
CorrectCount: 0,
WrongCount: 0,
Proficiency: 0,
ReviewInterval: 1,
FirstStudiedAt: now,
LastStudiedAt: now,
}
if err := s.db.Create(&progress).Error; err != nil {
continue
}
if err := s.db.Model(&progress).Update("is_favorite", true).Error; err != nil {
continue
}
count++
} else if err == nil && !progress.IsFavorite {
// 更新现有记录
if err := s.db.Model(&progress).Update("is_favorite", true).Error; err != nil {
continue
}
count++
}
}
return count, nil
}
// RemoveFromFavorite 从生词本移除
func (s *WordBookService) RemoveFromFavorite(userID, wordID int64) error {
return s.db.Model(&models.UserWordProgress{}).
Where("user_id = ? AND vocabulary_id = ?", userID, wordID).
Update("is_favorite", false).Error
}