From e40741462c2afafa898c0d3002e7495515c698bd Mon Sep 17 00:00:00 2001 From: Nickszy <1344398321@qq.com> Date: Tue, 6 Jan 2026 21:02:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Redis=20=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=BA=8F=E5=88=97=E5=8C=96=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20datetime/date=20=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AssetPrice.to_dict(): 修复 date 对象序列化,使用 isoformat() - AsyncRedisCache.set(): 添加自动序列化逻辑 - Pydantic model 使用 mode='json' 序列化 - 递归处理嵌套的 dict/list 中的 datetime 对象 - 支持 dataclass 的 to_dict() 方法 修复了 "Object of type date is not JSON serializable" 错误, 使缓存能够正常写入,避免每次请求都重新拉取数据。 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/server/domain/types.py | 13 +++--- .../infrastructure/cache/redis_cache.py | 42 ++++++++++++++++++- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/server/domain/types.py b/src/server/domain/types.py index 8710da1..8683a09 100644 --- a/src/server/domain/types.py +++ b/src/server/domain/types.py @@ -164,15 +164,18 @@ class AssetPrice: def to_dict(self) -> Dict[str, Any]: """Convert to dictionary representation.""" + # Handle both datetime and date objects for timestamp + ts = self.timestamp + if isinstance(ts, datetime): + ts = ts.isoformat() + elif hasattr(ts, 'isoformat'): # date object + ts = ts.isoformat() + return { "ticker": self.ticker, "price": float(self.price) if self.price else None, "currency": self.currency, - "timestamp": ( - self.timestamp.isoformat() - if isinstance(self.timestamp, datetime) - else self.timestamp - ), + "timestamp": ts, "volume": float(self.volume) if self.volume else None, "open_price": float(self.open_price) if self.open_price else None, "high_price": float(self.high_price) if self.high_price else None, diff --git a/src/server/infrastructure/cache/redis_cache.py b/src/server/infrastructure/cache/redis_cache.py index 1e255cc..6f35fd3 100644 --- a/src/server/infrastructure/cache/redis_cache.py +++ b/src/server/infrastructure/cache/redis_cache.py @@ -36,12 +36,52 @@ async def get(self, key: str) -> Optional[Any]: async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool: try: - await self._cache.set(key, value, ttl=ttl or self._ttl_default) + # Serialize value for JSON storage (handles datetime objects) + serialized = self._serialize_value(value) + await self._cache.set(key, serialized, ttl=ttl or self._ttl_default) return True except Exception as e: logger.error(f"❌ Cache set error for {key}: {e}") return False + def _serialize_value(self, value: Any) -> Any: + """Serialize value for JSON storage, handling datetime objects.""" + # Pydantic model: use mode='json' to convert datetime to ISO strings + if hasattr(value, 'model_dump'): + return value.model_dump(mode='json') + + # Dataclass with to_dict method + if hasattr(value, 'to_dict'): + result = value.to_dict() + # Recursively handle nested datetime objects + return self._serialize_dict(result) + + # List of items + if isinstance(value, list): + return [self._serialize_value(item) for item in value] + + # Dict with potential datetime values + if isinstance(value, dict): + return self._serialize_dict(value) + + return value + + def _serialize_dict(self, data: dict) -> dict: + """Recursively serialize dict values, converting datetime to ISO strings.""" + from datetime import date, datetime + + result = {} + for k, v in data.items(): + if isinstance(v, (datetime, date)): + result[k] = v.isoformat() + elif isinstance(v, dict): + result[k] = self._serialize_dict(v) + elif isinstance(v, list): + result[k] = [self._serialize_value(item) for item in v] + else: + result[k] = v + return result + async def delete(self, key: str) -> bool: try: await self._cache.delete(key)