This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 958a75990 fix(dlq): keep the redelivery count in exports and reject a 
zero-length window (#4718)
958a75990 is described below

commit 958a75990337806fc06df86ce753fbadb15f2a14
Author: btlqql <[email protected]>
AuthorDate: Mon Sep 21 20:16:39 2026 +0800

    fix(dlq): keep the redelivery count in exports and reject a zero-length 
window (#4718)
    
    Two DLQ fixes from the same author, consolidated into one change.
    
    1. `DLQService.validateTimeRange` only rejected a reversed window, so 
`start == end` was accepted and each queue ran a 32-message pull that could 
return messages outside the intended window. All four DLQ actions now reject a 
zero-length window with `BusinessException(400)`.
    2. The dead-letter Excel export dropped the redelivery count: 
`DLQMessageExcelRow` gains a `Reconsume Times` column, filled from 
`DLQMessageVO` in `from()`.
    
    Consolidates #4702 and #4718 (same author, same domain).
---
 .../studio/instance/dlq/DLQMessageExcelRow.java    |  3 +
 .../rocketmq/studio/instance/dlq/DLQService.java   |  8 +++
 .../instance/dlq/DLQMessageExcelRowTest.java       | 71 ++++++++++++++++++++++
 .../studio/instance/dlq/DLQServiceTest.java        | 31 ++++++++++
 4 files changed, 113 insertions(+)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQMessageExcelRow.java
 
b/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQMessageExcelRow.java
index 0b8e7de26..6d06c2542 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQMessageExcelRow.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQMessageExcelRow.java
@@ -43,6 +43,8 @@ public class DLQMessageExcelRow {
     private long offset;
     @ExcelProperty("Store Time")
     private String storeTime;
+    @ExcelProperty("Reconsume Times")
+    private int reconsumeTimes;
     @ExcelProperty("Keys")
     private String keys;
     @ExcelProperty("Body")
@@ -56,6 +58,7 @@ public class DLQMessageExcelRow {
         row.setOffset(vo.getOffset());
         row.setStoreTime(LocalDateTime.ofInstant(
                 Instant.ofEpochMilli(vo.getStoreTime()), 
ZoneId.systemDefault()).format(STORE_TIME_FORMAT));
+        row.setReconsumeTimes(vo.getReconsumeTimes());
         row.setKeys(vo.getKeys());
         row.setBody(vo.getBody());
         return row;
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQService.java 
b/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQService.java
index 8217622aa..11babd853 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQService.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQService.java
@@ -160,5 +160,13 @@ public class DLQService {
         if (endTime < startTime) {
             throw new BusinessException(400, "endTime must not be earlier than 
startTime");
         }
+        // A zero-length window cannot describe a range. The DLQ message list 
and resend paths
+        // reject it in the provider ("... start time must be before end 
time"), but the two
+        // export paths do not check it at all: the scan they run returns at 
most the single
+        // message sitting on that instant, so the caller received a 200 with 
a meaningless
+        // export instead of the 400 the other two actions return.
+        if (endTime.equals(startTime)) {
+            throw new BusinessException(400, "startTime must be before 
endTime");
+        }
     }
 }
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQMessageExcelRowTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQMessageExcelRowTest.java
new file mode 100644
index 000000000..cbb5d866f
--- /dev/null
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQMessageExcelRowTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.instance.dlq;
+
+import com.alibaba.excel.EasyExcel;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DLQMessageExcelRowTest {
+
+    @Test
+    void shouldExportTheMessageRedeliveryCountTest() {
+        DLQMessageVO message = DLQMessageVO.builder()
+                .msgId("msg-1")
+                .topic("%DLQ%group-1")
+                .queueId(7)
+                .offset(17L)
+                .storeTime(1_700_000_000_000L)
+                .reconsumeTimes(3)
+                .keys("order-1")
+                .body("payload")
+                .build();
+
+        Map<String, String> cells = firstDataRowByColumnName(List.of(message));
+
+        assertThat(cells)
+                .containsEntry("Message ID", "msg-1")
+                .containsEntry("Reconsume Times", "3");
+    }
+
+    private static Map<String, String> 
firstDataRowByColumnName(List<DLQMessageVO> messages) {
+        List<Map<Integer, String>> rows = readBack(messages);
+        Map<Integer, String> header = rows.get(0);
+        Map<Integer, String> values = rows.get(1);
+        Map<String, String> cells = new HashMap<>();
+        header.forEach((column, name) -> cells.put(name, values.get(column)));
+        return cells;
+    }
+
+    private static List<Map<Integer, String>> readBack(List<DLQMessageVO> 
messages) {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        EasyExcel.write(output, DLQMessageExcelRow.class)
+                .sheet("dlq")
+                
.doWrite(messages.stream().map(DLQMessageExcelRow::from).toList());
+        return EasyExcel.read(new ByteArrayInputStream(output.toByteArray()))
+                .sheet()
+                .headRowNumber(0)
+                .doReadSync();
+    }
+}
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQServiceTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQServiceTest.java
index cf3b16917..29964cfab 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQServiceTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQServiceTest.java
@@ -228,4 +228,35 @@ class DLQServiceTest {
 
         verifyNoInteractions(dlqProvider);
     }
+
+    @Test
+    void everyActionShouldRejectAnEmptyTimeWindowTest() {
+        assertThatThrownBy(() -> dlqService.resendMessages("instance-1", 
"group-1", 5000L, 5000L, null))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("startTime must be before endTime");
+        assertThatThrownBy(() -> dlqService.listMessages("instance-1", 
"group-1", 5000L, 5000L, 1, 20))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("startTime must be before endTime");
+        assertThatThrownBy(() -> dlqService.exportMessages("instance-1", 
"group-1", 5000L, 5000L, 100))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("startTime must be before endTime");
+        assertThatThrownBy(() -> dlqService.exportExcel("instance-1", 
"group-1", 5000L, 5000L, null))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("startTime must be before endTime");
+
+        verifyNoInteractions(dlqProvider);
+    }
+
+    @Test
+    void everyActionShouldStillAcceptAOneMillisecondTimeWindowTest() {
+        dlqService.resendMessages("instance-1", "group-1", 5000L, 5001L, null);
+        dlqService.listMessages("instance-1", "group-1", 5000L, 5001L, 1, 20);
+        dlqService.exportMessages("instance-1", "group-1", 5000L, 5001L, 100);
+        dlqService.exportExcel("instance-1", "group-1", 5000L, 5001L, null);
+
+        verify(dlqProvider).resendMessages("instance-1", "group-1", 5000L, 
5001L, null);
+        verify(dlqProvider).listMessages("instance-1", "group-1", 5000L, 
5001L, 1, 20);
+        verify(dlqProvider).exportMessages("instance-1", "group-1", 5000L, 
5001L, 100);
+        verify(dlqProvider).exportExcel("instance-1", "group-1", 5000L, 5001L, 
null);
+    }
 }

Reply via email to