Files
BuYuHouDuan/docs/05-捕鱼游戏模块-FishGame.md
T
2026-10-12 16:22:04 +08:00

564 lines
46 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 捕鱼游戏模块(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`,BaseUrl `http://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` 负责按配置定时伪造「机器人爆金击杀」全局公告。
### 2.2 核心业务流程(以代码为准)
1. **服务启动初始化房间(房间层)**
- `FishRoomHost\Program.cs` 启动时,若 `IRedisService.GetGameRoomAllList()` 为空,则调用 `IFishingRoomService.InitRoom()` 初始化房间。房间初始化失败会记录日志并抛出(`throw`)。
2. **大厅 → 房间列表(房间层)**
- 客户端连接 `/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`。
3. **房间初始化(房间层,`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。
4. **进房(对局层,`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 发布「房间变化」)。
5. **对局中(对局层)**
- **开炮 `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}` 供无租约实例读取。
6. **离房 / 掉线 / 结算(对局层)**
- **主动离房 `LeaveRoom`**:在分布式锁 `lock:fish:gold:{roomId}:{userId}` 下,先「标记离房(State=2)」,再在满足条件时执行 `LeaveRoomInternal`(累加在线时长、结算钓鱼进度与 `GameCoinsSettlement`、清理运行时数据、移除玩家、房主转移、`NormalizeRoomPlayerCounts`、无人时清空并 `UnregisterRoom`、广播 `OnSeatChange(leave)`/`RoomLeaveNotice`、`SendRoomChange`)。
- **掉线 `HandleDisconnect`**:玩家 State 置 1,房主转移;启动 60 秒延迟任务;超时后 `OnDisconnectTimeoutInternal` 结算并清理房间。
- **重连 `Reconnect` / `JoinRoom`**:恢复连接、取消待掉线定时器、恢复座位与房主、补发活跃鱼同步。
7. **结算(结算链)**
- `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`,Schema `public`);`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`:`RedisConfig` Host `localhost`、Port `6378`、Database `2`、密码 `123456`。
- 对局层 `Config\system.json`:`RedisConfig` Host `localhost`、Port `6379`、Database `1`、密码 `123456`。
- 数据存储:房间层 Mongo `mongodb://localhost:27018/?authSource=admin`;对局层 `mongodb://localhost:27017`(Database `data_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.Api`
- `FishGameHost` → `FishGameService`、`CommonInfrastructure`、`SharpAbp.Infrastructure.Api`
- `FishGameService` → `CommonDomain`、`CommonService`、`RedisService`、`UserService`
- `FishRoomService` → `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 或端口配置,请同步更新本文件。