package admin import ( "context" "fmt" v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" adminApi "github.com/go-nunu/nunu-layout-advanced/api/v1/admin" "github.com/go-nunu/nunu-layout-advanced/internal/model" "github.com/go-nunu/nunu-layout-advanced/internal/repository" "math" "strings" "time" ) type WafLogRepository interface { GetWafLog(ctx context.Context, id int64) (*model.WafLog, error) GetWafLogList(ctx context.Context, req adminApi.SearchWafLogParams) (*v1.PaginatedResponse[model.WafLog], error) AddWafLog(ctx context.Context, log *model.WafLog) error BatchAddWafLog(ctx context.Context, logs []*model.WafLog) error // 导出日志 ExportWafLog(ctx context.Context, req adminApi.ExportWafLog) ([]model.WafLog, error) // 支持分页的导出方法 ExportWafLogWithPagination(ctx context.Context, req adminApi.ExportWafLog, page, pageSize int) ([]model.WafLog, error) // 获取导出数据总数 GetWafLogExportCount(ctx context.Context, req adminApi.ExportWafLog) (int, error) // 获取网关组记录 GetWafLogGateWayIp(ctx context.Context, hostId int64, Uid int64,createdAt time.Time) (model.WafLog, error) // 批量获取网关组记录 BatchGetWafLogGateWayIps(ctx context.Context, hostIds []int64, uids []int64, maxCreatedAt time.Time) (map[string]model.WafLog, error) } func NewWafLogRepository( repository *repository.Repository, ) WafLogRepository { return &wafLogRepository{ Repository: repository, } } type wafLogRepository struct { *repository.Repository } func (r *wafLogRepository) GetWafLog(ctx context.Context, id int64) (*model.WafLog, error) { var res model.WafLog return &res, r.DBWithName(ctx,"admin").Where("id = ?", id).First(&res).Error } func (r *wafLogRepository) GetWafLogList(ctx context.Context, req adminApi.SearchWafLogParams) (*v1.PaginatedResponse[model.WafLog], error) { var res []model.WafLog var total int64 query := r.DBWithName(ctx,"admin").Model(&model.WafLog{}) if req.RequestIp != "" { trimmedName := strings.TrimSpace(req.RequestIp) // 使用 LIKE 进行模糊匹配 query = query.Where("request_ip LIKE CONCAT('%', ?, '%')", trimmedName) } if req.Uid != 0 { query = query.Where("uid = ?", req.Uid) } if req.Api != "" { trimmedName := strings.TrimSpace(req.Api) // 使用 LIKE 进行模糊匹配 query = query.Where("api LIKE CONCAT('%', ?, '%')", trimmedName) } if req.Name != "" { trimmedName := strings.TrimSpace(req.Name) // 使用 LIKE 进行模糊匹配 query = query.Where("name LIKE CONCAT('%', ?, '%')", trimmedName) } if req.RuleId != 0 { query = query.Where("rule_id = ?", req.RuleId) } if req.HostId != 0 { query = query.Where("host_id = ?", req.HostId) } if req.Api != "" { trimmedName := strings.TrimSpace(req.Api) // 使用 LIKE 进行模糊匹配 query = query.Where("api LIKE CONCAT('%', ?, '%')", trimmedName) } if req.UserAgent != "" { trimmedName := strings.TrimSpace(req.UserAgent) // 使用 LIKE 进行模糊匹配 query = query.Where("user_agent LIKE CONCAT('%', ?, '%')", trimmedName) } if req.ApiName != "" { trimmedName := strings.TrimSpace(req.ApiName) // 使用 LIKE 进行模糊匹配 query = query.Where("api_name LIKE CONCAT('%', ?, '%')", trimmedName) } if req.ApiType != "" { query = query.Where("api_type = ?", req.ApiType) } if req.Column != "" { query = query.Order(req.Column + " " + req.Order) } if err := query.Count(&total).Error; err != nil { // 如果连计数都失败了,直接返回错误 return nil, err } page := req.Current pageSize := req.PageSize if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 10 // 默认每页 10 条 } else if pageSize > 100 { pageSize = 100 // 每页最多 100 条 } // 计算 offset (偏移量) // 例如,第 1 页,offset = (1-1)*10 = 0 (从第0条开始) // 第 2 页,offset = (2-1)*10 = 10 (从第10条开始) offset := (page - 1) * pageSize // 3. 执行最终的查询 // 在所有条件都添加完毕后,再执行 .Find() result := query.Offset(offset).Limit(pageSize).Find(&res) if result.Error != nil { // 这里的错误可能是数据库连接问题等,而不是“未找到记录” return nil, result.Error } return &v1.PaginatedResponse[model.WafLog]{ Records: res, Page: page, PageSize: pageSize, Total: total, TotalPages: int(math.Ceil(float64(total) / float64(pageSize))), }, nil } func (r *wafLogRepository) AddWafLog(ctx context.Context, log *model.WafLog) error { return r.DBWithName(ctx,"admin").Create(log).Error } func (r *wafLogRepository) BatchAddWafLog(ctx context.Context, logs []*model.WafLog) error { if len(logs) == 0 { return nil } return r.DBWithName(ctx, "admin").CreateInBatches(logs, len(logs)).Error } func (r *wafLogRepository) ExportWafLog(ctx context.Context, req adminApi.ExportWafLog) ([]model.WafLog, error) { return r.ExportWafLogWithPagination(ctx, req, 0, 0) } // ExportWafLogWithPagination 支持分页的导出方法 func (r *wafLogRepository) ExportWafLogWithPagination(ctx context.Context, req adminApi.ExportWafLog, page, pageSize int) ([]model.WafLog, error) { var res []model.WafLog query := r.DBWithName(ctx,"admin").Model(&model.WafLog{}) if req.RequestIp != "" { trimmedName := strings.TrimSpace(req.RequestIp) // 使用 LIKE 进行模糊匹配 query = query.Where("request_ip = ?", trimmedName) } if req.Uid != 0 { query = query.Where("uid = ?", req.Uid) } if req.Api != "" { trimmedName := strings.TrimSpace(req.Api) // 使用 LIKE 进行模糊匹配 query = query.Where("api = ?", trimmedName) } if req.Name != "" { trimmedName := strings.TrimSpace(req.Name) // 使用 LIKE 进行模糊匹配 query = query.Where("name = ?", trimmedName) } if req.RuleId != 0 { query = query.Where("rule_id = ?", req.RuleId) } if len(req.HostIds) > 0 { query = query.Where("host_id IN ?", req.HostIds) } if req.Api != "" { trimmedName := strings.TrimSpace(req.Api) // 使用 LIKE 进行模糊匹配 query = query.Where("api = ?", trimmedName) } if req.UserAgent != "" { trimmedName := strings.TrimSpace(req.UserAgent) // 使用 LIKE 进行模糊匹配 query = query.Where("user_agent = ?", trimmedName) } if len(req.ApiNames) > 0 { trimmedNames := make([]string, len(req.ApiNames)) for _, apiName := range req.ApiNames { trimmedNames = append(trimmedNames, strings.TrimSpace(apiName)) } // 使用 LIKE 进行模糊匹配 query = query.Where("api_name IN ?", trimmedNames) } if len(req.ApiTypes) > 0 { query = query.Where("api_type IN ?", req.ApiTypes) } if req.StartTime != "" { trimmedName := strings.TrimSpace(req.StartTime) // 使用 LIKE 进行模糊匹配 query = query.Where("create_at > ?", trimmedName) } if req.EndTime != "" { trimmedName := strings.TrimSpace(req.EndTime) // 使用 LIKE 进行模糊匹配 query = query.Where("create_at < ?", trimmedName) } // 添加分页逻辑 if page > 0 && pageSize > 0 { offset := (page - 1) * pageSize query = query.Offset(offset).Limit(pageSize) } result := query.Find(&res) if result.Error != nil { return nil, result.Error } return res, nil } // GetWafLogExportCount 获取导出数据总数 func (r *wafLogRepository) GetWafLogExportCount(ctx context.Context, req adminApi.ExportWafLog) (int, error) { var count int64 query := r.DBWithName(ctx,"admin").Model(&model.WafLog{}) // 复用ExportWafLog的查询条件 if req.RequestIp != "" { trimmedName := strings.TrimSpace(req.RequestIp) query = query.Where("request_ip = ?", trimmedName) } if req.Uid != 0 { query = query.Where("uid = ?", req.Uid) } if req.Api != "" { trimmedName := strings.TrimSpace(req.Api) query = query.Where("api = ?", trimmedName) } if req.Name != "" { trimmedName := strings.TrimSpace(req.Name) query = query.Where("name = ?", trimmedName) } if req.RuleId != 0 { query = query.Where("rule_id = ?", req.RuleId) } if len(req.HostIds) > 0 { query = query.Where("host_id IN ?", req.HostIds) } if req.UserAgent != "" { trimmedName := strings.TrimSpace(req.UserAgent) query = query.Where("user_agent = ?", trimmedName) } if len(req.ApiNames) > 0 { trimmedNames := make([]string, len(req.ApiNames)) for _, apiName := range req.ApiNames { trimmedNames = append(trimmedNames, strings.TrimSpace(apiName)) } query = query.Where("api_name IN ?", trimmedNames) } if len(req.ApiTypes) > 0 { query = query.Where("api_type IN ?", req.ApiTypes) } if req.StartTime != "" { trimmedName := strings.TrimSpace(req.StartTime) query = query.Where("create_at > ?", trimmedName) } if req.EndTime != "" { trimmedName := strings.TrimSpace(req.EndTime) query = query.Where("create_at < ?", trimmedName) } result := query.Count(&count) if result.Error != nil { return 0, result.Error } return int(count), nil } func (r *wafLogRepository) GetWafLogGateWayIp(ctx context.Context, hostId int64, Uid int64,createdAt time.Time) (model.WafLog, error) { var res model.WafLog return res, r.DBWithName(ctx,"admin").Where("host_id = ? and uid = ? and api_name = ? and created_at < ? ", hostId, Uid, "分配网关组", createdAt).First(&res).Error } // BatchGetWafLogGateWayIps 批量获取网关组记录,避免N+1查询问题 func (r *wafLogRepository) BatchGetWafLogGateWayIps(ctx context.Context, hostIds []int64, uids []int64, maxCreatedAt time.Time) (map[string]model.WafLog, error) { if len(hostIds) == 0 || len(uids) == 0 { return make(map[string]model.WafLog), nil } var gateWayLogs []model.WafLog // 构建查询条件,获取所有相关的网关组记录 query := r.DBWithName(ctx, "admin"). Where("api_name = ?", "分配网关组"). Where("created_at < ?", maxCreatedAt) // 如果hostIds和uids数量较少,使用IN查询 if len(hostIds) <= 1000 && len(uids) <= 1000 { query = query.Where("host_id IN ? AND uid IN ?", hostIds, uids) } // 按创建时间倒序,确保获取最新的网关组配置 err := query.Order("created_at DESC").Find(&gateWayLogs).Error if err != nil { return nil, err } // 构建映射表,key为"hostId_uid",value为最新的网关组记录 result := make(map[string]model.WafLog) for _, log := range gateWayLogs { key := fmt.Sprintf("%d_%d", log.HostId, log.Uid) // 由于已经按创建时间倒序排列,第一次遇到的就是最新的记录 if _, exists := result[key]; !exists { result[key] = log } } return result, nil }