diff --git a/api/handler/admin/dashboard_handler.go b/api/handler/admin/dashboard_handler.go
new file mode 100644
index 00000000..b219ca39
--- /dev/null
+++ b/api/handler/admin/dashboard_handler.go
@@ -0,0 +1,55 @@
+package admin
+
+import (
+ "chatplus/core"
+ "chatplus/handler"
+ "chatplus/store/model"
+ "chatplus/utils/resp"
+ "github.com/gin-gonic/gin"
+ "gorm.io/gorm"
+ "time"
+)
+
+type DashboardHandler struct {
+ handler.BaseHandler
+ db *gorm.DB
+}
+
+func NewDashboardHandler(app *core.AppServer, db *gorm.DB) *DashboardHandler {
+ h := DashboardHandler{db: db}
+ h.App = app
+ return &h
+}
+
+type statsVo struct {
+ Users int64 `json:"users"`
+ Chats int64 `json:"chats"`
+ Tokens int64 `json:"tokens"`
+}
+
+func (h *DashboardHandler) Stats(c *gin.Context) {
+ stats := statsVo{}
+ // new users statistic
+ var userCount int64
+ now := time.Now()
+ zeroTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
+ res := h.db.Model(&model.User{}).Where("created_at > ?", zeroTime).Count(&userCount)
+ if res.Error == nil {
+ stats.Users = userCount
+ }
+
+ // new chats statistic
+ var chatCount int64
+ res = h.db.Model(&model.ChatItem{}).Where("created_at > ?", zeroTime).Count(&chatCount)
+ if res.Error == nil {
+ stats.Chats = chatCount
+ }
+
+ // tokens took stats
+ var tokenCount int64
+ res = h.db.Model(&model.HistoryMessage{}).Select("sum(tokens) as tokens_total").Where("created_at > ?", zeroTime).Scan(&tokenCount)
+ if res.Error == nil {
+ stats.Tokens = tokenCount
+ }
+ resp.SUCCESS(c, stats)
+}
diff --git a/api/main.go b/api/main.go
index 77f26705..cc77da2e 100644
--- a/api/main.go
+++ b/api/main.go
@@ -141,6 +141,7 @@ func main() {
fx.Provide(admin.NewUserHandler),
fx.Provide(admin.NewChatRoleHandler),
fx.Provide(admin.NewRewardHandler),
+ fx.Provide(admin.NewDashboardHandler),
// 创建服务
fx.Provide(service.NewAliYunSmsService),
@@ -231,6 +232,10 @@ func main() {
group := s.Engine.Group("/api/admin/reward/")
group.GET("list", h.List)
}),
+ fx.Invoke(func(s *core.AppServer, h *admin.DashboardHandler) {
+ group := s.Engine.Group("/api/admin/dashboard/")
+ group.GET("stats", h.Stats)
+ }),
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
err := s.Run(db)
diff --git a/web/package-lock.json b/web/package-lock.json
index e03388d5..a2ab92c4 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -25,7 +25,8 @@
"sortablejs": "^1.15.0",
"vant": "^4.5.0",
"vue": "^3.2.13",
- "vue-router": "^4.0.15"
+ "vue-router": "^4.0.15",
+ "vue-schart": "^2.0.0"
},
"devDependencies": {
"@babel/core": "7.18.6",
@@ -9366,6 +9367,11 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true
},
+ "node_modules/schart.js": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/schart.js/-/schart.js-3.0.4.tgz",
+ "integrity": "sha512-uylb2u9rrHX1jyAuSAJUQON8XTfyDKI9kWj1J3fUlCQCkLVZ4HG4+IiV8qm//Z71dqvLI78QZ/fCBw0reB22Zw=="
+ },
"node_modules/schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
@@ -10694,6 +10700,14 @@
"vue": "^3.2.0"
}
},
+ "node_modules/vue-schart": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/vue-schart/-/vue-schart-2.0.0.tgz",
+ "integrity": "sha512-qAu3e5wfMcq26wK1xeHExEWfGpnjfoN1R/9QXblNi+AsU/p52X7tTwhi+Fw7H/otfEufhEY2X7z7emaoF4QO+g==",
+ "dependencies": {
+ "schart.js": "^3.0.0"
+ }
+ },
"node_modules/vue-style-loader": {
"version": "4.1.3",
"resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
@@ -18621,6 +18635,11 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true
},
+ "schart.js": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/schart.js/-/schart.js-3.0.4.tgz",
+ "integrity": "sha512-uylb2u9rrHX1jyAuSAJUQON8XTfyDKI9kWj1J3fUlCQCkLVZ4HG4+IiV8qm//Z71dqvLI78QZ/fCBw0reB22Zw=="
+ },
"schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
@@ -19686,6 +19705,14 @@
"@vue/devtools-api": "^6.0.0"
}
},
+ "vue-schart": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/vue-schart/-/vue-schart-2.0.0.tgz",
+ "integrity": "sha512-qAu3e5wfMcq26wK1xeHExEWfGpnjfoN1R/9QXblNi+AsU/p52X7tTwhi+Fw7H/otfEufhEY2X7z7emaoF4QO+g==",
+ "requires": {
+ "schart.js": "^3.0.0"
+ }
+ },
"vue-style-loader": {
"version": "4.1.3",
"resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
diff --git a/web/src/components/admin/AdminSidebar.vue b/web/src/components/admin/AdminSidebar.vue
index 70269953..2b14e6df 100644
--- a/web/src/components/admin/AdminSidebar.vue
+++ b/web/src/components/admin/AdminSidebar.vue
@@ -71,7 +71,7 @@ httpGet('/api/admin/config/get?key=system').then(res => {
const items = [
{
icon: 'home',
- index: '/admin/welcome',
+ index: '/admin/dashboard',
title: '仪表盘',
},
{
diff --git a/web/src/router.js b/web/src/router.js
index 8d8b6ed0..67d150b6 100644
--- a/web/src/router.js
+++ b/web/src/router.js
@@ -35,15 +35,15 @@ const routes = [
{
name: 'admin',
path: '/admin',
- redirect: '/admin/welcome',
+ redirect: '/admin/dashboard',
component: () => import("@/views/admin/Home.vue"),
meta: {title: 'ChatGPT-Plus 管理后台'},
children: [
{
- path: '/admin/welcome',
- name: 'admin-home',
- meta: {title: '系统首页'},
- component: () => import('@/views/admin/Welcome.vue'),
+ path: '/admin/dashboard',
+ name: 'admin-dashboard',
+ meta: {title: '仪表盘'},
+ component: () => import('@/views/admin/Dashboard.vue'),
},
{
path: '/admin/system',
diff --git a/web/src/views/admin/Dashboard.vue b/web/src/views/admin/Dashboard.vue
new file mode 100644
index 00000000..dc785a7f
--- /dev/null
+++ b/web/src/views/admin/Dashboard.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
{{ stats.users }}
+
今日新增用户
+
+
+
+
+
+
+
+
+
+
+
+
{{ stats.chats }}
+
今日新增对话
+
+
+
+
+
+
+
+
+
+
+
+
{{ stats.tokens }}
+
今日消耗 Tokens
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/admin/Welcome.vue b/web/src/views/admin/Welcome.vue
deleted file mode 100644
index 866f43e4..00000000
--- a/web/src/views/admin/Welcome.vue
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
ChatGPT-PLUS 控制台
-
-
-
-
-
-