Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 131 additions & 3 deletions backend/docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,35 @@
],
"summary": "获取分类统计信息",
"operationId": "category-stat-dashboard",
"parameters": [
{
"maximum": 90,
"minimum": 24,
"type": "integer",
"default": 90,
"description": "持续时间 (小时或天数)`",
"name": "duration",
"in": "query"
},
{
"enum": [
"hour",
"day"
],
"type": "string",
"default": "day",
"description": "精度: \"hour\", \"day\"",
"name": "precision",
"in": "query",
"required": true
},
{
"type": "string",
"description": "用户ID,可jj",
"name": "user_id",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
Expand Down Expand Up @@ -663,6 +692,35 @@
],
"summary": "获取时间统计信息",
"operationId": "time-stat-dashboard",
"parameters": [
{
"maximum": 90,
"minimum": 24,
"type": "integer",
"default": 90,
"description": "持续时间 (小时或天数)`",
"name": "duration",
"in": "query"
},
{
"enum": [
"hour",
"day"
],
"type": "string",
"default": "day",
"description": "精度: \"hour\", \"day\"",
"name": "precision",
"in": "query",
"required": true
},
{
"type": "string",
"description": "用户ID,可jj",
"name": "user_id",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
Expand Down Expand Up @@ -699,6 +757,35 @@
],
"summary": "用户贡献榜",
"operationId": "user-code-rank-dashboard",
"parameters": [
{
"maximum": 90,
"minimum": 24,
"type": "integer",
"default": 90,
"description": "持续时间 (小时或天数)`",
"name": "duration",
"in": "query"
},
{
"enum": [
"hour",
"day"
],
"type": "string",
"default": "day",
"description": "精度: \"hour\", \"day\"",
"name": "precision",
"in": "query",
"required": true
},
{
"type": "string",
"description": "用户ID,可jj",
"name": "user_id",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
Expand Down Expand Up @@ -740,11 +827,31 @@
"operationId": "user-events-dashboard",
"parameters": [
{
"maximum": 90,
"minimum": 24,
"type": "integer",
"default": 90,
"description": "持续时间 (小时或天数)`",
"name": "duration",
"in": "query"
},
{
"enum": [
"hour",
"day"
],
"type": "string",
"description": "用户ID",
"name": "user_id",
"default": "day",
"description": "精度: \"hour\", \"day\"",
"name": "precision",
"in": "query",
"required": true
},
{
"type": "string",
"description": "用户ID,可jj",
"name": "user_id",
"in": "query"
}
],
"responses": {
Expand Down Expand Up @@ -833,8 +940,29 @@
"operationId": "user-stat-dashboard",
"parameters": [
{
"maximum": 90,
"minimum": 24,
"type": "integer",
"default": 90,
"description": "持续时间 (小时或天数)`",
"name": "duration",
"in": "query"
},
{
"enum": [
"hour",
"day"
],
"type": "string",
"description": "用户ID",
"default": "day",
"description": "精度: \"hour\", \"day\"",
"name": "precision",
"in": "query",
"required": true
},
{
"type": "string",
"description": "用户ID,可jj",
"name": "user_id",
"in": "query"
}
Expand Down
38 changes: 28 additions & 10 deletions backend/domain/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,28 @@ package domain

import (
"context"
"time"

"github.com/chaitin/MonkeyCode/backend/db"
)

type DashboardUsecase interface {
Statistics(ctx context.Context) (*Statistics, error)
CategoryStat(ctx context.Context) (*CategoryStat, error)
TimeStat(ctx context.Context) (*TimeStat, error)
UserCodeRank(ctx context.Context) ([]*UserCodeRank, error)
UserStat(ctx context.Context, userID string) (*UserStat, error)
UserEvents(ctx context.Context, userID string) ([]*UserEvent, error)
CategoryStat(ctx context.Context, req StatisticsFilter) (*CategoryStat, error)
TimeStat(ctx context.Context, req StatisticsFilter) (*TimeStat, error)
UserCodeRank(ctx context.Context, req StatisticsFilter) ([]*UserCodeRank, error)
UserStat(ctx context.Context, req StatisticsFilter) (*UserStat, error)
UserEvents(ctx context.Context, req StatisticsFilter) ([]*UserEvent, error)
UserHeatmap(ctx context.Context, userID string) (*UserHeatmapResp, error)
}

type DashboardRepo interface {
Statistics(ctx context.Context) (*Statistics, error)
CategoryStat(ctx context.Context) (*CategoryStat, error)
TimeStat(ctx context.Context) (*TimeStat, error)
UserCodeRank(ctx context.Context) ([]*UserCodeRank, error)
UserStat(ctx context.Context, userID string) (*UserStat, error)
UserEvents(ctx context.Context, userID string) ([]*UserEvent, error)
CategoryStat(ctx context.Context, req StatisticsFilter) (*CategoryStat, error)
TimeStat(ctx context.Context, req StatisticsFilter) (*TimeStat, error)
UserCodeRank(ctx context.Context, req StatisticsFilter) ([]*UserCodeRank, error)
UserStat(ctx context.Context, req StatisticsFilter) (*UserStat, error)
UserEvents(ctx context.Context, req StatisticsFilter) ([]*UserEvent, error)
UserHeatmap(ctx context.Context, userID string) ([]*UserHeatmap, error)
}

Expand All @@ -31,6 +32,23 @@ type Statistics struct {
DisabledUsers int64 `json:"disabled_users"` // 禁用用户数
}

type StatisticsFilter struct {
Precision string `json:"precision" query:"precision" validate:"required,oneof=hour day" default:"day"` // 精度: "hour", "day"
Duration int `json:"duration" query:"duration" validate:"gte=24,lte=90" default:"90"` // 持续时间 (小时或天数)`
UserID string `json:"user_id,omitempty" query:"user_id"` // 用户ID,可jj
}

func (s StatisticsFilter) StartTime() time.Time {
switch s.Precision {
case "hour":
return time.Now().Add(-time.Duration(s.Duration) * time.Hour)
case "day":
return time.Now().AddDate(0, 0, -int(s.Duration))
default:
return time.Now().AddDate(0, 0, -90)
}
}

type UserHeatmapResp struct {
MaxCount int64 `json:"max_count"`
Points []*UserHeatmap `json:"points"`
Expand Down
4 changes: 2 additions & 2 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.23.7

require (
entgo.io/ent v0.14.4
github.com/GoYoko/web v1.0.0
github.com/GoYoko/web v1.1.0
github.com/golang-migrate/migrate/v4 v4.18.3
github.com/google/uuid v1.6.0
github.com/google/wire v0.6.0
Expand All @@ -30,7 +30,7 @@ require (
github.com/go-openapi/inflect v0.21.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
Expand Down
8 changes: 4 additions & 4 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/GoYoko/web v1.0.0 h1:kcNxz8BvpKavE0/iqatOmUeCXVghaoD5xYDiHDulVaE=
github.com/GoYoko/web v1.0.0/go.mod h1:DL9/gvuUG2jcBE1XUIY+9QBrrhdshzPEdxMCzR9jUHo=
github.com/GoYoko/web v1.1.0 h1:nIbtol5z0Y03d0nHsvGjv+W0fgmFRGUL8fzPN3kmrOY=
github.com/GoYoko/web v1.1.0/go.mod h1:DL9/gvuUG2jcBE1XUIY+9QBrrhdshzPEdxMCzR9jUHo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
Expand Down Expand Up @@ -58,8 +58,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
Expand Down
43 changes: 23 additions & 20 deletions backend/internal/dashboard/handler/v1/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ func NewDashboardHandler(
g := w.Group("/api/v1/dashboard")
g.Use(auth.Auth())
g.GET("/statistics", web.BaseHandler(h.Statistics))
g.GET("/category-stat", web.BaseHandler(h.CategoryStat))
g.GET("/time-stat", web.BaseHandler(h.TimeStat))
g.GET("/user-stat", web.BaseHandler(h.UserStat))
g.GET("/user-events", web.BaseHandler(h.UserEvents))
g.GET("/user-code-rank", web.BaseHandler(h.UserCodeRank))
g.GET("/category-stat", web.BindHandler(h.CategoryStat))
g.GET("/time-stat", web.BindHandler(h.TimeStat))
g.GET("/user-stat", web.BindHandler(h.UserStat))
g.GET("/user-events", web.BindHandler(h.UserEvents))
g.GET("/user-code-rank", web.BindHandler(h.UserCodeRank))
g.GET("/user-heatmap", web.BaseHandler(h.UserHeatmap))

return h
Expand Down Expand Up @@ -57,10 +57,11 @@ func (h *DashboardHandler) Statistics(c *web.Context) error {
// @ID category-stat-dashboard
// @Accept json
// @Produce json
// @Success 200 {object} web.Resp{data=domain.CategoryStat}
// @Param filter query domain.StatisticsFilter true "筛选参数"
// @Success 200 {object} web.Resp{data=domain.CategoryStat}
// @Router /api/v1/dashboard/category-stat [get]
func (h *DashboardHandler) CategoryStat(c *web.Context) error {
categoryStat, err := h.usecase.CategoryStat(c.Request().Context())
func (h *DashboardHandler) CategoryStat(c *web.Context, req domain.StatisticsFilter) error {
categoryStat, err := h.usecase.CategoryStat(c.Request().Context(), req)
if err != nil {
return err
}
Expand All @@ -75,10 +76,11 @@ func (h *DashboardHandler) CategoryStat(c *web.Context) error {
// @ID time-stat-dashboard
// @Accept json
// @Produce json
// @Success 200 {object} web.Resp{data=domain.TimeStat}
// @Param filter query domain.StatisticsFilter true "筛选参数"
// @Success 200 {object} web.Resp{data=domain.TimeStat}
// @Router /api/v1/dashboard/time-stat [get]
func (h *DashboardHandler) TimeStat(c *web.Context) error {
timeStat, err := h.usecase.TimeStat(c.Request().Context())
func (h *DashboardHandler) TimeStat(c *web.Context, req domain.StatisticsFilter) error {
timeStat, err := h.usecase.TimeStat(c.Request().Context(), req)
if err != nil {
return err
}
Expand All @@ -93,11 +95,11 @@ func (h *DashboardHandler) TimeStat(c *web.Context) error {
// @ID user-stat-dashboard
// @Accept json
// @Produce json
// @Param user_id query string false "用户ID"
// @Param filter query domain.StatisticsFilter true "筛选参数"
// @Success 200 {object} web.Resp{data=domain.UserStat}
// @Router /api/v1/dashboard/user-stat [get]
func (h *DashboardHandler) UserStat(c *web.Context) error {
userStat, err := h.usecase.UserStat(c.Request().Context(), c.QueryParam("user_id"))
func (h *DashboardHandler) UserStat(c *web.Context, req domain.StatisticsFilter) error {
userStat, err := h.usecase.UserStat(c.Request().Context(), req)
if err != nil {
return err
}
Expand All @@ -112,11 +114,11 @@ func (h *DashboardHandler) UserStat(c *web.Context) error {
// @ID user-events-dashboard
// @Accept json
// @Produce json
// @Param user_id query string true "用户ID"
// @Param filter query domain.StatisticsFilter true "筛选参数"
// @Success 200 {object} web.Resp{data=[]domain.UserEvent}
// @Router /api/v1/dashboard/user-events [get]
func (h *DashboardHandler) UserEvents(c *web.Context) error {
userEvents, err := h.usecase.UserEvents(c.Request().Context(), c.QueryParam("user_id"))
func (h *DashboardHandler) UserEvents(c *web.Context, req domain.StatisticsFilter) error {
userEvents, err := h.usecase.UserEvents(c.Request().Context(), req)
if err != nil {
return err
}
Expand All @@ -131,10 +133,11 @@ func (h *DashboardHandler) UserEvents(c *web.Context) error {
// @ID user-code-rank-dashboard
// @Accept json
// @Produce json
// @Success 200 {object} web.Resp{data=[]domain.UserCodeRank}
// @Param filter query domain.StatisticsFilter true "筛选参数"
// @Success 200 {object} web.Resp{data=[]domain.UserCodeRank}
// @Router /api/v1/dashboard/user-code-rank [get]
func (h *DashboardHandler) UserCodeRank(c *web.Context) error {
userCodeRank, err := h.usecase.UserCodeRank(c.Request().Context())
func (h *DashboardHandler) UserCodeRank(c *web.Context, req domain.StatisticsFilter) error {
userCodeRank, err := h.usecase.UserCodeRank(c.Request().Context(), req)
if err != nil {
return err
}
Expand Down
Loading
Loading