package handler import ( "net/http" "strconv" "time" "github.com/company/ai-ops/internal/domain/model" "github.com/company/ai-ops/internal/service" "github.com/company/ai-ops/pkg/errors" "github.com/company/ai-ops/pkg/response" ) // LogHandler 是日志 HTTP 处理器 type LogHandler struct { service *service.LogService } func NewLogHandler(s *service.LogService) *LogHandler { return &LogHandler{service: s} } // RegisterRoutes 注册日志相关路由 func (h *LogHandler) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("GET /api/v1/ai-ops/logs", h.QueryLogs) mux.HandleFunc("GET /api/v1/ai-ops/logs/export", h.ExportLogs) } // QueryLogs 日志查询 func (h *LogHandler) QueryLogs(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() filter := model.LogQueryFilter{ Service: query.Get("service"), Path: query.Get("path"), UserID: query.Get("user_id"), SupplierID: query.Get("supplier_id"), } if startStr := query.Get("start"); startStr != "" { if t, err := time.Parse(time.RFC3339, startStr); err == nil { filter.StartTime = &t } } if endStr := query.Get("end"); endStr != "" { if t, err := time.Parse(time.RFC3339, endStr); err == nil { filter.EndTime = &t } } if codeStr := query.Get("status_code"); codeStr != "" { if code, err := strconv.Atoi(codeStr); err == nil { filter.StatusCode = &code } } if page, err := strconv.Atoi(query.Get("page")); err == nil && page > 0 { filter.Page = page } else { filter.Page = 1 } if pageSize, err := strconv.Atoi(query.Get("page_size")); err == nil && pageSize > 0 && pageSize <= 100 { filter.PageSize = pageSize } else { filter.PageSize = 20 } logs, total, err := h.service.QueryLogs(r.Context(), filter) if err != nil { response.Error(w, errors.Wrap(err, errors.ErrInternal)) return } response.PaginatedResponse(w, logs, total, filter.Page, filter.PageSize) } // ExportLogs 导出日志为 CSV func (h *LogHandler) ExportLogs(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() filter := model.LogQueryFilter{ Service: query.Get("service"), Path: query.Get("path"), UserID: query.Get("user_id"), SupplierID: query.Get("supplier_id"), } if startStr := query.Get("start"); startStr != "" { if t, err := time.Parse(time.RFC3339, startStr); err == nil { filter.StartTime = &t } } if endStr := query.Get("end"); endStr != "" { if t, err := time.Parse(time.RFC3339, endStr); err == nil { filter.EndTime = &t } } if codeStr := query.Get("status_code"); codeStr != "" { if code, err := strconv.Atoi(codeStr); err == nil { filter.StatusCode = &code } } w.Header().Set("Content-Type", "text/csv; charset=utf-8") w.Header().Set("Content-Disposition", "attachment; filename=logs_"+time.Now().Format("20060102_150405")+".csv") if err := h.service.ExportLogsCSV(r.Context(), filter, w); err != nil { response.Error(w, errors.Wrap(err, errors.ErrInternal)) return } }