package com.usky.vpp.util; import com.usky.vpp.constant.VppDrEventStatus; import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Map; /** * 运管平台 UN 报文解析辅助(兼容多种字段命名与嵌套结构) */ public final class VppUnPayloadHelper { private static final DateTimeFormatter[] DATE_TIME_FORMATTERS = { DateTimeFormatter.ISO_LOCAL_DATE_TIME, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"), DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss") }; private VppUnPayloadHelper() { } public static Map unwrapInvitationPayload(Map body) { return unwrap(body, "event", "eventInfo", "eventData", "invitation", "data"); } public static Map unwrapOptPayload(Map body) { Map nested = unwrap(body, "opt", "optInfo", "optRequest", "event", "eventInfo", "data"); return nested != null ? nested : body; } public static Map unwrapClearingPayload(Map body) { return unwrap(body, "cq", "cqInfo", "cqRequest", "clearing", "clearingInfo", "event", "data"); } @SuppressWarnings("unchecked") private static Map unwrap(Map body, String... nestedKeys) { if (body == null || body.isEmpty()) { return body; } for (String key : nestedKeys) { Object nested = body.get(key); if (nested instanceof Map) { return (Map) nested; } } return body; } public static boolean hasInvitationFields(Map payload) { if (payload == null) { return false; } if (payload.containsKey("events") || payload.containsKey("descriptor")) { return true; } return getDateTime(payload, "startTime", "start_time", "dtstart", "beginTime") != null || getDecimal(payload, "targetCapacityKw", "target_capacity_kw", "targetKw", "capacity", "targetCapacity") != null; } public static Boolean getBoolean(Map map, String... keys) { Object value = getValue(map, keys); if (value == null) { return null; } if (value instanceof Boolean) { return (Boolean) value; } String text = String.valueOf(value).trim().toLowerCase(); if ("true".equals(text) || "1".equals(text)) { return true; } if ("false".equals(text) || "0".equals(text)) { return false; } return null; } public static String getString(Map map, String... keys) { Object value = getValue(map, keys); if (value == null) { return null; } String text = String.valueOf(value).trim(); return StringUtils.hasText(text) ? text : null; } public static Integer getInteger(Map map, String... keys) { Object value = getValue(map, keys); if (value == null) { return null; } if (value instanceof Number) { return ((Number) value).intValue(); } String text = String.valueOf(value).trim(); if (!StringUtils.hasText(text)) { return null; } try { return Integer.parseInt(text); } catch (NumberFormatException ex) { return null; } } public static Object getRaw(Map map, String... keys) { return getValue(map, keys); } public static BigDecimal getDecimal(Map map, String... keys) { Object value = getValue(map, keys); if (value == null) { return null; } if (value instanceof BigDecimal) { return (BigDecimal) value; } if (value instanceof Number) { return BigDecimal.valueOf(((Number) value).doubleValue()); } String text = String.valueOf(value).trim(); if (!StringUtils.hasText(text)) { return null; } try { return new BigDecimal(text); } catch (NumberFormatException ex) { return null; } } public static LocalDateTime getDateTime(Map map, String... keys) { Object value = getValue(map, keys); if (value == null) { return null; } if (value instanceof LocalDateTime) { return (LocalDateTime) value; } if (value instanceof Number) { long epoch = ((Number) value).longValue(); if (String.valueOf(epoch).length() <= 10) { epoch *= 1000; } return LocalDateTime.ofInstant(Instant.ofEpochMilli(epoch), ZoneId.systemDefault()); } String text = String.valueOf(value).trim(); if (!StringUtils.hasText(text)) { return null; } for (DateTimeFormatter formatter : DATE_TIME_FORMATTERS) { try { return LocalDateTime.parse(text, formatter); } catch (DateTimeParseException ignored) { // try next pattern } } return null; } public static Integer parseResponseType(Object raw) { if (raw == null) { return null; } if (raw instanceof Number) { int value = ((Number) raw).intValue(); return value >= 1 && value <= 3 ? value : null; } String text = String.valueOf(raw).trim().toUpperCase(); if (text.contains("日前") || "DAY_AHEAD".equals(text) || "1".equals(text)) { return 1; } if (text.contains("日内") || "INTRADAY".equals(text) || "2".equals(text)) { return 2; } if (text.contains("秒") || "REALTIME".equals(text) || "3".equals(text)) { return 3; } return null; } public static Integer parseEventType(Object raw) { if (raw == null) { return null; } if (raw instanceof Number) { int value = ((Number) raw).intValue(); return value >= 1 && value <= 2 ? value : null; } String text = String.valueOf(raw).trim().toUpperCase(); if (text.contains("削峰") || "PEAK".equals(text) || "1".equals(text)) { return 1; } if (text.contains("填谷") || "VALLEY".equals(text) || "FILLVALLEY".equals(text) || "2".equals(text)) { return 2; } return null; } public static boolean isCancelled(Map payload) { Integer status = getInteger(payload, "eventStatus", "event_status", "status"); if (status != null && (status == 4 || status == VppDrEventStatus.CANCELLED)) { return true; } String text = getString(payload, "eventStatus", "event_status", "status", "eventState"); if (!StringUtils.hasText(text)) { return false; } text = text.trim().toUpperCase(); return text.contains("取消") || "CANCELLED".equals(text) || "CANCELED".equals(text) || "4".equals(text) || "5".equals(text); } private static Object getValue(Map map, String... keys) { if (map == null) { return null; } for (String key : keys) { if (map.containsKey(key) && map.get(key) != null) { return map.get(key); } } return null; } }