Browse Source

refactor(admin): 重构 WafLog 服务并添加数据清洗功能

- 在 WafLogService 中集成 WafLogDataCleanService- 新增 CleanedExtraData 结构用于存储清洗后的日志数据
- 使用 gjson 实现高效动态 JSON 解析
- 优化后端列表提取逻辑,支持多种数据格式
- 提取公共字段和动态字段,提高数据利用率- 重构导出逻辑,使用清洗后的数据填充 Excel 表格
fusu 3 days ago
parent
commit
aa069f3eb9

+ 2 - 1
api/v1/admin/wafLog.go

@@ -57,10 +57,11 @@ type ExportWafLogRes struct {
 	Name       string `json:"name" form:"name"`
 	RequestIp  string `json:"requestIp" form:"requestIp"`
 	HostId     int    `json:"hostId" form:"hostId"`
+	RuleId     int    `json:"ruleId,omitempty" form:"ruleId" `
 	ApiName    string `json:"apiName" form:"apiName"`
 	AddrBackendList  interface{} `json:"addrBackendList" form:"addrBackendList"`
 	Domain     string `json:"domain" form:"domain"`
-	CustomHost string `json:"customHost" form:"customHost"`
+	CustomHost []string `json:"customHost" form:"customHost"`
 	ExposeAddr  []string `json:"exposeAddr" form:"exposeAddr"`
 	Comment    string `json:"comment" form:"comment"`
 	CreatedAt  time.Time `json:"createdAt" form:"createdAt"`

+ 1 - 0
cmd/server/wire/wire.go

@@ -104,6 +104,7 @@ var serviceSet = wire.NewSet(
 	waf.NewZzybgpService,
 	admin.NewLogService,
 	admin.NewWafLogService,
+	admin.NewWafLogDataCleanService,
 )
 
 var handlerSet = wire.NewSet(

+ 3 - 2
cmd/server/wire/wire_gen.go

@@ -77,7 +77,8 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 	logRepository := repository.NewLogRepository(repositoryRepository)
 	logService := service.NewLogService(serviceService, logRepository)
 	wafLogRepository := admin.NewWafLogRepository(repositoryRepository)
-	wafLogService := admin2.NewWafLogService(serviceService, wafLogRepository, globalLimitRepository, rabbitMQ)
+	wafLogDataCleanService := admin2.NewWafLogDataCleanService(serviceService)
+	wafLogService := admin2.NewWafLogService(serviceService, wafLogRepository, globalLimitRepository, rabbitMQ, wafLogDataCleanService)
 	gatewayipService := waf2.NewGatewayipService(serviceService, gatewayipRepository, hostService, logService, wafLogService)
 	requestService := service.NewRequestService(serviceService)
 	cdnRepository := flexCdn.NewCdnRepository(repositoryRepository)
@@ -130,7 +131,7 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 
 var repositorySet = wire.NewSet(repository.NewDB, repository.NewRedis, repository.NewCasbinEnforcer, repository.NewMongoClient, repository.NewMongoDB, repository.NewRabbitMQ, repository.NewRepository, repository.NewTransaction, admin.NewAdminRepository, admin.NewUserRepository, repository.NewGameShieldRepository, repository.NewGameShieldPublicIpRepository, waf.NewWebForwardingRepository, waf.NewTcpforwardingRepository, waf.NewUdpForWardingRepository, repository.NewGameShieldUserIpRepository, repository.NewGameShieldBackendRepository, repository.NewGameShieldSdkIpRepository, repository.NewHostRepository, waf.NewGlobalLimitRepository, repository.NewGatewayGroupRepository, repository.NewGateWayGroupIpRepository, flexCdn.NewCdnRepository, waf.NewAllowAndDenyIpRepository, flexCdn.NewProxyRepository, flexCdn.NewCcRepository, repository.NewExpiredRepository, repository.NewLogRepository, waf.NewGatewayipRepository, admin.NewGatewayIpAdminRepository, flexCdn.NewCcIpListRepository, admin.NewLogRepository, admin.NewWafLogRepository)
 
-var serviceSet = wire.NewSet(service.NewService, admin2.NewUserService, admin2.NewGatewayIpAdminService, admin2.NewAdminService, gameShield.NewGameShieldService, service.NewAoDunService, service.NewGameShieldPublicIpService, service.NewDuedateService, service.NewFormatterService, service.NewParserService, service.NewRequiredService, service.NewCrawlerService, waf2.NewWebForwardingService, waf2.NewTcpforwardingService, waf2.NewUdpForWardingService, service.NewGameShieldUserIpService, gameShield.NewGameShieldBackendService, service.NewGameShieldSdkIpService, service.NewHostService, waf2.NewGlobalLimitService, service.NewGatewayGroupService, waf2.NewWafFormatterService, service.NewGateWayGroupIpService, service.NewRequestService, flexCdn2.NewCdnService, waf2.NewAllowAndDenyIpService, flexCdn2.NewProxyService, flexCdn2.NewSslCertService, flexCdn2.NewWebsocketService, waf2.NewCcService, service.NewLogService, waf2.NewGatewayipService, waf2.NewCcIpListService, waf2.NewCdnLogService, waf2.NewBuildAudunService, waf2.NewZzybgpService, admin2.NewLogService, admin2.NewWafLogService)
+var serviceSet = wire.NewSet(service.NewService, admin2.NewUserService, admin2.NewGatewayIpAdminService, admin2.NewAdminService, gameShield.NewGameShieldService, service.NewAoDunService, service.NewGameShieldPublicIpService, service.NewDuedateService, service.NewFormatterService, service.NewParserService, service.NewRequiredService, service.NewCrawlerService, waf2.NewWebForwardingService, waf2.NewTcpforwardingService, waf2.NewUdpForWardingService, service.NewGameShieldUserIpService, gameShield.NewGameShieldBackendService, service.NewGameShieldSdkIpService, service.NewHostService, waf2.NewGlobalLimitService, service.NewGatewayGroupService, waf2.NewWafFormatterService, service.NewGateWayGroupIpService, service.NewRequestService, flexCdn2.NewCdnService, waf2.NewAllowAndDenyIpService, flexCdn2.NewProxyService, flexCdn2.NewSslCertService, flexCdn2.NewWebsocketService, waf2.NewCcService, service.NewLogService, waf2.NewGatewayipService, waf2.NewCcIpListService, waf2.NewCdnLogService, waf2.NewBuildAudunService, waf2.NewZzybgpService, admin2.NewLogService, admin2.NewWafLogService, admin2.NewWafLogDataCleanService)
 
 var handlerSet = wire.NewSet(handler.NewHandler, admin3.NewUserHandler, admin3.NewAdminHandler, admin3.NewGatewayIpAdminHandler, handler.NewGameShieldHandler, handler.NewGameShieldPublicIpHandler, waf3.NewWebForwardingHandler, waf3.NewTcpforwardingHandler, waf3.NewUdpForWardingHandler, handler.NewGameShieldUserIpHandler, handler.NewGameShieldBackendHandler, handler.NewGameShieldSdkIpHandler, handler.NewHostHandler, waf3.NewGlobalLimitHandler, handler.NewGatewayGroupHandler, handler.NewGateWayGroupIpHandler, waf3.NewAllowAndDenyIpHandler, waf3.NewCcHandler, waf3.NewGatewayipHandler, waf3.NewCcIpListHandler, waf3.NewCdnLogHandler, admin3.NewLogHandler, admin3.NewWafLogHandler)
 

+ 1 - 0
cmd/task/wire/wire.go

@@ -104,6 +104,7 @@ var serviceSet = wire.NewSet(
 	waf.NewBuildAudunService,
 	waf.NewZzybgpService,
 	admin2.NewWafLogService,
+	admin2.NewWafLogDataCleanService,
 
 )
 

+ 3 - 2
cmd/task/wire/wire_gen.go

@@ -73,7 +73,8 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 	logRepository := repository.NewLogRepository(repositoryRepository)
 	logService := service.NewLogService(serviceService, logRepository)
 	wafLogRepository := admin.NewWafLogRepository(repositoryRepository)
-	wafLogService := admin2.NewWafLogService(serviceService, wafLogRepository, globalLimitRepository, rabbitMQ)
+	wafLogDataCleanService := admin2.NewWafLogDataCleanService(serviceService)
+	wafLogService := admin2.NewWafLogService(serviceService, wafLogRepository, globalLimitRepository, rabbitMQ, wafLogDataCleanService)
 	gatewayipService := waf2.NewGatewayipService(serviceService, gatewayipRepository, hostService, logService, wafLogService)
 	wafFormatterService := waf2.NewWafFormatterService(serviceService, globalLimitRepository, hostRepository, requiredService, parserService, tcpforwardingRepository, udpForWardingRepository, webForwardingRepository, rabbitMQ, hostService, gatewayipRepository, gatewayipService, cdnService, cdnRepository)
 	proxyRepository := flexCdn.NewProxyRepository(repositoryRepository)
@@ -113,7 +114,7 @@ var jobSet = wire.NewSet(job.NewJob, job.NewUserJob, job.NewWhitelistJob, job.Ne
 
 var serverSet = wire.NewSet(server.NewTaskServer, server.NewJobServer)
 
-var serviceSet = wire.NewSet(service.NewService, service.NewAoDunService, gameShield.NewGameShieldService, service.NewCrawlerService, service.NewGameShieldPublicIpService, service.NewDuedateService, service.NewFormatterService, service.NewParserService, service.NewRequiredService, service.NewHostService, gameShield.NewGameShieldBackendService, service.NewGameShieldSdkIpService, service.NewGameShieldUserIpService, waf2.NewWafFormatterService, flexCdn2.NewCdnService, service.NewRequestService, waf2.NewTcpforwardingService, waf2.NewUdpForWardingService, waf2.NewWebForwardingService, flexCdn2.NewProxyService, flexCdn2.NewSslCertService, flexCdn2.NewWebsocketService, waf2.NewCcService, waf2.NewGatewayipService, service.NewLogService, waf2.NewCcIpListService, waf2.NewBuildAudunService, waf2.NewZzybgpService, admin2.NewWafLogService)
+var serviceSet = wire.NewSet(service.NewService, service.NewAoDunService, gameShield.NewGameShieldService, service.NewCrawlerService, service.NewGameShieldPublicIpService, service.NewDuedateService, service.NewFormatterService, service.NewParserService, service.NewRequiredService, service.NewHostService, gameShield.NewGameShieldBackendService, service.NewGameShieldSdkIpService, service.NewGameShieldUserIpService, waf2.NewWafFormatterService, flexCdn2.NewCdnService, service.NewRequestService, waf2.NewTcpforwardingService, waf2.NewUdpForWardingService, waf2.NewWebForwardingService, flexCdn2.NewProxyService, flexCdn2.NewSslCertService, flexCdn2.NewWebsocketService, waf2.NewCcService, waf2.NewGatewayipService, service.NewLogService, waf2.NewCcIpListService, waf2.NewBuildAudunService, waf2.NewZzybgpService, admin2.NewWafLogService, admin2.NewWafLogDataCleanService)
 
 // build App
 func newApp(task2 *server.TaskServer,

+ 31 - 104
internal/service/admin/waflog.go

@@ -74,12 +74,14 @@ func NewWafLogService(
     wafLogRepository adminRep.WafLogRepository,
 	globalLimitRepository waf.GlobalLimitRepository,
 	mq *rabbitmq.RabbitMQ,
+	wafLogDataCleanService WafLogDataCleanService,
 ) WafLogService {
 	return &wafLogService{
 		Service:        service,
 		wafLogRepository: wafLogRepository,
 		globalLimitRepository: globalLimitRepository,
 		mq : mq,
+		wafLogDataCleanService: wafLogDataCleanService,
 	}
 }
 
@@ -88,6 +90,7 @@ type wafLogService struct {
 	wafLogRepository adminRep.WafLogRepository
 	globalLimitRepository waf.GlobalLimitRepository
 	mq *rabbitmq.RabbitMQ
+	wafLogDataCleanService WafLogDataCleanService
 }
 func (s *wafLogService) getFirstPathSegment(path string) (segment []string, ok bool) {
 	// 1. 为了统一处理,先去掉路径最前面的 "/"
@@ -274,11 +277,12 @@ func (s *wafLogService) SmartExportWafLog(ctx context.Context, req adminApi.Expo
 	exportType := excel.SmartExport(count, 200)
 	
 	// 3. 设置Excel表头映射
-	headers := []string{"name", "request_ip", "host_id", "api_name", "addr_backend_list", "domain", "comment", "custom_host", "expose_addr", "created_at"}
+	headers := []string{"name", "request_ip", "host_id", "rule_id", "api_name", "addr_backend_list", "domain", "comment", "custom_host", "expose_addr", "created_at"}
 	headerMap := map[string]string{
 		"name":             "用户名",
 		"request_ip":       "请求IP",
-		"host_id":          "主机ID", 
+		"host_id":          "主机ID",
+		"rule_id":          "规则ID",
 		"api_name":         "API名称",
 		"addr_backend_list": "后端地址",
 		"domain":           "域名",
@@ -322,11 +326,12 @@ func (s *wafLogService) normalExportWafLog(ctx context.Context, req adminApi.Exp
 			"name":             item.Name,
 			"request_ip":       item.RequestIp,
 			"host_id":          item.HostId,
+			"rule_id":          item.RuleId,
 			"api_name":         item.ApiName,
 			"addr_backend_list": s.formatBackendList(item.AddrBackendList),
 			"domain":           item.Domain,
 			"comment":          item.Comment,
-			"custom_host":      item.CustomHost,
+			"custom_host":      s.formatExposeAddr(item.CustomHost),
 			"expose_addr":      s.formatExposeAddr(item.ExposeAddr),
 			"created_at":       item.CreatedAt,
 		}
@@ -386,7 +391,7 @@ func (s *wafLogService) streamExportWafLog(ctx context.Context, req adminApi.Exp
 				"addr_backend_list": s.formatBackendList(item.AddrBackendList),
 				"domain":           item.Domain,
 				"comment":          item.Comment,
-				"custom_host":      item.CustomHost,
+				"custom_host":      s.formatExposeAddr(item.CustomHost),
 				"expose_addr":      s.formatExposeAddr(item.ExposeAddr),
 				"created_at":       item.CreatedAt,
 			}
@@ -459,17 +464,20 @@ func (s *wafLogService) formatExposeAddr(exposeAddr []string) string {
 
 
 
+
+
+
+
 // convertRawDataToExportResults 将原始数据转换为导出结果(复用原有的ExPortWafLog逻辑)
 func (s *wafLogService) convertRawDataToExportResults(ctx context.Context, rawData []model.WafLog) ([]adminApi.ExportWafLogRes, error) {
 	if len(rawData) == 0 {
 		return []adminApi.ExportWafLogRes{}, nil
 	}
 
-	// 收集所有需要查询的hostId和uid,用于批量获取网关组
+	// 批量准备逻辑保持不变...
 	hostIds := make([]int64, 0, len(rawData))
 	uids := make([]int64, 0, len(rawData))
 	maxCreatedAt := time.Time{}
-	
 	for _, v := range rawData {
 		hostIds = append(hostIds, int64(v.HostId))
 		uids = append(uids, int64(v.Uid))
@@ -477,132 +485,51 @@ func (s *wafLogService) convertRawDataToExportResults(ctx context.Context, rawDa
 			maxCreatedAt = v.CreatedAt
 		}
 	}
-
-	// 批量获取网关组数据
 	gatewayMap, err := s.wafLogRepository.BatchGetWafLogGateWayIps(ctx, hostIds, uids, maxCreatedAt)
 	if err != nil {
 		s.Logger.Warn("批量获取网关组失败,降级为单个查询", zap.Error(err))
-		gatewayMap = make(map[string]model.WafLog) // 空map,后续会降级处理
+		gatewayMap = make(map[string]model.WafLog)
 	}
 
 	var res []adminApi.ExportWafLogRes
 	for _, v := range rawData {
-		var AddrBackendList interface{}
-		var customHost string
-		var port string
-		var domain string
-		var comment string
-
-		var mapData map[string]interface{}
-		err := json.Unmarshal(v.ExtraData, &mapData)
-		if err != nil {
-			// 尝试解析为数组格式
-			var arrayData []interface{}
-			if arrayErr := json.Unmarshal(v.ExtraData, &arrayData); arrayErr != nil {
-				// 如果不是符合的JSON格式,直接把原始值作为字符串处理
-				s.Logger.Warn("额外数据不是有效JSON格式,使用原始值", zap.Error(err), zap.Int("id", v.Id), 
-					zap.String("extra_data", string(v.ExtraData)))
-				mapData = map[string]interface{}{
-					"raw_data": string(v.ExtraData),
-				}
-			} else {
-				// 如果是数组格式,将数组作为值存储
-				s.Logger.Warn("额外数据为数组格式,保存为数组值", zap.Int("id", v.Id))
-				mapData = map[string]interface{}{
-					"array_data": arrayData,
-				}
-			}
-		}
+		// --- 核心改动:一行代码完成所有数据清洗 ---
+		cleanedData := s.wafLogDataCleanService.ParseWafLogExtraData(v.ExtraData, v.ApiName)
 
-		if strings.Contains(v.ApiName, "tcp") || strings.Contains(v.ApiName, "udp") || strings.Contains(v.ApiName, "web") {
-			// 安全地获取extraData
-			var extraData map[string]interface{}
-			if mapData["data"] != nil {
-				if data, ok := mapData["data"].(map[string]interface{}); ok {
-					extraData = data
-				}
-			}
-			
-			if extraData != nil {
-				if extraData["port"] != nil {
-					if portStr, ok := extraData["port"].(string); ok {
-						port = portStr
-					}
-				}
-				if extraData["domain"] != nil {
-					if domainStr, ok := extraData["domain"].(string); ok {
-						domain = domainStr
-					}
-				}
-				if extraData["backendList"] != nil {
-					if strings.Contains(v.ApiName, "web") {
-						if backendListStr, ok := extraData["backendList"].(string); ok {
-							var backendList []map[string]interface{}
-							err := json.Unmarshal([]byte(backendListStr), &backendList)
-							if err != nil {
-								s.Logger.Error("解析后端列表失败", zap.Error(err))
-								continue
-							}
-							for _, backend := range backendList {
-								if backend["addr"] != nil {
-									AddrBackendList = backend["addr"]
-								}
-								if backend["customHost"] != nil {
-									if customHostStr, ok := backend["customHost"].(string); ok {
-										customHost = customHostStr
-									}
-								}
-							}
-						}
-					} else {
-						AddrBackendList = extraData["backendList"]
-					}
-				}
-			}
-		}
-
-		if mapData["comment"] != nil {
-			if commentStr, ok := mapData["comment"].(string); ok {
-				comment = commentStr
-			}
-		}
-
-		// 优化:从批量获取的网关组数据中查找
+		// 网关 IP 获取逻辑保持不变,但使用清洗后的 port
 		var exposeAddr []string
 		key := fmt.Sprintf("%d_%d", v.HostId, v.Uid)
 		if gatewayModel, exists := gatewayMap[key]; exists {
 			var gateWayIps []string
-			if err := json.Unmarshal(gatewayModel.ExtraData, &gateWayIps); err == nil {
-				if len(gateWayIps) > 0 && port != "" {
-					for _, ip := range gateWayIps {
-						exposeAddr = append(exposeAddr, ip+":"+port)
-					}
+			if err := json.Unmarshal(gatewayModel.ExtraData, &gateWayIps); err == nil && len(gateWayIps) > 0 && cleanedData.Port != "" {
+				for _, ip := range gateWayIps {
+					exposeAddr = append(exposeAddr, ip+":"+cleanedData.Port)
 				}
 			}
 		} else {
-			// 降级:单个查询
+			// 降级查询逻辑...
 			gateWayIpModel, err := s.wafLogRepository.GetWafLogGateWayIp(ctx, int64(v.HostId), int64(v.Uid), v.CreatedAt)
 			if err == nil {
 				var gateWayIps []string
-				if err := json.Unmarshal(gateWayIpModel.ExtraData, &gateWayIps); err == nil {
-					if len(gateWayIps) > 0 && port != "" {
-						for _, ip := range gateWayIps {
-							exposeAddr = append(exposeAddr, ip+":"+port)
-						}
+				if err := json.Unmarshal(gateWayIpModel.ExtraData, &gateWayIps); err == nil && len(gateWayIps) > 0 && cleanedData.Port != "" {
+					for _, ip := range gateWayIps {
+						exposeAddr = append(exposeAddr, ip+":"+cleanedData.Port)
 					}
 				}
 			}
 		}
 
+		// 构造结果,代码更清晰
 		res = append(res, adminApi.ExportWafLogRes{
 			Name:            v.Name,
 			RequestIp:       v.RequestIp,
 			HostId:          v.HostId,
+			RuleId:          v.RuleId,
 			ApiName:         v.ApiName,
-			AddrBackendList: AddrBackendList,
-			Domain:          domain,
-			Comment:         comment,
-			CustomHost:      customHost,
+			AddrBackendList: cleanedData.AddrBackendList,
+			Domain:          cleanedData.Domain,
+			Comment:         cleanedData.Comment,
+			CustomHost:      cleanedData.CustomHost,
 			ExposeAddr:      exposeAddr,
 			CreatedAt:       v.CreatedAt,
 		})

+ 314 - 0
internal/service/admin/waflogdataclean.go

@@ -0,0 +1,314 @@
+package admin
+
+import (
+	"encoding/json"
+	"github.com/go-nunu/nunu-layout-advanced/internal/service"
+	"github.com/tidwall/gjson"
+	"go.uber.org/zap"
+)
+
+type WafLogDataCleanService interface {
+	ParseWafLogExtraData(extraDataBytes json.RawMessage, apiName string) CleanedExtraData
+}
+func NewWafLogDataCleanService(
+    service *service.Service,
+) WafLogDataCleanService {
+	return &wafLogDataCleanService{
+		Service:        service,
+
+	}
+}
+
+type wafLogDataCleanService struct {
+	*service.Service
+
+}
+
+// BackendInfo 后端服务器信息
+type BackendInfo struct {
+	Addr       string `json:"addr,omitempty"`       // 后端地址
+	CustomHost string `json:"customHost,omitempty"` // 自定义Host头
+}
+
+// CleanedExtraData 使用动态结构存储解析后的数据
+type CleanedExtraData struct {
+	// 核心字段 - 新的数组结构
+	BackendList     []BackendInfo `json:"backendList,omitempty"`     // 完整的后端信息数组
+	
+	// 向后兼容字段
+	AddrBackendList []string `json:"addrBackendList,omitempty"` // 只包含地址的数组
+	CustomHostList  []string `json:"customHostList,omitempty"`  // 只包含customHost的数组
+	CustomHost      []string `json:"customHost,omitempty"`      // customHost数组,与CustomHostList相同
+	
+	// 基础字段
+	Port            string   `json:"port,omitempty"`
+	Domain          string   `json:"domain,omitempty"`
+	Comment         string   `json:"comment,omitempty"`
+	
+	// 扩展字段
+	UID             int64    `json:"uid,omitempty"`
+	HostID          int64    `json:"hostId,omitempty"`
+	Proxy           bool     `json:"proxy,omitempty"`
+	IsHttps         int      `json:"isHttps,omitempty"`
+	
+	// 动态字段存储,用于存储任意其他字段
+	DynamicFields map[string]interface{} `json:"dynamicFields,omitempty"`
+	
+	// 原始数据备份,用于调试和回溯
+	RawData map[string]interface{} `json:"rawData,omitempty"`
+}
+
+// backendInfo 用于解析 "web" 类型中 backendList JSON 字符串的内部结构
+type backendInfo struct {
+	Addr       string `json:"addr"`
+	CustomHost string `json:"customHost"`
+}
+
+
+
+
+// parseWafLogExtraData 使用gjson解析动态JSON结构,简洁高效
+func (s *wafLogDataCleanService) ParseWafLogExtraData(extraDataBytes json.RawMessage, apiName string) CleanedExtraData {
+	var result CleanedExtraData
+	result.DynamicFields = make(map[string]interface{})
+	
+	if len(extraDataBytes) == 0 {
+		return result
+	}
+
+	jsonStr := string(extraDataBytes)
+	if !gjson.Valid(jsonStr) {
+		s.Logger.Warn("ExtraData 不是有效的JSON", zap.String("raw_data", jsonStr))
+		return result
+	}
+
+	// 解析并保存原始数据
+	var rawData map[string]interface{}
+	json.Unmarshal(extraDataBytes, &rawData)
+	result.RawData = rawData
+
+	// 使用gjson进行智能字段提取
+	s.extractWithGjson(jsonStr, apiName, &result)
+
+	return result
+}
+
+// extractWithGjson 使用gjson进行智能字段提取
+func (s *wafLogDataCleanService) extractWithGjson(jsonStr, apiName string, result *CleanedExtraData) {
+	// 提取顶层字段
+	if uid := gjson.Get(jsonStr, "uid"); uid.Exists() {
+		result.UID = uid.Int()
+	}
+	if hostId := gjson.Get(jsonStr, "hostId"); hostId.Exists() {
+		result.HostID = hostId.Int()
+	}
+	
+	// 定义常见字段路径的优先级列表
+	fieldPaths := map[string][]string{
+		"comment": {"comment", "data.comment", "desc", "description", "remark", "note"},
+		"port":    {"port", "data.port", "config.port", "server.port"},
+		"domain":  {"domain", "data.domain", "host", "data.host", "hostname"},
+		"proxy":   {"proxy", "data.proxy"},
+		"isHttps": {"isHttps", "data.isHttps"},
+	}
+	
+	// 提取基础字段
+	for fieldName, paths := range fieldPaths {
+		value := s.getFirstValidPath(jsonStr, paths)
+		if value != "" {
+			switch fieldName {
+			case "comment":
+				result.Comment = value
+			case "port":
+				result.Port = value
+			case "domain":
+				result.Domain = value
+			case "proxy":
+				result.Proxy = gjson.Get(jsonStr, s.getFirstValidPathName(jsonStr, paths)).Bool()
+			case "isHttps":
+				result.IsHttps = int(gjson.Get(jsonStr, s.getFirstValidPathName(jsonStr, paths)).Int())
+			}
+
+		}
+	}
+	
+	// 智能提取 backendList
+	s.extractBackendListWithGjson(jsonStr, apiName, result)
+	
+	// 提取所有其他动态字段
+	s.extractDynamicFields(jsonStr, result)
+}
+
+// getFirstValidPath 从多个路径中获取第一个有效值
+func (s *wafLogDataCleanService) getFirstValidPath(jsonStr string, paths []string) string {
+	for _, path := range paths {
+		if value := gjson.Get(jsonStr, path); value.Exists() && value.String() != "" {
+			return value.String()
+		}
+	}
+	return ""
+}
+
+// getFirstValidPathName 获取第一个有效路径的名称
+func (s *wafLogDataCleanService) getFirstValidPathName(jsonStr string, paths []string) string {
+	for _, path := range paths {
+		if gjson.Get(jsonStr, path).Exists() {
+			return path
+		}
+	}
+	return ""
+}
+
+// extractBackendListWithGjson 使用gjson智能提取后端列表
+func (s *wafLogDataCleanService) extractBackendListWithGjson(jsonStr, apiName string, result *CleanedExtraData) {
+	// 定义可能的后端列表字段路径
+	backendPaths := []string{
+		"backendList", "data.backendList", "backends", "data.backends", 
+		"backend_list", "data.backend_list", "servers", "data.servers",
+		"upstreams", "data.upstreams", "targets", "data.targets",
+	}
+	
+	for _, path := range backendPaths {
+		backendResult := gjson.Get(jsonStr, path)
+		if !backendResult.Exists() {
+			continue
+		}
+		
+		// 根据数据类型进行处理
+		switch {
+		case backendResult.IsArray():
+			s.processArrayBackends(backendResult, result)
+		case backendResult.IsObject():
+			s.processObjectBackends(backendResult, result)
+		default:
+			// 字符串类型,可能是JSON字符串或单个地址
+			s.processStringBackends(backendResult.String(), result)
+		}
+		
+		// 找到有效数据就退出
+		if len(result.AddrBackendList) > 0 || len(result.CustomHostList) > 0 {
+			break
+		}
+	}
+}
+
+// processArrayBackends 处理数组格式的后端列表
+func (s *wafLogDataCleanService) processArrayBackends(backendResult gjson.Result, result *CleanedExtraData) {
+	backendResult.ForEach(func(key, value gjson.Result) bool {
+		if value.IsObject() {
+			// 创建BackendInfo结构
+			backend := BackendInfo{}
+			
+			// 尝试提取地址字段
+			addr := s.getFirstValidPath(value.Raw, []string{"addr", "address", "host", "server", "endpoint", "url"})
+			if addr != "" {
+				backend.Addr = addr
+				result.AddrBackendList = append(result.AddrBackendList, addr)
+			}
+			
+			// 提取customHost
+			if customHost := gjson.Get(value.Raw, "customHost").String(); customHost != "" {
+				backend.CustomHost = customHost
+				result.CustomHostList = append(result.CustomHostList, customHost)
+				result.CustomHost = append(result.CustomHost, customHost)
+			}
+			
+			// 只有当有有效数据时才添加到BackendList
+			if backend.Addr != "" || backend.CustomHost != "" {
+				result.BackendList = append(result.BackendList, backend)
+			}
+		} else {
+			// 直接作为地址处理
+			if addr := value.String(); addr != "" {
+				result.AddrBackendList = append(result.AddrBackendList, addr)
+				result.BackendList = append(result.BackendList, BackendInfo{Addr: addr})
+			}
+		}
+		return true
+	})
+}
+
+// processObjectBackends 处理对象格式的后端列表
+func (s *wafLogDataCleanService) processObjectBackends(backendResult gjson.Result, result *CleanedExtraData) {
+	// 创建BackendInfo结构
+	backend := BackendInfo{}
+	
+	addr := s.getFirstValidPath(backendResult.Raw, []string{"addr", "address", "host", "server", "endpoint", "url"})
+	if addr != "" {
+		backend.Addr = addr
+		result.AddrBackendList = append(result.AddrBackendList, addr)
+	}
+	
+	if customHost := gjson.Get(backendResult.Raw, "customHost").String(); customHost != "" {
+		backend.CustomHost = customHost
+		result.CustomHostList = append(result.CustomHostList, customHost)
+		result.CustomHost = append(result.CustomHost, customHost)
+	}
+	
+	// 只有当有有效数据时才添加到BackendList
+	if backend.Addr != "" || backend.CustomHost != "" {
+		result.BackendList = append(result.BackendList, backend)
+	}
+}
+
+// processStringBackends 处理字符串格式的后端列表
+func (s *wafLogDataCleanService) processStringBackends(backendStr string, result *CleanedExtraData) {
+	if backendStr == "" {
+		return
+	}
+	
+	// 尝试作为JSON解析
+	if gjson.Valid(backendStr) {
+		parsed := gjson.Parse(backendStr)
+		if parsed.IsArray() {
+			s.processArrayBackends(parsed, result)
+			return
+		} else if parsed.IsObject() {
+			s.processObjectBackends(parsed, result)
+			return
+		}
+	}
+	
+	// 作为单个地址处理
+	result.AddrBackendList = append(result.AddrBackendList, backendStr)
+	result.BackendList = append(result.BackendList, BackendInfo{Addr: backendStr})
+}
+
+// extractDynamicFields 提取所有动态字段到DynamicFields中
+func (s *wafLogDataCleanService) extractDynamicFields(jsonStr string, result *CleanedExtraData) {
+	// 已知的核心字段,不放入动态字段中
+	knownFields := map[string]bool{
+		"comment": true, "port": true, "domain": true, 
+		"backendList": true, "backends": true, "backend_list": true,
+		"data": true, // data字段的内容会被单独处理
+	}
+	
+	// 遍历顶层字段
+	gjson.Parse(jsonStr).ForEach(func(key, value gjson.Result) bool {
+		fieldName := key.String()
+		if !knownFields[fieldName] {
+			// 将未知字段存储到动态字段中
+			result.DynamicFields[fieldName] = value.Value()
+		}
+		return true
+	})
+	
+	// 特殊处理data字段中的未知字段
+	dataResult := gjson.Get(jsonStr, "data")
+	if dataResult.Exists() && dataResult.IsObject() {
+		dataKnownFields := map[string]bool{
+			"port": true, "domain": true, "backendList": true, 
+			"backends": true, "backend_list": true,
+		}
+		
+		dataResult.ForEach(func(key, value gjson.Result) bool {
+			fieldName := key.String()
+			if !dataKnownFields[fieldName] {
+				// 使用data.前缀避免冲突
+				result.DynamicFields["data."+fieldName] = value.Value()
+			}
+			return true
+		})
+	}
+}
+