package handlers import ( "strconv" "strings" "time" securityEntities "tyapi-server/internal/domains/security/entities" "tyapi-server/internal/shared/interfaces" "tyapi-server/internal/shared/ipgeo" "github.com/gin-gonic/gin" "go.uber.org/zap" "gorm.io/gorm" ) // AdminSecurityHandler 管理员安全数据处理器 type AdminSecurityHandler struct { db *gorm.DB responseBuilder interfaces.ResponseBuilder logger *zap.Logger ipLocator *ipgeo.Locator } func NewAdminSecurityHandler( db *gorm.DB, responseBuilder interfaces.ResponseBuilder, logger *zap.Logger, ipLocator *ipgeo.Locator, ) *AdminSecurityHandler { return &AdminSecurityHandler{ db: db, responseBuilder: responseBuilder, logger: logger, ipLocator: ipLocator, } } func (h *AdminSecurityHandler) getIntQuery(c *gin.Context, key string, defaultValue int) int { if value := c.Query(key); value != "" { if intValue, err := strconv.Atoi(value); err == nil && intValue > 0 { return intValue } } return defaultValue } func (h *AdminSecurityHandler) parseRange(c *gin.Context) (time.Time, time.Time, bool) { startTime := time.Now().Add(-24 * time.Hour) endTime := time.Now() if start := strings.TrimSpace(c.Query("start_time")); start != "" { t, err := time.Parse("2006-01-02 15:04:05", start) if err != nil { h.responseBuilder.BadRequest(c, "start_time格式错误,示例:2026-03-19 10:00:00") return time.Time{}, time.Time{}, false } startTime = t } if end := strings.TrimSpace(c.Query("end_time")); end != "" { t, err := time.Parse("2006-01-02 15:04:05", end) if err != nil { h.responseBuilder.BadRequest(c, "end_time格式错误,示例:2026-03-19 12:00:00") return time.Time{}, time.Time{}, false } endTime = t } return startTime, endTime, true } // ListSuspiciousIPs 获取可疑IP列表 func (h *AdminSecurityHandler) ListSuspiciousIPs(c *gin.Context) { page := h.getIntQuery(c, "page", 1) pageSize := h.getIntQuery(c, "page_size", 20) if pageSize > 100 { pageSize = 100 } startTime, endTime, ok := h.parseRange(c) if !ok { return } ip := strings.TrimSpace(c.Query("ip")) path := strings.TrimSpace(c.Query("path")) query := h.db.Model(&securityEntities.SuspiciousIPRecord{}). Where("created_at >= ? AND created_at <= ?", startTime, endTime) if ip != "" { query = query.Where("ip = ?", ip) } if path != "" { query = query.Where("path LIKE ?", "%"+path+"%") } var total int64 if err := query.Count(&total).Error; err != nil { h.logger.Error("查询可疑IP总数失败", zap.Error(err)) h.responseBuilder.InternalError(c, "查询失败") return } var items []securityEntities.SuspiciousIPRecord if err := query.Order("created_at DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&items).Error; err != nil { h.logger.Error("查询可疑IP列表失败", zap.Error(err)) h.responseBuilder.InternalError(c, "查询失败") return } h.responseBuilder.Success(c, gin.H{ "items": items, "total": total, }, "获取成功") } type geoStreamRow struct { IP string `json:"ip"` Path string `json:"path"` Count int `json:"count"` } // GetSuspiciousIPGeoStream 获取地球请求流数据 func (h *AdminSecurityHandler) GetSuspiciousIPGeoStream(c *gin.Context) { startTime, endTime, ok := h.parseRange(c) if !ok { return } topN := h.getIntQuery(c, "top_n", 200) if topN > 1000 { topN = 1000 } var rows []geoStreamRow err := h.db.Model(&securityEntities.SuspiciousIPRecord{}). Select("ip, path, COUNT(1) as count"). Where("created_at >= ? AND created_at <= ?", startTime, endTime). Group("ip, path"). Order("count DESC"). Limit(topN). Scan(&rows).Error if err != nil { h.logger.Error("查询地球请求流失败", zap.Error(err)) h.responseBuilder.InternalError(c, "查询失败") return } // 目标固定服务器点位(上海) const serverName = "TYAPI-Server" const serverLng = 121.4737 const serverLat = 31.2304 result := make([]gin.H, 0, len(rows)) for _, row := range rows { record := securityEntities.SuspiciousIPRecord{IP: row.IP} fromName, fromLng, fromLat := h.ipLocator.ToGeoPoint(record) result = append(result, gin.H{ "from_name": fromName, "from_lng": fromLng, "from_lat": fromLat, "to_name": serverName, "to_lng": serverLng, "to_lat": serverLat, "value": row.Count, "path": row.Path, "ip": row.IP, }) } h.responseBuilder.Success(c, result, "获取成功") }