# 修复群抓取:friend_list/es + friendTypeYunke=2 > 日期:2026-08-10|分支:`feat/yunke-data-mirror`|scraper fix(后端) > 云客真机核实(2026-08-10 Playwright):群数据 API = `POST /wechatweb/wxFriends/friend_list/es` body `{friendTypeYunke:"2", wechatId, ymdStart, ymdEnd, pageNum, pageSize, salesname}`——与好友共用端点,仅 friendTypeYunke 不同(1=好友,2=群)。 ## 根因 `scrape_wechat_groups` 用 `POST /wechatweb/chatroom/getChatroomList` + body `{pageNum,pageSize}`——**端点错 + 缺 friendTypeYunke + 缺 wechatId**。云客返回 0(smoke.db chat_group 0 行)。 ## 修复(spec + plan) ### 改 `app/scraper/wechat.py` 1. **`scrape_wechat_groups`**:改用 `FRIEND_LIST_PATH`(`/wechatweb/wxFriends/friend_list/es`)+ `_parse_friend_total` + `_parse_friend_rows`(复用好友的分页/解析,同端点同信封)+ 新 body builder `_build_group_body`(加 `friendTypeYunke:"2"`)。 2. **`_build_group_body(page_num, page_size)`**:返回 `{pageNum, pageSize, friendTypeYunke: "2"}`(最小 body;与好友 body builder 同模式 + friendTypeYunke;若测试返回 0 再加 ymdStart/ymdEnd/salesname)。 3. **`_normalize_chat_group`**:映射 friend_list/es 字段(替代 getChatroomList 字段): | ChatGroup 字段 | friend_list/es 来源 | |---|---| | room_id | `friendWxId` (e.g. `51983915836@chatroom`) | | room_name | `friendWxNickname` | | owner_account_id | `wxId` (staff account that has this group) | | owner_account_nickname | `ownerName` | | member_count | `friendNum` | | head_image_url | `friendHeadImageUrl` | | message_count | `messageCount` | | own_message_count | `sendNum` | | last_chat_time | `lastChatTime` (ms-epoch) | | owner_user_id | `userId` | | owner_user_phone | `phone` or `wechatPhone` | | version | `version` | | member_ids / member_ids_list | None / [](friend_list/es 不含 memberIds;GroupMember 暂不创建) | | raw_json | 整条 record | 4. **删除旧 getChatroomList 代码**:`CHATROOM_LIST_PATH`、`_parse_chatroom_total`、`_parse_chatroom_rows`、`_build_chatroom_body`。 5. `sync_wechat_groups`(orchestrator)不改(仍调 `scrape_wechat_groups`;签名不变)。 ### 测试 ```bash cd yunke-server && DB_URL="sqlite+aiosqlite:///./smoke.db" .venv/Scripts/python.exe -m app.sync.cli sync --entity groups ``` Expected: smoke.db `chat_group` > 0。若 0(body 不全),给 `_build_group_body` 加 `ymdStart:"0000-00-00"`/`ymdEnd:""`/`salesname:""`/按员工 `wechatId` 遍历。 ### 验收 - `pytest -q` 全绿(不破坏现有测试;group 相关测试如 `test_scraper_wechat` 或 `test_sync` 需更新 mock 响应——若有)。 - smoke.db `chat_group` > 0(实跑后)。 - `GET /api/groups` 返回群数据(GroupList 有数据)。 ### 风险 - **friend_list/es 无 wechatId 可能返回 0**(云客页面带 wechatId)。测试确认;若 0,需按员工 wxId 遍历(类似 sync_wechat_accounts → per-account friend_list/es)。 - **GroupMember**:friend_list/es 不含 memberIds → GroupMember 表暂不填(群列表可用;成员详情后续)。 - **测试 mock 更新**:现有 group 测试若 mock getChatroomList 响应,需改 mock 为 friend_list/es 响应。