Files
ai_english/serve/internal/services/study_plan_service.go

318 lines
8.5 KiB
Go
Raw Permalink Normal View History

2025-11-17 14:09:17 +08:00
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
}