feat: 服务层配置接线——email/push/rate_limit/AI凭据/行为开关/映射表/种子键

Task 9: 服务层对全局 settings 的业务引用清零,每个 DEFAULT_CONFIG 组
都有真实消费方。resolve_*_settings(email/push/ai)以空槽回退 env
(平台默认层);contact 行为开关/种子白名单/国名扩表/限流上限经
manager 的 runtime.config 接线。收口: lead_push session_factory 必填、
config.py 死代码清理、PromptProvider frozen、no_contact 视图直取、
AIService wall 参数定型 CompiledWall。403 测试全绿(389+14)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-12 21:37:48 +08:00
parent 8453d3cc56
commit 32b582aac8
15 changed files with 676 additions and 164 deletions
+12 -8
View File
@@ -335,19 +335,24 @@ def _mk_push_log(async_db, status="pending"):
def _run_push_case(async_db, monkeypatch, do_post, log_id):
"""注入 _do_post/_sleep/async_session 后驱动 _run_push,返回退避序列。"""
"""注入 _do_post/_sleep 后驱动 _run_push(Task 9: session_factory 直传,
不再借模块级 async_session——已删除),返回退避序列。"""
from app.engine.services import lead_push_service as svc
sleeps = []
async def fake_sleep(s):
sleeps.append(s)
monkeypatch.setattr(svc, "_do_post", do_post)
# Task 9: _do_post 多了 config_data 参数——适配层吸收,各用例 fake 保持单参
async def adapted_do_post(payload, config_data=None):
return await do_post(payload)
monkeypatch.setattr(svc, "_do_post", adapted_do_post)
monkeypatch.setattr(svc, "_sleep", fake_sleep)
monkeypatch.setattr(svc, "async_session", async_db)
async def run():
await svc._run_push(log_id, {"external_key": "ai-kefu-x", "event": "new"})
await svc._run_push(
log_id, {"external_key": "ai-kefu-x", "event": "new"}, async_db)
asyncio.run(run())
return sleeps
@@ -482,7 +487,7 @@ def test_fire_pending_creates_tasks(monkeypatch):
from app.engine.services import lead_push_service as svc
pushed = []
async def fake_run(log_id, payload, session_factory=None):
async def fake_run(log_id, payload, session_factory=None, config_data=None):
pushed.append((log_id, payload))
monkeypatch.setattr(svc, "_run_push", fake_run)
@@ -494,7 +499,7 @@ def test_fire_pending_creates_tasks(monkeypatch):
]
async def run():
svc.fire_pending_pushes(logs)
svc.fire_pending_pushes(logs, None)
await asyncio.sleep(0) # 同 loop 内让 create_task 跑一轮
asyncio.run(run())
assert pushed == [("id-pending", {"event": "new"})]
@@ -503,7 +508,6 @@ def test_fire_pending_creates_tasks(monkeypatch):
def test_sweep_stale_pending(async_db, monkeypatch):
from datetime import datetime, timezone
from app.engine.services import lead_push_service as svc
monkeypatch.setattr(svc, "async_session", async_db)
_seed_push_ctx(async_db)
async def run():
@@ -524,7 +528,7 @@ def test_sweep_stale_pending(async_db, monkeypatch):
updated_at=datetime(2020, 1, 1, tzinfo=timezone.utc))
db.add_all([stale, fresh, done])
await db.commit()
n = await svc.sweep_stale_pending(stale_minutes=30)
n = await svc.sweep_stale_pending(async_db, stale_minutes=30)
assert n == 1
async with async_db() as db:
logs = list((await db.execute(select(LeadPushLog))).scalars())