diff --git a/Content.Client/_Sunrise/MentorHelp/MentorHelpWindow.xaml b/Content.Client/_Sunrise/MentorHelp/MentorHelpWindow.xaml index 07d9b7e184..6b339d1c59 100644 --- a/Content.Client/_Sunrise/MentorHelp/MentorHelpWindow.xaml +++ b/Content.Client/_Sunrise/MentorHelp/MentorHelpWindow.xaml @@ -1,8 +1,9 @@ - - + + diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index 4422cec351..e89b890298 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -700,6 +700,40 @@ namespace Content.Server.Database return record == null ? null : MakePlayerRecord(record); } + public async Task> GetPlayerNamesBatchAsync(IEnumerable userIds, CancellationToken cancel) + { + await using var db = await GetDb(); + + var userIdList = userIds.ToList(); + if (userIdList.Count == 0) + return new Dictionary(); + + var records = await db.DbContext.Player + .Where(p => userIdList.Contains(p.UserId)) + .Select(p => new { p.UserId, p.LastSeenUserName }) + .ToListAsync(cancel); + + var result = new Dictionary(); + foreach (var record in records) + { + if (!string.IsNullOrWhiteSpace(record.LastSeenUserName)) + { + result[record.UserId] = record.LastSeenUserName; + } + } + + // Fill missing names with "Unknown" + foreach (var userId in userIdList) + { + if (!result.ContainsKey(userId)) + { + result[userId] = "Unknown"; + } + } + + return result; + } + protected async Task PlayerRecordExists(DbGuard db, NetUserId userId) { return await db.DbContext.Player.AnyAsync(p => p.UserId == userId); @@ -1898,41 +1932,37 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id} public async Task> GetMentorHelpTicketsByPlayerAsync(Guid playerId) { await using var db = await GetDb(); - return (await db.DbContext.MentorHelpTickets + return await db.DbContext.MentorHelpTickets .Where(t => t.PlayerId == playerId) - .ToListAsync()) .OrderByDescending(t => t.CreatedAt) - .ToList(); + .ToListAsync(); } public async Task> GetOpenMentorHelpTicketsAsync() { await using var db = await GetDb(); - return (await db.DbContext.MentorHelpTickets + return await db.DbContext.MentorHelpTickets .Where(t => t.Status != MentorHelpTicketStatus.Closed) - .ToListAsync()) .OrderByDescending(t => t.UpdatedAt) - .ToList(); + .ToListAsync(); } public async Task> GetAssignedMentorHelpTicketsAsync(Guid mentorId) { await using var db = await GetDb(); - return (await db.DbContext.MentorHelpTickets + return await db.DbContext.MentorHelpTickets .Where(t => t.AssignedToUserId == mentorId && t.Status != MentorHelpTicketStatus.Closed) - .ToListAsync()) .OrderByDescending(t => t.UpdatedAt) - .ToList(); + .ToListAsync(); } public async Task> GetClosedMentorHelpTicketsAsync() { await using var db = await GetDb(); - return (await db.DbContext.MentorHelpTickets + return await db.DbContext.MentorHelpTickets .Where(t => t.Status == MentorHelpTicketStatus.Closed) - .ToListAsync()) .OrderByDescending(t => t.UpdatedAt) - .ToList(); + .ToListAsync(); } public async Task AddMentorHelpMessageAsync(MentorHelpMessage message) diff --git a/Content.Server/Database/ServerDbManager.cs b/Content.Server/Database/ServerDbManager.cs index a95bafec8d..2628a41e60 100644 --- a/Content.Server/Database/ServerDbManager.cs +++ b/Content.Server/Database/ServerDbManager.cs @@ -198,6 +198,7 @@ namespace Content.Server.Database ImmutableTypedHwid? hwId); Task GetPlayerRecordByUserName(string userName, CancellationToken cancel = default); Task GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel = default); + Task> GetPlayerNamesBatchAsync(IEnumerable userIds, CancellationToken cancel = default); #endregion #region Connection Logs @@ -693,6 +694,12 @@ namespace Content.Server.Database return RunDbCommand(() => _db.GetPlayerRecordByUserId(userId, cancel)); } + public Task> GetPlayerNamesBatchAsync(IEnumerable userIds, CancellationToken cancel = default) + { + DbReadOpsMetric.Inc(); + return RunDbCommand(() => _db.GetPlayerNamesBatchAsync(userIds, cancel)); + } + public Task AddConnectionLogAsync( NetUserId userId, string userName, diff --git a/Content.Server/_Sunrise/MentorHelp/MentorHelpSystem.cs b/Content.Server/_Sunrise/MentorHelp/MentorHelpSystem.cs index e6a7123986..3b97a4caf0 100644 --- a/Content.Server/_Sunrise/MentorHelp/MentorHelpSystem.cs +++ b/Content.Server/_Sunrise/MentorHelp/MentorHelpSystem.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Content.Server.Administration.Managers; @@ -362,10 +363,25 @@ namespace Content.Server._Sunrise.MentorHelp tickets = openTickets.Concat(closedTickets).ToList(); } + // Collect all unique user IDs for batch loading + var userIds = new HashSet(); + foreach (var ticket in tickets) + { + userIds.Add(ticket.PlayerId); + if (ticket.AssignedToUserId.HasValue) + userIds.Add(ticket.AssignedToUserId.Value); + if (ticket.ClosedByUserId.HasValue) + userIds.Add(ticket.ClosedByUserId.Value); + } + + // Load all player names in one batch query + var playerNames = await _dbManager.GetPlayerNamesBatchAsync(userIds); + + // Convert tickets to data using cached names var ticketDataList = new List(); foreach (var ticket in tickets) { - ticketDataList.Add(await ConvertToTicketDataAsync(ticket)); + ticketDataList.Add(ConvertToTicketData(ticket, playerNames)); } RaiseNetworkEvent(new MentorHelpTicketsListMessage(ticketDataList), session.Channel); @@ -527,6 +543,31 @@ namespace Content.Server._Sunrise.MentorHelp } + private MentorHelpTicketData ConvertToTicketData(MentorHelpTicket ticket, Dictionary playerNames) + { + playerNames.TryGetValue(ticket.PlayerId, out var playerName); + var assignedToName = ticket.AssignedToUserId.HasValue && playerNames.TryGetValue(ticket.AssignedToUserId.Value, out var assignedName) ? assignedName : null; + var closedByName = ticket.ClosedByUserId.HasValue && playerNames.TryGetValue(ticket.ClosedByUserId.Value, out var closedName) ? closedName : null; + + return new MentorHelpTicketData + { + Id = ticket.Id, + PlayerId = new NetUserId(ticket.PlayerId), + PlayerName = playerName ?? "Unknown", + AssignedToUserId = ticket.AssignedToUserId.HasValue ? new NetUserId(ticket.AssignedToUserId.Value) : null, + AssignedToName = assignedToName, + Subject = ticket.Subject, + Status = ticket.Status, + CreatedAt = ticket.CreatedAt.DateTime, + UpdatedAt = ticket.UpdatedAt.DateTime, + ClosedAt = ticket.ClosedAt?.DateTime, + ClosedByUserId = ticket.ClosedByUserId.HasValue ? new NetUserId(ticket.ClosedByUserId.Value) : null, + ClosedByName = closedByName, + RoundId = ticket.RoundId, + HasUnreadMessages = false // Would need to implement read tracking + }; + } + private async Task ConvertToTicketDataAsync(MentorHelpTicket ticket) { var playerName = await GetPlayerNameAsync(ticket.PlayerId);