From cffcda8ea78aad065c813952d2fc024dfcdef30c Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Fri, 22 May 2026 16:47:25 +0800 Subject: [PATCH 01/13] feat: add system workflow webhook events Signed-off-by: huanghongbo-hhb --- .../core/common/repository/models/settings.go | 15 ++++ .../common/repository/mongodb/settings.go | 28 ++++++ .../service/instantmessage/workflow_task.go | 40 +++++++++ .../instantmessage/workflow_task_test.go | 74 ++++++++++++++++ .../common/service/webhooknotify/types.go | 37 ++++---- .../service/workflowcontroller/system_hook.go | 36 ++++++++ .../service/workflowcontroller/workflow.go | 5 ++ .../aslan/core/system/handler/router.go | 6 ++ .../core/system/handler/workflow_hook.go | 85 +++++++++++++++++++ .../core/system/service/workflow_hook.go | 30 +++++++ .../service/workflow/workflow_task_v4.go | 3 + 11 files changed, 341 insertions(+), 18 deletions(-) create mode 100644 pkg/microservice/aslan/core/common/service/instantmessage/workflow_task_test.go create mode 100644 pkg/microservice/aslan/core/common/service/workflowcontroller/system_hook.go create mode 100644 pkg/microservice/aslan/core/system/handler/workflow_hook.go create mode 100644 pkg/microservice/aslan/core/system/service/workflow_hook.go diff --git a/pkg/microservice/aslan/core/common/repository/models/settings.go b/pkg/microservice/aslan/core/common/repository/models/settings.go index 8041f93823..f8d3bacbf5 100644 --- a/pkg/microservice/aslan/core/common/repository/models/settings.go +++ b/pkg/microservice/aslan/core/common/repository/models/settings.go @@ -28,6 +28,7 @@ type SystemSetting struct { Privacy *PrivacySettings `bson:"privacy" json:"privacy"` Language string `bson:"language" json:"language"` ServerURL string `bson:"server_url" json:"server_url"` + WorkflowHook *WorkflowHookSettings `bson:"workflow_hook" json:"workflow_hook"` ReleasePlanHook *ReleasePlanHookSettings `bson:"release_plan_hook" json:"release_plan_hook"` UpdateTime int64 `bson:"update_time" json:"update_time"` } @@ -81,6 +82,13 @@ type ReleasePlanHookSettings struct { HookEvents []ReleasePlanHookEvent `json:"hook_events" bson:"hook_events"` } +type WorkflowHookSettings struct { + Enable bool `json:"enable" bson:"enable"` + HookAddress string `json:"hook_address" bson:"hook_address"` + HookSecret string `json:"hook_secret" bson:"hook_secret"` + HookEvents []WorkflowHookEvent `json:"hook_events" bson:"hook_events"` +} + func (r *ReleasePlanHookSettings) ToHookSettings() *HookSettings { return &HookSettings{ Enable: r.Enable, @@ -98,6 +106,13 @@ const ( ReleasePlanHookEventAllJobDone ReleasePlanHookEvent = "all_job_done" ) +type WorkflowHookEvent string + +const ( + WorkflowHookEventStartExecute WorkflowHookEvent = "start_execute" + WorkflowHookEventCompleteExecute WorkflowHookEvent = "complete_execute" +) + func (SystemSetting) TableName() string { return "system_setting" } diff --git a/pkg/microservice/aslan/core/common/repository/mongodb/settings.go b/pkg/microservice/aslan/core/common/repository/mongodb/settings.go index 36aa5d583e..d6c8f1ed50 100644 --- a/pkg/microservice/aslan/core/common/repository/mongodb/settings.go +++ b/pkg/microservice/aslan/core/common/repository/mongodb/settings.go @@ -223,3 +223,31 @@ func (c *SystemSettingColl) GetReleasePlanHookSetting() (*models.ReleasePlanHook return resp.ReleasePlanHook, nil } + +func (c *SystemSettingColl) UpdateWorkflowHookSetting(hookSetting *models.WorkflowHookSettings) error { + id, _ := primitive.ObjectIDFromHex(setting.LocalClusterID) + query := bson.M{"_id": id} + + change := bson.M{"$set": bson.M{"workflow_hook": hookSetting}} + + _, err := c.UpdateOne(context.TODO(), query, change) + return err +} + +func (c *SystemSettingColl) GetWorkflowHookSetting() (*models.WorkflowHookSettings, error) { + query := bson.M{} + resp := &models.SystemSetting{} + + err := c.FindOne(context.TODO(), query).Decode(resp) + if err != nil { + return nil, err + } + + if resp.WorkflowHook == nil { + return &models.WorkflowHookSettings{ + Enable: false, + }, nil + } + + return resp.WorkflowHook, nil +} diff --git a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go index 15ef5af21e..0f2fe87965 100644 --- a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go +++ b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go @@ -931,6 +931,46 @@ func (w *Service) getNotificationContent(notify *models.NotifyCtl, task *models. return w.getNotificationContentWithOptions(notify, task, nil) } +func (w *Service) BuildWorkflowWebhookNotify(task *models.WorkflowTask) (*webhooknotify.WorkflowNotify, error) { + _, _, _, webhookNotify, err := w.getNotificationContentWithOptions(&models.NotifyCtl{WebHookType: setting.NotifyWebHookTypeWebook}, task, nil) + if err != nil { + return nil, err + } + if webhookNotify == nil { + return nil, fmt.Errorf("failed to build workflow webhook payload for workflow %s, taskID: %d", task.WorkflowName, task.TaskID) + } + + return webhookNotify, nil +} + +func (w *Service) SendSystemWorkflowHook(task *models.WorkflowTask, hookSetting *models.WorkflowHookSettings, hookEvent models.WorkflowHookEvent) error { + if task == nil || !isWorkflowHookEventEnabled(hookSetting, hookEvent) { + return nil + } + + webhookNotify, err := w.BuildWorkflowWebhookNotify(task) + if err != nil { + return err + } + webhookNotify.EventName = hookEvent + + return webhooknotify.NewClient(hookSetting.HookAddress, hookSetting.HookSecret).SendWorkflowWebhook(webhookNotify) +} + +func isWorkflowHookEventEnabled(hookSetting *models.WorkflowHookSettings, hookEvent models.WorkflowHookEvent) bool { + if hookSetting == nil || !hookSetting.Enable { + return false + } + + for _, configuredEvent := range hookSetting.HookEvents { + if configuredEvent == hookEvent { + return true + } + } + + return false +} + type workflowNotificationOptions struct { StatusTextKeyOverride string PendingStageName string diff --git a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task_test.go b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task_test.go new file mode 100644 index 0000000000..3baf6c0a79 --- /dev/null +++ b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task_test.go @@ -0,0 +1,74 @@ +package instantmessage + +import ( + "testing" + + "github.com/stretchr/testify/require" + + commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" +) + +func TestIsWorkflowHookEventEnabled(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + hookSetting *commonmodels.WorkflowHookSettings + hookEvent commonmodels.WorkflowHookEvent + expected bool + }{ + { + name: "nil setting", + hookSetting: nil, + hookEvent: commonmodels.WorkflowHookEventStartExecute, + expected: false, + }, + { + name: "disabled setting", + hookSetting: &commonmodels.WorkflowHookSettings{ + Enable: false, + HookEvents: []commonmodels.WorkflowHookEvent{commonmodels.WorkflowHookEventStartExecute}, + }, + hookEvent: commonmodels.WorkflowHookEventStartExecute, + expected: false, + }, + { + name: "event not configured", + hookSetting: &commonmodels.WorkflowHookSettings{ + Enable: true, + HookEvents: []commonmodels.WorkflowHookEvent{commonmodels.WorkflowHookEventStartExecute}, + }, + hookEvent: commonmodels.WorkflowHookEventCompleteExecute, + expected: false, + }, + { + name: "start event configured", + hookSetting: &commonmodels.WorkflowHookSettings{ + Enable: true, + HookEvents: []commonmodels.WorkflowHookEvent{ + commonmodels.WorkflowHookEventStartExecute, + commonmodels.WorkflowHookEventCompleteExecute, + }, + }, + hookEvent: commonmodels.WorkflowHookEventStartExecute, + expected: true, + }, + { + name: "complete event configured", + hookSetting: &commonmodels.WorkflowHookSettings{ + Enable: true, + HookEvents: []commonmodels.WorkflowHookEvent{commonmodels.WorkflowHookEventCompleteExecute}, + }, + hookEvent: commonmodels.WorkflowHookEventCompleteExecute, + expected: true, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + require.Equal(t, testCase.expected, isWorkflowHookEventEnabled(testCase.hookSetting, testCase.hookEvent)) + }) + } +} diff --git a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go index 2c898d62e9..eb3755ceab 100644 --- a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go +++ b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go @@ -57,24 +57,25 @@ type WebHookNotify struct { } type WorkflowNotify struct { - TaskID int64 `json:"task_id"` - ProjectName string `json:"project_name"` - ProjectDisplayName string `json:"project_display_name"` - WorkflowName string `json:"workflow_name"` - WorkflowDisplayName string `json:"workflow_display_name"` - Status config.Status `json:"status"` - Remark string `json:"remark"` - DetailURL string `json:"detail_url"` - Error string `json:"error"` - CreateTime int64 `json:"create_time"` - StartTime int64 `json:"start_time"` - EndTime int64 `json:"end_time"` - Stages []*WorkflowNotifyStage `json:"stages"` - TaskCreator string `json:"task_creator"` - TaskCreatorID string `json:"task_creator_id"` - TaskCreatorPhone string `json:"task_creator_phone"` - TaskCreatorEmail string `json:"task_creator_email"` - TaskType config.CustomWorkflowTaskType `json:"task_type"` + TaskID int64 `json:"task_id"` + ProjectName string `json:"project_name"` + ProjectDisplayName string `json:"project_display_name"` + WorkflowName string `json:"workflow_name"` + WorkflowDisplayName string `json:"workflow_display_name"` + EventName commonmodels.WorkflowHookEvent `json:"event_name,omitempty"` + Status config.Status `json:"status"` + Remark string `json:"remark"` + DetailURL string `json:"detail_url"` + Error string `json:"error"` + CreateTime int64 `json:"create_time"` + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` + Stages []*WorkflowNotifyStage `json:"stages"` + TaskCreator string `json:"task_creator"` + TaskCreatorID string `json:"task_creator_id"` + TaskCreatorPhone string `json:"task_creator_phone"` + TaskCreatorEmail string `json:"task_creator_email"` + TaskType config.CustomWorkflowTaskType `json:"task_type"` } type WorkflowNotifyStage struct { diff --git a/pkg/microservice/aslan/core/common/service/workflowcontroller/system_hook.go b/pkg/microservice/aslan/core/common/service/workflowcontroller/system_hook.go new file mode 100644 index 0000000000..5db99b7632 --- /dev/null +++ b/pkg/microservice/aslan/core/common/service/workflowcontroller/system_hook.go @@ -0,0 +1,36 @@ +/* +Copyright 2025 The KodeRover Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package workflowcontroller + +import ( + commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" + commonrepo "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" + "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/instantmessage" +) + +func SendSystemWorkflowHook(task *commonmodels.WorkflowTask, hookEvent commonmodels.WorkflowHookEvent) error { + hookSetting, err := commonrepo.NewSystemSettingColl().GetWorkflowHookSetting() + if err != nil { + return err + } + + if err := instantmessage.NewWeChatClient().SendSystemWorkflowHook(task, hookSetting, hookEvent); err != nil { + return err + } + + return nil +} diff --git a/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go b/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go index f14cd7a50b..ace56cd749 100644 --- a/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go +++ b/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go @@ -570,6 +570,11 @@ func (c *workflowCtl) updateWorkflowTask() { if err := instantmessage.NewWeChatClient().SendWorkflowTaskNotifications(c.workflowTask); err != nil { c.logger.Errorf("send workflow task notification failed, error: %v", err) } + if c.workflowTask.Finished() { + if err := SendSystemWorkflowHook(c.workflowTask, commonmodels.WorkflowHookEventCompleteExecute); err != nil { + c.logger.Errorf("send system workflow complete hook failed, workflow: %s, taskID: %d, error: %v", c.workflowTask.WorkflowName, c.workflowTask.TaskID, err) + } + } q := ConvertTaskToQueue(c.workflowTask) if err := Remove(q); err != nil { c.logger.Errorf("remove queue task: %s:%d error: %v", c.workflowTask.WorkflowName, c.workflowTask.TaskID, err) diff --git a/pkg/microservice/aslan/core/system/handler/router.go b/pkg/microservice/aslan/core/system/handler/router.go index 2bd3aa14cc..4183635355 100644 --- a/pkg/microservice/aslan/core/system/handler/router.go +++ b/pkg/microservice/aslan/core/system/handler/router.go @@ -267,6 +267,12 @@ func (*Router) Inject(router *gin.RouterGroup) { server.PUT("/url", SetSystemServerURL) } + workflowHook := router.Group("workflowHook") + { + workflowHook.GET("", GetWorkflowHookSetting) + workflowHook.PUT("", UpdateWorkflowHookSetting) + } + // --------------------------------------------------------------------------------------- // external system API // --------------------------------------------------------------------------------------- diff --git a/pkg/microservice/aslan/core/system/handler/workflow_hook.go b/pkg/microservice/aslan/core/system/handler/workflow_hook.go new file mode 100644 index 0000000000..98dd8fe3a5 --- /dev/null +++ b/pkg/microservice/aslan/core/system/handler/workflow_hook.go @@ -0,0 +1,85 @@ +/* +Copyright 2025 The KodeRover Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package handler + +import ( + "fmt" + + "github.com/gin-gonic/gin" + + commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" + "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/system/service" + internalhandler "github.com/koderover/zadig/v2/pkg/shared/handler" + e "github.com/koderover/zadig/v2/pkg/tool/errors" +) + +// @Summary 获取系统工作流 WebHook 配置 +// @Description 获取系统工作流 WebHook 配置 +// @Tags system +// @Accept json +// @Produce json +// @Success 200 {object} models.WorkflowHookSettings +// @Router /api/aslan/system/workflowHook [get] +func GetWorkflowHookSetting(c *gin.Context) { + ctx, err := internalhandler.NewContextWithAuthorization(c) + defer func() { internalhandler.JSONResponse(c, ctx) }() + + if err != nil { + ctx.RespErr = fmt.Errorf("authorization Info Generation failed: err %s", err) + ctx.UnAuthorized = true + return + } + + if !ctx.Resources.IsSystemAdmin { + ctx.UnAuthorized = true + return + } + + ctx.Resp, ctx.RespErr = service.GetWorkflowHookSetting() +} + +// @Summary 更新系统工作流 WebHook 配置 +// @Description 更新系统工作流 WebHook 配置 +// @Tags system +// @Accept json +// @Produce json +// @Param body body models.WorkflowHookSettings true "body" +// @Success 200 +// @Router /api/aslan/system/workflowHook [put] +func UpdateWorkflowHookSetting(c *gin.Context) { + ctx, err := internalhandler.NewContextWithAuthorization(c) + defer func() { internalhandler.JSONResponse(c, ctx) }() + + if err != nil { + ctx.RespErr = fmt.Errorf("authorization Info Generation failed: err %s", err) + ctx.UnAuthorized = true + return + } + + if !ctx.Resources.IsSystemAdmin { + ctx.UnAuthorized = true + return + } + + req := new(commonmodels.WorkflowHookSettings) + if err := c.ShouldBindJSON(req); err != nil { + ctx.RespErr = e.ErrInvalidParam.AddDesc(err.Error()) + return + } + + ctx.RespErr = service.UpdateWorkflowHookSetting(req) +} diff --git a/pkg/microservice/aslan/core/system/service/workflow_hook.go b/pkg/microservice/aslan/core/system/service/workflow_hook.go new file mode 100644 index 0000000000..c67ce42199 --- /dev/null +++ b/pkg/microservice/aslan/core/system/service/workflow_hook.go @@ -0,0 +1,30 @@ +/* +Copyright 2025 The KodeRover Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" + commonrepo "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" +) + +func GetWorkflowHookSetting() (*commonmodels.WorkflowHookSettings, error) { + return commonrepo.NewSystemSettingColl().GetWorkflowHookSetting() +} + +func UpdateWorkflowHookSetting(hookSetting *commonmodels.WorkflowHookSettings) error { + return commonrepo.NewSystemSettingColl().UpdateWorkflowHookSetting(hookSetting) +} diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go index 5e95e09336..25905a94e5 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go @@ -672,6 +672,9 @@ func CreateWorkflowTaskV4(args *CreateWorkflowTaskV4Args, workflow *commonmodels if err := instantmessage.NewWeChatClient().SendWorkflowTaskNotifications(workflowTask); err != nil { log.Errorf("send workflow task notification failed, error: %v", err) } + if err := runtimeWorkflowController.SendSystemWorkflowHook(workflowTask, commonmodels.WorkflowHookEventStartExecute); err != nil { + log.Errorf("send system workflow start hook failed, workflow: %s, taskID: %d, error: %v", workflowTask.WorkflowName, workflowTask.TaskID, err) + } if err := runtimeWorkflowController.CreateTask(workflowTask); err != nil { log.Errorf("create workflow task error: %v", err) From 47c6938836e33c9dd0c2943f7296d757c41dd467 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Fri, 22 May 2026 17:05:21 +0800 Subject: [PATCH 02/13] fix: include release plan in workflow webhook payload Signed-off-by: huanghongbo-hhb --- .../common/service/instantmessage/workflow_task.go | 14 ++++++++++++++ .../core/common/service/webhooknotify/types.go | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go index 0f2fe87965..25c65c53e1 100644 --- a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go +++ b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go @@ -746,6 +746,18 @@ func getStageTaskByName(stages []*models.StageTask, stageName string) *models.St return nil } +func buildWorkflowNotifyReleasePlan(releasePlan *models.ReleasePlanRef) *webhooknotify.WorkflowNotifyReleasePlan { + if releasePlan == nil { + return nil + } + + return &webhooknotify.WorkflowNotifyReleasePlan{ + ID: releasePlan.ID, + Name: releasePlan.Name, + Index: releasePlan.Index, + } +} + func (w *Service) getApproveNotificationContent(notify *models.NotifyCtl, task *models.WorkflowTask) (string, string, *LarkCard, *webhooknotify.WorkflowNotify, error) { project, err := templaterepo.NewProductColl().Find(task.ProjectName) if err != nil { @@ -774,6 +786,7 @@ func (w *Service) getApproveNotificationContent(notify *models.NotifyCtl, task * WorkflowDisplayName: task.WorkflowDisplayName, ProjectName: task.ProjectName, ProjectDisplayName: project.ProjectName, + ReleasePlan: buildWorkflowNotifyReleasePlan(task.ReleasePlan), Status: task.Status, Remark: task.Remark, Error: task.Error, @@ -1013,6 +1026,7 @@ func (w *Service) getNotificationContentWithOptions(notify *models.NotifyCtl, ta WorkflowDisplayName: task.WorkflowDisplayName, ProjectName: task.ProjectName, ProjectDisplayName: project.ProjectName, + ReleasePlan: buildWorkflowNotifyReleasePlan(task.ReleasePlan), Status: task.Status, Remark: task.Remark, Error: task.Error, diff --git a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go index eb3755ceab..1531b9a6ad 100644 --- a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go +++ b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go @@ -62,6 +62,7 @@ type WorkflowNotify struct { ProjectDisplayName string `json:"project_display_name"` WorkflowName string `json:"workflow_name"` WorkflowDisplayName string `json:"workflow_display_name"` + ReleasePlan *WorkflowNotifyReleasePlan `json:"release_plan,omitempty"` EventName commonmodels.WorkflowHookEvent `json:"event_name,omitempty"` Status config.Status `json:"status"` Remark string `json:"remark"` @@ -78,6 +79,12 @@ type WorkflowNotify struct { TaskType config.CustomWorkflowTaskType `json:"task_type"` } +type WorkflowNotifyReleasePlan struct { + ID string `json:"id"` + Name string `json:"name"` + Index int64 `json:"index"` +} + type WorkflowNotifyStage struct { Name string `json:"name"` Status config.Status `json:"status"` From 0dea0f5c2bf5da609f1b83edf90c2b3548c4ad2e Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Fri, 22 May 2026 17:54:39 +0800 Subject: [PATCH 03/13] chore: remove accidental workflow hook test file Signed-off-by: huanghongbo-hhb --- .../instantmessage/workflow_task_test.go | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 pkg/microservice/aslan/core/common/service/instantmessage/workflow_task_test.go diff --git a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task_test.go b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task_test.go deleted file mode 100644 index 3baf6c0a79..0000000000 --- a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package instantmessage - -import ( - "testing" - - "github.com/stretchr/testify/require" - - commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" -) - -func TestIsWorkflowHookEventEnabled(t *testing.T) { - t.Parallel() - - testCases := []struct { - name string - hookSetting *commonmodels.WorkflowHookSettings - hookEvent commonmodels.WorkflowHookEvent - expected bool - }{ - { - name: "nil setting", - hookSetting: nil, - hookEvent: commonmodels.WorkflowHookEventStartExecute, - expected: false, - }, - { - name: "disabled setting", - hookSetting: &commonmodels.WorkflowHookSettings{ - Enable: false, - HookEvents: []commonmodels.WorkflowHookEvent{commonmodels.WorkflowHookEventStartExecute}, - }, - hookEvent: commonmodels.WorkflowHookEventStartExecute, - expected: false, - }, - { - name: "event not configured", - hookSetting: &commonmodels.WorkflowHookSettings{ - Enable: true, - HookEvents: []commonmodels.WorkflowHookEvent{commonmodels.WorkflowHookEventStartExecute}, - }, - hookEvent: commonmodels.WorkflowHookEventCompleteExecute, - expected: false, - }, - { - name: "start event configured", - hookSetting: &commonmodels.WorkflowHookSettings{ - Enable: true, - HookEvents: []commonmodels.WorkflowHookEvent{ - commonmodels.WorkflowHookEventStartExecute, - commonmodels.WorkflowHookEventCompleteExecute, - }, - }, - hookEvent: commonmodels.WorkflowHookEventStartExecute, - expected: true, - }, - { - name: "complete event configured", - hookSetting: &commonmodels.WorkflowHookSettings{ - Enable: true, - HookEvents: []commonmodels.WorkflowHookEvent{commonmodels.WorkflowHookEventCompleteExecute}, - }, - hookEvent: commonmodels.WorkflowHookEventCompleteExecute, - expected: true, - }, - } - - for _, testCase := range testCases { - testCase := testCase - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - require.Equal(t, testCase.expected, isWorkflowHookEventEnabled(testCase.hookSetting, testCase.hookEvent)) - }) - } -} From b49479299145bee8dcffc04de442835e617a2918 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 11:20:37 +0800 Subject: [PATCH 04/13] fix: limit workflow hook config to enterprise edition Signed-off-by: huanghongbo-hhb --- .../aslan/core/system/handler/workflow_hook.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/microservice/aslan/core/system/handler/workflow_hook.go b/pkg/microservice/aslan/core/system/handler/workflow_hook.go index 98dd8fe3a5..5fd55d62cc 100644 --- a/pkg/microservice/aslan/core/system/handler/workflow_hook.go +++ b/pkg/microservice/aslan/core/system/handler/workflow_hook.go @@ -22,6 +22,7 @@ import ( "github.com/gin-gonic/gin" commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" + "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/util" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/system/service" internalhandler "github.com/koderover/zadig/v2/pkg/shared/handler" e "github.com/koderover/zadig/v2/pkg/tool/errors" @@ -49,6 +50,11 @@ func GetWorkflowHookSetting(c *gin.Context) { return } + if err := util.CheckZadigEnterpriseLicense(); err != nil { + ctx.RespErr = err + return + } + ctx.Resp, ctx.RespErr = service.GetWorkflowHookSetting() } @@ -75,6 +81,11 @@ func UpdateWorkflowHookSetting(c *gin.Context) { return } + if err := util.CheckZadigEnterpriseLicense(); err != nil { + ctx.RespErr = err + return + } + req := new(commonmodels.WorkflowHookSettings) if err := c.ShouldBindJSON(req); err != nil { ctx.RespErr = e.ErrInvalidParam.AddDesc(err.Error()) From 058fac85083632fcc471ba9e8fd77c81b38236c6 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 16:18:56 +0800 Subject: [PATCH 05/13] fix: remove workflow webhook creator phone field Signed-off-by: huanghongbo-hhb --- .../aslan/core/common/service/instantmessage/workflow_task.go | 2 -- .../aslan/core/common/service/webhooknotify/types.go | 1 - 2 files changed, 3 deletions(-) diff --git a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go index 25c65c53e1..75e12669f4 100644 --- a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go +++ b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go @@ -795,7 +795,6 @@ func (w *Service) getApproveNotificationContent(notify *models.NotifyCtl, task * EndTime: task.EndTime, TaskCreator: task.TaskCreator, TaskCreatorID: task.TaskCreatorID, - TaskCreatorPhone: task.TaskCreatorPhone, TaskCreatorEmail: task.TaskCreatorEmail, } @@ -1035,7 +1034,6 @@ func (w *Service) getNotificationContentWithOptions(notify *models.NotifyCtl, ta EndTime: task.EndTime, TaskCreator: task.TaskCreator, TaskCreatorID: task.TaskCreatorID, - TaskCreatorPhone: task.TaskCreatorPhone, TaskCreatorEmail: task.TaskCreatorEmail, TaskType: task.Type, } diff --git a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go index 1531b9a6ad..587fe76459 100644 --- a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go +++ b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go @@ -74,7 +74,6 @@ type WorkflowNotify struct { Stages []*WorkflowNotifyStage `json:"stages"` TaskCreator string `json:"task_creator"` TaskCreatorID string `json:"task_creator_id"` - TaskCreatorPhone string `json:"task_creator_phone"` TaskCreatorEmail string `json:"task_creator_email"` TaskType config.CustomWorkflowTaskType `json:"task_type"` } From 68a633b193dc3573e25c02994534384767789dae Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 17:01:42 +0800 Subject: [PATCH 06/13] fix: handle empty workflow hook settings Signed-off-by: huanghongbo-hhb --- .../aslan/core/common/repository/mongodb/settings.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/microservice/aslan/core/common/repository/mongodb/settings.go b/pkg/microservice/aslan/core/common/repository/mongodb/settings.go index d6c8f1ed50..168ec8a65a 100644 --- a/pkg/microservice/aslan/core/common/repository/mongodb/settings.go +++ b/pkg/microservice/aslan/core/common/repository/mongodb/settings.go @@ -240,6 +240,11 @@ func (c *SystemSettingColl) GetWorkflowHookSetting() (*models.WorkflowHookSettin err := c.FindOne(context.TODO(), query).Decode(resp) if err != nil { + if err == mongo.ErrNoDocuments { + return &models.WorkflowHookSettings{ + Enable: false, + }, nil + } return nil, err } From abbd69a55e8c657c89edba0e9e51d172dbedcadd Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 17:59:45 +0800 Subject: [PATCH 07/13] fix: move workflow hook event to top-level event Signed-off-by: huanghongbo-hhb --- .../service/instantmessage/workflow_task.go | 16 +++++-- .../common/service/webhooknotify/client.go | 4 +- .../common/service/webhooknotify/types.go | 43 ++++++++++--------- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go index 75e12669f4..b1d5b7b1c9 100644 --- a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go +++ b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go @@ -964,9 +964,8 @@ func (w *Service) SendSystemWorkflowHook(task *models.WorkflowTask, hookSetting if err != nil { return err } - webhookNotify.EventName = hookEvent - return webhooknotify.NewClient(hookSetting.HookAddress, hookSetting.HookSecret).SendWorkflowWebhook(webhookNotify) + return webhooknotify.NewClient(hookSetting.HookAddress, hookSetting.HookSecret).SendWorkflowWebhook(webhookNotify, workflowHookEventToWebhookEvent(hookEvent)) } func isWorkflowHookEventEnabled(hookSetting *models.WorkflowHookSettings, hookEvent models.WorkflowHookEvent) bool { @@ -983,6 +982,17 @@ func isWorkflowHookEventEnabled(hookSetting *models.WorkflowHookSettings, hookEv return false } +func workflowHookEventToWebhookEvent(hookEvent models.WorkflowHookEvent) webhooknotify.WebHookNotifyEvent { + switch hookEvent { + case models.WorkflowHookEventStartExecute: + return webhooknotify.WebHookNotifyEventWorkflowStartExecute + case models.WorkflowHookEventCompleteExecute: + return webhooknotify.WebHookNotifyEventWorkflowCompleteExecute + default: + return webhooknotify.WebHookNotifyEventWorkflow + } +} + type workflowNotificationOptions struct { StatusTextKeyOverride string PendingStageName string @@ -1517,7 +1527,7 @@ func (w *Service) sendNotification(title, content string, notify *models.NotifyC } case setting.NotifyWebHookTypeWebook: webhookclient := webhooknotify.NewClient(notify.WebhookNotificationConfig.Address, notify.WebhookNotificationConfig.Token) - err := webhookclient.SendWorkflowWebhook(webhookNotify) + err := webhookclient.SendWorkflowWebhook(webhookNotify, webhooknotify.WebHookNotifyEventWorkflow) if err != nil { return fmt.Errorf("failed to send notification to webhook, address %s, token: %s, error: %v", notify.WebhookNotificationConfig.Address, notify.WebhookNotificationConfig.Token, err) } diff --git a/pkg/microservice/aslan/core/common/service/webhooknotify/client.go b/pkg/microservice/aslan/core/common/service/webhooknotify/client.go index da6d0202ca..cd1d041b81 100644 --- a/pkg/microservice/aslan/core/common/service/webhooknotify/client.go +++ b/pkg/microservice/aslan/core/common/service/webhooknotify/client.go @@ -36,10 +36,10 @@ func NewClient(address, token string) *webhookNotifyclient { } } -func (c *webhookNotifyclient) SendWorkflowWebhook(webhookNotify *WorkflowNotify) error { +func (c *webhookNotifyclient) SendWorkflowWebhook(webhookNotify *WorkflowNotify, event WebHookNotifyEvent) error { notify := &WebHookNotify{ ObjectKind: WebHookNotifyObjectKindWorkflow, - Event: WebHookNotifyEventWorkflow, + Event: event, Workflow: webhookNotify, } return c.sendWebhook(notify) diff --git a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go index 587fe76459..c6d146ca0e 100644 --- a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go +++ b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go @@ -38,8 +38,10 @@ const ( type WebHookNotifyEvent string const ( - WebHookNotifyEventWorkflow WebHookNotifyEvent = "workflow" - WebHookNotifyEventReleasePlan WebHookNotifyEvent = "release_plan" + WebHookNotifyEventWorkflow WebHookNotifyEvent = "workflow" + WebHookNotifyEventWorkflowStartExecute WebHookNotifyEvent = "start_execute" + WebHookNotifyEventWorkflowCompleteExecute WebHookNotifyEvent = "complete_execute" + WebHookNotifyEventReleasePlan WebHookNotifyEvent = "release_plan" ) type WebHookNotifyObjectKind string @@ -57,25 +59,24 @@ type WebHookNotify struct { } type WorkflowNotify struct { - TaskID int64 `json:"task_id"` - ProjectName string `json:"project_name"` - ProjectDisplayName string `json:"project_display_name"` - WorkflowName string `json:"workflow_name"` - WorkflowDisplayName string `json:"workflow_display_name"` - ReleasePlan *WorkflowNotifyReleasePlan `json:"release_plan,omitempty"` - EventName commonmodels.WorkflowHookEvent `json:"event_name,omitempty"` - Status config.Status `json:"status"` - Remark string `json:"remark"` - DetailURL string `json:"detail_url"` - Error string `json:"error"` - CreateTime int64 `json:"create_time"` - StartTime int64 `json:"start_time"` - EndTime int64 `json:"end_time"` - Stages []*WorkflowNotifyStage `json:"stages"` - TaskCreator string `json:"task_creator"` - TaskCreatorID string `json:"task_creator_id"` - TaskCreatorEmail string `json:"task_creator_email"` - TaskType config.CustomWorkflowTaskType `json:"task_type"` + TaskID int64 `json:"task_id"` + ProjectName string `json:"project_name"` + ProjectDisplayName string `json:"project_display_name"` + WorkflowName string `json:"workflow_name"` + WorkflowDisplayName string `json:"workflow_display_name"` + ReleasePlan *WorkflowNotifyReleasePlan `json:"release_plan,omitempty"` + Status config.Status `json:"status"` + Remark string `json:"remark"` + DetailURL string `json:"detail_url"` + Error string `json:"error"` + CreateTime int64 `json:"create_time"` + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` + Stages []*WorkflowNotifyStage `json:"stages"` + TaskCreator string `json:"task_creator"` + TaskCreatorID string `json:"task_creator_id"` + TaskCreatorEmail string `json:"task_creator_email"` + TaskType config.CustomWorkflowTaskType `json:"task_type"` } type WorkflowNotifyReleasePlan struct { From 27d5fc502057c568def637988a4a3237033dc866 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 18:14:04 +0800 Subject: [PATCH 08/13] Revert "fix: move workflow hook event to top-level event" This reverts commit 2d57f23e7c27dbad7d12f2853f1206980ad417b1. Signed-off-by: huanghongbo-hhb --- .../service/instantmessage/workflow_task.go | 16 ++----- .../common/service/webhooknotify/client.go | 4 +- .../common/service/webhooknotify/types.go | 43 +++++++++---------- 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go index b1d5b7b1c9..75e12669f4 100644 --- a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go +++ b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go @@ -964,8 +964,9 @@ func (w *Service) SendSystemWorkflowHook(task *models.WorkflowTask, hookSetting if err != nil { return err } + webhookNotify.EventName = hookEvent - return webhooknotify.NewClient(hookSetting.HookAddress, hookSetting.HookSecret).SendWorkflowWebhook(webhookNotify, workflowHookEventToWebhookEvent(hookEvent)) + return webhooknotify.NewClient(hookSetting.HookAddress, hookSetting.HookSecret).SendWorkflowWebhook(webhookNotify) } func isWorkflowHookEventEnabled(hookSetting *models.WorkflowHookSettings, hookEvent models.WorkflowHookEvent) bool { @@ -982,17 +983,6 @@ func isWorkflowHookEventEnabled(hookSetting *models.WorkflowHookSettings, hookEv return false } -func workflowHookEventToWebhookEvent(hookEvent models.WorkflowHookEvent) webhooknotify.WebHookNotifyEvent { - switch hookEvent { - case models.WorkflowHookEventStartExecute: - return webhooknotify.WebHookNotifyEventWorkflowStartExecute - case models.WorkflowHookEventCompleteExecute: - return webhooknotify.WebHookNotifyEventWorkflowCompleteExecute - default: - return webhooknotify.WebHookNotifyEventWorkflow - } -} - type workflowNotificationOptions struct { StatusTextKeyOverride string PendingStageName string @@ -1527,7 +1517,7 @@ func (w *Service) sendNotification(title, content string, notify *models.NotifyC } case setting.NotifyWebHookTypeWebook: webhookclient := webhooknotify.NewClient(notify.WebhookNotificationConfig.Address, notify.WebhookNotificationConfig.Token) - err := webhookclient.SendWorkflowWebhook(webhookNotify, webhooknotify.WebHookNotifyEventWorkflow) + err := webhookclient.SendWorkflowWebhook(webhookNotify) if err != nil { return fmt.Errorf("failed to send notification to webhook, address %s, token: %s, error: %v", notify.WebhookNotificationConfig.Address, notify.WebhookNotificationConfig.Token, err) } diff --git a/pkg/microservice/aslan/core/common/service/webhooknotify/client.go b/pkg/microservice/aslan/core/common/service/webhooknotify/client.go index cd1d041b81..da6d0202ca 100644 --- a/pkg/microservice/aslan/core/common/service/webhooknotify/client.go +++ b/pkg/microservice/aslan/core/common/service/webhooknotify/client.go @@ -36,10 +36,10 @@ func NewClient(address, token string) *webhookNotifyclient { } } -func (c *webhookNotifyclient) SendWorkflowWebhook(webhookNotify *WorkflowNotify, event WebHookNotifyEvent) error { +func (c *webhookNotifyclient) SendWorkflowWebhook(webhookNotify *WorkflowNotify) error { notify := &WebHookNotify{ ObjectKind: WebHookNotifyObjectKindWorkflow, - Event: event, + Event: WebHookNotifyEventWorkflow, Workflow: webhookNotify, } return c.sendWebhook(notify) diff --git a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go index c6d146ca0e..587fe76459 100644 --- a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go +++ b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go @@ -38,10 +38,8 @@ const ( type WebHookNotifyEvent string const ( - WebHookNotifyEventWorkflow WebHookNotifyEvent = "workflow" - WebHookNotifyEventWorkflowStartExecute WebHookNotifyEvent = "start_execute" - WebHookNotifyEventWorkflowCompleteExecute WebHookNotifyEvent = "complete_execute" - WebHookNotifyEventReleasePlan WebHookNotifyEvent = "release_plan" + WebHookNotifyEventWorkflow WebHookNotifyEvent = "workflow" + WebHookNotifyEventReleasePlan WebHookNotifyEvent = "release_plan" ) type WebHookNotifyObjectKind string @@ -59,24 +57,25 @@ type WebHookNotify struct { } type WorkflowNotify struct { - TaskID int64 `json:"task_id"` - ProjectName string `json:"project_name"` - ProjectDisplayName string `json:"project_display_name"` - WorkflowName string `json:"workflow_name"` - WorkflowDisplayName string `json:"workflow_display_name"` - ReleasePlan *WorkflowNotifyReleasePlan `json:"release_plan,omitempty"` - Status config.Status `json:"status"` - Remark string `json:"remark"` - DetailURL string `json:"detail_url"` - Error string `json:"error"` - CreateTime int64 `json:"create_time"` - StartTime int64 `json:"start_time"` - EndTime int64 `json:"end_time"` - Stages []*WorkflowNotifyStage `json:"stages"` - TaskCreator string `json:"task_creator"` - TaskCreatorID string `json:"task_creator_id"` - TaskCreatorEmail string `json:"task_creator_email"` - TaskType config.CustomWorkflowTaskType `json:"task_type"` + TaskID int64 `json:"task_id"` + ProjectName string `json:"project_name"` + ProjectDisplayName string `json:"project_display_name"` + WorkflowName string `json:"workflow_name"` + WorkflowDisplayName string `json:"workflow_display_name"` + ReleasePlan *WorkflowNotifyReleasePlan `json:"release_plan,omitempty"` + EventName commonmodels.WorkflowHookEvent `json:"event_name,omitempty"` + Status config.Status `json:"status"` + Remark string `json:"remark"` + DetailURL string `json:"detail_url"` + Error string `json:"error"` + CreateTime int64 `json:"create_time"` + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` + Stages []*WorkflowNotifyStage `json:"stages"` + TaskCreator string `json:"task_creator"` + TaskCreatorID string `json:"task_creator_id"` + TaskCreatorEmail string `json:"task_creator_email"` + TaskType config.CustomWorkflowTaskType `json:"task_type"` } type WorkflowNotifyReleasePlan struct { From 778c17f76ad0d2acd424205ecb5c87095ddcfad6 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 18:23:04 +0800 Subject: [PATCH 09/13] fix: promote webhook event to top-level field Signed-off-by: huanghongbo-hhb --- .../service/instantmessage/workflow_task.go | 16 +++++-- .../common/service/webhooknotify/client.go | 8 ++-- .../common/service/webhooknotify/types.go | 46 ++++++++++--------- .../core/release_plan/service/release_plan.go | 15 +++++- 4 files changed, 56 insertions(+), 29 deletions(-) diff --git a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go index 75e12669f4..b1d5b7b1c9 100644 --- a/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go +++ b/pkg/microservice/aslan/core/common/service/instantmessage/workflow_task.go @@ -964,9 +964,8 @@ func (w *Service) SendSystemWorkflowHook(task *models.WorkflowTask, hookSetting if err != nil { return err } - webhookNotify.EventName = hookEvent - return webhooknotify.NewClient(hookSetting.HookAddress, hookSetting.HookSecret).SendWorkflowWebhook(webhookNotify) + return webhooknotify.NewClient(hookSetting.HookAddress, hookSetting.HookSecret).SendWorkflowWebhook(webhookNotify, workflowHookEventToWebhookEvent(hookEvent)) } func isWorkflowHookEventEnabled(hookSetting *models.WorkflowHookSettings, hookEvent models.WorkflowHookEvent) bool { @@ -983,6 +982,17 @@ func isWorkflowHookEventEnabled(hookSetting *models.WorkflowHookSettings, hookEv return false } +func workflowHookEventToWebhookEvent(hookEvent models.WorkflowHookEvent) webhooknotify.WebHookNotifyEvent { + switch hookEvent { + case models.WorkflowHookEventStartExecute: + return webhooknotify.WebHookNotifyEventWorkflowStartExecute + case models.WorkflowHookEventCompleteExecute: + return webhooknotify.WebHookNotifyEventWorkflowCompleteExecute + default: + return webhooknotify.WebHookNotifyEventWorkflow + } +} + type workflowNotificationOptions struct { StatusTextKeyOverride string PendingStageName string @@ -1517,7 +1527,7 @@ func (w *Service) sendNotification(title, content string, notify *models.NotifyC } case setting.NotifyWebHookTypeWebook: webhookclient := webhooknotify.NewClient(notify.WebhookNotificationConfig.Address, notify.WebhookNotificationConfig.Token) - err := webhookclient.SendWorkflowWebhook(webhookNotify) + err := webhookclient.SendWorkflowWebhook(webhookNotify, webhooknotify.WebHookNotifyEventWorkflow) if err != nil { return fmt.Errorf("failed to send notification to webhook, address %s, token: %s, error: %v", notify.WebhookNotificationConfig.Address, notify.WebhookNotificationConfig.Token, err) } diff --git a/pkg/microservice/aslan/core/common/service/webhooknotify/client.go b/pkg/microservice/aslan/core/common/service/webhooknotify/client.go index da6d0202ca..6fc2a7149b 100644 --- a/pkg/microservice/aslan/core/common/service/webhooknotify/client.go +++ b/pkg/microservice/aslan/core/common/service/webhooknotify/client.go @@ -36,19 +36,19 @@ func NewClient(address, token string) *webhookNotifyclient { } } -func (c *webhookNotifyclient) SendWorkflowWebhook(webhookNotify *WorkflowNotify) error { +func (c *webhookNotifyclient) SendWorkflowWebhook(webhookNotify *WorkflowNotify, event WebHookNotifyEvent) error { notify := &WebHookNotify{ ObjectKind: WebHookNotifyObjectKindWorkflow, - Event: WebHookNotifyEventWorkflow, + Event: event, Workflow: webhookNotify, } return c.sendWebhook(notify) } -func (c *webhookNotifyclient) SendReleasePlanWebhook(webhookNotify *ReleasePlanHookBody) error { +func (c *webhookNotifyclient) SendReleasePlanWebhook(webhookNotify *ReleasePlanHookBody, event WebHookNotifyEvent) error { notify := &WebHookNotify{ ObjectKind: WebHookNotifyObjectKindReleasePlan, - Event: WebHookNotifyEventReleasePlan, + Event: event, ReleasePlan: webhookNotify, } return c.sendWebhook(notify) diff --git a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go index 587fe76459..00509f9ec8 100644 --- a/pkg/microservice/aslan/core/common/service/webhooknotify/types.go +++ b/pkg/microservice/aslan/core/common/service/webhooknotify/types.go @@ -38,8 +38,13 @@ const ( type WebHookNotifyEvent string const ( - WebHookNotifyEventWorkflow WebHookNotifyEvent = "workflow" - WebHookNotifyEventReleasePlan WebHookNotifyEvent = "release_plan" + WebHookNotifyEventWorkflow WebHookNotifyEvent = "workflow" + WebHookNotifyEventWorkflowStartExecute WebHookNotifyEvent = "start_execute" + WebHookNotifyEventWorkflowCompleteExecute WebHookNotifyEvent = "complete_execute" + WebHookNotifyEventReleasePlan WebHookNotifyEvent = "release_plan" + WebHookNotifyEventFinishPlanning WebHookNotifyEvent = "finish_planning" + WebHookNotifyEventReleasePlanStartExecute WebHookNotifyEvent = "start_execute" + WebHookNotifyEventAllJobDone WebHookNotifyEvent = "all_job_done" ) type WebHookNotifyObjectKind string @@ -57,25 +62,24 @@ type WebHookNotify struct { } type WorkflowNotify struct { - TaskID int64 `json:"task_id"` - ProjectName string `json:"project_name"` - ProjectDisplayName string `json:"project_display_name"` - WorkflowName string `json:"workflow_name"` - WorkflowDisplayName string `json:"workflow_display_name"` - ReleasePlan *WorkflowNotifyReleasePlan `json:"release_plan,omitempty"` - EventName commonmodels.WorkflowHookEvent `json:"event_name,omitempty"` - Status config.Status `json:"status"` - Remark string `json:"remark"` - DetailURL string `json:"detail_url"` - Error string `json:"error"` - CreateTime int64 `json:"create_time"` - StartTime int64 `json:"start_time"` - EndTime int64 `json:"end_time"` - Stages []*WorkflowNotifyStage `json:"stages"` - TaskCreator string `json:"task_creator"` - TaskCreatorID string `json:"task_creator_id"` - TaskCreatorEmail string `json:"task_creator_email"` - TaskType config.CustomWorkflowTaskType `json:"task_type"` + TaskID int64 `json:"task_id"` + ProjectName string `json:"project_name"` + ProjectDisplayName string `json:"project_display_name"` + WorkflowName string `json:"workflow_name"` + WorkflowDisplayName string `json:"workflow_display_name"` + ReleasePlan *WorkflowNotifyReleasePlan `json:"release_plan,omitempty"` + Status config.Status `json:"status"` + Remark string `json:"remark"` + DetailURL string `json:"detail_url"` + Error string `json:"error"` + CreateTime int64 `json:"create_time"` + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` + Stages []*WorkflowNotifyStage `json:"stages"` + TaskCreator string `json:"task_creator"` + TaskCreatorID string `json:"task_creator_id"` + TaskCreatorEmail string `json:"task_creator_email"` + TaskType config.CustomWorkflowTaskType `json:"task_type"` } type WorkflowNotifyReleasePlan struct { diff --git a/pkg/microservice/aslan/core/release_plan/service/release_plan.go b/pkg/microservice/aslan/core/release_plan/service/release_plan.go index e1f519b2fe..7458ca6b48 100644 --- a/pkg/microservice/aslan/core/release_plan/service/release_plan.go +++ b/pkg/microservice/aslan/core/release_plan/service/release_plan.go @@ -1734,7 +1734,7 @@ func sendReleasePlanHook(plan *models.ReleasePlan, systemHookSetting *commonmode return err } - err = webhooknotify.NewClient(systemHookSetting.HookAddress, systemHookSetting.HookSecret).SendReleasePlanWebhook(hookBody) + err = webhooknotify.NewClient(systemHookSetting.HookAddress, systemHookSetting.HookSecret).SendReleasePlanWebhook(hookBody, releasePlanHookEventToWebhookEvent(hookBody.EventName)) if err != nil { err = errors.Wrap(err, "send release plan hook") log.Error(err) @@ -1745,6 +1745,19 @@ func sendReleasePlanHook(plan *models.ReleasePlan, systemHookSetting *commonmode return nil } +func releasePlanHookEventToWebhookEvent(hookEvent commonmodels.ReleasePlanHookEvent) webhooknotify.WebHookNotifyEvent { + switch hookEvent { + case commonmodels.ReleasePlanHookEventFinishPlanning: + return webhooknotify.WebHookNotifyEventFinishPlanning + case commonmodels.ReleasePlanHookEventStartExecute: + return webhooknotify.WebHookNotifyEventReleasePlanStartExecute + case commonmodels.ReleasePlanHookEventAllJobDone: + return webhooknotify.WebHookNotifyEventAllJobDone + default: + return webhooknotify.WebHookNotifyEventReleasePlan + } +} + func convertReleasePlanToHookBody(plan *models.ReleasePlan, hookEvent commonmodels.ReleasePlanHookEvent) (*webhooknotify.ReleasePlanHookBody, error) { hookBody := &webhooknotify.ReleasePlanHookBody{ ID: plan.ID, From c578fa5912af7e518e8be98016cd2bb52501c482 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 18:51:43 +0800 Subject: [PATCH 10/13] fix: avoid duplicate workflow complete hooks on cancel Signed-off-by: huanghongbo-hhb --- .../aslan/core/common/service/workflowcontroller/workflow.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go b/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go index ace56cd749..9ef0ec1b5d 100644 --- a/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go +++ b/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go @@ -556,6 +556,7 @@ func (c *workflowCtl) updateWorkflowTask() { } c.workflowTask.Remark = "" + shouldSendCompleteHook := c.workflowTask.Finished() && taskInColl.EndTime == 0 && c.workflowTask.EndTime > 0 c.workflowTaskMutex.Lock() if err := commonrepo.NewworkflowTaskv4Coll().Update(c.workflowTask.ID.Hex(), c.workflowTask); err != nil { @@ -570,7 +571,7 @@ func (c *workflowCtl) updateWorkflowTask() { if err := instantmessage.NewWeChatClient().SendWorkflowTaskNotifications(c.workflowTask); err != nil { c.logger.Errorf("send workflow task notification failed, error: %v", err) } - if c.workflowTask.Finished() { + if shouldSendCompleteHook { if err := SendSystemWorkflowHook(c.workflowTask, commonmodels.WorkflowHookEventCompleteExecute); err != nil { c.logger.Errorf("send system workflow complete hook failed, workflow: %s, taskID: %d, error: %v", c.workflowTask.WorkflowName, c.workflowTask.TaskID, err) } From c28bb902d0d9efc5913a6061b16284dfdb16b065 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 19:09:48 +0800 Subject: [PATCH 11/13] fix: resend workflow hooks on retry Signed-off-by: huanghongbo-hhb --- .../aslan/core/workflow/service/workflow/workflow_task_v4.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go index 25905a94e5..b54c68a751 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go @@ -1009,9 +1009,14 @@ func RetryWorkflowTaskV4(workflowName string, taskID int64, logger *zap.SugaredL task.Status = config.StatusCreated task.StartTime = time.Now().Unix() + task.EndTime = 0 + task.Error = "" if err := instantmessage.NewWeChatClient().SendWorkflowTaskNotifications(task); err != nil { log.Errorf("send workflow task notification failed, error: %v", err) } + if err := runtimeWorkflowController.SendSystemWorkflowHook(task, commonmodels.WorkflowHookEventStartExecute); err != nil { + log.Errorf("send system workflow start hook failed on retry, workflow: %s, taskID: %d, error: %v", task.WorkflowName, task.TaskID, err) + } if err := runtimeWorkflowController.UpdateTask(task); err != nil { log.Errorf("retry workflow task error: %v", err) From 075b6281addc67a071a73449a341778059e78c61 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 19:21:54 +0800 Subject: [PATCH 12/13] fix: resend workflow hooks after manual execute Signed-off-by: huanghongbo-hhb --- .../aslan/core/workflow/service/workflow/workflow_task_v4.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go index b54c68a751..be02898cd7 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go @@ -1286,9 +1286,14 @@ func ManualExecWorkflowTaskV4(workflowName string, taskID int64, stageName strin } task.Status = config.StatusCreated + task.EndTime = 0 + task.Error = "" if err := instantmessage.NewWeChatClient().SendWorkflowTaskNotifications(task); err != nil { log.Errorf("send workflow task notification failed, error: %v", err) } + if err := runtimeWorkflowController.SendSystemWorkflowHook(task, commonmodels.WorkflowHookEventStartExecute); err != nil { + log.Errorf("send system workflow start hook failed on manual execute, workflow: %s, taskID: %d, error: %v", task.WorkflowName, task.TaskID, err) + } if err := runtimeWorkflowController.UpdateTask(task); err != nil { log.Errorf("manual execute workflow task error: %v", err) From c602c67f78f485326b5db7a7cd6f0c636abd85b1 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 26 May 2026 19:37:08 +0800 Subject: [PATCH 13/13] fix: skip start hook for manual execute resume Signed-off-by: huanghongbo-hhb --- .../aslan/core/workflow/service/workflow/workflow_task_v4.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go index be02898cd7..3bff0a6920 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go @@ -1291,9 +1291,6 @@ func ManualExecWorkflowTaskV4(workflowName string, taskID int64, stageName strin if err := instantmessage.NewWeChatClient().SendWorkflowTaskNotifications(task); err != nil { log.Errorf("send workflow task notification failed, error: %v", err) } - if err := runtimeWorkflowController.SendSystemWorkflowHook(task, commonmodels.WorkflowHookEventStartExecute); err != nil { - log.Errorf("send system workflow start hook failed on manual execute, workflow: %s, taskID: %d, error: %v", task.WorkflowName, task.TaskID, err) - } if err := runtimeWorkflowController.UpdateTask(task); err != nil { log.Errorf("manual execute workflow task error: %v", err)