Files
yunke/yunke-server/tests/cassettes/talkerList_fixture.py
T
浅念 2a74d3888d feat: wechat group+member and talker scrapers
Add scrape_wechat_groups (POST /wechatweb/chatroom/getChatroomList) and
scrape_wechat_talkers (POST /wechatweb/wxFriends/talkerList), both reusing
the page-number paginator. Groups parse the semicolon-joined memberIds into
a member_ids_list so upsert_chat_group can mirror GroupMember rows
(composite PK channel/room_id/member_id) idempotently alongside the
ChatGroup row. Talkers are a sync-planning input (member_count>0 marks a
group conversation); no dedicated table.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-01 13:54:16 +08:00

113 lines
4.0 KiB
Python

"""Fixture payloads for ``POST /wechatweb/wxFriends/talkerList`` (M2).
These records mirror the real field names captured from the live CRM (see the
data dictionary §2 会话列表 talker: ``friendWechatId, nickName, remarkName,
messageCount, lastChatTime, headUrl, memberCount, analyseResult``). The
endpoint is **per-staff-wechat**: the request body carries the ``wechatId`` of
the owning staff account, and the response lists that account's recent
conversations (direct chats AND group chats — ``memberCount > 0`` ⇒ group).
The envelope carries the row list under ``data.page`` with a boolean
``data.hasMore`` flag (no ``total`` field is documented for this endpoint)::
{"message":"调用成功","code":"10000","success":true,"traceId":"...",
"data":{"page":[ {<talker record>}, ... ], "hasMore":..}}
Because the page-number paginator reuses the empty-page stop signal, the
cassette ships the records across two pages plus a terminating empty page:
* page 1 -> 2 of the 3 fixture talks, ``hasMore=true``
* page 2 -> the remaining 1 talk, ``hasMore=false``
* (page 3 -> empty ``page`` array, served inline by the test)
Records exercise both the direct-message path (``memberCount == 0``) and the
group-chat path (``memberCount > 0``) so the orchestrator can distinguish them.
"""
# The module name uses camelCase deliberately to mirror the upstream endpoint
# name (``talkerList``); the M2 spec mandates this exact filename.
# ruff: noqa: N999
from __future__ import annotations
from typing import Any
# The owning staff wechat for this talker stream (the ``wechatId`` request
# field). All records below belong to this account's conversation list.
OWNER_WECHAT_ID = "wxid_staff_alice01"
# ---------------------------------------------------------------------------
# Capture-shaped talker (conversation) records (3 total across 2 pages).
# ---------------------------------------------------------------------------
TALKER_RECORDS: list[dict[str, Any]] = [
{
# Direct message (1:1 friend). memberCount == 0.
"friendWechatId": "wxid_friend_0001",
"nickName": "Alice Wang",
"remarkName": "Alice-VIP",
"messageCount": 46,
"lastChatTime": 1700000000123,
"headUrl": "https://wx.qlogo.cn/avatar_alice.png",
"memberCount": 0,
"displayName": "",
"analyseResult": "",
},
{
# Group chat. memberCount > 0 ⇒ the friendWechatId is a @chatroom id.
"friendWechatId": "12345678901@chatroom",
"nickName": "VIP客户交流群",
"remarkName": "",
"messageCount": 4521,
"lastChatTime": 1700000000456,
"headUrl": "https://wx.qlogo.cn/room_12345678901.png",
"memberCount": 3,
"displayName": "Alice (group)",
"analyseResult": "",
},
{
# Another direct message — a less active contact.
"friendWechatId": "wxid_friend_0009",
"nickName": "Quiet Lead",
"remarkName": "",
"messageCount": 2,
"lastChatTime": 1700000000789,
"headUrl": "https://wx.qlogo.cn/avatar_quiet.png",
"memberCount": 0,
"displayName": "",
"analyseResult": "",
},
]
# Default page size for the fixture (and the scraper). 3 records fill exactly
# 2 pages of size 2 plus a terminating empty page.
TOTAL = len(TALKER_RECORDS)
DEFAULT_PAGE_SIZE = 2
def make_talker_list_response(
records: list[dict[str, Any]],
*,
page_num: int = 1,
page_size: int = DEFAULT_PAGE_SIZE,
has_more: bool = False,
trace_id: str | None = None,
) -> dict[str, Any]:
"""Build a capture-shaped ``talkerList`` response envelope.
Shape (row list under ``data.page``, boolean ``data.hasMore``)::
{"message":"调用成功","code":"10000","success":true,"traceId":"...",
"data":{"page":[...], "hasMore":..}}
"""
return {
"message": "调用成功",
"code": "10000",
"success": True,
"traceId": trace_id or f"trace-{page_num}",
"data": {
"page": list(records),
"hasMore": has_more,
},
}