WechatOnCloud/bridge/woc_bridge/routes/status.py
Kris ed2ee086de refactor: 重构UI操作调度,引入优先级调度与监控
主要变更:
1. 新增UIActionScheduler实现优先级UI任务调度,支持CRITICAL/HIGH/NORMAL/LOW四级优先级
2. 替换原有SendQueue为UIActionScheduler,统一所有UI操作的调度逻辑
3. 为截图、登录、重启等API添加调度器封装,支持超时、限流与fast-fail机制
4. 新增Prometheus监控指标,统计UI任务执行、等待耗时、超时、限流与快速失败情况
5. 为QrCapture添加命令执行超时保护,避免X11操作卡死
6. 扩展StatusResponse与状态接口,暴露调度器指标
7. 新增完整的单元测试覆盖调度器功能
8. 为看门狗注入调度器,实现kill前队列清空与快速失败窗口
2026-07-18 03:43:28 +08:00

117 lines
4.8 KiB
Python
Raw Permalink 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.

from __future__ import annotations
import asyncio
import logging
import time
from fastapi import APIRouter
from woc_bridge.config import _state, _require_xdotool, _require_db_reader
from woc_bridge.db.coordinator import with_db_retry, _resolve_db_state
from woc_bridge.models import StatusResponse
from woc_bridge.ui.metrics import send_queue_pending as send_queue_pending_gauge
from woc_bridge.version import BRIDGE_VERSION, BRIDGE_CAPABILITIES, MEDIA_SUPPORTED
logger = logging.getLogger("woc-bridge")
router = APIRouter()
@router.get("/api/status", response_model=StatusResponse)
@with_db_retry
async def get_status() -> StatusResponse:
"""聚合返回 bridge 运行状态。
一次请求内完成 4 类检测进程运行、窗口存在、登录态、DB 可达性,
并返回当前账号信息与客户端轮询建议参数。所有调用方channels 适配器、
面板健康检查、诊断 autofix都应优先调本接口判断前置条件。
Returns:
StatusResponse含 bridge_version / wechat_running / wechat_window_found
/ login_state / db_accessible / current_wxid / current_nickname
/ uptime_seconds / display / max_batch_size / poll_interval_ms
Notes:
- 不抛业务错误:即使微信未运行 / DB 加密,也返回 200 + 对应 false 字段,
让调用方按字段判定下一步动作(而非按 HTTP 状态码)
- login_state 内联判定(不调 detect_login_state避免重复 pgrep/xdotool
- DB 加密时细分:有 key 且验证过 → encrypted_key_ok + db_accessible=true
无 key 且自动提取未启用 → encrypted_no_key
自动提取已尝试但失败 → key_extract_failed
二者 db_accessible 均为 false
- current_wxid/nickname 仅在 db_accessible=true 且 login_state=logged_in
时才尝试读 DB失败静默为空字符串
"""
xdotool = _require_xdotool()
db_reader = _require_db_reader()
# 一次 status 请求内复用 pgrep/xdotool 结果,避免重复调用
wechat_running = await xdotool.is_wechat_running()
window_id = await xdotool.find_wechat_window() if wechat_running else None
wechat_window_found = window_id is not None
# 登录态内联判定,避免 detect_login_state 内部再次 pgrep/xdotool search
if not wechat_running:
login_state = "not_running"
elif not wechat_window_found:
login_state = "not_logged_in"
else:
login_state = "logged_in"
# DB 状态:统一走 _resolve_db_state不触发自动提取仅解析
db_state = await _resolve_db_state()
db_accessible = db_state.db_accessible
db_error_code = db_state.db_error_code
init_in_progress = _state.init_state.state == "running"
init_progress_pct = _state.init_state.progress_pct
init_message = _state.init_state.message
# current_wxid / current_nickname从 DB 读,失败为空
current_wxid = ""
current_nickname = ""
if db_accessible and login_state == "logged_in":
try:
self_info = await asyncio.to_thread(db_reader.get_self_info)
current_wxid = self_info.get("wxid", "")
current_nickname = self_info.get("nickname", "")
except Exception:
current_wxid = ""
current_nickname = ""
uptime_seconds = time.monotonic() - _state.start_time
send_queue = _state.send_queue
send_queue_pending = send_queue.pending_count() if send_queue else 0
ui_scheduler_metrics = None
if send_queue is not None and hasattr(send_queue, "get_metrics"):
try:
ui_scheduler_metrics = send_queue.get_metrics()
except Exception as exc:
logger.warning("[status] get_metrics failed: %s", exc)
# 修复既有 Prometheus Gauge bug每次 status 请求时 set 当前 pending 数
try:
send_queue_pending_gauge.set(send_queue_pending)
except Exception as exc:
logger.debug("[status] send_queue_pending_gauge.set failed: %s", exc)
return StatusResponse(
bridge_version=BRIDGE_VERSION,
wechat_running=wechat_running,
wechat_window_found=wechat_window_found,
login_state=login_state,
db_accessible=db_accessible,
db_error_code=db_error_code,
init_in_progress=init_in_progress,
init_progress_pct=init_progress_pct,
init_message=init_message,
current_wxid=current_wxid,
current_nickname=current_nickname,
uptime_seconds=uptime_seconds,
display=_state.config.display,
max_batch_size=_state.config.max_batch_size,
poll_interval_ms=_state.config.poll_interval_ms,
send_queue_pending=send_queue_pending,
media_supported=MEDIA_SUPPORTED,
bridge_capabilities=list(BRIDGE_CAPABILITIES),
ui_scheduler=ui_scheduler_metrics,
)