package services import ( "errors" "time" "github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/models" "gorm.io/gorm" ) // StudyPlanService 学习计划服务 type StudyPlanService struct { db *gorm.DB } func NewStudyPlanService(db *gorm.DB) *StudyPlanService { return &StudyPlanService{db: db} } // CreateStudyPlan 创建学习计划 func (s *StudyPlanService) CreateStudyPlan(userID int64, planName string, description *string, dailyGoal int, bookID *string, startDate time.Time, endDate *time.Time, remindTime *string, remindDays *string) (*models.StudyPlan, error) { // 验证参数 if dailyGoal < 1 || dailyGoal > 200 { return nil, errors.New("每日目标必须在1-200之间") } // 如果指定了词汇书,获取总单词数 totalWords := 0 if bookID != nil && *bookID != "" { var count int64 s.db.Model(&models.VocabularyBookWord{}).Where("book_id = ?", *bookID).Count(&count) totalWords = int(count) } plan := &models.StudyPlan{ UserID: userID, BookID: bookID, PlanName: planName, Description: description, DailyGoal: dailyGoal, TotalWords: totalWords, LearnedWords: 0, StartDate: startDate, EndDate: endDate, Status: "active", RemindTime: remindTime, RemindDays: remindDays, IsRemindEnabled: remindTime != nil && remindDays != nil, StreakDays: 0, } if err := s.db.Create(plan).Error; err != nil { return nil, err } return plan, nil } // GetUserStudyPlans 获取用户的学习计划列表 func (s *StudyPlanService) GetUserStudyPlans(userID int64, status string) ([]models.StudyPlan, error) { var plans []models.StudyPlan query := s.db.Where("user_id = ?", userID) if status != "" && status != "all" { query = query.Where("status = ?", status) } err := query.Order("created_at DESC").Find(&plans).Error return plans, err } // GetStudyPlanByID 获取学习计划详情 func (s *StudyPlanService) GetStudyPlanByID(planID, userID int64) (*models.StudyPlan, error) { var plan models.StudyPlan err := s.db.Where("id = ? AND user_id = ?", planID, userID).First(&plan).Error if err != nil { return nil, err } return &plan, nil } // UpdateStudyPlan 更新学习计划 func (s *StudyPlanService) UpdateStudyPlan(planID, userID int64, updates map[string]interface{}) (*models.StudyPlan, error) { var plan models.StudyPlan if err := s.db.Where("id = ? AND user_id = ?", planID, userID).First(&plan).Error; err != nil { return nil, err } // 验证dailyGoal if dailyGoal, ok := updates["daily_goal"].(int); ok { if dailyGoal < 1 || dailyGoal > 200 { return nil, errors.New("每日目标必须在1-200之间") } } if err := s.db.Model(&plan).Updates(updates).Error; err != nil { return nil, err } return &plan, nil } // DeleteStudyPlan 删除学习计划(软删除) func (s *StudyPlanService) DeleteStudyPlan(planID, userID int64) error { result := s.db.Where("id = ? AND user_id = ?", planID, userID).Delete(&models.StudyPlan{}) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.New("计划不存在或无权删除") } return nil } // UpdatePlanStatus 更新计划状态 func (s *StudyPlanService) UpdatePlanStatus(planID, userID int64, status string) error { validStatuses := map[string]bool{ "active": true, "paused": true, "completed": true, "cancelled": true, } if !validStatuses[status] { return errors.New("无效的状态") } updates := map[string]interface{}{ "status": status, } if status == "completed" { now := time.Now() updates["completed_at"] = now } result := s.db.Model(&models.StudyPlan{}). Where("id = ? AND user_id = ?", planID, userID). Updates(updates) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.New("计划不存在或无权修改") } return nil } // RecordStudyProgress 记录学习进度 func (s *StudyPlanService) RecordStudyProgress(planID, userID int64, wordsStudied, studyDuration int) error { var plan models.StudyPlan if err := s.db.Where("id = ? AND user_id = ?", planID, userID).First(&plan).Error; err != nil { return err } today := time.Now().Truncate(24 * time.Hour) // 检查今天是否已有记录 var record models.StudyPlanRecord err := s.db.Where("plan_id = ? AND user_id = ? AND study_date = ?", planID, userID, today).First(&record).Error if err == gorm.ErrRecordNotFound { // 创建新记录 record = models.StudyPlanRecord{ PlanID: planID, UserID: userID, StudyDate: today, WordsStudied: wordsStudied, GoalCompleted: wordsStudied >= plan.DailyGoal, StudyDuration: studyDuration, } if err := s.db.Create(&record).Error; err != nil { return err } } else if err == nil { // 更新现有记录 record.WordsStudied += wordsStudied record.StudyDuration += studyDuration record.GoalCompleted = record.WordsStudied >= plan.DailyGoal if err := s.db.Save(&record).Error; err != nil { return err } } else { return err } // 更新计划的已学单词数 plan.LearnedWords += wordsStudied // 更新连续学习天数 if plan.LastStudyDate != nil { lastDate := plan.LastStudyDate.Truncate(24 * time.Hour) yesterday := today.AddDate(0, 0, -1) if lastDate.Equal(yesterday) { plan.StreakDays++ } else if !lastDate.Equal(today) { plan.StreakDays = 1 } } else { plan.StreakDays = 1 } plan.LastStudyDate = &today // 检查是否完成计划 if plan.TotalWords > 0 && plan.LearnedWords >= plan.TotalWords { plan.Status = "completed" now := time.Now() plan.CompletedAt = &now } return s.db.Save(&plan).Error } // GetStudyPlanStatistics 获取学习计划统计 func (s *StudyPlanService) GetStudyPlanStatistics(planID, userID int64) (map[string]interface{}, error) { var plan models.StudyPlan if err := s.db.Where("id = ? AND user_id = ?", planID, userID).First(&plan).Error; err != nil { return nil, err } // 统计总学习天数 var totalStudyDays int64 s.db.Model(&models.StudyPlanRecord{}). Where("plan_id = ? AND user_id = ?", planID, userID). Count(&totalStudyDays) // 统计完成目标的天数 var completedDays int64 s.db.Model(&models.StudyPlanRecord{}). Where("plan_id = ? AND user_id = ? AND goal_completed = ?", planID, userID, true). Count(&completedDays) // 计算平均每日学习单词数 var avgWords float64 s.db.Model(&models.StudyPlanRecord{}). Select("AVG(words_studied) as avg_words"). Where("plan_id = ? AND user_id = ?", planID, userID). Scan(&avgWords) // 计算完成率 completionRate := 0.0 if plan.TotalWords > 0 { completionRate = float64(plan.LearnedWords) / float64(plan.TotalWords) * 100 } // 获取最近7天的学习记录 var recentRecords []models.StudyPlanRecord s.db.Where("plan_id = ? AND user_id = ?", planID, userID). Order("study_date DESC"). Limit(7). Find(&recentRecords) return map[string]interface{}{ "plan": plan, "total_study_days": totalStudyDays, "completed_days": completedDays, "avg_words": avgWords, "completion_rate": completionRate, "recent_records": recentRecords, "streak_days": plan.StreakDays, }, nil } // GetTodayStudyPlans 获取今日需要执行的学习计划 func (s *StudyPlanService) GetTodayStudyPlans(userID int64) ([]map[string]interface{}, error) { var plans []models.StudyPlan today := time.Now().Truncate(24 * time.Hour) weekday := int(time.Now().Weekday()) if weekday == 0 { weekday = 7 // 将周日从0改为7 } // 查找活跃的计划 err := s.db.Where("user_id = ? AND status = ? AND start_date <= ?", userID, "active", today). Find(&plans).Error if err != nil { return nil, err } result := make([]map[string]interface{}, 0) for _, plan := range plans { // 检查今天是否已完成 var record models.StudyPlanRecord err := s.db.Where("plan_id = ? AND user_id = ? AND study_date = ?", plan.ID, userID, today). First(&record).Error todayCompleted := err == nil && record.GoalCompleted todayProgress := 0 if err == nil { todayProgress = record.WordsStudied } // 检查是否需要提醒 needRemind := false if plan.IsRemindEnabled && plan.RemindDays != nil { // 简单检查:这里可以根据RemindDays判断今天是否需要提醒 needRemind = true } result = append(result, map[string]interface{}{ "plan": plan, "today_completed": todayCompleted, "today_progress": todayProgress, "need_remind": needRemind, }) } return result, nil }