diff --git a/backend/src/TerritoryGame.Api/Hubs/CreateRoomParameters.cs b/backend/src/TerritoryGame.Api/Hubs/CreateRoomParameters.cs new file mode 100644 index 0000000000000000000000000000000000000000..cbf190d7809c23478c4d79ccf8d8f49fc2f30822 --- /dev/null +++ b/backend/src/TerritoryGame.Api/Hubs/CreateRoomParameters.cs @@ -0,0 +1,16 @@ +using System; + +namespace TerritoryGame.Api.Hubs +{ + public class CreateRoomParameters + { + public string name { get; set; } = string.Empty; + public string? password { get; set; } + public int maxPlayers { get; set; } = 6; + public int gameTime { get; set; } = 180; + public string description { get; set; } = string.Empty; + public string difficulty { get; set; } = string.Empty; + public string mapType { get; set; } = string.Empty; + public bool allowSpectators { get; set; } = true; + } +} \ 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 1845482979e409b728a304e589936866c52e9bc9..1f8b6572232a10c34da9f1ff6a21288eebf412ce 100644 --- a/backend/src/TerritoryGame.Api/Hubs/GameHub.cs +++ b/backend/src/TerritoryGame.Api/Hubs/GameHub.cs @@ -42,7 +42,7 @@ public class GameHub : Hub } // 创建房间 - public async Task CreateRoom(dynamic parameters) + public async Task CreateRoom(CreateRoomParameters parameters) { try { diff --git a/backend/src/TerritoryGame.Api/TerritoryGame.Api.http b/backend/src/TerritoryGame.Api/TerritoryGame.Api.http index b24cbe7148ed570accef58aa0bd4c1c12e7a6d81..7d824a8a54023c93bd9465897ab1a0cafcc29769 100644 --- a/backend/src/TerritoryGame.Api/TerritoryGame.Api.http +++ b/backend/src/TerritoryGame.Api/TerritoryGame.Api.http @@ -68,4 +68,4 @@ Content-Type: application/json "playerId": "{{$guid}}" } -### +###测试 diff --git a/backend/src/TerritoryGame.Domain/Entities/Spectator.cs b/backend/src/TerritoryGame.Domain/Entities/Spectator.cs index 6eadf5330a268f7cb82adc254b5810c3ae14972e..d3958a0c421ee28319326c4c71c7b9218ea13c0c 100644 --- a/backend/src/TerritoryGame.Domain/Entities/Spectator.cs +++ b/backend/src/TerritoryGame.Domain/Entities/Spectator.cs @@ -1,16 +1,21 @@ namespace TerritoryGame.Domain.Entities; -public class Spectator : Player +public class Spectator { - // 观战者特有的属性可以在这里添加 - - // 重写父类方法(如果需要) - public new void GoOffline() + public Guid Id { get; set; } = Guid.NewGuid(); + public string NickName { get; set; } = string.Empty; + public bool IsOnline { get; set; } = true; + public Guid RoomId { get; set; } + public DateTime JoinedAt { get; set; } = DateTime.UtcNow; + + // 设置观战者离线 + public void GoOffline() { IsOnline = false; } - public new void GoOnline() + // 设置观战者在线 + public void GoOnline() { IsOnline = true; } diff --git a/backend/src/TerritoryGame.Infrastructure/Data/GameDbContext.cs b/backend/src/TerritoryGame.Infrastructure/Data/GameDbContext.cs index c83184e49b225f7958f1e737257f5aed525e2413..8670e929a6bed47917884afcac5cd7a3ee7ee569 100644 --- a/backend/src/TerritoryGame.Infrastructure/Data/GameDbContext.cs +++ b/backend/src/TerritoryGame.Infrastructure/Data/GameDbContext.cs @@ -55,12 +55,20 @@ public class GameDbContext : DbContext, IGameDbContext entity.ToTable("Players"); // 明确指定表名 }); - // 配置Spectator (继承自Player) + // 配置Spectator modelBuilder.Entity(entity => { - entity.HasBaseType(); // 明确指定基类 + entity.HasKey(e => e.Id); + entity.Property(e => e.NickName).IsRequired().HasMaxLength(50); + entity.Property(e => e.IsOnline).IsRequired(); + entity.Property(e => e.JoinedAt).IsRequired(); entity.ToTable("Spectators"); // 明确指定表名 - // 这里可以配置Spectator特有的属性(如果有) + + // 配置外键关系 + entity.HasOne() + .WithMany(r => r.Spectators) + .HasForeignKey(s => s.RoomId) + .OnDelete(DeleteBehavior.Cascade); }); // 配置PaintAction diff --git a/backend/src/TerritoryGame.Infrastructure/Migrations/20250819073803_FixSpectatorForeignKey.Designer.cs b/backend/src/TerritoryGame.Infrastructure/Migrations/20250819073803_FixSpectatorForeignKey.Designer.cs new file mode 100644 index 0000000000000000000000000000000000000000..c3d6a3145c6d619eba6fd1f9e70e887f373b6a6d --- /dev/null +++ b/backend/src/TerritoryGame.Infrastructure/Migrations/20250819073803_FixSpectatorForeignKey.Designer.cs @@ -0,0 +1,190 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using TerritoryGame.Infrastructure.Data; + +#nullable disable + +namespace TerritoryGame.Infrastructure.Migrations +{ + [DbContext(typeof(GameDbContext))] + [Migration("20250819073803_FixSpectatorForeignKey")] + partial class FixSpectatorForeignKey + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("TerritoryGame.Domain.Entities.GameRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("GameDuration") + .HasColumnType("integer"); + + b.Property("MaxPlayers") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Password") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("GameRooms"); + }); + + modelBuilder.Entity("TerritoryGame.Domain.Entities.PaintAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BrushSize") + .HasColumnType("integer"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("RoomId") + .HasColumnType("uuid"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("X") + .HasColumnType("integer"); + + b.Property("Y") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("PaintActions"); + }); + + modelBuilder.Entity("TerritoryGame.Domain.Entities.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Area") + .HasColumnType("integer"); + + b.Property("Color") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("IsOnline") + .HasColumnType("boolean"); + + b.Property("JoinedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("NickName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("RoomId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoomId"); + + b.ToTable("Players", (string)null); + }); + + modelBuilder.Entity("TerritoryGame.Domain.Entities.Spectator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsOnline") + .HasColumnType("boolean"); + + b.Property("JoinedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("NickName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RoomId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoomId"); + + b.ToTable("Spectators", (string)null); + }); + + modelBuilder.Entity("TerritoryGame.Domain.Entities.Player", b => + { + b.HasOne("TerritoryGame.Domain.Entities.GameRoom", null) + .WithMany("Players") + .HasForeignKey("RoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("TerritoryGame.Domain.Entities.Spectator", b => + { + b.HasOne("TerritoryGame.Domain.Entities.GameRoom", null) + .WithMany("Spectators") + .HasForeignKey("RoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("TerritoryGame.Domain.Entities.GameRoom", b => + { + b.Navigation("Players"); + + b.Navigation("Spectators"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/TerritoryGame.Infrastructure/Migrations/20250819073803_FixSpectatorForeignKey.cs b/backend/src/TerritoryGame.Infrastructure/Migrations/20250819073803_FixSpectatorForeignKey.cs new file mode 100644 index 0000000000000000000000000000000000000000..ad235abcca9be2e811bf6aeba7fc80328b9eaa3f --- /dev/null +++ b/backend/src/TerritoryGame.Infrastructure/Migrations/20250819073803_FixSpectatorForeignKey.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TerritoryGame.Infrastructure.Migrations +{ + /// + public partial class FixSpectatorForeignKey : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/backend/src/TerritoryGame.Infrastructure/Migrations/GameDbContextModelSnapshot.cs b/backend/src/TerritoryGame.Infrastructure/Migrations/GameDbContextModelSnapshot.cs index 4b35cc3d6dd358d9c55ff2ef999165e3f6971949..1afbef8cf648a736b7102b60f2706f975312a15b 100644 --- a/backend/src/TerritoryGame.Infrastructure/Migrations/GameDbContextModelSnapshot.cs +++ b/backend/src/TerritoryGame.Infrastructure/Migrations/GameDbContextModelSnapshot.cs @@ -127,7 +127,7 @@ namespace TerritoryGame.Infrastructure.Migrations b.HasIndex("RoomId"); - b.ToTable("Players"); + b.ToTable("Players", (string)null); }); modelBuilder.Entity("TerritoryGame.Domain.Entities.Spectator", b => @@ -154,7 +154,7 @@ namespace TerritoryGame.Infrastructure.Migrations b.HasIndex("RoomId"); - b.ToTable("Spectators"); + b.ToTable("Spectators", (string)null); }); modelBuilder.Entity("TerritoryGame.Domain.Entities.Player", b =>