99import time
1010from dataclasses import dataclass
1111from datetime import datetime
12- from typing import Optional , Dict , Any , List
12+ from typing import Optional , Dict , Any , List , Callable
1313from enum import Enum
1414
1515from ..config .constants import EmailServiceType , OPENAI_EMAIL_SENDERS , OTP_CODE_PATTERN , OTP_CODE_SEMANTIC_PATTERN
@@ -139,6 +139,10 @@ def __init__(self, message: str = "当前邮件批次未发现 OpenAI 发件人"
139139 self .error_code = error_code
140140
141141
142+ class EmailServiceCancelledError (EmailServiceError ):
143+ """邮箱服务在轮询过程中收到取消信号。"""
144+
145+
142146class EmailServiceStatus (Enum ):
143147 """邮箱服务状态"""
144148 HEALTHY = "healthy"
@@ -168,6 +172,7 @@ def __init__(self, service_type: EmailServiceType, name: str = None):
168172 self ._provider_backoff = reset_adaptive_backoff ()
169173 self ._used_verification_codes : Dict [str , set ] = {}
170174 self ._seen_verification_messages : Dict [str , set ] = {}
175+ self .check_cancelled : Optional [Callable [[], bool ]] = None
171176
172177 _EMAIL_ADDRESS_PATTERN = re .compile (r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}" )
173178
@@ -190,6 +195,35 @@ def apply_provider_backoff_state(self, state: Optional[EmailProviderBackoffState
190195 """注入外部持久化的邮箱供应商退避状态"""
191196 self ._provider_backoff = state or reset_adaptive_backoff ()
192197
198+ def set_check_cancelled (self , callback : Optional [Callable [[], bool ]]) -> None :
199+ """注入外部取消检查回调。"""
200+ self .check_cancelled = callback if callable (callback ) else None
201+
202+ def _is_cancelled_requested (self ) -> bool :
203+ """检查邮箱服务是否收到取消请求。"""
204+ callback = self .check_cancelled
205+ if not callable (callback ):
206+ return False
207+ try :
208+ return bool (callback ())
209+ except Exception as e :
210+ logger .warning (f"检查邮箱服务取消状态失败: { e } " )
211+ return False
212+
213+ def _raise_if_cancelled (self , message : str = "任务已取消" ) -> None :
214+ """在轮询/等待阶段响应取消请求。"""
215+ if self ._is_cancelled_requested ():
216+ raise EmailServiceCancelledError (message )
217+
218+ def _sleep_with_cancel (self , seconds : float , chunk_seconds : float = 0.2 ) -> None :
219+ """可响应取消的短分片休眠。"""
220+ remaining = max (0.0 , float (seconds ))
221+ while remaining > 0 :
222+ self ._raise_if_cancelled ()
223+ sleep_for = min (chunk_seconds , remaining )
224+ time .sleep (sleep_for )
225+ remaining -= sleep_for
226+
193227 @abc .abstractmethod
194228 def create_email (self , config : Dict [str , Any ] = None ) -> Dict [str , Any ]:
195229 """
@@ -520,6 +554,7 @@ def wait_for_email(
520554 last_email_id = None
521555
522556 while time .time () - start_time < timeout :
557+ self ._raise_if_cancelled ("等待邮件时任务已取消" )
523558 try :
524559 emails = self .list_emails ()
525560 for email_info in emails :
@@ -562,7 +597,7 @@ def wait_for_email(
562597 except Exception as e :
563598 logger .warning (f"等待邮件时出错: { e } " )
564599
565- time . sleep (check_interval )
600+ self . _sleep_with_cancel (check_interval )
566601
567602 return None
568603
0 commit comments