Files
ln-bi/src/modules/assets/faults/analysis-model.test.ts
T
shishengliang 39ecde55d4
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
feat(assets): 完成故障维修运维 BI 与 Excel 报表导出
2026-10-13 16:07:11 +08:00

78 lines
5.8 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { mapFault, faultHandlers } from '../../../server/routes/asset-faults/model.js';
import type { FaultAnalysisRecord, FaultOption } from '../../../shared/assets/faults';
import { analyzeFaultRecords, filterFaultRecords, faultDimensionValues, faultParentTotals, FAULT_ANALYSIS_DIMENSIONS, FAULT_ANALYSIS_LABELS } from './analysis-model.js';
function fault(id: string, handlers: FaultOption[], manager = '客户业务经理', city = '上海市'): FaultAnalysisRecord {
const row = mapFault({ id, business_manager: manager, city_name: city }, new Map());
return { ...row, handlers, cell: row.assetCell };
}
const a = { value: 'id:1234567890123456789', label: '处置甲' };
const b = { value: 'id:2234567890123456789', label: '处置乙' };
const data = [fault('shared', [a, b, a]), fault('single', [a]), fault('none', [])];
test('运维处置人是独立维度,未分配不以业务负责人兜底', () => {
assert.ok(FAULT_ANALYSIS_DIMENSIONS.includes('handler')); assert.ok(FAULT_ANALYSIS_DIMENSIONS.includes('manager'));
assert.equal(FAULT_ANALYSIS_LABELS.handler, '运维处置人');
assert.deepEqual(faultDimensionValues(data[0], 'handler'), [a.value, b.value]);
assert.deepEqual(faultDimensionValues(data[0], 'manager'), ['客户业务经理']);
assert.deepEqual(faultDimensionValues(data[2], 'handler'), ['unassigned']);
assert.deepEqual(faultHandlers({ business_manager: '客户业务经理' }, new Map()), [{ value: 'unassigned', label: '未指派' }]);
});
test('多人各自成组,总数和组内明细按故障ID去重,父级小计取并集', () => {
const result = analyzeFaultRecords([...data, data[0]], ['city', 'handler']);
assert.equal(result.total, 3);
assert.equal(result.rows.find(row => row.values.handler === a.value)?.count, 2);
assert.equal(result.rows.find(row => row.values.handler === b.value)?.count, 1);
assert.equal(result.rows.reduce((n, row) => n + row.count, 0), 4);
assert.ok(faultParentTotals(result.rows, ['city', 'handler']).every(row => row[0] === 3));
for (const group of result.rows) assert.equal(new Set(group.recordIds).size, group.count);
});
test('多选处置人取并集,不分组时不放大;筛选甲后不出现共享故障的乙分组', () => {
assert.equal(filterFaultRecords(data, { handler: [a.value, b.value] }).length, 2);
assert.equal(analyzeFaultRecords(data, ['city'], { handler: [a.value, b.value] }).rows[0].count, 2);
const result = analyzeFaultRecords(data, ['handler'], { handler: [a.value] });
assert.equal(result.total, 2); assert.equal(result.rows.length, 1); assert.equal(result.rows[0].values.handler, a.value);
assert.equal(filterFaultRecords(data, { handler: [a.value], manager: ['其他业务经理'] }).length, 0);
assert.equal(filterFaultRecords(data, { handler: ['unassigned'] })[0].id, 'none');
});
test('同名处置人保持ID独立,组合筛选与组明细对应,空数据安全', () => {
const rows = [fault('one', [{ ...a, label: '同名' }]), fault('two', [{ ...b, label: '同名' }])];
const result = analyzeFaultRecords(rows, ['manager', 'handler']);
assert.equal(result.rows.length, 2);
for (const group of result.rows) {
const matched = filterFaultRecords(rows, { manager: [group.values.manager!], handler: [group.values.handler!] });
assert.deepEqual(matched.map(row => row.id), group.recordIds);
}
assert.deepEqual(analyzeFaultRecords([], ['handler']), { total: 0, rows: [] });
});
test('故障工单状态按归档闭环,与车辆运营状态独立;组合分组、筛选和去重口径一致', () => {
const states = ['已归档', '待处理', '处理中', '挂起', '未知状态'];
const rows = states.map((status, i) => ({ ...fault(String(i), i === 0 ? [a, b] : [a]), status }));
assert.deepEqual(rows.map(row => faultDimensionValues(row, 'faultStatus')), [['closed'], ['unclosed'], ['unclosed'], ['unclosed'], ['unclosed']]);
const result = analyzeFaultRecords(rows, ['handler', 'faultStatus']);
assert.equal(result.total, 5);
assert.equal(result.rows.find(row => row.values.handler === a.value && row.values.faultStatus === 'closed')?.count, 1);
assert.equal(result.rows.find(row => row.values.handler === a.value && row.values.faultStatus === 'unclosed')?.count, 4);
const parentTotals = faultParentTotals(result.rows, ['handler', 'faultStatus']);
result.rows.forEach((row, i) => assert.equal(parentTotals[i][0], row.values.handler === a.value ? 5 : 1));
assert.deepEqual(filterFaultRecords(rows, { faultStatus: ['closed'], handler: [a.value, b.value] }).map(row => row.id), ['0']);
assert.equal(filterFaultRecords(rows, { faultStatus: ['closed', 'unclosed'] }).length, 5);
assert.deepEqual(filterFaultRecords(rows, { faultStatus: ['unclosed'], handler: [b.value] }), []);
for (const group of result.rows) assert.deepEqual(filterFaultRecords(rows, { handler: [group.values.handler!], faultStatus: [group.values.faultStatus!] }).map(row => row.id), group.recordIds);
});
test('车牌位于第三个维度,同车多故障按单计数,支持组合筛选和缺失车牌', () => {
assert.equal(FAULT_ANALYSIS_DIMENSIONS[2], 'plate');
const rows = [{ ...fault('1', [a]), plate: '沪A12345' }, { ...fault('2', [b]), plate: '沪A12345' }, { ...fault('3', [a]), plate: '粤A12345' }, { ...fault('4', []), plate: '' }];
const grouped = analyzeFaultRecords(rows, ['plate']);
assert.equal(grouped.total, 4);
assert.equal(grouped.rows.find(row => row.values.plate === '沪A12345')?.count, 2);
assert.deepEqual(filterFaultRecords(rows, { plate: ['沪A12345'], handler: [b.value] }).map(row => row.id), ['2']);
assert.equal(filterFaultRecords(rows, { plate: ['沪A12345', '粤A12345'] }).length, 3);
assert.deepEqual(filterFaultRecords(rows, { plate: ['未关联车辆'] }).map(row => row.id), ['4']);
});