2744 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			2744 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package statistics
 | ||
| 
 | ||
| import (
 | ||
| 	"context"
 | ||
| 	"fmt"
 | ||
| 	"time"
 | ||
| 
 | ||
| 	"go.uber.org/zap"
 | ||
| 
 | ||
| 	"tyapi-server/internal/domains/statistics/entities"
 | ||
| 	"tyapi-server/internal/domains/statistics/repositories"
 | ||
| 	"tyapi-server/internal/domains/statistics/services"
 | ||
| 
 | ||
| 	// 认证领域
 | ||
| 	certificationEntities "tyapi-server/internal/domains/certification/entities"
 | ||
| 	certificationEnums "tyapi-server/internal/domains/certification/enums"
 | ||
| 	certificationQueries "tyapi-server/internal/domains/certification/repositories/queries"
 | ||
| 
 | ||
| 	// 添加其他领域的仓储接口
 | ||
| 	apiRepos "tyapi-server/internal/domains/api/repositories"
 | ||
| 	certificationRepos "tyapi-server/internal/domains/certification/repositories"
 | ||
| 	financeRepos "tyapi-server/internal/domains/finance/repositories"
 | ||
| 	productRepos "tyapi-server/internal/domains/product/repositories"
 | ||
| 	productQueries "tyapi-server/internal/domains/product/repositories/queries"
 | ||
| 	userRepos "tyapi-server/internal/domains/user/repositories"
 | ||
| )
 | ||
| 
 | ||
| // StatisticsApplicationServiceImpl 统计应用服务实现
 | ||
| type StatisticsApplicationServiceImpl struct {
 | ||
| 	// 领域服务
 | ||
| 	aggregateService   services.StatisticsAggregateService
 | ||
| 	calculationService services.StatisticsCalculationService
 | ||
| 	reportService      services.StatisticsReportService
 | ||
| 
 | ||
| 	// 统计仓储
 | ||
| 	metricRepo    repositories.StatisticsRepository
 | ||
| 	reportRepo    repositories.StatisticsReportRepository
 | ||
| 	dashboardRepo repositories.StatisticsDashboardRepository
 | ||
| 
 | ||
| 	// 其他领域仓储
 | ||
| 	userRepo              userRepos.UserRepository
 | ||
| 	enterpriseInfoRepo    userRepos.EnterpriseInfoRepository
 | ||
| 	apiCallRepo           apiRepos.ApiCallRepository
 | ||
| 	walletTransactionRepo financeRepos.WalletTransactionRepository
 | ||
| 	rechargeRecordRepo    financeRepos.RechargeRecordRepository
 | ||
| 	productRepo           productRepos.ProductRepository
 | ||
| 	certificationRepo     certificationRepos.CertificationQueryRepository
 | ||
| 
 | ||
| 	// 日志
 | ||
| 	logger *zap.Logger
 | ||
| }
 | ||
| 
 | ||
| // NewStatisticsApplicationService 创建统计应用服务
 | ||
