package services import ( "context" "fmt" "time" "go.uber.org/zap" "tyapi-server/internal/domains/statistics/entities" "tyapi-server/internal/domains/statistics/repositories" ) // StatisticsAggregateService 统计聚合服务接口 // 负责统计数据的聚合和计算 type StatisticsAggregateService interface { // 实时统计 UpdateRealtimeMetric(ctx context.Context, metricType, metricName string, value float64) error GetRealtimeMetrics(ctx context.Context, metricType string) (map[string]float64, error) // 历史统计聚合 AggregateHourlyMetrics(ctx context.Context, date time.Time) error AggregateDailyMetrics(ctx context.Context, date time.Time) error AggregateWeeklyMetrics(ctx context.Context, date time.Time) error AggregateMonthlyMetrics(ctx context.Context, date time.Time) error // 统计查询 GetMetricsByType(ctx context.Context, metricType string, startDate, endDate time.Time) ([]*entities.StatisticsMetric, error) GetMetricsByDimension(ctx context.Context, metricType, dimension string, startDate, endDate time.Time) ([]*entities.StatisticsMetric, error) // 统计计算 CalculateGrowthRate(ctx context.Context, metricType, metricName string, currentPeriod, previousPeriod time.Time) (float64, error) CalculateTrend(ctx context.Context, metricType, metricName string, startDate, endDate time.Time) (string, error) } // StatisticsAggregateServiceImpl 统计聚合服务实现 type StatisticsAggregateServiceImpl struct { metricRepo repositories.StatisticsRepository logger *zap.Logger } // NewStatisticsAggregateService 创建统计聚合服务 func NewStatisticsAggregateService( metricRepo repositories.StatisticsRepository, logger *zap.Logger, ) StatisticsAggregateService { return &StatisticsAggregateServiceImpl{ metricRepo: metricRepo, logger: logger, } } // UpdateRealtimeMetric 更新实时统计指标 func (s *StatisticsAggregateServiceImpl) UpdateRealtimeMetric(ctx context.Context, metricType, metricName string, value float64) error { if metricType == "" { return fmt.Errorf("指标类型不能为空") } if metricName == "" { return fmt.Errorf("指标名称不能为空") } // 创建或更新实时指标 metric, err := entities.NewStatisticsMetric(metricType, metricName, "realtime", value, time.Now()) if err != nil { s.logger.Error("创建统计指标失败", zap.String("metric_type", metricType), zap.String("metric_name", metricName), zap.Error(err)) return fmt.Errorf("创建统计指标失败: %w", err) } // 保存到数据库 err = s.metricRepo.Save(ctx, metric) if err != nil { s.logger.Error("保存统计指标失败", zap.String("metric_id", metric.ID), zap.Error(err)) return fmt.Errorf("保存统计指标失败: %w", err) } s.logger.Info("实时统计指标更新成功", zap.String("metric_type", metricType), zap.String("metric_name", metricName), zap.Float64("value", value)) return nil } // GetRealtimeMetrics 获取实时统计指标 func (s *StatisticsAggregateServiceImpl) GetRealtimeMetrics(ctx context.Context, metricType string) (map[string]float64, error) { if metricType == "" { return nil, fmt.Errorf("指标类型不能为空") } // 获取今天的实时指标 today := time.Now().Truncate(24 * time.Hour) tomorrow := today.Add(24 * time.Hour) metrics, err := s.metricRepo.FindByTypeAndDateRange(ctx, metricType, today, tomorrow) if err != nil { s.logger.Error("查询实时统计指标失败", zap.String("metric_type", metricType), zap.Error(err)) return nil, fmt.Errorf("查询实时统计指标失败: %w", err) } // 转换为map格式 result := make(map[string]float64) for _, metric := range metrics { if metric.Dimension == "realtime" { result[metric.MetricName] = metric.Value } } return result, nil } // AggregateHourlyMetrics 聚合小时级统计指标 func (s *StatisticsAggregateServiceImpl) AggregateHourlyMetrics(ctx context.Context, date time.Time) error { s.logger.Info("开始聚合小时级统计指标", zap.Time("date", date)) // 获取指定小时的所有实时指标 startTime := date.Truncate(time.Hour) endTime := startTime.Add(time.Hour) // 聚合不同类型的指标 metricTypes := []string{"api_calls", "users", "finance", "products", "certification"} for _, metricType := range metricTypes { err := s.aggregateMetricsByType(ctx, metricType, startTime, endTime, "hourly") if err != nil { s.logger.Error("聚合小时级指标失败", zap.String("metric_type", metricType), zap.Error(err)) return fmt.Errorf("聚合小时级指标失败: %w", err) } } s.logger.Info("小时级统计指标聚合完成", zap.Time("date", date)) return nil } // AggregateDailyMetrics 聚合日级统计指标 func (s *StatisticsAggregateServiceImpl) AggregateDailyMetrics(ctx context.Context, date time.Time) error { s.logger.Info("开始聚合日级统计指标", zap.Time("date", date)) // 获取指定日期的所有小时级指标 startTime := date.Truncate(24 * time.Hour) endTime := startTime.Add(24 * time.Hour) // 聚合不同类型的指标 metricTypes := []string{"api_calls", "users", "finance", "products", "certification"} for _, metricType := range metricTypes { err := s.aggregateMetricsByType(ctx, metricType, startTime, endTime, "daily") if err != nil { s.logger.Error("聚合日级指标失败", zap.String("metric_type", metricType), zap.Error(err)) return fmt.Errorf("聚合日级指标失败: %w", err) } } s.logger.Info("日级统计指标聚合完成", zap.Time("date", date)) return nil } // AggregateWeeklyMetrics 聚合周级统计指标 func (s *StatisticsAggregateServiceImpl) AggregateWeeklyMetrics(ctx context.Context, date time.Time) error { s.logger.Info("开始聚合周级统计指标", zap.Time("date", date)) // 获取指定周的所有日级指标 startTime := date.Truncate(24 * time.Hour) endTime := startTime.Add(7 * 24 * time.Hour) // 聚合不同类型的指标 metricTypes := []string{"api_calls", "users", "finance", "products", "certification"} for _, metricType := range metricTypes { err := s.aggregateMetricsByType(ctx, metricType, startTime, endTime, "weekly") if err != nil { s.logger.Error("聚合周级指标失败", zap.String("metric_type", metricType), zap.Error(err)) return fmt.Errorf("聚合周级指标失败: %w", err) } } s.logger.Info("周级统计指标聚合完成", zap.Time("date", date)) return nil } // AggregateMonthlyMetrics 聚合月级统计指标 func (s *StatisticsAggregateServiceImpl) AggregateMonthlyMetrics(ctx context.Context, date time.Time) error { s.logger.Info("开始聚合月级统计指标", zap.Time("date", date)) // 获取指定月的所有日级指标 startTime := date.Truncate(24 * time.Hour) endTime := startTime.AddDate(0, 1, 0) // 聚合不同类型的指标 metricTypes := []string{"api_calls", "users", "finance", "products", "certification"} for _, metricType := range metricTypes { err := s.aggregateMetricsByType(ctx, metricType, startTime, endTime, "monthly") if err != nil { s.logger.Error("聚合月级指标失败", zap.String("metric_type", metricType), zap.Error(err)) return fmt.Errorf("聚合月级指标失败: %w", err) } } s.logger.Info("月级统计指标聚合完成", zap.Time("date", date)) return nil } // GetMetricsByType 根据类型获取统计指标 func (s *StatisticsAggregateServiceImpl) GetMetricsByType(ctx context.Context, metricType string, startDate, endDate time.Time) ([]*entities.StatisticsMetric, error) { if metricType == "" { return nil, fmt.Errorf("指标类型不能为空") } metrics, err := s.metricRepo.FindByTypeAndDateRange(ctx, metricType, startDate, endDate) if err != nil { s.logger.Error("查询统计指标失败", zap.String("metric_type", metricType), zap.Time("start_date", startDate), zap.Time("end_date", endDate), zap.Error(err)) return nil, fmt.Errorf("查询统计指标失败: %w", err) } return metrics, nil } // GetMetricsByDimension 根据维度获取统计指标 func (s *StatisticsAggregateServiceImpl) GetMetricsByDimension(ctx context.Context, metricType, dimension string, startDate, endDate time.Time) ([]*entities.StatisticsMetric, error) { if metricType == "" { return nil, fmt.Errorf("指标类型不能为空") } if dimension == "" { return nil, fmt.Errorf("统计维度不能为空") } metrics, err := s.metricRepo.FindByTypeDimensionAndDateRange(ctx, metricType, dimension, startDate, endDate) if err != nil { s.logger.Error("查询统计指标失败", zap.String("metric_type", metricType), zap.String("dimension", dimension), zap.Time("start_date", startDate), zap.Time("end_date", endDate), zap.Error(err)) return nil, fmt.Errorf("查询统计指标失败: %w", err) } return metrics, nil } // CalculateGrowthRate 计算增长率 func (s *StatisticsAggregateServiceImpl) CalculateGrowthRate(ctx context.Context, metricType, metricName string, currentPeriod, previousPeriod time.Time) (float64, error) { if metricType == "" || metricName == "" { return 0, fmt.Errorf("指标类型和名称不能为空") } // 获取当前周期的指标值 currentMetrics, err := s.metricRepo.FindByTypeNameAndDateRange(ctx, metricType, metricName, currentPeriod, currentPeriod.Add(24*time.Hour)) if err != nil { return 0, fmt.Errorf("查询当前周期指标失败: %w", err) } // 获取上一周期的指标值 previousMetrics, err := s.metricRepo.FindByTypeNameAndDateRange(ctx, metricType, metricName, previousPeriod, previousPeriod.Add(24*time.Hour)) if err != nil { return 0, fmt.Errorf("查询上一周期指标失败: %w", err) } // 计算总值 var currentValue, previousValue float64 for _, metric := range currentMetrics { currentValue += metric.Value } for _, metric := range previousMetrics { previousValue += metric.Value } // 计算增长率 if previousValue == 0 { if currentValue > 0 { return 100, nil // 从0增长到正数,增长率为100% } return 0, nil // 都是0,增长率为0% } growthRate := ((currentValue - previousValue) / previousValue) * 100 return growthRate, nil } // CalculateTrend 计算趋势 func (s *StatisticsAggregateServiceImpl) CalculateTrend(ctx context.Context, metricType, metricName string, startDate, endDate time.Time) (string, error) { if metricType == "" || metricName == "" { return "", fmt.Errorf("指标类型和名称不能为空") } // 获取时间范围内的指标 metrics, err := s.metricRepo.FindByTypeNameAndDateRange(ctx, metricType, metricName, startDate, endDate) if err != nil { return "", fmt.Errorf("查询指标失败: %w", err) } if len(metrics) < 2 { return "insufficient_data", nil // 数据不足 } // 按时间排序 sortMetricsByDate(metrics) // 计算趋势 firstValue := metrics[0].Value lastValue := metrics[len(metrics)-1].Value if lastValue > firstValue { return "increasing", nil // 上升趋势 } else if lastValue < firstValue { return "decreasing", nil // 下降趋势 } else { return "stable", nil // 稳定趋势 } } // aggregateMetricsByType 按类型聚合指标 func (s *StatisticsAggregateServiceImpl) aggregateMetricsByType(ctx context.Context, metricType string, startTime, endTime time.Time, dimension string) error { // 获取源数据(实时或小时级数据) sourceDimension := "realtime" if dimension == "daily" { sourceDimension = "hourly" } else if dimension == "weekly" || dimension == "monthly" { sourceDimension = "daily" } // 查询源数据 sourceMetrics, err := s.metricRepo.FindByTypeDimensionAndDateRange(ctx, metricType, sourceDimension, startTime, endTime) if err != nil { return fmt.Errorf("查询源数据失败: %w", err) } // 按指标名称分组聚合 metricGroups := make(map[string][]*entities.StatisticsMetric) for _, metric := range sourceMetrics { metricGroups[metric.MetricName] = append(metricGroups[metric.MetricName], metric) } // 聚合每个指标 for metricName, metrics := range metricGroups { var totalValue float64 for _, metric := range metrics { totalValue += metric.Value } // 创建聚合后的指标 aggregatedMetric, err := entities.NewStatisticsMetric(metricType, metricName, dimension, totalValue, startTime) if err != nil { return fmt.Errorf("创建聚合指标失败: %w", err) } // 保存聚合指标 err = s.metricRepo.Save(ctx, aggregatedMetric) if err != nil { return fmt.Errorf("保存聚合指标失败: %w", err) } } return nil } // sortMetricsByDate 按日期排序指标 func sortMetricsByDate(metrics []*entities.StatisticsMetric) { // 简单的冒泡排序 n := len(metrics) for i := 0; i < n-1; i++ { for j := 0; j < n-i-1; j++ { if metrics[j].Date.After(metrics[j+1].Date) { metrics[j], metrics[j+1] = metrics[j+1], metrics[j] } } } }