From a38170d20545c03197a8eefffd5e8b8f7774cf07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Wed, 9 Sep 2026 14:09:21 +0800 Subject: [PATCH] =?UTF-8?q?test(management):=20=E9=80=9A=E8=AF=9Dfollowup?= =?UTF-8?q?=E5=88=A4=E5=88=AB=E8=A1=A5=E5=BC=BA=E2=80=94=E2=80=94=E6=9C=AA?= =?UTF-8?q?=E6=8E=A5=E9=80=9A=E5=8F=8D=E4=BE=8B/=E6=B8=A0=E9=81=93?= =?UTF-8?q?=E6=94=B6=E7=AA=84=E6=AD=A3=E5=8F=8D=E4=BE=8B/=E4=BB=BB?= =?UTF-8?q?=E4=B8=80=E8=B4=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- yunke-server/app/storage/employee_metrics.py | 1 + yunke-server/tests/test_employee_metrics.py | 38 ++++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/yunke-server/app/storage/employee_metrics.py b/yunke-server/app/storage/employee_metrics.py index dee656d..0e66c23 100644 --- a/yunke-server/app/storage/employee_metrics.py +++ b/yunke-server/app/storage/employee_metrics.py @@ -44,6 +44,7 @@ async def _followup_exists( ) -> bool: """通话后 24h 内员工任一账号对该客户有 1:1 消息。 + 含客户发来的消息——口径=窗口内存在任一方向 1:1 消息。 按通话类型收窄消息渠道(Task0:voice_call→wechat/wa_voice_call→whatsapp, 1:1 通话可匹配率 100%,@chatroom 群通话天然不匹配 1:1 消息)。""" if not owner_accounts or not peer: diff --git a/yunke-server/tests/test_employee_metrics.py b/yunke-server/tests/test_employee_metrics.py index 4fdc2d8..ad7d891 100644 --- a/yunke-server/tests/test_employee_metrics.py +++ b/yunke-server/tests/test_employee_metrics.py @@ -16,6 +16,7 @@ from app.db.models import ( PhoneCall, Staff, WechatAccount, + WhatsappAccount, ) from app.storage.employee_metrics import recompute_call_days @@ -29,6 +30,10 @@ def _ts(*args) -> int: async def _seed_org(session): session.add(Staff(user_id="u1", name="张三", is_on_job=True)) session.add(WechatAccount(wx_id="wx1", user_id="u1")) + # 第二个微信号:验证 followup「任一账号」语义(owner-IN 而非单账号) + session.add(WechatAccount(wx_id="wx2", user_id="u1")) + # WA 账号:验证 wa_voice_call 渠道收窄正例 + session.add(WhatsappAccount(uid="wa1", user_id="u1")) await session.commit() @@ -41,19 +46,40 @@ async def test_call_daily_aggregates(make_session): s.add(PhoneCall(call_id="c2", channel="voice_call", user_id="u1", call_number="p2", start_call_time=_ts(2026, 9, 1, 15, 0), call_duration=0)) # 未接通 - # c1 后 2h 员工有跟进消息(多账号语义:u1 只有 wx1) - s.add(Message(msg_id="f1", channel="wechat", owner_account_id="wx1", + # c3:WA 语音,接通,窗口内有 WA 消息 → followup 正例(渠道收窄匹配) + s.add(PhoneCall(call_id="c3", channel="wa_voice_call", user_id="u1", + call_number="p3", start_call_time=_ts(2026, 9, 1, 10, 0), + call_duration=60)) + # c4:微信语音,接通,但窗口内只有 WA 消息 → followup 反例(渠道收窄) + s.add(PhoneCall(call_id="c4", channel="voice_call", user_id="u1", + call_number="p4", start_call_time=_ts(2026, 9, 1, 10, 0), + call_duration=90)) + # c1 后 2h 员工**另一微信号 wx2** 有跟进消息(任一账号语义:owner-IN) + s.add(Message(msg_id="f1", channel="wechat", owner_account_id="wx2", talker_id="p1", timestamp=_ts(2026, 9, 1, 12, 0), is_send=True, type="text", text="跟进")) + # c2(未接通)后 1h 有消息:即使任一方向也不算 followup(仅已接通判定) + s.add(Message(msg_id="f2", channel="wechat", owner_account_id="wx1", + talker_id="p2", timestamp=_ts(2026, 9, 1, 16, 0), + is_send=False, type="text", text="客户回拨?")) + # c3 后 2h WA 账号有消息 → 正例 + s.add(Message(msg_id="f3", channel="whatsapp", owner_account_id="wa1", + talker_id="p3", timestamp=_ts(2026, 9, 1, 12, 0), + is_send=True, type="text", text="wa 跟进")) + # c4(voice_call→wechat)窗口内只有 WA 消息 → 不匹配渠道,反例 + s.add(Message(msg_id="f4", channel="whatsapp", owner_account_id="wa1", + talker_id="p4", timestamp=_ts(2026, 9, 1, 12, 0), + is_send=True, type="text", text="错渠道")) await s.commit() n = await recompute_call_days(s, [20260901]) await s.commit() assert n == 1 row = (await s.execute(sa.select(EmployeeCallDaily))).scalar_one() - assert row.call_count == 2 - assert row.call_seconds == 120 # duration 单位=秒(Task0 已核实) - assert row.answered_call_count == 1 - assert row.call_followup_count == 1 + assert row.call_count == 4 + assert row.call_seconds == 270 # 120+0+60+90;duration 单位=秒(Task0 已核实) + assert row.answered_call_count == 3 # c1/c3/c4 + # c1(wx2 任一账号)+ c3(wa 渠道正例);c2 未接通守卫、c4 渠道收窄反例 + assert row.call_followup_count == 2 async def test_call_daily_ignores_unknown_staff(make_session):