""" 辅助函数 """ from datetime import datetime, timedelta from typing import Optional, Any import json def format_datetime(dt: Optional[datetime]) -> Optional[str]: """格式化日期时间""" if not dt: return None return dt.isoformat() def parse_datetime(dt_str: str) -> Optional[datetime]: """解析日期时间字符串""" if not dt_str: return None try: return datetime.fromisoformat(dt_str) except: return None def format_duration(seconds: int) -> str: """格式化时长""" if seconds < 60: return f"{seconds}s" elif seconds < 3600: return f"{seconds // 60}m" elif seconds < 86400: return f"{seconds // 3600}h" else: return f"{seconds // 86400}d" def truncate_string(s: str, max_length: int = 100, suffix: str = "...") -> str: """截断字符串""" if len(s) <= max_length: return s return s[:max_length - len(suffix)] + suffix def safe_json_loads(data: str, default: Any = None) -> Any: """安全的 JSON 解析""" try: return json.loads(data) except: return default def safe_json_dumps(data: Any, default: str = "{}") -> str: """安全的 JSON 序列化""" try: return json.dumps(data, ensure_ascii=False) except: return default def generate_session_title() -> str: """生成默认会话标题""" return f"Session {datetime.utcnow().strftime('%Y-%m-%d %H:%M')}" def calculate_timeout(start_time: datetime, timeout_seconds: int) -> bool: """检查是否超时""" if not start_time: return True elapsed = (datetime.utcnow() - start_time).total_seconds() return elapsed > timeout_seconds def merge_dicts(base: dict, override: dict) -> dict: """合并字典""" result = base.copy() result.update(override) return result def filter_none_values(data: dict) -> dict: """过滤字典中的 None 值""" return {k: v for k, v in data.items() if v is not None} class PaginationHelper: """分页辅助类""" @staticmethod def paginate(query, page: int = 1, per_page: int = 20): """分页查询""" if page < 1: page = 1 if per_page < 1: per_page = 20 if per_page > 100: per_page = 100 total = query.count() items = query.offset((page - 1) * per_page).limit(per_page).all() pages = (total + per_page - 1) // per_page return { 'items': items, 'total': total, 'page': page, 'per_page': per_page, 'pages': pages, 'has_next': page < pages, 'has_prev': page > 1, }