Speed up journal save with AJAX and async K-line charts.
Defer exchange chart generation to a background thread and save via XHR so fill-from-trade no longer blocks on full page reload. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+57
-45
@@ -8857,29 +8857,28 @@ def del_order(id):
|
||||
@app.route("/add_journal", methods=["POST"])
|
||||
@login_required
|
||||
def add_journal():
|
||||
from lib.instance.journal_chart_async_lib import journal_ajax_or_flash_error
|
||||
|
||||
d = request.form
|
||||
order_type_norm = normalize_journal_order_type(d.get("order_type"))
|
||||
if not order_type_norm:
|
||||
flash("请选择下单类型")
|
||||
return _redirect_records()
|
||||
return journal_ajax_or_flash_error(request, "请选择下单类型", redirect_fn=_redirect_records)
|
||||
direction_norm = normalize_journal_direction(d.get("direction") or d.get("direction_hint"))
|
||||
if not direction_norm:
|
||||
flash("请选择方向")
|
||||
return _redirect_records()
|
||||
return journal_ajax_or_flash_error(request, "请选择方向", redirect_fn=_redirect_records)
|
||||
entry_reason_norm = normalize_journal_entry_reason(
|
||||
d.get("entry_reason"), ENTRY_REASON_OPTIONS, allow_legacy=False
|
||||
)
|
||||
if not entry_reason_norm:
|
||||
flash("请选择开仓类型")
|
||||
return _redirect_records()
|
||||
return journal_ajax_or_flash_error(request, "请选择开仓类型", redirect_fn=_redirect_records)
|
||||
early_exit_trigger = normalize_early_exit_trigger(d.get("early_exit_trigger"))
|
||||
early_exit_note = str(d.get("early_exit_note") or "").strip()
|
||||
if not early_exit_trigger:
|
||||
flash("请选择离场触发")
|
||||
return _redirect_records()
|
||||
return journal_ajax_or_flash_error(request, "请选择离场触发", redirect_fn=_redirect_records)
|
||||
if early_exit_trigger == "手动平仓" and not early_exit_note:
|
||||
flash("手工平仓必须填写补充说明")
|
||||
return _redirect_records()
|
||||
return journal_ajax_or_flash_error(
|
||||
request, "手工平仓必须填写补充说明", redirect_fn=_redirect_records
|
||||
)
|
||||
if early_exit_trigger != "手动平仓":
|
||||
early_exit_note = ""
|
||||
# 兼容字段:仅「手工平仓」记为「主观提前」语义下的「是」
|
||||
@@ -8912,14 +8911,14 @@ def add_journal():
|
||||
|
||||
want_exchange_chart = (
|
||||
not has_manual_uploads
|
||||
and ORDER_CHART_ENABLED
|
||||
and d.get("journal_exchange_chart", "").lower() in ("1", "true", "on", "yes")
|
||||
)
|
||||
chart_msg = None
|
||||
if want_exchange_chart and ORDER_CHART_ENABLED:
|
||||
chart_job = None
|
||||
if want_exchange_chart:
|
||||
coin = (d.get("coin") or "").strip().upper()
|
||||
symbol_guess = normalize_symbol_input(coin) or coin
|
||||
exchange_symbol = normalize_okx_symbol(symbol_guess)
|
||||
title_prefix = f"{symbol_guess} journal {entry_id[:8]}"
|
||||
journal_tfs = parse_journal_chart_timeframes(
|
||||
d.get("journal_chart_tf1"),
|
||||
d.get("journal_chart_tf2"),
|
||||
@@ -8927,36 +8926,21 @@ def add_journal():
|
||||
)
|
||||
journal_limit = parse_journal_chart_limit(d.get("journal_chart_limit"), ORDER_CHART_LIMIT)
|
||||
chart_anchor = parse_journal_chart_anchor(d.get("journal_chart_anchor"))
|
||||
marker_payload = {
|
||||
"entry_ts_ms": _local_input_datetime_to_ms(d.get("open_datetime")),
|
||||
"exit_ts_ms": _local_input_datetime_to_ms(d.get("close_datetime")),
|
||||
"entry_price": d.get("entry_price_hint"),
|
||||
"exit_price": d.get("exit_price_hint"),
|
||||
"stop_loss_price": d.get("stop_loss_hint"),
|
||||
"chart_anchor": chart_anchor,
|
||||
"now_ts_ms": int(app_now().timestamp() * 1000),
|
||||
chart_job = {
|
||||
"exchange_symbol": exchange_symbol,
|
||||
"title_prefix": f"{symbol_guess} journal {entry_id[:8]}",
|
||||
"journal_tfs": journal_tfs,
|
||||
"journal_limit": journal_limit,
|
||||
"marker_payload": {
|
||||
"entry_ts_ms": _local_input_datetime_to_ms(d.get("open_datetime")),
|
||||
"exit_ts_ms": _local_input_datetime_to_ms(d.get("close_datetime")),
|
||||
"entry_price": d.get("entry_price_hint"),
|
||||
"exit_price": d.get("exit_price_hint"),
|
||||
"stop_loss_price": d.get("stop_loss_hint"),
|
||||
"chart_anchor": chart_anchor,
|
||||
"now_ts_ms": int(app_now().timestamp() * 1000),
|
||||
},
|
||||
}
|
||||
try:
|
||||
chart_fname = f"journal_{entry_id}.png"
|
||||
saved = generate_multi_timeframe_chart_png(
|
||||
exchange_symbol,
|
||||
title_prefix,
|
||||
timeframes=journal_tfs,
|
||||
limit=journal_limit,
|
||||
out_dir=app.config["UPLOAD_FOLDER"],
|
||||
filename=chart_fname,
|
||||
filename_prefix="journal",
|
||||
marker_payload=marker_payload,
|
||||
marker_timeframes={x.strip().lower() for x in journal_tfs},
|
||||
layout="vertical",
|
||||
)
|
||||
if saved:
|
||||
image_filename = saved
|
||||
chart_msg = f"已生成复盘K线图({'/'.join(journal_tfs)} 各{journal_limit}根):/static/images/{saved}"
|
||||
else:
|
||||
chart_msg = "已勾选自动生成K线图,但生成失败(返回空).请检查 Pillow 是否安装,OKX 网络/代理是否正常."
|
||||
except Exception as e:
|
||||
chart_msg = f"自动生成K线图失败:{str(e)}"
|
||||
|
||||
conn = get_db()
|
||||
conn.execute(
|
||||
@@ -8993,10 +8977,38 @@ def add_journal():
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
if chart_msg:
|
||||
flash(f"交易复盘记录已保存.{chart_msg}")
|
||||
if chart_job:
|
||||
from lib.instance.journal_chart_async_lib import schedule_journal_exchange_chart
|
||||
|
||||
schedule_journal_exchange_chart(
|
||||
entry_id=entry_id,
|
||||
exchange_symbol=chart_job["exchange_symbol"],
|
||||
title_prefix=chart_job["title_prefix"],
|
||||
journal_tfs=chart_job["journal_tfs"],
|
||||
journal_limit=chart_job["journal_limit"],
|
||||
marker_payload=chart_job["marker_payload"],
|
||||
upload_folder=app.config["UPLOAD_FOLDER"],
|
||||
generate_chart_fn=generate_multi_timeframe_chart_png,
|
||||
get_db_fn=get_db,
|
||||
)
|
||||
msg = (
|
||||
"交易复盘记录已保存.K 线图后台生成中"
|
||||
f"({'/'.join(chart_job['journal_tfs'])} 各{chart_job['journal_limit']}根),稍后刷新列表可见."
|
||||
)
|
||||
else:
|
||||
flash("交易复盘记录已保存")
|
||||
msg = "交易复盘记录已保存"
|
||||
from lib.instance.journal_chart_async_lib import request_wants_journal_ajax
|
||||
|
||||
if request_wants_journal_ajax(request):
|
||||
return jsonify(
|
||||
{
|
||||
"ok": True,
|
||||
"id": entry_id,
|
||||
"msg": msg,
|
||||
"chart_pending": bool(chart_job),
|
||||
}
|
||||
)
|
||||
flash(msg)
|
||||
return _redirect_records()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user