package service import ( "context" "time" "github.com/user-management-system/internal/domain" ) // Interfaces for dependency inversion (DIP) — service layer depends on these abstractions, not concrete types. type statsUserRepository interface { List(ctx context.Context, offset, limit int) ([]*domain.User, int64, error) ListByStatus(ctx context.Context, status domain.UserStatus, offset, limit int) ([]*domain.User, int64, error) ListCreatedAfter(ctx context.Context, since time.Time, offset, limit int) ([]*domain.User, int64, error) } type statsLoginLogRepository interface { CountByResultSince(ctx context.Context, success bool, since time.Time) (int64, error) } // StatsService 统计服务 type StatsService struct { userRepo statsUserRepository loginLogRepo statsLoginLogRepository } // NewStatsService 创建统计服务 func NewStatsService( userRepo statsUserRepository, loginLogRepo statsLoginLogRepository, ) *StatsService { return &StatsService{ userRepo: userRepo, loginLogRepo: loginLogRepo, } } // UserStats 用户统计数据 type UserStats struct { TotalUsers int64 `json:"total_users"` ActiveUsers int64 `json:"active_users"` InactiveUsers int64 `json:"inactive_users"` LockedUsers int64 `json:"locked_users"` DisabledUsers int64 `json:"disabled_users"` NewUsersToday int64 `json:"new_users_today"` NewUsersWeek int64 `json:"new_users_week"` NewUsersMonth int64 `json:"new_users_month"` } // LoginStats 登录统计数据 type LoginStats struct { LoginsTodaySuccess int64 `json:"logins_today_success"` LoginsTodayFailed int64 `json:"logins_today_failed"` LoginsWeek int64 `json:"logins_week"` } // DashboardStats 仪表盘综合统计 type DashboardStats struct { Users UserStats `json:"users"` Logins LoginStats `json:"logins"` } // GetUserStats 获取用户统计 func (s *StatsService) GetUserStats(ctx context.Context) (*UserStats, error) { stats := &UserStats{} // 统计总用户数 _, total, err := s.userRepo.List(ctx, 0, 1) if err != nil { return nil, err } stats.TotalUsers = total // 按状态统计 statusCounts := map[domain.UserStatus]*int64{ domain.UserStatusActive: &stats.ActiveUsers, domain.UserStatusInactive: &stats.InactiveUsers, domain.UserStatusLocked: &stats.LockedUsers, domain.UserStatusDisabled: &stats.DisabledUsers, } for status, countPtr := range statusCounts { _, cnt, err := s.userRepo.ListByStatus(ctx, status, 0, 1) if err == nil { *countPtr = cnt } } // 今日新增 stats.NewUsersToday = s.countNewUsers(ctx, daysAgo(0)) // 本周新增 stats.NewUsersWeek = s.countNewUsers(ctx, daysAgo(7)) // 本月新增 stats.NewUsersMonth = s.countNewUsers(ctx, daysAgo(30)) return stats, nil } // countNewUsers 统计指定时间之后的新增用户数 func (s *StatsService) countNewUsers(ctx context.Context, since time.Time) int64 { _, count, err := s.userRepo.ListCreatedAfter(ctx, since, 0, 0) if err != nil { return 0 } return count } // GetDashboardStats 获取仪表盘综合统计 func (s *StatsService) GetDashboardStats(ctx context.Context) (*DashboardStats, error) { userStats, err := s.GetUserStats(ctx) if err != nil { return nil, err } loginStats := &LoginStats{} // 今日登录成功/失败 today := daysAgo(0) if s.loginLogRepo != nil { if successCount, err := s.loginLogRepo.CountByResultSince(ctx, true, today); err == nil { loginStats.LoginsTodaySuccess = successCount } if failedCount, err := s.loginLogRepo.CountByResultSince(ctx, false, today); err == nil { loginStats.LoginsTodayFailed = failedCount } if weekCount, err := s.loginLogRepo.CountByResultSince(ctx, true, daysAgo(7)); err == nil { loginStats.LoginsWeek = weekCount } } return &DashboardStats{ Users: *userStats, Logins: *loginStats, }, nil } // daysAgo 返回N天前的时间(当天0点) func daysAgo(n int) time.Time { now := time.Now() start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) return start.AddDate(0, 0, -n) }