improvement(chat): 单个消息库损坏时跳过并记录诊断而非整体失败

- /chat/messages 与会话最近消息构建在遇到 sqlite3.DatabaseError 时不再让整个接口 500,改为捕获诊断、记录 warning 日志后继续尝试其他 message_*.db。

- session_last_message 返回新增 skippedDbs 字段,方便排查遗漏库。
This commit is contained in:
2977094657
2026-04-29 17:24:52 +08:00
Unverified
parent 8d2dda61d8
commit a83f9b774c
2 changed files with 39 additions and 11 deletions
+16 -3
View File
@@ -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:
@@ -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),
}