docs: 新增PRD规范与WechatOnCloud改造、多应用桥接框架文档
新增三份文档: 1. 产品需求文档(PRD)编写规范 2. WechatOnCloud容器化微信改造需求方案 3. 多应用桥接框架整体设计方案
This commit is contained in:
parent
3effe1338c
commit
67aab58a00
1405
doc/bridge-prd-v1.0.md
Normal file
1405
doc/bridge-prd-v1.0.md
Normal file
File diff suppressed because it is too large
Load Diff
264
doc/产品需求文档规范.md
Normal file
264
doc/产品需求文档规范.md
Normal file
@ -0,0 +1,264 @@
|
||||
# 产品需求文档(PRD)规范
|
||||
|
||||
> 本规范用于约束 ForcePilot 平台产品需求文档(Product Requirements Document,简称 PRD)的编写、评审与维护,确保需求表达清晰、可追溯、可验收。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目的与适用范围
|
||||
|
||||
### 1.1 目的
|
||||
- 统一 PRD 的结构、粒度与写作风格,降低沟通成本
|
||||
- 明确需求从提出到交付的文档化要求,支撑研发、测试、设计协同
|
||||
- 为后续的需求变更管理、验收与回溯提供基线
|
||||
|
||||
### 1.2 适用范围
|
||||
- 适用于 ForcePilot 平台所有新增功能、功能优化、重构类需求
|
||||
- Bug 修复、文案调整等小改动可使用精简版 PRD(见第 7 节)
|
||||
- 紧急线上故障修复可先口头/IM 沟通,事后补齐文档
|
||||
|
||||
---
|
||||
|
||||
## 2. 文档基本信息
|
||||
|
||||
每份 PRD 必须包含以下元信息:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| 文档标题 | 简洁明确,体现功能主体与意图 |
|
||||
| 文档版本 | 采用 `v主版本.次版本`,如 v1.0、v1.1 |
|
||||
| 作者 | 姓名 + 工号/账号 |
|
||||
| 创建日期 | YYYY-MM-DD |
|
||||
| 最后更新日期 | YYYY-MM-DD |
|
||||
| 状态 | 草稿 / 评审中 / 已确认 / 已归档 |
|
||||
| 关联需求 | 关联的需求 ID、Issue 链接或 PR 链接 |
|
||||
| 评审人 | 产品、研发、测试、设计等关键评审人 |
|
||||
|
||||
---
|
||||
|
||||
## 3. PRD 标准结构
|
||||
|
||||
一份完整的 PRD 应按以下顺序组织章节,无内容的章节标注「不涉及」并保留标题:
|
||||
|
||||
1. 背景与目标
|
||||
2. 名词解释
|
||||
3. 用户与场景
|
||||
4. 功能需求
|
||||
5. 非功能需求
|
||||
6. 交互与设计要求
|
||||
7. 数据与接口需求
|
||||
8. 验收标准
|
||||
9. 排期与里程碑
|
||||
10. 风险与依赖
|
||||
11. 变更记录
|
||||
|
||||
---
|
||||
|
||||
## 4. 各章节编写规范
|
||||
|
||||
### 4.1 背景与目标
|
||||
- **背景**:说明需求来源(用户反馈、业务目标、技术债等),避免空泛描述
|
||||
- **目标**:使用可度量的指标或明确的终态描述,避免「提升体验」这类无法验证的表述
|
||||
- **非目标**:明确本次不做的事项,防止范围蔓延
|
||||
|
||||
### 4.2 名词解释
|
||||
- 列出文档中出现的领域术语、缩写、业务概念
|
||||
- 与既有文档/代码中的术语保持一致,避免同义多词
|
||||
|
||||
### 4.3 用户与场景
|
||||
- 明确目标用户角色(如:知识库管理员、普通使用者、API 调用方)
|
||||
- 描述典型使用场景,采用「作为…我希望…以便…」的用户故事格式
|
||||
- 复杂流程需配流程图或时序图
|
||||
|
||||
### 4.4 功能需求
|
||||
- 采用**需求项编号**(如 FR-01、FR-02),便于评审与追溯
|
||||
- 每个需求项包含:
|
||||
- 需求描述
|
||||
- 输入 / 输出
|
||||
- 业务规则
|
||||
- 异常与边界情况
|
||||
- 优先级(P0 / P1 / P2)
|
||||
- 禁止将多个独立功能合并为一个需求项
|
||||
- 涉及权限的需求需明确角色与权限矩阵
|
||||
|
||||
### 4.5 非功能需求
|
||||
按需覆盖以下维度,无要求时显式标注「不涉及」:
|
||||
- 性能(响应时间、吞吐量、并发数)
|
||||
- 可用性(SLA、容灾)
|
||||
- 安全性(鉴权、数据加密、审计)
|
||||
- 兼容性(浏览器、API 版本、依赖服务版本)
|
||||
- 可观测性(日志、指标、告警)
|
||||
- 国际化与无障碍
|
||||
|
||||
### 4.6 交互与设计要求
|
||||
- 引用原型图/设计稿链接,避免在 PRD 中重复描述视觉细节
|
||||
- 明确关键交互逻辑(如:loading 态、空态、错误态、确认弹窗)
|
||||
- 与设计规范文档保持一致
|
||||
|
||||
### 4.7 数据与接口需求
|
||||
- 涉及新增/变更的数据模型需列出字段说明
|
||||
- 接口需求需说明:路径、方法、入参、出参、错误码
|
||||
- 与既有 API 规范保持一致,避免破坏性变更;如必须破坏需显式标注并给出迁移方案
|
||||
|
||||
### 4.8 验收标准
|
||||
- 每个 P0/P1 功能需求必须对应至少一条可执行的验收标准
|
||||
- 验收标准应可被测试用例直接覆盖,避免主观表述
|
||||
- 推荐使用 Given-When-Then 格式
|
||||
|
||||
### 4.9 排期与里程碑
|
||||
- 列出关键节点:设计完成、开发完成、联调完成、测试完成、上线
|
||||
- 标注负责人与预期日期,日期变更需同步更新变更记录
|
||||
|
||||
### 4.10 风险与依赖
|
||||
- 识别技术依赖、外部服务依赖、资源依赖
|
||||
- 识别潜在风险并给出应对策略
|
||||
|
||||
### 4.11 变更记录
|
||||
- 每次文档修订需追加一行:版本、日期、修改人、修改内容摘要
|
||||
- 已确认状态的文档变更需重新触发评审
|
||||
|
||||
---
|
||||
|
||||
## 5. 写作与格式规范
|
||||
|
||||
### 5.1 语言风格
|
||||
- 使用简洁的书面中文,避免口语化与歧义
|
||||
- 使用「必须 / 应当 / 可以」区分强制、推荐、可选级别
|
||||
- 术语统一,避免中英文混用造成的歧义
|
||||
|
||||
### 5.2 Markdown 格式
|
||||
- 标题层级不超过四级(`####`)
|
||||
- 表格用于结构化数据,列表用于步骤或枚举
|
||||
- 代码、字段名、接口路径使用反引号包裹
|
||||
- 流程图、时序图使用 Mermaid 语法,确保可渲染
|
||||
|
||||
### 5.3 图表规范
|
||||
- 所有图表需有图题与编号(如:图 1 用户登录流程)
|
||||
- 截图需标注来源与版本,避免使用过期截图
|
||||
- 图表中的文字应可被复制检索,关键流程图优先使用 Mermaid
|
||||
|
||||
### 5.4 链接规范
|
||||
- 引用内部文档使用相对路径
|
||||
- 引用代码位置使用可点击的文件链接
|
||||
- 外部链接需注明访问日期或版本
|
||||
|
||||
---
|
||||
|
||||
## 6. 优先级定义
|
||||
|
||||
| 级别 | 含义 | 验收要求 |
|
||||
| --- | --- | --- |
|
||||
| P0 | 必须完成,阻塞上线 | 必须有验收标准与测试用例 |
|
||||
| P1 | 应当完成,影响主流程 | 必须有验收标准 |
|
||||
| P2 | 可以完成,体验优化 | 可简化验收 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 精简版 PRD
|
||||
|
||||
适用于改动范围小、影响面有限的需求,至少包含:
|
||||
|
||||
1. 背景与目标(1-2 句)
|
||||
2. 功能需求(编号 + 描述 + 优先级)
|
||||
3. 验收标准
|
||||
4. 排期
|
||||
|
||||
---
|
||||
|
||||
## 8. 评审与维护流程
|
||||
|
||||
### 8.1 评审流程
|
||||
1. 作者完成草稿,状态置为「评审中」
|
||||
2. 产品、研发、测试、设计分别评审,提出问题在文档中批注
|
||||
3. 作者汇总意见并修订,更新版本号
|
||||
4. 全部意见 resolved 后,状态置为「已确认」,进入开发
|
||||
|
||||
### 8.2 变更管理
|
||||
- 已确认的 PRD 如需变更,必须更新「变更记录」并通知相关方
|
||||
- 涉及范围、排期、验收标准的变更需重新评审
|
||||
- 开发过程中发现的需求偏差,应在变更记录中记录并同步
|
||||
|
||||
### 8.3 归档
|
||||
- 功能上线且验收通过后,PRD 状态置为「已归档」
|
||||
- 归档文档不再修改,后续迭代新建版本或新文档
|
||||
|
||||
---
|
||||
|
||||
## 9. 存放与命名规范
|
||||
|
||||
### 9.1 存放位置
|
||||
- PRD 文档统一存放于 `docs/vibe/<版本号>/需求文档/` 目录下
|
||||
- 关联的设计稿、原型图链接至 `docs/vibe/<版本号>/原型图/` 目录
|
||||
|
||||
### 9.2 命名规范
|
||||
- 文件名格式:`<模块>-<功能>-PRD-v<版本>.md`
|
||||
- 示例:`knowledgebase-import-PRD-v1.0.md`
|
||||
- 全部使用小写英文与连字符,避免空格与中文文件名
|
||||
|
||||
---
|
||||
|
||||
## 10. 模板速查
|
||||
|
||||
```markdown
|
||||
# <功能名称> 产品需求文档
|
||||
|
||||
| 字段 | 内容 |
|
||||
| --- | --- |
|
||||
| 文档版本 | v1.0 |
|
||||
| 作者 | |
|
||||
| 创建日期 | |
|
||||
| 最后更新日期 | |
|
||||
| 状态 | 草稿 |
|
||||
| 关联需求 | |
|
||||
| 评审人 | |
|
||||
|
||||
## 1. 背景与目标
|
||||
### 1.1 背景
|
||||
### 1.2 目标
|
||||
### 1.3 非目标
|
||||
|
||||
## 2. 名词解释
|
||||
|
||||
## 3. 用户与场景
|
||||
|
||||
## 4. 功能需求
|
||||
### FR-01 <需求标题>
|
||||
- 描述:
|
||||
- 输入:
|
||||
- 输出:
|
||||
- 业务规则:
|
||||
- 异常与边界:
|
||||
- 优先级:P0
|
||||
|
||||
## 5. 非功能需求
|
||||
|
||||
## 6. 交互与设计要求
|
||||
|
||||
## 7. 数据与接口需求
|
||||
|
||||
## 8. 验收标准
|
||||
- AC-01:Given… When… Then…
|
||||
|
||||
## 9. 排期与里程碑
|
||||
|
||||
## 10. 风险与依赖
|
||||
|
||||
## 11. 变更记录
|
||||
| 版本 | 日期 | 修改人 | 摘要 |
|
||||
| --- | --- | --- | --- |
|
||||
| v1.0 | | | 初稿 |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 检查清单
|
||||
|
||||
PRD 提交评审前,作者需逐项确认:
|
||||
|
||||
- [ ] 文档元信息完整
|
||||
- [ ] 目标可度量,非目标已明确
|
||||
- [ ] 功能需求已编号且粒度合理
|
||||
- [ ] 每个 P0/P1 需求有对应验收标准
|
||||
- [ ] 非功能需求已逐项确认或标注「不涉及」
|
||||
- [ ] 接口与数据变更已标注破坏性影响
|
||||
- [ ] 图表可渲染、链接可访问
|
||||
- [ ] 命名与存放符合本规范
|
||||
939
doc/优化方案/01-多应用桥接框架设计.md
Normal file
939
doc/优化方案/01-多应用桥接框架设计.md
Normal file
@ -0,0 +1,939 @@
|
||||
# 多应用桥接框架设计
|
||||
|
||||
> 目标:将 bridge 从"微信专属 API 服务"演进为"容器内多桌面应用的统一桥接框架"。一个容器内可同时运行多个应用(微信 / 小红书 / Telegram / Chromium / 自定义),每个应用作为独立实例被 bridge 管理,对外暴露结构化接口。
|
||||
|
||||
---
|
||||
|
||||
> **阅读提示**:本文档是**目标架构设计**,同时用 `[现状]` 标注当前代码的实际状态。当前 bridge 仍是一个约 2400 行的单文件服务([bridge/server.py](file:///D:/WechatOnCloud-main/bridge/server.py)),全局单例、微信强耦合;docker 与 panel 层已支持"一容器一应用"的多应用类型,但尚未支持"单容器多应用"。因此本文先聚焦 bridge 层改造,使其在"多容器单应用"模式下即可工作,单容器多应用作为后续扩展。
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与转变
|
||||
|
||||
### 1.1 现状
|
||||
|
||||
bridge 当前是**单应用、单进程、强耦合微信**的 API 服务:
|
||||
|
||||
- [bridge/server.py](../bridge/server.py) 约 2400 行单文件,全局单例 `_state = AppState()` 持有唯一的 `XdotoolDriver`、`DbReader`、`SendQueue`、`QrCapture`。
|
||||
- 所有路由均为 `/api/*` 无前缀(如 `/api/send/text`、`/api/messages/since`),直接操作全局 `_state`,没有 `app_id` 概念。
|
||||
- 响应模型字段名微信中心化:`wechat_running`、`wechat_window_found`、错误码 `WECHAT_NOT_RUNNING` / `WECHAT_NOT_LOGGED_IN`。
|
||||
- DB 解密、联系人/消息读取、二维码登录全部写死微信表结构与 SQLCipher 参数。
|
||||
- 一容器只跑一个应用(由 [docker/app-defs.sh](../docker/app-defs.sh) 的 `WOC_APP_TYPE` 决定);想新增小红书/Telegram 自动化,当前只能再起一个容器。
|
||||
|
||||
### 1.2 转变
|
||||
|
||||
用户需求:**一个容器内同时跑多个应用,每个应用对外暴露接口**。
|
||||
|
||||
这要求 bridge 从"微信适配器"升格为"应用桥接框架":
|
||||
|
||||
| 维度 | 现状 | 目标 |
|
||||
|------|------|------|
|
||||
| 容器内应用数 | 1(由 `WOC_APP_TYPE` 决定) | N(动态注册) |
|
||||
| bridge 进程 | 跟随单一应用 | 一个 bridge 管理多个应用实例 |
|
||||
| 路由 | 微信专属路由无前缀 | 通用路由无前缀 + 每个应用实例独立命名空间 |
|
||||
| 状态 | 全局单例 `_state` | 按 app_id 隔离的 AppInstance 容器 |
|
||||
| 资源调度 | 不需要 | 必须串行化 X11 / 剪贴板 / 焦点操作 |
|
||||
|
||||
### 1.3 已具备的基础(非本阶段改造重点)
|
||||
|
||||
- docker 层已通过 `WOC_APP_TYPE` 支持多种应用类型:`wechat` / `telegram` / `chromium` / `custom`([docker/app-defs.sh](../docker/app-defs.sh))。
|
||||
- panel 的 `Instance.appType` 已能标识实例承载的应用类型,并透传给容器环境变量([panel/server/src/store.ts](../panel/server/src/store.ts))。
|
||||
- bridge 基于 FastAPI + Pydantic,已有统一的 `BridgeError` 异常模型与全局异常处理器,为路由拆分和错误码扩展提供了基础。
|
||||
|
||||
> **本文档范围**:仅聚焦 bridge 层改造;panel UI、docker 单容器多应用启动机制不在本阶段文档范围内。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心理念
|
||||
|
||||
### 2.1 三层抽象
|
||||
|
||||
```
|
||||
应用类型 (AppKind) — 静态描述:微信 / 小红书 / Telegram / Chromium / Custom
|
||||
↓ 实例化
|
||||
应用实例 (AppInstance) — 运行期实体:唯一 app_id、独立状态、独立窗口
|
||||
↓ 适配
|
||||
应用驱动 (AppDriver) — 该实例的能力实现:发消息 / 发笔记 / 读 DB / 截图
|
||||
```
|
||||
|
||||
- **AppKind** 是注册表项,定义"这类应用能做什么、怎么启动、需要什么依赖"
|
||||
- **AppInstance** 是运行期实体,一个容器内可以同时存在多个同类实例(如两个微信账号、两个小红书账号)
|
||||
- **AppDriver** 是实例的能力实现层,框架通过它操作具体应用
|
||||
|
||||
### 2.2 框架职责边界
|
||||
|
||||
**框架负责(应用无关)**:
|
||||
|
||||
- HTTP 服务、路由分发、鉴权、限流
|
||||
- 请求/响应日志、异常处理、错误码体系
|
||||
- X11 串行化调度(避免多应用并发争用同一个 X server)
|
||||
- 截图、剪贴板、窗口管理(通用 X11 能力)
|
||||
- 应用生命周期管理(启动 / 停止 / 重启 / 健康检查)
|
||||
- 诊断框架(通用检查项 + 应用自报检查项)
|
||||
- 配置管理、命令行参数、环境变量
|
||||
|
||||
**应用驱动负责(应用相关)**:
|
||||
|
||||
- 进程启动命令与参数
|
||||
- 窗口识别(按 title / class / PID)
|
||||
- 登录态检测与登录流程
|
||||
- 应用专属能力实现(发消息、发笔记、读 DB、搜索等)
|
||||
- 应用专属路由注册
|
||||
- 应用专属诊断项
|
||||
|
||||
> **[现状]** 当前所有职责都混在 [bridge/server.py](../bridge/server.py) 中,没有框架/驱动分层;`XdotoolDriver` 直接实现微信专用方法,`SendQueue` 是全局单例限流器,UIScheduler 尚未引入。
|
||||
|
||||
### 2.3 设计原则
|
||||
|
||||
1. **组合优于继承**:driver 持有通用工具实例(`self.xd`),不继承工具类
|
||||
2. **一应用一命名空间**:每个实例独立 `app_id`,路由与状态都按 `app_id` 隔离
|
||||
3. **声明式能力**:driver 声明能力集合,框架据此挂载路由与协商
|
||||
4. **串行化 X11**:所有窗口操作经统一 UIScheduler 排队执行
|
||||
5. **应用无关的 DB 抽象延迟**:SQLCipher 解密暂留微信 driver 内,待第二个 SQLCipher 应用出现再抽公共层
|
||||
6. **向后兼容过渡**:旧路径与新路径并存,alias 机制保证现有调用方不破坏
|
||||
|
||||
---
|
||||
|
||||
## 3. 总体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Docker 容器 │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ bridge 框架进程 (FastAPI, :8088) │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
||||
│ │ │ 通用路由层 │ │ UIScheduler │ │ AppManager │ │ │
|
||||
│ │ │ /api/status │ │ (X11 串行) │ │ (生命周期) │ │ │
|
||||
│ │ │ /api/apps │ │ │ │ │ │ │
|
||||
│ │ │ /api/screens │ └──────────────┘ └──────┬───────┘ │ │
|
||||
│ │ │ /api/diag/* │ │ │ │
|
||||
│ │ └──────────────┘ │ │ │
|
||||
│ │ ┌─────────────┼─────────┐ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ ┌───────────────────┴───┐ ┌──────┴───────┐ │ │
|
||||
│ │ │ AppInstance[wx_a1] │ │ AppInstance │ │ │
|
||||
│ │ │ └ WechatDriver │ │ [xhs_b2] │ │ │
|
||||
│ │ │ routes: /api/apps/ │ │ └ XhsDriver │ │ │
|
||||
│ │ │ wx_a1/* │ │ routes: │ │ │
|
||||
│ │ │ capabilities: │ │ /api/apps/ │ │ │
|
||||
│ │ │ text_send, │ │ xhs_b2/* │ │ │
|
||||
│ │ │ db_read, │ │ capabilities│ │ │
|
||||
│ │ │ login_qr │ │ publish, │ │ │
|
||||
│ │ └───────────────────────┘ │ search │ │ │
|
||||
│ │ └──────────────┘ │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ X server (Xvfb :1) ◀── 所有应用共用,UIScheduler 串行化调度 │
|
||||
│ ├─ 微信窗口 (wxid_abc) │
|
||||
│ ├─ chromium 窗口 (xiaohongshu) │
|
||||
│ KasmVNC (:3000/3001) ◀── web 桌面串流 │
|
||||
│ │
|
||||
│ 数据卷 /config/ │
|
||||
│ ├─ wechat/<wxid>/ 微信数据 │
|
||||
│ ├─ xiaohongshu/ 小红书 cookie + 数据 │
|
||||
│ ├─ telegram/ Telegram 数据 │
|
||||
│ └─ apps.json 应用实例注册表(持久化) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
▲
|
||||
│ HTTP Bearer Token
|
||||
┌──────┴──────┐
|
||||
│ panel 面板 │ ◀── 统一入口,调度多容器、多应用
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
> **[现状]** 上图是目标态。当前每个 Docker 容器内只运行**一个**应用实例,bridge 进程也按单实例设计;`AppManager`、`UIScheduler`、`apps.json` 均未实现。本阶段优先让 bridge 在"多容器单应用"模式下具备框架能力(每个容器一个 bridge、一个 driver、一个 AppInstance),为未来的"单容器多应用"保留扩展点。
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心概念
|
||||
|
||||
### 4.1 AppKind(应用类型)
|
||||
|
||||
静态注册项,描述"这类应用是什么"。一个 AppKind 在 bridge 启动时注册到 `AppKindRegistry`。
|
||||
|
||||
| 属性 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `kind_id` | 唯一标识 | `"wechat"` / `"xiaohongshu"` / `"telegram"` / `"chromium"` / `"custom"` |
|
||||
| `name` | 显示名 | `"微信"` / `"小红书"` |
|
||||
| `driver_class` | AppDriver 实现类 | `WechatDriver` / `XiaohongshuDriver` |
|
||||
| `default_capabilities` | 默认能力集合 | `{"text_send","db_read","login_qr"}` |
|
||||
| `binary_path` | 可执行文件路径(用于检测是否已安装) | `/config/wechat/opt/wechat/wechat` |
|
||||
| `launch_args_template` | 启动参数模板(支持变量替换) | `"--user-data-dir={data_dir}"` |
|
||||
| `single_instance` | 是否限制单实例(如系统托盘类应用) | `true` / `false` |
|
||||
|
||||
> **[现状]** `AppKindRegistry` 尚未实现。当前应用类型由容器环境变量 `WOC_APP_TYPE` 决定,bridge 代码中无对应抽象;但目标类的字段(`kind_id`、`driver_class`、`binary_path` 等)可直接映射到现有 docker/app-defs.sh 的定义。
|
||||
|
||||
### 4.2 AppInstance(应用实例)
|
||||
|
||||
运行期实体,代表一个正在运行(或已注册待启动)的应用。每个实例有唯一 `app_id`。
|
||||
|
||||
| 属性 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `app_id` | 全局唯一实例标识 | `"wx_a1"` / `"xhs_b2"`(kind 前缀 + 短随机) |
|
||||
| `kind_id` | 所属应用类型 | `"wechat"` |
|
||||
| `display_name` | 用户可读名 | `"工作微信"` / `"小红书主号"` |
|
||||
| `data_dir` | 该实例独立数据目录 | `/config/apps/wx_a1` |
|
||||
| `window_id` | X11 窗口 ID(运行期动态) | `0x380000a` |
|
||||
| `pid` | 进程 PID | `1234` |
|
||||
| `login_state` | 当前登录态 | `"logged_in"` / `"need_login"` / `"not_running"` |
|
||||
| `capabilities` | 实际可用能力(动态计算) | `{"text_send"}`(DB 未解锁时 db_read 不在集合内) |
|
||||
| `status` | 实例状态 | `"registered"` / `"starting"` / `"running"` / `"stopped"` / `"crashed"` |
|
||||
|
||||
**实例寻址**:所有应用专属路由以 `/api/apps/{app_id}/` 为前缀,框架据此路由到对应实例的 driver。
|
||||
|
||||
> **[现状]** `AppInstance` 数据类尚未实现。当前 bridge 的运行期状态是全局单例 `_state`,没有 `app_id`。本阶段每个 bridge 进程仍只管理一个实例,可把该实例的 `app_id` 固定为 `"default"` 或从 `WOC_APP_TYPE` 派生,使新路由层能提前落地。
|
||||
|
||||
### 4.3 AppDriver(应用驱动)
|
||||
|
||||
每种 AppKind 对应一个 AppDriver 实现类,负责该类应用的所有操作。driver 实例与 AppInstance 一一绑定。
|
||||
|
||||
```python
|
||||
class AppDriver(ABC):
|
||||
kind_id: ClassVar[str]
|
||||
default_capabilities: ClassVar[set[str]]
|
||||
|
||||
def __init__(self, instance: AppInstance, ctx: BridgeContext) -> None:
|
||||
self.instance = instance
|
||||
self.ctx = ctx # 框架通用能力(xd / scheduler / screenshot)
|
||||
self.xd = ctx.xd_factory(instance) # 该实例专属的 XdotoolBase
|
||||
|
||||
# 生命周期
|
||||
async def install(self) -> None: ... # 首次安装(下载/解压)
|
||||
async def start(self) -> None: ... # 启动进程
|
||||
async def stop(self) -> None: ... # 停止进程
|
||||
async def restart(self) -> None: ...
|
||||
async def is_running(self) -> bool: ...
|
||||
async def health_check(self) -> dict: ...
|
||||
|
||||
# 窗口与登录
|
||||
async def find_window(self) -> int | None: ...
|
||||
async def activate_window(self) -> None: ...
|
||||
async def detect_login_state(self) -> str: ...
|
||||
async def wait_for_login(self, timeout: int) -> None: ...
|
||||
async def logout(self) -> None: ...
|
||||
|
||||
# 能力查询
|
||||
def capabilities(self) -> set[str]: ... # 返回当前实际可用能力(动态)
|
||||
|
||||
# 路由注册
|
||||
def register_routes(self, router: APIRouter) -> None: ...
|
||||
|
||||
# 诊断
|
||||
def diagnostic_items(self) -> list[dict]: ...
|
||||
async def diagnostic_run(self, check_id: str) -> dict: ...
|
||||
async def diagnostic_autofix(self, check_id: str) -> dict: ...
|
||||
```
|
||||
|
||||
> **[现状]** `AppDriver` 抽象基类尚未实现。当前微信相关逻辑直接写在 [bridge/server.py](../bridge/server.py) 和 [bridge/xdotool_driver.py](../bridge/xdotool_driver.py) 中,二者均强耦合微信。拆分时建议:
|
||||
> 1. 把 `xdotool_driver.py` 中通用 X11 操作(`_run`、`_key`、`_paste_via_xclip`、窗口几何)抽成 `XdotoolBase`;
|
||||
> 2. 把微信专属方法(`find_wechat_window`、`send_text`、`_open_session_by_name`)迁到 `drivers/wechat/driver.py` 的 `WechatDriver`;
|
||||
> 3. 微信 DB 读写、密钥提取、二维码登录迁到 `drivers/wechat/` 子模块。
|
||||
|
||||
### 4.4 BridgeContext(框架上下文)
|
||||
|
||||
driver 通过它访问框架通用能力。框架保证线程安全与串行化。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `config` | `BridgeConfig` | 全局配置 |
|
||||
| `xd_factory` | `Callable[[AppInstance], XdotoolBase]` | 为每个实例创建 X11 工具(绑定到该实例窗口) |
|
||||
| `ui_scheduler` | `UIScheduler` | X11 操作串行化调度器 |
|
||||
| `screenshot` | `Screenshot` | 通用截图能力 |
|
||||
| `send_queue` | `SendQueue` | UI 操作串行化队列(多应用共享) |
|
||||
| `logger` | `logging.Logger` | 框架 logger |
|
||||
|
||||
---
|
||||
|
||||
## 5. 模块设计
|
||||
|
||||
### 5.1 目录结构
|
||||
|
||||
```
|
||||
bridge/
|
||||
core/ 框架核心(应用无关)
|
||||
__init__.py
|
||||
context.py BridgeContext
|
||||
config.py BridgeConfig(命令行 + env)
|
||||
server.py FastAPI app 工厂 + lifespan + 全局中间件
|
||||
logging.py logger 配置 + 请求日志中间件
|
||||
errors.py BridgeError + ErrorResponse + 错误码表
|
||||
models.py 通用响应模型(StatusResponse / AppInstanceInfo 等)
|
||||
xdotool_base.py X11 通用操作基类
|
||||
screenshot.py 通用截图
|
||||
send_queue.py UI 串行化队列
|
||||
ui_scheduler.py X11 操作调度器(多应用串行)
|
||||
diagnostic.py 通用诊断框架
|
||||
app_manager.py AppManager(实例生命周期管理)
|
||||
app_kind.py AppKind + AppKindRegistry(类型注册表)
|
||||
app_instance.py AppInstance 数据类
|
||||
lifecycle.py InitState 后台任务框架
|
||||
drivers/ 应用适配器
|
||||
__init__.py
|
||||
base.py AppDriver 抽象基类
|
||||
wechat/
|
||||
__init__.py
|
||||
driver.py WechatDriver(AppDriver)
|
||||
db_reader.py 微信 DB 读取
|
||||
decryptor.py SQLCipher 解密
|
||||
key_cache.py 密钥缓存
|
||||
key_extractor.py 内存扫描提 key
|
||||
models.py 微信专属请求/响应模型
|
||||
routes.py 微信专属路由
|
||||
diagnostic.py 微信诊断项
|
||||
xiaohongshu/
|
||||
__init__.py
|
||||
driver.py XiaohongshuDriver(AppDriver)
|
||||
routes.py 小红书专属路由
|
||||
models.py
|
||||
telegram/
|
||||
__init__.py
|
||||
driver.py TelegramDriver(AppDriver)
|
||||
routes.py
|
||||
chromium/
|
||||
__init__.py
|
||||
driver.py ChromiumDriver(AppDriver) 通用浏览器
|
||||
custom/
|
||||
__init__.py
|
||||
driver.py CustomDriver(AppDriver)
|
||||
tools/ 调试脚本
|
||||
s6/ s6 服务定义
|
||||
main.py 入口:解析参数 → 注册 kinds → 启动 app
|
||||
```
|
||||
|
||||
> **[现状]** 上述 `core/`、`drivers/`、`main.py` 均为目标结构。当前 bridge 目录扁平:所有代码在 [bridge/server.py](../bridge/server.py)、[bridge/xdotool_driver.py](../bridge/xdotool_driver.py)、[bridge/send_queue.py](../bridge/send_queue.py) 等根级文件中。S1 阶段可按以下顺序迁移,避免一次性大爆炸:
|
||||
> 1. 先把 `server.py` 中通用配置/错误/模型迁到 `core/`;
|
||||
> 2. 创建 `drivers/base.py` 与 `drivers/wechat/`;
|
||||
> 3. 保持 `main.py` 仍只注册一个 `WechatDriver`,行为与旧版完全一致。
|
||||
|
||||
### 5.2 AppManager(应用管理器)
|
||||
|
||||
框架核心组件,管理容器内所有 AppInstance 的生命周期。
|
||||
|
||||
**职责**:
|
||||
|
||||
- 维护实例注册表(内存 + 持久化到 `/config/apps.json`)
|
||||
- 提供实例 CRUD:`register` / `unregister` / `list` / `get`
|
||||
- 控制实例生命周期:`start` / `stop` / `restart`
|
||||
- 健康检查:周期性轮询所有 running 实例的 `is_running`
|
||||
- 自动恢复:crashed 实例按策略重启(最多 N 次/小时)
|
||||
|
||||
**实例注册表结构**(`/config/apps.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"instances": [
|
||||
{
|
||||
"app_id": "wx_a1",
|
||||
"kind_id": "wechat",
|
||||
"display_name": "工作微信",
|
||||
"data_dir": "/config/apps/wx_a1",
|
||||
"auto_start": true,
|
||||
"created_at": 1735900800
|
||||
},
|
||||
{
|
||||
"app_id": "xhs_b2",
|
||||
"kind_id": "xiaohongshu",
|
||||
"display_name": "小红书主号",
|
||||
"data_dir": "/config/apps/xhs_b2",
|
||||
"auto_start": false,
|
||||
"created_at": 1735900900
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **[现状]** `AppManager` 与 `/config/apps.json` 均未实现。当前实例生命周期(容器启停、应用安装)由 panel 通过 Docker API 管理,bridge 只感知当前容器内已启动的单个应用。本阶段改造不推翻 panel 的生命周期管理,bridge 内只保留单实例的运行期抽象,为后续多实例注册表预留接口。
|
||||
|
||||
### 5.3 UIScheduler(X11 操作调度器)
|
||||
|
||||
**问题**:多应用共用一个 X server,并发操作会导致:
|
||||
|
||||
- 窗口焦点争用(A 激活窗口时 B 的操作打到 A)
|
||||
- 剪贴板污染(A 写剪贴板,B 还没读就被覆盖)
|
||||
- 键盘事件错位(A 的 Ctrl+V 发到 B 的窗口)
|
||||
|
||||
**解决**:所有 X11 操作(activate / key / paste / click / type)必须经 UIScheduler 排队执行。
|
||||
|
||||
```python
|
||||
class UIScheduler:
|
||||
"""X11 操作串行化调度器。
|
||||
|
||||
所有 driver 的 X11 操作通过 submit() 提交,调度器按 FIFO 顺序
|
||||
串行执行。同一时间只有一个操作在 X server 上进行。
|
||||
"""
|
||||
|
||||
async def submit(self, coro_factory: CoroFactory, *, app_id: str) -> Any:
|
||||
"""提交一个 X11 操作。
|
||||
|
||||
Args:
|
||||
coro_factory: 返回 coroutine 的工厂(便于重试与超时控制)
|
||||
app_id: 发起方实例 ID(用于日志与死锁检测)
|
||||
|
||||
Returns:
|
||||
操作结果
|
||||
|
||||
Notes:
|
||||
- 操作间最小间隔 100ms(避免 X server 压力)
|
||||
- 单操作超时 30s(避免死锁)
|
||||
- 操作日志带 app_id 前缀,便于排障
|
||||
"""
|
||||
```
|
||||
|
||||
**driver 使用方式**:
|
||||
|
||||
```python
|
||||
class WechatDriver(AppDriver):
|
||||
async def send_text(self, to: str, content: str) -> str:
|
||||
async def _op():
|
||||
await self.xd.activate_window(self.instance.window_id)
|
||||
await self.xd._open_session_by_name(to)
|
||||
await self.xd._paste_via_xclip(content)
|
||||
await self.xd._key("Return")
|
||||
return f"local_{int(time.time())}_{random.randint(0,0xFFFFFF):06x}"
|
||||
return await self.ctx.ui_scheduler.submit(_op, app_id=self.instance.app_id)
|
||||
```
|
||||
|
||||
**不进 UIScheduler 的操作**:
|
||||
|
||||
- 读窗口列表(`xdotool search`,只读)
|
||||
- 读进程状态(`pgrep`,只读)
|
||||
- 读 DB(不涉及 X11)
|
||||
- 截图(`scrot` 本身是只读快照,不争用焦点)
|
||||
|
||||
### 5.4 SendQueue 与 UIScheduler 的关系
|
||||
|
||||
| 组件 | 作用 | 粒度 |
|
||||
|------|------|------|
|
||||
| `SendQueue` | 发送类操作的限流(防止频率过高触发风控) | 单实例:每个 driver 自己一个队列 |
|
||||
| `UIScheduler` | X11 操作的串行化(防止多应用争用 X server) | 全局:所有 driver 共享 |
|
||||
|
||||
两者正交:一个操作可能先经 SendQueue 限流,再经 UIScheduler 串行执行。
|
||||
|
||||
> **[现状]** 当前只有一个全局 [SendQueue](../bridge/send_queue.py),用于单应用发送限流;`UIScheduler` 尚未实现,X11 操作在 HTTP 层并发调用时存在焦点/剪贴板冲突风险。S1 阶段可保持 `SendQueue` 不变,S2 引入多实例后再把 `SendQueue` 下放到每个 `AppInstance`,同时新增全局 `UIScheduler`。
|
||||
|
||||
---
|
||||
|
||||
## 6. 接口规范
|
||||
|
||||
### 6.1 路由分层
|
||||
|
||||
```
|
||||
/api/* 通用路由(无 app_id,框架级)
|
||||
/api/status 框架状态(聚合所有应用摘要)
|
||||
/api/apps 应用实例列表 / 注册新实例
|
||||
/api/apps/{app_id} 单实例状态 / 控制
|
||||
/api/apps/{app_id}/start 启动实例
|
||||
/api/apps/{app_id}/stop 停止实例
|
||||
/api/apps/{app_id}/restart 重启实例
|
||||
/api/kinds 支持的应用类型列表
|
||||
/api/screenshot 全屏截图(所有应用窗口)
|
||||
/api/diagnostic/items 通用诊断项 + 所有实例诊断项聚合
|
||||
/api/diagnostic/run/{id} 执行诊断
|
||||
/api/diagnostic/autofix/{id}
|
||||
|
||||
/api/apps/{app_id}/* 应用专属路由(由 driver.register_routes 挂载)
|
||||
/api/apps/{app_id}/messages 微信消息拉取
|
||||
/api/apps/{app_id}/contacts 微信联系人
|
||||
/api/apps/{app_id}/send/text 微信发文本
|
||||
/api/apps/{app_id}/login/qr 微信扫码登录
|
||||
/api/apps/{app_id}/db/decrypt 微信 DB 解密
|
||||
/api/apps/{app_id}/xhs/publish 小红书发笔记
|
||||
/api/apps/{app_id}/xhs/search 小红书搜索
|
||||
```
|
||||
|
||||
> **[现状]** 当前所有路由都是 `/api/*` 无前缀(如 `/api/send/text`、`/api/messages/since`、`/api/db/decrypt`),且全部写死微信逻辑。本阶段目标:
|
||||
> 1. 新增 `/api/apps/{app_id}/*` 路由层;
|
||||
> 2. 将旧路由作为 alias 内部转发到 `/api/apps/default/*`;
|
||||
> 3. `/api/status` 同时返回旧字段(取 default app)与新 `apps` 数组,保证面板零改动。
|
||||
|
||||
### 6.2 通用路由详述
|
||||
|
||||
#### GET /api/status
|
||||
|
||||
返回框架整体状态 + 所有实例摘要。
|
||||
|
||||
```json
|
||||
{
|
||||
"bridge_version": "2.0.0",
|
||||
"uptime_seconds": 3600,
|
||||
"display": ":1",
|
||||
"app_count": 2,
|
||||
"apps": [
|
||||
{
|
||||
"app_id": "wx_a1",
|
||||
"kind_id": "wechat",
|
||||
"display_name": "工作微信",
|
||||
"status": "running",
|
||||
"login_state": "logged_in",
|
||||
"capabilities": ["text_send", "db_read", "login_qr"]
|
||||
},
|
||||
{
|
||||
"app_id": "xhs_b2",
|
||||
"kind_id": "xiaohongshu",
|
||||
"display_name": "小红书主号",
|
||||
"status": "running",
|
||||
"login_state": "need_login",
|
||||
"capabilities": ["publish", "search"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/apps
|
||||
|
||||
列出所有实例。
|
||||
|
||||
#### POST /api/apps
|
||||
|
||||
注册新实例。
|
||||
|
||||
```json
|
||||
// 请求
|
||||
{
|
||||
"kind_id": "xiaohongshu",
|
||||
"display_name": "小红书小号",
|
||||
"auto_start": true
|
||||
}
|
||||
|
||||
// 响应
|
||||
{
|
||||
"app_id": "xhs_c3",
|
||||
"kind_id": "xiaohongshu",
|
||||
"display_name": "小红书小号",
|
||||
"data_dir": "/config/apps/xhs_c3",
|
||||
"status": "registered"
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/apps/{app_id}
|
||||
|
||||
单实例详细状态。
|
||||
|
||||
```json
|
||||
{
|
||||
"app_id": "wx_a1",
|
||||
"kind_id": "wechat",
|
||||
"display_name": "工作微信",
|
||||
"status": "running",
|
||||
"pid": 1234,
|
||||
"window_id": "0x380000a",
|
||||
"login_state": "logged_in",
|
||||
"capabilities": ["text_send", "db_read", "login_qr", "media"],
|
||||
"uptime_seconds": 1800,
|
||||
"db_accessible": true,
|
||||
"current_wxid": "wxid_abc",
|
||||
"current_nickname": "张三"
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /api/apps/{app_id}/start|stop|restart
|
||||
|
||||
控制实例生命周期。
|
||||
|
||||
#### GET /api/kinds
|
||||
|
||||
列出支持的 AppKind。
|
||||
|
||||
```json
|
||||
{
|
||||
"kinds": [
|
||||
{
|
||||
"kind_id": "wechat",
|
||||
"name": "微信",
|
||||
"default_capabilities": ["text_send", "db_read", "login_qr", "media"],
|
||||
"single_instance": false,
|
||||
"installed": true
|
||||
},
|
||||
{
|
||||
"kind_id": "xiaohongshu",
|
||||
"name": "小红书",
|
||||
"default_capabilities": ["publish", "search"],
|
||||
"single_instance": false,
|
||||
"installed": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 应用专属路由规范
|
||||
|
||||
由各 driver 通过 `register_routes(router)` 挂载,router 自动绑定 `/api/apps/{app_id}` 前缀。
|
||||
|
||||
**driver 实现示例**:
|
||||
|
||||
```python
|
||||
# drivers/wechat/routes.py
|
||||
def register_routes(router: APIRouter) -> None:
|
||||
@router.get("/messages/since")
|
||||
async def get_messages(app_id: str, cursor: int = 0, limit: int = 50):
|
||||
driver = app_manager.get_driver(app_id)
|
||||
return await driver.get_messages_since(cursor, limit)
|
||||
|
||||
@router.post("/send/text")
|
||||
async def send_text(app_id: str, req: SendTextRequest):
|
||||
driver = app_manager.get_driver(app_id)
|
||||
return await driver.send_text(req.to, req.content)
|
||||
```
|
||||
|
||||
### 6.4 错误码扩展
|
||||
|
||||
新增框架级错误码:
|
||||
|
||||
| 错误码 | HTTP | 说明 |
|
||||
|--------|------|------|
|
||||
| `APP_NOT_FOUND` | 404 | app_id 不存在 |
|
||||
| `APP_NOT_RUNNING` | 503 | 实例未运行 |
|
||||
| `APP_NOT_LOGGED_IN` | 401 | 实例未登录 |
|
||||
| `APP_KIND_UNKNOWN` | 400 | 未知应用类型 |
|
||||
| `APP_KIND_NOT_INSTALLED` | 503 | 应用类型未安装 |
|
||||
| `APP_ALREADY_EXISTS` | 409 | 实例已存在(single_instance 冲突) |
|
||||
| `UI_BUSY` | 503 | UIScheduler 队列拥塞 |
|
||||
| `APP_CAPABILITY_NOT_SUPPORTED` | 501 | 该实例不支持请求的能力 |
|
||||
|
||||
> **[现状]** 当前 [bridge/models.py](../bridge/models.py) 中只有微信中心化错误码,如 `WECHAT_NOT_RUNNING`、`WECHAT_NOT_LOGGED_IN` 等。拆分阶段应:
|
||||
> 1. 保留旧错误码作为微信 driver 的返回值;
|
||||
> 2. 新增框架级错误码上表;
|
||||
> 3. alias 路由命中时仍可使用旧错误码,避免面板/外部系统改动。
|
||||
|
||||
### 6.5 向后兼容层
|
||||
|
||||
现有无前缀路由(`/api/messages`、`/api/send/text` 等)保留为 alias,内部转发到 `/api/apps/{default_app_id}/...`。
|
||||
|
||||
**默认实例选择规则**:
|
||||
|
||||
1. 若容器内只有一个实例,自动作为 default
|
||||
2. 若多实例,读取 `/config/.woc-default-app`(由面板或首次注册时写入)
|
||||
3. 都没有则返回 404
|
||||
|
||||
**alias 路由行为**:
|
||||
|
||||
- 命中 alias 时日志打 `DEPRECATED` warning,提示调用方迁移
|
||||
- alias 在 major 版本升级时下线(v3.0.0 移除)
|
||||
|
||||
### 6.6 与既有系统的兼容性约束
|
||||
|
||||
改造必须尊重以下已验证的硬约束,否则现有微信实例会损坏:
|
||||
|
||||
1. **鉴权**:`/api/bridge/:id/*` 外部调用依赖 `WOC_BRIDGE_API_TOKEN` 作为 Bearer token;修改 `.env` 后必须重建 panel 容器(而非重启)才能生效。
|
||||
2. **DB 解密**:微信 DB 仅兼容 Linux WeChat 4.x,使用 SQLCipher 4 参数(AES-256-CBC、PBKDF2-HMAC-SHA512、256000 rounds、mac_salt = salt XOR 0x3A)。解密能力保留在 `drivers/wechat/` 内,不提前抽象。
|
||||
3. **ptrace**:自动提取 key 需要容器具备 `SYS_PTRACE` capability 或 `--privileged`,且宿主 `ptrace_scope=0`。
|
||||
4. **DB 路径**:自动检测写死 `/config/xwechat_files/<wxid>/db_storage/message/message_0.db`;改为 driver 内部常量,不作为框架公共假设。
|
||||
5. **发送定位**:微信 `send_text/send_file` 使用 `display_name`(备注/昵称)而非 wxid 搜索会话,该行为由 `WechatDriver` 封装,不进入框架公共层。
|
||||
6. **容器启动**:当前 [docker/autostart](../docker/autostart) 只启动一个应用;bridge 框架不假设自己能启动多个应用,生命周期控制仍由 panel/docker 负责。
|
||||
|
||||
---
|
||||
|
||||
## 7. 资源调度与并发
|
||||
|
||||
### 7.1 X11 资源争用对策
|
||||
|
||||
| 资源 | 争用场景 | 对策 |
|
||||
|------|---------|------|
|
||||
| 窗口焦点 | A 激活窗口时 B 失焦 | 所有 activate/key/click 经 UIScheduler 串行 |
|
||||
| 剪贴板 | A 写入后 B 未读就被 C 覆盖 | paste 操作是"写剪贴板+立即 Ctrl+V"原子组,经 UIScheduler 串行 |
|
||||
| 键盘事件 | A 的 Ctrl+V 发到 B 窗口 | activate 后立即操作,UIScheduler 保证原子 |
|
||||
| 鼠标坐标 | A 的 click 坐标被 B 的 mousemove 改变 | mousemove + click 必须在同一个 UIScheduler 任务内 |
|
||||
|
||||
### 7.2 并发模型
|
||||
|
||||
```
|
||||
HTTP 请求 (asyncio)
|
||||
↓
|
||||
driver 方法 (async)
|
||||
↓
|
||||
UIScheduler.submit(coro) ◀── 全局串行点(X11 操作)
|
||||
↓
|
||||
XdotoolBase._run (asyncio subprocess)
|
||||
```
|
||||
|
||||
- HTTP 层并发:FastAPI 默认并发处理请求
|
||||
- driver 层并发:每个实例的方法可并发调用
|
||||
- UIScheduler 层串行:所有 X11 写操作排队执行
|
||||
- DB 读取层并发:不涉及 X11,独立并发(受限于 SQLite 读写锁)
|
||||
|
||||
### 7.3 限流策略
|
||||
|
||||
| 层级 | 限流 | 配置 |
|
||||
|------|------|------|
|
||||
| HTTP 全局 | 单 IP 每秒 N 请求 | `WOC_BRIDGE_MAX_CALLS_PER_SEC` |
|
||||
| 单实例发送 | 发送类操作最小间隔 | `WOC_BRIDGE_SEND_DELAY_MS`(每实例独立) |
|
||||
| UIScheduler | 操作间最小间隔 | 固定 100ms(防止 X server 压力) |
|
||||
| UIScheduler 队列长度 | 最多排队 N 个操作 | 50,超限返回 `UI_BUSY` |
|
||||
|
||||
---
|
||||
|
||||
## 8. 应用生命周期
|
||||
|
||||
### 8.1 实例状态机
|
||||
|
||||
```
|
||||
register
|
||||
┌─────────────────────────┐
|
||||
│ ▼
|
||||
┌────────┐ start ┌─────────┐ running ┌────────┐
|
||||
│registered├────────►│starting├────────────►│running │
|
||||
└────────┘ └────┬───┘ └───┬────┘
|
||||
▲ │ fail │ stop
|
||||
│ ▼ ▼
|
||||
│ ┌────────┐ ┌────────┐
|
||||
│ │crashed │ │stopped │
|
||||
│ └───┬────┘ └───┬────┘
|
||||
│ │ restart │ start
|
||||
│ ▼ │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8.2 启动流程
|
||||
|
||||
1. `POST /api/apps` 注册实例 → 状态 `registered`
|
||||
2. `POST /api/apps/{app_id}/start` → 状态 `starting`
|
||||
3. driver.install()(首次启动,下载/解压)
|
||||
4. driver.start()(启动应用进程)
|
||||
5. 轮询 driver.find_window() 直到窗口出现(超时 60s)
|
||||
6. 状态 `running`
|
||||
7. 启动失败 → 状态 `crashed`,记录错误
|
||||
|
||||
### 8.3 自动启动
|
||||
|
||||
容器启动时,AppManager 读取 `/config/apps.json`,对所有 `auto_start=true` 的实例按顺序执行启动流程。
|
||||
|
||||
**顺序约束**:实例间启动间隔 5 秒(避免同时启动多个 GUI 应用导致内存峰值)。
|
||||
|
||||
### 8.4 健康检查
|
||||
|
||||
AppManager 后台任务(每 30s):
|
||||
|
||||
1. 遍历所有 `running` 状态实例
|
||||
2. 调用 `driver.is_running()`
|
||||
3. 失败则状态转 `crashed`
|
||||
4. 记录崩溃次数与时间
|
||||
5. 若配置了自动恢复且未超限(默认 3 次/小时),触发 restart
|
||||
|
||||
### 8.5 数据卷布局
|
||||
|
||||
```
|
||||
/config/
|
||||
apps.json 实例注册表
|
||||
.woc-default-app 默认实例 app_id(兼容层用)
|
||||
apps/
|
||||
wx_a1/
|
||||
data/ 应用数据根(driver 自由组织)
|
||||
wechat/
|
||||
opt/wechat/wechat 微信二进制
|
||||
xwechat_files/ 微信运行时数据
|
||||
logs/ 该实例日志
|
||||
state/ 该实例状态文件
|
||||
xhs_b2/
|
||||
data/
|
||||
chromium/ 小红书专用 chromium user-data-dir
|
||||
Default/
|
||||
Cookies 小红书登录 cookie
|
||||
logs/
|
||||
state/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 应用间通信
|
||||
|
||||
### 9.1 显式通信(推荐)
|
||||
|
||||
应用之间通过外部调用方(如面板或用户脚本)显式编排:
|
||||
|
||||
```
|
||||
# 伪代码:从小红书采集内容,发到微信
|
||||
xhs_note = await bridge.get(f"/api/apps/xhs_b2/xhs/search?keyword=foo")
|
||||
await bridge.post(f"/api/apps/wx_a1/send/text", json={"to":"文件助手","content":xhs_note})
|
||||
```
|
||||
|
||||
**优点**:流程显式、可观测、可调试。
|
||||
|
||||
**不提供应用间直接调用**:driver 之间不互相感知,避免隐式依赖。
|
||||
|
||||
### 9.2 事件总线(未来扩展)
|
||||
|
||||
可选的发布订阅机制:
|
||||
|
||||
```python
|
||||
# driver 发布事件
|
||||
await self.ctx.event_bus.publish("message_received", {"app_id": "wx_a1", "msg": {...}})
|
||||
|
||||
# 外部订阅
|
||||
GET /api/events/stream (SSE)
|
||||
```
|
||||
|
||||
P0 不实现,留待有明确需求时再做。
|
||||
|
||||
---
|
||||
|
||||
## 10. 部署模型
|
||||
|
||||
### 10.1 单容器多应用
|
||||
|
||||
```
|
||||
容器 woc-app-multi
|
||||
├─ bridge (:8088) 管理 2 个实例
|
||||
├─ Xvfb (:1)
|
||||
├─ KasmVNC (:3000)
|
||||
├─ 微信进程 (wx_a1)
|
||||
└─ chromium 进程 (xhs_b2)
|
||||
```
|
||||
|
||||
适用于:单用户、资源受限(NAS)、应用间需要协同(同一桌面)。
|
||||
|
||||
### 10.2 多容器单应用(现有模式保留)
|
||||
|
||||
```
|
||||
容器 woc-wx-<id1> 微信实例 1
|
||||
容器 woc-wx-<id2> 微信实例 2
|
||||
容器 woc-xhs-<id3> 小红书实例
|
||||
```
|
||||
|
||||
适用于:多用户隔离、横向扩展、单应用崩溃不影响其他。
|
||||
|
||||
### 10.3 混合模式
|
||||
|
||||
面板支持两种模式并存:
|
||||
|
||||
- 轻量场景用单容器多应用(省资源)
|
||||
- 隔离场景用多容器单应用(强隔离)
|
||||
|
||||
面板层提供创建实例时的"部署位置"选项:新建独立容器 or 加入已有容器的 bridge。
|
||||
|
||||
---
|
||||
|
||||
## 11. 演进路径
|
||||
|
||||
### S0:当前现状
|
||||
|
||||
- [bridge/server.py](../bridge/server.py) 约 2400 行单文件,全局单例 `_state`。
|
||||
- 所有 `/api/*` 路由直接写死微信逻辑,无 `app_id`、无 driver 抽象。
|
||||
- [docker/autostart](../docker/autostart) 一容器一应用;panel 通过 Docker API 管理生命周期。
|
||||
|
||||
### S1:抽 core/ 与 driver 抽象(不改行为,本阶段重点)
|
||||
|
||||
**目标**:把 server.py 拆成 `core/` + `drivers/wechat/`,建立 `AppDriver` 抽象,但保持单应用模式。
|
||||
|
||||
**改动**:
|
||||
|
||||
- 新建 `core/` 目录,迁移 `config` / `context` / `errors` / `logging` / `models` / `send_queue` / `screenshot`。
|
||||
- 抽 `XdotoolBase`(通用 X11 操作)到 `core/xdotool_base.py`。
|
||||
- 定义 `drivers/base.py` 的 `AppDriver` 抽象基类。
|
||||
- 微信逻辑迁移到 `drivers/wechat/`,`WechatDriver(AppDriver)` 用组合方式持有 `XdotoolBase`。
|
||||
- 微信路由迁移到 `drivers/wechat/routes.py`。
|
||||
- `main.py` 读 `WOC_APP_TYPE`,只注册一个 driver,创建一个 `AppInstance`(`app_id="default"`)。
|
||||
|
||||
**验证**:所有现有 API 行为不变,面板无感知;`pytest` 或现有集成测试全部通过。
|
||||
|
||||
### S2:新路由层 + 向后兼容层
|
||||
|
||||
**目标**:引入 `/api/apps/{app_id}/*` 路由,旧路由作为 alias 转发。
|
||||
|
||||
**改动**:
|
||||
|
||||
- 新增 `/api/apps/{app_id}/status`、`/api/apps/{app_id}/send/text` 等路由。
|
||||
- 旧路径 `/api/messages`、`/api/send/text` 等转为 alias,转发到 `/api/apps/default/...`。
|
||||
- `/api/status` 同时返回旧字段(取 default app)与新 `apps` 数组。
|
||||
- 旧字段日志打 `DEPRECATED` warning。
|
||||
|
||||
**验证**:面板与外部调用方无需改动即可工作;新路径可通过 `/api/apps/default/*` 访问。
|
||||
|
||||
### S3:UIScheduler 与 SendQueue 正交化
|
||||
|
||||
**目标**:解决多实例共享 X server 时的焦点/剪贴板冲突。
|
||||
|
||||
**改动**:
|
||||
|
||||
- 新增 `core/ui_scheduler.py`,所有 X11 写操作经其串行执行。
|
||||
- 每个 `AppInstance` 持有独立 `SendQueue`;`UIScheduler` 全局唯一。
|
||||
- 单应用模式下行为不变,多实例模式下才体现串行价值。
|
||||
|
||||
**验证**:高并发调用 `/api/apps/default/send/text` 不再出现剪贴板污染。
|
||||
|
||||
### S4:AppManager 与单容器多实例(未来扩展)
|
||||
|
||||
**目标**:支持容器内注册多个 `AppInstance`。
|
||||
|
||||
**改动**:
|
||||
|
||||
- 实现 `core/app_manager.py`、`core/app_instance.py`、`core/app_kind.py`。
|
||||
- 新增 `/api/apps` CRUD 路由。
|
||||
- `/config/apps.json` 持久化。
|
||||
- 需同步改造 [docker/autostart](../docker/autostart) 以支持启动多个应用。
|
||||
|
||||
**验证**:可在同一容器内通过 API 注册并启动微信 + 小红书两个实例。
|
||||
|
||||
### S5:新增 Xiaohongshu/Telegram Driver(框架可扩展性验证)
|
||||
|
||||
**目标**:落地非微信应用自动化。
|
||||
|
||||
**改动**:
|
||||
|
||||
- 实现 `drivers/xiaohongshu/driver.py` + `routes.py`(P0:publish/text、search)。
|
||||
- 或实现 `drivers/telegram/driver.py`(优先浏览器/桌面版,若走原生协议则另议)。
|
||||
|
||||
**验证**:通过 `/api/apps/{app_id}/xhs/publish` 成功发笔记。
|
||||
|
||||
### S6:面板适配与单容器多应用部署(不在本文档范围)
|
||||
|
||||
**目标**:面板支持多应用管理 UI 与"单容器多应用"部署选项。
|
||||
|
||||
**改动**(panel 层):
|
||||
|
||||
- 面板新增"应用实例"管理页。
|
||||
- 桌面入口支持选择"进入哪个应用窗口"。
|
||||
- 实例创建支持"新建独立容器"或"加入已有容器"。
|
||||
|
||||
### S7:下线兼容层
|
||||
|
||||
**目标**:移除 alias,完成迁移。
|
||||
|
||||
**前提**:面板与所有已知适配器已切换到新路径。
|
||||
|
||||
**改动**:
|
||||
|
||||
- 删除旧路径 alias。
|
||||
- `/api/status` 移除旧字段。
|
||||
- major 版本升级到 v3.0.0。
|
||||
|
||||
---
|
||||
|
||||
## 12. 不做的事
|
||||
|
||||
### 12.1 完全不做
|
||||
|
||||
1. **不引入应用间隐式依赖**:driver 之间不互相调用,编排由外部完成。
|
||||
2. **不做多容器 bridge 集群**:一个 bridge 进程只管一个容器内的应用,跨容器调度由面板负责。
|
||||
3. **不做应用沙箱**:应用间共享 X server 与文件系统(受 Linux 权限控制),不做额外隔离。
|
||||
4. **不做 RBAC**:`app_id` 级别的权限控制留给面板层,bridge 层只做 Bearer Token 全局鉴权。
|
||||
5. **不改 KasmVNC 串流层**:桌面串流保持现状(全屏共享),不按应用切分串流。
|
||||
6. **不做 OCR**:不引入 tesseract,页面识别靠 URL + 坐标启发式。
|
||||
|
||||
### 12.2 本阶段先不做
|
||||
|
||||
1. **不抽象 SQLCipher 解密**:解密能力暂留 `drivers/wechat/`,待第二个 SQLCipher 应用出现再抽公共层。
|
||||
2. **不实现单容器多应用启动**:[docker/autostart](../docker/autostart) 当前只启动一个应用,改造它需要 panel/docker 同步调整,不在本阶段 bridge 文档范围。
|
||||
3. **不新增非微信 driver**:先完成框架抽象与微信 driver 迁移,再落地 Xiaohongshu/Telegram driver。
|
||||
4. **不替换全局 `SendQueue`**:S1 保持现有全局队列,S3 再下放到每个 `AppInstance`。
|
||||
5. **不引入新依赖**:仍只使用 fastapi / uvicorn / pydantic / pillow / cryptography。
|
||||
|
||||
---
|
||||
|
||||
## 13. 关键决策记录
|
||||
|
||||
| 决策点 | 选择 | 理由 |
|
||||
|--------|------|------|
|
||||
| 文档定位 | 目标架构 + `[现状]` 注释 | 既描述最终形态,又不掩盖当前代码未落地的事实 |
|
||||
| 本阶段重点 | 先让 bridge 在"多容器单应用"模式下可工作 | docker autostart 当前只支持一容器一应用;先完成 bridge 框架抽象,再扩展单容器多应用 |
|
||||
| 文档范围 | 只聚焦 bridge 层 | panel UI、docker 多应用启动机制另行设计 |
|
||||
| 抽象颗粒度 | 一应用一 driver(粗粒度) | 业务流程差异大,能力复用价值低 |
|
||||
| 容器内应用数 | 目标为多实例并存,当前先单实例 | 满足"一容器多应用"长期需求,但本阶段保留单实例运行 |
|
||||
| 路由前缀 | `/api/apps/{app_id}/*` + alias 过渡 | 实例独立寻址 + 向后兼容 |
|
||||
| XdotoolBase 风格 | 组合(driver 持有 `self.xd`) | 避免多继承混乱 |
|
||||
| SQLCipher 解密位置 | 留 drivers/wechat/ | YAGNI,等第二个应用再抽 |
|
||||
| X11 并发控制 | 全局 UIScheduler 串行 | 多应用争用同一 X server 必须串行;当前尚未实现 |
|
||||
| 应用间通信 | 显式编排(无直接调用) | 流程可观测、可调试 |
|
||||
| 部署模型 | 单容器多应用 + 多容器单应用并存 | 兼顾资源效率与隔离性;当前仅多容器单应用可用 |
|
||||
1772
doc/优化方案/02-好友自动通过设计方案.md
Normal file
1772
doc/优化方案/02-好友自动通过设计方案.md
Normal file
File diff suppressed because it is too large
Load Diff
2560
doc/优化方案/03-微信UI自动化架构优化方案.md
Normal file
2560
doc/优化方案/03-微信UI自动化架构优化方案.md
Normal file
File diff suppressed because it is too large
Load Diff
1094
doc/优化方案/04-WechatOnCloud-改造需求方案.md
Normal file
1094
doc/优化方案/04-WechatOnCloud-改造需求方案.md
Normal file
File diff suppressed because it is too large
Load Diff
1645
doc/优化方案/05-UIActionScheduler统一调度方案.md
Normal file
1645
doc/优化方案/05-UIActionScheduler统一调度方案.md
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user