diff --git a/DbQueryApp/DbQueryApp.csproj b/DbQueryApp/DbQueryApp.csproj new file mode 100644 index 0000000000000000000000000000000000000000..44fac84807d13635d0b930e3e3d8ed579399964d --- /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 0000000000000000000000000000000000000000..1c554509cb54d3738255cf7aa789e2a34a6e419b --- /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 0000000000000000000000000000000000000000..f26b0d2d9bc9166e04018f5db71b37cb3567e87a --- /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 0000000000000000000000000000000000000000..87c145eda4cb5a4507aaedfc41c5cec4b4d5cca0 --- /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 1f8b6572232a10c34da9f1ff6a21288eebf412ce..60ff4699bc0297ca10fc418986f324ab5a4de885 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 6bd1a28e45dd12781cfaecf9ef343a274f592395..2f9a5d94a78814f488648291b193be385b4a1115 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 7d824a8a54023c93bd9465897ab1a0cafcc29769..e2c5db3628c2bb4fb4feb04b13650a668e5ebbbf 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 a8451e779bbba4ab7bc00916a85738190032388c..7884ad50ff20652167a6155e394b1ffa89a4880f 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 2801080b8ba5d08db30748361ac566cabe718165..37d0f0edda575d4133e9ba97afc60eff1a6727fe 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 79a91c1f4b67e382cd821a17980bb512fae5703b..724f028da9da9895efda86605e6c97607bb85ef5 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 af28ab9e323e7851893c546e007f5ec96347d1d6..3502250d8581a30040f4e7f0019b48884c8d526a 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 7bfb4c9766e64cccf9b112b4cee335b66b0af433..91ea86cd67661d742136b12a14d1b02d2c49419f 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 0000000000000000000000000000000000000000..858f1df90026c097d5e29c5fa1dd38fdb40a3a27 --- /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 3496b59471a9e8d3123bac0151ca28f3e851360d..389cf2423fde948f0dd79368d229dce1c77aba74 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 1609678085728e41d3b83b935b150c033fd32c96..586f156f466950165e57f233e239d29fa797ec2f 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -1,5 +1,6 @@