1449 lines
45 KiB
Markdown
1449 lines
45 KiB
Markdown
# 语言房重构方案
|
||
|
||
## 文档目的
|
||
|
||
本文只记录一套可执行的重构方案,本轮不改业务代码。
|
||
|
||
本次建议的重构范围不是“整套语言房全部推倒重写”,而是优先重构**语言房特效子系统**,重点解决以下问题:
|
||
|
||
- 礼物特效、进场、飞向麦位、房间飘屏并发时主线程抢占严重
|
||
- 特效触发源分散,队列和调度分散,后续越优化越难维护
|
||
- 房间内特效偶发残留到首页/其它页面,生命周期边界不清晰
|
||
- 已做过多轮局部优化,但整体架构仍然偏重,继续打补丁收益会越来越低
|
||
|
||
本方案的核心原则是:
|
||
|
||
- 尽可能保留当前礼物业务逻辑、素材、阈值和视觉表现
|
||
- 先重构特效系统,不一次性推倒 `RTC/RTM/房间业务` 主链路
|
||
- 先做“收口与解耦”,再做“性能压测与降级”
|
||
|
||
---
|
||
|
||
## 当前建议结论
|
||
|
||
建议重构方向:
|
||
|
||
- 保留现有房间业务、消息协议、礼物配置接口和大部分现有素材
|
||
- 将当前分散在 `VoiceRoomPage / RTM / RTC / OverlayManager / SCGiftVapSvgaManager / GiftAnimationManager / RoomEntranceQueue` 的特效触发与播放逻辑,统一收口到一个**语言房特效引擎**
|
||
- 把“事件归一化、去重、合并、优先级、预算控制、播放调度、生命周期回收”集中管理
|
||
|
||
不建议本轮做的事:
|
||
|
||
- 不建议同时重写整套语言房 UI、聊天室、麦位、RTC 状态层
|
||
- 不建议先动后端协议
|
||
- 不建议一开始就更换全部动画素材格式
|
||
|
||
---
|
||
|
||
## 当前架构现状
|
||
|
||
### 当前核心模块
|
||
|
||
| 模块 | 当前文件 | 当前职责 | 当前问题 |
|
||
| --- | --- | --- | --- |
|
||
| 房间主页面 | `lib/modules/room/voice_room_page.dart` | 挂载房间 UI、礼物播报、进场动画、幸运礼物动画、飞向麦位控制器 | 页面层知道太多特效细节,既处理业务又处理动画 |
|
||
| 房间消息入口 | `lib/services/audio/rtm_manager.dart` | 处理群消息、礼物消息、进场消息、全服广播、飘屏 | 直接触发 `play()`、`OverlayManager().addMessage()`,事件与 UI 强耦合 |
|
||
| 房间状态入口 | `lib/services/audio/rtc_manager.dart` | 管理进房、退房、麦位、在线用户、房间状态 | 也会直接触发进场坐骑特效,和 RTM 形成双入口 |
|
||
| 全屏礼物播放器 | `lib/shared/tools/sc_gift_vap_svga_manager.dart` | 管理高成本 `VAP/SVGA` 全屏特效任务 | 只管理自己这一条链路,不是整个房间特效系统的总调度器 |
|
||
| 房间调度器 | `lib/shared/tools/sc_room_effect_scheduler.dart` | 在高成本特效期间延后部分低优先级特效 | 只有“延后”能力,没有统一队列、优先级、预算、去重、丢弃策略 |
|
||
| 顶部礼物播报条 | `lib/services/gift/gift_animation_manager.dart` + `lib/ui_kit/widgets/room/anim/l_gift_animal_view.dart` | 4 个槽位的礼物播报、连击合并、幸运礼物奖励框 | 已做局部优化,但仍独立存在,未纳入统一引擎 |
|
||
| 进场动画 | `lib/ui_kit/widgets/room/anim/room_entrance_screen.dart` | 房内横向 banner 进场队列 | 又是一套独立队列与独立状态机 |
|
||
| 飞向麦位 | `lib/ui_kit/widgets/room/anim/room_gift_seat_flight_overlay.dart` | 礼物图片从中间飞向目标麦位 | 独立 overlay、独立队列、独立缓存 |
|
||
| 飘屏 | `lib/shared/data_sources/sources/local/floating_screen_manager.dart` | 幸运礼物、礼物、火箭、红包、游戏飘屏 | 仍是独立优先队列,与房间特效生命周期分离 |
|
||
| 旧进场链路 | `lib/ui_kit/widgets/room/anim/room_entrance_widget.dart` | 旧版 `SVGA` 进场队列与 helper | 当前基本不承担主展示,但代码仍存在,增加维护噪音 |
|
||
|
||
### 当前核心问题
|
||
|
||
- 同一套语言房里同时存在多套特效队列和状态机
|
||
- `RTM/RTC` 直接知道“怎么播动画”,不是只产出事件
|
||
- 房间专属特效有一部分挂在根层 `MaterialApp builder`,不是挂在房间路由内部
|
||
- 同类事件存在本地触发与远端回流双路径,天然有重复触发风险
|
||
- 当前调度器只在高成本特效时“暂缓别的”,但底层仍是多个系统各播各的
|
||
- 历史链路未完全收口,例如旧进场 helper 仍保留
|
||
|
||
---
|
||
|
||
## 根因判断
|
||
|
||
当前卡顿的根因更偏向**结构性问题**,而不是单个控件或单次绘制的问题:
|
||
|
||
1. 多源头直接驱动 UI
|
||
`RTM/RTC/RoomPage/GiftSystem` 都可能直接触发动画。
|
||
|
||
2. 多条高频动画链路并发
|
||
全屏 `VAP/SVGA`、顶部礼物条、进场 banner、飞向麦位、房间飘屏可能同时争抢主线程。
|
||
|
||
3. 房间特效不是一个完整子系统
|
||
现在只有局部管理器,没有一个真正拥有“总入口、总队列、总预算、总生命周期”的 runtime。
|
||
|
||
4. 业务状态更新和特效播放距离太近
|
||
房间麦位刷新、在线用户刷新、消息列表更新,会和特效播放共享同一个页面状态环境。
|
||
|
||
5. 生命周期边界偏松
|
||
房间退出后,根层 overlay 和异步回调仍然需要额外兜底清理。
|
||
|
||
---
|
||
|
||
## 重构目标
|
||
|
||
### 目标
|
||
|
||
- 让语言房特效具备统一入口、统一调度、统一生命周期
|
||
- 在不改现有礼物业务口径的前提下降低并发卡顿
|
||
- 让房间特效只在房间内部存在,不再溢出到其它页面
|
||
- 让每种特效都可以独立限流、合并、降级、观测
|
||
- 让后续继续优化时,能在统一引擎层处理,而不是继续在各个页面打补丁
|
||
|
||
### 非目标
|
||
|
||
- 本轮不改变房间消息协议
|
||
- 本轮不重做礼物页面或礼物发送接口
|
||
- 本轮不调整礼物业务规则口径
|
||
- 本轮不强制替换所有动画素材格式
|
||
|
||
---
|
||
|
||
## 必须保留的当前礼物/进场业务逻辑
|
||
|
||
本次重构的前提是:**默认保留当前业务判断口径,只调整结构和调度层**。以下逻辑建议在首轮迁移中原样保留。
|
||
|
||
| 逻辑项 | 当前口径 | 重构策略 |
|
||
| --- | --- | --- |
|
||
| 礼物消息来源 | 继续沿用当前房间 RTM 自定义消息 | 保留,不改协议 |
|
||
| 全屏礼物触发口径 | 继续按 `gift.special + giftSourceUrl + scGiftHasFullScreenEffect(...)` 判定 | 保留,先抽到策略层 |
|
||
| 礼物特效总开关 | 继续沿用 `SCGlobalConfig.isGiftSpecialEffects` | 保留,接入新引擎策略层 |
|
||
| 入场坐骑开关 | 继续沿用 `SCGlobalConfig.isEntryVehicleAnimation` | 保留,接入新引擎策略层 |
|
||
| 飘屏开关 | 继续沿用 `SCGlobalConfig.isFloatingAnimationInGlobal` | 保留,接入新引擎策略层 |
|
||
| 顶部礼物播报条合并规则 | 继续按当前 `labelId` 逻辑合并活动项和待播项 | 保留,迁移时保持视觉与合并口径一致 |
|
||
| 顶部礼物播报条槽位数 | 继续保持 `4` 个槽位 | 保留,首轮不改 |
|
||
| 顶部礼物播报待播上限 | 继续保持当前上限 `24` | 保留,后续再参数化 |
|
||
| 顶部礼物播报空闲消失时间 | 继续保持约 `3200ms` | 保留,后续再参数化 |
|
||
| `customAnimationCount` 逻辑 | 继续作为飞向麦位次数和礼物数量步进动画的依据 | 保留,不改视觉节奏 |
|
||
| 飞向麦位目标解析 | 继续按当前 `toUser -> account -> 麦位映射` 解析目标 | 保留,迁移时统一封装 |
|
||
| 飞向麦位会话去重 | 继续保留当前 `queueTag / sessionKey` 级别清理策略 | 保留 |
|
||
| 幸运礼物里程碑特效 | 继续保留当前里程碑资源集合与触发口径 | 保留 |
|
||
| 幸运礼物顶部奖励框 | 继续保留当前奖励金额展示方式 | 保留 |
|
||
| 幸运礼物 burst 条件 | 继续保持“倍率 `>= 10x` 或单次奖励 `> 5000`” | 保留 |
|
||
| 幸运礼物飘屏口径 | 继续保持当前服务端 `globalNews` + 倍率门槛逻辑 | 保留 |
|
||
| 大额礼物房间飘屏阈值 | 继续保持当前 `coins > 9999` 才触发房间礼物飘屏 | 保留 |
|
||
| 礼物触发麦位刷新 | 继续保留礼物触发后的节流刷新逻辑 | 保留,迁移时挪到 effect bridge 或 event side-effect 层 |
|
||
| 房间进场横幅 | 继续保留当前房内进场 banner 表达方式 | 保留视觉,不一定保留旧实现 |
|
||
|
||
### 建议保留但从硬编码改为配置化的参数
|
||
|
||
- 礼物播报槽位数
|
||
- 礼物播报待播上限
|
||
- 进场队列上限
|
||
- 飞向麦位单会话上限
|
||
- 高成本特效队列上限
|
||
- 飘屏丢弃阈值
|
||
- 各类优先级与独占关系
|
||
|
||
---
|
||
|
||
## 目标架构
|
||
|
||
### 架构总览
|
||
|
||
```mermaid
|
||
graph TD
|
||
A[RTM/RTC/房间本地动作] --> B[RoomEffectEventAdapter]
|
||
B --> C[RoomEffectEngine]
|
||
C --> D[EffectPolicy]
|
||
C --> E[Dedup & Merge]
|
||
C --> F[Lane Dispatcher]
|
||
F --> G[Fullscreen Lane]
|
||
F --> H[Entrance Lane]
|
||
F --> I[GiftTicker Lane]
|
||
F --> J[SeatFlight Lane]
|
||
F --> K[Floating Lane]
|
||
G --> L[RoomEffectStage]
|
||
H --> L
|
||
I --> L
|
||
J --> L
|
||
K --> L
|
||
L --> M[SVGA/VAP/Image/Implicit Animations]
|
||
C --> N[Metrics & Trace]
|
||
```
|
||
|
||
### 核心思想
|
||
|
||
- 所有原始事件先进入 `RoomEffectEventAdapter`
|
||
- adapter 只负责把“原始 RTM/RTC/本地动作”变成**标准化特效事件**
|
||
- 所有标准化事件再进入 `RoomEffectEngine`
|
||
- engine 统一做去重、合并、优先级排序、预算判断、降级和分 lane 调度
|
||
- 真正的渲染层只负责展示,不再关心业务来源
|
||
|
||
---
|
||
|
||
## 推荐模块拆分图
|
||
|
||
建议新增一个独立的语言房特效模块,命名可为 `lib/modules/room_effect/` 或 `lib/shared/room_effect/`。
|
||
|
||
```text
|
||
lib/
|
||
modules/
|
||
room_effect/
|
||
domain/
|
||
room_effect_event.dart
|
||
room_effect_task.dart
|
||
room_effect_lane.dart
|
||
room_effect_priority.dart
|
||
room_effect_policy_snapshot.dart
|
||
application/
|
||
room_effect_event_adapter.dart
|
||
room_effect_engine.dart
|
||
room_effect_deduplicator.dart
|
||
room_effect_merger.dart
|
||
room_effect_policy.dart
|
||
room_effect_metrics.dart
|
||
presentation/
|
||
room_effect_stage.dart
|
||
layers/
|
||
room_fullscreen_effect_layer.dart
|
||
room_entrance_effect_layer.dart
|
||
room_gift_ticker_layer.dart
|
||
room_seat_flight_layer.dart
|
||
room_floating_effect_layer.dart
|
||
infrastructure/
|
||
room_effect_asset_preloader.dart
|
||
room_effect_player_bridge.dart
|
||
room_effect_lifecycle_guard.dart
|
||
```
|
||
|
||
### 各模块职责
|
||
|
||
| 模块 | 职责 | 说明 |
|
||
| --- | --- | --- |
|
||
| `RoomEffectEventAdapter` | 事件归一化 | 把 `RTM/RTC/RoomPage` 的原始数据转为统一事件对象 |
|
||
| `RoomEffectEngine` | 统一调度核心 | 做入队、去重、合并、优先级、预算、丢弃、lane 分发 |
|
||
| `RoomEffectPolicy` | 播放策略 | 根据机型、开关、 backlog、当前播放状态决定播什么、延后什么、降级什么 |
|
||
| `RoomEffectStage` | 页面内特效承载层 | 只在语言房路由内部挂载,房间退出就一并销毁 |
|
||
| `PlayerBridge` | 播放器桥接层 | 首轮复用现有 `SCGiftVapSvgaManager` / `GiftAnimationManager` / 现有动画组件 |
|
||
| `AssetPreloader` | 资源预热 | 统一预热图片、SVGA、VAP,本轮不再让每个特效组件各自处理 |
|
||
| `Metrics` | 指标与日志 | 记录入队数、丢弃数、等待时长、播放耗时、异常和 backlog |
|
||
|
||
---
|
||
|
||
## 目标事件模型
|
||
|
||
建议统一定义一个标准化事件对象 `RoomEffectEvent`,至少包含以下信息:
|
||
|
||
- `eventId`
|
||
- `roomId`
|
||
- `eventType`
|
||
- `source`
|
||
- `createdAt`
|
||
- `priority`
|
||
- `dedupKey`
|
||
- `mergeKey`
|
||
- `userId`
|
||
- `targetUserId`
|
||
- `giftId`
|
||
- `payload`
|
||
- `requiresHeavyRenderer`
|
||
- `roomScoped`
|
||
|
||
### 推荐事件类型
|
||
|
||
- `roomUserJoin`
|
||
- `roomMountEntrance`
|
||
- `giftTicker`
|
||
- `giftFullscreen`
|
||
- `giftSeatFlight`
|
||
- `giftFloating`
|
||
- `luckyGiftMilestone`
|
||
- `luckyGiftRewardTicker`
|
||
- `luckyGiftBurst`
|
||
- `rocketFloating`
|
||
- `redPacketFloating`
|
||
- `gameFloating`
|
||
|
||
### 为什么要有标准化事件层
|
||
|
||
- 后续所有去重和合并都建立在统一事件模型上
|
||
- 可把“同一礼物事件触发 3 种视觉表现”拆成 3 个子任务,而不是让 RTM 直接调用 3 个 UI 系统
|
||
- 可支持本地自发事件和远端回流事件的统一去重
|
||
|
||
---
|
||
|
||
## 目标任务模型
|
||
|
||
事件进入引擎后,不直接播放,而是先变成 `RoomEffectTask`。
|
||
|
||
一个 task 建议至少包含:
|
||
|
||
- `taskId`
|
||
- `lane`
|
||
- `priority`
|
||
- `enqueueAt`
|
||
- `deadline`
|
||
- `exclusive`
|
||
- `mergeable`
|
||
- `mergeKey`
|
||
- `dropPolicy`
|
||
- `payload`
|
||
- `onStart`
|
||
- `onComplete`
|
||
- `onCancel`
|
||
|
||
### 推荐的 lane 划分
|
||
|
||
| Lane | 作用 | 特点 |
|
||
| --- | --- | --- |
|
||
| `fullscreen` | 全屏礼物、幸运礼物 burst、进场坐骑等高成本特效 | 可独占,优先级最高 |
|
||
| `entrance` | 房间进场 banner | 串行,可合并,可被延后 |
|
||
| `giftTicker` | 顶部礼物播报条 | 维持 4 槽位模型 |
|
||
| `seatFlight` | 飞向麦位 | 可批量、可会话级去重 |
|
||
| `floating` | 房间飘屏、幸运礼物飘屏、火箭、红包、游戏 | 可按消息类型和房间范围过滤 |
|
||
|
||
---
|
||
|
||
## 页面挂载方式调整
|
||
|
||
### 当前问题
|
||
|
||
当前房间特效有一部分挂在根层 `MaterialApp builder`,例如:
|
||
|
||
- 全屏 `VapPlusSvgaPlayer`
|
||
- `RoomGiftSeatFlightOverlay`
|
||
|
||
这意味着:
|
||
|
||
- 特效承载层不完全属于房间页面
|
||
- 房间退出后需要额外兜底清理
|
||
- 根层 overlay 容易被误复用到非房间页面
|
||
|
||
### 目标方式
|
||
|
||
推荐新增 `RoomEffectStage`,只挂在语言房页面内:
|
||
|
||
```mermaid
|
||
graph TD
|
||
A[VoiceRoomPage] --> B[RoomScene]
|
||
A --> C[RoomEffectStage]
|
||
C --> D[Fullscreen Layer]
|
||
C --> E[Entrance Layer]
|
||
C --> F[GiftTicker Layer]
|
||
C --> G[SeatFlight Layer]
|
||
C --> H[Floating Layer]
|
||
```
|
||
|
||
### 结果
|
||
|
||
- 房间特效生命周期跟随 `VoiceRoomPage`
|
||
- 退房、最小化、切房时统一收口
|
||
- 根层只保留真正全局的东西,房间特效不再越界
|
||
|
||
---
|
||
|
||
## 首轮推荐复用现有实现的方式
|
||
|
||
为尽量保留当前礼物逻辑,首轮不需要把所有 renderer 重写,可以先“新引擎 + 旧 renderer”。
|
||
|
||
### 可复用项
|
||
|
||
| 现有实现 | 首轮建议 |
|
||
| --- | --- |
|
||
| `SCGiftVapSvgaManager` | 作为 `fullscreen lane` 的底层播放器桥接 |
|
||
| `GiftAnimationManager` | 作为 `giftTicker lane` 的底层 4 槽位实现 |
|
||
| `RoomGiftSeatFlightOverlay` | 作为 `seatFlight lane` 的底层渲染器 |
|
||
| `RoomAnimationQueueScreen` | 其视觉可复用,但队列控制迁到新引擎 |
|
||
| `OverlayManager` | 首轮可继续承载非房间全局类型;房间类型逐步迁出 |
|
||
|
||
### 不建议继续保留为长期方案的实现
|
||
|
||
| 现有实现 | 建议 |
|
||
| --- | --- |
|
||
| `room_entrance_widget.dart` 的旧 helper/queue | 清理掉,不再作为正式链路 |
|
||
| 在 `RTM/RTC` 中直接 `play()` 或 `addMessage()` | 改为统一投递特效事件 |
|
||
| 在根层 `MaterialApp builder` 挂房间专属特效层 | 迁回房间路由内部 |
|
||
|
||
---
|
||
|
||
## 调度与降级策略
|
||
|
||
### 首轮策略
|
||
|
||
首轮重构不建议改业务口径,但建议统一调度策略。
|
||
|
||
#### 建议规则
|
||
|
||
- `fullscreen lane` 播放时,`entrance / floating / seatFlight` 默认延后
|
||
- `giftTicker lane` 继续独立展示,但只保留当前 4 槽位,不无限堆积
|
||
- `seatFlight lane` 保持当前 `customAnimationCount` 逻辑,但由引擎统一裁剪上限
|
||
- `entrance lane` 超过 backlog 后可合并为“X 人进入房间”
|
||
- `floating lane` 在高峰时优先保留高优先级类型,低优先级直接丢弃
|
||
|
||
### 第二阶段可再加的自适应策略
|
||
|
||
- backlog 超阈值时,把同礼物多次飞向麦位降成 1 次飞行 + 数量角标
|
||
- 进场队列过长时,多条普通进场合并成一条摘要
|
||
- 同时存在全屏高成本特效时,房间礼物飘屏暂停或只保留 1 条
|
||
- 低性能设备继续保留现有总开关,但高性能设备也可按瞬时 backlog 动态降级
|
||
|
||
---
|
||
|
||
## 迁移顺序
|
||
|
||
建议按“先收口入口,再迁移渲染,再清理历史代码”的顺序做,避免一口气全动。
|
||
|
||
### 阶段 0:建立基线与指标
|
||
|
||
目标:
|
||
|
||
- 先补齐特效侧可观测性
|
||
- 固化当前礼物逻辑和关键参数,形成“迁移不改口径”的基线
|
||
|
||
本阶段内容:
|
||
|
||
- 统计每类特效的入队数、丢弃数、平均等待时长、播放时长
|
||
- 统计 `fullscreen/entrance/giftTicker/seatFlight/floating` 的 backlog 峰值
|
||
- 用 `FrameTiming` 记录语言房高压场景的 build/raster 情况
|
||
|
||
本阶段不改:
|
||
|
||
- 不改现有播放逻辑
|
||
- 不改现有视觉
|
||
- 不改现有业务阈值
|
||
|
||
### 阶段 1:统一事件入口
|
||
|
||
目标:
|
||
|
||
- 引入 `RoomEffectEventAdapter`
|
||
- 所有房间特效先变成统一事件对象
|
||
|
||
本阶段内容:
|
||
|
||
- 把 `RTM/RTC/VoiceRoomPage/GiftSystem` 里直接触发特效的地方改为“投递事件”
|
||
- 本地触发与远端回流先统一走一层去重 key
|
||
|
||
本阶段不改:
|
||
|
||
- renderer 先不迁
|
||
- `SCGiftVapSvgaManager`、`GiftAnimationManager` 等先照旧使用
|
||
|
||
关键收益:
|
||
|
||
- 从这一阶段开始,特效来源就被收口了
|
||
|
||
### 阶段 2:落地 `RoomEffectEngine`
|
||
|
||
目标:
|
||
|
||
- 让所有统一事件进入引擎
|
||
- 引擎接管优先级、去重、合并和 lane 分发
|
||
|
||
本阶段内容:
|
||
|
||
- 新建 `RoomEffectEngine`
|
||
- 新建 lane 模型
|
||
- 先做不改变视觉的“旧 renderer 包装”
|
||
|
||
本阶段不改:
|
||
|
||
- 不改具体动画 UI
|
||
- 不改素材
|
||
|
||
关键收益:
|
||
|
||
- 从“多套队列系统”变成“一个总引擎 + 多条 lane”
|
||
|
||
### 阶段 3:把房间特效挂回房间路由内部
|
||
|
||
目标:
|
||
|
||
- 新建 `RoomEffectStage`
|
||
- 把房间专属层从根层迁回 `VoiceRoomPage`
|
||
|
||
本阶段内容:
|
||
|
||
- `RoomEffectStage` 承载房间内所有特效 layer
|
||
- 根层只保留真正全局的内容
|
||
- 统一退房清理、最小化清理、切房清理
|
||
|
||
本阶段重点回归:
|
||
|
||
- 房间退出后不再有礼物图残留
|
||
- 房间飘屏不再跑到首页
|
||
|
||
### 阶段 4:迁移 `fullscreen lane`
|
||
|
||
目标:
|
||
|
||
- 先把最高成本、影响最大的特效链路收口
|
||
|
||
本阶段内容:
|
||
|
||
- `SCGiftVapSvgaManager` 改为由 `fullscreen lane` 调用
|
||
- 进场坐骑、全屏礼物、幸运礼物 burst 都走统一 lane
|
||
- 自带独占、优先级和预热策略
|
||
|
||
本阶段必须保持不变:
|
||
|
||
- 当前全屏礼物触发口径不变
|
||
- 当前幸运礼物里程碑和 burst 口径不变
|
||
|
||
### 阶段 5:迁移 `entrance lane`
|
||
|
||
目标:
|
||
|
||
- 收口房间进场 banner
|
||
- 清理旧进场链路
|
||
|
||
本阶段内容:
|
||
|
||
- `RoomAnimationQueueScreen` 只保留渲染,队列逻辑迁到 `entrance lane`
|
||
- 清理 `room_entrance_widget.dart` 旧 helper 及残余引用
|
||
- 引入本地与远端进场事件去重
|
||
|
||
本阶段必须保持不变:
|
||
|
||
- 当前进场 UI 表达不变
|
||
- 当前入场后坐骑与横幅仍可共存,但由统一引擎调度
|
||
|
||
### 阶段 6:迁移 `giftTicker lane`
|
||
|
||
目标:
|
||
|
||
- 保留当前 4 槽位礼物播报条逻辑,但把队列和状态收回引擎
|
||
|
||
本阶段内容:
|
||
|
||
- `GiftAnimationManager` 改为 lane 内 renderer bridge
|
||
- 继续保留当前 `labelId` 合并、4 槽位、空闲消失时间、数量步进动画
|
||
- 幸运礼物奖励框仍保留当前样式
|
||
|
||
本阶段必须保持不变:
|
||
|
||
- 连击合并口径
|
||
- `customAnimationCount` 对数量展示的影响
|
||
- 幸运礼物奖励框逻辑
|
||
|
||
### 阶段 7:迁移 `seatFlight lane`
|
||
|
||
目标:
|
||
|
||
- 把飞向麦位从独立控制器改为统一 lane 管理
|
||
|
||
本阶段内容:
|
||
|
||
- 复用现有 `RoomGiftSeatFlightOverlay` 作为渲染层
|
||
- `queueTag/sessionKey` 的批量裁剪和清理迁入 engine
|
||
- 目标麦位 key 解析改成统一桥接
|
||
|
||
本阶段必须保持不变:
|
||
|
||
- 当前会话级去重逻辑
|
||
- 当前 `customAnimationCount` 粒度
|
||
- 当前图片预热行为
|
||
|
||
### 阶段 8:迁移 `floating lane`
|
||
|
||
目标:
|
||
|
||
- 收口房间礼物飘屏、幸运礼物飘屏、火箭、红包、游戏飘屏
|
||
|
||
本阶段内容:
|
||
|
||
- 房间内的 floating 类型全部走 `floating lane`
|
||
- 真正全局广播的类型与房间类型分开
|
||
- `OverlayManager` 只保留全局用法,房间类型从中剥离
|
||
|
||
本阶段必须保持不变:
|
||
|
||
- 幸运礼物 `globalNews` 逻辑不变
|
||
- 房间礼物 `coins > 9999` 才飘屏的逻辑不变
|
||
|
||
### 阶段 9:清理历史实现与补充自适应降级
|
||
|
||
目标:
|
||
|
||
- 删掉双轨实现
|
||
- 把当前“局部优化痕迹”收成统一结构
|
||
|
||
本阶段内容:
|
||
|
||
- 删除旧进场 helper
|
||
- 删除 `RTM/RTC` 中直接 `play()` 和 `addMessage()` 的旧入口
|
||
- 完成 backlog 降级策略与观测面板
|
||
|
||
---
|
||
|
||
## 关键兼容策略
|
||
|
||
### 1. 先换“控制面”,不先换“渲染面”
|
||
|
||
首轮不要把所有动画组件重写掉。
|
||
|
||
推荐策略:
|
||
|
||
- 新引擎先接管“什么时候播、播哪条、谁先谁后、是否丢弃”
|
||
- 旧组件先继续负责“怎么画出来”
|
||
|
||
这样做的好处:
|
||
|
||
- 视觉回归风险小
|
||
- 礼物逻辑更容易保持一致
|
||
- 每一阶段都可以独立回滚
|
||
|
||
### 2. 消息协议不动
|
||
|
||
当前房间消息、礼物消息、幸运礼物消息结构先不改。
|
||
|
||
这样可以:
|
||
|
||
- 避免前后端联动阻塞
|
||
- 避免把“性能问题”变成“协议问题”
|
||
|
||
### 3. 参数冻结后再迁移
|
||
|
||
建议先把以下参数冻结成一份配置文档或常量表:
|
||
|
||
- 幸运礼物里程碑列表
|
||
- 幸运礼物 burst 条件
|
||
- 房间礼物飘屏阈值
|
||
- 顶部礼物播报条槽位与时长
|
||
- 飞向麦位上限
|
||
- 进场队列上限
|
||
|
||
这样迁移时只改结构,不改结果。
|
||
|
||
---
|
||
|
||
## 风险点列表
|
||
|
||
| 风险点 | 说明 | 风险等级 | 缓解方式 |
|
||
| --- | --- | --- | --- |
|
||
| 本地事件与远端回流重复 | 自己送礼、自己进房、进场坐骑等容易双触发 | 高 | 引入 `dedupKey`,按 `roomId + type + sender + target + gift + timeWindow` 去重 |
|
||
| 去重过度导致特效漏播 | 某些高频连击若 key 设计过粗,会把应播事件吃掉 | 高 | 区分 `dedupKey` 和 `mergeKey`,先留日志,初期采用保守去重 |
|
||
| 房间退出后仍有旧特效回调 | `postFrame / delayed / timer / precache` 都可能晚到 | 高 | 所有 task 和 layer 引入 `sessionToken` 或 `roomLifecycleToken` 校验 |
|
||
| 渲染层迁移后层级错乱 | 根层迁到房间层后,z-order 可能改变 | 中 | 在 `RoomEffectStage` 中明确 layer 顺序,先对齐现有视觉层级 |
|
||
| 资源预热过多导致内存抖动 | 统一预热后若没有预算控制,容易顶高内存 | 中 | 按 lane、资源类型、尺寸做分层缓存和 LRU |
|
||
| 幸运礼物口径漂移 | 这块逻辑复杂,回归风险高 | 高 | 首轮冻结参数与触发口径,不做业务变更 |
|
||
| 飞向麦位目标丢失 | 目标座位 GlobalKey 尚未可用时可能丢动画 | 中 | 保留当前重试机制,但统一纳入 lane,限制重试次数 |
|
||
| 飘屏房间范围判断错误 | 房间飘屏与全局飘屏容易混 | 中 | 明确 `roomScoped` 与 `globalScoped` 两类事件 |
|
||
| 迁移阶段代码并存时间过长 | 双轨逻辑存在越久,后续越难收口 | 中 | 每一阶段都指定“完成后可删”的旧文件或旧入口 |
|
||
| 测试面过大 | 礼物、幸运礼物、火箭、红包、进场、坐骑交叉很多 | 高 | 用阶段化回归清单,按 lane 逐项验收 |
|
||
|
||
---
|
||
|
||
## 回归测试清单
|
||
|
||
### 礼物链路
|
||
|
||
- 单次送礼 1 次
|
||
- 同一礼物连续送 5 次、10 次
|
||
- `customAnimationCount > 1` 的批量场景
|
||
- 幸运礼物普通中奖
|
||
- 幸运礼物高倍率中奖
|
||
- 同时出现全屏礼物 + 顶部播报条 + 飞向麦位
|
||
|
||
### 进场链路
|
||
|
||
- 自己进入房间
|
||
- 别人进入房间
|
||
- 别人带坐骑进入房间
|
||
- 同时 5 人以上快速进入
|
||
|
||
### 飘屏链路
|
||
|
||
- 房间礼物飘屏
|
||
- 幸运礼物房间飘屏
|
||
- 全局广播幸运礼物飘屏
|
||
- 火箭飘屏
|
||
- 红包飘屏
|
||
|
||
### 生命周期
|
||
|
||
- 房间内最小化
|
||
- 退出房间
|
||
- 切到首页/消息/我的
|
||
- 从 A 房切到 B 房
|
||
|
||
### 性能
|
||
|
||
- 普通机型连续送礼 10 秒
|
||
- 低性能机型连续送礼 10 秒
|
||
- 进场和礼物叠加压测
|
||
- 房间内停留 10 分钟观察缓存与内存变化
|
||
|
||
---
|
||
|
||
## 验收指标建议
|
||
|
||
建议在重构过程中记录以下指标,避免“感觉快了”但没有客观标准:
|
||
|
||
- 语言房高压场景下 `FrameTiming` 的 `build/raster` P95
|
||
- `fullscreen lane` 平均等待时长
|
||
- `entrance/floating/seatFlight` backlog 峰值
|
||
- 被丢弃任务数量和占比
|
||
- 同事件重复触发数
|
||
- 房间退出后残留特效数
|
||
|
||
### 目标方向
|
||
|
||
- 房间高压场景下明显减少整页卡顿和掉帧
|
||
- 不再出现房间特效溢出到首页
|
||
- 不再依赖在多个管理器里各自补清理逻辑
|
||
- 新增一个特效类型时,只需要走“标准化事件 -> lane -> layer”链路
|
||
|
||
---
|
||
|
||
## 预计实施节奏
|
||
|
||
如果只重构语言房特效子系统,不推倒整套房间业务,建议节奏如下:
|
||
|
||
| 阶段 | 预估工作量 |
|
||
| --- | --- |
|
||
| 阶段 0 ~ 2 | 2 到 3 天 |
|
||
| 阶段 3 ~ 5 | 4 到 6 天 |
|
||
| 阶段 6 ~ 8 | 4 到 6 天 |
|
||
| 阶段 9 与回归 | 2 到 3 天 |
|
||
|
||
整体建议:
|
||
|
||
- 保守估计约 `2` 周
|
||
- 如果中途顺手一起重构 `RTC/RTM/RoomPage` 主业务结构,工期会明显变长
|
||
|
||
---
|
||
|
||
## 多线程并行执行方案
|
||
|
||
如果后续不是单线程顺着做,而是你准备把这次重构拆成多个并行线程分别跑,建议采用**“3 个主线程并行 + 1 个收尾线程串行收口”**的方案。
|
||
|
||
### 并行收益预估
|
||
|
||
按“最稳的预期”估算:
|
||
|
||
- 单线程完整落地:约 `20 到 24` 小时
|
||
- 多线程并行后:约 `14 到 16` 小时
|
||
|
||
这里的前提是:
|
||
|
||
- 先冻结接口命名和模块边界
|
||
- 尽量避免多个线程同时改同一批现有文件
|
||
- 最后仍保留一个统一收尾和回归线程
|
||
|
||
不建议的并行方式:
|
||
|
||
- 不建议 3 个线程同时直接改 `rtm_manager.dart / rtc_manager.dart / voice_room_page.dart`
|
||
- 不建议一边改 engine,一边让多个线程各自发明自己的事件模型
|
||
- 不建议一开始就多个线程同时删旧链路
|
||
|
||
### 并行原则
|
||
|
||
1. 先统一接口,再并行开发
|
||
至少先约定好以下对象名和职责:
|
||
|
||
- `RoomEffectEvent`
|
||
- `RoomEffectTask`
|
||
- `RoomEffectLane`
|
||
- `RoomEffectEngine`
|
||
- `RoomEffectStage`
|
||
- `RoomEffectEventAdapter`
|
||
|
||
2. 新文件优先并行,旧文件尽量收口到单一线程
|
||
新增模块最适合多线程拆分;旧有入口文件应尽量减少多人同时修改。
|
||
|
||
3. 先搭骨架,再接现有链路
|
||
先让 engine 与 stage 的框架稳定,再开始把 `RTM/RTC/VoiceRoomPage` 接上去。
|
||
|
||
4. 收尾一定要串行
|
||
旧链路删除、层级调整、房间生命周期回收、回归修 bug,这些必须收口到最后一个线程统一处理。
|
||
|
||
---
|
||
|
||
### 推荐线程划分
|
||
|
||
#### 线程 A:Engine 基础层
|
||
|
||
定位:
|
||
|
||
- 负责“语言房特效引擎”的核心模型与调度骨架
|
||
|
||
推荐负责范围:
|
||
|
||
- `lib/modules/room_effect/domain/`
|
||
- `lib/modules/room_effect/application/`
|
||
- `lib/modules/room_effect/infrastructure/` 中和调度、预热、生命周期守卫相关的文件
|
||
|
||
推荐文件所有权:
|
||
|
||
- `room_effect_event.dart`
|
||
- `room_effect_task.dart`
|
||
- `room_effect_lane.dart`
|
||
- `room_effect_priority.dart`
|
||
- `room_effect_engine.dart`
|
||
- `room_effect_policy.dart`
|
||
- `room_effect_deduplicator.dart`
|
||
- `room_effect_merger.dart`
|
||
- `room_effect_metrics.dart`
|
||
- `room_effect_asset_preloader.dart`
|
||
- `room_effect_lifecycle_guard.dart`
|
||
|
||
本线程不建议碰:
|
||
|
||
- `rtm_manager.dart`
|
||
- `rtc_manager.dart`
|
||
- `voice_room_page.dart`
|
||
- 现有具体动画 widget 的渲染实现
|
||
|
||
本线程交付物:
|
||
|
||
- 可编译的 engine/domain/application 基础骨架
|
||
- lane、task、event、policy 的统一接口
|
||
- 基本的 backlog、优先级、去重、合并接口
|
||
|
||
#### 线程 B:Stage 与渲染承载层
|
||
|
||
定位:
|
||
|
||
- 负责“语言房特效舞台”和各 layer 的承载结构
|
||
|
||
推荐负责范围:
|
||
|
||
- `lib/modules/room_effect/presentation/`
|
||
|
||
推荐文件所有权:
|
||
|
||
- `room_effect_stage.dart`
|
||
- `layers/room_fullscreen_effect_layer.dart`
|
||
- `layers/room_entrance_effect_layer.dart`
|
||
- `layers/room_gift_ticker_layer.dart`
|
||
- `layers/room_seat_flight_layer.dart`
|
||
- `layers/room_floating_effect_layer.dart`
|
||
- 如果需要,可新增 presentation 侧 controller 或 snapshot 文件
|
||
|
||
本线程可复用但不建议大改的旧文件:
|
||
|
||
- `lib/ui_kit/widgets/room/anim/l_gift_animal_view.dart`
|
||
- `lib/ui_kit/widgets/room/anim/room_entrance_screen.dart`
|
||
- `lib/ui_kit/widgets/room/anim/room_gift_seat_flight_overlay.dart`
|
||
- `lib/ui_kit/widgets/room/effect/vapp_svga_layer_widget.dart`
|
||
|
||
建议策略:
|
||
|
||
- 先做 layer 容器和接口桥接,不先重写底层动画实现
|
||
- 首轮允许通过 bridge 调现有 renderer
|
||
|
||
本线程不建议碰:
|
||
|
||
- `rtm_manager.dart`
|
||
- `rtc_manager.dart`
|
||
- `main.dart`
|
||
|
||
本线程交付物:
|
||
|
||
- `RoomEffectStage`
|
||
- 各特效 layer 的挂载顺序和层级
|
||
- 渲染层与 engine 的消费接口
|
||
|
||
#### 线程 C:旧链路入口收口与适配层
|
||
|
||
定位:
|
||
|
||
- 负责把现有 `RTM/RTC/VoiceRoomPage/GiftSystem` 的特效触发点统一改成事件投递
|
||
|
||
推荐负责范围:
|
||
|
||
- `lib/services/audio/rtm_manager.dart`
|
||
- `lib/services/audio/rtc_manager.dart`
|
||
- `lib/modules/room/voice_room_page.dart`
|
||
- `lib/services/gift/gift_system_manager.dart`
|
||
- 适配层文件,例如:
|
||
- `lib/modules/room_effect/application/room_effect_event_adapter.dart`
|
||
- 或单独的 bridge/wiring 文件
|
||
|
||
本线程重点任务:
|
||
|
||
- 找出所有直接 `play()`、`OverlayManager().addMessage()`、直接入队的触发点
|
||
- 改为投递标准化事件
|
||
- 做本地触发和远端回流的去重 key
|
||
- 保持现有礼物业务逻辑不变
|
||
|
||
本线程不建议碰:
|
||
|
||
- `RoomEffectStage` 内部结构
|
||
- 各 layer 的视觉实现
|
||
- 大量历史文件删除
|
||
|
||
本线程交付物:
|
||
|
||
- 统一事件入口
|
||
- `RTM/RTC` 不再直接知道“怎么播动画”
|
||
- 旧业务逻辑已迁到 adapter/event 侧表达
|
||
|
||
#### 线程 D:收尾、清理与回归
|
||
|
||
定位:
|
||
|
||
- 在 A/B/C 合并后串行执行,不建议一开始就启动
|
||
|
||
推荐负责范围:
|
||
|
||
- `lib/main.dart`
|
||
- `lib/modules/room/voice_room_page.dart`
|
||
- `lib/shared/data_sources/sources/local/floating_screen_manager.dart`
|
||
- `lib/shared/tools/sc_room_effect_scheduler.dart`
|
||
- `lib/ui_kit/widgets/room/anim/room_entrance_widget.dart`
|
||
- 其它需要删除旧逻辑、改挂载层级、补回归日志的文件
|
||
|
||
本线程重点任务:
|
||
|
||
- 把房间专属特效层从根层尽量收回房间页内部
|
||
- 清理旧进场 helper 和双轨实现
|
||
- 清理失效入口
|
||
- 跑一轮回归,修明显联调问题
|
||
|
||
---
|
||
|
||
### 最稳的执行顺序
|
||
|
||
推荐按下面顺序跑,而不是 4 个线程同时无序开工。
|
||
|
||
#### 第 1 步:接口冻结
|
||
|
||
由任意一个线程先完成,建议控制在 `0.5 到 1` 小时内。
|
||
|
||
需要先冻结的内容:
|
||
|
||
- `RoomEffectEvent` 字段
|
||
- `RoomEffectTask` 字段
|
||
- lane 列表
|
||
- engine 对外接口
|
||
- stage 对外接口
|
||
- adapter 的输入输出职责
|
||
|
||
这一步完成后,线程 A/B/C 才正式并行。
|
||
|
||
#### 第 2 步:A/B/C 三线程并行
|
||
|
||
并行期间建议这样分:
|
||
|
||
- A 只写 engine/domain/application 基础层
|
||
- B 只写 stage/presentation/layer 容器
|
||
- C 只写现有旧入口收口和事件适配
|
||
|
||
并行期间禁止多人同时改的文件:
|
||
|
||
- `lib/services/audio/rtm_manager.dart`
|
||
- `lib/services/audio/rtc_manager.dart`
|
||
- `lib/modules/room/voice_room_page.dart`
|
||
- `lib/main.dart`
|
||
|
||
这些文件建议默认归线程 C 或线程 D 所有。
|
||
|
||
#### 第 3 步:先合 A,再合 B,再合 C
|
||
|
||
推荐合并顺序:
|
||
|
||
1. 先合线程 A
|
||
先把 engine 和模型定住,避免后续接口继续漂。
|
||
|
||
2. 再合线程 B
|
||
让 stage 和 layer 按 A 的接口接上。
|
||
|
||
3. 再合线程 C
|
||
最后把现有旧入口真正接到 engine 上。
|
||
|
||
这样做的原因:
|
||
|
||
- 可以最大程度减少 `A/B/C` 三方接口对撞
|
||
- 让旧链路接入时面对的是已经稳定的 engine 与 stage
|
||
|
||
#### 第 4 步:最后由线程 D 串行收尾
|
||
|
||
线程 D 再做这些事:
|
||
|
||
- 根层与房间层的挂载调整
|
||
- 删除旧实现
|
||
- 生命周期清理
|
||
- 房间退出和切房回归
|
||
- backlog 观测和简单日志补充
|
||
|
||
---
|
||
|
||
### 推荐的文件写入边界
|
||
|
||
为了减少合并冲突,建议严格遵守下面的写入边界。
|
||
|
||
| 线程 | 允许重点修改 | 尽量不要修改 |
|
||
| --- | --- | --- |
|
||
| A | `lib/modules/room_effect/domain/`、`application/`、`infrastructure/` 新文件 | 现有房间业务文件 |
|
||
| B | `lib/modules/room_effect/presentation/` 新文件 | `rtm_manager.dart`、`rtc_manager.dart` |
|
||
| C | `rtm_manager.dart`、`rtc_manager.dart`、`voice_room_page.dart`、`gift_system_manager.dart`、adapter | `main.dart`、presentation 新文件的大结构 |
|
||
| D | `main.dart`、旧 helper 清理、根层挂载调整、遗留逻辑删除 | engine 核心接口 |
|
||
|
||
---
|
||
|
||
### 推荐的并行里程碑
|
||
|
||
建议把并行执行拆成 4 个里程碑,每个里程碑都要求能单独检查。
|
||
|
||
#### 里程碑 1
|
||
|
||
- A:engine/domain/task/lane 骨架完成
|
||
- B:stage/layer 容器骨架完成
|
||
- C:梳理所有旧触发点,列出事件映射表
|
||
|
||
#### 里程碑 2
|
||
|
||
- A:lane 调度、去重、合并接口可用
|
||
- B:各 layer 能消费统一 task 或 snapshot
|
||
- C:`RTM/RTC/VoiceRoomPage` 已改成先投递事件,不直接触发动画
|
||
|
||
#### 里程碑 3
|
||
|
||
- A/B:engine 与 stage 已能联通
|
||
- C:主要礼物/进场/飘屏入口已接新引擎
|
||
|
||
#### 里程碑 4
|
||
|
||
- D:旧链路清理、房间退出/切房回归完成
|
||
|
||
---
|
||
|
||
### 不同并行强度的建议
|
||
|
||
#### 最稳方案
|
||
|
||
- `3` 个主线程并行
|
||
- `1` 个收尾线程后置串行
|
||
|
||
适用场景:
|
||
|
||
- 你希望尽量减少互相撞车
|
||
- 你更在意一次性落稳,而不是追求最短墙钟时间
|
||
|
||
#### 更激进方案
|
||
|
||
- `4` 个线程一开始就并行
|
||
|
||
不太建议,原因是:
|
||
|
||
- `main.dart`
|
||
- `voice_room_page.dart`
|
||
- `rtm_manager.dart`
|
||
- `rtc_manager.dart`
|
||
|
||
这些文件很容易形成高冲突区,激进并行最后未必更快。
|
||
|
||
---
|
||
|
||
### 最终建议的并行执行口径
|
||
|
||
如果你后面准备“分别跑”多个线程,建议直接按下面口径执行:
|
||
|
||
- 线程 A:只做 engine/domain/application 基础层
|
||
- 线程 B:只做 stage/presentation/layer 承载层
|
||
- 线程 C:只做旧入口收口和事件适配
|
||
- 线程 D:最后统一清理旧链路、改挂载、跑回归
|
||
|
||
这是这次语言房特效系统重构里,**最稳、最不容易互相打架**的一种并行拆法。
|
||
|
||
---
|
||
|
||
## 可直接投喂 Codex 的执行 Prompts
|
||
|
||
下面这组 prompt 是给你后面开多个 Codex 线程时直接复制使用的。
|
||
|
||
推荐使用方式:
|
||
|
||
1. 先把“公共约定 prompt”复制给每个线程
|
||
2. 再把对应线程的专属 prompt 追加给该线程
|
||
3. 线程 D 不要一开始就跑,等 A/B/C 合并后再启动
|
||
|
||
### 公共约定 Prompt
|
||
|
||
建议把下面这段先发给每一个线程,作为共同上下文。
|
||
|
||
```text
|
||
你现在在 /Users/nigger/Documents/GitHub/chatapp3-flutter 仓库中工作。
|
||
|
||
当前目标不是重构整个语言房,而是优先重构“语言房特效子系统”。
|
||
请先阅读根目录文档:
|
||
- /Users/nigger/Documents/GitHub/chatapp3-flutter/语言房重构.md
|
||
|
||
本轮必须遵守这些硬约束:
|
||
|
||
1. 尽量保留当前礼物业务逻辑、阈值和视觉表现,不要擅自改业务口径。
|
||
2. 不要改房间消息协议,不要改后端接口。
|
||
3. 只在你被授权的写入范围内改文件,不要碰其它线程的主要负责文件。
|
||
4. 不要大规模格式化无关文件,不要顺手重构无关模块。
|
||
5. 如果你发现某个接口命名需要和其它线程统一,请优先遵守本文档中的统一命名,不要自行发明第二套。
|
||
|
||
统一命名约定如下:
|
||
|
||
- RoomEffectEvent
|
||
- RoomEffectTask
|
||
- RoomEffectLane
|
||
- RoomEffectPriority
|
||
- RoomEffectEngine
|
||
- RoomEffectPolicy
|
||
- RoomEffectEventAdapter
|
||
- RoomEffectStage
|
||
|
||
统一 lane 命名如下:
|
||
|
||
- fullscreen
|
||
- entrance
|
||
- giftTicker
|
||
- seatFlight
|
||
- floating
|
||
|
||
必须保留的当前关键逻辑:
|
||
|
||
- 顶部礼物播报条继续保持 4 个槽位
|
||
- 顶部礼物播报条待播上限继续按当前 24 处理
|
||
- 顶部礼物播报条空闲消失时长继续保持约 3200ms
|
||
- 飞向麦位继续保留 customAnimationCount 的粒度逻辑
|
||
- 幸运礼物 burst 继续保持“倍率 >= 10x 或单次奖励 > 5000”
|
||
- 房间礼物飘屏继续保持 coins > 9999 才触发
|
||
- 继续沿用当前特效总开关:
|
||
- SCGlobalConfig.isGiftSpecialEffects
|
||
- SCGlobalConfig.isEntryVehicleAnimation
|
||
- SCGlobalConfig.isFloatingAnimationInGlobal
|
||
|
||
完成后请在回复中明确给出:
|
||
|
||
- 改了哪些文件
|
||
- 你负责范围内已经完成了什么
|
||
- 还依赖其它线程提供什么
|
||
- 有没有你刻意没有动的风险点
|
||
```
|
||
|
||
---
|
||
|
||
### 线程 A Prompt
|
||
|
||
用途:
|
||
|
||
- 负责 engine/domain/application 基础层
|
||
|
||
直接投喂:
|
||
|
||
```text
|
||
在遵守“公共约定 prompt”的前提下,执行线程 A 的任务。
|
||
|
||
你的职责是:搭建语言房特效系统的 engine/domain/application 基础层,只负责新架构骨架,不接老的 RTM/RTC 入口,不改现有房间业务触发逻辑。
|
||
|
||
你的主要目标:
|
||
|
||
1. 新增语言房特效模块基础目录,优先建议放在:
|
||
- lib/modules/room_effect/domain/
|
||
- lib/modules/room_effect/application/
|
||
- lib/modules/room_effect/infrastructure/
|
||
|
||
2. 建立并实现以下核心对象或等价对象:
|
||
- RoomEffectEvent
|
||
- RoomEffectTask
|
||
- RoomEffectLane
|
||
- RoomEffectPriority
|
||
- RoomEffectPolicy
|
||
- RoomEffectEngine
|
||
- RoomEffectDeduplicator
|
||
- RoomEffectMerger
|
||
- RoomEffectMetrics
|
||
- RoomEffectLifecycleGuard
|
||
- RoomEffectAssetPreloader
|
||
|
||
3. engine 至少要具备这些能力:
|
||
- 接收 RoomEffectEvent
|
||
- 转换为 RoomEffectTask
|
||
- 按 lane 分发
|
||
- 基于 priority 排序
|
||
- 留出去重、合并、丢弃、延后、独占的接口
|
||
- 能产出每个 lane 的可消费状态或流
|
||
|
||
4. 你可以先做“最小可用骨架”,但不要只写空壳文件。至少保证接口能表达真实系统能力。
|
||
|
||
5. 你需要把这些现有业务约束沉淀到 policy 或 constants 中,但不要改业务阈值:
|
||
- giftTicker 4 槽位
|
||
- pending 24
|
||
- idle dismiss 3200ms
|
||
- burst 条件
|
||
- 飘屏阈值
|
||
|
||
你的明确写入范围:
|
||
|
||
- 可以新增和修改:
|
||
- lib/modules/room_effect/domain/**
|
||
- lib/modules/room_effect/application/**
|
||
- lib/modules/room_effect/infrastructure/**
|
||
|
||
你的禁止写入范围:
|
||
|
||
- 不要修改:
|
||
- lib/services/audio/rtm_manager.dart
|
||
- lib/services/audio/rtc_manager.dart
|
||
- lib/modules/room/voice_room_page.dart
|
||
- lib/main.dart
|
||
- lib/ui_kit/widgets/room/** 的现有渲染文件
|
||
|
||
你的产出要求:
|
||
|
||
- 代码必须可编译到“接口层面合理”
|
||
- 保持命名稳定,方便线程 B/C 对接
|
||
- 在最终回复里列出你定义的关键接口签名和文件列表
|
||
```
|
||
|
||
---
|
||
|
||
### 线程 B Prompt
|
||
|
||
用途:
|
||
|
||
- 负责 `RoomEffectStage` 和 presentation/layer 承载层
|
||
|
||
直接投喂:
|
||
|
||
```text
|
||
在遵守“公共约定 prompt”的前提下,执行线程 B 的任务。
|
||
|
||
你的职责是:搭建语言房特效系统的 presentation/stage/layer 承载层,重点是把房间内特效层级和挂载方式结构化,但不要改旧的 RTM/RTC 入口文件。
|
||
|
||
你的主要目标:
|
||
|
||
1. 新增 presentation 模块,优先建议放在:
|
||
- lib/modules/room_effect/presentation/
|
||
- lib/modules/room_effect/presentation/layers/
|
||
|
||
2. 建立并实现以下对象或等价对象:
|
||
- RoomEffectStage
|
||
- RoomFullscreenEffectLayer
|
||
- RoomEntranceEffectLayer
|
||
- RoomGiftTickerEffectLayer
|
||
- RoomSeatFlightEffectLayer
|
||
- RoomFloatingEffectLayer
|
||
|
||
3. 你的核心任务不是重写所有动画,而是先把“承载层和消费接口”搭好。首轮允许通过 bridge 复用现有 renderer。
|
||
|
||
4. 你需要明确 layer 的顺序和职责,建议至少能表达:
|
||
- fullscreen 层
|
||
- entrance 层
|
||
- giftTicker 层
|
||
- seatFlight 层
|
||
- floating 层
|
||
|
||
5. 你可以为每个 layer 设计最小消费接口,例如:
|
||
- 接收 RoomEffectTask 列表
|
||
- 接收 lane snapshot
|
||
- 接收 controller / stream / notifier
|
||
|
||
6. 你需要尽量让 stage 未来可以挂回 VoiceRoomPage 内部,但本线程先不要大改 voice_room_page.dart。
|
||
|
||
你的明确写入范围:
|
||
|
||
- 可以新增和修改:
|
||
- lib/modules/room_effect/presentation/**
|
||
|
||
- 如有必要,可少量只读参考:
|
||
- lib/ui_kit/widgets/room/anim/l_gift_animal_view.dart
|
||
- lib/ui_kit/widgets/room/anim/room_entrance_screen.dart
|
||
- lib/ui_kit/widgets/room/anim/room_gift_seat_flight_overlay.dart
|
||
- lib/ui_kit/widgets/room/effect/vapp_svga_layer_widget.dart
|
||
|
||
你的禁止写入范围:
|
||
|
||
- 不要修改:
|
||
- lib/services/audio/rtm_manager.dart
|
||
- lib/services/audio/rtc_manager.dart
|
||
- lib/modules/room/voice_room_page.dart
|
||
- lib/main.dart
|
||
- 线程 A 负责的新 engine/domain/application 文件
|
||
|
||
你的产出要求:
|
||
|
||
- 交付可读、清晰的 RoomEffectStage 与 layer 结构
|
||
- 接口命名必须对齐公共约定
|
||
- 在最终回复中列出:
|
||
- 你新增的 layer 文件
|
||
- 每个 layer 准备承接哪类任务
|
||
- 还依赖线程 A 提供哪些 engine 能力
|
||
```
|
||
|
||
---
|
||
|
||
### 线程 C Prompt
|
||
|
||
用途:
|
||
|
||
- 负责旧链路入口收口与适配层
|
||
|
||
直接投喂:
|
||
|
||
```text
|
||
在遵守“公共约定 prompt”的前提下,执行线程 C 的任务。
|
||
|
||
你的职责是:把现有语言房中的特效触发入口统一改成“事件投递”,尽量不碰 presentation 层结构,不大改渲染细节。
|
||
|
||
你的主要目标:
|
||
|
||
1. 梳理并改造当前这些文件里的直接特效触发点:
|
||
- lib/services/audio/rtm_manager.dart
|
||
- lib/services/audio/rtc_manager.dart
|
||
- lib/modules/room/voice_room_page.dart
|
||
- lib/services/gift/gift_system_manager.dart
|
||
|
||
2. 找出以下直接触发方式并逐步收口:
|
||
- 直接 SCGiftVapSvgaManager().play(...)
|
||
- 直接 OverlayManager().addMessage(...)
|
||
- 直接往独立动画队列入队
|
||
- 页面层自己拼装多个动画副作用
|
||
|
||
3. 你的目标不是立刻删掉所有旧逻辑,而是先把“触发入口”统一改成投递 RoomEffectEvent 或等价事件。
|
||
|
||
4. 你需要新增或实现适配层,建议命名:
|
||
- RoomEffectEventAdapter
|
||
- 或等价 wiring 文件
|
||
|
||
5. 需要特别注意去重场景:
|
||
- 本地自己进房 + 群消息回流
|
||
- 本地自己送礼 + 房间消息回流
|
||
- 幸运礼物广播 + 房间群消息双到达
|
||
|
||
6. 必须保留当前礼物业务判断口径,不要修改:
|
||
- 哪些礼物触发全屏
|
||
- 哪些礼物触发飘屏
|
||
- customAnimationCount 的使用方式
|
||
- 幸运礼物 burst 口径
|
||
|
||
你的明确写入范围:
|
||
|
||
- 可以修改:
|
||
- lib/services/audio/rtm_manager.dart
|
||
- lib/services/audio/rtc_manager.dart
|
||
- lib/modules/room/voice_room_page.dart
|
||
- lib/services/gift/gift_system_manager.dart
|
||
|
||
- 可以新增:
|
||
- lib/modules/room_effect/application/room_effect_event_adapter.dart
|
||
- 或你认为更合适但职责清晰的 adapter/binding 文件
|
||
|
||
你的禁止写入范围:
|
||
|
||
- 不要修改:
|
||
- lib/main.dart
|
||
- lib/modules/room_effect/presentation/**
|
||
- 线程 B 新增的 stage/layer 文件
|
||
- 线程 A 的核心 engine 接口定义
|
||
|
||
你的产出要求:
|
||
|
||
- 最终让旧入口尽量先统一走事件
|
||
- 即便 engine/stage 还未最终接上,也要把触发点收成统一入口
|
||
- 在最终回复中列出:
|
||
- 你改掉了哪些直接触发点
|
||
- 每类事件映射成了什么 RoomEffectEvent
|
||
- 哪些旧逻辑你暂时保留未删
|
||
```
|
||
|
||
---
|
||
|
||
### 线程 D Prompt
|
||
|
||
用途:
|
||
|
||
- A/B/C 合并后再启动,负责收尾、清理、挂载调整与回归
|
||
|
||
直接投喂:
|
||
|
||
```text
|
||
在遵守“公共约定 prompt”的前提下,执行线程 D 的任务。
|
||
|
||
注意:这个线程默认在 A/B/C 已经完成并合并后再启动。你的职责是最终收尾,不是从零搭骨架。
|
||
|
||
你的主要目标:
|
||
|
||
1. 把房间专属特效层尽量从根层收回房间内部,重点检查:
|
||
- lib/main.dart
|
||
- lib/modules/room/voice_room_page.dart
|
||
|
||
2. 清理旧双轨逻辑,重点检查:
|
||
- lib/ui_kit/widgets/room/anim/room_entrance_widget.dart
|
||
- 旧的 RoomEntranceHelper 残留引用
|
||
- 不再需要的直接 play/addMessage 入口
|
||
|
||
3. 统一房间生命周期清理:
|
||
- 退房
|
||
- 最小化
|
||
- 切房
|
||
- 房间页面销毁
|
||
|
||
4. 回归以下重点问题:
|
||
- 房间退出后礼物图或飞向麦位残留
|
||
- 房间飘屏跑到首页
|
||
- 进场与全屏特效并发时队列紊乱
|
||
- 幸运礼物逻辑回归
|
||
|
||
5. 你可以少量补充 metrics 或 debug log,但不要无限制新增临时调试代码。
|
||
|
||
你的明确写入范围:
|
||
|
||
- 可以修改:
|
||
- lib/main.dart
|
||
- lib/modules/room/voice_room_page.dart
|
||
- lib/shared/data_sources/sources/local/floating_screen_manager.dart
|
||
- lib/shared/tools/sc_room_effect_scheduler.dart
|
||
- lib/ui_kit/widgets/room/anim/room_entrance_widget.dart
|
||
- 其它你确认属于“旧链路清理 / 最终挂载调整 / 生命周期回收”的文件
|
||
|
||
你的禁止写入范围:
|
||
|
||
- 不要推翻线程 A 已经稳定的 engine 核心接口
|
||
- 不要重命名线程 B 已经落好的 presentation 核心对象
|
||
- 不要重新发明第二套 adapter 或第二套 lane
|
||
|
||
你的产出要求:
|
||
|
||
- 最终让新链路成为主链路
|
||
- 旧链路要么删除,要么明确降级成只读残留且不再实际生效
|
||
- 在最终回复中列出:
|
||
- 你清理了哪些旧链路
|
||
- 你改了哪些根层/房间层挂载
|
||
- 你验证了哪些关键回归项
|
||
- 你认为还剩哪些适合下一轮继续处理
|
||
```
|
||
|
||
---
|
||
|
||
### 推荐投喂顺序
|
||
|
||
建议你后面实际开线程时,按这个顺序喂:
|
||
|
||
1. 给 A、B、C 同时发送“公共约定 prompt”
|
||
2. 再分别发送各自的线程 prompt
|
||
3. 等 A、B、C 产出后完成合并
|
||
4. 最后再给 D 发送“公共约定 prompt + 线程 D prompt”
|
||
|
||
### 推荐合并检查点
|
||
|
||
在真正合并 A/B/C 前,建议你人工确认一次这几个点:
|
||
|
||
- A 的对象命名和 B/C 预期一致
|
||
- B 的 stage/layer 命名没有另起炉灶
|
||
- C 没有继续保留太多直接 `play()` 或 `addMessage()` 的新入口
|
||
- 三边没有同时改坏 `voice_room_page.dart / rtm_manager.dart / rtc_manager.dart`
|
||
|
||
---
|
||
|
||
## 最终建议
|
||
|
||
最终建议仍然是:
|
||
|
||
- **先重构语言房特效系统**
|
||
- **不先重构整套语言房**
|
||
- **先统一入口、统一引擎、统一生命周期**
|
||
- **尽量保留当前礼物业务逻辑与视觉**
|
||
|
||
这样做的收益最大,风险也最可控。
|
||
|
||
如果后续按本文实施,建议第一步从“阶段 0 + 阶段 1”开始,即:
|
||
|
||
- 先建事件模型和指标
|
||
- 再把所有直接触发动画的入口统一改成投递事件
|
||
|
||
一旦这一步完成,后面每条特效链路都可以逐条平滑迁移,不需要再一次性大改整间房。
|