polish access difference workspace
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { accessRowsToCSV, formatSeconds, thresholdForProtocol, updateProtocolThreshold } from './access';
|
||||
import type { AccessVehicleRow } from '../../api/types';
|
||||
import { accessIssueSummary, accessRowsToCSV, formatAccessTime, formatSeconds, thresholdForProtocol, updateProtocolThreshold } from './access';
|
||||
|
||||
describe('access domain helpers', () => {
|
||||
it('formats duration without hiding sign or long offline windows', () => {
|
||||
@@ -16,6 +17,23 @@ describe('access domain helpers', () => {
|
||||
expect(updateProtocolThreshold(config.protocols, 'JT808', 120)).toEqual([{ protocol: 'JT808', thresholdSec: 120 }]);
|
||||
});
|
||||
|
||||
it('keeps access timestamps and actionable differences explicit', () => {
|
||||
expect(formatAccessTime('2026-07-18T02:33:00Z')).toBe('2026-07-18 10:33:00');
|
||||
const row = {
|
||||
vin: 'VIN1',
|
||||
plate: '粤A1',
|
||||
actualProtocols: ['GB32960', 'JT808'],
|
||||
masterDataIssues: [],
|
||||
connectionState: 'degraded',
|
||||
protocolStatuses: [
|
||||
{ protocol: 'GB32960', connected: true, onlineState: 'online', delayAbnormal: true },
|
||||
{ protocol: 'JT808', connected: true, onlineState: 'offline', delayAbnormal: true }
|
||||
]
|
||||
} as unknown as AccessVehicleRow;
|
||||
expect(accessIssueSummary(row)).toBe('JT808 已离线;GB32960 数据延迟异常');
|
||||
expect(accessIssueSummary({ ...row, actualProtocols: [], protocolStatuses: [], connectionState: 'not_connected' })).toBe('尚未发现真实接入来源');
|
||||
});
|
||||
|
||||
it('exports explicit state and evidence fields', () => {
|
||||
const csv = accessRowsToCSV([{ vin: 'VIN1', plate: '粤A1', oem: '', model: '', company: '示范企业', protocol: 'JT808', provider: '', source: '', firstSeenAt: '', latestEventAt: '', latestReceivedAt: '', reportIntervalSec: null, dataDelaySec: 2, freshnessSec: 3, onlineState: 'online', thresholdSec: 60, latestMessageType: '位置,数据', latestEventId: '', latestError: '', delayAbnormal: false, firstSeenEvidence: '', firstSeenSource: '', reportIntervalEvidence: '', reportSampleCount: 2, expectedProtocols: [], actualProtocols: ['JT808'], missingProtocols: [], masterDataIssues: ['车辆品牌未维护'], protocolStatuses: [{ protocol: 'JT808', expected: false, connected: true, provider: 'G7', firstSeenAt: '2026-07-01T00:00:00+08:00', latestEventAt: '', latestReceivedAt: '2026-07-15T09:00:00+08:00', reportIntervalSec: 10, dataDelaySec: 2, freshnessSec: 3, onlineState: 'online', thresholdSec: 60, delayAbnormal: false, firstSeenEvidence: '网关首次观测', reportIntervalEvidence: '连续样本' }], connectionState: 'incomplete', expectationEvidence: '尚未接入业务应接口径' }]);
|
||||
expect(csv).toContain('在线');
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import type { AccessProtocolThreshold, AccessThresholdConfig, AccessVehicleRow } from '../../api/types';
|
||||
|
||||
const accessTimeFormatter = new Intl.DateTimeFormat('zh-CN', {
|
||||
year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
timeZone: 'Asia/Shanghai'
|
||||
});
|
||||
|
||||
export const accessStateLabels: Record<AccessVehicleRow['onlineState'], string> = {
|
||||
@@ -29,6 +36,25 @@ export function formatAccessTime(value: string) {
|
||||
return accessTimeFormatter.format(parsed).replace(/\//g, '-');
|
||||
}
|
||||
|
||||
export function accessIssueSummary(row: AccessVehicleRow) {
|
||||
const issues: string[] = [];
|
||||
const connected = row.protocolStatuses.filter((status) => status.connected);
|
||||
const protocols = (items: typeof connected) => items.map((status) => status.protocol).join(' / ');
|
||||
const offline = connected.filter((status) => status.onlineState === 'offline');
|
||||
const uncertain = connected.filter((status) => status.onlineState === 'unknown' || status.onlineState === 'never_reported');
|
||||
const delayed = connected.filter((status) => status.delayAbnormal && status.onlineState === 'online');
|
||||
|
||||
if (!row.actualProtocols.length) issues.push('尚未发现真实接入来源');
|
||||
if (offline.length) issues.push(`${protocols(offline)} 已离线`);
|
||||
if (uncertain.length) issues.push(`${protocols(uncertain)} 状态待确认`);
|
||||
if (delayed.length) issues.push(`${protocols(delayed)} 数据延迟异常`);
|
||||
if (row.masterDataIssues.length) issues.push(row.masterDataIssues.slice(0, 2).join('、'));
|
||||
|
||||
if (issues.length) return issues.slice(0, 2).join(';');
|
||||
if (row.connectionState === 'healthy') return '已接来源状态正常';
|
||||
return '接入状态待核对';
|
||||
}
|
||||
|
||||
export function thresholdForProtocol(config: AccessThresholdConfig, protocol: string) {
|
||||
return config.protocols.find((item) => item.protocol === protocol)?.thresholdSec ?? config.defaultThresholdSec;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user