diff --git a/src/models/node_resource.go b/src/models/node_resource.go index 44295377..d7b791cb 100644 --- a/src/models/node_resource.go +++ b/src/models/node_resource.go @@ -77,3 +77,34 @@ func ResIdsGetByNodeIds(nids []int64) ([]int64, error) { err := DB["rdb"].Table(new(NodeResource)).In("node_id", nids).Select("res_id").Find(&ids) return ids, err } + +// ResCountGetByNodeIdsAndWhere 根据叶子节点和Where条件获取资源数量表 +func ResCountGetByNodeIdsAndCate(nids []int64, cate string) (int, error) { + if len(nids) == 0 { + return 0, nil + } + + var nodeRess []NodeResource + err := DB["rdb"].Table(new(NodeResource)).In("node_id", nids).Find(&nodeRess) + if err != nil { + return 0, err + } + + cnt := 0 + for _, res := range nodeRess { + res, err := ResourceGet("id=?", res.ResId) + if err != nil { + return 0, err + } + + if res == nil { + continue + } + + if res.Cate == cate { + cnt++ + } + } + + return cnt, nil +} diff --git a/src/modules/rdb/http/router.go b/src/modules/rdb/http/router.go index d2733aa3..17aebdfe 100644 --- a/src/modules/rdb/http/router.go +++ b/src/modules/rdb/http/router.go @@ -87,6 +87,9 @@ func Config(r *gin.Engine) { rootLogin.GET("/sso/clients/:clientId", ssoClientGet) rootLogin.PUT("/sso/clients/:clientId", ssoClientPut) rootLogin.DELETE("/sso/clients/:clientId", ssoClientDel) + + rootLogin.GET("/resources/tenant-rank", tenantResourcesCountRank) + rootLogin.GET("/resources/project-rank", projectResourcesCountRank) } userLogin := r.Group("/api/rdb").Use(shouldBeLogin()) diff --git a/src/modules/rdb/http/router_resource.go b/src/modules/rdb/http/router_resource.go index 4e6204a7..cd39d950 100644 --- a/src/modules/rdb/http/router_resource.go +++ b/src/modules/rdb/http/router_resource.go @@ -2,6 +2,7 @@ package http import ( "fmt" + "sort" "strings" "github.com/gin-gonic/gin" @@ -570,3 +571,176 @@ func renderAllResourcesCountByCate(c *gin.Context) { renderData(c, list, nil) } + +// 租户项目粒度资源排行 +type resourceRank struct { + Name string `json:"name"` + Count int `json:"count"` +} + +func tenantResourcesCountRank(c *gin.Context) { + resCate := queryStr(c, "resource_cate", "virtual") + top := queryInt(c, "top", 0) + if top < 0 { + dangerous(fmt.Errorf("param top < 0")) + } + + tenantNode, err := models.NodeGets("cate=?", "tenant") + dangerous(err) + + tenantNodeLen := len(tenantNode) + tenantNodeName := make(map[string]string, tenantNodeLen) + for _, node := range tenantNode { + if node.Ident != "" && node.Name != "" { + tenantNodeName[node.Ident] = node.Name + } + } + + ress, err := models.ResourceGets("cate=?", resCate) + dangerous(err) + + resMap := make(map[string]int, 50) + for _, res := range ress { + tenant := res.Tenant + if tenant != "" { + if _, ok := resMap[tenant]; !ok { + resMap[tenant] = 0 + } + + resMap[tenant]++ + } + } + + var ret []*resourceRank + for k, v := range resMap { + tR := new(resourceRank) + name, ok := tenantNodeName[k] + if !ok { + name = k + } + tR.Name = name + tR.Count = v + + ret = append(ret, tR) + } + + retLen := len(ret) + if retLen > 0 { + sort.Slice(ret, func(i, j int) bool { return ret[i].Count > ret[j].Count }) + } + + if top == 0 { + renderData(c, ret, nil) + return + } + + if retLen > top { + renderData(c, ret[:top], nil) + return + } + + renderData(c, ret, nil) +} + +func projectResourcesCountRank(c *gin.Context) { + resCate := queryStr(c, "resource_cate", "virtual") + top := queryInt(c, "top", 0) + if top < 0 { + dangerous(fmt.Errorf("param top < 0")) + } + + // 获取全部project + projectNodes, err := models.NodeGets("cate=?", "project") + dangerous(err) + + projectNodesLen := len(projectNodes) + workerNum := 50 + if projectNodesLen < workerNum { + workerNum = projectNodesLen + } + + worker := make(chan struct{}, workerNum) // 控制 goroutine 并发数 + dataChan := make(chan *resourceRank, projectNodesLen) + + done := make(chan struct{}, 1) + resp := make([]*resourceRank, 0) + go func() { + defer func() { done <- struct{}{} }() + for d := range dataChan { + resp = append(resp, d) + } + }() + + for _, pN := range projectNodes { + worker <- struct{}{} + go singleProjectResCount(pN.Id, resCate, worker, + dataChan) + } + + // 等待所有 goroutine 执行完成 + for i := 0; i < workerNum; i++ { + worker <- struct{}{} + } + close(dataChan) + + // 等待所有 dataChan 被消费完 + <-done + + //整理resp中数据 + respLen := len(resp) + if respLen > 0 { + sort.Slice(resp, func(i, j int) bool { return resp[i].Count > resp[j].Count }) + } + + if top == 0 { + renderData(c, resp, nil) + return + } + + if respLen > top { + renderData(c, resp[:top], nil) + return + } + + renderData(c, resp, nil) +} + +func singleProjectResCount(id int64, resCate string, worker chan struct{}, dataChan chan *resourceRank) { + defer func() { + <-worker + }() + + node, err := models.NodeGet("id=?", id) + if err != nil { + logger.Error(err) + return + } + + if node == nil { + logger.Errorf("node id %d is nil", id) + return + } + + leafIds, err := node.LeafIds() + if err != nil { + logger.Error(err) + return + } + + cnt, err := models.ResCountGetByNodeIdsAndCate(leafIds, resCate) + if err != nil { + logger.Error(err) + return + } + + data := new(resourceRank) + nodeName := node.Name + if nodeName != "" { + data.Name = nodeName + } else { + data.Name = node.Ident + } + data.Count = cnt + + dataChan <- data +}