Fix namespace stats display info (#146)

### Modifications
* Obtaining namespace Statistics from Backend
* Fix broker stats display
* Fix MapperScan when spring boot start
diff --git a/front-end/src/api/namespaces.js b/front-end/src/api/namespaces.js
index 27280e3..a909699 100644
--- a/front-end/src/api/namespaces.js
+++ b/front-end/src/api/namespaces.js
@@ -25,6 +25,13 @@
   })
 }
 
+export function fetchNamespaceStats(tenant, namespace) {
+  return request({
+    url: SPRING_BASE_URL_V2 + `/namespaces/${tenant}/${namespace}/stats`,
+    method: 'get'
+  })
+}
+
 export function fetchNamespacePolicies(tenantNamespace) {
   return request({
     url: BASE_URL_V2 + `/namespaces/${tenantNamespace}`,
diff --git a/front-end/src/views/management/brokers/broker.vue b/front-end/src/views/management/brokers/broker.vue
index 6f59657..5ae56d5 100644
--- a/front-end/src/views/management/brokers/broker.vue
+++ b/front-end/src/views/management/brokers/broker.vue
@@ -184,10 +184,10 @@
           }
         }
         this.brokerStats.push({
-          'inBytes': throughputIn,
-          'outBytes': throughputOut,
-          'inMsg': bandwidthIn,
-          'outMsg': bandwidthOut
+          'inBytes': throughputIn.toFixed(2),
+          'outBytes': throughputOut.toFixed(2),
+          'inMsg': bandwidthIn.toFixed(2),
+          'outMsg': bandwidthOut.toFixed(2)
         })
       })
     },
diff --git a/front-end/src/views/management/clusters/cluster.vue b/front-end/src/views/management/clusters/cluster.vue
index 3af4e1e..314e8a5 100644
--- a/front-end/src/views/management/clusters/cluster.vue
+++ b/front-end/src/views/management/clusters/cluster.vue
@@ -112,7 +112,7 @@
               border
               fit
               highlight-current-row
-              style="width: 100%;">
+              style="margin-top:10px">
               <el-table-column :label="$t('ip.nameLabel')" min-width="50px" align="center">
                 <template slot-scope="scope">
                   <router-link :to="'/management/clusters/' + scope.row.cluster + '/' + scope.row.isolationPolicy + '/namespaceIsolationPolicy'" class="link-type">
@@ -422,10 +422,10 @@
               }
             }
             brokerInfo['ownedNamespaces'] = numberNamespaces
-            brokerInfo['throughputIn'] = throughputIn
-            brokerInfo['throughputOut'] = throughputOut
-            brokerInfo['msgRateOut'] = msgRateOut
-            brokerInfo['msgRateIn'] = msgRateIn
+            brokerInfo['throughputIn'] = throughputIn.toFixed(2)
+            brokerInfo['throughputOut'] = throughputOut.toFixed(2)
+            brokerInfo['msgRateOut'] = msgRateOut.toFixed(2)
+            brokerInfo['msgRateIn'] = msgRateIn.toFixed(2)
           })
           this.brokersList.push(brokerInfo)
         }
diff --git a/front-end/src/views/management/namespaces/namespace.vue b/front-end/src/views/management/namespaces/namespace.vue
index c95a4ad..64550af 100644
--- a/front-end/src/views/management/namespaces/namespace.vue
+++ b/front-end/src/views/management/namespaces/namespace.vue
@@ -677,7 +677,8 @@
   unloadOnCluster,
   clearBacklogOnCluster,
   deleteNamespace,
-  clearBundleBacklogOnCluster
+  clearBundleBacklogOnCluster,
+  fetchNamespaceStats
 } from '@/api/namespaces'
 import { putTopic, fetchTopicsStatsByPulsarManager } from '@/api/topics'
 import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
