3154365ab2
- Introduced a new database model `SmokeMotivationQuote` for storing motivational quotes. - Added a new API endpoint `GET /api/v1/smoke/motivation` to retrieve motivation quotes for users. - Updated the main.go file to include the new model in the auto-migration process. - Enhanced smoke_routes.go to register the new motivation route with the smoke handler.
116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
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
|
|
}
|