fix(api): staff-stats评审修复(GROUP BY显式列防ONLY_FULL_GROUP_BY/金额千分位/detail死参数/2边缘测试)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -313,3 +313,84 @@ async def test_detail_unknown_kind_422(make_client):
|
||||
r = await client.get("/api/staff-stats",
|
||||
params={"wechat_id": OWNER, "kind": "bogus"})
|
||||
assert r.status_code == 422
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 边缘口径
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
async def test_amount_thousands_separator(make_client):
|
||||
"""金额千分位:``¥1,000.50`` → 1000.5(不能截成 1);feedesc 同口径。"""
|
||||
|
||||
async def seed(s: AsyncSession) -> None:
|
||||
s.add(WechatAccount(wx_id=OWNER, user_id=UID))
|
||||
s.add(Message(msg_id="m_plain", channel="wechat", owner_account_id=OWNER,
|
||||
talker_id="c1", timestamp=IN, type="transfer",
|
||||
text="发送转账:¥1,000.50"))
|
||||
s.add(Message(msg_id="m_xml", channel="wechat", owner_account_id=OWNER,
|
||||
talker_id="c1", timestamp=IN + 1, type="red_packet",
|
||||
text="<wcpayinfo><feedesc>¥2,000.00</feedesc></wcpayinfo>"))
|
||||
await s.commit()
|
||||
|
||||
client = await make_client(seed)
|
||||
r = await client.get(
|
||||
"/api/staff-stats",
|
||||
params={"wechat_id": OWNER, "kind": "red_transfer",
|
||||
"start": WIN_START, "end": WIN_END},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
by = {row["msg_id"]: row["amount"] for row in r.json()["rows"]}
|
||||
assert by["m_plain"] == 1000.5
|
||||
assert by["m_xml"] == 2000.0
|
||||
|
||||
|
||||
async def test_voice_calls_account_without_user_id(make_client):
|
||||
"""账号存在但 user_id 为 NULL(未同步):voice_calls 计 0、明细空,
|
||||
而非把 user_id IS NULL 的通话误归属(防跨员工串数)。"""
|
||||
|
||||
async def seed(s: AsyncSession) -> None:
|
||||
s.add(WechatAccount(wx_id=OWNER, user_id=None))
|
||||
s.add(PhoneCall(call_id="pc_null", channel="voice_call", user_id=None,
|
||||
start_call_time=IN, call_number="c1"))
|
||||
await s.commit()
|
||||
|
||||
client = await make_client(seed)
|
||||
r_count = await client.get(
|
||||
"/api/staff-stats",
|
||||
params={"wechat_id": OWNER, "start": WIN_START, "end": WIN_END},
|
||||
)
|
||||
r_detail = await client.get(
|
||||
"/api/staff-stats",
|
||||
params={"wechat_id": OWNER, "kind": "voice_calls",
|
||||
"start": WIN_START, "end": WIN_END},
|
||||
)
|
||||
assert r_count.status_code == r_detail.status_code == 200
|
||||
assert r_count.json()["voice_calls"] == 0
|
||||
assert r_detail.json()["rows"] == []
|
||||
|
||||
|
||||
async def test_new_customers_fallback_first_msg_before_window(make_client):
|
||||
"""add_time NULL 兜底排除项:首条消息在窗前 → 不计(窗后/窗内才计)。"""
|
||||
|
||||
async def seed(s: AsyncSession) -> None:
|
||||
s.add(WechatAccount(wx_id=OWNER, user_id=UID))
|
||||
s.add(Customer(friend_id="c_early", channel="wechat", owner_account_id=OWNER,
|
||||
add_time=None))
|
||||
s.add(Message(msg_id="m_early", channel="wechat", owner_account_id=OWNER,
|
||||
talker_id="c_early", timestamp=WIN_START - 1))
|
||||
await s.commit()
|
||||
|
||||
client = await make_client(seed)
|
||||
r_count = await client.get(
|
||||
"/api/staff-stats",
|
||||
params={"wechat_id": OWNER, "start": WIN_START, "end": WIN_END},
|
||||
)
|
||||
r_detail = await client.get(
|
||||
"/api/staff-stats",
|
||||
params={"wechat_id": OWNER, "kind": "new_customers",
|
||||
"start": WIN_START, "end": WIN_END},
|
||||
)
|
||||
assert r_count.status_code == r_detail.status_code == 200
|
||||
assert r_count.json()["new_customers"] == 0
|
||||
assert r_detail.json()["rows"] == []
|
||||
|
||||
Reference in New Issue
Block a user