diff --git a/api/ServerStatistics.api b/api/ServerStatistics.api index d284234..2c057a8 100644 --- a/api/ServerStatistics.api +++ b/api/ServerStatistics.api @@ -116,10 +116,14 @@ type ( Messages []RecentChatMessageRespMessage `json:"messages"` // List of recent chat messages } RecentChatMessageRespMessage { - SteamID64 string `json:"steamID64"` // Player's SteamID64 - UserName string `json:"userName"` // Player's username at the time of message - Message string `json:"message"` // Chat message content - TimeStamp int64 `json:"timeStamp"` // Message timestamp as Unix timestamp in milliseconds + SteamID64 string `json:"steamID64"` // Player's SteamID64 + UserName string `json:"userName"` // Player's username at the time of message + Message string `json:"message"` // Chat message content + TimeStamp int64 `json:"timeStamp"` // Message timestamp as Unix timestamp in milliseconds + ServerName string `json:"serverName"` // Server name where the message was sent + MapName string `json:"mapName"` // Map name where the message was sent + ServerIP string `json:"serverIP"` // Server IP where the message was sent + ServerPort int `json:"serverPort"` // Server port where the message was sent } ) @@ -155,6 +159,21 @@ type ( } ) +type ( + TopKillerReq { + TimeRangeStart int64 `json:"timeRangeStart"` // Unix timestamp in milliseconds + TimeRangeEnd int64 `json:"timeRangeEnd"` // Unix timestamp in milliseconds + } + TopKillerResp { + Players []TopKillerRespPlayer `json:"players"` // List of top players by kill count + } + TopKillerRespPlayer { + SteamID64 string `json:"steamID64"` // Player's SteamID64 + UserName string `json:"userName"` // Player's username at the time of playing + KillCount int64 `json:"killCount"` // Kill count during the time range + } +) + @server ( prefix: /api/server/statistics ) @@ -242,5 +261,12 @@ service ServerStatistics { ) @handler topPlayTimeHandler post /top-play-time (TopPlayTimeReq) returns (TopPlayTimeResp) + + @doc ( + summary: "Get top players by kill count within a specified time range" + description: "Get top players by kill count within a specified time range" + ) + @handler topKillerHandler + post /top-killer (TopKillerReq) returns (TopKillerResp) } diff --git a/src/internal/handler/routes.go b/src/internal/handler/routes.go index f8a7048..710270a 100644 --- a/src/internal/handler/routes.go +++ b/src/internal/handler/routes.go @@ -44,6 +44,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/recent-player-join", Handler: recentPlayerJoinHandler(serverCtx), }, + { + // Get top players by kill count within a specified time range + Method: http.MethodPost, + Path: "/top-killer", + Handler: topKillerHandler(serverCtx), + }, { // Get top players by playtime within a specified time range Method: http.MethodPost, diff --git a/src/internal/handler/topkillerhandler.go b/src/internal/handler/topkillerhandler.go new file mode 100644 index 0000000..3f29f88 --- /dev/null +++ b/src/internal/handler/topkillerhandler.go @@ -0,0 +1,29 @@ +package handler + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "src/internal/logic" + "src/internal/svc" + "src/internal/types" +) + +// Get top players by kill count within a specified time range +func topKillerHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.TopKillerReq + if err := httpx.Parse(r, &req); err != nil { + httpx.ErrorCtx(r.Context(), w, err) + return + } + + l := logic.NewTopKillerLogic(r.Context(), svcCtx) + resp, err := l.TopKiller(&req) + if err != nil { + httpx.ErrorCtx(r.Context(), w, err) + } else { + httpx.OkJsonCtx(r.Context(), w, resp) + } + } +} diff --git a/src/internal/logic/recentchatmessagelogic.go b/src/internal/logic/recentchatmessagelogic.go index 623b3b1..3a82688 100644 --- a/src/internal/logic/recentchatmessagelogic.go +++ b/src/internal/logic/recentchatmessagelogic.go @@ -27,7 +27,8 @@ func NewRecentChatMessageLogic(ctx context.Context, svcCtx *svc.ServiceContext) func (l *RecentChatMessageLogic) RecentChatMessage(req *types.RecentChatMessageReq) (resp *types.RecentChatMessageResp, err error) { query := ` SELECT steamid64, player_name, message, - EXTRACT(EPOCH FROM message_time)::BIGINT * 1000 as timestamp + EXTRACT(EPOCH FROM message_time)::BIGINT * 1000 as timestamp, + server_name, mapname, server_ip, server_port FROM steam_union.chat_log WHERE message_time >= TO_TIMESTAMP($1 / 1000.0) AND message_time <= TO_TIMESTAMP($2 / 1000.0) @@ -44,7 +45,8 @@ func (l *RecentChatMessageLogic) RecentChatMessage(req *types.RecentChatMessageR var messages []types.RecentChatMessageRespMessage for rows.Next() { var msg types.RecentChatMessageRespMessage - if err := rows.Scan(&msg.SteamID64, &msg.UserName, &msg.Message, &msg.TimeStamp); err != nil { + if err := rows.Scan(&msg.SteamID64, &msg.UserName, &msg.Message, &msg.TimeStamp, + &msg.ServerName, &msg.MapName, &msg.ServerIP, &msg.ServerPort); err != nil { l.Errorf("Failed to scan chat message: %v", err) continue } diff --git a/src/internal/logic/topkillerlogic.go b/src/internal/logic/topkillerlogic.go new file mode 100644 index 0000000..abf9d40 --- /dev/null +++ b/src/internal/logic/topkillerlogic.go @@ -0,0 +1,60 @@ +package logic + +import ( + "context" + + "src/internal/svc" + "src/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type TopKillerLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// Get top players by kill count within a specified time range +func NewTopKillerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *TopKillerLogic { + return &TopKillerLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *TopKillerLogic) TopKiller(req *types.TopKillerReq) (resp *types.TopKillerResp, err error) { + query := ` + SELECT attacker_steamid64, attacker_name, + COUNT(*) as kill_count + FROM steam_union.event_player_death_log + WHERE event_player_death_time >= TO_TIMESTAMP($1 / 1000.0) + AND event_player_death_time <= TO_TIMESTAMP($2 / 1000.0) + AND attacker_steamid64 > 1 + GROUP BY attacker_steamid64, attacker_name + ORDER BY kill_count DESC + LIMIT 100 + ` + + rows, err := l.svcCtx.DB.QueryContext(l.ctx, query, req.TimeRangeStart, req.TimeRangeEnd) + if err != nil { + l.Errorf("Failed to query top killers: %v", err) + return nil, err + } + defer rows.Close() + + var players []types.TopKillerRespPlayer + for rows.Next() { + var player types.TopKillerRespPlayer + if err := rows.Scan(&player.SteamID64, &player.UserName, &player.KillCount); err != nil { + l.Errorf("Failed to scan top killer: %v", err) + continue + } + players = append(players, player) + } + + return &types.TopKillerResp{ + Players: players, + }, nil +} diff --git a/src/internal/types/types.go b/src/internal/types/types.go index 1785dd9..c988b6f 100644 --- a/src/internal/types/types.go +++ b/src/internal/types/types.go @@ -20,10 +20,14 @@ type RecentChatMessageResp struct { } type RecentChatMessageRespMessage struct { - SteamID64 string `json:"steamID64"` // Player's SteamID64 - UserName string `json:"userName"` // Player's username at the time of message - Message string `json:"message"` // Chat message content - TimeStamp int64 `json:"timeStamp"` // Message timestamp as Unix timestamp in milliseconds + SteamID64 string `json:"steamID64"` // Player's SteamID64 + UserName string `json:"userName"` // Player's username at the time of message + Message string `json:"message"` // Chat message content + TimeStamp int64 `json:"timeStamp"` // Message timestamp as Unix timestamp in milliseconds + ServerName string `json:"serverName"` // Server name where the message was sent + MapName string `json:"mapName"` // Map name where the message was sent + ServerIP string `json:"serverIP"` // Server IP where the message was sent + ServerPort int `json:"serverPort"` // Server port where the message was sent } type RecentJoinPlayerReq struct { @@ -59,6 +63,21 @@ type RecentPlayRespPlayer struct { PlayTime int64 `json:"playTime"` // Playtime in seconds during the time range } +type TopKillerReq struct { + TimeRangeStart int64 `json:"timeRangeStart"` // Unix timestamp in milliseconds + TimeRangeEnd int64 `json:"timeRangeEnd"` // Unix timestamp in milliseconds +} + +type TopKillerResp struct { + Players []TopKillerRespPlayer `json:"players"` // List of top players by kill count +} + +type TopKillerRespPlayer struct { + SteamID64 string `json:"steamID64"` // Player's SteamID64 + UserName string `json:"userName"` // Player's username at the time of playing + KillCount int64 `json:"killCount"` // Kill count during the time range +} + type TopPlayTimeReq struct { TimeRangeStart int64 `json:"timeRangeStart"` // Unix timestamp in milliseconds TimeRangeEnd int64 `json:"timeRangeEnd"` // Unix timestamp in milliseconds