|
|
@@ -294,6 +294,67 @@ class CockpitCurrentEventsControllerTest {
|
|
|
verifyNoInteractions(adapter);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 票 77:片区在这条端点上真的生效,三条判据成对取正反证 ——
|
|
|
+ * 合法值收窄列表、非法值 400(且不读适配器)、不传=全域(不得把「不传」当成某个片区)。
|
|
|
+ *
|
|
|
+ * <p>回显是双字段口径(与 events/multidimensional-analysis 同一份):未筛时
|
|
|
+ * district/districtName 都为 null;只看未分区时 district=null 但 districtName=「未分区」,
|
|
|
+ * 前端据此区分「没筛」和「筛了个空片区」。
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ void districtFilterNarrowsListEchoesBothFieldsAndKeepsAbsentAsGlobal() throws Exception {
|
|
|
+ mockMvc.perform(get(ENDPOINT).param("mode", "WARNING").param("district", "0"))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andExpect(jsonPath("$.data.filters.district").value(0))
|
|
|
+ .andExpect(jsonPath("$.data.filters.districtName").value("城南"))
|
|
|
+ .andExpect(jsonPath("$.data.pagination.total").value(1))
|
|
|
+ .andExpect(jsonPath("$.data.sections.events.data[0].eventNo").value("W-002"));
|
|
|
+
|
|
|
+ mockMvc.perform(get(ENDPOINT).param("mode", "WARNING").param("district", "1"))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andExpect(jsonPath("$.data.filters.district").value(1))
|
|
|
+ .andExpect(jsonPath("$.data.pagination.total").value(1))
|
|
|
+ .andExpect(jsonPath("$.data.sections.events.data[0].eventNo").value("W-003"));
|
|
|
+
|
|
|
+ // 只看未分区:没有可信片区的事件如实归入「未分区」,不被静默丢弃
|
|
|
+ mockMvc.perform(get(ENDPOINT).param("mode", "WARNING").param("district", "UNASSIGNED"))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andExpect(jsonPath("$.data.filters.district").value(nullValue()))
|
|
|
+ .andExpect(jsonPath("$.data.filters.districtName").value("未分区"))
|
|
|
+ .andExpect(jsonPath("$.data.pagination.total").value(3));
|
|
|
+
|
|
|
+ // 不传=全域
|
|
|
+ mockMvc.perform(get(ENDPOINT).param("mode", "WARNING"))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andExpect(jsonPath("$.data.filters.district").value(nullValue()))
|
|
|
+ .andExpect(jsonPath("$.data.filters.districtName").value(nullValue()))
|
|
|
+ .andExpect(jsonPath("$.data.pagination.total").value(5));
|
|
|
+
|
|
|
+ // 报警模式同一套语义(片区是事件族共同维度,不是预警专属)
|
|
|
+ mockMvc.perform(get(ENDPOINT).param("mode", "ALARM").param("district", "2"))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andExpect(jsonPath("$.data.filters.district").value(2))
|
|
|
+ .andExpect(jsonPath("$.data.filters.districtName").value("太常"))
|
|
|
+ .andExpect(jsonPath("$.data.pagination.total").value(1))
|
|
|
+ .andExpect(jsonPath("$.data.sections.events.data[0].eventNo").value("A-001"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void invalidDistrictReturnsTypedBadRequestBeforeReadAdapterIsCalled() throws Exception {
|
|
|
+ mockMvc.perform(get(ENDPOINT).param("mode", "WARNING").param("district", "99"))
|
|
|
+ .andExpect(status().isBadRequest())
|
|
|
+ .andExpect(jsonPath("$.code").value(400))
|
|
|
+ .andExpect(jsonPath("$.data.category").value("INVALID_REQUEST"))
|
|
|
+ .andExpect(jsonPath("$.data.message").value("district must be 0, 1, 2 or UNASSIGNED"));
|
|
|
+
|
|
|
+ mockMvc.perform(get(ENDPOINT).param("mode", "WARNING").param("district", "NOT_A_DISTRICT"))
|
|
|
+ .andExpect(status().isBadRequest())
|
|
|
+ .andExpect(jsonPath("$.data.message").value("district must be 0, 1, 2 or UNASSIGNED"));
|
|
|
+
|
|
|
+ verifyNoInteractions(adapter);
|
|
|
+ }
|
|
|
+
|
|
|
@Test
|
|
|
void serverFailureReturnsTypedServerError() throws Exception {
|
|
|
when(adapter.load()).thenThrow(new CockpitReadException("upstream unavailable"));
|
|
|
@@ -352,11 +413,11 @@ class CockpitCurrentEventsControllerTest {
|
|
|
warning("W-002", "燃气压力预警", "GAS_PRESSURE", 2, "ACTIVE",
|
|
|
"2026-09-02T09:45:00+08:00", "2026-09-02T09:46:00+08:00",
|
|
|
"0.32", "MPa", "D-GAS-2", "燃气浓度计-002", "MP-A-002", "城区A-燃气2号点", "城区A-燃气2号点",
|
|
|
- "GAS_SENSOR", "110.361", "28.441"),
|
|
|
+ "GAS_SENSOR", "110.361", "28.441", 0),
|
|
|
warning("W-003", "供水压力预警", "WATER_PRESSURE", 3, "PENDING",
|
|
|
"2026-09-02T09:40:00+08:00", "2026-09-02T09:41:00+08:00",
|
|
|
"0.18", "MPa", "D-WS-1", "供水压力计-001", "MP-B-001", "城区B-供水1号点", "城区B-供水1号点",
|
|
|
- "WATER_SUPPLY_SENSOR", "110.380", "28.460"),
|
|
|
+ "WATER_SUPPLY_SENSOR", "110.380", "28.460", 1),
|
|
|
warning("W-004", "污水浓度预警", "SEWAGE_CONCENTRATION", 4, "RELEASED",
|
|
|
"2026-09-02T09:35:00+08:00", "2026-09-02T09:36:00+08:00",
|
|
|
"18.2", "mg/L", "D-DR-1", "污水环境监测仪-001", null, null, "城区C-污水1号点",
|
|
|
@@ -377,7 +438,7 @@ class CockpitCurrentEventsControllerTest {
|
|
|
alarm("A-001", "燃气浓度报警", "GAS_CONCENTRATION", 1, "HANDLED",
|
|
|
"2026-09-02T09:55:00+08:00", "2026-09-02T09:56:00+08:00",
|
|
|
"22.6", "mg/m³", "D-GAS-3", "燃气浓度计-003", "MP-A-003", "城区A-燃气3号点", "城区A-燃气3号点",
|
|
|
- "GAS_SENSOR", "110.362", "28.442", true, "0.00", "20.00"),
|
|
|
+ "GAS_SENSOR", "110.362", "28.442", true, "0.00", "20.00", 2),
|
|
|
alarm("A-002", "污水浓度报警", "SEWAGE_CONCENTRATION", 2, "PROCESSING",
|
|
|
"2026-09-02T09:32:00+08:00", "2026-09-02T09:33:00+08:00",
|
|
|
"42.1", "mg/L", "D-DR-1", "污水环境监测仪-001", "MP-C-001", "城区C-污水1号点", "城区C-污水1号点",
|
|
|
@@ -447,11 +508,35 @@ class CockpitCurrentEventsControllerTest {
|
|
|
String deviceCode, String deviceName, String monitoringPointCode, String monitoringPointName,
|
|
|
String location, String typeCode,
|
|
|
String longitude, String latitude, boolean coordinateVerified) {
|
|
|
+ return warning(no, name, type, level, status, eventTime, updatedTime, value, unit,
|
|
|
+ deviceCode, deviceName, monitoringPointCode, monitoringPointName, location, typeCode,
|
|
|
+ longitude, latitude, coordinateVerified, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 票 77:带片区的预警(片区是筛选维度,必须能单独造样本)。 */
|
|
|
+ private static CockpitCurrentEventsSnapshot.SourceWarning warning(
|
|
|
+ String no, String name, String type, Integer level, String status,
|
|
|
+ String eventTime, String updatedTime, String value, String unit,
|
|
|
+ String deviceCode, String deviceName, String monitoringPointCode, String monitoringPointName,
|
|
|
+ String location, String typeCode,
|
|
|
+ String longitude, String latitude, Integer district) {
|
|
|
+ return warning(no, name, type, level, status, eventTime, updatedTime, value, unit,
|
|
|
+ deviceCode, deviceName, monitoringPointCode, monitoringPointName, location, typeCode,
|
|
|
+ longitude, latitude, false, district);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 最内层:真正构造快照的地方,片区随样本传入(null=未分区)。 */
|
|
|
+ private static CockpitCurrentEventsSnapshot.SourceWarning warning(
|
|
|
+ String no, String name, String type, Integer level, String status,
|
|
|
+ String eventTime, String updatedTime, String value, String unit,
|
|
|
+ String deviceCode, String deviceName, String monitoringPointCode, String monitoringPointName,
|
|
|
+ String location, String typeCode,
|
|
|
+ String longitude, String latitude, boolean coordinateVerified, Integer district) {
|
|
|
return new CockpitCurrentEventsSnapshot.SourceWarning(
|
|
|
no, name, type, level, status, eventTime, updatedTime,
|
|
|
new BigDecimal(value), unit, deviceCode, deviceName, monitoringPointCode, monitoringPointName,
|
|
|
location, typeCode,
|
|
|
- new BigDecimal(longitude), new BigDecimal(latitude), coordinateVerified);
|
|
|
+ new BigDecimal(longitude), new BigDecimal(latitude), coordinateVerified, district);
|
|
|
}
|
|
|
|
|
|
private static CockpitCurrentEventsSnapshot.SourceAlarm alarm(
|
|
|
@@ -472,13 +557,28 @@ class CockpitCurrentEventsControllerTest {
|
|
|
String location, String typeCode,
|
|
|
String longitude, String latitude,
|
|
|
boolean coordinateVerified, String thresholdMinValue, String thresholdMaxValue) {
|
|
|
+ return alarm(id, name, type, level, status, eventTime, updatedTime, value, unit,
|
|
|
+ deviceCode, deviceName, monitoringPointCode, monitoringPointName, location, typeCode,
|
|
|
+ longitude, latitude, coordinateVerified, thresholdMinValue, thresholdMaxValue, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 票 77:带片区的报警(最内层构造,片区随样本传入;null=未分区)。 */
|
|
|
+ private static CockpitCurrentEventsSnapshot.SourceAlarm alarm(
|
|
|
+ String id, String name, String type, Integer level, String status,
|
|
|
+ String eventTime, String updatedTime, String value, String unit,
|
|
|
+ String deviceCode, String deviceName, String monitoringPointCode, String monitoringPointName,
|
|
|
+ String location, String typeCode,
|
|
|
+ String longitude, String latitude,
|
|
|
+ boolean coordinateVerified, String thresholdMinValue, String thresholdMaxValue,
|
|
|
+ Integer district) {
|
|
|
return new CockpitCurrentEventsSnapshot.SourceAlarm(
|
|
|
id, name, type, level, status, eventTime, updatedTime,
|
|
|
new BigDecimal(value), unit, deviceCode, deviceName, monitoringPointCode, monitoringPointName,
|
|
|
location, typeCode,
|
|
|
new BigDecimal(longitude), new BigDecimal(latitude), coordinateVerified,
|
|
|
thresholdMinValue == null ? null : new BigDecimal(thresholdMinValue),
|
|
|
- thresholdMaxValue == null ? null : new BigDecimal(thresholdMaxValue));
|
|
|
+ thresholdMaxValue == null ? null : new BigDecimal(thresholdMaxValue),
|
|
|
+ district);
|
|
|
}
|
|
|
|
|
|
private static CockpitCurrentEventsSnapshot.SourceWorkOrderLink workOrder(
|