From 2d7fa8ea7ae72ec0d2540eacc322fc33b06092ee Mon Sep 17 00:00:00 2001 From: zelong <2895587166@qq.com> Date: Wed, 4 Feb 2026 14:14:03 +0800 Subject: [PATCH] =?UTF-8?q?'fix:task2-=E5=A4=9A=E6=9D=A1=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E5=A1=AB=E5=85=A5=E5=90=8C=E4=B8=80tapd=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E6=97=B6=E4=BB=8E=E7=BC=93=E5=AD=98=E8=AF=BB=E5=8F=96=E5=B7=B2?= =?UTF-8?q?=E6=8B=89=E5=8F=96=E5=86=85=E5=AE=B9'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/token_cache.json | 4 +- src2/sync_service.py | 125 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 6 deletions(-) diff --git a/config/token_cache.json b/config/token_cache.json index 09c5adc..6ed3d05 100644 --- a/config/token_cache.json +++ b/config/token_cache.json @@ -1,4 +1,4 @@ { - "access_token": "y4R56yl4blcYc8QUl5Dz04NUWBXZ-siYcApes3uEYuiz3EQpK0V3H6BCYvMNJliIFVFpMzGAQqhc9FRklpojrNSF-5p2x1sPrNy4MaZ85dZLhWP7kr0PRJPHMio-7xCvzL9aoI06HBvIqSum8OBxLueDGDlxHHMpmIH1HKj0bM30Arj4toE36mNck7ThF1ELklzzMtKlwnpVsVgQ3Wro6MuQS9_B0wosfDjFglxluLc", - "fetch_time": 1769071472.3016503 + "access_token": "2LMUjFN9nR32QOSk-RYWTfpdYRL4CLtdgk10JWam9o4mvbfTnsLt8a0ODt-S_eu7MFJ4CFnC6fjLMbAUmfOufPXZrwa2sndigq7xoTWnUqeGqsu_2YcRW3VwilGMJuMG1_6_SYJgNwizMoS8BapKpGW1b37i1ITlQERhRR-iCyO-4DOezbQzb_07y_G1XNM1T3uyo09BNvfIIRx1yGAbmyv_63koAjW4LG3x_AbZnPE", + "fetch_time": 1770177536.5388677 } \ No newline at end of file diff --git a/src2/sync_service.py b/src2/sync_service.py index d5927db..2e0f246 100644 --- a/src2/sync_service.py +++ b/src2/sync_service.py @@ -24,6 +24,16 @@ from src2.tapd_api import TAPDStoryApi, TERMINAL_STATUSES, StoryNotFoundExceptio from src2.smartsheet_sync import SmartSheetSync, REQUIRED_FIELDS +class CacheEntry: + """TAPD查询缓存条目""" + def __init__(self, success: bool, data: Optional[Dict] = None, + error: Optional[Exception] = None): + self.success = success + self.data = data + self.error = error + self.timestamp = datetime.now() + + class SyncService: """TAPD状态同步服务(支持多表格同步)""" @@ -73,6 +83,16 @@ class SyncService: print(f" ⚠ 计划字段映射获取失败: {e},将使用空映射") self.plan_mapping = {} + # TAPD查询缓存(在sync_once中初始化) + self._story_cache: Dict[str, CacheEntry] = {} + self._cache_stats = { + "total_queries": 0, + "cache_hits": 0, + "cache_misses": 0, + "api_calls": 0, + "cached_failures": 0 + } + print(f" ✓ 同步服务初始化完成") def _get_beijing_time_str(self) -> str: @@ -94,6 +114,16 @@ class SyncService: Returns: Dict: 同步结果统计 """ + # 初始化本次同步的缓存 + self._story_cache = {} + self._cache_stats = { + "total_queries": 0, + "cache_hits": 0, + "cache_misses": 0, + "api_calls": 0, + "cached_failures": 0 + } + result = { "success": False, "start_time": datetime.now().isoformat(), @@ -141,6 +171,10 @@ class SyncService: if all_failed_records: self._send_failure_notification(all_failed_records) + # 记录缓存统计 + self._log_cache_statistics() + result["cache_stats"] = self._cache_stats.copy() + result["end_time"] = datetime.now().isoformat() return result @@ -352,6 +386,59 @@ class SyncService: return sheet_result + def _get_story_with_cache(self, story_id: str) -> Dict: + """ + 带缓存的获取需求详情 + + Args: + story_id: 需求ID + + Returns: + Dict: 需求详细信息 + + Raises: + StoryNotFoundException: 需求不存在 + Exception: 其他API错误 + """ + self._cache_stats["total_queries"] += 1 + + # 检查缓存 + if story_id in self._story_cache: + cache_entry = self._story_cache[story_id] + self._cache_stats["cache_hits"] += 1 + + if cache_entry.success: + # 缓存命中 - 成功记录 + if self.test_mode: + print(f" [缓存命中] story_id={story_id}") + return cache_entry.data + else: + # 缓存命中 - 失败记录 + self._cache_stats["cached_failures"] += 1 + if self.test_mode: + print(f" [缓存命中-失败] story_id={story_id}") + raise cache_entry.error + + # 缓存未命中,调用API + self._cache_stats["cache_misses"] += 1 + self._cache_stats["api_calls"] += 1 + + if self.test_mode: + print(f" [API调用] story_id={story_id}") + + try: + story_info = self.tapd_api.get_story(story_id) + # 缓存成功结果 + self._story_cache[story_id] = CacheEntry(success=True, data=story_info) + return story_info + except StoryNotFoundException as e: + # 缓存确定性失败(单号无效) + self._story_cache[story_id] = CacheEntry(success=False, error=e) + raise + except Exception as e: + # 不缓存非确定性失败(网络错误、API限流等) + raise + def _process_record(self, record_info: Dict) -> Dict[str, Any]: """ 处理单条记录的同步 @@ -393,8 +480,8 @@ class SyncService: story_id = record_info["story_id"] try: - # 查询TAPD获取需求信息 - story_info = self.tapd_api.get_story(story_id) + # 查询TAPD获取需求信息(使用缓存) + story_info = self._get_story_with_cache(story_id) record_result["story_info"] = story_info # 提取需要同步的字段(None 转为空字符串) @@ -554,8 +641,8 @@ class SyncService: for record_info in records: try: - # 查询TAPD最新状态 - story_info = self.tapd_api.get_story(record_info["story_id"]) + # 查询TAPD最新状态(使用缓存) + story_info = self._get_story_with_cache(record_info["story_id"]) # 提取最新字段值(None 转为空字符串) new_status = story_info.get('status') or '' @@ -622,6 +709,36 @@ class SyncService: return result + def _log_cache_statistics(self): + """记录缓存统计信息到日志""" + stats = self._cache_stats + + if stats["total_queries"] == 0: + return + + hit_rate = (stats["cache_hits"] / stats["total_queries"]) * 100 + + print(f"\n{'='*60}") + print(f"TAPD查询缓存统计") + print(f"{'='*60}") + print(f" 总查询次数: {stats['total_queries']}") + print(f" 缓存命中: {stats['cache_hits']} ({hit_rate:.1f}%)") + print(f" 缓存未命中: {stats['cache_misses']}") + print(f" 实际API调用: {stats['api_calls']}") + print(f" 缓存失败记录命中: {stats['cached_failures']}") + print(f" 缓存条目数: {len(self._story_cache)}") + print(f"{'='*60}\n") + + # 记录到日志文件 + self.logger.log_api_call( + api_type="task2", + operation="cache_statistics", + request_data={}, + response_data=stats, + success=True, + error_message=None + ) + def _send_failure_notification(self, failed_records: List[Dict]) -> None: """ 发送同步失败通知