fix(calls+scraper): 语音群scope服务端过滤+对话发现缺口补全+抓取韧性三连修

- /api/calls 加 scope=friend|group(@chatroom后缀判群,与isRoom实测等价120=120),CallList去掉「全部」tab改服务端查
- 对话发现:全部customer pair ∪ 语音pair(weChatId,talker),未扫过的排最前——修复纯语音关系客户聊天记录永远空(35%语音客户pair从未扫过)
- voice-calls改浏览器直发(isRoom=0/1双趟+WA callSource=4,拦截方式已两次失效)
- 断连恢复重试30×2min(云客约3h一次断连,已实战扛过多次)
- WA messages upsert改整列表直调(_upsert_all逐条传dict被迭代成key字符串→TypeError)
- 405测试绿含scope新增3个

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
浅念
2026-08-19 16:48:22 +08:00
parent f9ed7dd821
commit ab5a918a3b
5 changed files with 293 additions and 80 deletions
+56
View File
@@ -291,3 +291,59 @@ async def test_record_media_hash_serializes(make_client):
assert by_id["c1"]["record_media_hash"] is None
assert by_id["c2"]["record_media_hash"] is None
assert by_id["c3"]["record_media_hash"] is None
# ---------------------------------------------------------------------------
# scope filter (voice-call 好友/群 server-side filter, 2026-08-18)
# ---------------------------------------------------------------------------
async def _seed_voice_scopes(session: AsyncSession) -> None:
"""Friend + group voice calls (group peer id ends '@chatroom')."""
session.add_all(
[
PhoneCall(
call_id="v_friend",
channel="voice_call",
user_id="u2",
user_id_name="Bob",
start_call_time=1000,
call_number="q290059956",
),
PhoneCall(
call_id="v_group",
channel="voice_call",
user_id="u2",
user_id_name="Bob",
start_call_time=2000,
call_number="45877925713@chatroom",
),
]
)
await session.commit()
async def test_scope_group_returns_only_chatroom_calls(make_client):
"""scope=group -> only '@chatroom' rows, total reflects the filter."""
client = await make_client(_seed_voice_scopes)
r = await client.get("/api/calls", params={"channel": "voice_call", "scope": "group"})
assert r.status_code == 200
body = r.json()
assert body["total"] == 1
assert [it["call_id"] for it in body["items"]] == ["v_group"]
async def test_scope_friend_excludes_chatroom_calls(make_client):
"""scope=friend -> only non-'@chatroom' rows."""
client = await make_client(_seed_voice_scopes)
r = await client.get("/api/calls", params={"channel": "voice_call", "scope": "friend"})
assert r.status_code == 200
body = r.json()
assert body["total"] == 1
assert [it["call_id"] for it in body["items"]] == ["v_friend"]
async def test_scope_invalid_value_rejected(make_client):
"""scope is Literal['friend','group'] — anything else is a 422."""
client = await make_client(_seed_voice_scopes)
r = await client.get("/api/calls", params={"scope": "bogus"})
assert r.status_code == 422