Prechádzať zdrojové kódy

sfeat(waflog): 实现日志导出功能支持分表查询

- 更新 buildExportQuery 函数以支持分表查询
- 新增 getExportTimeRange 函数解析时间范围
- 新增 buildSubQueryForTable 函数构建子查询
- 重构 ExportWafLogWithPagination 函数支持分表查询
- 更新 GetWafLogExportCount 函数以支持分表统计
- 优化日志导出相关的查询性能和逻辑
fusu 20 hodín pred
rodič
commit
5a11997191
3 zmenil súbory, kde vykonal 170 pridanie a 35 odobranie
  1. 0 2
      go.mod
  2. 0 5
      go.sum
  3. 170 28
      internal/repository/admin/waflog.go

+ 0 - 2
go.mod

@@ -85,7 +85,6 @@ require (
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/golang/snappy v1.0.0 // indirect
 	github.com/google/go-querystring v1.1.0 // indirect
-	github.com/google/subcommands v1.2.0 // indirect
 	github.com/gorilla/websocket v1.4.2 // indirect
 	github.com/hashicorp/errwrap v1.0.0 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
@@ -148,7 +147,6 @@ require (
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/arch v0.3.0 // indirect
 	golang.org/x/exp v0.0.0-20221208152030-732eee02a75a // indirect
-	golang.org/x/mod v0.25.0 // indirect
 	golang.org/x/sys v0.34.0 // indirect
 	golang.org/x/text v0.27.0 // indirect
 	golang.org/x/tools v0.34.0 // indirect

+ 0 - 5
go.sum

@@ -280,16 +280,12 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
 github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
 github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
-github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
 github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
-github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
 github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
 github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -876,7 +872,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=

+ 170 - 28
internal/repository/admin/waflog.go

@@ -36,10 +36,10 @@ type wafLogRepository struct {
 	*repository.Repository
 }
 
-// buildExportQuery 是一个辅助函数,用于构建导出日志的公共查询条件
-func (r *wafLogRepository) buildExportQuery(ctx context.Context, req adminApi.ExportWafLog) *gorm.DB {
-	// 使用 Table("waf_log as wl") 是为了给主表起一个别名,方便子查询中引用
-	query := r.DBWithName(ctx, "admin").Model(&model.WafLog{}).Table("waf_log as wl")
+// buildExportQuery 是一个辅助函数,用于构建导出日志的公共查询条件(支持分表)
+func (r *wafLogRepository) buildExportQuery(ctx context.Context, req adminApi.ExportWafLog, tableName string) *gorm.DB {
+	// 使用传入的表名,给表起一个别名,方便子查询中引用
+	query := r.DBWithName(ctx, "admin").Model(&model.WafLog{}).Table(tableName + " as wl")
 
 	if req.RequestIp != "" {
 		query = query.Where("wl.request_ip = ?", strings.TrimSpace(req.RequestIp))
@@ -78,6 +78,43 @@ func (r *wafLogRepository) buildExportQuery(ctx context.Context, req adminApi.Ex
 	return query
 }
 
+// getExportTimeRange 根据请求参数解析时间范围,用于确定需要查询的分表
+func (r *wafLogRepository) getExportTimeRange(req adminApi.ExportWafLog) (*time.Time, *time.Time) {
+	var startTime, endTime *time.Time
+	
+	if req.StartTime != "" {
+		if t, err := time.Parse("2006-01-02 15:04:05", strings.TrimSpace(req.StartTime)); err == nil {
+			startTime = &t
+		} else if t, err := time.Parse("2006-01-02", strings.TrimSpace(req.StartTime)); err == nil {
+			startTime = &t
+		}
+	}
+	
+	if req.EndTime != "" {
+		if t, err := time.Parse("2006-01-02 15:04:05", strings.TrimSpace(req.EndTime)); err == nil {
+			endTime = &t
+		} else if t, err := time.Parse("2006-01-02", strings.TrimSpace(req.EndTime)); err == nil {
+			endTime = &t
+		}
+	}
+	
+	return startTime, endTime
+}
+
+// buildSubQueryForTable 为指定的表构建子查询
+func (r *wafLogRepository) buildSubQueryForTable(ctx context.Context, tableName string) *gorm.DB {
+	// 需要在所有可能包含"分配网关组"数据的表中查找
+	// 这里简化处理,先在当前表中查找,如果需要跨表查找可以进一步优化
+	return r.DBWithName(ctx, "admin").Table(tableName).
+		Select("extra_data").
+		Where("api_name = ?", "分配网关组").
+		Where("host_id = wl.host_id").
+		Where("uid = wl.uid").
+		Where("created_at <= wl.created_at").
+		Order("created_at DESC").
+		Limit(1)
+}
+
 
 func (r *wafLogRepository) GetWafLog(ctx context.Context, id int64) (*model.WafLog, error) {
 	var res model.WafLog
@@ -338,54 +375,159 @@ func (r *wafLogRepository) ExportWafLog(ctx context.Context, req adminApi.Export
 	return r.ExportWafLogWithPagination(ctx, req, 0, 0)
 }
 
-// ExportWafLogWithPagination 使用子查询获取每条日志在当时时间点的正确网关组IP
+// ExportWafLogWithPagination 使用子查询获取每条日志在当时时间点的正确网关组IP(支持分表)
 func (r *wafLogRepository) ExportWafLogWithPagination(ctx context.Context, req adminApi.ExportWafLog, page, pageSize int) ([]model.WafLogWithGatewayIP, error) {
-	var res []model.WafLogWithGatewayIP
+	// 1. 解析时间范围
+	startTime, endTime := r.getExportTimeRange(req)
 	
-	// 1. 使用辅助函数构建基础查询
-	query := r.buildExportQuery(ctx, req)
+	// 2. 获取需要查询的分表
+	existingTables := r.Manager.GetTableNamesWithExistenceCheck(r.DBWithName(ctx, "admin"), "waf_log", startTime, endTime)
+	
+	if len(existingTables) == 0 {
+		return []model.WafLogWithGatewayIP{}, nil
+	}
+	
+	if len(existingTables) == 1 {
+		// 单表查询
+		return r.exportFromSingleTable(ctx, req, existingTables[0], page, pageSize)
+	}
+	
+	// 多表查询(不分页,获取所有数据)
+	if page > 0 && pageSize > 0 {
+		return r.exportFromMultipleTablesWithPagination(ctx, req, existingTables, page, pageSize)
+	} else {
+		return r.exportFromMultipleTables(ctx, req, existingTables)
+	}
+}
 
+// exportFromSingleTable 从单个表导出数据
+func (r *wafLogRepository) exportFromSingleTable(ctx context.Context, req adminApi.ExportWafLog, tableName string, page, pageSize int) ([]model.WafLogWithGatewayIP, error) {
+	var res []model.WafLogWithGatewayIP
+	
+	// 1. 构建基础查询
+	query := r.buildExportQuery(ctx, req, tableName)
+	
 	// 2. 构建子查询
-	subQuery := r.DBWithName(ctx, "admin").Model(&model.WafLog{}).
-		Select("extra_data").
-		Where("api_name = ?", "分配网关组").
-		Where("host_id = wl.host_id").
-		Where("uid = wl.uid").
-		Where("created_at <= wl.created_at").
-		Order("created_at DESC").
-		Limit(1)
-
-	// 3. 添加 Select 和分页
+	subQuery := r.buildSubQueryForTable(ctx, tableName)
+	
+	// 3. 添加 Select
 	query = query.Select("wl.*, (?) as gateway_ip_data", subQuery)
+	
+	// 4. 添加分页
 	if page > 0 && pageSize > 0 {
 		offset := (page - 1) * pageSize
 		query = query.Offset(offset).Limit(pageSize)
 	}
-
-	// 4. 执行查询
+	
+	// 5. 执行查询
 	if err := query.Find(&res).Error; err != nil {
 		if err == gorm.ErrRecordNotFound {
 			return []model.WafLogWithGatewayIP{}, nil
 		}
 		return nil, err
 	}
-
+	
 	return res, nil
 }
 
+// exportFromMultipleTables 从多个表导出所有数据(不分页)
+func (r *wafLogRepository) exportFromMultipleTables(ctx context.Context, req adminApi.ExportWafLog, tableNames []string) ([]model.WafLogWithGatewayIP, error) {
+	var allResults []model.WafLogWithGatewayIP
+	
+	for _, tableName := range tableNames {
+		tableResults, err := r.exportFromSingleTable(ctx, req, tableName, 0, 0)
+		if err != nil {
+			return nil, fmt.Errorf("查询表 %s 失败: %v", tableName, err)
+		}
+		
+		allResults = append(allResults, tableResults...)
+	}
+	
+	return allResults, nil
+}
 
-// GetWafLogExportCount 获取导出数据总数(已优化)
+// exportFromMultipleTablesWithPagination 从多个表导出数据(支持分页)
+func (r *wafLogRepository) exportFromMultipleTablesWithPagination(ctx context.Context, req adminApi.ExportWafLog, tableNames []string, page, pageSize int) ([]model.WafLogWithGatewayIP, error) {
+	var allResults []model.WafLogWithGatewayIP
+	currentOffset := (page - 1) * pageSize
+	remaining := pageSize
+	
+	for _, tableName := range tableNames {
+		if remaining <= 0 {
+			break
+		}
+		
+		// 先获取表的总记录数
+		countQuery := r.buildExportQuery(ctx, req, tableName)
+		var tableCount int64
+		if err := countQuery.Count(&tableCount).Error; err != nil {
+			return nil, fmt.Errorf("统计表 %s 记录数失败: %v", tableName, err)
+		}
+		
+		// 如果当前偏移量大于等于表记录数,跳过这个表
+		if currentOffset >= int(tableCount) {
+			currentOffset -= int(tableCount)
+			continue
+		}
+		
+		// 计算在当前表中需要查询的数据
+		tablePageSize := remaining
+		if int(tableCount) - currentOffset < tablePageSize {
+			tablePageSize = int(tableCount) - currentOffset
+		}
+		
+		tablePage := (currentOffset / pageSize) + 1
+		tableOffset := currentOffset % pageSize
+		
+		// 查询当前表数据
+		tableResults, err := r.exportFromSingleTable(ctx, req, tableName, tablePage, tablePageSize)
+		if err != nil {
+			return nil, fmt.Errorf("查询表 %s 失败: %v", tableName, err)
+		}
+		
+		// 如果有偏移量,跳过前面的记录
+		if tableOffset > 0 && len(tableResults) > tableOffset {
+			tableResults = tableResults[tableOffset:]
+		}
+		
+		allResults = append(allResults, tableResults...)
+		remaining -= len(tableResults)
+		currentOffset = 0 // 后续表从0开始
+	}
+	
+	return allResults, nil
+}
+
+
+// GetWafLogExportCount 获取导出数据总数(支持分表)
 func (r *wafLogRepository) GetWafLogExportCount(ctx context.Context, req adminApi.ExportWafLog) (int, error) {
-	var count int64
+	// 1. 解析时间范围
+	startTime, endTime := r.getExportTimeRange(req)
 	
-	// 直接复用 buildExportQuery 来构建查询
-	query := r.buildExportQuery(ctx, req)
+	// 2. 获取需要查询的分表
+	existingTables := r.Manager.GetTableNamesWithExistenceCheck(r.DBWithName(ctx, "admin"), "waf_log", startTime, endTime)
 	
-	if err := query.Count(&count).Error; err != nil {
-		return 0, err
+	if len(existingTables) == 0 {
+		return 0, nil
+	}
+	
+	var totalCount int64
+	
+	// 3. 逐表统计
+	for _, tableName := range existingTables {
+		var tableCount int64
+		
+		// 使用更新后的 buildExportQuery 方法
+		query := r.buildExportQuery(ctx, req, tableName)
+		
+		if err := query.Count(&tableCount).Error; err != nil {
+			return 0, fmt.Errorf("统计表 %s 记录数失败: %v", tableName, err)
+		}
+		
+		totalCount += tableCount
 	}
 	
-	return int(count), nil
+	return int(totalCount), nil
 }