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

337 lines
11 KiB
Go
Raw Permalink Normal View History

2025-11-17 14:09:17 +08:00
package services
import (
"database/sql"
"fmt"
"time"
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/models"
"gorm.io/gorm"
)
// WritingService 写作练习服务
type WritingService struct {
db *gorm.DB
}
// NewWritingService 创建写作练习服务实例
func NewWritingService(db *gorm.DB) *WritingService {
return &WritingService{
db: db,
}
}
// ===== 写作题目管理 =====
// GetWritingPrompts 获取写作题目列表
func (s *WritingService) GetWritingPrompts(difficulty string, category string, limit, offset int) ([]*models.WritingPrompt, error) {
var prompts []*models.WritingPrompt
query := s.db.Where("is_active = ?", true)
if difficulty != "" {
query = query.Where("level = ?", difficulty)
}
if category != "" {
query = query.Where("category = ?", category)
}
err := query.Order("created_at DESC").Limit(limit).Offset(offset).Find(&prompts).Error
return prompts, err
}
// GetWritingPrompt 获取单个写作题目
func (s *WritingService) GetWritingPrompt(id string) (*models.WritingPrompt, error) {
var prompt models.WritingPrompt
err := s.db.Where("id = ? AND is_active = ?", id, true).First(&prompt).Error
if err != nil {
return nil, err
}
return &prompt, nil
}
// CreateWritingPrompt 创建写作题目
func (s *WritingService) CreateWritingPrompt(prompt *models.WritingPrompt) error {
return s.db.Create(prompt).Error
}
// UpdateWritingPrompt 更新写作题目
func (s *WritingService) UpdateWritingPrompt(id string, prompt *models.WritingPrompt) error {
return s.db.Where("id = ?", id).Updates(prompt).Error
}
// DeleteWritingPrompt 删除写作题目(软删除)
func (s *WritingService) DeleteWritingPrompt(id string) error {
return s.db.Model(&models.WritingPrompt{}).Where("id = ?", id).Update("is_active", false).Error
}
// SearchWritingPrompts 搜索写作题目
func (s *WritingService) SearchWritingPrompts(keyword string, difficulty string, category string, limit, offset int) ([]*models.WritingPrompt, error) {
var prompts []*models.WritingPrompt
query := s.db.Where("is_active = ?", true)
if keyword != "" {
query = query.Where("title LIKE ? OR description LIKE ?", "%"+keyword+"%", "%"+keyword+"%")
}
if difficulty != "" {
query = query.Where("level = ?", difficulty)
}
if category != "" {
query = query.Where("category = ?", category)
}
err := query.Order("created_at DESC").Limit(limit).Offset(offset).Find(&prompts).Error
return prompts, err
}
// GetRecommendedPrompts 获取推荐写作题目
func (s *WritingService) GetRecommendedPrompts(userID string, limit int) ([]*models.WritingPrompt, error) {
// 基于用户历史表现推荐合适难度的题目
var prompts []*models.WritingPrompt
// 获取用户最近的写作记录,分析难度偏好
var avgScore sql.NullFloat64
s.db.Model(&models.WritingSubmission{}).
Where("user_id = ? AND score IS NOT NULL", userID).
Select("AVG(score)").Scan(&avgScore)
// 根据平均分推荐合适难度
var difficulty string
if !avgScore.Valid || avgScore.Float64 < 60 {
difficulty = "beginner"
} else if avgScore.Float64 < 80 {
difficulty = "intermediate"
} else {
difficulty = "advanced"
}
err := s.db.Where("level = ? AND is_active = ?", difficulty, true).
Order("RAND()").Limit(limit).Find(&prompts).Error
return prompts, err
}
// ===== 写作提交管理 =====
// CreateWritingSubmission 创建写作提交
func (s *WritingService) CreateWritingSubmission(submission *models.WritingSubmission) error {
return s.db.Create(submission).Error
}
// UpdateWritingSubmission 更新写作提交
func (s *WritingService) UpdateWritingSubmission(id string, submission *models.WritingSubmission) error {
return s.db.Where("id = ?", id).Updates(submission).Error
}
// GetWritingSubmission 获取写作提交详情
func (s *WritingService) GetWritingSubmission(id string) (*models.WritingSubmission, error) {
var submission models.WritingSubmission
err := s.db.Preload("Prompt").Where("id = ?", id).First(&submission).Error
if err != nil {
return nil, err
}
return &submission, nil
}
// GetUserWritingSubmissions 获取用户写作提交列表
func (s *WritingService) GetUserWritingSubmissions(userID string, limit, offset int) ([]*models.WritingSubmission, error) {
var submissions []*models.WritingSubmission
err := s.db.Preload("Prompt").Where("user_id = ?", userID).
Order("created_at DESC").Limit(limit).Offset(offset).Find(&submissions).Error
return submissions, err
}
// SubmitWriting 提交写作作业
func (s *WritingService) SubmitWriting(submissionID string, content string, timeSpent int) error {
now := time.Now()
updates := map[string]interface{}{
"content": content,
"word_count": len(content), // 简单字数统计,实际可能需要更复杂的逻辑
"time_spent": timeSpent,
"submitted_at": &now,
}
return s.db.Model(&models.WritingSubmission{}).Where("id = ?", submissionID).Updates(updates).Error
}
// GradeWriting AI批改写作
func (s *WritingService) GradeWriting(submissionID string, score, grammarScore, vocabScore, coherenceScore float64, feedback, suggestions string) error {
now := time.Now()
updates := map[string]interface{}{
"score": score,
"grammar_score": grammarScore,
"vocab_score": vocabScore,
"coherence_score": coherenceScore,
"feedback": feedback,
"suggestions": suggestions,
"graded_at": &now,
}
return s.db.Model(&models.WritingSubmission{}).Where("id = ?", submissionID).Updates(updates).Error
}
// ===== 写作统计分析 =====
// GetUserWritingStats 获取用户写作统计
func (s *WritingService) GetUserWritingStats(userID string) (map[string]interface{}, error) {
stats := make(map[string]interface{})
// 总提交数
var totalSubmissions int64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ?", userID).Count(&totalSubmissions)
stats["total_submissions"] = totalSubmissions
// 已完成提交数
var completedSubmissions int64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND submitted_at IS NOT NULL", userID).Count(&completedSubmissions)
stats["completed_submissions"] = completedSubmissions
// 已批改提交数
var gradedSubmissions int64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND graded_at IS NOT NULL", userID).Count(&gradedSubmissions)
stats["graded_submissions"] = gradedSubmissions
// 平均分数
var avgScore sql.NullFloat64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND score IS NOT NULL", userID).Select("AVG(score)").Scan(&avgScore)
if avgScore.Valid {
stats["average_score"] = fmt.Sprintf("%.2f", avgScore.Float64)
} else {
stats["average_score"] = "0.00"
}
// 平均语法分数
var avgGrammarScore sql.NullFloat64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND grammar_score IS NOT NULL", userID).Select("AVG(grammar_score)").Scan(&avgGrammarScore)
if avgGrammarScore.Valid {
stats["average_grammar_score"] = fmt.Sprintf("%.2f", avgGrammarScore.Float64)
} else {
stats["average_grammar_score"] = "0.00"
}
// 平均词汇分数
var avgVocabScore sql.NullFloat64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND vocab_score IS NOT NULL", userID).Select("AVG(vocab_score)").Scan(&avgVocabScore)
if avgVocabScore.Valid {
stats["average_vocab_score"] = fmt.Sprintf("%.2f", avgVocabScore.Float64)
} else {
stats["average_vocab_score"] = "0.00"
}
// 平均连贯性分数
var avgCoherenceScore sql.NullFloat64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND coherence_score IS NOT NULL", userID).Select("AVG(coherence_score)").Scan(&avgCoherenceScore)
if avgCoherenceScore.Valid {
stats["average_coherence_score"] = fmt.Sprintf("%.2f", avgCoherenceScore.Float64)
} else {
stats["average_coherence_score"] = "0.00"
}
// 总写作时间
var totalTimeSpent sql.NullInt64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND time_spent IS NOT NULL", userID).Select("SUM(time_spent)").Scan(&totalTimeSpent)
if totalTimeSpent.Valid {
stats["total_time_spent"] = totalTimeSpent.Int64
} else {
stats["total_time_spent"] = 0
}
// 平均写作时间
var avgTimeSpent sql.NullFloat64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND time_spent IS NOT NULL", userID).Select("AVG(time_spent)").Scan(&avgTimeSpent)
if avgTimeSpent.Valid {
stats["average_time_spent"] = fmt.Sprintf("%.2f", avgTimeSpent.Float64)
} else {
stats["average_time_spent"] = "0.00"
}
// 总字数
var totalWordCount sql.NullInt64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND word_count IS NOT NULL", userID).Select("SUM(word_count)").Scan(&totalWordCount)
if totalWordCount.Valid {
stats["total_word_count"] = totalWordCount.Int64
} else {
stats["total_word_count"] = 0
}
// 平均字数
var avgWordCount sql.NullFloat64
s.db.Model(&models.WritingSubmission{}).Where("user_id = ? AND word_count IS NOT NULL", userID).Select("AVG(word_count)").Scan(&avgWordCount)
if avgWordCount.Valid {
stats["average_word_count"] = fmt.Sprintf("%.2f", avgWordCount.Float64)
} else {
stats["average_word_count"] = "0.00"
}
// 连续写作天数
continuousDays := s.calculateContinuousWritingDays(userID)
stats["continuous_writing_days"] = continuousDays
// 按难度统计
difficultyStats := s.getWritingStatsByDifficulty(userID)
stats["difficulty_stats"] = difficultyStats
return stats, nil
}
// calculateContinuousWritingDays 计算连续写作天数
func (s *WritingService) calculateContinuousWritingDays(userID string) int {
var dates []time.Time
s.db.Model(&models.WritingSubmission{}).
Where("user_id = ? AND submitted_at IS NOT NULL", userID).
Select("DATE(submitted_at) as date").
Group("DATE(submitted_at)").
Order("date DESC").
Pluck("date", &dates)
if len(dates) == 0 {
return 0
}
continuousDays := 1
for i := 1; i < len(dates); i++ {
diff := dates[i-1].Sub(dates[i]).Hours() / 24
if diff == 1 {
continuousDays++
} else {
break
}
}
return continuousDays
}
// getWritingStatsByDifficulty 按难度获取写作统计
func (s *WritingService) getWritingStatsByDifficulty(userID string) map[string]interface{} {
type DifficultyStats struct {
Difficulty string `json:"difficulty"`
Count int64 `json:"count"`
AvgScore float64 `json:"avg_score"`
}
var stats []DifficultyStats
s.db.Model(&models.WritingSubmission{}).
Select("p.level as difficulty, COUNT(*) as count, COALESCE(AVG(writing_submissions.score), 0) as avg_score").
Joins("JOIN writing_prompts p ON writing_submissions.prompt_id = p.id").
Where("writing_submissions.user_id = ? AND writing_submissions.submitted_at IS NOT NULL", userID).
Group("p.level").
Scan(&stats)
result := make(map[string]interface{})
for _, stat := range stats {
result[stat.Difficulty] = map[string]interface{}{
"count": stat.Count,
"avg_score": fmt.Sprintf("%.2f", stat.AvgScore),
}
}
return result
}
// GetWritingProgress 获取写作进度
func (s *WritingService) GetWritingProgress(userID string, promptID string) (*models.WritingSubmission, error) {
var submission models.WritingSubmission
err := s.db.Where("user_id = ? AND prompt_id = ?", userID, promptID).First(&submission).Error
if err != nil {
return nil, err
}
return &submission, nil
}