diff --git a/backend/internal/api/handlers/pipeline.go b/backend/internal/api/handlers/pipeline.go index f9c0829..ff5a825 100644 --- a/backend/internal/api/handlers/pipeline.go +++ b/backend/internal/api/handlers/pipeline.go @@ -10,7 +10,7 @@ import ( "gorm.io/gorm" ) -// PipelineHandler 流水线处理器 +// PipelineHandler 流水线处理器(流水线 + 应用发布两个子域,应用发布方法拆分至 pipeline_release.go) // Logan.Li type PipelineHandler struct { db *gorm.DB } @@ -237,7 +237,6 @@ func (h *PipelineHandler) TriggerPipeline(c *gin.Context) { var req models.TriggerPipelineRequest c.ShouldBindJSON(&req) - // 获取最新构建号 var maxBuildNumber int h.db.Model(&models.PipelineRun{}).Where("pipeline_id = ?", id).Select("COALESCE(MAX(build_number), 0)").Scan(&maxBuildNumber) @@ -305,237 +304,3 @@ func (h *PipelineHandler) ListRuns(c *gin.Context) { }, }) } - -// ListReleases 获取发布列表 -// @Summary 获取发布列表 -// @Description 获取所有应用发布列表 -// @Tags 应用发布 -// @Produce json -// @Security Bearer -// @Param page query int false "页码" -// @Param page_size query int false "每页数量" -// @Success 200 {object} models.Response{data=models.ListResponse} -// @Router /releases [get] -func (h *PipelineHandler) ListReleases(c *gin.Context) { - var query models.PageQuery - c.ShouldBindQuery(&query) - - var total int64 - h.db.Model(&models.AppRelease{}).Count(&total) - - var releases []models.AppRelease - h.db.Order("created_at DESC"). - Offset(query.GetOffset()). - Limit(query.GetLimit()). - Find(&releases) - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "success", - Data: models.ListResponse{ - List: releases, - Total: total, - }, - }) -} - -// CreateRelease 创建发布 -// @Summary 创建发布 -// @Description 创建新应用发布 -// @Tags 应用发布 -// @Accept json -// @Produce json -// @Security Bearer -// @Param request body models.CreateAppReleaseRequest true "创建请求" -// @Success 200 {object} models.Response{data=models.AppRelease} -// @Router /releases [post] -func (h *PipelineHandler) CreateRelease(c *gin.Context) { - var req models.CreateAppReleaseRequest - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, models.Response{ - Code: 400, - Message: "请求参数错误: " + err.Error(), - }) - return - } - - release := models.AppRelease{ - AppName: req.AppName, - Version: req.Version, - Environment: req.Environment, - Status: "pending", - CommitID: req.CommitID, - CommitMsg: req.CommitMsg, - Servers: "[]", - Description: req.Description, - } - - if err := h.db.Create(&release).Error; err != nil { - c.JSON(http.StatusInternalServerError, models.Response{ - Code: 500, - Message: "创建失败", - }) - return - } - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "创建成功", - Data: release, - }) -} - -// ApproveRelease 审批发布 -// @Summary 审批发布 -// @Description 审批应用发布 -// @Tags 应用发布 -// @Produce json -// @Security Bearer -// @Param id path int true "发布ID" -// @Success 200 {object} models.Response{data=models.AppRelease} -// @Router /releases/{id}/approve [post] -func (h *PipelineHandler) ApproveRelease(c *gin.Context) { - id, _ := strconv.Atoi(c.Param("id")) - - var release models.AppRelease - if err := h.db.First(&release, id).Error; err != nil { - c.JSON(http.StatusNotFound, models.Response{ - Code: 404, - Message: "发布不存在", - }) - return - } - - release.Status = "approved" - // release.Approver = middleware.GetUsername(c) - // now := time.Now() - // release.ApprovedAt = &now - - if err := h.db.Save(&release).Error; err != nil { - c.JSON(http.StatusInternalServerError, models.Response{ - Code: 500, - Message: "审批失败", - }) - return - } - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "审批成功", - Data: release, - }) -} - -// DeployRelease 部署发布 -// @Summary 部署发布 -// @Description 部署应用发布 -// @Tags 应用发布 -// @Produce json -// @Security Bearer -// @Param id path int true "发布ID" -// @Success 200 {object} models.Response{data=models.AppRelease} -// @Router /releases/{id}/deploy [post] -func (h *PipelineHandler) DeployRelease(c *gin.Context) { - id, _ := strconv.Atoi(c.Param("id")) - - var release models.AppRelease - if err := h.db.First(&release, id).Error; err != nil { - c.JSON(http.StatusNotFound, models.Response{ - Code: 404, - Message: "发布不存在", - }) - return - } - - release.Status = "deploying" - if err := h.db.Save(&release).Error; err != nil { - c.JSON(http.StatusInternalServerError, models.Response{ - Code: 500, - Message: "部署失败", - }) - return - } - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "部署中", - Data: release, - }) -} - -// RejectRelease 驳回发布申请 // Logan.Li -func (h *PipelineHandler) RejectRelease(c *gin.Context) { - id, _ := strconv.Atoi(c.Param("id")) - - var release models.AppRelease - if err := h.db.First(&release, id).Error; err != nil { - c.JSON(http.StatusNotFound, models.Response{ - Code: 404, - Message: "发布不存在", - }) - return - } - - if release.Status != "pending" { - c.JSON(http.StatusBadRequest, models.Response{ - Code: 400, - Message: "仅待审批状态可驳回", - }) - return - } - - release.Status = "rejected" - if err := h.db.Save(&release).Error; err != nil { - c.JSON(http.StatusInternalServerError, models.Response{ - Code: 500, - Message: "驳回失败", - }) - return - } - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "已驳回", - Data: release, - }) -} - -// RollbackRelease 回滚发布 -// @Summary 回滚发布 -// @Description 回滚应用发布 -// @Tags 应用发布 -// @Produce json -// @Security Bearer -// @Param id path int true "发布ID" -// @Success 200 {object} models.Response{data=models.AppRelease} -// @Router /releases/{id}/rollback [post] -func (h *PipelineHandler) RollbackRelease(c *gin.Context) { - id, _ := strconv.Atoi(c.Param("id")) - - var release models.AppRelease - if err := h.db.First(&release, id).Error; err != nil { - c.JSON(http.StatusNotFound, models.Response{ - Code: 404, - Message: "发布不存在", - }) - return - } - - release.Status = "rolled_back" - // now := time.Now() - // release.RollbackAt = &now - - if err := h.db.Save(&release).Error; err != nil { - c.JSON(http.StatusInternalServerError, models.Response{ - Code: 500, - Message: "回滚失败", - }) - return - } - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "回滚成功", - Data: release, - }) -} diff --git a/backend/internal/api/handlers/pipeline_release.go b/backend/internal/api/handlers/pipeline_release.go new file mode 100644 index 0000000..310ec1b --- /dev/null +++ b/backend/internal/api/handlers/pipeline_release.go @@ -0,0 +1,246 @@ +package handlers + +import ( + "net/http" + "strconv" + + "ezops/internal/models" + + "github.com/gin-gonic/gin" +) + +// 应用发布(Release)相关 handler,与 PipelineHandler 共享 receiver,按应用发布域从 pipeline.go 拆分 // Logan.Li + +// ListReleases 获取发布列表 +// @Summary 获取发布列表 +// @Description 获取所有应用发布列表 +// @Tags 应用发布 +// @Produce json +// @Security Bearer +// @Param page query int false "页码" +// @Param page_size query int false "每页数量" +// @Success 200 {object} models.Response{data=models.ListResponse} +// @Router /releases [get] +func (h *PipelineHandler) ListReleases(c *gin.Context) { + var query models.PageQuery + c.ShouldBindQuery(&query) + + var total int64 + h.db.Model(&models.AppRelease{}).Count(&total) + + var releases []models.AppRelease + h.db.Order("created_at DESC"). + Offset(query.GetOffset()). + Limit(query.GetLimit()). + Find(&releases) + + c.JSON(http.StatusOK, models.Response{ + Code: 200, + Message: "success", + Data: models.ListResponse{ + List: releases, + Total: total, + }, + }) +} + +// CreateRelease 创建发布 +// @Summary 创建发布 +// @Description 创建新应用发布 +// @Tags 应用发布 +// @Accept json +// @Produce json +// @Security Bearer +// @Param request body models.CreateAppReleaseRequest true "创建请求" +// @Success 200 {object} models.Response{data=models.AppRelease} +// @Router /releases [post] +func (h *PipelineHandler) CreateRelease(c *gin.Context) { + var req models.CreateAppReleaseRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, models.Response{ + Code: 400, + Message: "请求参数错误: " + err.Error(), + }) + return + } + + release := models.AppRelease{ + AppName: req.AppName, + Version: req.Version, + Environment: req.Environment, + Status: "pending", + CommitID: req.CommitID, + CommitMsg: req.CommitMsg, + Servers: "[]", + Description: req.Description, + } + + if err := h.db.Create(&release).Error; err != nil { + c.JSON(http.StatusInternalServerError, models.Response{ + Code: 500, + Message: "创建失败", + }) + return + } + + c.JSON(http.StatusOK, models.Response{ + Code: 200, + Message: "创建成功", + Data: release, + }) +} + +// ApproveRelease 审批发布 +// @Summary 审批发布 +// @Description 审批应用发布 +// @Tags 应用发布 +// @Produce json +// @Security Bearer +// @Param id path int true "发布ID" +// @Success 200 {object} models.Response{data=models.AppRelease} +// @Router /releases/{id}/approve [post] +func (h *PipelineHandler) ApproveRelease(c *gin.Context) { + id, _ := strconv.Atoi(c.Param("id")) + + var release models.AppRelease + if err := h.db.First(&release, id).Error; err != nil { + c.JSON(http.StatusNotFound, models.Response{ + Code: 404, + Message: "发布不存在", + }) + return + } + + release.Status = "approved" + // release.Approver = middleware.GetUsername(c) + // now := time.Now() + // release.ApprovedAt = &now + + if err := h.db.Save(&release).Error; err != nil { + c.JSON(http.StatusInternalServerError, models.Response{ + Code: 500, + Message: "审批失败", + }) + return + } + + c.JSON(http.StatusOK, models.Response{ + Code: 200, + Message: "审批成功", + Data: release, + }) +} + +// DeployRelease 部署发布 +// @Summary 部署发布 +// @Description 部署应用发布 +// @Tags 应用发布 +// @Produce json +// @Security Bearer +// @Param id path int true "发布ID" +// @Success 200 {object} models.Response{data=models.AppRelease} +// @Router /releases/{id}/deploy [post] +func (h *PipelineHandler) DeployRelease(c *gin.Context) { + id, _ := strconv.Atoi(c.Param("id")) + + var release models.AppRelease + if err := h.db.First(&release, id).Error; err != nil { + c.JSON(http.StatusNotFound, models.Response{ + Code: 404, + Message: "发布不存在", + }) + return + } + + release.Status = "deploying" + if err := h.db.Save(&release).Error; err != nil { + c.JSON(http.StatusInternalServerError, models.Response{ + Code: 500, + Message: "部署失败", + }) + return + } + + c.JSON(http.StatusOK, models.Response{ + Code: 200, + Message: "部署中", + Data: release, + }) +} + +// RejectRelease 驳回发布申请 // Logan.Li +func (h *PipelineHandler) RejectRelease(c *gin.Context) { + id, _ := strconv.Atoi(c.Param("id")) + + var release models.AppRelease + if err := h.db.First(&release, id).Error; err != nil { + c.JSON(http.StatusNotFound, models.Response{ + Code: 404, + Message: "发布不存在", + }) + return + } + + if release.Status != "pending" { + c.JSON(http.StatusBadRequest, models.Response{ + Code: 400, + Message: "仅待审批状态可驳回", + }) + return + } + + release.Status = "rejected" + if err := h.db.Save(&release).Error; err != nil { + c.JSON(http.StatusInternalServerError, models.Response{ + Code: 500, + Message: "驳回失败", + }) + return + } + + c.JSON(http.StatusOK, models.Response{ + Code: 200, + Message: "已驳回", + Data: release, + }) +} + +// RollbackRelease 回滚发布 +// @Summary 回滚发布 +// @Description 回滚应用发布 +// @Tags 应用发布 +// @Produce json +// @Security Bearer +// @Param id path int true "发布ID" +// @Success 200 {object} models.Response{data=models.AppRelease} +// @Router /releases/{id}/rollback [post] +func (h *PipelineHandler) RollbackRelease(c *gin.Context) { + id, _ := strconv.Atoi(c.Param("id")) + + var release models.AppRelease + if err := h.db.First(&release, id).Error; err != nil { + c.JSON(http.StatusNotFound, models.Response{ + Code: 404, + Message: "发布不存在", + }) + return + } + + release.Status = "rolled_back" + // now := time.Now() + // release.RollbackAt = &now + + if err := h.db.Save(&release).Error; err != nil { + c.JSON(http.StatusInternalServerError, models.Response{ + Code: 500, + Message: "回滚失败", + }) + return + } + + c.JSON(http.StatusOK, models.Response{ + Code: 200, + Message: "回滚成功", + Data: release, + }) +}