Skip to main content
Glama
document_service.py11.4 kB
""" 文档服务模块 提供README生成、文档更新等文档相关服务。 """ import os from pathlib import Path from typing import Dict, Any, Optional, List from datetime import datetime from data_access.file_system import FileSystemInterface, FolderStructure from data_access.cache import CacheInterface from data_access.config import ConfigInterface import logging from middleware.logging import LoggingMiddleware from middleware.logging import log_performance logger = logging.getLogger(__name__) class DocumentService: """文档服务类""" def __init__( self, file_system: FileSystemInterface, template_service: Any, # TemplateService cache_service: Any, # CacheService config_interface: Optional[ConfigInterface] = None ): """ 初始化文档服务 Args: file_system: 文件系统接口 template_service: 模板服务 cache_service: 缓存服务 config_interface: 配置接口 """ self.file_system = file_system self.template_service = template_service self.cache_service = cache_service self.config_interface = config_interface or ConfigInterface() self.config = self.config_interface.get_config() @log_performance("generate_readme") async def generate_readme( self, folder_path: str, template_name: str = "simple", force_update: bool = False, include_hidden: bool = False ) -> Dict[str, Any]: """ 生成README文档 Args: folder_path: 文件夹路径 template_name: 模板名称 force_update: 是否强制更新 include_hidden: 是否包含隐藏文件 Returns: 生成结果 """ try: folder_path = os.path.abspath(folder_path) readme_path = Path(folder_path) / "README.md" # 检查是否需要更新 if not force_update and readme_path.exists(): # 检查文件是否有更新 should_update = await self.file_system.should_update_file( str(readme_path), [folder_path] ) if not should_update: return { "success": True, "message": "README已存在且无需更新", "path": str(readme_path), "updated": False } # 获取文件夹结构 structure = await self.file_system.get_folder_structure( folder_path, exclude_dirs=[], include_hidden=include_hidden ) # 生成README内容 content = await self._generate_readme_content( structure, template_name ) # 写入文件 success, error_msg = await self.file_system.write_file_content( str(readme_path), content, backup=True ) if not success: return { "success": False, "error": f"写入README失败: {error_msg}" } # 缓存生成结果 cache_key = self.cache_service.cache_file_content(str(readme_path)) self.cache_service.set(cache_key, content, ttl=3600) # 1小时 logging_middleware.logger.info( "README生成成功", path=str(readme_path), template=template_name, size=len(content) ) return { "success": True, "message": "README生成成功", "path": str(readme_path), "size": len(content), "template": template_name, "updated": True } except (RuntimeError, ValueError) as e: return { "success": False, "error": f"生成README失败: {str(e)}" } @log_performance("preview_readme") async def preview_readme( self, folder_path: str, template_name: str = "simple", include_hidden: bool = False ) -> Dict[str, Any]: """ 预览README内容 Args: folder_path: 文件夹路径 template_name: 模板名称 include_hidden: 是否包含隐藏文件 Returns: 预览结果 """ try: folder_path = os.path.abspath(folder_path) # 获取文件夹结构 structure = await self.file_system.get_folder_structure( folder_path, exclude_dirs=[], include_hidden=include_hidden ) # 生成README内容 content = await self._generate_readme_content( structure, template_name ) return { "success": True, "content": content, "template": template_name, "folder_path": folder_path, "size": len(content) } except (RuntimeError, ValueError) as e: return { "success": False, "error": f"预览README失败: {str(e)}" } async def _generate_readme_content( self, structure: FolderStructure, template_name: str ) -> str: """ 生成README内容 Args: structure: 文件夹结构 template_name: 模板名称 Returns: README内容 """ # 获取模板 if hasattr(self.template_service, 'render_template'): # 使用模板服务 context = self._build_template_context(structure) content = self.template_service.render_template(template_name, context) else: # 使用默认模板 content = self._generate_default_readme(structure) return content def _build_template_context(self, structure: FolderStructure) -> Dict[str, Any]: """ 构建模板上下文 Args: structure: 文件夹结构 Returns: 模板上下文 """ # 获取文件夹描述 folder_descriptions = self.config.folder_descriptions # 处理文件夹信息 folders = [] for folder_info in structure.folders: folder_name = folder_info.name description = folder_descriptions.get( folder_name, f"{folder_name}目录" ) folders.append({ "name": folder_name, "description": description, "size": folder_info.size, "modified_time": folder_info.modified_time }) # 处理文件信息 files = [] for file_info in structure.files: file_name = file_info.name description = self._get_file_description(file_info) files.append({ "name": file_name, "description": description, "size": file_info.size, "extension": file_info.extension, "type": file_info.mime_type }) return { "folder_name": structure.root_path.name, "description": folder_descriptions.get( structure.root_path.name, f"{structure.root_path.name}项目目录" ), "folders": folders, "files": files, "folder_count": structure.folder_count, "file_count": structure.file_count, "total_size": self._format_size(structure.total_size), "generation_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S") } def _generate_default_readme(self, structure: FolderStructure) -> str: """ 生成默认README内容 Args: structure: 文件夹结构 Returns: README内容 """ context = self._build_template_context(structure) # 简单的默认模板 content = f"""# {context['folder_name']} {context['description']} ## 📁 文件夹结构 """ if context['folders']: content += "### 📂 子文件夹\n\n" for folder in context['folders']: content += f"- `{folder['name']}/` - {folder['description']}\n" content += "\n" if context['files']: content += "### 📄 文件列表\n\n" for file in context['files']: content += f"- `{file['name']}` - {file['description']}\n" content += "\n" content += f"""## 📊 统计信息 - **文件夹数量**: {context['folder_count']} - **文件数量**: {context['file_count']} - **总大小**: {context['total_size']} - **生成时间**: {context['generation_time']} --- *此文档由 MCP服务器 自动生成于 {context['generation_time']}* """ return content def _get_file_description(self, file_info: Any) -> str: """ 获取文件描述 Args: file_info: 文件信息 Returns: 文件描述 """ # 根据文件扩展名生成描述 ext = file_info.extension.lower() descriptions = { '.py': 'Python源代码文件', '.js': 'JavaScript源代码文件', '.ts': 'TypeScript源代码文件', '.html': 'HTML网页文件', '.css': 'CSS样式文件', '.json': 'JSON数据文件', '.yaml': 'YAML配置文件', '.yml': 'YAML配置文件', '.md': 'Markdown文档文件', '.txt': '文本文件', '.sql': 'SQL数据库文件', '.sh': 'Shell脚本文件', '.bat': '批处理文件', '.png': 'PNG图片文件', '.jpg': 'JPEG图片文件', '.jpeg': 'JPEG图片文件', '.gif': 'GIF图片文件', '.svg': 'SVG矢量图文件' } return descriptions.get(ext, f'{ext}文件') def _format_size(self, size_bytes: int) -> str: """ 格式化文件大小 Args: size_bytes: 字节数 Returns: 格式化的大小字符串 """ if size_bytes == 0: return "0 B" size_names = ["B", "KB", "MB", "GB", "TB"] i = 0 size = float(size_bytes) while size >= 1024.0 and i < len(size_names) - 1: size /= 1024.0 i += 1 return f"{size:.1f} {size_names[i]}"

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kscz0000/Zhiwen-Assistant-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server