diff --git a/DbQueryApp/DbQueryApp.csproj b/DbQueryApp/DbQueryApp.csproj
index 44fac84807d13635d0b930e3e3d8ed579399964d..3e4aec921cc2cf790fe41f2eae0f8a9f642ffe60 100644
--- a/DbQueryApp/DbQueryApp.csproj
+++ b/DbQueryApp/DbQueryApp.csproj
@@ -8,7 +8,7 @@
-
+
-
\ No newline at end of file
+
diff --git a/DbQueryApp/Program.cs b/DbQueryApp/Program.cs
index 1c554509cb54d3738255cf7aa789e2a34a6e419b..75d63b971145e8aff4d36ebda9beff5f1ee67277 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 0000000000000000000000000000000000000000..5d5d01e82ce729d56e9c292fa96e793c2ed7998c
--- /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 0000000000000000000000000000000000000000..efd57d2e992c30c2b8bb7b1e32bd4e2d3c671aa5
--- /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/HealthCheckTest/HealthCheckTest.csproj b/HealthCheckTest/HealthCheckTest.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..91b464afeacc105176e61d05a1def7d10b514917
--- /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 0000000000000000000000000000000000000000..3751555cbd32d09340c8cb7b75ce8311c4330054
--- /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/TestDbConnection.cs b/TestDbConnection.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3d0d2b8acbf6da373acd9f65601eea55f8f8ea7c
--- /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 0000000000000000000000000000000000000000..8eedfa13efe7a444ee1797d576b497b61de83e66
--- /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 0000000000000000000000000000000000000000..44e2a68773ae23693062d7b173a1d46e23b60726
--- /dev/null
+++ b/TestDbConnectionDir/TestDbConnection.cs
@@ -0,0 +1,140 @@
+using StackExchange.Redis;
+using System;
+using System.Threading.Tasks;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ try
+ {
+ // 从配置中获取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($"Redis连接失败: {ex.Message}");
+ Console.WriteLine($"错误详情: {ex.InnerException?.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/TestDbConnectionDir/TestDbConnection.csproj b/TestDbConnectionDir/TestDbConnection.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..bc999e3e1c0b7e0f7a7d604afbbeba4e9e3a6133
--- /dev/null
+++ b/TestDbConnectionDir/TestDbConnection.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestRedisConnection.cs b/TestRedisConnection.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f43a3ee9348330d41ed3e88cd520bdada496aa9f
--- /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 0000000000000000000000000000000000000000..df07d8a4741dbed748176026ad58f5a9f45aa013
--- /dev/null
+++ b/TestRedisConnection.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/CompleteDITest.cs b/backend/CompleteDITest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6a53e9f57ccb38ca911aa6e0a2beb66d42131524
--- /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 0000000000000000000000000000000000000000..62a745c0d7afd1efc928aeb14a20817b7b3bdd03
--- /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 0000000000000000000000000000000000000000..f7002a93b659d87b8e24594682b7df4147a9f83e
--- /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 1f8b6572232a10c34da9f1ff6a21288eebf412ce..5099d78f2c381fb38ff6b400494a83b009290bdd 100644
--- a/backend/src/TerritoryGame.Api/Hubs/GameHub.cs
+++ b/backend/src/TerritoryGame.Api/Hubs/GameHub.cs
@@ -2,27 +2,58 @@ 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()
{
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", new { rooms = formattedRooms });
+ } catch (Exception ex) {
+ Console.WriteLine($"Error in GetRoomList: {ex.Message}");
await Clients.Caller.SendAsync("GetRoomListFailed", new { success = false, message = ex.Message });
}
}
@@ -321,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 46e60c8464748df0efc9ec1b8fb09bf7570a8a4b..7959d20a3d3750003035e0e08bcfc90deb1a40ce 100644
--- a/backend/src/TerritoryGame.Api/Program.cs
+++ b/backend/src/TerritoryGame.Api/Program.cs
@@ -38,14 +38,13 @@ 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 =>
{
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 b96e1644ba602636da6c8f5ace1f53a3337b07a3..564a2971bf4c8b5ca24c08e04ad84128edf326cd 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 dfb18c3932ba4d9006d4459a63a041951b42506d..2baf8feff1ae043876628776bcf9463f90851888 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;
@@ -95,38 +96,105 @@ public class GameService : DomainServices.IGameService
try
{
// 先从缓存获取
- var cachedRooms = await _cache.GetStringAsync("available_rooms");
- if (!string.IsNullOrEmpty(cachedRooms))
+ List rooms = null;
+ try
+ {
+ // 优化:添加异步操作超时处理
+ var cacheTask = _cache.GetStringAsync("available_rooms");
+ if (await Task.WhenAny(cacheTask, Task.Delay(1000)) == cacheTask)
+ {
+ var cachedRooms = await cacheTask;
+ if (!string.IsNullOrEmpty(cachedRooms))
+ {
+ rooms = JsonSerializer.Deserialize>(cachedRooms);
+ return rooms;
+ }
+ }
+ else
+ {
+ Console.WriteLine("Cache retrieval timed out");
+ }
+ }
+ 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 ?? "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)
+ {
+ Console.WriteLine($"Error reading from cache: {ex.Message}");
}
- // 缓存未命中,从数据库获取
- var rooms = await _context.GameRooms
+ // 缓存未命中或读取失败,从数据库获取
+ 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();
+ // 如果没有房间,创建一个测试房间用于调试
+ if (roomsFromDb.Count == 0)
+ {
+ 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();
+ roomsFromDb = new List { testRoom };
+ }
+
// 更新缓存
try
{
- await _cache.SetStringAsync(
- "available_rooms",
- JsonSerializer.Serialize(rooms),
- new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) }
- );
+ // 优化:调整缓存策略,增加绝对过期时间
+ 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: {ex.Message}");
+ Console.WriteLine($"Redis connection error when updating cache: {ex.Message}");
+ Console.WriteLine($"Redis error details: {ex.InnerException?.Message ?? "No inner exception"}");
+ }
+ 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;
}
}
@@ -134,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)
{
- return JsonSerializer.Deserialize(cachedRoom)!
- ?? throw new InvalidOperationException("Failed to deserialize cached room");
+ var cachedRoom = await cacheTask;
+ if (!string.IsNullOrEmpty(cachedRoom))
+ {
+ return JsonSerializer.Deserialize(cachedRoom)!
+ ?? throw new InvalidOperationException("Failed to deserialize cached room");
+ }
+ }
+ else
+ {
+ 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)
@@ -177,51 +271,102 @@ 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();
+
+ // 清除旧缓存并缓存更新后的房间信息
+ 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;
+ }
+ 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;
+ }
}
// 离开房间
@@ -412,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.Domain/Interfaces/IGameDbContext.cs b/backend/src/TerritoryGame.Domain/Interfaces/IGameDbContext.cs
index ea40f9ca5689aadcee81b648892690ce946dd6e7..706febb4fb29b1940e057bd972881a11e79530d5 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 8670e929a6bed47917884afcac5cd7a3ee7ee569..19165113335608790df20cb342efae36e71a6669 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/DependencyInjection.cs b/backend/src/TerritoryGame.Infrastructure/DependencyInjection.cs
index 37d0f0edda575d4133e9ba97afc60eff1a6727fe..3a84190ff9a547a2acabc954d78b761d771794d3 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;
@@ -31,28 +32,28 @@ public static class DependencyInjection
// 创建Redis配置选项
var redisConfig = StackExchange.Redis.ConfigurationOptions.Parse(redisConnectionString);
- // 设置连接超时为10秒
- redisConfig.ConnectTimeout = 10000;
+ // 连接优化配置
+ redisConfig.ConnectTimeout = 5000; // 减少连接超时时间到5秒
+ redisConfig.SyncTimeout = 5000; // 减少同步操作超时到5秒
+ redisConfig.AsyncTimeout = 5000; // 减少异步操作超时到5秒
- // 设置同步操作超时为10秒
- redisConfig.SyncTimeout = 10000;
+ // 重试策略
+ redisConfig.ConnectRetry = 3; // 重试次数
+ redisConfig.ReconnectRetryPolicy = new StackExchange.Redis.ExponentialRetry(1000, 10); // 指数退避重试策略
- // 设置异步操作超时为10秒
- redisConfig.AsyncTimeout = 10000;
-
- // 启用连接重试
- redisConfig.ConnectRetry = 3;
-
- // 允许管理员操作
- redisConfig.AllowAdmin = true;
-
- // 配置SSL(如果需要)
- redisConfig.Ssl = false;
+ // 其他配置
+ redisConfig.AllowAdmin = true; // 允许管理员操作
+ redisConfig.Ssl = false; // 禁用SSL,因为连接字符串中没有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 0000000000000000000000000000000000000000..b8e418cc3cb6861b422767e4be32aafde57a7624
--- /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 724f028da9da9895efda86605e6c97607bb85ef5..a4aab94209880707bc0294aed06c69b8da0e0b60 100644
--- a/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj
+++ b/backend/src/TerritoryGame.Infrastructure/TerritoryGame.Infrastructure.csproj
@@ -11,8 +11,9 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
+
diff --git a/frontend/src/components/GameCanvas.vue b/frontend/src/components/GameCanvas.vue
index be6c69078079ed28164eb1dd0c5520d93ff3f078..4146c97c307cf74a501724e9681da7228c61da0f 100644
--- a/frontend/src/components/GameCanvas.vue
+++ b/frontend/src/components/GameCanvas.vue
@@ -1,23 +1,30 @@
-
+
+
+
+
+
+
+
{{ canvasTooltipText }}
+
\ No newline at end of file