feat(platform): add mileage impact scope board
This commit is contained in:
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user