@@ -9,6 +9,7 @@ import {
Card ,
Col ,
DatePicker ,
Descriptions ,
Divider ,
InputNumber ,
Modal ,
@@ -189,61 +190,7 @@ const LT_FACTOR_ROWS = [
{ key : '5' , lt : '第 11 条及以后' , factor : 1.0 } ,
] ;
type AdRevenueDetailRow = AdRevenueRecord & {
source_adn? : string | null ;
is_impression_only? : boolean ;
} ;
const FORMULA_EMPTY_STATUS_LABEL : Record < string , string > = {
capped : '次数超限' ,
ecpm_missing : 'eCPM 缺失' ,
too_short : '观看时长不足' ,
closed_early : '提前关闭' ,
impression_only : '仅展示' ,
} ;
// 未发奖记录用可读状态代替空值,避免误认为数据丢失;悬停时说明为何没有计算因子。
function renderFormulaValue ( value : string | number , row : AdRevenueDetailRow ) {
if ( value !== '-' ) return value ;
const isGrantedButMissing = row . status === 'granted' ;
const reason = isGrantedButMissing
? '该记录标记为已发奖,但计算因子缺失,需要进一步排查'
: row . is_impression_only
? '该记录只有广告展示,没有对应发奖记录'
: REWARD_STATUS_HINT [ row . status ] ? ? ` 发奖状态为 ${ row . status || '未知' } ` ;
const statusLabel = isGrantedButMissing
? '计算数据缺失'
: ( FORMULA_EMPTY_STATUS_LABEL [ row . status ] ? ? row . status ) || '未进入计算' ;
return (
< Tooltip
title = { isGrantedButMissing
? reason
: ` ${ reason } ;未进入金币计算,不占用 LT 累计条数,因此不展示因子。 ` }
>
< span > { statusLabel } < / 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 ,
source_adn : row.adn ,
is_impression_only : true ,
} ;
}
type AdRevenueDetailRow = AdRevenueRecord & { source_adn? : string | null } ;
// 展开行(逐条事件展开)- 该条的发奖复算明细(还原金币审计的 eCPM/因子1/份数/LT/因子2 等列)
const DETAIL_COLUMNS : ColumnsType < AdRevenueDetailRow > = [
@@ -261,25 +208,18 @@ 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 , row : AdRevenueDetailRow ) = > renderFormulaValue ( v ? ? '-' , row ) ,
} ,
{ title : '因子1' , dataIndex : 'ecpm_factor' , width : 70 , render : ( v : number | null ) = > v ? ? '-' } ,
{
title : '因子2' ,
key : 'lt_factor' ,
width : 90 ,
render : ( _ : unknown , row : AdRevenueDetailRow ) = >
renderFormulaValue ( fmtFactorRange ( row . lt_factor_start , row . lt_factor_end ) , row ) ,
render : ( _ : unknown , r : AdRevenueDetailRow ) = > fmtFactorRange ( r . lt_factor_start , r . lt_factor_end ) ,
} ,
{
title : 'LT累计条数' ,
key : 'lt_index' ,
width : 100 ,
render : ( _ : unknown , row : AdRevenueDetailRow ) = >
renderFormulaValue ( fmtIndexRange ( row . lt_index_start , row . lt_index_end ) , row ) ,
render : ( _ : unknown , r : AdRevenueDetailRow ) = > fmtIndexRange ( r . lt_index_start , r . lt_index_end ) ,
} ,
] ;
@@ -721,11 +661,17 @@ export default function AdRevenueReportPage() {
: data . date_from
: ` ${ data . date_from } ~ ${ data . date_to } ` ;
// 第二行大盘「分广告类型」:draw / 激励视频 各自 收益 / eCPM / 展示条数;eCPM = 收益÷展示×1000 。
// eCPM 优先使用后端按真实展示加权的经营分类口径;兼容旧后端时才用收益÷展示数回退 。
const drawStat = data ? . type_stats ? . draw ;
const rvStat = data ? . type_stats ? . reward_video ;
const withdrawalVideoStat = data ? . type_stats ? . withdrawal_video ;
const drawEcpmStat = data ? . category_stats ? . draw ? ? drawStat ;
const videoEcpmStat = data ? . category_stats ? . video ? ? {
impressions : ( rvStat ? . impressions ? ? 0 ) + ( withdrawalVideoStat ? . impressions ? ? 0 ) ,
revenue_yuan : ( rvStat ? . revenue_yuan ? ? 0 ) + ( withdrawalVideoStat ? . revenue_yuan ? ? 0 ) ,
} ;
const ecpmOf = ( s? : AdRevenueTypeStat ) = >
s && s . impressions > 0 ? ( s . revenue_yuan / s . impressions ) * 1000 : 0 ;
s ? . ecpm_yuan ? ? ( s && s . impressions > 0 ? ( s . revenue_yuan / s . impressions ) * 1000 : 0 ) ;
// 明细直接用后端返回的当前页 items(「场景」已由后端 feed_scene 全局过滤,前端不再二次筛)。
const items = data ? . items ? ? [ ] ;
@@ -1041,7 +987,7 @@ 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 = { ecpmOf ( drawEcpmStat ) } precision = { 2 } / >
< / Col >
< Col span = { 4 } >
< Statistic title = "Draw 信息流条数" value = { drawStat ? . impressions ? ? 0 } / >
@@ -1050,7 +996,7 @@ export default function AdRevenueReportPage() {
< Statistic title = "看视频收益(元)" value = { rvStat ? . revenue_yuan ? ? 0 } precision = { 4 } / >
< / Col >
< Col span = { 4 } >
< Statistic title = "看视频 eCPM(元/千次)" value = { ecpmOf ( rvStat ) } precision = { 2 } / >
< Statistic title = "看视频 eCPM(元/千次)" value = { ecpmOf ( videoEcpmStat ) } precision = { 2 } / >
< / Col >
< Col span = { 4 } >
< Statistic title = "看视频条数" value = { rvStat ? . impressions ? ? 0 } / >
@@ -1186,19 +1132,24 @@ export default function AdRevenueReportPage() {
< / div >
) : (
< div >
< 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 } }
/ >
< 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 >
< / div >
) ,
} }