You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by st...@apache.org on 2021/10/09 10:57:05 UTC

[rocketmq-dashboard] branch master updated: [ISSUE #25]Add filter in consumer list. (#26)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new cc30bb2  [ISSUE #25]Add filter in consumer list. (#26)
cc30bb2 is described below

commit cc30bb23739604657de3fe1fad8bd67a0f13ab60
Author: zhangjidi2016 <10...@qq.com>
AuthorDate: Sat Oct 9 18:56:58 2021 +0800

    [ISSUE #25]Add filter in consumer list. (#26)
    
    Co-authored-by: zhangjidi <zh...@cmss.chinamobile.com>
---
 .../dashboard/controller/ConsumerController.java   |   4 +-
 .../dashboard/service/ConsumerService.java         |   2 +-
 .../service/impl/ConsumerServiceImpl.java          |  26 ++-
 src/main/resources/static/src/consumer.js          |  30 +++-
 src/main/resources/static/view/pages/consumer.html |  44 ++---
 src/main/resources/static/view/pages/message.html  | 147 ++++++++--------
 .../resources/static/view/pages/messageTrace.html  | 187 +++++++++++----------
 src/main/resources/static/view/pages/topic.html    |   2 +-
 .../controller/ConsumerControllerTest.java         |   2 +-
 .../rocketmq/dashboard/util/MockObjectUtil.java    |   5 +-
 10 files changed, 255 insertions(+), 194 deletions(-)

diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/ConsumerController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/ConsumerController.java
index f636945..68becd1 100644
--- a/src/main/java/org/apache/rocketmq/dashboard/controller/ConsumerController.java
+++ b/src/main/java/org/apache/rocketmq/dashboard/controller/ConsumerController.java
@@ -47,8 +47,8 @@ public class ConsumerController {
 
     @RequestMapping(value = "/groupList.query")
     @ResponseBody
-    public Object list() {
-        return consumerService.queryGroupList();
+    public Object list(@RequestParam(value = "skipSysGroup", required = false) boolean skipSysGroup) {
+        return consumerService.queryGroupList(skipSysGroup);
     }
 
     @RequestMapping(value = "/group.query")
diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/ConsumerService.java b/src/main/java/org/apache/rocketmq/dashboard/service/ConsumerService.java
index 2c18f98..6f4965c 100644
--- a/src/main/java/org/apache/rocketmq/dashboard/service/ConsumerService.java
+++ b/src/main/java/org/apache/rocketmq/dashboard/service/ConsumerService.java
@@ -31,7 +31,7 @@ import java.util.Map;
 import java.util.Set;
 
 public interface ConsumerService {
-    List<GroupConsumeInfo> queryGroupList();
+    List<GroupConsumeInfo> queryGroupList(boolean skipSysGroup);
 
     GroupConsumeInfo queryGroup(String consumerGroup);
 
diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java
index 37073d7..4e6c637 100644
--- a/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java
+++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java
@@ -24,13 +24,16 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.rocketmq.client.exception.MQClientException;
 import org.apache.rocketmq.common.MQVersion;
+import org.apache.rocketmq.common.MixAll;
 import org.apache.rocketmq.common.admin.ConsumeStats;
 import org.apache.rocketmq.common.admin.RollbackStats;
 import org.apache.rocketmq.common.message.MessageQueue;
@@ -62,8 +65,21 @@ import static com.google.common.base.Throwables.propagate;
 public class ConsumerServiceImpl extends AbstractCommonService implements ConsumerService {
     private Logger logger = LoggerFactory.getLogger(ConsumerServiceImpl.class);
 
+    private static final Set<String> SYSTEM_GROUP_SET = new HashSet<>();
+
+    static {
+        SYSTEM_GROUP_SET.add(MixAll.TOOLS_CONSUMER_GROUP);
+        SYSTEM_GROUP_SET.add(MixAll.FILTERSRV_CONSUMER_GROUP);
+        SYSTEM_GROUP_SET.add(MixAll.SELF_TEST_CONSUMER_GROUP);
+        SYSTEM_GROUP_SET.add(MixAll.ONS_HTTP_PROXY_GROUP);
+        SYSTEM_GROUP_SET.add(MixAll.CID_ONSAPI_PULL_GROUP);
+        SYSTEM_GROUP_SET.add(MixAll.CID_ONSAPI_PERMISSION_GROUP);
+        SYSTEM_GROUP_SET.add(MixAll.CID_ONSAPI_OWNER_GROUP);
+        SYSTEM_GROUP_SET.add(MixAll.CID_SYS_RMQ_TRANS);
+    }
+
     @Override
-    public List<GroupConsumeInfo> queryGroupList() {
+    public List<GroupConsumeInfo> queryGroupList(boolean skipSysGroup) {
         Set<String> consumerGroupSet = Sets.newHashSet();
         try {
             ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
@@ -79,6 +95,14 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
         for (String consumerGroup : consumerGroupSet) {
             groupConsumeInfoList.add(queryGroup(consumerGroup));
         }
+        if (!skipSysGroup) {
+            groupConsumeInfoList.stream().map(group -> {
+                if (SYSTEM_GROUP_SET.contains(group.getGroup())) {
+                    group.setGroup(String.format("%s%s", "%SYS%", group.getGroup()));
+                }
+                return group;
+            }).collect(Collectors.toList());
+        }
         Collections.sort(groupConsumeInfoList);
         return groupConsumeInfoList;
     }
diff --git a/src/main/resources/static/src/consumer.js b/src/main/resources/static/src/consumer.js
index 0059192..6641dd8 100644
--- a/src/main/resources/static/src/consumer.js
+++ b/src/main/resources/static/src/consumer.js
@@ -43,6 +43,8 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http', 'Notific
     };
     $scope.userRole = $window.sessionStorage.getItem("userrole");
     $scope.writeOperationEnabled =  $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false);
+    $scope.filterNormal = true;
+    $scope.filterSystem = false;
 
     $scope.doSort = function () {// todo  how to change this fe's code ? (it's dirty)
         if ($scope.sortKey == 'diffTotal') {
@@ -125,13 +127,37 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http', 'Notific
         $scope.filterList(1)
     });
 
+    $scope.$watch('filterNormal', function () {
+        $scope.filterList(1);
+    });
+
+    $scope.$watch('filterSystem', function () {
+        $scope.filterList(1);
+    });
+
+    $scope.filterByType = function (str) {
+        if ($scope.filterSystem) {
+            if (str.startsWith("%S")) {
+                return true
+            }
+        }
+        if ($scope.filterNormal) {
+            if (str.startsWith("%") == false) {
+                return true
+            }
+        }
+        return false;
+    };
+
     $scope.filterList = function (currentPage) {
         var lowExceptStr = $scope.filterStr.toLowerCase();
         var canShowList = [];
         $scope.allConsumerGrouopList.forEach(function (element) {
             console.log(element)
-            if (element.group.toLowerCase().indexOf(lowExceptStr) != -1) {
-                canShowList.push(element);
+            if ($scope.filterByType(element.group)) {
+                if (element.group.toLowerCase().indexOf(lowExceptStr) != -1) {
+                    canShowList.push(element);
+                }
             }
         });
         $scope.paginationConf.totalItems = canShowList.length;
diff --git a/src/main/resources/static/view/pages/consumer.html b/src/main/resources/static/view/pages/consumer.html
index b973ad8..1f952a8 100644
--- a/src/main/resources/static/view/pages/consumer.html
+++ b/src/main/resources/static/view/pages/consumer.html
@@ -24,27 +24,25 @@
                     <label>{{ 'SUBSCRIPTION_GROUP' | translate}}:</label>
                     <input type="text" class="form-control" ng-model="filterStr">
                 </div>
-                <div class="form-group form-group-sm">
-                    <button class="btn btn-raised btn-sm btn-primary" type="button" ng-show="{{writeOperationEnabled}}"
-                            ng-click="openAddDialog()">{{'ADD' | translate}}/ {{'UPDATE' | translate}}
-                    </button>
-                </div>
-                <div class="form-group form-group-sm">
-                    <button class="btn btn-raised btn-sm btn-primary" type="button" ng-click="refreshConsumerData()">
-                        {{'REFRESH' | translate}}
-                    </button>
-                </div>
-                <div class="form-group form-group-sm">
-                    <md-switch class="md-primary" md-no-ink aria-label="Switch No Ink" ng-model="intervalProcessSwitch">
-                        {{'AUTO_REFRESH' | translate}}
-                    </md-switch>
-                </div>
+                <md-checkbox aria-label="Checkbox" ng-model="filterNormal" class="md-primary">{{'NORMAL' | translate}}
+                </md-checkbox>
+                <md-checkbox aria-label="Checkbox" ng-model="filterSystem" class="md-primary">{{'SYSTEM' | translate}}
+                </md-checkbox>
+                <button class="btn btn-raised btn-sm btn-primary" type="button" ng-show="{{writeOperationEnabled}}"
+                        ng-click="openAddDialog()">{{'ADD' | translate}}/ {{'UPDATE' | translate}}
+                </button>
+                <button class="btn btn-raised btn-sm btn-primary" type="button" ng-click="refreshConsumerData()">
+                    {{'REFRESH' | translate}}
+                </button>
+                <md-switch class="md-primary" md-no-ink aria-label="Switch No Ink" ng-model="intervalProcessSwitch">
+                    {{'AUTO_REFRESH' | translate}}
+                </md-switch>
             </form>
         </div>
         <br>
         <div>
             <div id="deployList" class="row">
-                <table class="table table-bordered">
+                <table class="table table-bordered text-middle">
                     <tr>
                         <th class="text-center"><a ng-click="sortByKey('group')">{{ 'SUBSCRIPTION_GROUP' | translate}}</a></th>
                         <th class="text-center"><a ng-click="sortByKey('count')">{{ 'QUANTITY' | translate}}</a></th>
@@ -55,15 +53,17 @@
                         <th class="text-center"><a ng-click="sortByKey('diffTotal')">{{ 'DELAY' | translate}}</a></th>
                         <th class="text-center">{{ 'OPERATION' | translate}}</th>
                     </tr>
-                    <tr ng-repeat="consumerGroup in consumerGroupShowList">
-                        <td class="text-left">{{consumerGroup.group}}</td>
+                    <tr ng-repeat="consumerGroup in consumerGroupShowList"
+                        ng-init="sysFlag = consumerGroup.group.startsWith('%SYS%')">
+                        <td class="text-center"><font color={{sysFlag?"red":""}}>
+                            {{sysFlag?consumerGroup.group.substring(5):consumerGroup.group}}</font></td>
                         <td class="text-center">{{consumerGroup.count}}</td>
                         <td class="text-center">{{consumerGroup.version}}</td>
                         <td class="text-center">{{consumerGroup.consumeType}}</td>
                         <td class="text-center">{{consumerGroup.messageModel}}</td>
                         <td class="text-center">{{consumerGroup.consumeTps}}</td>
                         <td class="text-center">{{consumerGroup.diffTotal}}</td>
-                        <td class="text-center">
+                        <td class="text-left">
                             <button name="client" ng-click="client(consumerGroup.group)"
                                     class="btn btn-raised btn-sm btn-primary"
                                     type="button">{{'CLIENT' | translate}}
@@ -80,7 +80,7 @@
                             <!--</button>-->
                             <button name="client" ng-click="delete(consumerGroup.group)"
                                     class="btn btn-raised btn-sm btn-danger"
-                                    ng-show="{{writeOperationEnabled}}"
+                                    ng-show="{{!sysFlag && writeOperationEnabled}}"
                                     type="button">{{'DELETE' | translate}}
                             </button>
 
@@ -284,7 +284,7 @@
                     <!--</div>-->
                     <!--</div>-->
                     <div class="form-group">
-                        <label class="control-label col-sm-4">brokerId:</label>
+                        <label class="control-label col-sm-2">brokerId:</label>
                         <div class="col-sm-8">
                             <input class="form-control" ng-model="item.subscriptionGroupConfig.brokerId" type="text"
                                    ng-disabled="{{!ngDialogData.writeOperationEnabled}}" required/>
@@ -292,7 +292,7 @@
                         </div>
                     </div>
                     <div class="form-group">
-                        <label class="control-label col-sm-4">whichBrokerWhenConsumeSlowly:</label>
+                        <label class="control-label col-sm-2">whichBrokerWhenConsumeSlowly:</label>
                         <div class="col-sm-8">
                             <input class="form-control"
                                    ng-model="item.subscriptionGroupConfig.whichBrokerWhenConsumeSlowly" type="text"
diff --git a/src/main/resources/static/view/pages/message.html b/src/main/resources/static/view/pages/message.html
index 2c200b6..ca626e4 100644
--- a/src/main/resources/static/view/pages/message.html
+++ b/src/main/resources/static/view/pages/message.html
@@ -20,10 +20,10 @@
             <md-content>
                 <md-tabs md-dynamic-height="" md-border-bottom="">
                     <md-tab label="Topic">
+                        <h5 class="md-display-5">Total {{paginationConf.totalItems}} Messages</h5>
                         <md-content class="md-padding" style="min-height:600px">
-                            <h5 class="md-display-5">Total {{paginationConf.totalItems}} Messages</h5>
                             <div class="row">
-                                <form class="form-inline pull-left col-sm-12">
+                                <form class="form-inline pull-left">
                                     <div class="form-group">
                                         <label>{{'TOPIC' | translate}}:</label>
                                     </div>
@@ -65,7 +65,7 @@
                                         <span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
                                     </button>
                                 </form>
-                                <table class="table table-bordered">
+                                <table class="table table-bordered text-middle">
                                     <tr>
                                         <th class="text-center">Message ID</th>
                                         <th class="text-center">Tag</th>
@@ -91,84 +91,87 @@
                         </md-content>
                     </md-tab>
                     <md-tab label="Message Key">
+                        <h5 class="md-display-5">Only Return 64 Messages</h5>
                         <md-content class="md-padding" style="min-height:600px">
-                            <h5 class="md-display-5">Only Return 64 Messages</h5>
-
-                            <form class="form-inline pull-left col-sm-12">
-                                <div class="form-group">
-                                    <label>Topic:</label>
-                                </div>
-                                <div class="form-group">
-                                    <div style="width: 300px">
-                                        <select name="mySelectTopic" chosen
-                                                ng-model="selectedTopic"
-                                                ng-options="item for item in allTopicList"
-                                                required>
-                                            <option value=""></option>
-                                        </select>
+                            <div class="row">
+                                <form class="form-inline pull-left">
+                                    <div class="form-group">
+                                        <label>Topic:</label>
+                                    </div>
+                                    <div class="form-group">
+                                        <div style="width: 300px">
+                                            <select name="mySelectTopic" chosen
+                                                    ng-model="selectedTopic"
+                                                    ng-options="item for item in allTopicList"
+                                                    required>
+                                                <option value=""></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>Key:</label>
+                                        <input class="form-control" style="width: 450px" type="text" ng-model="key"
+                                               required/>
                                     </div>
-                                </div>
-                                <div class="form-group">
-                                    <label>Key:</label>
-                                    <input class="form-control" style="width: 450px" type="text" ng-model="key"
-                                           required/>
-                                </div>
 
-                                <button type="button" class="btn btn-raised btn-sm  btn-primary" data-toggle="modal"
-                                        ng-click="queryMessageByTopicAndKey()">
-                                    <span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
-                                </button>
-                            </form>
-                            <table class="table table-bordered">
-                                <tr>
-                                    <th class="text-center">Message ID</th>
-                                    <th class="text-center">Tag</th>
-                                    <th class="text-center">Key</th>
-                                    <th class="text-center">StoreTime</th>
-                                    <th class="text-center">Operation</th>
-                                </tr>
-                                <tr ng-repeat="item in queryMessageByTopicAndKeyResult">
-                                    <td class="text-center">{{item.msgId}}</td>
-                                    <td class="text-center">{{item.properties.TAGS}}</td>
-                                    <td class="text-center">{{item.properties.KEYS}}</td>
-                                    <td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
-                                    </td>
-                                    <td class="text-center">
-                                        <button class="btn btn-raised btn-sm btn-primary" type="button"
-                                                ng-click="queryMessageByMessageId(item.msgId,item.topic)">Message Detail
-                                        </button>
-                                    </td>
-                                </tr>
-                            </table>
+                                    <button type="button" class="btn btn-raised btn-sm  btn-primary" data-toggle="modal"
+                                            ng-click="queryMessageByTopicAndKey()">
+                                        <span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
+                                    </button>
+                                </form>
+                                <table class="table table-bordered text-middle">
+                                    <tr>
+                                        <th class="text-center">Message ID</th>
+                                        <th class="text-center">Tag</th>
+                                        <th class="text-center">Key</th>
+                                        <th class="text-center">StoreTime</th>
+                                        <th class="text-center">Operation</th>
+                                    </tr>
+                                    <tr ng-repeat="item in queryMessageByTopicAndKeyResult">
+                                        <td class="text-center">{{item.msgId}}</td>
+                                        <td class="text-center">{{item.properties.TAGS}}</td>
+                                        <td class="text-center">{{item.properties.KEYS}}</td>
+                                        <td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
+                                        </td>
+                                        <td class="text-center">
+                                            <button class="btn btn-raised btn-sm btn-primary" type="button"
+                                                    ng-click="queryMessageByMessageId(item.msgId,item.topic)">Message Detail
+                                            </button>
+                                        </td>
+                                    </tr>
+                                </table>
+                            </div>
                         </md-content>
                     </md-tab>
                     <md-tab label="Message ID">
                         <h5 class="md-display-5">topic can't be empty if you producer client version>=v3.5.8</h5>
                         <md-content class="md-padding" style="min-height:600px">
-                            <form class="form-inline pull-left col-sm-12">
-                                <div class="form-group">
-                                    <label>Topic:</label>
-                                </div>
-                                <div class="form-group ">
-                                    <div style="width: 300px">
-                                        <select name="mySelectTopic" chosen
-                                                ng-model="selectedTopic"
-                                                ng-options="item for item in allTopicList"
-                                                required>
-                                            <option value=""></option>
-                                        </select>
+                            <div class="row">
+                                <form class="form-inline pull-left">
+                                    <div class="form-group">
+                                        <label>Topic:</label>
                                     </div>
-                                </div>
-                                <div class="form-group">
-                                    <label>MessageId:</label>
-                                    <input class="form-control" style="width: 450px" type="text" ng-model="messageId"
-                                           required/>
-                                </div>
-                                <button type="button" class="btn btn-raised btn-sm  btn-primary" data-toggle="modal"
-                                        ng-click="queryMessageByMessageId(messageId,selectedTopic)">
-                                    <span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
-                                </button>
-                            </form>
+                                    <div class="form-group ">
+                                        <div style="width: 300px">
+                                            <select name="mySelectTopic" chosen
+                                                    ng-model="selectedTopic"
+                                                    ng-options="item for item in allTopicList"
+                                                    required>
+                                                <option value=""></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>MessageId:</label>
+                                        <input class="form-control" style="width: 450px" type="text" ng-model="messageId"
+                                               required/>
+                                    </div>
+                                    <button type="button" class="btn btn-raised btn-sm  btn-primary" data-toggle="modal"
+                                            ng-click="queryMessageByMessageId(messageId,selectedTopic)">
+                                        <span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
+                                    </button>
+                                </form>
+                            </div>
                         </md-content>
                     </md-tab>
                 </md-tabs>
diff --git a/src/main/resources/static/view/pages/messageTrace.html b/src/main/resources/static/view/pages/messageTrace.html
index 8f317ac..24b906f 100644
--- a/src/main/resources/static/view/pages/messageTrace.html
+++ b/src/main/resources/static/view/pages/messageTrace.html
@@ -15,8 +15,8 @@
   ~ limitations under the License.
   -->
 <div class="container-fluid" id="deployHistoryList">
-    <div class="modal-header">
-        <div class="row">
+    <div class="modal-header" style="border-bottom: 0px">
+        <div class="row" style="margin-left: 0px">
             <label style="color: #000000">{{ 'TRACE_TOPIC' | translate }}:</label>
             <div style="display: inline-block; min-width: 300px">
                 <select name="mySelect" chosen
@@ -34,105 +34,110 @@
             <md-content>
                 <md-tabs md-dynamic-height="" md-border-bottom="">
                     <md-tab label="Message Key">
+                        <h5 class="md-display-5">Only Return 64 Messages</h5>
                         <md-content class="md-padding" style="min-height:600px">
-                            <h5 class="md-display-5">Only Return 64 Messages</h5>
+                            <div class="row">
 
-                            <form class="form-inline pull-left col-sm-12">
-                                <div class="form-group">
-                                    <label>Topic:</label>
-                                </div>
-                                <div class="form-group">
-                                    <div style="width: 300px">
-                                        <select name="mySelectTopic" chosen
-                                                ng-model="selectedTopic"
-                                                ng-options="item for item in allTopicList"
-                                                required>
-                                            <option value=""></option>
-                                        </select>
+                                <form class="form-inline pull-left">
+                                    <div class="form-group">
+                                        <label>Topic:</label>
+                                    </div>
+                                    <div class="form-group">
+                                        <div style="width: 300px">
+                                            <select name="mySelectTopic" chosen
+                                                    ng-model="selectedTopic"
+                                                    ng-options="item for item in allTopicList"
+                                                    required>
+                                                <option value=""></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>Key:</label>
+                                        <input class="form-control" style="width: 450px" type="text" ng-model="key"
+                                               required/>
                                     </div>
-                                </div>
-                                <div class="form-group">
-                                    <label>Key:</label>
-                                    <input class="form-control" style="width: 450px" type="text" ng-model="key"
-                                           required/>
-                                </div>
 
-                                <button type="button" class="btn btn-raised btn-sm  btn-primary" data-toggle="modal"
-                                        ng-click="queryMessageByTopicAndKey()">
-                                    <span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
-                                </button>
-                            </form>
-                            <table class="table table-bordered">
-                                <tr>
-                                    <th class="text-center">Message ID</th>
-                                    <th class="text-center">Tag</th>
-                                    <th class="text-center">Message Key</th>
-                                    <th class="text-center">StoreTime</th>
-                                    <th class="text-center">Operation</th>
-                                </tr>
-                                <tr ng-repeat="item in queryMessageByTopicAndKeyResult">
-                                    <td class="text-center">{{item.msgId}}</td>
-                                    <td class="text-center">{{item.properties.TAGS}}</td>
-                                    <td class="text-center">{{item.properties.KEYS}}</td>
-                                    <td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
-                                    </td>
-                                    <td class="text-center">
-                                        <button class="btn btn-raised btn-sm btn-primary" type="button"
-                                                ng-click="queryMessageTraceByMessageId(item.msgId, selectedTraceTopic)">{{ 'MESSAGE_TRACE_DETAIL' | translate }}
-                                        </button>
-                                    </td>
-                                </tr>
-                            </table>
+                                    <button type="button" class="btn btn-raised btn-sm  btn-primary" data-toggle="modal"
+                                            ng-click="queryMessageByTopicAndKey()">
+                                        <span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
+                                    </button>
+                                </form>
+                                <table class="table table-bordered text-middle">
+                                    <tr>
+                                        <th class="text-center">Message ID</th>
+                                        <th class="text-center">Tag</th>
+                                        <th class="text-center">Message Key</th>
+                                        <th class="text-center">StoreTime</th>
+                                        <th class="text-center">Operation</th>
+                                    </tr>
+                                    <tr ng-repeat="item in queryMessageByTopicAndKeyResult">
+                                        <td class="text-center">{{item.msgId}}</td>
+                                        <td class="text-center">{{item.properties.TAGS}}</td>
+                                        <td class="text-center">{{item.properties.KEYS}}</td>
+                                        <td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
+                                        </td>
+                                        <td class="text-center">
+                                            <button class="btn btn-raised btn-sm btn-primary" type="button"
+                                                    ng-click="queryMessageTraceByMessageId(item.msgId, selectedTraceTopic)">{{ 'MESSAGE_TRACE_DETAIL' | translate }}
+                                            </button>
+                                        </td>
+                                    </tr>
+                                </table>
+                            </div>
                         </md-content>
                     </md-tab>
                     <md-tab label="Message ID">
                         <h5 class="md-display-5">topic can't be empty if you producer client version>=v3.5.8</h5>
                         <md-content class="md-padding" style="min-height:600px">
-                            <form class="form-inline pull-left col-sm-12">
-                                <div class="form-group">
-                                    <label>Topic:</label>
-                                </div>
-                                <div class="form-group ">
-                                    <div style="width: 300px">
-                                        <select name="mySelectTopic" chosen
-                                                ng-model="selectedTopic"
-                                                ng-options="item for item in allTopicList"
-                                                required>
-                                            <option value=""></option>
-                                        </select>
+                            <div class="row">
+
+                                <form class="form-inline pull-left">
+                                    <div class="form-group">
+                                        <label>Topic:</label>
                                     </div>
-                                </div>
-                                <div class="form-group">
-                                    <label>MessageId:</label>
-                                    <input class="form-control" style="width: 450px" type="text" ng-model="messageId"
-                                           required/>
-                                </div>
-                                <button type="button" class="btn btn-raised btn-sm  btn-primary" data-toggle="modal"
-                                        ng-click="queryMessageByMessageId(messageId,selectedTopic)">
-                                    <span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
-                                </button>
-                            </form>
-                            <table class="table table-bordered">
-                                <tr>
-                                    <th class="text-center">Message ID</th>
-                                    <th class="text-center">Tag</th>
-                                    <th class="text-center">Message Key</th>
-                                    <th class="text-center">StoreTime</th>
-                                    <th class="text-center">Operation</th>
-                                </tr>
-                                <tr ng-repeat="item in queryMessageByMessageIdResult">
-                                    <td class="text-center">{{item.msgId}}</td>
-                                    <td class="text-center">{{item.properties.TAGS}}</td>
-                                    <td class="text-center">{{item.properties.KEYS}}</td>
-                                    <td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
-                                    </td>
-                                    <td class="text-center">
-                                        <button class="btn btn-raised btn-sm btn-primary" type="button"
-                                                ng-click="queryMessageTraceByMessageId(item.msgId, selectedTraceTopic)">{{ 'MESSAGE_TRACE_DETAIL' | translate }}
-                                        </button>
-                                    </td>
-                                </tr>
-                            </table>
+                                    <div class="form-group ">
+                                        <div style="width: 300px">
+                                            <select name="mySelectTopic" chosen
+                                                    ng-model="selectedTopic"
+                                                    ng-options="item for item in allTopicList"
+                                                    required>
+                                                <option value=""></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>MessageId:</label>
+                                        <input class="form-control" style="width: 450px" type="text" ng-model="messageId"
+                                               required/>
+                                    </div>
+                                    <button type="button" class="btn btn-raised btn-sm  btn-primary" data-toggle="modal"
+                                            ng-click="queryMessageByMessageId(messageId,selectedTopic)">
+                                        <span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
+                                    </button>
+                                </form>
+                                <table class="table table-bordered text-middle">
+                                    <tr>
+                                        <th class="text-center">Message ID</th>
+                                        <th class="text-center">Tag</th>
+                                        <th class="text-center">Message Key</th>
+                                        <th class="text-center">StoreTime</th>
+                                        <th class="text-center">Operation</th>
+                                    </tr>
+                                    <tr ng-repeat="item in queryMessageByMessageIdResult">
+                                        <td class="text-center">{{item.msgId}}</td>
+                                        <td class="text-center">{{item.properties.TAGS}}</td>
+                                        <td class="text-center">{{item.properties.KEYS}}</td>
+                                        <td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
+                                        </td>
+                                        <td class="text-center">
+                                            <button class="btn btn-raised btn-sm btn-primary" type="button"
+                                                    ng-click="queryMessageTraceByMessageId(item.msgId, selectedTraceTopic)">{{ 'MESSAGE_TRACE_DETAIL' | translate }}
+                                            </button>
+                                        </td>
+                                    </tr>
+                                </table>
+                            </div>
                         </md-content>
                     </md-tab>
                 </md-tabs>
diff --git a/src/main/resources/static/view/pages/topic.html b/src/main/resources/static/view/pages/topic.html
index d28f676..bea5ac7 100644
--- a/src/main/resources/static/view/pages/topic.html
+++ b/src/main/resources/static/view/pages/topic.html
@@ -48,7 +48,7 @@
                     </tr>
                     <tr ng-repeat="fTopic in topicShowList"
                         ng-init="sysFlag = fTopic.startsWith('%SYS%'); topic = sysFlag?fTopic.substring(5):fTopic">
-                        <td class="text-left"><font color={{sysFlag?"red":""}}>{{topic}}</font></td>
+                        <td class="text-center"><font color={{sysFlag?"red":""}}>{{topic}}</font></td>
                         <td class="text-left">
                             <button class="btn btn-raised btn-sm btn-primary" type="button"
                                     ng-click="statsView(topic)">{{'STATUS' | translate}}
diff --git a/src/test/java/org/apache/rocketmq/dashboard/controller/ConsumerControllerTest.java b/src/test/java/org/apache/rocketmq/dashboard/controller/ConsumerControllerTest.java
index 45b6bf9..87c3cc0 100644
--- a/src/test/java/org/apache/rocketmq/dashboard/controller/ConsumerControllerTest.java
+++ b/src/test/java/org/apache/rocketmq/dashboard/controller/ConsumerControllerTest.java
@@ -92,7 +92,7 @@ public class ConsumerControllerTest extends BaseControllerTest {
         requestBuilder = MockMvcRequestBuilders.get(url);
         perform = mockMvc.perform(requestBuilder);
         perform.andExpect(status().isOk())
-            .andExpect(jsonPath("$.data", hasSize(1)))
+            .andExpect(jsonPath("$.data", hasSize(2)))
             .andExpect(jsonPath("$.data[0].group").value("group_test"))
             .andExpect(jsonPath("$.data[0].consumeType").value(ConsumeType.CONSUME_ACTIVELY.name()))
             .andExpect(jsonPath("$.data[0].messageModel").value(MessageModel.CLUSTERING.name()));
diff --git a/src/test/java/org/apache/rocketmq/dashboard/util/MockObjectUtil.java b/src/test/java/org/apache/rocketmq/dashboard/util/MockObjectUtil.java
index 83fbffb..89a6764 100644
--- a/src/test/java/org/apache/rocketmq/dashboard/util/MockObjectUtil.java
+++ b/src/test/java/org/apache/rocketmq/dashboard/util/MockObjectUtil.java
@@ -135,10 +135,13 @@ public class MockObjectUtil {
 
     public static SubscriptionGroupWrapper createSubscriptionGroupWrapper() {
         SubscriptionGroupWrapper wrapper = new SubscriptionGroupWrapper();
+        ConcurrentMap<String, SubscriptionGroupConfig> subscriptionGroupTable = new ConcurrentHashMap(2);
         SubscriptionGroupConfig config = new SubscriptionGroupConfig();
         config.setGroupName("group_test");
-        ConcurrentMap<String, SubscriptionGroupConfig> subscriptionGroupTable = new ConcurrentHashMap(2);
         subscriptionGroupTable.put("group_test", config);
+        SubscriptionGroupConfig sysGroupConfig = new SubscriptionGroupConfig();
+        sysGroupConfig.setGroupName(MixAll.TOOLS_CONSUMER_GROUP);
+        subscriptionGroupTable.put(MixAll.TOOLS_CONSUMER_GROUP, sysGroupConfig);
         wrapper.setSubscriptionGroupTable(subscriptionGroupTable);
         wrapper.setDataVersion(new DataVersion());
         return wrapper;