数据统计

dev
truthhun 1 year ago
parent d3f28a5ca0
commit 0c589190e5

@ -48,7 +48,7 @@ message ConfigSystem {
string favicon = 6;
string icp = 7;
string analytics = 8;
string sitename = 9;
string sitename = 9;
string copyright_start_year = 10;
string register_background = 11;
string login_background = 12;
@ -81,7 +81,22 @@ message Settings {
ConfigSystem system = 1;
ConfigFooter footer = 2;
ConfigSecurity security = 3;
// ConfigCaptcha captcha = 4;
// ConfigCaptcha captcha = 4;
}
message Stats {
int64 user_count = 1;
int64 document_count = 2;
int64 category_count = 3;
int64 article_count = 4;
int64 comment_count = 5;
int64 banner_count = 6;
int64 friendlink_count = 7;
string os = 8;
string version = 9;
string hash = 10;
string build_at = 11;
int64 report_count = 12;
}
service ConfigAPI {
@ -106,4 +121,11 @@ service ConfigAPI {
get : '/api/v1/config/list',
};
}
//
rpc GetStats(google.protobuf.Empty) returns (Stats) {
option (google.api.http) = {
get : "/api/v1/stats"
};
}
}

@ -3,6 +3,7 @@ package biz
import (
"context"
"fmt"
"runtime"
pb "moredoc/api/v1"
"moredoc/middleware/auth"
@ -104,3 +105,32 @@ func (s *ConfigAPIService) GetSettings(ctx context.Context, req *emptypb.Empty)
return res, nil
}
func (s *ConfigAPIService) GetStats(ctx context.Context, req *emptypb.Empty) (res *pb.Stats, err error) {
res = &pb.Stats{
UserCount: 0,
DocumentCount: 0,
CategoryCount: 0,
ArticleCount: 0,
CommentCount: 0,
BannerCount: 0,
FriendlinkCount: 0,
Os: runtime.GOOS,
Version: "1.0.0",
Hash: "hash",
BuildAt: "2021-01-01 00:00:00",
}
res.UserCount, _ = s.dbModel.CountUser()
res.DocumentCount, _ = s.dbModel.CountDocument()
_, errPermission := s.checkPermission(ctx)
if errPermission == nil {
res.CategoryCount, _ = s.dbModel.CountCategory()
res.ArticleCount, _ = s.dbModel.CountArticle()
res.CommentCount, _ = s.dbModel.CountComment()
res.BannerCount, _ = s.dbModel.CountBanner()
res.FriendlinkCount, _ = s.dbModel.CountFriendlink()
res.ReportCount, _ = s.dbModel.CountReport()
}
return
}

@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@ -17,6 +17,7 @@ package cmd
import (
"moredoc/service"
"moredoc/util"
"github.com/spf13/cobra"
)
@ -27,6 +28,9 @@ var serveCmd = &cobra.Command{
Short: "start a server",
Long: `start a server`,
Run: func(cmd *cobra.Command, args []string) {
util.Version = Version
util.Hash = GitHash
util.BuildAt = BuildAt
service.Run(cfg, logger)
},
}

@ -215,3 +215,8 @@ func (m *DBModel) checkArticleFile(article *Article) {
}
}
}
func (m *DBModel) CountArticle() (count int64, err error) {
err = m.db.Model(&Article{}).Count(&count).Error
return
}

@ -136,3 +136,8 @@ func (m *DBModel) DeleteBanner(ids []int64) (err error) {
return
}
func (m *DBModel) CountBanner() (count int64, err error) {
err = m.db.Model(&Banner{}).Count(&count).Error
return
}

@ -163,3 +163,11 @@ func (m *DBModel) DeleteCategory(ids []int64) (err error) {
}
return
}
func (m *DBModel) CountCategory() (count int64, err error) {
err = m.db.Model(&Category{}).Count(&count).Error
if err != nil {
m.logger.Error("CountCategory", zap.Error(err))
}
return
}

@ -226,3 +226,11 @@ func (m *DBModel) UpdateCommentStatus(ids []int64, status int32) (err error) {
}
return
}
func (m *DBModel) CountComment() (count int64, err error) {
err = m.db.Model(&Comment{}).Count(&count).Error
if err != nil {
m.logger.Error("CountComment", zap.Error(err))
}
return
}

