30 KiB
棋牌游戏模块(ChessGame)—— 功能与代码目录文档
本文档为 棋牌游戏模块(ChessGame) 的完整上下文说明,供其他对话 / 开发者在不阅读全部源码的情况下理解该模块。文中所有结论均以仓库
d:\BUYUHouDuanNet\BuYu下的实际源码为准,路径均为相对该仓库根目录的相对路径。
一、平台背景与模块位置
本平台是一个捕鱼 + 棋牌游戏运营平台后端,解决方案文件为 Template0524.sln。整体技术栈:
- 运行时 / 框架:.NET 8
- 数据库:MongoDB(文档存储)、PostgreSQL(CAP 消息表 / 日志表)
- 缓存:Redis
- 消息队列:RabbitMQ(通过 DotNetCore.CAP 封装为事件总线)
- 实时通信:SignalR(Hub,部分为 WebSocket-only)
- 网关:Ocelot
- 日志:NLog
- 依赖注入:Autofac
网关(Ocelot) 监听 http://localhost:8001,按路由将请求转发到各 Host 进程:
| 网关前缀 | 目标 Host | 端口 |
|---|---|---|
/adminapi |
AdminHost | 5241 |
/api、/loginhub |
UserHost | 5245 |
/customerapi、/customerHub |
CustomerHost | 5247 |
/chess、/chessgamehub |
ChessGameHost | 5242 |
/fishingroomhub |
FishRoomHost | 5244 |
/gamehub |
FishGameHost | 5248 |
本模块(棋牌游戏模块 ChessGame)的位置:
- 入口进程:
ChessGameHost(ChessGameHost/ChessGameHost.csproj)。 - 网关前缀:
/chess、/chessgamehub。 - 实时对局通信:SignalR Hub,映射在
/chessgamehub路由,并且仅启用 WebSockets 传输(见ChessGameHost/Program.cs)。 - 在
Template0524.sln中已注册的项目:ChessGameSrc\ChessGameDomain、ChessGameSrc\ChessGameService、ChessGameHost(解决方案中存在ChessGameSrc解决方案文件夹)。 - 端口说明(以实际配置为准):
ChessGameHost/docker-compose.yaml将容器内80映射到宿主 5242(与网关一致);ChessGameHost/Properties/launchSettings.json本地 http 调试端口为5290;ChessGameHost/Config/system.json中另声明了"urls": "http://*:8005"。三者用途不同(发布容器 / 本地调试 / 配置声明),实际生效端口取决于部署方式与启动参数。
二、模块简介
2.1 定位与职责
棋牌游戏模块负责中国象棋(真人 / 机器人)实时对局服务,核心职责:
- 房间的创建、加入、退出、后台踢人、房间重置与清理。
- 对局全生命周期:设金币 → 准备 → 开始 → 走子/操作转发 → 结束(认输 / 双方确认结束 / 断线超时 / 挂机超时)。
- 对局结算:按房间下注金币在胜者与败者之间进行用户资产(金币)加减,并发布资产变更通知。
- 通过 SignalR 向大厅(Lobby)与房间组实时推送房间状态、对局事件。
- 后台任务:断线/挂机超时自动判负、机器人填充空房、空房率维护、启动时重置异常房间。
房间的运行态数据存放在 Redis(通过 IRedisService 读写,见 RedisService/IRedisService.cs),并非存放于 MongoDB。
2.2 房间状态机(ChessGameRoom.state,依据代码推断)
| state | 含义(依据代码使用位置推断) |
|---|---|
0 |
空闲 / 待开放房间(机器人服务 ChessBotService 会将 state==0 且无人的房间填充为机器人房;清理逻辑会将其重置回 0) |
1 |
已创建 / 已有人进入,等待开始(StartGame 要求 state==1;CreateRoom / JoinGame 成功后置 1) |
2 |
对局进行中(EndGame / 超时 / 断线均要求或针对 state==2) |
3 |
对局结束 / 房间释放(EndGame 与 ExitRoom 均将 state 置 3) |
2.3 核心业务流程(以代码为准)
① 进房 / 创建房间
CreateRoom(Hub)→ChessGameService.CreateRoomAsync:- 校验用户存在、游戏配置(
IRedisService.GetGameConfig()的ChessConfig)存在。 - 使用分布式锁
lock:chess:room:{roomId}(RedisKey.DistributedLockPrefix+chess:room:{roomId},expiry=10s)串行化操作。 - 若该用户已存在于其他房间,则从旧房间移除或清空旧房间。
- 房间处于
state==0或state==3 且无人时视为“开房/配置密码”场景,需满足ChessConfig.CreateRoomLimit权限(3=所有人、1=VIP、2=代理),否则返回“只有 VIP/代理才能配置密码”。 - 否则按二/一号位(
type,红黑方)加入,最多 2 人。
- 校验用户存在、游戏配置(
JoinGame(Hub)→JoinGameAsync:必须携带正确密码(room.password为空或错误均拒绝),随后入房、置state==1。- 注意:
CreateRoom场景与JoinGame场景对密码的要求不同——JoinGameAsync强制校验密码。
- 注意:
② 对局准备与开始
StartGame:仅房主(createUserId)可发起,人数需 ≥ 2,推送GoldConfigRequired(要求红方配置金币)。GoldConfig:仅红方(type==2)可配置,金币需 ≥ChessConfig.MinBetGold,且不得超过红方玩家自身金币。GameReady:玩家准备;两人全部准备后房间置state==2(真正开始),推送AllBeginTime。
③ 对局中的操作转发
GameOperation(gameId, type, data):校验调用者身份并刷新活跃时间;当type==2时视为“当前玩家获胜”,直接结束对局(另一方判负);否则把{type,data}广播给房间组。SyncGameData(gameId, data):把数据仅转发给房间内另一个连接(对手),用于走子同步。Heartbeat:刷新房间LastActiveTime。
④ 结束对局(多种触发)
EndGame(gameId, winUserId)(Hub)→ChessGameService.EndGame:- 在分布式锁内执行(
lock:chess:room:{gameId},expiry=30s,不重试),房间置state==3、IsGameEnded=true。 - 计算实际下注
actualBet = min(room.gold, 败者真实金币);先扣败者(Source = RewardSource.象棋),再给胜者加币;任一失败则回滚(扣款失败回退房间状态为 2,胜者加币失败则给败者回退)。 - 结算后发布
BackgroundOperation.NotifyUser通知(胜方ChangeType=1、败方ChangeType=2)。 - 最后清空房间(
gold=0、gameType=0、robotCount=0、清空确认集合、清空users),并对所有相关 userId 执行UpdateUserGameType(userId, 0)。
- 在分布式锁内执行(
RequestEndGame:双方确认制,玩家各自登记结束请求(EndGameRequests),当房间内所有玩家都确认(且人数 ≥ 2)时自动调用EndGame;否则向对手推送OpponentRequestEnd。- 断线超时:
CheckTimeout(Hub 主动查询)与后台ChessTimeoutService周期扫描,玩家断线超过room.TimeoutSeconds(默认 60s)判负。 - 挂机超时:
CheckRoomAfkTimeout,单方挂机超过room.AfkTimeoutSeconds(默认 120s)判负;双方同时挂机时,先挂机的一方判负。
⑤ 退出与踢人
ExitRoom(Hub)按房主退出 / 对局中退出 / 普通退出分别处理,必要时重置房间。KickPlayer(Hub):仅房主、非对局中、不能踢自己;被踢者收KickedFromRoom并回到大厅组。- 后台管理员踢人:由
AdminKickChessPlayerSubscriber处理(见第七节)。
⑥ 机器人与空房维护
ChessBotService定期把空闲房间填充为机器人房、并维护“空房率”(见第三节·后台服务)。
三、业务功能清单
3.1 SignalR Hub 方法(客户端 → 服务端可调用)
Hub 类:ChessGameService/ChessGameHub.cs(namespace ChessGameService),继承 Hub。大厅组常量:ChessGameHub.LobbyGroup = "chess_lobby"。
| 方法 | 说明 |
|---|---|
OnConnectedAsync() / OnDisconnectedAsync(Exception?) |
连接 / 断开的生命周期处理;支持按 query 参数 userId 重连回房 |
CreateRoom(CreateRoomParam param) |
创建 / 开房 |
JoinGame(gameId, pwd, userId) |
凭密码加入房间 |
StartGame(gameId, userId) |
房主开始游戏(进入金币配置环节) |
GoldConfig(gameId, gold, userId) |
红方配置本局下注金币 |
GameReady(gameId, userId) |
玩家准备 |
EndGame(gameId, winUserId) |
结束对局并结算(指定胜者) |
RestartGame(gameId, userId) |
对局结束后重开 |
RequestEndGame(gameId, userId, winUserId) |
请求结束对局(双确认) |
Heartbeat(gameId) |
心跳,刷新活跃时间 |
SetPlayerAfk(gameId, userId) / CancelPlayerAfk(gameId, userId) |
设置 / 取消挂机 |
CheckTimeout(gameId) |
主动检查断线超时 |
ExitRoom(gameId, userId) |
退出房间 |
KickPlayer(gameId, targetUserId) |
房主踢人 |
GetRoomList(partition=0) |
获取房间列表(最多 100,按房号分区过滤) |
GetRoomState(gameId) |
获取房间当前状态 |
SyncGameData(gameId, data) |
走子/对局数据转发给对手 |
GameOperation(gameId, type, data) |
对局操作透传;type==2 时结束对局 |
3.2 Hub 推送事件(服务端 → 客户端)
以下为代码中实际出现的事件名(Clients.*.SendAsync(...)):
| 事件名 | 说明 |
|---|---|
BeginGame |
推送房间完整数据(进房 / 重连 / 状态刷新) |
RoomStateNotice |
房间状态通知 |
JoinRoom |
加入房间成功(发给调用者) |
JoinRoomNotice |
有人加入房间(发给大厅) |
CreateRoomNotice |
房间被创建(发给大厅) |
ExitRoomNotice |
房间退出 / 重置(发给大厅或相关连接) |
ExitRoomSuccess |
退出房间成功 |
GameError |
错误消息(发给调用者) |
ReadyNotice / AllBeginTime |
准备通知 / 全部准备开始 |
GoldConfigRequired / GoldConfigSuccess / GoldConfigRoomNotice |
金币配置相关 |
GameEnd |
对局结束(含胜者与房间数据) |
RestartGameSuccess / RestartRoomNotice |
重开相关 |
OpponentRequestEnd / EndGameRequestSent |
请求结束对局相关 |
PlayerAfkNotice / PlayerCancelAfkNotice |
挂机相关 |
KickedFromRoom |
被踢出房间(含 gameId、userId、reason、source) |
Disconnect / Reconnect |
断线 / 重连通知 |
SyncGameData / GameOperation |
对局数据转发 |
GetRoomList |
房间列表响应 |
3.3 后台服务(ChessGameHost/Services/)
| 服务 | 周期 | 说明 |
|---|---|---|
ChessTimeoutService |
每 10 秒 | 遍历所有 state==2 房间:断线超过 TimeoutSeconds 判负;随后对有挂机的房间执行 CheckRoomAfkTimeout(挂机超时判负)。判负后推送 GameEnd / ExitRoomNotice,并把相关连接移回大厅组 |
ChessBotService |
启动延迟 10s,之后每 2 分钟 | ① 启动时 CleanStaleRooms 清理空房间(state==3 或 state==1 且房主为空);② EnsureChessEmptyRoomRate 依据 MongoDB 中的 EmptyRoomRateConfig(按 GameType==中国象棋)维护空房率,必要时释放“纯机器人房”为空房;③ 从 state==0 且无人无机器人的房间中挑选候选,置为机器人房(robotCount=1、state=1、随机密码),并向大厅推送 JoinRoomNotice(单批最多 5 个) |
3.4 CAP 消息订阅者(ChessGameService/Subscribers/)
| 订阅者 | 订阅主题 | 说明 |
|---|---|---|
ChessLoginSubscriber |
TopicKeys.登录绑定(Topic_0524_LoginBind) |
处理“登录绑定”:查该用户是否正处于 state==2 未结束的对局中,若是则调用 EndGame 以对手为胜者结束并结算,随后发布 TopicKeys.登录结果通知 |
AdminKickChessPlayerSubscriber |
TopicKeys.后台踢出象棋玩家(Topic_0524_AdminKickChessPlayer) |
处理后台管理员踢人:踢出后更新用户游戏状态、推送 KickedFromRoom,把连接移回大厅组并广播房间状态 |
四、项目结构与技术要点
4.1 各 csproj:目标框架与依赖关系
ChessGameSrc/ChessGameDomain/ChessGameDomain.csproj
- 目标框架:
net8.0(ImplicitUsings/Nullable均开启) - 项目引用:
Infrastructure\MongoRepository\MongoRepository.csproj - 说明:对局/房间实体层,无 NuGet 包直接引用(Mongo 相关特性来自 MongoRepository 项目)。
ChessGameSrc/ChessGameService/ChessGameService.csproj
- 目标框架:
net8.0(ImplicitUsings/Nullable均开启) - NuGet 包:
Microsoft.AspNetCore.SignalR.Core(1.2.9)、Microsoft.Extensions.Logging.Abstractions(10.0.3) - 项目引用:
RedisService\RedisService.csproj、UserSrc\UserService\UserService.csproj、ChessGameDomain\ChessGameDomain.csproj - 说明:业务服务层,包含 Hub、服务接口/实现、订阅者、参数模型。
ChessGameHost/ChessGameHost.csproj
- 目标框架:
net8.0,SDK =Microsoft.NET.Sdk.Web(Web 进程入口) - NuGet 包:
NLog.Web.AspNetCore(6.1.2)、Autofac(8.3.0)、Autofac.Extensions.DependencyInjection(10.0.0)、AutoMapper(14.0.0)、DotNetCore.CAP(8.3.4)、DotNetCore.CAP.Dashboard(8.3.4)、DotNetCore.CAP.PostgreSql(8.3.4)、DotNetCore.CAP.RabbitMQ(8.3.4)、Microsoft.AspNetCore.SignalR.Protocols.MessagePack(8.0.17) - 项目引用:
ChessGameSrc\ChessGameService、CommonService、Infrastructure\CommonInfrastructure、Infrastructure\SharpAbp.Infrastructure.Api - 内容文件:链接引用仓库根
nlog.config;docker-compose.yaml、Dockerfile设为始终复制(注意:Dockerfile被 csproj 引用,但当前ChessGameHost目录下未发现该文件)。
依赖方向: ChessGameHost → ChessGameService → { ChessGameDomain、RedisService、UserService } → ChessGameDomain → MongoRepository。
4.2 进程入口要点(ChessGameHost/Program.cs)
- 设置环境变量
SERVICE_NAME、INSTANCE_ID,并注册单例AppInstanceId。 - 日志:
builder.Logging.AddNLog()+builder.Host.UseNLog()(NLog 6.x)。 - 配置:加载
Config/system.json;绑定MongoDbSettings并注册为单例IMongoDbSettings。 - SignalR 配置(
AddSignalR(...).AddJsonProtocol()):MaximumReceiveMessageSize = 64 * 1024(64KB)StreamBufferCapacity = 512ClientTimeoutInterval = 70s、KeepAliveInterval = 15s- 全局 Hub 过滤器:
MovementFilter、HubExceptionFilter
- 其他:
FormOptions.MultipartBodyLengthLimit = 16MB、AddMemoryCache()、JSON 转换器DatetimeJsonConverter。 - DI:
IHubSend<ChessGameHub>→HubSend<ChessGameHub>、IRequestApi→RequestApi、AddAutoMapper(typeof(AutoMapperConfig))。 - 后台服务:
AddHostedService<ChessTimeoutService>()、AddHostedService<ChessBotService>()。 - 健康检查:
AddHealthChecks().AddCheck("self", ...)。 - CORS 策略
"Cors":SetIsOriginAllowed(_ => true)、允许任意 Header / Method、AllowCredentials()。 AddCustomJwt(builder.Configuration)、AddRedisCacheSingleton(builder.Configuration)。- Autofac:
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())并注册AutofacModule。 - CAP:
builder.Services.AddCap(builder.Configuration)(见Extension/CapExtension.cs)。 - 启动自愈逻辑(
app.Run()前):redis.ClearAllLoginUserIds();遍历所有象棋房间,收集受影响 userId;对state != 2的房间执行ExitRoom释放,对state == 2的房间把用户标记为IsLeave=true、DisconnectTime=now、Cid=""并写回;最后对所有受影响用户UpdateUserGameType(userId, 0)。整段包在 try/catch 中(失败不影响启动)。 - 中间件管线顺序:
UseApiLogging()→UseRouting()→UseGlobalExceptionHandler()→UseStaticFiles()→UseCors("Cors")→UseAuthentication()→UseAuthorization()。 - 终结点:
MapHub<ChessGameHub>("/chessgamehub", options => options.Transports = WebSockets)、MapControllers()、MapHealthChecks("/health", ...)(返回 JSON 健康报告)。
4.3 SignalR / CAP / Redis 配置
- SignalR:单个 Hub
ChessGameHub,映射路由/chessgamehub,仅 WebSockets;启用 JSON 协议;消息封包协议包MessagePack(8.0.17)已被引用。 - CAP(
ChessGameHost/Extension/CapExtension.cs):UseDashboard()(CAP 面板)。UseRabbitMQ:Host/UserName/Password/VirtualHost取自配置节CapConfig,交换机名cap.exchange。UsePostgreSql:连接串取自CapConfig:PGLogHost,Schema=public。SucceedMessageExpiredAfter = 7*24*3600、FailedRetryCount = 5、FailedRetryInterval = 10、FailedThresholdCallback。- 显式注册订阅者:
ChessLoginSubscriber、AdminKickChessPlayerSubscriber(AddTransient<ICapSubscribe, ...>)。
- Redis:
AddRedisCacheSingleton(builder.Configuration);读取配置节RedisConfig。房间键定义在Infrastructure/Common/Redis/RedisKey.cs:ChessRoomList(...chess:room:list)、ChessRoomKey(...chess:room:{0}),分布式锁前缀DistributedLockPrefix(...lock:)。
4.4 DI 注册位置
- Autofac 模块
ChessGameHost/Middleware/AutofacModule.cs:- 注册泛型仓储/服务:
MongoBaseRepository<>→IMongoBaseRepository<>、MongoBaseService<>→IMongoBaseService<>(InstancePerLifetimeScope)。 - 注册
RedisService.RedisService→IRedisService。 - 按程序集扫描注册:
CommonService(ApiLogService所在程序集)、ChessGameService、UserService中所有以Service结尾的类型为“实现其接口”。
- 注册泛型仓储/服务:
ChessGameHub由 SignalR 框架实例化(不在 Autofac 模块中显式注册),其构造函数依赖ILogger<ChessGameHub>、IChessGameService、IRedisService。
4.5 docker-compose 端口
ChessGameHost/docker-compose.yaml:
name: webapi
services:
0524chess:
image: 0524chess
container_name: '0524chess'
restart: always
ports:
- '5242:80'
即宿主 5242 → 容器 80。
4.6 配置文件(ChessGameHost/)
appsettings.json:仅Logging(Default=Error、Microsoft.AspNetCore=Error)与AllowedHosts=*。appsettings.Development.json:仅Logging。Config/system.json(由Program.cs额外加载):包含urls(http://*:8005)、JwtConfig、ConnectionStrings:PgSqlConnection、CapConfig(RabbitMQ + PG 日志库)、RedisConfig(Host localhost、Port 6378、Database 1)、MongoDbSettings(mongodb://localhost:27018)、WXConfig。
五、代码目录说明(完整目录树)
ChessGameSrc/ # 棋牌游戏业务源码解决方案文件夹
├── ChessGameDomain/ # 领域层:对局/房间实体(net8.0)
│ ├── ChessGameDomain.csproj # 项目文件,引用 MongoRepository
│ ├── ChessGameRoom.cs # 房间实体(BsonCollection "game_room",库 data_0524_chess)
│ └── RedisChessGameUser.cs # 房间内玩家实体(存放在房间的 users 列表)
└── ChessGameService/ # 业务服务层(net8.0)
├── ChessGameService.csproj # 项目文件,引用 RedisService / UserService / ChessGameDomain
├── IChessGameService.cs # 对局业务接口(房间/对局/结算/超时/踢人)
├── ChessGameService.cs # 对局业务核心实现(房间状态机 + 结算逻辑)
├── ChessGameHub.cs # SignalR Hub,映射路由 /chessgamehub
├── Param/
│ └── CreateRoomParam.cs # 创建房间入参(roomId/userId/gameType/password/gold)
└── Subscribers/
├── ChessLoginSubscriber.cs # 订阅“登录绑定”,结束旧对局并回发登录结果
└── AdminKickChessPlayerSubscriber.cs # 订阅“后台踢出象棋玩家”,执行管理员踢人
ChessGameHost/ # 棋牌游戏进程入口(Web 主机,net8.0)
├── ChessGameHost.csproj # 项目文件,含 NLog/Autofac/CAP/SignalR MessagePack 等包
├── Program.cs # 进程入口:DI/中间件/Hub 映射/启动自愈逻辑
├── appsettings.json # 基础日志配置
├── appsettings.Development.json # 开发环境日志配置
├── docker-compose.yaml # 容器编排,端口 5242:80
├── .dockerignore # 容器构建忽略清单
├── Config/
│ └── system.json # 业务配置:JWT/PG/CAP(RabbitMQ)/Redis/Mongo/微信
├── Extension/
│ └── CapExtension.cs # CAP 配置扩展(RabbitMQ+PG+Dashboard+订阅者注册)
├── Middleware/
│ ├── AutofacModule.cs # Autofac 依赖注入模块(仓储/Redis/服务扫描)
│ ├── ApiLoggingMiddleware.cs # /api 请求响应日志中间件(含脱敏)
│ ├── HubExceptionFilter.cs # SignalR Hub 全局异常/接口日志过滤器
│ └── MovementFilter.cs # SignalR Hub 过滤器(内含预留的 RateLimiter 令牌桶)
├── Properties/
│ └── launchSettings.json # 本地调试配置(http 端口 5290 / IIS Express 21784)
└── Services/
├── ChessTimeoutService.cs # 后台服务:断线/挂机超时判负(每 10s)
└── ChessBotService.cs # 后台服务:机器人填充/空房率维护/启动清理(每 2min)
六、关键文件索引
| 文件相对路径 | 职责 |
|---|---|
ChessGameSrc/ChessGameDomain/ChessGameRoom.cs |
房间实体,定义 users、RoomId、gameType、password、createUserId、state、gold、robotCount、EndGameRequests、TimeoutSeconds、AfkTimeoutSeconds、IsGameEnded 等字段及默认值 |
ChessGameSrc/ChessGameDomain/RedisChessGameUser.cs |
房间内玩家实体(Cid、userId、userName、userAvatar、type、IsLeave、IsReady、Gold、DisconnectTime、IsAfk、AfkTime) |
ChessGameSrc/ChessGameService/IChessGameService.cs |
对局业务接口,声明创建/加入/就绪/结束/重启/超时/踢人/结算等全部方法 |
ChessGameSrc/ChessGameService/ChessGameService.cs |
对局业务核心实现:房间状态机、金币结算(EndGame)、超时判定、权限校验等 |
ChessGameSrc/ChessGameService/ChessGameHub.cs |
SignalR Hub:映射 /chessgamehub,处理连接/断开、进房、开始、准备、走子转发、结束、踢人等,并定义大厅组 chess_lobby |
ChessGameSrc/ChessGameService/Param/CreateRoomParam.cs |
创建房间参数模型 |
ChessGameSrc/ChessGameService/Subscribers/ChessLoginSubscriber.cs |
订阅 Topic_0524_LoginBind,登录绑定触发旧对局结算,回发 Topic_0524_LoginBindResult |
ChessGameSrc/ChessGameService/Subscribers/AdminKickChessPlayerSubscriber.cs |
订阅 Topic_0524_AdminKickChessPlayer,后台踢人并广播 |
ChessGameHost/Program.cs |
进程入口:服务注册、SignalR/CAP/Redis 装配、中间件与终结点、启动房间自愈 |
ChessGameHost/Extension/CapExtension.cs |
CAP 事件总线配置与订阅者注册 |
ChessGameHost/Middleware/AutofacModule.cs |
DI 容器装配(仓储、Redis、各 Service 程序集扫描) |
ChessGameHost/Middleware/ApiLoggingMiddleware.cs |
/api 请求日志中间件 |
ChessGameHost/Middleware/HubExceptionFilter.cs |
Hub 调用日志与异常捕获(SyncGameData 除外) |
ChessGameHost/Middleware/MovementFilter.cs |
Hub 过滤器(预留限流 RateLimiter) |
ChessGameHost/Services/ChessTimeoutService.cs |
断线/挂机超时后台判负服务 |
ChessGameHost/Services/ChessBotService.cs |
机器人填充与空房率维护后台服务 |
ChessGameHost/Config/system.json |
业务配置(JWT/PG/CAP/Redis/MongoDB/微信) |
ChessGameHost/docker-compose.yaml |
容器端口映射 5242:80 |
ChessGameHost/Properties/launchSettings.json |
本地调试端口 |
七、与其他模块的交互
7.1 CAP / RabbitMQ 消息主题
主题常量定义在 CommonDomain/TopicKeys.cs,消息体定义在 CommonDomain/CapMessageModels.cs、CommonDomain/AdminKickChessPlayerMessage.cs。本模块相关的发布/订阅如下:
| 方向 | 主题(常量 → 实际值) | 消息体 | 交互方 |
|---|---|---|---|
| 订阅 | TopicKeys.登录绑定 → Topic_0524_LoginBind |
LoginBindMessage { UserId, OperationId } |
发布方:UserHost(UserSrc/UserService/Hubs/LoginHub.cs);本模块 ChessLoginSubscriber 消费 |
| 发布 | TopicKeys.登录结果通知 → Topic_0524_LoginBindResult |
LoginBindResultMessage { UserId, RoomId, OperationId, Success, Error } |
消费方:UserHost(LoginResultSubscriber → LoginSettlementWaiter);本模块在结算后发布 |
| 订阅 | TopicKeys.后台踢出象棋玩家 → Topic_0524_AdminKickChessPlayer |
AdminKickChessPlayerMessage { RoomId, UserId, Reason } |
发布方:AdminHost(AdminSrc/AdminApi/GameConfigs/AdminRoomController.cs);本模块 AdminKickChessPlayerSubscriber 消费 |
| 发布 | "BackgroundOperation.NotifyUser"(字符串字面量,非常量) |
BackgroundOperationNotifyMessage |
消费方:UserHost(AdminNotifySubscriber)与 FishGame(BackgroundAssetSubscriber);本模块在 EndGame 结算后发布(胜方 ChangeType=1、败方 ChangeType=2,CurrencyType=1,Source="象棋对局胜利"/"象棋对局失败") |
注意:
登录绑定主题除本模块的ChessLoginSubscriber外,FishGameSrc/FishGameService/Subscribers/LoginBindSubscriber.cs也以相同主题订阅(处理捕鱼房旧房间)。二者是各自独立的订阅者。
7.2 登录绑定(与 User 模块)完整流程
- 用户在 UserHost 登录(
LoginHub),若检测到存在待处理的捕鱼房/象棋房(IRedisService.GetPendingChessRoomId(userId)),生成OperationId,注册LoginSettlementWaiter,并通过_capPublisher.PublishAsync(TopicKeys.登录绑定, new LoginBindMessage{ UserId, OperationId })发布“登录绑定”事件,然后等待结果(超时 15s)。 - 本模块
ChessLoginSubscriber.HandleLoginBind消费该事件:遍历所有象棋房间,若发现该用户处于state==2且未结束的对局中,则以对手为胜者调用ChessGameService.EndGame结算,随后发布TopicKeys.登录结果通知(成功Success=true,异常Success=false并带Error)。 - UserHost
LoginResultSubscriber消费“登录结果通知”,通过LoginSettlementWaiter唤醒等待中的登录流程;随后写入新的登录 Cid(SetRedisUserCId)。 - 另有
LoginHub.DismissPendingRoom会发布TopicKeys.拒绝回房(Topic_0524_DismissPendingRoom,消息体同为LoginBindMessage)。
7.3 Redis
- 本模块对局房间的运行时数据全部存放于 Redis,通过
IRedisService读写(SetChessRoom/GetChessRoom/GetAllChessRooms/RemoveChessRoom等)。相关键:ChessRoomList(...chess:room:list)、ChessRoomKey(...chess:room:{0})。 - 使用分布式锁:键前缀
RedisKey.DistributedLockPrefix(...lock:)+chess:room:{roomId},串行化房间的创建、准备、结束等操作。 - 配置节
RedisConfig(localhost:6378,Database 1)。 - 启动时调用
ClearAllLoginUserIds()清理登录用户映射。
7.4 MongoDB
- 房间实体
ChessGameRoom标注了[BsonCollection("game_room", ServiceDatabases.Chess)],其中ServiceDatabases.Chess = "data_0524_chess"(见Infrastructure/MongoRepository/Config/MongoDbSettings.cs)。但本模块代码实际通过 Redis 存取房间运行态,未在本模块中发现向该 Mongo 集合写房间的逻辑。 ChessBotService通过IMongoBaseRepository<EmptyRoomRateConfig>读取空房率配置;EmptyRoomRateConfig标注在集合empty_room_rate_config、库data_0524(见CommonDomain/EmptyRoomRateConfig.cs),按GameType == 中国象棋(GameTypeEnum.中国象棋 = 3)过滤。- 对局日志:
HubExceptionFilter/ApiLoggingMiddleware通过IApiLogService写入接口日志(Mongo 仓储)。
7.5 HTTP 调用
Program.cs 注册了 IRequestApi → RequestApi(Common.Request)单例,作为通用 HTTP 请求封装;在本模块已阅读的对局代码中未见直接调用。
7.6 共享实体 / 项目引用
ChessGameService引用RedisService、UserService、ChessGameDomain;ChessGameHost引用ChessGameService、CommonService、CommonInfrastructure、SharpAbp.Infrastructure.Api;ChessGameDomain引用MongoRepository。- 与 User 模块的耦合点:
ChessGameService通过IUserService(UserSrc/UserService)执行GetUser/UpdateUser/UpdateAssets;UpdateUserGameType写入用户Static.CurrentGameType与Static.RoomNumber(游戏状态标记);UpdateAssets使用Source = RewardSource.象棋、UserAssetChangeTypeEnum.增加/减少完成金币加减。 - 共享领域类型:
CommonDomain(TopicKeys、LoginBindMessage、LoginBindResultMessage、AdminKickChessPlayerMessage、BackgroundOperationNotifyMessage、EmptyRoomRateConfig、GameTypeEnum等)、Common.Redis(RedisKey)、CommonDomain.BaseModel.GameBaseConfig(ChessConfig:CreateRoomLimit/MinBetGold/BetGoldLimits/CreateRoomVipLevels)。
7.7 与 Admin 模块的交互
- AdminHost 的
AdminRoomController(AdminSrc/AdminApi/GameConfigs/AdminRoomController.cs)在管理员执行“踢出象棋玩家”时发布TopicKeys.后台踢出象棋玩家,由本模块AdminKickChessPlayerSubscriber消费并执行踢人 + 通知。 - 象棋游戏配置(
ChessConfig:开房权限、最小下注等)由 Admin 侧维护,本模块通过IRedisService.GetGameConfig()读取并据此校验开房权限与金币下限。