package service import ( "context" "errors" "time" "gorm.io/gorm" smokemodel "wx_service/internal/smoke/model" ) type SmokeMotivation struct { Message string `json:"message"` Type string `json:"type"` } func (s *SmokeLogService) Motivation(ctx context.Context, uid int, asOf time.Time, profile *smokemodel.SmokeUserProfile) (SmokeMotivation, error) { home, err := s.HomeSummary(ctx, uid, asOf) if err != nil { return SmokeMotivation{}, err } minutesSinceLast := -1 if home.LastSmokeAt != nil { diff := int(asOf.Sub(*home.LastSmokeAt).Minutes()) if diff < 0 { diff = 0 } minutesSinceLast = diff } dailyTarget := 0 if profile != nil && profile.BaselineCigsPerDay > 0 { dailyTarget = profile.BaselineCigsPerDay } quitMotivation := "" if profile != nil && len(profile.QuitMotivations) > 0 { quitMotivation = profile.QuitMotivations[0] } scene, fallback := buildMotivationScene(home, minutesSinceLast, dailyTarget, quitMotivation) if quote, ok, err := s.loadMotivationQuote(ctx, scene); err != nil { return fallback, err } else if ok { return SmokeMotivation{Message: quote.Message, Type: quote.Type}, nil } if scene != "default" { if quote, ok, err := s.loadMotivationQuote(ctx, "default"); err != nil { return fallback, err } else if ok { return SmokeMotivation{Message: quote.Message, Type: quote.Type}, nil } } return fallback, nil } func buildMotivationScene(home SmokeHomeSummary, minutesSinceLast int, dailyTarget int, quitMotivation string) (string, SmokeMotivation) { if home.ResistedCount > 0 && minutesSinceLast >= 0 && minutesSinceLast < 30 { return "recent_resist", SmokeMotivation{ Message: "太棒了!你刚刚成功抵抗了一次烟瘾", Type: "praise", } } if dailyTarget > 0 && home.TodayCount < int(float64(dailyTarget)*0.5) { return "below_half_target", SmokeMotivation{ Message: "今天的表现非常出色,继续保持!", Type: "encourage", } } if dailyTarget > 0 && home.TodayCount == dailyTarget-1 { return "near_limit", SmokeMotivation{ Message: "还剩最后一支配额,考虑把它留到睡前?", Type: "hint", } } if dailyTarget > 0 && home.TodayCount > dailyTarget { msg := "没关系,明天是新的一天。" if quitMotivation != "" { msg = msg + "记住你为什么要戒烟:" + quitMotivation } return "over_target", SmokeMotivation{ Message: msg, Type: "comfort", } } return "default", SmokeMotivation{ Message: "保持连胜纪录!", Type: "encourage", } } func (s *SmokeLogService) loadMotivationQuote(ctx context.Context, scene string) (smokemodel.SmokeMotivationQuote, bool, error) { var quote smokemodel.SmokeMotivationQuote if err := s.db.WithContext(ctx). Where("scene = ? AND enabled = 1", scene). Order("weight DESC, id ASC"). First("e).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return smokemodel.SmokeMotivationQuote{}, false, nil } return smokemodel.SmokeMotivationQuote{}, false, err } if quote.Message == "" || quote.Type == "" { return smokemodel.SmokeMotivationQuote{}, false, nil } return quote, true, nil }