feat(energy): rebuild hydrogen BI board and drill-through
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -2,12 +2,37 @@ import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import type { HydrogenOverviewResponse } from '../api.js';
|
||||
import {
|
||||
buildCustomerDrillPayload,
|
||||
buildMetricDrillPayload,
|
||||
buildMonthDrillPayload,
|
||||
buildRegionDrillPayload,
|
||||
buildStationDrillPayload,
|
||||
deriveOverviewMetrics,
|
||||
formatKg,
|
||||
formatRelative,
|
||||
formatYuan,
|
||||
} from './model.js';
|
||||
|
||||
const drillKpi = {
|
||||
yearKg: 10_000,
|
||||
yearFee: 80_000,
|
||||
yearRevenue: 75_000,
|
||||
yearProfit: 15_000,
|
||||
ourYearKg: 4_000,
|
||||
ourYearFee: 20_000,
|
||||
customerYearKg: 6_000,
|
||||
monthKg: 1_000,
|
||||
monthFee: 8_000,
|
||||
monthRevenue: 7_500,
|
||||
monthProfit: 1_500,
|
||||
todayKg: 100,
|
||||
todayFee: 800,
|
||||
todayRevenue: 750,
|
||||
todayProfit: 150,
|
||||
lingniuBornKg: 0,
|
||||
lingniuBornFee: 0,
|
||||
};
|
||||
|
||||
test('重量和金额单位保持现有阈值及负数格式', () => {
|
||||
assert.deepEqual(formatKg(999.5), { value: '999.50', unit: 'Kg' });
|
||||
assert.deepEqual(formatKg(1000), { value: '1.00', unit: 'T' });
|
||||
@@ -45,7 +70,7 @@ test('总览派生指标保持月份顺序、动能、集中度和收益率口
|
||||
lingniuBornKg: 0,
|
||||
lingniuBornFee: 0,
|
||||
},
|
||||
top5: [{ rank: 1, name: 'A', kg: 600, fee: 100, share: 0.6 }],
|
||||
top5: [{ id: 1, rank: 1, name: 'A', kg: 600, fee: 100, share: 0.6 }],
|
||||
regions: [],
|
||||
monthly: [
|
||||
{ month: '2026-01', kg: 100, fee: 20, revenue: 30, profit: 10 },
|
||||
@@ -53,11 +78,13 @@ test('总览派生指标保持月份顺序、动能、集中度和收益率口
|
||||
],
|
||||
customers: [],
|
||||
stations: [
|
||||
{ name: 'A', kg: 600, share: 0.6, revenue: 300, revenueShare: 0.6 },
|
||||
{ name: 'B', kg: 400, share: 0.4, revenue: 200, revenueShare: 0.4 },
|
||||
{ id: 1, name: 'A', kg: 600, share: 0.6, revenue: 300, revenueShare: 0.6 },
|
||||
{ id: 2, name: 'B', kg: 400, share: 0.4, revenue: 200, revenueShare: 0.4 },
|
||||
],
|
||||
availableYears: [2026],
|
||||
year: 2026,
|
||||
latestLedgerTime: '2026-08-17 21:50:09',
|
||||
filter: { stationId: null, vehicleScope: 'all', verifyScope: 'all' },
|
||||
} satisfies HydrogenOverviewResponse;
|
||||
|
||||
const metrics = deriveOverviewMetrics(data);
|
||||
@@ -66,7 +93,56 @@ test('总览派生指标保持月份顺序、动能、集中度和收益率口
|
||||
assert.equal(metrics.latestMonth?.month, '2026-02');
|
||||
assert.equal(metrics.monthMomentum, 50);
|
||||
assert.equal(metrics.top5Share, 60);
|
||||
assert.equal(metrics.profitYield, -10);
|
||||
assert.equal(metrics.customerGrossMarginPct, -10);
|
||||
assert.equal(metrics.stationAvgKg, 500);
|
||||
assert.deepEqual(metrics.monthlyDual.map(item => item.monthLabel), ['1月', '2月']);
|
||||
});
|
||||
|
||||
test('KPI 下钻只使用真实汇总口径且保留客户单毛利语义', () => {
|
||||
const payload = buildMetricDrillPayload(drillKpi, 'yearProfit');
|
||||
assert.equal(payload.title, '客户单毛利');
|
||||
assert.equal(payload.metrics[0]?.label, '客户单毛利');
|
||||
assert.equal(payload.metrics[0]?.value, '¥1.5 万元');
|
||||
assert.equal(payload.rows.length, 0);
|
||||
assert.match(payload.emptyMessage ?? '', /未返回站点、客户、车牌及单笔订单层级/);
|
||||
});
|
||||
|
||||
test('月度下钻按接口返回的羚牛与外部车辆加氢量拆分', () => {
|
||||
const payload = buildMonthDrillPayload({
|
||||
month: '2026-08',
|
||||
kg: 1_000,
|
||||
lingniuKg: 700,
|
||||
externalKg: 300,
|
||||
fee: 4_500,
|
||||
revenue: 5_000,
|
||||
profit: 500,
|
||||
});
|
||||
assert.equal(payload.rows[0]?.category, '羚牛车辆');
|
||||
assert.equal(payload.rows[0]?.share, '70.0%');
|
||||
assert.equal(payload.rows[1]?.share, '30.0%');
|
||||
assert.equal(payload.metrics[3]?.label, '客户单毛利');
|
||||
});
|
||||
|
||||
test('省级区域下钻仅关联同省真实站点,市级缺少映射时明确空态', () => {
|
||||
const stations = [
|
||||
{ id: 1, name: '嘉兴站', province: '浙江', kg: 600, revenue: 1_000, share: 0.6, revenueShare: 0.5 },
|
||||
{ id: 2, name: '广州站', province: '广东', kg: 400, revenue: 1_000, share: 0.4, revenueShare: 0.5 },
|
||||
];
|
||||
const provincePayload = buildRegionDrillPayload({ region: '浙江', kg: 600, share: 0.6 }, stations, 'province');
|
||||
assert.equal(provincePayload.rows.length, 1);
|
||||
assert.equal(provincePayload.rows[0]?.station, '嘉兴站');
|
||||
|
||||
const cityPayload = buildRegionDrillPayload({ region: '嘉兴', kg: 600, share: 0.6 }, stations, 'city');
|
||||
assert.equal(cityPayload.rows.length, 0);
|
||||
assert.match(cityPayload.emptyMessage ?? '', /未返回城市字段/);
|
||||
});
|
||||
|
||||
test('站点与客户弹层提供汇总指标及真实事件入口', () => {
|
||||
const stationPayload = buildStationDrillPayload({ id: 9, name: '测试站', province: '广东', kg: 900, revenue: 3_000, share: 0.3, revenueShare: 0.4 });
|
||||
assert.equal(stationPayload.primaryActionLabel, '进入单站视图');
|
||||
assert.equal(stationPayload.metrics[0]?.value, '900.00 Kg');
|
||||
|
||||
const customerPayload = buildCustomerDrillPayload({ name: '测试客户', payer: 'customer', kg: 500, cost: 2_000, revenue: 2_500 });
|
||||
assert.equal(customerPayload.metrics[3]?.label, '价差');
|
||||
assert.equal(customerPayload.metrics[3]?.value, '¥500 元');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user