file_manager.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import os
  2. import logging
  3. from typing import Dict, Set, Optional
  4. from pathlib import Path
  5. class FileNameManager:
  6. """文件名管理器,处理文件命名和冲突"""
  7. def __init__(self, output_dir: str):
  8. self.output_dir = output_dir
  9. self.used_names: Set[str] = set()
  10. self.name_mapping: Dict[str, str] = {} # 原始名 -> 实际文件名
  11. self.logger = logging.getLogger("schema_tools.FileNameManager")
  12. # 扫描已存在的文件
  13. self._scan_existing_files()
  14. def _scan_existing_files(self):
  15. """扫描输出目录中已存在的文件"""
  16. if not os.path.exists(self.output_dir):
  17. return
  18. for root, dirs, files in os.walk(self.output_dir):
  19. for file in files:
  20. if file.endswith(('.ddl', '.md')):
  21. self.used_names.add(file)
  22. def get_safe_filename(self, schema_name: str, table_name: str, suffix: str) -> str:
  23. """
  24. 生成安全的文件名,避免冲突
  25. Args:
  26. schema_name: Schema名称
  27. table_name: 表名
  28. suffix: 文件后缀(如 .ddl 或 _detail.md)
  29. Returns:
  30. 安全的文件名
  31. """
  32. # 生成基础文件名
  33. base_name = self._generate_base_name(schema_name, table_name)
  34. # 添加后缀
  35. if suffix.startswith('.'):
  36. filename = f"{base_name}{suffix}"
  37. else:
  38. filename = f"{base_name}{suffix}"
  39. # 检查冲突并生成唯一名称
  40. unique_filename = self._ensure_unique_filename(filename)
  41. # 记录映射关系
  42. original_key = f"{schema_name}.{table_name}"
  43. self.name_mapping[original_key] = unique_filename
  44. self.used_names.add(unique_filename)
  45. return unique_filename
  46. def _generate_base_name(self, schema_name: str, table_name: str) -> str:
  47. """
  48. 生成基础文件名
  49. 规则:
  50. - public.table_name → table_name
  51. - schema.table_name → schema__table_name
  52. - 特殊字符替换: . → __, - → _, 空格 → _
  53. """
  54. if schema_name.lower() == 'public':
  55. safe_name = table_name
  56. else:
  57. safe_name = f"{schema_name}__{table_name}"
  58. # 替换特殊字符
  59. replacements = {
  60. '.': '__',
  61. '-': '_',
  62. ' ': '_',
  63. '/': '_',
  64. '\\': '_',
  65. ':': '_',
  66. '*': '_',
  67. '?': '_',
  68. '"': '_',
  69. '<': '_',
  70. '>': '_',
  71. '|': '_'
  72. }
  73. for old_char, new_char in replacements.items():
  74. safe_name = safe_name.replace(old_char, new_char)
  75. # 移除连续的下划线
  76. while '__' in safe_name:
  77. safe_name = safe_name.replace('__', '_')
  78. return safe_name
  79. def _ensure_unique_filename(self, filename: str) -> str:
  80. """确保文件名唯一性"""
  81. if filename not in self.used_names:
  82. return filename
  83. # 如果重名,添加数字后缀
  84. base, ext = os.path.splitext(filename)
  85. counter = 1
  86. while True:
  87. unique_name = f"{base}_{counter}{ext}"
  88. if unique_name not in self.used_names:
  89. self.logger.warning(f"文件名冲突,'{filename}' 重命名为 '{unique_name}'")
  90. return unique_name
  91. counter += 1
  92. def get_full_path(self, filename: str, subdirectory: Optional[str] = None) -> str:
  93. """
  94. 获取完整文件路径
  95. Args:
  96. filename: 文件名
  97. subdirectory: 子目录(如 'ddl' 或 'docs')
  98. Returns:
  99. 完整路径
  100. """
  101. if subdirectory:
  102. full_path = os.path.join(self.output_dir, subdirectory, filename)
  103. else:
  104. full_path = os.path.join(self.output_dir, filename)
  105. # 确保目录存在
  106. os.makedirs(os.path.dirname(full_path), exist_ok=True)
  107. return full_path
  108. def get_mapping_report(self) -> Dict[str, str]:
  109. """获取文件名映射报告"""
  110. return self.name_mapping.copy()
  111. def write_mapping_report(self):
  112. """写入文件名映射报告"""
  113. report_path = os.path.join(self.output_dir, "filename_mapping.txt")
  114. try:
  115. with open(report_path, 'w', encoding='utf-8') as f:
  116. f.write("# 文件名映射报告\n")
  117. f.write("# 格式: 原始表名 -> 实际文件名\n\n")
  118. for original, actual in sorted(self.name_mapping.items()):
  119. f.write(f"{original} -> {actual}\n")
  120. self.logger.info(f"文件名映射报告已保存到: {report_path}")
  121. except Exception as e:
  122. self.logger.error(f"写入文件名映射报告失败: {e}")