t65-metric-units.mjs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import assert from 'node:assert/strict'
  2. import { normalizeEntityDetail } from '../src/modules/cockpit/domain/entityDetail.js'
  3. const fieldOf = (detail, label) => detail.sections.flatMap((s) => s.fields).find((f) => f.label === label)
  4. const summaryOf = (detail, label) => detail.summary.find((s) => s.label === label)
  5. const NUMERIC = /^\d+(\.\d+)?$/ // 纯数字=没带单位
  6. /** 数值型读数必须带单位:值形如 "10 万m³/d",不能是裸数字。 */
  7. function assertUnit(label, value, expectedUnit) {
  8. assert.ok(value && value.includes(expectedUnit), label + ' 应有单位 ' + expectedUnit + ',实际: ' + value)
  9. assert.ok(!NUMERIC.test(value), label + ' 不得是裸数字: ' + value)
  10. }
  11. // —— 管网:长度/管径带单位,摘要与明细一致 ——
  12. const pipe = normalizeEntityDetail({ kind: 'facility', id: 'n1', category: 'PIPE_NETWORK' }, {
  13. category: 'PIPE_NETWORK', id: 'n1', name: '污水干管', status: '0',
  14. attributes: { networkType: '1', networkLevel: '0', lengthMeters: 1200, diameterMillimeters: 300, material: 'HDPE' },
  15. })
  16. assertUnit('长度(明细)', fieldOf(pipe, '长度').value, '米')
  17. assertUnit('管径(明细)', fieldOf(pipe, '管径').value, '毫米')
  18. assertUnit('长度(摘要)', summaryOf(pipe, '长度').value, '米')
  19. assertUnit('管径(摘要)', summaryOf(pipe, '管径').value, '毫米')
  20. assert.equal(fieldOf(pipe, '材质').value, 'HDPE')
  21. // —— 水厂 ——
  22. const plant = normalizeEntityDetail({ kind: 'facility', id: '1', category: 'WATER_PLANT' }, {
  23. category: 'WATER_PLANT', id: '1', name: '一水厂', status: '0',
  24. attributes: { designCapacity: 10, currentCapacity: 8.5, treatmentProcess: '过滤' },
  25. })
  26. assertUnit('设计能力(明细)', fieldOf(plant, '设计能力').value, '万m³/d')
  27. assertUnit('当前能力(明细)', fieldOf(plant, '当前能力').value, '万m³/d')
  28. assertUnit('当前能力(摘要)', summaryOf(plant, '当前能力').value, '万m³/d')
  29. // —— 泵站:设计流量是 m³/h ——
  30. const station = normalizeEntityDetail({ kind: 'facility', id: '2', category: 'PUMP_STATION' }, {
  31. category: 'PUMP_STATION', id: '2', name: '一号泵站', status: '0',
  32. attributes: { pumpCount: 3, designFlow: 1200, designHead: 25, powerCapacity: 90 },
  33. })
  34. assertUnit('水泵数量', fieldOf(station, '水泵数量').value, '台')
  35. assertUnit('设计流量(泵站)', fieldOf(station, '设计流量').value, 'm³/h')
  36. assertUnit('设计扬程', fieldOf(station, '设计扬程').value, 'm')
  37. assertUnit('装机功率', fieldOf(station, '装机功率').value, 'kW')
  38. assertUnit('设计流量(摘要)', summaryOf(station, '设计流量').value, 'm³/h')
  39. // —— 排水工程设施:设计流量是 m³/s(与泵站同名字段不同量纲) ——
  40. const engineering = normalizeEntityDetail({ kind: 'facility', id: 'f1', category: 'ENGINEERING_FACILITY' }, {
  41. category: 'ENGINEERING_FACILITY', id: 'f1', name: '东门闸', status: '0',
  42. attributes: { facilityType: '0', designFlow: 3.5, designHead: 4, storageCapacity: 1000 },
  43. })
  44. assertUnit('设计流量(工程设施)', fieldOf(engineering, '设计流量').value, 'm³/s')
  45. assertUnit('设计扬程(工程设施)', fieldOf(engineering, '设计扬程').value, 'm')
  46. assertUnit('调蓄容积', fieldOf(engineering, '调蓄容积').value, 'm³')
  47. // —— 应急资源 ——
  48. const resource = normalizeEntityDetail({ kind: 'emergency', id: 'e1', category: 'EMERGENCY_RESOURCE' }, {
  49. category: 'EMERGENCY_RESOURCE', id: 'e1', name: '应急水泵', attributes: { assetValue: 12000, useLifeYears: 8 },
  50. })
  51. assertUnit('资产价值', fieldOf(resource, '资产价值').value, '元')
  52. assertUnit('预计使用年限', fieldOf(resource, '预计使用年限').value, '年')
  53. // —— 防护目标 ——
  54. const target = normalizeEntityDetail({ kind: 'emergency', id: 'r1', category: 'PROTECTION_TARGET' }, {
  55. category: 'PROTECTION_TARGET', id: 'r1', name: '学校', attributes: { riskScore: 88 },
  56. })
  57. assertUnit('风险分值', fieldOf(target, '风险分值').value, '分')
  58. assertUnit('风险分值(摘要)', summaryOf(target, '风险分值').value, '分')
  59. // —— 预警/报警读数:实际值、阈值、超出幅度都带单位 ——
  60. const warning = normalizeEntityDetail({ kind: 'event', id: 'W1', family: 'WARNING' }, {
  61. warningNo: 'W1', warningName: '电导率预警', status: 'PENDING', warningLevel: 2,
  62. value: 454, unit: 'μS/cm', thresholdMinValue: 100, thresholdMaxValue: 5000, deviationRatio: 12.5,
  63. })
  64. assertUnit('实际值', fieldOf(warning, '实际值').value, 'μS/cm')
  65. assertUnit('阈值', fieldOf(warning, '阈值').value, 'μS/cm')
  66. assertUnit('超出幅度', fieldOf(warning, '超出幅度').value, '%')
  67. // —— 命名规范:标签一律中文且不带括注 ——
  68. for (const detail of [pipe, plant, station, engineering, resource, target, warning]) {
  69. for (const section of detail.sections) {
  70. for (const item of section.fields) {
  71. assert.ok(/[\u4e00-\u9fff]/.test(item.label), '字段名必须中文: ' + item.label)
  72. assert.ok(!item.label.includes('('), '字段名不得带括注: ' + item.label)
  73. }
  74. }
  75. }
  76. console.log('t65-metric-units: 40/40')