refactor(api): staff-chat-stats 确定性排序 + last_is_send=None 分支测试 + 类型标注
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -36,7 +36,7 @@ def _private_window(msg, owner: str, start: int, end: int):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
async def _aggregate(session: AsyncSession, owner: str, start: int, end: int):
|
async def _aggregate(session: AsyncSession, owner: str, start: int, end: int) -> tuple[list, dict[str, bool | None]]:
|
||||||
"""返回 [(talker_id, send, recv, interaction_days, last_time)] + last_is_send map。"""
|
"""返回 [(talker_id, send, recv, interaction_days, last_time)] + last_is_send map。"""
|
||||||
# 跨库日期提取:从 ms-epoch 取日期。镜像 repos.py 方言检测。
|
# 跨库日期提取:从 ms-epoch 取日期。镜像 repos.py 方言检测。
|
||||||
dialect = session.bind.name # 镜像 app/storage/repos.py(.name 等价 .dialect.name)
|
dialect = session.bind.name # 镜像 app/storage/repos.py(.name 等价 .dialect.name)
|
||||||
@@ -56,6 +56,7 @@ async def _aggregate(session: AsyncSession, owner: str, start: int, end: int):
|
|||||||
)
|
)
|
||||||
.where(*base)
|
.where(*base)
|
||||||
.group_by(Message.talker_id)
|
.group_by(Message.talker_id)
|
||||||
|
.order_by(Message.talker_id)
|
||||||
)
|
)
|
||||||
agg_rows = (await session.execute(agg_stmt)).all()
|
agg_rows = (await session.execute(agg_stmt)).all()
|
||||||
|
|
||||||
|
|||||||
@@ -135,3 +135,47 @@ async def test_default_window_returns_ok(make_client):
|
|||||||
r = await client.get("/api/staff-chat-stats", params={"owner_account_id": OWNER})
|
r = await client.get("/api/staff-chat-stats", params={"owner_account_id": OWNER})
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert "summary" in r.json() and "customers" in r.json()
|
assert "summary" in r.json() and "customers" in r.json()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_last_is_send_none_excluded_from_rates(make_client):
|
||||||
|
"""最后一条私聊 is_send IS NULL → last_is_send=None,计入 chat_count/
|
||||||
|
interaction_days,但被 ==1/==0 同时排除(两率都不计)。"""
|
||||||
|
async def seed(session: AsyncSession) -> None:
|
||||||
|
session.add(
|
||||||
|
Message(
|
||||||
|
msg_id="n1",
|
||||||
|
channel="wechat",
|
||||||
|
owner_account_id=OWNER,
|
||||||
|
talker_id="t_n",
|
||||||
|
room_id=None,
|
||||||
|
is_send=None,
|
||||||
|
timestamp=WIN_START,
|
||||||
|
type="text",
|
||||||
|
type_raw=1,
|
||||||
|
text="unknown-dir",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
client = await make_client(seed)
|
||||||
|
r = await client.get(
|
||||||
|
"/api/staff-chat-stats",
|
||||||
|
params={"owner_account_id": OWNER, "start": WIN_START, "end": WIN_END},
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
body = r.json()
|
||||||
|
customers = body["customers"]
|
||||||
|
assert len(customers) == 1
|
||||||
|
c = customers[0]
|
||||||
|
assert c["talker_id"] == "t_n"
|
||||||
|
assert c["last_is_send"] is None
|
||||||
|
assert c["send_count"] == 0
|
||||||
|
assert c["receive_count"] == 0
|
||||||
|
assert c["interaction_days"] == 1
|
||||||
|
assert body["summary"] == {
|
||||||
|
"chat_count": 1,
|
||||||
|
"effective_count": 0,
|
||||||
|
"follow_count": 0,
|
||||||
|
"last_reply_count": 0,
|
||||||
|
"no_reply_count": 0,
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user