merge_backend_simple.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 后端源代码软著申请专用拼接脚本 (Python版本)
  5. 功能:将所有后端源代码文件完整拼接成单一文档,专用于软件著作权申请材料
  6. 特点:
  7. - 支持多种后端语言(Java, Python, Node.js, PHP等)
  8. - 智能识别源代码文件类型
  9. - 保持代码格式和注释完整性
  10. - 跨平台兼容(Windows/Linux/macOS)
  11. """
  12. import os
  13. import sys
  14. import json
  15. from pathlib import Path
  16. from datetime import datetime
  17. from typing import List, Optional, Dict
  18. # 颜色输出类
  19. class Colors:
  20. RED = '\033[0;31m'
  21. GREEN = '\033[0;32m'
  22. YELLOW = '\033[1;33m'
  23. BLUE = '\033[0;34m'
  24. NC = '\033[0m' # No Color
  25. def print_success(message: str):
  26. print(f"{Colors.GREEN}✓ {message}{Colors.NC}")
  27. def print_info(message: str):
  28. print(f"{Colors.BLUE}ℹ {message}{Colors.NC}")
  29. def print_warning(message: str):
  30. print(f"{Colors.YELLOW}⚠ {message}{Colors.NC}")
  31. def print_error(message: str):
  32. print(f"{Colors.RED}✗ {message}{Colors.NC}")
  33. def get_project_config() -> Optional[dict]:
  34. """读取项目配置文件"""
  35. config_file = Path("ai-copyright-config.json")
  36. if not config_file.exists():
  37. print_error("配置文件不存在: ai-copyright-config.json")
  38. return None
  39. try:
  40. with open(config_file, 'r', encoding='utf-8') as f:
  41. return json.load(f)
  42. except json.JSONDecodeError as e:
  43. print_error(f"配置文件JSON格式错误: {e}")
  44. return None
  45. except Exception as e:
  46. print_error(f"读取配置文件失败: {e}")
  47. return None
  48. def get_source_file_extensions() -> Dict[str, List[str]]:
  49. """获取不同语言的源代码文件扩展名"""
  50. return {
  51. 'Java': ['.java', '.jsp', '.xml', '.properties'],
  52. 'Python': ['.py', '.pyx', '.pyi', '.pyw'],
  53. 'JavaScript': ['.js', '.ts', '.jsx', '.tsx', '.json'],
  54. 'Node.js': ['.js', '.ts', '.json', '.mjs'],
  55. 'PHP': ['.php', '.php3', '.php4', '.php5', '.phtml'],
  56. 'C#': ['.cs', '.csx', '.vb'],
  57. 'C++': ['.cpp', '.cc', '.cxx', '.c', '.h', '.hpp'],
  58. 'Go': ['.go'],
  59. 'Ruby': ['.rb', '.rbw'],
  60. 'Rust': ['.rs'],
  61. 'Kotlin': ['.kt', '.kts'],
  62. 'Swift': ['.swift'],
  63. 'Common': ['.sql', '.yml', '.yaml', '.txt', '.md', '.xml', '.json', '.properties', '.env']
  64. }
  65. def collect_source_files(backend_dir: Path, backend_tech: str) -> List[Path]:
  66. """收集所有源代码文件"""
  67. if not backend_dir.exists():
  68. return []
  69. extensions_map = get_source_file_extensions()
  70. # 根据后端技术确定文件扩展名
  71. target_extensions = set()
  72. # 添加指定技术的扩展名
  73. if backend_tech in extensions_map:
  74. target_extensions.update(extensions_map[backend_tech])
  75. # 始终添加通用文件类型
  76. target_extensions.update(extensions_map['Common'])
  77. # 如果未识别技术,包含更多常见扩展名
  78. if backend_tech not in extensions_map:
  79. for exts in extensions_map.values():
  80. target_extensions.update(exts)
  81. source_files = []
  82. # 递归搜索源代码文件
  83. for file_path in backend_dir.rglob('*'):
  84. if file_path.is_file():
  85. # 检查文件扩展名
  86. if file_path.suffix.lower() in target_extensions:
  87. # 排除某些不需要的文件
  88. if not should_exclude_file(file_path):
  89. source_files.append(file_path)
  90. # 按相对路径排序,确保一致的输出顺序
  91. source_files.sort(key=lambda x: str(x.relative_to(backend_dir)).lower())
  92. return source_files
  93. def should_exclude_file(file_path: Path) -> bool:
  94. """判断是否应该排除某个文件"""
  95. exclude_patterns = [
  96. '__pycache__',
  97. '.git',
  98. '.svn',
  99. 'node_modules',
  100. '.class',
  101. '.pyc',
  102. '.pyo',
  103. '.log',
  104. '.tmp',
  105. '.temp',
  106. 'target',
  107. 'build',
  108. 'dist'
  109. ]
  110. file_str = str(file_path).lower()
  111. for pattern in exclude_patterns:
  112. if pattern in file_str:
  113. return True
  114. # 排除空文件或过大的文件
  115. try:
  116. file_size = file_path.stat().st_size
  117. if file_size == 0 or file_size > 10 * 1024 * 1024: # 10MB限制
  118. return True
  119. except:
  120. return True
  121. return False
  122. def read_source_file(file_path: Path) -> str:
  123. """安全读取源代码文件内容"""
  124. try:
  125. with open(file_path, 'r', encoding='utf-8') as f:
  126. return f.read()
  127. except UnicodeDecodeError:
  128. # 尝试其他编码
  129. encodings = ['gb2312', 'gbk', 'iso-8859-1', 'latin-1']
  130. for encoding in encodings:
  131. try:
  132. with open(file_path, 'r', encoding=encoding) as f:
  133. return f.read()
  134. except:
  135. continue
  136. print_warning(f"无法读取文件 {file_path.name}: 编码问题")
  137. return f"// 文件读取失败: {file_path.name} (编码问题)"
  138. except Exception as e:
  139. print_warning(f"无法读取文件 {file_path.name}: {e}")
  140. return f"// 文件读取失败: {file_path.name}"
  141. def generate_header(config: dict, file_count: int, backend_tech: str) -> str:
  142. """生成文档头部信息"""
  143. current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  144. header = f"""
  145. {'-' * 80}
  146. 软件著作权申请材料 - 后端源代码文档
  147. {'-' * 80}
  148. 软件名称: {config.get('title', '未设置')}
  149. 软件简称: {config.get('short_title', config.get('title', '未设置'))}
  150. 后端技术: {backend_tech}
  151. 生成模式: {config.get('generation_mode', '未设置')}
  152. 文档生成信息:
  153. - 生成时间: {current_time}
  154. - 源代码文件数量: {file_count}
  155. - 文档类型: 后端源代码完整文档
  156. - 编码格式: UTF-8
  157. {'-' * 80}
  158. """
  159. return header
  160. def generate_footer() -> str:
  161. """生成文档尾部信息"""
  162. footer = f"""
  163. {'-' * 80}
  164. 文档结束
  165. 生成工具: AI驱动的软件著作权申请材料生成系统 (Python版本)
  166. 生成时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
  167. {'-' * 80}
  168. """
  169. return footer
  170. def merge_backend_files():
  171. """主要的后端文件合并逻辑"""
  172. print_info("🔄 开始拼接后端源代码...")
  173. # 1. 确定项目根目录
  174. script_dir = Path(__file__).parent.parent.parent
  175. backend_dir = script_dir / "output_sourcecode" / "backend"
  176. output_dir = script_dir / "output_docs"
  177. output_file = output_dir / "后端源代码.txt"
  178. print_info(f"项目根目录: {script_dir}")
  179. print_info(f"后端目录: {backend_dir}")
  180. print_info(f"输出文件: {output_file}")
  181. # 2. 检查后端目录
  182. if not backend_dir.exists():
  183. print_error(f"后端目录不存在: {backend_dir}")
  184. print_info("💡 请先生成后端源代码文件")
  185. return False
  186. # 3. 确保输出目录存在
  187. output_dir.mkdir(parents=True, exist_ok=True)
  188. # 4. 读取项目配置
  189. config = get_project_config()
  190. if not config:
  191. print_warning("无法读取项目配置,使用默认配置")
  192. config = {
  193. 'title': '软件系统',
  194. 'short_title': '软件系统',
  195. 'backend': 'Java',
  196. 'generation_mode': 'fast'
  197. }
  198. backend_tech = config.get('backend', 'Java')
  199. # 5. 收集源代码文件
  200. source_files = collect_source_files(backend_dir, backend_tech)
  201. if not source_files:
  202. print_error(f"在 {backend_dir} 中未发现源代码文件")
  203. print_info("💡 请先生成后端源代码文件")
  204. return False
  205. print_success(f"发现 {len(source_files)} 个源代码文件 (技术栈: {backend_tech})")
  206. # 6. 按文件类型分组统计
  207. file_stats = {}
  208. for file_path in source_files:
  209. ext = file_path.suffix.lower()
  210. if ext not in file_stats:
  211. file_stats[ext] = 0
  212. file_stats[ext] += 1
  213. print_info("文件类型统计:")
  214. for ext, count in sorted(file_stats.items()):
  215. print_info(f" {ext or '(无扩展名)'}: {count} 个文件")
  216. # 7. 开始合并文件
  217. try:
  218. with open(output_file, 'w', encoding='utf-8') as output:
  219. # 写入文档头部
  220. output.write(generate_header(config, len(source_files), backend_tech))
  221. # 逐个处理源代码文件
  222. for i, source_file in enumerate(source_files, 1):
  223. rel_path = source_file.relative_to(backend_dir)
  224. print_info(f"处理文件 {i}/{len(source_files)}: {rel_path}")
  225. # 添加文件分隔标识
  226. separator = f"""
  227. {'=' * 80}
  228. 文件 {i}: {source_file.name}
  229. 文件路径: output_sourcecode/backend/{rel_path}
  230. 文件类型: {source_file.suffix or '(无扩展名)'}
  231. 文件大小: {source_file.stat().st_size} 字节
  232. {'=' * 80}
  233. """
  234. output.write(separator)
  235. # 读取并写入文件内容
  236. content = read_source_file(source_file)
  237. output.write(content)
  238. # 确保文件内容以换行结束
  239. if content and not content.endswith('\n'):
  240. output.write('\n')
  241. # 添加文件结束标识
  242. output.write(f"\n\n{'=' * 80}\n文件 {i} 结束: {source_file.name}\n{'=' * 80}\n\n")
  243. # 写入文档尾部
  244. output.write(generate_footer())
  245. # 8. 输出统计信息
  246. file_size = output_file.stat().st_size
  247. file_size_mb = file_size / (1024 * 1024)
  248. print_success("✅ 后端源代码拼接完成")
  249. print_info(f"📄 输出文件: {output_file}")
  250. print_info(f"📊 文件统计:")
  251. print_info(f" - 源代码文件数量: {len(source_files)}")
  252. print_info(f" - 总文件大小: {file_size:,} 字节 ({file_size_mb:.2f} MB)")
  253. print_info(f" - 平均文件大小: {file_size // len(source_files):,} 字节")
  254. # 9. 生成详细报告
  255. with open(output_dir / "后端拼接报告.txt", 'w', encoding='utf-8') as report:
  256. report.write(f"后端源代码拼接报告\n")
  257. report.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
  258. report.write(f"后端技术: {backend_tech}\n\n")
  259. report.write(f"文件类型统计:\n")
  260. for ext, count in sorted(file_stats.items()):
  261. report.write(f" {ext or '(无扩展名)'}: {count} 个文件\n")
  262. report.write(f"\n文件列表:\n")
  263. for i, source_file in enumerate(source_files, 1):
  264. rel_path = source_file.relative_to(backend_dir)
  265. file_size = source_file.stat().st_size
  266. report.write(f"{i:3d}. {rel_path} ({file_size:,} 字节)\n")
  267. total_size = sum(f.stat().st_size for f in source_files)
  268. report.write(f"\n总计: {len(source_files)} 个文件,{total_size:,} 字节\n")
  269. print_success("📋 生成详细报告: 后端拼接报告.txt")
  270. return True
  271. except Exception as e:
  272. print_error(f"文件合并过程中发生错误: {e}")
  273. return False
  274. def main():
  275. """主函数"""
  276. if len(sys.argv) > 1 and sys.argv[1] in ['-h', '--help']:
  277. print("后端源代码拼接脚本 (Python版本)")
  278. print("\n用法:")
  279. print(" python3 merge_backend_simple.py")
  280. print("\n说明:")
  281. print(" 将 output_sourcecode/backend/ 目录下的所有源代码文件")
  282. print(" 拼接成单一的源代码文档用于软著申请")
  283. print("\n支持的技术栈:")
  284. extensions_map = get_source_file_extensions()
  285. for tech in sorted(extensions_map.keys()):
  286. if tech != 'Common':
  287. print(f" - {tech}")
  288. print("\n输出:")
  289. print(" output_docs/后端源代码.txt")
  290. print(" output_docs/后端拼接报告.txt")
  291. return
  292. success = merge_backend_files()
  293. sys.exit(0 if success else 1)
  294. if __name__ == "__main__":
  295. main()