749 lines
32 KiB
Markdown
749 lines
32 KiB
Markdown
# Tinode 话题系统详解
|
|
|
|
> Tinode 的核心设计理念:**一切皆话题 (Topic-Oriented)**
|
|
|
|
## 概述
|
|
|
|
在 Tinode 中,私聊、群聊、个人信息、搜索等功能都通过"话题 (Topic)"来实现。话题是一个隔离的通信通道,不同话题之间互不干扰。
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ Tinode 话题架构 │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ 【通信类话题】- 可以发送消息 │
|
|
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
|
|
│ │ p2p 私聊 │ │ grp 群聊 │ │ slf 笔记 │ │
|
|
│ │ p2pXXX │ │ grpXXX │ │ slfXXX │ │
|
|
│ └───────────────┘ └───────────────┘ └───────────────┘ │
|
|
│ │
|
|
│ 【系统类话题】- 特殊用途 │
|
|
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
|
|
│ │ me 个人 │ │ fnd 搜索 │ │ sys 系统 │ │
|
|
│ │ usrXXX │ │ fnd │ │ sys │ │
|
|
│ └───────────────┘ └───────────────┘ └───────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 话题类型
|
|
|
|
### 类型定义
|
|
|
|
```go
|
|
// server/store/types/types.go
|
|
type TopicCat int
|
|
|
|
const (
|
|
TopicCatMe TopicCat = iota // 'me' 话题 - 用户个人信息
|
|
TopicCatFnd // 'fnd' 话题 - 用户搜索
|
|
TopicCatP2P // 'p2p' 话题 - 私聊 (点对点)
|
|
TopicCatGrp // 'grp' 话题 - 群聊
|
|
TopicCatSys // 'sys' 话题 - 系统话题
|
|
TopicCatSlf // 'slf' 话题 - 自我话题 (笔记/收藏)
|
|
)
|
|
```
|
|
|
|
### 命名规则
|
|
|
|
```go
|
|
func GetTopicCat(name string) TopicCat {
|
|
switch name[:3] {
|
|
case "usr": return TopicCatMe // usrABC123 → me 话题
|
|
case "p2p": return TopicCatP2P // p2pABC... → 私聊
|
|
case "grp": return TopicCatGrp // grpABC... → 群聊
|
|
case "chn": return TopicCatGrp // chnABC... → 频道 (也是群聊)
|
|
case "fnd": return TopicCatFnd // fnd → 搜索话题
|
|
case "sys": return TopicCatSys // sys → 系统话题
|
|
case "slf": return TopicCatSlf // slfABC... → 自我话题
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 六种话题详解
|
|
|
|
### 1. P2P 私聊话题 (`p2p`)
|
|
|
|
**命名格式**: `p2p` + 两用户ID排序拼接
|
|
|
|
**示例**: Alice (usrA) 和 Bob (usrB) 的私聊话题名为 `p2pusrAusrB`
|
|
|
|
```
|
|
用户 Alice (usrA) 想给用户 Bob (usrB) 发消息
|
|
|
|
┌─────────┐ ┌─────────┐
|
|
│ Alice │ │ Bob │
|
|
│ usrA │ │ usrB │
|
|
└────┬────┘ └────┬────┘
|
|
│ │
|
|
│ 订阅 p2pusrAusrB │ 订阅 p2pusrAusrB
|
|
│ │
|
|
│ ┌───────────────────────┴────────────────┐
|
|
│ │ │
|
|
│ │ P2P Topic: p2pusrAusrB │
|
|
│ │ │
|
|
│ │ perUser: │
|
|
│ │ usrA → {modeWant, modeGiven} │
|
|
│ │ usrB → {modeWant, modeGiven} │
|
|
│ │ │
|
|
│ │ messages: [msg1, msg2, ...] │
|
|
│ │ │
|
|
│ └────────────────────────────────────────┘
|
|
│ │
|
|
└──────── 发送消息 ───────────────┘
|
|
↓
|
|
广播给所有订阅者
|
|
(Alice 和 Bob 都能收到)
|
|
```
|
|
|
|
**关键特性**:
|
|
|
|
| 特性 | 说明 |
|
|
|------|------|
|
|
| 自动创建 | 当 Alice 给 Bob 发第一条消息时自动创建 |
|
|
| 双向订阅 | 两个用户都是订阅者,权限对等 |
|
|
| 自动删除 | 当两个用户都取消订阅时,话题自动删除 |
|
|
| 消息存储 | 存储两个人的聊天记录 |
|
|
| 无管理员 | 没有管理员概念,两人权限相同 |
|
|
|
|
**创建流程**:
|
|
|
|
```
|
|
1. Alice 发送订阅请求 {sub: {topic: "usrB"}}
|
|
(注意:订阅的是 Bob 的用户ID,不是 p2p 话题名)
|
|
|
|
2. 服务器检测到这是 P2P 订阅
|
|
- 解析出目标用户: usrB
|
|
- 生成话题名: p2pusrAusrB (排序后)
|
|
|
|
3. 检查话题是否存在
|
|
- 不存在 → 创建新话题
|
|
- 存在 → 直接订阅
|
|
|
|
4. 通知 Bob 有新的订阅请求
|
|
- 如果 Bob 在线,推送 {pres: {what: "knock"}}
|
|
- Bob 可以选择订阅或忽略
|
|
|
|
5. Alice 发送消息 → 广播给 Alice 和 Bob
|
|
```
|
|
|
|
---
|
|
|
|
### 2. GRP 群聊话题 (`grp` / `chn`)
|
|
|
|
**命名格式**: `grp` 或 `chn` + 唯一ID
|
|
|
|
**示例**: `grpTechGroup123`, `chnAnnouncements456`
|
|
|
|
```
|
|
群组: "技术交流群"
|
|
话题名: grpTechGroup123
|
|
|
|
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
|
|
│ Alice │ │ Bob │ │ Carol │ │ Dave │
|
|
│ (创建者) │ │ (管理员) │ │ (成员) │ │ (成员) │
|
|
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
|
|
│ │ │ │
|
|
│ │ │ │
|
|
└────────────┴────────────┴────────────┘
|
|
│
|
|
▼
|
|
┌───────────────────────────────────────┐
|
|
│ GRP Topic: grpTechGroup123 │
|
|
│ │
|
|
│ owner: usrA (Alice) │
|
|
│ │
|
|
│ perUser: │
|
|
│ usrA → {owner, admin, write} │
|
|
│ usrB → {admin, write} │
|
|
│ usrC → {write} │
|
|
│ usrD → {write} │
|
|
│ │
|
|
│ messages: [msg1, msg2, ...] │
|
|
│ │
|
|
│ tags: ["技术", "编程", "Go"] │
|
|
│ │
|
|
│ public: { │
|
|
│ "name": "技术交流群", │
|
|
│ "avatar": "group.jpg" │
|
|
│ } │
|
|
└───────────────────────────────────────┘
|
|
```
|
|
|
|
**关键特性**:
|
|
|
|
| 特性 | 说明 |
|
|
|------|------|
|
|
| 手动创建 | 需要显式创建,设置群名、头像等 |
|
|
| 多人订阅 | 可以有很多成员,无上限 |
|
|
| 权限管理 | 拥有者 > 管理员 > 普通成员 > 访客 |
|
|
| 持久化 | 不会因为用户离开而删除 |
|
|
| 可搜索 | 通过 tags 可以被搜索到 |
|
|
| 邀请机制 | 可以邀请他人加入 |
|
|
|
|
**权限层级**:
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ 群组权限层级 │
|
|
├─────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ Owner (拥有者) │
|
|
│ ├── 可以删除群组 │
|
|
│ ├── 可以转让拥有者 │
|
|
│ ├── 可以任命/撤销管理员 │
|
|
│ └── 拥有所有权限 │
|
|
│ │
|
|
│ Admin (管理员) │
|
|
│ ├── 可以修改群信息 │
|
|
│ ├── 可以踢出成员 │
|
|
│ ├── 可以邀请新成员 │
|
|
│ └── 可以设置成员权限 │
|
|
│ │
|
|
│ Member (普通成员) │
|
|
│ ├── 可以发送消息 │
|
|
│ ├── 可以查看群信息 │
|
|
│ └── 可以主动退出 │
|
|
│ │
|
|
│ Guest (访客) │
|
|
│ ├── 只能接收消息 │
|
|
│ └── 不能发送 │
|
|
│ │
|
|
└─────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**频道模式 (chn)**:
|
|
|
|
`chn` 前缀表示频道,是群聊的一种特殊形式:
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ 频道 vs 群聊 │
|
|
├─────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ 群聊 (grp) │
|
|
│ ├── 所有成员都能发言 │
|
|
│ ├── 消息对所有人可见 │
|
|
│ └── 适合团队协作 │
|
|
│ │
|
|
│ 频道 (chn) │
|
|
│ ├── 只有管理员能发言 │
|
|
│ ├── 类似微信公众号 │
|
|
│ ├── 订阅者只能接收 │
|
|
│ └── 适合公告、广播 │
|
|
│ │
|
|
└─────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
### 3. ME 个人话题 (`usr`)
|
|
|
|
**命名格式**: `usr` + 用户ID (就是用户自己的ID)
|
|
|
|
**示例**: 用户 Alice 的 ID 是 `usrABC123`,她的 me 话题名也是 `usrABC123`
|
|
|
|
```
|
|
用户: Alice
|
|
话题名: usrABC123 (用户自己的 ID)
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ ME Topic: usrABC123 │
|
|
│ │
|
|
│ 这是用户的"影子"话题,不存储消息,只管理: │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ perSubs (联系人列表) │ │
|
|
│ │ │ │
|
|
│ │ p2pusrAusrB → {unread: 5, lastSeen: ...} │ │
|
|
│ │ grpTechGroup → {unread: 12, lastSeen: ...} │ │
|
|
│ │ p2pusrAusrC → {unread: 0, lastSeen: ...} │ │
|
|
│ │ │ │
|
|
│ │ 这些是 Alice 订阅的所有话题的引用 │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ 功能: │
|
|
│ - 管理联系人列表 │
|
|
│ - 推送在线状态 (pres: {what: "online"/"offline"}) │
|
|
│ - 设备管理 (多设备登录) │
|
|
│ - 收到新消息的推送 │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**关键特性**:
|
|
|
|
| 特性 | 说明 |
|
|
|------|------|
|
|
| 临时话题 | 不存储在 topics 表中,是用户 ID 的引用 |
|
|
| 不能发消息 | 只用于管理,不能发送聊天消息 |
|
|
| 联系人管理 | 管理用户订阅的所有话题 |
|
|
| 在线状态 | 推送 "online" / "offline" 状态变化 |
|
|
| 设备信息 | 管理多设备登录 |
|
|
| 单用户 | 只有用户自己订阅 |
|
|
|
|
**联系人列表结构**:
|
|
|
|
```javascript
|
|
// 用户订阅 me 话题后,会收到联系人列表
|
|
{
|
|
"ctrl": {
|
|
"params": {
|
|
"sub": [
|
|
{
|
|
"topic": "p2pusrAusrB",
|
|
"unread": 5,
|
|
"lastSeen": "2024-01-15T10:30:00Z"
|
|
},
|
|
{
|
|
"topic": "grpTechGroup",
|
|
"unread": 12,
|
|
"lastSeen": "2024-01-15T09:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 4. FND 搜索话题 (`fnd`)
|
|
|
|
**命名格式**: 固定为 `fnd`
|
|
|
|
```
|
|
用户 Alice 想搜索其他用户
|
|
|
|
Alice 发送请求:
|
|
{
|
|
"sub": {
|
|
"topic": "fnd",
|
|
"set": {
|
|
"desc": {
|
|
"public": {"tags": ["Go", "程序员"]}
|
|
}
|
|
},
|
|
"get": {
|
|
"what": "desc",
|
|
"desc": {"limit": 20}
|
|
}
|
|
}
|
|
}
|
|
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ FND Topic: fnd │
|
|
│ │
|
|
│ 根据用户设置的搜索条件,返回匹配的用户列表 │
|
|
│ │
|
|
│ 搜索逻辑: │
|
|
│ 1. Alice 设置 tags: ["Go", "程序员"] │
|
|
│ 2. 服务器搜索所有包含这些 tags 的用户 │
|
|
│ 3. 返回匹配的用户列表 │
|
|
│ │
|
|
│ 返回: │
|
|
│ - usrB (Bob) - tags: ["Go", "后端"] │
|
|
│ - usrC (Carol) - tags: ["程序员", "Java"] │
|
|
│ - usrD (Dave) - tags: ["Go", "架构师"] │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**关键特性**:
|
|
|
|
| 特性 | 说明 |
|
|
|------|------|
|
|
| 搜索专用 | 用于搜索其他用户 |
|
|
| 基于标签 | 通过 tags 进行搜索匹配 |
|
|
| 单用户 | 只有搜索者自己订阅 |
|
|
| 不持久化 | 搜索结果不存储 |
|
|
|
|
**搜索请求示例**:
|
|
|
|
```javascript
|
|
// 订阅 fnd 话题并设置搜索条件
|
|
{
|
|
"sub": {
|
|
"id": "search123",
|
|
"topic": "fnd",
|
|
"set": {
|
|
"desc": {
|
|
"public": {
|
|
"tags": ["Go", "后端", "架构师"]
|
|
}
|
|
}
|
|
},
|
|
"get": {
|
|
"what": "desc"
|
|
}
|
|
}
|
|
}
|
|
|
|
// 服务器返回匹配的用户
|
|
{
|
|
"pres": {
|
|
"topic": "fnd",
|
|
"what": "acs"
|
|
},
|
|
"desc": {
|
|
"topic": "usrB",
|
|
"public": {
|
|
"fn": "Bob",
|
|
"tags": ["Go", "后端"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 5. SLF 自我话题 (`slf`)
|
|
|
|
**命名格式**: `slf` + 唯一ID
|
|
|
|
**示例**: `slfNotes123`
|
|
|
|
```
|
|
用户: Alice
|
|
话题名: slfNotes123
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ SLF Topic: slfNotes123 │
|
|
│ │
|
|
│ 这是"给自己发消息"的话题 │
|
|
│ │
|
|
│ 用途: │
|
|
│ - 草稿箱 │
|
|
│ - 收藏的消息 │
|
|
│ - 个人备忘录 │
|
|
│ - 跨设备同步的笔记 │
|
|
│ │
|
|
│ 特点: │
|
|
│ - 只有自己一个订阅者 │
|
|
│ - 可以发消息给自己 │
|
|
│ - 消息只有自己能看到 │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**关键特性**:
|
|
|
|
| 特性 | 说明 |
|
|
|------|------|
|
|
| 个人笔记 | 给自己发消息 |
|
|
| 跨设备同步 | 手机、电脑都能看到 |
|
|
| 收藏功能 | 可以收藏其他话题的消息 |
|
|
| 私密 | 只有自己能看到 |
|
|
|
|
**使用场景**:
|
|
|
|
```javascript
|
|
// 保存一条笔记
|
|
{
|
|
"pub": {
|
|
"id": "note123",
|
|
"topic": "slfNotes123",
|
|
"content": "明天开会,记得带资料"
|
|
}
|
|
}
|
|
|
|
// 收藏一条消息到笔记
|
|
// 1. 先复制消息内容
|
|
// 2. 发送到 slf 话题
|
|
{
|
|
"pub": {
|
|
"id": "note456",
|
|
"topic": "slfNotes123",
|
|
"content": {
|
|
"from": "grpTechGroup",
|
|
"text": "重要的技术讨论记录..."
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 6. SYS 系统话题 (`sys`)
|
|
|
|
**命名格式**: 固定为 `sys`
|
|
|
|
```
|
|
系统全局广播
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ SYS Topic: sys │
|
|
│ │
|
|
│ 用于系统级通知 │
|
|
│ │
|
|
│ 用途: │
|
|
│ - 系统公告 │
|
|
│ - 维护通知 │
|
|
│ - 版本更新 │
|
|
│ - 紧急通知 │
|
|
│ │
|
|
│ 特点: │
|
|
│ - 只有 root 用户可以发送 │
|
|
│ - 广播给所有用户 │
|
|
│ - 强制推送 │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**关键特性**:
|
|
|
|
| 特性 | 说明 |
|
|
|------|------|
|
|
| 系统广播 | 发送系统级通知 |
|
|
| Root 专用 | 只有 root 用户可以发送 |
|
|
| 全员推送 | 广播给所有用户 |
|
|
| 不可取消 | 用户不能取消订阅 |
|
|
|
|
---
|
|
|
|
## 话题类型对比
|
|
|
|
| 话题类型 | 命名前缀 | 能发消息 | 持久化 | 订阅人数 | 用途 |
|
|
|---------|---------|---------|--------|---------|------|
|
|
| **P2P** | `p2p` | ✅ | ✅ | 2人 | 私聊 |
|
|
| **GRP** | `grp`/`chn` | ✅ | ✅ | 多人 | 群聊/频道 |
|
|
| **SLF** | `slf` | ✅ | ✅ | 1人 | 笔记/收藏 |
|
|
| **ME** | `usr` | ❌ | ❌ | 1人 | 个人信息/联系人 |
|
|
| **FND** | `fnd` | ❌ | ❌ | 1人 | 搜索用户 |
|
|
| **SYS** | `sys` | ✅ | ✅ | 所有人 | 系统广播 |
|
|
|
|
---
|
|
|
|
## 私聊 vs 群聊
|
|
|
|
### 核心差异
|
|
|
|
```
|
|
私聊 (P2P):
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ p2pusrAusrB │
|
|
│ │
|
|
│ • 订阅者固定为2人 (usrA + usrB) │
|
|
│ • 任一人发消息,另一人必收 │
|
|
│ • 最后一人取消订阅 → 话题删除 │
|
|
│ • 没有管理员概念,权限对等 │
|
|
│ • 订阅即创建,无需手动创建 │
|
|
│ • 适合私密对话 │
|
|
└─────────────────────────────────────────────────────┘
|
|
|
|
群聊 (GRP):
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ grpTechGroup123 │
|
|
│ │
|
|
│ • 订阅者可以是任意多人 │
|
|
│ • 有创建者、管理员、普通成员之分 │
|
|
│ • 创建者离开不会删除话题 │
|
|
│ • 可以踢人、禁言、设置权限 │
|
|
│ • 需要显式创建 │
|
|
│ • 适合团队协作、社区交流 │
|
|
└─────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 创建流程对比
|
|
|
|
**私聊创建**:
|
|
|
|
```
|
|
1. Alice 发送: {sub: {topic: "usrB"}}
|
|
2. 服务器:
|
|
- 解析目标: usrB
|
|
- 生成话题名: p2pusrAusrB
|
|
- 检查是否存在
|
|
- 不存在则创建
|
|
3. Alice 订阅成功
|
|
4. Bob 收到通知: {pres: {what: "knock"}}
|
|
5. Bob 订阅: {sub: {topic: "usrA"}}
|
|
6. 双方可以发消息
|
|
```
|
|
|
|
**群聊创建**:
|
|
|
|
```
|
|
1. Alice 发送: {
|
|
acc: {
|
|
"user": "new", // 创建新话题
|
|
"scheme": "basic",
|
|
"secret": "群密码",
|
|
"desc": {
|
|
"public": {
|
|
"fn": "技术交流群",
|
|
"note": "讨论技术问题"
|
|
},
|
|
"defacs": {
|
|
"auth": "JRWS", // 默认权限
|
|
"anon": "N"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
2. 服务器:
|
|
- 创建群话题: grpXXX
|
|
- Alice 自动成为拥有者
|
|
- 初始化群信息
|
|
3. Alice 可以邀请他人加入
|
|
```
|
|
|
|
---
|
|
|
|
## 话题核心数据结构
|
|
|
|
```go
|
|
// server/topic.go
|
|
type Topic struct {
|
|
// 话题名称
|
|
name string
|
|
|
|
// 话题类型
|
|
cat types.TopicCat
|
|
|
|
// 创建/更新时间
|
|
created time.Time
|
|
updated time.Time
|
|
|
|
// 最后消息ID
|
|
lastID int
|
|
|
|
// 拥有者
|
|
owner types.Uid
|
|
|
|
// 默认访问权限
|
|
accessAuth types.AccessMode // 认证用户默认权限
|
|
accessAnon types.AccessMode // 匿名用户默认权限
|
|
|
|
// 发现标签
|
|
tags []string
|
|
|
|
// 扩展数据
|
|
aux map[string]any
|
|
|
|
// 公开数据 (群名、头像等)
|
|
public any
|
|
trusted any
|
|
|
|
// 订阅者数据
|
|
perUser map[types.Uid]perUserData
|
|
|
|
// 会话列表
|
|
sessions map[*Session]perSessionData
|
|
|
|
// 消息通道
|
|
clientMsg chan *ClientComMessage
|
|
serverMsg chan *ServerComMessage
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 订阅者数据结构
|
|
|
|
```go
|
|
// 每个用户在话题中的数据
|
|
type perUserData struct {
|
|
// 权限
|
|
modeWant types.AccessMode // 用户想要的权限
|
|
modeGiven types.AccessMode // 话题给予的权限
|
|
// 实际权限 = modeWant & modeGiven
|
|
|
|
// 状态
|
|
deleted bool
|
|
|
|
// 私有数据
|
|
private any
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 访问权限说明
|
|
|
|
权限用字符串表示,如 `"JRWS"`:
|
|
|
|
| 权限 | 含义 | 说明 |
|
|
|------|------|------|
|
|
| J | Join | 加入话题 |
|
|
| R | Read | 读取消息 |
|
|
| W | Write | 发送消息 |
|
|
| P | Presence | 接收在线状态 |
|
|
| A | Admin | 管理权限 |
|
|
| S | Share | 分享权限 |
|
|
| D | Delete | 删除权限 |
|
|
| O | Owner | 拥有者权限 |
|
|
|
|
**权限组合示例**:
|
|
|
|
- `"JRWP"` - 普通成员:可以加群、读消息、发消息、看在线状态
|
|
- `"JRWS"` - 可分享成员:额外可以分享群链接
|
|
- `"JRWPA"` - 管理员:额外可以管理成员
|
|
- `"JRWPAO"` - 拥有者:完全控制
|
|
|
|
---
|
|
|
|
## 消息流转
|
|
|
|
### 发布消息流程
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ 消息发布流程 │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ① 客户端发送 │
|
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ {pub: {topic: "grpTechGroup", content: "Hello!"}} │ │
|
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ② Session.dispatch() → Session.publish() │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ③ Hub 路由到 Topic │
|
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ hub.topicGet("grpTechGroup") → topic │ │
|
|
│ │ topic.clientMsg <- msg │ │
|
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ④ Topic 处理消息 │
|
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ 1. 检查发送者权限 (W) │ │
|
|
│ │ 2. 生成消息ID (lastID++) │ │
|
|
│ │ 3. 存储到数据库 │ │
|
|
│ │ 4. 准备广播 │ │
|
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ⑤ 广播给所有订阅者 │
|
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ for session := range topic.sessions { │ │
|
|
│ │ session.send <- {data: {topic: "grpTechGroup", content: ...}} │ │
|
|
│ │ } │ │
|
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ⑥ 订阅者的 writeLoop 发送 │
|
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ ws.WriteMessage(TextMessage, JSON) │ │
|
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 总结
|
|
|
|
Tinode 的话题系统是其核心架构:
|
|
|
|
1. **统一模型**: 私聊、群聊、个人信息都通过话题实现
|
|
2. **命名规则**: 通过前缀区分类型 (`p2p`, `grp`, `usr` 等)
|
|
3. **权限控制**: 细粒度的访问控制 (J/R/W/P/A/S/D/O)
|
|
4. **发布订阅**: 消息通过话题广播给所有订阅者
|
|
|
|
理解话题系统是理解 Tinode 整体架构的关键。 |