From 06f9d05d78e1858a54fc6dc7d26350ae8069b2e1 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 16:50:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=8E=B7=E5=8F=96=E6=88=BF?= =?UTF-8?q?=E9=97=B4=E5=88=97=E8=A1=A8=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DbQueryApp/DbQueryApp.csproj | 14 +++ DbQueryApp/Program.cs | 56 ++++++++++ QueryRooms.cs | 39 +++++++ QueryRooms.csproj | 20 ++++ backend/src/TerritoryGame.Api/Hubs/GameHub.cs | 3 +- .../TerritoryGame.Api.csproj | 2 +- .../TerritoryGame.Api/TerritoryGame.Api.http | 6 +- .../Services/GameService.cs | 29 ++--- .../DependencyInjection.cs | 26 ++++- .../TerritoryGame.Infrastructure.csproj | 10 +- frontend/package.json | 1 + frontend/src/components/RoomList.vue | 37 +++++- frontend/src/components/TestRoomList.vue | 105 ++++++++++++++++++ frontend/src/services/socketService.js | 45 ++++++-- frontend/src/views/HomeView.vue | 7 ++ test_rooms.js | 64 +++++++++++ 16 files changed, 425 insertions(+), 39 deletions(-) create mode 100644 DbQueryApp/DbQueryApp.csproj create mode 100644 DbQueryApp/Program.cs create mode 100644 QueryRooms.cs create mode 100644 QueryRooms.csproj create mode 100644 frontend/src/components/TestRoomList.vue create mode 100644 test_rooms.js diff --git a/DbQueryApp/DbQueryApp.csproj b/DbQueryApp/DbQueryApp.csproj new file mode 100644 index 0000000..44fac84 --- /dev/null +++ b/DbQueryApp/DbQueryApp.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + \ No newline at end of file diff --git a/DbQueryApp/Program.cs b/DbQueryApp/Program.cs new file mode 100644 index 0000000..1c55450 --- /dev/null +++ b/DbQueryApp/Program.cs @@ -0,0 +1,56 @@ +using System; +using Npgsql; + +namespace DbQueryApp +{ + class Program + { + static void Main(string[] args) + { + try + { + // 数据库连接字符串 + string connectionString = "Host=39.105.7.196;Port=5432;Database=TerritoryGame;Username=postgres;Password=xyq0716"; + + // 创建连接 + using (var connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + Console.WriteLine("成功连接到数据库"); + + // 查询等待状态的房间 + string query = "SELECT \"Id\", \"Name\", \"CreatedAt\" FROM \"GameRooms\" WHERE \"Status\" = 0"; + using (var command = new NpgsqlCommand(query, connection)) + { + using (var reader = command.ExecuteReader()) + { + 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}"); + } + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"发生错误: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/QueryRooms.cs b/QueryRooms.cs new file mode 100644 index 0000000..f26b0d2 --- /dev/null +++ b/QueryRooms.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using TerritoryGame.Domain.Entities; +using TerritoryGame.Infrastructure.Data; + +namespace QueryRooms +{ + class Program + { + static void Main(string[] args) + { + // 创建数据库上下文 + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseNpgsql("Host=39.105.7.196;Port=5432;Database=TerritoryGame;Username=postgres;Password=xyq0716"); + + using (var context = new GameDbContext(optionsBuilder.Options)) + { + try + { + // 查询等待状态的房间 + var waitingRooms = context.GameRooms + .Where(r => r.Status == GameStatus.Waiting) + .ToList(); + + Console.WriteLine($"找到 {waitingRooms.Count} 个等待状态的房间"); + foreach (var room in waitingRooms) + { + Console.WriteLine($"房间ID: {room.Id}, 名称: {room.Name}, 创建时间: {room.CreatedAt}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"查询出错: {ex.Message}"); + } + } + } + } +} \ No newline at end of file diff --git a/QueryRooms.csproj b/QueryRooms.csproj new file mode 100644 index 0000000..87c145e --- /dev/null +++ b/QueryRooms.csproj @@ -0,0 +1,20 @@ + + + + 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..60ff469 100644 --- a/backend/src/TerritoryGame.Api/Hubs/GameHub.cs +++ b/backend/src/TerritoryGame.Api/Hubs/GameHub.cs @@ -19,7 +19,8 @@ public class GameHub : Hub try { var rooms = await _gameService.GetAvailableRoomsAsync(); - await Clients.Caller.SendAsync("RoomListUpdated", new { rooms }); + // 直接发送房间数组,而不是包装在对象中 + await Clients.Caller.SendAsync("RoomListUpdated", rooms); } catch (Exception ex) { diff --git a/backend/src/TerritoryGame.Api/TerritoryGame.Api.csproj b/backend/src/TerritoryGame.Api/TerritoryGame.Api.csproj index 6bd1a28..2f9a5d9 100644 --- a/backend/src/TerritoryGame.Api/TerritoryGame.Api.csproj +++ b/backend/src/TerritoryGame.Api/TerritoryGame.Api.csproj @@ -7,7 +7,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/backend/src/TerritoryGame.Api/TerritoryGame.Api.http b/backend/src/TerritoryGame.Api/TerritoryGame.Api.http index 7d824a8..e2c5db3 100644 --- a/backend/src/TerritoryGame.Api/TerritoryGame.Api.http +++ b/backend/src/TerritoryGame.Api/TerritoryGame.Api.http @@ -19,7 +19,7 @@ Content-Type: application/json ### # 测试加入房间端点 @roomId = {{$guid}} -POST {{url}}/api/Game/rooms/3dfcbab9-3481-4d16-b445-a9bd6d9dae46/join +POST {{url}}/api/Game/rooms/1291d4d3-fac3-489c-82d7-171aad48f922/join Content-Type: application/json { @@ -29,9 +29,11 @@ Content-Type: application/json ### # 测试获取房间信息端点 -GET {{url}}/api/Game/rooms/3dfcbab9-3481-4d16-b445-a9bd6d9dae46 +GET {{url}}/api/Game/rooms/1291d4d3-fac3-489c-82d7-171aad48f922 Accept: application/json +### +GET {{url}}/api/Game/rooms/available HTTP/1.1 ### # 测试开始游戏端点 POST {{url}}/api/Game/rooms/3dfcbab9-3481-4d16-b445-a9bd6d9dae46/start diff --git a/backend/src/TerritoryGame.Application/Services/GameService.cs b/backend/src/TerritoryGame.Application/Services/GameService.cs index a8451e7..7884ad5 100644 --- a/backend/src/TerritoryGame.Application/Services/GameService.cs +++ b/backend/src/TerritoryGame.Application/Services/GameService.cs @@ -94,34 +94,21 @@ public class GameService : DomainServices.IGameService { try { - // 先从缓存获取 - var cachedRooms = await _cache.GetStringAsync("available_rooms"); - if (!string.IsNullOrEmpty(cachedRooms)) - { - return JsonSerializer.Deserialize>(cachedRooms); - } + // 调试日志:记录方法开始执行 + Console.WriteLine("GetAvailableRoomsAsync method started"); + + // 暂时禁用缓存,直接从数据库获取 + Console.WriteLine("Bypassing cache, fetching directly from database"); - // 缓存未命中,从数据库获取 + // 从数据库获取 var rooms = await _context.GameRooms .Include(r => r.Players) .Include(r => r.Spectators) - .Where(r => r.Status == GameStatus.Waiting) .OrderByDescending(r => r.CreatedAt) .ToListAsync(); - // 更新缓存 - try - { - await _cache.SetStringAsync( - "available_rooms", - JsonSerializer.Serialize(rooms), - new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) } - ); - } - catch (RedisConnectionException ex) - { - Console.WriteLine($"Redis connection error: {ex.Message}"); - } + // 调试日志:记录获取到的房间数量 + Console.WriteLine($"Fetched {rooms.Count} rooms from database"); return rooms; } diff --git a/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs b/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs index 2801080..37d0f0e 100644 --- a/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs +++ b/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs @@ -25,7 +25,31 @@ public static class DependencyInjection // 添加缓存 services.AddStackExchangeRedisCache(options => { - options.Configuration = configuration.GetConnectionString("Redis"); + // 从配置中获取Redis连接字符串 + var redisConnectionString = configuration.GetConnectionString("Redis"); + + // 创建Redis配置选项 + var redisConfig = StackExchange.Redis.ConfigurationOptions.Parse(redisConnectionString); + + // 设置连接超时为10秒 + redisConfig.ConnectTimeout = 10000; + + // 设置同步操作超时为10秒 + redisConfig.SyncTimeout = 10000; + + // 设置异步操作超时为10秒 + redisConfig.AsyncTimeout = 10000; + + // 启用连接重试 + redisConfig.ConnectRetry = 3; + + // 允许管理员操作 + redisConfig.AllowAdmin = true; + + // 配置SSL(如果需要) + redisConfig.Ssl = false; + + options.ConfigurationOptions = redisConfig; options.InstanceName = "TerritoryGame_"; }); diff --git a/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj b/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj index 79a91c1..724f028 100644 --- a/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj +++ b/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj @@ -6,14 +6,14 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + diff --git a/frontend/package.json b/frontend/package.json index af28ab9..3502250 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,6 +16,7 @@ "dependencies": { "@microsoft/signalr": "^9.0.6", "chart.js": "^4.5.0", + "node-fetch": "^3.3.2", "pinia": "^3.0.3", "vue": "^3.5.18", "vue-router": "^4.5.1" diff --git a/frontend/src/components/RoomList.vue b/frontend/src/components/RoomList.vue index 7bfb4c9..91ea86c 100644 --- a/frontend/src/components/RoomList.vue +++ b/frontend/src/components/RoomList.vue @@ -149,11 +149,32 @@ onUnmounted(() => { // 获取房间列表 function fetchRoomList() { console.log('Requesting room list from server...'); - socketService.emit('GetRoomList'); + // 添加超时处理,确保请求不会无限等待 + const timeoutId = setTimeout(() => { + console.error('GetRoomList request timed out'); + }, 5000); + + // 发送请求并处理响应 + socketService.emit('GetRoomList', {}, (response) => { + clearTimeout(timeoutId); + console.log('GetRoomList response received:', response); + // 手动调用处理函数 + handleRoomListUpdated(response); + }); } +// 状态转换映射 +const statusMap = { + 'Waiting': '等待中', + 'Playing': '游戏中', + 'Finished': '已结束' +}; + // 监听房间列表更新事件 function handleRoomListUpdated(response) { + // 调试:打印完整响应 + console.log('Received RoomListUpdated response:', response); + // 处理两种可能的响应格式 let updatedRooms = []; if (Array.isArray(response)) { @@ -168,6 +189,20 @@ function handleRoomListUpdated(response) { console.error('Invalid room list response:', response); } + // 调试:打印接收到的房间数量 + console.log(`Total rooms received: ${updatedRooms.length}`); + + // 如果有房间数据,打印第一个房间的详细信息 + if (updatedRooms.length > 0) { + console.log('First room details:', updatedRooms[0]); + } + + // 转换房间状态为中文 + updatedRooms = updatedRooms.map(room => ({ + ...room, + status: statusMap[room.status] || room.status + })); + // 调试:查看满员房间数量和状态 const fullRooms = updatedRooms.filter(room => room.currentPlayers === room.maxPlayers); console.log(`Received ${updatedRooms.length} rooms, ${fullRooms.length} are full`); diff --git a/frontend/src/components/TestRoomList.vue b/frontend/src/components/TestRoomList.vue new file mode 100644 index 0000000..858f1df --- /dev/null +++ b/frontend/src/components/TestRoomList.vue @@ -0,0 +1,105 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/services/socketService.js b/frontend/src/services/socketService.js index 3496b59..389cf24 100644 --- a/frontend/src/services/socketService.js +++ b/frontend/src/services/socketService.js @@ -133,6 +133,20 @@ export class SocketService { // 发送消息 (调用SignalR方法) emit(methodName, ...args) { + // 保存原始回调(如果有) + let originalCallback = null; + if (args.length > 0 && typeof args[args.length - 1] === 'function') { + originalCallback = args.pop(); + } + + // 包装回调以添加更多调试信息 + const wrappedCallback = (result) => { + console.log(`Received response for ${methodName}:`, result); + if (originalCallback) { + originalCallback(result); + } + }; + // 确保连接已初始化 if (!this.connection) { console.error(`Cannot invoke ${methodName}: Connection not initialized. Call init() first.`); @@ -140,10 +154,18 @@ export class SocketService { this.init() .then(() => { console.log(`Connection initialized, invoking ${methodName}...`); - this.emit(methodName, ...args); + // 重新调用,带上包装后的回调 + if (originalCallback) { + this.emit(methodName, ...args, wrappedCallback); + } else { + this.emit(methodName, ...args); + } }) .catch(error => { console.error(`Failed to initialize connection when invoking ${methodName}:`, error); + if (originalCallback) { + originalCallback({ error: error.message }); + } }); return; } @@ -155,24 +177,33 @@ export class SocketService { this.init() .then(() => { console.log(`Connection restored, retrying ${methodName}...`); - this.emit(methodName, ...args); + // 重新调用,带上包装后的回调 + if (originalCallback) { + this.emit(methodName, ...args, wrappedCallback); + } else { + this.emit(methodName, ...args); + } }) .catch(error => { console.error(`Failed to reconnect when invoking ${methodName}:`, error); + if (originalCallback) { + originalCallback({ error: error.message }); + } }); return; } console.log(`Invoking SignalR method: ${methodName}`); - // 如果最后一个参数是函数,将其作为回调 - if (typeof args[args.length - 1] === 'function') { - const callback = args.pop(); + if (originalCallback) { this.connection.invoke(methodName, ...args) .then(result => { console.log(`Successfully invoked ${methodName}`); - callback(result); + wrappedCallback(result); }) - .catch(error => console.error(`Error invoking ${methodName}:`, error)); + .catch(error => { + console.error(`Error invoking ${methodName}:`, error); + wrappedCallback({ error: error.message }); + }); } else { this.connection.invoke(methodName, ...args) .then(() => console.log(`Successfully invoked ${methodName}`)) diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index 1609678..586f156 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -1,5 +1,6 @@