From 4b2b61e39421720bdc29d84cd2d74b29bc337a5d Mon Sep 17 00:00:00 2001 From: zhangjidi2016 <1017543663@qq.com> Date: Sun, 31 Oct 2021 11:29:55 +0800 Subject: [PATCH] [ISSUE #30]Added DLQ message management (#31) * [ISSUE #30]Added DLQ message management * remove the specific namesrvAddr in application.properties. Co-authored-by: zhangjidi --- pom.xml | 13 + .../controller/DlqMessageController.java | 73 +++++ .../dashboard/model/DlqMessageExcelModel.java | 81 ++++++ .../dashboard/service/DlqMessageService.java | 26 ++ .../service/impl/DlqMessageServiceImpl.java | 68 +++++ .../rocketmq/dashboard/util/ExcelUtil.java | 56 ++++ src/main/resources/static/index.html | 1 + src/main/resources/static/src/app.js | 3 + src/main/resources/static/src/dlqMessage.js | 184 ++++++++++++ src/main/resources/static/src/i18n/en.js | 5 +- src/main/resources/static/src/i18n/zh.js | 7 +- .../resources/static/view/layout/_header.html | 1 + .../resources/static/view/pages/consumer.html | 32 +-- .../static/view/pages/dlqMessage.html | 266 ++++++++++++++++++ .../resources/static/view/pages/message.html | 2 +- .../controller/DlqMessageControllerTest.java | 138 +++++++++ 16 files changed, 936 insertions(+), 20 deletions(-) create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/DlqMessageController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/DlqMessageExcelModel.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/DlqMessageService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/DlqMessageServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/util/ExcelUtil.java create mode 100644 src/main/resources/static/src/dlqMessage.js create mode 100644 src/main/resources/static/view/pages/dlqMessage.html create mode 100644 src/test/java/org/apache/rocketmq/dashboard/controller/DlqMessageControllerTest.java diff --git a/pom.xml b/pom.xml index ff1384d..2ebb77a 100644 --- a/pom.xml +++ b/pom.xml @@ -100,6 +100,8 @@ 3.3.3 2.3.1 2.4.3 + 2.2.10 + 4.2 @@ -234,6 +236,17 @@ commons-pool2 ${commons-pool2.version} + + com.alibaba + easyexcel + ${easyexcel.version} + + + org.ow2.asm + asm + ${asm.version} + + diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/DlqMessageController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/DlqMessageController.java new file mode 100644 index 0000000..500040d --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/DlqMessageController.java @@ -0,0 +1,73 @@ +/* + * 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.dashboard.controller; + +import com.google.common.collect.Lists; +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.dashboard.exception.ServiceException; +import org.apache.rocketmq.dashboard.model.DlqMessageExcelModel; +import org.apache.rocketmq.dashboard.model.request.MessageQuery; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.DlqMessageService; +import org.apache.rocketmq.dashboard.util.ExcelUtil; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/dlqMessage") +@Permission +public class DlqMessageController { + + @Resource + private DlqMessageService dlqMessageService; + + @Resource + private MQAdminExt mqAdminExt; + + @RequestMapping(value = "/queryDlqMessageByConsumerGroup.query", method = RequestMethod.POST) + @ResponseBody + public Object queryDlqMessageByConsumerGroup(@RequestBody MessageQuery query) { + return dlqMessageService.queryDlqMessageByPage(query); + } + + @GetMapping(value = "/exportDlqMessage.do") + public void exportDlqMessage(HttpServletResponse response, @RequestParam String consumerGroup, + @RequestParam String msgId) { + MessageExt messageExt = null; + try { + String topic = MixAll.DLQ_GROUP_TOPIC_PREFIX + consumerGroup; + messageExt = mqAdminExt.viewMessage(topic, msgId); + } catch (Exception e) { + throw new ServiceException(-1, String.format("Failed to query message by Id: %s", msgId)); + } + DlqMessageExcelModel excelModel = new DlqMessageExcelModel(messageExt); + try { + ExcelUtil.writeExcel(response, Lists.newArrayList(excelModel), "dlq", "dlq", DlqMessageExcelModel.class); + } catch (Exception e) { + throw new ServiceException(-1, String.format("export dlq message failed!")); + } + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/DlqMessageExcelModel.java b/src/main/java/org/apache/rocketmq/dashboard/model/DlqMessageExcelModel.java new file mode 100644 index 0000000..2476a23 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/DlqMessageExcelModel.java @@ -0,0 +1,81 @@ +/* + * 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.dashboard.model; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.metadata.BaseRowModel; +import com.alibaba.excel.util.DateUtils; +import com.google.common.base.Charsets; +import java.io.Serializable; +import java.util.Date; +import lombok.Data; +import org.apache.rocketmq.common.message.MessageExt; + +@Data +public class DlqMessageExcelModel extends BaseRowModel implements Serializable { + + @ExcelProperty(value = "topic", index = 0) + @ColumnWidth(value = 15) + private String topic; + + @ExcelProperty(value = "msgId", index = 1) + @ColumnWidth(value = 15) + private String msgId; + + @ExcelProperty(value = "bornHost", index = 2) + @ColumnWidth(value = 15) + private String bornHost; + + @ExcelProperty(value = "bornTimestamp", index = 3) + @ColumnWidth(value = 25) + private String bornTimestamp; + + @ExcelProperty(value = "storeTimestamp", index = 4) + @ColumnWidth(value = 25) + private String storeTimestamp; + + @ExcelProperty(value = "reconsumeTimes", index = 5) + @ColumnWidth(value = 25) + private int reconsumeTimes; + + @ExcelProperty(value = "properties", index = 6) + @ColumnWidth(value = 20) + private String properties; + + @ExcelProperty(value = "messageBody", index = 7) + @ColumnWidth(value = 20) + private String messageBody; + + @ExcelProperty(value = "bodyCRC", index = 8) + @ColumnWidth(value = 15) + private int bodyCRC; + + public DlqMessageExcelModel(MessageExt messageExt) { + this.topic = messageExt.getTopic(); + this.msgId = messageExt.getMsgId(); + this.bornHost = messageExt.getBornHostString(); + this.bornTimestamp = DateUtils.format(new Date(messageExt.getBornTimestamp()), DateUtils.DATE_FORMAT_19); + this.storeTimestamp = DateUtils.format(new Date(messageExt.getStoreTimestamp()), DateUtils.DATE_FORMAT_19); + this.reconsumeTimes = messageExt.getReconsumeTimes(); + this.properties = messageExt.getProperties().toString(); + this.messageBody = new String(messageExt.getBody(), Charsets.UTF_8); + this.bodyCRC = messageExt.getBodyCRC(); + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/DlqMessageService.java b/src/main/java/org/apache/rocketmq/dashboard/service/DlqMessageService.java new file mode 100644 index 0000000..5cf9eb9 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/DlqMessageService.java @@ -0,0 +1,26 @@ +/* + * 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.dashboard.service; + +import org.apache.rocketmq.dashboard.model.MessagePage; +import org.apache.rocketmq.dashboard.model.request.MessageQuery; + +public interface DlqMessageService { + + MessagePage queryDlqMessageByPage(MessageQuery query); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/DlqMessageServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/DlqMessageServiceImpl.java new file mode 100644 index 0000000..6fb822a --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/DlqMessageServiceImpl.java @@ -0,0 +1,68 @@ +/* + * 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.dashboard.service.impl; + +import com.google.common.base.Throwables; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.protocol.ResponseCode; +import org.apache.rocketmq.dashboard.model.MessagePage; +import org.apache.rocketmq.dashboard.model.MessageView; +import org.apache.rocketmq.dashboard.model.request.MessageQuery; +import org.apache.rocketmq.dashboard.service.DlqMessageService; +import org.apache.rocketmq.dashboard.service.MessageService; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class DlqMessageServiceImpl implements DlqMessageService { + + @Resource + private MQAdminExt mqAdminExt; + + @Resource + private MessageService messageService; + + @Override + public MessagePage queryDlqMessageByPage(MessageQuery query) { + List messageViews = new ArrayList<>(); + PageRequest page = PageRequest.of(query.getPageNum(), query.getPageSize()); + String topic = query.getTopic(); + try { + mqAdminExt.examineTopicRouteInfo(topic); + } catch (MQClientException e) { + // If the %DLQ%Group does not exist, the message returns null + if (topic.startsWith(MixAll.DLQ_GROUP_TOPIC_PREFIX) + && e.getResponseCode() == ResponseCode.TOPIC_NOT_EXIST) { + return new MessagePage(new PageImpl<>(messageViews, page, 0), query.getTaskId()); + } else { + throw Throwables.propagate(e); + } + } catch (Exception e) { + throw Throwables.propagate(e); + } + return messageService.queryMessageByPage(query); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/util/ExcelUtil.java b/src/main/java/org/apache/rocketmq/dashboard/util/ExcelUtil.java new file mode 100644 index 0000000..e95d165 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/util/ExcelUtil.java @@ -0,0 +1,56 @@ +/* + * 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.dashboard.util; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.apache.poi.ss.usermodel.HorizontalAlignment; + +public class ExcelUtil { + + public static void writeExcel(HttpServletResponse response, List data, String fileName, + String sheetName, Class clazz) throws Exception { + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + WriteFont writeFont = new WriteFont(); + writeFont.setFontHeightInPoints((short)12); + writeFont.setFontName("Microsoft YaHei UI"); + headWriteCellStyle.setWriteFont(writeFont); + headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + + WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); + contentWriteCellStyle.setWriteFont(writeFont); + contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); + EasyExcel.write(getOutputStream(fileName, response), clazz) + .excelType(ExcelTypeEnum.XLSX).sheet(sheetName).registerWriteHandler(horizontalCellStyleStrategy).doWrite(data); + } + + private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception { + fileName = URLEncoder.encode(fileName, "UTF-8"); + response.setContentType("application/vnd.ms-excel"); + response.setCharacterEncoding("utf8"); + response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx"); + return response.getOutputStream(); + } +} diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 2b14c29..f212527 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -107,6 +107,7 @@ + diff --git a/src/main/resources/static/src/app.js b/src/main/resources/static/src/app.js index f8183fb..7fa68dc 100644 --- a/src/main/resources/static/src/app.js +++ b/src/main/resources/static/src/app.js @@ -195,6 +195,9 @@ app.config(['$routeProvider', '$httpProvider','$cookiesProvider','getDictNamePro }).when('/message', { templateUrl: 'view/pages/message.html', controller:'messageController' + }).when('/dlqMessage', { + templateUrl: 'view/pages/dlqMessage.html', + controller:'dlqMessageController' }).when('/messageTrace', { templateUrl: 'view/pages/messageTrace.html', controller:'messageTraceController' diff --git a/src/main/resources/static/src/dlqMessage.js b/src/main/resources/static/src/dlqMessage.js new file mode 100644 index 0000000..fd82db1 --- /dev/null +++ b/src/main/resources/static/src/dlqMessage.js @@ -0,0 +1,184 @@ +/* + * 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. + */ + +var module = app; +const SYS_GROUP_TOPIC_PREFIX = "%SYS%"; +module.controller('dlqMessageController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.allConsumerGroupList = []; + $scope.selectedConsumerGroup = []; + $scope.messageId = ""; + $scope.queryDlqMessageByConsumerGroupResult = []; + $scope.queryDlqMessageByMessageIdResult = {}; + $http({ + method: "GET", + url: "consumer/groupList.query" + }).success(function (resp) { + if (resp.status == 0) { + for (const consumerGroup of resp.data) { + if (!consumerGroup.group.startsWith(SYS_GROUP_TOPIC_PREFIX)) { + $scope.allConsumerGroupList.push(consumerGroup.group); + } + } + $scope.allConsumerGroupList.sort(); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + $scope.timepickerBegin = moment().subtract(3, 'hour').format('YYYY-MM-DD HH:mm'); + $scope.timepickerEnd = moment().format('YYYY-MM-DD HH:mm'); + $scope.timepickerOptions = {format: 'YYYY-MM-DD HH:mm', showClear: true}; + + $scope.taskId = ""; + + $scope.paginationConf = { + currentPage: 1, + totalItems: 0, + itemsPerPage: 20, + pagesLength: 15, + perPageOptions: [10], + rememberPerPage: 'perPageItems', + onChange: function () { + $scope.queryDlqMessageByConsumerGroup() + } + }; + + $scope.queryDlqMessageByConsumerGroup = function () { + $("#noMsgTip").css("display", "none"); + if ($scope.timepickerEnd < $scope.timepickerBegin) { + Notification.error({message: "endTime is later than beginTime!", delay: 2000}); + return + } + if ($scope.selectedConsumerGroup === [] || (typeof $scope.selectedConsumerGroup) == "object") { + return + } + $http({ + method: "POST", + url: "dlqMessage/queryDlqMessageByConsumerGroup.query", + data: { + topic: DLQ_GROUP_TOPIC_PREFIX + $scope.selectedConsumerGroup, + begin: $scope.timepickerBegin.valueOf(), + end: $scope.timepickerEnd.valueOf(), + pageNum: $scope.paginationConf.currentPage, + pageSize: $scope.paginationConf.itemsPerPage, + taskId: $scope.taskId + } + }).success(function (resp) { + if (resp.status === 0) { + $scope.messageShowList = resp.data.page.content; + if ($scope.messageShowList.length == 0){ + $("#noMsgTip").removeAttr("style"); + } + console.log($scope.messageShowList); + if (resp.data.page.first) { + $scope.paginationConf.currentPage = 1; + } + $scope.paginationConf.currentPage = resp.data.page.number + 1; + $scope.paginationConf.totalItems = resp.data.page.totalElements; + $scope.taskId = resp.data.taskId + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } + + $scope.queryDlqMessageDetail = function (messageId, consumerGroup) { + $http({ + method: "GET", + url: "messageTrace/viewMessage.query", + params: { + msgId: messageId, + topic: DLQ_GROUP_TOPIC_PREFIX + consumerGroup + } + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + ngDialog.open({ + template: 'dlqMessageDetailViewDialog', + data: resp.data + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.queryDlqMessageByMessageId = function (messageId, consumerGroup) { + $http({ + method: "GET", + url: "messageTrace/viewMessage.query", + params: { + msgId: messageId, + topic: DLQ_GROUP_TOPIC_PREFIX + consumerGroup + } + }).success(function (resp) { + if (resp.status == 0) { + $scope.queryDlqMessageByMessageIdResult = resp.data; + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.changeShowMessageList = function (currentPage, totalItem) { + var perPage = $scope.paginationConf.itemsPerPage; + var from = (currentPage - 1) * perPage; + var to = (from + perPage) > totalItem ? totalItem : from + perPage; + $scope.messageShowList = $scope.queryMessageByTopicResult.slice(from, to); + $scope.paginationConf.totalItems = totalItem; + }; + + $scope.onChangeQueryCondition = function () { + console.log("change") + $scope.taskId = ""; + $scope.paginationConf.currentPage = 1; + $scope.paginationConf.totalItems = 0; + } + + $scope.resendDlqMessage = function (messageView, consumerGroup) { + const topic = messageView.properties.RETRY_TOPIC; + const msgId = messageView.properties.ORIGIN_MESSAGE_ID; + $http({ + method: "POST", + url: "message/consumeMessageDirectly.do", + params: { + msgId: msgId, + consumerGroup: consumerGroup, + topic: topic + } + }).success(function (resp) { + if (resp.status == 0) { + ngDialog.open({ + template: 'operationResultDialog', + data: { + result: resp.data + } + }); + } else { + ngDialog.open({ + template: 'operationResultDialog', + data: { + result: resp.errMsg + } + }); + } + }); + }; + + $scope.exportDlqMessage = function (msgId, consumerGroup) { + window.location.href = "dlqMessage/exportDlqMessage.do?msgId=" + msgId + "&consumerGroup=" + consumerGroup; + } +}]); \ No newline at end of file diff --git a/src/main/resources/static/src/i18n/en.js b/src/main/resources/static/src/i18n/en.js index 0792691..48f73b4 100644 --- a/src/main/resources/static/src/i18n/en.js +++ b/src/main/resources/static/src/i18n/en.js @@ -41,6 +41,7 @@ var en = { "RESEND_MESSAGE":"Resend Message", "VIEW_EXCEPTION":"View Exception", "MESSAGETRACE":"MessageTrace", + "DLQ_MESSAGE":"DLQMessage", "COMMIT": "Commit", "OPERATION": "Operation", "ADD": "Add", @@ -108,5 +109,7 @@ var en = { "ENABLE_MESSAGE_TRACE":"Enable Message Trace", "MESSAGE_TRACE_DETAIL":"Message Trace Detail", "TRACE_TOPIC":"TraceTopic", - "SELECT_TRACE_TOPIC":"selectTraceTopic" + "SELECT_TRACE_TOPIC":"selectTraceTopic", + "EXPORT": "export", + "NO_MATCH_RESULT": "no match result" } \ No newline at end of file diff --git a/src/main/resources/static/src/i18n/zh.js b/src/main/resources/static/src/i18n/zh.js index af813d0..dad46d7 100644 --- a/src/main/resources/static/src/i18n/zh.js +++ b/src/main/resources/static/src/i18n/zh.js @@ -39,8 +39,9 @@ var zh = { "PRODUCER":"生产者", "MESSAGE":"消息", "MESSAGE_DETAIL":"消息详情", - "RESEND_MESSAGE":"重新消费", + "RESEND_MESSAGE":"重新发送", "VIEW_EXCEPTION":"查看异常", + "DLQ_MESSAGE":"死信消息", "MESSAGETRACE":"消息轨迹", "OPERATION": "操作", "ADD": "新增", @@ -109,5 +110,7 @@ var zh = { "ENABLE_MESSAGE_TRACE":"开启消息轨迹", "MESSAGE_TRACE_DETAIL":"消息轨迹详情", "TRACE_TOPIC":"消息轨迹主题", - "SELECT_TRACE_TOPIC":"选择消息轨迹主题" + "SELECT_TRACE_TOPIC":"选择消息轨迹主题", + "EXPORT": "导出", + "NO_MATCH_RESULT": "没有查到符合条件的结果" } \ No newline at end of file diff --git a/src/main/resources/static/view/layout/_header.html b/src/main/resources/static/view/layout/_header.html index 21ed7cf..0309947 100644 --- a/src/main/resources/static/view/layout/_header.html +++ b/src/main/resources/static/view/layout/_header.html @@ -34,6 +34,7 @@
  • {{'CONSUMER' | translate}}
  • {{'PRODUCER' | translate}}
  • {{'MESSAGE' | translate}}
  • +
  • {{'DLQ_MESSAGE' | translate}}
  • {{'MESSAGETRACE' | translate}}