591 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			591 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | package migrations | |||
|  | 
 | |||
|  | import ( | |||
|  | 	"fmt" | |||
|  | 	"time" | |||
|  | 
 | |||
|  | 	"gorm.io/gorm" | |||
|  | 
 | |||
|  | 	"tyapi-server/internal/domains/statistics/entities" | |||
|  | ) | |||
|  | 
 | |||
|  | // StatisticsMigrationComplete 统计模块完整数据迁移 | |||
|  | type StatisticsMigrationComplete struct { | |||
|  | 	db *gorm.DB | |||
|  | } | |||
|  | 
 | |||
|  | // NewStatisticsMigrationComplete 创建统计模块完整数据迁移 | |||
|  | func NewStatisticsMigrationComplete(db *gorm.DB) *StatisticsMigrationComplete { | |||
|  | 	return &StatisticsMigrationComplete{ | |||
|  | 		db: db, | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // Migrate 执行完整的数据迁移 | |||
|  | func (m *StatisticsMigrationComplete) Migrate() error { | |||
|  | 	fmt.Println("开始执行统计模块完整数据迁移...") | |||
|  | 
 | |||
|  | 	// 1. 迁移表结构 | |||
|  | 	err := m.migrateTables() | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("迁移表结构失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 2. 创建索引 | |||
|  | 	err = m.createIndexes() | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("创建索引失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 3. 插入初始数据 | |||
|  | 	err = m.insertInitialData() | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("插入初始数据失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	fmt.Println("统计模块完整数据迁移完成") | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // migrateTables 迁移表结构 | |||
|  | func (m *StatisticsMigrationComplete) migrateTables() error { | |||
|  | 	fmt.Println("迁移统计模块表结构...") | |||
|  | 
 | |||
|  | 	// 迁移统计指标表 | |||
|  | 	err := m.db.AutoMigrate(&entities.StatisticsMetric{}) | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("迁移统计指标表失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 迁移统计报告表 | |||
|  | 	err = m.db.AutoMigrate(&entities.StatisticsReport{}) | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("迁移统计报告表失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 迁移统计仪表板表 | |||
|  | 	err = m.db.AutoMigrate(&entities.StatisticsDashboard{}) | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("迁移统计仪表板表失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	fmt.Println("统计模块表结构迁移完成") | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // createIndexes 创建索引 | |||
|  | func (m *StatisticsMigrationComplete) createIndexes() error { | |||
|  | 	fmt.Println("创建统计模块索引...") | |||
|  | 
 | |||
|  | 	// 统计指标表索引 | |||
|  | 	err := m.createStatisticsMetricsIndexes() | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("创建统计指标表索引失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 统计报告表索引 | |||
|  | 	err = m.createStatisticsReportsIndexes() | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("创建统计报告表索引失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 统计仪表板表索引 | |||
|  | 	err = m.createStatisticsDashboardsIndexes() | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("创建统计仪表板表索引失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	fmt.Println("统计模块索引创建完成") | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // createStatisticsMetricsIndexes 创建统计指标表索引 | |||
|  | func (m *StatisticsMigrationComplete) createStatisticsMetricsIndexes() error { | |||
|  | 	indexes := []string{ | |||
|  | 		// 复合索引:metric_type + date | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_metrics_type_date  | |||
|  | 		 ON statistics_metrics (metric_type, date)`, | |||
|  | 		 | |||
|  | 		// 复合索引:metric_type + dimension + date | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_metrics_type_dimension_date  | |||
|  | 		 ON statistics_metrics (metric_type, dimension, date)`, | |||
|  | 		 | |||
|  | 		// 复合索引:metric_type + metric_name + date | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_metrics_type_name_date  | |||
|  | 		 ON statistics_metrics (metric_type, metric_name, date)`, | |||
|  | 		 | |||
|  | 		// 单列索引:dimension | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_metrics_dimension  | |||
|  | 		 ON statistics_metrics (dimension)`, | |||
|  | 		 | |||
|  | 		// 单列索引:metric_name | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_metrics_name  | |||
|  | 		 ON statistics_metrics (metric_name)`, | |||
|  | 		 | |||
|  | 		// 单列索引:value(用于范围查询) | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_metrics_value  | |||
|  | 		 ON statistics_metrics (value)`, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for _, indexSQL := range indexes { | |||
|  | 		err := m.db.Exec(indexSQL).Error | |||
|  | 		if err != nil { | |||
|  | 			return fmt.Errorf("创建索引失败: %w", err) | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // createStatisticsReportsIndexes 创建统计报告表索引 | |||
|  | func (m *StatisticsMigrationComplete) createStatisticsReportsIndexes() error { | |||
|  | 	indexes := []string{ | |||
|  | 		// 复合索引:report_type + created_at | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_reports_type_created  | |||
|  | 		 ON statistics_reports (report_type, created_at)`, | |||
|  | 		 | |||
|  | 		// 复合索引:user_role + created_at | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_reports_role_created  | |||
|  | 		 ON statistics_reports (user_role, created_at)`, | |||
|  | 		 | |||
|  | 		// 复合索引:status + created_at | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_reports_status_created  | |||
|  | 		 ON statistics_reports (status, created_at)`, | |||
|  | 		 | |||
|  | 		// 单列索引:generated_by | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_reports_generated_by  | |||
|  | 		 ON statistics_reports (generated_by)`, | |||
|  | 		 | |||
|  | 		// 单列索引:expires_at | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_reports_expires_at  | |||
|  | 		 ON statistics_reports (expires_at)`, | |||
|  | 		 | |||
|  | 		// 单列索引:period | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_reports_period  | |||
|  | 		 ON statistics_reports (period)`, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for _, indexSQL := range indexes { | |||
|  | 		err := m.db.Exec(indexSQL).Error | |||
|  | 		if err != nil { | |||
|  | 			return fmt.Errorf("创建索引失败: %w", err) | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // createStatisticsDashboardsIndexes 创建统计仪表板表索引 | |||
|  | func (m *StatisticsMigrationComplete) createStatisticsDashboardsIndexes() error { | |||
|  | 	indexes := []string{ | |||
|  | 		// 复合索引:user_role + is_active | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_dashboards_role_active  | |||
|  | 		 ON statistics_dashboards (user_role, is_active)`, | |||
|  | 		 | |||
|  | 		// 复合索引:user_role + is_default | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_dashboards_role_default  | |||
|  | 		 ON statistics_dashboards (user_role, is_default)`, | |||
|  | 		 | |||
|  | 		// 单列索引:created_by | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_dashboards_created_by  | |||
|  | 		 ON statistics_dashboards (created_by)`, | |||
|  | 		 | |||
|  | 		// 单列索引:access_level | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_dashboards_access_level  | |||
|  | 		 ON statistics_dashboards (access_level)`, | |||
|  | 		 | |||
|  | 		// 单列索引:name(用于搜索) | |||
|  | 		`CREATE INDEX IF NOT EXISTS idx_statistics_dashboards_name  | |||
|  | 		 ON statistics_dashboards (name)`, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for _, indexSQL := range indexes { | |||
|  | 		err := m.db.Exec(indexSQL).Error | |||
|  | 		if err != nil { | |||
|  | 			return fmt.Errorf("创建索引失败: %w", err) | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // insertInitialData 插入初始数据 | |||
|  | func (m *StatisticsMigrationComplete) insertInitialData() error { | |||
|  | 	fmt.Println("插入统计模块初始数据...") | |||
|  | 
 | |||
|  | 	// 插入默认仪表板 | |||
|  | 	err := m.insertDefaultDashboards() | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("插入默认仪表板失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 插入初始指标数据 | |||
|  | 	err = m.insertInitialMetrics() | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("插入初始指标数据失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	fmt.Println("统计模块初始数据插入完成") | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // insertDefaultDashboards 插入默认仪表板 | |||
|  | func (m *StatisticsMigrationComplete) insertDefaultDashboards() error { | |||
|  | 	// 检查是否已存在默认仪表板 | |||
|  | 	var count int64 | |||
|  | 	err := m.db.Model(&entities.StatisticsDashboard{}).Where("is_default = ?", true).Count(&count).Error | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("检查默认仪表板失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 如果已存在默认仪表板,跳过插入 | |||
|  | 	if count > 0 { | |||
|  | 		fmt.Println("默认仪表板已存在,跳过插入") | |||
|  | 		return nil | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 管理员默认仪表板 | |||
|  | 	adminDashboard := &entities.StatisticsDashboard{ | |||
|  | 		Name:            "管理员仪表板", | |||
|  | 		Description:     "系统管理员专用仪表板,包含所有统计信息", | |||
|  | 		UserRole:        "admin", | |||
|  | 		IsDefault:       true, | |||
|  | 		IsActive:        true, | |||
|  | 		AccessLevel:     "private", | |||
|  | 		RefreshInterval: 300, | |||
|  | 		CreatedBy:       "system", | |||
|  | 		Layout:          `{"columns": 3, "rows": 4}`, | |||
|  | 		Widgets:         `[{"type": "api_calls", "position": {"x": 0, "y": 0}}, {"type": "users", "position": {"x": 1, "y": 0}}, {"type": "finance", "position": {"x": 2, "y": 0}}]`, | |||
|  | 		Settings:        `{"theme": "dark", "auto_refresh": true}`, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	err = m.db.Create(adminDashboard).Error | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("创建管理员仪表板失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 用户默认仪表板 | |||
|  | 	userDashboard := &entities.StatisticsDashboard{ | |||
|  | 		Name:            "用户仪表板", | |||
|  | 		Description:     "普通用户专用仪表板,包含基础统计信息", | |||
|  | 		UserRole:        "user", | |||
|  | 		IsDefault:       true, | |||
|  | 		IsActive:        true, | |||
|  | 		AccessLevel:     "private", | |||
|  | 		RefreshInterval: 600, | |||
|  | 		CreatedBy:       "system", | |||
|  | 		Layout:          `{"columns": 2, "rows": 3}`, | |||
|  | 		Widgets:         `[{"type": "api_calls", "position": {"x": 0, "y": 0}}, {"type": "users", "position": {"x": 1, "y": 0}}]`, | |||
|  | 		Settings:        `{"theme": "light", "auto_refresh": false}`, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	err = m.db.Create(userDashboard).Error | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("创建用户仪表板失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 经理默认仪表板 | |||
|  | 	managerDashboard := &entities.StatisticsDashboard{ | |||
|  | 		Name:            "经理仪表板", | |||
|  | 		Description:     "经理专用仪表板,包含管理相关统计信息", | |||
|  | 		UserRole:        "manager", | |||
|  | 		IsDefault:       true, | |||
|  | 		IsActive:        true, | |||
|  | 		AccessLevel:     "private", | |||
|  | 		RefreshInterval: 300, | |||
|  | 		CreatedBy:       "system", | |||
|  | 		Layout:          `{"columns": 3, "rows": 3}`, | |||
|  | 		Widgets:         `[{"type": "api_calls", "position": {"x": 0, "y": 0}}, {"type": "users", "position": {"x": 1, "y": 0}}, {"type": "finance", "position": {"x": 2, "y": 0}}]`, | |||
|  | 		Settings:        `{"theme": "dark", "auto_refresh": true}`, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	err = m.db.Create(managerDashboard).Error | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("创建经理仪表板失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 分析师默认仪表板 | |||
|  | 	analystDashboard := &entities.StatisticsDashboard{ | |||
|  | 		Name:            "分析师仪表板", | |||
|  | 		Description:     "数据分析师专用仪表板,包含详细分析信息", | |||
|  | 		UserRole:        "analyst", | |||
|  | 		IsDefault:       true, | |||
|  | 		IsActive:        true, | |||
|  | 		AccessLevel:     "private", | |||
|  | 		RefreshInterval: 180, | |||
|  | 		CreatedBy:       "system", | |||
|  | 		Layout:          `{"columns": 4, "rows": 4}`, | |||
|  | 		Widgets:         `[{"type": "api_calls", "position": {"x": 0, "y": 0}}, {"type": "users", "position": {"x": 1, "y": 0}}, {"type": "finance", "position": {"x": 2, "y": 0}}, {"type": "products", "position": {"x": 3, "y": 0}}]`, | |||
|  | 		Settings:        `{"theme": "dark", "auto_refresh": true, "show_trends": true}`, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	err = m.db.Create(analystDashboard).Error | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("创建分析师仪表板失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	fmt.Println("默认仪表板创建完成") | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // insertInitialMetrics 插入初始指标数据 | |||
|  | func (m *StatisticsMigrationComplete) insertInitialMetrics() error { | |||
|  | 	now := time.Now() | |||
|  | 	today := now.Truncate(24 * time.Hour) | |||
|  | 
 | |||
|  | 	// 检查是否已存在今日指标数据 | |||
|  | 	var count int64 | |||
|  | 	err := m.db.Model(&entities.StatisticsMetric{}).Where("date = ?", today).Count(&count).Error | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("检查指标数据失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 如果已存在今日指标数据,跳过插入 | |||
|  | 	if count > 0 { | |||
|  | 		fmt.Println("今日指标数据已存在,跳过插入") | |||
|  | 		return nil | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 插入初始API调用指标 | |||
|  | 	apiMetrics := []*entities.StatisticsMetric{ | |||
|  | 		{ | |||
|  | 			MetricType: "api_calls", | |||
|  | 			MetricName: "total_count", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "api_calls", | |||
|  | 			MetricName: "success_count", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "api_calls", | |||
|  | 			MetricName: "failed_count", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "api_calls", | |||
|  | 			MetricName: "response_time", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "api_calls", | |||
|  | 			MetricName: "avg_response_time", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 插入初始用户指标 | |||
|  | 	userMetrics := []*entities.StatisticsMetric{ | |||
|  | 		{ | |||
|  | 			MetricType: "users", | |||
|  | 			MetricName: "total_count", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "users", | |||
|  | 			MetricName: "certified_count", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "users", | |||
|  | 			MetricName: "active_count", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "users", | |||
|  | 			MetricName: "new_users_today", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 插入初始财务指标 | |||
|  | 	financeMetrics := []*entities.StatisticsMetric{ | |||
|  | 		{ | |||
|  | 			MetricType: "finance", | |||
|  | 			MetricName: "total_amount", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "finance", | |||
|  | 			MetricName: "recharge_amount", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "finance", | |||
|  | 			MetricName: "deduct_amount", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "finance", | |||
|  | 			MetricName: "recharge_count", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "finance", | |||
|  | 			MetricName: "deduct_count", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 插入初始产品指标 | |||
|  | 	productMetrics := []*entities.StatisticsMetric{ | |||
|  | 		{ | |||
|  | 			MetricType: "products", | |||
|  | 			MetricName: "total_products", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "products", | |||
|  | 			MetricName: "active_products", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "products", | |||
|  | 			MetricName: "total_subscriptions", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "products", | |||
|  | 			MetricName: "active_subscriptions", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "products", | |||
|  | 			MetricName: "new_subscriptions_today", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 插入初始认证指标 | |||
|  | 	certificationMetrics := []*entities.StatisticsMetric{ | |||
|  | 		{ | |||
|  | 			MetricType: "certification", | |||
|  | 			MetricName: "total_certifications", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "certification", | |||
|  | 			MetricName: "completed_certifications", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "certification", | |||
|  | 			MetricName: "pending_certifications", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "certification", | |||
|  | 			MetricName: "failed_certifications", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 		{ | |||
|  | 			MetricType: "certification", | |||
|  | 			MetricName: "certification_rate", | |||
|  | 			Dimension:  "realtime", | |||
|  | 			Value:      0, | |||
|  | 			Date:       today, | |||
|  | 		}, | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 批量插入所有指标 | |||
|  | 	allMetrics := append(apiMetrics, userMetrics...) | |||
|  | 	allMetrics = append(allMetrics, financeMetrics...) | |||
|  | 	allMetrics = append(allMetrics, productMetrics...) | |||
|  | 	allMetrics = append(allMetrics, certificationMetrics...) | |||
|  | 
 | |||
|  | 	err = m.db.CreateInBatches(allMetrics, 100).Error | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("批量插入初始指标失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	fmt.Println("初始指标数据创建完成") | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // Rollback 回滚迁移 | |||
|  | func (m *StatisticsMigrationComplete) Rollback() error { | |||
|  | 	fmt.Println("开始回滚统计模块数据迁移...") | |||
|  | 
 | |||
|  | 	// 删除表 | |||
|  | 	err := m.db.Migrator().DropTable(&entities.StatisticsDashboard{}) | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("删除统计仪表板表失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	err = m.db.Migrator().DropTable(&entities.StatisticsReport{}) | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("删除统计报告表失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	err = m.db.Migrator().DropTable(&entities.StatisticsMetric{}) | |||
|  | 	if err != nil { | |||
|  | 		return fmt.Errorf("删除统计指标表失败: %w", err) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	fmt.Println("统计模块数据迁移回滚完成") | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // GetTableInfo 获取表信息 | |||
|  | func (m *StatisticsMigrationComplete) GetTableInfo() map[string]interface{} { | |||
|  | 	info := make(map[string]interface{}) | |||
|  | 
 | |||
|  | 	// 获取表统计信息 | |||
|  | 	tables := []string{"statistics_metrics", "statistics_reports", "statistics_dashboards"} | |||
|  | 	 | |||
|  | 	for _, table := range tables { | |||
|  | 		var count int64 | |||
|  | 		m.db.Table(table).Count(&count) | |||
|  | 		info[table] = count | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return info | |||
|  | } |