# 设备自动化后台(platform-tools) 基于 **STF(Smartphone Test Farm)+ uiautomator2 + Flask** 的 Android 多设备自动化任务执行平台。 提供 Web 管理后台,支持多设备并发任务执行、定时调度、设备分组管理、APK 批量安装、UI 元素抓取等功能。内置抖音养号任务和通用步骤任务,可扩展任意 App 的自动化操作。 --- ## 目录 - [快速上手](#快速上手) - [项目结构](#项目结构) - [核心概念](#核心概念) - [配置说明](#配置说明) - [Web 管理后台](#web-管理后台) - [任务系统](#任务系统) - [日志系统](#日志系统) - [常用脚本](#常用脚本) - [常见问题](#常见问题) - [更多文档](#更多文档) --- ## 快速上手 ### 环境要求 - **Python 3.10+**(推荐 3.12) - **OpenSTF 服务**:已部署并运行,设备已接入 STF - **Windows / Linux / macOS**均可(adb 二进制需放对应平台版本到 `bin/adb/`) - 设备需开启 **USB 调试**或通过 **adb 网络连接**(IP:5555)接入 ### 三步启动 ```bash # 1. 安装依赖 pip install -r requirements.txt # 2. 修改配置(STF 地址、token、adb 路径等) # 编辑 config.py 中的 STF_URL 和 STF_TOKEN # 3. 启动 Web 后台 python web_server.py ``` 启动后访问 **http://localhost:18050/**,默认账号 `admin` / `admin123`。 > Windows 用户也可双击 `start_web.bat` 启动(自动提权 + 添加防火墙规则,支持局域网访问)。 ### 验证启动 控制台看到以下日志即表示启动成功: ``` [INFO] [core.worker] 心跳看门狗已启动 [INFO] [core.tm] 从数据库加载 X 个分组, X 个任务 [INFO] [web] 管理后台: http://localhost:18050/ (admin/admin123) [INFO] [web] 启动服务: http://localhost:18050/ ``` ### 首次使用流程 1. **登录后台** → 用 `admin/admin123` 登录,建议立即修改密码 2. **查看设备** → 首页"监控"Tab 自动展示 STF 设备池状态 3. **创建分组**(可选)→ "分组"Tab 按批次/项目给设备分组 4. **创建任务** → "任务"Tab 新建任务,选择任务类型、目标设备、参数、调度 5. **执行任务** → 任务列表点"立即执行",或在"监控"Tab 勾选设备批量操作 6. **查看日志** → "日志"Tab 实时查看运行日志 --- ## 项目结构 ``` platform-tools/ ├── config.py # 根配置(STF地址/token、adb路径、web端口) ├── web_server.py # Flask 入口(API + 登录 + 单页应用) ├── main.py # 命令行入口(单设备跑一次,不走 web) ├── create_task.py # 一次性脚本(创建定时任务示例) ├── requirements.txt # Python 依赖清单 ├── start_web.bat # Windows 一键启动(提权 + 防火墙) │ ├── core/ # 核心基础设施层 │ ├── logger.py # 统一日志(分文件、10MB 滚动) │ ├── stf_client.py # STF REST API 封装(占用/释放/远程连接) │ ├── adb_helper.py # adb 命令封装(全局锁,绝不 kill-server) │ ├── device_worker.py # BaseWorker 基类 + STFDevice + 心跳看门狗 │ ├── task_manager.py # TaskManager 调度器 + 前台App扫描器 │ ├── u2_helper.py # uiautomator2 通用辅助函数 │ ├── uiauto_helper.py # uiautodev 元素抓取客户端 │ ├── apk_manager.py # APK 上传/解析/批量安装 │ ├── models.py # SQLAlchemy 数据模型 + 数据库初始化 │ └── actions/ │ ├── __init__.py # 全局 Action 框架导出 │ └── base.py # BaseAction 基类 + 注册机制 │ ├── tasks/ # 任务定义层(每个 App 一个子包) │ ├── __init__.py # 全局任务注册表 │ ├── base.py # BaseTask 基类 + @register_task 装饰器 │ ├── douyin/ # 抖音养号任务 │ │ ├── __init__.py │ │ ├── task.py # DEFAULT_PARAMS + Worker + Task │ │ └── actions/ # 抖音专属操作(点赞等) │ │ ├── __init__.py │ │ ├── base.py # 抖音 ACTIONS 注册表 │ │ └── like.py # 点赞操作(找红心/双击回退) │ └── generic/ # 通用步骤任务(可视化编辑器编排) │ ├── __init__.py │ └── task.py # 步骤执行引擎(open_app/click/swipe/loop...) │ ├── templates/admin/ # 前端页面 │ ├── monitor.html # 单页应用(监控/任务/分组/日志/用户 5 Tab) │ └── login.html # 登录页 │ ├── static/admin/ │ ├── custom.css # 自定义样式 │ └── monitor.js # 前端 JS(已从 monitor.html 拆分) │ ├── data/ # 运行时数据 │ ├── users.db # SQLite(用户/分组/任务/自定义动作/APK记录) │ └── apks/ # 上传的 APK 文件存储 │ ├── logs/ # 日志文件(自动生成,10MB 滚动保留 5 份) │ ├── core.log # 核心程序日志 │ ├── task.log # 任务执行日志 │ ├── web.log # Web 请求日志 │ └── action.log # 操作执行日志 │ ├── bin/adb/ # adb 可执行文件(Windows: adb.exe + dll) ├── scripts/ # 实用脚本 │ ├── cleanup.py # 清理 STF 残留设备占用 │ └── pack.py # 打包项目为 zip(排除运行时产物) └── doc/ # 项目文档 ├── TASK_DEV.md # 任务开发指南(新增 App 任务模板) ├── ARCHITECTURE.md # 架构详解 ├── DEPLOY.md # 部署指南 └── API.md # API 接口文档 ``` --- ## 核心概念 ### 设备生命周期 ``` STF occupy(占用设备) ↓ STF remoteConnect(建立远程 ADB 隧道) ↓ adb connect(连接远程设备) ↓ u2.connect(连接 uiautomator2,自动推送 atx-agent) ↓ Worker.run_task(执行业务逻辑) ↓ adb disconnect + STF release(释放设备) ``` > 直连模式:当设备 serial 为 `IP:5555` 格式时,直接 adb connect,不走 STF 桥接,更稳定。 ### Worker — 单设备执行线程 每台设备对应一个 `Worker` 线程,继承 `BaseWorker`(`core/device_worker.py`)。基类已封装: - STF 设备占用/释放(try/finally 保证释放) - adb 连接 + u2.connect(带 30 秒超时保护) - 状态上报(实时推送到前端监控大屏) - 异常捕获(设备离线不重试,其他异常按策略重试) - stop 停止信号(循环里检查 `self.stopped()`) - 心跳看门狗(120 秒无心跳自动标记卡死) 子类只需实现 `run_task(d)` 方法专注业务逻辑。 ### TaskType — 任务类型 | 任务类型 | task_type | 说明 | |---------|-----------|------| | 抖音养号 | `douyin_nurture` | 自动看视频 + 随机点赞,被退出自动重连 | | 通用步骤 | `generic_steps` | 可视化步骤编辑器编排流程,支持循环/点击/滑动等 | ### TaskJob — 任务计划 一个 TaskJob 描述"什么时候、在哪些设备上、用什么参数执行什么任务": | 字段 | 说明 | 示例 | |------|------|------| | `task_type` | 任务类型 | `"douyin_nurture"` | | `target` | 目标设备 | `{"mode": "all"}` 或 `{"mode": "group", "group_name": "A组"}` 或 `{"mode": "serial", "serial": "192.168.1.100:5555"}` | | `params` | 任务参数(与默认值深合并) | `{"watch_count": 50, "actions": {"like": {"params": {"rate": 0.5}}}}` | | `schedule` | 调度策略 | `{"mode": "once"}` 或 `{"mode": "cron", "cron": "0 9 * * *"}` 或 `{"mode": "cron_stop", "cron": "0 9 * * *", "stop_cron": "0 18 * * *"}` | | `retry` | 重试策略 | `{"max_attempts": 3, "delay": 60}` | | `enabled` | 是否启用 | `true` | ### 调度模式 | 模式 | 行为 | |------|------| | `once` | 手动执行(前端点"立即执行") | | `cron` | 定时启动:到 cron 时间点自动启动所有目标设备 | | `cron_stop` | 定时启停:启动 cron 到点启动,停止 cron 到点停止本任务 worker | cron 表达式为标准 5 段格式:`分 时 日 月 周`(如 `0 9 * * *` = 每天 9:00) ### 设备分组 设备分组存于 SQLite,便于按批次/项目分组下发任务。一个 Job 指定 `target.mode="group"` 时,调度器展开为组内全部设备。 ### 进度上报 Worker 通过 `self.set_progress()` 上报通用进度字段,前端统一解析展示: ```python self.set_progress(done=5, total=80, unit="视频", action_counts={"like": 3}) ``` 前端展示:进度条 + `5/80 视频` + `点赞 3` 徽章。 --- ## 配置说明 所有核心配置在 [config.py](file:///d:/platform-tools/config.py),**任务参数不放在这里**(放各自 `tasks/xxx.py` 顶部)。 | 配置项 | 默认值 | 说明 | |-------|--------|------| | `STF_URL` | `http://192.168.20.220:7100` | OpenSTF 服务地址 | | `STF_TOKEN` | (需替换) | STF API Token,在 STF 个人设置里生成 | | `ADB_PATH` | 自动识别 | adb 二进制路径,自动区分 Windows/Linux | | `WEB_HOST` | `0.0.0.0` | Web 监听地址(0.0.0.0 支持局域网访问) | | `WEB_PORT` | `18050` | Web 端口(避开 Windows 动态端口范围) | | `DATA_DIR` | `data/` | 持久化数据目录 | | `APK_DIR` | `data/apks/` | APK 文件存储目录 | **必须修改的配置**:`STF_URL` 和 `STF_TOKEN`,改成你自己的 STF 服务地址和 Token。 --- ## Web 管理后台 ### 页面结构 单页应用(`templates/admin/monitor.html`),5 个 Tab: | Tab | 功能 | |-----|------| | 监控 | 设备状态大屏:在线/离线、运行任务、当前动作、进度条、前台 App、截图、停止 | | 任务 | 任务计划 CRUD:新建/编辑/删除/启用停用/立即执行 | | 分组 | 设备分组管理:创建/编辑/删除分组 | | 日志 | 实时日志查看:按模块切换(core/task/web/action) | | 用户 | 用户管理:创建/删除/修改密码 | ### 前台 App 扫描 监控页"扫描前台App"按钮,获取所有设备当前前台 App。**不打扰设备**: | 设备状态 | 处理方式 | |---------|---------| | worker 运行中 | 复用已有 ADB 连接查询 | | 自己占用 | 通过 STF remoteConnect 隧道查询(不 occupy/release) | | 完全空闲 | 返回"空闲"(不主动 adb connect,避免 STF 误判离线) | | 他人占用 | 标记"(他人占用)" | ### 截图功能 监控页每台设备可查看实时截图。用 `adb exec-out screencap -p`,只读操作,**任务运行中也能安全调用**(不抢占 u2 的 atx-agent 通道)。 ### 元素抓取 任务编辑器的"抓取元素"按钮可拉取设备当前 UI 元素树,点击元素一键回填选择器。依赖本地运行的 uiautodev 服务(端口 20242),`web_server.py` 启动时会自动拉起。 --- ## 任务系统 ### 抖音养号(douyin_nurture) 自动观看抖音视频,按配置随机执行点赞操作。参数(在 `tasks/douyin/task.py` 顶部): | 参数 | 默认值 | 说明 | |------|--------|------| | `watch_count` | 80 | 观看视频数量(0=不限,靠时长停止) | | `watch_min` / `watch_max` | 5.0 / 35.0 | 单视频观看时长范围(秒) | | `max_duration` | 0 | 最大运行时长(秒),0=不限时 | | `swipe_min` / `swipe_max` | 0.25 / 0.50 | 上滑手势时长范围(秒) | | `gap_min` / `gap_max` | 1.0 / 3.0 | 视频间隔时长范围(秒) | | `actions.like.enabled` | true | 是否启用点赞 | | `actions.like.params.rate` | 0.3 | 点赞概率(0~1) | | `actions.like.params.method` | by_element | 点赞方式:by_element(找红心) / double_click(双击) | 终止条件(哪个先到就停):`watch_count` 数量到 / `max_duration` 时长到 / 手动停止。 ### 通用步骤(generic_steps) 通过可视化步骤编辑器编排任务流程,worker 按步骤顺序执行。支持的步骤类型: | 步骤类型 | 说明 | |---------|------| | `open_app` | 启动 App(指定包名,可选等待首页) | | `swipe` | 滑动(上/下/左/右,可配置时长) | | `click` | 点击元素(支持 xpath/description/resourceId/text 选择器) | | `input_text` | 输入文字(随机候选/固定文字) | | `wait` | 等待(可配置时长范围) | | `loop` | 循环块(含子步骤,可配置循环次数) | | `group` | 动作组(含子步骤,按序执行一次) | ### 新增 App 任务 参照 `tasks/douyin/` 结构,6 步即可新增一个 App 任务,详见 [doc/TASK_DEV.md](doc/TASK_DEV.md)。 --- ## 日志系统 日志按模块分文件,自动滚动(10MB 一份,保留 5 份历史): | 文件 | 模块前缀 | 内容 | |------|---------|------| | `logs/core.log` | `core.*` | STF/adb/worker/task_manager 核心程序 | | `logs/task.log` | `task.*` | 任务执行(worker 业务逻辑) | | `logs/web.log` | `web.*` | Web 请求/管理 | | `logs/action.log` | `action.*` | 操作执行(点赞/评论等) | 使用方式: ```python from core.logger import get_logger log = get_logger("task.douyin") # 写入 task.log log.info(f"[{self.serial}] 开始任务") ``` Web 后台"日志"Tab 可实时查看各模块日志。 --- ## 常用脚本 | 脚本 | 用途 | |------|------| | `python web_server.py` | 启动 Web 管理后台 | | `python main.py` | 命令行跑一次抖音养号(单设备,不走 web) | | `python create_task.py` | 创建"抖音定时8-9点"任务(示例脚本) | | `python scripts/cleanup.py` | 清理 STF 残留设备占用(强杀进程后用) | | `python scripts/pack.py` | 打包项目为 zip(排除日志/数据库/APK) | | `start_web.bat` | Windows 一键启动(提权 + 防火墙规则) | --- ## 常见问题 ### 端口被占用(WinError 10013/10048) `web_server.py` 会自动重试候选端口(原端口 → 127.0.0.1:原端口 → 127.0.0.1:原端口+1~+5)。如果全部失败,检查 Windows 动态端口范围: ```bash netsh interface ipv4 show excludedportrange protocol=tcp ``` 修改 `config.py` 中的 `WEB_PORT` 到一个不在排除范围内的端口。 ### STF 设备显示离线但实际在线 STF 状态有缓存,`present=True` 不代表设备真在线。用"扫描前台App"按钮复测,或直接尝试执行任务。 ### 设备一直被占用无法释放 运行清理脚本: ```bash python scripts/cleanup.py ``` 会释放当前账户占用的所有设备。 ### u2.connect 卡死 `u2.connect()` 在 atx-agent 无响应时会永久 hang。基类已用 `ThreadPoolExecutor + 30 秒超时` 保护,超时自动放弃。如果频繁超时,检查设备 atx-agent 是否正常(重启设备或重新推送 atx-agent)。 ### 任务运行中看门狗误杀 看门狗 120 秒无心跳会标记卡死。长耗时操作(如长视频等待)需在循环内周期性调用 `self.heartbeat()`。 ### 中文输入失败 uiautomator2 默认 IME 不支持中文,需切到 FastInput 输入法: ```python try: d.set_fastinput_ime(True) d.send_keys("中文内容") finally: d.set_fastinput_ime(False) ``` ### 修改 core/ 后不生效 `web_server.py` 以 `debug=False` 运行,Python 不会热重载。修改 `core/` 目录下的文件后**必须重启 web_server 进程**。 ### 元素抓取按钮不可用 依赖 uiautodev 服务(端口 20242)。确保已安装 `pip install uiautodev`。`web_server.py` 启动时会自动拉起该服务。 --- ## 更多文档 | 文档 | 内容 | |------|------| | [doc/DEVELOPMENT.md](doc/DEVELOPMENT.md) | **开发手册**:开发流程、git 工作流、技术红线、环境、本地开发 | | [doc/TASK_DEV.md](doc/TASK_DEV.md) | 任务开发指南(新增 App 任务的完整模板和规范) | | [doc/ARCHITECTURE.md](doc/ARCHITECTURE.md) | 架构详解(分层设计、数据流、关键设计决策) | | [doc/DEPLOY.md](doc/DEPLOY.md) | 部署指南(环境准备、STF 配置、生产部署) | | [doc/API.md](doc/API.md) | API 接口文档(全部 HTTP 接口说明) | > **注意**:所有 git 操作(含 push 到 dev)都需负责人确认后才能执行,详见开发手册。修改 `core/`、`tasks/`、`templates/` 后需重启服务/强刷浏览器才生效。 --- ## 技术栈 | 组件 | 用途 | |------|------| | Flask + Flask-Login | Web 后台 + 用户认证 | | Flask-SQLAlchemy | SQLite 数据持久化 | | APScheduler | 定时任务调度 | | uiautomator2 | Android UI 自动化 | | uiautodev | UI 元素抓取(步骤编辑器"抓取元素") | | pyaxmlparser | APK 元信息解析 | | OpenSTF | 设备农场管理(占用/释放/远程 ADB) |