feat(pool): 号池额度主动探测、封禁自动清除、调度硬优先级与前端重构

- 新增 PoolQuotaProbeScheduler,按 probing_interval_minutes 主动探测静默 Key 额度
- pool_advanced 增加 probing_enabled / auto_remove_banned_keys 配置项
- error_handler 和 quota_service 支持封禁 Key 自动删除及缓存清理
- multi_score 策略从加权混合重构为硬优先级排序,引入 mutex_group 互斥组
- 指纹注入从 handler 层下移至 ClaudeCode envelope 层
- OAuth 批量导入支持 concurrency 并发参数
- 前端号池管理拆分高级设置/账号批量/代理设置为独立组件
- 号池总览接口精简,仅返回已启用调度的 Provider
This commit is contained in:
fawney19
2026-03-05 15:15:26 +08:00
parent fdb50a065b
commit b1be413dc0
29 changed files with 3098 additions and 852 deletions

View File

@@ -0,0 +1,65 @@
from __future__ import annotations
from types import SimpleNamespace
from typing import Any, cast
from src.services.orchestration.error_handler import ErrorHandlerService
class _FakeDB:
def __init__(self) -> None:
self.deleted: list[object] = []
self.commit_count = 0
def delete(self, obj: object) -> None:
self.deleted.append(obj)
def commit(self) -> None:
self.commit_count += 1
def _build_key() -> SimpleNamespace:
return SimpleNamespace(
id="k1",
provider_id="p1",
name="k1-name",
auth_type="oauth",
auth_config=None,
oauth_invalid_at=None,
oauth_invalid_reason=None,
is_active=True,
)
def test_mark_oauth_key_blocked_auto_remove_enabled(monkeypatch: Any) -> None:
db = _FakeDB()
service = ErrorHandlerService(db=cast(Any, db))
key = _build_key()
provider = SimpleNamespace(config={"pool_advanced": {"auto_remove_banned_keys": True}})
monkeypatch.setattr(
ErrorHandlerService,
"_schedule_auto_cleanup_after_delete",
staticmethod(lambda **kwargs: None),
)
service._mark_oauth_key_blocked(cast(Any, key), "req-1", provider=cast(Any, provider))
assert db.commit_count == 1
assert db.deleted == [key]
assert key.is_active is False
assert str(key.oauth_invalid_reason).startswith("[ACCOUNT_BLOCK] ")
def test_mark_oauth_key_blocked_auto_remove_disabled() -> None:
db = _FakeDB()
service = ErrorHandlerService(db=cast(Any, db))
key = _build_key()
provider = SimpleNamespace(config={"pool_advanced": {"auto_remove_banned_keys": False}})
service._mark_oauth_key_blocked(cast(Any, key), "req-1", provider=cast(Any, provider))
assert db.commit_count == 1
assert db.deleted == []
assert key.is_active is False
assert str(key.oauth_invalid_reason).startswith("[ACCOUNT_BLOCK] ")