Files
auto_control/doc/DEVELOPMENT.md
T
butubb d40c867d6f fix: 备份覆盖补全(动作库/系统配置入清单)+ 覆盖自检与未登记表告警;发布流程/红线文档更新
问题(2026-09-10 用户反馈):动作库"没有备份"。实测导出 zip 里 agent_action 数据其实在
(导出是 users.db 全库快照),但清单/预览没列它 → 看起来像没备份。同类还有 app_meta。

修复(core/system_backup.py):
- SUMMARY_TABLES 补 agent_action(动作库) 与 app_meta(系统配置) + 中文标签;
- 导出侧**覆盖自检**:登记表若在快照缺失 → manifest.coverage_missing + 日志告警;
- 导入侧**反向自检**:备份含未登记表 → 预览告警提示登记(extra_tables);
- 顶部注释写明新增持久化表必须登记(红线)。

其余:
- monitor.html:数据备份面板文案改为明列全部业务表 + 指引(预览见表行数 / 新增表须登记);
- doc/DEPLOY.md §3.5:新增「备份覆盖清单(红线)」小节(含清单与两侧自检说明);
- doc/DEVELOPMENT.md:§5.6 增「备份覆盖红线」;§6 发布流程重写为分支流程
  (本机建分支 → 用户确认 → 合 dev → dev 整体就绪 → 合 main → 220 部署,附部署命令);
- doc/ARCHITECTURE.md §3.6:补备份覆盖登记提示。

实测:导出清单 12 表(含动作库 3 行、系统配置 8 行),coverage_missing 空;上传预览同样显示、
extra_tables 空。
2026-09-10 21:03:41 +08:00

