feat: DB-first architecture with lock-safe writes

- price_monitor: writes live prices to both JSON and mofin.db (holdings + live_prices + portfolio_summary)
- mofin_db: added execute_with_retry/commit_with_retry with exponential backoff on 'database is locked'
- mofin_db: increased timeout 5s->15s, added PRAGMA busy_timeout=15000
- price_monitor retry loop: fixed break-before-if-ok bug (was not retrying on write failure)
- DB connection: WAL mode + retry decorator for all write operations
- cash sync: preserves DB authoritative cash (JSON cash not pushed to DB)

This is the DB-first version. JSON writes remain for dashboard compatibility.
Next step: remove JSON writes entirely for full DB-only architecture.
This commit is contained in:
知微
2026-07-06 12:02:11 +08:00
parent 687155487d
commit e185b4e4dc
30 changed files with 1856 additions and 2042 deletions
+3 -5
View File
@@ -20,6 +20,7 @@ import threading
import time
from datetime import datetime, time
from mo_data import read_portfolio, read_decisions
from mofin_db import get_conn
# ── MoFin unified model ──────────────────────────────────────────────
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -63,9 +64,7 @@ def fetch_trend_data(code):
# 价格从 DB 读取,不再自拉腾讯 API
current = 0
try:
import sqlite3
db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
db.row_factory = sqlite3.Row
db = get_conn()
row = db.execute("SELECT price FROM holdings WHERE code=? AND is_active=1", (code,)).fetchone()
if not row:
row = db.execute("SELECT price FROM watchlist_stocks WHERE code=? AND is_active=1", (code,)).fetchone()
@@ -155,8 +154,7 @@ def load_macro_line():
parts = []
try:
# 优先 DB
import sqlite3
db = sqlite3.connect("/home/hmo/MoFin/data/mofin.db")
db = get_conn()
row = db.execute(
"SELECT structure FROM macro_context_log "
"WHERE has_valid_data=1 ORDER BY created_at DESC LIMIT 1"