2121from cryptography .hazmat .primitives .kdf .pbkdf2 import PBKDF2HMAC
2222
2323from .app_paths import get_output_databases_dir
24+ from .database_filters import should_skip_source_database
2425from .sqlite_diagnostics import collect_sqlite_diagnostics , sqlite_diagnostics_status
2526
2627# 注意:不再支持默认密钥,所有密钥必须通过参数传入
@@ -64,6 +65,59 @@ def _derive_account_name_from_path(path: Path) -> str:
6465 return "unknown_account"
6566
6667
68+ def _build_decrypt_failure_message (result : dict ) -> str :
69+ failed_pages = int (result .get ("failed_pages" ) or 0 )
70+ successful_pages = int (result .get ("successful_pages" ) or 0 )
71+ diagnostic_status = str (result .get ("diagnostic_status" ) or "" ).strip ()
72+ diagnostics = dict (result .get ("diagnostics" ) or {})
73+
74+ detail = (
75+ diagnostics .get ("quick_check_error" )
76+ or diagnostics .get ("connect_error" )
77+ or diagnostics .get ("table_list_error" )
78+ or diagnostics .get ("page_count_error" )
79+ or diagnostics .get ("quick_check" )
80+ or diagnostic_status
81+ )
82+ detail_text = " " .join (str (detail or "" ).split ()).strip ()
83+
84+ if failed_pages > 0 and successful_pages == 0 :
85+ if detail_text :
86+ return f"数据库校验未通过,密钥可能不匹配当前账号: { detail_text } "
87+ return "数据库校验未通过,密钥可能不匹配当前账号"
88+
89+ if diagnostic_status and diagnostic_status != "ok" :
90+ if detail_text :
91+ return f"解密输出不是有效的 SQLite 数据库: { detail_text } "
92+ return "解密输出不是有效的 SQLite 数据库"
93+
94+ if failed_pages > 0 :
95+ return "解密输出包含页失败,结果不完整"
96+
97+ return ""
98+
99+
100+ def build_decrypt_summary_message (* , success_count : int , total_databases : int , diagnostic_warning_count : int ) -> str :
101+ success_count = int (success_count or 0 )
102+ total_databases = int (total_databases or 0 )
103+ diagnostic_warning_count = int (diagnostic_warning_count or 0 )
104+
105+ if total_databases <= 0 :
106+ return "未找到可解密的数据库"
107+
108+ if success_count <= 0 :
109+ if diagnostic_warning_count > 0 :
110+ return "解密失败:数据库校验未通过,密钥可能不匹配当前账号。"
111+ return "解密失败:未能成功解密任何数据库。"
112+
113+ if success_count < total_databases :
114+ if diagnostic_warning_count > 0 :
115+ return f"解密部分成功:成功 { success_count } /{ total_databases } ,其余数据库校验未通过。"
116+ return f"解密部分成功:成功 { success_count } /{ total_databases } 。"
117+
118+ return f"解密完成: 成功 { success_count } /{ total_databases } "
119+
120+
67121def _resolve_db_storage_roots (storage_path : Path ) -> list [Path ]:
68122 try :
69123 target = storage_path .resolve ()
@@ -158,7 +212,7 @@ def scan_account_databases_from_path(db_storage_path: str) -> dict:
158212 for file_name in files :
159213 if not file_name .endswith (".db" ):
160214 continue
161- if file_name in [ "key_info.db" ] :
215+ if should_skip_source_database ( file_name ) :
162216 continue
163217 db_path = os .path .join (root , file_name )
164218 databases .append (
@@ -266,7 +320,8 @@ def _append_failed_page(page_num: int, reason: str, error: str = "") -> None:
266320 result ["failed_page_samples" ].append (item )
267321
268322 def _finalize (success : bool , error : str = "" ) -> bool :
269- result ["success" ] = bool (success )
323+ normalized_success = bool (success )
324+ result ["success" ] = normalized_success
270325 if error :
271326 result ["error" ] = " " .join (str (error ).split ()).strip ()
272327
@@ -281,6 +336,19 @@ def _finalize(success: bool, error: str = "") -> bool:
281336 result ["diagnostics" ] = diagnostics
282337 result ["diagnostic_status" ] = sqlite_diagnostics_status (diagnostics )
283338
339+ if normalized_success :
340+ failure_message = _build_decrypt_failure_message (result )
341+ if failure_message :
342+ normalized_success = False
343+ result ["success" ] = False
344+ if not result ["error" ]:
345+ result ["error" ] = failure_message
346+ if output_file .exists ():
347+ try :
348+ output_file .unlink ()
349+ except Exception as exc :
350+ logger .warning ("删除无效解密输出失败: %s, 错误: %s" , output_file , exc )
351+
284352 payload = {
285353 "db_name" : result ["db_name" ],
286354 "db_path" : result ["db_path" ],
@@ -307,7 +375,7 @@ def _finalize(success: bool, error: str = "") -> bool:
307375 log_fn = logger .warning
308376 log_fn ("[decrypt.diagnostic] %s" , json .dumps (payload , ensure_ascii = False , sort_keys = True ))
309377 self .last_result = result
310- return bool (success )
378+ return bool (result [ " success" ] )
311379
312380 logger .info (f"开始解密数据库: { db_path } " )
313381
@@ -693,7 +761,11 @@ def decrypt_wechat_databases(db_storage_path: str = None, key: str = None) -> di
693761 # 返回结果
694762 result = {
695763 "status" : "success" if success_count > 0 else "error" ,
696- "message" : f"解密完成: 成功 { success_count } /{ total_databases } " ,
764+ "message" : build_decrypt_summary_message (
765+ success_count = success_count ,
766+ total_databases = total_databases ,
767+ diagnostic_warning_count = diagnostic_warning_count ,
768+ ),
697769 "total_databases" : total_databases ,
698770 "successful_count" : success_count ,
699771 "failed_count" : total_databases - success_count ,
0 commit comments