290 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 开发手册(DEVELOPMENT)
面向本项目开发者:开发流程、git 工作流、环境说明、技术红线、本地开发、常见开发任务。
---
## 1. 开发流程与 git 工作流
### 1.1 分支策略
| 分支 | 用途 |
|------|------|
| `dev` | **开发分支**,所有新功能/修复都在这里开发 |
| `main` | **生产分支**(主分支),只放已确认的稳定版本 |
生产环境 = 部署机 `192.168.20.220` 的 `/mnt/data/openstf/auto_control`(python-app 容器运行 `web_server.py`)。
### 1.2 git 操作铁律(重要)
**所有 git 操作都必须先经项目负责人明确确认后才能执行**,包括但不限于:
- `commit` / `push`(**即使 push 到 dev 也要确认**)
- `merge`(dev → main)
- `revert` / `checkout` / `reset` / `branch -D` 等
**标准流程:**
```
1. 在 dev 分支开发、本地测试
2. 完成改动 → 把改动清单 + 建议 commit 信息 列给负责人
3. 负责人确认 → 才能 commit + push dev
4. 需要发布 → 负责人确认后再合并到 main
5. 部署生产 → 负责人明确指示后才 pull 到 220
```
> 不允许"开发完顺手就 commit/push"。即使是一次性小改动,也要先确认。
---
## 2. 环境说明
| 环境 | 位置 | 说明 |
|------|------|------|
| 开发机 | 本机(192.168.20.57) | `.venv` + 本地运行 `web_server.py` |
| 生产机 | 部署机 220 的 `auto_control` | python-app 容器,`network_mode: host` |
| STF 服务 | ~~`192.168.20.220:7100`~~ | 已停用(代码已摘除依赖)。注意:此处"已停用"与 [STF_REMOVAL.md](STF_REMOVAL.md) 的"待人工确认"项矛盾(220 侧是否已 `docker stop stf` 未核实),需以 220 实际为准 |
| adb 容器 | 220 上 `adb`(host 网络 5037) | USB 设备远程 adb server;网络设备补连用 |
| 设备 | Tailscale `100.100.10.x:5555` | Xiaomi 舰队,本机 `100.100.10.2` 在 tailnet 内 |
| uiautodev | 本机 `20242` | 元素抓取服务(web_server 自动拉起) |
### 2.1 adb key(关键)
- 本机 `~/.android/adbkey` 沿用历史 key(原取自 STF adb 容器,全部设备都信任)
- **不要随意更换 key**——设备会变 unauthorized 连不上
- 旧 key 备份在 `~/.android/adbkey.local.bak`
- 生产环境的容器也需要用这把 key(部署时处理)
### 2.2 设备连接方式
- 设备 serial 是 `IP:5555`(Tailscale 地址),**直连**优先(只 connect、绝不 disconnect)
- USB 设备(serial 无冒号):插本机走本地 adb;插 220 走远程 adb server(`USB_ADB_HOST:5037`)
- 本机已在 tailnet 内,直连可靠且快(<1s)
- 设备加入/退出平台:工具 → 设备池管理(SQLite 清单,自动连接 + 型号采集)
### 2.3 配置键速查(`config.py` / `.env`)
`.env` 加载方式:项目根目录逐行解析、`os.environ.setdefault`(环境变量已设则不覆盖)。以下默认值以 `config.py` 为准:
| 键 | 默认值 | 说明 |
|------|------|------|
| `WEB_HOST` / `WEB_PORT` | `0.0.0.0` / `18050` | Web 后台监听(18050 避开 Windows 动态端口范围) |
| `DATA_DIR` / `APK_DIR` | `data/` / `data/apks/` | 持久化数据目录 / APK 存储目录 |
| `BACKUP_DIR` / `RESTORE_STAGING_DIR` / `RESTORE_PENDING_DIR` | `data/backups` / `data/restore_staging` / `data/restore_pending` | 系统备份:导出 zip、导入暂存、待重启生效的恢复目录 |
| `ADB_PATH` | `bin/adb/adb`(Windows 为 `adb.exe`) | 按平台自动识别,代码只拼路径 |
| `USB_ADB_HOST` / `USB_ADB_PORT` | `100.100.10.1` / `5037` | 220 的 adb 容器(host 网络),驱动远程 USB 设备 |
| `DISCOVERY_PORT` / `DISCOVERY_SUBNETS` / `DISCOVERY_INTERVAL` | `5555` / 局域网+Tailscale 网段 / `60` | 设备自动发现;可在工具页设备池面板改,存 `app_meta` `discovery_*` 覆盖默认 |
| `TAILSCALE_API_KEY` / `TAILSCALE_TAILNET` | 空 / 空 | 工具页 Tailscale 管理(官方 API v2) |
| `WEB_SECRET_KEY` | 未配置则随机生成 | 会话密钥(web_server 读 `.env`;不配则重启登录态失效) |
| `STF_URL` / `STF_TOKEN` / `STF_SSH_*` | 废弃 | STF 摘除后仅历史保留,代码不再使用 |
---
## 3. 技术红线(开发限制)—— 违反会打断共享 adb transport,需人工恢复
这些是踩过坑后总结的,**任何修改都不能引入**。违反任何一条都会导致设备连接被全部重建(历史原因:STF provider 共享同一 adb transport,摘除 STF 后仍保留此约束):
1. **绝不 `adb kill-server`**
- 会断开所有设备的 adb transport,全部设备连接被重建,运行中任务中断
- 见 `core/adb_helper.py`
2. **绝不对 `IP:5555` 设备 `adb disconnect`**
- 该地址的 adb transport 是共享的(历史与 STF provider 共用),disconnect 会断掉全部相关连接
- 直连模式下 `release()` 不 disconnect
- 见 `core/device_worker.py` `STFDevice.release()`
3. **空闲设备扫描不主动 connect/disconnect**
- IP:5555 的 transport 由多方共享(历史与 STF provider 共用),外部 connect/disconnect 会扰动共享连接
- `_ForegroundScanner._scan_free` 对空闲设备直接返回"空闲",不碰 adb
- 见 `core/task_manager.py`
4. **adb key 保持历史 key 不变**(见 2.1,设备信任该 key)
5. **直连优先,不引入第三方桥接**(见 2.2)
### 3.1 其他开发限制
- **`web_server.py` 以 `debug=False` 运行,不热重载**:改 `core/`、`tasks/`、`templates/`、`static/` 后必须重启 web_server(前端 HTML 改完强刷浏览器)
- **任务参数放各自 `tasks/<app>/task.py` 顶部,不放 `config.py`**
- **生产环境(220)默认只读**:任何写操作(改文件/重启容器/部署)都必须先经负责人确认
- **数据库是 SQLite**(`data/users.db`,WAL 模式):运行时数据不提交 git
- **前端 JS 已拆分多文件**(均位于 `static/admin/`):`monitor.html` 按 `base.js → markdown.js → list.js → monitor.js → editor.js → tasks.js → tools.js → apps.js → admin.js → agent.js → system.js` 的顺序用 `<script src>` 加载(`markdown.js` 提供 `renderMarkdown()`,AI 控制台回答渲染用;依赖 `base.js` 的 `esc()`,故排在其后、agent.js 之前)
- **监控页/大列表已加分页**:100 台设备也只渲染 10 行/页,不要移除分页逻辑
- **任务批量触发已错峰**(`_START_STAGGER_SEC`):避免大量设备同时启动造成 adb 连接风暴,不要移除
---
## 4. 本地开发手册
### 4.1 首次安装
```bash
# 用 Python 3.12 建 venv(README 推荐版本)
/opt/homebrew/bin/python3.12 -m venv .venv
.venv/bin/pip install -r requirements.txt
# macOS:adb 用系统自带的(bin/adb/adb 是被 gitignore 的符号链接)
ln -sf /opt/homebrew/bin/adb bin/adb/adb
# 确认 adb key(必须是 STF 容器的 key,否则设备连不上)
ls -la ~/.android/adbkey
```
### 4.2 启动
```bash
.venv/bin/python web_server.py
# 访问 http://localhost:18050/ 账号 admin/admin123
```
启动日志看到以下即成功:
```
[INFO] [core.worker] 心跳看门狗已启动
[INFO] [web] 管理后台: http://localhost:18050/ (admin/admin123)
[INFO] [web] 启动服务: http://localhost:18050/
```
### 4.3 测试
- **后端逻辑**:直接 `.venv/bin/python -c "..."` 调用(如 `tasks/`、`core/` 的函数)
- **前端 UI**:Playwright(系统 `python3` 已装),脚本示例见下
- **浏览器冒烟**:切 6 个 tab、开任务编辑器,确认无 JS 错误
```python
# Playwright 冒烟示例(python3 运行)
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
b = p.chromium.launch(headless=True)
pg = b.new_page()
pg.goto("http://127.0.0.1:18050/login")
pg.fill("input[name=username]", "admin")
pg.fill("input[name=password]", "admin123")
pg.click("input[type=submit], button[type=submit]")
pg.wait_for_load_state("networkidle")
# ... 检查各 tab、编辑器
b.close()
```
### 4.4 常用调试
- **看日志**:`logs/` 下 `core.log` / `task.log` / `web.log` / `action.log`(10MB 滚动,保留 5 份)
- **看设备/任务状态**:浏览器监控页,或 `GET /api/status`、`GET /api/health`
- **停止设备/清异常**:监控页工具条用"停止全部 / 停止选中 / 清除全部异常"(`AUTO_RELEASE_STALE_OCCUPY` 等 STF occupy 残留清理配置代码已不再读取);崩溃残留的 worker 状态重启即清零
- **打包项目**:`python scripts/pack.py`
---
## 5. 常见开发任务
### 5.1 新增 App 任务
参照 `tasks/douyin/` 结构,详见 **[doc/TASK_DEV.md](TASK_DEV.md)**(6 步模板)。
```
tasks/<app>/
__init__.py # from . import task
task.py # DEFAULT_PARAMS + Worker + @register_task
actions/ # 专属操作(可选)
```
### 5.2 新增专属操作
在 `tasks/<app>/actions/` 建 `.py`,继承 `BaseAction` + `@register_action(ACTIONS)`,在 `__init__.py` import。
### 5.3 修改前端
| 文件 | 内容 |
|------|------|
| `templates/admin/monitor.html` | HTML 结构 + CSS + `<script src>` 引用 |
| `static/admin/`(base/markdown/list/monitor/editor/tasks/tools/apps/admin/agent/system.js) | 前端 JS(按 monitor.html 中 `<script src>` 顺序拆分加载,功能归属见各文件;`markdown.js` = 轻量 Markdown 渲染器,AI 控制台回答用) |
改完**强刷浏览器**(Cmd/Ctrl+Shift+R),必要时重启 web_server。
### 5.4 新增 API
路由按功能域放在 `web/` 蓝图包(`web/__init__.py` 的 `register_blueprints(app)` 统一注册 10 个蓝图:auth / monitor / tasks / admin / tools / devices / apks / tailscale / agent / system)。
- 新增 API:在对应功能域的 `web/xxx_api.py` 里加 `@bp.route(...)` + 权限装饰器(如 `admin_required`)
- 新建蓝图:需在 `web/__init__.py` 里 import 并加进 `register_blueprints` 的注册元组
- `web_server.py` 只做 app 装配(初始化、`register_blueprints(app)`、常驻线程启动),一般不改
- 最后更新 **[doc/API.md](API.md)**
### 5.5 新增数据库字段/表
- 模型改 `core/models.py`,首次建表用 `create_all()`;SQLAlchemy 模型未写 `__tablename__` 时默认表名 = 小写类名
- **已有数据的老库**:在 `core/models.py` 的 `SCHEMA_MIGRATIONS` 里加迁移(版本号递增 + SQL)
- `app_meta`(KV 配置表)由 `core/models.py` 的 `_migrate_schema()` 建表并维护 `schema_version`
- `agent_conversation` / `agent_experience` / `experience_audit` / `agent_action`(动作经验库)由 `web/agent_api.py` 内的原生 `CREATE TABLE IF NOT EXISTS` 幂等创建(模块内首次用时执行),**不经 `SCHEMA_MIGRATIONS`,无版本管理**
### 5.6 改动必须同步文档
任何功能/配置/接口/页面改动,须与代码同一 commit 同步更新对应文档:
| 改动类型 | 对应文档 |
|------|------|
| HTTP 接口 | [API.md](API.md) |
| 数据表 / schema | [ARCHITECTURE.md](ARCHITECTURE.md) §3.6 |
| `config.py` / `.env` 键增删 | 本文档 §2 + [.env.example](../.env.example) |
| 页面 Tab / 子分栏 / 前端拆分 | [ARCHITECTURE.md](ARCHITECTURE.md) §5 |
| 任务 / 步骤 | [TASK_DEV.md](TASK_DEV.md) |
| 常驻线程 / 进程与装配 | [ARCHITECTURE.md](ARCHITECTURE.md) §7 |
| MCP 工具 | [MCP.md](MCP.md) 与 [MCP_DESIGN.md](MCP_DESIGN.md) |
| 对外接入 / 数字员工知识库 | [staffdeck/KNOWLEDGE_BASE.md](staffdeck/KNOWLEDGE_BASE.md) 与 [staffdeck/JOB_SPEC.md](staffdeck/JOB_SPEC.md) |
> **备份覆盖红线(2026-09-10 新增)**:**新增任何持久化表**(业务数据)时,必须同步把它登记进
> `core/system_backup.py` 的 `SUMMARY_TABLES`(并在 `TABLE_LABELS` 给中文名)+ 更新
> [DEPLOY.md](DEPLOY.md) §3.5 的覆盖清单。理由:清单漏登记 → 导出预览看不到该表 → 会被误判为
> "没有备份"(动作库 `agent_action` 就踩过)。导出侧有覆盖自检、导入侧有未登记表反向告警。
注:STF_REMOVAL.md 是历史迁移记录,不改写。
---
## 6. 发布流程(团队约定,2026-09-10 更新)
**每个改动都走分支,确认后再并 dev;dev 整体就绪后才并 main 上生产。**
```
① 本机新建分支 fix/xxx 或 feat/xxx(从 dev 切出)
② 分支上开发 + 自测 本机跑通(服务/接口/页面)
③ 交负责人确认 ★ 未经确认不合 dev
④ 合并到 dev 确认通过后(fast-forward 或 merge)
⑤ dev 整体就绪 dev 上功能齐全、验证完毕
⑥ 合并到 main ★ 负责人确认后
⑦ 生产 220 部署 git pull → 重启 python-app 容器 → 验证
```
**生产 220 部署(目录 `/mnt/data/openstf/auto_control`,容器 `python-app` 挂到 `/app`)**:
```bash
cd /mnt/data/openstf/auto_control
git fetch origin && git checkout main && git pull --ff-only origin main
docker restart python-app # 入口 scripts/start.sh:依赖守卫 → 拉起 MCP → exec web_server
# 验证:curl -s http://127.0.0.1:18050/api/health ; ss -ltnp | grep -E ':(18050|8033|20242)'
```
**发布前检查**:生产容器 adb key、依赖(新增依赖看 `requirements.txt`)、数据库迁移(`create_all` 自动补新表)、
以及"新增持久化表是否已登记进备份覆盖清单"(见 §5.6 红线)。
**数据迁移**:用平台自带「系统 → 数据备份导出/导入」;导入后需**重启容器**才生效(见 [DEPLOY.md](DEPLOY.md) §3.5)。
---
## 7. 文档索引
| 文档 | 内容 |
|------|------|
| [README](../README.md) | 项目总览、快速上手 |
| **[DEVELOPMENT.md](DEVELOPMENT.md)** | 本文档:流程/准则/限制/手册 |
| [TASK_DEV.md](TASK_DEV.md) | 任务开发指南(新增 App 任务模板) |
| [ARCHITECTURE.md](ARCHITECTURE.md) | 架构详解(分层、数据流、设计决策) |
| [API.md](API.md) | 全部 HTTP 接口说明 |
| [DEPLOY.md](DEPLOY.md) | 部署指南(环境、生产、故障排查) |
| [STF_REMOVAL.md](STF_REMOVAL.md) | 摘除 STF 的历史迁移记录(2026-08,不随现状改写) |
| [MCP.md](MCP.md) | MCP 手机控制使用手册(工具清单/用法) |
| [MCP_DESIGN.md](MCP_DESIGN.md) | MCP 架构与演进设计 |
| [AI_TASK_GEN.md](AI_TASK_GEN.md) | AI 建任务设计文档 |
| [staffdeck/KNOWLEDGE_BASE.md](staffdeck/KNOWLEDGE_BASE.md) | 给 StaffDeck 数字员工的知识库(MCP 接入/工具/约定/红线) |
| [staffdeck/JOB_SPEC.md](staffdeck/JOB_SPEC.md) | 数字员工岗位说明(岗位描述/看板摘要/执行约束) |
| [backlog/TODO.md](backlog/TODO.md) | 待完成项(已确认但暂缓的功能/优化,完成时移出并同步文档) |