feat: update smoke profile area handling

This commit is contained in:
你çšnepiedg
2026-03-19 15:41:26 +08:00
parent 9278260681
commit 515fba0b55
6 changed files with 60 additions and 0 deletions
@@ -18,6 +18,7 @@ type upsertSmokeProfileRequest struct {
BaselineCigsPerDay *int `json:"baseline_cigs_per_day"`
SmokingYears *float64 `json:"smoking_years"`
PackPriceCent *int `json:"pack_price_cent"`
Mode *string `json:"mode"`
SmokeMotivations *[]string `json:"smoke_motivations"`
QuitMotivations *[]string `json:"quit_motivations"`
@@ -71,6 +72,13 @@ func (h *SmokeHandler) UpsertProfile(c *gin.Context) {
return
}
}
if req.Mode != nil {
mode := strings.TrimSpace(*req.Mode)
if mode != "" && mode != smokeservice.SmokeModeQuit && mode != smokeservice.SmokeModeRecord {
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "mode 仅支持 quit 或 record"))
return
}
}
quitDateProvided := false
var quitDate *time.Time
@@ -91,6 +99,7 @@ func (h *SmokeHandler) UpsertProfile(c *gin.Context) {
BaselineCigsPerDay: req.BaselineCigsPerDay,
SmokingYears: req.SmokingYears,
PackPriceCent: req.PackPriceCent,
Mode: req.Mode,
SmokeMotivations: req.SmokeMotivations,
QuitMotivations: req.QuitMotivations,
WakeUpTime: req.WakeUpTime,
+1
View File
@@ -67,6 +67,7 @@ type SmokeUserProfile struct {
SmokeMotivations StringSlice `gorm:"column:smoke_motivations;type:json;comment:抽烟动机(JSON数组)" json:"smoke_motivations"`
QuitMotivations StringSlice `gorm:"column:quit_motivations;type:json;comment:戒烟动力(JSON数组)" json:"quit_motivations"`
Mode string `gorm:"column:mode;size:16;default:record;comment:使用模式(quit=戒烟打卡,record=记录抽烟)" json:"mode,omitempty"`
WakeUpTime string `gorm:"column:wake_up_time;size:5;comment:起床时间(HH:MM)" json:"wake_up_time"`
SleepTime string `gorm:"column:sleep_time;size:5;comment:入睡时间(HH:MM)" json:"sleep_time"`
@@ -16,6 +16,11 @@ var (
ErrSmokeProfileInvalidTime = errors.New("invalid time format, expected HH:MM")
)
const (
SmokeModeQuit = "quit"
SmokeModeRecord = "record"
)
type SmokeProfileService struct {
db *gorm.DB
}
@@ -47,6 +52,7 @@ func (s *SmokeProfileService) GetView(ctx context.Context, uid int) (SmokeProfil
BaselineIntervalMinute: 0,
}, nil
}
profile.Mode = normalizedSmokeMode(profile.Mode)
awakeMinutes, err := awakeMinutesWithFallback(profile.WakeUpTime, profile.SleepTime)
if err != nil {
@@ -83,6 +89,7 @@ type UpsertSmokeProfileRequest struct {
BaselineCigsPerDay *int
SmokingYears *float64
PackPriceCent *int
Mode *string
SmokeMotivations *[]string
QuitMotivations *[]string
@@ -116,6 +123,12 @@ func (s *SmokeProfileService) Upsert(ctx context.Context, uid int, req UpsertSmo
*dst = *v
}
}
applyMode := func(dst *string, v *string) {
if v == nil {
return
}
*dst = normalizedSmokeMode(*v)
}
applyTimeStr := func(dst *string, v *string) error {
if v == nil {
return nil
@@ -135,6 +148,7 @@ func (s *SmokeProfileService) Upsert(ctx context.Context, uid int, req UpsertSmo
applyInt(&profile.BaselineCigsPerDay, req.BaselineCigsPerDay)
applyFloat(&profile.SmokingYears, req.SmokingYears)
applyInt(&profile.PackPriceCent, req.PackPriceCent)
applyMode(&profile.Mode, req.Mode)
if req.SmokeMotivations != nil {
profile.SmokeMotivations = smokemodel.StringSlice(*req.SmokeMotivations)
@@ -154,6 +168,7 @@ func (s *SmokeProfileService) Upsert(ctx context.Context, uid int, req UpsertSmo
}
now := time.Now()
profile.Mode = normalizedSmokeMode(profile.Mode)
if profile.OnboardingCompletedAt == nil && isSmokeProfileCompleted(profile) {
profile.OnboardingCompletedAt = &now
}
@@ -215,6 +230,17 @@ func baselineIntervalMinutes(awakeMinutes int, baselineCigsPerDay int) int {
return interval
}
func normalizedSmokeMode(mode string) string {
switch strings.TrimSpace(mode) {
case SmokeModeQuit:
return SmokeModeQuit
case SmokeModeRecord:
return SmokeModeRecord
default:
return SmokeModeRecord
}
}
func parseHHMMToMinutes(s string) (int, error) {
s = strings.TrimSpace(s)
if len(s) != 5 || s[2] != ':' {
@@ -106,3 +106,23 @@ func TestIsSmokeProfileCompleted(t *testing.T) {
t.Fatalf("isSmokeProfileCompleted: expected false when quit_motivations missing")
}
}
func TestNormalizedSmokeMode(t *testing.T) {
t.Parallel()
cases := []struct {
in string
want string
}{
{in: SmokeModeQuit, want: SmokeModeQuit},
{in: SmokeModeRecord, want: SmokeModeRecord},
{in: "", want: SmokeModeRecord},
{in: "unknown", want: SmokeModeRecord},
}
for _, c := range cases {
if got := normalizedSmokeMode(c.in); got != c.want {
t.Fatalf("normalizedSmokeMode(%q): got %q, want %q", c.in, got, c.want)
}
}
}