feat(admin): add quit-checkin admin endpoints and smoke profile fields

Made-with: Cursor
This commit is contained in:
nepiedg
2026-04-07 22:10:31 +08:00
parent fd097729d7
commit a6f0bfd4e8
4 changed files with 278 additions and 0 deletions
@@ -0,0 +1,179 @@
package service
import (
"context"
"strings"
"time"
quitmodel "wx_service/internal/quitcheckin/model"
)
// ListQuitDailyStatusesQuery 戒烟打卡每日状态列表查询。
type ListQuitDailyStatusesQuery struct {
Page int
PageSize int
UID int
DateFrom *time.Time
DateTo *time.Time
}
// QuitDailyStatusItem 管理端展示用(含 uid)。
type QuitDailyStatusItem struct {
ID int `json:"id"`
UID int `json:"uid"`
Date string `json:"date"`
Status string `json:"status"`
CheckInAt *time.Time `json:"check_in_at,omitempty"`
RelapsedAt *time.Time `json:"relapsed_at,omitempty"`
RelapseNum int `json:"relapse_num"`
Reason string `json:"reason,omitempty"`
Note string `json:"note,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// ListQuitDailyStatusesResult 分页结果。
type ListQuitDailyStatusesResult struct {
List []QuitDailyStatusItem `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// ListQuitDailyStatuses 分页查询 fa_quit_checkin_daily_status。
func (s *Service) ListQuitDailyStatuses(ctx context.Context, q ListQuitDailyStatusesQuery) (*ListQuitDailyStatusesResult, error) {
q.Page, q.PageSize = normalizePage(q.Page, q.PageSize)
dbQuery := s.db.WithContext(ctx).Model(&quitmodel.DailyStatus{})
if q.UID > 0 {
dbQuery = dbQuery.Where("uid = ?", q.UID)
}
if q.DateFrom != nil {
dbQuery = dbQuery.Where("date >= ?", q.DateFrom.Format("2006-01-02"))
}
if q.DateTo != nil {
dbQuery = dbQuery.Where("date <= ?", q.DateTo.Format("2006-01-02"))
}
var total int64
if err := dbQuery.Count(&total).Error; err != nil {
return nil, err
}
var rows []quitmodel.DailyStatus
if total > 0 {
if err := dbQuery.Order("date DESC, id DESC").
Limit(q.PageSize).
Offset((q.Page - 1) * q.PageSize).
Find(&rows).Error; err != nil {
return nil, err
}
}
list := make([]QuitDailyStatusItem, 0, len(rows))
for _, r := range rows {
dateStr := ""
if !r.Date.IsZero() {
dateStr = r.Date.Format("2006-01-02")
}
list = append(list, QuitDailyStatusItem{
ID: int(r.ID),
UID: r.UID,
Date: dateStr,
Status: r.Status,
CheckInAt: r.CheckInAt,
RelapsedAt: r.RelapsedAt,
RelapseNum: r.RelapseNum,
Reason: r.Reason,
Note: r.Note,
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
})
}
return &ListQuitDailyStatusesResult{
List: list,
Total: total,
Page: q.Page,
PageSize: q.PageSize,
}, nil
}
// ListQuitRewardGoalsQuery 用户梦想目标列表查询。
type ListQuitRewardGoalsQuery struct {
Page int
PageSize int
UID int
Status string
}
// QuitRewardGoalItem 管理端展示用。
type QuitRewardGoalItem struct {
ID int `json:"id"`
UID int `json:"uid"`
Title string `json:"title"`
TargetAmountCent int `json:"target_amount_cent"`
CoverImage string `json:"cover_image,omitempty"`
Status string `json:"status"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// ListQuitRewardGoalsResult 分页结果。
type ListQuitRewardGoalsResult struct {
List []QuitRewardGoalItem `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// ListQuitRewardGoals 分页查询 fa_quit_checkin_reward_goal。
func (s *Service) ListQuitRewardGoals(ctx context.Context, q ListQuitRewardGoalsQuery) (*ListQuitRewardGoalsResult, error) {
q.Page, q.PageSize = normalizePage(q.Page, q.PageSize)
dbQuery := s.db.WithContext(ctx).Model(&quitmodel.RewardGoal{})
if q.UID > 0 {
dbQuery = dbQuery.Where("uid = ?", q.UID)
}
if st := strings.TrimSpace(q.Status); st != "" && st != "all" {
dbQuery = dbQuery.Where("status = ?", st)
}
var total int64
if err := dbQuery.Count(&total).Error; err != nil {
return nil, err
}
var rows []quitmodel.RewardGoal
if total > 0 {
if err := dbQuery.Order("id DESC").
Limit(q.PageSize).
Offset((q.Page - 1) * q.PageSize).
Find(&rows).Error; err != nil {
return nil, err
}
}
list := make([]QuitRewardGoalItem, 0, len(rows))
for _, r := range rows {
list = append(list, QuitRewardGoalItem{
ID: int(r.ID),
UID: r.UID,
Title: r.Title,
TargetAmountCent: r.TargetAmountCent,
CoverImage: r.CoverImage,
Status: r.Status,
CompletedAt: r.CompletedAt,
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
})
}
return &ListQuitRewardGoalsResult{
List: list,
Total: total,
Page: q.Page,
PageSize: q.PageSize,
}, nil
}
+4
View File
@@ -247,6 +247,7 @@ type ListSmokeProfilesQuery struct {
type SmokeProfileItem struct {
ID uint `json:"id"`
UID int `json:"uid"`
Mode string `json:"mode,omitempty"`
BaselineCigsPerDay int `json:"baseline_cigs_per_day"`
SmokingYears float64 `json:"smoking_years"`
PackPriceCent int `json:"pack_price_cent"`
@@ -255,6 +256,7 @@ type SmokeProfileItem struct {
WakeUpTime string `json:"wake_up_time"`
SleepTime string `json:"sleep_time"`
QuitDate *time.Time `json:"quit_date,omitempty"`
AchievementThemeID *uint `json:"achievement_theme_id,omitempty"`
OnboardingCompletedAt *time.Time `json:"onboarding_completed_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
@@ -368,6 +370,7 @@ func convertSmokeProfile(row smokemodel.SmokeUserProfile) SmokeProfileItem {
return SmokeProfileItem{
ID: row.ID,
UID: row.UID,
Mode: row.Mode,
BaselineCigsPerDay: row.BaselineCigsPerDay,
SmokingYears: row.SmokingYears,
PackPriceCent: row.PackPriceCent,
@@ -376,6 +379,7 @@ func convertSmokeProfile(row smokemodel.SmokeUserProfile) SmokeProfileItem {
WakeUpTime: row.WakeUpTime,
SleepTime: row.SleepTime,
QuitDate: row.QuitDate,
AchievementThemeID: row.AchievementThemeID,
OnboardingCompletedAt: row.OnboardingCompletedAt,
CreatedAt: row.CreatedAt,
UpdatedAt: row.UpdatedAt,