添加项目文件。

This commit is contained in:
2026-10-12 16:22:04 +08:00
parent 7c68a0ca0a
commit aad11be43d
1000 changed files with 120807 additions and 0 deletions
@@ -0,0 +1,224 @@
using System.Text;
using AdminService.GameConfigs;
using Common;
using CommonDomain;
using CommonDomain.BaseModel;
using CommonInfrastructure;
using Microsoft.AspNetCore.Mvc;
using NewLife;
using RedisService;
using XAct;
namespace AdminApi.GameConfigs
{
[ApiController, Route("adminapi/[controller]")]
public class ActivityConfigController : BaseController
{
private readonly IActivityConfigService _activityConfigService;
private readonly IRedisService _redisService;
public ActivityConfigController(IActivityConfigService activityConfigService, IRedisService redisService)
{
_activityConfigService = activityConfigService;
_redisService = redisService;
}
[HttpGet]
[HttpGet("Get")]
public async Task<BaseResponse<ActivityConfig>> Get()
{
var config = await _activityConfigService.Get();
if (config != null)
return this.ToResult(config);
else
return this.ToFailResult<ActivityConfig>("配置不存在");
}
[HttpPost("Update")]
public async Task<BaseResponse<string>> Update(ActivityConfig config)
{
var res = await _activityConfigService.Update(config);
if (res.isok)
return this.ToResult(res.msg);
else
return this.ToFailResult<string>(res.msg);
}
[HttpPost("Delete")]
public async Task<BaseResponse<string>> Delete()
{
var res = await _activityConfigService.Delete();
if (res.isok)
return this.ToResult(res.msg);
else
return this.ToFailResult<string>(res.msg);
}
[HttpPost("UpdateBattleOrder")]
public async Task<BaseResponse<string>> UpdateBattleOrder(BattleOrderConfig config)
{
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
activityConfig.BattleOrderConfig = config;
var res = await _activityConfigService.Update(activityConfig);
if (res.isok)
return this.ToResult(res.msg);
else
return this.ToFailResult<string>(res.msg);
}
[HttpPost("UpdateRelief")]
public async Task<BaseResponse<string>> UpdateRelief(ReliefConfig config)
{
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
activityConfig.ReliefConfig = config;
var res = await _activityConfigService.Update(activityConfig);
if (res.isok)
return this.ToResult(res.msg);
else
return this.ToFailResult<string>(res.msg);
}
[HttpPost("UpdateSign")]
public async Task<BaseResponse<string>> UpdateSign(List<SignConfig> configs)
{
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
activityConfig.SignConfigs = configs;
var res = await _activityConfigService.Update(activityConfig);
if (res.isok)
return this.ToResult(res.msg);
else
return this.ToFailResult<string>(res.msg);
}
[HttpPost("UpdateGoldShop")]
public async Task<BaseResponse<string>> UpdateGoldShop(List<GoldShopConfig> configs)
{
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
activityConfig.GoldShopConfigs = configs;
var res = await _activityConfigService.Update(activityConfig);
if (res.isok)
return this.ToResult(res.msg);
else
return this.ToFailResult<string>(res.msg);
}
[HttpPost("UpdateDiamondShop")]
public async Task<BaseResponse<string>> UpdateDiamondShop(List<DiamondShopConfig> configs)
{
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
activityConfig.DiamondShopConfigs = configs;
var res = await _activityConfigService.Update(activityConfig);
if (res.isok)
return this.ToResult(res.msg);
else
return this.ToFailResult<string>(res.msg);
}
[HttpPost("UpdateRegister")]
public async Task<BaseResponse<string>> UpdateRegister(RegisterConfig config)
{
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
activityConfig.RegisterConfig = config;
var res = await _activityConfigService.Update(activityConfig);
if (res.isok)
return this.ToResult(res.msg);
else
return this.ToFailResult<string>(res.msg);
}
[HttpPost("UpdateFishingReturn")]
public async Task<BaseResponse<string>> UpdateFishingReturn(FishingReturnConfig config)
{
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
activityConfig.FishingReturnConfig = config;
var res = await _activityConfigService.Update(activityConfig);
if (res.isok)
return this.ToResult(res.msg);
else
return this.ToFailResult<string>(res.msg);
}
[HttpGet("GetFishingReturn")]
public async Task<BaseResponse<FishingReturnConfig>> GetFishingReturn()
{
var config = await _activityConfigService.Get();
if (config?.FishingReturnConfig != null)
return this.ToResult(config.FishingReturnConfig);
else
return this.ToFailResult<FishingReturnConfig>("配置不存在");
}
[HttpGet("SaveSection/{sectionName}")]
public async Task<BaseResponse<string>> SaveSection(string sectionName)
{
var prop = typeof(ActivityConfig).GetProperty(sectionName);
if (prop == null)
return this.ToFailResult<string>($"板块 {sectionName} 不存在");
var config = await _activityConfigService.Get();
if (config == null)
return this.ToFailResult<string>("配置不存在");
var sectionValue = prop.GetValue(config);
if (sectionValue == null)
return this.ToFailResult<string>($"板块 {sectionName} 数据为空");
var jsonPath = global::System.IO.Path.Combine(AppContext.BaseDirectory, "Config", "activityconfig.json");
try
{
await _activityConfigService.Update(config);
ActivityConfig fileConfig;
if (global::System.IO.File.Exists(jsonPath))
{
var json = await global::System.IO.File.ReadAllTextAsync(jsonPath, global::System.Text.Encoding.UTF8);
fileConfig = global::System.Text.Json.JsonSerializer.Deserialize<ActivityConfig>(json, new global::System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true, ReadCommentHandling = global::System.Text.Json.JsonCommentHandling.Skip }) ?? new ActivityConfig();
}
else
{
fileConfig = new ActivityConfig();
}
prop.SetValue(fileConfig, sectionValue);
var outJson = global::System.Text.Json.JsonSerializer.Serialize(fileConfig, new global::System.Text.Json.JsonSerializerOptions
{
WriteIndented = true,
Encoder = global::System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
await global::System.IO.File.WriteAllTextAsync(jsonPath, outJson, global::System.Text.Encoding.UTF8);
return this.ToResult($"{sectionName} 已保存到 activityconfig.json");
}
catch (Exception ex)
{
return this.ToFailResult<string>($"保存失败: {ex.Message}");
}
}
[HttpGet("ExportSection/{sectionName}")]
public async Task<IActionResult> ExportSection(string sectionName)
{
var config = await _activityConfigService.Get();
if (config == null)
return NotFound("配置不存在");
var prop = typeof(ActivityConfig).GetProperty(sectionName);
if (prop == null)
return NotFound($"板块 {sectionName} 不存在");
var data = prop.GetValue(config);
if (data == null)
return NotFound($"板块 {sectionName} 数据为空");
var json = global::System.Text.Json.JsonSerializer.Serialize(data, new global::System.Text.Json.JsonSerializerOptions
{
WriteIndented = true,
Encoder = global::System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
return File(global::System.Text.Encoding.UTF8.GetBytes(json), "application/json", $"{sectionName}.json");
}
}
}
@@ -0,0 +1,828 @@
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<UserEntity> _userMongo;
private readonly ILogger<RoomController> _logger;
public RoomController(IRedisService redisService, IGameConfigService gameConfigService, ICapPublisher capPublisher, IMongoBaseRepository<UserEntity> userMongo, ILogger<RoomController> logger)
{
_redisService = redisService;
_gameConfigService = gameConfigService;
_capPublisher = capPublisher;
_userMongo = userMongo;
_logger = logger;
}
[HttpGet("GetAllRooms")]
public async Task<PageResponse<RedisGameRoom>> 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<RedisGameRoom>();
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<PageResponse<ChessGameRoom>> 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<BaseResponse<ChessGameRoom>> GetChessRoomDetail(string roomId)
{
var room = _redisService.GetChessRoom(roomId);
if (room == null)
return this.ToFailResult<ChessGameRoom>("房间不存在");
return this.ToResult(room);
}
[HttpGet("ClearChessRoom")]
public async Task<BaseResponse<string>> ClearChessRoom(string roomId)
{
var room = _redisService.GetChessRoom(roomId);
if (room == null)
return this.ToFailResult<string>("房间不存在");
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<BaseResponse<string>> KickChessPlayer(string roomId, string userId)
{
if (string.IsNullOrWhiteSpace(roomId) || string.IsNullOrWhiteSpace(userId))
return this.ToFailResult<string>("房间ID和玩家ID不能为空");
var room = _redisService.GetChessRoom(roomId);
if (room == null)
return this.ToFailResult<string>("房间不存在");
if (room.state == 2)
return this.ToFailResult<string>("游戏进行中不能直接踢人,请先结束对局");
if (room.users?.Any(u => u.userId == userId) != true)
return this.ToFailResult<string>("玩家不在该房间中");
await _capPublisher.PublishAsync(TopicKeys.后台踢出象棋玩家, new AdminKickChessPlayerMessage
{
RoomId = roomId,
UserId = userId,
Reason = "被管理员移出房间"
});
return this.ToResult("踢人指令已提交");
}
[HttpGet("GetChessRoomStatistics")]
public async Task<BaseResponse<ChessRoomStatistics>> GetChessRoomStatistics()
{
var rooms = _redisService.GetAllChessRooms() ?? new List<ChessGameRoom>();
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<RedisChessGameUser>()).Count(),
BlackSidePlayers = rooms.SelectMany(r => r.users ?? new List<RedisChessGameUser>()).Count(u => u.type == 1),
RedSidePlayers = rooms.SelectMany(r => r.users ?? new List<RedisChessGameUser>()).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<BaseResponse<string>> DeleteChessRoom(string roomId)
{
var room = _redisService.GetChessRoom(roomId);
if (room == null)
return this.ToFailResult<string>("房间不存在");
_redisService.RemoveChessRoom(roomId);
return this.ToResult("已删除房间 " + roomId);
}
[HttpGet("AddChessRobot")]
public async Task<BaseResponse<string>> AddChessRobot(string roomId, int count)
{
var room = _redisService.GetChessRoom(roomId);
if (room == null)
return this.ToFailResult<string>("房间不存在");
var realPlayers = room.users?.Where(u => u != null && !u.IsLeave).ToList();
if (realPlayers != null && realPlayers.Count > 0)
return this.ToFailResult<string>("房间有真人玩家,不能修改机器人数量");
room.robotCount += count;
ApplyRobotStateRule(room);
_redisService.SetChessRoom(room);
return this.ToResult("添加成功");
}
[HttpGet("SetChessRobot")]
public async Task<BaseResponse<string>> SetChessRobot(string roomId, int count)
{
var room = _redisService.GetChessRoom(roomId);
if (room == null)
return this.ToFailResult<string>("房间不存在");
var realPlayers = room.users?.Where(u => u != null && !u.IsLeave).ToList();
if (realPlayers != null && realPlayers.Count > 0)
return this.ToFailResult<string>("房间有真人玩家,不能修改机器人数量");
room.robotCount = Math.Clamp(count, 0, 2);
ApplyRobotStateRule(room);
_redisService.SetChessRoom(room);
return this.ToResult("设置成功");
}
[HttpGet("GetRoomDetail")]
public async Task<BaseResponse<RedisGameRoom>> GetRoomDetail(string roomId)
{
var room = _redisService.GetGameRoom(roomId);
if (room == null)
return this.ToFailResult<RedisGameRoom>("房间不存在");
return this.ToResult(room);
}
[HttpGet("GetUserRoomRealtime")]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public async Task<BaseResponse<UserRoomRealtimeDto>> GetUserRoomRealtime(string userId)
{
if (string.IsNullOrWhiteSpace(userId))
return this.ToFailResult<UserRoomRealtimeDto>("用户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<BaseResponse<string>> AddRobot(string roomId, int count)
{
var room = _redisService.GetGameRoom(roomId);
if (room == null)
return this.ToFailResult<string>("房间不存在");
if (count < 0)
return this.ToFailResult<string>("机器人增加数量不能小于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<BaseResponse<string>> SetRobotCount(string roomId, int count)
{
var room = _redisService.GetGameRoom(roomId);
if (room == null)
return this.ToFailResult<string>("房间不存在");
if (count < 0)
return this.ToFailResult<string>("机器人数量不能小于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<BaseResponse<string>> RegenerateAllBotIds()
{
var rooms = GetAllRoomsFromRedis()?.ToList() ?? new List<RedisGameRoom>();
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<BaseResponse<string>> ClearRoom(string roomId)
{
var room = _redisService.GetGameRoom(roomId);
if (room == null)
return this.ToFailResult<string>("房间不存在");
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<BaseResponse<RoomStatistics>> 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<BaseResponse<string>> RepairChessRooms()
{
var rooms = _redisService.GetAllChessRooms();
if (rooms == null || rooms.Count == 0)
return this.ToFailResult<string>("没有象棋房间");
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<BaseResponse<string>> 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<RedisChessGameUser>(),
EndGameRequests = new Dictionary<string, long>(),
LastActiveTime = 0,
TimeoutSeconds = 60,
robotCount = 0
};
ApplyRobotStateRule(room);
_redisService.SetChessRoom(room);
}
}
}
}
return this.ToResult("房间初始化成功");
}
[HttpGet("ReInitChessRooms")]
public async Task<BaseResponse<string>> ReInitChessRooms()
{
var config = await _gameConfigService.Get();
var chessConfig = config?.ChessConfig;
if (chessConfig == null)
return this.ToFailResult<string>("象棋配置不存在");
_redisService.ClearAllChessRooms();
var chessRoomConfigs = config.RoomConfigs.Where(c => c.GameType == GameTypeEnum.中国象棋).ToList();
if (chessRoomConfigs.Count == 0)
return this.ToFailResult<string>("未找到象棋房间配置");
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<RedisChessGameUser>(),
EndGameRequests = new Dictionary<string, long>(),
LastActiveTime = 0,
TimeoutSeconds = 60,
robotCount = 0
};
ApplyRobotStateRule(room);
_redisService.SetChessRoom(room);
count++;
}
}
}
return this.ToResult($"象棋房间已重新初始化,共 {count} 个房间");
}
private IEnumerable<RedisGameRoom> 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<int, int> RoomsByLevel { get; set; } = new();
public Dictionary<string, int> 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; }
}
}
@@ -0,0 +1,63 @@
using Common;
using CommonDomain;
using CommonInfrastructure;
using Microsoft.AspNetCore.Mvc;
using MongoDB.Driver;
using MongoRepository;
namespace AdminApi.GameConfigs
{
[ApiController, Route("adminapi/[controller]")]
public class BotConfigController : BaseController
{
private readonly IMongoBaseRepository<BotProfileEntity> _botRepo;
public BotConfigController(IMongoBaseRepository<BotProfileEntity> botRepo)
{
_botRepo = botRepo;
}
[HttpGet("GetAll")]
public async Task<BaseResponse<List<BotProfileEntity>>> GetAll()
{
var list = await _botRepo.FilterByAsync(_ => true);
return this.ToResult(list.ToList());
}
[HttpPost("Add")]
public async Task<BaseResponse<string>> Add(BotProfileEntity entity)
{
if (string.IsNullOrWhiteSpace(entity.Name))
return this.ToFailResult<string>("名称不能为空");
await _botRepo.InsertOneAsync(entity);
return this.ToResult("添加成功");
}
[HttpPost("Update")]
public async Task<BaseResponse<string>> Update(BotProfileEntity entity)
{
if (string.IsNullOrWhiteSpace(entity.Id))
return this.ToFailResult<string>("缺少Id");
if (string.IsNullOrWhiteSpace(entity.Name))
return this.ToFailResult<string>("名称不能为空");
var update = Builders<BotProfileEntity>.Update
.Set(x => x.Name, entity.Name)
.Set(x => x.Avatar, entity.Avatar ?? string.Empty);
await _botRepo.UpdateByIdAsync(entity.Id, update);
return this.ToResult("更新成功");
}
[HttpPost("Delete")]
public async Task<BaseResponse<string>> Delete([FromQuery] string id)
{
if (string.IsNullOrWhiteSpace(id))
return this.ToFailResult<string>("缺少Id");
await _botRepo.DeleteByIdAsync(id);
return this.ToResult("删除成功");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,98 @@
using System;
using Common;
using CommonDomain;
using CommonInfrastructure;
using DotNetCore.CAP;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using RedisService;
namespace AdminApi.GameConfigs
{
[ApiController, Route("adminapi/Admin/[controller]")]
[AllowAnonymous]
public class TempSpawnFishController : BaseController
{
private readonly ICapPublisher _capPublisher;
private readonly IRedisService _redisService;
private readonly ILogger<TempSpawnFishController> _logger;
public TempSpawnFishController(ICapPublisher capPublisher, IRedisService redisService, ILogger<TempSpawnFishController> logger)
{
_capPublisher = capPublisher;
_redisService = redisService;
_logger = logger;
}
[HttpGet("SpawnFish")]
public async Task<BaseResponse<string>> SpawnFish(
string roomId,
string fishNumber,
int count = 1,
int? seconds = null,
int? routeId = null)
{
if (string.IsNullOrWhiteSpace(roomId) || string.IsNullOrWhiteSpace(fishNumber))
{
return new BaseResponse<string>
{
Code = CommonResponseCodeEnum.ParamError,
Info = "roomId / fishNumber 不能为空"
};
}
var room = _redisService.GetGameRoom(roomId);
if (room == null)
{
return new BaseResponse<string>
{
Code = CommonResponseCodeEnum.ParamError,
Info = $"房间 {roomId} 不存在或不在线"
};
}
count = Math.Clamp(count <= 0 ? 1 : count, 1, 2000);
var effectiveSeconds = (seconds.HasValue && seconds.Value > 0) ? seconds.Value : 30;
var operationId = Guid.NewGuid().ToString("N");
var publishedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var msg = new TempSpawnFishMessage
{
RoomId = roomId,
FishNumber = fishNumber,
Count = count,
RouteId = routeId,
Seconds = effectiveSeconds,
OperationId = operationId,
PublishedAtUnixMs = publishedAt
};
try
{
await _capPublisher.PublishAsync(TopicKeys.临时强制出鱼, msg);
return new BaseResponse<string>
{
Code = CommonResponseCodeEnum.Success,
Info = "已推送临时出鱼指令(异步),operationId=" + operationId,
Data = operationId
};
}
catch (Exception ex)
{
_logger.LogError(ex,
"[TempSpawn] CAP发布失败 operationId={OperationId} roomId={RoomId} fishNumber={FishNumber} count={Count}",
operationId, roomId, fishNumber, count);
return new BaseResponse<string>
{
Code = CommonResponseCodeEnum.Error,
Info = "发布失败:" + ex.Message
};
}
}
}
}