feat: add study session APIs and per-user review isolation
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -168,13 +169,13 @@ func (s *WordService) DownloadAudio(url, filePath string) error {
|
||||
}
|
||||
|
||||
// 保存单词到数据库
|
||||
func (s *WordService) SaveWord(word string, youdaoResp *model.YoudaoResponse) (*model.Word, error) {
|
||||
func (s *WordService) SaveWord(userID int64, word string, youdaoResp *model.YoudaoResponse) (*model.Word, error) {
|
||||
var existingWord model.Word
|
||||
|
||||
// 检查单词是否已存在
|
||||
if err := s.db.Where("word = ?", word).First(&existingWord).Error; err == nil {
|
||||
// 单词已存在,更新记忆记录
|
||||
s.updateMemoryRecord(existingWord.ID, true)
|
||||
s.updateMemoryRecord(userID, existingWord.ID, true)
|
||||
return &existingWord, nil
|
||||
}
|
||||
|
||||
@@ -271,17 +272,17 @@ func (s *WordService) SaveWord(word string, youdaoResp *model.YoudaoResponse) (*
|
||||
}
|
||||
|
||||
// 创建记忆记录 + 记一次"背过"
|
||||
_ = s.createMemoryRecord(newWord.ID)
|
||||
_ = s.updateMemoryRecord(newWord.ID, true)
|
||||
_ = s.createMemoryRecord(userID, newWord.ID)
|
||||
_ = s.updateMemoryRecord(userID, newWord.ID, true)
|
||||
|
||||
return &newWord, nil
|
||||
}
|
||||
|
||||
// 创建记忆记录
|
||||
func (s *WordService) createMemoryRecord(wordID int64) error {
|
||||
func (s *WordService) createMemoryRecord(userID, wordID int64) error {
|
||||
record := model.MemoryRecord{
|
||||
WordID: wordID,
|
||||
UserID: 1,
|
||||
UserID: userID,
|
||||
CorrectCount: 0,
|
||||
TotalCount: 0,
|
||||
MasteryLevel: 0,
|
||||
@@ -290,10 +291,19 @@ func (s *WordService) createMemoryRecord(wordID int64) error {
|
||||
}
|
||||
|
||||
// 更新记忆记录
|
||||
func (s *WordService) updateMemoryRecord(wordID int64, correct bool) error {
|
||||
func (s *WordService) updateMemoryRecord(userID, wordID int64, correct bool) error {
|
||||
var record model.MemoryRecord
|
||||
if err := s.db.Where("word_id = ?", wordID).First(&record).Error; err != nil {
|
||||
return err
|
||||
if err := s.db.Where("word_id = ? AND user_id = ?", wordID, userID).First(&record).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if createErr := s.createMemoryRecord(userID, wordID); createErr != nil {
|
||||
return createErr
|
||||
}
|
||||
if err = s.db.Where("word_id = ? AND user_id = ?", wordID, userID).First(&record).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
record.TotalCount++
|
||||
@@ -317,10 +327,10 @@ func (s *WordService) updateMemoryRecord(wordID int64, correct bool) error {
|
||||
}
|
||||
|
||||
// 获取待复习单词
|
||||
func (s *WordService) GetReviewWords(mode string, limit int) ([]model.MemoryRecord, error) {
|
||||
func (s *WordService) GetReviewWords(userID int64, mode string, limit int) ([]model.MemoryRecord, error) {
|
||||
var records []model.MemoryRecord
|
||||
|
||||
query := s.db.Preload("Word").Where("next_review_at <= ? OR next_review_at IS NULL", time.Now())
|
||||
query := s.db.Preload("Word").Where("user_id = ? AND (next_review_at <= ? OR next_review_at IS NULL)", userID, time.Now())
|
||||
|
||||
if err := query.Limit(limit).Find(&records).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -328,7 +338,7 @@ func (s *WordService) GetReviewWords(mode string, limit int) ([]model.MemoryReco
|
||||
|
||||
// 如果没有需要复习的,随机获取一些
|
||||
if len(records) == 0 {
|
||||
if err := s.db.Preload("Word").Limit(limit).Find(&records).Error; err != nil {
|
||||
if err := s.db.Preload("Word").Where("user_id = ?", userID).Limit(limit).Find(&records).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -361,9 +371,9 @@ func containsAny(def string, ans string) bool {
|
||||
}
|
||||
|
||||
// 提交复习答案
|
||||
func (s *WordService) SubmitReviewAnswer(req request.ReviewAnswerRequest) (*response.ReviewResult, error) {
|
||||
func (s *WordService) SubmitReviewAnswer(userID int64, req request.ReviewAnswerRequest) (*response.ReviewResult, error) {
|
||||
var record model.MemoryRecord
|
||||
if err := s.db.Preload("Word").Where("id = ?", req.RecordID).First(&record).Error; err != nil {
|
||||
if err := s.db.Preload("Word").Where("id = ? AND user_id = ?", req.RecordID, userID).First(&record).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -387,7 +397,7 @@ func (s *WordService) SubmitReviewAnswer(req request.ReviewAnswerRequest) (*resp
|
||||
}
|
||||
|
||||
// 更新记忆记录
|
||||
s.updateMemoryRecord(record.WordID, correct)
|
||||
s.updateMemoryRecord(userID, record.WordID, correct)
|
||||
|
||||
return &response.ReviewResult{
|
||||
Word: word,
|
||||
@@ -412,16 +422,16 @@ func (s *WordService) GetAllWords(limit, offset int) ([]model.Word, int64, error
|
||||
}
|
||||
|
||||
// 获取记忆统计
|
||||
func (s *WordService) GetStatistics() (map[string]interface{}, error) {
|
||||
func (s *WordService) GetStatistics(userID int64) (map[string]interface{}, error) {
|
||||
var totalWords int64
|
||||
var masteredWords int64
|
||||
var needReview int64
|
||||
var todayReviewed int64
|
||||
|
||||
s.db.Model(&model.Word{}).Count(&totalWords)
|
||||
s.db.Model(&model.MemoryRecord{}).Where("mastery_level >= 4").Count(&masteredWords)
|
||||
s.db.Model(&model.MemoryRecord{}).Where("next_review_at <= ?", time.Now()).Count(&needReview)
|
||||
s.db.Model(&model.MemoryRecord{}).Where("last_reviewed_at >= ?", time.Now().Format("2006-01-02")).Count(&todayReviewed)
|
||||
s.db.Model(&model.MemoryRecord{}).Where("user_id = ?", userID).Count(&totalWords)
|
||||
s.db.Model(&model.MemoryRecord{}).Where("user_id = ? AND mastery_level >= 4", userID).Count(&masteredWords)
|
||||
s.db.Model(&model.MemoryRecord{}).Where("user_id = ? AND next_review_at <= ?", userID, time.Now()).Count(&needReview)
|
||||
s.db.Model(&model.MemoryRecord{}).Where("user_id = ? AND last_reviewed_at >= ?", userID, time.Now().Format("2006-01-02")).Count(&todayReviewed)
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_words": totalWords,
|
||||
@@ -430,3 +440,42 @@ func (s *WordService) GetStatistics() (map[string]interface{}, error) {
|
||||
"today_reviewed": todayReviewed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *WordService) CreateStudySession(userID int64, limit int) ([]model.Word, error) {
|
||||
if limit <= 0 || limit > 50 {
|
||||
limit = 10
|
||||
}
|
||||
var words []model.Word
|
||||
if err := s.db.Order("created_at DESC").Limit(limit).Find(&words).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range words {
|
||||
_ = s.createMemoryRecord(userID, words[i].ID)
|
||||
}
|
||||
return words, nil
|
||||
}
|
||||
|
||||
func (s *WordService) SubmitStudyAnswer(userID int64, req request.SubmitStudyAnswerRequest) (*response.ReviewResult, error) {
|
||||
reviewReq := request.ReviewAnswerRequest{
|
||||
RecordID: 0,
|
||||
Answer: req.Answer,
|
||||
Mode: req.Mode,
|
||||
}
|
||||
|
||||
var record model.MemoryRecord
|
||||
if err := s.db.Where("user_id = ? AND word_id = ?", userID, req.WordID).First(&record).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if createErr := s.createMemoryRecord(userID, req.WordID); createErr != nil {
|
||||
return nil, createErr
|
||||
}
|
||||
if err = s.db.Where("user_id = ? AND word_id = ?", userID, req.WordID).First(&record).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
reviewReq.RecordID = record.ID
|
||||
return s.SubmitReviewAnswer(userID, reviewReq)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user