Оптимизирована загрузка имен игроков с помощью пакетного запроса и переработан интерфейс работы окна MentorHelp.

This commit is contained in:
Vigers Ray 2026-01-04 06:32:17 +01:00
parent 30ea82a02b
commit 4670886c44
4 changed files with 99 additions and 20 deletions

View file

@ -1,8 +1,9 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:mentorHelp="clr-namespace:Content.Client._Sunrise.MentorHelp"
SetSize="900 700"
HeaderClass="windowHeaderHelp"
TitleClass="windowTitleHelp"
Title="{Loc 'mentor-help-window-title'}" >
<mentorHelp:MentorHelpControl Name="MentorHelp" Access="Public"/>
<DefaultWindow
HeaderClass="windowHeaderHelp"
SetSize="1100 700"
Title="{Loc 'mentor-help-window-title'}"
TitleClass="windowTitleHelp"
xmlns="https://spacestation14.io"
xmlns:mentorHelp="clr-namespace:Content.Client._Sunrise.MentorHelp">
<mentorHelp:MentorHelpControl Access="Public" Name="MentorHelp" />
</DefaultWindow>

View file

@ -700,6 +700,40 @@ namespace Content.Server.Database
return record == null ? null : MakePlayerRecord(record);
}
public async Task<Dictionary<Guid, string>> GetPlayerNamesBatchAsync(IEnumerable<Guid> userIds, CancellationToken cancel)
{
await using var db = await GetDb();
var userIdList = userIds.ToList();
if (userIdList.Count == 0)
return new Dictionary<Guid, string>();
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<Guid, string>();
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<bool> 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<List<MentorHelpTicket>> 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<List<MentorHelpTicket>> 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<List<MentorHelpTicket>> 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<List<MentorHelpTicket>> 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)

View file

@ -198,6 +198,7 @@ namespace Content.Server.Database
ImmutableTypedHwid? hwId);
Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel = default);
Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel = default);
Task<Dictionary<Guid, string>> GetPlayerNamesBatchAsync(IEnumerable<Guid> userIds, CancellationToken cancel = default);
#endregion
#region Connection Logs
@ -693,6 +694,12 @@ namespace Content.Server.Database
return RunDbCommand(() => _db.GetPlayerRecordByUserId(userId, cancel));
}
public Task<Dictionary<Guid, string>> GetPlayerNamesBatchAsync(IEnumerable<Guid> userIds, CancellationToken cancel = default)
{
DbReadOpsMetric.Inc();
return RunDbCommand(() => _db.GetPlayerNamesBatchAsync(userIds, cancel));
}
public Task<int> AddConnectionLogAsync(
NetUserId userId,
string userName,

View file

@ -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<Guid>();
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<MentorHelpTicketData>();
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<Guid, string> 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<MentorHelpTicketData> ConvertToTicketDataAsync(MentorHelpTicket ticket)
{
var playerName = await GetPlayerNameAsync(ticket.PlayerId);