From a83f9b774c63258dc42a63667e1cdca4efde47ce Mon Sep 17 00:00:00 2001 From: 2977094657 <2977094657@qq.com> Date: Wed, 29 Apr 2026 17:24:52 +0800 Subject: [PATCH] =?UTF-8?q?improvement(chat):=20=E5=8D=95=E4=B8=AA?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=BA=93=E6=8D=9F=E5=9D=8F=E6=97=B6=E8=B7=B3?= =?UTF-8?q?=E8=BF=87=E5=B9=B6=E8=AE=B0=E5=BD=95=E8=AF=8A=E6=96=AD=E8=80=8C?= =?UTF-8?q?=E9=9D=9E=E6=95=B4=E4=BD=93=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /chat/messages 与会话最近消息构建在遇到 sqlite3.DatabaseError 时不再让整个接口 500,改为捕获诊断、记录 warning 日志后继续尝试其他 message_*.db。 - session_last_message 返回新增 skippedDbs 字段,方便排查遗漏库。 --- src/wechat_decrypt_tool/routers/chat.py | 19 ++++++++++-- .../session_last_message.py | 31 ++++++++++++++----- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/wechat_decrypt_tool/routers/chat.py b/src/wechat_decrypt_tool/routers/chat.py index fe97f36..e143d4a 100644 --- a/src/wechat_decrypt_tool/routers/chat.py +++ b/src/wechat_decrypt_tool/routers/chat.py @@ -4512,9 +4512,10 @@ def _collect_chat_messages( contact_conn = None for db_path in db_paths: - conn = sqlite3.connect(str(db_path)) - conn.row_factory = sqlite3.Row + conn: Optional[sqlite3.Connection] = None try: + conn = sqlite3.connect(str(db_path)) + conn.row_factory = sqlite3.Row table_name = _resolve_msg_table_name(conn, username) if not table_name: continue @@ -5024,8 +5025,20 @@ def _collect_chat_messages( "_rawText": raw_text if local_type in (10000, 266287972401) else "", } ) + except sqlite3.DatabaseError as e: + # 单个解密库损坏时不要让整个聊天详情接口 500;保留诊断日志,继续尝试其他 message_*.db。 + logger.warning( + "[chat.messages] malformed message db skipped account=%s username=%s db=%s error=%s diag=%s", + account_dir.name, + username, + str(db_path), + str(e), + format_sqlite_diagnostics(collect_sqlite_diagnostics(db_path, quick_check=True)), + ) + continue finally: - conn.close() + if conn is not None: + conn.close() if contact_conn is not None: try: diff --git a/src/wechat_decrypt_tool/session_last_message.py b/src/wechat_decrypt_tool/session_last_message.py index 45a0c22..4e12a87 100644 --- a/src/wechat_decrypt_tool/session_last_message.py +++ b/src/wechat_decrypt_tool/session_last_message.py @@ -18,6 +18,7 @@ from .chat_helpers import ( _should_keep_session, ) from .logging_config import get_logger +from .sqlite_diagnostics import collect_sqlite_diagnostics, format_sqlite_diagnostics logger = get_logger(__name__) @@ -241,11 +242,13 @@ def build_session_last_message_table( best: dict[str, tuple[tuple[int, int, int], dict[str, Any]]] = {} + skipped_dbs = 0 for db_path in db_paths: - conn = sqlite3.connect(str(db_path)) - conn.row_factory = sqlite3.Row - conn.text_factory = bytes + conn: Optional[sqlite3.Connection] = None try: + conn = sqlite3.connect(str(db_path)) + conn.row_factory = sqlite3.Row + conn.text_factory = bytes trows = conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall() md5_to_table: dict[str, str] = {} for tr in trows: @@ -414,11 +417,22 @@ def build_session_last_message_table( "table_name": str(table_name), }, ) + except sqlite3.DatabaseError as e: + skipped_dbs += 1 + logger.warning( + "[session_last_message] malformed message db skipped account=%s db=%s error=%s diag=%s", + account_dir.name, + str(db_path), + str(e), + format_sqlite_diagnostics(collect_sqlite_diagnostics(db_path, quick_check=True)), + ) + continue finally: - try: - conn.close() - except Exception: - pass + if conn is not None: + try: + conn.close() + except Exception: + pass # Fallback: always have a non-empty preview for UI. for r in sessions: @@ -493,7 +507,7 @@ def build_session_last_message_table( duration = max(0.0, time.time() - started) logger.info( f"[session_last_message] build done account={account_dir.name} sessions={len(best)} " - f"durationSec={round(duration, 3)} table={_TABLE_NAME}" + f"durationSec={round(duration, 3)} table={_TABLE_NAME} skippedDbs={skipped_dbs}" ) return { "status": "success", @@ -501,4 +515,5 @@ def build_session_last_message_table( "built": len(best), "table": _TABLE_NAME, "durationSec": round(duration, 3), + "skippedDbs": int(skipped_dbs), }