fix(analytics): 环比同期对齐口径+趋势窗校验+异常新员工明细
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
"""A1 总览指标:卡片/首沟通口径/环比(仅周月)/异常警示。"""
|
||||
"""A1 总览指标:卡片/首沟通口径/环比同期对齐(仅周月)/异常警示。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from app.analytics import metrics
|
||||
from app.db.models import (
|
||||
MsgDailyStats,
|
||||
@@ -118,3 +120,87 @@ async def test_anomaly_fallback_multi_account(make_client, monkeypatch):
|
||||
assert staff["u1"]["prev7_mean"] == 100.0
|
||||
|
||||
await make_client(seed)
|
||||
|
||||
|
||||
def test_prev_scope_days_aligned():
|
||||
"""环比窗=同期对齐(非滚动紧邻):周各日-7天;月=上月同日短月钳月末。"""
|
||||
week = metrics.scope_day_keys("week", 20260902) # 周三:本周一..三
|
||||
assert week == [20260831, 20260901, 20260902]
|
||||
# 上周周一..上周三(各日-7)——不是跨周末的滚动紧邻窗
|
||||
assert metrics._prev_scope_days("week", week) == [20260824, 20260825, 20260826]
|
||||
month = metrics.scope_day_keys("month", 20260902)
|
||||
assert month == [20260901, 20260902]
|
||||
assert metrics._prev_scope_days("month", month) == [20260801, 20260802]
|
||||
# 月末短月钳制:2026 非闰年 3/31→2/28;闰年 2024 3/31→2/29;跨年 1/1→上年 12/1
|
||||
assert metrics._prev_scope_days("month", [20260331]) == [20260228]
|
||||
assert metrics._prev_scope_days("month", [20240331]) == [20240229]
|
||||
assert metrics._prev_scope_days("month", [20260101]) == [20251201]
|
||||
|
||||
|
||||
async def test_overview_trend_days_validation(make_client, monkeypatch):
|
||||
"""trend_days 越界(1..90 外)直接 ValueError,不静默出奇怪窗口。"""
|
||||
monkeypatch.setattr(metrics, "today_key", lambda: _TODAY)
|
||||
async def seed(session):
|
||||
with pytest.raises(ValueError, match="trend_days"):
|
||||
await metrics.overview(session, channel="wechat", scope="today",
|
||||
trend_days=0)
|
||||
with pytest.raises(ValueError, match="trend_days"):
|
||||
await metrics.overview(session, channel="wechat", scope="today",
|
||||
trend_days=91)
|
||||
|
||||
await make_client(seed)
|
||||
|
||||
|
||||
async def test_week_delta_none_when_prev_week_empty(make_client, monkeypatch):
|
||||
"""上周边无数据(prev_total=0)→ delta=None,不除零不误报。"""
|
||||
monkeypatch.setattr(metrics, "today_key", lambda: _TODAY)
|
||||
async def seed(session):
|
||||
session.add(MsgDailyStats(channel="wechat", owner_account_id="wx1",
|
||||
peer_kind="friend", peer_id="t1", day=20260904,
|
||||
send_cnt=5, recv_cnt=0, last_ts=1788500000000,
|
||||
last_is_send=True))
|
||||
await session.commit()
|
||||
out = await metrics.overview(session, channel="wechat", scope="week")
|
||||
assert out["cards"]["prev_msg_total"] == 0
|
||||
assert out["cards"]["msg_total_delta"] is None
|
||||
|
||||
await make_client(seed)
|
||||
|
||||
|
||||
async def test_anomaly_none_when_no_prev_data(make_client, monkeypatch):
|
||||
"""全员无前 7 日数据(totals_p<=0)→ 异常警示 None(无基数可算)。"""
|
||||
monkeypatch.setattr(metrics, "today_key", lambda: _TODAY)
|
||||
async def seed(session):
|
||||
session.add(StatDailyStaff(day=20260903, channel="wechat", user_id="u1",
|
||||
msg_sent=100, msg_recv=0,
|
||||
active_friends=1, active_rooms=0))
|
||||
await session.commit()
|
||||
assert await metrics.anomaly(session, channel="wechat") is None
|
||||
|
||||
await make_client(seed)
|
||||
|
||||
|
||||
async def test_anomaly_new_staff_detail_and_sort(make_client, monkeypatch):
|
||||
"""新员工(前7日无量、昨日>0)进明细带 new 标记;明细按昨日量降序。"""
|
||||
monkeypatch.setattr(metrics, "today_key", lambda: _TODAY)
|
||||
async def seed(session):
|
||||
for day, sent in [(20260827, 100), (20260828, 100), (20260829, 100),
|
||||
(20260830, 100), (20260831, 100), (20260901, 100),
|
||||
(20260902, 100), (20260903, 250)]:
|
||||
session.add(StatDailyStaff(day=day, channel="wechat", user_id="u1",
|
||||
msg_sent=sent, msg_recv=0,
|
||||
active_friends=1, active_rooms=0))
|
||||
# u2 昨日新出现(前 7 日无量)——修复前从明细里消失
|
||||
session.add(StatDailyStaff(day=20260903, channel="wechat", user_id="u2",
|
||||
msg_sent=80, msg_recv=0,
|
||||
active_friends=1, active_rooms=0))
|
||||
await session.commit()
|
||||
a = await metrics.anomaly(session, channel="wechat")
|
||||
assert a is not None
|
||||
assert [s["user_id"] for s in a["staff"]] == ["u1", "u2"] # 降序:250 > 80
|
||||
staff = {s["user_id"]: s for s in a["staff"]}
|
||||
assert staff["u2"]["new"] is True
|
||||
assert staff["u2"]["yesterday"] == 80
|
||||
assert "new" not in staff["u1"] # 老员工不带标记
|
||||
|
||||
await make_client(seed)
|
||||
|
||||
Reference in New Issue
Block a user