99from datetime import datetime
1010import platform
1111import sys
12+ import time
1213import psutil
1314
1415from PySide6 .QtWidgets import QWidget , QFileDialog
@@ -940,11 +941,14 @@ def _export_diagnostic_files(file_path: str, app_dir: Path) -> int:
940941 导出的文件数量
941942 """
942943 export_folders = [
943- Path ("config" ),
944- Path ("app" ) / "data" / "list" ,
945- Path ("app" ) / "data" / "Language" ,
946- Path ("app" ) / "data" / "history" ,
947- Path ("logs" ),
944+ get_path ("config" ),
945+ get_data_path ("list" ),
946+ get_data_path ("Language" ),
947+ get_data_path ("history" ),
948+ get_data_path ("audio" ),
949+ get_data_path ("CSES" ),
950+ get_data_path ("images" ),
951+ get_path (LOG_DIR ),
948952 ]
949953
950954 exported_count = 0
@@ -1061,11 +1065,14 @@ def _collect_system_info(
10611065 "export_folders" : [
10621066 str (folder )
10631067 for folder in [
1064- Path ("config" ),
1065- Path ("app" ) / "data" / "list" ,
1066- Path ("app" ) / "data" / "Language" ,
1067- Path ("app" ) / "data" / "history" ,
1068- Path ("logs" ),
1068+ get_path ("config" ),
1069+ get_data_path ("list" ),
1070+ get_data_path ("Language" ),
1071+ get_data_path ("history" ),
1072+ get_data_path ("audio" ),
1073+ get_data_path ("CSES" ),
1074+ get_data_path ("images" ),
1075+ get_path (LOG_DIR ),
10691076 ]
10701077 ],
10711078 "export_location" : str (file_path ),
@@ -1272,11 +1279,14 @@ def export_all_data(parent: Optional[QWidget] = None) -> None:
12721279 file_path += ".zip"
12731280
12741281 dirs_to_backup = [
1275- ("config" , Path ("config" )),
1276- ("list" , Path ("app" ) / "data" / "list" ),
1277- ("Language" , Path ("app" ) / "data" / "Language" ),
1278- ("history" , Path ("app" ) / "data" / "history" ),
1279- ("logs" , Path ("logs" )),
1282+ ("config" , get_path ("config" )),
1283+ ("list" , get_data_path ("list" )),
1284+ ("Language" , get_data_path ("Language" )),
1285+ ("history" , get_data_path ("history" )),
1286+ ("audio" , get_data_path ("audio" )),
1287+ ("CSES" , get_data_path ("CSES" )),
1288+ ("images" , get_data_path ("images" )),
1289+ ("logs" , get_path (LOG_DIR )),
12801290 ]
12811291
12821292 with zipfile .ZipFile (file_path , "w" , zipfile .ZIP_DEFLATED ) as zipf :
@@ -1360,7 +1370,7 @@ def import_all_data(parent: Optional[QWidget] = None) -> None:
13601370 if not _confirm_import (parent ):
13611371 return
13621372
1363- _extract_data_files (file_path )
1373+ skipped_files = _extract_data_files (file_path )
13641374
13651375 success_dialog = MessageBox (
13661376 get_any_position_value_async (
@@ -1369,11 +1379,20 @@ def import_all_data(parent: Optional[QWidget] = None) -> None:
13691379 "import_success_title" ,
13701380 "name" ,
13711381 ),
1372- get_any_position_value_async (
1373- "basic_settings" ,
1374- "data_import_export" ,
1375- "import_success_content" ,
1376- "name" ,
1382+ (
1383+ get_any_position_value_async (
1384+ "basic_settings" ,
1385+ "data_import_export" ,
1386+ "import_success_content_skipped" ,
1387+ "name" ,
1388+ ).format (count = len (skipped_files ))
1389+ if skipped_files
1390+ else get_any_position_value_async (
1391+ "basic_settings" ,
1392+ "data_import_export" ,
1393+ "import_success_content" ,
1394+ "name" ,
1395+ )
13771396 ),
13781397 parent ,
13791398 )
@@ -1480,11 +1499,14 @@ def _check_existing_files(file_path: str) -> list:
14801499 """检查已存在的文件"""
14811500 existing_files = []
14821501 target_dirs = {
1483- "config" : Path ("config" ),
1484- "list" : Path ("data/list" ),
1485- "Language" : Path ("data/Language" ),
1486- "history" : Path ("data/history" ),
1487- "logs" : Path ("logs" ),
1502+ "config" : get_path ("config" ),
1503+ "list" : get_data_path ("list" ),
1504+ "Language" : get_data_path ("Language" ),
1505+ "history" : get_data_path ("history" ),
1506+ "audio" : get_data_path ("audio" ),
1507+ "CSES" : get_data_path ("CSES" ),
1508+ "images" : get_data_path ("images" ),
1509+ "logs" : get_path (LOG_DIR ),
14881510 }
14891511
14901512 with zipfile .ZipFile (file_path , "r" ) as zipf :
@@ -1562,16 +1584,22 @@ def _confirm_import(parent: Optional[QWidget]) -> bool:
15621584 return dialog .exec ()
15631585
15641586
1565- def _extract_data_files (file_path : str ) -> None :
1587+ def _extract_data_files (file_path : str ) -> list :
15661588 """提取数据文件"""
15671589 target_dirs = {
1568- "config" : Path ("config" ),
1569- "list" : Path ("data/list" ),
1570- "Language" : Path ("data/Language" ),
1571- "history" : Path ("data/history" ),
1572- "logs" : Path ("logs" ),
1590+ "config" : get_path ("config" ),
1591+ "list" : get_data_path ("list" ),
1592+ "Language" : get_data_path ("Language" ),
1593+ "history" : get_data_path ("history" ),
1594+ "CSES" : get_data_path ("CSES" ),
1595+ "images" : get_data_path ("images" ),
1596+ "audio" : get_data_path ("audio" ),
1597+ "logs" : get_path (LOG_DIR ),
15731598 }
15741599
1600+ skipped_files = []
1601+ logs_root = get_path (LOG_DIR ).resolve ()
1602+
15751603 with zipfile .ZipFile (file_path , "r" ) as zipf :
15761604 for member in zipf .namelist ():
15771605 if member == "version.json" :
@@ -1586,9 +1614,50 @@ def _extract_data_files(file_path: str) -> None:
15861614 target_path = target_dirs [dir_name ] / relative_path
15871615 target_path .parent .mkdir (parents = True , exist_ok = True )
15881616
1589- with zipf .open (member ) as source , open (target_path , "wb" ) as target :
1617+ tmp_path = target_path .with_name (f"{ target_path .name } .import_tmp" )
1618+ if tmp_path .exists ():
1619+ try :
1620+ tmp_path .unlink ()
1621+ except Exception :
1622+ pass
1623+
1624+ with zipf .open (member ) as source , open (tmp_path , "wb" ) as target :
15901625 shutil .copyfileobj (source , target )
15911626
1627+ for attempt in range (3 ):
1628+ try :
1629+ os .replace (tmp_path , target_path )
1630+ break
1631+ except PermissionError as e :
1632+ if attempt < 2 :
1633+ time .sleep (0.2 )
1634+ continue
1635+
1636+ is_logs_file = False
1637+ try :
1638+ is_logs_file = target_path .resolve ().is_relative_to (
1639+ logs_root
1640+ )
1641+ except Exception :
1642+ is_logs_file = (
1643+ str (target_path )
1644+ .replace ("\\ " , "/" )
1645+ .startswith (str (logs_root ).replace ("\\ " , "/" ) + "/" )
1646+ )
1647+
1648+ if is_logs_file :
1649+ skipped_files .append (str (target_path ))
1650+ try :
1651+ tmp_path .unlink ()
1652+ except Exception :
1653+ pass
1654+ logger .warning (f"导入文件被占用,已跳过: { target_path } " )
1655+ break
1656+
1657+ raise e
1658+
1659+ return skipped_files
1660+
15921661
15931662# ==================== 记录管理模块 ====================
15941663
0 commit comments