import assert from 'node:assert/strict' import { normalizeEntityDetail } from '../src/modules/cockpit/domain/entityDetail.js' const fieldOf = (detail, label) => detail.sections.flatMap((s) => s.fields).find((f) => f.label === label) const summaryOf = (detail, label) => detail.summary.find((s) => s.label === label) const NUMERIC = /^\d+(\.\d+)?$/ // 纯数字=没带单位 /** 数值型读数必须带单位:值形如 "10 万m³/d",不能是裸数字。 */ function assertUnit(label, value, expectedUnit) { assert.ok(value && value.includes(expectedUnit), label + ' 应有单位 ' + expectedUnit + ',实际: ' + value) assert.ok(!NUMERIC.test(value), label + ' 不得是裸数字: ' + value) } // —— 管网:长度/管径带单位,摘要与明细一致 —— const pipe = normalizeEntityDetail({ kind: 'facility', id: 'n1', category: 'PIPE_NETWORK' }, { category: 'PIPE_NETWORK', id: 'n1', name: '污水干管', status: '0', attributes: { networkType: '1', networkLevel: '0', lengthMeters: 1200, diameterMillimeters: 300, material: 'HDPE' }, }) assertUnit('长度(明细)', fieldOf(pipe, '长度').value, '米') assertUnit('管径(明细)', fieldOf(pipe, '管径').value, '毫米') assertUnit('长度(摘要)', summaryOf(pipe, '长度').value, '米') assertUnit('管径(摘要)', summaryOf(pipe, '管径').value, '毫米') assert.equal(fieldOf(pipe, '材质').value, 'HDPE') // —— 水厂 —— const plant = normalizeEntityDetail({ kind: 'facility', id: '1', category: 'WATER_PLANT' }, { category: 'WATER_PLANT', id: '1', name: '一水厂', status: '0', attributes: { designCapacity: 10, currentCapacity: 8.5, treatmentProcess: '过滤' }, }) assertUnit('设计能力(明细)', fieldOf(plant, '设计能力').value, '万m³/d') assertUnit('当前能力(明细)', fieldOf(plant, '当前能力').value, '万m³/d') assertUnit('当前能力(摘要)', summaryOf(plant, '当前能力').value, '万m³/d') // —— 泵站:设计流量是 m³/h —— const station = normalizeEntityDetail({ kind: 'facility', id: '2', category: 'PUMP_STATION' }, { category: 'PUMP_STATION', id: '2', name: '一号泵站', status: '0', attributes: { pumpCount: 3, designFlow: 1200, designHead: 25, powerCapacity: 90 }, }) assertUnit('水泵数量', fieldOf(station, '水泵数量').value, '台') assertUnit('设计流量(泵站)', fieldOf(station, '设计流量').value, 'm³/h') assertUnit('设计扬程', fieldOf(station, '设计扬程').value, 'm') assertUnit('装机功率', fieldOf(station, '装机功率').value, 'kW') assertUnit('设计流量(摘要)', summaryOf(station, '设计流量').value, 'm³/h') // —— 排水工程设施:设计流量是 m³/s(与泵站同名字段不同量纲) —— const engineering = normalizeEntityDetail({ kind: 'facility', id: 'f1', category: 'ENGINEERING_FACILITY' }, { category: 'ENGINEERING_FACILITY', id: 'f1', name: '东门闸', status: '0', attributes: { facilityType: '0', designFlow: 3.5, designHead: 4, storageCapacity: 1000 }, }) assertUnit('设计流量(工程设施)', fieldOf(engineering, '设计流量').value, 'm³/s') assertUnit('设计扬程(工程设施)', fieldOf(engineering, '设计扬程').value, 'm') assertUnit('调蓄容积', fieldOf(engineering, '调蓄容积').value, 'm³') // —— 应急资源 —— const resource = normalizeEntityDetail({ kind: 'emergency', id: 'e1', category: 'EMERGENCY_RESOURCE' }, { category: 'EMERGENCY_RESOURCE', id: 'e1', name: '应急水泵', attributes: { assetValue: 12000, useLifeYears: 8 }, }) assertUnit('资产价值', fieldOf(resource, '资产价值').value, '元') assertUnit('预计使用年限', fieldOf(resource, '预计使用年限').value, '年') // —— 防护目标 —— const target = normalizeEntityDetail({ kind: 'emergency', id: 'r1', category: 'PROTECTION_TARGET' }, { category: 'PROTECTION_TARGET', id: 'r1', name: '学校', attributes: { riskScore: 88 }, }) assertUnit('风险分值', fieldOf(target, '风险分值').value, '分') assertUnit('风险分值(摘要)', summaryOf(target, '风险分值').value, '分') // —— 预警/报警读数:实际值、阈值、超出幅度都带单位 —— const warning = normalizeEntityDetail({ kind: 'event', id: 'W1', family: 'WARNING' }, { warningNo: 'W1', warningName: '电导率预警', status: 'PENDING', warningLevel: 2, value: 454, unit: 'μS/cm', thresholdMinValue: 100, thresholdMaxValue: 5000, deviationRatio: 12.5, }) assertUnit('实际值', fieldOf(warning, '实际值').value, 'μS/cm') assertUnit('阈值', fieldOf(warning, '阈值').value, 'μS/cm') assertUnit('超出幅度', fieldOf(warning, '超出幅度').value, '%') // —— 命名规范:标签一律中文且不带括注 —— for (const detail of [pipe, plant, station, engineering, resource, target, warning]) { for (const section of detail.sections) { for (const item of section.fields) { assert.ok(/[\u4e00-\u9fff]/.test(item.label), '字段名必须中文: ' + item.label) assert.ok(!item.label.includes('('), '字段名不得带括注: ' + item.label) } } } console.log('t65-metric-units: 40/40')