77f772f47c
## 背景 - 比价记录页和领券记录页前端已经只拉当前页,并直接展示后端 summary。 - 原后端仍会将筛选区间内的全部耗时值取回 Python 计算分位数,生产数据量增大后会放大数据库读取和应用内存开销。 ## 修改内容 - 比价记录:成功耗时 P5/P50/P95/P99、平均耗时以及中途退出耗时 P5/P50/P95 改用 PostgreSQL percentile_cont/AVG 聚合。 - 领券记录:发起数、完成数、平均耗时和完成耗时 P5/P50/P95/P99 合并为一条 PostgreSQL 聚合查询。 - 日期、用户、环境、状态、店铺和商品等筛选条件继续与列表共用,统计口径不变。 - SQLite 不支持 percentile_cont,仅在本地和测试环境回退读取耗时单列;不加载完整业务记录。 - API 字段与前端展示保持不变,无需前端改动。 ## 验证 - 比价/领券及关联广告收益、点位、按券统计测试:26 passed。 - 本次涉及文件 ruff 检查通过。 - PostgreSQL SQL 编译测试确认使用 ordered-set percentile_cont 聚合。 - 全量测试:497 passed;8 个现有失败集中在邀请奖励、提现档位和代理转发等无关模块。 --------- Co-authored-by: unknown <798648091@qq.com> Reviewed-on: #161 Co-authored-by: linkeyu <linkeyu@wonderable.ai> Co-committed-by: linkeyu <linkeyu@wonderable.ai>
82 lines
2.8 KiB
Python
82 lines
2.8 KiB
Python
"""比价记录页后端分页与概览聚合。"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import UTC, date, datetime
|
|
|
|
import pytest
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
from app.admin.repositories import queries
|
|
from app.db.session import SessionLocal
|
|
from app.models.comparison import ComparisonRecord
|
|
|
|
|
|
def test_postgresql_duration_summary_uses_ordered_set_aggregates() -> None:
|
|
stmt = queries._comparison_duration_aggregate_stmt(
|
|
[], "success", (0.05, 0.5, 0.95, 0.99)
|
|
)
|
|
sql = str(
|
|
stmt.compile(
|
|
dialect=postgresql.dialect(),
|
|
compile_kwargs={"literal_binds": True},
|
|
)
|
|
)
|
|
|
|
assert sql.count("percentile_cont") == 4
|
|
assert "comparison_record.status = 'success'" in sql
|
|
|
|
|
|
def test_summary_uses_only_success_durations_and_filters_beijing_date() -> None:
|
|
db = SessionLocal()
|
|
try:
|
|
rows = [
|
|
("summary-success-a", "success", 1000, 1.0, 100),
|
|
("summary-success-b", "success", 3000, 2.0, 0),
|
|
("summary-failed", "failed", 100_000, 3.0, 0),
|
|
("summary-cancelled", "cancelled", 5000, 4.0, 0),
|
|
]
|
|
for trace_id, status, total_ms, cost, saved in rows:
|
|
db.add(ComparisonRecord(
|
|
trace_id=trace_id,
|
|
status=status,
|
|
total_ms=total_ms,
|
|
llm_cost_yuan=cost,
|
|
saved_amount_cents=saved,
|
|
created_at=datetime(2038, 1, 15, 12, tzinfo=UTC),
|
|
))
|
|
db.add(ComparisonRecord(
|
|
trace_id="summary-outside-day",
|
|
status="success",
|
|
total_ms=999_999,
|
|
created_at=datetime(2038, 1, 16, 16, tzinfo=UTC),
|
|
))
|
|
db.flush()
|
|
|
|
summary = queries.comparison_records_summary(
|
|
db, date_from=date(2038, 1, 15), date_to=date(2038, 1, 15)
|
|
)
|
|
|
|
assert summary["started"] == 4
|
|
assert summary["completed"] == 3
|
|
assert summary["success"] == 2
|
|
assert summary["success_rate"] == pytest.approx(2 / 3)
|
|
assert summary["avg_token_cost"] == pytest.approx(2.5)
|
|
assert summary["lower_price_rate"] == 0.5
|
|
assert summary["avg_duration_ms"] == 2000
|
|
assert summary["p5_duration_ms"] == 1100
|
|
assert summary["p50_duration_ms"] == 2000
|
|
assert summary["p95_duration_ms"] == 2900
|
|
assert summary["p99_duration_ms"] == 2980
|
|
assert summary["cancelled"] == 1
|
|
assert summary["cancelled_rate"] == 0.25
|
|
assert summary["cancelled_p50_ms"] == 5000
|
|
|
|
items, _next_cursor, total = queries.list_comparison_records(
|
|
db, date_from=date(2038, 1, 15), date_to=date(2038, 1, 15), limit=20
|
|
)
|
|
assert total == 4
|
|
assert {item.trace_id for item in items} == {row[0] for row in rows}
|
|
finally:
|
|
db.rollback()
|
|
db.close()
|