feat(api): update server total data response to use 'OnlineUsers' and implement daily traffic statistics logging

This commit is contained in:
Chang lue Tsen 2025-09-01 05:30:05 -04:00
parent 9ff965bbea
commit 4184a32c0f
5 changed files with 124 additions and 3 deletions

View File

@ -21,7 +21,7 @@ type (
Download int64 `json:"download"`
}
ServerTotalDataResponse {
OnlineUserIPs int64 `json:"online_user_ips"`
OnlineUsers int64 `json:"online_users"`
OnlineServers int64 `json:"online_servers"`
OfflineServers int64 `json:"offline_servers"`
TodayUpload int64 `json:"today_upload"`

View File

@ -7,6 +7,7 @@ import (
"time"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/internal/model/traffic"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
@ -149,7 +150,78 @@ func (l *QueryServerTotalDataLogic) QueryServerTotalData() (resp *types.ServerTo
}
}
// query online user count
onlineUsers, err := l.svcCtx.NodeModel.OnlineUserSubscribeGlobal(l.ctx)
if err != nil {
l.Errorw("[QueryServerTotalDataLogic] OnlineUserSubscribeGlobal error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "OnlineUserSubscribeGlobal error: %v", err)
}
// query online/offline server count
var onlineServers, offlineServers int64
err = query.Model(&node.Server{}).Where("`last_reported_at` > ?", now.Add(-5*time.Minute)).Count(&onlineServers).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
l.Errorw("[QueryServerTotalDataLogic] Count online servers error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Count online servers error: %v", err)
}
err = query.Model(&node.Server{}).Where("`last_reported_at` <= ? OR `last_reported_at` IS NULL", now.Add(-5*time.Minute)).Count(&offlineServers).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
l.Errorw("[QueryServerTotalDataLogic] Count offline servers error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Count offline servers error: %v", err)
}
// TodayUpload, TodayDownload, MonthlyUpload, MonthlyDownload
var todayUpload, todayDownload, monthlyUpload, monthlyDownload int64
type trafficSum struct {
Upload int64
Download int64
}
var todayTraffic trafficSum
// Today
err = query.Model(&traffic.TrafficLog{}).Select("SUM(upload) AS upload, SUM(download) AS download").
Where("timestamp BETWEEN ? AND ?", todayStart, todayEnd).
Scan(&todayTraffic).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
l.Errorw("[QueryServerTotalDataLogic] Sum today traffic error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Sum today traffic error: %v", err)
}
todayUpload = todayTraffic.Upload
todayDownload = todayTraffic.Download
// Monthly
monthlyUpload += todayUpload
monthlyDownload += todayDownload
for i := now.Day() - 1; i >= 1; i-- {
var logInfo log.SystemLog
date := time.Date(now.Year(), now.Month(), i, 0, 0, 0, 0, now.Location()).Format(time.DateOnly)
err = query.Model(&log.SystemLog{}).Where("`date` = ? AND `type` = ?", date, log.TypeTrafficStat).First(&logInfo).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
l.Errorw("[QueryServerTotalDataLogic] Query daily traffic stat log error", logger.Field("error", err.Error()), logger.Field("date", date))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query daily traffic stat log error: %v", err)
}
if logInfo.Id > 0 {
var stat log.TrafficStat
err = stat.Unmarshal([]byte(logInfo.Content))
if err != nil {
l.Errorw("[QueryServerTotalDataLogic] Unmarshal daily traffic stat log error", logger.Field("error", err.Error()), logger.Field("date", date))
continue
}
monthlyUpload += stat.Upload
monthlyDownload += stat.Download
}
}
resp = &types.ServerTotalDataResponse{
OnlineUsers: onlineUsers,
OnlineServers: onlineServers,
OfflineServers: offlineServers,
TodayUpload: todayUpload,
TodayDownload: todayDownload,
MonthlyUpload: monthlyUpload,
MonthlyDownload: monthlyDownload,
UpdatedAt: now.Unix(),
ServerTrafficRankingToday: todayServerRanking,
ServerTrafficRankingYesterday: yesterdayTop10Server,
UserTrafficRankingToday: userTodayTrafficRanking,
@ -214,7 +286,7 @@ func (l *QueryServerTotalDataLogic) mockRevenueStatistics() *types.ServerTotalDa
//}
//
return &types.ServerTotalDataResponse{
OnlineUserIPs: 1688,
OnlineUsers: 1688,
OnlineServers: 8,
OfflineServers: 2,
TodayUpload: 8888888888, // ~8.3GB

View File

@ -30,6 +30,7 @@ const (
TypeGift Type = 34 // Gift log
TypeUserTrafficRank Type = 40 // Top 10 User traffic rank log
TypeServerTrafficRank Type = 41 // Top 10 Server traffic rank log
TypeTrafficStat Type = 42 // Daily traffic statistics log
)
const (
ResetSubscribeTypeAuto uint16 = 231 // Auto reset
@ -393,3 +394,27 @@ func (s *ServerTrafficRank) Unmarshal(data []byte) error {
aux := (*Alias)(s)
return json.Unmarshal(data, aux)
}
// TrafficStat represents a daily traffic statistics log entry.
type TrafficStat struct {
Upload int64 `json:"upload"`
Download int64 `json:"download"`
Total int64 `json:"total"`
}
// Marshal implements the json.Marshaler interface for TrafficStat.
func (t *TrafficStat) Marshal() ([]byte, error) {
type Alias TrafficStat
return json.Marshal(&struct {
*Alias
}{
Alias: (*Alias)(t),
})
}
// Unmarshal implements the json.Unmarshaler interface for TrafficStat.
func (t *TrafficStat) Unmarshal(data []byte) error {
type Alias TrafficStat
aux := (*Alias)(t)
return json.Unmarshal(data, aux)
}

View File

@ -1739,7 +1739,7 @@ type ServerStatus struct {
}
type ServerTotalDataResponse struct {
OnlineUserIPs int64 `json:"online_user_ips"`
OnlineUsers int64 `json:"online_users"`
OnlineServers int64 `json:"online_servers"`
OfflineServers int64 `json:"offline_servers"`
TodayUpload int64 `json:"today_upload"`

View File

@ -141,6 +141,30 @@ func (l *StatLogic) ProcessTask(ctx context.Context, _ *asynq.Task) error {
return err
}
// traffic stat
var stat log.TrafficStat
err = tx.WithContext(ctx).Model(&traffic.TrafficLog{}).
Select("SUM(download + upload) AS total, SUM(download) AS download, SUM(upload) AS upload").
Where("timestamp BETWEEN ? AND ?", start, end).
Scan(&stat).Error
if err != nil {
logger.Errorf("[Traffic Stat Queue] Query traffic stat failed: %v", err.Error())
return err
}
// 更新流量统计日志
content, _ := stat.Marshal()
err = tx.WithContext(ctx).Model(&log.SystemLog{}).Create(&log.SystemLog{
Type: log.TypeTrafficStat.Uint8(),
Date: date,
ObjectID: 0,
Content: string(content),
}).Error
if err != nil {
logger.Errorf("[Traffic Stat Queue] Create traffic stat log failed: %v", err.Error())
return err
}
// Delete old traffic logs
err = tx.WithContext(ctx).Model(&traffic.TrafficLog{}).Where("created_at <= ?", end).Delete(&traffic.TrafficLog{}).Error
if err != nil {