| func NewStatisticsApplicationService(
 | ||
| 	aggregateService services.StatisticsAggregateService,
 | ||
| 	calculationService services.StatisticsCalculationService,
 | ||
| 	reportService services.StatisticsReportService,
 | ||
| 	metricRepo repositories.StatisticsRepository,
 | ||
| 	reportRepo repositories.StatisticsReportRepository,
 | ||
| 	dashboardRepo repositories.StatisticsDashboardRepository,
 | ||
| 	userRepo userRepos.UserRepository,
 | ||
| 	enterpriseInfoRepo userRepos.EnterpriseInfoRepository,
 | ||
| 	apiCallRepo apiRepos.ApiCallRepository,
 | ||
| 	walletTransactionRepo financeRepos.WalletTransactionRepository,
 | ||
| 	rechargeRecordRepo financeRepos.RechargeRecordRepository,
 | ||
| 	productRepo productRepos.ProductRepository,
 | ||
| 	certificationRepo certificationRepos.CertificationQueryRepository,
 | ||
| 	logger *zap.Logger,
 | ||
| ) StatisticsApplicationService {
 | ||
| 	return &StatisticsApplicationServiceImpl{
 | ||
| 		aggregateService:      aggregateService,
 | ||
| 		calculationService:    calculationService,
 | ||
| 		reportService:         reportService,
 | ||
| 		metricRepo:            metricRepo,
 | ||
| 		reportRepo:            reportRepo,
 | ||
| 		dashboardRepo:         dashboardRepo,
 | ||
| 		userRepo:              userRepo,
 | ||
| 		enterpriseInfoRepo:    enterpriseInfoRepo,
 | ||
| 		apiCallRepo:           apiCallRepo,
 | ||
| 		walletTransactionRepo: walletTransactionRepo,
 | ||
| 		rechargeRecordRepo:    rechargeRecordRepo,
 | ||
| 		productRepo:           productRepo,
 | ||
| 		certificationRepo:     certificationRepo,
 | ||
| 		logger:                logger,
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // ================ 指标管理 ================
 | ||
| 
 | ||
| // CreateMetric 创建统计指标
 | ||
| func (s *StatisticsApplicationServiceImpl) CreateMetric(ctx context.Context, cmd *CreateMetricCommand) (*CommandResponse, error) {
 | ||
| 	// 验证命令
 | ||
| 	if err := cmd.Validate(); err != nil {
 | ||
| 		s.logger.Error("创建指标命令验证失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "命令验证失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 创建指标实体
 | ||
| 	metric, err := entities.NewStatisticsMetric(
 | ||
| 		cmd.MetricType,
 | ||
| 		cmd.MetricName,
 | ||
| 		cmd.Dimension,
 | ||
| 		cmd.Value,
 | ||
| 		cmd.Date,
 | ||
| 	)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("创建指标实体失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "创建指标失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 保存指标
 | ||
| 	err = s.metricRepo.Save(ctx, metric)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("保存指标失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "保存指标失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	dto := s.convertMetricToDTO(metric)
 | ||
| 
 | ||
| 	s.logger.Info("指标创建成功", zap.String("metric_id", metric.ID))
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "指标创建成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // UpdateMetric 更新统计指标
 | ||
| func (s *StatisticsApplicationServiceImpl) UpdateMetric(ctx context.Context, cmd *UpdateMetricCommand) (*CommandResponse, error) {
 | ||
| 	// 验证命令
 | ||
| 	if err := cmd.Validate(); err != nil {
 | ||
| 		s.logger.Error("更新指标命令验证失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "命令验证失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取指标
 | ||
| 	metric, err := s.metricRepo.FindByID(ctx, cmd.ID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("查询指标失败", zap.String("metric_id", cmd.ID), zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "查询指标失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 更新指标值
 | ||
| 	err = metric.UpdateValue(cmd.Value)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("更新指标值失败", zap.String("metric_id", cmd.ID), zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "更新指标值失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 保存更新
 | ||
| 	err = s.metricRepo.Update(ctx, metric)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("保存指标更新失败", zap.String("metric_id", cmd.ID), zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "保存指标更新失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	dto := s.convertMetricToDTO(metric)
 | ||
| 
 | ||
| 	s.logger.Info("指标更新成功", zap.String("metric_id", metric.ID))
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "指标更新成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // DeleteMetric 删除统计指标
 | ||
| func (s *StatisticsApplicationServiceImpl) DeleteMetric(ctx context.Context, cmd *DeleteMetricCommand) (*CommandResponse, error) {
 | ||
| 	// 验证命令
 | ||
| 	if cmd.ID == "" {
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "指标ID不能为空",
 | ||
| 			Error:   "指标ID不能为空",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 删除指标
 | ||
| 	err := s.metricRepo.Delete(ctx, cmd.ID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("删除指标失败", zap.String("metric_id", cmd.ID), zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "删除指标失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	s.logger.Info("指标删除成功", zap.String("metric_id", cmd.ID))
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "指标删除成功",
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetMetric 获取单个指标
 | ||
| func (s *StatisticsApplicationServiceImpl) GetMetric(ctx context.Context, query *GetMetricQuery) (*QueryResponse, error) {
 | ||
| 	// 验证查询
 | ||
| 	if query.MetricID == "" {
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "指标ID不能为空",
 | ||
| 			Error:   "指标ID不能为空",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 查询指标
 | ||
| 	metric, err := s.metricRepo.FindByID(ctx, query.MetricID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("查询指标失败", zap.String("metric_id", query.MetricID), zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "查询指标失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	dto := s.convertMetricToDTO(metric)
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "查询成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetMetrics 获取指标列表
 | ||
| func (s *StatisticsApplicationServiceImpl) GetMetrics(ctx context.Context, query *GetMetricsQuery) (*ListResponse, error) {
 | ||
| 	// 设置默认值
 | ||
| 	if query.Limit <= 0 {
 | ||
| 		query.Limit = 20
 | ||
| 	}
 | ||
| 	if query.Limit > 1000 {
 | ||
| 		query.Limit = 1000
 | ||
| 	}
 | ||
| 
 | ||
| 	// 查询指标
 | ||
| 	var metrics []*entities.StatisticsMetric
 | ||
| 	var err error
 | ||
| 
 | ||
| 	if query.MetricType != "" && !query.StartDate.IsZero() && !query.EndDate.IsZero() {
 | ||
| 		metrics, err = s.metricRepo.FindByTypeAndDateRange(ctx, query.MetricType, query.StartDate, query.EndDate)
 | ||
| 	} else if query.MetricType != "" {
 | ||
| 		metrics, err = s.metricRepo.FindByType(ctx, query.MetricType, query.Limit, query.Offset)
 | ||
| 	} else {
 | ||
| 		return &ListResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "查询条件不完整",
 | ||
| 			Data:    ListDataDTO{},
 | ||
| 			Error:   "查询条件不完整",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("查询指标列表失败", zap.Error(err))
 | ||
| 		return &ListResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "查询指标列表失败",
 | ||
| 			Data:    ListDataDTO{},
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	var dtos []interface{}
 | ||
| 	for _, metric := range metrics {
 | ||
| 		dtos = append(dtos, s.convertMetricToDTO(metric))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 计算分页信息
 | ||
| 	total := int64(len(metrics))
 | ||
| 
 | ||
| 	return &ListResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "查询成功",
 | ||
| 		Data: ListDataDTO{
 | ||
| 			Total: total,
 | ||
| 			Page:  query.Offset/query.Limit + 1,
 | ||
| 			Size:  query.Limit,
 | ||
| 			Items: dtos,
 | ||
| 		},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 实时统计 ================
 | ||
| 
 | ||
| // GetRealtimeMetrics 获取实时指标
 | ||
| func (s *StatisticsApplicationServiceImpl) GetRealtimeMetrics(ctx context.Context, query *GetRealtimeMetricsQuery) (*QueryResponse, error) {
 | ||
| 	// 验证查询
 | ||
| 	if query.MetricType == "" {
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "指标类型不能为空",
 | ||
| 			Error:   "指标类型不能为空",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取实时指标
 | ||
| 	metrics, err := s.aggregateService.GetRealtimeMetrics(ctx, query.MetricType)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取实时指标失败", zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "获取实时指标失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 构建响应
 | ||
| 	dto := &RealtimeMetricsDTO{
 | ||
| 		MetricType: query.MetricType,
 | ||
| 		Metrics:    metrics,
 | ||
| 		Timestamp:  time.Now(),
 | ||
| 		Metadata: map[string]interface{}{
 | ||
| 			"time_range": query.TimeRange,
 | ||
| 			"dimension":  query.Dimension,
 | ||
| 		},
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取实时指标成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // UpdateRealtimeMetric 更新实时指标
 | ||
| func (s *StatisticsApplicationServiceImpl) UpdateRealtimeMetric(ctx context.Context, metricType, metricName string, value float64) error {
 | ||
| 	return s.aggregateService.UpdateRealtimeMetric(ctx, metricType, metricName, value)
 | ||
| }
 | ||
| 
 | ||
| // ================ 历史统计 ================
 | ||
| 
 | ||
| // GetHistoricalMetrics 获取历史指标
 | ||
| func (s *StatisticsApplicationServiceImpl) GetHistoricalMetrics(ctx context.Context, query *GetHistoricalMetricsQuery) (*QueryResponse, error) {
 | ||
| 	// 验证查询
 | ||
| 	if query.MetricType == "" {
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "指标类型不能为空",
 | ||
| 			Error:   "指标类型不能为空",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取历史指标
 | ||
| 	var metrics []*entities.StatisticsMetric
 | ||
| 	var err error
 | ||
| 
 | ||
| 	if query.MetricName != "" {
 | ||
| 		metrics, err = s.metricRepo.FindByTypeNameAndDateRange(ctx, query.MetricType, query.MetricName, query.StartDate, query.EndDate)
 | ||
| 	} else {
 | ||
| 		metrics, err = s.metricRepo.FindByTypeAndDateRange(ctx, query.MetricType, query.StartDate, query.EndDate)
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取历史指标失败", zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "获取历史指标失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	var dataPoints []DataPointDTO
 | ||
| 	var total, sum float64
 | ||
| 	var count int64
 | ||
| 	var max, min float64
 | ||
| 
 | ||
| 	if len(metrics) > 0 {
 | ||
| 		max = metrics[0].Value
 | ||
| 		min = metrics[0].Value
 | ||
| 	}
 | ||
| 
 | ||
| 	for _, metric := range metrics {
 | ||
| 		dataPoints = append(dataPoints, DataPointDTO{
 | ||
| 			Date:  metric.Date,
 | ||
| 			Value: metric.Value,
 | ||
| 			Label: metric.MetricName,
 | ||
| 		})
 | ||
| 		total += metric.Value
 | ||
| 		sum += metric.Value
 | ||
| 		count++
 | ||
| 		if metric.Value > max {
 | ||
| 			max = metric.Value
 | ||
| 		}
 | ||
| 		if metric.Value < min {
 | ||
| 			min = metric.Value
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 计算汇总信息
 | ||
| 	summary := MetricsSummaryDTO{
 | ||
| 		Total:   total,
 | ||
| 		Average: sum / float64(count),
 | ||
| 		Max:     max,
 | ||
| 		Min:     min,
 | ||
| 		Count:   count,
 | ||
| 	}
 | ||
| 
 | ||
| 	// 构建响应
 | ||
| 	dto := &HistoricalMetricsDTO{
 | ||
| 		MetricType: query.MetricType,
 | ||
| 		MetricName: query.MetricName,
 | ||
| 		Dimension:  query.Dimension,
 | ||
| 		DataPoints: dataPoints,
 | ||
| 		Summary:    summary,
 | ||
| 		Metadata: map[string]interface{}{
 | ||
| 			"period":       query.Period,
 | ||
| 			"aggregate_by": query.AggregateBy,
 | ||
| 			"group_by":     query.GroupBy,
 | ||
| 		},
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取历史指标成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // AggregateMetrics 聚合指标
 | ||
| func (s *StatisticsApplicationServiceImpl) AggregateMetrics(ctx context.Context, metricType, dimension string, startDate, endDate time.Time) error {
 | ||
| 	return s.aggregateService.AggregateHourlyMetrics(ctx, startDate)
 | ||
| }
 | ||
| 
 | ||
| // ================ 仪表板管理 ================
 | ||
| 
 | ||
| // CreateDashboard 创建仪表板
 | ||
| func (s *StatisticsApplicationServiceImpl) CreateDashboard(ctx context.Context, cmd *CreateDashboardCommand) (*CommandResponse, error) {
 | ||
| 	// 验证命令
 | ||
| 	if err := cmd.Validate(); err != nil {
 | ||
| 		s.logger.Error("创建仪表板命令验证失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "命令验证失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 创建仪表板实体
 | ||
| 	dashboard, err := entities.NewStatisticsDashboard(
 | ||
| 		cmd.Name,
 | ||
| 		cmd.Description,
 | ||
| 		cmd.UserRole,
 | ||
| 		cmd.CreatedBy,
 | ||
| 	)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("创建仪表板实体失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "创建仪表板失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 设置配置
 | ||
| 	if cmd.Layout != "" {
 | ||
| 		dashboard.UpdateLayout(cmd.Layout)
 | ||
| 	}
 | ||
| 	if cmd.Widgets != "" {
 | ||
| 		dashboard.UpdateWidgets(cmd.Widgets)
 | ||
| 	}
 | ||
| 	if cmd.Settings != "" {
 | ||
| 		dashboard.UpdateSettings(cmd.Settings)
 | ||
| 	}
 | ||
| 	if cmd.RefreshInterval > 0 {
 | ||
| 		dashboard.UpdateRefreshInterval(cmd.RefreshInterval)
 | ||
| 	}
 | ||
| 	if cmd.AccessLevel != "" {
 | ||
| 		dashboard.AccessLevel = cmd.AccessLevel
 | ||
| 	}
 | ||
| 
 | ||
| 	// 保存仪表板
 | ||
| 	err = s.dashboardRepo.Save(ctx, dashboard)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("保存仪表板失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "保存仪表板失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	dto := s.convertDashboardToDTO(dashboard)
 | ||
| 
 | ||
| 	s.logger.Info("仪表板创建成功", zap.String("dashboard_id", dashboard.ID))
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "仪表板创建成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetDashboardData 获取仪表板数据
 | ||
| func (s *StatisticsApplicationServiceImpl) GetDashboardData(ctx context.Context, query *GetDashboardDataQuery) (*QueryResponse, error) {
 | ||
| 	// 验证查询
 | ||
| 	if query.UserRole == "" {
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "用户角色不能为空",
 | ||
| 			Error:   "用户角色不能为空",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 设置默认时间范围
 | ||
| 	if query.StartDate.IsZero() || query.EndDate.IsZero() {
 | ||
| 		now := time.Now()
 | ||
| 		switch query.Period {
 | ||
| 		case "today":
 | ||
| 			query.StartDate = now.Truncate(24 * time.Hour)
 | ||
| 			query.EndDate = query.StartDate.Add(24 * time.Hour)
 | ||
| 		case "week":
 | ||
| 			query.StartDate = now.Truncate(24*time.Hour).AddDate(0, 0, -7)
 | ||
| 			query.EndDate = now
 | ||
| 		case "month":
 | ||
| 			query.StartDate = now.Truncate(24*time.Hour).AddDate(0, 0, -30)
 | ||
| 			query.EndDate = now
 | ||
| 		default:
 | ||
| 			query.StartDate = now.Truncate(24 * time.Hour)
 | ||
| 			query.EndDate = query.StartDate.Add(24 * time.Hour)
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 构建仪表板数据
 | ||
| 	dto := &DashboardDataDTO{}
 | ||
| 
 | ||
| 	// API调用统计
 | ||
| 	apiCallsTotal, _ := s.calculationService.CalculateTotal(ctx, "api_calls", "total_count", query.StartDate, query.EndDate)
 | ||
| 	apiCallsSuccess, _ := s.calculationService.CalculateTotal(ctx, "api_calls", "success_count", query.StartDate, query.EndDate)
 | ||
| 	apiCallsFailed, _ := s.calculationService.CalculateTotal(ctx, "api_calls", "failed_count", query.StartDate, query.EndDate)
 | ||
| 	avgResponseTime, _ := s.calculationService.CalculateAverage(ctx, "api_calls", "response_time", query.StartDate, query.EndDate)
 | ||
| 
 | ||
| 	dto.APICalls.TotalCount = int64(apiCallsTotal)
 | ||
| 	dto.APICalls.SuccessCount = int64(apiCallsSuccess)
 | ||
| 	dto.APICalls.FailedCount = int64(apiCallsFailed)
 | ||
| 	dto.APICalls.SuccessRate = s.calculateRate(apiCallsSuccess, apiCallsTotal)
 | ||
| 	dto.APICalls.AvgResponseTime = avgResponseTime
 | ||
| 
 | ||
| 	// 用户统计
 | ||
| 	usersTotal, _ := s.calculationService.CalculateTotal(ctx, "users", "total_count", query.StartDate, query.EndDate)
 | ||
| 	usersCertified, _ := s.calculationService.CalculateTotal(ctx, "users", "certified_count", query.StartDate, query.EndDate)
 | ||
| 	usersActive, _ := s.calculationService.CalculateTotal(ctx, "users", "active_count", query.StartDate, query.EndDate)
 | ||
| 
 | ||
| 	dto.Users.TotalCount = int64(usersTotal)
 | ||
| 	dto.Users.CertifiedCount = int64(usersCertified)
 | ||
| 	dto.Users.ActiveCount = int64(usersActive)
 | ||
| 	dto.Users.CertificationRate = s.calculateRate(usersCertified, usersTotal)
 | ||
| 	dto.Users.RetentionRate = s.calculateRate(usersActive, usersTotal)
 | ||
| 
 | ||
| 	// 财务统计
 | ||
| 	financeTotal, _ := s.calculationService.CalculateTotal(ctx, "finance", "total_amount", query.StartDate, query.EndDate)
 | ||
| 	rechargeAmount, _ := s.calculationService.CalculateTotal(ctx, "finance", "recharge_amount", query.StartDate, query.EndDate)
 | ||
| 	deductAmount, _ := s.calculationService.CalculateTotal(ctx, "finance", "deduct_amount", query.StartDate, query.EndDate)
 | ||
| 
 | ||
| 	dto.Finance.TotalAmount = financeTotal
 | ||
| 	dto.Finance.RechargeAmount = rechargeAmount
 | ||
| 	dto.Finance.DeductAmount = deductAmount
 | ||
| 	dto.Finance.NetAmount = rechargeAmount - deductAmount
 | ||
| 
 | ||
| 	// 设置时间信息
 | ||
| 	dto.Period.StartDate = query.StartDate.Format("2006-01-02")
 | ||
| 	dto.Period.EndDate = query.EndDate.Format("2006-01-02")
 | ||
| 	dto.Period.Period = query.Period
 | ||
| 
 | ||
| 	// 设置元数据
 | ||
| 	dto.Metadata.GeneratedAt = time.Now().Format("2006-01-02 15:04:05")
 | ||
| 	dto.Metadata.UserRole = query.UserRole
 | ||
| 	dto.Metadata.DataVersion = "1.0"
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取仪表板数据成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 报告管理 ================
 | ||
| 
 | ||
| // GenerateReport 生成报告
 | ||
| func (s *StatisticsApplicationServiceImpl) GenerateReport(ctx context.Context, cmd *GenerateReportCommand) (*CommandResponse, error) {
 | ||
| 	// 验证命令
 | ||
| 	if err := cmd.Validate(); err != nil {
 | ||
| 		s.logger.Error("生成报告命令验证失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "命令验证失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 生成报告
 | ||
| 	var report *entities.StatisticsReport
 | ||
| 	var err error
 | ||
| 
 | ||
| 	switch cmd.ReportType {
 | ||
| 	case "dashboard":
 | ||
| 		report, err = s.reportService.GenerateDashboardReport(ctx, cmd.UserRole, cmd.Period)
 | ||
| 	case "summary":
 | ||
| 		report, err = s.reportService.GenerateSummaryReport(ctx, cmd.Period, cmd.StartDate, cmd.EndDate)
 | ||
| 	case "detailed":
 | ||
| 		report, err = s.reportService.GenerateDetailedReport(ctx, cmd.Title, cmd.StartDate, cmd.EndDate, cmd.Filters)
 | ||
| 	default:
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "不支持的报告类型",
 | ||
| 			Error:   "不支持的报告类型",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("生成报告失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "生成报告失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	dto := s.convertReportToDTO(report)
 | ||
| 
 | ||
| 	s.logger.Info("报告生成成功", zap.String("report_id", report.ID))
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "报告生成成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 定时任务 ================
 | ||
| 
 | ||
| // ProcessHourlyAggregation 处理小时级聚合
 | ||
| func (s *StatisticsApplicationServiceImpl) ProcessHourlyAggregation(ctx context.Context, date time.Time) error {
 | ||
| 	return s.aggregateService.AggregateHourlyMetrics(ctx, date)
 | ||
| }
 | ||
| 
 | ||
| // ProcessDailyAggregation 处理日级聚合
 | ||
| func (s *StatisticsApplicationServiceImpl) ProcessDailyAggregation(ctx context.Context, date time.Time) error {
 | ||
| 	return s.aggregateService.AggregateDailyMetrics(ctx, date)
 | ||
| }
 | ||
| 
 | ||
| // ProcessWeeklyAggregation 处理周级聚合
 | ||
| func (s *StatisticsApplicationServiceImpl) ProcessWeeklyAggregation(ctx context.Context, date time.Time) error {
 | ||
| 	return s.aggregateService.AggregateWeeklyMetrics(ctx, date)
 | ||
| }
 | ||
| 
 | ||
| // ProcessMonthlyAggregation 处理月级聚合
 | ||
| func (s *StatisticsApplicationServiceImpl) ProcessMonthlyAggregation(ctx context.Context, date time.Time) error {
 | ||
| 	return s.aggregateService.AggregateMonthlyMetrics(ctx, date)
 | ||
| }
 | ||
| 
 | ||
| // CleanupExpiredData 清理过期数据
 | ||
| func (s *StatisticsApplicationServiceImpl) CleanupExpiredData(ctx context.Context) error {
 | ||
| 	return s.reportService.CleanupExpiredReports(ctx)
 | ||
| }
 | ||
| 
 | ||
| // ================ 辅助方法 ================
 | ||
| 
 | ||
| // convertMetricToDTO 转换指标实体为DTO
 | ||
| func (s *StatisticsApplicationServiceImpl) convertMetricToDTO(metric *entities.StatisticsMetric) *StatisticsMetricDTO {
 | ||
| 	return &StatisticsMetricDTO{
 | ||
| 		ID:         metric.ID,
 | ||
| 		MetricType: metric.MetricType,
 | ||
| 		MetricName: metric.MetricName,
 | ||
| 		Dimension:  metric.Dimension,
 | ||
| 		Value:      metric.Value,
 | ||
| 		Metadata:   metric.Metadata,
 | ||
| 		Date:       metric.Date,
 | ||
| 		CreatedAt:  metric.CreatedAt,
 | ||
| 		UpdatedAt:  metric.UpdatedAt,
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // convertReportToDTO 转换报告实体为DTO
 | ||
| func (s *StatisticsApplicationServiceImpl) convertReportToDTO(report *entities.StatisticsReport) *StatisticsReportDTO {
 | ||
| 	return &StatisticsReportDTO{
 | ||
| 		ID:          report.ID,
 | ||
| 		ReportType:  report.ReportType,
 | ||
| 		Title:       report.Title,
 | ||
| 		Content:     report.Content,
 | ||
| 		Period:      report.Period,
 | ||
| 		UserRole:    report.UserRole,
 | ||
| 		Status:      report.Status,
 | ||
| 		GeneratedBy: report.GeneratedBy,
 | ||
| 		GeneratedAt: report.GeneratedAt,
 | ||
| 		ExpiresAt:   report.ExpiresAt,
 | ||
| 		CreatedAt:   report.CreatedAt,
 | ||
| 		UpdatedAt:   report.UpdatedAt,
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // convertDashboardToDTO 转换仪表板实体为DTO
 | ||
| func (s *StatisticsApplicationServiceImpl) convertDashboardToDTO(dashboard *entities.StatisticsDashboard) *StatisticsDashboardDTO {
 | ||
| 	return &StatisticsDashboardDTO{
 | ||
| 		ID:              dashboard.ID,
 | ||
| 		Name:            dashboard.Name,
 | ||
| 		Description:     dashboard.Description,
 | ||
| 		UserRole:        dashboard.UserRole,
 | ||
| 		IsDefault:       dashboard.IsDefault,
 | ||
| 		IsActive:        dashboard.IsActive,
 | ||
| 		Layout:          dashboard.Layout,
 | ||
| 		Widgets:         dashboard.Widgets,
 | ||
| 		Settings:        dashboard.Settings,
 | ||
| 		RefreshInterval: dashboard.RefreshInterval,
 | ||
| 		CreatedBy:       dashboard.CreatedBy,
 | ||
| 		AccessLevel:     dashboard.AccessLevel,
 | ||
| 		CreatedAt:       dashboard.CreatedAt,
 | ||
| 		UpdatedAt:       dashboard.UpdatedAt,
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // calculateRate 计算比率
 | ||
| func (s *StatisticsApplicationServiceImpl) calculateRate(numerator, denominator float64) float64 {
 | ||
| 	if denominator == 0 {
 | ||
| 		return 0
 | ||
| 	}
 | ||
| 	return (numerator / denominator) * 100
 | ||
| }
 | ||
| 
 | ||
| // ================ 其他方法的简化实现 ================
 | ||
| 
 | ||
| // UpdateDashboard 更新仪表板
 | ||
| func (s *StatisticsApplicationServiceImpl) UpdateDashboard(ctx context.Context, cmd *UpdateDashboardCommand) (*CommandResponse, error) {
 | ||
| 	// 验证命令
 | ||
| 	if err := cmd.Validate(); err != nil {
 | ||
| 		s.logger.Error("更新仪表板命令验证失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "命令验证失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取仪表板
 | ||
| 	dashboard, err := s.dashboardRepo.FindByID(ctx, cmd.ID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("查询仪表板失败", zap.String("dashboard_id", cmd.ID), zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "查询仪表板失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 更新仪表板信息
 | ||
| 	if cmd.Name != "" {
 | ||
| 		// 直接设置字段值,因为实体没有UpdateName方法
 | ||
| 		dashboard.Name = cmd.Name
 | ||
| 	}
 | ||
| 	if cmd.Description != "" {
 | ||
| 		// 直接设置字段值,因为实体没有UpdateDescription方法
 | ||
| 		dashboard.Description = cmd.Description
 | ||
| 	}
 | ||
| 	if cmd.Layout != "" {
 | ||
| 		dashboard.UpdateLayout(cmd.Layout)
 | ||
| 	}
 | ||
| 	if cmd.Widgets != "" {
 | ||
| 		dashboard.UpdateWidgets(cmd.Widgets)
 | ||
| 	}
 | ||
| 	if cmd.Settings != "" {
 | ||
| 		dashboard.UpdateSettings(cmd.Settings)
 | ||
| 	}
 | ||
| 	if cmd.RefreshInterval > 0 {
 | ||
| 		dashboard.UpdateRefreshInterval(cmd.RefreshInterval)
 | ||
| 	}
 | ||
| 	if cmd.AccessLevel != "" {
 | ||
| 		dashboard.AccessLevel = cmd.AccessLevel
 | ||
| 	}
 | ||
| 
 | ||
| 	// 保存更新
 | ||
| 	err = s.dashboardRepo.Update(ctx, dashboard)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("保存仪表板更新失败", zap.String("dashboard_id", cmd.ID), zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "保存仪表板更新失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	dto := s.convertDashboardToDTO(dashboard)
 | ||
| 
 | ||
| 	s.logger.Info("仪表板更新成功", zap.String("dashboard_id", cmd.ID))
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "仪表板更新成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // DeleteDashboard 删除仪表板
 | ||
| func (s *StatisticsApplicationServiceImpl) DeleteDashboard(ctx context.Context, cmd *DeleteDashboardCommand) (*CommandResponse, error) {
 | ||
| 	// 验证命令
 | ||
| 	if cmd.DashboardID == "" {
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "仪表板ID不能为空",
 | ||
| 			Error:   "仪表板ID不能为空",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 检查仪表板是否存在
 | ||
| 	_, err := s.dashboardRepo.FindByID(ctx, cmd.DashboardID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("查询仪表板失败", zap.String("dashboard_id", cmd.DashboardID), zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "仪表板不存在",
 | ||
| 			Error:   "仪表板不存在",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 删除仪表板
 | ||
| 	err = s.dashboardRepo.Delete(ctx, cmd.DashboardID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("删除仪表板失败", zap.String("dashboard_id", cmd.DashboardID), zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "删除仪表板失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	s.logger.Info("仪表板删除成功", zap.String("dashboard_id", cmd.DashboardID))
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "仪表板删除成功",
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetDashboard 获取单个仪表板
 | ||
| func (s *StatisticsApplicationServiceImpl) GetDashboard(ctx context.Context, query *GetDashboardQuery) (*QueryResponse, error) {
 | ||
| 	// 验证查询
 | ||
| 	if query.DashboardID == "" {
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "仪表板ID不能为空",
 | ||
| 			Error:   "仪表板ID不能为空",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 查询仪表板
 | ||
| 	dashboard, err := s.dashboardRepo.FindByID(ctx, query.DashboardID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("查询仪表板失败", zap.String("dashboard_id", query.DashboardID), zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "查询仪表板失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	dto := s.convertDashboardToDTO(dashboard)
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "查询成功",
 | ||
| 		Data:    dto,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetDashboards 获取仪表板列表
 | ||
| func (s *StatisticsApplicationServiceImpl) GetDashboards(ctx context.Context, query *GetDashboardsQuery) (*ListResponse, error) {
 | ||
| 	// 设置默认值
 | ||
| 	if query.Limit <= 0 {
 | ||
| 		query.Limit = 20
 | ||
| 	}
 | ||
| 	if query.Limit > 1000 {
 | ||
| 		query.Limit = 1000
 | ||
| 	}
 | ||
| 
 | ||
| 	// 查询仪表板列表
 | ||
| 	var dashboards []*entities.StatisticsDashboard
 | ||
| 	var err error
 | ||
| 
 | ||
| 	if query.UserRole != "" {
 | ||
| 		dashboards, err = s.dashboardRepo.FindByUserRole(ctx, query.UserRole, query.Limit, query.Offset)
 | ||
| 	} else if query.CreatedBy != "" {
 | ||
| 		dashboards, err = s.dashboardRepo.FindByUser(ctx, query.CreatedBy, query.Limit, query.Offset)
 | ||
| 	} else {
 | ||
| 		// 如果没有指定条件,返回空列表
 | ||
| 		dashboards = []*entities.StatisticsDashboard{}
 | ||
| 		err = nil
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("查询仪表板列表失败", zap.Error(err))
 | ||
| 		return &ListResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "查询仪表板列表失败",
 | ||
| 			Data:    ListDataDTO{},
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 转换为DTO
 | ||
| 	var dtos []interface{}
 | ||
| 	for _, dashboard := range dashboards {
 | ||
| 		dtos = append(dtos, s.convertDashboardToDTO(dashboard))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 计算分页信息
 | ||
| 	total := int64(len(dashboards))
 | ||
| 
 | ||
| 	return &ListResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "查询成功",
 | ||
| 		Data: ListDataDTO{
 | ||
| 			Total: total,
 | ||
| 			Page:  query.Offset/query.Limit + 1,
 | ||
| 			Size:  query.Limit,
 | ||
| 			Items: dtos,
 | ||
| 		},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // SetDefaultDashboard 设置默认仪表板
 | ||
| func (s *StatisticsApplicationServiceImpl) SetDefaultDashboard(ctx context.Context, cmd *SetDefaultDashboardCommand) (*CommandResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "设置默认仪表板成功",
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ActivateDashboard 激活仪表板
 | ||
| func (s *StatisticsApplicationServiceImpl) ActivateDashboard(ctx context.Context, cmd *ActivateDashboardCommand) (*CommandResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "激活仪表板成功",
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // DeactivateDashboard 停用仪表板
 | ||
| func (s *StatisticsApplicationServiceImpl) DeactivateDashboard(ctx context.Context, cmd *DeactivateDashboardCommand) (*CommandResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "停用仪表板成功",
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetReport 获取单个报告
 | ||
| func (s *StatisticsApplicationServiceImpl) GetReport(ctx context.Context, query *GetReportQuery) (*QueryResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "查询成功",
 | ||
| 		Data:    map[string]interface{}{},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetReports 获取报告列表
 | ||
| func (s *StatisticsApplicationServiceImpl) GetReports(ctx context.Context, query *GetReportsQuery) (*ListResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &ListResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "查询成功",
 | ||
| 		Data: ListDataDTO{
 | ||
| 			Total: 0,
 | ||
| 			Page:  1,
 | ||
| 			Size:  query.Limit,
 | ||
| 			Items: []interface{}{},
 | ||
| 		},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // DeleteReport 删除报告
 | ||
| func (s *StatisticsApplicationServiceImpl) DeleteReport(ctx context.Context, reportID string) (*CommandResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "报告删除成功",
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // CalculateGrowthRate 计算增长率
 | ||
| func (s *StatisticsApplicationServiceImpl) CalculateGrowthRate(ctx context.Context, query *CalculateGrowthRateQuery) (*QueryResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "计算增长率成功",
 | ||
| 		Data:    map[string]interface{}{"growth_rate": 0.0},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // CalculateTrend 计算趋势
 | ||
| func (s *StatisticsApplicationServiceImpl) CalculateTrend(ctx context.Context, query *CalculateTrendQuery) (*QueryResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "计算趋势成功",
 | ||
| 		Data:    map[string]interface{}{"trend": "stable"},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // CalculateCorrelation 计算相关性
 | ||
| func (s *StatisticsApplicationServiceImpl) CalculateCorrelation(ctx context.Context, query *CalculateCorrelationQuery) (*QueryResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "计算相关性成功",
 | ||
| 		Data:    map[string]interface{}{"correlation": 0.0},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // CalculateMovingAverage 计算移动平均
 | ||
| func (s *StatisticsApplicationServiceImpl) CalculateMovingAverage(ctx context.Context, query *CalculateMovingAverageQuery) (*QueryResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "计算移动平均成功",
 | ||
| 		Data:    map[string]interface{}{"moving_averages": []float64{}},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // CalculateSeasonality 计算季节性
 | ||
| func (s *StatisticsApplicationServiceImpl) CalculateSeasonality(ctx context.Context, query *CalculateSeasonalityQuery) (*QueryResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "计算季节性成功",
 | ||
| 		Data:    map[string]interface{}{"seasonality": map[string]float64{}},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ExportData 导出数据
 | ||
| func (s *StatisticsApplicationServiceImpl) ExportData(ctx context.Context, cmd *ExportDataCommand) (*CommandResponse, error) {
 | ||
| 	// 简化实现,实际应该完整实现
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "数据导出成功",
 | ||
| 		Data:    map[string]interface{}{"download_url": ""},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 管理员专用方法 ================
 | ||
| 
 | ||
| // AdminGetSystemStatistics 管理员获取系统统计 - 简化版
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetSystemStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error) {
 | ||
| 	// 解析时间参数
 | ||
| 	var startTime, endTime time.Time
 | ||
| 	var err error
 | ||
| 
 | ||
| 	if startDate != "" {
 | ||
| 		startTime, err = time.Parse("2006-01-02", startDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("开始日期格式错误", zap.Error(err))
 | ||
| 			return &QueryResponse{
 | ||
| 				Success: false,
 | ||
| 				Message: "开始日期格式错误",
 | ||
| 				Error:   err.Error(),
 | ||
| 			}, nil
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	if endDate != "" {
 | ||
| 		endTime, err = time.Parse("2006-01-02", endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("结束日期格式错误", zap.Error(err))
 | ||
| 			return &QueryResponse{
 | ||
| 				Success: false,
 | ||
| 				Message: "结束日期格式错误",
 | ||
| 				Error:   err.Error(),
 | ||
| 			}, nil
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取系统统计数据,传递时间参数
 | ||
| 	systemStats, err := s.getAdminSystemStats(ctx, period, startTime, endTime)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取系统统计失败", zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "获取系统统计失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加查询参数
 | ||
| 	systemStats["period"] = period
 | ||
| 	systemStats["start_date"] = startDate
 | ||
| 	systemStats["end_date"] = endDate
 | ||
| 
 | ||
| 	s.logger.Info("管理员获取系统统计", zap.String("period", period), zap.String("start_date", startDate), zap.String("end_date", endDate))
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取系统统计成功",
 | ||
| 		Data:    systemStats,
 | ||
| 		Meta: map[string]interface{}{
 | ||
| 			"generated_at": time.Now(),
 | ||
| 			"period_type":  period,
 | ||
| 		},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // AdminTriggerAggregation 管理员触发数据聚合
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminTriggerAggregation(ctx context.Context, cmd *TriggerAggregationCommand) (*CommandResponse, error) {
 | ||
| 	// 验证命令
 | ||
| 	if err := cmd.Validate(); err != nil {
 | ||
| 		s.logger.Error("触发聚合命令验证失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "命令验证失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 根据周期类型执行不同的聚合任务
 | ||
| 	var err error
 | ||
| 	switch cmd.Period {
 | ||
| 	case "hourly":
 | ||
| 		err = s.ProcessHourlyAggregation(ctx, cmd.StartDate)
 | ||
| 	case "daily":
 | ||
| 		err = s.ProcessDailyAggregation(ctx, cmd.StartDate)
 | ||
| 	case "weekly":
 | ||
| 		err = s.ProcessWeeklyAggregation(ctx, cmd.StartDate)
 | ||
| 	case "monthly":
 | ||
| 		err = s.ProcessMonthlyAggregation(ctx, cmd.StartDate)
 | ||
| 	default:
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "不支持的聚合周期",
 | ||
| 			Error:   "不支持的聚合周期",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("触发聚合失败", zap.Error(err))
 | ||
| 		return &CommandResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "触发聚合失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	s.logger.Info("管理员触发聚合成功",
 | ||
| 		zap.String("metric_type", cmd.MetricType),
 | ||
| 		zap.String("period", cmd.Period),
 | ||
| 		zap.String("triggered_by", cmd.TriggeredBy))
 | ||
| 
 | ||
| 	return &CommandResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "触发数据聚合成功",
 | ||
| 		Data: map[string]interface{}{
 | ||
| 			"metric_type": cmd.MetricType,
 | ||
| 			"period":      cmd.Period,
 | ||
| 			"start_date":  cmd.StartDate,
 | ||
| 			"end_date":    cmd.EndDate,
 | ||
| 			"force":       cmd.Force,
 | ||
| 		},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 公开和用户统计方法 ================
 | ||
| 
 | ||
| // GetPublicStatistics 获取公开统计信息
 | ||
| func (s *StatisticsApplicationServiceImpl) GetPublicStatistics(ctx context.Context) (*QueryResponse, error) {
 | ||
| 	// 获取公开的统计信息
 | ||
| 	publicStats := map[string]interface{}{
 | ||
| 		"total_users":      0,
 | ||
| 		"total_products":   0,
 | ||
| 		"total_categories": 0,
 | ||
| 		"total_api_calls":  0,
 | ||
| 		"system_uptime":    "0天",
 | ||
| 		"last_updated":     time.Now(),
 | ||
| 	}
 | ||
| 
 | ||
| 	// 实际实现中应该查询数据库获取真实数据
 | ||
| 	// 这里暂时返回模拟数据
 | ||
| 	s.logger.Info("获取公开统计信息")
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取公开统计信息成功",
 | ||
| 		Data:    publicStats,
 | ||
| 		Meta: map[string]interface{}{
 | ||
| 			"generated_at": time.Now(),
 | ||
| 			"cache_ttl":    300, // 5分钟缓存
 | ||
| 		},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetUserStatistics 获取用户统计信息
 | ||
| func (s *StatisticsApplicationServiceImpl) GetUserStatistics(ctx context.Context, userID string) (*QueryResponse, error) {
 | ||
| 	// 验证用户ID
 | ||
| 	if userID == "" {
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "用户ID不能为空",
 | ||
| 			Error:   "用户ID不能为空",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取用户API调用统计
 | ||
| 	apiCalls, err := s.getUserApiCallsStats(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户API调用统计失败", zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "获取API调用统计失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取用户消费统计
 | ||
| 	consumption, err := s.getUserConsumptionStats(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户消费统计失败", zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "获取消费统计失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取用户充值统计
 | ||
| 	recharge, err := s.getUserRechargeStats(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户充值统计失败", zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "获取充值统计失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 组装用户统计数据
 | ||
| 	userStats := map[string]interface{}{
 | ||
| 		"user_id":     userID,
 | ||
| 		"api_calls":   apiCalls,
 | ||
| 		"consumption": consumption,
 | ||
| 		"recharge":    recharge,
 | ||
| 		"summary": map[string]interface{}{
 | ||
| 			"total_calls":     apiCalls["total_calls"],
 | ||
| 			"total_consumed":  consumption["total_amount"],
 | ||
| 			"total_recharged": recharge["total_amount"],
 | ||
| 			"balance":         recharge["total_amount"].(float64) - consumption["total_amount"].(float64),
 | ||
| 		},
 | ||
| 	}
 | ||
| 
 | ||
| 	s.logger.Info("获取用户统计信息", zap.String("user_id", userID))
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取用户统计信息成功",
 | ||
| 		Data:    userStats,
 | ||
| 		Meta: map[string]interface{}{
 | ||
| 			"generated_at": time.Now(),
 | ||
| 			"user_id":      userID,
 | ||
| 		},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 简化版统计辅助方法 ================
 | ||
| 
 | ||
| // getUserApiCallsStats 获取用户API调用统计
 | ||
| func (s *StatisticsApplicationServiceImpl) getUserApiCallsStats(ctx context.Context, userID string) (map[string]interface{}, error) {
 | ||
| 	// 获取总调用次数
 | ||
| 	totalCalls, err := s.apiCallRepo.CountByUserId(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户API调用总数失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取今日调用次数
 | ||
| 	today := time.Now().Truncate(24 * time.Hour)
 | ||
| 	tomorrow := today.Add(24 * time.Hour)
 | ||
| 	todayCalls, err := s.getApiCallsCountByDateRange(ctx, userID, today, tomorrow)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取今日API调用次数失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取本月调用次数
 | ||
| 	monthStart := time.Now().Truncate(24*time.Hour).AddDate(0, 0, -time.Now().Day()+1)
 | ||
| 	monthEnd := monthStart.AddDate(0, 1, 0)
 | ||
| 	monthCalls, err := s.getApiCallsCountByDateRange(ctx, userID, monthStart, monthEnd)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取本月API调用次数失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取每日趋势(最近7天)
 | ||
| 	endDate := time.Now()
 | ||
| 	startDate := endDate.AddDate(0, 0, -7)
 | ||
| 	dailyTrend, err := s.getApiCallsDailyTrend(ctx, userID, startDate, endDate)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取API调用每日趋势失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取每月趋势(最近6个月)
 | ||
| 	endDate = time.Now()
 | ||
| 	startDate = endDate.AddDate(0, -6, 0)
 | ||
| 	monthlyTrend, err := s.getApiCallsMonthlyTrend(ctx, userID, startDate, endDate)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取API调用每月趋势失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	stats := map[string]interface{}{
 | ||
| 		"total_calls":      totalCalls,
 | ||
| 		"today_calls":      todayCalls,
 | ||
| 		"this_month_calls": monthCalls,
 | ||
| 		"daily_trend":      dailyTrend,
 | ||
| 		"monthly_trend":    monthlyTrend,
 | ||
| 	}
 | ||
| 	return stats, nil
 | ||
| }
 | ||
| 
 | ||
| // getUserConsumptionStats 获取用户消费统计
 | ||
| func (s *StatisticsApplicationServiceImpl) getUserConsumptionStats(ctx context.Context, userID string) (map[string]interface{}, error) {
 | ||
| 	// 获取总消费金额
 | ||
| 	totalAmount, err := s.getTotalWalletTransactionAmount(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户总消费金额失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取今日消费金额
 | ||
| 	today := time.Now().Truncate(24 * time.Hour)
 | ||
| 	tomorrow := today.Add(24 * time.Hour)
 | ||
| 	todayAmount, err := s.getWalletTransactionsByDateRange(ctx, userID, today, tomorrow)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取今日消费金额失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取本月消费金额
 | ||
| 	monthStart := time.Now().Truncate(24*time.Hour).AddDate(0, 0, -time.Now().Day()+1)
 | ||
| 	monthEnd := monthStart.AddDate(0, 1, 0)
 | ||
| 	monthAmount, err := s.getWalletTransactionsByDateRange(ctx, userID, monthStart, monthEnd)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取本月消费金额失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取每日趋势(最近7天)
 | ||
| 	endDate := time.Now()
 | ||
| 	startDate := endDate.AddDate(0, 0, -7)
 | ||
| 	dailyTrend, err := s.getConsumptionDailyTrend(ctx, userID, startDate, endDate)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取消费每日趋势失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取每月趋势(最近6个月)
 | ||
| 	endDate = time.Now()
 | ||
| 	startDate = endDate.AddDate(0, -6, 0)
 | ||
| 	monthlyTrend, err := s.getConsumptionMonthlyTrend(ctx, userID, startDate, endDate)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取消费每月趋势失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	stats := map[string]interface{}{
 | ||
| 		"total_amount":      totalAmount,
 | ||
| 		"today_amount":      todayAmount,
 | ||
| 		"this_month_amount": monthAmount,
 | ||
| 		"daily_trend":       dailyTrend,
 | ||
| 		"monthly_trend":     monthlyTrend,
 | ||
| 	}
 | ||
| 	return stats, nil
 | ||
| }
 | ||
| 
 | ||
| // getUserRechargeStats 获取用户充值统计
 | ||
| func (s *StatisticsApplicationServiceImpl) getUserRechargeStats(ctx context.Context, userID string) (map[string]interface{}, error) {
 | ||
| 	// 获取总充值金额
 | ||
| 	totalAmount, err := s.getTotalRechargeAmount(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户总充值金额失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取今日充值金额
 | ||
| 	today := time.Now().Truncate(24 * time.Hour)
 | ||
| 	tomorrow := today.Add(24 * time.Hour)
 | ||
| 	todayAmount, err := s.getRechargeRecordsByDateRange(ctx, userID, today, tomorrow)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取今日充值金额失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取本月充值金额
 | ||
| 	monthStart := time.Now().Truncate(24*time.Hour).AddDate(0, 0, -time.Now().Day()+1)
 | ||
| 	monthEnd := monthStart.AddDate(0, 1, 0)
 | ||
| 	monthAmount, err := s.getRechargeRecordsByDateRange(ctx, userID, monthStart, monthEnd)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取本月充值金额失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取每日趋势(最近7天)
 | ||
| 	endDate := time.Now()
 | ||
| 	startDate := endDate.AddDate(0, 0, -7)
 | ||
| 	dailyTrend, err := s.getRechargeDailyTrend(ctx, userID, startDate, endDate)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取充值每日趋势失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取每月趋势(最近6个月)
 | ||
| 	endDate = time.Now()
 | ||
| 	startDate = endDate.AddDate(0, -6, 0)
 | ||
| 	monthlyTrend, err := s.getRechargeMonthlyTrend(ctx, userID, startDate, endDate)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取充值每月趋势失败", zap.String("user_id", userID), zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	stats := map[string]interface{}{
 | ||
| 		"total_amount":      totalAmount,
 | ||
| 		"today_amount":      todayAmount,
 | ||
| 		"this_month_amount": monthAmount,
 | ||
| 		"daily_trend":       dailyTrend,
 | ||
| 		"monthly_trend":     monthlyTrend,
 | ||
| 	}
 | ||
| 	return stats, nil
 | ||
| }
 | ||
| 
 | ||
| // getAdminSystemStats 获取管理员系统统计
 | ||
| func (s *StatisticsApplicationServiceImpl) getAdminSystemStats(ctx context.Context, period string, startTime, endTime time.Time) (map[string]interface{}, error) {
 | ||
| 	// 获取用户统计
 | ||
| 	userStats, err := s.getUserStats(ctx, period, startTime, endTime)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户统计失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取认证统计
 | ||
| 	certificationStats, err := s.getCertificationStats(ctx, period, startTime, endTime)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取认证统计失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取API调用统计
 | ||
| 	apiCallStats, err := s.getSystemApiCallStats(ctx, period, startTime, endTime)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取API调用统计失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取财务统计
 | ||
| 	financeStats, err := s.getSystemFinanceStats(ctx, period, startTime, endTime)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取财务统计失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	stats := map[string]interface{}{
 | ||
| 		"users":         userStats,
 | ||
| 		"certification": certificationStats,
 | ||
| 		"api_calls":     apiCallStats,
 | ||
| 		"finance":       financeStats,
 | ||
| 	}
 | ||
| 	return stats, nil
 | ||
| }
 | ||
| 
 | ||
| // AdminGetUserStatistics 管理员获取单个用户统计
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetUserStatistics(ctx context.Context, userID string) (*QueryResponse, error) {
 | ||
| 	// 验证用户ID
 | ||
| 	if userID == "" {
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "用户ID不能为空",
 | ||
| 			Error:   "用户ID不能为空",
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取用户API调用统计
 | ||
| 	apiCalls, err := s.getUserApiCallsStats(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户API调用统计失败", zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "获取API调用统计失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取用户消费统计
 | ||
| 	consumption, err := s.getUserConsumptionStats(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户消费统计失败", zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "获取消费统计失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取用户充值统计
 | ||
| 	recharge, err := s.getUserRechargeStats(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户充值统计失败", zap.Error(err))
 | ||
| 		return &QueryResponse{
 | ||
| 			Success: false,
 | ||
| 			Message: "获取充值统计失败",
 | ||
| 			Error:   err.Error(),
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 组装用户统计数据
 | ||
| 	userStats := map[string]interface{}{
 | ||
| 		"user_id":     userID,
 | ||
| 		"api_calls":   apiCalls,
 | ||
| 		"consumption": consumption,
 | ||
| 		"recharge":    recharge,
 | ||
| 		"summary": map[string]interface{}{
 | ||
| 			"total_calls":     apiCalls["total_calls"],
 | ||
| 			"total_consumed":  consumption["total_amount"],
 | ||
| 			"total_recharged": recharge["total_amount"],
 | ||
| 			"balance":         recharge["total_amount"].(float64) - consumption["total_amount"].(float64),
 | ||
| 		},
 | ||
| 	}
 | ||
| 
 | ||
| 	s.logger.Info("管理员获取用户统计", zap.String("user_id", userID))
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取用户统计成功",
 | ||
| 		Data:    userStats,
 | ||
| 		Meta: map[string]interface{}{
 | ||
| 			"generated_at": time.Now(),
 | ||
| 			"user_id":      userID,
 | ||
| 		},
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 统计查询辅助方法 ================
 | ||
| 
 | ||
| // getApiCallsCountByDateRange 获取指定日期范围内的API调用次数
 | ||
| func (s *StatisticsApplicationServiceImpl) getApiCallsCountByDateRange(ctx context.Context, userID string, startDate, endDate time.Time) (int64, error) {
 | ||
| 	return s.apiCallRepo.CountByUserIdAndDateRange(ctx, userID, startDate, endDate)
 | ||
| }
 | ||
| 
 | ||
| // getApiCallsDailyTrend 获取API调用每日趋势
 | ||
| func (s *StatisticsApplicationServiceImpl) getApiCallsDailyTrend(ctx context.Context, userID string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
 | ||
| 	return s.apiCallRepo.GetDailyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| }
 | ||
| 
 | ||
| // getApiCallsMonthlyTrend 获取API调用每月趋势
 | ||
| func (s *StatisticsApplicationServiceImpl) getApiCallsMonthlyTrend(ctx context.Context, userID string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
 | ||
| 	return s.apiCallRepo.GetMonthlyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| }
 | ||
| 
 | ||
| // ================ 更多统计查询辅助方法 ================
 | ||
| 
 | ||
| // getTotalWalletTransactionAmount 获取用户总钱包交易金额
 | ||
| func (s *StatisticsApplicationServiceImpl) getTotalWalletTransactionAmount(ctx context.Context, userID string) (float64, error) {
 | ||
| 	return s.walletTransactionRepo.GetTotalAmountByUserId(ctx, userID)
 | ||
| }
 | ||
| 
 | ||
| // getTotalRechargeAmount 获取用户总充值金额
 | ||
| func (s *StatisticsApplicationServiceImpl) getTotalRechargeAmount(ctx context.Context, userID string) (float64, error) {
 | ||
| 	return s.rechargeRecordRepo.GetTotalAmountByUserId(ctx, userID)
 | ||
| }
 | ||
| 
 | ||
| // getConsumptionDailyTrend 获取消费每日趋势
 | ||
| func (s *StatisticsApplicationServiceImpl) getConsumptionDailyTrend(ctx context.Context, userID string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
 | ||
| 	return s.walletTransactionRepo.GetDailyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| }
 | ||
| 
 | ||
| // getConsumptionMonthlyTrend 获取消费每月趋势
 | ||
| func (s *StatisticsApplicationServiceImpl) getConsumptionMonthlyTrend(ctx context.Context, userID string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
 | ||
| 	return s.walletTransactionRepo.GetMonthlyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| }
 | ||
| 
 | ||
| // getRechargeDailyTrend 获取充值每日趋势
 | ||
| func (s *StatisticsApplicationServiceImpl) getRechargeDailyTrend(ctx context.Context, userID string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
 | ||
| 	return s.rechargeRecordRepo.GetDailyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| }
 | ||
| 
 | ||
| // getRechargeMonthlyTrend 获取充值每月趋势
 | ||
| func (s *StatisticsApplicationServiceImpl) getRechargeMonthlyTrend(ctx context.Context, userID string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
 | ||
| 	return s.rechargeRecordRepo.GetMonthlyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| }
 | ||
| 
 | ||
| // getUserStats 获取用户统计
 | ||
| func (s *StatisticsApplicationServiceImpl) getUserStats(ctx context.Context, period string, startTime, endTime time.Time) (map[string]interface{}, error) {
 | ||
| 	// 获取系统用户统计信息
 | ||
| 	userStats, err := s.userRepo.GetSystemUserStats(ctx)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户统计失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 根据时间范围获取趋势数据
 | ||
| 	var trendData []map[string]interface{}
 | ||
| 	if !startTime.IsZero() && !endTime.IsZero() {
 | ||
| 		if period == "day" {
 | ||
| 			trendData, err = s.userRepo.GetSystemDailyUserStats(ctx, startTime, endTime)
 | ||
| 		} else if period == "month" {
 | ||
| 			trendData, err = s.userRepo.GetSystemMonthlyUserStats(ctx, startTime, endTime)
 | ||
| 		}
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取用户趋势数据失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	} else {
 | ||
| 		// 默认获取最近7天的数据
 | ||
| 		endDate := time.Now()
 | ||
| 		startDate := endDate.AddDate(0, 0, -7)
 | ||
| 		trendData, err = s.userRepo.GetSystemDailyUserStats(ctx, startDate, endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取用户每日趋势失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 计算时间范围内的新增用户数
 | ||
| 	var newInRange int64
 | ||
| 	if !startTime.IsZero() && !endTime.IsZero() {
 | ||
| 		rangeStats, err := s.userRepo.GetSystemUserStatsByDateRange(ctx, startTime, endTime)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取时间范围内用户统计失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 		newInRange = rangeStats.TodayRegistrations
 | ||
| 	}
 | ||
| 
 | ||
| 	stats := map[string]interface{}{
 | ||
| 		"total_users":  userStats.TotalUsers,
 | ||
| 		"new_today":    userStats.TodayRegistrations,
 | ||
| 		"new_in_range": newInRange,
 | ||
| 		"daily_trend":  trendData,
 | ||
| 	}
 | ||
| 	return stats, nil
 | ||
| }
 | ||
| 
 | ||
| // getCertificationStats 获取认证统计
 | ||
| func (s *StatisticsApplicationServiceImpl) getCertificationStats(ctx context.Context, period string, startTime, endTime time.Time) (map[string]interface{}, error) {
 | ||
| 	// 获取系统用户统计信息
 | ||
| 	userStats, err := s.userRepo.GetSystemUserStats(ctx)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户统计失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 计算认证成功率
 | ||
| 	var successRate float64
 | ||
| 	if userStats.TotalUsers > 0 {
 | ||
| 		successRate = float64(userStats.CertifiedUsers) / float64(userStats.TotalUsers)
 | ||
| 	}
 | ||
| 
 | ||
| 	// 根据时间范围获取趋势数据
 | ||
| 	var trendData []map[string]interface{}
 | ||
| 	if !startTime.IsZero() && !endTime.IsZero() {
 | ||
| 		if period == "day" {
 | ||
| 			trendData, err = s.userRepo.GetSystemDailyUserStats(ctx, startTime, endTime)
 | ||
| 		} else if period == "month" {
 | ||
| 			trendData, err = s.userRepo.GetSystemMonthlyUserStats(ctx, startTime, endTime)
 | ||
| 		}
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取认证趋势数据失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	} else {
 | ||
| 		// 默认获取最近7天的数据
 | ||
| 		endDate := time.Now()
 | ||
| 		startDate := endDate.AddDate(0, 0, -7)
 | ||
| 		trendData, err = s.userRepo.GetSystemDailyUserStats(ctx, startDate, endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取认证每日趋势失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	stats := map[string]interface{}{
 | ||
| 		"total_certified": userStats.CertifiedUsers,
 | ||
| 		"certified_today": userStats.TodayRegistrations, // 今日注册的用户
 | ||
| 		"success_rate":    successRate,
 | ||
| 		"daily_trend":     trendData,
 | ||
| 	}
 | ||
| 	return stats, nil
 | ||
| }
 | ||
| 
 | ||
| // getSystemApiCallStats 获取系统API调用统计
 | ||
| func (s *StatisticsApplicationServiceImpl) getSystemApiCallStats(ctx context.Context, period string, startTime, endTime time.Time) (map[string]interface{}, error) {
 | ||
| 	// 获取系统总API调用次数
 | ||
| 	totalCalls, err := s.apiCallRepo.GetSystemTotalCalls(ctx)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取系统总API调用次数失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取今日API调用次数
 | ||
| 	today := time.Now().Truncate(24 * time.Hour)
 | ||
| 	tomorrow := today.Add(24 * time.Hour)
 | ||
| 	todayCalls, err := s.apiCallRepo.GetSystemCallsByDateRange(ctx, today, tomorrow)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取今日API调用次数失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 根据时间范围获取趋势数据
 | ||
| 	var trendData []map[string]interface{}
 | ||
| 	if !startTime.IsZero() && !endTime.IsZero() {
 | ||
| 		if period == "day" {
 | ||
| 			trendData, err = s.apiCallRepo.GetSystemDailyStats(ctx, startTime, endTime)
 | ||
| 		} else if period == "month" {
 | ||
| 			trendData, err = s.apiCallRepo.GetSystemMonthlyStats(ctx, startTime, endTime)
 | ||
| 		}
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取API调用趋势数据失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	} else {
 | ||
| 		// 默认获取最近7天的数据
 | ||
| 		endDate := time.Now()
 | ||
| 		startDate := endDate.AddDate(0, 0, -7)
 | ||
| 		trendData, err = s.apiCallRepo.GetSystemDailyStats(ctx, startDate, endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取API调用每日趋势失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	stats := map[string]interface{}{
 | ||
| 		"total_calls": totalCalls,
 | ||
| 		"calls_today": todayCalls,
 | ||
| 		"daily_trend": trendData,
 | ||
| 	}
 | ||
| 	return stats, nil
 | ||
| }
 | ||
| 
 | ||
| // getSystemFinanceStats 获取系统财务统计
 | ||
| func (s *StatisticsApplicationServiceImpl) getSystemFinanceStats(ctx context.Context, period string, startTime, endTime time.Time) (map[string]interface{}, error) {
 | ||
| 	// 获取系统总消费金额
 | ||
| 	totalConsumption, err := s.walletTransactionRepo.GetSystemTotalAmount(ctx)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取系统总消费金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取系统总充值金额
 | ||
| 	totalRecharge, err := s.rechargeRecordRepo.GetSystemTotalAmount(ctx)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取系统总充值金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取今日消费金额
 | ||
| 	today := time.Now().Truncate(24 * time.Hour)
 | ||
| 	tomorrow := today.Add(24 * time.Hour)
 | ||
| 	todayConsumption, err := s.walletTransactionRepo.GetSystemAmountByDateRange(ctx, today, tomorrow)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取今日消费金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取今日充值金额
 | ||
| 	todayRecharge, err := s.rechargeRecordRepo.GetSystemAmountByDateRange(ctx, today, tomorrow)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取今日充值金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 根据时间范围获取趋势数据
 | ||
| 	var trendData []map[string]interface{}
 | ||
| 	if !startTime.IsZero() && !endTime.IsZero() {
 | ||
| 		if period == "day" {
 | ||
| 			// 获取消费趋势
 | ||
| 			consumptionTrend, err := s.walletTransactionRepo.GetSystemDailyStats(ctx, startTime, endTime)
 | ||
| 			if err != nil {
 | ||
| 				s.logger.Error("获取消费趋势数据失败", zap.Error(err))
 | ||
| 				return nil, err
 | ||
| 			}
 | ||
| 			// 获取充值趋势
 | ||
| 			rechargeTrend, err := s.rechargeRecordRepo.GetSystemDailyStats(ctx, startTime, endTime)
 | ||
| 			if err != nil {
 | ||
| 				s.logger.Error("获取充值趋势数据失败", zap.Error(err))
 | ||
| 				return nil, err
 | ||
| 			}
 | ||
| 			// 合并趋势数据
 | ||
| 			trendData = s.mergeFinanceTrends(consumptionTrend, rechargeTrend, "day")
 | ||
| 		} else if period == "month" {
 | ||
| 			// 获取消费趋势
 | ||
| 			consumptionTrend, err := s.walletTransactionRepo.GetSystemMonthlyStats(ctx, startTime, endTime)
 | ||
| 			if err != nil {
 | ||
| 				s.logger.Error("获取消费趋势数据失败", zap.Error(err))
 | ||
| 				return nil, err
 | ||
| 			}
 | ||
| 			// 获取充值趋势
 | ||
| 			rechargeTrend, err := s.rechargeRecordRepo.GetSystemMonthlyStats(ctx, startTime, endTime)
 | ||
| 			if err != nil {
 | ||
| 				s.logger.Error("获取充值趋势数据失败", zap.Error(err))
 | ||
| 				return nil, err
 | ||
| 			}
 | ||
| 			// 合并趋势数据
 | ||
| 			trendData = s.mergeFinanceTrends(consumptionTrend, rechargeTrend, "month")
 | ||
| 		}
 | ||
| 	} else {
 | ||
| 		// 默认获取最近7天的数据
 | ||
| 		endDate := time.Now()
 | ||
| 		startDate := endDate.AddDate(0, 0, -7)
 | ||
| 		consumptionTrend, err := s.walletTransactionRepo.GetSystemDailyStats(ctx, startDate, endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取消费每日趋势失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 		rechargeTrend, err := s.rechargeRecordRepo.GetSystemDailyStats(ctx, startDate, endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取充值每日趋势失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 		trendData = s.mergeFinanceTrends(consumptionTrend, rechargeTrend, "day")
 | ||
| 	}
 | ||
| 
 | ||
| 	stats := map[string]interface{}{
 | ||
| 		"total_deduct":   totalConsumption,
 | ||
| 		"deduct_today":   todayConsumption,
 | ||
| 		"total_recharge": totalRecharge,
 | ||
| 		"recharge_today": todayRecharge,
 | ||
| 		"daily_trend":    trendData,
 | ||
| 	}
 | ||
| 	return stats, nil
 | ||
| }
 | ||
| 
 | ||
| // mergeFinanceTrends 合并财务趋势数据
 | ||
| func (s *StatisticsApplicationServiceImpl) mergeFinanceTrends(consumptionTrend, rechargeTrend []map[string]interface{}, period string) []map[string]interface{} {
 | ||
| 	// 创建日期到数据的映射
 | ||
| 	consumptionMap := make(map[string]float64)
 | ||
| 	rechargeMap := make(map[string]float64)
 | ||
| 
 | ||
| 	// 处理消费数据
 | ||
| 	for _, item := range consumptionTrend {
 | ||
| 		var dateKey string
 | ||
| 		var amount float64
 | ||
| 
 | ||
| 		if period == "day" {
 | ||
| 			if date, ok := item["date"].(string); ok {
 | ||
| 				dateKey = date
 | ||
| 			}
 | ||
| 			if amt, ok := item["amount"].(float64); ok {
 | ||
| 				amount = amt
 | ||
| 			}
 | ||
| 		} else if period == "month" {
 | ||
| 			if month, ok := item["month"].(string); ok {
 | ||
| 				dateKey = month
 | ||
| 			}
 | ||
| 			if amt, ok := item["amount"].(float64); ok {
 | ||
| 				amount = amt
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if dateKey != "" {
 | ||
| 			consumptionMap[dateKey] = amount
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 处理充值数据
 | ||
| 	for _, item := range rechargeTrend {
 | ||
| 		var dateKey string
 | ||
| 		var amount float64
 | ||
| 
 | ||
| 		if period == "day" {
 | ||
| 			if date, ok := item["date"].(string); ok {
 | ||
| 				dateKey = date
 | ||
| 			}
 | ||
| 			if amt, ok := item["amount"].(float64); ok {
 | ||
| 				amount = amt
 | ||
| 			}
 | ||
| 		} else if period == "month" {
 | ||
| 			if month, ok := item["month"].(string); ok {
 | ||
| 				dateKey = month
 | ||
| 			}
 | ||
| 			if amt, ok := item["amount"].(float64); ok {
 | ||
| 				amount = amt
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if dateKey != "" {
 | ||
| 			rechargeMap[dateKey] = amount
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 合并数据
 | ||
| 	var mergedTrend []map[string]interface{}
 | ||
| 	allDates := make(map[string]bool)
 | ||
| 
 | ||
| 	// 收集所有日期
 | ||
| 	for date := range consumptionMap {
 | ||
| 		allDates[date] = true
 | ||
| 	}
 | ||
| 	for date := range rechargeMap {
 | ||
| 		allDates[date] = true
 | ||
| 	}
 | ||
| 
 | ||
| 	// 按日期排序并合并
 | ||
| 	for date := range allDates {
 | ||
| 		consumption := consumptionMap[date]
 | ||
| 		recharge := rechargeMap[date]
 | ||
| 
 | ||
| 		item := map[string]interface{}{
 | ||
| 			"date":     date,
 | ||
| 			"deduct":   consumption,
 | ||
| 			"recharge": recharge,
 | ||
| 		}
 | ||
| 		mergedTrend = append(mergedTrend, item)
 | ||
| 	}
 | ||
| 
 | ||
| 	// 简单排序(按日期字符串)
 | ||
| 	for i := 0; i < len(mergedTrend)-1; i++ {
 | ||
| 		for j := i + 1; j < len(mergedTrend); j++ {
 | ||
| 			if mergedTrend[i]["date"].(string) > mergedTrend[j]["date"].(string) {
 | ||
| 				mergedTrend[i], mergedTrend[j] = mergedTrend[j], mergedTrend[i]
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	return mergedTrend
 | ||
| }
 | ||
| 
 | ||
| // getUserDailyTrend 获取用户每日趋势
 | ||
| func (s *StatisticsApplicationServiceImpl) getUserDailyTrend(ctx context.Context, days int) ([]map[string]interface{}, error) {
 | ||
| 	// 生成最近N天的日期列表
 | ||
| 	var trend []map[string]interface{}
 | ||
| 	now := time.Now()
 | ||
| 
 | ||
| 	for i := days - 1; i >= 0; i-- {
 | ||
| 		date := now.AddDate(0, 0, -i).Truncate(24 * time.Hour)
 | ||
| 
 | ||
| 		// 这里需要实现按日期查询用户注册数的逻辑
 | ||
| 		// 暂时使用模拟数据
 | ||
| 		count := int64(10 + i*2) // 模拟数据
 | ||
| 
 | ||
| 		trend = append(trend, map[string]interface{}{
 | ||
| 			"date":  date.Format("2006-01-02"),
 | ||
| 			"count": count,
 | ||
| 		})
 | ||
| 	}
 | ||
| 
 | ||
| 	return trend, nil
 | ||
| }
 | ||
| 
 | ||
| // getCertificationDailyTrend 获取认证每日趋势
 | ||
| func (s *StatisticsApplicationServiceImpl) getCertificationDailyTrend(ctx context.Context, days int) ([]map[string]interface{}, error) {
 | ||
| 	// 生成最近N天的日期列表
 | ||
| 	var trend []map[string]interface{}
 | ||
| 	now := time.Now()
 | ||
| 
 | ||
| 	for i := days - 1; i >= 0; i-- {
 | ||
| 		date := now.AddDate(0, 0, -i).Truncate(24 * time.Hour)
 | ||
| 
 | ||
| 		// 这里需要实现按日期查询认证数的逻辑
 | ||
| 		// 暂时使用模拟数据
 | ||
| 		count := int64(5 + i) // 模拟数据
 | ||
| 
 | ||
| 		trend = append(trend, map[string]interface{}{
 | ||
| 			"date":  date.Format("2006-01-02"),
 | ||
| 			"count": count,
 | ||
| 		})
 | ||
| 	}
 | ||
| 
 | ||
| 	return trend, nil
 | ||
| }
 | ||
| 
 | ||
| // getWalletTransactionsByDateRange 获取指定日期范围内的钱包交易金额
 | ||
| func (s *StatisticsApplicationServiceImpl) getWalletTransactionsByDateRange(ctx context.Context, userID string, startDate, endDate time.Time) (float64, error) {
 | ||
| 	// 这里需要实现按日期范围查询钱包交易金额的逻辑
 | ||
| 	// 暂时返回0,实际实现需要扩展仓储接口或使用原生SQL查询
 | ||
| 	return 0.0, nil
 | ||
| }
 | ||
| 
 | ||
| // getRechargeRecordsByDateRange 获取指定日期范围内的充值金额(排除赠送)
 | ||
| func (s *StatisticsApplicationServiceImpl) getRechargeRecordsByDateRange(ctx context.Context, userID string, startDate, endDate time.Time) (float64, error) {
 | ||
| 	return s.rechargeRecordRepo.GetTotalAmountByUserIdAndDateRange(ctx, userID, startDate, endDate)
 | ||
| }
 | ||
| 
 | ||
| // ================ 独立统计接口实现 ================
 | ||
| 
 | ||
| // GetApiCallsStatistics 获取API调用统计
 | ||
| func (s *StatisticsApplicationServiceImpl) GetApiCallsStatistics(ctx context.Context, userID string, startDate, endDate time.Time, unit string) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("获取API调用统计",
 | ||
| 		zap.String("user_id", userID),
 | ||
| 		zap.Time("start_date", startDate),
 | ||
| 		zap.Time("end_date", endDate),
 | ||
| 		zap.String("unit", unit))
 | ||
| 
 | ||
| 	// 获取总调用次数
 | ||
| 	totalCalls, err := s.apiCallRepo.CountByUserId(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取API调用总数失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取指定时间范围内的调用次数
 | ||
| 	rangeCalls, err := s.apiCallRepo.CountByUserIdAndDateRange(ctx, userID, startDate, endDate)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取API调用范围统计失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	var trendData []map[string]interface{}
 | ||
| 	if unit == "day" {
 | ||
| 		trendData, err = s.apiCallRepo.GetDailyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| 	} else if unit == "month" {
 | ||
| 		trendData, err = s.apiCallRepo.GetMonthlyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取API调用趋势数据失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"total_calls": totalCalls,
 | ||
| 		"range_calls": rangeCalls,
 | ||
| 		"trend_data":  trendData,
 | ||
| 		"unit":        unit,
 | ||
| 		"start_date":  startDate.Format("2006-01-02"),
 | ||
| 		"end_date":    endDate.Format("2006-01-02"),
 | ||
| 		"user_id":     userID,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取API调用统计成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetConsumptionStatistics 获取消费统计
 | ||
| func (s *StatisticsApplicationServiceImpl) GetConsumptionStatistics(ctx context.Context, userID string, startDate, endDate time.Time, unit string) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("获取消费统计",
 | ||
| 		zap.String("user_id", userID),
 | ||
| 		zap.Time("start_date", startDate),
 | ||
| 		zap.Time("end_date", endDate),
 | ||
| 		zap.String("unit", unit))
 | ||
| 
 | ||
| 	// 获取总消费金额
 | ||
| 	totalAmount, err := s.walletTransactionRepo.GetTotalAmountByUserId(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取消费总金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取指定时间范围内的消费金额
 | ||
| 	rangeAmount, err := s.walletTransactionRepo.GetTotalAmountByUserIdAndDateRange(ctx, userID, startDate, endDate)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取消费范围统计失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	var trendData []map[string]interface{}
 | ||
| 	if unit == "day" {
 | ||
| 		trendData, err = s.walletTransactionRepo.GetDailyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| 	} else if unit == "month" {
 | ||
| 		trendData, err = s.walletTransactionRepo.GetMonthlyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取消费趋势数据失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"total_amount": totalAmount,
 | ||
| 		"range_amount": rangeAmount,
 | ||
| 		"trend_data":   trendData,
 | ||
| 		"unit":         unit,
 | ||
| 		"start_date":   startDate.Format("2006-01-02"),
 | ||
| 		"end_date":     endDate.Format("2006-01-02"),
 | ||
| 		"user_id":      userID,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取消费统计成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetRechargeStatistics 获取充值统计
 | ||
| func (s *StatisticsApplicationServiceImpl) GetRechargeStatistics(ctx context.Context, userID string, startDate, endDate time.Time, unit string) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("获取充值统计",
 | ||
| 		zap.String("user_id", userID),
 | ||
| 		zap.Time("start_date", startDate),
 | ||
| 		zap.Time("end_date", endDate),
 | ||
| 		zap.String("unit", unit))
 | ||
| 
 | ||
| 	// 获取总充值金额
 | ||
| 	totalAmount, err := s.rechargeRecordRepo.GetTotalAmountByUserId(ctx, userID)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取充值总金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取指定时间范围内的充值金额
 | ||
| 	rangeAmount, err := s.rechargeRecordRepo.GetTotalAmountByUserIdAndDateRange(ctx, userID, startDate, endDate)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取充值范围统计失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	var trendData []map[string]interface{}
 | ||
| 	if unit == "day" {
 | ||
| 		trendData, err = s.rechargeRecordRepo.GetDailyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| 	} else if unit == "month" {
 | ||
| 		trendData, err = s.rechargeRecordRepo.GetMonthlyStatsByUserId(ctx, userID, startDate, endDate)
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取充值趋势数据失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"total_amount": totalAmount,
 | ||
| 		"range_amount": rangeAmount,
 | ||
| 		"trend_data":   trendData,
 | ||
| 		"unit":         unit,
 | ||
| 		"start_date":   startDate.Format("2006-01-02"),
 | ||
| 		"end_date":     endDate.Format("2006-01-02"),
 | ||
| 		"user_id":      userID,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取充值统计成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // GetLatestProducts 获取最新产品推荐
 | ||
| func (s *StatisticsApplicationServiceImpl) GetLatestProducts(ctx context.Context, limit int) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("获取最新产品推荐", zap.Int("limit", limit))
 | ||
| 
 | ||
| 	// 获取最新的产品
 | ||
| 	query := &productQueries.ListProductsQuery{
 | ||
| 		Page:      1,
 | ||
| 		PageSize:  limit,
 | ||
| 		IsVisible: &[]bool{true}[0],
 | ||
| 		IsEnabled: &[]bool{true}[0],
 | ||
| 		SortBy:    "created_at",
 | ||
| 		SortOrder: "desc",
 | ||
| 	}
 | ||
| 
 | ||
| 	productsList, _, err := s.productRepo.ListProducts(ctx, query)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取最新产品失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	var products []map[string]interface{}
 | ||
| 	for _, product := range productsList {
 | ||
| 		products = append(products, map[string]interface{}{
 | ||
| 			"id":          product.ID,
 | ||
| 			"name":        product.Name,
 | ||
| 			"description": product.Description,
 | ||
| 			"code":        product.Code,
 | ||
| 			"price":       product.Price,
 | ||
| 			"category_id": product.CategoryID,
 | ||
| 			"created_at":  product.CreatedAt,
 | ||
| 			"is_new":      true, // 暂时都标记为新产品
 | ||
| 		})
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"products": products,
 | ||
| 		"count":    len(products),
 | ||
| 		"limit":    limit,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取最新产品推荐成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 管理员独立域统计接口 ================
 | ||
| 
 | ||
| // AdminGetUserDomainStatistics 管理员获取用户域统计
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetUserDomainStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("管理员获取用户域统计", zap.String("period", period), zap.String("startDate", startDate), zap.String("endDate", endDate))
 | ||
| 
 | ||
| 	// 解析日期
 | ||
| 	var startTime, endTime time.Time
 | ||
| 	var err error
 | ||
| 	if startDate != "" {
 | ||
| 		startTime, err = time.Parse("2006-01-02", startDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("解析开始日期失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if endDate != "" {
 | ||
| 		endTime, err = time.Parse("2006-01-02", endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("解析结束日期失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取用户统计数据
 | ||
| 	userStats, err := s.getUserStats(ctx, period, startTime, endTime)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户统计数据失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取认证统计数据
 | ||
| 	certificationStats, err := s.getCertificationStats(ctx, period, startTime, endTime)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取认证统计数据失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"user_stats":          userStats,
 | ||
| 		"certification_stats": certificationStats,
 | ||
| 		"period":              period,
 | ||
| 		"start_date":          startDate,
 | ||
| 		"end_date":            endDate,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取用户域统计成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // AdminGetApiDomainStatistics 管理员获取API域统计
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetApiDomainStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("管理员获取API域统计", zap.String("period", period), zap.String("startDate", startDate), zap.String("endDate", endDate))
 | ||
| 
 | ||
| 	// 解析日期
 | ||
| 	var startTime, endTime time.Time
 | ||
| 	var err error
 | ||
| 	if startDate != "" {
 | ||
| 		startTime, err = time.Parse("2006-01-02", startDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("解析开始日期失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if endDate != "" {
 | ||
| 		endTime, err = time.Parse("2006-01-02", endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("解析结束日期失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取API调用统计数据
 | ||
| 	apiCallStats, err := s.getSystemApiCallStats(ctx, period, startTime, endTime)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取API调用统计数据失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"api_call_stats": apiCallStats,
 | ||
| 		"period":         period,
 | ||
| 		"start_date":     startDate,
 | ||
| 		"end_date":       endDate,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取API域统计成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // AdminGetConsumptionDomainStatistics 管理员获取消费域统计
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetConsumptionDomainStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("管理员获取消费域统计", zap.String("period", period), zap.String("startDate", startDate), zap.String("endDate", endDate))
 | ||
| 
 | ||
| 	// 解析日期
 | ||
| 	var startTime, endTime time.Time
 | ||
| 	var err error
 | ||
| 	if startDate != "" {
 | ||
| 		startTime, err = time.Parse("2006-01-02", startDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("解析开始日期失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if endDate != "" {
 | ||
| 		endTime, err = time.Parse("2006-01-02", endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("解析结束日期失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取消费统计数据
 | ||
| 	totalConsumption, err := s.walletTransactionRepo.GetSystemTotalAmount(ctx)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取系统总消费金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取今日消费金额
 | ||
| 	today := time.Now().Truncate(24 * time.Hour)
 | ||
| 	tomorrow := today.Add(24 * time.Hour)
 | ||
| 	todayConsumption, err := s.walletTransactionRepo.GetSystemAmountByDateRange(ctx, today, tomorrow)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取今日消费金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取指定时间范围内的消费金额
 | ||
| 	rangeConsumption := float64(0)
 | ||
| 	if !startTime.IsZero() && !endTime.IsZero() {
 | ||
| 		rangeConsumption, err = s.walletTransactionRepo.GetSystemAmountByDateRange(ctx, startTime, endTime)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取指定时间范围消费金额失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	var consumptionTrend []map[string]interface{}
 | ||
| 	if !startTime.IsZero() && !endTime.IsZero() {
 | ||
| 		if period == "day" {
 | ||
| 			consumptionTrend, err = s.walletTransactionRepo.GetSystemDailyStats(ctx, startTime, endTime)
 | ||
| 		} else if period == "month" {
 | ||
| 			consumptionTrend, err = s.walletTransactionRepo.GetSystemMonthlyStats(ctx, startTime, endTime)
 | ||
| 		}
 | ||
| 
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取消费趋势数据失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	} else {
 | ||
| 		// 如果没有指定时间范围,获取最近7天的数据
 | ||
| 		defaultEndDate := time.Now()
 | ||
| 		defaultStartDate := defaultEndDate.AddDate(0, 0, -7)
 | ||
| 		consumptionTrend, err = s.walletTransactionRepo.GetSystemDailyStats(ctx, defaultStartDate, defaultEndDate)
 | ||
| 		if err != nil {  
 | ||
| 			s.logger.Error("获取消费每日趋势失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"total_consumption": totalConsumption,
 | ||
| 		"today_consumption": todayConsumption,
 | ||
| 		"range_consumption": rangeConsumption,
 | ||
| 		"consumption_trend": consumptionTrend,
 | ||
| 		"period":            period,
 | ||
| 		"start_date":        startDate,
 | ||
| 		"end_date":          endDate,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取消费域统计成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // AdminGetRechargeDomainStatistics 管理员获取充值域统计
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetRechargeDomainStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("管理员获取充值域统计", zap.String("period", period), zap.String("startDate", startDate), zap.String("endDate", endDate))
 | ||
| 
 | ||
| 	// 解析日期
 | ||
| 	var startTime, endTime time.Time
 | ||
| 	var err error
 | ||
| 	if startDate != "" {
 | ||
| 		startTime, err = time.Parse("2006-01-02", startDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("解析开始日期失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if endDate != "" {
 | ||
| 		endTime, err = time.Parse("2006-01-02", endDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("解析结束日期失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取充值统计数据
 | ||
| 	totalRecharge, err := s.rechargeRecordRepo.GetSystemTotalAmount(ctx)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取系统总充值金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取今日充值金额
 | ||
| 	today := time.Now().Truncate(24 * time.Hour)
 | ||
| 	tomorrow := today.Add(24 * time.Hour)
 | ||
| 	todayRecharge, err := s.rechargeRecordRepo.GetSystemAmountByDateRange(ctx, today, tomorrow)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取今日充值金额失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取指定时间范围内的充值金额
 | ||
| 	rangeRecharge := float64(0)
 | ||
| 	if !startTime.IsZero() && !endTime.IsZero() {
 | ||
| 		rangeRecharge, err = s.rechargeRecordRepo.GetSystemAmountByDateRange(ctx, startTime, endTime)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取指定时间范围充值金额失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	var rechargeTrend []map[string]interface{}
 | ||
| 	if !startTime.IsZero() && !endTime.IsZero() {
 | ||
| 		if period == "day" {
 | ||
| 			rechargeTrend, err = s.rechargeRecordRepo.GetSystemDailyStats(ctx, startTime, endTime)
 | ||
| 		} else if period == "month" {
 | ||
| 			rechargeTrend, err = s.rechargeRecordRepo.GetSystemMonthlyStats(ctx, startTime, endTime)
 | ||
| 		}
 | ||
| 
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取充值趋势数据失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	} else {
 | ||
| 		// 如果没有指定时间范围,获取最近7天的数据
 | ||
| 		defaultEndDate := time.Now()
 | ||
| 		defaultStartDate := defaultEndDate.AddDate(0, 0, -7)
 | ||
| 		rechargeTrend, err = s.rechargeRecordRepo.GetSystemDailyStats(ctx, defaultStartDate, defaultEndDate)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Error("获取充值每日趋势失败", zap.Error(err))
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"total_recharge": totalRecharge,
 | ||
| 		"today_recharge": todayRecharge,
 | ||
| 		"range_recharge": rangeRecharge,
 | ||
| 		"recharge_trend": rechargeTrend,
 | ||
| 		"period":         period,
 | ||
| 		"start_date":     startDate,
 | ||
| 		"end_date":       endDate,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取充值域统计成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // AdminGetUserCallRanking 获取用户调用排行榜
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetUserCallRanking(ctx context.Context, rankingType, period string, limit int) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("获取用户调用排行榜", zap.String("type", rankingType), zap.String("period", period), zap.Int("limit", limit))
 | ||
| 
 | ||
| 	var rankings []map[string]interface{}
 | ||
| 	var err error
 | ||
| 
 | ||
| 	switch rankingType {
 | ||
| 	case "calls":
 | ||
| 		// 按调用次数排行
 | ||
| 		switch period {
 | ||
| 		case "today":
 | ||
| 			rankings, err = s.getUserCallRankingByCalls(ctx, "today", limit)
 | ||
| 		case "month":
 | ||
| 			rankings, err = s.getUserCallRankingByCalls(ctx, "month", limit)
 | ||
| 		case "total":
 | ||
| 			rankings, err = s.getUserCallRankingByCalls(ctx, "total", limit)
 | ||
| 		default:
 | ||
| 			return nil, fmt.Errorf("不支持的时间周期: %s", period)
 | ||
| 		}
 | ||
| 	case "consumption":
 | ||
| 		// 按消费金额排行
 | ||
| 		switch period {
 | ||
| 		case "today":
 | ||
| 			rankings, err = s.getUserCallRankingByConsumption(ctx, "today", limit)
 | ||
| 		case "month":
 | ||
| 			rankings, err = s.getUserCallRankingByConsumption(ctx, "month", limit)
 | ||
| 		case "total":
 | ||
| 			rankings, err = s.getUserCallRankingByConsumption(ctx, "total", limit)
 | ||
| 		default:
 | ||
| 			return nil, fmt.Errorf("不支持的时间周期: %s", period)
 | ||
| 		}
 | ||
| 	default:
 | ||
| 		return nil, fmt.Errorf("不支持的排行类型: %s", rankingType)
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户调用排行榜失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"ranking_type": rankingType,
 | ||
| 		"period":       period,
 | ||
| 		"limit":        limit,
 | ||
| 		"rankings":     rankings,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取用户调用排行榜成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // AdminGetRechargeRanking 获取充值排行榜
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetRechargeRanking(ctx context.Context, period string, limit int) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("获取充值排行榜", zap.String("period", period), zap.Int("limit", limit))
 | ||
| 
 | ||
| 	var rankings []map[string]interface{}
 | ||
| 	var err error
 | ||
| 
 | ||
| 	switch period {
 | ||
| 	case "today":
 | ||
| 		rankings, err = s.getRechargeRanking(ctx, "today", limit)
 | ||
| 	case "month":
 | ||
| 		rankings, err = s.getRechargeRanking(ctx, "month", limit)
 | ||
| 	case "total":
 | ||
| 		rankings, err = s.getRechargeRanking(ctx, "total", limit)
 | ||
| 	default:
 | ||
| 		return nil, fmt.Errorf("不支持的时间周期: %s", period)
 | ||
| 	}
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取充值排行榜失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"period":   period,
 | ||
| 		"limit":    limit,
 | ||
| 		"rankings": rankings,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取充值排行榜成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // getUserCallRankingByCalls 按调用次数获取用户排行
 | ||
| func (s *StatisticsApplicationServiceImpl) getUserCallRankingByCalls(ctx context.Context, period string, limit int) ([]map[string]interface{}, error) {
 | ||
| 	// 调用用户仓储获取真实数据
 | ||
| 	rankings, err := s.userRepo.GetUserCallRankingByCalls(ctx, period, limit)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户调用次数排行失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加排名信息
 | ||
| 	for i, ranking := range rankings {
 | ||
| 		ranking["rank"] = i + 1
 | ||
| 	}
 | ||
| 
 | ||
| 	return rankings, nil
 | ||
| }
 | ||
| 
 | ||
| // getUserCallRankingByConsumption 按消费金额获取用户排行
 | ||
| func (s *StatisticsApplicationServiceImpl) getUserCallRankingByConsumption(ctx context.Context, period string, limit int) ([]map[string]interface{}, error) {
 | ||
| 	// 调用用户仓储获取真实数据
 | ||
| 	rankings, err := s.userRepo.GetUserCallRankingByConsumption(ctx, period, limit)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取用户消费金额排行失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加排名信息
 | ||
| 	for i, ranking := range rankings {
 | ||
| 		ranking["rank"] = i + 1
 | ||
| 	}
 | ||
| 
 | ||
| 	return rankings, nil
 | ||
| }
 | ||
| 
 | ||
| // getRechargeRanking 获取充值排行
 | ||
| func (s *StatisticsApplicationServiceImpl) getRechargeRanking(ctx context.Context, period string, limit int) ([]map[string]interface{}, error) {
 | ||
| 	// 调用用户仓储获取真实数据
 | ||
| 	rankings, err := s.userRepo.GetRechargeRanking(ctx, period, limit)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取充值排行失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加排名信息
 | ||
| 	for i, ranking := range rankings {
 | ||
| 		ranking["rank"] = i + 1
 | ||
| 	}
 | ||
| 
 | ||
| 	return rankings, nil
 | ||
| }
 | ||
| 
 | ||
| // AdminGetApiPopularityRanking 获取API受欢迎程度排行榜
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetApiPopularityRanking(ctx context.Context, period string, limit int) (*QueryResponse, error) {
 | ||
| 	s.logger.Info("获取API受欢迎程度排行榜", zap.String("period", period), zap.Int("limit", limit))
 | ||
| 
 | ||
| 	// 调用API调用仓储获取真实数据
 | ||
| 	rankings, err := s.apiCallRepo.GetApiPopularityRanking(ctx, period, limit)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取API受欢迎程度排行榜失败", zap.Error(err))
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加排名信息
 | ||
| 	for i, ranking := range rankings {
 | ||
| 		ranking["rank"] = i + 1
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"period":   period,
 | ||
| 		"limit":    limit,
 | ||
| 		"rankings": rankings,
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取API受欢迎程度排行榜成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 | ||
| 
 | ||
| // AdminGetTodayCertifiedEnterprises 获取今日认证企业列表
 | ||
| func (s *StatisticsApplicationServiceImpl) AdminGetTodayCertifiedEnterprises(ctx context.Context, limit int) (*QueryResponse, error) {
 | ||
| 	// 获取今日开始和结束时间
 | ||
| 	now := time.Now()
 | ||
| 	todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
 | ||
| 	todayEnd := todayStart.Add(24 * time.Hour)
 | ||
| 
 | ||
| 	// 查询所有已完成的认证,然后过滤今日完成的
 | ||
| 	query := &certificationQueries.ListCertificationsQuery{
 | ||
| 		Page:      1,
 | ||
| 		PageSize:  1000, // 设置较大的页面大小以获取所有数据
 | ||
| 		SortBy:    "updated_at",
 | ||
| 		SortOrder: "desc",
 | ||
| 		Status:    certificationEnums.StatusCompleted,
 | ||
| 	}
 | ||
| 
 | ||
| 	certifications, _, err := s.certificationRepo.List(ctx, query)
 | ||
| 	if err != nil {
 | ||
| 		s.logger.Error("获取今日认证企业失败", zap.Error(err))
 | ||
| 		return nil, fmt.Errorf("获取今日认证企业失败: %w", err)
 | ||
| 	}
 | ||
| 
 | ||
| 	// 过滤出今日完成的认证(基于completed_at字段)
 | ||
| 	var completedCertifications []*certificationEntities.Certification
 | ||
| 	for _, cert := range certifications {
 | ||
| 		if cert.CompletedAt != nil &&
 | ||
| 			cert.CompletedAt.After(todayStart) &&
 | ||
| 			cert.CompletedAt.Before(todayEnd) {
 | ||
| 			completedCertifications = append(completedCertifications, cert)
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 按完成时间排序(最新的在前)
 | ||
| 	for i := 0; i < len(completedCertifications)-1; i++ {
 | ||
| 		for j := i + 1; j < len(completedCertifications); j++ {
 | ||
| 			if completedCertifications[i].CompletedAt.Before(*completedCertifications[j].CompletedAt) {
 | ||
| 				completedCertifications[i], completedCertifications[j] = completedCertifications[j], completedCertifications[i]
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 限制返回数量
 | ||
| 	if limit > 0 && len(completedCertifications) > limit {
 | ||
| 		completedCertifications = completedCertifications[:limit]
 | ||
| 	}
 | ||
| 
 | ||
| 	// 直接从企业信息表获取数据
 | ||
| 	var enterprises []map[string]interface{}
 | ||
| 	for _, cert := range completedCertifications {
 | ||
| 		// 获取企业信息
 | ||
| 		enterpriseInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, cert.UserID)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Warn("获取企业信息失败", zap.String("user_id", cert.UserID), zap.Error(err))
 | ||
| 			continue
 | ||
| 		}
 | ||
| 
 | ||
| 		// 获取用户基本信息(仅需要用户名)
 | ||
| 		user, err := s.userRepo.GetByID(ctx, cert.UserID)
 | ||
| 		if err != nil {
 | ||
| 			s.logger.Warn("获取用户信息失败", zap.String("user_id", cert.UserID), zap.Error(err))
 | ||
| 			continue
 | ||
| 		}
 | ||
| 
 | ||
| 		enterprise := map[string]interface{}{
 | ||
| 			"id":                    cert.ID,
 | ||
| 			"user_id":              cert.UserID,
 | ||
| 			"username":             user.Username,
 | ||
| 			"enterprise_name":      enterpriseInfo.CompanyName,
 | ||
| 			"legal_person_name":    enterpriseInfo.LegalPersonName,
 | ||
| 			"legal_person_phone":   enterpriseInfo.LegalPersonPhone,
 | ||
| 			"unified_social_code":  enterpriseInfo.UnifiedSocialCode,
 | ||
| 			"enterprise_address":   enterpriseInfo.EnterpriseAddress,
 | ||
| 			"certified_at":         cert.CompletedAt.Format(time.RFC3339),
 | ||
| 		}
 | ||
| 		enterprises = append(enterprises, enterprise)
 | ||
| 	}
 | ||
| 
 | ||
| 	result := map[string]interface{}{
 | ||
| 		"enterprises": enterprises,
 | ||
| 		"total":       len(enterprises),
 | ||
| 		"date":        todayStart.Format("2006-01-02"),
 | ||
| 	}
 | ||
| 
 | ||
| 	return &QueryResponse{
 | ||
| 		Success: true,
 | ||
| 		Message: "获取今日认证企业列表成功",
 | ||
| 		Data:    result,
 | ||
| 	}, nil
 | ||
| }
 |