From e4c8deaacfa5e0fd0277f584d0c639f066c342bb Mon Sep 17 00:00:00 2001 From: Rogee Date: Wed, 17 Dec 2025 14:57:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85=E6=8B=AC?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E6=8E=A5=E5=8F=A3=E5=92=8C=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/http/super/dto/tenant.go | 3 +- backend/app/http/super/dto/user.go | 7 ++++ backend/app/http/super/routes.gen.go | 4 +++ backend/app/http/super/user.go | 15 +++++++++ backend/app/services/user.go | 16 +++++++++ backend/app/services/user_test.go | 12 +++++++ backend/docs/docs.go | 49 ++++++++++++++++++++++++++++ backend/docs/swagger.json | 49 ++++++++++++++++++++++++++++ backend/docs/swagger.yaml | 31 ++++++++++++++++++ 9 files changed, 185 insertions(+), 1 deletion(-) diff --git a/backend/app/http/super/dto/tenant.go b/backend/app/http/super/dto/tenant.go index 2562ae3..ff03d1b 100644 --- a/backend/app/http/super/dto/tenant.go +++ b/backend/app/http/super/dto/tenant.go @@ -13,7 +13,8 @@ type TenantFilter struct { requests.Pagination requests.SortQueryFilter - Name *string `json:"name,omitempty" query:"name"` + Name *string `json:"name,omitempty" query:"name"` + Status *string `json:"status,omitempty" query:"status"` } type TenantItem struct { diff --git a/backend/app/http/super/dto/user.go b/backend/app/http/super/dto/user.go index 71fed30..1e92929 100644 --- a/backend/app/http/super/dto/user.go +++ b/backend/app/http/super/dto/user.go @@ -11,6 +11,7 @@ type UserPageFilter struct { requests.SortQueryFilter Username *string `query:"username"` + Status *string `query:"status"` TenantID *int64 `query:"tenant_id"` } @@ -23,3 +24,9 @@ type UserItem struct { type UserStatusUpdateForm struct { Status consts.UserStatus `json:"status" validate:"required,oneof=normal disabled"` } + +type UserStatistics struct { + Status consts.UserStatus `json:"status"` + StatusDescription string `json:"status_description"` + Count int64 `json:"count"` +} diff --git a/backend/app/http/super/routes.gen.go b/backend/app/http/super/routes.gen.go index 3325792..2e6c1aa 100644 --- a/backend/app/http/super/routes.gen.go +++ b/backend/app/http/super/routes.gen.go @@ -74,6 +74,10 @@ func (r *Routes) Register(router fiber.Router) { r.user.list, Query[dto.UserPageFilter]("filter"), )) + r.log.Debugf("Registering route: Get /super/v1/users/statistics -> user.statistics") + router.Get("/super/v1/users/statistics", DataFunc0( + r.user.statistics, + )) r.log.Debugf("Registering route: Get /super/v1/users/statuses -> user.statusList") router.Get("/super/v1/users/statuses", DataFunc0( r.user.statusList, diff --git a/backend/app/http/super/user.go b/backend/app/http/super/user.go index 004d6eb..c25ce7d 100644 --- a/backend/app/http/super/user.go +++ b/backend/app/http/super/user.go @@ -61,3 +61,18 @@ func (*user) statusList(ctx fiber.Ctx) ([]requests.KV, error) { return requests.NewKV(item.String(), item.Description()) }), nil } + +// statistics +// +// @Summary 用户统计信息 +// @Tags Super +// @Accept json +// @Produce json +// @Success 200 {array} dto.UserStatistics +// +// @Router /super/v1/users/statistics [get] +// @Bind userID path +// @Bind form body +func (*user) statistics(ctx fiber.Ctx) ([]*dto.UserStatistics, error) { + return services.User.Statistics(ctx) +} diff --git a/backend/app/services/user.go b/backend/app/services/user.go index e436005..9145a8d 100644 --- a/backend/app/services/user.go +++ b/backend/app/services/user.go @@ -107,3 +107,19 @@ func (t *user) UpdateStatus(ctx context.Context, userID int64, status consts.Use return nil } + +// Statistics +func (t *user) Statistics(ctx context.Context) ([]*dto.UserStatistics, error) { + tbl, query := models.UserQuery.QueryContext(ctx) + + var statistics []*dto.UserStatistics + err := query.Select(tbl.Status, tbl.ID.Count().As("count")).Group(tbl.Status).Scan(&statistics) + if err != nil { + return nil, err + } + + return lo.Map(statistics, func(item *dto.UserStatistics, _ int) *dto.UserStatistics { + item.StatusDescription = item.Status.Description() + return item + }), nil +} diff --git a/backend/app/services/user_test.go b/backend/app/services/user_test.go index 55801a2..2d2ebef 100644 --- a/backend/app/services/user_test.go +++ b/backend/app/services/user_test.go @@ -261,3 +261,15 @@ func (t *UserTestSuite) Test_Relations() { }) }) } + +func (t *UserTestSuite) Test_Statistics() { + Convey("test page", t.T(), func() { + Convey("filter tenant users", func() { + m, err := User.Statistics(t.T().Context()) + So(err, ShouldBeNil) + // So(m.OwnedTenant, ShouldNotBeNil) + // So(m.Tenants, ShouldHaveLength, 10) + t.T().Logf("%s", utils.MustJsonString(m)) + }) + }) +} diff --git a/backend/docs/docs.go b/backend/docs/docs.go index d2b47ff..149544b 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -93,6 +93,11 @@ const docTemplate = `{ "type": "integer", "name": "page", "in": "query" + }, + { + "type": "string", + "name": "status", + "in": "query" } ], "responses": { @@ -243,6 +248,11 @@ const docTemplate = `{ "name": "page", "in": "query" }, + { + "type": "string", + "name": "status", + "in": "query" + }, { "type": "integer", "name": "tenantID", @@ -276,6 +286,31 @@ const docTemplate = `{ } } }, + "/super/v1/users/statistics": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Super" + ], + "summary": "用户统计信息", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.UserStatistics" + } + } + } + } + } + }, "/super/v1/users/statuses": { "get": { "consumes": [ @@ -536,6 +571,20 @@ const docTemplate = `{ } } }, + "dto.UserStatistics": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "status": { + "$ref": "#/definitions/consts.UserStatus" + }, + "status_description": { + "type": "string" + } + } + }, "dto.UserStatusUpdateForm": { "type": "object", "required": [ diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 01f4ab9..b029048 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -87,6 +87,11 @@ "type": "integer", "name": "page", "in": "query" + }, + { + "type": "string", + "name": "status", + "in": "query" } ], "responses": { @@ -237,6 +242,11 @@ "name": "page", "in": "query" }, + { + "type": "string", + "name": "status", + "in": "query" + }, { "type": "integer", "name": "tenantID", @@ -270,6 +280,31 @@ } } }, + "/super/v1/users/statistics": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Super" + ], + "summary": "用户统计信息", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.UserStatistics" + } + } + } + } + } + }, "/super/v1/users/statuses": { "get": { "consumes": [ @@ -530,6 +565,20 @@ } } }, + "dto.UserStatistics": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "status": { + "$ref": "#/definitions/consts.UserStatus" + }, + "status_description": { + "type": "string" + } + } + }, "dto.UserStatusUpdateForm": { "type": "object", "required": [ diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index d0995be..739ff03 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -134,6 +134,15 @@ definitions: verified_at: type: string type: object + dto.UserStatistics: + properties: + count: + type: integer + status: + $ref: '#/definitions/consts.UserStatus' + status_description: + type: string + type: object dto.UserStatusUpdateForm: properties: status: @@ -288,6 +297,9 @@ paths: - in: query name: page type: integer + - in: query + name: status + type: string produces: - application/json responses: @@ -382,6 +394,9 @@ paths: - in: query name: page type: integer + - in: query + name: status + type: string - in: query name: tenantID type: integer @@ -426,6 +441,22 @@ paths: summary: 更新用户状态 tags: - Super + /super/v1/users/statistics: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/dto.UserStatistics' + type: array + summary: 用户统计信息 + tags: + - Super /super/v1/users/statuses: get: consumes: