mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-05-05 09:24:29 +08:00
优化 dashboard 页面
This commit is contained in:
@@ -13,10 +13,11 @@ import (
|
|||||||
"geekai/handler"
|
"geekai/handler"
|
||||||
"geekai/store/model"
|
"geekai/store/model"
|
||||||
"geekai/utils/resp"
|
"geekai/utils/resp"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DashboardHandler struct {
|
type DashboardHandler struct {
|
||||||
@@ -33,12 +34,41 @@ func (h *DashboardHandler) RegisterRoutes() {
|
|||||||
group.GET("stats", h.Stats)
|
group.GET("stats", h.Stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// statsVo 增加 recentOrders、recentUsers 字段
|
||||||
|
// 最近订单
|
||||||
|
type OrderBrief struct {
|
||||||
|
OrderNo string `json:"order_no"`
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最近用户
|
||||||
|
type UserBrief struct {
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
LastActive time.Time `json:"last_active"`
|
||||||
|
}
|
||||||
|
|
||||||
type statsVo struct {
|
type statsVo struct {
|
||||||
Users int64 `json:"users"`
|
Users int64 `json:"users"`
|
||||||
Chats int64 `json:"chats"`
|
Chats int64 `json:"chats"`
|
||||||
Tokens int `json:"tokens"`
|
Tokens int `json:"tokens"`
|
||||||
Income float64 `json:"income"`
|
Income float64 `json:"income"`
|
||||||
Chart map[string]map[string]float64 `json:"chart"`
|
Chart map[string]map[string]float64 `json:"chart"`
|
||||||
|
TodayUsers int64 `json:"today_users"`
|
||||||
|
TodayChats int64 `json:"today_chats"`
|
||||||
|
TodayTokens int `json:"today_tokens"`
|
||||||
|
TodayIncome float64 `json:"today_income"`
|
||||||
|
TodayOrders int64 `json:"today_orders"`
|
||||||
|
TodayImageJobs int64 `json:"today_image_jobs"`
|
||||||
|
TodayVideoJobs int64 `json:"today_video_jobs"`
|
||||||
|
TodayMusicJobs int64 `json:"today_music_jobs"`
|
||||||
|
Orders int64 `json:"orders"`
|
||||||
|
ImageJobs int64 `json:"image_jobs"`
|
||||||
|
VideoJobs int64 `json:"video_jobs"`
|
||||||
|
MusicJobs int64 `json:"music_jobs"`
|
||||||
|
RecentOrders []OrderBrief `json:"recentOrders"`
|
||||||
|
RecentUsers []UserBrief `json:"recentUsers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *DashboardHandler) Stats(c *gin.Context) {
|
func (h *DashboardHandler) Stats(c *gin.Context) {
|
||||||
@@ -126,6 +156,31 @@ func (h *DashboardHandler) Stats(c *gin.Context) {
|
|||||||
// 今日音乐生成任务统计
|
// 今日音乐生成任务统计
|
||||||
h.DB.Model(&model.SunoJob{}).Where("created_at > ?", zeroTime).Count(&stats.TodayMusicJobs)
|
h.DB.Model(&model.SunoJob{}).Where("created_at > ?", zeroTime).Count(&stats.TodayMusicJobs)
|
||||||
|
|
||||||
|
// recentOrders: 最近10条已支付订单
|
||||||
|
var orderList []model.Order
|
||||||
|
h.DB.Model(&model.Order{}).Where("status = ?", types.OrderPaidSuccess).Order("created_at desc").Limit(10).Find(&orderList)
|
||||||
|
for _, o := range orderList {
|
||||||
|
stats.RecentOrders = append(stats.RecentOrders, OrderBrief{
|
||||||
|
OrderNo: o.OrderNo,
|
||||||
|
Amount: o.Amount,
|
||||||
|
CreatedAt: o.CreatedAt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// recentUsers: 最近10个注册用户
|
||||||
|
var userList []model.User
|
||||||
|
h.DB.Model(&model.User{}).Order("created_at desc").Limit(10).Find(&userList)
|
||||||
|
for _, u := range userList {
|
||||||
|
lastActive := u.UpdatedAt
|
||||||
|
if lastActive.IsZero() {
|
||||||
|
lastActive = u.CreatedAt
|
||||||
|
}
|
||||||
|
stats.RecentUsers = append(stats.RecentUsers, UserBrief{
|
||||||
|
Nickname: u.Nickname,
|
||||||
|
Avatar: u.Avatar,
|
||||||
|
LastActive: lastActive,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 统计7天的订单的图表
|
// 统计7天的订单的图表
|
||||||
startDate := now.Add(-7 * 24 * time.Hour).Format("2006-01-02")
|
startDate := now.Add(-7 * 24 * time.Hour).Format("2006-01-02")
|
||||||
var statsChart = make(map[string]map[string]float64)
|
var statsChart = make(map[string]map[string]float64)
|
||||||
@@ -140,24 +195,28 @@ func (h *DashboardHandler) Stats(c *gin.Context) {
|
|||||||
|
|
||||||
// 统计用户7天增加的曲线
|
// 统计用户7天增加的曲线
|
||||||
var users []model.User
|
var users []model.User
|
||||||
res = h.DB.Model(&model.User{}).Where("created_at > ?", startDate).Find(&users)
|
err := h.DB.Model(&model.User{}).Where("created_at > ?", startDate).Find(&users).Error
|
||||||
if res.Error == nil {
|
if err == nil {
|
||||||
for _, item := range users {
|
for _, item := range users {
|
||||||
userStatistic[item.CreatedAt.Format("2006-01-02")] += 1
|
userStatistic[item.CreatedAt.Format("2006-01-02")] += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计7天Token 消耗
|
// 统计7天Token 消耗
|
||||||
res = h.DB.Where("created_at > ?", startDate).Find(&historyMessages)
|
err = h.DB.Where("created_at > ?", startDate).Find(&historyMessages).Error
|
||||||
for _, item := range historyMessages {
|
if err == nil {
|
||||||
historyMessagesStatistic[item.CreatedAt.Format("2006-01-02")] += float64(item.Tokens)
|
for _, item := range historyMessages {
|
||||||
|
historyMessagesStatistic[item.CreatedAt.Format("2006-01-02")] += float64(item.Tokens)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计最近7天的订单
|
// 统计最近7天的订单
|
||||||
var orders []model.Order
|
var orders []model.Order
|
||||||
res = h.DB.Where("status = ?", types.OrderPaidSuccess).Where("created_at > ?", startDate).Find(&orders)
|
err = h.DB.Where("status = ?", types.OrderPaidSuccess).Where("created_at > ?", startDate).Find(&orders).Error
|
||||||
for _, item := range orders {
|
if err == nil {
|
||||||
incomeStatistic[item.CreatedAt.Format("2006-01-02")], _ = decimal.NewFromFloat(incomeStatistic[item.CreatedAt.Format("2006-01-02")]).Add(decimal.NewFromFloat(item.Amount)).Float64()
|
for _, item := range orders {
|
||||||
|
incomeStatistic[item.CreatedAt.Format("2006-01-02")], _ = decimal.NewFromFloat(incomeStatistic[item.CreatedAt.Format("2006-01-02")]).Add(decimal.NewFromFloat(item.Amount)).Float64()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
statsChart["users"] = userStatistic
|
statsChart["users"] = userStatistic
|
||||||
|
|||||||
@@ -159,16 +159,13 @@
|
|||||||
<h3>最近订单</h3>
|
<h3>最近订单</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="order-list">
|
<div class="order-list">
|
||||||
<div class="order-item" v-for="order in recentOrders" :key="order.id">
|
<div class="order-item" v-for="order in recentOrders" :key="order.order_no">
|
||||||
<div class="order-info">
|
<div class="order-info">
|
||||||
<div class="order-id">#{{ order.id }}</div>
|
<div class="order-id">#{{ order.order_no }}</div>
|
||||||
<div class="order-amount">{{ order.amount }}</div>
|
<div class="order-amount">¥{{ order.amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="order-meta">
|
<div class="order-meta">
|
||||||
<div class="order-date">{{ order.date }}</div>
|
<div class="order-date">{{ formatTime(order.created_at) }}</div>
|
||||||
<el-tag :type="order.status === '已支付' ? 'success' : 'warning'" size="small">
|
|
||||||
{{ order.status }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -182,17 +179,15 @@
|
|||||||
<h3>最近用户</h3>
|
<h3>最近用户</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-list">
|
<div class="user-list">
|
||||||
<div class="user-item" v-for="user in recentUsers" :key="user.id">
|
<div class="user-item" v-for="user in recentUsers" :key="user.nickname">
|
||||||
<div class="user-avatar">
|
<div class="user-avatar">
|
||||||
<el-avatar :size="40" :src="user.avatar">{{ user.name.charAt(0) }}</el-avatar>
|
<el-avatar :size="40" :src="user.avatar">{{ user.nickname.charAt(0) }}</el-avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<div class="user-name">{{ user.name }}</div>
|
<div class="user-name">{{ user.nickname }}</div>
|
||||||
<div class="user-id">{{ user.userId }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="user-meta">
|
<div class="user-meta">
|
||||||
<div class="user-time">{{ user.time }}</div>
|
<div class="user-time">{{ formatTime(user.last_active) }}</div>
|
||||||
<el-tag type="info" size="small">{{ user.status }}</el-tag>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -297,6 +292,20 @@ const formatNumber = (num) => {
|
|||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 时间格式化工具
|
||||||
|
const formatTime = (dateStr) => {
|
||||||
|
const date = new Date(dateStr)
|
||||||
|
const now = new Date()
|
||||||
|
const diff = (now - date) / 1000
|
||||||
|
if (diff < 60 * 60) {
|
||||||
|
return Math.floor(diff / 60) + '分钟前'
|
||||||
|
} else if (diff < 60 * 60 * 24) {
|
||||||
|
return Math.floor(diff / 3600) + '小时前'
|
||||||
|
} else {
|
||||||
|
return Math.floor(diff / 86400) + '天前'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const chartUsers = echarts.init(document.getElementById('chart-users'))
|
const chartUsers = echarts.init(document.getElementById('chart-users'))
|
||||||
const chartTokens = echarts.init(document.getElementById('chart-tokens'))
|
const chartTokens = echarts.init(document.getElementById('chart-tokens'))
|
||||||
@@ -305,6 +314,8 @@ onMounted(() => {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
// 更新统计数据
|
// 更新统计数据
|
||||||
Object.assign(stats.value, res.data)
|
Object.assign(stats.value, res.data)
|
||||||
|
recentOrders.value = res.data.recentOrders || []
|
||||||
|
recentUsers.value = res.data.recentUsers || []
|
||||||
const chartData = res.data.chart
|
const chartData = res.data.chart
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user