@wenbin_wb/dsh-bridge 2.5.0 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +122 -19
- package/README.md +122 -21
- package/client/client.js +308 -28
- package/client/index.js +283 -32
- package/docs/banner.jpg +0 -0
- package/docs/feishu-usage.md +5 -0
- package/docs/platform-abstraction-design.md +473 -0
- package/docs/screenshots/admin-lock-screen.jpg +0 -0
- package/docs/screenshots/feishu-bot-config.jpg +0 -0
- package/docs/screenshots/feishu-chat.jpg +0 -0
- package/docs/screenshots/lan-access.jpg +0 -0
- package/docs/screenshots/qq-bot-config.jpg +0 -0
- package/docs/screenshots/qq-chat.jpg +0 -0
- package/docs/screenshots/qq-group.jpg +0 -0
- package/docs/screenshots/qr-scan.jpg +0 -0
- package/docs/screenshots/remote-auth-login.jpg +0 -0
- package/docs/screenshots/remote-web-mobile.jpg +0 -0
- package/docs/screenshots/security-auth-config.jpg +0 -0
- package/docs/screenshots/telegram-bot-config.jpg +0 -0
- package/docs/screenshots/tunnel-access.jpg +0 -0
- package/docs/screenshots/wechat-bot-config.jpg +0 -0
- package/docs/screenshots/wechat-chat.jpg +0 -0
- package/docs/telegram-usage.md +2 -0
- package/docs/wechat-bot-plan.md +294 -0
- package/lib/auth/manager.js +63 -18
- package/lib/bridge-rpc.js +95 -8
- package/lib/feishu/node.js +4 -4
- package/lib/index.js +91 -22
- package/lib/qq/node.js +9 -2
- package/lib/telegram/node.js +4 -4
- package/lib/tunnel-client.mjs +11 -2
- package/lib/wechat/media.js +8 -2
- package/lib/wechat/node.js +2 -2
- package/package.json +11 -12
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
# 平台抽象层设计方案
|
|
2
|
+
|
|
3
|
+
## 目标
|
|
4
|
+
|
|
5
|
+
将 `dsh-bridge` 从单一微信集成重构为支持多 IM 平台的架构:
|
|
6
|
+
- 抽象出平台无关的核心逻辑(会话管理、消息路由、审批流程)
|
|
7
|
+
- 定义统一的平台接口(Platform Interface)
|
|
8
|
+
- 每个 IM 平台实现自己的适配器(WeChat / QQ / Feishu / Telegram)
|
|
9
|
+
- UI 支持多平台并存,每个平台独立 Tab
|
|
10
|
+
|
|
11
|
+
## 现状分析
|
|
12
|
+
|
|
13
|
+
### 当前架构(v1.2.5,WeChat 专用)
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
lib/
|
|
17
|
+
index.js 主插件入口,注册服务 + RPC
|
|
18
|
+
bridge-rpc.js Loopback RPC(浏览器 ⇄ Node)
|
|
19
|
+
wechat/
|
|
20
|
+
index.js WechatService 协调器
|
|
21
|
+
gateway.js iLink 协议客户端(扫码/收发/typing)
|
|
22
|
+
node.js WechatConversationNode 会话桥
|
|
23
|
+
media.js AES-128-ECB 媒体加解密(v0.1 未用)
|
|
24
|
+
client/
|
|
25
|
+
index.js React UI(单一 WeChat 面板)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 核心逻辑分层
|
|
29
|
+
|
|
30
|
+
#### 1. **平台协议层**(gateway.js)
|
|
31
|
+
- 协议细节:iLink API、长轮询、扫码登录、消息收发
|
|
32
|
+
- 状态机:idle → connecting → online → offline
|
|
33
|
+
- 凭证管理:token/accountId 持久化
|
|
34
|
+
- **平台特定**:每个 IM 平台协议完全不同
|
|
35
|
+
|
|
36
|
+
#### 2. **会话桥接层**(node.js)
|
|
37
|
+
- **平台通用**部分:
|
|
38
|
+
- 白名单验证(allowFrom)
|
|
39
|
+
- DSH 会话生命周期(创建/切换/停止/恢复)
|
|
40
|
+
- 审批问答(approval request/response)
|
|
41
|
+
- 出站 digest 摘要
|
|
42
|
+
- 工作区选择
|
|
43
|
+
- **平台特定**部分:
|
|
44
|
+
- 消息格式转换(IM → DSH UserMessage)
|
|
45
|
+
- 出站分块策略(微信 2000 字符限制)
|
|
46
|
+
- 媒体处理(图片/文件/语音)
|
|
47
|
+
|
|
48
|
+
#### 3. **UI 层**(client/index.js)
|
|
49
|
+
- 扫码登录流程
|
|
50
|
+
- 状态展示(在线/离线/会话信息)
|
|
51
|
+
- 白名单配置
|
|
52
|
+
- 参数调整(digest 间隔、审批超时)
|
|
53
|
+
|
|
54
|
+
## 重构方案
|
|
55
|
+
|
|
56
|
+
### 架构图
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
lib/
|
|
60
|
+
index.js # 主插件入口
|
|
61
|
+
bridge-rpc.js # RPC 层(保持不变)
|
|
62
|
+
|
|
63
|
+
platform/
|
|
64
|
+
base.js # Platform 基类(抽象接口)
|
|
65
|
+
manager.js # PlatformManager(多平台协调)
|
|
66
|
+
conversation-bridge.js # 平台无关的会话桥逻辑
|
|
67
|
+
|
|
68
|
+
platforms/
|
|
69
|
+
wechat/
|
|
70
|
+
index.js # WechatPlatform extends Platform
|
|
71
|
+
gateway.js # iLink 协议(保持不变)
|
|
72
|
+
adapter.js # WeChat 消息适配器
|
|
73
|
+
media.js # 媒体处理(保持不变)
|
|
74
|
+
|
|
75
|
+
qq/
|
|
76
|
+
index.js # QQPlatform extends Platform
|
|
77
|
+
gateway.js # QQ Bot API
|
|
78
|
+
adapter.js # QQ 消息适配器
|
|
79
|
+
|
|
80
|
+
feishu/
|
|
81
|
+
index.js # FeishuPlatform extends Platform
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
client/
|
|
85
|
+
index.js # 重构:多 Tab UI
|
|
86
|
+
components/
|
|
87
|
+
PlatformPanel.js # 通用平台面板组件
|
|
88
|
+
WechatPanel.js # 微信专用配置
|
|
89
|
+
QQPanel.js # QQ 专用配置
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 核心接口设计
|
|
93
|
+
|
|
94
|
+
#### Platform 基类
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
// lib/platform/base.js
|
|
98
|
+
export class Platform {
|
|
99
|
+
constructor({ ctx, logger, config, onPersist }) {
|
|
100
|
+
this.ctx = ctx
|
|
101
|
+
this.logger = logger
|
|
102
|
+
this.config = config
|
|
103
|
+
this.onPersist = onPersist
|
|
104
|
+
|
|
105
|
+
// 平台标识
|
|
106
|
+
this.id = '' // 'wechat' | 'qq' | 'feishu'
|
|
107
|
+
this.name = '' // '微信' | 'QQ' | '飞书'
|
|
108
|
+
this.icon = '' // emoji or icon name
|
|
109
|
+
|
|
110
|
+
// 状态
|
|
111
|
+
this.status = 'idle' // idle | connecting | online | offline | error
|
|
112
|
+
this.accountId = null
|
|
113
|
+
|
|
114
|
+
// 会话桥
|
|
115
|
+
this.bridge = null // ConversationBridge 实例
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// --- 生命周期 ---
|
|
119
|
+
async start() { throw new Error('Not implemented') }
|
|
120
|
+
async stop() { throw new Error('Not implemented') }
|
|
121
|
+
dispose() { throw new Error('Not implemented') }
|
|
122
|
+
|
|
123
|
+
// --- 登录 ---
|
|
124
|
+
async login(opts) { throw new Error('Not implemented') }
|
|
125
|
+
getLoginState() { throw new Error('Not implemented') }
|
|
126
|
+
|
|
127
|
+
// --- 消息收发 ---
|
|
128
|
+
async sendText(peerId, text, opts) { throw new Error('Not implemented') }
|
|
129
|
+
async sendTyping(peerId, state) { throw new Error('Not implemented') }
|
|
130
|
+
async sendMedia(peerId, media) { throw new Error('Not implemented') }
|
|
131
|
+
|
|
132
|
+
// --- 配置 ---
|
|
133
|
+
getStatus() { throw new Error('Not implemented') }
|
|
134
|
+
async setAllowFrom(list) { throw new Error('Not implemented') }
|
|
135
|
+
async updateConfig(patch) { throw new Error('Not implemented') }
|
|
136
|
+
|
|
137
|
+
// --- 平台特定能力 ---
|
|
138
|
+
get capabilities() {
|
|
139
|
+
return {
|
|
140
|
+
supportsGroup: false, // 是否支持群聊
|
|
141
|
+
supportsMedia: false, // 是否支持媒体
|
|
142
|
+
supportsVoice: false, // 是否支持语音
|
|
143
|
+
supportsTyping: false, // 是否支持 typing 状态
|
|
144
|
+
maxMessageChars: 2000, // 单条消息最大字符数
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### ConversationBridge(平台无关)
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
// lib/platform/conversation-bridge.js
|
|
154
|
+
export class ConversationBridge {
|
|
155
|
+
constructor({ ctx, platform, logger, config }) {
|
|
156
|
+
this.ctx = ctx
|
|
157
|
+
this.platform = platform // Platform 实例(用于发送消息)
|
|
158
|
+
this.logger = logger
|
|
159
|
+
this.config = config
|
|
160
|
+
|
|
161
|
+
// 平台无关的状态
|
|
162
|
+
this.allowFrom = config.allowFrom ?? []
|
|
163
|
+
this.peerId = null // 当前对话的 peer
|
|
164
|
+
this.activeSessionId = null
|
|
165
|
+
this.pendingApprovals = new Map()
|
|
166
|
+
|
|
167
|
+
// 定时器
|
|
168
|
+
this.digestTimer = null
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// --- 消息处理(平台无关逻辑)---
|
|
172
|
+
async handleInboundMessage(message) {
|
|
173
|
+
// 1. 白名单验证
|
|
174
|
+
if (!this.isAllowed(message.senderId)) {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 2. 路由到当前会话或创建新会话
|
|
179
|
+
const session = await this.getOrCreateSession()
|
|
180
|
+
|
|
181
|
+
// 3. 转换为 DSH UserMessage(调用平台适配器)
|
|
182
|
+
const userMessage = await this.platform.adapter.toDSHMessage(message)
|
|
183
|
+
|
|
184
|
+
// 4. 发送到 agent
|
|
185
|
+
await this.sendToAgent(session, userMessage)
|
|
186
|
+
|
|
187
|
+
// 5. 启动 digest 定时器
|
|
188
|
+
this.scheduleDigest()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// --- 审批处理(平台无关)---
|
|
192
|
+
async handleApprovalRequest(request) { /* ... */ }
|
|
193
|
+
async handleApprovalResponse(senderId, response) { /* ... */ }
|
|
194
|
+
|
|
195
|
+
// --- Digest 摘要(平台无关)---
|
|
196
|
+
async sendDigest(session) { /* ... */ }
|
|
197
|
+
|
|
198
|
+
// --- 会话管理(平台无关)---
|
|
199
|
+
async createSession(cwd) { /* ... */ }
|
|
200
|
+
async switchSession(sessionId) { /* ... */ }
|
|
201
|
+
async stopSession() { /* ... */ }
|
|
202
|
+
|
|
203
|
+
// --- 工作区选择(平台无关)---
|
|
204
|
+
async listWorkspaces() { /* ... */ }
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### MessageAdapter(平台特定)
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
// lib/platforms/wechat/adapter.js
|
|
212
|
+
export class WechatMessageAdapter {
|
|
213
|
+
// IM 消息 → DSH UserMessage
|
|
214
|
+
async toDSHMessage(wechatMessage) {
|
|
215
|
+
const content = []
|
|
216
|
+
|
|
217
|
+
for (const item of wechatMessage.items) {
|
|
218
|
+
if (item.item_type === ITEM_TEXT) {
|
|
219
|
+
content.push({ type: 'text', text: item.text })
|
|
220
|
+
} else if (item.item_type === ITEM_IMAGE) {
|
|
221
|
+
// 下载 + 解密
|
|
222
|
+
const imageData = await this.downloadMedia(item.item_content)
|
|
223
|
+
content.push({ type: 'image', source: { ... } })
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return createUserMessage({ content })
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// DSH 消息 → IM 格式(出站分块)
|
|
231
|
+
async toIMChunks(assistantMessage, maxChars) {
|
|
232
|
+
const text = textOfAssistantMessage(assistantMessage)
|
|
233
|
+
return splitForWechat(text, maxChars)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 下载媒体
|
|
237
|
+
async downloadMedia(url) { /* ... */ }
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### PlatformManager(多平台协调)
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
// lib/platform/manager.js
|
|
245
|
+
export class PlatformManager {
|
|
246
|
+
constructor({ ctx, logger, config, onPersist }) {
|
|
247
|
+
this.ctx = ctx
|
|
248
|
+
this.logger = logger
|
|
249
|
+
this.platforms = new Map() // platformId → Platform 实例
|
|
250
|
+
|
|
251
|
+
// 注册平台
|
|
252
|
+
this.register(new WechatPlatform({ ctx, logger, config: config.wechat, onPersist }))
|
|
253
|
+
// this.register(new QQPlatform({ ... }))
|
|
254
|
+
// this.register(new FeishuPlatform({ ... }))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
register(platform) {
|
|
258
|
+
this.platforms.set(platform.id, platform)
|
|
259
|
+
|
|
260
|
+
// 如果已配置,自动启动
|
|
261
|
+
if (platform.config?.token) {
|
|
262
|
+
void platform.start()
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
get(platformId) {
|
|
267
|
+
return this.platforms.get(platformId)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
getStatus() {
|
|
271
|
+
const status = {}
|
|
272
|
+
for (const [id, platform] of this.platforms) {
|
|
273
|
+
status[id] = platform.getStatus()
|
|
274
|
+
}
|
|
275
|
+
return status
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
dispose() {
|
|
279
|
+
for (const platform of this.platforms.values()) {
|
|
280
|
+
platform.dispose()
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### RPC 接口调整
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
// bridge-rpc.js(新增 platformId 参数)
|
|
290
|
+
|
|
291
|
+
// 旧:getWechatStatus() → 新:getPlatformStatus(platformId)
|
|
292
|
+
async getPlatformStatus({ platformId }) {
|
|
293
|
+
const platform = ctx.platformManager.get(platformId)
|
|
294
|
+
return platform?.getStatus() ?? null
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 旧:wechatLogin(opts) → 新:platformLogin({ platformId, opts })
|
|
298
|
+
async platformLogin({ platformId, opts }) {
|
|
299
|
+
const platform = ctx.platformManager.get(platformId)
|
|
300
|
+
return await platform.login(opts)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 新增:listPlatforms()
|
|
304
|
+
async listPlatforms() {
|
|
305
|
+
return ctx.platformManager.getStatus()
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### UI 重构(多 Tab)
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
// client/index.js(伪代码)
|
|
313
|
+
|
|
314
|
+
function RemoteAccessPanel() {
|
|
315
|
+
const [platforms, setPlatforms] = useState({})
|
|
316
|
+
const [activeTab, setActiveTab] = useState('wechat')
|
|
317
|
+
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
// 轮询所有平台状态
|
|
320
|
+
const timer = setInterval(async () => {
|
|
321
|
+
const status = await rpc('listPlatforms')
|
|
322
|
+
setPlatforms(status)
|
|
323
|
+
}, 2000)
|
|
324
|
+
return () => clearInterval(timer)
|
|
325
|
+
}, [])
|
|
326
|
+
|
|
327
|
+
return h('div', null,
|
|
328
|
+
// Tab 导航
|
|
329
|
+
h('div', { style: tabNav },
|
|
330
|
+
h('button', { onClick: () => setActiveTab('wechat') }, '微信'),
|
|
331
|
+
h('button', { onClick: () => setActiveTab('qq') }, 'QQ'),
|
|
332
|
+
h('button', { onClick: () => setActiveTab('feishu') }, '飞书'),
|
|
333
|
+
),
|
|
334
|
+
|
|
335
|
+
// 当前 Tab 面板
|
|
336
|
+
activeTab === 'wechat' && h(WechatPanel, { status: platforms.wechat }),
|
|
337
|
+
activeTab === 'qq' && h(QQPanel, { status: platforms.qq }),
|
|
338
|
+
activeTab === 'feishu' && h(FeishuPanel, { status: platforms.feishu }),
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 通用平台面板组件
|
|
343
|
+
function PlatformPanel({ platform, status, children }) {
|
|
344
|
+
return h('div', { style: card },
|
|
345
|
+
h('h3', null, platform.name),
|
|
346
|
+
h('div', null, `状态:${status.status}`),
|
|
347
|
+
h('div', null, `账号:${status.accountId ?? '未登录'}`),
|
|
348
|
+
|
|
349
|
+
// 平台特定配置(通过 children 插槽)
|
|
350
|
+
children,
|
|
351
|
+
)
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## 迁移路径
|
|
356
|
+
|
|
357
|
+
### 阶段 1:抽取平台无关逻辑
|
|
358
|
+
1. 创建 `lib/platform/base.js`(Platform 基类)
|
|
359
|
+
2. 创建 `lib/platform/conversation-bridge.js`(从 `node.js` 提取)
|
|
360
|
+
3. 创建 `lib/platform/manager.js`(PlatformManager)
|
|
361
|
+
|
|
362
|
+
### 阶段 2:重构 WeChat 为平台适配器
|
|
363
|
+
1. 创建 `lib/platforms/wechat/index.js`(WechatPlatform extends Platform)
|
|
364
|
+
2. 创建 `lib/platforms/wechat/adapter.js`(WechatMessageAdapter)
|
|
365
|
+
3. 移动 `lib/wechat/*` → `lib/platforms/wechat/*`
|
|
366
|
+
4. 重构 `WechatService` → `WechatPlatform`(实现 Platform 接口)
|
|
367
|
+
|
|
368
|
+
### 阶段 3:更新主插件 + RPC
|
|
369
|
+
1. `lib/index.js`:从 `WechatService` 改为 `PlatformManager`
|
|
370
|
+
2. `lib/bridge-rpc.js`:添加 `platformId` 参数到所有 RPC 方法
|
|
371
|
+
3. 保持向后兼容:`getWechatStatus()` 内部调用 `getPlatformStatus({ platformId: 'wechat' })`
|
|
372
|
+
|
|
373
|
+
### 阶段 4:重构 UI
|
|
374
|
+
1. 创建 `client/components/PlatformPanel.js`(通用组件)
|
|
375
|
+
2. 创建 `client/components/WechatPanel.js`(微信专用)
|
|
376
|
+
3. 重构 `client/index.js`:多 Tab 布局
|
|
377
|
+
|
|
378
|
+
### 阶段 5:测试 + 发版
|
|
379
|
+
1. 所有现有测试必须通过(WeChat 功能不能退化)
|
|
380
|
+
2. 验证 UI 在单平台(WeChat)下与之前行为一致
|
|
381
|
+
3. 发布 v2.0.0(大版本变更:架构重构)
|
|
382
|
+
|
|
383
|
+
### 阶段 6:实现第二个平台(QQ)
|
|
384
|
+
1. 创建 `lib/platforms/qq/*`
|
|
385
|
+
2. 实现 `QQPlatform` + `QQMessageAdapter`
|
|
386
|
+
3. 添加 `client/components/QQPanel.js`
|
|
387
|
+
4. 验证多平台并存
|
|
388
|
+
|
|
389
|
+
## 设计原则
|
|
390
|
+
|
|
391
|
+
1. **单一职责**:
|
|
392
|
+
- Platform:协议 + 连接管理
|
|
393
|
+
- ConversationBridge:会话逻辑 + DSH 集成
|
|
394
|
+
- MessageAdapter:消息格式转换
|
|
395
|
+
|
|
396
|
+
2. **开放封闭**:
|
|
397
|
+
- 新增平台无需修改核心逻辑
|
|
398
|
+
- Platform 接口固定,平台内部实现自由
|
|
399
|
+
|
|
400
|
+
3. **依赖注入**:
|
|
401
|
+
- Platform 通过构造函数注入 ctx/logger/config
|
|
402
|
+
- ConversationBridge 注入 Platform 实例
|
|
403
|
+
|
|
404
|
+
4. **向后兼容**:
|
|
405
|
+
- 持久化配置结构保持:`config.wechat` / `config.qq`
|
|
406
|
+
- RPC 方法添加新参数,旧方法 deprecated 但保留
|
|
407
|
+
|
|
408
|
+
5. **测试覆盖**:
|
|
409
|
+
- Platform 基类可 mock(方便测试 ConversationBridge)
|
|
410
|
+
- 每个平台独立测试套件
|
|
411
|
+
|
|
412
|
+
## 风险与缓解
|
|
413
|
+
|
|
414
|
+
| 风险 | 影响 | 缓解措施 |
|
|
415
|
+
|------|------|----------|
|
|
416
|
+
| 重构破坏现有 WeChat 功能 | 高 | 保持所有测试通过;灰度发布 |
|
|
417
|
+
| 抽象层过度设计 | 中 | 先实现 WeChat + QQ 两个平台验证接口合理性 |
|
|
418
|
+
| UI 重构影响用户体验 | 中 | 保持单平台下 UI 与 v1.x 一致 |
|
|
419
|
+
| 配置迁移问题 | 低 | 保持配置结构兼容;自动迁移脚本 |
|
|
420
|
+
|
|
421
|
+
## 实施进度
|
|
422
|
+
|
|
423
|
+
### ✅ 阶段 1:抽取平台无关逻辑(已完成)
|
|
424
|
+
- ✅ 创建 `lib/platform/base.js`(Platform 基类)
|
|
425
|
+
- ✅ 创建 `lib/platform/conversation-bridge.js`(从 `node.js` 提取)
|
|
426
|
+
- ✅ 创建 `lib/platform/manager.js`(PlatformManager)
|
|
427
|
+
- ✅ 13 个平台抽象层测试通过
|
|
428
|
+
- 提交:`e31d723`
|
|
429
|
+
|
|
430
|
+
### ✅ 阶段 2:重构 WeChat 为平台适配器(已完成)
|
|
431
|
+
- ✅ `lib/wechat/index.js` → `WechatPlatform extends Platform`
|
|
432
|
+
- ✅ `lib/wechat/node.js` 继承 `ConversationBridge`,仅保留微信特定逻辑
|
|
433
|
+
- ✅ 通过 `makePlatform(ctx)` 适配对象桥接 gateway → Platform 接口
|
|
434
|
+
- ✅ 保持 `config.wechat` 配置结构不变
|
|
435
|
+
- ✅ 45/45 测试通过(零退化)
|
|
436
|
+
- 提交:`e31d723`
|
|
437
|
+
|
|
438
|
+
### ✅ 阶段 3:更新主插件 + RPC(已完成)
|
|
439
|
+
- ✅ `lib/index.js` 使用 `PlatformManager` 管理所有平台
|
|
440
|
+
- ✅ 添加新端点:`listPlatforms` / `platformLogin` / `platformSetAllowFrom` / `platformSetConfig` / `platformStop` / `platformUnbind`
|
|
441
|
+
- ✅ 保留旧端点向后兼容:`wechatGetStatus` / `wechatLogin` 等(内部调用新端点)
|
|
442
|
+
- ✅ RPC 方法添加 `platformId` 参数
|
|
443
|
+
- ✅ 47/47 测试通过
|
|
444
|
+
- 提交:`e31d723`
|
|
445
|
+
|
|
446
|
+
### ✅ 阶段 4:重构 UI(已完成)
|
|
447
|
+
- ✅ 创建 `PlatformCard` 通用组件(替代 `WechatCard`)
|
|
448
|
+
- ✅ 支持动态平台选择(wechat / qq / feishu)
|
|
449
|
+
- ✅ 平台选择器从 `listPlatforms` RPC 动态读取状态(available / connected / starting)
|
|
450
|
+
- ✅ 平台选择器显示连接状态绿点,可点击切换
|
|
451
|
+
- ✅ 通过 `platformId` / `platformName` / `platformDesc` 参数化组件
|
|
452
|
+
- ✅ 保留微信使用说明链接(`platformId === 'wechat'` 时)
|
|
453
|
+
- ✅ 客户端构建成功,47/47 测试通过
|
|
454
|
+
- 提交:`8b98ef5`
|
|
455
|
+
|
|
456
|
+
### ✅ 阶段 5:测试 + 发版 v2.0.0(进行中)
|
|
457
|
+
- ✅ 所有单元测试通过:47/47
|
|
458
|
+
- ✅ 客户端构建成功
|
|
459
|
+
- ✅ 架构完整性验证:Platform 抽象层 / ConversationBridge / PlatformManager / 统一 RPC / 多平台 UI
|
|
460
|
+
- ✅ 向后兼容性验证:v1.x `wechat*` 端点保留,配置结构不变
|
|
461
|
+
- 🔄 更新文档(CHANGELOG / README / 设计文档)
|
|
462
|
+
- ⏳ 准备发布 npm 包 v2.0.0
|
|
463
|
+
- 提交:`[待提交]`
|
|
464
|
+
|
|
465
|
+
### ⏳ 阶段 6:实现第二个平台(QQ)
|
|
466
|
+
- [ ] 创建 `lib/qq/index.js`(QQPlatform)
|
|
467
|
+
- [ ] 实现 QQ Bot 协议(NapCat / Mirai)
|
|
468
|
+
- [ ] 添加 QQ 特定消息解析
|
|
469
|
+
- [ ] 验证多平台并存
|
|
470
|
+
|
|
471
|
+
## 下一步行动
|
|
472
|
+
|
|
473
|
+
阶段 5 完成,准备提交并发布 v2.0.0。阶段 6(QQ 平台)可作为独立任务开始。
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|