mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-06-18 15:54:08 +08:00
improvement(chat): 单个消息库损坏时跳过并记录诊断而非整体失败
- /chat/messages 与会话最近消息构建在遇到 sqlite3.DatabaseError 时不再让整个接口 500,改为捕获诊断、记录 warning 日志后继续尝试其他 message_*.db。 - session_last_message 返回新增 skippedDbs 字段,方便排查遗漏库。
This commit is contained in:
@@ -4512,9 +4512,10 @@ def _collect_chat_messages(
|
|||||||
contact_conn = None
|
contact_conn = None
|
||||||
|
|
||||||
for db_path in db_paths:
|
for db_path in db_paths:
|
||||||
conn = sqlite3.connect(str(db_path))
|
conn: Optional[sqlite3.Connection] = None
|
||||||
conn.row_factory = sqlite3.Row
|
|
||||||
try:
|
try:
|
||||||
|
conn = sqlite3.connect(str(db_path))
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
table_name = _resolve_msg_table_name(conn, username)
|
table_name = _resolve_msg_table_name(conn, username)
|
||||||
if not table_name:
|
if not table_name:
|
||||||
continue
|
continue
|
||||||
@@ -5024,8 +5025,20 @@ def _collect_chat_messages(
|
|||||||
"_rawText": raw_text if local_type in (10000, 266287972401) else "",
|
"_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:
|
finally:
|
||||||
conn.close()
|
if conn is not None:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
if contact_conn is not None:
|
if contact_conn is not None:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from .chat_helpers import (
|
|||||||
_should_keep_session,
|
_should_keep_session,
|
||||||
)
|
)
|
||||||
from .logging_config import get_logger
|
from .logging_config import get_logger
|
||||||
|
from .sqlite_diagnostics import collect_sqlite_diagnostics, format_sqlite_diagnostics
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
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]]] = {}
|
best: dict[str, tuple[tuple[int, int, int], dict[str, Any]]] = {}
|
||||||
|
|
||||||
|
skipped_dbs = 0
|
||||||
for db_path in db_paths:
|
for db_path in db_paths:
|
||||||
conn = sqlite3.connect(str(db_path))
|
conn: Optional[sqlite3.Connection] = None
|
||||||
conn.row_factory = sqlite3.Row
|
|
||||||
conn.text_factory = bytes
|
|
||||||
try:
|
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()
|
trows = conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
|
||||||
md5_to_table: dict[str, str] = {}
|
md5_to_table: dict[str, str] = {}
|
||||||
for tr in trows:
|
for tr in trows:
|
||||||
@@ -414,11 +417,22 @@ def build_session_last_message_table(
|
|||||||
"table_name": str(table_name),
|
"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:
|
finally:
|
||||||
try:
|
if conn is not None:
|
||||||
conn.close()
|
try:
|
||||||
except Exception:
|
conn.close()
|
||||||
pass
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# Fallback: always have a non-empty preview for UI.
|
# Fallback: always have a non-empty preview for UI.
|
||||||
for r in sessions:
|
for r in sessions:
|
||||||
@@ -493,7 +507,7 @@ def build_session_last_message_table(
|
|||||||
duration = max(0.0, time.time() - started)
|
duration = max(0.0, time.time() - started)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[session_last_message] build done account={account_dir.name} sessions={len(best)} "
|
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 {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
@@ -501,4 +515,5 @@ def build_session_last_message_table(
|
|||||||
"built": len(best),
|
"built": len(best),
|
||||||
"table": _TABLE_NAME,
|
"table": _TABLE_NAME,
|
||||||
"durationSec": round(duration, 3),
|
"durationSec": round(duration, 3),
|
||||||
|
"skippedDbs": int(skipped_dbs),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user