Files
whale-town-end/docs/systems/zulip/README.md
angjustinl 8f9a6e7f9d feat(login, zulip): 引入 JWT 验证并重构 API 密钥管理
### 详细变更描述

* **修复 JWT 签名冲突**:重构 `LoginService.generateTokenPair()`,移除载荷(Payload)中的 `iss` (issuer) 与 `aud` (audience) 字段,解决签名校验失败的问题。
* **统一验证逻辑**:更新 `ZulipService` 以调用 `LoginService.verifyToken()`,消除重复的 JWT 校验代码,确保逻辑单一职责化(Single Responsibility)。
* **修复硬编码 API 密钥问题**:消息发送功能不再依赖静态配置,改为从 Redis 动态读取用户真实的 API 密钥。
* **解耦依赖注入**:在 `ZulipModule` 中注入 `AuthModule` 依赖,以支持标准的 Token 验证流程。
* **完善技术文档**:补充了关于 JWT 验证流程及 API 密钥管理逻辑的详细文档。
* **新增测试工具**:添加 `test-get-messages.js` 脚本,用于验证通过 WebSocket 接收消息的功能。
* **更新自动化脚本**:同步更新了 API 密钥验证及用户注册校验的快速测试脚本。
* **端到端功能验证**:确保消息发送逻辑能够正确映射并调用用户真实的 Zulip API 密钥。
2026-01-06 18:51:37 +08:00

11 KiB
Raw Blame History

Zulip 集成系统文档

概述

Zulip 集成系统是一个为 2D 社交 MMO 游戏设计的跨平台聊天解决方案。该系统实现了游戏内外的无缝互通,让不玩游戏的 Zulip 社群成员也能与游戏内玩家实时交流。

核心设计理念

系统采用 统一网关 (Unified Gateway) 架构,利用 Zulip 的 Stream-Topic 线程模型与游戏世界的空间概念进行映射:

游戏概念 Zulip 概念 示例
Game World / Map Stream #Novice_Village
Interactive Object / Event Topic Notice Board, Tavern Gossip
Whisper / Party Private Message 私聊消息

架构优势

  1. 客户端极度简化: Godot 客户端无需处理 HTTP 请求、Long Polling 或复杂 JSON 解析
  2. 安全性: Zulip API Key 永不下发到客户端,位置欺诈完全消除
  3. 协议统一: 单一 WebSocket 协议,网络层代码减半

系统架构

┌─────────────────┐     WebSocket      ┌─────────────────────────────────────┐
│  Godot Client   │◄──────────────────►│     NestJS 中间件服务器              │
│  (Game Client)  │   Game Protocol    │  ┌─────────────────────────────────┐│
└─────────────────┘                    │  │  WebSocket Gateway              ││
                                       │  │  ├─ Session Manager             ││
                                       │  │  ├─ Message Filter              ││
                                       │  │  └─ Zulip Client Pool           ││
                                       │  └─────────────────────────────────┘│
                                       └──────────────┬──────────────────────┘
                                                      │ REST API / Long Polling
                                                      ▼
                                       ┌─────────────────────────────────────┐
                                       │         Zulip Server                │
                                       │  ├─ REST API                        │
                                       │  └─ Event Queue                     │
                                       └─────────────────────────────────────┘

核心组件

1. WebSocket Gateway (zulip-websocket.gateway.ts)

统一网关,处理所有 Godot 客户端连接,实现游戏协议到 Zulip 协议的转换。

主要功能:

  • 连接认证和会话管理
  • 消息路由和协议转换
  • 权限控制和上下文注入

支持的消息类型:

  • login: 玩家登录
  • chat: 发送聊天消息
  • position_update: 位置更新
  • logout: 玩家登出

2. Session Manager (session-manager.service.ts)

会话管理器,维护 Socket_ID 与 Zulip_Queue_ID 的绑定关系。

