Enhance smoking tracking API and documentation

- Updated the main.go file to set the local time zone to Asia/Shanghai.
- Changed API endpoints from `PUT` to `POST` for user profile and logs management in multiple documentation files to reflect the correct usage.
- Added new fields in the API response for home summary, including `last_smoke_at`, `today_count`, `resisted_count`, and `reduced_from_yesterday`.
- Enhanced documentation across various files to accurately describe the updated API endpoints and their expected behaviors.
This commit is contained in:
nepiedg
2026-01-25 07:55:32 +00:00
parent be6d579d41
commit b67dc32369
12 changed files with 758 additions and 37 deletions
+92 -12
View File
@@ -14,13 +14,17 @@ import (
)
type nextSmokeTimeUnifiedResponse struct {
Source string `json:"source"`
NotBeforeAt string `json:"not_before_at"`
SuggestedAt string `json:"suggested_at"`
TimeNodes []string `json:"time_nodes,omitempty"`
Advice string `json:"advice,omitempty"`
Default smokeservice.NextSmokeSuggestion `json:"default"`
AI *smokeservice.AINextSmokeSuggestion `json:"ai,omitempty"`
Source string `json:"source"`
NotBeforeAt string `json:"not_before_at"`
SuggestedAt string `json:"suggested_at"`
LastSmokeAt string `json:"last_smoke_at,omitempty"`
TodayCount int `json:"today_count"`
Resisted int `json:"resisted_count"`
Reduced int `json:"reduced_from_yesterday"`
TimeNodes []string `json:"time_nodes,omitempty"`
Advice string `json:"advice,omitempty"`
Default nextSmokeDefaultResponse `json:"default"`
AI *nextSmokeAIResponse `json:"ai,omitempty"`
}
func (h *SmokeHandler) GetNextSmokeTime(c *gin.Context) {
@@ -68,20 +72,72 @@ func (h *SmokeHandler) GetNextSmokeTime(c *gin.Context) {
return
}
homeSummary, err := h.smokeLogService.HomeSummary(c.Request.Context(), int(user.ID), asOf)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "获取首页汇总失败,请稍后重试"))
return
}
mode := strings.ToLower(strings.TrimSpace(c.DefaultQuery("mode", "auto")))
formatPtr := func(t *time.Time) string {
if t == nil {
return ""
}
return t.In(time.Local).Format(time.RFC3339)
return t.In(time.Local).Format(dateTimeLayout)
}
formatTimeString := func(value string) string {
value = strings.TrimSpace(value)
if value == "" {
return ""
}
if t, err := time.Parse(time.RFC3339, value); err == nil {
return t.In(time.Local).Format(dateTimeLayout)
}
if t, err := time.ParseInLocation(dateTimeLayout, value, time.Local); err == nil {
return t.In(time.Local).Format(dateTimeLayout)
}
return value
}
formatDefault := func(s smokeservice.NextSmokeSuggestion) nextSmokeDefaultResponse {
out := nextSmokeDefaultResponse{
LastSmokeAt: formatPtr(s.LastSmokeAt),
NextSmokeAt: formatPtr(s.NextSmokeAt),
BaseIntervalMinutes: s.BaseIntervalMinutes,
IntervalMinutes: s.IntervalMinutes,
Stage: s.Stage,
Resisted7d: s.Resisted7d,
SleepAdjusted: s.SleepAdjusted,
Algorithm: s.Algorithm,
}
out.AsOf = formatTimeString(s.AsOf)
return out
}
formatAI := func(s smokeservice.AINextSmokeSuggestion) nextSmokeAIResponse {
return nextSmokeAIResponse{
PlanDate: s.PlanDate,
NotBeforeAt: formatTimeString(s.NotBeforeAt),
SuggestedAt: formatTimeString(s.SuggestedAt),
TimeNodes: s.TimeNodes,
Advice: s.Advice,
PromptVersion: s.PromptVersion,
Model: s.Model,
Provider: s.Provider,
}
}
resp := nextSmokeTimeUnifiedResponse{
Source: "default",
NotBeforeAt: formatPtr(defaultSuggestion.NextSmokeAt),
SuggestedAt: formatPtr(defaultSuggestion.NextSmokeAt),
Default: defaultSuggestion,
LastSmokeAt: formatPtr(homeSummary.LastSmokeAt),
TodayCount: homeSummary.TodayCount,
Resisted: homeSummary.ResistedCount,
Reduced: homeSummary.ReducedFromYesterday,
Default: formatDefault(defaultSuggestion),
}
// mode=default: 永远返回默认建议
@@ -124,11 +180,12 @@ func (h *SmokeHandler) GetNextSmokeTime(c *gin.Context) {
if hasAI {
resp.Source = "ai"
resp.NotBeforeAt = ai.NotBeforeAt
resp.SuggestedAt = ai.SuggestedAt
resp.NotBeforeAt = formatTimeString(ai.NotBeforeAt)
resp.SuggestedAt = formatTimeString(ai.SuggestedAt)
resp.TimeNodes = ai.TimeNodes
resp.Advice = ai.Advice
resp.AI = &ai
formattedAI := formatAI(ai)
resp.AI = &formattedAI
}
c.JSON(http.StatusOK, model.Success(resp))
@@ -138,3 +195,26 @@ func dateOnlyLocal(t time.Time) time.Time {
local := t.In(time.Local)
return time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, time.Local)
}
type nextSmokeDefaultResponse struct {
LastSmokeAt string `json:"last_smoke_at,omitempty"`
NextSmokeAt string `json:"next_smoke_at,omitempty"`
BaseIntervalMinutes int `json:"base_interval_minutes"`
IntervalMinutes int `json:"interval_minutes"`
Stage int `json:"stage"`
Resisted7d int `json:"resisted_7d"`
SleepAdjusted bool `json:"sleep_adjusted"`
Algorithm string `json:"algorithm"`
AsOf string `json:"as_of"`
}
type nextSmokeAIResponse struct {
PlanDate string `json:"plan_date"`
NotBeforeAt string `json:"not_before_at"`
SuggestedAt string `json:"suggested_at"`
TimeNodes []string `json:"time_nodes"`
Advice string `json:"advice"`
PromptVersion string `json:"prompt_version"`
Model string `json:"model,omitempty"`
Provider string `json:"provider,omitempty"`
}