fix(scraper): voice body 加 departmentId/isRoom/时间窗 + sync 解析 root department_id
实测 queryWeChatVoiceList 必须带 callSource+departmentId+isRoom+startTime+endTime, 否则 200 但 data 空(0 数据)。WA callSource=4 / 微信 callSource=0,宽窗抓全量。 sync_*_voice_calls 照 sync_wechat_accounts 模式解析 _find_root_department_id。 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -488,21 +488,54 @@ async def sync_wechat_groups(
|
||||
|
||||
|
||||
async def sync_wechat_voice_calls(
|
||||
session: AsyncSession, client: YunkeClient
|
||||
session: AsyncSession,
|
||||
client: YunkeClient,
|
||||
*,
|
||||
department_id: str | None = None,
|
||||
) -> SyncResult:
|
||||
"""Full re-scrape of all WeChat voice-call records and idempotent upsert."""
|
||||
"""Full re-scrape of all WeChat voice-call records and idempotent upsert.
|
||||
|
||||
``department_id`` is the company-root dept id REQUIRED by the
|
||||
``queryWeChatVoiceList`` form body — without it (and the accompanying
|
||||
``callSource`` / ``isRoom`` / ``startTime`` / ``endTime`` filters) the
|
||||
endpoint returns 200 with an EMPTY ``data.data`` array (0 rows; live
|
||||
diagnosis 2026-08-07). When omitted, the orchestrator resolves it from
|
||||
the mirrored ``Department`` table via :func:`_find_root_department_id`
|
||||
— which is why :func:`sync_departments` must run first. If no root can
|
||||
be resolved, the scrape is SKIPPED with a logged warning (returns a
|
||||
``status='skipped'`` :class:`SyncResult` and records no ``SyncRun``) so
|
||||
a missing org tree does not abort a multi-entity batch.
|
||||
"""
|
||||
resolved = department_id
|
||||
if resolved is None:
|
||||
resolved = await _find_root_department_id(session)
|
||||
if not resolved:
|
||||
logger.warning(
|
||||
"sync_wechat_voice_calls: no root department_id resolved"
|
||||
" (sync departments first?); skipping queryWeChatVoiceList scrape"
|
||||
)
|
||||
return SyncResult(
|
||||
entity=ENTITY_WECHAT_VOICE_CALL,
|
||||
status="skipped",
|
||||
fetched=0,
|
||||
upserted=0,
|
||||
error="no root department_id available",
|
||||
)
|
||||
return await _sync_full_rescrape(
|
||||
session,
|
||||
entity_type=ENTITY_WECHAT_VOICE_CALL,
|
||||
channel=None,
|
||||
scope_id=None,
|
||||
scrape=lambda: scrape_wechat_voice_calls(client),
|
||||
scrape=lambda: scrape_wechat_voice_calls(client, department_id=resolved),
|
||||
upsert=upsert_voice_call,
|
||||
)
|
||||
|
||||
|
||||
async def sync_wa_voice_calls(
|
||||
session: AsyncSession, client: YunkeClient
|
||||
session: AsyncSession,
|
||||
client: YunkeClient,
|
||||
*,
|
||||
department_id: str | None = None,
|
||||
) -> SyncResult:
|
||||
"""Full re-scrape of all WhatsApp voice-call records and idempotent upsert.
|
||||
|
||||
@@ -513,13 +546,36 @@ async def sync_wa_voice_calls(
|
||||
twice -> one refreshed row). The ``channel="wa_voice_call"`` value the
|
||||
scraper stamps distinguishes WA rows from WeChat rows in the shared
|
||||
``PhoneCall`` table; the SyncRun row records ``entity_type='wa_voice_call'``.
|
||||
|
||||
``department_id`` resolution mirrors :func:`sync_wechat_accounts`: the
|
||||
``queryWeChatVoiceList`` form body REQUIRES the company-root
|
||||
``departmentId`` (live diagnosis 2026-08-07 — without it the endpoint
|
||||
returns 200 + empty ``data.data``, 0 rows). When omitted, the
|
||||
orchestrator resolves it from the mirrored ``Department`` table; if no
|
||||
root can be resolved, the scrape is SKIPPED with a logged warning and a
|
||||
``status='skipped'`` :class:`SyncResult`.
|
||||
"""
|
||||
resolved = department_id
|
||||
if resolved is None:
|
||||
resolved = await _find_root_department_id(session)
|
||||
if not resolved:
|
||||
logger.warning(
|
||||
"sync_wa_voice_calls: no root department_id resolved"
|
||||
" (sync departments first?); skipping queryWeChatVoiceList scrape"
|
||||
)
|
||||
return SyncResult(
|
||||
entity=ENTITY_WA_VOICE_CALL,
|
||||
status="skipped",
|
||||
fetched=0,
|
||||
upserted=0,
|
||||
error="no root department_id available",
|
||||
)
|
||||
return await _sync_full_rescrape(
|
||||
session,
|
||||
entity_type=ENTITY_WA_VOICE_CALL,
|
||||
channel=None,
|
||||
scope_id=None,
|
||||
scrape=lambda: scrape_wa_voice_calls(client),
|
||||
scrape=lambda: scrape_wa_voice_calls(client, department_id=resolved),
|
||||
upsert=upsert_voice_call,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user