This commit is contained in:
2025-09-12 01:15:09 +08:00
parent c563b2266b
commit e05ad9e223
103 changed files with 20034 additions and 1041 deletions

View File

@@ -0,0 +1,584 @@
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
}