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']); });