@ -59,9 +59,10 @@ func getPermissions() (permissions []Permission) {
{Title: "批量审核评论", Description: "", Method: "GRPC", Path: "/api.v1.CommentAPI/CheckComment"},
{Title: "删除评论", Description: "", Method: "GRPC", Path: "/api.v1.CommentAPI/DeleteComment"},
{Title: "推荐文档", Description: "", Method: "GRPC", Path: "/api.v1.DocumentAPI/SetDocumentRecommend"},
{Title: "获取举报列表", Description: "", Method: "GRPC", Path: "/api.v1.ReportAPI/ListReport"},
{Title: "处理举报信息", Description: "", Method: "GRPC", Path: "/api.v1.ReportAPI/UpdateReport"},
{Title: "查询举报列表", Description: "", Method: "GRPC", Path: "/api.v1.ReportAPI/ListReport"},
{Title: "处理举报内容", Description: "", Method: "GRPC", Path: "/api.v1.ReportAPI/UpdateReport"},
{Title: "删除举报内容", Description: "", Method: "GRPC", Path: "/api.v1.ReportAPI/DeleteReport"},
{Title: "查看系统信息", Description: "", Method: "GRPC", Path: "/api.v1.ConfigAPI/GetStats"},
}
return
}

@ -743,3 +743,15 @@ func (m *DBModel) SetDocumentRecommend(documentIds []int64, typ int32) (err erro
}
return
}
func (m *DBModel) CountDocument(status ...int) (count int64, err error) {
db := m.db.Model(&Document{})
if len(status) > 0 {
db = db.Where("status in (?)", status)
}
err = db.Count(&count).Error
if err != nil {
m.logger.Error("CountDocument", zap.Error(err))
}
return
}

@ -116,3 +116,15 @@ func (m *DBModel) DeleteFriendlink(ids []int64) (err error) {
}
return
}
func (m *DBModel) CountFriendlink(enable ...bool) (count int64, err error) {
db := m.db.Model(&Friendlink{})
if len(enable) > 0 {
db = db.Where("enable in ?", enable)
}
err = db.Count(&count).Error
if err != nil {
m.logger.Error("CountFriendlink", zap.Error(err))
}
return
}

@ -132,3 +132,12 @@ func (m *DBModel) GetReportByDocUser(docId, userId int64) (report Report, err er
err = m.db.Where("document_id = ? and user_id = ?", docId, userId).First(&report).Error
return
}
func (m *DBModel) CountReport(status ...bool) (count int64, err error) {
db := m.db.Model(&Report{})
if len(status) > 0 {
db = db.Where("status in ?", status)
}
err = db.Count(&count).Error
return
}

@ -470,3 +470,16 @@ func (m *DBModel) CanIPublishComment(userId int64) (defaultCommentStatus int8, e
return
}
func (s *DBModel) CountUser(status ...int) (count int64, err error) {
db := s.db.Model(&User{})
if len(status) > 0 {
db = db.Where("status in (?)", status)
}
err = db.Count(&count).Error
if err != nil {
s.logger.Error("CountUser", zap.Error(err))
return
}
return
}

@ -0,0 +1,10 @@
package util
var (
// Version 版本号
Version = "0.0.1"
// Hash 版本号
Hash = "hash"
// BuildAt 编译时间
BuildAt = "2006-01-02 00:00:00"
)

@ -22,3 +22,10 @@ export const getSettings = () => {
method: 'get',
})
}
export const getStats = () => {
return service({
url: '/api/v1/stats',
method: 'get',
})
}

