// 心情接口 package app import ( "context" "fmt" "math/rand" "os" "simple-memo/global" "simple-memo/models" "simple-memo/utils" "time" "gorm.io/gorm" "github.com/gin-gonic/gin" ) // -------------------------- 1. 定义 Handler 接口 -------------------------- // MoodHandler 心情模块接口定义 type MoodHandler interface { GetMoodByDate(c *gin.Context) SaveMood(c *gin.Context) GetMoodList(c *gin.Context) DeleteMood(c *gin.Context) MoodStat(c *gin.Context) // 心情统计 } // -------------------------- 2. 实现结构体 -------------------------- // moodHandler 接口实现结构体 type moodHandler struct { db *gorm.DB } // NewMoodHandler 创建心情处理器 func NewMoodHandler() MoodHandler { return &moodHandler{ db: global.DB, } } // -------------------------- 3. 请求结构体 -------------------------- // GetMoodByDateReq 获取某天心情请求 type GetMoodByDateReq struct { Date string `json:"date" binding:"required"` } // SaveMoodReq 保存心情请求 type SaveMoodReq struct { ID uint `json:"id"` // 编辑时传入 Date string `json:"date" binding:"required"` Emoji string `json:"emoji" binding:"required"` Content string `json:"content"` } // GetMoodListReq 获取心情列表请求 type GetMoodListReq struct { StartDate string `json:"start_date"` EndDate string `json:"end_date"` } // DeleteMoodReq 删除心情请求 type DeleteMoodReq struct { ID uint `json:"id" binding:"required"` } // MoodStatReq 心情统计请求 type MoodStatReq struct { Month string `json:"month" binding:"required"` // YYYY-MM } // -------------------------- 4. 接口实现 -------------------------- // MoodCalendarItem 日历心情项(优化后的返回结构) type MoodCalendarItem struct { ID uint `json:"id"` Date string `json:"date"` Emoji string `json:"emoji"` Content string `json:"content"` AIStatus int `json:"ai_status"` // 0无 1生成中 2成功 3失败 AIText string `json:"ai_text"` AIImageURL string `json:"ai_image_url"` AIGenerateCount int `json:"ai_generate_count"` } // GetMoodByDate 获取某天的心情(包含AI生成内容) func (h *moodHandler) GetMoodByDate(c *gin.Context) { userID := c.GetUint("userID") var req GetMoodByDateReq if err := c.ShouldBindJSON(&req); err != nil { utils.Fail(c, "参数解析失败") return } // 使用 GORM Joins 联表查询 var result MoodCalendarItem h.db.Model(&models.Mood{}). Select(` sm_mood.ID, sm_mood.date, sm_mood.emoji, sm_mood.content, COALESCE(sm_mood_ai_generations.status, 0) as ai_status, COALESCE(sm_mood_ai_generations.ai_text, '') as ai_text, COALESCE(sm_mood_ai_generations.ai_image_url, '') as ai_image_url, COALESCE(sm_mood_ai_generations.generate_count, 0) as ai_generate_count `). Joins("LEFT JOIN sm_mood_ai_generations ON sm_mood.id = sm_mood_ai_generations.mood_id"). Where("sm_mood.user_id = ? AND sm_mood.date = ?", userID, req.Date). Scan(&result) // 检查是否找到记录 if result.ID == 0 { utils.Ok(c, nil) return } utils.Ok(c, result) } // SaveMood 保存心情 func (h *moodHandler) SaveMood(c *gin.Context) { userID := c.GetUint("userID") var req SaveMoodReq if err := c.ShouldBindJSON(&req); err != nil { utils.Fail(c, "参数解析失败") return } // 敏感词过滤 filteredContent := utils.FilterSensitive(req.Content) var mood models.Mood isNewRecord := false // 如果传入了 id,先尝试按 id 查找 if req.ID > 0 { err := h.db.Where("id = ? AND user_id = ?", req.ID, userID).First(&mood).Error if err == nil { // 编辑模式 mood.Emoji = req.Emoji mood.Content = filteredContent if err := h.db.Save(&mood).Error; err != nil { utils.Fail(c, "保存失败") return } } else { // id 找不到,按日期查找 err = h.db.Where("user_id = ? AND date = ?", userID, req.Date).First(&mood).Error if err == nil { mood.Emoji = req.Emoji mood.Content = filteredContent if err := h.db.Save(&mood).Error; err != nil { utils.Fail(c, "保存失败") return } } else { // 新增模式 mood = models.Mood{ UserID: userID, Date: req.Date, Emoji: req.Emoji, Content: filteredContent, } if err := h.db.Create(&mood).Error; err != nil { utils.Fail(c, "保存失败") return } isNewRecord = true } } } else { // 没有传入 id,按日期查找 err := h.db.Where("user_id = ? AND date = ?", userID, req.Date).First(&mood).Error if err == nil { // 更新已有记录 mood.Emoji = req.Emoji mood.Content = filteredContent if err := h.db.Save(&mood).Error; err != nil { utils.Fail(c, "保存失败") return } isNewRecord = true } else { // 新增记录 mood = models.Mood{ UserID: userID, Date: req.Date, Emoji: req.Emoji, Content: filteredContent, } if err := h.db.Create(&mood).Error; err != nil { utils.Fail(c, "保存失败") return } isNewRecord = true } } var growthResult any debugMode := os.Getenv("DEBUG_MODE") if isNewRecord { // ==================== 调试模式 ==================== if debugMode == "true" { rand.Seed(time.Now().UnixNano()) levelUp := true // rand.Intn(10) < 2 // 20%概率升级 hasAchievement := true // rand.Intn(10) < 3 // 30%概率获得成就 growthResult = MockGrowthRewards(levelUp, hasAchievement) } else { // 生产模式:真实调用 today := time.Now().Format("2006-01-02") if req.Date == today { ctx := context.Background() key := fmt.Sprintf("mood_count:%d:%s", userID, today) exists, _ := global.Redis.Exists(ctx, key).Result() if exists == 0 { growthHandler := NewGrowthHandler() growthResult = growthHandler.AddExpInternal(userID, "mood", fmt.Sprintf("%d", mood.ID), nil) } } } // ================================================== } // 异步触发AI生成(仅新记录触发,编辑不触发) if isNewRecord { GenerateMoodAIContent(mood.ID, userID, mood.Emoji, mood.Content, mood.Date) } // 查询 AI 生成记录并填充 var aiGen models.MoodAIGeneration h.db.Where("mood_id = ?", mood.ID).First(&aiGen) mood.AIGen = aiGen utils.Ok(c, gin.H{ "success": true, "growth": growthResult, "mood": mood, "debugMode": debugMode, "isDebug": debugMode == "true", "isNewRecord": isNewRecord, }) } // GetMoodList 获取心情列表(包含AI生成内容) func (h *moodHandler) GetMoodList(c *gin.Context) { userID := c.GetUint("userID") var req GetMoodListReq if err := c.ShouldBindJSON(&req); err != nil { utils.Fail(c, "参数解析失败") return } // 构建基础查询 query := h.db.Model(&models.Mood{}). Select(` sm_mood.ID, sm_mood.date, sm_mood.emoji, sm_mood.content, COALESCE(sm_mood_ai_generations.status, 0) as ai_status, COALESCE(sm_mood_ai_generations.ai_text, '') as ai_text, COALESCE(sm_mood_ai_generations.ai_image_url, '') as ai_image_url, COALESCE(sm_mood_ai_generations.generate_count, 0) as ai_generate_count `). Joins("LEFT JOIN sm_mood_ai_generations ON sm_mood.id = sm_mood_ai_generations.mood_id"). Where("sm_mood.user_id = ?", userID) // 日期筛选 if req.StartDate != "" && req.EndDate != "" { query = query.Where("sm_mood.date BETWEEN ? AND ?", req.StartDate, req.EndDate) } else if req.StartDate != "" { query = query.Where("sm_mood.date >= ?", req.StartDate) } else if req.EndDate != "" { query = query.Where("sm_mood.date <= ?", req.EndDate) } var results []MoodCalendarItem if err := query.Order("sm_mood.date DESC").Scan(&results).Error; err != nil { utils.Fail(c, "获取失败") return } utils.Ok(c, results) } // DeleteMood 删除心情 func (h *moodHandler) DeleteMood(c *gin.Context) { userID := c.GetUint("userID") var req DeleteMoodReq if err := c.ShouldBindJSON(&req); err != nil { utils.Fail(c, "参数解析失败") return } // 查询心情信息(用于扣除积分) var mood models.Mood err := h.db.Where("id = ? AND user_id = ?", req.ID, userID).First(&mood).Error if err != nil { utils.Fail(c, "心情记录不存在") return } // 删除心情 err = h.db.Where("id = ? AND user_id = ?", req.ID, userID).Delete(&models.Mood{}).Error if err != nil { utils.Fail(c, "删除失败") return } // 扣除今天添加时获得的积分 growthHandler := NewGrowthHandler() today := time.Now().Format("2006-01-02") deducted := growthHandler.DeductExpInternal(userID, "mood", fmt.Sprintf("%d", req.ID), today) utils.Ok(c, gin.H{ "deducted_exp": deducted, }) } // MoodStat 心情统计 func (h *moodHandler) MoodStat(c *gin.Context) { userID := c.GetUint("userID") var req MoodStatReq if err := c.ShouldBindJSON(&req); err != nil { utils.Fail(c, "参数解析失败") return } // 计算月份范围 startDate := req.Month + "-01" year, month := 0, 0 fmt.Sscanf(req.Month, "%d-%d", &year, &month) _, lastDay := utils.GetMonthLastDay(year, month) endDate := fmt.Sprintf("%s-%02d", req.Month, lastDay) // 查询该月的心情记录 var moods []models.Mood h.db.Where("user_id = ? AND date BETWEEN ? AND ?", userID, startDate, endDate). Order("date ASC"). Find(&moods) // 心情分布 moodDistribution := make(map[string]int) for _, m := range moods { moodDistribution[m.Emoji]++ } // 计算总天数和已记录天数 totalDays := lastDay recordedDays := len(moods) // 周趋势(按周分组) weeklyTrend := []gin.H{} weekMap := make(map[string][]models.Mood) for _, m := range moods { // 解析日期,计算是第几周 t, _ := time.Parse("2006-01-02", m.Date) _, week := t.ISOWeek() weekKey := fmt.Sprintf("W%d", week) weekMap[weekKey] = append(weekMap[weekKey], m) } // 计算每周的平均分数 // 心情emoji映射到分数(简单映射) emojiScore := map[string]float64{ "😊": 5.0, "😄": 5.0, "😎": 4.5, "😌": 4.0, "🙂": 3.5, "😐": 3.0, "😔": 2.5, "😢": 2.0, "😤": 2.0, "😰": 1.5, "😴": 3.0, "🤔": 3.0, } for weekKey, weekMoods := range weekMap { var totalScore float64 for _, m := range weekMoods { if score, ok := emojiScore[m.Emoji]; ok { totalScore += score } else { totalScore += 3.0 // 默认中等分数 } } avgScore := 0.0 if len(weekMoods) > 0 { avgScore = totalScore / float64(len(weekMoods)) } weeklyTrend = append(weeklyTrend, gin.H{ "week": weekKey, "avg_score": avgScore, }) } // 高频词汇(这里简化处理,返回固定示例) topPhrases := []string{"开心", "平静", "工作顺利", "充实", "放松"} utils.Ok(c, gin.H{ "month": req.Month, "total_days": totalDays, "recorded_days": recordedDays, "mood_distribution": moodDistribution, "weekly_trend": weeklyTrend, "top_phrases": topPhrases, }) }