feat(监控): 实时服务状态界面 - Oracle 新增 service_activity 活动表(只保留7天,写入时清理)与 /api/oracle/activity 接口(队列实时状态/当前处理视频/模型调用/rclone/人物合并/最近50条活动);VideoQueue 打点+当前处理跟踪+status();PersonService 打点;rclone_sync.sh 同步结果写活动表;fam-ui 新增'🖥 服务状态'页(状态卡+活动时间流,直连 Oracle 实时拉取)

This commit is contained in:
ericwyuan
2026-08-21 15:52:36 +08:00
parent da7631976d
commit 0d0a7f6ef8
7 changed files with 311 additions and 9 deletions

View File

@@ -52,6 +52,11 @@ def _sync_token():
return _SYNC_TOK or ''
def _now_iso_str() -> str:
from datetime import datetime, timezone, timedelta
return datetime.now(timezone(timedelta(hours=8))).strftime('%Y-%m-%d %H:%M:%S')
@api_bp.route('/api/oracle/sync', methods=['GET'])
def sync_pull():
"""NAS 拉取增量数据。since=ISO 时间字符串(默认 '' 拉全量)。
@@ -145,6 +150,45 @@ def event_thumb(event_id):
return send_file(path, mimetype='image/jpeg')
@api_bp.route('/api/oracle/activity', methods=['GET'])
def activity():
"""实时服务状态 + 最近活动流token 校验)。
返回各服务当前状态(队列/当前处理视频/rclone 最近同步/人物合并/模型调用)
与最近 50 条活动service_activity只保留 7 天)。
"""
if not _check_token():
return jsonify({"error": "unauthorized"}), 401
db = state.get_db()
queue = state.get_queue()
# 队列实时状态
q_status = None
if queue is not None:
try:
q_status = queue.status()
except Exception as e:
logger.warning(f"queue.status 异常: {e}")
# 各服务最近活动rclone / person / model 切换)
def _last_activity(service):
row = db._conn.execute(
"SELECT service, action, detail, ts FROM service_activity "
"WHERE service=? ORDER BY id DESC LIMIT 1", (service,)).fetchone()
return dict(row) if row else None
# 最近模型调用(实时模型卡)
model_calls = db._conn.execute(
"SELECT id, provider, model, filename, started_at, duration_sec, "
"success, error FROM model_calls ORDER BY id DESC LIMIT 5").fetchall()
return jsonify({
"queue": q_status,
"db": db.get_queue_status(),
"rclone": _last_activity('rclone'),
"person": _last_activity('person'),
"model_calls": [dict(m) for m in model_calls],
"activities": db.get_recent_activities(50),
"ts": _now_iso_str(),
}), 200
@api_bp.route('/health', methods=['GET'])
def health():
"""健康检查"""