""" 配置文件加载和验证工具 """ import yaml from pathlib import Path from typing import Dict, Any def load_config(config_path: str) -> Dict[str, Any]: """ 加载配置文件 Args: config_path: 配置文件路径 Returns: 配置字典 Raises: FileNotFoundError: 配置文件不存在 yaml.YAMLError: 配置文件格式错误 """ config_file = Path(config_path) if not config_file.exists(): raise FileNotFoundError(f"配置文件不存在: {config_path}") try: with open(config_file, 'r', encoding='utf-8') as f: config = yaml.safe_load(f) return config except yaml.YAMLError as e: raise yaml.YAMLError(f"配置文件格式错误: {str(e)}") def validate_config(config: Dict[str, Any]) -> None: """ 验证配置文件的完整性和有效性 Args: config: 配置字典 Raises: ValueError: 配置验证失败 """ # 检查必需的顶级配置块 required_sections = ['ai', 'feishu', 'system'] for section in required_sections: if section not in config: raise ValueError(f"缺少必需的配置块: {section}") # 验证AI配置 ai_config = config['ai'] if not ai_config.get('api_key'): raise ValueError("AI配置缺少 api_key") if not ai_config.get('base_url'): raise ValueError("AI配置缺少 base_url") if not ai_config.get('model'): raise ValueError("AI配置缺少 model") # 验证飞书配置 feishu_config = config['feishu'] if not feishu_config.get('app_id'): raise ValueError("飞书配置缺少 app_id") if not feishu_config.get('app_secret'): raise ValueError("飞书配置缺少 app_secret") if not feishu_config.get('app_token'): raise ValueError("飞书配置缺少 app_token") if not feishu_config.get('table_id'): raise ValueError("飞书配置缺少 table_id") # 验证系统配置 system_config = config['system'] if not system_config.get('watch_folder'): raise ValueError("系统配置缺少 watch_folder") # 验证后处理配置 post_process = system_config.get('post_process', 'keep') valid_post_process = ['delete', 'move', 'keep'] if post_process not in valid_post_process: raise ValueError(f"post_process 必须是 {valid_post_process} 之一,当前值: {post_process}") if post_process == 'move' and not system_config.get('processed_folder'): raise ValueError("当 post_process 为 'move' 时,必须配置 processed_folder") # 验证路径格式 watch_folder = Path(system_config['watch_folder']) if not watch_folder.is_absolute(): # 如果是相对路径,转换为绝对路径 watch_folder = Path.cwd() / watch_folder # 检查监控文件夹是否存在(允许不存在,会在运行时创建) if watch_folder.exists() and not watch_folder.is_dir(): raise ValueError(f"watch_folder 必须是一个目录: {watch_folder}") # 如果配置了移动目标文件夹,检查其路径 if post_process == 'move': processed_folder = Path(system_config['processed_folder']) if not processed_folder.is_absolute(): processed_folder = Path.cwd() / processed_folder if processed_folder.exists() and not processed_folder.is_dir(): raise ValueError(f"processed_folder 必须是一个目录: {processed_folder}") def save_config(config: Dict[str, Any], config_path: str) -> None: """ 保存配置文件 Args: config: 配置字典 config_path: 配置文件路径 """ config_file = Path(config_path) # 确保目录存在 config_file.parent.mkdir(parents=True, exist_ok=True) with open(config_file, 'w', encoding='utf-8') as f: yaml.dump(config, f, default_flow_style=False, allow_unicode=True) def create_default_config() -> Dict[str, Any]: """ 创建默认配置 Returns: 默认配置字典 """ return { 'ai': { 'api_key': 'sk-xxxxxxxxxxxxxxxxxxxxxxxx', 'base_url': 'https://api.siliconflow.cn/v1', 'model': 'gemini-2.5-pro' }, 'feishu': { 'app_id': 'cli_xxxxxxxxxxxx', 'app_secret': 'xxxxxxxxxxxxxxxxxxxxxxxx', 'app_token': 'bascnxxxxxxxxxxxxxxx', 'table_id': 'tblxxxxxxxxxxxx' }, 'system': { 'watch_folder': './monitor_images', 'post_process': 'move', 'processed_folder': './processed_images' } } def create_sample_config(config_path: str = 'config.example.yaml') -> None: """ 创建示例配置文件 Args: config_path: 示例配置文件路径 """ default_config = create_default_config() save_config(default_config, config_path) print(f"示例配置文件已创建: {config_path}") def get_config_summary(config: Dict[str, Any]) -> str: """ 获取配置摘要信息 Args: config: 配置字典 Returns: 配置摘要字符串 """ summary = [] summary.append("配置摘要:") summary.append(f" AI模型: {config['ai']['model']}") summary.append(f" AI服务: {config['ai']['base_url']}") summary.append(f" 监控文件夹: {config['system']['watch_folder']}") summary.append(f" 后处理方式: {config['system'].get('post_process', 'keep')}") if config['system'].get('post_process') == 'move': summary.append(f" 处理后文件夹: {config['system']['processed_folder']}") return '\n'.join(summary) if __name__ == "__main__": # 测试配置验证 try: # 创建示例配置 create_sample_config('config.example.yaml') print("示例配置创建成功!") # 加载并验证示例配置 config = load_config('config.example.yaml') validate_config(config) print("配置验证通过!") print(get_config_summary(config)) except Exception as e: print(f"配置测试失败: {e}")