主要功能:

  • 会话创建/销毁
  • 玩家位置跟踪
  • 上下文注入(根据位置确定 Stream/Topic
  • 空间过滤(获取指定地图的所有 Socket

3. Zulip Client Pool (zulip-client-pool.service.ts)

Zulip 客户端池,为每个用户维护专用的 Zulip 客户端实例。

主要功能:

  • API Key 管理
  • 事件队列注册
  • 消息发送/接收
  • 客户端生命周期管理

4. Message Filter (message-filter.service.ts)

消息过滤器,实施内容审核和频率控制。

主要功能:

  • 敏感词过滤
  • 频率限制(默认 10 条/分钟)
  • 消息长度限制(默认 1000 字符)
  • 重复内容检测
  • 权限验证

5. Config Manager (config-manager.service.ts)

配置管理器,管理地图映射配置和系统参数。

主要功能:

  • 地图到 Stream 的映射
  • 交互对象到 Topic 的映射
  • 配置热重载
  • 配置验证

6. Stream Initializer Service (stream-initializer.service.ts)

Stream 初始化服务,在系统启动时自动检查并创建缺失的 Zulip Streams。

主要功能:

  • 启动时自动检查所有地图对应的 Streams
  • 自动创建缺失的 Streams
  • 使用 Bot API Key 或管理员账号创建 Streams
  • 记录初始化结果和错误

权限说明:

  • Bot 账号可能缺少创建 Stream 的权限
  • 建议使用管理员账号手动创建 Streams
  • 或在 Zulip 服务器中为 Bot 授予相应权限

7. Monitoring Service (monitoring.service.ts)

监控服务,提供系统健康检查和指标收集。

主要功能:

  • 连接指标监控
  • 消息指标监控
  • 系统健康检查
  • 告警通知

消息协议

客户端发送格式

登录消息

{
  "type": "login",
  "token": "user_game_token"
}

聊天消息

{
  "t": "chat",
  "content": "Hello",
  "scope": "local"
}

位置更新

{
  "t": "position",
  "x": 100,
  "y": 200,
  "mapId": "novice_village"
}

客户端接收格式

聊天渲染消息

{
  "t": "chat_render",
  "from": "User_B",
  "txt": "Hi",
  "bubble": true
}

登录确认

{
  "t": "login_success",
  "sessionId": "session_123",
  "currentMap": "novice_village"
}

错误消息

{
  "t": "error",
  "code": "RATE_LIMIT",
  "message": "消息发送过于频繁,请稍后再试"
}

配置说明

环境变量配置

# Zulip 服务器配置
ZULIP_SERVER_URL=https://your-zulip-server.com
ZULIP_BOT_EMAIL=bot@your-zulip-server.com
ZULIP_BOT_API_KEY=your-bot-api-key

# WebSocket 配置
WEBSOCKET_PORT=3001
WEBSOCKET_NAMESPACE=/game

# 消息配置
MESSAGE_RATE_LIMIT=10          # 消息频率限制(条/分钟)
MESSAGE_MAX_LENGTH=1000        # 消息最大长度

# 会话配置
SESSION_TIMEOUT=30             # 会话超时时间(分钟)
CLEANUP_INTERVAL=5             # 清理间隔(分钟)

Stream 初始化

系统在启动时会自动检查并尝试创建缺失的 Zulip Streams。

注意事项:

  • Bot 账号可能缺少创建 Stream 的权限
  • 建议使用管理员账号预先创建所有 Streams
  • 或在 Zulip 服务器中为 Bot 授予 "Create streams" 权限

手动创建 Streams:

# 使用测试脚本创建所有地图区域的 Streams
node test-stream-initialization.js

详细配置说明请参考 配置管理指南

地图映射配置

配置文件位置: config/zulip/map-config.json

系统支持 9 个地图区域,每个区域对应一个 Zulip Stream

  1. 鲸之港 (Whale Port) - 中心城区,默认出生点
  2. 南瓜谷 (Pumpkin Valley) - 新手学习区
  3. Offer 城 (Offer City) - 职业发展区
  4. 模型工厂 (Model Factory) - AI/代码构建区
  5. 内核岛 (Kernel Island) - 核心技术研究区
  6. 摸鱼海滩 (Moyu Beach) - 休闲娱乐区
  7. 天梯峰 (Ladder Peak) - 挑战竞赛区
  8. 星河湾 (Galaxy Bay) - 创意设计区
  9. 数据遗迹 (Data Ruins) - 数据库归档区

配置示例:

{
  "version": "2.0.0",
  "description": "基于像素大地图的 Zulip 映射配置",
  "maps": [
    {
      "mapId": "whale_port",
      "mapName": "鲸之港",
      "zulipStream": "Whale Port",
      "description": "中心城区,交通枢纽与主要聚会点",
      "interactionObjects": [
        {
          "objectId": "whale_statue",
          "objectName": "鲸鱼雕像",
          "zulipTopic": "Announcements",
          "position": { "x": 600, "y": 400 }
        }
      ]
    }
  ]
}

数据流程

发送消息流程 (游戏 → Zulip)

  1. 玩家在游戏中输入消息
  2. Godot 客户端通过 WebSocket 发送 chat 消息
  3. WebSocket Gateway 接收消息
  4. Session Manager 获取玩家当前位置
  5. 上下文注入:根据位置确定目标 Stream/Topic
  6. Message Filter 进行内容过滤和频率检查
  7. Zulip Client Pool 使用用户的 API Key 发送消息到 Zulip
  8. 返回发送确认给客户端

接收消息流程 (Zulip → 游戏)

  1. Zulip 服务器推送消息事件到 Event Queue
  2. Zulip Event Processor 接收并处理事件
  3. Session Manager 进行空间过滤,确定目标玩家
  4. 消息转换为游戏协议格式
  5. WebSocket Gateway 推送 chat_render 消息给目标客户端
  6. Godot 客户端显示聊天气泡

错误处理

错误码说明

错误码 说明 处理建议
AUTH_FAILED 认证失败 检查 Token 有效性
RATE_LIMIT 频率限制 等待后重试
CONTENT_FILTERED 内容被过滤 修改消息内容
PERMISSION_DENIED 权限不足 检查用户权限
ZULIP_ERROR Zulip 服务错误 系统自动重试
SESSION_EXPIRED 会话过期 重新登录

降级策略

当 Zulip 服务不可用时,系统会自动切换到本地聊天模式:

  • 消息仅在游戏内传播
  • 不同步到 Zulip
  • 服务恢复后自动切换回正常模式

安全机制

API Key 安全

  • API Key 加密存储在数据库中
  • 永不下发到客户端
  • 支持强制刷新机制

消息安全

  • 敏感词过滤
  • 频率限制防刷屏
  • 位置验证防欺诈
  • 消息长度限制

连接安全

  • Token 验证
  • 会话超时自动断开
  • 异常连接检测和拒绝

监控指标

连接指标

  • zulip.connections.active: 活跃连接数
  • zulip.connections.total: 总连接数
  • zulip.connections.errors: 连接错误数

消息指标

  • zulip.messages.sent: 发送消息数
  • zulip.messages.received: 接收消息数
  • zulip.messages.filtered: 被过滤消息数
  • zulip.messages.latency: 消息延迟

系统指标

  • zulip.sessions.active: 活跃会话数
  • zulip.zulip_clients.active: 活跃 Zulip 客户端数
  • zulip.event_queues.active: 活跃事件队列数

相关文档

更新日志

v1.1.0 (2026-01-06)

  • 修复 JWT Token 验证和 API Key 管理
    • 修复 LoginService.generateTokenPair() 的 JWT 签名冲突问题
    • ZulipService 现在复用 LoginService.verifyToken() 进行 Token 验证
    • 修复消息发送时使用错误的硬编码 API Key 问题
    • 现在正确从 Redis 读取用户注册时存储的真实 API Key
    • 添加 AuthModuleZulipModule 的依赖注入
    • 消息发送功能现已完全正常工作

v1.0.1 (2025-12-25)

  • 更新地图配置为 9 区域系统
  • 添加 Stream Initializer Service 自动初始化服务
  • 更新默认出生点为鲸之港 (Whale Port)
  • 添加地图区域描述字段
  • 修复上下文注入使用 ConfigManager
  • 改进错误处理和日志记录

v1.0.0 (2025-12-25)

  • 初始版本发布
  • 实现 WebSocket Gateway 统一网关
  • 实现 Session Manager 会话管理
  • 实现 Zulip Client Pool 客户端池
  • 实现 Message Filter 消息过滤
  • 实现 Config Manager 配置管理
  • 实现 Monitoring Service 监控服务
  • 完成集成测试覆盖