197 lines
6.4 KiB
Go
197 lines
6.4 KiB
Go
package handler
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"wx_service/internal/middleware"
|
|
"wx_service/internal/model"
|
|
smokeservice "wx_service/internal/smoke/service"
|
|
)
|
|
|
|
type createSmokeShareRequest struct {
|
|
Days *int `json:"days"`
|
|
}
|
|
|
|
func (h *SmokeHandler) CreateShare(c *gin.Context) {
|
|
user := middleware.MustCurrentUser(c)
|
|
|
|
var req createSmokeShareRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil && !errors.Is(err, io.EOF) {
|
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "请求参数错误"))
|
|
return
|
|
}
|
|
|
|
days := 0
|
|
if req.Days != nil {
|
|
days = *req.Days
|
|
}
|
|
|
|
share, err := h.smokeShareService.Create(c.Request.Context(), int(user.ID), smokeservice.CreateSmokeShareRequest{Days: days})
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "创建分享失败,请稍后重试"))
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.Success(gin.H{
|
|
"share_token": share.ShareToken,
|
|
"expire_at": share.ExpireAt.Format(time.RFC3339),
|
|
"share_path": fmt.Sprintf("/pages/share/index?share_token=%s", share.ShareToken),
|
|
}))
|
|
}
|
|
|
|
func (h *SmokeHandler) GetShareView(c *gin.Context) {
|
|
token := strings.TrimSpace(c.Param("token"))
|
|
if token == "" {
|
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "share_token 不能为空"))
|
|
return
|
|
}
|
|
|
|
share, err := h.smokeShareService.GetByToken(c.Request.Context(), token)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, smokeservice.ErrSmokeShareNotFound):
|
|
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "分享不存在"))
|
|
case errors.Is(err, smokeservice.ErrSmokeShareExpired):
|
|
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "分享已过期"))
|
|
case errors.Is(err, smokeservice.ErrSmokeShareRevoked):
|
|
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "分享已失效"))
|
|
default:
|
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "加载分享失败,请稍后重试"))
|
|
}
|
|
return
|
|
}
|
|
|
|
_ = h.smokeShareService.TouchViewed(c.Request.Context(), share.ID)
|
|
|
|
rangeType := strings.ToLower(strings.TrimSpace(c.DefaultQuery("range", "week")))
|
|
asOf := time.Now().In(time.Local)
|
|
if v := strings.TrimSpace(c.Query("date")); v != "" {
|
|
parsed, parseErr := time.ParseInLocation(dateLayout, v, time.Local)
|
|
if parseErr != nil {
|
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "date 格式错误,应为 YYYY-MM-DD"))
|
|
return
|
|
}
|
|
asOf = time.Date(parsed.Year(), parsed.Month(), parsed.Day(), 23, 59, 59, 0, time.Local)
|
|
}
|
|
|
|
statsReq, err := buildStatsRequest(rangeType, asOf)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
|
return
|
|
}
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
|
listType := strings.ToLower(strings.TrimSpace(c.DefaultQuery("type", "all")))
|
|
if listType != "all" && listType != "smoke" && listType != "resisted" {
|
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "type 应为 all|smoke|resisted"))
|
|
return
|
|
}
|
|
|
|
owner, err := h.smokeShareService.GetOwnerPublic(c.Request.Context(), share.UID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "加载分享用户信息失败"))
|
|
return
|
|
}
|
|
|
|
profile, err := h.smokeProfileService.Get(c.Request.Context(), share.UID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "获取基础信息失败,请稍后重试"))
|
|
return
|
|
}
|
|
|
|
stats, err := h.smokeLogService.Stats(c.Request.Context(), share.UID, statsReq, profile)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "获取统计数据失败,请稍后重试"))
|
|
return
|
|
}
|
|
|
|
homeSummary, err := h.smokeLogService.HomeSummary(c.Request.Context(), share.UID, asOf)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "获取首页汇总失败,请稍后重试"))
|
|
return
|
|
}
|
|
|
|
logs, err := h.smokeLogService.List(c.Request.Context(), share.UID, smokeservice.ListSmokeLogsRequest{
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
Type: listType,
|
|
})
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "查询记录失败,请稍后重试"))
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.Success(gin.H{
|
|
"owner": gin.H{
|
|
"nickname": maskShareNickname(owner.NickName),
|
|
"avatar_url": owner.AvatarURL,
|
|
},
|
|
"share": gin.H{
|
|
"share_token": share.ShareToken,
|
|
"expire_at": share.ExpireAt,
|
|
"last_viewed_at": share.LastViewedAt,
|
|
"view_count": share.ViewCount + 1,
|
|
},
|
|
"overview": gin.H{
|
|
"today_count": homeSummary.TodayCount,
|
|
"resisted_count": homeSummary.ResistedCount,
|
|
"reduced_from_yesterday": homeSummary.ReducedFromYesterday,
|
|
"exceeded_yesterday": homeSummary.ExceededYesterday,
|
|
"last_smoke_at": homeSummary.LastSmokeAt,
|
|
"seconds_since_last": homeSummary.SecondsSinceLast,
|
|
"streak_days": stats.StreakDays,
|
|
},
|
|
"stats": stats,
|
|
"logs": gin.H{
|
|
"items": logs.Items,
|
|
"total": logs.Total,
|
|
"page": logs.Page,
|
|
"page_size": logs.PageSize,
|
|
},
|
|
}))
|
|
}
|
|
|
|
func (h *SmokeHandler) RevokeShare(c *gin.Context) {
|
|
user := middleware.MustCurrentUser(c)
|
|
token := strings.TrimSpace(c.Param("token"))
|
|
if token == "" {
|
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "share_token 不能为空"))
|
|
return
|
|
}
|
|
|
|
err := h.smokeShareService.Revoke(c.Request.Context(), int(user.ID), token)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, smokeservice.ErrSmokeShareNotFound):
|
|
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "分享不存在"))
|
|
case errors.Is(err, smokeservice.ErrSmokeShareForbidden):
|
|
c.JSON(http.StatusForbidden, model.Error(http.StatusForbidden, "无权限操作该分享"))
|
|
default:
|
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "撤销分享失败,请稍后重试"))
|
|
}
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.Success(gin.H{"revoked": true}))
|
|
}
|
|
|
|
func maskShareNickname(name string) string {
|
|
runes := []rune(strings.TrimSpace(name))
|
|
if len(runes) <= 1 {
|
|
return "戒烟用户"
|
|
}
|
|
if len(runes) == 2 {
|
|
return string(runes[0]) + "*"
|
|
}
|
|
return string(runes[0]) + strings.Repeat("*", len(runes)-2) + string(runes[len(runes)-1])
|
|
}
|