From 15b920d25bca0f7a8335adc59bb3a60b94c64c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E9=9B=A8=E6=99=B4?= <1207713896@qq.com> Date: Tue, 19 Aug 2025 17:43:59 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E9=A1=B5=E9=9D=A2=EF=BC=8C=E5=92=8C=E6=B8=B8=E6=88=8F?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/GameService.cs | 104 +++- frontend/src/components/GameCanvas.vue | 283 ++++++++- frontend/src/components/Leaderboard.vue | 2 +- frontend/src/components/RoomList.vue | 2 +- frontend/src/views/GameRoomView.vue | 555 +++++++++++++++--- 5 files changed, 820 insertions(+), 126 deletions(-) diff --git a/backend/src/TerritoryGame.Application/Services/GameService.cs b/backend/src/TerritoryGame.Application/Services/GameService.cs index dfb18c3..27753c1 100644 --- a/backend/src/TerritoryGame.Application/Services/GameService.cs +++ b/backend/src/TerritoryGame.Application/Services/GameService.cs @@ -177,51 +177,93 @@ public class GameService : DomainServices.IGameService // 加入房间 public async Task JoinRoomAsync(Guid roomId, Player player) { - var room = await GetRoomByIdAsync(roomId); - - // 检查房间是否已满 - if (room.Players.Count >= room.MaxPlayers) + // 使用事务确保数据一致性 + using var transaction = await _context.Database.BeginTransactionAsync(); + try { - throw new InvalidOperationException("Room is full"); - } + // 直接查询房间,避免额外的方法调用开销 + var room = await _context.GameRooms + .Include(r => r.Players) + .FirstOrDefaultAsync(r => r.Id == roomId); - // 检查玩家是否已在房间中 - if (room.Players.Any(p => p.Id == player.Id)) - { - throw new InvalidOperationException("Player is already in the room"); - } + if (room == null) + { + throw new KeyNotFoundException($"Room with ID {roomId} not found"); + } - // 添加玩家到房间 - room.AddPlayer(player); - _context.Players.Add(player); - await _context.SaveChangesAsync(); + // 检查房间是否已满 + if (room.Players.Count >= room.MaxPlayers) + { + throw new InvalidOperationException("Room is full"); + } - // 缓存更新后的房间信息 - await CacheRoomAsync(room); + // 检查玩家是否已在房间中 + if (room.Players.Any(p => p.Id == player.Id)) + { + throw new InvalidOperationException("Player is already in the room"); + } - return room; + // 添加玩家到房间 + room.AddPlayer(player); + _context.Players.Add(player); + await _context.SaveChangesAsync(); + + // 清除旧缓存并缓存更新后的房间信息 + await _cache.RemoveAsync($"room:{room.Id}"); + await _cache.RemoveAsync("available_rooms"); + await CacheRoomAsync(room); + + await transaction.CommitAsync(); + return room; + } + catch + { + await transaction.RollbackAsync(); + throw; + } } // 添加观战者 public async Task AddSpectatorAsync(Guid roomId, Spectator spectator) { - var room = await GetRoomByIdAsync(roomId); - - // 检查观战者是否已在房间中 - if (room.Spectators.Any(s => s.Id == spectator.Id)) + // 使用事务确保数据一致性 + using var transaction = await _context.Database.BeginTransactionAsync(); + try { - throw new InvalidOperationException("Spectator is already in the room"); - } + // 直接查询房间,避免额外的方法调用开销 + var room = await _context.GameRooms + .Include(r => r.Spectators) + .FirstOrDefaultAsync(r => r.Id == roomId); - // 添加观战者到房间 - room.AddSpectator(spectator); - _context.Spectators.Add(spectator); - await _context.SaveChangesAsync(); + if (room == null) + { + throw new KeyNotFoundException($"Room with ID {roomId} not found"); + } - // 缓存更新后的房间信息 - await CacheRoomAsync(room); + // 检查观战者是否已在房间中 + if (room.Spectators.Any(s => s.Id == spectator.Id)) + { + throw new InvalidOperationException("Spectator is already in the room"); + } - return room; + // 添加观战者到房间 + room.AddSpectator(spectator); + _context.Spectators.Add(spectator); + await _context.SaveChangesAsync(); + + // 清除旧缓存并缓存更新后的房间信息 + await _cache.RemoveAsync($"room:{room.Id}"); + await _cache.RemoveAsync("available_rooms"); + await CacheRoomAsync(room); + + await transaction.CommitAsync(); + return room; + } + catch + { + await transaction.RollbackAsync(); + throw; + } } // 离开房间 diff --git a/frontend/src/components/GameCanvas.vue b/frontend/src/components/GameCanvas.vue index be6c690..4146c97 100644 --- a/frontend/src/components/GameCanvas.vue +++ b/frontend/src/components/GameCanvas.vue @@ -1,23 +1,30 @@ \ No newline at end of file -- Gitee From 54a891bfec56f4bf2359fccd42ca23708b6869dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E9=9B=A8=E6=99=B4?= <1207713896@qq.com> Date: Wed, 20 Aug 2025 00:15:05 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=88=BF=E9=97=B4?= =?UTF-8?q?=E5=8D=A1=E7=89=87=EF=BC=8C=E5=A2=9E=E6=B7=BB=E8=A7=82=E6=88=98?= =?UTF-8?q?=EF=BC=8C=E6=88=BF=E9=97=B4=E7=8A=B6=E6=80=81=E7=9A=84=E4=BA=A4?= =?UTF-8?q?=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DbQueryApp/DbQueryApp.csproj | 8 +- DbQueryApp/Program.cs | 113 +++++++---- DbQueryAppNet8/DbQueryAppNet8.csproj | 14 ++ DbQueryAppNet8/Program.cs | 186 ++++++++++++++++++ TestDbConnection.cs | 27 +++ TestDbConnection.csproj | 16 ++ TestDbConnectionDir/TestDbConnection.cs | 28 +++ TestDbConnectionDir/TestDbConnection.csproj | 16 ++ backend/src/TerritoryGame.Api/Hubs/GameHub.cs | 36 +++- backend/src/TerritoryGame.Api/Program.cs | 2 +- .../src/TerritoryGame.Api/appsettings.json | 4 +- .../Services/GameService.cs | 77 +++++++- backend/src/TerritoryGame.Console/Program.cs | 54 +++++ .../Interfaces/IGameDbContext.cs | 4 + .../Data/GameDbContext.cs | 5 + .../TerritoryGame.Infrastructure.csproj | 2 +- frontend/src/components/RoomList.vue | 74 ++++++- 17 files changed, 605 insertions(+), 61 deletions(-) create mode 100644 DbQueryAppNet8/DbQueryAppNet8.csproj create mode 100644 DbQueryAppNet8/Program.cs create mode 100644 TestDbConnection.cs create mode 100644 TestDbConnection.csproj create mode 100644 TestDbConnectionDir/TestDbConnection.cs create mode 100644 TestDbConnectionDir/TestDbConnection.csproj create mode 100644 backend/src/TerritoryGame.Console/Program.cs diff --git a/DbQueryApp/DbQueryApp.csproj b/DbQueryApp/DbQueryApp.csproj index 44fac84..d051d20 100644 --- a/DbQueryApp/DbQueryApp.csproj +++ b/DbQueryApp/DbQueryApp.csproj @@ -1,14 +1,14 @@ - + Exe - net8.0 + net9.0 enable enable - + - \ No newline at end of file + diff --git a/DbQueryApp/Program.cs b/DbQueryApp/Program.cs index 1c55450..75d63b9 100644 --- a/DbQueryApp/Program.cs +++ b/DbQueryApp/Program.cs @@ -1,56 +1,95 @@ using System; +using System.Collections.Generic; +using System.Data; using Npgsql; -namespace DbQueryApp +class Program { - class Program + static void Main(string[] args) { - static void Main(string[] args) + // 数据库连接字符串 + string connectionString = "Host=localhost;Port=5432;Database=territory_game;Username=postgres;Password=your_password"; + + try { - try + // 创建连接 + using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) { - // 数据库连接字符串 - string connectionString = "Host=39.105.7.196;Port=5432;Database=TerritoryGame;Username=postgres;Password=xyq0716"; + connection.Open(); + Console.WriteLine("成功连接到数据库"); - // 创建连接 - using (var connection = new NpgsqlConnection(connectionString)) + // 查询所有房间 + string roomQuery = "SELECT * FROM GameRooms"; + using (NpgsqlCommand roomCommand = new NpgsqlCommand(roomQuery, connection)) { - connection.Open(); - Console.WriteLine("成功连接到数据库"); - - // 查询等待状态的房间 - string query = "SELECT \"Id\", \"Name\", \"CreatedAt\" FROM \"GameRooms\" WHERE \"Status\" = 0"; - using (var command = new NpgsqlCommand(query, connection)) + using (NpgsqlDataReader roomReader = roomCommand.ExecuteReader()) { - using (var reader = command.ExecuteReader()) + Console.WriteLine("\n房间列表:"); + Console.WriteLine("------------------------------------------------------------"); + + while (roomReader.Read()) { - if (!reader.HasRows) - { - Console.WriteLine("没有找到等待状态的房间"); - return; - } - - Console.WriteLine("等待状态的房间列表:"); - Console.WriteLine("----------------------------------------"); - Console.WriteLine("房间ID | 房间名称 | 创建时间"); - Console.WriteLine("----------------------------------------"); - - while (reader.Read()) - { - var id = reader.GetGuid(0); - var name = reader.GetString(1); - var createdAt = reader.GetDateTime(2); - - Console.WriteLine($"{id} | {name} | {createdAt}"); - } + int roomId = roomReader.GetInt32(roomReader.GetOrdinal("Id")); + string roomName = roomReader.GetString(roomReader.GetOrdinal("Name")); + DateTime createdAt = roomReader.GetDateTime(roomReader.GetOrdinal("CreatedAt")); + int status = roomReader.GetInt32(roomReader.GetOrdinal("Status")); + + Console.WriteLine($"房间ID: {roomId}, 名称: {roomName}, 创建时间: {createdAt}, 状态: {status}"); + + // 查询该房间的玩家 + List players = GetRoomPlayers(roomId, connection); + Console.WriteLine($"玩家: {string.Join(", ", players)}"); + + // 查询该房间的观众 + List spectators = GetRoomSpectators(roomId, connection); + Console.WriteLine($"观众: {string.Join(", ", spectators)}"); + Console.WriteLine("------------------------------------------------------------"); } } } } - catch (Exception ex) + } + catch (Exception ex) + { + Console.WriteLine($"发生错误: {ex.Message}"); + } + } + + // 获取房间玩家 + static List GetRoomPlayers(int roomId, NpgsqlConnection connection) + { + List players = new List(); + string query = "SELECT Username FROM Players WHERE GameRoomId = @RoomId"; + using (NpgsqlCommand command = new NpgsqlCommand(query, connection)) + { + command.Parameters.AddWithValue("@RoomId", roomId); + using (NpgsqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + players.Add(reader.GetString(0)); + } + } + } + return players; + } + + // 获取房间观众 + static List GetRoomSpectators(int roomId, NpgsqlConnection connection) + { + List spectators = new List(); + string query = "SELECT Username FROM Spectators WHERE GameRoomId = @RoomId"; + using (NpgsqlCommand command = new NpgsqlCommand(query, connection)) + { + command.Parameters.AddWithValue("@RoomId", roomId); + using (NpgsqlDataReader reader = command.ExecuteReader()) { - Console.WriteLine($"发生错误: {ex.Message}"); + while (reader.Read()) + { + spectators.Add(reader.GetString(0)); + } } } + return spectators; } -} \ No newline at end of file +} diff --git a/DbQueryAppNet8/DbQueryAppNet8.csproj b/DbQueryAppNet8/DbQueryAppNet8.csproj new file mode 100644 index 0000000..5d5d01e --- /dev/null +++ b/DbQueryAppNet8/DbQueryAppNet8.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/DbQueryAppNet8/Program.cs b/DbQueryAppNet8/Program.cs new file mode 100644 index 0000000..efd57d2 --- /dev/null +++ b/DbQueryAppNet8/Program.cs @@ -0,0 +1,186 @@ +using Npgsql; +using System; +using System.Collections.Generic; + +class Program +{ + static void Main() + { + string connectionString = "Host=39.105.7.196;Port=5432;Database=TerritoryGame;Username=postgres;Password=xyq0716;"; + + Console.WriteLine("开始查询表结构..."); + // 首先查询表结构以确定列名 + QueryTableStructure("Players", connectionString); + QueryTableStructure("Spectators", connectionString); + Console.WriteLine("表结构查询完成\n"); + + try + { + using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + Console.WriteLine("数据库连接成功"); + + // 查询所有房间 + string roomQuery = "SELECT * FROM \"GameRooms\" WHERE \"Status\" = 0"; + + using (NpgsqlCommand roomCommand = new NpgsqlCommand(roomQuery, connection)) + using (NpgsqlDataReader roomReader = roomCommand.ExecuteReader()) + { + Console.WriteLine("\n房间列表:"); + Console.WriteLine("----------------------------------------------------------------------------------------------------"); + Console.WriteLine("房间ID | 名称 | 创建时间 | 状态 | 玩家 | 观众"); + Console.WriteLine("----------------------------------------------------------------------------------------------------"); + + while (roomReader.Read()) + { + try + { + Guid id = roomReader.GetGuid(roomReader.GetOrdinal("Id")); + string name = roomReader.GetString(roomReader.GetOrdinal("Name")); + + // 更健壮的时间戳处理 + DateTime createdAt; + try + { + // 尝试直接读取为DateTime + createdAt = roomReader.GetDateTime(roomReader.GetOrdinal("CreatedAt")); + } + catch (InvalidCastException) + { + // 如果不是DateTime类型,尝试作为数字处理 + try + { + long createdAtTimestamp = roomReader.GetInt64(roomReader.GetOrdinal("CreatedAt")); + Console.WriteLine($"原始时间戳值: {createdAtTimestamp}"); + + // 尝试不同的时间戳单位 + if (createdAtTimestamp > 1000000000000) // 毫秒 + { + createdAt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(createdAtTimestamp); + } + else if (createdAtTimestamp > 1000000000) // 秒 + { + createdAt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(createdAtTimestamp); + } + else // 假设是天 + { + createdAt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddDays(createdAtTimestamp); + } + } + catch (Exception ex) + { + Console.WriteLine($"时间戳转换失败: {ex.Message}"); + createdAt = DateTime.MinValue; // 设置默认值 + } + } + + int status = roomReader.GetInt32(roomReader.GetOrdinal("Status")); + + // 查询该房间的玩家 + List players = GetRoomPlayers(id, connectionString); + List spectators = GetRoomSpectators(id, connectionString); + + // 格式化输出 + Console.WriteLine($"{id} | {name} | {createdAt} | {status} | {string.Join(',', players)} | {string.Join(',', spectators)}"); + } + catch (Exception ex) + { + Console.WriteLine($"处理房间数据时出错: {ex.Message}"); + } + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"发生错误: {ex.Message}"); + } + } + + // 查询表结构 + static void QueryTableStructure(string tableName, string connectionString) + { + try + { + using (var connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + string query = $"SELECT column_name FROM information_schema.columns WHERE table_name = '{tableName}'"; + using (var command = new NpgsqlCommand(query, connection)) + using (var reader = command.ExecuteReader()) + { + Console.WriteLine($"\n表 {tableName} 的列名:"); + while (reader.Read()) + { + Console.WriteLine(reader.GetString(0)); + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"查询表 {tableName} 结构失败: {ex.Message}"); + } + } + + // 获取房间玩家 + static List GetRoomPlayers(Guid roomId, string connectionString) + { + List players = new List(); + try + { + using (var connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + string query = "SELECT \"NickName\" FROM public.\"Players\" WHERE \"RoomId\" = @RoomId"; + using (var command = new NpgsqlCommand(query, connection)) + { + command.Parameters.AddWithValue("@RoomId", roomId); + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + players.Add(reader.GetString(0)); + } + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"获取房间玩家失败: {ex.Message}"); + } + return players; + } + + // 获取房间观众 + static List GetRoomSpectators(Guid roomId, string connectionString) + { + List spectators = new List(); + try + { + using (var connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + string query = "SELECT \"NickName\" FROM public.\"Spectators\" WHERE \"RoomId\" = @RoomId"; + using (var command = new NpgsqlCommand(query, connection)) + { + command.Parameters.AddWithValue("@RoomId", roomId); + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + spectators.Add(reader.GetString(0)); + } + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"获取房间观众失败: {ex.Message}"); + } + return spectators; + } +} \ No newline at end of file diff --git a/TestDbConnection.cs b/TestDbConnection.cs new file mode 100644 index 0000000..3d0d2b8 --- /dev/null +++ b/TestDbConnection.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using Npgsql; + +class Program +{ + static async Task Main() + { + string connectionString = "Host=39.105.7.196;Port=5432;Database=TerritoryGame;Username=postgres;Password=xyq0716"; + + try + { + await using var conn = new NpgsqlConnection(connectionString); + Console.WriteLine("Opening connection..."); + await conn.OpenAsync(); + Console.WriteLine("Connection opened successfully."); + + await using var cmd = new NpgsqlCommand("SELECT COUNT(*) FROM GameRooms", conn); + var count = await cmd.ExecuteScalarAsync(); + Console.WriteLine($"Number of rooms in database: {count}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/TestDbConnection.csproj b/TestDbConnection.csproj new file mode 100644 index 0000000..8eedfa1 --- /dev/null +++ b/TestDbConnection.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + \ No newline at end of file diff --git a/TestDbConnectionDir/TestDbConnection.cs b/TestDbConnectionDir/TestDbConnection.cs new file mode 100644 index 0000000..21408be --- /dev/null +++ b/TestDbConnectionDir/TestDbConnection.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; +using Npgsql; + +class Program +{ + static async Task Main() + { + string connectionString = "Host=39.105.7.196;Port=5432;Database=TerritoryGame;Username=postgres;Password=xyq0716"; + + try + { + await using var conn = new NpgsqlConnection(connectionString); + Console.WriteLine("Opening connection..."); + await conn.OpenAsync(); + Console.WriteLine("Connection opened successfully."); + + // 查询GameRooms表的记录数(注意表名大小写) + await using var cmd = new NpgsqlCommand("SELECT COUNT(*) FROM \"GameRooms\"", conn); + var count = await cmd.ExecuteScalarAsync(); + Console.WriteLine($"Number of rooms in database: {count}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/TestDbConnectionDir/TestDbConnection.csproj b/TestDbConnectionDir/TestDbConnection.csproj new file mode 100644 index 0000000..8eedfa1 --- /dev/null +++ b/TestDbConnectionDir/TestDbConnection.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + \ No newline at end of file diff --git a/backend/src/TerritoryGame.Api/Hubs/GameHub.cs b/backend/src/TerritoryGame.Api/Hubs/GameHub.cs index 1f8b657..e6e1181 100644 --- a/backend/src/TerritoryGame.Api/Hubs/GameHub.cs +++ b/backend/src/TerritoryGame.Api/Hubs/GameHub.cs @@ -18,11 +18,39 @@ public class GameHub : Hub { try { + Console.WriteLine("GetRoomList method called"); var rooms = await _gameService.GetAvailableRoomsAsync(); - await Clients.Caller.SendAsync("RoomListUpdated", new { rooms }); - } - catch (Exception ex) - { + Console.WriteLine($"Retrieved {rooms.Count} rooms from service"); + // 输出前3个房间的详细信息用于调试 + for (int i = 0; i < Math.Min(rooms.Count, 3); i++) + { + var room = rooms[i]; + Console.WriteLine($"Room {i+1}: {room.Name}, Status: {room.Status}, Players: {room.Players.Count}/{room.MaxPlayers}"); + } + // 确保每个房间都包含CurrentPlayers属性 + var formattedRooms = rooms.Select(room => new { + Id = room.Id, + Name = room.Name, + Password = room.Password, + MaxPlayers = room.MaxPlayers, + CurrentPlayers = room.Players.Count, + SpectatorsCount = room.Spectators.Count, + Status = room.Status.ToString(), + GameDuration = room.GameDuration, + CreatedAt = room.CreatedAt, + UpdatedAt = room.UpdatedAt, + HasPassword = !string.IsNullOrEmpty(room.Password) + }).ToList(); + Console.WriteLine($"Formatted {formattedRooms.Count} rooms to send to client"); + // 输出前3个格式化后的房间信息 + for (int i = 0; i < Math.Min(formattedRooms.Count, 3); i++) + { + var room = formattedRooms[i]; + Console.WriteLine($"Formatted Room {i+1}: {room.Name}, Status: {room.Status}, Players: {room.CurrentPlayers}/{room.MaxPlayers}"); + } + await Clients.Caller.SendAsync("RoomListUpdated", formattedRooms); + } catch (Exception ex) { + Console.WriteLine($"Error in GetRoomList: {ex.Message}"); await Clients.Caller.SendAsync("GetRoomListFailed", new { success = false, message = ex.Message }); } } diff --git a/backend/src/TerritoryGame.Api/Program.cs b/backend/src/TerritoryGame.Api/Program.cs index 46e60c8..994c829 100644 --- a/backend/src/TerritoryGame.Api/Program.cs +++ b/backend/src/TerritoryGame.Api/Program.cs @@ -45,7 +45,7 @@ builder.Services.AddCors(options => { options.AddPolicy("AllowAll", builder => { - builder.WithOrigins("http://localhost:5173", "http://localhost:5120") + builder.WithOrigins("http://localhost:5173", "http://localhost:5174", "http://localhost:5120") .SetIsOriginAllowedToAllowWildcardSubdomains() .AllowAnyMethod() .AllowAnyHeader() diff --git a/backend/src/TerritoryGame.Api/appsettings.json b/backend/src/TerritoryGame.Api/appsettings.json index b96e164..564a297 100644 --- a/backend/src/TerritoryGame.Api/appsettings.json +++ b/backend/src/TerritoryGame.Api/appsettings.json @@ -5,8 +5,8 @@ }, "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Debug", + "Microsoft.AspNetCore": "Debug" } }, "AllowedHosts": "*" diff --git a/backend/src/TerritoryGame.Application/Services/GameService.cs b/backend/src/TerritoryGame.Application/Services/GameService.cs index 27753c1..4ac1db3 100644 --- a/backend/src/TerritoryGame.Application/Services/GameService.cs +++ b/backend/src/TerritoryGame.Application/Services/GameService.cs @@ -94,39 +94,100 @@ public class GameService : DomainServices.IGameService { try { + Console.WriteLine("=== GetAvailableRoomsAsync Called ==="); // 先从缓存获取 - var cachedRooms = await _cache.GetStringAsync("available_rooms"); - if (!string.IsNullOrEmpty(cachedRooms)) + List rooms = null; + try + { + var cachedRooms = await _cache.GetStringAsync("available_rooms"); + if (!string.IsNullOrEmpty(cachedRooms)) + { + Console.WriteLine("Found rooms in cache"); + rooms = JsonSerializer.Deserialize>(cachedRooms); + Console.WriteLine($"Successfully deserialized {rooms?.Count ?? 0} rooms from cache"); + return rooms; + } + else + { + Console.WriteLine("No rooms found in cache"); + } + } + catch (RedisConnectionException ex) { - return JsonSerializer.Deserialize>(cachedRooms); + Console.WriteLine($"Redis connection error when reading cache: {ex.Message}"); + Console.WriteLine($"Redis error details: {ex.InnerException?.Message}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error reading from cache: {ex.Message}"); } - // 缓存未命中,从数据库获取 - var rooms = await _context.GameRooms + // 缓存未命中或读取失败,从数据库获取 + Console.WriteLine("Fetching rooms from database"); + var roomsFromDb = await _context.GameRooms .Include(r => r.Players) .Include(r => r.Spectators) .OrderByDescending(r => r.CreatedAt) .ToListAsync(); + Console.WriteLine($"Retrieved {roomsFromDb.Count} rooms from database"); + + // 如果没有房间,创建一个测试房间用于调试 + if (roomsFromDb.Count == 0) + { + Console.WriteLine("No rooms in database, creating a test room"); + var testRoom = new GameRoom + { + Name = "Test Room", + MaxPlayers = 6, + Status = GameStatus.Waiting, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + GameDuration = 180 + }; + _context.GameRooms.Add(testRoom); + await _context.SaveChangesAsync(); + Console.WriteLine($"Created test room with ID: {testRoom.Id}"); + roomsFromDb = new List { testRoom }; + } + + // 输出前3个房间的详细信息 + for (int i = 0; i < Math.Min(roomsFromDb.Count, 3); i++) + { + var room = roomsFromDb[i]; + Console.WriteLine($"Room {i+1}: {room.Name}, Status: {room.Status}, Players: {room.Players.Count}/{room.MaxPlayers}, Spectators: {room.Spectators.Count}"); + } // 更新缓存 try { + Console.WriteLine("Attempting to update cache with rooms data"); await _cache.SetStringAsync( "available_rooms", - JsonSerializer.Serialize(rooms), + JsonSerializer.Serialize(roomsFromDb), new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) } ); + Console.WriteLine("Cache updated successfully"); } catch (RedisConnectionException ex) { - Console.WriteLine($"Redis connection error: {ex.Message}"); + Console.WriteLine($"Redis connection error when updating cache: {ex.Message}"); + Console.WriteLine($"Redis error details: {ex.InnerException?.Message}"); + } + catch (JsonException ex) + { + Console.WriteLine($"JSON serialization error: {ex.Message}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error updating cache: {ex.Message}"); } - return rooms; + return roomsFromDb; } catch (Exception ex) { Console.WriteLine($"Error getting available rooms: {ex.Message}"); + Console.WriteLine($"Error stack trace: {ex.StackTrace}"); throw; } } diff --git a/backend/src/TerritoryGame.Console/Program.cs b/backend/src/TerritoryGame.Console/Program.cs new file mode 100644 index 0000000..9c968bf --- /dev/null +++ b/backend/src/TerritoryGame.Console/Program.cs @@ -0,0 +1,54 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using TerritoryGame.Infrastructure.Data; +using TerritoryGame.Domain.Entities; + +namespace TerritoryGame.Console +{ + class Program + { + static async Task Main(string[] args) + { + Console.WriteLine("连接数据库..."); + + var options = new DbContextOptionsBuilder() + .UseNpgsql("Host=39.105.7.196;Port=5432;Database=TerritoryGame;Username=postgres;Password=xyq0716") + .Options; + + using (var context = new GameDbContext(options)) + { + try + { + // 测试数据库连接 + await context.Database.OpenConnectionAsync(); + Console.WriteLine("数据库连接成功!"); + + // 查询房间数量 + var roomCount = await context.GameRooms.CountAsync(); + Console.WriteLine($"当前房间数量: {roomCount}"); + + // 查询所有房间 + var rooms = await context.GameRooms + .Include(r => r.Players) + .Include(r => r.Spectators) + .ToListAsync(); + + Console.WriteLine("房间详情:"); + foreach (var room in rooms) + { + Console.WriteLine($"- 房间名: {room.Name}, 状态: {room.Status}, 玩家数: {room.Players.Count}/{room.MaxPlayers}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"数据库操作失败: {ex.Message}"); + } + finally + { + await context.Database.CloseConnectionAsync(); + } + } + } + } +} \ No newline at end of file diff --git a/backend/src/TerritoryGame.Domain/Interfaces/IGameDbContext.cs b/backend/src/TerritoryGame.Domain/Interfaces/IGameDbContext.cs index ea40f9c..706febb 100644 --- a/backend/src/TerritoryGame.Domain/Interfaces/IGameDbContext.cs +++ b/backend/src/TerritoryGame.Domain/Interfaces/IGameDbContext.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; using TerritoryGame.Domain.Entities; namespace TerritoryGame.Domain.Interfaces; @@ -10,5 +11,8 @@ public interface IGameDbContext DbSet PaintActions { get; set; } DbSet Spectators { get; set; } + // DatabaseFacade位于Infrastructure命名空间中 + DatabaseFacade Database { get; } + Task SaveChangesAsync(CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/backend/src/TerritoryGame.Infrastructure/Data/GameDbContext.cs b/backend/src/TerritoryGame.Infrastructure/Data/GameDbContext.cs index 8670e92..1916511 100644 --- a/backend/src/TerritoryGame.Infrastructure/Data/GameDbContext.cs +++ b/backend/src/TerritoryGame.Infrastructure/Data/GameDbContext.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; using TerritoryGame.Domain.Entities; using TerritoryGame.Domain.Interfaces; @@ -16,6 +17,10 @@ public class GameDbContext : DbContext, IGameDbContext public DbSet PaintActions { get; set; } public DbSet Spectators { get; set; } + // 显式实现IGameDbContext接口的Database属性 + // 使用new关键字避免隐藏继承成员的警告 + public new DatabaseFacade Database => base.Database; + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); diff --git a/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj b/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj index 724f028..68e13a2 100644 --- a/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj +++ b/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj @@ -11,7 +11,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/frontend/src/components/RoomList.vue b/frontend/src/components/RoomList.vue index 02bb3ae..26c83b0 100644 --- a/frontend/src/components/RoomList.vue +++ b/frontend/src/components/RoomList.vue @@ -108,22 +108,35 @@ const filteredRooms = computed(() => { }); onMounted(() => { + console.log('RoomList component mounted'); // 确保Socket连接已初始化 - socketService.init().catch(error => { + console.log('Initializing socket connection...'); + socketService.init().then(() => { + console.log('Socket connection initialized successfully'); + // 等待连接成功后初始化房间列表 + socketService.waitForConnection(() => { + console.log('Socket connection established, fetching room list...'); + fetchRoomList(); + }); + }).catch(error => { console.error('Failed to initialize socket connection:', error); }); // 监听房间列表更新 socketService.on('RoomListUpdated', handleRoomListUpdated); + console.log('Registered RoomListUpdated event listener'); // 监听加入房间结果 socketService.on('OnJoinRoomResult', handleJoinRoomResult); + console.log('Registered OnJoinRoomResult event listener'); // 监听观战房间结果 socketService.on('OnSpectateRoomResult', handleSpectateRoomResult); + console.log('Registered OnSpectateRoomResult event listener'); // 监听连接状态变化 socketService.on('reconnect', () => { console.log('Reconnected to server'); fetchRoomList(); }); + console.log('Registered reconnect event listener'); // 监听玩家加入事件 socketService.on('PlayerJoined', handlePlayerJoined); console.log('Registered PlayerJoined event listener'); @@ -132,8 +145,18 @@ onMounted(() => { console.log('PlayerJoined event received in RoomList:', data); }); - // 等待连接成功后初始化房间列表 - socketService.waitForConnection(() => fetchRoomList()); + // 添加连接状态监听 + socketService.on('connect', () => { + console.log('Socket connected to server'); + }); + + socketService.on('disconnect', () => { + console.log('Socket disconnected from server'); + }); + + socketService.on('connect_error', (error) => { + console.error('Socket connection error:', error); + }); // 清理事件监听器 onUnmounted(() => { @@ -161,6 +184,12 @@ const statusMap = { // 监听房间列表更新事件 function handleRoomListUpdated(response) { + // 添加详细的调试信息 + console.log('=== Room List Update Received ==='); + console.log('Response type:', typeof response); + console.log('Is array?', Array.isArray(response)); + console.log('Raw room list response:', JSON.stringify(response, null, 2)); + // 处理两种可能的响应格式 let updatedRooms = []; if (Array.isArray(response)) { @@ -171,8 +200,45 @@ function handleRoomListUpdated(response) { // 格式2: 返回包含rooms属性的对象 updatedRooms = response.rooms; console.log('Room list updated successfully (object format)'); + } else if (response && response.data && response.data.rooms) { + // 格式3: 返回包含data.rooms属性的对象 + updatedRooms = response.data.rooms; + console.log('Room list updated successfully (data.rooms format)'); } else { - console.error('Invalid room list response:', response); + console.error('Invalid room list response format:', response); + // 尝试直接渲染原始响应,用于调试 + updatedRooms = [response] || []; + } + + console.log(`Total rooms received: ${updatedRooms.length}`); + // 显示前3个房间的详细信息 + if (updatedRooms.length > 0) { + console.log('First 3 rooms details:'); + updatedRooms.slice(0, 3).forEach((room, index) => { + console.log(`Room ${index + 1}:`, room); + console.log(`Room ${index + 1} has required fields:`, + 'id' in room ? '✓ id' : '✗ id', + 'name' in room ? '✓ name' : '✗ name', + 'currentPlayers' in room ? '✓ currentPlayers' : '✗ currentPlayers', + 'maxPlayers' in room ? '✓ maxPlayers' : '✗ maxPlayers', + 'status' in room ? '✓ status' : '✗ status' + ); + }); + } + + // 检查是否有房间数据 + console.log(`Received ${updatedRooms.length} rooms from server`); + if (updatedRooms.length === 0) { + console.log('No rooms available, checking if server has rooms'); + // 尝试直接调用API获取房间列表 + fetch('/api/game/rooms') + .then(res => res.json()) + .then(data => { + console.log('Direct API call rooms data:', data); + }) + .catch(err => { + console.error('Failed to fetch rooms directly:', err); + }); } // 转换房间状态为中文 -- Gitee From 42a936ee8bc3a838bf598dd59abf128403c02ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E9=9B=A8=E6=99=B4?= <1207713896@qq.com> Date: Wed, 20 Aug 2025 02:05:02 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E8=A7=A3=E5=86=B3Redis=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DbQueryApp/DbQueryApp.csproj | 4 +- HealthCheckTest/HealthCheckTest.csproj | 10 ++ HealthCheckTest/Program.cs | 2 + TestDbConnectionDir/TestDbConnection.cs | 140 +++++++++++++++-- TestDbConnectionDir/TestDbConnection.csproj | 4 +- TestRedisConnection.cs | 41 +++++ TestRedisConnection.csproj | 16 ++ .../Services/GameService.cs | 144 ++++++++++++------ backend/src/TerritoryGame.Console/Program.cs | 54 ------- .../DependencyInjection.cs | 37 +++-- .../HealthChecks/RedisHealthCheck.cs | 38 +++++ .../TerritoryGame.Infrastructure.csproj | 1 + 12 files changed, 356 insertions(+), 135 deletions(-) create mode 100644 HealthCheckTest/HealthCheckTest.csproj create mode 100644 HealthCheckTest/Program.cs create mode 100644 TestRedisConnection.cs create mode 100644 TestRedisConnection.csproj delete mode 100644 backend/src/TerritoryGame.Console/Program.cs create mode 100644 backend/src/TerritoryGame.Infrastructure/HealthChecks/RedisHealthCheck.cs diff --git a/DbQueryApp/DbQueryApp.csproj b/DbQueryApp/DbQueryApp.csproj index d051d20..3e4aec9 100644 --- a/DbQueryApp/DbQueryApp.csproj +++ b/DbQueryApp/DbQueryApp.csproj @@ -1,8 +1,8 @@ - + Exe - net9.0 + net8.0 enable enable diff --git a/HealthCheckTest/HealthCheckTest.csproj b/HealthCheckTest/HealthCheckTest.csproj new file mode 100644 index 0000000..91b464a --- /dev/null +++ b/HealthCheckTest/HealthCheckTest.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/HealthCheckTest/Program.cs b/HealthCheckTest/Program.cs new file mode 100644 index 0000000..3751555 --- /dev/null +++ b/HealthCheckTest/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/TestDbConnectionDir/TestDbConnection.cs b/TestDbConnectionDir/TestDbConnection.cs index 21408be..44e2a68 100644 --- a/TestDbConnectionDir/TestDbConnection.cs +++ b/TestDbConnectionDir/TestDbConnection.cs @@ -1,28 +1,140 @@ +using StackExchange.Redis; using System; using System.Threading.Tasks; -using Npgsql; class Program { - static async Task Main() + static async Task Main(string[] args) { - string connectionString = "Host=39.105.7.196;Port=5432;Database=TerritoryGame;Username=postgres;Password=xyq0716"; - try { - await using var conn = new NpgsqlConnection(connectionString); - Console.WriteLine("Opening connection..."); - await conn.OpenAsync(); - Console.WriteLine("Connection opened successfully."); - - // 查询GameRooms表的记录数(注意表名大小写) - await using var cmd = new NpgsqlCommand("SELECT COUNT(*) FROM \"GameRooms\"", conn); - var count = await cmd.ExecuteScalarAsync(); - Console.WriteLine($"Number of rooms in database: {count}"); + // 从配置中获取Redis连接字符串 + string redisHost = "39.105.7.196"; + int redisPort = 6379; + string redisConnectionString = $"{redisHost}:{redisPort}"; + Console.WriteLine($"尝试连接到Redis服务器: {redisConnectionString}"); + + // 检查服务器是否可达 + Console.WriteLine("正在检查服务器可达性..."); + try + { + // 使用Ping命令检查服务器是否可达 + var ping = new System.Net.NetworkInformation.Ping(); + var reply = ping.Send(redisHost, 5000); // 5秒超时 + if (reply.Status == System.Net.NetworkInformation.IPStatus.Success) + { + Console.WriteLine($"服务器 {redisHost} 可达,延迟: {reply.RoundtripTime}ms"); + } + else + { + Console.WriteLine($"服务器 {redisHost} 不可达,状态: {reply.Status}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Ping测试失败: {ex.Message}"); + } + + + + // 创建连接配置 + ConfigurationOptions options = ConfigurationOptions.Parse(redisConnectionString); + options.AbortOnConnectFail = false; // 允许连接失败后继续重试 + options.ConnectTimeout = 10000; // 设置连接超时时间为10秒 + options.SyncTimeout = 10000; // 设置同步操作超时时间为10秒 + options.AsyncTimeout = 10000; // 设置异步操作超时时间为10秒 + options.ConnectRetry = 3; // 启用连接重试,最多重试3次 + options.AllowAdmin = true; // 允许管理员操作 + options.Ssl = false; // 不使用SSL + options.ClientName = "TestClient"; + + Console.WriteLine("正在尝试连接到Redis服务器..."); + // 测试Redis端口连接 + try + { + using (var client = new System.Net.Sockets.TcpClient()) + { + Console.WriteLine($"正在尝试连接到 {redisHost}:{redisPort}..."); + var result = client.BeginConnect(redisHost, redisPort, null, null); + var success = result.AsyncWaitHandle.WaitOne(10000); // 10秒超时 + if (!success) + { + Console.WriteLine($"Redis服务器端口连接失败: 无法在10秒内连接到 {redisHost}:{redisPort}"); + throw new Exception($"Failed to connect to {redisHost}:{redisPort} within timeout period"); + } + client.EndConnect(result); + Console.WriteLine($"Redis服务器端口连接成功: 已连接到 {redisHost}:{redisPort}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Redis服务器端口连接异常: {ex.Message}"); + throw; + } + + // 创建连接 + ConnectionMultiplexer redis = await ConnectionMultiplexer.ConnectAsync(options); + Console.WriteLine("Redis连接成功!"); + + // 获取服务器信息 + var server = redis.GetServer("39.105.7.196", 6379); + try + { + var info = server.Info(); + Console.WriteLine("Redis服务器信息:"); + // 简化版本信息获取 + string version = "未知"; + var serverInfo = info.FirstOrDefault(i => i.Key == "server"); + if (serverInfo != null) + { + var versionEntry = serverInfo.FirstOrDefault(v => v.Key == "redis_version"); + if (versionEntry.Key != null) + { + version = versionEntry.Value; + } + } + Console.WriteLine($"版本: {version}"); + } + catch (Exception ex) + { + Console.WriteLine($"获取服务器信息失败: {ex.Message}"); + } + + // 获取数据库 + IDatabase db = redis.GetDatabase(); + + // 测试Ping + var pingResult = await db.PingAsync(); + Console.WriteLine($"Ping结果: {pingResult.TotalMilliseconds}ms"); + + // 测试设置和获取值 + string key = "test_key"; + string value = "Hello Redis!"; + bool setResult = await db.StringSetAsync(key, value); + Console.WriteLine($"设置键: {key}, 值: {value}, 结果: {setResult}"); + + string retrievedValue = await db.StringGetAsync(key); + Console.WriteLine($"获取键: {key}, 值: {retrievedValue}"); + + // 测试获取客户端列表 + try + { + var clients = server.ClientList(); + Console.WriteLine($"当前连接的客户端数量: {clients.Length}"); + } + catch (Exception ex) + { + Console.WriteLine($"获取客户端列表失败: {ex.Message}"); + } + + // 断开连接 + redis.Close(); + Console.WriteLine("Redis连接已关闭"); } catch (Exception ex) { - Console.WriteLine($"Error: {ex.Message}"); + Console.WriteLine($"Redis连接失败: {ex.Message}"); + Console.WriteLine($"错误详情: {ex.InnerException?.Message}"); } } } \ No newline at end of file diff --git a/TestDbConnectionDir/TestDbConnection.csproj b/TestDbConnectionDir/TestDbConnection.csproj index 8eedfa1..bc999e3 100644 --- a/TestDbConnectionDir/TestDbConnection.csproj +++ b/TestDbConnectionDir/TestDbConnection.csproj @@ -8,9 +8,7 @@ - + - - \ No newline at end of file diff --git a/TestRedisConnection.cs b/TestRedisConnection.cs new file mode 100644 index 0000000..f43a3ee --- /dev/null +++ b/TestRedisConnection.cs @@ -0,0 +1,41 @@ +using StackExchange.Redis; +using System; +using System.Threading.Tasks; + +class Program +{ + static async Task Main(string[] args) + { + try + { + // 从配置中获取Redis连接字符串 + string redisConnectionString = "39.105.7.196:6379"; + Console.WriteLine($"尝试连接到Redis服务器: {redisConnectionString}"); + + // 创建连接 + ConnectionMultiplexer redis = await ConnectionMultiplexer.ConnectAsync(redisConnectionString); + Console.WriteLine("Redis连接成功!"); + + // 获取数据库 + IDatabase db = redis.GetDatabase(); + + // 测试设置和获取值 + string key = "test_key"; + string value = "Hello Redis!"; + await db.StringSetAsync(key, value); + Console.WriteLine($"设置键: {key}, 值: {value}"); + + string retrievedValue = await db.StringGetAsync(key); + Console.WriteLine($"获取键: {key}, 值: {retrievedValue}"); + + // 断开连接 + redis.Close(); + Console.WriteLine("Redis连接已关闭"); + } + catch (Exception ex) + { + Console.WriteLine($"Redis连接失败: {ex.Message}"); + Console.WriteLine($"错误详情: {ex.InnerException?.Message}"); + } + } +} \ No newline at end of file diff --git a/TestRedisConnection.csproj b/TestRedisConnection.csproj new file mode 100644 index 0000000..df07d8a --- /dev/null +++ b/TestRedisConnection.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + \ No newline at end of file diff --git a/backend/src/TerritoryGame.Application/Services/GameService.cs b/backend/src/TerritoryGame.Application/Services/GameService.cs index 4ac1db3..2baf8fe 100644 --- a/backend/src/TerritoryGame.Application/Services/GameService.cs +++ b/backend/src/TerritoryGame.Application/Services/GameService.cs @@ -46,13 +46,14 @@ public class GameService : DomainServices.IGameService try { - // 缓存房间信息 - await CacheRoomAsync(room); + // 优化:异步缓存不阻塞主线程 + _ = CacheRoomAsync(room); } catch (RedisConnectionException ex) { // Redis连接失败,记录日志但不影响程序运行 - Console.WriteLine($"Redis connection error: {ex.Message}"); + Console.WriteLine($"Redis connection error in CacheRoomAsync: {ex.Message}"); + Console.WriteLine($"Redis error details: {ex.InnerException?.Message ?? "No inner exception"}"); } return room; @@ -94,28 +95,45 @@ public class GameService : DomainServices.IGameService { try { - Console.WriteLine("=== GetAvailableRoomsAsync Called ==="); // 先从缓存获取 List rooms = null; try { - var cachedRooms = await _cache.GetStringAsync("available_rooms"); - if (!string.IsNullOrEmpty(cachedRooms)) + // 优化:添加异步操作超时处理 + var cacheTask = _cache.GetStringAsync("available_rooms"); + if (await Task.WhenAny(cacheTask, Task.Delay(1000)) == cacheTask) { - Console.WriteLine("Found rooms in cache"); - rooms = JsonSerializer.Deserialize>(cachedRooms); - Console.WriteLine($"Successfully deserialized {rooms?.Count ?? 0} rooms from cache"); - return rooms; + var cachedRooms = await cacheTask; + if (!string.IsNullOrEmpty(cachedRooms)) + { + rooms = JsonSerializer.Deserialize>(cachedRooms); + return rooms; + } } else { - Console.WriteLine("No rooms found in cache"); + Console.WriteLine("Cache retrieval timed out"); } } catch (RedisConnectionException ex) { Console.WriteLine($"Redis connection error when reading cache: {ex.Message}"); - Console.WriteLine($"Redis error details: {ex.InnerException?.Message}"); + Console.WriteLine($"Redis error details: {ex.InnerException?.Message ?? "No inner exception"}"); + // 优化:添加简单的重试逻辑 + try + { + await Task.Delay(200); // 短暂延迟后重试 + var cachedRooms = await _cache.GetStringAsync("available_rooms"); + if (!string.IsNullOrEmpty(cachedRooms)) + { + rooms = JsonSerializer.Deserialize>(cachedRooms); + return rooms; + } + } + catch (Exception retryEx) + { + Console.WriteLine($"Retry failed: {retryEx.Message}"); + } } catch (Exception ex) { @@ -123,18 +141,16 @@ public class GameService : DomainServices.IGameService } // 缓存未命中或读取失败,从数据库获取 - Console.WriteLine("Fetching rooms from database"); var roomsFromDb = await _context.GameRooms .Include(r => r.Players) .Include(r => r.Spectators) + .Where(r => r.Status == GameStatus.Waiting || r.Status == GameStatus.Playing) .OrderByDescending(r => r.CreatedAt) .ToListAsync(); - Console.WriteLine($"Retrieved {roomsFromDb.Count} rooms from database"); // 如果没有房间,创建一个测试房间用于调试 if (roomsFromDb.Count == 0) { - Console.WriteLine("No rooms in database, creating a test room"); var testRoom = new GameRoom { Name = "Test Room", @@ -146,36 +162,27 @@ public class GameService : DomainServices.IGameService }; _context.GameRooms.Add(testRoom); await _context.SaveChangesAsync(); - Console.WriteLine($"Created test room with ID: {testRoom.Id}"); roomsFromDb = new List { testRoom }; } - // 输出前3个房间的详细信息 - for (int i = 0; i < Math.Min(roomsFromDb.Count, 3); i++) - { - var room = roomsFromDb[i]; - Console.WriteLine($"Room {i+1}: {room.Name}, Status: {room.Status}, Players: {room.Players.Count}/{room.MaxPlayers}, Spectators: {room.Spectators.Count}"); - } - // 更新缓存 try { - Console.WriteLine("Attempting to update cache with rooms data"); - await _cache.SetStringAsync( - "available_rooms", - JsonSerializer.Serialize(roomsFromDb), - new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) } - ); - Console.WriteLine("Cache updated successfully"); + // 优化:调整缓存策略,增加绝对过期时间 + var options = new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5), + SlidingExpiration = TimeSpan.FromMinutes(1) + }; + + // 优化:使用更高效的序列化选项 + var serializedRooms = JsonSerializer.Serialize(roomsFromDb, new JsonSerializerOptions { WriteIndented = false }); + await _cache.SetStringAsync("available_rooms", serializedRooms, options); } catch (RedisConnectionException ex) { Console.WriteLine($"Redis connection error when updating cache: {ex.Message}"); - Console.WriteLine($"Redis error details: {ex.InnerException?.Message}"); - } - catch (JsonException ex) - { - Console.WriteLine($"JSON serialization error: {ex.Message}"); + Console.WriteLine($"Redis error details: {ex.InnerException?.Message ?? "No inner exception"}"); } catch (Exception ex) { @@ -195,25 +202,51 @@ public class GameService : DomainServices.IGameService // 获取房间 public async Task GetRoomByIdAsync(Guid roomId) { + // 先从缓存获取 try { - // 先从缓存获取 - var cachedRoom = await _cache.GetStringAsync($"room:{roomId}"); - if (!string.IsNullOrEmpty(cachedRoom)) + // 优化:添加异步操作超时处理 + var cacheTask = _cache.GetStringAsync($"room:{roomId}"); + if (await Task.WhenAny(cacheTask, Task.Delay(1000)) == cacheTask) + { + var cachedRoom = await cacheTask; + if (!string.IsNullOrEmpty(cachedRoom)) + { + return JsonSerializer.Deserialize(cachedRoom)! + ?? throw new InvalidOperationException("Failed to deserialize cached room"); + } + } + else { - return JsonSerializer.Deserialize(cachedRoom)! - ?? throw new InvalidOperationException("Failed to deserialize cached room"); + Console.WriteLine("Cache retrieval timed out for room: {0}", roomId); } } catch (RedisConnectionException ex) { // Redis连接失败,记录日志并继续从数据库获取 - Console.WriteLine($"Redis connection error: {ex.Message}"); + Console.WriteLine($"Redis connection error when reading cache: {ex.Message}"); + Console.WriteLine($"Redis error details: {ex.InnerException?.Message ?? "No inner exception"}"); + // 优化:添加简单的重试逻辑 + try + { + await Task.Delay(200); // 短暂延迟后重试 + var cachedRoom = await _cache.GetStringAsync($"room:{roomId}"); + if (!string.IsNullOrEmpty(cachedRoom)) + { + return JsonSerializer.Deserialize(cachedRoom)! + ?? throw new InvalidOperationException("Failed to deserialize cached room"); + } + } + catch (Exception retryEx) + { + Console.WriteLine($"Retry failed: {retryEx.Message}"); + } } // 从数据库获取 var room = await _context.GameRooms .Include(r => r.Players) + .Include(r => r.Spectators) // 优化:包含观战者数据 .FirstOrDefaultAsync(r => r.Id == roomId); if (room == null) @@ -270,9 +303,18 @@ public class GameService : DomainServices.IGameService await _context.SaveChangesAsync(); // 清除旧缓存并缓存更新后的房间信息 - await _cache.RemoveAsync($"room:{room.Id}"); - await _cache.RemoveAsync("available_rooms"); - await CacheRoomAsync(room); + try + { + await _cache.RemoveAsync($"room:{room.Id}"); + await _cache.RemoveAsync("available_rooms"); + await CacheRoomAsync(room); + } + catch (RedisConnectionException ex) + { + // Redis连接失败,记录日志但不影响程序运行 + Console.WriteLine($"Redis connection error in JoinRoomAsync: {ex.Message}"); + Console.WriteLine($"Redis error details: {ex.InnerException?.Message}"); + } await transaction.CommitAsync(); return room; @@ -515,16 +557,24 @@ public class GameService : DomainServices.IGameService { var options = new DistributedCacheEntryOptions { - AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) + AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10), // 优化:缩短绝对过期时间 + SlidingExpiration = TimeSpan.FromMinutes(2) // 新增:滑动过期,2分钟未访问则刷新 }; - var roomJson = JsonSerializer.Serialize(room); + // 优化:使用更高效的序列化选项 + var roomJson = JsonSerializer.Serialize(room, new JsonSerializerOptions { WriteIndented = false }); await _cache.SetStringAsync($"room:{room.Id}", roomJson, options); } catch (RedisConnectionException ex) { // Redis连接失败,记录日志但不影响程序运行 - Console.WriteLine($"Redis connection error: {ex.Message}"); + Console.WriteLine($"Redis connection error in CacheRoomAsync: {ex.Message}"); + Console.WriteLine($"Redis error details: {ex.InnerException?.Message ?? "No inner exception"}"); + } + catch (Exception ex) + { + // 捕获其他异常 + Console.WriteLine($"Error caching room {room.Id}: {ex.Message}"); } } diff --git a/backend/src/TerritoryGame.Console/Program.cs b/backend/src/TerritoryGame.Console/Program.cs deleted file mode 100644 index 9c968bf..0000000 --- a/backend/src/TerritoryGame.Console/Program.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using TerritoryGame.Infrastructure.Data; -using TerritoryGame.Domain.Entities; - -namespace TerritoryGame.Console -{ - class Program - { - static async Task Main(string[] args) - { - Console.WriteLine("连接数据库..."); - - var options = new DbContextOptionsBuilder() - .UseNpgsql("Host=39.105.7.196;Port=5432;Database=TerritoryGame;Username=postgres;Password=xyq0716") - .Options; - - using (var context = new GameDbContext(options)) - { - try - { - // 测试数据库连接 - await context.Database.OpenConnectionAsync(); - Console.WriteLine("数据库连接成功!"); - - // 查询房间数量 - var roomCount = await context.GameRooms.CountAsync(); - Console.WriteLine($"当前房间数量: {roomCount}"); - - // 查询所有房间 - var rooms = await context.GameRooms - .Include(r => r.Players) - .Include(r => r.Spectators) - .ToListAsync(); - - Console.WriteLine("房间详情:"); - foreach (var room in rooms) - { - Console.WriteLine($"- 房间名: {room.Name}, 状态: {room.Status}, 玩家数: {room.Players.Count}/{room.MaxPlayers}"); - } - } - catch (Exception ex) - { - Console.WriteLine($"数据库操作失败: {ex.Message}"); - } - finally - { - await context.Database.CloseConnectionAsync(); - } - } - } - } -} \ No newline at end of file diff --git a/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs b/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs index 37d0f0e..600237f 100644 --- a/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs +++ b/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Caching.StackExchangeRedis; +using TerritoryGame.Infrastructure.HealthChecks; using TerritoryGame.Application.Services; using TerritoryGame.Application.Interfaces; using TerritoryGame.Domain.Services; @@ -28,31 +29,37 @@ public static class DependencyInjection // 从配置中获取Redis连接字符串 var redisConnectionString = configuration.GetConnectionString("Redis"); + // 添加连接池大小配置到连接字符串 + if (!redisConnectionString.Contains("poolsize")) + { + redisConnectionString += ",poolsize=50"; + } + // 创建Redis配置选项 var redisConfig = StackExchange.Redis.ConfigurationOptions.Parse(redisConnectionString); - // 设置连接超时为10秒 - redisConfig.ConnectTimeout = 10000; - - // 设置同步操作超时为10秒 - redisConfig.SyncTimeout = 10000; - - // 设置异步操作超时为10秒 - redisConfig.AsyncTimeout = 10000; + // 连接优化配置 + redisConfig.ConnectTimeout = 5000; // 减少连接超时时间到5秒 + redisConfig.SyncTimeout = 5000; // 减少同步操作超时到5秒 + redisConfig.AsyncTimeout = 5000; // 减少异步操作超时到5秒 - // 启用连接重试 - redisConfig.ConnectRetry = 3; + // 重试策略 + redisConfig.ConnectRetry = 3; // 重试次数 + redisConfig.ReconnectRetryPolicy = new StackExchange.Redis.ExponentialRetry(1000, 10); // 指数退避重试策略 - // 允许管理员操作 - redisConfig.AllowAdmin = true; - - // 配置SSL(如果需要) - redisConfig.Ssl = false; + // 其他配置 + redisConfig.AllowAdmin = true; // 允许管理员操作 + redisConfig.Ssl = true; // 使用SSL + redisConfig.AbortOnConnectFail = false; // 连接失败时不中断应用 options.ConfigurationOptions = redisConfig; options.InstanceName = "TerritoryGame_"; }); + // 注册自定义Redis健康检查 + services.AddHealthChecks() + .AddCheck("redis"); + // 注册服务 services.AddScoped(); services.AddScoped(); diff --git a/backend/src/TerritoryGame.Infrastructure/HealthChecks/RedisHealthCheck.cs b/backend/src/TerritoryGame.Infrastructure/HealthChecks/RedisHealthCheck.cs new file mode 100644 index 0000000..b8e418c --- /dev/null +++ b/backend/src/TerritoryGame.Infrastructure/HealthChecks/RedisHealthCheck.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; +using StackExchange.Redis; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Configuration; + +namespace TerritoryGame.Infrastructure.HealthChecks; + +public class RedisHealthCheck : IHealthCheck +{ + private readonly IConnectionMultiplexer _redisConnection; + private readonly ILogger _logger; + + public RedisHealthCheck(IConnectionMultiplexer redisConnection, ILogger logger) + { + _redisConnection = redisConnection; + _logger = logger; + } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + // 检查Redis连接是否健康 + var server = _redisConnection.GetServer(_redisConnection.GetEndPoints().First()); + var info = server.Info(); + + _logger.LogInformation("Redis health check passed"); + return Task.FromResult(HealthCheckResult.Healthy("Redis connection is healthy")); + } + catch (Exception ex) + { + _logger.LogError(ex, "Redis health check failed"); + return Task.FromResult(HealthCheckResult.Unhealthy("Redis connection is unhealthy", ex)); + } + } +} \ No newline at end of file diff --git a/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj b/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj index 68e13a2..a4aab94 100644 --- a/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj +++ b/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj @@ -13,6 +13,7 @@ + -- Gitee From 5c7d918f769e4dda9928656bc72796194ecfbe48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E9=9B=A8=E6=99=B4?= <1207713896@qq.com> Date: Wed, 20 Aug 2025 08:01:03 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E5=AE=B9=E5=99=A8=E6=97=A0=E6=B3=95=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/CompleteDITest.cs | 58 +++++++++++++++++++ backend/CompleteDITest/CompleteDITest.csproj | 25 ++++++++ backend/CompleteDITest/Program.cs | 58 +++++++++++++++++++ backend/src/TerritoryGame.Api/Hubs/GameHub.cs | 40 +++++++++++-- backend/src/TerritoryGame.Api/Program.cs | 3 +- .../DependencyInjection.cs | 8 +-- 6 files changed, 179 insertions(+), 13 deletions(-) create mode 100644 backend/CompleteDITest.cs create mode 100644 backend/CompleteDITest/CompleteDITest.csproj create mode 100644 backend/CompleteDITest/Program.cs diff --git a/backend/CompleteDITest.cs b/backend/CompleteDITest.cs new file mode 100644 index 0000000..6a53e9f --- /dev/null +++ b/backend/CompleteDITest.cs @@ -0,0 +1,58 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using System; +using System.IO; +using TerritoryGame.Application.Services; +using TerritoryGame.Domain.Services; +using TerritoryGame.Infrastructure; +using TerritoryGame.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.StackExchangeRedis; +using StackExchange.Redis; + +class CompleteDITest +{ + static void Main(string[] args) + { + // 创建配置 + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile(Path.Combine(Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.FullName, "backend", "src", "TerritoryGame.Api", "appsettings.json")) + .Build(); + + // 创建服务集合 + var services = new ServiceCollection(); + + // 添加配置 + services.AddSingleton(configuration); + + // 注册Infrastructure服务 + services.AddInfrastructure(configuration); + + // 构建服务提供程序 + var serviceProvider = services.BuildServiceProvider(); + + // 解析服务 + try + { + Console.WriteLine("开始解析IGameService..."); + var gameService = serviceProvider.GetRequiredService(); + Console.WriteLine("成功解析IGameService"); + + // 测试服务功能 + Console.WriteLine("开始创建房间..."); + var room = gameService.CreateRoomAsync("测试房间").Result; + Console.WriteLine($"成功创建房间: {room.Name}, ID: {room.Id}"); + } + catch (Exception ex) + { + Console.WriteLine($"解析IGameService失败: {ex.Message}"); + Console.WriteLine(ex.StackTrace); + if (ex.InnerException != null) + { + Console.WriteLine($"内部异常: {ex.InnerException.Message}"); + Console.WriteLine(ex.InnerException.StackTrace); + } + } + } +} \ No newline at end of file diff --git a/backend/CompleteDITest/CompleteDITest.csproj b/backend/CompleteDITest/CompleteDITest.csproj new file mode 100644 index 0000000..62a745c --- /dev/null +++ b/backend/CompleteDITest/CompleteDITest.csproj @@ -0,0 +1,25 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + diff --git a/backend/CompleteDITest/Program.cs b/backend/CompleteDITest/Program.cs new file mode 100644 index 0000000..f7002a9 --- /dev/null +++ b/backend/CompleteDITest/Program.cs @@ -0,0 +1,58 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using System; +using System.IO; +using TerritoryGame.Application.Services; +using TerritoryGame.Domain.Services; +using TerritoryGame.Infrastructure; +using TerritoryGame.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.StackExchangeRedis; +using StackExchange.Redis; + +class Program +{ + static void Main(string[] args) + { + // 创建配置 + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("c:\\Users\\Asus\\Desktop\\lightweight-realtime-collab-app-class1-group2\\backend\\src\\TerritoryGame.Api\\appsettings.json") + .Build(); + + // 创建服务集合 + var services = new ServiceCollection(); + + // 添加配置 + services.AddSingleton(configuration); + + // 注册Infrastructure服务 + services.AddInfrastructure(configuration); + + // 构建服务提供程序 + var serviceProvider = services.BuildServiceProvider(); + + // 解析服务 + try + { + Console.WriteLine("开始解析IGameService..."); + var gameService = serviceProvider.GetRequiredService(); + Console.WriteLine("成功解析IGameService"); + + // 测试服务功能 + Console.WriteLine("开始创建房间..."); + var room = gameService.CreateRoomAsync("测试房间", null, 6).Result; + Console.WriteLine($"成功创建房间: {room.Name}, ID: {room.Id}"); + } + catch (Exception ex) + { + Console.WriteLine($"解析IGameService失败: {ex.Message}"); + Console.WriteLine(ex.StackTrace); + if (ex.InnerException != null) + { + Console.WriteLine($"内部异常: {ex.InnerException.Message}"); + Console.WriteLine(ex.InnerException.StackTrace); + } + } + } +} diff --git a/backend/src/TerritoryGame.Api/Hubs/GameHub.cs b/backend/src/TerritoryGame.Api/Hubs/GameHub.cs index e6e1181..5099d78 100644 --- a/backend/src/TerritoryGame.Api/Hubs/GameHub.cs +++ b/backend/src/TerritoryGame.Api/Hubs/GameHub.cs @@ -2,16 +2,19 @@ using Microsoft.AspNetCore.SignalR; using TerritoryGame.Domain.Entities; using DomainServices = TerritoryGame.Domain.Services; using System.Security.Claims; +using Microsoft.Extensions.Logging; namespace TerritoryGame.Api.Hubs; public class GameHub : Hub { private readonly DomainServices.IGameService _gameService; + private readonly ILogger _logger; - public GameHub(DomainServices.IGameService gameService) + public GameHub(DomainServices.IGameService gameService, ILogger logger) { _gameService = gameService; + _logger = logger; } // 获取房间列表 public async Task GetRoomList() @@ -48,7 +51,7 @@ public class GameHub : Hub var room = formattedRooms[i]; Console.WriteLine($"Formatted Room {i+1}: {room.Name}, Status: {room.Status}, Players: {room.CurrentPlayers}/{room.MaxPlayers}"); } - await Clients.Caller.SendAsync("RoomListUpdated", formattedRooms); + await Clients.Caller.SendAsync("RoomListUpdated", new { rooms = formattedRooms }); } catch (Exception ex) { Console.WriteLine($"Error in GetRoomList: {ex.Message}"); await Clients.Caller.SendAsync("GetRoomListFailed", new { success = false, message = ex.Message }); @@ -349,10 +352,39 @@ public class GameHub : Hub Context.Items["RoomId"] = room.Id; await Clients.Group(room.Id.ToString()).SendAsync("PlayerJoined", player); await Clients.Others.SendAsync("RoomListUpdated", await _gameService.GetAvailableRoomsAsync()); + } catch (Exception ex) { + await Clients.Caller.SendAsync("QuickStartResult", new { success = false, message = ex.Message }); } - catch (Exception ex) + } + + // 连接建立时调用 + public override async Task OnConnectedAsync() + { + _logger.LogInformation("Client connected: {ConnectionId}", Context.ConnectionId); + await base.OnConnectedAsync(); + } + + // 连接断开时调用 + public override async Task OnDisconnectedAsync(Exception exception) + { + _logger.LogInformation("Client disconnected: {ConnectionId}, Exception: {Exception}", Context.ConnectionId, exception?.Message); + + // 处理玩家断开连接的逻辑 + if (Context.Items.TryGetValue("PlayerId", out var playerIdObj) && playerIdObj is Guid playerId && + Context.Items.TryGetValue("RoomId", out var roomIdObj) && roomIdObj is Guid roomId) { - await Clients.Caller.SendAsync("QuickStartResult", new { success = false, message = ex.Message }); + try + { + await NotifyPlayerLeft(roomId.ToString(), playerId); + // 从房间中移除玩家 + await _gameService.LeaveRoomAsync(roomId, playerId); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error handling player disconnect: {ConnectionId}", Context.ConnectionId); + } } + + await base.OnDisconnectedAsync(exception); } } \ No newline at end of file diff --git a/backend/src/TerritoryGame.Api/Program.cs b/backend/src/TerritoryGame.Api/Program.cs index 994c829..7959d20 100644 --- a/backend/src/TerritoryGame.Api/Program.cs +++ b/backend/src/TerritoryGame.Api/Program.cs @@ -38,8 +38,7 @@ builder.Services.AddSwaggerGen(c => // Add SignalR builder.Services.AddSignalR(); -// Register application services -builder.Services.AddScoped(); +// Application services are registered in AddInfrastructure method builder.Services.AddCors(options => { diff --git a/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs b/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs index 600237f..3a84190 100644 --- a/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs +++ b/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs @@ -29,12 +29,6 @@ public static class DependencyInjection // 从配置中获取Redis连接字符串 var redisConnectionString = configuration.GetConnectionString("Redis"); - // 添加连接池大小配置到连接字符串 - if (!redisConnectionString.Contains("poolsize")) - { - redisConnectionString += ",poolsize=50"; - } - // 创建Redis配置选项 var redisConfig = StackExchange.Redis.ConfigurationOptions.Parse(redisConnectionString); @@ -49,7 +43,7 @@ public static class DependencyInjection // 其他配置 redisConfig.AllowAdmin = true; // 允许管理员操作 - redisConfig.Ssl = true; // 使用SSL + redisConfig.Ssl = false; // 禁用SSL,因为连接字符串中没有SSL参数 redisConfig.AbortOnConnectFail = false; // 连接失败时不中断应用 options.ConfigurationOptions = redisConfig; -- Gitee