From b3b176941164814014e1f7a9da554c1e55182f4e Mon Sep 17 00:00:00 2001 From: tbphp Date: Sat, 12 Jul 2025 10:45:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=97=A5=E5=BF=97=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/handler/log_handler.go | 145 ++++++++++++++++++++++++-------- 1 file changed, 112 insertions(+), 33 deletions(-) diff --git a/internal/handler/log_handler.go b/internal/handler/log_handler.go index 5ca08b9..1691616 100644 --- a/internal/handler/log_handler.go +++ b/internal/handler/log_handler.go @@ -1,7 +1,6 @@ package handler import ( - "net/http" "strconv" "time" @@ -12,56 +11,136 @@ import ( "gpt-load/internal/response" ) -// GetLogs godoc -func GetLogs(c *gin.Context) { - page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) - size, _ := strconv.Atoi(c.DefaultQuery("size", "10")) - offset := (page - 1) * size +// LogResponse defines the structure for log entries in the API response, +// enriching the base log with related data. +type LogResponse struct { + models.RequestLog + GroupName string `json:"group_name"` + KeyValue string `json:"key_value"` +} +// GetLogs Get request logs +func GetLogs(c *gin.Context) { + // --- 1. Build WHERE conditions --- query := db.DB.Model(&models.RequestLog{}) - if groupIDStr := c.Query("group_id"); groupIDStr != "" { - groupID, err := strconv.Atoi(groupIDStr) - if err == nil { - query = query.Where("group_id = ?", groupID) + if groupName := c.Query("group_name"); groupName != "" { + var groupIDs []uint + db.DB.Model(&models.Group{}).Where("name LIKE ? OR display_name LIKE ?", "%"+groupName+"%", "%"+groupName+"%").Pluck("id", &groupIDs) + if len(groupIDs) == 0 { + response.Success(c, &response.PaginatedResponse{ + Items: []LogResponse{}, + Pagination: response.Pagination{TotalItems: 0, Page: 1, PageSize: response.DefaultPageSize}, + }) + return + } + query = query.Where("group_id IN ?", groupIDs) + } + if keyValue := c.Query("key_value"); keyValue != "" { + var keyIDs []uint + likePattern := "%" + keyValue[1:len(keyValue)-1] + "%" + db.DB.Model(&models.APIKey{}).Where("key_value LIKE ?", likePattern).Pluck("id", &keyIDs) + if len(keyIDs) == 0 { + response.Success(c, &response.PaginatedResponse{ + Items: []LogResponse{}, + Pagination: response.Pagination{TotalItems: 0, Page: 1, PageSize: response.DefaultPageSize}, + }) + return + } + query = query.Where("key_id IN ?", keyIDs) + } + if isSuccessStr := c.Query("is_success"); isSuccessStr != "" { + if isSuccess, err := strconv.ParseBool(isSuccessStr); err == nil { + query = query.Where("is_success = ?", isSuccess) } } - + if statusCodeStr := c.Query("status_code"); statusCodeStr != "" { + if statusCode, err := strconv.Atoi(statusCodeStr); err == nil { + query = query.Where("status_code = ?", statusCode) + } + } + if sourceIP := c.Query("source_ip"); sourceIP != "" { + query = query.Where("source_ip = ?", sourceIP) + } + if errorContains := c.Query("error_contains"); errorContains != "" { + query = query.Where("error_message LIKE ?", "%"+errorContains+"%") + } if startTimeStr := c.Query("start_time"); startTimeStr != "" { - startTime, err := time.Parse(time.RFC3339, startTimeStr) - if err == nil { + if startTime, err := time.Parse(time.RFC3339, startTimeStr); err == nil { query = query.Where("timestamp >= ?", startTime) } } - if endTimeStr := c.Query("end_time"); endTimeStr != "" { - endTime, err := time.Parse(time.RFC3339, endTimeStr) - if err == nil { + if endTime, err := time.Parse(time.RFC3339, endTimeStr); err == nil { query = query.Where("timestamp <= ?", endTime) } } - if statusCodeStr := c.Query("status_code"); statusCodeStr != "" { - statusCode, err := strconv.Atoi(statusCodeStr) - if err == nil { - query = query.Where("status_code = ?", statusCode) - } - } - + // --- 2. Get Paginated Logs --- var logs []models.RequestLog - var total int64 - - query.Count(&total) - err := query.Order("timestamp desc").Offset(offset).Limit(size).Find(&logs).Error + query = query.Order("timestamp desc") // Apply ordering before pagination + pagination, err := response.Paginate(c, query, &logs) if err != nil { response.Error(c, app_errors.ParseDBError(err)) return } - c.JSON(http.StatusOK, gin.H{ - "total": total, - "page": page, - "size": size, - "data": logs, - }) + // --- 3. Enrich Logs with GroupName and KeyValue --- + if len(logs) == 0 { + response.Success(c, pagination) // Return empty pagination response + return + } + + // Collect IDs for enrichment + groupIds := make(map[uint]bool) + keyIds := make(map[uint]bool) + for _, log := range logs { + if log.GroupID != 0 { + groupIds[log.GroupID] = true + } + if log.KeyID != 0 { + keyIds[log.KeyID] = true + } + } + + // Fetch enrichment data + groupMap := make(map[uint]string) + if len(groupIds) > 0 { + var groups []models.Group + var ids []uint + for id := range groupIds { + ids = append(ids, id) + } + db.DB.Where("id IN ?", ids).Find(&groups) + for _, group := range groups { + groupMap[group.ID] = group.Name + } + } + + keyMap := make(map[uint]string) + if len(keyIds) > 0 { + var keys []models.APIKey + var ids []uint + for id := range keyIds { + ids = append(ids, id) + } + db.DB.Where("id IN ?", ids).Find(&keys) + for _, key := range keys { + keyMap[key.ID] = key.KeyValue + } + } + + // Build final response + logResponses := make([]LogResponse, len(logs)) + for i, log := range logs { + logResponses[i] = LogResponse{ + RequestLog: log, + GroupName: groupMap[log.GroupID], + KeyValue: keyMap[log.KeyID], + } + } + + // --- 4. Send Response --- + pagination.Items = logResponses + response.Success(c, pagination) }