Files
YouduWiki/backend/handler/v1/stat.go
2026-05-21 19:52:45 +08:00

296 lines
9.3 KiB
Go

package v1
import (
"github.com/labstack/echo/v4"
v1 "github.com/chaitin/panda-wiki/api/stat/v1"
"github.com/chaitin/panda-wiki/consts"
"github.com/chaitin/panda-wiki/domain"
"github.com/chaitin/panda-wiki/handler"
"github.com/chaitin/panda-wiki/log"
"github.com/chaitin/panda-wiki/middleware"
"github.com/chaitin/panda-wiki/usecase"
)
type StatHandler struct {
*handler.BaseHandler
usecase *usecase.StatUseCase
auth middleware.AuthMiddleware
logger *log.Logger
}
func NewStatHandler(baseHandler *handler.BaseHandler, echo *echo.Echo, usecase *usecase.StatUseCase, logger *log.Logger, auth middleware.AuthMiddleware) *StatHandler {
h := &StatHandler{
BaseHandler: baseHandler,
usecase: usecase,
auth: auth,
logger: logger.WithModule("handler.v1.stat"),
}
group := echo.Group("/api/v1/stat", h.auth.Authorize, auth.ValidateKBUserPerm(consts.UserKBPermissionDataOperate))
// 实时
group.GET("/instant_count", h.GetInstantCount) // instant count (30min, every 1min)
group.GET("/instant_pages", h.GetInstantPages) // instant pages (latest 10 pages)
// 周期统计
group.GET("/count", h.StatCount)
group.GET("/geo_count", h.StatGeoCountReq) // geo (24h)
group.GET("/conversation_distribution", h.StatConversationDistribution) // conversation (24h)
group.GET("/hot_pages", h.StatHotPages)
group.GET("/referer_hosts", h.StatRefererHosts)
group.GET("/browsers", h.StatBrowsers)
return h
}
// StatCount 全局统计
//
// @Summary 全局统计
// @Description 全局统计
// @Tags stat
// @Accept json
// @Produce json
// @Security bearerAuth
// @Param para query v1.StatCountReq true "para"
// @Success 200 {object} domain.PWResponse{data=v1.StatCountResp}
// @Router /api/v1/stat/count [get]
func (h *StatHandler) StatCount(c echo.Context) error {
var req v1.StatCountReq
if err := c.Bind(&req); err != nil {
return h.NewResponseWithError(c, "invalid request parameters", err)
}
if err := c.Validate(&req); err != nil {
return h.NewResponseWithError(c, "validation failed", err)
}
if err := h.usecase.ValidateStatDay(req.Day, consts.GetLicenseEdition(c)); err != nil {
h.logger.Error("validate stat day failed")
return h.NewResponseWithErrCode(c, domain.ErrCodePermissionDenied)
}
count, err := h.usecase.GetStatCount(c.Request().Context(), req.KbID, req.Day)
if err != nil {
return h.NewResponseWithError(c, "get count failed", err)
}
return h.NewResponseWithData(c, count)
}
// StatGeoCountReq 用户地理分布
//
// @Summary 用户地理分布
// @Description 用户地理分布
// @Tags stat
// @Accept json
// @Produce json
// @Security bearerAuth
// @Param para query v1.StatGeoCountReq true "para"
// @Success 200 {object} domain.Response
// @Router /api/v1/stat/geo_count [get]
func (h *StatHandler) StatGeoCountReq(c echo.Context) error {
var req v1.StatGeoCountReq
if err := c.Bind(&req); err != nil {
return h.NewResponseWithError(c, "invalid request parameters", err)
}
if err := c.Validate(&req); err != nil {
return h.NewResponseWithError(c, "validation failed", err)
}
if err := h.usecase.ValidateStatDay(req.Day, consts.GetLicenseEdition(c)); err != nil {
h.logger.Error("validate stat day failed")
return h.NewResponseWithErrCode(c, domain.ErrCodePermissionDenied)
}
geoCount, err := h.usecase.GetGeoCount(c.Request().Context(), req.KbID, req.Day)
if err != nil {
return h.NewResponseWithError(c, "get geo count failed", err)
}
return h.NewResponseWithData(c, geoCount)
}
// StatConversationDistribution
//
// @Summary 问答来源
// @Description 问答来源
// @Tags stat
// @Accept json
// @Produce json
// @Security bearerAuth
// @Param para query v1.StatConversationDistributionReq true "para"
// @Success 200 {object} domain.Response{data=[]v1.StatConversationDistributionResp}
// @Router /api/v1/stat/conversation_distribution [get]
func (h *StatHandler) StatConversationDistribution(c echo.Context) error {
var req v1.StatConversationDistributionReq
if err := c.Bind(&req); err != nil {
return h.NewResponseWithError(c, "invalid request parameters", err)
}
if err := c.Validate(&req); err != nil {
return h.NewResponseWithError(c, "validation failed", err)
}
if err := h.usecase.ValidateStatDay(req.Day, consts.GetLicenseEdition(c)); err != nil {
h.logger.Error("validate stat day failed")
return h.NewResponseWithErrCode(c, domain.ErrCodePermissionDenied)
}
distribution, err := h.usecase.GetConversationDistribution(c.Request().Context(), req.KbID, req.Day)
if err != nil {
return h.NewResponseWithError(c, "get conversation distribution failed", err)
}
return h.NewResponseWithData(c, distribution)
}
// StatHotPages 热门文档
//
// @Summary 热门文档
// @Description 热门文档
// @Tags stat
// @Accept json
// @Produce json
// @Security bearerAuth
// @Param para query v1.StatHotPagesReq true "para"
// @Success 200 {object} domain.Response{data=[]domain.HotPage}
// @Router /api/v1/stat/hot_pages [get]
func (h *StatHandler) StatHotPages(c echo.Context) error {
var req v1.StatHotPagesReq
if err := c.Bind(&req); err != nil {
return h.NewResponseWithError(c, "invalid request parameters", err)
}
if err := c.Validate(&req); err != nil {
return h.NewResponseWithError(c, "validation failed", err)
}
if err := h.usecase.ValidateStatDay(req.Day, consts.GetLicenseEdition(c)); err != nil {
return h.NewResponseWithError(c, err.Error(), err)
}
hotPages, err := h.usecase.GetHotPages(c.Request().Context(), req.KbID, req.Day)
if err != nil {
return h.NewResponseWithError(c, "get hot pages failed", err)
}
return h.NewResponseWithData(c, hotPages)
}
// StatRefererHosts 来源域名
//
// @Summary 来源域名
// @Description 来源域名
// @Tags stat
// @Accept json
// @Produce json
// @Security bearerAuth
// @Param para query v1.StatRefererHostsReq true "para"
// @Success 200 {object} domain.Response{data=[]domain.HotRefererHost}
// @Router /api/v1/stat/referer_hosts [get]
func (h *StatHandler) StatRefererHosts(c echo.Context) error {
var req v1.StatRefererHostsReq
if err := c.Bind(&req); err != nil {
return h.NewResponseWithError(c, "invalid request parameters", err)
}
if err := c.Validate(&req); err != nil {
return h.NewResponseWithError(c, "validation failed", err)
}
if err := h.usecase.ValidateStatDay(req.Day, consts.GetLicenseEdition(c)); err != nil {
return h.NewResponseWithError(c, err.Error(), err)
}
refererHosts, err := h.usecase.GetHotRefererHosts(c.Request().Context(), req.KbID, req.Day)
if err != nil {
return h.NewResponseWithError(c, "get hot referer hosts failed", err)
}
return h.NewResponseWithData(c, refererHosts)
}
// StatBrowsers 客户端统计
//
// @Summary 客户端统计
// @Description 客户端统计
// @Tags stat
// @Accept json
// @Produce json
// @Security bearerAuth
// @Param para query v1.StatBrowsersReq true "para"
// @Success 200 {object} domain.Response{data=domain.HotBrowser}
// @Router /api/v1/stat/browsers [get]
func (h *StatHandler) StatBrowsers(c echo.Context) error {
var req v1.StatBrowsersReq
if err := c.Bind(&req); err != nil {
return h.NewResponseWithError(c, "invalid request parameters", err)
}
if err := c.Validate(&req); err != nil {
return h.NewResponseWithError(c, "validation failed", err)
}
if err := h.usecase.ValidateStatDay(req.Day, consts.GetLicenseEdition(c)); err != nil {
return h.NewResponseWithError(c, err.Error(), err)
}
hotBrowsers, err := h.usecase.GetHotBrowsers(c.Request().Context(), req.KbID, req.Day)
if err != nil {
return h.NewResponseWithError(c, "get hot browsers failed", err)
}
return h.NewResponseWithData(c, hotBrowsers)
}
// GetInstantCount get instant count
//
// @Summary GetInstantCount
// @Description GetInstantCount
// @Tags stat
// @Accept json
// @Produce json
// @Security bearerAuth
// @Param para query v1.StatInstantCountReq true "para"
// @Success 200 {object} domain.Response{data=[]domain.InstantCountResp}
// @Router /api/v1/stat/instant_count [get]
func (h *StatHandler) GetInstantCount(c echo.Context) error {
var req v1.StatInstantCountReq
if err := c.Bind(&req); err != nil {
return h.NewResponseWithError(c, "invalid request parameters", err)
}
if err := c.Validate(&req); err != nil {
return h.NewResponseWithError(c, "validation failed", err)
}
count, err := h.usecase.GetInstantCount(c.Request().Context(), req.KbID)
if err != nil {
return h.NewResponseWithError(c, "get instant count failed", err)
}
return h.NewResponseWithData(c, count)
}
// GetInstantPages get instant pages
//
// @Summary GetInstantPages
// @Description GetInstantPages
// @Tags stat
// @Accept json
// @Produce json
// @Security bearerAuth
// @Param para query v1.StatInstantPagesReq true "para"
// @Success 200 {object} domain.Response{data=[]domain.InstantPageResp}
// @Router /api/v1/stat/instant_pages [get]
func (h *StatHandler) GetInstantPages(c echo.Context) error {
var req v1.StatInstantPagesReq
if err := c.Bind(&req); err != nil {
return h.NewResponseWithError(c, "invalid request parameters", err)
}
if err := c.Validate(&req); err != nil {
return h.NewResponseWithError(c, "validation failed", err)
}
pages, err := h.usecase.GetInstantPages(c.Request().Context(), req.KbID)
if err != nil {
return h.NewResponseWithError(c, "get instant pages failed", err)
}
return h.NewResponseWithData(c, pages)
}