@@ -896,7 +897,20 @@
     this.activeBundleCluster = this.replicationClustersValue.length > 0 ? this.replicationClustersValue[0] : ''
   },
   methods: {
+    getNamespaceStats() {
+      fetchNamespaceStats(this.postForm.tenant, this.postForm.namespace).then(response => {
+        if (!response.data) return
+        this.namespaceStats = []
+        this.namespaceStats.push({
+          inMsg: response.data.inMsg,
+          outMsg: response.data.outMsg,
+          inBytes: response.data.msgThroughputIn,
+          outBytes: response.data.msgThroughputOut
+        })
+      })
+    },
     getStats() {
+      this.getNamespaceStats()
       this.getTopics()
     },
     getTopicsStats() {
diff --git a/src/main/java/io/streamnative/pulsar/manager/PulsarManagerApplication.java b/src/main/java/io/streamnative/pulsar/manager/PulsarManagerApplication.java
index b707da1..0142c9c 100644
--- a/src/main/java/io/streamnative/pulsar/manager/PulsarManagerApplication.java
+++ b/src/main/java/io/streamnative/pulsar/manager/PulsarManagerApplication.java
@@ -20,7 +20,7 @@
 
 @SpringBootApplication
 @EnableZuulProxy
-@MapperScan("com.manager.pulsar.mapper")
+@MapperScan("io.streamnative.pulsar.manager.mapper")
 public class PulsarManagerApplication {
 
 	public static void main(String[] args) {
diff --git a/src/main/java/io/streamnative/pulsar/manager/controller/NamespacesController.java b/src/main/java/io/streamnative/pulsar/manager/controller/NamespacesController.java
index f013348..6faefa4 100644
--- a/src/main/java/io/streamnative/pulsar/manager/controller/NamespacesController.java
+++ b/src/main/java/io/streamnative/pulsar/manager/controller/NamespacesController.java
@@ -123,4 +123,22 @@
         return ResponseEntity.ok(namespacesEntity);
     }
 
+    @ApiOperation(value = "Query namespace stats info by tenant and namespace")
+    @ApiResponses({
+            @ApiResponse(code = 200, message = "ok"),
+            @ApiResponse(code = 500, message = "Internal server error")
+    })
+    @RequestMapping(value = "/namespaces/{tenant}/{namespace}/stats", method = RequestMethod.GET)
+    public ResponseEntity<Map<String, Object>> getNamespacesStats(
+            @ApiParam(value = "The name of tenant")
+            @Size(min = 1, max = 255)
+            @PathVariable String tenant,
+            @ApiParam(value = "The name of namespace")
+            @Size(min = 1, max = 255)
+            @PathVariable String namespace) {
+        String env = request.getHeader("environment");
+        Map<String, Object> namespaceStats = namespacesService.getNamespaceStats(tenant, namespace, env);
+        return ResponseEntity.ok(namespaceStats);
+    }
+
 }
diff --git a/src/main/java/io/streamnative/pulsar/manager/dao/TopicsStatsRepositoryImpl.java b/src/main/java/io/streamnative/pulsar/manager/dao/TopicsStatsRepositoryImpl.java
index db3bc86..5802f45 100644
--- a/src/main/java/io/streamnative/pulsar/manager/dao/TopicsStatsRepositoryImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/dao/TopicsStatsRepositoryImpl.java
@@ -56,13 +56,12 @@
     public Page<TopicStatsEntity> findByNamespace(Integer pageNum,
                                                   Integer pageSize,
                                                   String environment,
-                                                  String cluster,
                                                   String tenant,
                                                   String namespace,
                                                   long timestamp) {
         PageHelper.startPage(pageNum, pageSize);
         Page<TopicStatsEntity> topicStatsEntities =
-            topicsStatsMapper.findByNamespace(environment, cluster, tenant, namespace, timestamp);
+            topicsStatsMapper.findByNamespace(environment, tenant, namespace, timestamp);
         return topicStatsEntities;
     }
 
diff --git a/src/main/java/io/streamnative/pulsar/manager/entity/TopicsStatsRepository.java b/src/main/java/io/streamnative/pulsar/manager/entity/TopicsStatsRepository.java
index d4db0b8..aaa21ae 100644
--- a/src/main/java/io/streamnative/pulsar/manager/entity/TopicsStatsRepository.java
+++ b/src/main/java/io/streamnative/pulsar/manager/entity/TopicsStatsRepository.java
@@ -35,7 +35,6 @@
     Page<TopicStatsEntity> findByNamespace(Integer pageNum,
                                            Integer pageSize,
                                            String environment,
-                                           String cluster,
                                            String tenant,
                                            String namespace,
                                            long timestamp);
diff --git a/src/main/java/io/streamnative/pulsar/manager/mapper/TopicsStatsMapper.java b/src/main/java/io/streamnative/pulsar/manager/mapper/TopicsStatsMapper.java
index 1d8fece..b70d466 100644
--- a/src/main/java/io/streamnative/pulsar/manager/mapper/TopicsStatsMapper.java
+++ b/src/main/java/io/streamnative/pulsar/manager/mapper/TopicsStatsMapper.java
@@ -47,11 +47,10 @@
 
     @Select("SELECT topicStatsId,environment,cluster,tenant,namespace,bundle,persistent,topic,producerCount,subscriptionCount," +
             "msgRateIn,msgThroughputIn,msgRateOut,msgThroughputOut,averageMsgSize,storageSize,timestamp FROM topicsStats " +
-            "WHERE cluster=#{cluster} and environment=#{environment} and tenant=#{tenant} and namespace=#{namespace} " +
+            "WHERE environment=#{environment} and tenant=#{tenant} and namespace=#{namespace} " +
             "and timestamp=#{timestamp}")
     Page<TopicStatsEntity> findByNamespace(
             @Param("environment") String environment,
-            @Param("cluster") String cluster,
             @Param("tenant") String tenant,
             @Param("namespace") String namespace,
             @Param("timestamp") long timestamp);
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/NamespacesService.java b/src/main/java/io/streamnative/pulsar/manager/service/NamespacesService.java
index 5cbdc96..c1dea4c 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/NamespacesService.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/NamespacesService.java
@@ -13,6 +13,8 @@
  */
 package io.streamnative.pulsar.manager.service;
 
+import com.google.common.collect.Maps;
+
 import java.util.Map;
 
 public interface NamespacesService {
@@ -20,4 +22,5 @@
     Map<String, Object> getNamespaceList(
             Integer pageNum, Integer pageSize, String tenant, String requestHost);
 
+    Map<String, Object> getNamespaceStats(String tenant, String namespace, String env);
 }
\ No newline at end of file
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/impl/NamespacesServiceImpl.java b/src/main/java/io/streamnative/pulsar/manager/service/impl/NamespacesServiceImpl.java
index 61c1fc3..b5ec5bf 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/impl/NamespacesServiceImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/impl/NamespacesServiceImpl.java
@@ -13,9 +13,12 @@
  */
 package io.streamnative.pulsar.manager.service.impl;
 
+import com.github.pagehelper.Page;
 import com.google.common.collect.Maps;
 import com.google.common.reflect.TypeToken;
 import com.google.gson.Gson;
+import io.streamnative.pulsar.manager.entity.TopicStatsEntity;
+import io.streamnative.pulsar.manager.entity.TopicsStatsRepository;
 import io.streamnative.pulsar.manager.service.NamespacesService;
 import io.streamnative.pulsar.manager.service.TopicsService;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
@@ -23,9 +26,7 @@
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 @Service
 public class NamespacesServiceImpl implements NamespacesService {
@@ -34,6 +35,9 @@
     private boolean directRequestBroker;
 
     @Autowired
+    private TopicsStatsRepository topicsStatsRepository;
+
+    @Autowired
     private TopicsService topicsService;
 
     public Map<String, Object> getNamespaceList(Integer pageNum, Integer pageSize, String tenant, String requestHost) {
@@ -65,4 +69,35 @@
         return namespacesMap;
     }
 
+    public Map<String, Object> getNamespaceStats(
+            String tenant,
+            String namespace,
+            String env) {
+        Map<String, Object> namespaceStatsMap = Maps.newHashMap();
+        Optional<TopicStatsEntity> topicStatsEntity = topicsStatsRepository.findMaxTime();
+        if (topicStatsEntity.isPresent()) {
+            double msgRateIn = 0;
+            double msgThroughputIn = 0;
+            double msgRateOut = 0;
+            double msgThroughputOut = 0;
+            TopicStatsEntity topicStats = topicStatsEntity.get();
+            Page<TopicStatsEntity> topicCountPage = topicsStatsRepository.findByNamespace(
+                    1, 1, env, tenant, namespace, topicStats.getTimestamp());
+            topicCountPage.count(true);
+            Page<TopicStatsEntity> topicStatsEntities = topicsStatsRepository.findByNamespace(
+                    1, (int) topicCountPage.getTotal(), env, tenant, namespace, topicStats.getTimestamp());
+            for (TopicStatsEntity statsEntity : topicStatsEntities.getResult()) {
+                msgRateIn += statsEntity.getMsgRateIn();
+                msgRateOut += statsEntity.getMsgRateOut();
+                msgThroughputIn += statsEntity.getMsgThroughputIn();
+                msgThroughputOut += statsEntity.getMsgThroughputOut();
+            }
+            namespaceStatsMap.put("inMsg", msgRateIn);
+            namespaceStatsMap.put("outMsg", msgRateOut);
+            namespaceStatsMap.put("msgThroughputIn", msgThroughputIn);
+            namespaceStatsMap.put("msgThroughputOut", msgThroughputOut);
+        }
+        return namespaceStatsMap;
+    }
+
 }
\ No newline at end of file
diff --git a/src/test/java/io/streamnative/pulsar/manager/service/NamespacesServiceImplTest.java b/src/test/java/io/streamnative/pulsar/manager/service/NamespacesServiceImplTest.java
index e2547af..5bc2373 100644
--- a/src/test/java/io/streamnative/pulsar/manager/service/NamespacesServiceImplTest.java
+++ b/src/test/java/io/streamnative/pulsar/manager/service/NamespacesServiceImplTest.java
@@ -30,6 +30,7 @@
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
 
+import java.util.List;
 import java.util.Map;
 
 @RunWith(PowerMockRunner.class)
@@ -48,6 +49,9 @@
     @Autowired
     private NamespacesService namespacesService;
 
+    @Autowired
+    private BrokerStatsService brokerStatsService;
+
     @Test
     public void namespaceServiceImplTest() {
         PowerMockito.mockStatic(HttpUtil.class);
@@ -66,4 +70,40 @@
         Assert.assertFalse((Boolean) result.get("isPage"));
         Assert.assertEquals(result.get("data").toString(), "[{topics=1, namespace=default}]");
     }
+
+    @Test
+    public void getNamespaceStatsTest() {
+        PowerMockito.mockStatic(HttpUtil.class);
+        Map<String, String> header = Maps.newHashMap();
+        header.put("Content-Type", "application/json");
+        PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/clusters", header))
+                .thenReturn("[\"standalone\"]");
+
+        PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/brokers/standalone", header))
+                .thenReturn("[\"localhost:8080\"]");
+        PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/broker-stats/topics", header))
+                .thenReturn(BrokerStatsServiceImplTest.testData);
+        PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/clusters/standalone/failureDomains", header))
+                .thenReturn("{}");
+        PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/clusters/standalone", header))
+                .thenReturn("{\n" +
+                        "\"serviceUrl\" : \"http://tengdeMBP:8080\",\n" +
+                        "\"brokerServiceUrl\" : \"pulsar://tengdeMBP:6650\"\n" +
+                        "}");
+        String environment = "staging";
+        String cluster = "standalone";
+        String serviceUrl = "http://localhost:8080";
+        brokerStatsService.collectStatsToDB(
+                System.currentTimeMillis() / 1000,
+                environment,
+                cluster,
+                serviceUrl
+        );
+        Map<String, Object> namespaceStats = namespacesService.getNamespaceStats(
+                environment, "public", "functions");
+        Assert.assertEquals(namespaceStats.get("outMsg"), 0.0);
+        Assert.assertEquals(namespaceStats.get("inMsg"), 0.0);
+        Assert.assertEquals(namespaceStats.get("msgThroughputIn"), 0.0);
+        Assert.assertEquals(namespaceStats.get("msgThroughputOut"), 0.0);
+    }
 }