From 7440c377fb8bcebb350acec489003fb488b31e7c Mon Sep 17 00:00:00 2001 From: engigu Date: Wed, 24 Jan 2024 21:33:51 +0800 Subject: [PATCH] feat: add data statistic chart --- models/send_tasks_logs.go | 63 ++++++ routers/api/v1/statistic.go | 25 +++ routers/router.go | 3 + service/statistic_service/statistic.go | 12 + web/package-lock.json | 59 +++++ web/package.json | 1 + web/src/router/index.js | 7 +- web/src/views/home/index.vue | 5 +- .../tabsTools/settings/view/siteCustom.vue | 2 - .../views/tabsTools/statistic/statistic.vue | 210 ++++++++++++++++++ 10 files changed, 382 insertions(+), 5 deletions(-) create mode 100644 routers/api/v1/statistic.go create mode 100644 service/statistic_service/statistic.go create mode 100644 web/src/views/tabsTools/statistic/statistic.vue diff --git a/models/send_tasks_logs.go b/models/send_tasks_logs.go index 31c267a..9001d6d 100644 --- a/models/send_tasks_logs.go +++ b/models/send_tasks_logs.go @@ -101,3 +101,66 @@ func DeleteOutDateLogs(keepNum int) (int, error) { affectedRows = int(result.RowsAffected) return affectedRows, nil } + +type StatisticData struct { + TodaySuccNum int `json:"today_succ_num"` + TodayFailedNum int `json:"today_failed_num"` + TodayTotalNum int `json:"today_total_num"` + + LatestSendData []LatestSendData `json:"latest_send_data"` + WayCateData []WayCateData `json:"way_cate_data"` +} + +type LatestSendData struct { + Day string `json:"day"` + Num int `json:"num"` +} + +type WayCateData struct { + WayName string `json:"way_name"` + CountNum int `json:"count_num"` +} + +// GetStatisticData 获取统计数据 +func GetStatisticData() (StatisticData, error) { + var statistic StatisticData + var latestData []LatestSendData + var wayCateData []WayCateData + logt := db.NewScope(SendTasksLogs{}).TableName() + inst := db.NewScope(SendTasksIns{}).TableName() + wayst := db.NewScope(SendWays{}).TableName() + + // 今日统计数据 + query := db. + Table(logt). + Select(` +COUNT(*) AS today_total_num, +SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS today_succ_num, +SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS today_failed_num`). + Where("DATE(created_on) = CURDATE()") + query.First(&statistic) + + // 最近30天数据 + days := 30 + queryData := db. + Table(logt). + Select(` + CAST(DATE(created_on) AS CHAR) AS day, + COUNT(*) AS num`). + Where(" created_on >= CURDATE() - INTERVAL ? DAY", days). + Group("DATE(created_on)"). + Order("created_on") + queryData.Scan(&latestData) + + // 消息实例分类数目 + db. + Table(inst). + Select(fmt.Sprintf("%s.name as way_name, count(%s.id) as count_num", wayst, wayst)). + Joins(fmt.Sprintf("JOIN %s ON %s.way_id = %s.id", wayst, inst, wayst)). + Group(fmt.Sprintf("%s.id", wayst)). + Scan(&wayCateData) + + statistic.LatestSendData = latestData + statistic.WayCateData = wayCateData + return statistic, nil +} diff --git a/routers/api/v1/statistic.go b/routers/api/v1/statistic.go new file mode 100644 index 0000000..f9d6605 --- /dev/null +++ b/routers/api/v1/statistic.go @@ -0,0 +1,25 @@ +package v1 + +import ( + "fmt" + "github.com/gin-gonic/gin" + "message-nest/pkg/app" + "message-nest/service/statistic_service" + "net/http" +) + +// GetStatisticData 获取发送统计数据 +func GetStatisticData(c *gin.Context) { + var ( + appG = app.Gin{C: c} + ) + + msgService := statistic_service.StatisticService{} + data, err := msgService.GetStatisticData() + if err != nil { + appG.CResponse(http.StatusInternalServerError, fmt.Sprintf("获取失败!原因:%s", err), nil) + return + } + + appG.CResponse(http.StatusOK, "获取成功", data) +} diff --git a/routers/router.go b/routers/router.go index 9fa698e..52c858e 100644 --- a/routers/router.go +++ b/routers/router.go @@ -81,6 +81,9 @@ func InitRouter(f embed.FS) *gin.Engine { apiV1.POST("/settings/reset", v1.RestDefaultSettings) apiV1.GET("/settings/getsetting", v1.GetUserSetting) + // statistic + apiV1.GET("/statistic", v1.GetStatisticData) + } return app diff --git a/service/statistic_service/statistic.go b/service/statistic_service/statistic.go new file mode 100644 index 0000000..835d446 --- /dev/null +++ b/service/statistic_service/statistic.go @@ -0,0 +1,12 @@ +package statistic_service + +import ( + "message-nest/models" +) + +type StatisticService struct { +} + +func (sw *StatisticService) GetStatisticData() (models.StatisticData, error) { + return models.GetStatisticData() +} diff --git a/web/package-lock.json b/web/package-lock.json index fc38a82..5771b85 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@element-plus/icons-vue": "^2.1.0", "axios": "^1.6.2", + "echarts": "^5.4.3", "element-plus": "^2.4.2", "lodash": "^4.17.21", "pinia": "^2.1.7", @@ -1772,6 +1773,20 @@ "node": ">=6.0.0" } }, + "node_modules/echarts": { + "version": "5.4.3", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.4.3.tgz", + "integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.4.4" + } + }, + "node_modules/echarts/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, "node_modules/electron-to-chromium": { "version": "1.4.616", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz", @@ -3745,6 +3760,19 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zrender": { + "version": "5.4.4", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.4.4.tgz", + "integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==", + "dependencies": { + "tslib": "2.3.0" + } + }, + "node_modules/zrender/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } }, "dependencies": { @@ -4960,6 +4988,22 @@ "esutils": "^2.0.2" } }, + "echarts": { + "version": "5.4.3", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.4.3.tgz", + "integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==", + "requires": { + "tslib": "2.3.0", + "zrender": "5.4.4" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } + }, "electron-to-chromium": { "version": "1.4.616", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz", @@ -6277,6 +6321,21 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zrender": { + "version": "5.4.4", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.4.4.tgz", + "integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==", + "requires": { + "tslib": "2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } } } } diff --git a/web/package.json b/web/package.json index 662692e..971a938 100644 --- a/web/package.json +++ b/web/package.json @@ -14,6 +14,7 @@ "dependencies": { "@element-plus/icons-vue": "^2.1.0", "axios": "^1.6.2", + "echarts": "^5.4.3", "element-plus": "^2.4.2", "lodash": "^4.17.21", "pinia": "^2.1.7", diff --git a/web/src/router/index.js b/web/src/router/index.js index 2f15dc1..63effbd 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -11,6 +11,12 @@ const router = createRouter({ name: 'login', component: LoginInex }, + { + path: '/statistic', + name: 'statistic', + alias: '/', + component: () => import('../views/tabsTools/statistic/statistic.vue') + }, { path: '/sendways', name: 'sendWays', @@ -23,7 +29,6 @@ const router = createRouter({ }, { path: '/sendlogs', - alias: '/', name: 'sendlogs', component: () => import('../views/tabsTools/sendLogs/sendLogs.vue') }, diff --git a/web/src/views/home/index.vue b/web/src/views/home/index.vue index d234bd2..3017582 100644 --- a/web/src/views/home/index.vue +++ b/web/src/views/home/index.vue @@ -2,7 +2,7 @@
- + @@ -33,6 +33,7 @@ export default { titleLogo: '', }); const menuData = reactive([ + { id: '0', title: '数据统计', path: '/statistic' }, { id: '1', title: '发信日志', path: '/sendlogs' }, { id: '2', title: '发信任务', path: '/sendtasks' }, { id: '3', title: '发信渠道', path: '/sendways' }, @@ -54,7 +55,7 @@ export default { const currActivate = () => { const cur_path = useRoute().path; - let result = '1'; + let result = '0'; menuData.forEach(element => { if (element.path == cur_path) { result = element.id; diff --git a/web/src/views/tabsTools/settings/view/siteCustom.vue b/web/src/views/tabsTools/settings/view/siteCustom.vue index d7a7619..35b64c9 100644 --- a/web/src/views/tabsTools/settings/view/siteCustom.vue +++ b/web/src/views/tabsTools/settings/view/siteCustom.vue @@ -42,9 +42,7 @@ import { defineComponent, reactive, toRefs, onMounted } from 'vue'; import { ElMessage } from 'element-plus' import { QuestionFilled } from '@element-plus/icons-vue' -import { useRouter } from 'vue-router'; import { request } from '@/api/api' -import { CONSTANT } from '@/constant' import { LocalStieConfigUtils } from '@/util/localSiteConfig' diff --git a/web/src/views/tabsTools/statistic/statistic.vue b/web/src/views/tabsTools/statistic/statistic.vue new file mode 100644 index 0000000..65563ac --- /dev/null +++ b/web/src/views/tabsTools/statistic/statistic.vue @@ -0,0 +1,210 @@ + + + + + + \ No newline at end of file