Skip to content

Commit 86b97fe

Browse files
committed
fix(registration): preserve batch task mailboxes
Use each registration task's bound email_service_id during batch runs instead of falling back to the batch-level mailbox configuration. This keeps Outlook batch registration aligned with the user-selected inboxes in both parallel and pipeline execution paths, and adds regression coverage for task-bound mailbox resolution.
1 parent ce69e5b commit 86b97fe

2 files changed

Lines changed: 101 additions & 2 deletions

File tree

src/web/routes/registration.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,10 @@ def _raise_if_cancelled(reason: str = "任务已取消") -> None:
404404
service_type = EmailServiceType(email_service_type)
405405
settings = get_settings()
406406

407+
# 批量任务可能已在创建阶段绑定了独立邮箱服务 ID(例如 Outlook 批量注册)。
408+
if not email_service_id and getattr(task, "email_service_id", None):
409+
email_service_id = int(task.email_service_id)
410+
407411
# 优先使用数据库中配置的邮箱服务
408412
if email_service_id:
409413
from ...database.models import EmailService as EmailServiceModel
@@ -900,6 +904,21 @@ def update_batch_status(**kwargs):
900904
return add_batch_log, update_batch_status
901905

902906

907+
def _resolve_task_email_service_id(
908+
task_uuid: str,
909+
fallback_email_service_id: Optional[int],
910+
) -> Optional[int]:
911+
"""优先使用任务自身绑定的邮箱服务,缺失时再回退到批量级配置。"""
912+
try:
913+
with get_db() as db:
914+
task = crud.get_registration_task(db, task_uuid)
915+
if task and getattr(task, "email_service_id", None):
916+
return int(task.email_service_id)
917+
except Exception as exc:
918+
logger.warning("读取任务绑定邮箱服务失败 %s: %s", task_uuid, exc)
919+
return fallback_email_service_id
920+
921+
903922
async def run_batch_parallel(
904923
batch_id: str,
905924
task_uuids: List[str],
@@ -942,8 +961,9 @@ async def _run_one(idx: int, uuid: str):
942961
task_manager.cancel_task(uuid)
943962
task_manager.update_status(uuid, "cancelled", error="批量任务已取消")
944963
return
964+
resolved_email_service_id = _resolve_task_email_service_id(uuid, email_service_id)
945965
await run_registration_task(
946-
uuid, email_service_type, proxy, email_service_config, email_service_id,
966+
uuid, email_service_type, proxy, email_service_config, resolved_email_service_id,
947967
log_prefix=prefix, batch_id=batch_id,
948968
auto_upload_cpa=auto_upload_cpa, cpa_service_ids=cpa_service_ids or [],
949969
auto_upload_sub2api=auto_upload_sub2api, sub2api_service_ids=sub2api_service_ids or [],
@@ -1025,8 +1045,9 @@ async def _run_and_release(idx: int, uuid: str, pfx: str):
10251045
task_manager.cancel_task(uuid)
10261046
task_manager.update_status(uuid, "cancelled", error="批量任务已取消")
10271047
return
1048+
resolved_email_service_id = _resolve_task_email_service_id(uuid, email_service_id)
10281049
await run_registration_task(
1029-
uuid, email_service_type, proxy, email_service_config, email_service_id,
1050+
uuid, email_service_type, proxy, email_service_config, resolved_email_service_id,
10301051
log_prefix=pfx, batch_id=batch_id,
10311052
auto_upload_cpa=auto_upload_cpa, cpa_service_ids=cpa_service_ids or [],
10321053
auto_upload_sub2api=auto_upload_sub2api, sub2api_service_ids=sub2api_service_ids or [],

tests/test_outlook_batch_registration.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,81 @@ async def fake_run_batch_registration(**kwargs):
7171
assert created_service_ids == [1, 2, 3]
7272
assert captured["email_service_type"] == "outlook"
7373
assert captured["registration_type"] == "parent"
74+
75+
76+
def test_resolve_task_email_service_id_prefers_task_binding(monkeypatch):
77+
class DummyDb:
78+
def __enter__(self):
79+
return self
80+
81+
def __exit__(self, exc_type, exc, tb):
82+
return False
83+
84+
class DummyTask:
85+
email_service_id = 42
86+
87+
monkeypatch.setattr(registration, "get_db", lambda: DummyDb())
88+
monkeypatch.setattr(registration.crud, "get_registration_task", lambda db, task_uuid: DummyTask())
89+
90+
assert registration._resolve_task_email_service_id("task-1", None) == 42
91+
92+
93+
def test_run_batch_pipeline_uses_task_bound_email_service_ids(monkeypatch):
94+
captured = []
95+
96+
class DummyDb:
97+
def __enter__(self):
98+
return self
99+
100+
def __exit__(self, exc_type, exc, tb):
101+
return False
102+
103+
class DummyTask:
104+
status = "completed"
105+
error_message = None
106+
107+
async def fake_run_registration_task(
108+
task_uuid,
109+
email_service_type,
110+
proxy,
111+
email_service_config,
112+
email_service_id=None,
113+
**kwargs,
114+
):
115+
captured.append((task_uuid, email_service_id))
116+
117+
monkeypatch.setattr(registration, "run_registration_task", fake_run_registration_task)
118+
monkeypatch.setattr(
119+
registration,
120+
"_resolve_task_email_service_id",
121+
lambda task_uuid, fallback: {"task-1": 101, "task-2": 202}[task_uuid],
122+
)
123+
monkeypatch.setattr(registration, "get_db", lambda: DummyDb())
124+
monkeypatch.setattr(registration.crud, "get_registration_task", lambda db, task_uuid: DummyTask())
125+
monkeypatch.setattr(registration.task_manager, "init_batch", lambda batch_id, total: None)
126+
monkeypatch.setattr(registration.task_manager, "add_batch_log", lambda batch_id, message: None)
127+
monkeypatch.setattr(registration.task_manager, "update_batch_status", lambda batch_id, **kwargs: None)
128+
monkeypatch.setattr(registration.task_manager, "is_batch_cancelled", lambda batch_id: False)
129+
monkeypatch.setattr(registration.task_manager, "cancel_task", lambda task_uuid: None)
130+
monkeypatch.setattr(registration.task_manager, "update_status", lambda task_uuid, status, error=None: None)
131+
monkeypatch.setattr(registration.random, "randint", lambda a, b: 0)
132+
133+
asyncio.run(
134+
registration.run_batch_pipeline(
135+
batch_id="batch-pipeline",
136+
task_uuids=["task-1", "task-2"],
137+
email_service_type="outlook",
138+
proxy=None,
139+
email_service_config=None,
140+
email_service_id=None,
141+
interval_min=0,
142+
interval_max=0,
143+
concurrency=1,
144+
registration_type="child",
145+
)
146+
)
147+
148+
try:
149+
assert captured == [("task-1", 101), ("task-2", 202)]
150+
finally:
151+
registration.batch_tasks.pop("batch-pipeline", None)

0 commit comments

Comments
 (0)