From fe2582dbd93acbd2b51cbaaf2c2b6ddb9b6e9a65 Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Tue, 21 Apr 2026 04:51:21 +0000 Subject: [PATCH 1/4] feat(data_export): add AuditWorkflowAndAdvanceStep to WorkflowRepo DEV-015: Add AuditWorkflowAndAdvanceStep method to the WorkflowRepo interface and its storage implementation. This method atomically updates the current step state and advances CurrentWorkflowStepId to the next step within a transaction, providing the foundation for multi-step approval in data export workflows. refs actiontech/dms-ee#784 (cherry picked from commit 35fb0dae38f6da94d3ef6af78eb11d2e6b9ed235) --- internal/dms/biz/data_export_workflow.go | 1 + internal/dms/storage/workflow.go | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/internal/dms/biz/data_export_workflow.go b/internal/dms/biz/data_export_workflow.go index 8554717d..7b27a7e8 100644 --- a/internal/dms/biz/data_export_workflow.go +++ b/internal/dms/biz/data_export_workflow.go @@ -117,6 +117,7 @@ type WorkflowRepo interface { GetDataExportWorkflowsByDBServices(ctx context.Context, dbUid []string) ([]string, error) DeleteDataExportWorkflowsByIds(ctx context.Context, dataExportWorkflowUid []string) error GetGlobalWorkflowsByParameterMap(ctx context.Context, data map[string]interface{}) ([]*Workflow, int64, error) + AuditWorkflowAndAdvanceStep(ctx context.Context, workflowRecordUid string, step *WorkflowStep, nextStepId uint64, operateId, reason string) error } type DataExportWorkflowUsecase struct { diff --git a/internal/dms/storage/workflow.go b/internal/dms/storage/workflow.go index aa96437e..cd983a46 100644 --- a/internal/dms/storage/workflow.go +++ b/internal/dms/storage/workflow.go @@ -285,6 +285,24 @@ func (d *WorkflowRepo) AuditWorkflow(ctx context.Context, dataExportWorkflowUid }) } +func (d *WorkflowRepo) AuditWorkflowAndAdvanceStep(ctx context.Context, workflowRecordUid string, step *biz.WorkflowStep, nextStepId uint64, operateId, reason string) error { + return transaction(d.log, ctx, d.db, func(tx *gorm.DB) error { + // update current step state + operateTime := time.Now() + fields := map[string]interface{}{"operation_user_uid": operateId, "operate_at": operateTime, "reason": reason, "state": step.State} + if err := tx.WithContext(ctx).Model(&model.WorkflowStep{}).Where("step_id = ? and workflow_record_uid = ?", step.StepId, step.WorkflowRecordUid).Updates(fields).Error; err != nil { + return fmt.Errorf("failed to update current workflow step, err: %v", err) + } + + // advance CurrentWorkflowStepId to next step + if err := tx.WithContext(ctx).Model(&model.WorkflowRecord{}).Where("uid = ?", workflowRecordUid).Update("current_workflow_step_id", nextStepId).Error; err != nil { + return fmt.Errorf("failed to advance workflow step, err: %v", err) + } + + return nil + }) +} + func (d *WorkflowRepo) UpdateWorkflowStatusById(ctx context.Context, dataExportWorkflowUid string, status biz.DataExportWorkflowStatus) error { return transaction(d.log, ctx, d.db, func(tx *gorm.DB) error { if err := tx.WithContext(ctx).Model(&model.WorkflowRecord{}).Where("uid = ?", dataExportWorkflowUid).Update("status", status).Error; err != nil { From 1d55f298765acf010bcaf64ae372004716ed9fff Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Wed, 22 Apr 2026 08:35:04 +0000 Subject: [PATCH 2/4] fix: increase ProxyTarget version column size to 512 The version column (size:64) was too small for dev branch names like "feat/784-data-export-workflow-template <40-char-hash>" (79+ chars), causing auto-migration to fail with "Data truncated" error and preventing DMS from starting. (cherry picked from commit f1fd24ed15b5d2af88bdc6dca9e98922c42d08db) --- internal/dms/storage/model/model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/dms/storage/model/model.go b/internal/dms/storage/model/model.go index 082d5a7a..472beff7 100644 --- a/internal/dms/storage/model/model.go +++ b/internal/dms/storage/model/model.go @@ -257,7 +257,7 @@ const ( type ProxyTarget struct { Name string `json:"name" gorm:"primaryKey;size:200;not null;column:name"` Url string `json:"url" gorm:"size:255;column:url"` - Version string `json:"version" gorm:"size:64;column:version"` + Version string `json:"version" gorm:"size:512;column:version"` ProxyUrlPrefixs string `json:"proxy_url_prefixs" gorm:"size:255;column:proxy_url_prefixs"` Scenario string `json:"scenario" gorm:"size:64;column:scenario;default:'internal_service'"` } From d528e2ba8bf1e973ec0f48c8eae1b95fc199897e Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Thu, 23 Apr 2026 07:19:13 +0000 Subject: [PATCH 3/4] fix(#784): add bounds check for CurrentWorkflowStepId in list endpoints When a data export workflow has no approval steps (CurrentWorkflowStepId=0, WorkflowSteps=[]), the list and global list endpoints would panic with "index out of range" when trying to access WorkflowSteps[CurrentWorkflowStepId-1]. Add bounds checks to prevent this panic, consistent with the webhook fix in the previous commit. (cherry picked from commit 0e8ba22932da83133602d7bdea8c126064d0f60e) --- internal/dms/service/data_export_workflow.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/dms/service/data_export_workflow.go b/internal/dms/service/data_export_workflow.go index 4a8712cd..0463dd9e 100644 --- a/internal/dms/service/data_export_workflow.go +++ b/internal/dms/service/data_export_workflow.go @@ -147,7 +147,8 @@ func (d *DMSService) ListDataExportWorkflow(ctx context.Context, req *dmsV1.List ret[i].Creater = creater[0] } // 结束时不显示当前步骤操作人,其他状态显示当前步骤操作人 - if w.Status != string(dmsV1.StatusFinish) { + if w.Status != string(dmsV1.StatusFinish) && w.WorkflowRecord.CurrentWorkflowStepId > 0 && + int(w.WorkflowRecord.CurrentWorkflowStepId-1) < len(w.WorkflowRecord.WorkflowSteps) { ret[i].CurrentStepAssigneeUsers = convertBizUidWithName(d.UserUsecase.GetBizUserIncludeDeletedWithNameByUids(ctx, w.WorkflowRecord.WorkflowSteps[w.WorkflowRecord.CurrentWorkflowStepId-1].Assignees)) } } @@ -192,7 +193,8 @@ func (d *DMSService) GetGlobalWorkflowsList(ctx context.Context, req *dmsV1.Filt ret[i].Creater = creater[0] } // 结束时不显示当前步骤操作人,其他状态显示当前步骤操作人 - if w.Status != string(dmsV1.StatusFinish) { + if w.Status != string(dmsV1.StatusFinish) && w.WorkflowRecord.CurrentWorkflowStepId > 0 && + int(w.WorkflowRecord.CurrentWorkflowStepId-1) < len(w.WorkflowRecord.WorkflowSteps) { ret[i].CurrentStepAssigneeUsers = convertBizUidWithName(d.UserUsecase.GetBizUserIncludeDeletedWithNameByUids(ctx, w.WorkflowRecord.WorkflowSteps[w.WorkflowRecord.CurrentWorkflowStepId-1].Assignees)) } } From e08338a877325b9cc524d571a7d9e1fa7bd8114c Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Thu, 23 Apr 2026 08:56:51 +0000 Subject: [PATCH 4/4] fix(#784): show workflow creator as pending operator in wait_for_export status When data export workflow enters wait_for_export or exporting status, display the workflow creator as the current step assignee user in the list endpoint, since the export executor is always the workflow creator. (cherry picked from commit 06b2a1f3db3b44698ba3407d230df98c36453343) --- internal/dms/service/data_export_workflow.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/dms/service/data_export_workflow.go b/internal/dms/service/data_export_workflow.go index 0463dd9e..e02b6a1e 100644 --- a/internal/dms/service/data_export_workflow.go +++ b/internal/dms/service/data_export_workflow.go @@ -147,7 +147,10 @@ func (d *DMSService) ListDataExportWorkflow(ctx context.Context, req *dmsV1.List ret[i].Creater = creater[0] } // 结束时不显示当前步骤操作人,其他状态显示当前步骤操作人 - if w.Status != string(dmsV1.StatusFinish) && w.WorkflowRecord.CurrentWorkflowStepId > 0 && + if w.Status == string(dmsV1.DataExportWorkflowStatusWaitForExport) || w.Status == string(dmsV1.DataExportWorkflowStatusWaitForExporting) { + // wait_for_export/wait_for_exporting 状态下,待操作人为工单创建者 + ret[i].CurrentStepAssigneeUsers = convertBizUidWithName(d.UserUsecase.GetBizUserIncludeDeletedWithNameByUids(ctx, []string{w.CreateUserUID})) + } else if w.Status != string(dmsV1.StatusFinish) && w.WorkflowRecord.CurrentWorkflowStepId > 0 && int(w.WorkflowRecord.CurrentWorkflowStepId-1) < len(w.WorkflowRecord.WorkflowSteps) { ret[i].CurrentStepAssigneeUsers = convertBizUidWithName(d.UserUsecase.GetBizUserIncludeDeletedWithNameByUids(ctx, w.WorkflowRecord.WorkflowSteps[w.WorkflowRecord.CurrentWorkflowStepId-1].Assignees)) } @@ -193,7 +196,10 @@ func (d *DMSService) GetGlobalWorkflowsList(ctx context.Context, req *dmsV1.Filt ret[i].Creater = creater[0] } // 结束时不显示当前步骤操作人,其他状态显示当前步骤操作人 - if w.Status != string(dmsV1.StatusFinish) && w.WorkflowRecord.CurrentWorkflowStepId > 0 && + if w.Status == string(dmsV1.DataExportWorkflowStatusWaitForExport) || w.Status == string(dmsV1.DataExportWorkflowStatusWaitForExporting) { + // wait_for_export/wait_for_exporting 状态下,待操作人为工单创建者 + ret[i].CurrentStepAssigneeUsers = convertBizUidWithName(d.UserUsecase.GetBizUserIncludeDeletedWithNameByUids(ctx, []string{w.CreateUserUID})) + } else if w.Status != string(dmsV1.StatusFinish) && w.WorkflowRecord.CurrentWorkflowStepId > 0 && int(w.WorkflowRecord.CurrentWorkflowStepId-1) < len(w.WorkflowRecord.WorkflowSteps) { ret[i].CurrentStepAssigneeUsers = convertBizUidWithName(d.UserUsecase.GetBizUserIncludeDeletedWithNameByUids(ctx, w.WorkflowRecord.WorkflowSteps[w.WorkflowRecord.CurrentWorkflowStepId-1].Assignees)) }