提交 63ed9c69 authored 作者: 陈泽健's avatar 陈泽健

docs(ui-automation): 精简 HANDOFF 文档至会话48核心内容(755行→66行)

- 删除会话47~36历史章节、时间线表格、关键约束/配置/启动运维/文档索引、老待办清单
- 保留会话48双档(sut-explore缺口补齐 + 定时任务看门狗修复)核心记录
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
上级 545ee9b4
......@@ -2,8 +2,8 @@
> **生成时间**: 2026-09-01
> **当前分支**: `platform-auto-test`
> **最近提交**: `ec6b4382` feat(ui-automation): sut-explore 补齐会议运维+待办巡检缺口用例 8/8 通过
> **状态**: 🟢 **会话48:sut-explore 缺口补齐(会议运维 6 Tab + 待办巡检 2 用例)——待办巡检 monitor 直达 URL 内层路由竞态定位并改用首页「巡检报表」入口点击绕过,最终 8/8 用例 100% 通过**(同日另一会话 48 完成定时任务看门狗修复,见下双章节)
> **最近提交**: `720bf764` feat(ui-automation): sut-explore 补齐会议运维+待办巡检缺口用例 8/8 通过
> **状态**: 🟢 **会话48:sut-explore 缺口补齐(会议运维 6 Tab + 待办巡检 2 用例)——待办巡检 monitor 直达 URL 内层路由竞态定位并改用首页「巡检报表」入口点击绕过,最终 8/8 用例 100% 通过;已提交推送,8 条缺口用例已同步三台(5.44/5.202→407、5.60→409,无重启)**(同日另一会话 48 完成定时任务看门狗修复,见下双章节)
---
......@@ -12,741 +12,55 @@
**sut-explore 缺口补齐:会议运维 6 Tab + 待办巡检 2 用例,最终 8/8 100% 通过**。应 `/sut-explore` 全量盘点确认 7 个缺口功能(待办巡检 + 会议运维 6 Tab),生成 8 个可执行 UI 用例写入 SQLite(`generate_gap_cases.py`,幂等)。会议运维 6 用例 navigate 直达一次全绿;**待办巡检 2 用例经历 monitor 直达 URL 内层路由竞态(首轮通过 / 复验 2 次全失败)攻坚后,改用首页「巡检报表」入口点击稳定通过**
### ✅ 会议运维 6 用例(navigate 直达,一次通过)
- 直达 URL:`https://192.168.5.44/#/meetingV3?meetingV3=%2FmeetingV3%2F%23%2FMeetingmaintenance`(登录态跨 hash 保留,可 navigate 直达)
- 断言模式:`body:has-text("工单列表")` + `.el-table` + `.el-table__row`(≥1)+ `.el-tabs__item.is-active:has-text("{Tab}")`
- 覆盖:页面访问验证(默认终端列表 Tab)+ 终端列表/终端链路图/当前告警/历史告警/工单列表 5 个 Tab 切换验证
- 探测沉淀:`probe_gap_meeting_tabs.py`(Tab 选择器)、`probe_gap_modules_v4.py`(微应用 frame/micro-app 属性)
- 直达 URL:`https://192.168.5.44/#/meetingV3?meetingV3=%2FmeetingV3%2F%23%2FMeetingmaintenance`
- 覆盖:页面访问验证 + 终端列表/终端链路图/当前告警/历史告警/工单列表 5 个 Tab 切换验证
- 探测沉淀:`probe_gap_meeting_tabs.py``probe_gap_modules_v4.py`
### ✅ 待办巡检 2 用例(monitor 路由竞态攻坚,核心)
**失败现象**:navigate 直达 MONITOR_URL 间歇性落设备列表页(首轮通过 / 复验 0/2 失败)。
**失败现象**:navigate 直达 `MONITOR_URL``/backend/monitor?monitor=...Checklist`)+ 执行器二段式 warm-up,**间歇性**落入设备列表页而非巡检列表(首轮 exec_0f4627... 表格用例通过、复验 exec_926f... 0/2 全失败)。
**根因(探测证据链)**
| 项 | 结论 |
|----|------|
| monitor 整页加载后内层路由 | **默认落设备列表(Monitoringindex)页**(ths=设备名称/告警/在线/开关/运行),即使 URL hash 含 `...adminsystem/monitor/Checklist` |
| 绕不过的竞态 | navPayload.path 各候选(`/adminsystem/monitor/Checklist``/monitor/Checklist``list=Checklist`、空 path)+ 真实首页 navPayload(operationStatistics)+ JS 重设外层 hash 共 6+ 变体**全部落设备列表页**——平台壳派发内层 hash 的时序竞态,偶发可达 |
| 执行器就绪判定缺陷 | `_ensure_micro_app_loaded` marker("Checklist") 从外层 URL 解析恒存在 + 设备列表 body 非空 → **设备列表页被误判为"就绪"**,后续断言在错误页面失败 |
| 附带断言缺陷 | 「一键巡检」是 `<span>`(父级 `.company-edmit-right`),非 `<button>`;原 `button:has-text("一键巡检")` 即使到对页面也找不到 |
**稳定解法**:不 navigate 直达,改为 **navigate 首页 → 真实点击首页「巡检报表」入口 `[id="data_list.inspect_report"]`**(DIV.block,父级 `.first_box_flex`)→ 平台壳自身完成 monitor 初始化 + 内层路由派发 → **100% 落巡检列表页**。独立验证 5 次 + 执行器内全绿。
**根因**`_ensure_micro_app_loaded` 误判就绪 + 「一键巡检」为 span 元素而非 button。
- 落地后特征:URL `/backend/monitor?monitor=...Checklist`;表格列头=巡检名称/区域名称/检测结果/启用/执行方式/定时方式/开始日期/结束日期/执行时间/更新时间/操作;5 行真实数据(急急急/测试 hhh);工具栏=一键巡检/新增/编辑/删除
**稳定解法**:navigate 首页 → 点击 `[id="data_list.inspect_report"]` → 100% 落 Checklist 页。
### ✅ 最终复验(`exec_03e8940a29a34ae6b1633a24e747e521`)
**8/8 用例 100% 通过,0 失败**:会议运维 6 用例 + 待办巡检-页面访问验证 + 待办巡检-表格数据验证。
**最终复验**`exec_03e8940a29a34ae6b1633a24e747e521` 8/8 100% 通过。
**改动清单**
| 文件 | 说明 |
|------|------|
| `backend/data/test_platform.db` | 8 条缺口用例(会议运维 6 已通过;待办巡检 2 条步骤重写为首页入口点击路径) |
| `backend/scripts/generate_gap_cases.py` | 待办巡检生成逻辑同步更新(含完整踩坑注释,幂等可重复执行) |
| `backend/data/test_platform.db` | 8 条缺口用例 |
| `backend/scripts/generate_gap_cases.py` | 待办巡检生成逻辑同步更新(含踩坑注释) |
| `backend/scripts/probe_monitor_checklist_rerun.py` / `probe_real_entry_click.py` / `probe_gap_meeting_tabs.py` / `probe_gap_modules_v4.py` | 探测/复现脚本沉淀 |
| 记忆 `monitor-checklist-direct-url-race.md` | 固化"monitor 直达 URL 竞态 → 首页巡检报表入口点击"解法 |
| `memory/monitor-checklist-direct-url-race.md` | 固化解法 |
| `sync_cases_to_servers.py` | 8 条缺口用例 UPSERT 至 5.44/5.202/5.60(无重启,定时任务不受影响) |
**git 记录**:提交 `ec6b4382` → rebase(HANDOFF 4 处冲突合并)→ 变基为 `720bf764` → 已推送 `origin/platform-auto-test``97e802a6..720bf764`)。
---
## 📊 当前状态(会话 48,2026-09-01 · 定时任务看门狗修复)
**定时任务看门狗失败执行无报告输出(P0)**:5.44「每日定时自动化测试」连续 4 次被看门狗判失败、**报告目录无一文件**(最近一次 `exec_24b3a329` 09-01 01:25~06:09 约 4.7h 后看门狗标记 failed)。根因 = `scheduler_service.run_task_once()` 报告生成条件 `task.auto_report and run_ok`,看门狗置 failed 后 `run_ok=False` → 报告被跳过;而被判死时 213 passed / 95 failed 用例结果已实时写库,`ReportService.generate_html()` 只读 case_results 渲染,failed 执行完全能出报告
**定时任务看门狗失败执行无报告输出(P0)**:5.44 连续 4 次被看门狗判失败、报告目录无文件。根因 = `run_ok=False` 时跳过报告生成
### ✅ A 修复(看门狗失败执行也生成报告)— 已实施并部署三台
| 改动 | 文件 |
|------|------|
| 新增 `_has_any_case_results(db, execution_id)` 辅助函数:查询该执行是否已有用例结果 | `scheduler_service.py` |
| UI 路径报告生成条件:`run_ok``run_ok or await _has_any_case_results(...)` | 同上(line ~370) |
| Security 路径同条件统一(`_run_security_task_once`) | 同上(line ~243) |
- git diff +35/-5;语法已通过 `ast.parse` 校验;已提交 `80bbb6ac` 并部署 5.44/5.202/5.60(见下「部署完成」)
- 验收:成功执行行为不变;看门狗失败但有结果 → 出报告;启动即异常无结果 → 不出空报告
### 🔍 B 卡死点排查结论(并行,6 条证据)
| # | 结论 | 证据 |
|---|------|------|
| 1 | 每次执行都卡在「信息发布/消息通知」之后,最终被看门狗标记 | 24b3a 最后一条完成 05:51:35,其后 24 条 pending 批量置 failed |
| 2 | **case_results.start_time 从未写入**`_persist_case_result` 只写 end_time),全表 NULL → 无法精确定位卡死用例 | DB 全表扫描 |
| 3 | 容器日志已轮转(仅剩 8-29),9-01 卡死时段无日志可取证 | `docker logs` 仅含 8-29 内容;无 max-size 配置 |
| 4 | 历史日志 Chromium 页面崩溃高频(`Page crashed`/`Target crashed`/`Target closed`) | 8-29 日志 |
| 5 | 当前执行 fd29f4fe 健康(30 分钟产出 41 条结果),非全链路卡死 | DB 实况 |
| 6 | 无「单用例超时兜底」:单步超时 30s 有,整用例可无限挂起 | `playwright_executor.py` 各操作 timeout |
**卡死根因推断**:长跑 4~5h 后 Chromium 页面/tab 崩溃或目标系统无响应 → 用例操作在崩溃态挂起 → 30 分钟无进展 → 看门狗判死。B 治理(start_time 写入 / 单用例超时兜底 / CPU 崩溃恢复)见执行计划文档。
| 新增 `_has_any_case_results` 辅助函数 | `scheduler_service.py` |
| UI/Security 路径报告生成条件:`run_ok or has_results` | 同上 |
### ✅ 部署完成(A+B+日志轮转,三台服务器验证通过)
**提交**`80bbb6ac` + `f9b37c6b`**后端测试 364/364 全绿**
**提交**`80bbb6ac`(A `_has_any_case_results` + B1 start_time 回推 + B2 双层硬超时 + B3 崩溃恢复,含 `deploy/docker-compose.yml` logging 配置)+ `f9b37c6b`(deploy_fix_560 补传递依赖)。**后端测试 364/364 全绿**
**B2 方案最终形态**(未用 ThreadPoolExecutor——Windows 与 Playwright 专用线程不兼容):`playwright_executor.py` 内部双层硬超时——`_case_start_mono` + `case_remaining_seconds()``case_hard_timeout` 默认 600s)+ 步骤级 `_step_deadline_mono``step_hard_timeout` = max(120, min(case_hard_timeout,600)-60));`_do_in_micro_apps`/`_bounded_wait`/`_remaining_step_ms` 所有等待点同受步骤级+用例级截止点约束,任一先到即抛超时 → 该用例按失败处理并继续,单用例不可能无限挂起整次执行。
**B3 方案**`_is_crash_error` 识别 10 类崩溃标记(`Page crashed`/`Target crashed`/`Target closed`/`browser has been closed` 等)→ `_rebuild_page_after_crash``_context.new_page()` + 重挂 webdriver 隐藏 init script + `_detect_login_state()` 重新登录态探测,或 stop()+start())→ 从失败步骤重试(不重跑已成功步骤),`_crash_retried` 每用例重置。
**部署**`deploy_44_202.py`(5.44/5.202,root)+ `deploy_fix_560.py --no-fix --runs 0`(5.60,ubains)。**日志轮转**:三台服务器 compose 注入 `logging: json-file 50m×5``docker compose up -d --force-recreate app` 生效(仅 `docker restart` 不重新应用 compose logging 配置)。
**验收(2026-09-01)**
**B2/B3 方案**`playwright_executor.py` 内部双层硬超时 + Chromium 崩溃自动重建。
**验收**
| 服务器 | 健康检查 | LogConfig | A/B1/B2/B3 标记 | 容器状态 |
|--------|----------|-----------|----------------|----------|
| 5.44(:8081) | 200 healthy | `json-file` 50m×5 ✅ | 6/6 ✅ | Up (healthy) |
| 5.202(:8081) | 200 healthy | `json-file` 50m×5 ✅ | 6/6 ✅ | Up (healthy) |
| 5.60(:80) | 200 healthy | `json-file` 50m×5 ✅ | 6/6 ✅ | Up (healthy) |
MySQL / EMQX 容器未重建、数据无影响。遗留 1 项:**执行级功能验证待定**(在 5.44 触发一次「每日定时」确认看门狗失败路径产出报告,可选非阻塞)。
### 📄 文档产出
| 文档 | 状态 |
|------|------|
| `_问题处理_定时任务看门狗失败执行无报告输出.md` | 含根因分析 + A 修复方案 + 验收标准 |
| `_执行计划_修复定时任务看门狗失败执行无报告输出.md` | A+B 已实施 + 三台部署验收表 + 遗留问题(状态:已完成) |
---
## 📊 当前状态(会话 47,2026-08-31)
**性能测试模块目标机监控增强与修复**:本次会话在 UI 自动化 HANDOFF 中统一记录一档(对应的独立交接文档为 `HANDOFF_性能测试.md`,位于 `Docs/PRD/性能测试/`)。
### ✅ 会话内容(会话 47,2026-08-31)
| 批次 | 内容 | 提交 |
|------|------|------|
| **Batch A** | 5 个 Java 服务关键字(gateway / auth / modules-system / meeting-message-scheduling / meeting-mqtt,优先于泛化 `ubains-meeting`)+ 多 PID 服务级聚合(按采样时间点求和、avg/max 按服务级时间点)+ `pidCount`/`pids` 字段 + MySQL 容器趋势 series + 报告动态 Y 轴 + PRD/计划执行文档 | `ad4163bb` |
| **Batch B** | Java CPU 监控失真修复:`ps -eo %cpu=` 是进程生命周期平均(重启 JVM 被严重低估,实际 top 已 300%+)→ 改为 JSTAT 两次 `/proc/<pid>/stat` 瞬时采样(间隔 0.2s,tick 差/clk/0.2,top 口径可超 100%);上限 `cores*100` 防时钟异常,缺失/零差回退 ps;`sample()` 新增 `cpuCores`/`clkTck` | `ad4163bb` |
### ✅ 验证与部署(会话 47,2026-08-31)
- **测试**:新增 `backend/tests/test_target_resource_monitor.py` 15 passed(瞬时 CPU 计算/多 PID 独立/ps 回退/单快照缺失/核数截断/零差回退/段落边界/关键字与 comm 过滤/具体优先于泛化);既有 summary 7 + executor 25 全绿,无回归
- **5.44 实测**:8 核、clk=100,6 个 Java 服务正常解析;`while :` 忙循环实测每核 ≈100%(top 语义)
- **部署 5.60**:scp `target_resource_monitor.py` + `docker compose restart app` + `/health` 200 + 容器内 grep 确认 7 处 JSTAT 逻辑
- **文档**`Docs/PRD/性能测试/HANDOFF_性能测试.md` 已更新(`4e19844c`),含 P1 待办:压测期间端到端验证 Java CPU 显示 300%+ 级别
- **推送**:功能提交 `ad4163bb` + 文档提交 `4e19844c` 均已推送 `origin/platform-auto-test`
---
## 📊 当前状态(会话 46,2026-08-31)
**执行耗时优化(P0/P1/P2)**:10 条 UI 用例(含 7 条失败)执行耗时 24.8 分钟,根因是 `step_retry_count=2` × 多策略超时叠加(单条失败用例 ~210s)。输出 PRD/计划文档后实现三层优化,本地全量测试 352 通过,部署三台服务器;用 5.44 同一 10 用例定时任务实测 **24.8min → 10.2min(-59%)**,达成 ≤12min 目标,通过率 30% 无回归。
### ✅ 优化内容(会话 46,2026-08-31)
| 层 | 内容 | 改动文件 |
|----|------|---------|
| **P0 重试收紧** | `step_retry_count` 默认 2→1(共 2 次尝试,兼顾效率与偶发重试收益) | `execution_service.py` + `playwright_executor.py` + `recorder.py` |
| **P1 超时收紧** | has-text 前置等待 25s→10s;表格行轮询 150s→30s;checkbox 轮询 40s→20s;iframe 点击 5s→3s | `playwright_executor.py` |
| **P2 快速失败** | `_selector_exists_fast()` 探测(主页面/iframe/micro-app 三段,≤500ms);`_do_click`/`_do_fill`/`_do_check` 入口探测到元素不在 DOM 即跳过策略链,失败场景 100ms 即知 | `playwright_executor.py` |
**测试**`tests/test_executor_timeout_optimization.py` 9 项(P0 默认值=1/可覆盖;P2 不在 DOM 跳过策略/存在走完整链/micro-app 回退保留;P1 超时常量源码断言防回归)。**全量 352 passed,无回归**
### ✅ 部署结果(会话 46,2026-08-31,`deploy_timeout_opt.py`)
部署制品:3 个后端文件(`playwright_executor.py` + `execution_service.py` + `recorder.py`
| 服务器 | 结果 | 部署前运行任务 |
|--------|------|---------------|
| 5.60 | ✅ 健康检查/AST/容器内 SHA/执行器验证全通过 | 无 |
| 5.44 | ✅ 同上 | 无 |
| 5.202 | ✅ 同上 | 无 |
### ✅ 生产实测(5.44,同一 10 用例定时任务「小模块定时任务验证」)
| 指标 | 优化前 | 优化后 | 变化 |
|------|--------|--------|------|
| 执行 ID | `exec_0d65e9d7`(会话 44 前) | `exec_92c4b6af996b4a2ab6fc8ee4ecf1cfbe` | - |
| 总耗时 | 1485s(24.8min) | **614.36s(10.2min)** | **-59%** |
| 通过率 | 3 pass / 7 fail | 3 pass / 7 fail(30%) | 无回归 |
**结果**:耗时减半以上,达成 PRD ≤12min 目标;7 条失败为过时选择器真实失败(PRD 已预判,非优化引入)。
**定时任务状态**:「小模块定时任务验证」已启用(`enabled=true`,下轮 2026-08-31 13:55),并手动触发一轮验证执行。
**遗留观察(风险预案)**:P0 把重试从 3 次尝试降到 2 次,若后续通过率下降,回滚 P0 保留 P1+P2(PRD 风险预案)。
---
## 📊 当前状态(会话 45,2026-08-31)
**截图定期清理 + 5.60 磁盘满紧急修复**:5.44 报告 `MySQL "No space left on device"` 导致执行中心返回 500(截图目录累积 8.6GB 占满 `/data` 分区)。用户手动清理后,本次实现截图定期清理循环并完成三台部署。部署过程中发现 5.60 因 `main.py` 残留 `projects` router 引用导致容器崩溃循环,已紧急回滚并重新部署。
### ✅ 修复内容(会话 45,2026-08-31)
| 项目 | 说明 |
|------|------|
| 制品 | `cleanup_service.py`(新增截图清理循环)+ `config.py``SCREENSHOT_RETENTION_DAYS` 配置)+ `main.py`(lifespan 启动/停止截图清理) |
| 清理参数 | 扫描间隔 3600s(1 小时);保留天数 `SCREENSHOT_RETENTION_DAYS=3`(环境变量可覆盖) |
| 清理逻辑 | 按文件修改时间过滤过期截图;只删文件不删子目录;异常不中断循环(下一轮继续);`db=None` 模式仅清理文件不动数据库 |
| 测试 | `tests/test_screenshot_cleanup.py` 10 项测试(过期文件删除 / dry_run / 空目录 / 不存在目录 / 子目录跳过 / 大小计算 / 启动返回 Task / 调用一次 / 日志记录 / 异常不终止) |
### ✅ 部署结果(会话 45,2026-08-31,`deploy_watchdog.py`)
**部署制品**:4 个后端文件(`execution_service.py` + `cleanup_service.py` + `main.py` + `config.py`
| 服务器 | 结果 | 附带清理 |
|--------|------|----------|
| 5.44 | ✅ 看门狗 + 截图清理已启动 | 启动恢复清掉 1 条僵尸执行(167 条用例结果标 failed) |
| 5.202 | ✅ 看门狗 + 截图清理已启动 | 调度正常恢复 |
| 5.60 | ✅ 看门狗 + 截图清理已启动 | 首轮清理删除 **24135** 个过期截图,释放 **8.77GB** |
**5.60 事故处理**
1. 首次部署 `main.py` 时因本地工作树残留 `projects` router 引用(未提交),容器因 `ImportError: cannot import name 'projects'` 崩溃循环
2. 紧急从备份 `.bak-1788141147` 恢复 `main.py`,容器恢复正常
3. 从本地 `main.py` 移除 `projects` import + router 注册(仅保留截图清理改动)
4. 重新部署 4 个制品,全部通过(健康检查 + AST + SHA-256 一致)
**定时任务状态**:三台容器重启后调度引擎自动恢复,到期任务已自动触发补跑;`auto_report=True` 均已开启,执行完成后自动生成 HTML 报告。
---
## 📊 当前状态(会话 44,2026-08-29)
**僵尸执行常驻看门狗**:会话 42 的 `recover_interrupted_executions()` 只兜「进程重启」场景;8/29 排查发现 5.44 一条 `exec_23393d`(8/28 09:16 定时回归,332 用例跑完 221 条后线程死亡)卡 running **29 小时**,期间调度器 DB 守卫每轮跳过(日志刷「到点但 DB 中仍有 UI 执行在跑」),且 `POST /run` 返回 `triggered` 但内层静默跳过(外层只查内存锁)。手动 cancel 后 4 秒调度恢复并触发新一轮——证实根因链。
### ✅ 修复内容(会话 44,2026-08-29)
| 项目 | 说明 |
|------|------|
| 制品 | `execution_service.py`(看门狗实现)+ `main.py`(lifespan 启动/停止) |
| 看门狗参数 | 扫描间隔 60s;静默阈值 1800s(30 分钟无进展判僵尸);运行存活下限 300s(启动窗口期不判罚) |
| 双信号活性判定 | ① 内存心跳:工作线程每完成一条用例 `touch_execution_heartbeat()`;② DB 结果计数**增长**(跨进程兜底;关键设计:**存量结果不算增长**——首次观测只记基线,防止把死执行误判为活跃,即 5.44 事件形态) |
| 判罚动作 | 执行 + 关联 pending/running 用例结果标记 failed + 清心跳 + 释放内存锁 → 调度立即恢复 |
| 防误伤 | security 执行不纳入;pending 需超静默阈值才判;running 需「存活超下限 + 双信号全静默」 |
| 测试 | `tests/test_watchdog_stale_execution.py` 9 个用例(含增长信号跨扫描语义);全量 333 passed |
### ✅ 部署结果(会话 44,2026-08-29,`deploy_watchdog.py`)
| 服务器 | 结果 | 附带清理 |
|--------|------|----------|
| 5.202 | ✅ 看门狗已启动(日志确认) | 启动恢复清掉 8/27 22:39 僵尸 1 条 + 330 条卡住结果;调度恢复后新一轮回归已自动触发(真实执行) |
| 5.60 | ✅ 看门狗已启动(日志确认) | 启动恢复清掉 8/27 22:40 僵尸 1 条;新回归已自动触发 |
| 5.44 | ⏳ 等待中 | 14:43 清僵尸后调度恢复、新一轮 332 用例回归正在健康执行(chromium 活跃、结果增长,~2 分钟/条),**不可打断**;后台长轮询(8h)等 running=0 自动部署 |
**遗留观察(非本次范围)**:「每日定时自动回归测试」实际 `interval=6h`,但单轮 332 用例需 8 小时以上(8/26 那轮跑了 19.4h)——执行窗口远大于调度周期,即使无僵尸,除首轮外每 6h 的触发都会被运行守卫跳过。如需高频回归需缩减用例集或拆分任务。
---
## 📊 当前状态(会话 43,2026-08-27)
**薄弱模块 R7 执行器修复部署三台完成**:会话 39 产出的 `playwright_executor.py` 两处修复(`_navigate_to_target_page` 改走 `_do_navigate` + `_ensure_micro_app_loaded` 阶段4 reload 兜底)此前只做了本地验证,本次正式部署到三台生产服务器。
### ✅ 部署内容(会话 43,2026-08-27)
| 项目 | 值 |
|------|-----|
| 制品 | `git show a63d090c:backend/app/executors/playwright_executor.py`**R7 提交版**,SHA-256 `8bed2b78...`,不含工作树 P1/P2 增强) |
| 部署脚本 | `backend/scripts/deploy_executor_r7.py`(本次新增 `--artifact` 参数,可指定部署本地任意执行器文件) |
| 安全流程 | 每台串行:备份 `.bak-<时间戳>` → 上传 `.tmp` → 远端 SHA-256 一致 → 原子 mv → 恢复 owner/mode → 重算正式文件 SHA → 仅成功才 `docker restart` → 健康检查 + 容器内 AST + 容器内 SHA 比对 |
| 部署结果 | 三台 **6/6 步骤全通过**(备份/上传哈希/原子替换/正式哈希/重启/容器内验证) |
| 服务器 | 部署前远端 SHA | 部署后容器内 SHA | 健康检查 | 结果 |
|--------|---------------|-----------------|---------|------|
| 5.44 | `1a88c7b1...` | `8bed2b78...` ✅ | /health 200 | ✅ |
| 5.202 | `1a88c7b1...` | `8bed2b78...` ✅ | /health 200 | ✅ |
| 5.60 | `1a88c7b1...` | `8bed2b78...` ✅ | /health 200 | ✅ |
**过程说明**
- 部署前 pre-flight:5.44/5.202 各有 1 条运行中执行、5.60 无(用户确认直接部署,重启中断的运行任务由会话 42 的 `recover_interrupted_executions()` 启动恢复机制处理✅)
- 中断的 2 条执行已被本轮容器重启接入的启动恢复标记 failed,不会残留僵尸
- 本次**仅部署执行器**,P1/P2(`smart_locate_service.py`/`smart_locate.py`/`smart_locate.py`/前端 dist)仍为本地工作树状态,后续择机部署
**数据同步回顾**:用例数据已在会话 40 同步到位(14 个断言对齐已上三台),本次执行器代码补齐后,**远端薄弱模块(信息窗管理/告警工单/文件列表/下载列表/会务统筹/会务工单/消息通知/工单列表/我的工单/通讯录)的 R7 全量修复在三台服务器已完整生效**
---
## 📊 会话历史(会话 42,2026-08-27)
**定时任务重叠 + 僵尸执行修复并部署三台**:用户在 5.44/5.202 发现「设置了定时任务执行 UI 自动化,但一个报告都没输出」,主要因上一个定时任务还没执行完、下一个就开始执行。定位到根因并完成修复。
### ✅ 问题根因(会话 42,2026-08-27)
| 根因 | 说明 |
|------|------|
| **报告只在执行正常返回时生成** | `scheduler_service.py` `if task.auto_report and run_ok`(约 line 322)。执行要跑 332 个用例,大量用例因选择器超时卡住(`.el-drawer >> text='运维巡检'` Timeout 5000ms、`.el-table, .el-card` 等 30000ms,单用例 200~386s),一轮执行远超触发间隔 → 一直跑不完 → 报告一直不生成 |
| **重叠守卫只靠内存锁,进程重启即失效** | `run_task_once``is_execution_running()`(内存 `_running_execution_id`)判断跳过。容器 `docker restart` 后内存锁清空,但 DB 里上一轮执行仍停在 `running` → 定时任务照常触发新执行、新开浏览器,与旧进程残留 Playwright 线程并发互相抢占 → 进一步拖垮执行、彻底产不出报告 |
| **僵尸记录不断累积** | 5.44 **18 条**、5.202 **14 条**、5.60 **68 条** `running` 僵尸(2026-08-19~08-27,`end_time` 全空);日志每 30s 刷「任务正在执行中,忽略重复触发」——内存级任务去重在跑,但拦不住跨重启堆积 |
### ✅ 修复内容(会话 42,2026-08-27)
| 文件 | 改动 |
|------|------|
| `backend/app/services/execution_service.py` | **新增 `recover_interrupted_executions()`**:扫描 DB,把 `start_time` 超过 300s 仍处于 `running/pending` 的 UI 执行标记为 `failed`,并同步关联的 `pending/running` 用例结果为 `failed`(security 不处理) |
| `backend/app/services/scheduler_service.py` | **DB 级重叠兜底守卫**`run_task_once` 在内存锁预检外新增 DB 查询——只要 DB 中还有 UI 执行处于 `running/pending`,即使内存锁为空也跳过本次触发并推进 `next_run_at` |
| `backend/app/main.py` | lifespan 启动时调用 `recover_interrupted_executions()`,打印清理日志 |
| `backend/tests/test_scheduler_recovery.py` | 新增 5 项测试(僵尸标记/年轻记录不误伤/空库/DB 有 running 跳过/DB 无 running 正常触发) |
### ✅ 部署结果(会话 42,2026-08-27)
三台服务器按安全流程(备份→上传→SHA-256→原子替换→docker restart→健康检查→容器内 AST/SHA 比对)部署 `execution_service.py` + `scheduler_service.py` + `main.py`,全通过。
| 服务器 | 启动恢复日志 |
|--------|-------------|
| 5.44 | 清理 **18** 条僵尸执行、2606 条用例结果 |
| 5.202 | 清理 **14** 条僵尸执行、1814 条用例结果 |
| 5.60 | 清理 **68** 条僵尸执行、401 条用例结果 |
**验证**:5.44 复核——旧的 18 条 `running` 全部转 `failed`,重启后定时任务正常触发一次新执行,DB 仅剩 1 条 running,不再堆积。全量后端测试 **324 passed**
**遗留提醒**:报告生成的前提是执行能跑完一轮。僵尸/重叠已解决,但日志里的选择器超时(`.el-drawer`/`.el-table` 等)本身未修——正是 P1/P2 智能定位要解决的事。若这些用例继续大面积超时,执行虽能完成并产出报告,但报告会大量 failed。
---
## 📊 会话历史时间线(会话 41,2026-08-26)
**预定数据模块用例覆盖盲区补齐**:应 `/sut-explore` 盘点需求,发现「预定数据」入口(首页功能卡片)在用例库中分散且步骤过旧(click 菜单导航,headless 下不稳定)。通过 `menu_block_urls_v3.json` 确认**首页「预定数据」入口实际指向 `StatisticsModule`(数据统计页)**,非独立的「预定2.0」系统;独立「预定2.0(meetingV2)」子模块(会议室列表/已预定会议/历史记录/会议审批/参会人模板)在首页功能卡片中**无独立直达入口**(仅 `elements_mapping``[data-id]` 映射,无直达 URL 配置)。
### ✅ 预定数据用例生成(会话 41,2026-08-26)
**背景**:盘点发现数据统计页相关用例分散在两个模块(`module_parent_shujutongji` 10 条 + `module_6790b997...` 数据分析 42 条),「预定数据」入口用例首步多为 click 菜单(headless 下 `.block` 不稳定,已被会话 34 验证踩坑)。
**生成方式**:新建 `backend/scripts/generate_yuding_cases.py`(幂等:先 DELETE 同名旧用例再 INSERT),写入模块 `module_parent_yuding20`,首步一律 `navigate` 直达 `StatisticsModule` URL,`config``auto_login:true + headless:true + step_retry_count:2`
**执行结果 R1**`exec_35868ddb9ebe4498ab93aadc4205ccee`):
| 用例 | 结果 | 耗时 |
|------|------|------|
| 预定数据-页面访问验证 | ✅ passed | 24.1s |
| 预定数据-图表数量验证 | ✅ passed | 17.0s |
| 预定数据-预定会议数据筛选切换 | ✅ passed | 25.0s |
| 预定数据-时间筛选验证 | ✅ passed | 57.1s |
| 预定数据-满意度评价统计查看 | ✅ passed | 16.7s |
| 预定数据-会议通知发送统计切换 | ✅ passed | 98.2s |
**总结果:6/6 通过,100%,一次执行即全绿**(无需迭代修复)。
**关键选择器**(沿用现有数据统计已验证模式):
```python
# 图表断言
canvas / element_count_min canvas >= 3
# 时间维度下拉(Element UI el-select readonly)
input[readonly][value="日"] / input[value="日"] / input[readonly][placeholder*="选择"]
# 通知类型下拉
input[readonly][value="短信通知"] / input[value="短信通知"]
# 筛选器计数
.el-select element_count_min >= 3
```
**改动清单**
| 文件 | 说明 |
|------|------|
| `backend/scripts/generate_yuding_cases.py` | 新增生成脚本(幂等,6 用例,navigate 直达模板) |
| `backend/data/test_platform.db` | 新增 6 条用例至 `module_parent_yuding20`(已清理 2 条同名旧用例) |
**遗留说明**
- 本次新增 6 条用例已同步**三台服务器**:5.44(393→399)、5.202(393→399)、5.60(394→400),三端模块内均已验证 8 条用例存在;**未提交 git**
- **独立「预定2.0」(meetingV2)** 若需覆盖,因无首页直达入口,需进一步探测(`elements_mapping``[data-id="meetingRoomList"]` 等映射,可能需要首页功能中心 Tab 点击进入)——P2 待办。
---
## 📊 会话历史(会话 40,2026-08-25)
**新增用例数据全量同步三台测试管理平台(192.168.5.44 / 192.168.5.202 / 192.168.5.60)**:应需求将本地新增/更新的用例数据补充到三台服务器。三台应用均为 Docker MySQL 架构(`plat-auto-test-app` 容器内连 `mysql` 容器,库 `plat_auto_test`,表 `test_cases`),直接 SQL UPSERT 写入即可生效,无需重启容器。
### ✅ 三端用例库同步(会话 40,2026-08-25)
**差异对比**
| 数据源 | 用例数 |
|--------|--------|
| 本地 SQLite(`backend/data/test_platform.db`) | 315 条 |
| 5.44 / 5.202 MySQL(同步前) | 371 条(缺 22 条本地新增) |
| 5.60 MySQL(同步前) | 393 条(缺 1 条本地新增) |
**同步结果**(全量 UPSERT,单向,不删除远端独有用例):
| 服务器 | 同步前 | 同步后 | 新增 | 更新 |
|--------|--------|--------|------|------|
| **5.44** | 371 | **393** | 22 | 293 |
| **5.202** | 371 | **393** | 22 | 293 |
| **5.60** | 393 | **394** | 1 | 314 |
**覆盖内容**
- **会话 38 新增**`资产信息-新增资产闭环`(case_bc72e3de...,27 步闭环用例)→ 三台均补 ✅
- **会话 37 新增**:待办事项 3 个 + 会议室列表 7 个直达 URL 化 + 资产信息 3 个(5.44/5.202 各补 22 条)
- **会话 39 更新**:14 个用例断言对齐(通讯录/会务工单/消息通知/信息窗管理等,updated_at=2026-08-24 17:30)→ 三台均更新 ✅
**验证**
- 三台用例数:5.44=393、5.202=393、5.60=394 ✅
- 模块表三端无缺失(本地 28 个模块在三端全部存在;远端另有 sec_* 安全等本地没有的模块,未受影响)✅
- 抽查 9 个关键用例(新增闭环 + 会话37 新用例 + 会话39 断言对齐):三台 **9/9 全部命中**,updated_at 与本地一致 ✅
**改动清单**
| 文件 | 说明 |
|------|------|
| `backend/scripts/sync_cases_to_servers.py` | 新增同步脚本:读本地 SQLite 全量用例 UPSERT 到 5.44/5.202/5.60 三台 MySQL(docker exec 管道传 JSON,无重启) |
**遗留说明**:本次仅同步用例数据(`test_cases` 表)。会话 39 的 `playwright_executor.py` 两处代码修复(`_do_navigate` 接入 + reload 兜底)**尚未部署**三台服务器;14 个用例断言对齐已随用例同步到位(但执行器无 reload 兜底时,薄弱模块在远端仍可能复现 NO-BODY 问题,需尽快部署代码)。
---
## 📊 当前状态(会话 39,2026-08-25)
**薄弱模块 31 个用例全量修复验证 R7 100% 通过**:本轮目标是把 10 个覆盖薄弱模块(信息窗管理/告警工单/文件列表/下载列表/会务统筹/会务工单/消息通知/工单列表/我的工单/通讯录,共 31 个用例)的执行通过率从 R6 的约 2/3 拉到 100%。**R6 失败根因是 NO-BODY 家族问题**:第二次 monitor/meetingV3 导航后平台壳已初始化,二段式 warm-up 无法触发 micro-app 重新挂载,body 始终为空 → 断言全部失败,且同一页面/断言在不同执行位置表现不一致(`body:has-text('工单列表')` 在 pos4 ✅ / pos23 ❌)。
**关键根因**`_navigate_to_target_page`(Phase 4 页面识别直达导航)用的是裸 `self._page.goto()`**绕过了** `_do_navigate` 里的跨微应用 warm-up + reload 兜底修复路径 → 已登录直达导航的场景完全没吃到修复。
### ✅ 薄弱模块全量修复(会话 39,2026-08-25)
**3 个修复维度**
| 维度 | 内容 |
|------|------|
| **执行器修复** | ① `_navigate_to_target_page` 弃用裸 `goto`,改走 `_do_navigate`(含跨微应用 warm-up + `_goto_full_page` + `_ensure_micro_app_loaded` + `_verify_micro_app_stable`)② `_ensure_micro_app_loaded` 末尾新增**阶段4 reload 兜底**:body 为 NO-BODY 时执行 `page.reload()`(探测证明 A 删 pageOpen / B pageOpen=false / C 清 monitor 键全部失败,**reload 是唯一有效恢复手段**) |
| **断言对齐** | 删除 monitor 系列空表行数断言(monitor 固定渲染设备列表 1 表 0 行,忽略内层路由);删除消息通知表格断言(0 表);通讯录 `退出` 按钮断言改为 `body:has-text('退出')`;表格数据验证恢复有效 `.el-table` 断言 |
| **DB 用例修正** | 9 个 monitor 用例 + 3 个消息通知 + 1 个信息窗管理 + 1 个通讯录共 14 个用例 UPDATE 步骤,去掉不可达断言 |
**探测脚本**(沉淀在 `backend/scripts/`):
| 脚本 | 结论 |
|------|------|
| `probe_monitor_remount.py` | 4 方案对比:A(removeItem)=❌ B(pageOpen=false)=❌ C(清全部键)=❌ **D(page.reload)=✅** |
| `probe_cross_case_monitor.py` | 复刻 executor 跨用例场景(ALARM→Home→FILE→Home→ALARM),复现第 2 次 monitor 导航 NO-BODY |
| `probe_failing_assert_targets.py` | 逐一验证 R6 失败断言选择器是否命中(meetingV3 4 页) |
| `probe_monitor_sidebar.py` | monitor 侧边栏点击探测(确认内层路由被忽略) |
| `probe_v3_monitor_stability.py` | meetingV3 ×3 新上下文稳定性 + monitor 复刻 executor 三阶段 |
**R7 执行结果**`exec_111da3930d734bc7a4a2ea009336558f`):
| 指标 | 值 |
|------|-----|
| 执行 ID | `exec_111da3930d734bc7a4a2ea009336558f` |
| 总用例 / 通过 / 失败 | **31 / 31 / 0** |
| 通过率 | **100%** |
| 总耗时 | 799.7s |
**31 个用例全部通过**:工单列表×3、信息窗管理×3、我的工单×3、告警工单×3、会务统筹×3、会务工单×3、消息通知×3、文件列表×3、下载列表×3、通讯录×3 @ 100%。
**改动清单**
| 文件 | 说明 |
|------|------|
| `backend/app/executors/playwright_executor.py` | `_navigate_to_target_page` 改走 `_do_navigate`(line ~988);`_ensure_micro_app_loaded` 阶段4 reload 兜底(line ~1946) |
| `backend/data/test_platform.db` | 14 个用例步骤断言对齐(monitor/消息通知/信息窗/通讯录) |
| `backend/scripts/probe_*.py` | 5 个探测/复刻脚本沉淀 |
**已知遗留**:monitor 微应用内层路由(Maintenancelist/Filemanage)仍被忽略,固定渲染设备列表 → 相关用例只断言实际渲染内容(页面可达 + `.el-table` 存在),不做行数/列名断言。
---
## 📊 会话历史时间线(会话 38,2026-08-24)
**资产信息模块 CRUD 覆盖盲区补齐**:盘点发现资产信息模块只有 3 个只读验证用例(页面访问/表格数据/筛选器交互),无「新增资产」执行流程用例。通过 Playwright 探测(v1~v4 四轮)摸清「添加条目」弹窗完整表单结构、删除确认弹窗(`el-message-box`)和行内操作按钮(`<div class="operation_btn">`)DOM,生成 27 步闭环用例,首次执行即 100% 通过(254.9s),数据已闭环清理不污染 SUT。
### ✅ 资产信息-新增资产闭环用例(会话 38,2026-08-24)
**背景**:用户询问「资产信息有新增资产的用例吗?」→ 盘点确认无此类用例(覆盖盲区)→ 用户选择「新增→验证→删除闭环」方案(闭环不污染数据)。
**用例信息**
| 项目 | 值 |
|------|-----|
| 用例 ID | `case_bc72e3de150c475aa43762d7e6493465` |
| 用例名 | 资产信息-新增资产闭环 |
| 所属模块 | 资产管理(`module_251aebd2e6dd4d09acda0eb179c35d23`) |
| 步骤数 | 27 |
| 执行 ID | `exec_1afc0b7509724182b817fe2b5c0c95ca` |
| 执行结果 | ✅ passed(27/27 步全绿,254.9s) |
**用例流程**(4 个 Phase):
| Phase | 步骤 | 内容 |
|-------|------|------|
| 新增 | 1~12 | navigate 直达 → 点添加条目 → 填 6 必填字段 → 提交 |
| 验证 | 13~18 | 表格有数据 → 搜索框输入唯一名 → 确定搜索 → 断言新数据出现 |
| 删除 | 19~22 | 行内删除按钮 → MessageBox 确认 |
| 清理验证 | 23~27 | 清空搜索重置 → 表格正常(数据已删) |
**探测结论**(4 轮探测脚本沉淀):
| 探测项 | 关键发现 |
|--------|---------|
| 添加弹窗标题 | 「添加资产」,底部仅 1 个确定按钮 |
| 必填字段(红色*) | 名称/品牌/型号/序列号/参考价值 + 保修时长(`el-input-number` 默认 1,min=1,无需填写) |
| 所属区域 | `el-cascader` 级联选择器(**非 el-select 非 radio**),非必填跳过 |
| 在线状态 | `el-select`,选项「在线/离线」,非必填跳过 |
| 删除确认 | `el-message-box`「是否确认删除操作?」按钮:取消/确定(**非 el-dialog**) |
| 行操作按钮 | `<div class="operation_btn">编辑/删除/报废</div>`(div 非 button) |
**唯一数据方案**`parameters``__CTX` 上下文变量 + `__RANDOM_NAME_N__` 内置生成器:
```json
{"testName": "TA-{__RANDOM_NAME_8__}", "testBrand": "TB-{__RANDOM_NAME_6__}", "testModel": "TM-{__RANDOM_NAME_6__}", "testSerial": "SN-{__RANDOM_NAME_8__}"}
```
fill 步骤 value 用 `{__CTX:testName__}` 引用,执行时渲染为随机唯一名,避免与表格已有 30 条数据冲突,且天然幂等(每次执行数据不同)。
**关键选择器**
```python
# 添加按钮(页面级 :has-text 有效)
button:has-text('添加条目')
# 表单字段(placeholder 定位)
input[placeholder='填写名称'] / 填写品牌 / 填写型号 / 填写序列号 / ¥填写价格
# 弹窗提交
.el-dialog:visible .el-button--primary:has-text('确定')
# 搜索(页面级确定按钮)
button:has-text('确定')
# 行删除按钮(div 非 button,搜索过滤后唯一定位)
.el-table__row .operation_btn:has-text('删除')
# 删除确认(el-message-box 非 el-dialog)
.el-message-box button:has-text('确定')
```
**改动清单**
| 文件 | 说明 |
|------|------|
| `backend/scripts/generate_asset_info_add_case.py` | 生成脚本(INSERT 27 步闭环用例) |
| `backend/scripts/explore_asset_add_form.py` + `_v2`~`_v4` | 4 轮探测脚本(表单/弹窗/下拉/MessageBox) |
| `backend/data/test_platform.db` | 新增用例(已清理 1 个重复 ID) |
**已知问题**:Step 16/25「点击确定搜索」各耗时 **88s**(其余步骤 <4s),疑似搜索按钮点击后触发执行器内部重试或长等待,功能通过但拉长总时长至 254.9s。**待优化(P2)**
---
## 📊 会话历史时间线
**会议室列表 + 资产信息模块通过 `/sut-explore` 探测完成 navigate 直达 URL 改造**:原有 7 个会议室列表用例 + 3 个资产信息用例全部从「click 导航」改为「navigate 直达 URL」模式;`page_url_mapping.json` 补充 `asset_info` 页面完整配置(url_patterns/fingerprint/match_rules/skip_steps);新增执行器 `element_count_min` micro-app 轮询兜底(8s 轮询)。本地执行 **10/10 通过,100%**。已通过 `sync_to_560_full.py` 部署到 5.60 生产。
### ✅ 会议室列表直达 URL 改造(会话 37,2026-08-21)
**背景**:既有 7 个会议室列表用例首步为 click 导航,headless 下微前端菜单不稳定 → 全部改为 navigate 直达 `MeetingRoomList` URL。
**改动清单**
| 文件 | 改动 |
|------|------|
| `backend/scripts/generate_meetingroom_cases.py` | 7 个用例全量重写为首步 navigate 直达 MeetingRoomList URL(页面访问/查看详情/预览功能/搜索/筛选/预约会议/预览详情) |
| `backend/scripts/fix_meetingroom_cases_r2.py` | 第二轮修复 4 个失败用例(详见下) |
| `backend/app/executors/playwright_executor.py` | **`element_count_min` 新增 micro-app 轮询兜底**`query_selector_all` 无法穿透 `<micro-app-body>`,8s 轮询调用 `_do_in_micro_apps('count', selector)` 直到 count ≥ min_count |
**2 轮迭代执行结果(本地)**
| 轮次 | 执行 | 结果 | 失败根因 |
|------|------|------|----------|
| R1 `exec_7d4610da` | 3 pass / 4 fail | ① 预约会议 step 3 名称含"新建会议" → 触发 `create_meeting` 页面识别跳过(score 4.5 > meeting_room_list 3.0)② 查看详情/预览详情 element_exists 5s 超时(micro-app 内容渲染慢)③ 筛选功能 `element_count_min` 零等待返回 0 |
| R2 `exec_ba70a09e` | **7 pass / 0 fail(100%)** | 修复后全绿 |
**R2 修复详情**
| 用例 | 失败根因 | 修复 |
|------|----------|------|
| 预约会议验证 | step 3 名称"点击新建会议按钮"含"新建会议"→ `create_meeting` 页面识别得分 4.5 > meeting_room_list 3.0 → step 被 skip → 后续断言失败 | 重命名为"点击预约会议入口"(不含"新建会议"→ create_meeting 降为 2.5,meeting_room_list 3.0 获胜) |
| 查看详情 | element_exists `input[placeholder='日期']` 5s 超时(micro-app 内容渲染慢) | 添加 wait `.room_item` 20000ms 等待卡片加载 |
| 预览详情 | element_exists `button:has-text('预览')` 5s 超时 | 同上,添加 wait `.room_item` 20000ms |
| 筛选功能验证 | `element_count_min input.el-input__inner ≥ 2` 零等待返回 0(micro-app 内容未渲染) | 改为 wait `input[placeholder='日期']` 20000ms + 逐个 element_exists 断言 |
**最终 7 用例结果(R2)**
| 用例 | 用时 | 状态 |
|------|------|------|
| 会议室列表页面访问验证 | 14.5s | ✅ |
| 会议室列表-查看详情 | 13.5s | ✅ |
| 会议室列表-预览功能 | 15.8s | ✅ |
| 会议室列表-搜索功能 | 14.2s | ✅ |
| 会议室列表-筛选功能验证 | 15.1s | ✅ |
| 会议室列表-预约会议验证 | 18.9s | ✅ |
| 会议室列表-预览详情验证 | 15.6s | ✅ |
### ✅ 资产信息探测与直达 URL 改造(会话 37,2026-08-21)
**背景**`page_url_mapping.json` 会话 35 已注册 `asset_info` 条目但 url_patterns/fingerprint 留空。通过 `/sut-explore 资产信息` 探测真实 DOM 与 URL,完成完整配置。
**探测发现**(Chrome DevTools MCP 真机):
- 直达 URL:`https://192.168.5.44/#/meetingV3?meetingV3=%2FmeetingV3%2F%23%2FAssetinformation`
- 真实 DOM:`input[placeholder='请输入关键字查询']``input[placeholder='请选择状态']`(readonly)、按钮「添加条目/下载模板/导入表格/批量报修/批量报废/批量删除」
- 表格 26 条数据,选择器 `.el-table__row`
- **操作按钮为 `<div class="operation_btn">` 非 `<button>`**(关键发现)
**改动清单**
| 文件 | 改动 |
|------|------|
| `backend/app/data/page_url_mapping.json` | `asset_info` 条目补全:url(直达 Assetinformation URL)、url_patterns(`Assetinformation`)、fingerprint(`input[placeholder='请输入关键字查询']`)、match_rules(step_name_contains: 资产信息 + url_contains: Assetinformation)、skip_steps(资产信息/点击进入主页 + click) |
| `backend/scripts/generate_asset_info_cases.py` | 3 个用例全量重写为首步 navigate 直达(页面访问验证/表格数据验证/筛选器交互验证) |
**2 轮迭代执行结果(本地)**
| 轮次 | 执行 | 结果 | 失败根因 |
|------|------|------|----------|
| R1 `exec_129e8410` | 2 pass / 1 fail | 表格数据验证 step 6 `button:has-text('编辑')` 5s 超时 → 操作按钮是 `<div class="operation_btn">` 不是 `<button>``:has-text()` 是 Playwright 伪选择器在 micro-app eval 中被剥离 |
| R2 `exec_adf5c2fc` | **3 pass / 0 fail(100%)** | 修复后全绿(选择器从 `button:has-text('编辑')` 改为 `.operation_btn``button:has-text('删除')` 改为 `element_count_min .operation_btn ≥ 2`) |
**最终 3 用例结果(R2)**
| 用例 | 用时 | 状态 |
|------|------|------|
| 资产信息-页面访问验证 | 12.3s | ✅ |
| 资产信息-表格数据验证 | 12.8s | ✅ |
| 资产信息-筛选器交互验证 | 14.1s | ✅ |
### ✅ 5.60 生产部署(会话 37,2026-08-21)
**部署内容**`sync_to_560_full.py` 一键同步):
| 项目 | 说明 |
|------|------|
| 5 个后端文件 | `playwright_executor.py`(148KB,含 element_count_min 轮询增强)、`smart_locate_service.py`(90KB)、`execution_service.py`(40KB)、`scheduled_tasks.py`(11KB)、`page_url_mapping.json`(12KB,含 asset_info 完整配置) |
| 314 个用例 | UPSERT 到 MySQL `test_cases` 表(含 7 个会议室列表 + 3 个资产信息新用例) |
| 容器重启 | `docker restart plat-auto-test-app` → healthy |
| 验证 | 7 个会议室用例全部存在、`page_url_mapping.json` md5 本地与远程一致(`cb02b6603275422bb531489e3bf8284a`) |
---
**P1 遗留待办已闭环并提交**`466b9f40`):验证字段统一 + 断言补全 + 前端验证编辑区 + 二级菜单注册 19 页。详见 `Docs/PRD/需求文档/用例管理/_PRD_需求文档_P1遗留_验证步骤前端适配与定位器优化.md`
---
## 📊 会话历史时间线
| 会话 | 日期 | 主题 | 结果 |
|------|------|------|------|
| 48 | 09-01 | **sut-explore 缺口补齐(会议运维 6 Tab + 待办巡检 2 用例)**:盘点确认 7 缺口;8 用例生成入库;会议运维 navigate 直达一次全绿;待办巡检 monitor 直达 URL 内层路由竞态(首轮通过/复验 2 次失败)→ 定位为设备列表页误判 → 改走首页「巡检报表」入口点击 `[id="data_list.inspect_report"]` 绕过 → 最终 **8/8 100% 通过** | ✅ 8/8 全绿 |
| 48 | 09-01 | **定时任务看门狗失败执行无报告输出(P0)**:根因=报告生成条件 `run_ok`,看门狗置 failed 后跳过报告;A 修复新增 `_has_any_case_results`,UI/Security 两条路径 `run_ok or has_results` 均生成报告;B1 start_time 回推 + B2 双层硬超时兜底 + B3 崩溃恢复;已部署 5.44/5.202/5.60 三台(提交 `80bbb6ac`/`f9b37c6b`,364 测试全绿,6/6 标记验证通过),容器日志轮转 50m×5 已生效;产出问题处理 + 执行计划文档 | ✅ 已部署三台 |
| 47 | 08-31 | **性能测试目标机监控增强与修复**:Batch A 5 Java 关键字 + 多 PID 聚合 + MySQL 趋势;Batch B JSTAT 瞬时 CPU 采样(ps 平均失真修复);15 项单测全绿;部署 5.60 | ✅ 已部署 + 已推送 origin |
| 46 | 08-31 | **执行耗时优化 P0/P1/P2**`step_retry_count` 默认 2→1;has-text 25s→10s / 表格行 150s→30s / checkbox 40s→20s / iframe 5s→3s;新增 `_selector_exists_fast` 快速失败探测;9 项单测 + 全量 352 通过;`deploy_timeout_opt.py` 部署三台;5.44 同一 10 用例定时任务 24.8min→10.2min(-59%),通过率 30% 无回归 | ✅ 三台部署 + 生产实测达标 |
| 45 | 08-31 | **截图定期清理 + 5.60 磁盘满修复**`cleanup_service.py` 新增 `cleanup_screenshots_loop`(3600s 间隔/3 天保留)+ `config.py` 新增 `SCREENSHOT_RETENTION_DAYS` + `main.py` lifespan 启动/停止;10 项单元测试;5.60 因 `projects` router 引用崩溃→回滚→移除→重部署;三台部署完成(5.60 首轮清理释放 8.77GB) | ✅ 三台 6/6 验证通过 |
| 44 | 08-29 | **僵尸执行常驻看门狗**:新增 `watchdog_loop`(60s 扫描/30 分钟无进展判僵尸/双信号活性判定:内存心跳 + DB 结果增长)+ `deploy_watchdog.py`;5.202/5.60 部署完成(清理 8/27 僵尸) | ✅ 看门狗已启动 |
| 43 | 08-27 | **薄弱模块 R7 执行器修复部署三台**`playwright_executor.py`(commit `a63d090c`)按安全流程部署 5.44/5.202/5.60,三台容器内 SHA 一致、健康检查通过;`deploy_executor_r7.py` 新增 `--artifact` 参数;会话 40 用例数据 + 本次代码修复完整生效,远端薄弱模块 NO-BODY 解除 | ✅ 三台 6/6 验证全通过 |
| 42 | 08-27 | **定时任务僵尸执行 + 重叠守卫修复并部署三台**:新增 `recover_interrupted_executions()` 启动恢复 + DB 级重叠兜底守卫 + lifespan 调用;清理三台 running 僵尸记录(5.44=18/5.202=14/5.60=68 条) | ✅ 全量测试 324 passed、三台部署验证通过 |
| 41 | 08-26 | **预定数据用例覆盖盲区补齐**:生成 6 个 navigate 直达用例(页面访问/图表数量/筛选切换/时间筛选/满意度/通知统计)至 `module_parent_yuding20`;沉淀 `generate_yuding_cases.py`(幂等);确认预定数据入口=数据统计页。**已同步三台**(5.44/5.202 393→399、5.60 394→400) | ✅ 本地 6/6 R1 一次通过;三端已同步 |
| 40 | 08-25 | **新增用例数据同步三台测试管理平台**:本地 315 条 UPSERT 至 5.44/5.202/5.60 MySQL(各补 22/22/1 条,含会话37/38/39 新增 + 14 条断言对齐更新);沉淀 `sync_cases_to_servers.py`(docker exec 管道传 JSON,无重启) | ✅ 5.44=393、5.202=393、5.60=394,抽查 9 用例三台全命中 |
| 39 | 08-25 | **薄弱模块 31 用例全量修复 R7 100%**`_navigate_to_target_page` 改走 `_do_navigate`(含 reload 兜底);`_ensure_micro_app_loaded` 阶段4 reload 修复;5 个探测脚本沉淀;14 个用例断言对齐 | ✅ 本地 31/31,799.7s |
| 38 | 08-24 | **资产信息-新增资产闭环用例**:Playwright 4 轮探测添加弹窗(表单/radio/cascader/select/MessageBox);生成 27 步闭环用例(新增→验证→删除);首次执行 27/27 100% 通过 | ✅ 本地 1/1,254.9s |
| 37 | 08-21 | **会议室列表 + 资产信息直达 URL 化改造 + 5.60 部署**:7+3 用例 navigate 直达;page_url_mapping 补全 asset_info;`element_count_min` micro-app 8s 轮询兜底 | ✅ 本地 10/10、已部署 5.60 |
| 36 | 08-21 | **会议模板/参会人模板直达 URL 化改造**:9 用例 navigate 直达;注册 2 页;修复 2 个执行器 BUG | ✅ 本地 9/9 |
| 35 | 08-21 | **P1 遗留待办闭环**:verify 字段统一 + body_not_contains 断言 + 前端编辑 + 二级菜单 19 页注册 | ✅ 后端 239 测试、前端 build 通过 |
| 34 | 08-20 | **修复门口屏发布 2 用例 micro-app 穿透**`_ensure_micro_app_loaded` 阶段1 deadline 毫秒未转秒 bug(8ms→8s);阶段2 无条件 hash 重置 | ✅ Round 15 2/2 全绿 65.4s |
| 33 | 08-20 | **5.41 服务器部署最新版前后端包** | ✅ 容器 healthy |
| 31 | 08-19 | **定时任务「立即执行」不生成记录修复 + 取消即停**`manual` 参数区分触发来源;`request_cancel` 信号中断工作线程 | ✅ 4 文件已部署 5.44+5.202 |
| 30 | 08-19 | **数据统计模块新增 10 用例 + `/sut-explore` Skill 沉淀**:V4 全量 10/10 通过 | ✅ 本地验收通过,待同步 5.60 |
| 29 | 08-19 | **修复 MySQL 1205 行锁取消 + SUT URL 校验加固**:置 running 后立即 commit;身份映射隐藏 bug 修复;`_validate_sut_url` 拒绝非法地址 | ✅ 后端 186 测试、已部署 5.60、取消 91ms |
| 28 | 08-19 | **系统配置切换 SUT 后用例自动跟随(地址重写)**`_rebase_sut_url` 130 条存量硬编码自动跟随 | ✅ 后端 182 测试、已部署 5.60 |
| 27 | 08-19 | **UI自动化定时任务模块 + 系统配置SUT URL**:调度引擎/CRUD/报告/配置持久化 | ✅ 后端 179 测试、已部署 5.60 |
| 26 | 08-19 | **新建会议 step 11/17 执行失败修复**:保留页面直达 + 策略5 增强 + step 12 改「保持立即开始」 | ✅ 本地+生产各 3 次全绿 |
| 25 | 08-19 | 新建会议执行失败根因定位(页面直达+表格懒加载+checkbox 隐藏) | ✅ 已定位 |
| 24 | 08-18 | finally api_call 真实化 + 完整 17 步验收通过 | ✅ 5.60 全绿 |
| 23 | 08-18 | 语义化机制部署 5.60 + 生产验证(连跑两次全绿) | ✅ 已部署 |
| 22 | 08-18 | 语义化机制真实验证(16 步主流程连跑两次全绿) | ✅ 验证通过 |
| 21 | 08-18 | 语义化阶段6:前端语义编辑 + 示范用例 + 参数持久化修复 | ✅ 代码已提交 |
| 20~9 | 08-11~18 | 映射修复/部署/智能定位/Claude 语义增强/微前端支持/Phase1-6 | ✅ 全部完成 |
| 1~7 | 08-05~07 | 验证类步骤自动识别/智能定位 Phase1-6 端到端验证 | ✅ 完成 |
---
## ⚠️ 关键约束
| # | 约束 | 说明 |
|---|------|------|
| 1 | **Playwright 必须同步 API** | Windows 异步 API 无法启动。`main.py` 已配 ProactorEventLoop;`execution_service``run_in_executor` 线程池执行 |
| 2 | **工作线程 `asyncio.set_event_loop(None)`** | 在 `start()` 内绕过 Playwright 的 `get_running_loop()` 检测 |
| 3 | **每用例新建 PlaywrightExecutor 实例** | 共享实例 `stop()``_is_running=False`,下次 `start()` 失败 |
| 4 | **前端 axios baseURL 必须空字符串** | 路径写完整 `/api/...`,否则 `/api/api/cases` 404 |
| 5 | **`useRoute()` 必须在 setup 顶层** | computed 内调用报错,页面空白 |
| 6 | **前端 axios 超时 5 分钟** | `request.ts` 已配 300000ms |
| 7 | **SQLite 并发写锁** | 已配 `check_same_thread=False` + `pool_pre_ping=True` |
| 8 | **uvicorn 勿加 `--reload`(Windows)** | WatchFiles 卡死,改后必须重启进程 |
| 9 | **MySQL 密码勿含 `@`** | 生产用 `PlatApp2026`;ID 字段 `String(64)` |
| 10 | **page_url_mapping.json 单例不热加载** | 新增条目需重启 uvicorn |
| 11 | **操作按钮 selector 注意** | 资产信息等页面操作按钮是 `<div class="operation_btn">``<button>``:has-text()` 在 micro-app eval 中被剥离 |
| 12 | **语义化开关(settings flag)** | `SEMANTIC_ENABLED` / `USE_KEYWORD_MATCHER` / `USE_FUZZY_MAPPING`(默认 false)/ `ENFORCE_VERIFICATION` |
| 13 | **tar 打包前端 dist 用 `arcname='dist'`** | 否则解压后嵌套目录,`assets/` 为空 → 容器启动报错 |
---
## 🔑 关键配置信息
| 项目 | 值 |
|------|-----|
| 服务器 IP | 192.168.5.60(Docker 容器化,`plat-auto-test-app`) |
| 5.41(测试管理平台部署服务器) | root / **Ubains@123** · 端口 8081 · 单独部署脚本 `deploy/deploy.41.sh` |
| 5.44 SSH(平台部署服务器 + 被测系统) | root / **Ubains@123** |
| 5.60 SSH 用户/密码 | ubains / **Ubains@123** |
| 部署目录 | `/data/third_party/plat-auto-test/` |
| 前端 / API / 健康检查 | http://192.168.5.60 / `/docs` / `/health` |
| MySQL 外部访问 | 192.168.5.60:3307 · platapp / **PlatApp2026** |
| 被测系统 | https://192.168.5.44(微前端 micro-app) |
| 登录凭据 | admin@xty / **Ubains@13579** · 验证码固定 `csba` |
| 远程仓库 | http://git.ubainsyun.com/bing/ubains-module-test.git |
---
## 🚀 启动与运维
```bash
# 后端(端口 8001;勿加 --reload)
cd backend && pip install -r requirements.txt && playwright install chromium
uvicorn app.main:app --port 8001
# 前端(端口 3000,代理指向 8001)
cd frontend && npm install && npm run dev
# 测试
cd backend && pytest tests/ -v --cov=app --cov-report=html
cd frontend && npm run build
```
---
## 📑 文档索引
| 文档 | 路径 |
|------|------|
| 项目指南 | `CLAUDE.md` |
| 多窗口并行开发指南 | `Docs/多窗口并行开发指南.md` |
| 主 PRD | `Docs/PRD/需求文档/总览/_PRD_平台自动化测试可视化系统需求文档.md` |
| 语义化用例执行 PRD/计划 | `Docs/PRD/需求文档/用例管理/_PRD_复杂用例通用执行机制_语义化用例执行.md` |
| 定时任务模块 PRD/计划 | `Docs/PRD/需求文档/定时任务/_PRD_需求文档_UI自动化定时任务模块与系统配置被测系统URL.md` |
| 执行耗时优化 PRD/计划 | `Docs/PRD/需求文档/执行中心/_PRD_需求文档_执行耗时优化.md` / `_执行计划_执行耗时优化.md` |
| 定时任务看门狗失败无报告问题处理 | `Docs/PRD/问题处理/执行中心/_问题处理_定时任务看门狗失败执行无报告输出.md` |
| 定时任务看门狗失败无报告执行计划 | `Docs/PRD/问题处理/执行中心/_执行计划_修复定时任务看门狗失败执行无报告输出.md` |
| P1 遗留待办 PRD/计划 | `Docs/PRD/需求文档/用例管理/_PRD_需求文档_P1遗留_验证步骤前端适配与定位器优化.md` |
| 新建会议问题处理 | `Docs/PRD/问题处理/会议管理/_问题处理_新建会议用例执行失败_页面直达后会议室表格checkbox不可见.md` |
| HANDOFF 总交接 | `HANDOFF.md` |
| V2 部署交接 | `HANDOFF_V2部署升级.md` |
| 安全测试交接 | `HANDOFF_安全测试.md` |
---
## ✅ 待办事项(仅未完成)
### P0(本次会话产出,已完成 → 移至下方会话 48 部署记录)
- [x] ~~**提交 + 部署 A 修复(看门狗失败执行也生成报告)**~~(会话 48 完成:`_has_any_case_results` 已提交 `80bbb6ac` 并部署 5.44/5.202/5.60;容器内标记复核 6/6 ✅)
- [x] ~~**B1:case_results 写入 start_time**~~(会话 48 完成:`_persist_case_result``start_time = now - duration`,已提交部署)
- [x] ~~**B2:单用例执行超时兜底**~~(会话 48 完成:`playwright_executor.py` 双层硬超时——`_case_start_mono` + `case_remaining_seconds()` + 步骤级 `_step_deadline_mono`,所有等待点同受步骤级+用例级截止点约束;已提交部署)
- [x] ~~**B3:Chromium 崩溃自动恢复**~~(会话 48 完成:`_is_crash_error` 识别 10 类崩溃标记 + `_rebuild_page_after_crash` 重建页面从失败步骤重试,`_crash_retried` 每用例重置;已提交部署)
- [x] ~~**容器日志轮转配置**~~(会话 48 完成:compose `logging: json-file 50m×5` + `docker compose up -d --force-recreate app`,三台 `docker inspect` 确认生效)
- [ ] **(可选)执行级功能验证**:在 5.44 触发一次「每日定时」任务,确认看门狗失败路径产出报告(代码标记与健康已全量验证,此项为端到端抽查)
### P2(低优先级)
- [ ] **观察 5.44 定时任务通过率**(会话 46 P0 把重试 3→2 次尝试;若后续通过率下降,回滚 P0 保留 P1+P2)
- [ ] **排查搜索确定按钮 88s 卡点**(会话 38 新增资产闭环 Step 16/25 各 88s,功能通过但拉长总时长 254.9s;会话 46 超时收紧后应有改善,待复测)
- [ ] **预定2.0(meetingV2)独立页面探测与用例生成**(首页无独立直达入口,需从功能中心 Tab/`[data-id]` 入口探测)
- [x] ~~**定时任务僵尸执行 + 重叠守卫**~~(会话 42 已修复并部署三台:`recover_interrupted_executions()` 启动恢复 + DB 级重叠兜底守卫 + lifespan 调用;清理 5.44/5.202/5.60 的 18/14/68 条 running 僵尸,对应 2606/1814/401 用例结果标 failed;324 测试全绿)
- [x] ~~**薄弱模块 R7 修复部署三台服务器(5.44/5.202/5.60)**~~(会话 43 完成:`playwright_executor.py` commit `a63d090c` 按安全流程部署三台,容器内 SHA 一致、健康检查通过;会话 40 用例数据 + 本次代码修复完整生效,远端薄弱模块 NO-BODY 解除。`deploy_executor_r7.py` 新增 `--artifact` 参数支持指定部署制品)
- [x] ~~**P1/P2 执行器代码部署三台**~~(会话 43 已完成:`deploy_p1p2.py` 部署 `playwright_executor.py` P1b/P2 增强 + `smart_locate_service.py` + `smart_locate.py` + 前端 dist 三台,6/6 验证通过;已提交 `3a29883b`
- [ ] **monitor 内层路由被忽略**(微应用整页加载后固定渲染设备列表,忽略 URL 内层 Maintenancelist/Filemanage 路由;相关用例已按实际渲染断言。**例外:待办巡检 Checklist 已绕过**——经首页「巡检报表」入口点击 `[id="data_list.inspect_report"]` 可 100% 达巡检列表页,见会话 48)
- [ ] 完善二级菜单映射表(`menu_mapping.py`
- [ ] 智能定位 API 与用例执行路径统一(当前两者逻辑分离)
- [ ] Claude 调用性能优化(缓存结果、减少超时时间)
- [ ] 维护 jieba 自定义词典(专业术语分词优化)
- [ ] Docker 容器安装中文字体(修复截图乱码,不影响定位)
### 待验证(会话 31 产出)
| 5.44/5.202 | 200 healthy | `json-file` 50m×5 ✅ | 6/6 ✅ | Up |
| 5.60 | 200 healthy | `json-file` 50m×5 ✅ | 6/6 ✅ | Up |
- [ ] **5.44 前端点击「立即执行」验证**:此前点击不会生成新执行记录;修复后应生成新 execution,`status` 应为 `triggered`
- [ ] **5.44 取消即停验证**:执行中点击取消 → 工作线程在用例/步骤边界退出,`run_execution` 快速返回,锁释放,可立即再点执行
- [ ] **前端 `status` 字段消费**(可选增强):后端已返回 `status: triggered/skipped` + `running_execution_id`
**遗留**:执行级功能验证待定。
---
*本文档由 Claude Code 于 2026-09-01 更新(会话 48 两档并存:① 定时任务看门狗失败执行无报告输出修复——已完成:A `_has_any_case_results` + UI/Security 两条路径 `run_ok or has_results` + B1 start_time 回推 + B2 双层硬超时兜底 + B3 Chromium 崩溃恢复 + 容器日志轮转 50m×5,全部提交(`80bbb6ac`/`f9b37c6b`)并部署 5.44/5.202/5.60 三台,364/364 测试全绿,6/6 代码标记验证通过,LogConfig 生效;遗留:可选执行级端到端抽查。② sut-explore 缺口补齐——会议运维 6 Tab + 待办巡检 2 用例;待办巡检 monitor 直达 URL 内层路由竞态定位(设备列表页误判)→ 改走首页「巡检报表」入口点击 `[id="data_list.inspect_report"]` 绕过;最终 8/8 用例 100% 通过;`generate_gap_cases.py` 幂等可重复执行)。*
*本文档由 Claude Code 于 2026-09-01 更新(会话 48 两档并存:① 定时任务看门狗失败执行无报告输出修复——已完成 A/B1/B2/B3 + 容器日志轮转 50m×5,全部提交并部署 5.44/5.202/5.60 三台,364/364 测试全绿,6/6 代码标记验证通过,LogConfig 生效;遗留:可选执行级端到端抽查。② sut-explore 缺口补齐——会议运维 6 Tab + 待办巡检 2 用例;待办巡检 monitor 直达 URL 内层路由竞态定位→改走首页「巡检报表」入口点击;最终 8/8 用例 100% 通过;`generate_gap_cases.py` 幂等可重复执行;已提交推送 `720bf764`,8 条缺口用例已同步 5.44/5.202/5.60 三台(无重启,定时任务不受影响))。*
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论