Files
BackupX/server/internal/service/dashboard_service.go
2026-03-17 13:29:09 +08:00

110 lines
4.3 KiB
Go

package service
import (
"context"
"time"
"backupx/server/internal/apperror"
"backupx/server/internal/repository"
)
type DashboardStorageUsageItem struct {
StorageTargetID uint `json:"storageTargetId"`
TargetName string `json:"targetName"`
TotalSize int64 `json:"totalSize"`
}
type DashboardStats struct {
TotalTasks int64 `json:"totalTasks"`
EnabledTasks int64 `json:"enabledTasks"`
TotalRecords int64 `json:"totalRecords"`
SuccessRate float64 `json:"successRate"`
TotalBackupBytes int64 `json:"totalBackupBytes"`
LastBackupAt *time.Time `json:"lastBackupAt,omitempty"`
RecentRecords []BackupRecordSummary `json:"recentRecords"`
StorageUsage []DashboardStorageUsageItem `json:"storageUsage"`
}
type DashboardService struct {
tasks repository.BackupTaskRepository
records repository.BackupRecordRepository
targets repository.StorageTargetRepository
}
func NewDashboardService(tasks repository.BackupTaskRepository, records repository.BackupRecordRepository, targets repository.StorageTargetRepository) *DashboardService {
return &DashboardService{tasks: tasks, records: records, targets: targets}
}
func (s *DashboardService) Stats(ctx context.Context) (*DashboardStats, error) {
totalTasks, err := s.tasks.Count(ctx)
if err != nil {
return nil, apperror.Internal("DASHBOARD_STATS_FAILED", "无法统计备份任务数量", err)
}
enabledTasks, err := s.tasks.CountEnabled(ctx)
if err != nil {
return nil, apperror.Internal("DASHBOARD_STATS_FAILED", "无法统计启用任务数量", err)
}
totalRecords, err := s.records.Count(ctx)
if err != nil {
return nil, apperror.Internal("DASHBOARD_STATS_FAILED", "无法统计备份记录数量", err)
}
since := time.Now().UTC().AddDate(0, 0, -30)
recentRecordsCount, err := s.records.CountSince(ctx, since)
if err != nil {
return nil, apperror.Internal("DASHBOARD_STATS_FAILED", "无法统计最近记录数量", err)
}
successRecordsCount, err := s.records.CountSuccessSince(ctx, since)
if err != nil {
return nil, apperror.Internal("DASHBOARD_STATS_FAILED", "无法统计最近成功记录数量", err)
}
totalBackupBytes, err := s.records.SumFileSize(ctx)
if err != nil {
return nil, apperror.Internal("DASHBOARD_STATS_FAILED", "无法统计备份总量", err)
}
recentRecords, err := s.records.ListRecent(ctx, 10)
if err != nil {
return nil, apperror.Internal("DASHBOARD_STATS_FAILED", "无法获取最近备份记录", err)
}
targetList, err := s.targets.List(ctx)
if err != nil {
return nil, apperror.Internal("DASHBOARD_STATS_FAILED", "无法获取存储目标信息", err)
}
targetNames := make(map[uint]string, len(targetList))
for _, item := range targetList {
targetNames[item.ID] = item.Name
}
usageItems, err := s.records.StorageUsage(ctx)
if err != nil {
return nil, apperror.Internal("DASHBOARD_STATS_FAILED", "无法统计存储使用量", err)
}
storageUsage := make([]DashboardStorageUsageItem, 0, len(usageItems))
for _, item := range usageItems {
storageUsage = append(storageUsage, DashboardStorageUsageItem{StorageTargetID: item.StorageTargetID, TargetName: targetNames[item.StorageTargetID], TotalSize: item.TotalSize})
}
result := &DashboardStats{TotalTasks: totalTasks, EnabledTasks: enabledTasks, TotalRecords: totalRecords, TotalBackupBytes: totalBackupBytes, RecentRecords: make([]BackupRecordSummary, 0, len(recentRecords)), StorageUsage: storageUsage}
if recentRecordsCount > 0 {
result.SuccessRate = float64(successRecordsCount) / float64(recentRecordsCount)
}
if len(recentRecords) > 0 {
result.LastBackupAt = &recentRecords[0].StartedAt
}
for _, item := range recentRecords {
result.RecentRecords = append(result.RecentRecords, toBackupRecordSummary(&item))
}
return result, nil
}
func (s *DashboardService) Timeline(ctx context.Context, days int) ([]repository.BackupTimelinePoint, error) {
if days <= 0 {
days = 30
}
items, err := s.records.TimelineSince(ctx, time.Now().UTC().AddDate(0, 0, -days))
if err != nil {
return nil, apperror.Internal("DASHBOARD_TIMELINE_FAILED", "无法获取备份时间线", err)
}
if items == nil {
items = []repository.BackupTimelinePoint{}
}
return items, nil
}