Skip to content

Commit 7539ab9

Browse files
committed
add rollback stat api
Signed-off-by: Patrick Zhao <zhaoyu@koderover.com>
1 parent 5704ee6 commit 7539ab9

File tree

9 files changed

+735
-3
lines changed

9 files changed

+735
-3
lines changed

pkg/microservice/aslan/config/consts.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,20 @@ const (
708708

709709
type ProductionType string
710710

711+
func (p ProductionType) ToBool() *bool {
712+
switch p {
713+
case Production:
714+
val := true
715+
return &val
716+
case Testing:
717+
val := false
718+
return &val
719+
case Both:
720+
return nil
721+
}
722+
return nil
723+
}
724+
711725
const (
712726
Production = "production"
713727
Testing = "testing"

pkg/microservice/aslan/core/common/repository/mongodb/env_info.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package mongodb
22

33
import (
44
"context"
5+
"fmt"
56
"time"
67

78
"github.com/pkg/errors"
@@ -102,6 +103,7 @@ type ListEnvInfoOption struct {
102103
StartTime int64
103104
EndTime int64
104105
Operation config.EnvOperation
106+
Production *bool
105107
}
106108

107109
func (c *EnvInfoColl) List(ctx context.Context, opt *ListEnvInfoOption) ([]*models.EnvInfo, int64, error) {
@@ -125,6 +127,10 @@ func (c *EnvInfoColl) List(ctx context.Context, opt *ListEnvInfoOption) ([]*mode
125127
if opt.Operation != "" {
126128
findOption["operation"] = opt.Operation
127129
}
130+
// 根据 production 参数过滤环境信息
131+
if opt.Production != nil {
132+
findOption["production"] = *opt.Production
133+
}
128134
findOption["create_time"] = bson.M{
129135
"$gte": opt.StartTime,
130136
"$lte": opt.EndTime,
@@ -205,3 +211,88 @@ func (c *EnvInfoColl) List(ctx context.Context, opt *ListEnvInfoOption) ([]*mode
205211

206212
return res, total, nil
207213
}
214+
215+
type RollbackServiceCount struct {
216+
Production bool `bson:"production" json:"production"`
217+
ProjectName string `bson:"project_name,omitempty" json:"project_name,omitempty"`
218+
ServiceName string `bson:"service_name,omitempty" json:"service_name,omitempty"`
219+
Count int `bson:"count" json:"count"`
220+
}
221+
222+
func (c *EnvInfoColl) GetTopRollbackedService(ctx context.Context, startTime, endTime int64, productionType config.ProductionType, projects []string, top int) ([]*RollbackServiceCount, error) {
223+
// 参数验证
224+
if startTime > endTime {
225+
return nil, fmt.Errorf("invalid time range: startTime (%d) should be less than or equal to endTime (%d)", startTime, endTime)
226+
}
227+
if top <= 0 {
228+
return nil, fmt.Errorf("invalid top parameter: top (%d) should be greater than 0", top)
229+
}
230+
231+
// 构建查询条件:只查询 rollback 操作
232+
query := bson.M{
233+
"operation": config.EnvOperationRollback,
234+
"create_time": bson.M{"$gte": startTime, "$lte": endTime},
235+
}
236+
237+
// 根据生产环境类型过滤
238+
switch productionType {
239+
case config.Production:
240+
query["production"] = true
241+
case config.Testing:
242+
query["production"] = false
243+
case config.Both:
244+
break
245+
default:
246+
return nil, fmt.Errorf("invalid production type: %s", productionType)
247+
}
248+
249+
if len(projects) > 0 {
250+
query["project_name"] = bson.M{"$in": projects}
251+
}
252+
253+
// 构建聚合管道
254+
pipeline := []bson.M{
255+
// 匹配 rollback 操作记录
256+
{"$match": query},
257+
// 按项目、服务和生产环境类型分组,统计 rollback 次数
258+
{
259+
"$group": bson.M{
260+
"_id": bson.M{
261+
"production": "$production",
262+
"project_name": "$project_name",
263+
"service_name": "$service_name",
264+
},
265+
"count": bson.M{"$sum": 1},
266+
},
267+
},
268+
// 按 rollback 次数降序排序
269+
{"$sort": bson.M{"count": -1}},
270+
// 限制返回数量
271+
{"$limit": top},
272+
// 投影字段,映射到返回结构
273+
{
274+
"$project": bson.M{
275+
"_id": 0,
276+
"production": "$_id.production",
277+
"project_name": "$_id.project_name",
278+
"service_name": "$_id.service_name",
279+
"count": 1,
280+
},
281+
},
282+
}
283+
284+
// 执行聚合查询
285+
cursor, err := c.Aggregate(mongotool.SessionContext(ctx, c.Session), pipeline)
286+
if err != nil {
287+
return nil, errors.Wrap(err, "failed to aggregate rollback service statistics")
288+
}
289+
defer cursor.Close(mongotool.SessionContext(ctx, c.Session))
290+
291+
// 解析结果
292+
result := make([]*RollbackServiceCount, 0)
293+
if err := cursor.All(mongotool.SessionContext(ctx, c.Session), &result); err != nil {
294+
return nil, errors.Wrap(err, "failed to decode rollback service statistics")
295+
}
296+
297+
return result, nil
298+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
Copyright 2024 The KodeRover Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package handler
18+
19+
import (
20+
"github.com/gin-gonic/gin"
21+
e "github.com/koderover/zadig/v2/pkg/tool/errors"
22+
23+
"github.com/koderover/zadig/v2/pkg/microservice/aslan/core/stat/service"
24+
internalhandler "github.com/koderover/zadig/v2/pkg/shared/handler"
25+
)
26+
27+
func CreateWeeklyRollbackStat(c *gin.Context) {
28+
ctx := internalhandler.NewContext(c)
29+
defer func() { internalhandler.JSONResponse(c, ctx) }()
30+
31+
ctx.RespErr = service.CreateWeeklyRollbackStat(ctx.Logger)
32+
}
33+
34+
// @Summary 获取回滚统计总数
35+
// @Description
36+
// @Tags stat
37+
// @Accept json
38+
// @Produce json
39+
// @Param startDate query int true "开始时间,格式为时间戳"
40+
// @Param endDate query int true "结束时间,格式为时间戳"
41+
// @Param projects query []string true "项目列表"
42+
// @Param type query string true "环境类型,可选值为 production、testing、both"
43+
// @Success 200 {object} service.RollbackTotalStat
44+
// @Router /api/aslan/stat/v2/quality/rollback/total [get]
45+
func GetRollbackTotalStat(c *gin.Context) {
46+
ctx := internalhandler.NewContext(c)
47+
defer func() { internalhandler.JSONResponse(c, ctx) }()
48+
49+
args := new(getStatGeneralReq)
50+
if err := c.ShouldBindQuery(args); err != nil {
51+
ctx.RespErr = e.ErrInvalidParam.AddErr(err)
52+
return
53+
}
54+
55+
ctx.Resp, ctx.RespErr = service.GetRollbackTotalStat(args.StartTime, args.EndTime, args.Projects, args.ProductionType, ctx.Logger)
56+
}
57+
58+
// @Summary 获取回滚次数TopN的服务
59+
// @Description
60+
// @Tags stat
61+
// @Accept json
62+
// @Produce json
63+
// @Param startDate query int true "开始时间,格式为时间戳"
64+
// @Param endDate query int true "结束时间,格式为时间戳"
65+
// @Param top query int true "TopN"
66+
// @Param projects query []string true "项目列表"
67+
// @Param type query string true "环境类型,可选值为 production、testing、both"
68+
// @Success 200 {array} mongodb.RollbackServiceCount
69+
// @Router /api/aslan/stat/v2/quality/rollback/service/top [get]
70+
func GetTopRollbackedService(c *gin.Context) {
71+
ctx := internalhandler.NewContext(c)
72+
defer func() { internalhandler.JSONResponse(c, ctx) }()
73+
74+
args := new(getStateReqWithTop)
75+
if err := c.ShouldBindQuery(args); err != nil {
76+
ctx.RespErr = e.ErrInvalidParam.AddErr(err)
77+
return
78+
}
79+
80+
ctx.Resp, ctx.RespErr = service.GetTopRollbackedProject(args.StartTime, args.EndTime, args.Top, args.ProductionType, args.Projects, ctx.Logger)
81+
}
82+
83+
// @Summary 获取回滚周趋势
84+
// @Description
85+
// @Tags stat
86+
// @Accept json
87+
// @Produce json
88+
// @Param startDate query int true "开始时间,格式为时间戳"
89+
// @Param endDate query int true "结束时间,格式为时间戳"
90+
// @Param projects query []string true "项目列表"
91+
// @Param type query string true "环境类型,可选值为 production、testing、both"
92+
// @Success 200 {array} models.WeeklyRollbackStat
93+
// @Router /api/aslan/stat/v2/quality/rollback/trend/weekly [get]
94+
func GetRollbackWeeklyTrend(c *gin.Context) {
95+
ctx := internalhandler.NewContext(c)
96+
defer func() { internalhandler.JSONResponse(c, ctx) }()
97+
98+
args := new(getStatGeneralReq)
99+
if err := c.ShouldBindQuery(args); err != nil {
100+
ctx.RespErr = e.ErrInvalidParam.AddErr(err)
101+
return
102+
}
103+
104+
ctx.Resp, ctx.RespErr = service.GetRollbackWeeklyTrend(args.StartTime, args.EndTime, args.Projects, args.ProductionType, ctx.Logger)
105+
}
106+
107+
// @Summary 获取回滚数据详情
108+
// @Description
109+
// @Tags stat
110+
// @Accept json
111+
// @Produce json
112+
// @Param startDate query int true "开始时间,格式为时间戳"
113+
// @Param endDate query int true "结束时间,格式为时间戳"
114+
// @Param projects query []string true "项目列表"
115+
// @Param type query string true "环境类型,可选值为 production、testing、both"
116+
// @Success 200 {object} service.GetRollbackStatResponse
117+
// @Router /api/aslan/stat/v2/quality/rollback/stat [get]
118+
func GetRollbackStat(c *gin.Context) {
119+
ctx := internalhandler.NewContext(c)
120+
defer func() { internalhandler.JSONResponse(c, ctx) }()
121+
122+
args := new(getStatGeneralReq)
123+
if err := c.ShouldBindQuery(args); err != nil {
124+
ctx.RespErr = e.ErrInvalidParam.AddErr(err)
125+
return
126+
}
127+
128+
ctx.Resp, ctx.RespErr = service.GetRollbackStat(args.StartTime, args.EndTime, args.ProductionType, args.Projects, ctx.Logger)
129+
}

pkg/microservice/aslan/core/stat/handler/router.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ func (*Router) Inject(router *gin.RouterGroup) {
105105
deployV2.GET("/service/failure", GetTopDeployFailuresByService)
106106
}
107107

108+
rollbackV2 := qualityV2.Group("rollback")
109+
{
110+
rollbackV2.POST("/weekly", CreateWeeklyRollbackStat)
111+
rollbackV2.GET("/total", GetRollbackTotalStat)
112+
rollbackV2.GET("/service/top", GetTopRollbackedService)
113+
rollbackV2.GET("/trend/weekly", GetRollbackWeeklyTrend)
114+
rollbackV2.GET("/stat", GetRollbackStat)
115+
}
116+
108117
testV2 := qualityV2.Group("test")
109118
{
110119
testV2.GET("/count", GetTestCount)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Copyright 2024 The KodeRover Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package models
18+
19+
type WeeklyRollbackStat struct {
20+
ProjectKey string `bson:"project_key" json:"project_key,omitempty"`
21+
Production bool `bson:"production" json:"production"`
22+
Rollback int `bson:"rollback" json:"rollback"`
23+
Date string `bson:"date" json:"date"`
24+
CreateTime int64 `bson:"create_time" json:"create_time"`
25+
UpdateTime int64 `bson:"update_time" json:"update_time"`
26+
}
27+
28+
func (WeeklyRollbackStat) TableName() string {
29+
return "rollback_stat_weekly"
30+
}

0 commit comments

Comments
 (0)