@@ -9,7 +9,6 @@ import {
Card ,
Col ,
DatePicker ,
Descriptions ,
Divider ,
InputNumber ,
Modal ,
@@ -43,6 +42,8 @@ import type {
import UserAdRevenueDrawer from './UserAdRevenueDrawer' ;
const { RangePicker } = DatePicker ;
const DEFAULT_PAGE_SIZE = 50 ;
const ALL_FILTER_VALUE = '__all__' ;
// 广告类型标签
const TYPE_TAG : Record < string , { color : string ; label : string } > = {
@@ -81,20 +82,12 @@ const PLAYBACK_STATUS_TAG: Record<string, { color: string; label: string }> = {
unknown : { color : 'default' , label : '未知' } ,
} ;
const ZERO_REVENUE_REWARD_VIDEO_STATUSES = new Set ( [ 'closed_early' , 'too_short' ] ) ;
function rewardStatuses ( row : AdRevenueRow ) : string [ ] {
if ( row . sub_rewards ? . length ) return row . sub_rewards . map ( ( reward ) = > reward . status ) ;
return row . status ? [ row . status ] : [ ] ;
}
function effectiveRevenueYuan ( row : AdRevenueRow ) : number {
if (
row . ad_type === 'reward_video'
&& rewardStatuses ( row ) . some ( ( status ) = > ZERO_REVENUE_REWARD_VIDEO_STATUSES . has ( status ) )
) {
return 0 ;
}
return row . row_revenue_yuan ? ? row . revenue_yuan ;
}
@@ -190,11 +183,59 @@ const LT_FACTOR_ROWS = [
{ key : '5' , lt : '第 11 条及以后' , factor : 1.0 } ,
] ;
type AdRevenueDetailRow = AdRevenueRecord & { source_adn? : string | null } ;
type AdRevenueDetailRow = AdRevenueRecord & {
source_adn? : string | null ;
is_impression_only? : boolean ;
} ;
// 未发奖记录保留「-」以避免把潜在奖励误读为实际计算结果;悬停时说明为何没有因子。
function renderFormulaValue ( value : string | number , row : AdRevenueDetailRow ) {
if ( value !== '-' || row . status === 'granted' ) return value ;
const reason = row . is_impression_only
? '该记录只有广告展示,没有对应发奖记录'
: REWARD_STATUS_HINT [ row . status ] ? ? ` 发奖状态为 ${ row . status } ` ;
return (
< Tooltip title = { ` ${ reason } ;未进入金币计算,不占用 LT 累计条数,因此不展示因子。 ` } >
< span > - < / span >
< / Tooltip >
) ;
}
function impressionOnlyDetail ( row : AdRevenueRow ) : AdRevenueDetailRow {
return {
// 每个展开表只有这一条纯展示明细,使用稳定的本行唯一键即可。
record_id : 0 ,
created_at : row.created_at ,
status : 'impression_only' ,
ecpm : row.ecpm ,
ecpm_factor : null ,
units : 0 ,
lt_index_start : null ,
lt_index_end : null ,
lt_factor_start : null ,
lt_factor_end : null ,
expected_coin : 0 ,
actual_coin : 0 ,
matched : true ,
adn : row.adn ,
slot_id : row.slot_id ,
source_adn : row.adn ,
is_impression_only : true ,
} ;
}
// 展开行(逐条事件展开)- 该条的发奖复算明细(还原金币审计的 eCPM/因子1/份数/LT/因子2 等列)
const DETAIL_COLUMNS : ColumnsType < AdRevenueDetailRow > = [
{ title : '来源广告网络' , dataIndex : 'source_adn' , width : 110 , render : ( value : string | null ) = > value || '-' } ,
{
title : '来源广告网络' ,
dataIndex : 'source_adn' ,
width : 110 ,
render : ( value : string | null ) = > value ? . trim ( ) || (
< Tooltip title = "该历史记录没有可唯一确认的 SDK 广告来源,未猜测填充。" >
< span > 未 上 报 < / span >
< / Tooltip >
) ,
} ,
{
title : 'eCPM(元)' ,
dataIndex : 'ecpm' ,
@@ -208,33 +249,40 @@ const DETAIL_COLUMNS: ColumnsType<AdRevenueDetailRow> = [
render : ( v : string | null ) = > ( v == null || Number . isNaN ( Number ( v ) ) ? '-' : ( Number ( v ) / 100 / 1000 ) . toFixed ( 4 ) ) ,
} ,
{ title : '实发金币数' , dataIndex : 'actual_coin' , width : 100 } ,
{ title : '因子1' , dataIndex : 'ecpm_factor' , width : 70 , render : ( v : number | null ) = > v ? ? '-' } ,
{
title : '因子1' ,
dataIndex : 'ecpm_factor' ,
width : 70 ,
render : ( v : number | null , r : AdRevenueDetailRow ) = > renderFormulaValue ( v ? ? '-' , r ) ,
} ,
{
title : '因子2' ,
key : 'lt_factor' ,
width : 90 ,
render : ( _ : unknown , r : AdRevenueDetailRow ) = > fmtFactorRange ( r . lt_factor_start , r . lt_factor_end ) ,
render : ( _ : unknown , r : AdRevenueDetailRow ) = >
renderFormulaValue ( fmtFactorRange ( r . lt_factor_start , r . lt_factor_end ) , r ) ,
} ,
{
title : 'LT累计条数' ,
key : 'lt_index' ,
width : 100 ,
render : ( _ : unknown , r : AdRevenueDetailRow ) = > fmtIndexRange ( r . lt_index_start , r . lt_index_end ) ,
render : ( _ : unknown , r : AdRevenueDetailRow ) = >
renderFormulaValue ( fmtIndexRange ( r . lt_index_start , r . lt_index_end ) , r ) ,
} ,
] ;
// 趋势图(纯 SVG,零依赖):柱=展示条数(左轴),橙线=客户端预估收益元(右轴),
// 绿线=穿山甲后台预估收益 元(右轴,仅按天且有数据时出现)。x 轴按传入点序。
// 绿线=GroMore 排序价预估 元(右轴,仅按天且有数据时出现)。x 轴按传入点序。
const CHART_BAR = '#69b1ff' ;
const CHART_LINE = '#fa8c16' ;
const CHART_LINE2 = '#52c41a' ; // 穿山甲后台收益 线
const CHART_LINE2 = '#52c41a' ; // GroMore 排序价预估 线
interface TrendPoint {
label : string ; // x 轴刻度文案(小时数 / MM-DD)
tip : string ; // hover 原生 tooltip 全文
impressions : number ;
revenue : number ;
pangleRevenue : number | null ; // 穿山甲后台预估收益 (元);无则 null(不画绿线点)
pangleRevenue : number | null ; // GroMore 排序价预估 (元);无则 null(不画绿线点)
}
// 按小时聚合(0– 23),数据源是 hourly(后端全量聚合,不受分页影响)。穿山甲为天级,小时视图无该线。
@@ -255,7 +303,7 @@ function aggregateHourly(rows: AdRevenueHourly[]): TrendPoint[] {
}
// 按天聚合,数据源是 daily(全量,不受 limit 影响);用 from..to 补齐空缺日为 0,轴连续。
// 穿山甲收益 (pangle_revenue_yuan)为 null 时该日不画绿线点(T+1 未出/非全量视图)。
// GroMore 排序价预估 (pangle_revenue_yuan)为 null 时该日不画绿线点(T+1 未出/非全量视图)。
function aggregateDaily ( dateFrom : string , dateTo : string , daily : AdRevenueDaily [ ] ) : TrendPoint [ ] {
const map = new Map ( daily . map ( ( d ) = > [ d . date , d ] ) ) ;
const out : TrendPoint [ ] = [ ] ;
@@ -344,7 +392,7 @@ function TrendChart({ points }: { points: TrendPoint[] }) {
< title > { p . tip } < / title >
< / circle >
) ) }
{ /* 穿山甲后台收益 线(绿,仅非 null 的天画;按天且已同步 T+1 数据时出现) */ }
{ /* GroMore 排序价预估 线(绿,仅非 null 的天画;按天且已同步 T+1 数据时出现) */ }
{ ( ( ) = > {
const pp = points
. map ( ( p , i ) = > ( { i , v : p.pangleRevenue , tip : p.tip } ) )
@@ -387,13 +435,13 @@ export default function AdRevenueReportPage() {
const [ scene , setScene ] = useState < string | undefined > ( ) ;
const [ granularity , setGranularity ] = useState < 'day' | 'hour' > ( 'day' ) ;
const [ sortBy , setSortBy ] = useState < 'time' | 'ecpm' > ( 'time' ) ; // 明细排序:time=时间倒序 / ecpm=eCPM 倒序
const [ limit , setLimit ] = useState < number > ( 1000 ) ; // 默认拉满接口单页上限,供单次流程广告数分位统计
const [ limit , setLimit ] = useState < number > ( DEFAULT_PAGE_SIZE ) ;
const [ page , setPage ] = useState < number > ( 1 ) ; // 当前页码(后端分页;1 起)
const [ queriedGranularity , setQueriedGranularity ] = useState < 'day' | 'hour' > ( 'day' ) ; // 本次结果对应的粒度,决定是否显示「小时」列
const [ queriedMultiDay , setQueriedMultiDay ] = useState ( false ) ; // 本次结果是否跨多天,决定显示「日期」列 + 按天/按小时图
const [ data , setData ] = useState < AdRevenueReport | null > ( null ) ;
const [ allFlowItems , setAllFlowItems ] = useState < AdRevenueRow [ ] > ( [ ] ) ;
const [ queriedLimit , setQueriedLimit ] = useState < number > ( 1000 ) ;
const [ queriedLimit , setQueriedLimit ] = useState < number > ( DEFAULT_PAGE_SIZE ) ;
const [ loading , setLoading ] = useState ( false ) ;
const [ formulaOpen , setFormulaOpen ] = useState ( false ) ;
// 点用户手机号弹出的「用户广告收益详情」半屏抽屉(userId + 手机号;null=关闭)
@@ -558,7 +606,6 @@ export default function AdRevenueReportPage() {
width : 110 ,
align : 'right' ,
// 一次比价/领券聚合行用 row_revenue_yuan(该次发奖广告 eCPM 折算之和),其它行用展示侧 revenue_yuan
// 激励视频提前关闭/时长不足时防御性显示 0,与后端有效收益口径一致。
render : ( _v : number , r : AdRevenueRow ) = > effectiveRevenueYuan ( r ) . toFixed ( 4 ) ,
} ,
{
@@ -637,6 +684,8 @@ export default function AdRevenueReportPage() {
// 应发实发差额(金币)= 应发−实发(正=少发/负=多发);ARPU = 区间预估收益÷区间活跃用户(见 dau)。
const derived = data
? {
// ADN Reporting API 只覆盖已配置回传的广告源,不能据此推导全量 ARPU / 毛利。
// 在接入最终结算单前,核心经营指标始终使用 SDK 展示预估,并明确标注其性质。
payoutYuan : data.total_actual_coin / COIN_PER_YUAN ,
grossProfit : data.total_revenue_yuan - data . total_actual_coin / COIN_PER_YUAN ,
payoutRatioPct :
@@ -644,7 +693,6 @@ export default function AdRevenueReportPage() {
? ( data . total_actual_coin / COIN_PER_YUAN / data . total_revenue_yuan ) * 100
: null ,
coinGap : data.total_expected_coin - data . total_actual_coin ,
// ARPU = 区间预估广告收益 ÷ 区间去重活跃用户(dau,口径同数据大盘);dau 为 0 时不可算 → null,显示「-」。
arpu : data.dau && data . dau > 0 ? data . total_revenue_yuan / data.dau : null ,
}
: null ;
@@ -661,11 +709,15 @@ export default function AdRevenueReportPage() {
: data . date_from
: ` ${ data . date_from } ~ ${ data . date_to } ` ;
// 第二行大盘「分广告类型」:draw / 激励视频 各自 收益 / eCPM / 展示条数;eCPM = 收益÷展示×1000 。
const drawStat = data ? . type_stats ? . draw ;
const rvStat = data ? . type_stats ? . reward_video ;
const ecpmOf = ( s? : AdRevenueTypeStat ) = >
s && s . impressions > 0 ? ( s . revenue_yuan / s . impressions ) * 1000 : 0 ;
// 经营分类由后端统一:Draw = draw + 历史 feed;看视频 = 福利视频 + 提现视频 。
// eCPM 同时按每次展示的 SDK 原始值加权,不能从前端漏项后再临时平均。
const drawStat = data ? . category_stats ? . draw ;
const videoStat = data ? . category_stats ? . video ;
const videoLabel = adType === 'withdrawal_video'
? '提现看视频'
: adType === 'reward_video'
? '福利看视频'
: '看视频(福利+提现)' ;
// 明细直接用后端返回的当前页 items(「场景」已由后端 feed_scene 全局过滤,前端不再二次筛)。
const items = data ? . items ? ? [ ] ;
@@ -704,14 +756,19 @@ export default function AdRevenueReportPage() {
逐 条 展 示 不 单 独 占 行 ( 其 展 示 数 / eCPM / 预 估 收 益 已 计 入 上 方 合 计 与 大 盘 ) , 点 行 左 侧 「 + 」 展 开 看 该 次 金 币 复 算 。
< br / >
< br / >
「 预 估 收 益 」 为 客 户 端 在 广 告 展 示 ( onAdShow ) 时 上 报 eCPM 折 算 的 预 估 值 ( 每 千 次 展 示 ÷ 1000
累 加 ) ; < b > 激 励 视 频 提 前 关 闭 或 播 放 时 长 不 足 时 按 0 计 < / b > , 其 他 有 效 展 示 按 eCPM 折 算 。 穿 山 甲 仍 可 能 过 滤 无 效 曝 光 ,
< b > 实 际 收 益 一 律 以 穿 山 甲 后 台 结 算 为 准 < / b > 。 测 试 应 用 多 为 0 。 「 广 告 位 ID / 来 源 应 用 」 为 本 期 新 增 , 历 史 记 录 留 空 。
「 客 户 端 SDK 展 示 预 估 」 按 广 告 展 示 ( onAdShow ) 后 端 留 存 的 eCPM 折 算 ( 每 千 次 展 示 ÷ 1000
累 加 ; 不 足 1 秒 的 异 常 曝 光 沿 用 采 集 规 则 记 0 ) 。 是 否 看 满 只 影 响 金 币 发 放 ; 发 奖 防 作 弊 的 eCPM 上 限 不 参 与 收 入 计 算 。
< b > 最 终 收 入 仍 以 各 ADN 结 算 单 为 准 < / b > 。
< br / >
< br / >
核 心 指 标 里 的 < b > 「 穿 山 甲 后 台 收 益 ( T + 1 ) 」 < / b > 来 自 穿 山 甲 GroMore 数 据 API ( 后 台 结 算 口 径 , 次 日 出 数 ) :
< b > 穿 山 甲 预 估 收 益 < / b > = 接 口 revenue 、 < b > 收 益 API < / b > = 各 ADN 回 传 更 接 近 结 算 。 穿 山 甲 不 提 供 分 用 户 / 类 型 / 场 景 维 度 ,
故 仅 在 < b > 可 对 账 视 图 < / b > ( 未 按 用 户 / 类 型 / 场 景 筛 选 ) 展 示 ; 环 境 和 数 据 范 围 会 同 时 作 用 于 客 户 端 与 穿 山 甲 汇 总 。
< b > GroMore 排 序 价 预 估 < / b > 来 自 接 口 revenue , 只 是 排 序 价 / 竞 价 实 时 价 估 算 , 不 是 结 算 收 入 ;
< b > ADN Reporting API 收 益 < / b > 来 自 已 配 置 广 告 源 的 api_revenue 回 传 ; 每 天 14 :00 后 仅 代 表 同 步 窗 口 完 成 ,
不 代 表 覆 盖 全 部 广 告 源 或 最 终 结 算 , 因 此 不 用 于 自 动 计 算 全 量 毛 利 和 ARPU 。
< br / >
< br / >
分 类 卡 口 径 : < b > Draw 信 息 流 = draw + 历 史 feed < / b > ; < b > 看 视 频 = 福 利 视 频 + 提 现 视 频 < / b > 。
两 者 的 eCPM 都 由 后 端 按 每 次 展 示 的 SDK eCPM 加 权 计 算 ( eCPM 总 和 ÷ 展 示 次 数 ) , 不 从 分 页 明 细 或 单 一 子 类 型 取 值 。
GroMore 不 提 供 分 用 户 / 类 型 / 场 景 维 度 , 故 仅 在 < b > 可 对 账 视 图 < / b > ( 未 按 用 户 / 类 型 / 场 景 筛 选 ) 展 示 。
默 认 “ 正 式 + 业 务 代 码 位 ” 用 于 同 口 径 对 账 ; 切 到 “ 全 部 代 码 位 ” 会 包 含 广 告 测 试 等 非 业 务 曝 光 。
< / div >
}
@@ -775,12 +832,11 @@ export default function AdRevenueReportPage() {
< Space size = { 6 } >
< Typography.Text type = "secondary" > 类 型 < / Typography.Text >
< Select
placeholder = "全部"
value = { adType }
onChange = { setAdType }
allowClear
value = { adType ? ? ALL_FILTER_VALUE }
onChange = { ( value ) = > setAdType ( value === ALL_FILTER_VALUE ? undefined : value ) }
style = { { width : 150 } }
options = { [
{ value : ALL_FILTER_VALUE , label : '全部' } ,
{ value : 'reward_video' , label : '看视频' } ,
{ value : 'draw' , label : 'Draw 信息流' } ,
{ value : 'withdrawal_video' , label : '提现看视频' } ,
@@ -790,12 +846,11 @@ export default function AdRevenueReportPage() {
< Space size = { 6 } >
< Typography.Text type = "secondary" > 场 景 < / Typography.Text >
< Select
placeholder = "全部"
value = { scene }
onChange = { setScene }
allowClear
value = { scene ? ? ALL_FILTER_VALUE }
onChange = { ( value ) = > setScene ( value === ALL_FILTER_VALUE ? undefined : value ) }
style = { { width : 130 } }
options = { [
{ value : ALL_FILTER_VALUE , label : '全部' } ,
{ value : 'comparison' , label : '比价' } ,
{ value : 'coupon' , label : '领券' } ,
{ value : 'welfare' , label : '福利' } ,
@@ -858,16 +913,16 @@ export default function AdRevenueReportPage() {
< / Divider >
< Row gutter = { [ 16 , 12 ] } >
< Col flex = "1 1 0" >
< Statistic title = "预估收益合计 (元)" value = { data . total_revenue_yuan } precision = { 4 } / >
< Statistic title = "客户端 SDK 展示 预估(元)" value = { data . total_revenue_yuan } precision = { 4 } / >
< / Col >
< Col flex = "1 1 0" >
< Statistic
title = {
< Tooltip
title = { ` 每活跃用户广告预估收入 = 区间预估收益 ÷ 区间去重活跃用户(口径同数据大盘) 。统计区间:${ dauRangeLabel || '—' } ` }
title = { ` 客户端 SDK 展示预估 ÷ 区间去重活跃用户。ADN Reporting API 可能只覆盖已配置回传的广告源,不能作为全量 ARPU 。统计区间:${ dauRangeLabel || '—' } ` }
>
< span >
{ dauRangeIsToday ? 'ARPU(今日)' : 'ARPU' }
SDK 预 估 ARPU
< InfoCircleOutlined style = { { marginLeft : 4 } } / >
< / span >
< / Tooltip >
@@ -896,7 +951,7 @@ export default function AdRevenueReportPage() {
< / Col >
< Col flex = "1 1 0" >
< Statistic
title = "预估广告毛利(元)"
title = "SDK 预估广告毛利(元)"
value = { derived . grossProfit }
precision = { 4 }
prefix = { derived . grossProfit >= 0 ? < RiseOutlined / > : < FallOutlined / > }
@@ -909,9 +964,9 @@ export default function AdRevenueReportPage() {
< / Col >
< / Row >
< Divider orientation = "left" plain style = { { marginTop : 20 , marginBottom : 16 } } >
< Tooltip title = "来自穿山甲 GroMore 数据 API(后台结算口径,T+1 次日出数)。穿山甲不提供分用户/类型/场景维度,故仅在「全量视图」(未按用户/类型/场景筛选)展示;按代码位×应用×日期汇总。revenue=预估收益、收益API=各 ADN 回传更接近 结算。" >
< Tooltip title = "来自 GroMore 天级 数据 API。revenue 是排序价/竞价实时价预估;api_revenue 是已配置 ADN 的 Reporting 回传收益。D+1 14:00 后仅表示 API 同步窗口完成,不代表全部广告源或最终 结算。" >
< Typography.Text type = "secondary" style = { { fontSize : 12 } } >
穿 山 甲 后 台 收 益 ( T + 1 )
GroMore / ADN 收 益 对 账 ( T + 1 )
< InfoCircleOutlined style = { { marginLeft : 4 } } / >
< / Typography.Text >
< / Tooltip >
@@ -920,29 +975,41 @@ export default function AdRevenueReportPage() {
< Row gutter = { [ 16 , 12 ] } >
< Col flex = "1 1 0" >
< Statistic
title = "穿山甲预估收益 (元)"
title = "GroMore 排序价预估 (元)"
value = { data . total_pangle_revenue_yuan ? ? 0 }
precision = { 4 }
/ >
< / Col >
< Col flex = "1 1 0" >
< Statistic
title = "穿山甲收益API (元)"
title = "ADN Reporting API 收益 (元)"
value = { data . total_pangle_api_revenue_yuan ? ? '-' }
precision = { data . total_pangle_api_revenue_yuan == null ? undefined : 4 }
/ >
{ data . total_pangle_api_revenue_yuan == null && (
{ data . total_pangle_api_revenue_yuan == null ? (
< Typography.Text type = "secondary" style = { { fontSize : 12 } } >
未 配 Reporting / 当 天 无
未 配 置 Reporting 、 当 天 未 出 数 或 所 选 维 度 不 可 对 账
< / Typography.Text >
) : (
< Typography.Text
type = { data . pangle_api_revenue_complete ? 'success' : 'warning' }
style = { { fontSize : 12 } }
>
{ data . pangle_api_revenue_complete
? '已完成 API 同步(不代表全量结算)'
: '暂估,等待 14:30 API 同步' }
{ data . pangle_latest_synced_at
? ` · 同步于 ${ formatUtcTime ( data . pangle_latest_synced_at ) } `
: '' }
< / Typography.Text >
) }
< / Col >
< Col flex = "1 1 0" >
< Statistic
title = {
< Tooltip title = "客户端预估相对穿山甲预估的高估幅度 =(客户端预估 − 穿山甲预估)÷ 穿山甲预估;客户端按 onAdShow 计、不滤无效曝光,通常偏高 " >
< Tooltip title = "客户端 SDK 展示预估相对 GroMore 排序价预估的差异;两者都是估算,只用于检查埋点和排序价口径,不代表结算差异 " >
< span >
客 户 端 预 估 偏 差
SDK / GroMore 预 估 差
< InfoCircleOutlined style = { { marginLeft : 4 } } / >
< / span >
< / Tooltip >
@@ -967,8 +1034,7 @@ export default function AdRevenueReportPage() {
< / Row >
) : (
< Typography.Text type = "secondary" style = { { fontSize : 13 } } >
本视图无穿山甲后台收益 : 仅 「 全 量 视 图 」 ( 未 按 用 户 / 类 型 / 场 景 筛 选 ) 且 对 应 日 期 已 同 步 到 数 据 时 展 示 。
穿 山 甲 T + 1 出 数 , 可 由 < Typography.Text code > scripts / sync_pangle_revenue < / Typography.Text > 每 日 拉 取 入 库 。
本 视 图 无 GroMore / ADN 对 账 收 益 : 仅 未 按 用 户 、 类 型 、 场 景 筛 选 且 对 应 日 期 已 同 步 时 展 示 。
< / Typography.Text >
) }
< Divider orientation = "left" plain style = { { marginTop : 20 , marginBottom : 16 } } >
@@ -981,19 +1047,19 @@ export default function AdRevenueReportPage() {
< Statistic title = "Draw 信息流收益(元)" value = { drawStat ? . revenue_yuan ? ? 0 } precision = { 4 } / >
< / Col >
< Col span = { 4 } >
< Statistic title = "Draw 信息流 eCPM(元/千次)" value = { ecpmOf ( drawStat ) } precision = { 2 } / >
< Statistic title = "Draw 信息流 eCPM(元/千次)" value = { drawStat ? . ecpm_yuan ? ? 0 } precision = { 2 } / >
< / Col >
< Col span = { 4 } >
< Statistic title = "Draw 信息流条数" value = { drawStat ? . impressions ? ? 0 } / >
< / Col >
< Col span = { 4 } >
< Statistic title = "看视频收益(元)" value = { rvStat ? . revenue_yuan ? ? 0 } precision = { 4 } / >
< Statistic title = { ` ${ videoLabel } 收益(元) ` } value = { videoStat ? . revenue_yuan ? ? 0 } precision = { 4 } / >
< / Col >
< Col span = { 4 } >
< Statistic title = "看视频 eCPM(元/千次)" value = { ecpmOf ( rvStat ) } precision = { 2 } / >
< Statistic title = { ` ${ videoLabel } eCPM(元/千次) ` } value = { videoStat ? . ecpm_yuan ? ? 0 } precision = { 2 } / >
< / Col >
< Col span = { 4 } >
< Statistic title = "看视频条数" value = { rvStat ? . impressions ? ? 0 } / >
< Statistic title = { ` ${ videoLabel } 条数 ` } value = { videoStat ? . impressions ? ? 0 } / >
< / Col >
< / Row >
< Divider orientation = "left" plain style = { { marginTop : 20 , marginBottom : 16 } } >
@@ -1058,7 +1124,7 @@ export default function AdRevenueReportPage() {
verticalAlign : 'middle' ,
} }
/ >
穿 山 甲 后 台 收 益 ( 元 )
GroMore 排 序 价 预 估 ( 元 )
< / span >
) }
< / Space >
@@ -1102,7 +1168,11 @@ export default function AdRevenueReportPage() {
style = { { marginTop : 8 } }
rowKey = "record_id"
columns = { DETAIL_COLUMNS }
dataSource = { r . sub_rewards . map ( ( detail ) = > ( { . . . detail , source_adn : r.adn } ) ) }
dataSource = { r . sub_rewards . map ( ( detail ) = > ( {
. . . detail ,
// 一次比价/领券可能由不同 ADN 连续填充,必须优先显示本条发奖记录的来源。
source_adn : detail.adn ? ? r . adn ,
} ) ) }
pagination = { false }
size = "small"
scroll = { { x : 900 } }
@@ -1110,15 +1180,18 @@ export default function AdRevenueReportPage() {
< / div >
) : r . reward_detail ? (
< div >
< Typography.Text strong > 金 币 复 算 明 细 < / Typography.Text > { ' ' }
< Typography.Text strong > 本 次 广 告 逐 条 明 细 < / Typography.Text > { ' ' }
< Typography.Text type = "secondary" >
( 本 条 广 告 应 发 { r . reward_detail . expected_coin } / 实 发 { r . reward_detail . actual_coin } )
( 共 1 条 · 应 发 { r . reward_detail . expected_coin } / 实 发 { r . reward_detail . actual_coin } )
< / Typography.Text >
< Table < AdRevenueDetailRow >
style = { { marginTop : 8 } }
rowKey = "record_id"
columns = { DETAIL_COLUMNS }
dataSource = { [ { . . . r . reward_detail , source_adn : r.adn } ] }
dataSource = { [ {
. . . r . reward_detail ,
source_adn : r.reward_detail.adn ? ? r . adn ,
} ] }
pagination = { false }
size = "small"
scroll = { { x : 900 } }
@@ -1126,24 +1199,17 @@ export default function AdRevenueReportPage() {
< / div >
) : (
< div >
< Typography.Text strong > 展 示 明 细 < / Typography.Text > { ' ' }
< Typography.Text type = "secondary" > ( 纯 展 示 事 件 , 未 单 独 发 奖 , 无 金 币 复 算 ) < / Typography.Text >
< Descriptions size = "small" column = { 3 } bordered style = { { marginTop : 8 } } >
< Descriptions.Item label = "时间" > { formatUtcTime ( r . created_at ) } < / Descriptions.Item >
< Descriptions.Item label = "eCPM(分)" > { r . ecpm ? ? '-' } < / Descriptions.Item >
< Descriptions.Item label = "eCPM(元)" >
{ r . ecpm != null && ! Number . isNaN ( Number ( r . ecpm ) )
? ( Number ( r . ecpm ) / 100 ) . toFixed ( 2 )
: '-' }
< / Descriptions.Item >
< Descriptions.Item label = "预估收益(元)" >
{ r . has_impression ? r . revenue_yuan . toFixed ( 4 ) : '-' }
< / Descriptions.Item >
< Descriptions.Item label = "底层 ADN" > { r . adn ? ? '-' } < / Descriptions.Item >
< Descriptions.Item label = "底层代码位(rit)" > { r . slot_id ? ? '-' } < / Descriptions.Item >
< Descriptions.Item label = "我方代码位" > { r . our_code_id ? ? '-' } < / Descriptions.Item >
< Descriptions.Item label = "来源应用" > { r . app_env ? ? '-' } < / Descriptions.Item >
< / Descriptions >
< Typography.Text strong > 本 次 广 告 逐 条 明 细 < / Typography.Text > { ' ' }
< Typography.Text type = "secondary" > ( 共 1 条 · 应 发 0 / 实 发 0 ; 仅 展 示 , 未 单 独 发 奖 ) < / Typography.Text >
< Table < AdRevenueDetailRow >
style = { { marginTop : 8 } }
rowKey = "record_id"
columns = { DETAIL_COLUMNS }
dataSource = { [ impressionOnlyDetail ( r ) ] }
pagination = { false }
size = "small"
scroll = { { x : 900 } }
/ >
< / div >
) ,
} }