更新数据大屏金币统计列
This commit is contained in:
parent
f5e1a20dd9
commit
a952b4262e
@ -33,8 +33,10 @@ const countryColumns = [
|
||||
{ key: "lucky_gift_turnover", label: "幸运礼物流水/利润率", render: (row) => <FlowRateCell rate={row.lucky_gift_profit_rate} value={row.lucky_gift_turnover} />, sortable: false },
|
||||
{ key: "super_lucky_gift_turnover", label: "超级幸运礼物流水/利润率", render: (row) => <FlowRateCell rate={row.super_lucky_gift_profit_rate} value={row.super_lucky_gift_turnover} />, sortable: false },
|
||||
{ key: "gift_coin_spent", label: "礼物流水", render: (row) => formatOptionalCoin(row.gift_coin_spent), sortable: false },
|
||||
{ key: "coin_total", label: "金币数量(总和)", render: (row) => formatOptionalCoin(row.coin_total), sortable: false },
|
||||
{ key: "coin_total", label: "用户金币数", render: (row) => formatOptionalCoin(row.coin_total), sortable: false },
|
||||
{ key: "consumed_coin", label: "消耗金币", render: (row) => formatOptionalCoin(row.consumed_coin), sortable: false },
|
||||
{ key: "output_coin", label: "产出金币", render: (row) => formatOptionalCoin(row.output_coin), sortable: false },
|
||||
{ key: "consume_output_ratio", label: "消耗产出比", render: (row) => <ConsumeOutputCell row={row} />, sortable: false },
|
||||
{ key: "platform_grant_coin", label: "平台发放金币", render: (row, context) => <PlatformGrantCoinCell context={context} row={row} />, sortable: false },
|
||||
{ key: "manual_grant_coin", label: "人工发放金币", render: (row) => formatOptionalCoin(row.manual_grant_coin), sortable: false },
|
||||
{ key: "salary_usd_minor", label: "工资总和(主播/代理)", render: (row) => formatOptionalMoney(row.salary_usd_minor), sortable: false },
|
||||
@ -736,6 +738,18 @@ function FlowRateCell({ rate, value }) {
|
||||
);
|
||||
}
|
||||
|
||||
function ConsumeOutputCell({ row }) {
|
||||
if (isMissing(row.consume_output_ratio) && isMissing(row.consume_output_delta)) {
|
||||
return <span className="report-placeholder">--</span>;
|
||||
}
|
||||
return (
|
||||
<span className="report-pair-cell">
|
||||
<b>{formatOptionalPercent(row.consume_output_ratio)}</b>
|
||||
<small>差额 {formatOptionalCoin(row.consume_output_delta)}</small>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function TrendValue({ value }) {
|
||||
return <span className={Number(value) >= 0 ? "report-trend-up" : "report-trend-down"}>{formatSignedPercent(value)}</span>;
|
||||
}
|
||||
|
||||
@ -59,6 +59,10 @@ test("shows daily totals first and expands daily country details on demand", asy
|
||||
|
||||
const stickyDateHeader = container.querySelector(".report-table--wide th.is-sticky-left");
|
||||
expect(stickyDateHeader).toHaveTextContent("日期");
|
||||
expect(screen.getByRole("columnheader", { name: "用户金币数" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("columnheader", { name: "产出金币" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("columnheader", { name: "消耗产出比" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("columnheader", { name: "金币数量(总和)" })).not.toBeInTheDocument();
|
||||
expect(container.querySelector(".report-table--wide td.is-sticky-left .report-date-cell")).toBeTruthy();
|
||||
|
||||
const june5Row = screen.getByRole("button", { name: "展开 2026-06-05 国家明细" });
|
||||
|
||||
@ -339,6 +339,7 @@ function summarizeReportRows(rows) {
|
||||
"coin_seller_transfer_coin",
|
||||
"coin_total",
|
||||
"consumed_coin",
|
||||
"output_coin",
|
||||
"game_payout",
|
||||
"game_refund",
|
||||
"game_turnover",
|
||||
@ -398,6 +399,12 @@ function normalizeCountryRow(item, index, totalRecharge) {
|
||||
const micOnlineUsers = readOptionalNumber(item, "mic_online_users", "micOnlineUsers");
|
||||
const avgMicOnlineMS = readOptionalNumber(item, "avg_mic_online_ms", "avgMicOnlineMs") ??
|
||||
(micOnlineMS === null || micOnlineUsers === null ? null : Math.round(ratio(micOnlineMS, micOnlineUsers)));
|
||||
const consumedCoin = readOptionalNumber(item, "consumed_coin", "consumedCoin");
|
||||
const outputCoin = readOptionalNumber(item, "output_coin", "outputCoin");
|
||||
const consumeOutputRatio = readOptionalNumber(item, "consume_output_ratio", "consumeOutputRatio") ??
|
||||
(consumedCoin === null || outputCoin === null ? null : ratio(outputCoin, consumedCoin));
|
||||
const consumeOutputDelta = readOptionalNumber(item, "consume_output_delta", "consumeOutputDelta") ??
|
||||
(consumedCoin === null || outputCoin === null ? null : consumedCoin - outputCoin);
|
||||
return {
|
||||
active_users: activeUsers,
|
||||
arpu_usd_minor: numberValue(item.arpu_usd_minor ?? item.arpuUsdMinor),
|
||||
@ -408,7 +415,9 @@ function normalizeCountryRow(item, index, totalRecharge) {
|
||||
coin_seller_stock_coin: readOptionalNumber(item, "coin_seller_stock_coin", "coinSellerStockCoin"),
|
||||
coin_seller_transfer_coin: numberValue(item.coin_seller_transfer_coin ?? item.coinSellerTransferCoin),
|
||||
coin_total: readOptionalNumber(item, "coin_total", "coinTotal"),
|
||||
consumed_coin: readOptionalNumber(item, "consumed_coin", "consumedCoin"),
|
||||
consumed_coin: consumedCoin,
|
||||
consume_output_delta: consumeOutputDelta,
|
||||
consume_output_ratio: consumeOutputRatio,
|
||||
country: meta?.label || country,
|
||||
country_id: countryId,
|
||||
country_code: code || meta?.code || "",
|
||||
@ -432,6 +441,7 @@ function normalizeCountryRow(item, index, totalRecharge) {
|
||||
new_user_recharge_usd_minor: numberValue(item.new_user_recharge_usd_minor ?? item.newUserRechargeUsdMinor),
|
||||
paid_users: paidUsers,
|
||||
payer_rate: ratio(paidUsers, activeUsers),
|
||||
output_coin: outputCoin,
|
||||
platform_grant_coin: readOptionalNumber(item, "platform_grant_coin", "platformGrantCoin"),
|
||||
recharge_usd_minor: recharge,
|
||||
recharge_conversion_rate: item.recharge_conversion_rate !== undefined || item.rechargeConversionRate !== undefined
|
||||
@ -487,6 +497,12 @@ function normalizeDailySeries(source) {
|
||||
const micOnlineUsers = readOptionalNumber(item, "mic_online_users", "micOnlineUsers");
|
||||
const avgMicOnlineMS = readOptionalNumber(item, "avg_mic_online_ms", "avgMicOnlineMs") ??
|
||||
(micOnlineMS === null || micOnlineUsers === null ? null : Math.round(ratio(micOnlineMS, micOnlineUsers)));
|
||||
const consumedCoin = readOptionalNumber(item, "consumed_coin", "consumedCoin");
|
||||
const outputCoin = readOptionalNumber(item, "output_coin", "outputCoin");
|
||||
const consumeOutputRatio = readOptionalNumber(item, "consume_output_ratio", "consumeOutputRatio") ??
|
||||
(consumedCoin === null || outputCoin === null ? null : ratio(outputCoin, consumedCoin));
|
||||
const consumeOutputDelta = readOptionalNumber(item, "consume_output_delta", "consumeOutputDelta") ??
|
||||
(consumedCoin === null || outputCoin === null ? null : consumedCoin - outputCoin);
|
||||
return {
|
||||
active_users: readNumber(item, "active_users", "activeUsers"),
|
||||
arppu_usd_minor: readNumber(item, "arppu_usd_minor", "arppuUsdMinor"),
|
||||
@ -497,7 +513,9 @@ function normalizeDailySeries(source) {
|
||||
coin_seller_stock_coin: readOptionalNumber(item, "coin_seller_stock_coin", "coinSellerStockCoin"),
|
||||
coin_seller_transfer_coin: readNumber(item, "coin_seller_transfer_coin", "coinSellerTransferCoin"),
|
||||
coin_total: readOptionalNumber(item, "coin_total", "coinTotal"),
|
||||
consumed_coin: readOptionalNumber(item, "consumed_coin", "consumedCoin"),
|
||||
consumed_coin: consumedCoin,
|
||||
consume_output_delta: consumeOutputDelta,
|
||||
consume_output_ratio: consumeOutputRatio,
|
||||
game_profit: gameProfit,
|
||||
game_turnover: gameTurnover,
|
||||
gift_coin_spent: readNumber(item, "gift_coin_spent", "giftCoinSpent"),
|
||||
@ -514,6 +532,7 @@ function normalizeDailySeries(source) {
|
||||
new_paid_users: readNumber(item, "new_paid_users", "newPaidUsers"),
|
||||
new_user_recharge_usd_minor: readNumber(item, "new_user_recharge_usd_minor", "newUserRechargeUsdMinor"),
|
||||
new_users: readNumber(item, "new_users", "newUsers"),
|
||||
output_coin: outputCoin,
|
||||
paid_users: readNumber(item, "paid_users", "paidUsers"),
|
||||
platform_grant_coin: readOptionalNumber(item, "platform_grant_coin", "platformGrantCoin"),
|
||||
recharge_usd_minor: recharge,
|
||||
|
||||
@ -147,6 +147,7 @@ test("builds report rows with range total, daily totals, and daily country rows"
|
||||
manual_grant_coin: 20,
|
||||
mic_online_ms: 6_000,
|
||||
mic_online_users: 3,
|
||||
output_coin: 120,
|
||||
platform_grant_coin: 150,
|
||||
recharge_usd_minor: 100,
|
||||
salary_usd_minor: 900,
|
||||
@ -172,6 +173,7 @@ test("builds report rows with range total, daily totals, and daily country rows"
|
||||
manual_grant_coin: 20,
|
||||
mic_online_ms: 6_000,
|
||||
mic_online_users: 3,
|
||||
output_coin: 120,
|
||||
platform_grant_coin: 150,
|
||||
recharge_usd_minor: 100,
|
||||
salary_usd_minor: 900,
|
||||
@ -188,10 +190,13 @@ test("builds report rows with range total, daily totals, and daily country rows"
|
||||
avg_mic_online_ms: 2_000,
|
||||
coin_total: 1_200,
|
||||
consumed_coin: 300,
|
||||
consume_output_delta: 180,
|
||||
consume_output_ratio: 0.4,
|
||||
country: "总计",
|
||||
date_label: "2026-06-05",
|
||||
manual_grant_coin: 20,
|
||||
mic_online_ms: 6_000,
|
||||
output_coin: 120,
|
||||
platform_grant_coin: 150,
|
||||
salary_usd_minor: 900
|
||||
}));
|
||||
@ -202,11 +207,14 @@ test("builds report rows with range total, daily totals, and daily country rows"
|
||||
coin_seller_transfer_coin: 500,
|
||||
coin_total: 1_200,
|
||||
consumed_coin: 300,
|
||||
consume_output_delta: 180,
|
||||
consume_output_ratio: 0.4,
|
||||
country: "巴西",
|
||||
game_turnover: 300,
|
||||
lucky_gift_turnover: 200,
|
||||
manual_grant_coin: 20,
|
||||
mic_online_ms: 6_000,
|
||||
output_coin: 120,
|
||||
platform_grant_coin: 150,
|
||||
salary_usd_minor: 900,
|
||||
salary_transfer_coin: 700,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user