diff --git a/AX.WebDrillServer/AX.WebDrillServer.sln b/AX.WebDrillServer/AX.WebDrillServer.sln index dc60608..4c14b7f 100644 --- a/AX.WebDrillServer/AX.WebDrillServer.sln +++ b/AX.WebDrillServer/AX.WebDrillServer.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.3.32825.248 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AX.WebDrillServer", "AX.WebDrillServer.csproj", "{B594C541-C030-412E-8B89-D38C24765D2D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestHub", "..\TestHub\TestHub.csproj", "{4B962EF6-1C2A-4283-B21D-46E6C57DD8D2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,6 +20,12 @@ Global {B594C541-C030-412E-8B89-D38C24765D2D}.Release|Any CPU.Build.0 = Release|Any CPU {B594C541-C030-412E-8B89-D38C24765D2D}.Test|Any CPU.ActiveCfg = Test|Any CPU {B594C541-C030-412E-8B89-D38C24765D2D}.Test|Any CPU.Build.0 = Test|Any CPU + {4B962EF6-1C2A-4283-B21D-46E6C57DD8D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B962EF6-1C2A-4283-B21D-46E6C57DD8D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B962EF6-1C2A-4283-B21D-46E6C57DD8D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B962EF6-1C2A-4283-B21D-46E6C57DD8D2}.Release|Any CPU.Build.0 = Release|Any CPU + {4B962EF6-1C2A-4283-B21D-46E6C57DD8D2}.Test|Any CPU.ActiveCfg = Debug|Any CPU + {4B962EF6-1C2A-4283-B21D-46E6C57DD8D2}.Test|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AX.WebDrillServer/Controllers/AccountsController.cs b/AX.WebDrillServer/Controllers/AccountsController.cs index c641126..6a74a58 100644 --- a/AX.WebDrillServer/Controllers/AccountsController.cs +++ b/AX.WebDrillServer/Controllers/AccountsController.cs @@ -109,18 +109,16 @@ namespace AX.WebDrillServer.Controllers } } /// - /// 登出 + /// 登出系统。 /// - /// /// - [AllowAnonymous] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [HttpPost("[action]")] - public async Task> SignOut([FromBody] UserForCreateDto dto) + [ProducesResponseType(StatusCodes.Status200OK)] + [HttpPost("SignOut")] + public ActionResult AccountSignOut() { - throw new NotImplementedException(); + //TODO: 把 JWT 放入黑名单,其它地方则验证黑名单,将来再处理 + return Ok(); } /// /// 刷新令牌。 diff --git a/AX.WebDrillServer/Data/ApplicationDbContextSeed.cs b/AX.WebDrillServer/Data/ApplicationDbContextSeed.cs index a736b26..4477c7f 100644 --- a/AX.WebDrillServer/Data/ApplicationDbContextSeed.cs +++ b/AX.WebDrillServer/Data/ApplicationDbContextSeed.cs @@ -72,15 +72,15 @@ namespace AX.WebDrillServer.Data var org2 = new Organization() { Code = "1-1-1", - Name = "XXX大队", - Level = OrganizationLevel.Battalion, + Name = "XXX支队", + Level = OrganizationLevel.Brigade, }; context.Organizations.Add(org2); var org3 = new Organization() { Code = "1-1-1-1", - Name = "XXX支队", - Level = OrganizationLevel.Brigade, + Name = "XXX大队", + Level = OrganizationLevel.Battalion, }; context.Organizations.Add(org3); var org4 = new Organization() diff --git a/AX.WebDrillServer/Hubs/FireDeductionHub.cs b/AX.WebDrillServer/Hubs/FireDeductionHub.cs new file mode 100644 index 0000000..1315797 --- /dev/null +++ b/AX.WebDrillServer/Hubs/FireDeductionHub.cs @@ -0,0 +1,176 @@ +using AX.WebDrillServer.Data; +using AX.WebDrillServer.Extensions; +using AX.WebDrillServer.Middlewares.Jwts; +using AX.WebDrillServer.Models; +using AX.WebDrillServer.Services.FireDeductionHub; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using System.Collections.Concurrent; +using System.Security.Claims; + +namespace AX.WebDrillServer.Hubs +{ + //[Authorize] + public class FireDeductionHub : Hub + { + private readonly RoomManager roomManager; + + private readonly ApplicationDbContext _dbContext; + + private readonly ILogger _logger; + + public FireDeductionHub( + ApplicationDbContext dbContext, + ILogger logger, + RoomManager roomManager) + { + _dbContext = dbContext; + _logger = logger; + this.roomManager = roomManager; + } + public async Task CreateRoom(RoomCreateDto createInfo, FireDeductionUser user) + { + FireDeductionRoom room = new FireDeductionRoom(); + room.Owner = createInfo.UserId; + room.RoomId = Guid.NewGuid().ToString(); + room.RoomName = createInfo.RoomName; + room.Password = createInfo.RoomPassword; + room.Users.Add(user); + await Groups.AddToGroupAsync(user.ConnectionId, room.RoomId); + return room; + } + public async Task EnterRoom(string roomId, FireDeductionUser user, string? password = null) + { + bool result = false; + + var room = roomManager.GetRoom(roomId); + if (room != null && room.Password == password) + { + if (!room.Users.Contains(user)) + { + result = true; + } + else + { + throw new NotImplementedException("房间中已经存在该用户!"); + } + } + else + { + throw new NotImplementedException("房间信息有误!"); + } + + + return result; + } + + public async Task LeaveRoom(string roomId, string userId) + { + bool result = false; + await Task.Run(() => + { + var room = roomManager.GetRoom(roomId); + if (room != null) + { + var user = room.Users.FirstOrDefault(u => u.UserId == userId); + if (user != null) + { + room.Users.Remove(user); + if (room.Users.Count == 0) + { + //TODO:保持空房间xx分钟 + } + } + else + { + throw new NotImplementedException("用户信息有误!"); + } + } + else + { + throw new NotImplementedException("房间信息有误!"); + } + }); + + return result; + } + public async Task RoomSendMessage(string RoomId, string Message) + { + await Clients.Group(RoomId).SendAsync(Message); + //TODO:保存数据 + } + + public async Task SendMessage(string user, string Message) + { + await Clients.All.SendAsync(Message); + //TODO:保存数据 + } + + public override async Task OnConnectedAsync() + { + try + { + var userId = Context.UserIdentifier; + var userName = Context.GetNameOfCurrentUser(); + FireDeductionUser? user = roomManager.GetUser(userId!); + + if (user != null) + { + user.Online = true; + var room = roomManager.GetRoomByUserId(user.UserId); + if (room != null) + { + //断线重连 + } + } + else + { + user = new FireDeductionUser(); + user.ConnectionId = Context.ConnectionId; + user.UserId = userId!; + user.UserName = userName!; + } + _logger.LogInformation("[{userId}][{name}] connected", userId, userName); + + await base.OnConnectedAsync(); + } + catch (Exception) + { + throw; + } + } + + public override async Task OnDisconnectedAsync(Exception? exception) + { + try + { + var userId = Context.UserIdentifier; + var userName = Context.User?.FindFirstValue(JwtClaimTypes.Name); + if (userId == null) _logger.LogError("无效的 userId: [{userId}]!", userId); + + if (userId != null) + { + var user = roomManager.GetUser(userId!); + if (user != null) + { + user.Online = false; + var room = roomManager.GetRoomByUserId(user.UserId); + if (room != null) + { + //断线 + } + } + } + + await base.OnDisconnectedAsync(exception); + _logger.LogInformation("[{userId}][{name}] 断开了连接", userId, userName); + } + catch (Exception) + { + throw; + } + } + } +} diff --git a/AX.WebDrillServer/Models/Room.cs b/AX.WebDrillServer/Models/Room.cs deleted file mode 100644 index 4270008..0000000 --- a/AX.WebDrillServer/Models/Room.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace AX.WebDrillServer.Models -{ - /// - /// 房间表 - /// - public class Room : EntityBase - { - /// - /// 密码 - /// - public string? Password { get; set; } - /// - /// 所属 - /// - public User? Owner { get; set; } - /// - /// 用户 - /// - public ICollection? Users { get; set; } - } -} diff --git a/AX.WebDrillServer/Program.cs b/AX.WebDrillServer/Program.cs index b7be7c4..1038054 100644 --- a/AX.WebDrillServer/Program.cs +++ b/AX.WebDrillServer/Program.cs @@ -5,6 +5,7 @@ using AX.WebDrillServer.Extensions; using AX.WebDrillServer.Hubs; using AX.WebDrillServer.Middlewares; using AX.WebDrillServer.Middlewares.Jwts; +using AX.WebDrillServer.Services.FireDeductionHub; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.AspNetCore.Mvc.Formatters; @@ -18,7 +19,7 @@ using System.Text; using System.Text.Json; using System.Text.Json.Serialization; -const string ProductName = "Web服务器"; +const string ProductName = "沙盘推演"; const string ProductVersion = "v1.0"; const string ApiVersion = "v1"; @@ -108,7 +109,7 @@ builder.Services.AddAuthorization(); // Email Service //builder.Services.AddSingleton(); - +builder.Services.AddSingleton(); builder.Services.AddMemoryCache(); builder.Services @@ -271,5 +272,6 @@ app.MapControllers(); //app.MapHub("/hubs/notification"); //app.MapHub("/hubs/taskchat"); +app.MapHub("/hubs/FireDeductionHub"); app.Run(); \ No newline at end of file diff --git a/AX.WebDrillServer/Services/FireDeductionHub/FireDeductionRoom.cs b/AX.WebDrillServer/Services/FireDeductionHub/FireDeductionRoom.cs new file mode 100644 index 0000000..64e06c6 --- /dev/null +++ b/AX.WebDrillServer/Services/FireDeductionHub/FireDeductionRoom.cs @@ -0,0 +1,35 @@ +using Org.BouncyCastle.Asn1.X509; + +namespace AX.WebDrillServer.Services.FireDeductionHub +{ + public enum RoomState + { + 准备 = 0, + 进行中, + 结束 + } + /// + /// 沙盘推演房间 + /// + public class FireDeductionRoom + { + public string RoomId { get; set; } = null!; + public string RoomName { get; set; } = null!; + /// + /// 房间密码 + /// + public string? Password; + public RoomState State { get; set; } = RoomState.准备; + /// + /// 房间最大人数 + /// + public int MaxPersons = 100; + /// + /// 房间所属,默认是创建者 + /// + public string Owner { get; set; } = null!; + public List Users { get; set; } = new List(); + public Dictionary? FrameData { get; set; } + public Dictionary? EventData { get; set; } + } +} diff --git a/AX.WebDrillServer/Services/FireDeductionHub/FireDeductionUser.cs b/AX.WebDrillServer/Services/FireDeductionHub/FireDeductionUser.cs new file mode 100644 index 0000000..2e937ca --- /dev/null +++ b/AX.WebDrillServer/Services/FireDeductionHub/FireDeductionUser.cs @@ -0,0 +1,14 @@ +namespace AX.WebDrillServer.Services.FireDeductionHub +{ + /// + /// 沙盘推演用户 + /// + public class FireDeductionUser + { + public string UserId { get; set; } = null!; + public string UserName { get; set; } = null!; + public bool Online { get; set; } = true; + public bool Left { get; set; } + public string ConnectionId { get; set; } = null!; + } +} diff --git a/AX.WebDrillServer/Services/FireDeductionHub/RoomCreateDto.cs b/AX.WebDrillServer/Services/FireDeductionHub/RoomCreateDto.cs new file mode 100644 index 0000000..4246cf8 --- /dev/null +++ b/AX.WebDrillServer/Services/FireDeductionHub/RoomCreateDto.cs @@ -0,0 +1,11 @@ +namespace AX.WebDrillServer.Services.FireDeductionHub +{ + public class RoomCreateDto + { + public string UserId { get; set; } = null!; + public string? RoomPassword { get; set; } + public string RoomName { get; set; } = null!; + public int MaxPerson { get; set; } = 20; + + } +} diff --git a/AX.WebDrillServer/Services/FireDeductionHub/RoomManager.cs b/AX.WebDrillServer/Services/FireDeductionHub/RoomManager.cs new file mode 100644 index 0000000..e6ac7cd --- /dev/null +++ b/AX.WebDrillServer/Services/FireDeductionHub/RoomManager.cs @@ -0,0 +1,79 @@ +namespace AX.WebDrillServer.Services.FireDeductionHub +{ + public class RoomManager + { + + private List fireDeductionRooms = new List(); + private List fireDeductionUsers = new List(); + + + public FireDeductionRoom? GetRoom(string roomId) + { + lock (this) + { + return fireDeductionRooms.Where(r => r.RoomId == roomId).SingleOrDefault(); + } + } + public FireDeductionUser? GetUser(string userId) + { + lock (this) + { + return fireDeductionUsers.Where(r => r.UserId == userId).SingleOrDefault(); + } + } + public FireDeductionRoom? GetRoomByUserId(string userId) + { + lock (this) + { + return fireDeductionRooms.Where(r => r.Users.Where(u => u.UserId == userId).SingleOrDefault() != null).SingleOrDefault(); + } + } + public void AddRoom(FireDeductionRoom room) + { + lock (this) + { + if (!fireDeductionRooms.Contains(room)) + { + fireDeductionRooms.Add(room); + } + } + } + public void RemoveRoom(string roomId) + { + lock (this) + { + for (int i = 0; i < fireDeductionRooms.Count; i++) + { + if (fireDeductionRooms[i].RoomId == roomId) + { + fireDeductionRooms.RemoveAt(i); + } + } + } + } + + public void AddUser(FireDeductionUser user) + { + lock (this) + { + if (!fireDeductionUsers.Contains(user)) + { + fireDeductionUsers.Add(user); + } + } + } + public void RemoveUser(string userId) + { + lock (this) + { + for (int i = 0; i < fireDeductionUsers.Count; i++) + { + if (fireDeductionUsers[i].UserId == userId) + { + fireDeductionUsers.RemoveAt(i); + } + } + } + } + } +}