""" Confirm Card Repository 确认卡片持久化仓储 - JSON文件存储 """ from pathlib import Path from typing import List, Optional import json import uuid from datetime import datetime from app.models.confirm_card import ( ConfirmCard, ConfirmCardStatus, ConfirmCardOption ) from app.utils.logger import get_logger logger = get_logger(__name__) # 数据文件路径 DATA_DIR = Path(__file__).parent.parent.parent / "data" CONFIRM_CARDS_FILE = DATA_DIR / "confirm_cards.json" class ConfirmCardRepository: """确认卡片仓储""" def __init__(self): self.file_path = CONFIRM_CARDS_FILE self._cards: Dict[str, ConfirmCard] = {} self._ensure_data_dir() self._load() def _ensure_data_dir(self): """确保数据目录存在""" self.file_path.parent.mkdir(parents=True, exist_ok=True) def _load(self): """从文件加载""" if self.file_path.exists(): try: content = self.file_path.read_text(encoding="utf-8") if content.strip(): data = json.loads(content) for card_id, card_data in data.items(): # 转换选项列表 if "options" in card_data and isinstance(card_data["options"], list): card_data["options"] = [ ConfirmCardOption(**opt) for opt in card_data["options"] ] # 确保datetime正确转换 if isinstance(card_data.get("created_at"), str): card_data["created_at"] = datetime.fromisoformat( card_data["created_at"].replace('Z', '+00:00') ) if isinstance(card_data.get("confirmed_at"), str): card_data["confirmed_at"] = datetime.fromisoformat( card_data["confirmed_at"].replace('Z', '+00:00') ) if isinstance(card_data.get("expires_at"), str): card_data["expires_at"] = datetime.fromisoformat( card_data["expires_at"].replace('Z', '+00:00') ) self._cards[card_id] = ConfirmCard(**card_data) except Exception as e: logger.error(f"加载确认卡片失败: {e}") def _save(self): """保存到文件""" try: data = {} for card_id, card in self._cards.items(): card_dict = card.dict() # 确保datetime正确序列化 if isinstance(card_dict.get("created_at"), datetime): card_dict["created_at"] = card_dict["created_at"].isoformat() if isinstance(card_dict.get("confirmed_at"), datetime): card_dict["confirmed_at"] = card_dict["confirmed_at"].isoformat() if isinstance(card_dict.get("expires_at"), datetime): card_dict["expires_at"] = card_dict["expires_at"].isoformat() data[card_id] = card_dict self.file_path.write_text( json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8" ) except Exception as e: logger.error(f"保存确认卡片失败: {e}") raise async def create(self, card: ConfirmCard) -> ConfirmCard: """创建确认卡片""" if not card.id: card.id = str(uuid.uuid4()) self._cards[card.id] = card self._save() logger.info(f"创建确认卡片: {card.id}") return card async def get(self, card_id: str) -> Optional[ConfirmCard]: """获取单个卡片""" return self._cards.get(card_id) async def list_by_project( self, project_id: str, status: Optional[ConfirmCardStatus] = None, include_expired: bool = False ) -> List[ConfirmCard]: """列出项目的确认卡片""" cards = [ card for card in self._cards.values() if card.project_id == project_id ] if status: cards = [c for c in cards if c.status == status] if not include_expired: now = datetime.now() cards = [c for c in cards if not c.expires_at or c.expires_at > now] # 按创建时间倒序 cards = sorted(cards, key=lambda c: c.created_at, reverse=True) return cards async def update_status( self, card_id: str, status: ConfirmCardStatus, selected_option_id: Optional[str] = None, custom_response: Optional[str] = None, user_notes: Optional[str] = None ) -> Optional[ConfirmCard]: """更新确认卡片状态""" card = self._cards.get(card_id) if not card: return None card.status = status card.selected_option_id = selected_option_id card.custom_response = custom_response card.user_notes = user_notes card.confirmed_at = datetime.now() self._cards[card_id] = card self._save() logger.info(f"更新确认卡片状态: {card_id} -> {status}") return card async def delete(self, card_id: str) -> bool: """删除确认卡片""" if card_id in self._cards: del self._cards[card_id] self._save() return True return False async def cleanup_expired(self) -> int: """清理过期的确认卡片""" now = datetime.now() expired_ids = [ card_id for card_id, card in self._cards.items() if card.expires_at and card.expires_at < now ] for card_id in expired_ids: self._cards[card_id].status = ConfirmCardStatus.EXPIRED if expired_ids: self._save() logger.info(f"标记 {len(expired_ids)} 个确认卡片为过期") return len(expired_ids) # 全局单例 _confirm_card_repo = None def get_confirm_card_repo() -> ConfirmCardRepository: """获取确认卡片仓储单例""" global _confirm_card_repo if _confirm_card_repo is None: _confirm_card_repo = ConfirmCardRepository() return _confirm_card_repo # 导入Dict类型 from typing import Dict