package cache import ( "context" "encoding/json" "fmt" "time" "github.com/redis/go-redis/v9" "tyapi-server/internal/domains/statistics/entities" ) // RedisStatisticsCache Redis统计缓存实现 type RedisStatisticsCache struct { client *redis.Client prefix string } // NewRedisStatisticsCache 创建Redis统计缓存 func NewRedisStatisticsCache(client *redis.Client) *RedisStatisticsCache { return &RedisStatisticsCache{ client: client, prefix: "statistics:", } } // ================ 指标缓存 ================ // SetMetric 设置指标缓存 func (c *RedisStatisticsCache) SetMetric(ctx context.Context, metric *entities.StatisticsMetric, expiration time.Duration) error { if metric == nil { return fmt.Errorf("统计指标不能为空") } key := c.getMetricKey(metric.ID) data, err := json.Marshal(metric) if err != nil { return fmt.Errorf("序列化指标失败: %w", err) } err = c.client.Set(ctx, key, data, expiration).Err() if err != nil { return fmt.Errorf("设置指标缓存失败: %w", err) } return nil } // GetMetric 获取指标缓存 func (c *RedisStatisticsCache) GetMetric(ctx context.Context, metricID string) (*entities.StatisticsMetric, error) { if metricID == "" { return nil, fmt.Errorf("指标ID不能为空") } key := c.getMetricKey(metricID) data, err := c.client.Get(ctx, key).Result() if err != nil { if err == redis.Nil { return nil, nil // 缓存未命中 } return nil, fmt.Errorf("获取指标缓存失败: %w", err) } var metric entities.StatisticsMetric err = json.Unmarshal([]byte(data), &metric) if err != nil { return nil, fmt.Errorf("反序列化指标失败: %w", err) } return &metric, nil } // DeleteMetric 删除指标缓存 func (c *RedisStatisticsCache) DeleteMetric(ctx context.Context, metricID string) error { if metricID == "" { return fmt.Errorf("指标ID不能为空") } key := c.getMetricKey(metricID) err := c.client.Del(ctx, key).Err() if err != nil { return fmt.Errorf("删除指标缓存失败: %w", err) } return nil } // SetMetricsByType 设置按类型分组的指标缓存 func (c *RedisStatisticsCache) SetMetricsByType(ctx context.Context, metricType string, metrics []*entities.StatisticsMetric, expiration time.Duration) error { if metricType == "" { return fmt.Errorf("指标类型不能为空") } key := c.getMetricsByTypeKey(metricType) data, err := json.Marshal(metrics) if err != nil { return fmt.Errorf("序列化指标列表失败: %w", err) } err = c.client.Set(ctx, key, data, expiration).Err() if err != nil { return fmt.Errorf("设置指标列表缓存失败: %w", err) } return nil } // GetMetricsByType 获取按类型分组的指标缓存 func (c *RedisStatisticsCache) GetMetricsByType(ctx context.Context, metricType string) ([]*entities.StatisticsMetric, error) { if metricType == "" { return nil, fmt.Errorf("指标类型不能为空") } key := c.getMetricsByTypeKey(metricType) data, err := c.client.Get(ctx, key).Result() if err != nil { if err == redis.Nil { return nil, nil // 缓存未命中 } return nil, fmt.Errorf("获取指标列表缓存失败: %w", err) } var metrics []*entities.StatisticsMetric err = json.Unmarshal([]byte(data), &metrics) if err != nil { return nil, fmt.Errorf("反序列化指标列表失败: %w", err) } return metrics, nil } // DeleteMetricsByType 删除按类型分组的指标缓存 func (c *RedisStatisticsCache) DeleteMetricsByType(ctx context.Context, metricType string) error { if metricType == "" { return fmt.Errorf("指标类型不能为空") } key := c.getMetricsByTypeKey(metricType) err := c.client.Del(ctx, key).Err() if err != nil { return fmt.Errorf("删除指标列表缓存失败: %w", err) } return nil } // ================ 实时指标缓存 ================ // SetRealtimeMetrics 设置实时指标缓存 func (c *RedisStatisticsCache) SetRealtimeMetrics(ctx context.Context, metricType string, metrics map[string]float64, expiration time.Duration) error { if metricType == "" { return fmt.Errorf("指标类型不能为空") } key := c.getRealtimeMetricsKey(metricType) data, err := json.Marshal(metrics) if err != nil { return fmt.Errorf("序列化实时指标失败: %w", err) } err = c.client.Set(ctx, key, data, expiration).Err() if err != nil { return fmt.Errorf("设置实时指标缓存失败: %w", err) } return nil } // GetRealtimeMetrics 获取实时指标缓存 func (c *RedisStatisticsCache) GetRealtimeMetrics(ctx context.Context, metricType string) (map[string]float64, error) { if metricType == "" { return nil, fmt.Errorf("指标类型不能为空") } key := c.getRealtimeMetricsKey(metricType) data, err := c.client.Get(ctx, key).Result() if err != nil { if err == redis.Nil { return nil, nil // 缓存未命中 } return nil, fmt.Errorf("获取实时指标缓存失败: %w", err) } var metrics map[string]float64 err = json.Unmarshal([]byte(data), &metrics) if err != nil { return nil, fmt.Errorf("反序列化实时指标失败: %w", err) } return metrics, nil } // UpdateRealtimeMetric 更新实时指标 func (c *RedisStatisticsCache) UpdateRealtimeMetric(ctx context.Context, metricType, metricName string, value float64, expiration time.Duration) error { if metricType == "" || metricName == "" { return fmt.Errorf("指标类型和名称不能为空") } // 获取现有指标 metrics, err := c.GetRealtimeMetrics(ctx, metricType) if err != nil { return fmt.Errorf("获取实时指标失败: %w", err) } if metrics == nil { metrics = make(map[string]float64) } // 更新指标值 metrics[metricName] = value // 保存更新后的指标 err = c.SetRealtimeMetrics(ctx, metricType, metrics, expiration) if err != nil { return fmt.Errorf("更新实时指标失败: %w", err) } return nil } // ================ 报告缓存 ================ // SetReport 设置报告缓存 func (c *RedisStatisticsCache) SetReport(ctx context.Context, report *entities.StatisticsReport, expiration time.Duration) error { if report == nil { return fmt.Errorf("统计报告不能为空") } key := c.getReportKey(report.ID) data, err := json.Marshal(report) if err != nil { return fmt.Errorf("序列化报告失败: %w", err) } err = c.client.Set(ctx, key, data, expiration).Err() if err != nil { return fmt.Errorf("设置报告缓存失败: %w", err) } return nil } // GetReport 获取报告缓存 func (c *RedisStatisticsCache) GetReport(ctx context.Context, reportID string) (*entities.StatisticsReport, error) { if reportID == "" { return nil, fmt.Errorf("报告ID不能为空") } key := c.getReportKey(reportID) data, err := c.client.Get(ctx, key).Result() if err != nil { if err == redis.Nil { return nil, nil // 缓存未命中 } return nil, fmt.Errorf("获取报告缓存失败: %w", err) } var report entities.StatisticsReport err = json.Unmarshal([]byte(data), &report) if err != nil { return nil, fmt.Errorf("反序列化报告失败: %w", err) } return &report, nil } // DeleteReport 删除报告缓存 func (c *RedisStatisticsCache) DeleteReport(ctx context.Context, reportID string) error { if reportID == "" { return fmt.Errorf("报告ID不能为空") } key := c.getReportKey(reportID) err := c.client.Del(ctx, key).Err() if err != nil { return fmt.Errorf("删除报告缓存失败: %w", err) } return nil } // ================ 仪表板缓存 ================ // SetDashboard 设置仪表板缓存 func (c *RedisStatisticsCache) SetDashboard(ctx context.Context, dashboard *entities.StatisticsDashboard, expiration time.Duration) error { if dashboard == nil { return fmt.Errorf("统计仪表板不能为空") } key := c.getDashboardKey(dashboard.ID) data, err := json.Marshal(dashboard) if err != nil { return fmt.Errorf("序列化仪表板失败: %w", err) } err = c.client.Set(ctx, key, data, expiration).Err() if err != nil { return fmt.Errorf("设置仪表板缓存失败: %w", err) } return nil } // GetDashboard 获取仪表板缓存 func (c *RedisStatisticsCache) GetDashboard(ctx context.Context, dashboardID string) (*entities.StatisticsDashboard, error) { if dashboardID == "" { return nil, fmt.Errorf("仪表板ID不能为空") } key := c.getDashboardKey(dashboardID) data, err := c.client.Get(ctx, key).Result() if err != nil { if err == redis.Nil { return nil, nil // 缓存未命中 } return nil, fmt.Errorf("获取仪表板缓存失败: %w", err) } var dashboard entities.StatisticsDashboard err = json.Unmarshal([]byte(data), &dashboard) if err != nil { return nil, fmt.Errorf("反序列化仪表板失败: %w", err) } return &dashboard, nil } // DeleteDashboard 删除仪表板缓存 func (c *RedisStatisticsCache) DeleteDashboard(ctx context.Context, dashboardID string) error { if dashboardID == "" { return fmt.Errorf("仪表板ID不能为空") } key := c.getDashboardKey(dashboardID) err := c.client.Del(ctx, key).Err() if err != nil { return fmt.Errorf("删除仪表板缓存失败: %w", err) } return nil } // SetDashboardData 设置仪表板数据缓存 func (c *RedisStatisticsCache) SetDashboardData(ctx context.Context, userRole string, data interface{}, expiration time.Duration) error { if userRole == "" { return fmt.Errorf("用户角色不能为空") } key := c.getDashboardDataKey(userRole) jsonData, err := json.Marshal(data) if err != nil { return fmt.Errorf("序列化仪表板数据失败: %w", err) } err = c.client.Set(ctx, key, jsonData, expiration).Err() if err != nil { return fmt.Errorf("设置仪表板数据缓存失败: %w", err) } return nil } // GetDashboardData 获取仪表板数据缓存 func (c *RedisStatisticsCache) GetDashboardData(ctx context.Context, userRole string) (interface{}, error) { if userRole == "" { return nil, fmt.Errorf("用户角色不能为空") } key := c.getDashboardDataKey(userRole) data, err := c.client.Get(ctx, key).Result() if err != nil { if err == redis.Nil { return nil, nil // 缓存未命中 } return nil, fmt.Errorf("获取仪表板数据缓存失败: %w", err) } var result interface{} err = json.Unmarshal([]byte(data), &result) if err != nil { return nil, fmt.Errorf("反序列化仪表板数据失败: %w", err) } return result, nil } // DeleteDashboardData 删除仪表板数据缓存 func (c *RedisStatisticsCache) DeleteDashboardData(ctx context.Context, userRole string) error { if userRole == "" { return fmt.Errorf("用户角色不能为空") } key := c.getDashboardDataKey(userRole) err := c.client.Del(ctx, key).Err() if err != nil { return fmt.Errorf("删除仪表板数据缓存失败: %w", err) } return nil } // ================ 缓存键生成 ================ // getMetricKey 获取指标缓存键 func (c *RedisStatisticsCache) getMetricKey(metricID string) string { return c.prefix + "metric:" + metricID } // getMetricsByTypeKey 获取按类型分组的指标缓存键 func (c *RedisStatisticsCache) getMetricsByTypeKey(metricType string) string { return c.prefix + "metrics:type:" + metricType } // getRealtimeMetricsKey 获取实时指标缓存键 func (c *RedisStatisticsCache) getRealtimeMetricsKey(metricType string) string { return c.prefix + "realtime:" + metricType } // getReportKey 获取报告缓存键 func (c *RedisStatisticsCache) getReportKey(reportID string) string { return c.prefix + "report:" + reportID } // getDashboardKey 获取仪表板缓存键 func (c *RedisStatisticsCache) getDashboardKey(dashboardID string) string { return c.prefix + "dashboard:" + dashboardID } // getDashboardDataKey 获取仪表板数据缓存键 func (c *RedisStatisticsCache) getDashboardDataKey(userRole string) string { return c.prefix + "dashboard:data:" + userRole } // ================ 批量操作 ================ // BatchDeleteMetrics 批量删除指标缓存 func (c *RedisStatisticsCache) BatchDeleteMetrics(ctx context.Context, metricIDs []string) error { if len(metricIDs) == 0 { return nil } keys := make([]string, len(metricIDs)) for i, id := range metricIDs { keys[i] = c.getMetricKey(id) } err := c.client.Del(ctx, keys...).Err() if err != nil { return fmt.Errorf("批量删除指标缓存失败: %w", err) } return nil } // BatchDeleteReports 批量删除报告缓存 func (c *RedisStatisticsCache) BatchDeleteReports(ctx context.Context, reportIDs []string) error { if len(reportIDs) == 0 { return nil } keys := make([]string, len(reportIDs)) for i, id := range reportIDs { keys[i] = c.getReportKey(id) } err := c.client.Del(ctx, keys...).Err() if err != nil { return fmt.Errorf("批量删除报告缓存失败: %w", err) } return nil } // BatchDeleteDashboards 批量删除仪表板缓存 func (c *RedisStatisticsCache) BatchDeleteDashboards(ctx context.Context, dashboardIDs []string) error { if len(dashboardIDs) == 0 { return nil } keys := make([]string, len(dashboardIDs)) for i, id := range dashboardIDs { keys[i] = c.getDashboardKey(id) } err := c.client.Del(ctx, keys...).Err() if err != nil { return fmt.Errorf("批量删除仪表板缓存失败: %w", err) } return nil } // ================ 缓存清理 ================ // ClearAllStatisticsCache 清理所有统计缓存 func (c *RedisStatisticsCache) ClearAllStatisticsCache(ctx context.Context) error { pattern := c.prefix + "*" keys, err := c.client.Keys(ctx, pattern).Result() if err != nil { return fmt.Errorf("获取缓存键失败: %w", err) } if len(keys) > 0 { err = c.client.Del(ctx, keys...).Err() if err != nil { return fmt.Errorf("清理统计缓存失败: %w", err) } } return nil } // ClearMetricsCache 清理指标缓存 func (c *RedisStatisticsCache) ClearMetricsCache(ctx context.Context) error { pattern := c.prefix + "metric:*" keys, err := c.client.Keys(ctx, pattern).Result() if err != nil { return fmt.Errorf("获取指标缓存键失败: %w", err) } if len(keys) > 0 { err = c.client.Del(ctx, keys...).Err() if err != nil { return fmt.Errorf("清理指标缓存失败: %w", err) } } return nil } // ClearRealtimeCache 清理实时缓存 func (c *RedisStatisticsCache) ClearRealtimeCache(ctx context.Context) error { pattern := c.prefix + "realtime:*" keys, err := c.client.Keys(ctx, pattern).Result() if err != nil { return fmt.Errorf("获取实时缓存键失败: %w", err) } if len(keys) > 0 { err = c.client.Del(ctx, keys...).Err() if err != nil { return fmt.Errorf("清理实时缓存失败: %w", err) } } return nil } // ClearReportsCache 清理报告缓存 func (c *RedisStatisticsCache) ClearReportsCache(ctx context.Context) error { pattern := c.prefix + "report:*" keys, err := c.client.Keys(ctx, pattern).Result() if err != nil { return fmt.Errorf("获取报告缓存键失败: %w", err) } if len(keys) > 0 { err = c.client.Del(ctx, keys...).Err() if err != nil { return fmt.Errorf("清理报告缓存失败: %w", err) } } return nil } // ClearDashboardsCache 清理仪表板缓存 func (c *RedisStatisticsCache) ClearDashboardsCache(ctx context.Context) error { pattern := c.prefix + "dashboard:*" keys, err := c.client.Keys(ctx, pattern).Result() if err != nil { return fmt.Errorf("获取仪表板缓存键失败: %w", err) } if len(keys) > 0 { err = c.client.Del(ctx, keys...).Err() if err != nil { return fmt.Errorf("清理仪表板缓存失败: %w", err) } } return nil }