46 KiB
捕鱼游戏模块(FishGame)—— 功能与代码目录文档
本文档为「捕鱼游戏模块(FishGame)」的完整上下文说明,供其他对话/开发者在不阅读源码的情况下理解该模块的定位、职责、核心业务流程、项目结构、代码目录与模块间交互。文档内容全部基于对真实源代码的通读整理,未读到的内容不作臆测。
一、平台背景与模块位置
1.1 平台总览
本平台是一套「捕鱼 + 棋牌」游戏运营平台后端,解决方案文件为 Template0524.sln(位于仓库根目录 d:\BUYUHouDuanNet\BuYu)。
技术栈(依据各项目 .csproj 与配置核对):
- 运行时/框架:.NET 8(
net8.0,Microsoft.NET.Sdk.Web/Microsoft.NET.Sdk) - 数据存储:MongoDB(
MongoRepository/MongoDB.Driver,[BsonCollection(..., "data_0524")]) - 缓存:Redis(
StackExchange.Redis、RedisService、RedisHelper) - 消息队列:RabbitMQ,通过 DotNetCore.CAP(
DotNetCore.CAP+.RabbitMQ+.PostgreSql+.Dashboard)封装发布/订阅 - 实时通信:SignalR(
Microsoft.AspNetCore.SignalR,JSON 协议 +MessagePack协议包引用) - 网关:Ocelot(
Gateway\ocelot.json,BaseUrlhttp://localhost:8001) - 日志:NLog(
NLog.Web.AspNetCore+ 根目录nlog.config) - DI:Autofac(
Autofac+Autofac.Extensions.DependencyInjection) - 对象映射:AutoMapper
1.2 网关路由(来自 Gateway\ocelot.json)
网关全局 BaseUrl:http://localhost:8001。各上游路径映射到的下游服务与端口如下:
| 上游路径(网关) | 下游路径模板 | 下游端口 | 对应服务 |
|---|---|---|---|
/adminapi/{everything} |
/adminapi/{everything} |
5241 | AdminHost |
/api/{everything} |
/api/{everything} |
5245 | UserHost |
/loginhub/{everything} |
/loginhub/{everything} |
5245 | UserHost |
/customerapi/{everything} |
/customerapi/{everything} |
5247 | CustomerHost |
/customerHub/{everything} |
/customerHub/{everything} |
5247 | CustomerHost |
/chess/{everything} |
/chess/{everything} |
5242 | ChessGameHost |
/chessgamehub/{everything} |
/chessgamehub/{everything} |
5242 | ChessGameHost |
/fishingroomhub/{everything} |
/fishingroomhub/{everything} |
5244 | FishRoomHost(本模块·房间层) |
/gamehub/{everything} |
/fishinghub/{everything} |
5248 | FishGameHost(本模块·对局层) |
注意:ocelot 中捕鱼「对局层」的上游是
/gamehub,下游实际转发到 FishGameHost 的/fishinghub(端口 5248)。这是本模块一个容易混淆的点。
1.3 本模块两个进程入口
本模块由两个独立进程(微服务)构成:
- 房间层:
FishGameSrc\FishRoomHost—— 监听端口 5244,SignalR Hub 路径/fishingroomhub(网关上游同名),负责大厅房间列表、房间初始化、房间分组广播等。 - 对局层:
FishGameSrc\FishGameHost—— 网关侧端口 5248,SignalR Hub 路径/fishinghub(网关上游为/gamehub),负责进房、对局(开炮/击杀)、出鱼、结算、掉线重连等核心玩法逻辑。
两个进程各自引用一个类库:
FishGameSrc\FishGameService(被 FishGameHost 引用,承载对局层业务逻辑)FishRoomService(位于仓库根目录,被 FishRoomHost 引用,承载房间层业务逻辑)
二、模块简介
2.1 定位与职责
捕鱼游戏模块是整个运营平台的「捕鱼玩法」业务域,划分为 房间层 与 对局层 两层职责:
-
房间层(FishRoomHost + FishRoomService)
- 维护「大区/分区 → 房间列表」,按后台下发的
GameConfig(房间配置)批量生成房间,并结合「空房率配置(EmptyRoomRateConfig)」与「机器人配置(BotConfig)」决定每个房间预置的机器人数量。 - 玩家在大厅请求房间列表时,将其连接加入一个「分区 SignalR 分组」,并单独推送房间列表。
- 订阅「房间变化」消息,向对应分区分组广播房间状态变化;订阅「用户资产同步前端」消息,向玩家推送资产变更。
- 维护「大区/分区 → 房间列表」,按后台下发的
-
对局层(FishGameHost + FishGameService)
- 玩家从大厅进入某个房间 → 连接到对局层 Hub(
/fishinghub)→ 绑定用户 → 加入房间(占座、生成玩家对象、推送房间信息与活跃鱼)→ 对局(开炮、击杀、道具、鱼雷、炮倍切换、挂机)→ 离房/掉线结算。 - 由后台服务
FishSpawnService负责每个房间的「出鱼」(按概率配置按类型/航线定时刷鱼)、活跃鱼快照、出鱼权租约、命中校验与击杀标记;RobotKillAnnounceService负责按配置定时伪造「机器人爆金击杀」全局公告。
- 玩家从大厅进入某个房间 → 连接到对局层 Hub(
2.2 核心业务流程(以代码为准)
-
服务启动初始化房间(房间层)
FishRoomHost\Program.cs启动时,若IRedisService.GetGameRoomAllList()为空,则调用IFishingRoomService.InitRoom()初始化房间。房间初始化失败会记录日志并抛出(throw)。
-
大厅 → 房间列表(房间层)
- 客户端连接
/fishingroomhub,调用 Hub 方法GetRoomList(userId, gameType, roomLevel, roomPartition)。 FishingRoomService.GetRoomList计算分区字符串partition = $"{(int)gameType}{roomLevel}{roomPartition}";写入/更新RedisUserCid(含PartitionId、FishRoomCId、CId);把当前连接加入该分区的 SignalR 分组;读取该分区房间列表,若为空则即时InitRoom(...)生成;最后通过FishingRoomSendService.GetRoomListResult单独回推GetRoomListResult(按RoomNumber排序)。- 另有
GetRoomInfo(roomId)直接从 Redis 读取单个房间回推GetRoomInfoResult。
- 客户端连接
-
房间初始化(房间层,
InitRoom)- 依据
GameConfig.RoomConfigs(可用gameType/roomLevel过滤)逐条房间配置,遍历RoomPartitionNum × RoomCount生成RedisGameRoom。 - 读取该 GameType/RoomLevel 的
EmptyRoomRateConfig(空房率);结合GameConfig.BotConfig.Enabled:机器人关闭时空房率为 100%(无机器人房间);否则按100 - EmptyRoomRate的比例随机挑选要预置机器人的房间,RobotCount = Random(1, MaxPlayerCount)。 - 每生成一个房间前先
RemoveGameRoom(partition)(清除旧数据)再重建;RoomId = $"{(int)GameType}{RoomLevel}{RoomPartition}{c:00}"。 - 房间上限:
星际迷航/中国象棋为 2 人,其余为 4 人(MaxPlayerCount)。 RoomBotFactory.FixedBotSeats当前为空数组,故FixedBotSeatsEnabled == false,固定座位机器人逻辑(CreateFixedSeatBots)当前不会生效。- 最后逐个
SetGameRoom写回 Redis。
- 依据
-
进房(对局层,
FishingGameService.JoinRoom)- 若玩家此前在其它房间,先
SettleGoldAsync结算旧房间金币。 - 从 Redis 取房间;若房间不存在则以默认「新手渔场(风雷之渊/1)」房间创建兜底。
- 已有玩家(重连/重复加入):刷新座位 CId、重置 State、必要时接管房主,推送
RoomInfo、OnPlayerInfo、SendRoomChange后返回。 - 新玩家:校验座位(固定座位、手动选座权限
SeatSelectLimit:3=全部可手选,1=VIP,2=代理),校验RoomGoldLimit金币门槛;构建RedisGamePlayer(金币、钻石、炮台/翅膀/战神、初始炮倍、BeginCoin、PlayerFactor等)加入房间;CurrentOnlinePlayerCount++、加入房间分组、设置房主。 - 依据
BotConfig决定是否创建机器人(CreateRoomBots)或移除机器人;随后推送RoomInfo(含活跃鱼)、RoomJoinNotice、OnSeatChange(join)、OnPlayerInfo,并SendRoomChange(CAP 发布「房间变化」)。
- 若玩家此前在其它房间,先
-
对局中(对局层)
- 开炮
FireCannon:校验签名与时间窗、玩家身份、金币、扣费、生成子弹 Id 存入BulletTracker、广播RoomFireNotice、累加消耗进度与「开炮任务」进度。 - 击杀
KillFish:校验签名/子弹/鱼命中(FishSpawnService.ValidateFishHit);按玩家因子(PlayerFactor)、系统因子、鱼KillWeight加权随机判定是否击杀;击杀后计算爆金(CalcSkillGold,支持 Boss 轮盘/扑克/飞盘/全屏/范围/概率/星际巡航/烈焰风暴/炸弹/机械蝎/神龙等玩法)、掉落、经验、任务进度,并处理补偿/罚款配额;广播RoomKillNotice;超阈值爆金触发全局击杀公告(CAP 发布「全局击杀公告」+ Redis 频道广播)。 - 道具/鱼雷:
UseProp(消耗钻石或背包道具,40003触发冻结鱼FishFrozen,40004触发狂暴任务)、UseTorpedo(按权重抽奖加金币)。 - 炮倍/挂机/信息:
ChangeGunMultiple、SetPlayerAfk/CancelPlayerAfk、GetPlayerInfo。 - 出鱼:
FishSpawnService以 1 秒 Tick 驱动机器人房间出鱼逻辑,并通过 Redis 租约(fish:spawn:owner:{roomId})保证同一房间只有一台实例出鱼,快照写fish:spawn:snapshot:{roomId}供无租约实例读取。
- 开炮
-
离房 / 掉线 / 结算(对局层)
- 主动离房
LeaveRoom:在分布式锁lock:fish:gold:{roomId}:{userId}下,先「标记离房(State=2)」,再在满足条件时执行LeaveRoomInternal(累加在线时长、结算钓鱼进度与GameCoinsSettlement、清理运行时数据、移除玩家、房主转移、NormalizeRoomPlayerCounts、无人时清空并UnregisterRoom、广播OnSeatChange(leave)/RoomLeaveNotice、SendRoomChange)。 - 掉线
HandleDisconnect:玩家 State 置 1,房主转移;启动 60 秒延迟任务;超时后OnDisconnectTimeoutInternal结算并清理房间。 - 重连
Reconnect/JoinRoom:恢复连接、取消待掉线定时器、恢复座位与房主、补发活跃鱼同步。
- 主动离房
-
结算(结算链)
TryFlushFishingSettlement→GameCoinsSettlement(记录本局金币变化),TryFlushFishingProgress结算经验(UpdateAssets)与消耗记录(RecordFishingConsume),FlushFishingTaskProgress发布各「任务完成」事件。
三、业务功能清单
3.1 房间层(FishRoomHost / FishRoomService)
SignalR Hub:FishRoomService\Hubs\FishRoomHub.cs(挂载于 /fishingroomhub)
| 类型 | 名称 | 说明 |
|---|---|---|
| Hub 方法 | OnConnectedAsync / OnDisconnectedAsync |
仅透传基类实现 |
| Hub 方法 | GetRoomList(userId, gameType, roomLevel, roomPartition) |
大厅请求房间列表(含分组加入与即时初始化) |
| Hub 方法 | GetRoomInfo(roomId) |
读取单房间并回推 GetRoomInfoResult |
| 服务端推送 | GetRoomListResult |
房间列表结果(GameRoomReponse 列表) |
| 服务端推送 | GetRoomInfoResult |
单房间信息 |
| 服务端推送 | RoomChange |
房间变化广播(向分区分组) |
| 服务端推送 | AssetUpdated |
用户资产变更推送 |
业务服务
| 接口/类 | 职责 |
|---|---|
IFishingRoomService / FishingRoomService |
GetRoomList(大厅房间列表)、InitRoom(按配置初始化房间) |
IFishingRoomSendService / FishingRoomSendService |
GetRoomListResult(回推列表)、RoomChange(向分区分组广播) |
IRoomMQService / RoomMQService(实现 ICapSubscribe) |
订阅「房间变化」与「用户资产同步前端」 |
消息订阅者(CAP)
| 订阅方法 | 主题(TopicKeys) |
行为 |
|---|---|---|
RoomMQService.RoomChange |
房间变化 = Topic_0524_RoomChage |
调用 FishingRoomSendService.RoomChange,向房间分区分组广播 RoomChange |
RoomMQService.UserAssetSync |
用户资产同步前端 = Topic_0524_UserAssetSyncFrontend |
按 RedisUserCid(FishRoomCId/CId)定位连接,推送 AssetUpdated |
HTTP 控制器:无(房间层仅 Hub + MQ)。
3.2 对局层(FishGameHost / FishGameService)
SignalR Hub:FishGameService\Hubs\FishGameHub.cs(挂载于 /fishinghub)
| 类型 | 名称 | 说明 |
|---|---|---|
| Hub 方法 | OnConnectedAsync |
建立连接(异常记录日志) |
| Hub 方法 | OnDisconnectedAsync |
断线处理(调用 HandleDisconnect) |
| Hub 方法 | Reconnect(userId) |
重连恢复(座位、房主、活跃鱼同步) |
| Hub 方法 | JoinRoom(roomId, userId, posituin) |
加入房间 |
| Hub 方法 | GetRoomInfo(roomId) |
读取房间并回推 GetRoomInfoResult(需连接在房) |
| Hub 方法 | SyncGameData(roomId, data) |
房间内同步自定义数据 |
| Hub 方法 | UseProp(roomId, userId, propNumber, diamond) |
使用道具,返回剩余数量 |
| Hub 方法 | UseTorpedo(roomId, userId, propNumber) |
使用鱼雷,返回金币 |
| Hub 方法 | FireCannon(roomId, userId, gold, count, timestamp, sign, extra) |
开炮,返回投递的子弹 Id 列表与金币 |
| Hub 方法 | KillFish(roomId, userId, fishNumber, fihsId, gold, timespan, bulletId, timestamp, sign) |
击杀鱼,返回当前金币 |
| Hub 方法 | LeaveRoom(roomId, userId) |
主动离房 |
| Hub 方法 | ChangeGunMultiple(roomId, userId, multiple) |
切换炮倍 |
| Hub 方法 | GetPlayerInfo(roomId, userId) |
请求玩家信息 |
| Hub 方法 | SetPlayerAfk / CancelPlayerAfk |
挂机 / 取消挂机 |
| Hub 方法 | BotFireCannon / BotKillFish |
机器人开炮 / 击杀(返回子弹 Id / 无返回) |
| 服务端推送 | RoomInfo |
房间信息(含活跃鱼 activeFishes) |
| 服务端推送 | OnFishSpawn |
出鱼/首屏鱼同步(type = init/spawn/sync) |
| 服务端推送 | RoomKillNotice |
房间内击杀公告(含倍数、掉落、玩法结果 param) |
| 服务端推送 | RoomFireNotice |
开炮广播 |
| 服务端推送 | RoomJoinNotice / RoomLeaveNotice / BotLeaveRoomNotice |
加入/离开/机器人离开 |
| 服务端推送 | RoomDisconnectNotice / HostDisconnect |
断线通知 / 房主断线 |
| 服务端推送 | ChangeControllerUser |
房主变更 |
| 服务端推送 | OnSeatChange |
座位变化(join/leave) |
| 服务端推送 | OnPlayerInfo |
本玩家信息(金币、钻石、炮倍、炮台、翅膀、座位等) |
| 服务端推送 | SyncPlayerGold / SendPlayerInfoResult |
金币同步 / 玩家信息结果 |
| 服务端推送 | ChangeGunMultipleNotice / UsePropNotice / TorpedoResultNotice |
炮倍/道具/鱼雷结果 |
| 服务端推送 | PlayerAfkNotice / PlayerCancelAfkNotice |
挂机状态 |
| 服务端推送 | Exception / BindSuccess / KickToMainPage |
错误 / 绑定成功 / 踢回主页 |
| 服务端推送 | AssetUpdated |
资产变更(道具消耗等) |
| 服务端推送 | FishFrozen / SummonCardFishNotice |
冻结鱼 / 召唤卡出鱼 |
| 全局推送 | KillNotice |
经 Redis 频道 fishgame:global 广播的全服击杀公告 |
HTTP 控制器(FishGameHost\Controllers)
| 控制器 | 路由 | 方法 | 说明 |
|---|---|---|---|
TimeController |
fishapi/Time/GetDateTime |
GET | 返回服务端时间/时间戳/时区/容器名(供客户端校时) |
RoomController |
api/Room/QuickJoin |
GET | 按 gameType/level 从未满房间随机快速匹配,返回 RoomId 与 WebSocket 地址 |
RoomController |
api/Room/Assign |
GET | 按 roomId 返回求 WebSocket 地址(读取配置 GameServerInstances) |
PlayerController |
api/Player/login |
POST | 演示用登录:生成 JWT(uid 倒序 + uname),返回固定玩家数据 |
后台服务
| 服务 | 基类 | 职责 |
|---|---|---|
FishSpawnService |
BackgroundService(同时注册为 IHostedService) |
房间出鱼主循环:租约、刷鱼、活跃鱼快照、命中/击杀校验、系统因子同步、冻结延时、临时/召唤卡出鱼 |
RobotKillAnnounceService |
BackgroundService |
按 RobotKillConfig 定时(最小 30 秒)伪造机器人爆金击杀并发布「全局击杀公告」;仅 INSTANCE_ID 为 1 或 default 的实例运行 |
GlobalBroadcastSubscriber(FishGameHost\Services) |
BackgroundService |
订阅 Redis 频道 fishgame:global,按 Target=All 或 Room 通过 Hub 广播事件 |
消息订阅者(CAP)
| 订阅方法 | 主题(TopicKeys) |
行为 |
|---|---|---|
PlayerFactorSubscriber.HandlePlayerFactorChanged |
因子变更 = Topic_0524_PlayerFactorChanged |
清理本地玩家因子缓存,并把最新因子同步到 Redis 房间与玩家 |
PlayerFactorSubscriber.HandleConfigChanged |
配置变更 = Topic_0524_ConfigChanged |
清空配置缓存、失效出鱼配置、重载所有房间配置 |
LoginBindSubscriber.HandleLoginBind |
登录绑定 = Topic_0524_LoginBind |
结算并清理玩家在房状态,回发「登录结果通知」 |
LoginBindSubscriber.HandleDismissPendingRoom |
拒绝回房 = Topic_0524_DismissPendingRoom |
强制执行 CleanupRoomAsync(userId, force: true) |
BackgroundAssetSubscriber.HandleNotify |
BackgroundOperation.NotifyUser |
后台加减金币(仅 CurrencyType==1),分布式锁下更新房间内玩家金币并 SyncPlayerGold |
TempSpawnFishSubscriber.HandleTempSpawnFish |
临时强制出鱼 = Topic_0524_TempSpawnFish |
调用 FishSpawnService.SpawnTemporaryFish |
TempSpawnFishSubscriber.HandleSummonCardFish |
召唤卡出鱼 = Topic_0524_SummonCardFish |
调用 SpawnSummonCardFish 并推送 SummonCardFishNotice |
对局层对外发布的消息
| 发布者 | 主题(TopicKeys) |
触发点 |
|---|---|---|
FishingGameService.SendRoomChange |
房间变化 = Topic_0524_RoomChage |
进房/离房/清理房间等(PublishAsync) |
FishingGameService.UseProp / UseTorpedo |
用户资产变更 = Topic_0524_UserAssetsChanged |
消耗钻石/背包道具/鱼雷(Publish) |
FishingGameService.FishKillControl |
全局击杀公告 = Topic_0524_GlobalKillAnnounce |
房间内爆金达到阈值 |
FishingGameService.FlushFishingTaskProgress |
任务完成 = Topic_0524_CompletedTask |
离房/结算时汇总任务进度 |
RobotKillAnnounceService |
全局击杀公告 = Topic_0524_GlobalKillAnnounce |
定时伪造机器人爆金 |
LoginBindSubscriber.HandleLoginBind |
登录结果通知 = Topic_0524_LoginBindResult |
登录结算成功/失败回执 |
说明:对局层 Hub 的
KillNotice还有一条「非 CAP」的广播通道——FishingSendService.KillNotice通过IConnectionMultiplexer向 Redis 频道fishgame:global发布 JSON 消息,由GlobalBroadcastSubscriber消费后经 Hub 下发。
四、项目结构与技术要点
4.1 各项目 csproj(框架版本与依赖)
| 项目 | 相对路径 | SDK/TFM | 项目引用 | 关键 NuGet |
|---|---|---|---|---|
| FishRoomHost | FishGameSrc\FishRoomHost\FishRoomHost.csproj |
Microsoft.NET.Sdk.Web / net8.0 |
FishRoomService、Infrastructure\CommonInfrastructure、Infrastructure\SharpAbp.Infrastructure.Api |
SignalR.StackExchangeRedis 8.0.0、StackExchange.Redis 2.7.4、NLog.Web.AspNetCore 6.1.2、Autofac 8.3.0、AutoMapper 14.0.0、DotNetCore.CAP(.Dashboard/.PostgreSql/.RabbitMQ) 8.3.4、SignalR.Protocols.MessagePack 8.0.17 |
| FishGameHost | FishGameSrc\FishGameHost\FishGameHost.csproj |
Microsoft.NET.Sdk.Web / net8.0 |
FishGameService、Infrastructure\CommonInfrastructure、Infrastructure\SharpAbp.Infrastructure.Api |
StackExchange.Redis 2.7.4、SignalR.Protocols.MessagePack 8.0.0、NLog.Web.AspNetCore 6.1.2、Autofac 8.3.0、AutoMapper 14.0.0、DotNetCore.CAP(.Dashboard/.PostgreSql/.RabbitMQ) 8.3.4 |
| FishGameService | FishGameSrc\FishGameService\FishGameService.csproj |
Microsoft.NET.Sdk / net8.0(类库) |
CommonDomain、CommonService、RedisService、UserSrc\UserService |
AutoMapper 14.0.0、DotNetCore.CAP 8.3.4 |
| FishRoomService | FishRoomService\FishRoomService.csproj |
Microsoft.NET.Sdk / net8.0(类库) |
CommonService、RedisService、UserSrc\UserService |
AutoMapper 14.0.0、DotNetCore.CAP 8.3.4、Microsoft.AspNetCore.SignalR.Core 1.2.9 |
三个 Host/Service 项目均以 None 方式引入根目录 nlog.config,并拷贝 docker-compose.yaml、Dockerfile 到输出目录。
4.2 两个进程入口要点
FishRoomHost\Program.cs
- 设置环境变量
SERVICE_NAME、INSTANCE_ID(默认default),并注册单例AppInstanceId。 - 配置加载顺序:默认
appsettings*.json之后追加Config\system.json(builder.Configuration.AddJsonFile(Path.Combine("Config", "system.json"), optional: true))。 - 绑定
MongoDbSettings(IOptions<MongoDbSettings>+IMongoDbSettings)。 - Redis 连接串优先取
ConnectionStrings:Redis,否则由RedisConfig(Host/Port/Password/Database)拼装,注册单例IConnectionMultiplexer。 - SignalR:
MaximumReceiveMessageSize = 64KB、StreamBufferCapacity = 512、ClientTimeoutInterval = 60s、KeepAliveInterval = 5s,注册 Hub 过滤器MovementFilter与HubExceptionFilter,使用.AddJsonProtocol()。 - 其他注册:
FormOptions上传上限 16MB、内存缓存、DatetimeJsonConverter、IHttpContextAccessor、IHubSend<FishRoomHub>、IRequestApi、AddAutoMapper(typeof(AutoMapperConfig))、健康检查self、CORS(Cors策略,允许任意源+凭证)、AddCustomJwt、AddRedisCacheSingleton。 - Autofac:
UseServiceProviderFactory<AutofacServiceProviderFactory>+RegisterModule<AutofacModule>()。 - CAP:
builder.Services.AddCap(builder.Configuration)(见 4.4)。 - 管道:
UseApiLogging→UseRouting→UseGlobalExceptionHandler→UseStaticFiles→UseCors("Cors")→UseAuthentication→UseAuthorization→MapHub<FishRoomHub>("/fishingroomhub", WebSockets)→MapControllers→MapHealthChecks("/health")。 - 启动后若房间列表为空则
InitRoom()。
FishGameHost\Program.cs
- 与 FishRoomHost 结构基本一致(环境变量、
AppInstanceId、配置、Redis、SignalR、CORS、JWT、RedisCache)。 - 额外注册
BulletConfig(GetSection("BulletConfig"))。 - CAP 相关:显式注册 4 个
ICapSubscribe订阅者(PlayerFactorSubscriber、LoginBindSubscriber、BackgroundAssetSubscriber、TempSpawnFishSubscriber),再AddCap。 - 后台服务:
AddHostedService<GlobalBroadcastSubscriber>()、AddHostedService<RobotKillAnnounceService>()(FishSpawnService在 AutofacModule 中注册为单例IHostedService)。 - 管道:
MapHub<FishGameHub>("/fishinghub", WebSockets)。
4.3 DI 注册位置(AutofacModule)
FishRoomHost\Middleware\AutofacModule.cs
- 泛型注册
MongoBaseRepository<>→IMongoBaseRepository<>、MongoBaseService<>→IMongoBaseService<>(InstancePerLifetimeScope)。 - 注册
RedisService→IRedisService。 - 按程序集批量注册以
Service结尾的类型(ApiLogService所在程序集、FishingRoomService所在程序集、UserService.User.UserService所在程序集),AsImplementedInterfaces。
FishGameHost\Middleware\AutofacModule.cs
- 上述 Mongo/Redis/批量服务注册之外,额外:
ReconnectTracker→IReconnectTracker(单例)BulletTracker→IBulletTracker(单例)FishKillTracker→IFishKillTracker(单例)FishSpawnConfigStore(AsSelf,单例)FishSpawnService→AsSelf且As<IHostedService>(单例)- 批量注册
Service结尾类型(排除FishSpawnService、RobotKillAnnounceService)与Subscriber结尾类型(AsSelf)。
4.4 SignalR / CAP / Redis 配置
SignalR:两进程均在 Program.cs 配置过滤器和消息包参数;Hub 均只启用 WebSockets 传输。房间层 MovementFilter 内附一个令牌桶式 RateLimiter(速率常量 30、间隔 33ms),但调用点目前被注释;对局层 MovementFilter 记录非跳过方法(SyncGameData、FireCannon、KillFish、BotFireCannon、BotKillFish 被跳过)的调用日志。
CAP(两个 CapExtension.cs):
- 均启用
UseDashboard();RabbitMQ 读取CapConfig:Host/UserName/Password/VirtualHost,ExchangeName = "cap.exchange";存储用 PostgreSQL(CapConfig:PGLogHost,Schemapublic);SucceedMessageExpiredAfter = 7*24*3600、FailedRetryCount = 5、FailedRetryInterval = 10,并设置失败阈值回调。 - 差异:
FishGameHost\Extension\CapExtension.cs额外设置x.DefaultGroupName = $"FishGameHost-{instanceId}"(INSTANCE_ID环境变量),用于多实例分组订阅;FishRoomHost的 CapExtension 额外注册IRoomMQService → RoomMQService。
Redis:
- 房间层
Config\system.json:RedisConfigHostlocalhost、Port6378、Database2、密码123456。 - 对局层
Config\system.json:RedisConfigHostlocalhost、Port6379、Database1、密码123456。 - 数据存储:房间层 Mongo
mongodb://localhost:27018/?authSource=admin;对局层mongodb://localhost:27017(Databasedata_buyu)。 - CAP 存储库:两进程 PostgreSQL 均指向
data_0524库。
对局层独有配置(FishGameHost\Config\system.json):
"BulletConfig": { "SignKey": "63215f0afa3f5b25", "MaxBulletPerUser": 100, "TimeWindowSeconds": 5 },
"GameServerInstances": [ "http://localhost/fishinghub?instance=1" ]
BulletConfig用于开炮/击杀签名校验(SignHelper:MD5(raw + "|" + key)取前 8 位小写十六进制)与时间窗防重放(TimeWindowSeconds默认 5 秒)。GameServerInstances供RoomController返回 WebSocket 地址。
端口补充说明(以代码与配置文件实际值为准,存在多处不一致,需注意):
- 房间层
Program.cs将 Hub 挂载在/fishingroomhub;Config\system.json中"urls": "http://*:8004";launchSettings.json开发 URL 为http://localhost:8004;docker-compose.yaml(服务名0524fishroom)映射5244:80;ocelot 下游指向 5244。- 对局层
Program.cs将 Hub 挂载在/fishinghub;Config\system.json中"urls": "http://*:8003";launchSettings.json开发 URL 为http://localhost:5263;docker-compose.yaml(服务名0524fishgame)映射5243:80;ocelot 下游指向 5248。即:容器内监听 80,
docker-compose将房间层映射到宿主机 5244;对局层的 compose 映射到 5243,而 ocelot 指向 5248——两部分配置本身不一致,文档如实记录,未作统一。
4.5 docker-compose 端口
FishGameSrc\FishRoomHost\docker-compose.yaml:name: webapi,服务0524fishroom,镜像0524fishroom,ports: 5244:80,restart: always。FishGameSrc\FishGameHost\docker-compose.yaml:name: webapi,服务0524fishgame,镜像0524fishgame,ports: 5243:80,restart: always。
五、代码目录说明
以下目录树覆盖
FishGameSrc\**与FishRoomService\**下的全部文件夹与.cs/.json文件,并标注关键文件职责。
5.1 FishGameSrc\FishRoomHost(房间层进程,5244 / /fishingroomhub)
FishGameSrc\FishRoomHost\
├── Config\
│ └── system.json # 运行配置:urls=8004、JwtConfig、PgSql、Cap(RabbitMQ+PG)、RedisConfig、MongoDbSettings、WXConfig
├── Extension\
│ └── CapExtension.cs # AddCap 扩展:RabbitMQ(cap.exchange)+PostgreSQL 存储+Dashboard,并注册 IRoomMQService
├── Middleware\
│ ├── ApiLoggingMiddleware.cs # /api 请求响应日志中间件(脱敏 Data 后写 ApiLog 到 Mongo),含 UseApiLogging 扩展
│ ├── AutofacModule.cs # Autofac 注册:Mongo 泛型仓储/服务、RedisService、按程序集批量注册 Service
│ ├── HubExceptionFilter.cs # SignalR Hub 异常过滤器(记录并吞掉异常,返回 null)
│ └── MovementFilter.cs # SignalR Hub 过滤器(含未启用的 RateLimiter 令牌桶)
├── Properties\
│ └── launchSettings.json # 开发启动配置(applicationUrl 8004)
├── .dockerignore # Docker 忽略规则
├── appsettings.json # 基础日志配置
├── appsettings.Development.json # 开发日志配置
├── docker-compose.yaml # 容器编排:0524fishroom,5244:80
├── FishRoomHost.csproj # 房间层进程项目文件
└── Program.cs # 进程入口:DI、SignalR、CAP、JWT、Redis、MapHub(/fishingroomhub)、启动即 InitRoom
5.2 FishGameSrc\FishGameHost(对局层进程,5248 / /fishinghub)
FishGameSrc\FishGameHost\
├── Config\
│ └── system.json # urls=8003、JwtConfig、PgSql、Cap、RedisConfig、MongoDbSettings、WXConfig、BulletConfig、GameServerInstances
├── Controllers\
│ ├── PlayerController.cs # api/Player/login:演示登录签发 JWT 并返回固定玩家数据
│ ├── RoomController.cs # api/Room/QuickJoin、api/Room/Assign:快速匹配与游戏服地址分配
│ └── TimeController.cs # fishapi/Time/GetDateTime:服务端校时
├── Extension\
│ └── CapExtension.cs # AddCap 扩展:含 DefaultGroupName=FishGameHost-{instanceId} 的多实例分组订阅
├── Middleware\
│ ├── ApiLoggingMiddleware.cs # 同房间层,/api 日志中间件
│ ├── AutofacModule.cs # Autofac:额外注册 Tracker/ConfigStore/FishSpawnService(IHostedService)/Subscriber
│ ├── HubExceptionFilter.cs # Hub 异常过滤器
│ └── MovementFilter.cs # Hub 过滤器(日志,跳过高频方法的参数打印)
├── Properties\
│ └── launchSettings.json # 开发启动配置(applicationUrl 5263)
├── Services\
│ └── GlobalBroadcastSubscriber.cs# 订阅 Redis 频道 fishgame:global,按 All/Room 经 Hub 广播(含 GlobalBroadcastMessage 定义)
├── .dockerignore # Docker 忽略规则
├── appsettings.json # 基础日志配置
├── appsettings.Development.json # 开发日志配置
├── docker-compose.yaml # 容器编排:0524fishgame,5243:80
├── FishGameHost.csproj # 对局层进程项目文件
└── Program.cs # 进程入口:DI、SignalR、CAP 订阅者注册、托管服务、MapHub(/fishinghub)
5.3 FishGameSrc\FishGameService(对局层类库)
FishGameSrc\FishGameService\
├── Config\
│ └── BulletConfig.cs # 开炮/击杀签名配置:SignKey、MaxBulletPerUser、TimeWindowSeconds
├── GameSend\
│ ├── IFishingSendService.cs # 对局层推送服务接口(房间信息/击杀/开炮/座位/道具等全部服务端事件)
│ └── FishingSendService.cs # 实现:Hub 发送封装 + 经 Redis 频道 fishgame:global 发布 KillNotice 全局公告
├── GameService\
│ ├── FishSpawnConfigStore.cs # 出鱼配置存储:从 Redis(GetFishSpawnConfig) 读取并按 {gameType}:{roomLevel} 缓存,缺失时从 GameConfig 提取
│ ├── FishingGameService.cs # 对局核心服务(进房/开炮/击杀/道具/鱼雷/离房/掉线/结算/因子/机器人),实现 IPendingSettlementService
│ ├── IBulletTracker.cs # 子弹追踪(BulletTracker) 与 击杀标记(FishKillTracker) 接口与实现(内存,带上限 LRU)
│ ├── IFishingGameService.cs # 对局核心服务接口
│ └── ReconnectTracker.cs # 重连追踪(内存 ConcurrentDictionary)
├── Hubs\
│ └── FishGameHub.cs # 对局 Hub:全部客户端方法入口与基础校验
├── Services\
│ ├── FishSpawnService.cs # 出鱼后台服务(IHostedService):租约/刷鱼/快照/命中击杀校验/临时出鱼/冻结,含 RoomFishState、ActiveFish、SpawnFishItem
│ ├── KillAnnouncePolicy.cs # 击杀公告阈值策略:gold >= minGold(默认 10,000,000)
│ └── RobotKillAnnounceService.cs # 机器人爆金公告后台服务(RobotKillConfig 驱动)
├── Subscribers\
│ ├── BackgroundAssetSubscriber.cs# 订阅 BackgroundOperation.NotifyUser:后台加减金币并同步房间
│ ├── LoginBindSubscriber.cs # 订阅「登录绑定」「拒绝回房」
│ ├── PlayerFactorSubscriber.cs # 订阅「因子变更」「配置变更」
│ └── TempSpawnFishSubscriber.cs # 订阅「临时强制出鱼」「召唤卡出鱼」
├── Utils\
│ └── SignHelper.cs # 签名工具:MD5(raw|key) 取前 8 位 + 校验
└── FishGameService.csproj # 对局层类库项目文件
5.4 FishRoomService(仓库根目录,房间层类库)
FishRoomService\
├── Hubs\
│ └── FishRoomHub.cs # 房间 Hub:GetRoomList、GetRoomInfo
├── MQ\
│ ├── IRoomMQService.cs # 房间 MQ 服务接口(RoomChange)
│ └── RoomMQService.cs # ICapSubscribe:订阅「房间变化」「用户资产同步前端」
├── RoomSend\
│ ├── FishingRoomSendService.cs # 房间推送实现:GetRoomListResult、RoomChange
│ ├── IFishingRoomSendService.cs # 房间推送接口
│ └── Response\
│ └── GameRoomReponse.cs # 房间列表响应 DTO(RoomId/MaxPlayerCount/CurrentOnlinePlayerCount/RobotCount/RoomNumber)
├── RoomService\
│ ├── FishingRoomService.cs # 房间业务:GetRoomList(分组+列表)、InitRoom(按配置+空房率+机器人初始化)
│ └── IFishingRoomService.cs # 房间业务接口
├── AutoMapperConfig.cs # AutoMapper 配置:RedisGameRoom → GameRoomReponse
└── FishRoomService.csproj # 房间层类库项目文件
六、关键文件索引
| 文件相对路径 | 职责 |
|---|---|
FishGameSrc\FishRoomHost\Program.cs |
房间层进程入口:DI、Redis、SignalR(/fishingroomhub)、CAP、JWT、启动 InitRoom |
FishGameSrc\FishRoomHost\Extension\CapExtension.cs |
房间层 CAP 配置(RabbitMQ+PostgreSQL+Dashboard)与 IRoomMQService 注册 |
FishGameSrc\FishRoomHost\Middleware\AutofacModule.cs |
房间层 Autofac 容器注册 |
FishGameSrc\FishRoomHost\Middleware\ApiLoggingMiddleware.cs |
房间层 /api 日志中间件 |
FishGameSrc\FishRoomHost\Config\system.json |
房间层运行配置(端口/Redis/Mongo/CAP/JWT) |
FishGameSrc\FishRoomHost\docker-compose.yaml |
房间层容器编排(5244:80) |
FishGameSrc\FishGameHost\Program.cs |
对局层进程入口:DI、SignalR(/fishinghub)、CAP 订阅者、托管服务 |
FishGameSrc\FishGameHost\Extension\CapExtension.cs |
对局层 CAP 配置(含多实例 DefaultGroupName) |
FishGameSrc\FishGameHost\Middleware\AutofacModule.cs |
对局层 Autofac 容器注册(Tracker/ConfigStore/FishSpawnService) |
FishGameSrc\FishGameHost\Services\GlobalBroadcastSubscriber.cs |
订阅 Redis 频道 fishgame:global 并广播 |
FishGameSrc\FishGameHost\Controllers\RoomController.cs |
api/Room/QuickJoin、api/Room/Assign |
FishGameSrc\FishGameHost\Controllers\TimeController.cs |
fishapi/Time/GetDateTime 校时接口 |
FishGameSrc\FishGameHost\Controllers\PlayerController.cs |
api/Player/login 演示登录 |
FishGameSrc\FishGameHost\Config\system.json |
对局层运行配置(BulletConfig、GameServerInstances 等) |
FishGameSrc\FishGameHost\docker-compose.yaml |
对局层容器编排(5243:80) |
FishGameSrc\FishGameService\Hubs\FishGameHub.cs |
对局 Hub:全部客户端方法入口 |
FishGameSrc\FishGameService\GameService\FishingGameService.cs |
对局核心业务(进房/开炮/击杀/结算/掉线/机器人/因子) |
FishGameSrc\FishGameService\GameService\IFishingGameService.cs |
对局核心服务接口 |
FishGameSrc\FishGameService\GameService\IBulletTracker.cs |
子弹追踪与击杀标记(内存实现) |
FishGameSrc\FishGameService\GameService\ReconnectTracker.cs |
重连追踪 |
FishGameSrc\FishGameService\GameService\FishSpawnConfigStore.cs |
出鱼配置缓存与从 GameConfig 提取 |
FishGameSrc\FishGameService\Services\FishSpawnService.cs |
出鱼后台服务(租约/刷鱼/快照/命中校验/冻结) |
FishGameSrc\FishGameService\Services\RobotKillAnnounceService.cs |
机器人爆金公告后台服务 |
FishGameSrc\FishGameService\Services\KillAnnouncePolicy.cs |
击杀公告阈值策略 |
FishGameSrc\FishGameService\GameSend\FishingSendService.cs |
对局层推送实现 + Redis 全局击杀公告 |
FishGameSrc\FishGameService\GameSend\IFishingSendService.cs |
对局层推送接口 |
FishGameSrc\FishGameService\Subscribers\LoginBindSubscriber.cs |
订阅登录绑定/拒绝回房 |
FishGameSrc\FishGameService\Subscribers\PlayerFactorSubscriber.cs |
订阅因子变更/配置变更 |
FishGameSrc\FishGameService\Subscribers\TempSpawnFishSubscriber.cs |
订阅临时出鱼/召唤卡出鱼 |
FishGameSrc\FishGameService\Subscribers\BackgroundAssetSubscriber.cs |
订阅后台资产变更 |
FishGameSrc\FishGameService\Config\BulletConfig.cs |
签名与子弹配置模型 |
FishGameSrc\FishGameService\Utils\SignHelper.cs |
签名计算与校验 |
FishRoomService\Hubs\FishRoomHub.cs |
房间 Hub:GetRoomList、GetRoomInfo |
FishRoomService\RoomService\FishingRoomService.cs |
房间业务:大厅列表与房间初始化 |
FishRoomService\RoomService\IFishingRoomService.cs |
房间业务接口 |
FishRoomService\RoomSend\FishingRoomSendService.cs |
房间推送实现 |
FishRoomService\MQ\RoomMQService.cs |
房间级 CAP 订阅(房间变化、资产同步前端) |
FishRoomService\RoomSend\Response\GameRoomReponse.cs |
房间列表响应 DTO |
FishRoomService\AutoMapperConfig.cs |
房间层 AutoMapper 配置 |
Gateway\ocelot.json |
网关路由(含 /fishingroomhub→5244、/gamehub→/fishinghub 5248) |
CommonDomain\TopicKeys.cs |
全局 CAP 主题常量定义 |
CommonDomain\GameConfig.cs |
游戏配置(房间/鱼/机器人/击杀/出鱼因子等) |
CommonDomain\EmptyRoomRateConfig.cs |
空房率配置 |
CommonDomain\BotProfileEntity.cs |
机器人昵称/头像配置 |
CommonDomain\CapMessageModels.cs |
CAP 消息模型(登录绑定/临时出鱼/召唤卡/因子变更/配置变更/后台操作) |
Infrastructure\Common\Redis\RedisKey.cs |
Redis Key 生成器(含捕鱼相关 Key) |
RedisService\IRedisService.cs |
Redis 服务接口(房间/玩家/配置/因子/进度等读写) |
RedisService\Param\RedisGameRoom.cs |
房间模型 |
RedisService\Param\RedisGamePlayer.cs |
房间内玩家模型 |
RedisService\Param\RedisUserCid.cs |
用户-连接-房间映射模型 |
RedisService\Param\RoomBotFactory.cs |
机器人工厂(FixedBotSeats 当前为空) |
七、与其他模块的交互
7.1 CAP / RabbitMQ 消息(主题常量见 CommonDomain\TopicKeys.cs)
本模块「订阅」的消息
| 主题常量 | 实际主题名 | 订阅者 |
|---|---|---|
房间变化 |
Topic_0524_RoomChage |
FishRoomService\MQ\RoomMQService(房间层向分区分组广播 RoomChange) |
用户资产同步前端 |
Topic_0524_UserAssetSyncFrontend |
RoomMQService(房间层推送 AssetUpdated) |
因子变更 |
Topic_0524_PlayerFactorChanged |
PlayerFactorSubscriber(对局层) |
配置变更 |
Topic_0524_ConfigChanged |
PlayerFactorSubscriber(对局层) |
登录绑定 |
Topic_0524_LoginBind |
LoginBindSubscriber(对局层,登录前结算离房) |
拒绝回房 |
Topic_0524_DismissPendingRoom |
LoginBindSubscriber(对局层强制清房) |
临时强制出鱼 |
Topic_0524_TempSpawnFish |
TempSpawnFishSubscriber(对局层) |
召唤卡出鱼 |
Topic_0524_SummonCardFish |
TempSpawnFishSubscriber(对局层) |
BackgroundOperation.NotifyUser(字符串常量) |
BackgroundOperation.NotifyUser |
BackgroundAssetSubscriber(对局层后台加减币) |
本模块「发布」的消息
| 主题常量 | 实际主题名 | 发布者 | 消费方(平台其它模块,来自全局定义) |
|---|---|---|---|
房间变化 |
Topic_0524_RoomChage |
FishingGameService.SendRoomChange(对局层) |
房间层 RoomMQService(本模块内闭环) |
用户资产变更 |
Topic_0524_UserAssetsChanged |
FishingGameService.UseProp/UseTorpedo |
用户资产模块(UserService) |
全局击杀公告 |
Topic_0524_GlobalKillAnnounce |
FishingGameService.FishKillControl、RobotKillAnnounceService |
全服公告消费方(UserService\Hubs\KillAnnounceSubscriber.cs 等) |
任务完成 |
Topic_0524_CompletedTask |
FishingGameService.FlushFishingTaskProgress |
任务系统(CompletedTaskParam,UserService 侧) |
登录结果通知 |
Topic_0524_LoginBindResult |
LoginBindSubscriber.HandleLoginBind |
登录模块(UserService LoginResultSubscriber) |
说明:
全局击杀公告由UserService\Hubs\KillAnnounceSubscriber.cs承接(全服广播);登录绑定/登录结果通知/用户资产变更等由 UserService 侧订阅者处理,跨进程完成「登录前结算离房」「资产落库与前端同步」等协作。以上消费方文件位于本模块之外,此处仅据主题名与命名对应关系指出大致去向,具体实现不在本模块范围内。
7.2 Redis Key
来源 Infrastructure\Common\Redis\RedisKey.cs + RedisKeyGenerator.cs。RedisKeyGenerator 按服务名加前缀(game→game-service、user→user-service、common→app-shared),故实际 Key 形如 game-service:...:
| 属性/常量 | 生成方式 | 实际 Key | 用途 |
|---|---|---|---|
GameConfig |
GameKey("config") |
game-service:config |
游戏配置(GameConfig) |
ActivityConfig |
GameKey("activity:config") |
game-service:activity:config |
活动配置 |
FishingKillConfig |
GameKey("fishing:killconfig") |
game-service:fishing:killconfig |
击杀/玩法配置(FishingKillConfig) |
FishSpawnConfig |
GameKey("fish:spawn:config:v1") |
game-service:fish:spawn:config:v1 |
出鱼配置 JSON |
SystemGoldDayKey |
GameKey("fish:gamestatic") |
game-service:fish:gamestatic |
系统当日金币(系统因子计算) |
GameRoomIdKey |
GameKey("room:model:{0}_{1}") |
game-service:room:model:{0}_{1} |
房间模型 Key |
DistributedLockPrefix |
CommonKey("lock:") |
app-shared:lock: |
分布式锁前缀 |
UserCIDGID |
UserKey("uid:{0}") |
user-service:uid:{0} |
用户 → 连接/房间映射 |
CIDUser / LoginCIDUser |
UserKey("cid:{0}") |
user-service:cid:{0} |
连接 → 用户映射 |
FishSpawnService 内自定义 Redis Key:
| 常量前缀 | 完整 Key | 用途 |
|---|---|---|
fish:spawn:global:id |
同左 | 全局自增鱼 Id |
SpawnOwnerKeyPrefix |
fish:spawn:owner:{roomId} |
房间出鱼权租约(10 秒) |
ActiveFishSnapshotKeyPrefix |
fish:spawn:snapshot:{roomId} |
活跃鱼快照(60 秒) |
FishKilledKeyPrefix |
fish:spawn:killed:{roomId}:{fishId} |
鱼已击杀标记 |
SpawnStopKeyPrefix |
fish:spawn:stop:{roomId} |
房间停止出鱼标记 |
FishingGameService 使用分布式锁 Key:{lock:}fish:gold:{roomId}:{userId}(金币/结算互斥)。
Redis 发布/订阅频道(非 CAP):fishgame:global —— FishingSendService.KillNotice 发布,GlobalBroadcastSubscriber 订阅。
7.3 HTTP 调用
- 本模块内
FishGameHost\Controllers\RoomController.cs读取配置GameServerInstances返回游戏服 WebSocket 地址(当前取列表第 0 项),并读取 Redis 房间列表做快速匹配。 - 通过
IRequestApi(注册于Program.cs)提供对外 HTTP 请求能力;FishingGameService、房间层服务通过IUserService(UserService 项目)读写用户资产、属性、结算等(GameCoinsSettlement、UpdateAssets、UpdateUser、RecordFishingConsume、GetUser)。 - 校时接口
fishapi/Time/GetDateTime供客户端对齐服务器时间。
7.4 共享实体 / 项目引用
- 项目引用链:
FishRoomHost→FishRoomService、CommonInfrastructure、SharpAbp.Infrastructure.ApiFishGameHost→FishGameService、CommonInfrastructure、SharpAbp.Infrastructure.ApiFishGameService→CommonDomain、CommonService、RedisService、UserServiceFishRoomService→CommonService、RedisService、UserService
- 共享实体/模型:
RedisService.Param(RedisGameRoom、RedisGamePlayer、RedisUserCid、RoomBotFactory)、CommonDomain(GameConfig、EmptyRoomRateConfig、BotProfileEntity、FishingKillConfig、ActiveFish相关、TopicKeys、CapMessageModels、KillAnnounceParam)、UserDomain.Entity(UserEntity)、CommonDomain.Enum(GameTypeEnum、FishTypeEnum、ProductTypeEnum、TaskEvent、ErrorCodes等)。 - 公共基础设施:
MongoRepository(IMongoBaseRepository<>/IMongoBaseService<>)、RedisService/RedisHelper(ICache、IDistributedLock)、SharpAbp.Infrastructure.Api(UseGlobalExceptionHandler等中间件)、JWTHelper(AddCustomJwt、JwtTools)、CommonService(IHubSend<>、HubSend<>、ApiLogService、AppInstanceId)。
本文档为一次性通读源码后整理的静态说明;如后续修改了 Hub 方法、CAP 主题、Redis Key 或端口配置,请同步更新本文件。