using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AdminService.GameConfigs; using ChessGameDomain; using Common; using CommonDomain; using CommonDomain.Enum; using CommonInfrastructure; using DotNetCore.CAP; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using MongoRepository; using RedisService; using RedisService.Param; using UserDomain.Entity; namespace AdminApi.GameConfigs { [ApiController, Route("adminapi/Admin/[controller]")] [Authorize] public class RoomController : BaseController { private readonly IRedisService _redisService; private readonly IGameConfigService _gameConfigService; private readonly ICapPublisher _capPublisher; private readonly IMongoBaseRepository _userMongo; private readonly ILogger _logger; public RoomController(IRedisService redisService, IGameConfigService gameConfigService, ICapPublisher capPublisher, IMongoBaseRepository userMongo, ILogger logger) { _redisService = redisService; _gameConfigService = gameConfigService; _capPublisher = capPublisher; _userMongo = userMongo; _logger = logger; } [HttpGet("GetAllRooms")] public async Task> GetAllRooms( int pageIndex = 1, int pageSize = 20, int? roomLevel = null, string? gameType = null, string? sortField = null, string? sortOrder = "desc") { var rooms = GetAllRoomsFromRedis()?.ToList() ?? new List(); foreach (var room in rooms) { if (room?.Players != null && room.Players.Count > 1) room.Players = room.Players.OrderBy(p => p.Position).ToList(); } if (roomLevel.HasValue) { rooms = rooms.Where(r => r != null && r.RoomLevel == roomLevel.Value).ToList(); } if (!string.IsNullOrEmpty(gameType) && int.TryParse(gameType, out var gameTypeValue)) { rooms = rooms.Where(r => r != null && (int)r.GameType == gameTypeValue).ToList(); } var total = rooms.Count; if (!string.IsNullOrEmpty(sortField)) { switch (sortField.ToLower()) { case "currentonline": rooms = sortOrder?.ToLower() == "asc" ? rooms.OrderBy(r => r.CurrentOnlinePlayerCount).ToList() : rooms.OrderByDescending(r => r.CurrentOnlinePlayerCount).ToList(); break; case "robotcount": rooms = sortOrder?.ToLower() == "asc" ? rooms.OrderBy(r => r.RobotCount).ToList() : rooms.OrderByDescending(r => r.RobotCount).ToList(); break; case "roomlevel": rooms = sortOrder?.ToLower() == "asc" ? rooms.OrderBy(r => r.RoomLevel).ToList() : rooms.OrderByDescending(r => r.RoomLevel).ToList(); break; default: rooms = rooms.OrderByDescending(r => r.RoomId).ToList(); break; } } var pagedRooms = rooms.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); return this.ToPageResult(pagedRooms, total); } [HttpGet("GetAllChessRooms")] public async Task> GetAllChessRooms( int pageIndex = 1, int pageSize = 20, int? state = null, int? gameType = null) { var rooms = _redisService.GetAllChessRooms()?.ToList(); if (state.HasValue) { rooms = rooms.Where(r => r != null && r.state == state.Value).ToList(); } if (gameType.HasValue) { rooms = rooms.Where(r => r != null && r.gameType == gameType.Value).ToList(); } var total = rooms.Count; rooms = rooms.OrderByDescending(r => r.CreateTime).ToList(); var pagedRooms = rooms.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); return this.ToPageResult(pagedRooms, total); } [HttpGet("GetChessRoomDetail")] public async Task> GetChessRoomDetail(string roomId) { var room = _redisService.GetChessRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); return this.ToResult(room); } [HttpGet("ClearChessRoom")] public async Task> ClearChessRoom(string roomId) { var room = _redisService.GetChessRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); int playerCount = 0; foreach (var user in room.users.ToList()) { try { var userRedis = _redisService.GetRedisUserCId(user.userId); if (userRedis != null) { userRedis.CId = ""; _redisService.SetRedisUserCId(userRedis); } playerCount++; } catch (Exception ex) { _logger.LogError(ex, "管理员清空象棋房间 清理userRedis失败 roomId={RoomId} userId={UserId}", roomId, user.userId); } } room.users.Clear(); room.state = 0; room.robotCount = 0; room.gold = 0; room.gameType = 0; room.password = ""; room.IsGameEnded = false; room.EndGameRequests?.Clear(); room.PendingWinUserId = null; room.TimeoutLoserUserId = ""; room.LastActiveTime = 0; _redisService.SetChessRoom(room); return this.ToResult($"已清空房间 {roomId} 的所有玩家({playerCount} 人)"); } [HttpGet("KickChessPlayer")] public async Task> KickChessPlayer(string roomId, string userId) { if (string.IsNullOrWhiteSpace(roomId) || string.IsNullOrWhiteSpace(userId)) return this.ToFailResult("房间ID和玩家ID不能为空"); var room = _redisService.GetChessRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); if (room.state == 2) return this.ToFailResult("游戏进行中不能直接踢人,请先结束对局"); if (room.users?.Any(u => u.userId == userId) != true) return this.ToFailResult("玩家不在该房间中"); await _capPublisher.PublishAsync(TopicKeys.后台踢出象棋玩家, new AdminKickChessPlayerMessage { RoomId = roomId, UserId = userId, Reason = "被管理员移出房间" }); return this.ToResult("踢人指令已提交"); } [HttpGet("GetChessRoomStatistics")] public async Task> GetChessRoomStatistics() { var rooms = _redisService.GetAllChessRooms() ?? new List(); var stats = new ChessRoomStatistics { TotalRooms = rooms.Count, EmptyRooms = rooms.Count(r => r.state == 0 && (r.users == null || !r.users.Any(u => u != null && !u.IsLeave)) && r.robotCount == 0), WaitingRooms = rooms.Count(r => r.state == 1), PlayingRooms = rooms.Count(r => r.state == 2), FinishedRooms = rooms.Count(r => r.state == 3), TotalPlayers = rooms.SelectMany(r => r.users ?? new List()).Count(), BlackSidePlayers = rooms.SelectMany(r => r.users ?? new List()).Count(u => u.type == 1), RedSidePlayers = rooms.SelectMany(r => r.users ?? new List()).Count(u => u.type == 2) }; stats.EmptyRoomRate = stats.TotalRooms == 0 ? 0 : Math.Round(stats.EmptyRooms * 100.0 / stats.TotalRooms, 2); return this.ToResult(stats); } [HttpGet("DeleteChessRoom")] public async Task> DeleteChessRoom(string roomId) { var room = _redisService.GetChessRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); _redisService.RemoveChessRoom(roomId); return this.ToResult("已删除房间 " + roomId); } [HttpGet("AddChessRobot")] public async Task> AddChessRobot(string roomId, int count) { var room = _redisService.GetChessRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); var realPlayers = room.users?.Where(u => u != null && !u.IsLeave).ToList(); if (realPlayers != null && realPlayers.Count > 0) return this.ToFailResult("房间有真人玩家,不能修改机器人数量"); room.robotCount += count; ApplyRobotStateRule(room); _redisService.SetChessRoom(room); return this.ToResult("添加成功"); } [HttpGet("SetChessRobot")] public async Task> SetChessRobot(string roomId, int count) { var room = _redisService.GetChessRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); var realPlayers = room.users?.Where(u => u != null && !u.IsLeave).ToList(); if (realPlayers != null && realPlayers.Count > 0) return this.ToFailResult("房间有真人玩家,不能修改机器人数量"); room.robotCount = Math.Clamp(count, 0, 2); ApplyRobotStateRule(room); _redisService.SetChessRoom(room); return this.ToResult("设置成功"); } [HttpGet("GetRoomDetail")] public async Task> GetRoomDetail(string roomId) { var room = _redisService.GetGameRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); return this.ToResult(room); } [HttpGet("GetUserRoomRealtime")] [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] public async Task> GetUserRoomRealtime(string userId) { if (string.IsNullOrWhiteSpace(userId)) return this.ToFailResult("用户ID不能为空"); var dto = new UserRoomRealtimeDto { Found = true }; var user = await _userMongo.FindByIdAsync(userId); var factor = user?.PlayerFactor ?? new PlayerFactor(); dto.Gold = user?.Assets?.Gold ?? 0; dto.TotalRechargeGold = user?.Static?.TotalRechargeGold ?? 0; RedisGameRoom? matchedRoom = null; RedisGamePlayer? matchedPlayer = null; var session = _redisService.GetRedisUserCId(userId); if (!string.IsNullOrWhiteSpace(session?.RoomId)) { matchedRoom = _redisService.GetGameRoom(session.RoomId); matchedPlayer = matchedRoom?.Players? .FirstOrDefault(p => p != null && !p.IsBot && p.UserId == userId); } if (matchedPlayer == null) { var fallback = _redisService.GetGameRoomAllList() .Where(r => r != null && r.Players != null) .SelectMany(r => r.Players.Select(p => new { Room = r, Player = p })) .FirstOrDefault(x => x.Player != null && !x.Player.IsBot && x.Player.UserId == userId); matchedRoom = fallback?.Room; matchedPlayer = fallback?.Player; } if (matchedRoom != null && matchedPlayer != null) { var room = matchedRoom; var player = matchedPlayer; dto.InRoom = true; dto.RoomId = room.RoomId; dto.RoomName = room.RoomName; dto.GameType = (int)room.GameType; dto.GameTypeName = room.GameType.ToString(); dto.RoomLevel = room.RoomLevel; dto.RoomPartition = room.RoomPartition; dto.RoomNumber = room.RoomNumber; dto.SystemFactor = room.SystemFactor; dto.CurrentOnlinePlayerCount = room.CurrentOnlinePlayerCount; dto.RobotCount = room.RobotCount; dto.Position = player.Position; dto.UserName = player.UserName; dto.UserNumber = player.UserNumber; dto.Gold = player.Gold; dto.MaxGold = player.MaxGold; dto.BeginCoin = player.BeginCoin; dto.CannonMultiple = player.CannonMultiple; dto.CannonId = player.CannonId; dto.State = player.State; dto.IsAfk = player.IsAfk; dto.TotalRechargeGold = player.TotalRechargeGold; var delta = player.Gold - player.BeginCoin; dto.WinGold = delta > 0 ? delta : 0; dto.LoseGold = delta < 0 ? -delta : 0; var realtimeFactor = player.PlayerFactor ?? new PlayerFactor(); if (realtimeFactor.ViolateValue > 0) factor.ViolateValue = realtimeFactor.ViolateValue; } dto.IsBlack = factor.IsBlack; dto.IsIgnore = factor.IsIgnore; dto.ViolateValue = factor.ViolateValue; dto.AdditionalFactor = factor.AdditionalFactor; dto.MissRate = factor.MissRate; dto.CompensateQuota = factor.CompensateQuota; dto.CompensateStartGold = factor.CompensateStartGold; dto.CompensateTargetGold = factor.CompensateTargetGold; if (factor.CompensateQuota <= 0) dto.CompensateRemainingQuota = 0; else dto.CompensateRemainingQuota = factor.CompensateTargetGold > 0 ? Math.Max(0, factor.CompensateTargetGold - dto.Gold) : Math.Max(0, factor.CompensateQuota); dto.PenaltyQuota = factor.PenaltyQuota; dto.StartingBonus = factor.StartingBonus; dto.CompensateRateBoss = factor.CompensateRate?.BossRate ?? 0; dto.CompensateRateGold = factor.CompensateRate?.GoldRate ?? 0; dto.CompensateRateSpecial = factor.CompensateRate?.SpecialRate ?? 0; dto.CompensateRateOrdinary = factor.CompensateRate?.Ordinary ?? 0; dto.PenaltyRateBoss = factor.PenaltyRate?.BossRate ?? 0; dto.PenaltyRateGold = factor.PenaltyRate?.GoldRate ?? 0; dto.PenaltyRateSpecial = factor.PenaltyRate?.SpecialRate ?? 0; dto.PenaltyRateOrdinary = factor.PenaltyRate?.Ordinary ?? 0; return this.ToResult(dto); } [HttpGet("AddRobot")] public async Task> AddRobot(string roomId, int count) { var room = _redisService.GetGameRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); if (count < 0) return this.ToFailResult("机器人增加数量不能小于0"); var realPlayerCount = room.Players?.Count(p => !p.IsBot) ?? room.CurrentOnlinePlayerCount; room.CurrentOnlinePlayerCount = realPlayerCount; room.RobotCount = Math.Clamp(room.RobotCount + count, 0, Math.Max(0, room.MaxPlayerCount - realPlayerCount)); if (room.Players != null) { var excessBots = room.Players.Where(p => p.IsBot).Skip(room.RobotCount).ToList(); foreach (var bot in excessBots) { _redisService.ClearBulletIds(roomId, bot.UserId); room.Players.Remove(bot); } room.RobotCount = Math.Max(room.RobotCount, room.Players.Count(p => p.IsBot)); } _redisService.SetGameRoom(room); return this.ToResult($"已为房间 {roomId} 添加 {count} 个机器人"); } [HttpGet("SetRobotCount")] public async Task> SetRobotCount(string roomId, int count) { var room = _redisService.GetGameRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); if (count < 0) return this.ToFailResult("机器人数量不能小于0"); var realPlayerCount = room.Players?.Count(p => !p.IsBot) ?? room.CurrentOnlinePlayerCount; room.CurrentOnlinePlayerCount = realPlayerCount; room.RobotCount = Math.Clamp(count, 0, Math.Max(0, room.MaxPlayerCount - realPlayerCount)); if (room.Players != null) { var excessBots = room.Players.Where(p => p.IsBot).Skip(room.RobotCount).ToList(); foreach (var bot in excessBots) { _redisService.ClearBulletIds(roomId, bot.UserId); room.Players.Remove(bot); } room.RobotCount = Math.Max(room.RobotCount, room.Players.Count(p => p.IsBot)); } _redisService.SetGameRoom(room); return this.ToResult($"已将房间 {roomId} 的机器人数量设置为 {count}"); } [HttpGet("RegenerateAllBotIds")] public async Task> RegenerateAllBotIds() { var rooms = GetAllRoomsFromRedis()?.ToList() ?? new List(); int totalBots = 0; foreach (var room in rooms) { if (room?.Players == null || room.Players.Count == 0) continue; var bots = room.Players.Where(p => p.IsBot).ToList(); if (bots.Count == 0) continue; foreach (var bot in bots) { var oldUserId = bot.UserId; bot.UserId = Guid.NewGuid().ToString("N"); bot.UserNumber = Common.Utils.RandomHelper.RandomNumbers(8); totalBots++; } _redisService.SetGameRoom(room); } return this.ToResult($"已刷新 {totalBots} 个机器人编号(涉及 {rooms.Count(r => r?.Players?.Any(p => p.IsBot) == true)} 个房间)"); } [HttpGet("ClearRoom")] public async Task> ClearRoom(string roomId) { var room = _redisService.GetGameRoom(roomId); if (room == null) return this.ToFailResult("房间不存在"); int success = 0, fail = 0; foreach (var player in room.Players.Where(p => !p.IsBot).ToList()) { try { await _capPublisher.PublishAsync(TopicKeys.游戏金币结算, new { UserId = player.UserId, BeforeGold = player.BeginCoin, AfterGold = player.Gold, Remark = "管理员清空房间结算", RoomName = room.RoomName ?? "", RoomInfo = room.RoomId ?? "" }); } catch (Exception ex) { _logger.LogError(ex, "管理员清空房间 CAP 结算失败 roomId={RoomId} userId={UserId}", roomId, player.UserId); } try { _redisService.ClearBulletIds(roomId, player.UserId); } catch (Exception ex) { _logger.LogError(ex, "管理员清空房间 清理子弹失败 roomId={RoomId} userId={UserId}", roomId, player.UserId); } try { var userRedis = _redisService.GetRedisUserCId(player.UserId); if (userRedis != null && userRedis.RoomId == roomId) { userRedis.RoomId = ""; userRedis.CId = ""; _redisService.SetRedisUserCId(userRedis); } success++; } catch (Exception ex) { _logger.LogError(ex, "管理员清空房间 清理userRedis失败 roomId={RoomId} userId={UserId}", roomId, player.UserId); fail++; } } foreach (var player in room.Players.Where(p => p.IsBot).ToList()) { try { _redisService.ClearBulletIds(roomId, player.UserId); } catch { } } var robotCount = room.RobotCount; room.Players.Clear(); room.CurrentOnlinePlayerCount = 0; room.RobotCount = Math.Clamp(robotCount, 0, room.MaxPlayerCount); _redisService.SetGameRoom(room); var msg = fail > 0 ? $"已清空房间 {roomId}:成功 {success} 人,失败 {fail} 人" : $"已清空房间 {roomId} 的所有玩家({success} 人)"; return this.ToResult(msg); } [HttpGet("GetRoomStatistics")] public async Task> GetRoomStatistics() { var rooms = GetAllRoomsFromRedis(); if (rooms == null || rooms.Count() == 0) { return this.ToResult(new RoomStatistics()); } var stats = new RoomStatistics { TotalRooms = rooms?.Count()?? 0, TotalPlayers = rooms?.Where(_=>_!=null).Sum(r => r.CurrentOnlinePlayerCount) ?? 0, TotalRobots = rooms?.Where(_ => _ != null).Sum(r => r.RobotCount) ?? 0 }; if (rooms != null && rooms.Count() > 0) { stats.RoomsByLevel = rooms.Where(_ => _ != null).GroupBy(r => r.RoomLevel) .ToDictionary(g => g.Key, g => g.Count()); stats.RoomsByType = rooms.Where(_ => _ != null).GroupBy(r => r.GameType.ToString()) .ToDictionary(g => g.Key, g => g.Count()); } return this.ToResult(stats); } [HttpGet("RepairChessRooms")] public async Task> RepairChessRooms() { var rooms = _redisService.GetAllChessRooms(); if (rooms == null || rooms.Count == 0) return this.ToFailResult("没有象棋房间"); int fixedCount = 0; foreach (var room in rooms) { if (room.users != null && room.users.Any(u => !u.IsLeave)) continue; ApplyRobotStateRule(room); _redisService.SetChessRoom(room); fixedCount++; } return this.ToResult($"已修复 {fixedCount} 个象棋房间"); } [HttpGet("InitRooms")] public async Task> InitRooms() { _redisService.RemoveGameRoomAll(); _redisService.ClearAllChessRooms(); await _gameConfigService.InitRoom(); var config = await _gameConfigService.Get(); var chessRoomConfigs = config?.RoomConfigs.Where(c => c.GameType == GameTypeEnum.中国象棋).ToList(); if (chessRoomConfigs != null) { foreach (var roomConfig in chessRoomConfigs) { for (int y = 1; y <= roomConfig.RoomPartitionNum; y++) { for (int i = 0; i < roomConfig.RoomCount; i++) { var room = new ChessGameRoom { RoomId = $"{(int)GameTypeEnum.中国象棋}{y}{i.ToString("00")}", CreateTime = DateTime.Now, createUserId = "", gameType = 0, gold = roomConfig.RoomGoldLimit, Id = Guid.NewGuid().ToString(), users = new List(), EndGameRequests = new Dictionary(), LastActiveTime = 0, TimeoutSeconds = 60, robotCount = 0 }; ApplyRobotStateRule(room); _redisService.SetChessRoom(room); } } } } return this.ToResult("房间初始化成功"); } [HttpGet("ReInitChessRooms")] public async Task> ReInitChessRooms() { var config = await _gameConfigService.Get(); var chessConfig = config?.ChessConfig; if (chessConfig == null) return this.ToFailResult("象棋配置不存在"); _redisService.ClearAllChessRooms(); var chessRoomConfigs = config.RoomConfigs.Where(c => c.GameType == GameTypeEnum.中国象棋).ToList(); if (chessRoomConfigs.Count == 0) return this.ToFailResult("未找到象棋房间配置"); int count = 0; foreach (var roomConfig in chessRoomConfigs) { for (int y = 1; y <= roomConfig.RoomPartitionNum; y++) { for (int i = 0; i < roomConfig.RoomCount; i++) { var room = new ChessGameRoom { RoomId = $"{(int)GameTypeEnum.中国象棋}{y}{i.ToString("00")}", CreateTime = DateTime.Now, createUserId = "", gameType = 0, gold = roomConfig.RoomGoldLimit, Id = Guid.NewGuid().ToString(), users = new List(), EndGameRequests = new Dictionary(), LastActiveTime = 0, TimeoutSeconds = 60, robotCount = 0 }; ApplyRobotStateRule(room); _redisService.SetChessRoom(room); count++; } } } return this.ToResult($"象棋房间已重新初始化,共 {count} 个房间"); } private IEnumerable GetAllRoomsFromRedis() { return _redisService.GetGameRoomAllList(); } private static void ApplyRobotStateRule(ChessGameRoom room) { var userCount = room.users?.Count ?? 0; var totalCount = userCount + room.robotCount; if (totalCount == 0) { room.state = 0; room.password = ""; } else if (totalCount == 1) { room.state = 1; if (string.IsNullOrEmpty(room.password)) room.password = Random.Shared.Next(100000, 999999).ToString(); } else { room.state = 2; if (string.IsNullOrEmpty(room.password)) room.password = Random.Shared.Next(100000, 999999).ToString(); } } } public class RoomStatistics { public int TotalRooms { get; set; } = 0; public int TotalPlayers { get; set; } = 0; public int TotalRobots { get; set; } = 0; public Dictionary RoomsByLevel { get; set; } = new(); public Dictionary RoomsByType { get; set; } = new(); } public class ChessRoomStatistics { public int TotalRooms { get; set; } = 0; public int EmptyRooms { get; set; } = 0; public double EmptyRoomRate { get; set; } = 0; public int WaitingRooms { get; set; } = 0; public int PlayingRooms { get; set; } = 0; public int FinishedRooms { get; set; } = 0; public int TotalPlayers { get; set; } = 0; public int BlackSidePlayers { get; set; } = 0; public int RedSidePlayers { get; set; } = 0; } public class UserRoomRealtimeDto { public bool Found { get; set; } = false; public bool InRoom { get; set; } = false; public long WinGold { get; set; } = 0; public long LoseGold { get; set; } = 0; public string RoomId { get; set; } = string.Empty; public string RoomName { get; set; } = string.Empty; public int GameType { get; set; } public string GameTypeName { get; set; } = string.Empty; public int RoomLevel { get; set; } public int RoomPartition { get; set; } public int RoomNumber { get; set; } public double SystemFactor { get; set; } = 1.0; public int CurrentOnlinePlayerCount { get; set; } public int RobotCount { get; set; } public int Position { get; set; } public string UserName { get; set; } = string.Empty; public string UserNumber { get; set; } = string.Empty; public long Gold { get; set; } public long BeginCoin { get; set; } public long MaxGold { get; set; } public int CannonMultiple { get; set; } public string CannonId { get; set; } = string.Empty; public int State { get; set; } public bool IsAfk { get; set; } public long TotalRechargeGold { get; set; } public bool IsBlack { get; set; } public bool IsIgnore { get; set; } public double ViolateValue { get; set; } public double AdditionalFactor { get; set; } public double MissRate { get; set; } public int CompensateQuota { get; set; } public long CompensateStartGold { get; set; } public long CompensateTargetGold { get; set; } public long CompensateRemainingQuota { get; set; } public int PenaltyQuota { get; set; } public long StartingBonus { get; set; } public double CompensateRateBoss { get; set; } public double CompensateRateGold { get; set; } public double CompensateRateSpecial { get; set; } public double CompensateRateOrdinary { get; set; } public double PenaltyRateBoss { get; set; } public double PenaltyRateGold { get; set; } public double PenaltyRateSpecial { get; set; } public double PenaltyRateOrdinary { get; set; } } }