125 lines
3.3 KiB
Go
125 lines
3.3 KiB
Go
|
|
package service
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"github.com/user-management-system/internal/domain"
|
|||
|
|
"github.com/user-management-system/internal/repository"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// StatsService 统计服务
|
|||
|
|
type StatsService struct {
|
|||
|
|
userRepo *repository.UserRepository
|
|||
|
|
loginLogRepo *repository.LoginLogRepository
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewStatsService 创建统计服务
|
|||
|
|
func NewStatsService(
|
|||
|
|
userRepo *repository.UserRepository,
|
|||
|
|
loginLogRepo *repository.LoginLogRepository,
|
|||
|
|
) *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 {
|
|||
|
|
loginStats.LoginsTodaySuccess = s.loginLogRepo.CountByResultSince(ctx, true, today)
|
|||
|
|
loginStats.LoginsTodayFailed = s.loginLogRepo.CountByResultSince(ctx, false, today)
|
|||
|
|
loginStats.LoginsWeek = s.loginLogRepo.CountByResultSince(ctx, true, daysAgo(7))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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)
|
|||
|
|
}
|