|
|
@@ -0,0 +1,558 @@
|
|
|
+package com.zksy.base.perception.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.zksy.base.domain.EquipmentBase;
|
|
|
+import com.zksy.base.domain.EquipmentStatus;
|
|
|
+import com.zksy.base.domain.EquipmentType;
|
|
|
+import com.zksy.base.domain.vo.EquipmentBaseVO;
|
|
|
+import com.zksy.base.mapper.EquipmentBaseMapper;
|
|
|
+import com.zksy.base.mapper.EquipmentStatusMapper;
|
|
|
+import com.zksy.base.mapper.EquipmentTypeMapper;
|
|
|
+import com.zksy.base.perception.domain.vo.AlarmTypeDistVO;
|
|
|
+import com.zksy.base.perception.domain.vo.AlarmVO;
|
|
|
+import com.zksy.base.perception.domain.vo.AreaRankVO;
|
|
|
+import com.zksy.base.perception.domain.vo.EquipmentProfileVO;
|
|
|
+import com.zksy.base.perception.domain.vo.EquipmentRealtimeVO;
|
|
|
+import com.zksy.base.perception.mapper.PerceptionMapper;
|
|
|
+import com.zksy.base.perception.service.PerceptionService;
|
|
|
+import com.zksy.base.service.EquipmentBaseService;
|
|
|
+import com.zksy.base.alarm.domain.AlarmData;
|
|
|
+import com.zksy.base.alarm.mapper.AlarmDataMapper;
|
|
|
+import com.zksy.base.warning.domain.EquipmentWarningType;
|
|
|
+import com.zksy.base.warning.domain.WarningType;
|
|
|
+import com.zksy.base.warning.mapper.EquipmentWarningTypeMapper;
|
|
|
+import com.zksy.base.warning.mapper.WarningTypeMapper;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.text.DecimalFormat;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.time.temporal.TemporalAccessor;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 感知信息实时展示服务实现。
|
|
|
+ * 设备最后通讯时间 / 采集历史均按 equipment_type.orig_coll_table 动态定位原始采集表。
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class PerceptionServiceImpl implements PerceptionService {
|
|
|
+
|
|
|
+ /** 原始采集表 -> 设备标识列(白名单,防止动态表名 SQL 注入)。 */
|
|
|
+ private static final Map<String, String> TABLE_ID_COLUMN = new LinkedHashMap<>();
|
|
|
+ static {
|
|
|
+ TABLE_ID_COLUMN.put("e_real_time_data", "device_id");
|
|
|
+ TABLE_ID_COLUMN.put("firefighting_pressure", "telemetering_station");
|
|
|
+ TABLE_ID_COLUMN.put("gas_monitor_data", "mac_address");
|
|
|
+ TABLE_ID_COLUMN.put("jg_device_data", "imei_card_number");
|
|
|
+ TABLE_ID_COLUMN.put("radar_data", "device_code");
|
|
|
+ TABLE_ID_COLUMN.put("telemetry_data", "device_code");
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 片区编码 -> 名称。 */
|
|
|
+ private static final Map<Integer, String> DISTRICT_NAMES = new HashMap<>();
|
|
|
+ static {
|
|
|
+ DISTRICT_NAMES.put(0, "城南");
|
|
|
+ DISTRICT_NAMES.put(1, "城北");
|
|
|
+ DISTRICT_NAMES.put(2, "太常");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private EquipmentBaseService equipmentBaseService;
|
|
|
+ @Autowired
|
|
|
+ private EquipmentBaseMapper equipmentBaseMapper;
|
|
|
+ @Autowired
|
|
|
+ private EquipmentTypeMapper equipmentTypeMapper;
|
|
|
+ @Autowired
|
|
|
+ private AlarmDataMapper alarmDataMapper;
|
|
|
+ @Autowired
|
|
|
+ private WarningTypeMapper warningTypeMapper;
|
|
|
+ @Autowired
|
|
|
+ private EquipmentWarningTypeMapper equipmentWarningTypeMapper;
|
|
|
+ @Autowired
|
|
|
+ private PerceptionMapper perceptionMapper;
|
|
|
+ @Autowired
|
|
|
+ private EquipmentStatusMapper equipmentStatusMapper;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Page<EquipmentRealtimeVO> pageEquipment(long pageNum, long pageSize, String equipmentCode,
|
|
|
+ String equipmentName, Integer equipmentTypeId, Integer rootTypeId,
|
|
|
+ Integer onlineStatus, Integer alarmStatus,
|
|
|
+ Integer district, Integer currentStatus) {
|
|
|
+ Page<EquipmentBaseVO> basePage = equipmentBaseService.findByPage(pageNum, pageSize, equipmentCode,
|
|
|
+ equipmentName, equipmentTypeId, rootTypeId, null, onlineStatus, alarmStatus,
|
|
|
+ null, district, currentStatus);
|
|
|
+ Page<EquipmentRealtimeVO> result = new Page<>(basePage.getCurrent(), basePage.getSize(), basePage.getTotal());
|
|
|
+ List<EquipmentRealtimeVO> records = new ArrayList<>();
|
|
|
+ for (EquipmentBaseVO vo : basePage.getRecords()) {
|
|
|
+ EquipmentRealtimeVO rv = new EquipmentRealtimeVO();
|
|
|
+ BeanUtils.copyProperties(vo, rv);
|
|
|
+ rv.setLastCommTime(queryLastCommTime(vo.getEquipmentId()));
|
|
|
+ records.add(rv);
|
|
|
+ }
|
|
|
+ result.setRecords(records);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private LocalDateTime queryLastCommTime(String equipmentId) {
|
|
|
+ EquipmentBase base = equipmentBaseMapper.selectById(equipmentId);
|
|
|
+ if (base == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String table = resolveTable(base.getEquipmentTypeId());
|
|
|
+ if (table == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return perceptionMapper.selectLastCommTime(table, TABLE_ID_COLUMN.get(table), base.getEquipmentCode());
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 由 equipment_type_id -> equipment_type.orig_coll_table 解析出原始采集表;不在白名单返回 null。 */
|
|
|
+ private String resolveTable(Integer equipmentTypeId) {
|
|
|
+ if (equipmentTypeId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ EquipmentType type = equipmentTypeMapper.selectById(equipmentTypeId);
|
|
|
+ if (type == null || type.getOrigCollTable() == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String table = type.getOrigCollTable().trim();
|
|
|
+ return TABLE_ID_COLUMN.containsKey(table) ? table : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public EquipmentProfileVO getProfile(String equipmentId) {
|
|
|
+ EquipmentBase base = equipmentBaseMapper.selectById(equipmentId);
|
|
|
+ EquipmentProfileVO profile = new EquipmentProfileVO();
|
|
|
+ if (base == null) {
|
|
|
+ return profile;
|
|
|
+ }
|
|
|
+ EquipmentBaseVO vo = new EquipmentBaseVO();
|
|
|
+ BeanUtils.copyProperties(base, vo);
|
|
|
+ if (base.getEquipmentTypeId() != null) {
|
|
|
+ vo.setEquipmentTypeId(String.valueOf(base.getEquipmentTypeId()));
|
|
|
+ }
|
|
|
+ EquipmentStatus status = equipmentStatusMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<EquipmentStatus>().eq(EquipmentStatus::getEquipmentId, equipmentId));
|
|
|
+ if (status != null) {
|
|
|
+ vo.setCurrentStatus(status.getCurrentStatus());
|
|
|
+ vo.setAlarmStatus(status.getAlarmStatus());
|
|
|
+ vo.setOnlineStatus(status.getOnlineStatus());
|
|
|
+ }
|
|
|
+ profile.setBase(vo);
|
|
|
+ profile.setLastCommTime(queryLastCommTime(equipmentId));
|
|
|
+
|
|
|
+ EquipmentType type = equipmentTypeMapper.selectById(base.getEquipmentTypeId());
|
|
|
+ if (type != null) {
|
|
|
+ profile.setOrigCollTable(type.getOrigCollTable());
|
|
|
+ vo.setEquipmentTypeName(type.getTypeName());
|
|
|
+ }
|
|
|
+ String table = resolveTable(base.getEquipmentTypeId());
|
|
|
+ List<Map<String, Object>> rawRecords;
|
|
|
+ if (table != null) {
|
|
|
+ profile.setIdColumn(TABLE_ID_COLUMN.get(table));
|
|
|
+ rawRecords = perceptionMapper.selectLatestRecords(table, TABLE_ID_COLUMN.get(table),
|
|
|
+ base.getEquipmentCode(), 50);
|
|
|
+ } else {
|
|
|
+ rawRecords = Collections.emptyList();
|
|
|
+ }
|
|
|
+ // 保留原始记录(前端兼容),同时生成按表定义的展示视图
|
|
|
+ profile.setRecords(rawRecords);
|
|
|
+ profile.setRecordView(buildRecordView(table, rawRecords));
|
|
|
+ profile.setIndicators(queryIndicators(base.getEquipmentTypeId()));
|
|
|
+ return profile;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 设备类型关联的监测指标(名称 + 单位),参考 warning_type。 */
|
|
|
+ private List<EquipmentProfileVO.WarningIndicator> queryIndicators(Integer equipmentTypeId) {
|
|
|
+ List<EquipmentProfileVO.WarningIndicator> list = new ArrayList<>();
|
|
|
+ if (equipmentTypeId == null) {
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+ List<EquipmentWarningType> rels = equipmentWarningTypeMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<EquipmentWarningType>().eq(EquipmentWarningType::getEquipmentTypeId, equipmentTypeId));
|
|
|
+ if (rels.isEmpty()) {
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+ List<Integer> wtIds = rels.stream().map(EquipmentWarningType::getWarningTypeId).collect(Collectors.toList());
|
|
|
+ List<WarningType> types = warningTypeMapper.selectBatchIds(wtIds);
|
|
|
+ Map<Integer, WarningType> typeMap = types.stream().collect(Collectors.toMap(WarningType::getId, t -> t, (a, b) -> a));
|
|
|
+ for (EquipmentWarningType rel : rels) {
|
|
|
+ WarningType wt = typeMap.get(rel.getWarningTypeId());
|
|
|
+ if (wt == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ EquipmentProfileVO.WarningIndicator ind = new EquipmentProfileVO.WarningIndicator();
|
|
|
+ ind.setWarningCode(wt.getWarningCode());
|
|
|
+ ind.setWarningName(wt.getWarningName());
|
|
|
+ ind.setWarningUnit(wt.getWarningUnit());
|
|
|
+ list.add(ind);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Page<AlarmVO> pageActiveAlarms(long pageNum, long pageSize) {
|
|
|
+ Page<AlarmData> page = alarmDataMapper.selectPage(new Page<>(pageNum, pageSize),
|
|
|
+ new LambdaQueryWrapper<AlarmData>()
|
|
|
+ .eq(AlarmData::getAuditStatus, 1)
|
|
|
+ .eq(AlarmData::getAlarmStatus, 0)
|
|
|
+ .orderByDesc(AlarmData::getCreateTime));
|
|
|
+
|
|
|
+ List<WarningType> wtList = warningTypeMapper.selectList(null);
|
|
|
+ Map<String, WarningType> wtMap = wtList.stream()
|
|
|
+ .collect(Collectors.toMap(WarningType::getWarningCode, w -> w, (a, b) -> a));
|
|
|
+
|
|
|
+ List<String> codes = page.getRecords().stream().map(AlarmData::getDeviceCode)
|
|
|
+ .filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
|
|
+ Map<String, EquipmentBase> devMap = new HashMap<>();
|
|
|
+ if (!codes.isEmpty()) {
|
|
|
+ devMap = equipmentBaseMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<EquipmentBase>().in(EquipmentBase::getEquipmentCode, codes))
|
|
|
+ .stream().collect(Collectors.toMap(EquipmentBase::getEquipmentCode, e -> e, (a, b) -> a));
|
|
|
+ }
|
|
|
+
|
|
|
+ Page<AlarmVO> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
|
|
|
+ List<AlarmVO> records = new ArrayList<>();
|
|
|
+ for (AlarmData a : page.getRecords()) {
|
|
|
+ AlarmVO vo = new AlarmVO();
|
|
|
+ BeanUtils.copyProperties(a, vo);
|
|
|
+ WarningType wt = wtMap.get(a.getWarningCode());
|
|
|
+ if (wt != null) {
|
|
|
+ vo.setWarningName(wt.getWarningName());
|
|
|
+ vo.setWarningUnit(wt.getWarningUnit());
|
|
|
+ }
|
|
|
+ EquipmentBase dev = devMap.get(a.getDeviceCode());
|
|
|
+ if (dev != null) {
|
|
|
+ vo.setDeviceName(dev.getEquipmentName());
|
|
|
+ vo.setDeviceLocation(dev.getEquipmentLocation());
|
|
|
+ }
|
|
|
+ records.add(vo);
|
|
|
+ }
|
|
|
+ result.setRecords(records);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<AreaRankVO> areaRank() {
|
|
|
+ List<Map<String, Object>> rows = perceptionMapper.selectAreaStats();
|
|
|
+ List<AreaRankVO> list = new ArrayList<>();
|
|
|
+ for (Map<String, Object> row : rows) {
|
|
|
+ AreaRankVO vo = new AreaRankVO();
|
|
|
+ Integer district = row.get("district") == null ? null : ((Number) row.get("district")).intValue();
|
|
|
+ vo.setDistrict(district);
|
|
|
+ vo.setDistrictName(DISTRICT_NAMES.getOrDefault(district, "未知"));
|
|
|
+ vo.setTotal(toLong(row.get("total")));
|
|
|
+ vo.setOnline(toLong(row.get("online")));
|
|
|
+ vo.setOffline(toLong(row.get("offline")));
|
|
|
+ vo.setAlarm(toLong(row.get("alarm")));
|
|
|
+ list.add(vo);
|
|
|
+ }
|
|
|
+ // 按在线率降序排行(在线率 = online / total),并列时按在线数降序
|
|
|
+ list.sort((x, y) -> {
|
|
|
+ double rx = (x.getTotal() == null || x.getTotal() == 0) ? 0 : (double) x.getOnline() / x.getTotal();
|
|
|
+ double ry = (y.getTotal() == null || y.getTotal() == 0) ? 0 : (double) y.getOnline() / y.getTotal();
|
|
|
+ if (rx != ry) return Double.compare(ry, rx);
|
|
|
+ return Long.compare(y.getOnline() == null ? 0 : y.getOnline(), x.getOnline() == null ? 0 : x.getOnline());
|
|
|
+ });
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<AlarmTypeDistVO> alarmTypeDist() {
|
|
|
+ List<Map<String, Object>> rows = perceptionMapper.selectAlarmTypeDist();
|
|
|
+ List<WarningType> wtList = warningTypeMapper.selectList(null);
|
|
|
+ Map<String, WarningType> wtMap = wtList.stream()
|
|
|
+ .collect(Collectors.toMap(WarningType::getWarningCode, w -> w, (a, b) -> a));
|
|
|
+ List<AlarmTypeDistVO> list = new ArrayList<>();
|
|
|
+ for (Map<String, Object> row : rows) {
|
|
|
+ String code = (String) row.get("warningCode");
|
|
|
+ AlarmTypeDistVO vo = new AlarmTypeDistVO();
|
|
|
+ vo.setWarningCode(code);
|
|
|
+ vo.setCount(toLong(row.get("cnt")));
|
|
|
+ WarningType wt = wtMap.get(code);
|
|
|
+ if (wt != null) {
|
|
|
+ vo.setWarningName(wt.getWarningName());
|
|
|
+ vo.setWarningUnit(wt.getWarningUnit());
|
|
|
+ }
|
|
|
+ list.add(vo);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按原始采集表类型,把最近 50 条原始记录转换为前端可直接渲染的展示视图。
|
|
|
+ * 列名 / 字段说明 / 单位 / 展示形式严格按各原始表定义。表中缺失的列优雅降级为"—"。
|
|
|
+ */
|
|
|
+ private EquipmentProfileVO.RecordView buildRecordView(String table, List<Map<String, Object>> raw) {
|
|
|
+ EquipmentProfileVO.RecordView view = new EquipmentProfileVO.RecordView();
|
|
|
+ view.setColumns(new ArrayList<>());
|
|
|
+ view.setRows(new ArrayList<>());
|
|
|
+ if (raw == null) {
|
|
|
+ raw = Collections.emptyList();
|
|
|
+ }
|
|
|
+ if (table == null) {
|
|
|
+ table = "";
|
|
|
+ }
|
|
|
+ switch (table) {
|
|
|
+ case "e_real_time_data":
|
|
|
+ // 竖表:一行一指标,node_id 决定监测因子
|
|
|
+ view.setLayout("long");
|
|
|
+ view.setColumns(Arrays.asList(
|
|
|
+ column("create_time", "采集时间", "", false),
|
|
|
+ column("indicator", "水质指标", "", false)));
|
|
|
+ for (Map<String, Object> r : raw) {
|
|
|
+ EquipmentProfileVO.RecordRow row = new EquipmentProfileVO.RecordRow();
|
|
|
+ row.setCreateTime(asString(r.get("create_time")));
|
|
|
+ Integer node = toInt(r.get("node_id"));
|
|
|
+ String src;
|
|
|
+ String name;
|
|
|
+ String unit;
|
|
|
+ if (node != null && node == 1) {
|
|
|
+ name = "悬浮物";
|
|
|
+ src = "float_value";
|
|
|
+ unit = "mg/L";
|
|
|
+ } else if (node != null && node == 2) {
|
|
|
+ name = "COD";
|
|
|
+ src = "hum";
|
|
|
+ unit = "mg/L";
|
|
|
+ } else if (node != null && node == 3) {
|
|
|
+ name = "氨氮";
|
|
|
+ src = "hum";
|
|
|
+ unit = "mg/L";
|
|
|
+ } else if (node != null && node == 4) {
|
|
|
+ name = "电导率";
|
|
|
+ src = "hum";
|
|
|
+ unit = "μS/cm";
|
|
|
+ } else if (node != null && node == 5) {
|
|
|
+ name = "PH";
|
|
|
+ src = "hum";
|
|
|
+ unit = "";
|
|
|
+ } else {
|
|
|
+ name = "未知因子";
|
|
|
+ src = "hum";
|
|
|
+ unit = "";
|
|
|
+ }
|
|
|
+ EquipmentProfileVO.RecordCell cell = new EquipmentProfileVO.RecordCell();
|
|
|
+ cell.setKey("indicator");
|
|
|
+ String disp = name + ":" + formatVal(r.get(src), unit);
|
|
|
+ cell.setDisplay(disp);
|
|
|
+ cell.setValue(disp);
|
|
|
+ cell.setAlarm(false);
|
|
|
+ row.setCells(Collections.singletonList(cell));
|
|
|
+ view.getRows().add(row);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case "firefighting_pressure":
|
|
|
+ view.setLayout("wide");
|
|
|
+ view.setColumns(Arrays.asList(
|
|
|
+ column("create_time", "采集时间", "", false),
|
|
|
+ column("pressure_value", "压力", "MPa", false)));
|
|
|
+ view.setRows(mapWide(raw, Collections.singletonList(
|
|
|
+ new ColDef("pressure_value", "压力", "MPa", false))));
|
|
|
+ break;
|
|
|
+ case "gas_monitor_data": {
|
|
|
+ List<ColDef> defs = Arrays.asList(
|
|
|
+ new ColDef("gas_concentration", "气体浓度", "%LEL", false),
|
|
|
+ new ColDef("temperature", "温度", "℃", false),
|
|
|
+ new ColDef("humidity", "湿度", "%RH", false),
|
|
|
+ new ColDef("battery", "电池电量", "%", false));
|
|
|
+ view.setLayout("wide");
|
|
|
+ view.setColumns(columnsWithTime(defs));
|
|
|
+ view.setRows(mapWide(raw, defs));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "jg_device_data": {
|
|
|
+ List<ColDef> defs = Arrays.asList(
|
|
|
+ new ColDef("tilt_angle", "倾斜角度", "°", false),
|
|
|
+ new ColDef("water_infiltration_alarm_status", "水浸报警状态", "", true),
|
|
|
+ new ColDef("water_level_alarm_status", "水位报警状态", "", true),
|
|
|
+ new ColDef("temperature_value", "温度", "℃", false),
|
|
|
+ new ColDef("battery_level", "电池电量", "%", false));
|
|
|
+ view.setLayout("wide");
|
|
|
+ view.setColumns(columnsWithTime(defs));
|
|
|
+ view.setRows(mapWide(raw, defs));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "radar_data": {
|
|
|
+ List<ColDef> defs = Arrays.asList(
|
|
|
+ new ColDef("meter1_net_total", "累计流量", "m³/h", false),
|
|
|
+ new ColDef("meter1_instant_flow", "瞬时流量", "m³/h", false),
|
|
|
+ new ColDef("flow_speed", "流速", "m/s", false),
|
|
|
+ new ColDef("water_level", "水位", "m", false));
|
|
|
+ view.setLayout("wide");
|
|
|
+ view.setColumns(columnsWithTime(defs));
|
|
|
+ view.setRows(mapWide(raw, defs));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "telemetry_data": {
|
|
|
+ List<ColDef> defs = Arrays.asList(
|
|
|
+ new ColDef("pressure", "压力", "MPa", false),
|
|
|
+ new ColDef("flow_speed", "流速", "m/s", false),
|
|
|
+ new ColDef("temperature", "温度", "℃", false));
|
|
|
+ view.setLayout("wide");
|
|
|
+ view.setColumns(columnsWithTime(defs));
|
|
|
+ view.setRows(mapWide(raw, defs));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ view.setLayout("wide");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return view;
|
|
|
+ }
|
|
|
+
|
|
|
+ private EquipmentProfileVO.RecordColumn column(String key, String label, String unit, boolean alarm) {
|
|
|
+ EquipmentProfileVO.RecordColumn c = new EquipmentProfileVO.RecordColumn();
|
|
|
+ c.setKey(key);
|
|
|
+ c.setLabel(label);
|
|
|
+ c.setUnit(unit);
|
|
|
+ c.setAlarm(alarm);
|
|
|
+ return c;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<EquipmentProfileVO.RecordColumn> columnsWithTime(List<ColDef> defs) {
|
|
|
+ List<EquipmentProfileVO.RecordColumn> cols = new ArrayList<>();
|
|
|
+ cols.add(column("create_time", "采集时间", "", false));
|
|
|
+ for (ColDef d : defs) {
|
|
|
+ cols.add(d.toColumn());
|
|
|
+ }
|
|
|
+ return cols;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<EquipmentProfileVO.RecordRow> mapWide(List<Map<String, Object>> raw, List<ColDef> defs) {
|
|
|
+ List<EquipmentProfileVO.RecordRow> rows = new ArrayList<>();
|
|
|
+ for (Map<String, Object> r : raw) {
|
|
|
+ EquipmentProfileVO.RecordRow row = new EquipmentProfileVO.RecordRow();
|
|
|
+ row.setCreateTime(asString(r.get("create_time")));
|
|
|
+ List<EquipmentProfileVO.RecordCell> cells = new ArrayList<>();
|
|
|
+ for (ColDef d : defs) {
|
|
|
+ EquipmentProfileVO.RecordCell c = new EquipmentProfileVO.RecordCell();
|
|
|
+ c.setKey(d.source);
|
|
|
+ if (d.alarm) {
|
|
|
+ Integer v = toInt(r.get(d.source));
|
|
|
+ boolean on = v != null && v == 1;
|
|
|
+ c.setDisplay(on ? "报警" : "正常");
|
|
|
+ c.setValue(c.getDisplay());
|
|
|
+ c.setAlarm(on);
|
|
|
+ } else {
|
|
|
+ c.setDisplay(formatVal(r.get(d.source), d.unit));
|
|
|
+ c.setValue(formatPlain(r.get(d.source)));
|
|
|
+ c.setAlarm(false);
|
|
|
+ }
|
|
|
+ cells.add(c);
|
|
|
+ }
|
|
|
+ row.setCells(cells);
|
|
|
+ rows.add(row);
|
|
|
+ }
|
|
|
+ return rows;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String formatVal(Object v, String unit) {
|
|
|
+ if (v == null) {
|
|
|
+ return "—";
|
|
|
+ }
|
|
|
+ if (v instanceof Number) {
|
|
|
+ double d = ((Number) v).doubleValue();
|
|
|
+ if (Double.isNaN(d) || Double.isInfinite(d)) {
|
|
|
+ return "—";
|
|
|
+ }
|
|
|
+ return new DecimalFormat("0.##").format(d) + unit;
|
|
|
+ }
|
|
|
+ String s = String.valueOf(v).trim();
|
|
|
+ if (s.isEmpty()) {
|
|
|
+ return "—";
|
|
|
+ }
|
|
|
+ return s + unit;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String formatPlain(Object v) {
|
|
|
+ if (v == null) {
|
|
|
+ return "—";
|
|
|
+ }
|
|
|
+ if (v instanceof Number) {
|
|
|
+ double d = ((Number) v).doubleValue();
|
|
|
+ if (Double.isNaN(d) || Double.isInfinite(d)) {
|
|
|
+ return "—";
|
|
|
+ }
|
|
|
+ return new DecimalFormat("0.##").format(d);
|
|
|
+ }
|
|
|
+ String s = String.valueOf(v).trim();
|
|
|
+ return s.isEmpty() ? "—" : s;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Integer toInt(Object o) {
|
|
|
+ if (o == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (o instanceof Number) {
|
|
|
+ return ((Number) o).intValue();
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return Integer.parseInt(String.valueOf(o).trim());
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String asString(Object o) {
|
|
|
+ if (o == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ if (o instanceof TemporalAccessor) {
|
|
|
+ try {
|
|
|
+ return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format((TemporalAccessor) o);
|
|
|
+ } catch (Exception ignored) {
|
|
|
+ // 回退到下面处理
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (o instanceof java.util.Date) {
|
|
|
+ // MyBatis 默认将 datetime 映射为 java.sql.Timestamp,其 toString 会带 .0,需格式化去除
|
|
|
+ return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((java.util.Date) o);
|
|
|
+ }
|
|
|
+ return String.valueOf(o);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 采集记录列定义:source=原始表列名;label=展示标题;unit=单位;alarm=0正常/1报警。 */
|
|
|
+ private static class ColDef {
|
|
|
+ final String source;
|
|
|
+ final String label;
|
|
|
+ final String unit;
|
|
|
+ final boolean alarm;
|
|
|
+
|
|
|
+ ColDef(String source, String label, String unit, boolean alarm) {
|
|
|
+ this.source = source;
|
|
|
+ this.label = label;
|
|
|
+ this.unit = unit;
|
|
|
+ this.alarm = alarm;
|
|
|
+ }
|
|
|
+
|
|
|
+ EquipmentProfileVO.RecordColumn toColumn() {
|
|
|
+ EquipmentProfileVO.RecordColumn c = new EquipmentProfileVO.RecordColumn();
|
|
|
+ c.setKey(source);
|
|
|
+ c.setLabel(label);
|
|
|
+ c.setUnit(unit);
|
|
|
+ c.setAlarm(alarm);
|
|
|
+ return c;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Long toLong(Object o) {
|
|
|
+ if (o == null) {
|
|
|
+ return 0L;
|
|
|
+ }
|
|
|
+ if (o instanceof Number) {
|
|
|
+ return ((Number) o).longValue();
|
|
|
+ }
|
|
|
+ return 0L;
|
|
|
+ }
|
|
|
+}
|