feat(admin): add fa_smoke CRUD APIs with Chinese comments (#45)
This commit is contained in:
@@ -0,0 +1,962 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
adminservice "wx_service/internal/admin/service"
|
||||
"wx_service/internal/model"
|
||||
smokemodel "wx_service/internal/smoke/model"
|
||||
)
|
||||
|
||||
// ===== 戒烟记录(fa_smoke_log) =====
|
||||
|
||||
type smokeLogListQuery struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
UID int `form:"uid"`
|
||||
DateFrom string `form:"date_from"`
|
||||
DateTo string `form:"date_to"`
|
||||
}
|
||||
|
||||
type smokeLogUpsertRequest struct {
|
||||
UID *int `json:"uid"`
|
||||
SmokeTime *string `json:"smoke_time"`
|
||||
SmokeAt *string `json:"smoke_at"`
|
||||
Remark *string `json:"remark"`
|
||||
Level *int64 `json:"level"`
|
||||
Num *int `json:"num"`
|
||||
}
|
||||
|
||||
func (h *Handler) ListSmokeLogs(c *gin.Context) {
|
||||
var query smokeLogListQuery
|
||||
if err := c.ShouldBindQuery(&query); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid query"))
|
||||
return
|
||||
}
|
||||
if query.Page == 0 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize == 0 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
|
||||
dateFrom, err := parseDateOnly(query.DateFrom)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid date_from, expected YYYY-MM-DD"))
|
||||
return
|
||||
}
|
||||
dateTo, err := parseDateOnly(query.DateTo)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid date_to, expected YYYY-MM-DD"))
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.svc.ListSmokeLogs(c.Request.Context(), adminservice.ListSmokeLogsQuery{
|
||||
Page: query.Page,
|
||||
PageSize: query.PageSize,
|
||||
UID: query.UID,
|
||||
DateFrom: dateFrom,
|
||||
DateTo: dateTo,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke logs failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) GetSmokeLog(c *gin.Context) {
|
||||
id, err := strconv.Atoi(strings.TrimSpace(c.Param("id")))
|
||||
if err != nil || id <= 0 {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.svc.GetSmokeLog(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeLogNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke log not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke log failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) CreateSmokeLog(c *gin.Context) {
|
||||
var req smokeLogUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
|
||||
input, err := buildSmokeLogInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.svc.CreateSmokeLog(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrInvalidInput) {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "uid is required"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "create smoke log failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateSmokeLog(c *gin.Context) {
|
||||
id, err := strconv.Atoi(strings.TrimSpace(c.Param("id")))
|
||||
if err != nil || id <= 0 {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
|
||||
var req smokeLogUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input, err := buildSmokeLogInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.svc.UpdateSmokeLog(c.Request.Context(), id, input)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, adminservice.ErrSmokeLogNotFound):
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke log not found"))
|
||||
case errors.Is(err, adminservice.ErrInvalidInput):
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid input"))
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "update smoke log failed"))
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteSmokeLog(c *gin.Context) {
|
||||
id, err := strconv.Atoi(strings.TrimSpace(c.Param("id")))
|
||||
if err != nil || id <= 0 {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteSmokeLog(c.Request.Context(), id); err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeLogNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke log not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "delete smoke log failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(gin.H{"message": "删除成功"}))
|
||||
}
|
||||
|
||||
func buildSmokeLogInput(req smokeLogUpsertRequest) (adminservice.SmokeLogUpsertInput, error) {
|
||||
input := adminservice.SmokeLogUpsertInput{
|
||||
UID: req.UID,
|
||||
Remark: req.Remark,
|
||||
Level: req.Level,
|
||||
Num: req.Num,
|
||||
}
|
||||
if req.SmokeTime != nil {
|
||||
parsed, err := parseDateOnlyRequired(*req.SmokeTime)
|
||||
if err != nil {
|
||||
return input, errors.New("invalid smoke_time, expected YYYY-MM-DD")
|
||||
}
|
||||
value := &parsed
|
||||
input.SmokeTime = &value
|
||||
}
|
||||
if req.SmokeAt != nil {
|
||||
parsed, err := parseDatetimeRequired(*req.SmokeAt)
|
||||
if err != nil {
|
||||
return input, errors.New("invalid smoke_at, expected YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
value := &parsed
|
||||
input.SmokeAt = &value
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// ===== 用户画像(fa_smoke_user_profile) =====
|
||||
|
||||
type smokeProfileListQuery struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
UID int `form:"uid"`
|
||||
}
|
||||
|
||||
type smokeProfileUpsertRequest struct {
|
||||
UID *int `json:"uid"`
|
||||
BaselineCigsPerDay *int `json:"baseline_cigs_per_day"`
|
||||
SmokingYears *float64 `json:"smoking_years"`
|
||||
PackPriceCent *int `json:"pack_price_cent"`
|
||||
SmokeMotivations []string `json:"smoke_motivations"`
|
||||
QuitMotivations []string `json:"quit_motivations"`
|
||||
WakeUpTime *string `json:"wake_up_time"`
|
||||
SleepTime *string `json:"sleep_time"`
|
||||
QuitDate *string `json:"quit_date"`
|
||||
OnboardingCompletedAt *string `json:"onboarding_completed_at"`
|
||||
}
|
||||
|
||||
func (h *Handler) ListSmokeProfiles(c *gin.Context) {
|
||||
var query smokeProfileListQuery
|
||||
if err := c.ShouldBindQuery(&query); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid query"))
|
||||
return
|
||||
}
|
||||
if query.Page == 0 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize == 0 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
|
||||
data, err := h.svc.ListSmokeProfiles(c.Request.Context(), adminservice.ListSmokeProfilesQuery{Page: query.Page, PageSize: query.PageSize, UID: query.UID})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke profiles failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) GetSmokeProfile(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.GetSmokeProfile(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeProfileNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke profile not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke profile failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) CreateSmokeProfile(c *gin.Context) {
|
||||
var req smokeProfileUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input, err := buildSmokeProfileInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.CreateSmokeProfile(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrInvalidInput) {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "uid is required or duplicated"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "create smoke profile failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateSmokeProfile(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
var req smokeProfileUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input, err := buildSmokeProfileInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.UpdateSmokeProfile(c.Request.Context(), id, input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeProfileNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke profile not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "update smoke profile failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteSmokeProfile(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteSmokeProfile(c.Request.Context(), id); err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeProfileNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke profile not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "delete smoke profile failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(gin.H{"message": "删除成功"}))
|
||||
}
|
||||
|
||||
func buildSmokeProfileInput(req smokeProfileUpsertRequest) (adminservice.SmokeProfileUpsertInput, error) {
|
||||
input := adminservice.SmokeProfileUpsertInput{
|
||||
UID: req.UID,
|
||||
BaselineCigsPerDay: req.BaselineCigsPerDay,
|
||||
SmokingYears: req.SmokingYears,
|
||||
PackPriceCent: req.PackPriceCent,
|
||||
WakeUpTime: req.WakeUpTime,
|
||||
SleepTime: req.SleepTime,
|
||||
}
|
||||
|
||||
if req.SmokeMotivations != nil {
|
||||
values := smokemodel.StringSlice(req.SmokeMotivations)
|
||||
input.SmokeMotivations = &values
|
||||
}
|
||||
if req.QuitMotivations != nil {
|
||||
values := smokemodel.StringSlice(req.QuitMotivations)
|
||||
input.QuitMotivations = &values
|
||||
}
|
||||
if req.QuitDate != nil {
|
||||
parsed, err := parseDateOnlyRequired(*req.QuitDate)
|
||||
if err != nil {
|
||||
return input, errors.New("invalid quit_date, expected YYYY-MM-DD")
|
||||
}
|
||||
value := &parsed
|
||||
input.QuitDate = &value
|
||||
}
|
||||
if req.OnboardingCompletedAt != nil {
|
||||
parsed, err := parseDatetimeRequired(*req.OnboardingCompletedAt)
|
||||
if err != nil {
|
||||
return input, errors.New("invalid onboarding_completed_at, expected YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
value := &parsed
|
||||
input.OnboardingCompletedAt = &value
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// ===== AI 建议(fa_smoke_ai_advice) =====
|
||||
|
||||
type smokeAIAdviceListQuery struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
UID int `form:"uid"`
|
||||
Type string `form:"type"`
|
||||
AdviceDate string `form:"advice_date"`
|
||||
}
|
||||
|
||||
type smokeAIAdviceUpsertRequest struct {
|
||||
UID *int `json:"uid"`
|
||||
Type *string `json:"type"`
|
||||
AdviceDate *string `json:"advice_date"`
|
||||
PromptVersion *string `json:"prompt_version"`
|
||||
Provider *string `json:"provider"`
|
||||
Model *string `json:"model"`
|
||||
InputSnapshot *string `json:"input_snapshot"`
|
||||
Advice *string `json:"advice"`
|
||||
TokensIn *int `json:"tokens_in"`
|
||||
TokensOut *int `json:"tokens_out"`
|
||||
CostCent *int `json:"cost_cent"`
|
||||
}
|
||||
|
||||
func (h *Handler) ListSmokeAIAdvices(c *gin.Context) {
|
||||
var query smokeAIAdviceListQuery
|
||||
if err := c.ShouldBindQuery(&query); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid query"))
|
||||
return
|
||||
}
|
||||
if query.Page == 0 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize == 0 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
adviceDate, err := parseDateOnly(query.AdviceDate)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid advice_date, expected YYYY-MM-DD"))
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.svc.ListSmokeAIAdvices(c.Request.Context(), adminservice.ListSmokeAIAdvicesQuery{Page: query.Page, PageSize: query.PageSize, UID: query.UID, Type: query.Type, AdviceDate: adviceDate})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke ai advices failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) GetSmokeAIAdvice(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.GetSmokeAIAdvice(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeAIAdviceNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke ai advice not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke ai advice failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) CreateSmokeAIAdvice(c *gin.Context) {
|
||||
var req smokeAIAdviceUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input, err := buildSmokeAIAdviceInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.CreateSmokeAIAdvice(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrInvalidInput) {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "uid/advice/advice_date are required"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "create smoke ai advice failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateSmokeAIAdvice(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
var req smokeAIAdviceUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input, err := buildSmokeAIAdviceInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.UpdateSmokeAIAdvice(c.Request.Context(), id, input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeAIAdviceNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke ai advice not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "update smoke ai advice failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteSmokeAIAdvice(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteSmokeAIAdvice(c.Request.Context(), id); err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeAIAdviceNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke ai advice not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "delete smoke ai advice failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(gin.H{"message": "删除成功"}))
|
||||
}
|
||||
|
||||
func buildSmokeAIAdviceInput(req smokeAIAdviceUpsertRequest) (adminservice.SmokeAIAdviceUpsertInput, error) {
|
||||
input := adminservice.SmokeAIAdviceUpsertInput{
|
||||
UID: req.UID,
|
||||
Type: req.Type,
|
||||
PromptVersion: req.PromptVersion,
|
||||
Provider: req.Provider,
|
||||
Model: req.Model,
|
||||
InputSnapshot: req.InputSnapshot,
|
||||
Advice: req.Advice,
|
||||
TokensIn: req.TokensIn,
|
||||
TokensOut: req.TokensOut,
|
||||
CostCent: req.CostCent,
|
||||
}
|
||||
if req.AdviceDate != nil {
|
||||
parsed, err := parseDateOnlyRequired(*req.AdviceDate)
|
||||
if err != nil {
|
||||
return input, errors.New("invalid advice_date, expected YYYY-MM-DD")
|
||||
}
|
||||
input.AdviceDate = &parsed
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// ===== AI 解锁(fa_smoke_ai_advice_unlocks) =====
|
||||
|
||||
type smokeAIUnlockListQuery struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
UID int `form:"uid"`
|
||||
UnlockDate string `form:"unlock_date"`
|
||||
}
|
||||
|
||||
type smokeAIUnlockUpsertRequest struct {
|
||||
UID *int `json:"uid"`
|
||||
UnlockDate *string `json:"unlock_date"`
|
||||
AdWatchedAt *string `json:"ad_watched_at"`
|
||||
}
|
||||
|
||||
func (h *Handler) ListSmokeAIUnlocks(c *gin.Context) {
|
||||
var query smokeAIUnlockListQuery
|
||||
if err := c.ShouldBindQuery(&query); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid query"))
|
||||
return
|
||||
}
|
||||
if query.Page == 0 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize == 0 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
unlockDate, err := parseDateOnly(query.UnlockDate)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid unlock_date, expected YYYY-MM-DD"))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.ListSmokeAIUnlocks(c.Request.Context(), adminservice.ListSmokeAIUnlocksQuery{Page: query.Page, PageSize: query.PageSize, UID: query.UID, UnlockDate: unlockDate})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke ai unlocks failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) GetSmokeAIUnlock(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.GetSmokeAIUnlock(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeAIUnlockNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke ai unlock not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke ai unlock failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) CreateSmokeAIUnlock(c *gin.Context) {
|
||||
var req smokeAIUnlockUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input, err := buildSmokeAIUnlockInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.CreateSmokeAIUnlock(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrInvalidInput) {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "uid/unlock_date/ad_watched_at are required"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "create smoke ai unlock failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateSmokeAIUnlock(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
var req smokeAIUnlockUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input, err := buildSmokeAIUnlockInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.UpdateSmokeAIUnlock(c.Request.Context(), id, input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeAIUnlockNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke ai unlock not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "update smoke ai unlock failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteSmokeAIUnlock(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteSmokeAIUnlock(c.Request.Context(), id); err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeAIUnlockNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke ai unlock not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "delete smoke ai unlock failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(gin.H{"message": "删除成功"}))
|
||||
}
|
||||
|
||||
func buildSmokeAIUnlockInput(req smokeAIUnlockUpsertRequest) (adminservice.SmokeAIUnlockUpsertInput, error) {
|
||||
input := adminservice.SmokeAIUnlockUpsertInput{UID: req.UID}
|
||||
if req.UnlockDate != nil {
|
||||
parsed, err := parseDateOnlyRequired(*req.UnlockDate)
|
||||
if err != nil {
|
||||
return input, errors.New("invalid unlock_date, expected YYYY-MM-DD")
|
||||
}
|
||||
input.UnlockDate = &parsed
|
||||
}
|
||||
if req.AdWatchedAt != nil {
|
||||
parsed, err := parseDatetimeRequired(*req.AdWatchedAt)
|
||||
if err != nil {
|
||||
return input, errors.New("invalid ad_watched_at, expected YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
input.AdWatchedAt = &parsed
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// ===== AI 下次抽烟节点(fa_smoke_ai_next_smoke) =====
|
||||
|
||||
type smokeAINextListQuery struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
UID int `form:"uid"`
|
||||
PlanDate string `form:"plan_date"`
|
||||
}
|
||||
|
||||
type smokeAINextUpsertRequest struct {
|
||||
UID *int `json:"uid"`
|
||||
PlanDate *string `json:"plan_date"`
|
||||
AIAdviceID *uint `json:"ai_advice_id"`
|
||||
NodeType *string `json:"node_type"`
|
||||
NodeAt *string `json:"node_at"`
|
||||
}
|
||||
|
||||
func (h *Handler) ListSmokeAINexts(c *gin.Context) {
|
||||
var query smokeAINextListQuery
|
||||
if err := c.ShouldBindQuery(&query); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid query"))
|
||||
return
|
||||
}
|
||||
if query.Page == 0 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize == 0 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
planDate, err := parseDateOnly(query.PlanDate)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid plan_date, expected YYYY-MM-DD"))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.ListSmokeAINexts(c.Request.Context(), adminservice.ListSmokeAINextsQuery{Page: query.Page, PageSize: query.PageSize, UID: query.UID, PlanDate: planDate})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke ai next nodes failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) GetSmokeAINext(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.GetSmokeAINext(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeAINextNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke ai next node not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke ai next node failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) CreateSmokeAINext(c *gin.Context) {
|
||||
var req smokeAINextUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input, err := buildSmokeAINextInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.CreateSmokeAINext(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrInvalidInput) {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "uid/plan_date/ai_advice_id/node_type/node_at are required"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "create smoke ai next node failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateSmokeAINext(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
var req smokeAINextUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input, err := buildSmokeAINextInput(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.UpdateSmokeAINext(c.Request.Context(), id, input)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, adminservice.ErrSmokeAINextNotFound):
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke ai next node not found"))
|
||||
case errors.Is(err, adminservice.ErrInvalidInput):
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid input"))
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "update smoke ai next node failed"))
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteSmokeAINext(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteSmokeAINext(c.Request.Context(), id); err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeAINextNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke ai next node not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "delete smoke ai next node failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(gin.H{"message": "删除成功"}))
|
||||
}
|
||||
|
||||
func buildSmokeAINextInput(req smokeAINextUpsertRequest) (adminservice.SmokeAINextUpsertInput, error) {
|
||||
input := adminservice.SmokeAINextUpsertInput{UID: req.UID, AIAdviceID: req.AIAdviceID, NodeType: req.NodeType}
|
||||
if req.PlanDate != nil {
|
||||
parsed, err := parseDateOnlyRequired(*req.PlanDate)
|
||||
if err != nil {
|
||||
return input, errors.New("invalid plan_date, expected YYYY-MM-DD")
|
||||
}
|
||||
input.PlanDate = &parsed
|
||||
}
|
||||
if req.NodeAt != nil {
|
||||
parsed, err := parseDatetimeRequired(*req.NodeAt)
|
||||
if err != nil {
|
||||
return input, errors.New("invalid node_at, expected YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
input.NodeAt = &parsed
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// ===== 激励语模板(fa_smoke_motivation_quote) =====
|
||||
|
||||
type smokeMotivationListQuery struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
Scene string `form:"scene"`
|
||||
Type string `form:"type"`
|
||||
Enabled string `form:"enabled"`
|
||||
}
|
||||
|
||||
type smokeMotivationUpsertRequest struct {
|
||||
Scene *string `json:"scene"`
|
||||
Type *string `json:"type"`
|
||||
Message *string `json:"message"`
|
||||
AIPrompt *string `json:"ai_prompt"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
Weight *int `json:"weight"`
|
||||
}
|
||||
|
||||
func (h *Handler) ListSmokeMotivations(c *gin.Context) {
|
||||
var query smokeMotivationListQuery
|
||||
if err := c.ShouldBindQuery(&query); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid query"))
|
||||
return
|
||||
}
|
||||
if query.Page == 0 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize == 0 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
var enabled *bool
|
||||
if strings.TrimSpace(query.Enabled) != "" {
|
||||
parsed, err := parseOptionalBool(query.Enabled)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid enabled, expected true/false/1/0"))
|
||||
return
|
||||
}
|
||||
enabled = parsed
|
||||
}
|
||||
data, err := h.svc.ListSmokeMotivations(c.Request.Context(), adminservice.ListSmokeMotivationsQuery{
|
||||
Page: query.Page, PageSize: query.PageSize, Scene: query.Scene, Type: query.Type, Enabled: enabled,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke motivations failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) GetSmokeMotivation(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
data, err := h.svc.GetSmokeMotivation(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeMotivationNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke motivation not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "load smoke motivation failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) CreateSmokeMotivation(c *gin.Context) {
|
||||
var req smokeMotivationUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input := adminservice.SmokeMotivationUpsertInput{Scene: req.Scene, Type: req.Type, Message: req.Message, AIPrompt: req.AIPrompt, Enabled: req.Enabled, Weight: req.Weight}
|
||||
data, err := h.svc.CreateSmokeMotivation(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrInvalidInput) {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "scene/type/message are required"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "create smoke motivation failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateSmokeMotivation(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
var req smokeMotivationUpsertRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid request payload"))
|
||||
return
|
||||
}
|
||||
input := adminservice.SmokeMotivationUpsertInput{Scene: req.Scene, Type: req.Type, Message: req.Message, AIPrompt: req.AIPrompt, Enabled: req.Enabled, Weight: req.Weight}
|
||||
data, err := h.svc.UpdateSmokeMotivation(c.Request.Context(), id, input)
|
||||
if err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeMotivationNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke motivation not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "update smoke motivation failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(data))
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteSmokeMotivation(c *gin.Context) {
|
||||
id, err := parseUintID(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "invalid id"))
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteSmokeMotivation(c.Request.Context(), id); err != nil {
|
||||
if errors.Is(err, adminservice.ErrSmokeMotivationNotFound) {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "smoke motivation not found"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "delete smoke motivation failed"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Success(gin.H{"message": "删除成功"}))
|
||||
}
|
||||
|
||||
// ===== 时间解析辅助函数 =====
|
||||
|
||||
func parseDateOnly(raw string) (*time.Time, error) {
|
||||
text := strings.TrimSpace(raw)
|
||||
if text == "" {
|
||||
return nil, nil
|
||||
}
|
||||
parsed, err := parseDateOnlyRequired(text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &parsed, nil
|
||||
}
|
||||
|
||||
func parseDateOnlyRequired(raw string) (time.Time, error) {
|
||||
return time.ParseInLocation("2006-01-02", strings.TrimSpace(raw), time.Local)
|
||||
}
|
||||
|
||||
func parseDatetimeRequired(raw string) (time.Time, error) {
|
||||
return time.ParseInLocation("2006-01-02 15:04:05", strings.TrimSpace(raw), time.Local)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,12 @@ var (
|
||||
ErrMiniProgramAppIDUsed = errors.New("mini program app_id already exists")
|
||||
ErrAdminUserNotFound = errors.New("admin user not found")
|
||||
ErrMembershipRedeemCodeNotFound = errors.New("membership redeem code not found")
|
||||
ErrSmokeLogNotFound = errors.New("smoke log not found")
|
||||
ErrSmokeProfileNotFound = errors.New("smoke profile not found")
|
||||
ErrSmokeAIAdviceNotFound = errors.New("smoke ai advice not found")
|
||||
ErrSmokeAIUnlockNotFound = errors.New("smoke ai unlock not found")
|
||||
ErrSmokeAINextNotFound = errors.New("smoke ai next smoke not found")
|
||||
ErrSmokeMotivationNotFound = errors.New("smoke motivation quote not found")
|
||||
ErrInvalidInput = errors.New("invalid input")
|
||||
ErrPasswordPolicyViolation = errors.New("password policy violation")
|
||||
)
|
||||
|
||||
@@ -52,6 +52,43 @@ func registerAdminRoutes(
|
||||
protected.GET("/watermark/video-parse-unlocks", handler.ListVideoParseUnlocks)
|
||||
protected.GET("/watermark/video-download-failures", handler.ListVideoDownloadFailures)
|
||||
|
||||
// 戒烟小程序(fa_smoke)后台管理接口。
|
||||
protected.GET("/smoke/logs", handler.ListSmokeLogs)
|
||||
protected.GET("/smoke/logs/:id", handler.GetSmokeLog)
|
||||
protected.POST("/smoke/logs", handler.CreateSmokeLog)
|
||||
protected.PUT("/smoke/logs/:id", handler.UpdateSmokeLog)
|
||||
protected.DELETE("/smoke/logs/:id", handler.DeleteSmokeLog)
|
||||
|
||||
protected.GET("/smoke/profiles", handler.ListSmokeProfiles)
|
||||
protected.GET("/smoke/profiles/:id", handler.GetSmokeProfile)
|
||||
protected.POST("/smoke/profiles", handler.CreateSmokeProfile)
|
||||
protected.PUT("/smoke/profiles/:id", handler.UpdateSmokeProfile)
|
||||
protected.DELETE("/smoke/profiles/:id", handler.DeleteSmokeProfile)
|
||||
|
||||
protected.GET("/smoke/ai-advices", handler.ListSmokeAIAdvices)
|
||||
protected.GET("/smoke/ai-advices/:id", handler.GetSmokeAIAdvice)
|
||||
protected.POST("/smoke/ai-advices", handler.CreateSmokeAIAdvice)
|
||||
protected.PUT("/smoke/ai-advices/:id", handler.UpdateSmokeAIAdvice)
|
||||
protected.DELETE("/smoke/ai-advices/:id", handler.DeleteSmokeAIAdvice)
|
||||
|
||||
protected.GET("/smoke/ai-unlocks", handler.ListSmokeAIUnlocks)
|
||||
protected.GET("/smoke/ai-unlocks/:id", handler.GetSmokeAIUnlock)
|
||||
protected.POST("/smoke/ai-unlocks", handler.CreateSmokeAIUnlock)
|
||||
protected.PUT("/smoke/ai-unlocks/:id", handler.UpdateSmokeAIUnlock)
|
||||
protected.DELETE("/smoke/ai-unlocks/:id", handler.DeleteSmokeAIUnlock)
|
||||
|
||||
protected.GET("/smoke/ai-next-smokes", handler.ListSmokeAINexts)
|
||||
protected.GET("/smoke/ai-next-smokes/:id", handler.GetSmokeAINext)
|
||||
protected.POST("/smoke/ai-next-smokes", handler.CreateSmokeAINext)
|
||||
protected.PUT("/smoke/ai-next-smokes/:id", handler.UpdateSmokeAINext)
|
||||
protected.DELETE("/smoke/ai-next-smokes/:id", handler.DeleteSmokeAINext)
|
||||
|
||||
protected.GET("/smoke/motivation-quotes", handler.ListSmokeMotivations)
|
||||
protected.GET("/smoke/motivation-quotes/:id", handler.GetSmokeMotivation)
|
||||
protected.POST("/smoke/motivation-quotes", handler.CreateSmokeMotivation)
|
||||
protected.PUT("/smoke/motivation-quotes/:id", handler.UpdateSmokeMotivation)
|
||||
protected.DELETE("/smoke/motivation-quotes/:id", handler.DeleteSmokeMotivation)
|
||||
|
||||
protected.GET("/memberships/overview", handler.MembershipOverview)
|
||||
protected.GET("/memberships/redeem-codes", handler.ListMembershipRedeemCodes)
|
||||
protected.POST("/memberships/redeem-codes", handler.CreateMembershipRedeemCodes)
|
||||
|
||||
Reference in New Issue
Block a user