RYZZ-项目技术评审与改进建议
基于对 RYZZ 全栈代码的深度审查(site 主站、admin 管理后台、public/binding/landPages H5 页面),从工程质量、架构设计、性能、安全、可维护性等维度进行全面评估。
一、做得好的地方
1.1 Monorepo 架构设计优秀
- Turborepo + PNPM Workspace 的组合成熟稳定,
turbo prune --docker精准裁剪 Monorepo 构建上下文,Docker 多阶段构建充分利用层缓存 - 8 个公共包(constant、function、hooks、types、contract、login、dauth-web、report-sdk)按职责清晰划分,跨应用复用率高
workspace:*统一版本管理,避免内部包版本漂移- Turbo Remote Cache 跨 CI 运行共享构建缓存,显著减少重复构建时间
1.2 WebSocket 通讯系统设计精妙
三层架构(连接管理 → Protobuf 编解码 → 两级事件路由)是整个项目最精巧的设计,新增业务消息类型无需修改传输层和编解码逻辑,开闭原则体现到位。
- 三层架构(连接管理 → Protobuf 编解码 → 两级事件路由)职责清晰,解耦彻底
- Protobuf 二进制编码替代 JSON,带宽节省约 50%,90+ 个 proto 定义覆盖全业务
- 两级事件路由是整个系统最精巧的设计——服务端统一通过
comment通道下发所有互动消息,客户端按type字段二次分发,实现了「单通道多业务」的优雅解耦 MessageBatch批量消息协议降低小消息的网络开销useSubscription/useMsgPushSubscription自定义 Hook 封装 WS 订阅,业务组件使用简洁
1.3 Web3 集成体系完整且创新
DAuth MPC 钱包(2-of-3 分片)+ EIP-4337 账户抽象是整个项目最具技术深度的亮点,完整实现了「Web3 无感化」。
- DAuth MPC 钱包(2-of-3 分片)+ EIP-4337 账户抽象是整个项目最具技术深度的亮点,让用户通过邮箱/手机号即可拥有链上钱包
- GG20 多方计算签名协议、密钥恢复机制、Relayer 代提交交易,完整实现了「Web3 无感化」
- 支持 MetaMask/WalletConnect + DAuth + Google/Twitter 三种登录方式,覆盖面广
TransactionReceiverWrapper 统一处理链上交易签名和确认,避免各业务模块重复实现
1.4 TypeScript 严格模式
- 基础 tsconfig 配置了
strict: true,从源头保障类型安全 - Recoil Atom 全部有明确的泛型类型约束和合理的默认值
- WebSocket 协议路径使用枚举(
EReqProtocolPath/ERspProtocolPath)而非魔术字符串
1.5 状态管理设计合理
- Recoil 原子化状态天然适合直播场景中「频道切换时部分状态重置、部分状态保留」的需求
- 17+ 个 Atom 按业务域严格分离(currency、channel、betting、gift 等),每个 Atom 独立订阅避免不必要的重渲染
- 15 层 Provider 嵌套顺序经过精心设计(WagmiConfig → Auth → WebSocket → Currency → Betting),层级间依赖关系清晰
1.6 CI/CD 与部署
- Docker 四阶段构建 + Next.js Standalone 输出,最终镜像体积小
- 非 Root 用户运行容器,符合安全最佳实践
- Kustomize 多环境 Overlay(test / production)配置管理规范
- PM2 进程管理提供守护和优雅重启
1.7 跨应用第三方登录架构
- binding 应用作为 OAuth 回调中转站,职责单一且解耦
- 双通道回调(postMessage 主通道 + Cookie/Focus 降级通道)保障跨浏览器兼容性
- 四层 Token 交换链路设计严谨,PKCE 防止授权码截获攻击
1.8 其他亮点
- 阿里云 Aliplayer 帧追赶 + 倍速播放的低延迟直播方案
useMemoizedFn(ahooks)避免回调函数重建导致的不必要渲染- 三平台埋点(GA + Facebook Pixel + Twitter Conversion)同时上报
PageTime工具记录用户页面停留时长BigNumber处理链上 Token 余额,避免 JS 浮点精度问题- VConsole 仅在
isDebug && isMobile时动态导入,不影响生产包体积
二、改进建议
2.1 【紧急】安全凭证泄露
代码库中存在多处硬编码的安全凭证,需要立即轮换所有已泄露的凭证并迁移到环境变量。
现状:代码库中存在多处硬编码的安全凭证:
| 文件 | 泄露内容 | 严重程度 |
|---|---|---|
.sentryclirc | Sentry Auth Token(sntrys_eyJ...) | 极高 — 可操作 Sentry 项目 |
src/config/constants/common.ts | WalletConnect Project ID,且生产/测试使用同一个值 | 高 |
sentry.client.config.ts | Sentry DSN 硬编码 | 中 |
风险:
.sentryclirc中的 Sentry Auth Token 可被任何有代码读取权限的人用来上传恶意 sourcemap、修改项目配置- WalletConnect ID 泄露且生产/测试环境完全相同(
isProduction ? A : A三元表达式无效)
建议:
- 立即轮换所有已泄露的凭证
- 将凭证迁移到环境变量(
process.env.NEXT_PUBLIC_*/ CI secrets) - 将
.sentryclirc添加到.gitignore - 使用
git-secrets或gitleaks扫描防止未来凭证提交
2.2 【紧急】target="_blank" 安全风险(41 处)
现状:41 处 <a target="_blank"> 缺少 rel="noopener noreferrer" 属性,且有 2 处 window.open(userCustomUrl) 直接打开用户提交的 URL 无校验。
风险:
- 外部页面可通过
window.opener操作原始页面(反向点击劫持) - 用户提交
javascript:协议 URL 可导致 XSS
建议:
- 全局搜索
target="_blank"添加rel="noopener noreferrer" window.open(url)前校验 URL 必须以https://开头- 安装
eslint-plugin-react启用react/jsx-no-target-blank规则
2.3 【严重】零测试覆盖
整个项目没有任何测试覆盖,复杂的 Web3 交易逻辑一旦出错可能导致资金损失,WebSocket 核心模块也无测试保护。
现状:整个 RYZZ 项目(site、admin、public、binding、landPages)没有任何单元测试、集成测试或 E2E 测试文件。
风险:
- 每次代码修改都是「盲改」,无法确认是否引入回归 Bug
- 复杂的 Web3 交易逻辑(DAuth MPC 签名、合约调用)一旦出错可能导致资金损失
- WebSocket 消息解析、Protobuf 编解码等核心模块无测试保护
建议:
- 优先级最高:为 DAuth MPC 钱包模块(
packages/dauth-web)添加单元测试,覆盖密钥生成、恢复、签名、交易执行等核心路径 - 为 WebSocket 通讯模块(
src/utils/websocket/)添加测试,覆盖连接管理、重连、消息队列缓冲、Protobuf 编解码 - 为
packages/login的useAuthHook 添加测试,覆盖三种登录流程 - 引入 Vitest 或 Jest 作为测试框架,配合 React Testing Library 测试组件
- 在 CI 中添加测试覆盖率门槛(建议初始目标 30%,逐步提高)
2.2 【严重】WebSocket 重连策略不健壮
现状:core.ts 使用固定 5 秒延迟重连,无指数退避、无最大重试次数限制、无网络状态检测。
风险:
- 服务器宕机时客户端每 5 秒发起一次重连请求,大量用户同时重连形成惊群效应(thundering herd),可能压垮恢复中的服务器
- 无最大重试次数限制,用户在无网络环境下电池持续消耗
isReconnectionLoading标志位虽然防止并发重连,但缺乏防抖保护
建议:
实现指数退避 + 抖动(Jitter)重连策略:
- 初始延迟:1 秒
- 最大延迟:60 秒
- 计算公式:delay = min(maxDelay, baseDelay * 2^attempt + random(0, 1000)ms)
- 最大重试次数:20 次(超过后停止重连,提示用户手动刷新)
- 监听 navigator.onLine / online 事件,离线时暂停重连,上线时立即触发
- 监听 document.visibilitychange,页面切到后台时降低重连频率
2.3 【重要】全局错误响应拦截器被注释
现状:src/utils/request.ts 中的全局错误响应拦截器被注释掉(// if (data.show) {),导致:
- 部分 API 错误静默失败,用户无感知
WebSocket/index.tsx:62行data.show检查也被注释,所有 push 消息都会触发message.info
建议:
- 恢复全局错误响应拦截器,统一处理 HTTP 错误和业务错误码
- 按错误严重性分级处理:网络错误 → Toast 提示 + 自动重试;401 → 自动登出 + 跳转登录;业务错误 → Toast 提示
2.4 【重要】SEO 严重不足
现状:
- 仅首页和
_document.tsx设置了固定的<title>和<meta description> - 30+ 个页面路由中,只有首页有独立的
<Head>标签 - 直播间页面(
/channel/[channelId])未利用getServerSideProps获取频道信息注入 meta 标签 - 缺少 Open Graph / Twitter Card 标签,社交分享时无法展示预览
建议:
- 为每个页面添加动态
<title>和<meta description> - 直播间页面使用
getServerSideProps获取频道名称、封面图,生成动态 OG 标签 - 创建
<SEOHead>通用组件,统一管理 OG、Twitter Card、canonical URL - 添加
robots.txt和sitemap.xml
2.5 【重要】缺少 ErrorBoundary
现状:整个 site 应用没有 React ErrorBoundary,一个子组件的 JS 错误会导致整个页面白屏。
建议:
- 在
_app.tsx的 Provider 链最外层添加全局 ErrorBoundary,捕获未处理的渲染错误并展示友好降级 UI - 为直播间、创作者后台等核心页面添加局部 ErrorBoundary,隔离错误影响范围
- ErrorBoundary 的
componentDidCatch中上报错误到 Sentry
2.6 【重要】Next.js 版本陈旧
现状:使用 Next.js 13.4.1(Pages Router),已落后多个主要版本。
风险:
- 无法使用 App Router、Server Components、Streaming SSR 等新特性
- 性能优化能力受限(无法使用
next/image的新优化选项、Turbopack 等) - 安全补丁滞后
建议:
- 短期:升级到 Next.js 13 最新 patch 版本,获取安全修复
- 中期:规划迁移到 Next.js 14+ 的 App Router,利用 Server Components 减少客户端 JS 体积
- 利用
next/image替代原生<img>标签(当前项目中有大量直接使用<img>的场景)
2.7 【中等】Recoil 已停止维护
现状:Recoil(0.7.6)自 2023 年起已基本停止维护(Meta 团队已转向 Relay 和其他方案)。
风险:
- 未来 React 版本升级时可能出现兼容性问题
- Bug 修复和性能优化停滞
- 社区生态萎缩,第三方工具支持减少
建议:
- 中长期规划迁移到 Jotai(API 与 Recoil 高度相似,迁移成本低)或 Zustand
- Jotai 支持与 React Suspense 深度集成,更适合未来 App Router 架构
- 可以渐进迁移:新功能使用 Jotai,旧功能保持 Recoil,最终统一
2.8 【中等】代码组织可优化
现状:
useTokenWar.tsx单文件 24,662 行,是一个「上帝 Hook」,包含注册、排行榜、贡献排名、奖励规则等所有 Token War 逻辑views/Creator/Channel/Live/components/CreatorSide/index.tsx内部定义 12 个箭头函数,职责过于集中- 存在 3 个空
interface IProps {}(InsufficientCoin、AccountSetting、TopUpCoins),增加代码噪音 - 部分注释掉的 JSX 代码散落在组件中(约 8 处
{/* <Button ... */})
建议:
- 将
useTokenWar.tsx拆分为useTokenWarRegistration、useTokenWarLeaderboard、useTokenWarContribution、useTokenWarRewards等多个独立 Hook - 大组件使用
useReducer或提取子组件来降低单文件复杂度 - 清理空接口和注释掉的代码,使用 Git 历史追溯而非代码注释保留旧逻辑
- 建立代码审查规范:单文件不超过 500 行,单函数不超过 50 行
2.9 【中等】SSR 能力未充分利用
现状:
- 大多数页面的
getServerSideProps/getStaticProps仅用于获取翻译文件(getTranslationStaticProps),未做任何数据预取 - 首页使用
getStaticProps,但未生成静态首页内容 - 直播间等动态页面完全依赖客户端请求数据,首屏白屏时间长
建议:
- 首页
getStaticProps中预取推荐频道列表和 Banner 数据,结合 ISR(增量静态生成)定期刷新 - 直播间页
getServerSideProps中预取频道基本信息(名称、封面、在线人数),减少首屏数据请求 - 用户资料页使用
getServerSideProps预取用户信息,提升 LCP
2.10 【重要】Promise 错误被静默吞掉(20+ 处)
现状:大量位置使用 .catch(() => {}) 完全忽略错误:
// src/components/TransactionModal/AA/index.tsx
login().then(() => {}).catch(() => {}); // 登录失败完全静默
// src/views/Home/components/Banner/index.tsx
}).catch(()=>{})
// src/views/Creator/Channel/Betting/index.tsx
await login().catch(() => {});
风险:用户操作失败后没有任何提示,无法排查线上问题。
建议:
- 逐一检查
.catch(() => {})调用,至少添加 Toast 提示或 Sentry 上报 - 对于确实需要静默的场景(如预加载),添加注释说明原因
2.11 【重要】两套重复的 HTTP 请求工具
现状:项目同时维护 request.ts 和 requestLib.ts 两个 Axios 实例,功能几乎完全重复,且 Header 名称大小写不一致(X-LCID vs X-Lcid、X-DEVICE-ID vs X-Device-Id)。
风险:
- 服务端可能因大小写不同解析不到正确的请求头
- 两套工具的响应拦截器行为不一致,部分调用手动检查
res.code === 200
建议:统一为一个请求工具,消除重复代码。
2.12 【重要】Aliplayer SDK 静态导入阻塞 SSR
现状:src/components/Player/index.tsx 直接 import Aliplayer from 'aliyun-aliplayer',该库是纯浏览器库,静态导入会增大首屏 bundle 且可能导致 SSR 报错。
建议:使用 next/dynamic 动态导入:dynamic(() => import('aliyun-aliplayer'), { ssr: false })
2.13 【中等】moment.js 与 date-fns 重复依赖(~287KB 浪费)
现状:项目同时依赖 moment(~287KB)和 date-fns,其中 moment 仅在 2 个文件中使用(Revenue、CreateTicketCollection),date-fns 在 10+ 个文件中使用。
建议:将 2 处 moment 调用迁移到 date-fns 并移除 moment 依赖,节省约 287KB bundle 体积。
2.14 【中等】react-hot-toast 与 react-toastify 重复
现状:两套 Toast 库同时使用,react-hot-toast 是主要库,react-toastify 使用极少但在 _app.tsx 中挂载了 ToastContainer。
建议:统一为一套 Toast 方案,移除另一套。
2.15 【中等】Next.js 图片优化被禁用
现状:next.config.js 中 images: { unoptimized: true } 关闭了所有图片优化,且有 93 处使用原生 <img> 标签、332 处空 alt=""。
建议:
- 开启 Next.js 图片优化,配置
remotePatterns白名单 - 将高频展示的图片(频道封面、用户头像等)迁移到
next/image - 为功能性图片添加有意义的
alt描述
2.16 【中等】Sentry 集成不完整
现状:
sentry.client.config.ts已初始化但next.config.js中的withSentryConfig被完全注释掉- 导致 source map 无法上传到 Sentry,生产环境错误堆栈无法还原
tracesSampleRate: 1设置为 100% 采样,性能开销大
建议:
- 恢复
withSentryConfig集成,确保 source map 正确上传 - 将
tracesSampleRate调低至 0.1(10%) - 验证 Sentry 错误上报是否正常工作
2.17 【中等】定时器泄漏与内存管理
现状:SetStreamModal 中 setTimeout 赋值给外部变量但无 cleanup;useEffect 中有未清理的监听器;useState 存储 Function 类型(应使用 useRef)。
建议:
- 所有
setTimeout/setInterval在useEffectcleanup 中清理 useState<Function>改为useRef<(() => void) | null>(null)
2.18 【中等】React Strict Mode 被注释
现状:next.config.js 第 7 行 // reactStrictMode: true 被注释掉,无法在开发期间发现副作用问题。
建议:取消注释,启用 Strict Mode。
2.19 【中等】API 文件命名为中文拼音
现状:OpenAPI 自动生成的 API 文件全部使用中文拼音命名(如 shuzihuobixiangguanjiekou.ts),可读性极差。
建议:在 openapi.ts 脚本中配置自定义命名策略(使用英文语义命名)。
2.20 【中等】Recoil 重复 Atom Key 检查被关闭
现状:.env 中设置 RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false,掩盖了重复 atom key 的潜在问题。
建议:启用检查,排查并修复重复的 atom key。
2.21 【中等】postMessage 缺少来源校验
现状:Jackpot 组件监听 window.addEventListener('message') 但未检查 event.origin,可能被恶意页面注入消息。
建议:校验 event.origin 是否为可信域名列表。
2.22 【低等】缺少 Bundle 分析工具
现状:package.json 中未配置 @next/bundle-analyzer 或类似工具。
建议:
- 安装
@next/bundle-analyzer,定期分析 bundle 组成 - 检查 ethers.js 是否可以用更轻量的 viem 替代(项目中已安装 viem 但似乎未完全迁移)
2.23 【低等】国际化硬编码问题
现状:
- 项目支持英文/中文两种语言,使用 next-i18next
- 部分页面标题(如
_document.tsx中的Ryzz - Stream, Earn and Beyond)是硬编码的英文 - 部分组件内的提示文案可能遗漏翻译
建议:
_document.tsx中的固定标题无法国际化(Server 端无 i18n context),但可以在_app.tsx中通过<Head>动态覆盖- 建立翻译键值完整性检查脚本,检测遗漏的翻译键
2.24 【低等】Wagmi v1 已过时
现状:使用 Wagmi v1(1.3.10)+ Web3Modal v2。
风险:
- Wagmi v2 有重大 API 变更,与 Viem v2 深度整合
- Web3Modal 已更名为 AppKit(由 WalletConnect 团队维护),v3/v4 UI 体验大幅改善
- 安全更新和 Bug 修复集中在新版本
建议:
- 规划迁移到 Wagmi v2 + Viem v2 + AppKit,获取更好的钱包连接体验和性能
- 迁移时可以借助
@wagmi/cli自动生成合约类型
2.25 【低等】缺少 Web Vitals 性能监控
现状:无性能监控(LCP、FID、CLS 等),26 处 console.log/error 散落在生产代码中。
建议:
- 使用
web-vitals库 + 自有后端埋点收集 Core Web Vitals 数据 - 添加关键用户路径的自定义性能指标(直播间加载时间、WS 连接建立时间等)
- 将散落的
console.log替换为logger.*保持一致性
2.26 【低等】NextUI 使用 beta 版本
现状:@nextui-org/react: 1.0.0-beta.12,beta 版本在生产环境存在不稳定风险。
建议:升级到 NextUI v2 正式版。
2.27 【低等】大量内联样式(151 处)
现状:151 处 style={{}} 内联样式,动态样式无法被 CSS 引擎缓存。
建议:将可预定义的样式迁移到 Tailwind 类或 CSS Modules,仅保留真正需要动态计算的内联样式。
2.28 【低等】无障碍访问(A11y)缺失
现状:
- 组件中几乎没有
aria-*属性 - 自定义弹窗、抽屉等交互组件未实现焦点管理
- 图片缺少
alt属性
建议:
- 至少为交互组件(Modal、Drawer、Dropdown)添加基本的 ARIA 角色和焦点管理
- 图片添加
alt描述 - 安装
eslint-plugin-jsx-a11y自动检测常见无障碍问题
2.29 【低等】Provider 嵌套过深
现状:15 层 Provider 嵌套虽然逻辑清晰,但:
- 调试时 React DevTools 显示层级极深
- 某些 Provider(如 Modal、Wallet)的依赖关系不强,可以合并或平铺
建议:
- 考虑使用
compose函数将多个 Provider 组合,降低 JSX 嵌套深度 - 评估是否可以将部分 Wrapper(Activity、Betting、Wallet、Modal)合并为单个
InteractiveProvider
2.30 【低等】API/WS 地址硬编码在代码中
现状:src/config/constants/common.ts 中 API 和 WebSocket 地址直接通过三元表达式硬编码,无法在不修改代码的情况下更换地址。
建议:使用 NEXT_PUBLIC_API_URL 等环境变量注入,配合 .env.example 文件。
2.31 【低等】环境变量管理
现状:
- 环境变量通过
config/constants/集中管理,设计合理 - 但
.env文件未被追踪,新开发者需要手动配置
建议:
- 提供
.env.example文件列出所有必需环境变量及说明 - 添加启动时环境变量校验(如
@t3-oss/env-nextjs),缺少关键变量时立即报错而非运行时崩溃
三、架构设计层面分析
以上的改进建议偏向代码质量细节,本节从更高的技术方案和架构设计视角进行评审。
3.1 架构亮点
3.1.1 WebSocket Protobuf 三层通讯架构
这是 RYZZ 最核心的架构设计,实现了一套完整的实时通讯框架:
┌─────────────────────────────────────────────┐
│ 应用层:useSubscription / useMsgPushSubscription │
│ → 业务组件只关心「监听哪个事件 + 处理数据」 │
├─────────────────────────────────────────────┤
│ 协议层:protobuf.ts │
│ → reqProtocolMap / rspProtocolMap 双向映射 │
│ → 自动 encode/decode,业务无感知 │
│ → commentTypeMap 二级路由(单通道多业务) │
├─────────────────────────────────────────────┤
│ 传输层:core.ts (Ws class) │
│ → 连接管理 / 心跳 / 重连 / 消息队列缓冲 │
│ → EventEmitter 事件分发 │
│ → MessageBatch 批量消息降低网络开销 │
└─────────────────────────────────────────────┘
设计亮点:传输层完全不关心业务语义,协议层纯粹做序列化/反序列化,应用层通过声明式 Hook 订阅。三层之间通过 path 字符串和 EventEmitter 松耦合,新增业务消息类型只需:
- 添加 proto 文件
- 在 protocolMap 注册映射
- 在组件中
ws.on(path, handler)监听
无需修改传输层和编解码逻辑,开闭原则体现到位。
3.1.2 "洋葱模型"跨关注点治理
_app.tsx 中 15 层 Provider 嵌套看似夸张,实际上是一种系统化的跨关注点分离架构:
RecoilRoot → Internationalization → NextUIProvider → WagmiConfig
→ Auth → PortalProvider → Page → WebSocket → CustomWrapper
→ Currency → Activity → Betting → Wallet → Modal → Layout
设计思路:每个 Wrapper 只负责一个关注点(认证、国际化、WebSocket 连接、货币数据...),并且严格按依赖顺序排列(例如 WebSocket 必须在 Auth 之后,因为连接需要 token;Currency 必须在 WebSocket 之后,因为余额通过 WS 推送更新)。这让任何单一关注点的修改都不会影响其他层,也便于在开发时逐层关闭调试。
3.1.3 跨应用 OAuth 中枢设计
binding 应用作为独立的 OAuth 回调中转站,解决了"多个前端应用需要对接多个第三方登录提供商"的 N×M 问题:
site ──┐ ┌── Google OAuth
admin ─┤── binding 中枢 ──├── Twitter OAuth
mobile ─┘ (统一回调) └── Apple OAuth
双通道回调保障兼容性:
├── 主通道:window.postMessage(现代浏览器)
└── 降级通道:Cookie + window.focus(Safari 隐私限制)
设计亮点:所有 OAuth 回调 URL 统一指向 binding 应用,新增第三方登录只需在 binding 中添加处理逻辑,不需要修改 site/admin/mobile 各自的回调配置。PKCE 流程防止授权码截获攻击。
3.1.4 共享包的职责分层设计
packages/
├── 基础设施层(无业务语义)
│ ├── constant/ → 环境检测、存储键名
│ ├── function/ → 通用工具函数
│ ├── hooks/ → 通用 React Hooks
│ └── types/ → 跨应用类型定义
│
├── 业务能力层(封装特定领域能力)
│ ├── login/ → 认证(Web3 + OAuth,跨 site/admin/mobile)
│ ├── contract/ → 智能合约 ABI + TypeChain 类型
│ ├── report-sdk/ → 三平台埋点(GA + Facebook + Twitter)
│ └── dauth-web/ → MPC 钱包 SDK
│
├── UI 表现层
│ └── ryzz-uikit/ → Ryzz 品牌 UI 组件
│
└── 工程配置层
├── tsconfig/ → 统一 TypeScript 配置
└── eslint-config-custom/ → 统一代码规范
层级清晰,上层依赖下层,不存在循环依赖。login 包通过 ELoginOrg 枚举支持多应用切换,一套代码服务 site/admin/mobile 三端。
3.1.5 Docker + Turbo Prune 零冗余构建
# turbo prune site --docker 只提取 site 及其依赖包
# 而不是整个 monorepo 的 30+ 个包
turbo prune site --docker
# ↓ 输出结果:
# out/json/ → 仅 site 相关的 package.json(用于依赖安装缓存层)
# out/full/ → 仅 site 相关的源码
# out/pnpm-lock.yaml → 精简后的锁文件
四阶段构建(base → builder → installer → runner)充分利用 Docker 层缓存,代码不变时依赖安装层完全命中缓存。output: 'standalone' + PM2 运行时保证最终镜像最小化。
3.2 架构改进建议
3.2.1 SSR 能力严重浪费 — Next.js 沦为纯 CSR 框架
现状:项目选择了 Next.js(Pages Router)作为框架,但几乎所有页面的 getServerSideProps / getStaticProps 仅用于加载翻译文件,未做任何业务数据预取。实际上 Next.js 被当作纯 CSR 应用使用。
// 当前大多数页面的 SSR 使用方式
export const getStaticProps = getTranslationStaticProps; // 只加载翻译
// 页面数据全部在客户端 useEffect 中获取
架构影响:
- 首屏白屏时间长(HTML 是空壳,所有数据等客户端 JS 执行后才获取)
- SEO 近乎为零(搜索引擎爬到的是空页面)
- 浪费了 Node.js 服务端的渲染能力
- 直播间页面可以通过 SSR 预取频道信息,显著改善 LCP
建议方案:
- 首页:使用
getStaticProps+ ISR(每 60 秒重新生成),预取推荐频道列表 - 直播间
[channelId]:使用getServerSideProps预取频道基本信息(名称、封面、在线人数、SEO meta) - 用户资料页:使用
getServerSideProps预取公开用户信息 - 将「翻译加载 + 业务数据预取」合并到同一个 SSR 函数中
3.2.2 缺乏 BFF 聚合层 — 前端直连 14+ 个微服务
现状:根据 admin/config/openapi.ts,后端有 14 个微服务(live、coin、user、guild、common、trade、report...),前端通过 API Gateway 直接调用所有服务的 API。
前端 ──→ API Gateway ──→ 14 个微服务
(纯透传)
架构问题:
- 一个页面可能需要调用 3-5 个微服务的 API,前端需要自己编排调用顺序和数据聚合
- 前端承担了过多的数据转换和组装逻辑
- 移动端和 PC 端对同一数据可能有不同的聚合需求,但只能复用同一批 API
- 微服务内部接口变动直接影响前端
建议方案: 引入 BFF(Backend For Frontend)层,按端提供聚合 API:
site ──→ site-bff ──→ API Gateway ──→ 微服务
admin ──→ admin-bff ──→ API Gateway ──→ 微服务
mobile ──→ mobile-bff ──→ API Gateway ──→ 微服务
BFF 可以用 Next.js API Routes 实现(零额外部署成本),也可以用独立的 Node.js 服务。BFF 负责:
- 多微服务数据聚合(一次请求代替前端多次请求)
- 数据裁剪(只返回前端需要的字段,减少传输量)
- 隔离微服务接口变动对前端的影响
3.2.3 前端无离线容错架构
现状:作为一个直播 + Web3 应用,完全没有离线容错设计:
- 无 Service Worker(Next.js 的
next-pwa未配置) - WebSocket 断开时用户无感知,消息静默丢失
- 网络恢复后无消息补偿机制(不知道断线期间错过了哪些消息)
- HTTP 请求失败无自动重试,无 Optimistic Update
架构影响:
- 移动端用户在弱网环境下体验极差
- 直播间断线重连后,错过的礼物特效、评论消息无法恢复
- Web3 交易提交后网络断开,用户无法知道交易结果
建议方案:
- WebSocket 消息补偿:重连后发送
lastMessageId,服务端补推断线期间的消息 - Optimistic UI:点赞、评论等操作先本地展示,异步同步到服务器,失败时回滚
- 请求队列:离线时将操作缓存到 IndexedDB,上线后批量同步
- 网络状态感知:全局 NetworkStatus Provider,断网时显示提示横幅
3.2.4 状态管理缺乏持久化和同步策略
现状:17+ 个 Recoil Atom 全部存储在内存中,页面刷新后状态完全丢失。用户认证信息虽然存在 localStorage 中,但业务状态(当前频道、投注状态、钱包信息)全部重新加载。
架构问题:
- 用户刷新页面 = 重新走一遍完整的初始化流程(WS 连接 → 认证 → 进房间 → 拉数据)
- 多 Tab 之间状态不同步(Tab A 中充值,Tab B 余额不更新)
- 关键业务状态(如投注进行中)刷新后丢失
建议方案:
- 分级持久化策略:
- 不持久化:实时数据(在线人数、评论流)
- SessionStorage:当前会话状态(频道、投注上下文)
- localStorage:用户偏好(语言、主题、通知设置)
- 多 Tab 同步:使用
BroadcastChannelAPI 在多个 Tab 之间同步余额、认证状态等关键数据 - 可以使用 Jotai 的
atomWithStorage或 Zustand 的persistmiddleware 来实现
3.2.5 缺乏特性开关和灰度发布机制
现状:所有功能通过 isProduction 环境变量做开关,没有运行时特性开关。
架构问题:
- 无法对部分用户灰度发布新功能
- 紧急回滚需要重新部署
- A/B 测试无基础设施支持
- Token War、竞猜等高风险功能无法按地区/用户组逐步开放
建议方案: 引入轻量特性开关系统(可以从远端配置中心拉取,也可以基于现有的后端配置服务):
const { isEnabled } = useFeatureFlag('token-war-v2');
if (isEnabled) {
return <TokenWarV2 />;
}
return <TokenWarV1 />;
3.2.6 请求层和 WebSocket 层割裂,缺乏统一的数据获取策略
现状:HTTP 请求(requestLib.ts)和 WebSocket(core.ts)是两套完全独立的通讯系统,没有统一的:
- 错误处理策略(HTTP 有拦截器,WS 的错误被大量
.catch(() => {})吞掉) - 重试策略(HTTP 无重试,WS 有重连但无消息重试)
- 缓存策略(HTTP 无缓存,WS 无消息去重)
- 认证管理(HTTP 用 Authorization header,WS 用独立的 auth 命令)
建议方案: 建立统一的数据获取层(Data Fetching Layer),将 HTTP 和 WS 统一纳管:
- 使用 SWR 或 React Query 管理 HTTP 请求的缓存和重试
- WebSocket 消息与 React Query 的缓存打通(WS 推送 → 自动更新 React Query 缓存)
- 统一的认证 Token 管理(Token 刷新对 HTTP 和 WS 同时生效)
四、改进优先级矩阵
| 优先级 | 改进项 | 预估工作量 | 影响范围 |
|---|---|---|---|
| P0 | 轮换泄露的凭证(Sentry Token、WalletConnect ID) | 1 小时 | 安全 |
| P0 | 修复 target="_blank" 安全风险(41 处) | 0.5 天 | 安全 |
| P0 | 添加核心模块测试(DAuth、WebSocket、useAuth) | 2-3 周 | 全局稳定性 |
| P0 | WebSocket 指数退避重连 | 1 天 | 用户体验 + 服务器稳定性 |
| P1 | 恢复全局错误拦截器 | 0.5 天 | 用户体验 |
| P1 | 添加 ErrorBoundary | 1 天 | 用户体验 |
| P1 | 修复 20+ 处静默 .catch(() => {}) | 1 天 | 可靠性 |
| P1 | SEO 优化(动态 meta 标签) | 3-5 天 | 搜索流量 |
| P1 | 修复 Sentry 集成(恢复 withSentryConfig) | 0.5 天 | 可观测性 |
| P2 | 合并两套 HTTP 请求工具 | 1-2 天 | 代码质量 |
| P2 | Aliplayer 动态导入 | 0.5 天 | 首屏性能 |
| P2 | 移除 moment.js(节省 ~287KB) | 0.5 天 | Bundle 体积 |
| P2 | 开启 Next.js 图片优化 | 2-3 天 | 性能 |
| P2 | SSR 数据预取 | 1 周 | 首屏加载性能 |
| P2 | useTokenWar 拆分重构 | 3-5 天 | 代码可维护性 |
| P3 | Next.js 版本升级 | 1-2 周 | 长期技术栈健康 |
| P3 | Recoil → Jotai 迁移 | 2-3 周 | 长期技术栈健康 |
| P3 | Wagmi v1 → v2 迁移 | 1-2 周 | Web3 体验 |
| P3 | Web Vitals 性能监控 | 2-3 天 | 可观测性 |
| P3 | 启用 React Strict Mode | 0.1 天 | 开发质量 |
| P4 | 无障碍访问改进 | 1 周 | 合规性 |
| P4 | 环境变量校验 | 0.5 天 | 开发体验 |
| P4 | API 文件英文重命名 | 1 天 | 可读性 |
| P4 | 升级 NextUI 到正式版 | 1-2 天 | 稳定性 |
五、总结
RYZZ 主站是一个技术深度很高的项目——WebSocket Protobuf 通讯系统、DAuth MPC 钱包、EIP-4337 账户抽象等实现都展现了扎实的工程能力和对前沿技术的理解。Monorepo 架构、CI/CD 流水线、多环境部署等工程化实践也相当成熟。
主要短板集中在三个方面:
- 安全问题:凭证泄露、target="_blank" 风险、postMessage 无来源校验等需要立即修复
- 质量保障缺失:零测试覆盖、无 ErrorBoundary、大量静默 catch 是最大的稳定性风险
- 性能优化空间:图片优化被禁用、Aliplayer 静态导入、重复依赖(moment.js、双 Toast 库、双 HTTP 工具)导致 bundle 体积偏大
部分依赖(Next.js 13、Recoil、Wagmi v1、NextUI beta)已进入维护尾期,需要规划技术栈升级。
一句话总结:功能实现能力强,安全加固和质量保障需要优先补齐。