@ -1,16 +1,170 @@
<template>
<div>
<h1>Welcome to MOREDOC!</h1>
<div class="page-admin-dashboard">
<el-card shadow="never">
<div slot="header">数据统计</div>
<el-descriptions class="margin-top" :column="2" border>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-tickets"></i>
文档
</template>
{{ stats.document_count || 0 }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-chat-dot-square"></i>
评论
</template>
{{ stats.comment_count || 0 }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-user"></i>
用户
</template>
{{ stats.user_count || 0 }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-s-grid"></i>
分类
</template>
{{ stats.category_count || 0 }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-warning"></i>
举报
</template>
{{ stats.report_count || 0 }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-picture-outline"></i>
横幅
</template>
{{ stats.banner_count || 0 }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-link"></i>
友链
</template>
{{ stats.friendlink_count || 0 }}
</el-descriptions-item>
</el-descriptions>
</el-card>
<el-card shadow="never" class="mgt-20px">
<div slot="header">系统信息</div>
<el-descriptions class="margin-top" :column="1" border>
<el-descriptions-item>
<template slot="label"> 操作系统 </template>
{{ stats.os }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"> 程序名称 </template>
moredoc · 魔豆文库
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"> 程序版本 </template>
{{ stats.version }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"> 程序Hash </template>
{{ stats.hash }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"> 构建时间 </template>
{{ formatDatetime(stats.build_at) }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"> 程序开发 </template>
深圳市摩枫网络科技有限公司 <b>M</b>orefun <b>N</b>etwork
<b>T</b>echnology Co., <b>Ltd</b>.
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"> 服务支持邮箱 </template>
truthhun@mnt.ltd
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"> 服务支持官网 </template>
<a
href="https://mnt.ltd"
class="el-link el-link--primary"
target="_blank"
title="摩枫网络科技 MNT.Ltd"
>https://mnt.ltd</a
>
</el-descriptions-item>
</el-descriptions>
</el-card>
</div>
</template>
<script>
import { getStats } from '~/api/config'
import { formatDatetime } from '~/utils/utils'
export default {
layout: 'admin',
head() {
return {
title: `面板 - MOREDOC · 魔豆文库`,
title: `面板 - ${this.settings.system.sitename}`,
}
},
data() {
return {
stats: {
admin_count: 0,
student_count: 0,
company_count: 0,
category_count: 0,
article_count: 0,
article_pending_count: 0,
comment_count: 0,
comment_pending_count: 0,
banner_count: 0,
friendlink_count: 0,
user_pending_count: 0,
os: '-',
version: '-',
hash: '-',
build_at: '',
},
}
},
computed: {
settings() {
return this.$store.state.setting.settings
},
},
created() {
this.getStats()
},
methods: {
formatDatetime,
async getStats() {
const res = await getStats()
if (res.status === 200) {
this.stats = {
...this.stats,
...res.data,
}
}
console.log(res)
},
},
}
</script>
<style lang="scss">
.page-admin-dashboard {
.el-descriptions-item__label.is-bordered-label {
width: 150px;
}
.systeminfo {
b {
color: crimson;
}
}
}
</style>

@ -53,12 +53,18 @@
<el-col :span="12">
<small>收录文档</small>
<div>
<span class="el-link el-link--primary">3235</span>
<span class="el-link el-link--primary">{{
stats.document_count || 0
}}</span>
</div>
</el-col>
<el-col :span="12">
<small>注册用户</small>
<div><span class="el-link el-link--primary">872</span></div>
<div>
<span class="el-link el-link--primary">{{
stats.user_count || 0
}}</span>
</div>
</el-col>
</el-row>
</el-card>
@ -283,6 +289,7 @@ import UserAvatar from '~/components/UserAvatar.vue'
import { listBanner } from '~/api/banner'
import { listDocument, listDocumentForHome } from '~/api/document'
import { getSignedToday, signToday } from '~/api/user'
import { getStats } from '~/api/config'
export default {
components: { UserAvatar },
data() {
@ -296,6 +303,10 @@ export default {
sign: {
sign_at: 0,
},
stats: {
document_count: '-',
user_count: '-',
},
}
},
head() {
@ -315,6 +326,7 @@ export default {
this.listBanner(),
this.getDocuments(),
this.getSignedToday(),
this.getStats(),
])
},
methods: {
@ -374,6 +386,14 @@ export default {
console.log(res)
}
},
async getStats() {
const res = await getStats()
if (res.status === 200) {
this.stats = res.data || {}
} else {
console.log(res)
}
},
login() {
//
this.$router.push('/login')

@ -108,7 +108,9 @@
本次搜索耗时
<span class="el-link el-link--danger">{{ spend || '0.000' }}</span>
<span class="el-link el-link--primary">3235</span>
<span class="el-link el-link--primary">{{
stats.document_count || '0'
}}</span>
篇文档中为您找到相关结果约
<span class="el-link el-link--danger">{{ total || 0 }}</span> .
</div>
@ -194,6 +196,7 @@
<script>
import { mapGetters } from 'vuex'
import { getStats } from '~/api/config'
import { searchDocument } from '~/api/document'
import { formatBytes, getIcon } from '~/utils/utils'
export default {
@ -229,6 +232,9 @@ export default {
total: 0,
spend: '',
keywords: [],
stats: {
document_count: '-',
},
}
},
head() {
@ -261,6 +267,7 @@ export default {
query.page = parseInt(query.page) || 1
query.size = parseInt(query.size) || 10
this.query = query
this.getStats()
},
methods: {
formatBytes,
@ -276,6 +283,12 @@ export default {
},
})
},
async getStats() {
const res = await getStats()
if (res.status === 200) {
this.stats = res.data
}
},
async execSearch() {
this.loading = true
const res = await searchDocument(this.query)

Loading…
Cancel
Save