feat(platform): add mileage impact scope board

This commit is contained in:
lingniu
2026-07-04 22:35:05 +08:00
parent 5dea611dd9
commit 6803d51b78
3 changed files with 197 additions and 2 deletions

View File

@@ -228,6 +228,54 @@ function mileageBIPublishText({
].join('\n');
}
function statisticsImpactText({
filters,
summary,
onlineSummary,
closureStatus,
confidenceStatus,
anomalyCount,
pageCoverageRate,
publishAuditConclusion,
statisticsURL,
vehicleURL
}: {
filters: Record<string, string>;
summary: MileageSummary;
onlineSummary: OnlineStatisticsSummary;
closureStatus: string;
confidenceStatus: string;
anomalyCount: number;
pageCoverageRate: number;
publishAuditConclusion: string;
statisticsURL: string;
vehicleURL?: string;
}) {
const keyword = filters.keyword?.trim() || '全部车辆';
const protocol = filters.protocol?.trim() || '全部来源';
const dateFrom = filters.dateFrom?.trim();
const dateTo = filters.dateTo?.trim();
const dateRange = dateFrom || dateTo ? `${dateFrom || '-'}${dateTo || '-'}` : '全部日期';
return [
'【统计影响范围】',
`车辆范围:${keyword}`,
`数据来源:${protocol}`,
`日期范围:${dateRange}`,
`影响车辆:${summary.vehicleCount.toLocaleString()}`,
`统计记录:${summary.recordCount.toLocaleString()}`,
`累计里程:${formatKm(summary.totalMileageKm)} km`,
`在线状态:${onlineSummary.onlineVehicleCount.toLocaleString()} 在线 / ${onlineSummary.offlineVehicleCount.toLocaleString()} 离线 / 在线率 ${formatPercent(onlineSummary.onlineRatePercent)}`,
`闭合状态:${closureStatus}`,
`发布判断:${publishAuditConclusion}`,
`可信度:${confidenceStatus}`,
`异常记录:${anomalyCount.toLocaleString()}`,
`当前页覆盖:${formatPercent(pageCoverageRate)}`,
`业务影响:${publishAuditConclusion === '可发布' ? '当前统计范围可进入运营和BI口径' : '当前统计范围需要先复核轨迹、RAW和离线车辆影响'}`,
`统计查询:${statisticsURL}`,
...(vehicleURL ? [`车辆服务:${vehicleURL}`] : [])
].join('\n');
}
type MileagePublishAuditItem = {
key: string;
label: string;
@@ -472,6 +520,40 @@ export function Mileage({
const publishReviewCount = publishAuditItems.filter((item) => item.status === 'review').length;
const publishAuditConclusion = publishBlockedCount > 0 ? '阻断发布' : publishReviewCount > 0 ? '复核后发布' : '可发布';
const publishAuditColor = publishBlockedCount > 0 ? 'red' as const : publishReviewCount > 0 ? 'orange' as const : 'green' as const;
const statisticsImpactColor = publishAuditColor;
const statisticsImpactLevel = publishAuditConclusion === '可发布' ? '可运营发布' : publishAuditConclusion === '阻断发布' ? '发布阻断' : '影响待复核';
const statisticsImpactItems = [
{
label: '影响车辆',
value: `${summary.vehicleCount.toLocaleString()}`,
detail: `${onlineVehicleCount.toLocaleString()} 在线 / ${offlineVehicleCount.toLocaleString()} 离线`,
color: offlineVehicleCount > 0 ? 'orange' as const : 'green' as const
},
{
label: '统计规模',
value: `${summary.recordCount.toLocaleString()}`,
detail: `${summary.sourceCount.toLocaleString()} 个来源,累计 ${formatKm(summary.totalMileageKm)} km`,
color: summary.recordCount > 0 ? 'blue' as const : 'grey' as const
},
{
label: '闭合影响',
value: closureStatus,
detail: closureActionText,
color: closureStatusColor
},
{
label: '异常影响',
value: `${anomalyRows.length.toLocaleString()}`,
detail: anomalyRows.length > 0 ? '异常会影响BI与运营日报需要回到证据复核。' : '当前明细未命中异常标记。',
color: anomalyRows.length > 0 ? 'orange' as const : 'green' as const
},
{
label: '发布判断',
value: publishAuditConclusion,
detail: publishAuditConclusion === '可发布' ? '统计范围闭合且审计项通过。' : '发布前需要补齐审计项证据。',
color: publishAuditColor
}
];
const maxDateMileage = Math.max(...dateSeries.map((item) => item.value), 0);
const maxSourceMileage = Math.max(...sourceSeries.map((item) => item.value), 0);
const filterSummary = [
@@ -610,6 +692,26 @@ export function Mileage({
'统计发布审计'
);
};
const copyStatisticsImpact = () => {
const vehicleURL = currentVehicleKeyword
? appURL(buildAppHash({ page: 'detail', keyword: currentVehicleKeyword, protocol: currentProtocol }))
: undefined;
copyText(
statisticsImpactText({
filters,
summary,
onlineSummary,
closureStatus,
confidenceStatus,
anomalyCount: anomalyRows.length,
pageCoverageRate,
publishAuditConclusion,
statisticsURL: appURL(window.location.hash || buildAppHash({ page: 'mileage', keyword: currentVehicleKeyword, protocol: currentProtocol })),
vehicleURL
}),
'统计影响范围'
);
};
const copyOnlineStatusHandoff = () => {
copyText(
onlineStatusHandoffText({
@@ -689,6 +791,35 @@ export function Mileage({
))}
</div>
</Card>
<Card
bordered
title={<Space><span></span><Button size="small" onClick={copyStatisticsImpact}></Button></Space>}
style={{ marginTop: 16 }}
>
<div className="vp-stat-impact-board">
<div className="vp-stat-impact-summary">
<Tag color={statisticsImpactColor}>{statisticsImpactLevel}</Tag>
<div className="vp-monitor-metric-value">{summary.vehicleCount.toLocaleString()} </div>
<Typography.Text type="secondary">
{summary.recordCount.toLocaleString()} {formatKm(summary.totalMileageKm)} km {offlineVehicleCount.toLocaleString()} 线
</Typography.Text>
<Space wrap>
<Tag color={confidenceColor}>{confidenceStatus}</Tag>
<Tag color={closureStatusColor}>{closureStatus}</Tag>
<Tag color={publishAuditColor}>{publishAuditConclusion}</Tag>
</Space>
</div>
<div className="vp-stat-impact-grid">
{statisticsImpactItems.map((item) => (
<div key={item.label} className="vp-stat-impact-item">
<Tag color={item.color}>{item.label}</Tag>
<strong>{item.value}</strong>
<Typography.Text type="secondary">{item.detail}</Typography.Text>
</div>
))}
</div>
</div>
</Card>
<Card bordered>
<Form key={JSON.stringify(filters)} initValues={filters} layout="horizontal" onSubmit={(values) => {
const nextFilters = values as Record<string, string>;