'fix:task2-多条记录填入同一tapd链接时从缓存读取已拉取内容'
This commit is contained in:
parent
01d768b16a
commit
2d7fa8ea7a
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"access_token": "y4R56yl4blcYc8QUl5Dz04NUWBXZ-siYcApes3uEYuiz3EQpK0V3H6BCYvMNJliIFVFpMzGAQqhc9FRklpojrNSF-5p2x1sPrNy4MaZ85dZLhWP7kr0PRJPHMio-7xCvzL9aoI06HBvIqSum8OBxLueDGDlxHHMpmIH1HKj0bM30Arj4toE36mNck7ThF1ELklzzMtKlwnpVsVgQ3Wro6MuQS9_B0wosfDjFglxluLc",
|
"access_token": "2LMUjFN9nR32QOSk-RYWTfpdYRL4CLtdgk10JWam9o4mvbfTnsLt8a0ODt-S_eu7MFJ4CFnC6fjLMbAUmfOufPXZrwa2sndigq7xoTWnUqeGqsu_2YcRW3VwilGMJuMG1_6_SYJgNwizMoS8BapKpGW1b37i1ITlQERhRR-iCyO-4DOezbQzb_07y_G1XNM1T3uyo09BNvfIIRx1yGAbmyv_63koAjW4LG3x_AbZnPE",
|
||||||
"fetch_time": 1769071472.3016503
|
"fetch_time": 1770177536.5388677
|
||||||
}
|
}
|
||||||
@ -24,6 +24,16 @@ from src2.tapd_api import TAPDStoryApi, TERMINAL_STATUSES, StoryNotFoundExceptio
|
|||||||
from src2.smartsheet_sync import SmartSheetSync, REQUIRED_FIELDS
|
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:
|
class SyncService:
|
||||||
"""TAPD状态同步服务(支持多表格同步)"""
|
"""TAPD状态同步服务(支持多表格同步)"""
|
||||||
|
|
||||||
@ -73,6 +83,16 @@ class SyncService:
|
|||||||
print(f" ⚠ 计划字段映射获取失败: {e},将使用空映射")
|
print(f" ⚠ 计划字段映射获取失败: {e},将使用空映射")
|
||||||
self.plan_mapping = {}
|
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" ✓ 同步服务初始化完成")
|
print(f" ✓ 同步服务初始化完成")
|
||||||
|
|
||||||
def _get_beijing_time_str(self) -> str:
|
def _get_beijing_time_str(self) -> str:
|
||||||
@ -94,6 +114,16 @@ class SyncService:
|
|||||||
Returns:
|
Returns:
|
||||||
Dict: 同步结果统计
|
Dict: 同步结果统计
|
||||||
"""
|
"""
|
||||||
|
# 初始化本次同步的缓存
|
||||||
|
self._story_cache = {}
|
||||||
|
self._cache_stats = {
|
||||||
|
"total_queries": 0,
|
||||||
|
"cache_hits": 0,
|
||||||
|
"cache_misses": 0,
|
||||||
|
"api_calls": 0,
|
||||||
|
"cached_failures": 0
|
||||||
|
}
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"success": False,
|
"success": False,
|
||||||
"start_time": datetime.now().isoformat(),
|
"start_time": datetime.now().isoformat(),
|
||||||
@ -141,6 +171,10 @@ class SyncService:
|
|||||||
if all_failed_records:
|
if all_failed_records:
|
||||||
self._send_failure_notification(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()
|
result["end_time"] = datetime.now().isoformat()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -352,6 +386,59 @@ class SyncService:
|
|||||||
|
|
||||||
return sheet_result
|
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]:
|
def _process_record(self, record_info: Dict) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
处理单条记录的同步
|
处理单条记录的同步
|
||||||
@ -393,8 +480,8 @@ class SyncService:
|
|||||||
story_id = record_info["story_id"]
|
story_id = record_info["story_id"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 查询TAPD获取需求信息
|
# 查询TAPD获取需求信息(使用缓存)
|
||||||
story_info = self.tapd_api.get_story(story_id)
|
story_info = self._get_story_with_cache(story_id)
|
||||||
record_result["story_info"] = story_info
|
record_result["story_info"] = story_info
|
||||||
|
|
||||||
# 提取需要同步的字段(None 转为空字符串)
|
# 提取需要同步的字段(None 转为空字符串)
|
||||||
@ -554,8 +641,8 @@ class SyncService:
|
|||||||
|
|
||||||
for record_info in records:
|
for record_info in records:
|
||||||
try:
|
try:
|
||||||
# 查询TAPD最新状态
|
# 查询TAPD最新状态(使用缓存)
|
||||||
story_info = self.tapd_api.get_story(record_info["story_id"])
|
story_info = self._get_story_with_cache(record_info["story_id"])
|
||||||
|
|
||||||
# 提取最新字段值(None 转为空字符串)
|
# 提取最新字段值(None 转为空字符串)
|
||||||
new_status = story_info.get('status') or ''
|
new_status = story_info.get('status') or ''
|
||||||
@ -622,6 +709,36 @@ class SyncService:
|
|||||||
|
|
||||||
return result
|
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:
|
def _send_failure_notification(self, failed_records: List[Dict]) -> None:
|
||||||
"""
|
"""
|
||||||
发送同步失败通知
|
发送同步失败通知
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user