package commentser import ( "fmt" "net/url" "strconv" "strings" "time" "videoweb/app/service/authser" "videoweb/app/service/upstream" "videoweb/pub/common/log" "github.com/gin-gonic/gin" ) const ( listPath = "/api/app/comment/list" defaultCommentSize = 10 ) // ListQuery 评论列表查询参数。 type ListQuery struct { ObjectID int64 ObjectType int PageNum int PageSize int SortOrder int } // Normalize 校正分页与排序默认值。 func (q *ListQuery) Normalize() { if q.PageNum <= 0 { q.PageNum = 1 } if q.PageSize <= 0 { q.PageSize = defaultCommentSize } if q.PageSize > 50 { q.PageSize = 50 } if q.ObjectType != ObjectTypeChapter { q.ObjectType = ObjectTypePlaylet } if q.SortOrder != SortLatest { q.SortOrder = SortHot } } // FetchList 分页查询评论。 func FetchList(c *gin.Context, q ListQuery) (*GetCommentListResp, error) { q.Normalize() if q.ObjectID <= 0 { return nil, fmt.Errorf("comment list invalid objectId") } params := url.Values{} params.Set("objectId", strconv.FormatInt(q.ObjectID, 10)) params.Set("objectType", strconv.Itoa(q.ObjectType)) params.Set("pageNum", strconv.Itoa(q.PageNum)) params.Set("pageSize", strconv.Itoa(q.PageSize)) params.Set("sortOrder", strconv.Itoa(q.SortOrder)) authser.EnsureGuestOnEnter(c) requestPath := listPath + "?" + params.Encode() env, err := fetchListOnce(c, requestPath) if err != nil { return nil, err } if env.Code != 200 && authser.IsSessionExpiredCode(env.Code) && authser.RecoverContentSession(c, env.Code) { env, err = fetchListOnce(c, requestPath) if err != nil { return nil, err } } if env.Code != 200 { return nil, fmt.Errorf("comment list code=%d msg=%s", env.Code, env.Msg) } return &env.Data, nil } func fetchListOnce(c *gin.Context, path string) (*APIEnvelope[GetCommentListResp], error) { var env APIEnvelope[GetCommentListResp] if err := upstream.GetJSON(c, path, &env); err != nil { return nil, err } return &env, nil } // FetchListOrEmpty 失败时返回空列表。 func FetchListOrEmpty(c *gin.Context, q ListQuery) *GetCommentListResp { data, err := FetchList(c, q) if err != nil { log.Error("fetch comment list failed", log.E(err)) return &GetCommentListResp{ ObjectID: q.ObjectID, PageNum: q.PageNum, PageSize: q.PageSize, } } if data == nil { return &GetCommentListResp{ ObjectID: q.ObjectID, PageNum: q.PageNum, PageSize: q.PageSize, } } return data } // ParseSortOrder 解析站点评论排序(1 热门 / 2 最新)。 func ParseSortOrder(s string) int { n, err := strconv.Atoi(strings.TrimSpace(s)) if err != nil || n != SortLatest { return SortHot } return SortLatest } // FormatCreatedAt 展示用时间格式化(非字段映射)。 func FormatCreatedAt(raw string) string { raw = strings.TrimSpace(raw) if raw == "" { return "" } layouts := []string{ time.RFC3339Nano, time.RFC3339, "2006-01-02T15:04:05Z07:00", "2006-01-02 15:04:05", } for _, layout := range layouts { if t, err := time.Parse(layout, raw); err == nil { return t.Local().Format("2006/1/2 15:04") } } return raw } // AvatarOrDefault 头像空时回退默认图。 func AvatarOrDefault(avatar string) string { avatar = strings.TrimSpace(avatar) if avatar == "" { return "/static/images/avatar.png" } return avatar }