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) }