init
This commit is contained in:
237
serve/internal/services/word_book_service.go
Normal file
237
serve/internal/services/word_book_service.go
Normal file
@@ -0,0 +1,237 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user