"""复盘自动 K 线:后台线程生成,避免 /add_journal 同步卡住.""" from __future__ import annotations import logging import threading from typing import Any, Callable logger = logging.getLogger(__name__) def schedule_journal_exchange_chart( *, entry_id: str, exchange_symbol: str, title_prefix: str, journal_tfs: list[str], journal_limit: int, marker_payload: dict[str, Any], upload_folder: str, generate_chart_fn: Callable[..., str | None], get_db_fn: Callable[[], Any], ) -> None: """提交后立即返回;线程内画图并写回 journal_entries.image.""" entry_id = str(entry_id or "").strip() if not entry_id or not callable(generate_chart_fn) or not callable(get_db_fn): return tfs = [str(x).strip() for x in (journal_tfs or []) if str(x).strip()] if not tfs: return def _run() -> None: try: chart_fname = f"journal_{entry_id}.png" saved = generate_chart_fn( exchange_symbol, title_prefix, timeframes=tfs, limit=journal_limit, out_dir=upload_folder, filename=chart_fname, filename_prefix="journal", marker_payload=marker_payload, marker_timeframes={x.lower() for x in tfs}, layout="vertical", ) if not saved: logger.warning("journal chart async empty entry_id=%s", entry_id) return conn = get_db_fn() try: conn.execute( "UPDATE journal_entries SET image=? WHERE id=?", (saved, entry_id), ) conn.commit() finally: conn.close() except Exception: logger.exception("journal chart async failed entry_id=%s", entry_id) threading.Thread( target=_run, name=f"journal-chart-{entry_id[:8]}", daemon=True, ).start() def request_wants_journal_ajax(request: Any) -> bool: """XHR / Accept:json / form ajax=1 → 返回 JSON,避免整页刷新.""" xrw = str(getattr(request, "headers", {}).get("X-Requested-With") or "").lower() if xrw == "xmlhttprequest": return True form = getattr(request, "form", None) if form is not None: raw = str(form.get("ajax") or "").strip().lower() if raw in ("1", "true", "yes", "on"): return True accept = str(getattr(request, "headers", {}).get("Accept") or "").lower() if "application/json" in accept and accept.strip().startswith("application/json"): return True return False def journal_ajax_or_flash_error(request: Any, msg: str, *, redirect_fn: Callable[[], Any]): """校验失败:AJAX 返回 JSON,否则 flash + 跳转.""" from flask import flash, jsonify if request_wants_journal_ajax(request): return jsonify({"ok": False, "msg": msg}), 400 flash(msg) return redirect_fn()