From 2b062550247128f2a6bd1c171d0d8bb37f45db27 Mon Sep 17 00:00:00 2001 From: zzhyyyyy <2685922758@qq.com> Date: Mon, 29 Jun 2026 22:29:08 +0800 Subject: [PATCH] =?UTF-8?q?fix(ad-revenue):=20=E6=8A=A5=E8=A1=A8=E6=94=B6?= =?UTF-8?q?=E7=9B=8A=E6=8C=89=E6=BB=A1=E9=A2=9D=E9=92=B3=E9=A1=B6=20+=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20app=5Fenv=20=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 广告收益报表两处隐患修复(admin/repositories/ad_revenue.py + routers/ad_revenue.py): - 收益钳顶:单次展示收益原用裸 parse_ecpm_yuan、未钳上限;发奖侧 calculate_ad_reward_coin 已钳 AD_ECPM_MAX_FEN(¥500 CPM)。异常/伪造天价 eCPM 会让报表预估收益虚高任意大。改为 min(parse_ecpm_yuan, AD_ECPM_MAX_FEN/100)/1000,与发奖同口径。 - app_env 过滤:报表聚合原无 app_env 过滤,测试应用假 eCPM(如 ¥678 CPM)会进正式收益合计/平均。 新增 app_env 参数(repository + router Query),显式传 prod/test 才过滤;默认仍全部(不擅自改成 默认排除 test:本地 dev 库多为 test 会空、且属产品口径)。穿山甲后台收益列暂未联动此过滤。 Co-Authored-By: Claude Opus 4.8 --- app/admin/repositories/ad_revenue.py | 17 +++++++++++++++-- app/admin/routers/ad_revenue.py | 9 ++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/admin/repositories/ad_revenue.py b/app/admin/repositories/ad_revenue.py index 062fa75..ec6bd4a 100644 --- a/app/admin/repositories/ad_revenue.py +++ b/app/admin/repositories/ad_revenue.py @@ -87,6 +87,7 @@ def ad_revenue_report( user_id: int | None = None, ad_type: str | None = None, feed_scene: str | None = None, + app_env: str | None = None, granularity: str = "day", limit: int = 500, offset: int = 0, @@ -156,8 +157,12 @@ def ad_revenue_report( "has_impression": True, "impressions": 1, "ecpm": rec.ecpm_raw, - # 单次展示收益(元)= eCPM元 ÷ 1000(每千次→单次);与发奖同源解析,口径一致。 - "revenue_yuan": round(rewards.parse_ecpm_yuan(rec.ecpm_raw) / 1000.0, 6), + # 单次展示收益(元)= eCPM元 ÷ 1000(每千次→单次)。eCPM 先钳到 AD_ECPM_MAX_FEN(¥500 CPM) + # 再折收益,与发奖口径 [rewards.calculate_ad_reward_coin] 一致(2026-06-29 修:原裸 parse_ecpm_yuan + # 不钳,伪造/异常天价 eCPM 会把报表预估收益冲到任意大;金币侧已钳、收益侧漏钳)。 + "revenue_yuan": round( + min(rewards.parse_ecpm_yuan(rec.ecpm_raw), rewards.AD_ECPM_MAX_FEN / 100.0) / 1000.0, 6, + ), "adn": rec.adn, "slot_id": rec.slot_id, } @@ -213,6 +218,14 @@ def ad_revenue_report( if feed_scene is not None: events = [e for e in events if e.get("feed_scene") == feed_scene] + # app_env 过滤(2026-06-29 新增能力,修隐患:测试应用上报的假 eCPM 如 ¥678 CPM 会污染正式收益合计/平均): + # 显式传 "prod"/"test" 只看该环境;不传=全部(维持现状)。**不擅自把默认改成排除 test**——本地 dev 库多为 + # test 数据、默认排除会使本地报表空,且「正式报表是否含 test」属产品口径。建议前端报表页加 app_env 筛选器 + # (默认选 prod),或产品确认后再把默认改成排除 test。注:穿山甲后台收益列(total_pangle_*)暂未联动此过滤 + # (它是独立对照列,且 pangle 的 test 是真实小额、非客户端那种假值)。 + if app_env is not None: + events = [e for e in events if e.get("app_env") == app_env] + # 排序:time=按时间倒序(新→旧);ecpm=按 eCPM 数值倒序(eCPM 原值是字符串「分」,转数值排; # 纯发奖行用其发奖采用的 eCPM,缺失/非法计 0 排末尾)。 if sort == "ecpm": diff --git a/app/admin/routers/ad_revenue.py b/app/admin/routers/ad_revenue.py index e9d81a9..dc6129f 100644 --- a/app/admin/routers/ad_revenue.py +++ b/app/admin/routers/ad_revenue.py @@ -56,6 +56,13 @@ def get_ad_revenue_report( "全局筛选,同时影响明细 / 合计 / 趋势" ), ] = None, + app_env: Annotated[ + str | None, + Query( + description="prod(正式应用) / test(测试应用);不传=全部。" + "建议正式收益报表选 prod,避免测试应用的假 eCPM 污染收益合计/平均" + ), + ] = None, granularity: Annotated[ str, Query(description="day=按天 / hour=按小时(北京时间);区间>1 天建议用 day") ] = "day", @@ -75,7 +82,7 @@ def get_ad_revenue_report( result = ad_revenue.ad_revenue_report( db, date_from=d_from.isoformat(), date_to=d_to.isoformat(), - user_id=user_id, ad_type=ad_type, feed_scene=feed_scene, + user_id=user_id, ad_type=ad_type, feed_scene=feed_scene, app_env=app_env, granularity=granularity, limit=limit, offset=offset, sort=sort, ) return AdRevenueReportOut(