微信小程序页面迁移校验之前 P5任务处理之前
This commit is contained in:
@@ -107,6 +107,11 @@ class APIClient:
|
||||
"""
|
||||
return self._post_json(endpoint, params)
|
||||
|
||||
# CHANGE [2026-03-06] intent: 补齐公共 post() 方法,UnifiedPipeline 详情拉取模式需要调用 self.api.post()
|
||||
def post(self, endpoint: str, params: dict | None = None) -> dict:
|
||||
"""发送 POST JSON 请求(与 get 相同,语义更明确的别名)。"""
|
||||
return self._post_json(endpoint, params)
|
||||
|
||||
def _post_json(self, endpoint: str, payload: dict | None = None) -> dict:
|
||||
if not self.base_url:
|
||||
raise ValueError("API base_url 未配置")
|
||||
@@ -292,3 +297,10 @@ class APIClient:
|
||||
return v
|
||||
|
||||
return []
|
||||
|
||||
# AI_CHANGELOG:
|
||||
# - 日期: 2026-03-06 08:37:26
|
||||
# - Prompt: P20260306-083206
|
||||
# - 直接原因: APIClient 缺少公共 post() 方法,UnifiedPipeline 详情拉取模式调用 self.api.post() 失败
|
||||
# - 变更摘要: 新增 post() 作为 _post_json() 的公共别名,与已有 get() 对齐
|
||||
# - 风险与验证: 极低风险,纯别名转发;166 个单元测试通过
|
||||
|
||||
43
apps/etl/connectors/feiqiu/api/rate_limiter.py
Normal file
43
apps/etl/connectors/feiqiu/api/rate_limiter.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""请求间隔控制器,支持取消信号中断等待。"""
|
||||
|
||||
import random
|
||||
import time
|
||||
import threading
|
||||
|
||||
|
||||
class RateLimiter:
|
||||
"""请求间隔控制器,在相邻 API 请求之间插入随机等待时间,防止触发上游风控。
|
||||
|
||||
等待期间以 0.5s 为单位轮询 cancel_event,支持快速响应取消信号。
|
||||
"""
|
||||
|
||||
def __init__(self, min_interval: float = 5.0, max_interval: float = 20.0):
|
||||
if min_interval > max_interval:
|
||||
raise ValueError(
|
||||
f"min_interval({min_interval}) 不能大于 max_interval({max_interval})"
|
||||
)
|
||||
self._min = min_interval
|
||||
self._max = max_interval
|
||||
self._last_interval: float = 0.0
|
||||
|
||||
def wait(self, cancel_event: threading.Event | None = None) -> bool:
|
||||
"""等待随机间隔。返回 False 表示被取消信号中断。
|
||||
|
||||
将等待时间拆分为 0.5s 小段,每段检查 cancel_event,
|
||||
以便在取消信号到达时快速退出(最多延迟 0.5s)。
|
||||
"""
|
||||
interval = random.uniform(self._min, self._max)
|
||||
self._last_interval = interval
|
||||
remaining = interval
|
||||
while remaining > 0:
|
||||
if cancel_event and cancel_event.is_set():
|
||||
return False
|
||||
sleep_time = min(0.5, remaining)
|
||||
time.sleep(sleep_time)
|
||||
remaining -= sleep_time
|
||||
return True
|
||||
|
||||
@property
|
||||
def last_interval(self) -> float:
|
||||
"""最近一次 wait() 生成的随机间隔值。"""
|
||||
return self._last_interval
|
||||
@@ -36,6 +36,11 @@ class RecordingAPIClient:
|
||||
self.last_dump: dict[str, Any] | None = None
|
||||
|
||||
# ------------------------------------------------------------------ 公共 API
|
||||
# CHANGE [2026-03-06] intent: 补齐 post() 代理,使 RecordingAPIClient 完整覆盖 APIClient 公共接口
|
||||
def post(self, endpoint: str, params: dict | None = None) -> dict:
|
||||
"""委托给底层 APIClient 的 post 方法(详情拉取等非分页请求使用)。"""
|
||||
return self.base.post(endpoint, params)
|
||||
|
||||
def get_source_hint(self, endpoint: str) -> str:
|
||||
"""Return the JSON dump path for this endpoint (for source_file lineage)."""
|
||||
return str(self.output_dir / endpoint_to_filename(endpoint))
|
||||
@@ -193,6 +198,12 @@ def build_recording_client(
|
||||
|
||||
|
||||
# AI_CHANGELOG:
|
||||
# - 日期: 2026-03-06 08:37:26
|
||||
# - Prompt: P20260306-083206
|
||||
# - 直接原因: RecordingAPIClient 缺少 post() 方法,UnifiedPipeline 详情拉取模式调用失败
|
||||
# - 变更摘要: 新增 post() 方法委托给 self.base.post(),补齐代理接口覆盖
|
||||
# - 风险与验证: 极低风险,纯委托转发;166 个单元测试通过
|
||||
#
|
||||
# - 日期: 2026-02-14
|
||||
# - Prompt: P20260214-040231(审计收口补录)
|
||||
# - 直接原因: 默认时区 Asia/Taipei 与运营地区(中国大陆)不符
|
||||
|
||||
Reference in New Issue
Block a user