1827 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			1827 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
|  * Copyright 2025 coze-dev 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 repo
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"strconv"
 | |
| 	"time"
 | |
| 
 | |
| 	einoCompose "github.com/cloudwego/eino/compose"
 | |
| 	"github.com/cloudwego/eino/schema"
 | |
| 	"golang.org/x/exp/maps"
 | |
| 	"gorm.io/gen"
 | |
| 	"gorm.io/gen/field"
 | |
| 	"gorm.io/gorm"
 | |
| 
 | |
| 	workflowModel "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/workflow"
 | |
| 	workflow3 "github.com/coze-dev/coze-studio/backend/api/model/workflow"
 | |
| 	"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/workflow"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/workflow/entity"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/canvas/adaptor"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/compose"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/execute"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/repo/dal/model"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/repo/dal/query"
 | |
| 	"github.com/coze-dev/coze-studio/backend/infra/contract/cache"
 | |
| 	cm "github.com/coze-dev/coze-studio/backend/infra/contract/chatmodel"
 | |
| 	"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
 | |
| 	"github.com/coze-dev/coze-studio/backend/infra/contract/storage"
 | |
| 	"github.com/coze-dev/coze-studio/backend/pkg/errorx"
 | |
| 	"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
 | |
| 	"github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
 | |
| 	"github.com/coze-dev/coze-studio/backend/pkg/lang/ternary"
 | |
| 	"github.com/coze-dev/coze-studio/backend/pkg/logs"
 | |
| 	"github.com/coze-dev/coze-studio/backend/pkg/safego"
 | |
| 	"github.com/coze-dev/coze-studio/backend/pkg/sonic"
 | |
| 	"github.com/coze-dev/coze-studio/backend/types/errno"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	batchCreateSize = 10
 | |
| )
 | |
| 
 | |
| type RepositoryImpl struct {
 | |
| 	idgen.IDGenerator
 | |
| 	query *query.Query
 | |
| 	redis cache.Cmdable
 | |
| 	tos   storage.Storage
 | |
| 	einoCompose.CheckPointStore
 | |
| 	workflow.InterruptEventStore
 | |
| 	workflow.CancelSignalStore
 | |
| 	workflow.ExecuteHistoryStore
 | |
| 	builtinModel cm.BaseChatModel
 | |
| 	workflow.WorkflowConfig
 | |
| 	workflow.Suggester
 | |
| }
 | |
| 
 | |
| func NewRepository(idgen idgen.IDGenerator, db *gorm.DB, redis cache.Cmdable, tos storage.Storage,
 | |
| 	cpStore einoCompose.CheckPointStore, chatModel cm.BaseChatModel, workflowConfig workflow.WorkflowConfig) (workflow.Repository, error) {
 | |
| 	sg, err := NewSuggester(chatModel)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return &RepositoryImpl{
 | |
| 		IDGenerator:     idgen,
 | |
| 		query:           query.Use(db),
 | |
| 		redis:           redis,
 | |
| 		tos:             tos,
 | |
| 		CheckPointStore: cpStore,
 | |
| 		InterruptEventStore: &interruptEventStoreImpl{
 | |
| 			redis: redis,
 | |
| 		},
 | |
| 		CancelSignalStore: &cancelSignalStoreImpl{
 | |
| 			redis: redis,
 | |
| 		},
 | |
| 		ExecuteHistoryStore: &executeHistoryStoreImpl{
 | |
| 			query: query.Use(db),
 | |
| 			redis: redis,
 | |
| 		},
 | |
| 
 | |
| 		builtinModel:   chatModel,
 | |
| 		Suggester:      sg,
 | |
| 		WorkflowConfig: workflowConfig,
 | |
| 	}, nil
 | |
| 
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) CreateMeta(ctx context.Context, meta *vo.Meta) (int64, error) {
 | |
| 	id, err := r.GenID(ctx)
 | |
| 	if err != nil {
 | |
| 		return 0, vo.WrapError(errno.ErrIDGenError, err)
 | |
| 	}
 | |
| 	wfMeta := &model.WorkflowMeta{
 | |
| 		ID:          id,
 | |
| 		Name:        meta.Name,
 | |
| 		Description: meta.Desc,
 | |
| 		IconURI:     meta.IconURI,
 | |
| 		ContentType: int32(meta.ContentType),
 | |
| 		Mode:        int32(meta.Mode),
 | |
| 		CreatorID:   meta.CreatorID,
 | |
| 		AuthorID:    meta.AuthorID,
 | |
| 		SpaceID:     meta.SpaceID,
 | |
| 		DeletedAt:   gorm.DeletedAt{Valid: false},
 | |
| 	}
 | |
| 
 | |
| 	if meta.Tag != nil {
 | |
| 		wfMeta.Tag = int32(*meta.Tag)
 | |
| 	}
 | |
| 
 | |
| 	if meta.SourceID != nil {
 | |
| 		wfMeta.SourceID = *meta.SourceID
 | |
| 	}
 | |
| 
 | |
| 	if meta.AppID != nil {
 | |
| 		wfMeta.AppID = *meta.AppID
 | |
| 	}
 | |
| 
 | |
| 	if err = r.query.WorkflowMeta.Create(wfMeta); err != nil {
 | |
| 		return 0, vo.WrapError(errno.ErrDatabaseError, fmt.Errorf("create workflow meta: %w", err))
 | |
| 	}
 | |
| 
 | |
| 	return id, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) updateReferences(ctx context.Context, id int64, wfRefs map[entity.WorkflowReferenceKey]struct{}) (
 | |
| 	err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	currentRefs, err := r.query.WorkflowReference.WithContext(ctx).Where(
 | |
| 		r.query.WorkflowReference.ReferringID.Eq(id)).Find()
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("failed to find workflow reference: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	if len(currentRefs) == 0 {
 | |
| 		if len(wfRefs) == 0 {
 | |
| 			return nil
 | |
| 		}
 | |
| 
 | |
| 		refsToCreateModel := make([]*model.WorkflowReference, 0, len(wfRefs))
 | |
| 		refIDs, err := r.GenMultiIDs(ctx, len(wfRefs))
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("failed to gen id for workflow reference: %w", err)
 | |
| 		}
 | |
| 		var index int
 | |
| 		for key := range wfRefs {
 | |
| 			refsToCreateModel = append(refsToCreateModel, &model.WorkflowReference{
 | |
| 				ID:               refIDs[index],
 | |
| 				ReferredID:       key.ReferredID,
 | |
| 				ReferringID:      key.ReferringID,
 | |
| 				ReferType:        int32(key.ReferType),
 | |
| 				ReferringBizType: int32(key.ReferringBizType),
 | |
| 				Status:           1,
 | |
| 			})
 | |
| 			index++
 | |
| 		}
 | |
| 
 | |
| 		return r.query.WorkflowReference.WithContext(ctx).Create(refsToCreateModel...)
 | |
| 	}
 | |
| 
 | |
| 	if len(wfRefs) == 0 {
 | |
| 		_, err = r.query.WorkflowReference.WithContext(ctx).
 | |
| 			Where(r.query.WorkflowReference.ID.In(slices.Transform(currentRefs,
 | |
| 				func(reference *model.WorkflowReference) int64 {
 | |
| 					return reference.ID
 | |
| 				})...)).
 | |
| 			UpdateColumnSimple(r.query.WorkflowReference.Status.Value(0))
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		refsToDisable  []int64
 | |
| 		refsToEnable   []int64
 | |
| 		refsToCreate   = maps.Clone(wfRefs)
 | |
| 		existingRefMap = slices.ToMap(currentRefs, func(reference *model.WorkflowReference) (
 | |
| 			entity.WorkflowReferenceKey, *model.WorkflowReference) {
 | |
| 			return entity.WorkflowReferenceKey{
 | |
| 				ReferredID:       reference.ReferredID,
 | |
| 				ReferringID:      reference.ReferringID,
 | |
| 				ReferType:        vo.ReferType(reference.ReferType),
 | |
| 				ReferringBizType: vo.ReferringBizType(reference.ReferringBizType),
 | |
| 			}, reference
 | |
| 		})
 | |
| 	)
 | |
| 	for key, ref := range existingRefMap {
 | |
| 		if ref.Status == 1 {
 | |
| 			if _, ok := wfRefs[key]; !ok {
 | |
| 				refsToDisable = append(refsToDisable, ref.ID)
 | |
| 			}
 | |
| 		} else {
 | |
| 			if _, ok := wfRefs[key]; ok {
 | |
| 				refsToEnable = append(refsToEnable, ref.ID)
 | |
| 				delete(refsToCreate, key)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for key := range refsToCreate {
 | |
| 		if _, ok := existingRefMap[key]; ok {
 | |
| 			delete(refsToCreate, key)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if len(refsToCreate) > 0 {
 | |
| 		refsToCreateModel := make([]*model.WorkflowReference, 0, len(refsToCreate))
 | |
| 		refIDs, err := r.GenMultiIDs(ctx, len(refsToCreate))
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("failed to gen id for workflow reference: %w", err)
 | |
| 		}
 | |
| 		var index int
 | |
| 		for key := range refsToCreate {
 | |
| 			refsToCreateModel = append(refsToCreateModel, &model.WorkflowReference{
 | |
| 				ID:               refIDs[index],
 | |
| 				ReferredID:       key.ReferredID,
 | |
| 				ReferringID:      key.ReferringID,
 | |
| 				ReferType:        int32(key.ReferType),
 | |
| 				ReferringBizType: int32(key.ReferringBizType),
 | |
| 				Status:           1,
 | |
| 			})
 | |
| 			index++
 | |
| 		}
 | |
| 
 | |
| 		if err = r.query.WorkflowReference.WithContext(ctx).Create(refsToCreateModel...); err != nil {
 | |
| 			return fmt.Errorf("failed to create workflow reference for workflowID %d, childIDs %v: %v",
 | |
| 				id, refsToCreate, err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if len(refsToDisable) > 0 {
 | |
| 		_, err = r.query.WorkflowReference.WithContext(ctx).
 | |
| 			Where(r.query.WorkflowReference.ID.In(refsToDisable...)).
 | |
| 			UpdateColumnSimple(r.query.WorkflowReference.Status.Value(0))
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("failed to disable workflow reference for workflowID %d, childIDs %v: %v",
 | |
| 				id, refsToDisable, err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if len(refsToEnable) > 0 {
 | |
| 		_, err = r.query.WorkflowReference.WithContext(ctx).
 | |
| 			Where(r.query.WorkflowReference.ID.In(refsToEnable...)).
 | |
| 			UpdateColumnSimple(r.query.WorkflowReference.Status.Value(1))
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("failed to enable workflow reference for workflowID %d, childIDs %v: %v",
 | |
| 				id, refsToEnable, err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) CreateVersion(ctx context.Context, id int64, info *vo.VersionInfo, newRefs map[entity.WorkflowReferenceKey]struct{}) (err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	if err = r.updateReferences(ctx, id, newRefs); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err = r.query.WorkflowVersion.WithContext(ctx).Create(&model.WorkflowVersion{
 | |
| 		// ID: auto_increment
 | |
| 		WorkflowID:         id,
 | |
| 		Version:            info.Version,
 | |
| 		VersionDescription: info.VersionDescription,
 | |
| 		Canvas:             info.Canvas,
 | |
| 		InputParams:        info.InputParamsStr,
 | |
| 		OutputParams:       info.OutputParamsStr,
 | |
| 		CreatorID:          info.VersionCreatorID,
 | |
| 		CommitID:           info.CommitID,
 | |
| 	}); err != nil {
 | |
| 		return fmt.Errorf("publish failed: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	var result gen.ResultInfo
 | |
| 	result, err = r.query.WorkflowDraft.WithContext(ctx).
 | |
| 		Where(r.query.WorkflowDraft.ID.Eq(id),
 | |
| 			r.query.WorkflowDraft.CommitID.Eq(info.CommitID)).
 | |
| 		UpdateColumnSimple(
 | |
| 			r.query.WorkflowDraft.Modified.Value(false),
 | |
| 			r.query.WorkflowDraft.TestRunSuccess.Value(true),
 | |
| 		)
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("update workflow draft when publish failed: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	if result.RowsAffected == 0 {
 | |
| 		logs.CtxWarnf(ctx, "update workflow draft when publish failed: no rows affected. WorkflowID: %d", id)
 | |
| 	}
 | |
| 
 | |
| 	_, err = r.query.WorkflowMeta.WithContext(ctx).
 | |
| 		Where(r.query.WorkflowMeta.ID.Eq(id)).
 | |
| 		UpdateColumnSimple(
 | |
| 			r.query.WorkflowMeta.Status.Value(1),
 | |
| 			r.query.WorkflowMeta.LatestVersion.Value(info.Version),
 | |
| 			r.query.WorkflowMeta.LatestVersionTs.Value(time.Now().UnixMilli()),
 | |
| 		)
 | |
| 	if err != nil {
 | |
| 		logs.CtxWarnf(ctx, "update workflow meta when publish failed: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) CreateOrUpdateDraft(ctx context.Context, id int64, draft *vo.DraftInfo) error {
 | |
| 	d := &model.WorkflowDraft{
 | |
| 		ID:           id,
 | |
| 		Canvas:       draft.Canvas,
 | |
| 		InputParams:  draft.InputParamsStr,
 | |
| 		OutputParams: draft.OutputParamsStr,
 | |
| 		CommitID:     draft.CommitID,
 | |
| 	}
 | |
| 
 | |
| 	if draft.DraftMeta != nil {
 | |
| 		d.Modified = draft.DraftMeta.Modified
 | |
| 		d.TestRunSuccess = draft.DraftMeta.TestRunSuccess
 | |
| 	}
 | |
| 
 | |
| 	if err := r.query.WorkflowDraft.WithContext(ctx).Save(d); err != nil {
 | |
| 		return vo.WrapError(errno.ErrDatabaseError, fmt.Errorf("save workflow draft: %w", err))
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) UpdateWorkflowDraftTestRunSuccess(ctx context.Context, id int64) error {
 | |
| 	if _, err := r.query.WorkflowDraft.WithContext(ctx).Where(r.query.WorkflowDraft.ID.Eq(id)).UpdateColumnSimple(r.query.WorkflowDraft.TestRunSuccess.Value(true)); err != nil {
 | |
| 		return vo.WrapError(errno.ErrDatabaseError, fmt.Errorf("update workflow draft test run success failed: %w", err))
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) Delete(ctx context.Context, id int64) (err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	return r.query.Transaction(func(tx *query.Query) error {
 | |
| 		// Delete from workflow_meta
 | |
| 		_, err := tx.WorkflowMeta.WithContext(ctx).Where(tx.WorkflowMeta.ID.Eq(id)).Delete()
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("delete workflow meta: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		_, err = tx.WorkflowDraft.WithContext(ctx).Where(tx.WorkflowDraft.ID.Eq(id)).Delete()
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("delete workflow draft: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		_, err = tx.WorkflowVersion.WithContext(ctx).Where(tx.WorkflowVersion.WorkflowID.Eq(id)).Delete()
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("delete workflow versions: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		_, err = tx.WorkflowReference.WithContext(ctx).Where(tx.WorkflowReference.ReferredID.Eq(id)).Delete()
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("delete workflow references: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		_, err = tx.WorkflowReference.WithContext(ctx).Where(tx.WorkflowReference.ReferringID.Eq(id)).Delete()
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("delete incoming workflow references: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		return nil
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) MDelete(ctx context.Context, ids []int64) error {
 | |
| 	_, err := r.query.WorkflowMeta.WithContext(ctx).Where(r.query.WorkflowMeta.ID.In(ids...)).Delete()
 | |
| 	if err != nil {
 | |
| 		return vo.WrapError(errno.ErrDatabaseError, fmt.Errorf("delete workflow meta failed err=%w", err))
 | |
| 	}
 | |
| 
 | |
| 	safego.Go(ctx, func() {
 | |
| 		_, err = r.query.WorkflowDraft.WithContext(ctx).Where(r.query.WorkflowDraft.ID.In(ids...)).Delete()
 | |
| 		if err != nil {
 | |
| 			logs.Warnf("delete workflow draft failed err=%v, ids %v", err, ids)
 | |
| 		}
 | |
| 
 | |
| 		_, err = r.query.WorkflowVersion.WithContext(ctx).Where(r.query.WorkflowVersion.WorkflowID.In(ids...)).Delete()
 | |
| 		if err != nil {
 | |
| 			logs.Warnf("delete workflow version failed err=%v, ids %v", err, ids)
 | |
| 		}
 | |
| 
 | |
| 		_, err = r.query.WorkflowReference.WithContext(ctx).Where(r.query.WorkflowReference.ID.In(ids...)).Delete()
 | |
| 		if err != nil {
 | |
| 			logs.Warnf("delete workflow reference failed err=%v, ids %v", err, ids)
 | |
| 
 | |
| 		}
 | |
| 		_, err = r.query.WorkflowReference.WithContext(ctx).Where(r.query.WorkflowReference.ReferringID.In(ids...)).Delete()
 | |
| 		if err != nil {
 | |
| 			logs.Warnf("delete workflow reference refer failed err=%v, ids %v", err, ids)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) GetMeta(ctx context.Context, id int64) (_ *vo.Meta, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	meta, err := r.query.WorkflowMeta.WithContext(ctx).Debug().Where(r.query.WorkflowMeta.ID.Eq(id)).First()
 | |
| 	if err != nil {
 | |
| 		if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			return nil, vo.WrapError(errno.ErrWorkflowNotFound, fmt.Errorf("workflow meta not found for ID %d: %w", id, err),
 | |
| 				errorx.KV("id", strconv.FormatInt(id, 10)))
 | |
| 		}
 | |
| 		return nil, fmt.Errorf("failed to get workflow meta for ID %d: %w", id, err)
 | |
| 	}
 | |
| 
 | |
| 	return r.convertMeta(ctx, meta)
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) convertMeta(ctx context.Context, meta *model.WorkflowMeta) (*vo.Meta, error) {
 | |
| 	url, err := r.tos.GetObjectUrl(ctx, meta.IconURI)
 | |
| 	if err != nil {
 | |
| 		logs.Warnf("failed to get url for workflow meta %v", err)
 | |
| 	}
 | |
| 	// Initialize the result entity
 | |
| 	wfMeta := &vo.Meta{
 | |
| 		Name:        meta.Name,
 | |
| 		Desc:        meta.Description,
 | |
| 		IconURI:     meta.IconURI,
 | |
| 		IconURL:     url,
 | |
| 		ContentType: entity.ContentType(meta.ContentType),
 | |
| 		Mode:        entity.Mode(meta.Mode),
 | |
| 		CreatorID:   meta.CreatorID,
 | |
| 		AuthorID:    meta.AuthorID,
 | |
| 		SpaceID:     meta.SpaceID,
 | |
| 		CreatedAt:   time.UnixMilli(meta.CreatedAt),
 | |
| 	}
 | |
| 	if meta.Tag != 0 {
 | |
| 		tag := entity.Tag(meta.Tag)
 | |
| 		wfMeta.Tag = &tag
 | |
| 	}
 | |
| 	if meta.SourceID != 0 {
 | |
| 		wfMeta.SourceID = &meta.SourceID
 | |
| 	}
 | |
| 	if meta.AppID != 0 {
 | |
| 		wfMeta.AppID = &meta.AppID
 | |
| 	}
 | |
| 	if meta.UpdatedAt > 0 {
 | |
| 		wfMeta.UpdatedAt = ptr.Of(time.UnixMilli(meta.UpdatedAt))
 | |
| 	}
 | |
| 	if meta.Status > 0 {
 | |
| 		wfMeta.HasPublished = true
 | |
| 	}
 | |
| 	if meta.LatestVersion != "" {
 | |
| 		wfMeta.LatestPublishedVersion = ptr.Of(meta.LatestVersion)
 | |
| 	}
 | |
| 
 | |
| 	return wfMeta, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) UpdateMeta(ctx context.Context, id int64, metaUpdate *vo.MetaUpdate) error {
 | |
| 	var expressions []field.AssignExpr
 | |
| 
 | |
| 	if metaUpdate.Name != nil {
 | |
| 		expressions = append(expressions, r.query.WorkflowMeta.Name.Value(*metaUpdate.Name))
 | |
| 	}
 | |
| 
 | |
| 	if metaUpdate.Desc != nil {
 | |
| 		expressions = append(expressions, r.query.WorkflowMeta.Description.Value(*metaUpdate.Desc))
 | |
| 	}
 | |
| 
 | |
| 	if metaUpdate.IconURI != nil {
 | |
| 		expressions = append(expressions, r.query.WorkflowMeta.IconURI.Value(*metaUpdate.IconURI))
 | |
| 	}
 | |
| 
 | |
| 	if metaUpdate.HasPublished != nil {
 | |
| 		if *metaUpdate.HasPublished {
 | |
| 			expressions = append(expressions, r.query.WorkflowMeta.Status.Value(1))
 | |
| 		} else {
 | |
| 			expressions = append(expressions, r.query.WorkflowMeta.Status.Value(0))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if metaUpdate.LatestPublishedVersion != nil {
 | |
| 		expressions = append(expressions, r.query.WorkflowMeta.LatestVersion.Value(*metaUpdate.LatestPublishedVersion))
 | |
| 	}
 | |
| 
 | |
| 	if metaUpdate.WorkflowMode != nil {
 | |
| 		expressions = append(expressions, r.query.WorkflowMeta.Mode.Value(int32(*metaUpdate.WorkflowMode)))
 | |
| 	}
 | |
| 
 | |
| 	if len(expressions) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	_, err := r.query.WorkflowMeta.WithContext(ctx).Where(r.query.WorkflowMeta.ID.Eq(id)).
 | |
| 		UpdateColumnSimple(expressions...)
 | |
| 	if err != nil {
 | |
| 		return vo.WrapError(errno.ErrDatabaseError, fmt.Errorf("update workflow meta: %w", err))
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) GetEntity(ctx context.Context, policy *vo.GetPolicy) (_ *entity.Workflow, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	meta, err := r.GetMeta(ctx, policy.ID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if policy.MetaOnly {
 | |
| 		return &entity.Workflow{
 | |
| 			ID:   policy.ID,
 | |
| 			Meta: meta,
 | |
| 		}, nil
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		canvas, inputParams, outputParams string
 | |
| 		draftMeta                         *vo.DraftMeta
 | |
| 		versionMeta                       *vo.VersionMeta
 | |
| 		commitID                          string
 | |
| 	)
 | |
| 	switch policy.QType {
 | |
| 	case workflowModel.FromDraft:
 | |
| 		draft, err := r.DraftV2(ctx, policy.ID, policy.CommitID)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		canvas = draft.Canvas
 | |
| 		inputParams = draft.InputParamsStr
 | |
| 		outputParams = draft.OutputParamsStr
 | |
| 		draftMeta = draft.DraftMeta
 | |
| 		commitID = draft.CommitID
 | |
| 	case workflowModel.FromSpecificVersion:
 | |
| 		v, existed, err := r.GetVersion(ctx, policy.ID, policy.Version)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if !existed {
 | |
| 			return nil, vo.WrapError(errno.ErrWorkflowNotFound, fmt.Errorf("workflow version %s not found for ID %d: %w", policy.Version, policy.ID, err), errorx.KV("id", strconv.FormatInt(policy.ID, 10)))
 | |
| 		}
 | |
| 		canvas = v.Canvas
 | |
| 		inputParams = v.InputParamsStr
 | |
| 		outputParams = v.OutputParamsStr
 | |
| 		versionMeta = v.VersionMeta
 | |
| 		commitID = v.CommitID
 | |
| 	case workflowModel.FromLatestVersion:
 | |
| 		v, err := r.GetLatestVersion(ctx, policy.ID)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		canvas = v.Canvas
 | |
| 		inputParams = v.InputParamsStr
 | |
| 		outputParams = v.OutputParamsStr
 | |
| 		versionMeta = v.VersionMeta
 | |
| 		commitID = v.CommitID
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("invalid query type: %v", policy.QType))
 | |
| 	}
 | |
| 
 | |
| 	var inputs, outputs []*vo.NamedTypeInfo
 | |
| 	if inputParams != "" {
 | |
| 		err = sonic.UnmarshalString(inputParams, &inputs)
 | |
| 		if err != nil {
 | |
| 			return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 		}
 | |
| 	}
 | |
| 	if outputParams != "" {
 | |
| 		err = sonic.UnmarshalString(outputParams, &outputs)
 | |
| 		if err != nil {
 | |
| 			return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return &entity.Workflow{
 | |
| 		ID:       policy.ID,
 | |
| 		CommitID: commitID,
 | |
| 		Meta:     meta,
 | |
| 		CanvasInfo: &vo.CanvasInfo{
 | |
| 			Canvas:          canvas,
 | |
| 			InputParams:     inputs,
 | |
| 			OutputParams:    outputs,
 | |
| 			InputParamsStr:  inputParams,
 | |
| 			OutputParamsStr: outputParams,
 | |
| 		},
 | |
| 		DraftMeta:   draftMeta,
 | |
| 		VersionMeta: versionMeta,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) CreateChatFlowRoleConfig(ctx context.Context, chatFlowRole *entity.ChatFlowRole) (int64, error) {
 | |
| 	id, err := r.GenID(ctx)
 | |
| 	if err != nil {
 | |
| 		return 0, vo.WrapError(errno.ErrIDGenError, err)
 | |
| 	}
 | |
| 	chatFlowRoleConfig := &model.ChatFlowRoleConfig{
 | |
| 		ID:                  id,
 | |
| 		WorkflowID:          chatFlowRole.WorkflowID,
 | |
| 		Name:                chatFlowRole.Name,
 | |
| 		Description:         chatFlowRole.Description,
 | |
| 		Avatar:              chatFlowRole.AvatarUri,
 | |
| 		AudioConfig:         chatFlowRole.AudioConfig,
 | |
| 		BackgroundImageInfo: chatFlowRole.BackgroundImageInfo,
 | |
| 		OnboardingInfo:      chatFlowRole.OnboardingInfo,
 | |
| 		SuggestReplyInfo:    chatFlowRole.SuggestReplyInfo,
 | |
| 		UserInputConfig:     chatFlowRole.UserInputConfig,
 | |
| 		CreatorID:           chatFlowRole.CreatorID,
 | |
| 		Version:             chatFlowRole.Version,
 | |
| 	}
 | |
| 
 | |
| 	if err := r.query.ChatFlowRoleConfig.WithContext(ctx).Create(chatFlowRoleConfig); err != nil {
 | |
| 		return 0, vo.WrapError(errno.ErrDatabaseError, fmt.Errorf("create chat flow role: %w", err))
 | |
| 	}
 | |
| 
 | |
| 	return id, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) UpdateChatFlowRoleConfig(ctx context.Context, workflowID int64, chatFlowRole *vo.ChatFlowRoleUpdate) error {
 | |
| 	var expressions []field.AssignExpr
 | |
| 	if chatFlowRole.Name != nil {
 | |
| 		expressions = append(expressions, r.query.ChatFlowRoleConfig.Name.Value(*chatFlowRole.Name))
 | |
| 	}
 | |
| 	if chatFlowRole.Description != nil {
 | |
| 		expressions = append(expressions, r.query.ChatFlowRoleConfig.Description.Value(*chatFlowRole.Description))
 | |
| 	}
 | |
| 	if chatFlowRole.AvatarUri != nil {
 | |
| 		expressions = append(expressions, r.query.ChatFlowRoleConfig.Avatar.Value(*chatFlowRole.AvatarUri))
 | |
| 	}
 | |
| 	if chatFlowRole.AudioConfig != nil {
 | |
| 		expressions = append(expressions, r.query.ChatFlowRoleConfig.AudioConfig.Value(*chatFlowRole.AudioConfig))
 | |
| 	}
 | |
| 	if chatFlowRole.BackgroundImageInfo != nil {
 | |
| 		expressions = append(expressions, r.query.ChatFlowRoleConfig.BackgroundImageInfo.Value(*chatFlowRole.BackgroundImageInfo))
 | |
| 	}
 | |
| 	if chatFlowRole.OnboardingInfo != nil {
 | |
| 		expressions = append(expressions, r.query.ChatFlowRoleConfig.OnboardingInfo.Value(*chatFlowRole.OnboardingInfo))
 | |
| 	}
 | |
| 	if chatFlowRole.SuggestReplyInfo != nil {
 | |
| 		expressions = append(expressions, r.query.ChatFlowRoleConfig.SuggestReplyInfo.Value(*chatFlowRole.SuggestReplyInfo))
 | |
| 	}
 | |
| 	if chatFlowRole.UserInputConfig != nil {
 | |
| 		expressions = append(expressions, r.query.ChatFlowRoleConfig.UserInputConfig.Value(*chatFlowRole.UserInputConfig))
 | |
| 	}
 | |
| 
 | |
| 	if len(expressions) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	_, err := r.query.ChatFlowRoleConfig.WithContext(ctx).Where(r.query.ChatFlowRoleConfig.WorkflowID.Eq(workflowID)).
 | |
| 		UpdateColumnSimple(expressions...)
 | |
| 	if err != nil {
 | |
| 		return vo.WrapError(errno.ErrDatabaseError, fmt.Errorf("update chat flow role: %w", err))
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) GetChatFlowRoleConfig(ctx context.Context, workflowID int64, version string) (_ *entity.ChatFlowRole, err error, isExist bool) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 	role := &model.ChatFlowRoleConfig{}
 | |
| 	if version != "" {
 | |
| 		role, err = r.query.ChatFlowRoleConfig.WithContext(ctx).Where(r.query.ChatFlowRoleConfig.WorkflowID.Eq(workflowID), r.query.ChatFlowRoleConfig.Version.Eq(version)).First()
 | |
| 	} else {
 | |
| 		role, err = r.query.ChatFlowRoleConfig.WithContext(ctx).Where(r.query.ChatFlowRoleConfig.WorkflowID.Eq(workflowID)).First()
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			return nil, err, false
 | |
| 		}
 | |
| 		return nil, fmt.Errorf("failed to get chat flow role for chatflowID %d: %w", workflowID, err), true
 | |
| 	}
 | |
| 	res := &entity.ChatFlowRole{
 | |
| 		ID:                  role.ID,
 | |
| 		WorkflowID:          role.WorkflowID,
 | |
| 		Name:                role.Name,
 | |
| 		Description:         role.Description,
 | |
| 		AvatarUri:           role.Avatar,
 | |
| 		AudioConfig:         role.AudioConfig,
 | |
| 		BackgroundImageInfo: role.BackgroundImageInfo,
 | |
| 		OnboardingInfo:      role.OnboardingInfo,
 | |
| 		SuggestReplyInfo:    role.SuggestReplyInfo,
 | |
| 		UserInputConfig:     role.UserInputConfig,
 | |
| 		CreatorID:           role.CreatorID,
 | |
| 		CreatedAt:           time.UnixMilli(role.CreatedAt),
 | |
| 	}
 | |
| 	if role.UpdatedAt > 0 {
 | |
| 		res.UpdatedAt = time.UnixMilli(role.UpdatedAt)
 | |
| 	}
 | |
| 	return res, err, true
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) DeleteChatFlowRoleConfig(ctx context.Context, id int64, workflowID int64) error {
 | |
| 	_, err := r.query.ChatFlowRoleConfig.WithContext(ctx).Where(r.query.ChatFlowRoleConfig.ID.Eq(id), r.query.ChatFlowRoleConfig.WorkflowID.Eq(workflowID)).Delete()
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) GetVersion(ctx context.Context, id int64, version string) (_ *vo.VersionInfo, existed bool, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	wfVersion, err := r.query.WorkflowVersion.WithContext(ctx).
 | |
| 		Where(r.query.WorkflowVersion.WorkflowID.Eq(id), r.query.WorkflowVersion.Version.Eq(version)).
 | |
| 		First()
 | |
| 	if err != nil {
 | |
| 		if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			return nil, false, nil
 | |
| 		}
 | |
| 		return nil, false, fmt.Errorf("failed to get workflow version %s for ID %d: %w", version, id, err)
 | |
| 	}
 | |
| 
 | |
| 	return &vo.VersionInfo{
 | |
| 		VersionMeta: &vo.VersionMeta{
 | |
| 			Version:            wfVersion.Version,
 | |
| 			VersionDescription: wfVersion.VersionDescription,
 | |
| 			VersionCreatedAt:   time.UnixMilli(wfVersion.CreatedAt),
 | |
| 			VersionCreatorID:   wfVersion.CreatorID,
 | |
| 		},
 | |
| 		CanvasInfo: vo.CanvasInfo{
 | |
| 			Canvas:          wfVersion.Canvas,
 | |
| 			InputParamsStr:  wfVersion.InputParams,
 | |
| 			OutputParamsStr: wfVersion.OutputParams,
 | |
| 		},
 | |
| 		CommitID: wfVersion.CommitID,
 | |
| 	}, true, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) GetVersionListByConnectorAndWorkflowID(ctx context.Context, connectorID, workflowID int64, limit int) (_ []string, err error) {
 | |
| 	if limit <= 0 {
 | |
| 		return nil, vo.WrapError(errno.ErrInvalidParameter, errors.New("limit must be greater than 0"))
 | |
| 	}
 | |
| 
 | |
| 	connectorWorkflowVersion := r.query.ConnectorWorkflowVersion
 | |
| 	vl, err := connectorWorkflowVersion.WithContext(ctx).
 | |
| 		Where(connectorWorkflowVersion.ConnectorID.Eq(connectorID),
 | |
| 			connectorWorkflowVersion.WorkflowID.Eq(workflowID)).
 | |
| 		Order(connectorWorkflowVersion.CreatedAt.Desc()).
 | |
| 		Limit(limit).
 | |
| 		Find()
 | |
| 	if err != nil {
 | |
| 		return nil, vo.WrapError(errno.ErrDatabaseError, err)
 | |
| 	}
 | |
| 	var versionList []string
 | |
| 	for _, v := range vl {
 | |
| 		versionList = append(versionList, v.Version)
 | |
| 	}
 | |
| 	return versionList, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) IsApplicationConnectorWorkflowVersion(ctx context.Context, connectorID, workflowID int64, version string) (b bool, err error) {
 | |
| 	connectorWorkflowVersion := r.query.ConnectorWorkflowVersion
 | |
| 	_, err = connectorWorkflowVersion.WithContext(ctx).
 | |
| 		Where(connectorWorkflowVersion.ConnectorID.Eq(connectorID),
 | |
| 			connectorWorkflowVersion.WorkflowID.Eq(workflowID),
 | |
| 			connectorWorkflowVersion.Version.Eq(version)).
 | |
| 		First()
 | |
| 
 | |
| 	if err != nil {
 | |
| 		if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 		return false, vo.WrapError(errno.ErrDatabaseError, err)
 | |
| 	}
 | |
| 	return true, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) DraftV2(ctx context.Context, id int64, commitID string) (_ *vo.DraftInfo, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	var conds []gen.Condition
 | |
| 	conds = append(conds, r.query.WorkflowDraft.ID.Eq(id))
 | |
| 	if commitID != "" {
 | |
| 		conds = append(conds, r.query.WorkflowDraft.CommitID.Eq(commitID))
 | |
| 	}
 | |
| 
 | |
| 	draft, err := r.query.WorkflowDraft.WithContext(ctx).Where(conds...).First()
 | |
| 	if err != nil {
 | |
| 		if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			if len(commitID) == 0 {
 | |
| 				return nil, vo.WrapError(errno.ErrWorkflowNotFound, fmt.Errorf("workflow draft not found for ID %d: %w", id, err),
 | |
| 					errorx.KV("id", strconv.FormatInt(id, 10)))
 | |
| 			} else {
 | |
| 				snapshot, err := r.query.WorkflowSnapshot.WithContext(ctx).Where(
 | |
| 					r.query.WorkflowSnapshot.WorkflowID.Eq(id),
 | |
| 					r.query.WorkflowSnapshot.CommitID.Eq(commitID),
 | |
| 				).First()
 | |
| 				if err != nil {
 | |
| 					if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 						return nil, vo.WrapError(errno.ErrWorkflowSnapshotNotFound,
 | |
| 							fmt.Errorf("workflow snapshot not found for ID %d, commitID %s: %w",
 | |
| 								id, commitID, err),
 | |
| 							errorx.KV("id", strconv.FormatInt(id, 10)),
 | |
| 							errorx.KV("commit_id", commitID))
 | |
| 					} else {
 | |
| 						return nil, fmt.Errorf("failed to query workflow snapshot for ID %d, commitID %s: %w",
 | |
| 							id, commitID, err)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return &vo.DraftInfo{
 | |
| 					DraftMeta: &vo.DraftMeta{
 | |
| 						Timestamp:  time.UnixMilli(snapshot.CreatedAt),
 | |
| 						IsSnapshot: true,
 | |
| 					},
 | |
| 
 | |
| 					Canvas:          snapshot.Canvas,
 | |
| 					InputParamsStr:  snapshot.InputParams,
 | |
| 					OutputParamsStr: snapshot.OutputParams,
 | |
| 					CommitID:        snapshot.CommitID,
 | |
| 				}, nil
 | |
| 			}
 | |
| 		}
 | |
| 		return nil, fmt.Errorf("failed to get workflow draft for ID %d, commitID %s: %w", id, commitID, err)
 | |
| 	}
 | |
| 
 | |
| 	return &vo.DraftInfo{
 | |
| 		DraftMeta: &vo.DraftMeta{
 | |
| 			TestRunSuccess: draft.TestRunSuccess,
 | |
| 			Modified:       draft.Modified,
 | |
| 			Timestamp:      time.UnixMilli(draft.UpdatedAt),
 | |
| 			IsSnapshot:     false,
 | |
| 		},
 | |
| 
 | |
| 		Canvas:          draft.Canvas,
 | |
| 		InputParamsStr:  draft.InputParams,
 | |
| 		OutputParamsStr: draft.OutputParams,
 | |
| 		CommitID:        draft.CommitID,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) MGetDrafts(ctx context.Context, policy *vo.MGetPolicy) (_ []*entity.Workflow, totalCount int64, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	q := policy.MetaQuery
 | |
| 	if len(q.IDs) == 0 && q.Page == nil && q.Name == nil && q.AppID == nil {
 | |
| 		return nil, 0, vo.WrapError(errno.ErrInternalBadRequest,
 | |
| 			fmt.Errorf("insufficient query parameters for workflow draft: %+v", q),
 | |
| 			errorx.KV("scene", "query workflow drafts"))
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		conditions []gen.Condition
 | |
| 	)
 | |
| 	if len(q.IDs) > 0 {
 | |
| 		conditions = append(conditions, r.query.WorkflowDraft.ID.In(q.IDs...))
 | |
| 	}
 | |
| 
 | |
| 	if q.Name != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.Name.Like(`%%`+*q.Name+`%%`))
 | |
| 	}
 | |
| 
 | |
| 	if q.SpaceID != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.SpaceID.Eq(*q.SpaceID))
 | |
| 	}
 | |
| 
 | |
| 	if q.PublishStatus != nil {
 | |
| 		if *q.PublishStatus == vo.HasPublished {
 | |
| 			conditions = append(conditions, r.query.WorkflowMeta.Status.Eq(1))
 | |
| 		} else {
 | |
| 			conditions = append(conditions, r.query.WorkflowMeta.Status.Eq(0))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if q.AppID != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.AppID.Eq(*q.AppID))
 | |
| 	}
 | |
| 
 | |
| 	if q.LibOnly {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.AppID.Eq(0))
 | |
| 	}
 | |
| 
 | |
| 	if q.Mode != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.Mode.Eq(int32(*q.Mode)))
 | |
| 	}
 | |
| 
 | |
| 	type combinedDraft struct {
 | |
| 		model.WorkflowDraft
 | |
| 		Name          string `gorm:"column:name"`
 | |
| 		Description   string `gorm:"column:description"`
 | |
| 		AppID         int64  `gorm:"column:app_id"`
 | |
| 		Status        int32  `gorm:"column:status"`
 | |
| 		SpaceID       int64  `gorm:"column:space_id"`
 | |
| 		IconURI       string `gorm:"column:icon_uri"`
 | |
| 		ContentType   int32  `gorm:"column:content_type"`
 | |
| 		Mode          int32  `gorm:"column:mode"`
 | |
| 		CreatedAt     int64  `gorm:"column:created_at"`
 | |
| 		CreatorID     int64  `gorm:"column:creator_id"`
 | |
| 		Tag           int32  `gorm:"column:tag"`
 | |
| 		LatestVersion string `gorm:"column:latest_version"`
 | |
| 	}
 | |
| 
 | |
| 	selectColumns := r.query.WorkflowDraft.Columns(r.query.WorkflowDraft.ALL)
 | |
| 	selectColumns = append(selectColumns, r.query.WorkflowMeta.Name.As("name"),
 | |
| 		r.query.WorkflowMeta.Description.As("description"),
 | |
| 		r.query.WorkflowMeta.AppID.As("app_id"),
 | |
| 		r.query.WorkflowMeta.Status.As("status"),
 | |
| 		r.query.WorkflowMeta.SpaceID.As("space_id"),
 | |
| 		r.query.WorkflowMeta.IconURI.As("icon_uri"),
 | |
| 		r.query.WorkflowMeta.ContentType.As("content_type"),
 | |
| 		r.query.WorkflowMeta.Mode.As("mode"),
 | |
| 		r.query.WorkflowMeta.CreatedAt.As("created_at"),
 | |
| 		r.query.WorkflowMeta.CreatorID.As("creator_id"),
 | |
| 		r.query.WorkflowMeta.Tag.As("tag"),
 | |
| 		r.query.WorkflowMeta.LatestVersion.As("latest_version"),
 | |
| 	)
 | |
| 
 | |
| 	d := r.query.WorkflowDraft.Debug().WithContext(ctx).
 | |
| 		Join(r.query.WorkflowMeta, r.query.WorkflowDraft.ID.EqCol(r.query.WorkflowMeta.ID)).
 | |
| 		Select(selectColumns...).
 | |
| 		Where(conditions...)
 | |
| 
 | |
| 	if q.NeedTotalNumber {
 | |
| 		totalCount, err = d.Count()
 | |
| 		if err != nil {
 | |
| 			return nil, 0, fmt.Errorf("failed to get workflow draft count for policy %+v: %w", policy, err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if q.DescByUpdate {
 | |
| 		d = d.Order(r.query.WorkflowDraft.UpdatedAt.Desc())
 | |
| 	} else {
 | |
| 		d = d.Order(r.query.WorkflowMeta.CreatedAt.Desc())
 | |
| 	}
 | |
| 
 | |
| 	var combinedDrafts []combinedDraft
 | |
| 	if q.Page != nil {
 | |
| 		_, err = d.ScanByPage(&combinedDrafts, q.Page.Offset(), q.Page.Limit())
 | |
| 	} else {
 | |
| 		err = d.Scan(&combinedDrafts)
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			return nil, 0, nil
 | |
| 		}
 | |
| 		return nil, 0, fmt.Errorf("failed to get workflow draft for policy %+v: %w", policy, err)
 | |
| 	}
 | |
| 
 | |
| 	result := make([]*entity.Workflow, len(combinedDrafts))
 | |
| 	for i, draft := range combinedDrafts {
 | |
| 		url, err := r.tos.GetObjectUrl(ctx, draft.IconURI)
 | |
| 		if err != nil {
 | |
| 			logs.Warnf("failed to get url for workflow meta %v", err)
 | |
| 		}
 | |
| 
 | |
| 		canvasInfo := &vo.CanvasInfo{
 | |
| 			Canvas:          draft.Canvas,
 | |
| 			InputParamsStr:  draft.InputParams,
 | |
| 			OutputParamsStr: draft.OutputParams,
 | |
| 		}
 | |
| 		if err = canvasInfo.Unmarshal(); err != nil {
 | |
| 			return nil, 0, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 		}
 | |
| 
 | |
| 		wf := &entity.Workflow{
 | |
| 			ID:       draft.ID,
 | |
| 			CommitID: draft.CommitID,
 | |
| 			Meta: &vo.Meta{
 | |
| 				SpaceID:     draft.SpaceID,
 | |
| 				CreatorID:   draft.CreatorID,
 | |
| 				CreatedAt:   time.UnixMilli(draft.CreatedAt),
 | |
| 				ContentType: entity.ContentType(draft.ContentType),
 | |
| 				Name:        draft.Name,
 | |
| 				Desc:        draft.Description,
 | |
| 				IconURI:     draft.IconURI,
 | |
| 				IconURL:     url,
 | |
| 				Mode:        entity.Mode(draft.Mode),
 | |
| 			},
 | |
| 			DraftMeta: &vo.DraftMeta{
 | |
| 				TestRunSuccess: draft.TestRunSuccess,
 | |
| 				Modified:       draft.Modified,
 | |
| 				Timestamp:      time.UnixMilli(draft.UpdatedAt),
 | |
| 				IsSnapshot:     false,
 | |
| 			},
 | |
| 			CanvasInfo: canvasInfo,
 | |
| 		}
 | |
| 
 | |
| 		if draft.Tag != 0 {
 | |
| 			wf.Meta.Tag = ptr.Of(entity.Tag(draft.Tag))
 | |
| 		}
 | |
| 		if draft.AppID != 0 {
 | |
| 			wf.Meta.AppID = &draft.AppID
 | |
| 		}
 | |
| 		if draft.Status > 0 {
 | |
| 			wf.Meta.HasPublished = true
 | |
| 		}
 | |
| 		if draft.LatestVersion != "" {
 | |
| 			wf.Meta.LatestPublishedVersion = &draft.LatestVersion
 | |
| 		}
 | |
| 
 | |
| 		result[i] = wf
 | |
| 	}
 | |
| 
 | |
| 	return result, totalCount, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) MGetLatestVersion(ctx context.Context, policy *vo.MGetPolicy) (
 | |
| 	_ []*entity.Workflow, totalCount int64, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	q := policy.MetaQuery
 | |
| 	if len(q.IDs) == 0 && q.Page == nil && q.Name == nil && q.AppID == nil {
 | |
| 		return nil, 0, vo.WrapError(errno.ErrInternalBadRequest,
 | |
| 			fmt.Errorf("insufficient query parameters for workflow latest versions: %+v", q),
 | |
| 			errorx.KV("scene", "query latest workflow version"))
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		conditions []gen.Condition
 | |
| 	)
 | |
| 	if len(q.IDs) > 0 {
 | |
| 		conditions = append(conditions, r.query.WorkflowVersion.WorkflowID.In(q.IDs...))
 | |
| 	}
 | |
| 
 | |
| 	if q.Name != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.Name.Like(`%%`+*q.Name+`%%`))
 | |
| 	}
 | |
| 
 | |
| 	if q.SpaceID != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.SpaceID.Eq(*q.SpaceID))
 | |
| 	}
 | |
| 
 | |
| 	if q.PublishStatus != nil {
 | |
| 		if *q.PublishStatus == vo.HasPublished {
 | |
| 			conditions = append(conditions, r.query.WorkflowMeta.Status.Eq(1))
 | |
| 		} else {
 | |
| 			conditions = append(conditions, r.query.WorkflowMeta.Status.Eq(0))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if q.AppID != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.AppID.Eq(*q.AppID))
 | |
| 	}
 | |
| 
 | |
| 	if q.LibOnly {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.AppID.Eq(0))
 | |
| 	}
 | |
| 
 | |
| 	if q.Mode != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.Mode.Eq(int32(*q.Mode)))
 | |
| 	}
 | |
| 
 | |
| 	type combinedVersion struct {
 | |
| 		model.WorkflowMeta
 | |
| 		Version            string `gorm:"column:version"`             // release version
 | |
| 		VersionDescription string `gorm:"column:version_description"` // version description
 | |
| 		Canvas             string `gorm:"column:canvas"`              // Front-end schema
 | |
| 		InputParams        string `gorm:"column:input_params"`
 | |
| 		OutputParams       string `gorm:"column:output_params"`
 | |
| 		VersionCreatorID   int64  `gorm:"column:version_creator_id"` // Publish user ID
 | |
| 		VersionCreatedAt   int64  `gorm:"column:version_created_at"` // Creation time millisecond timestamp
 | |
| 		CommitID           string `gorm:"column:commit_id"`          // the commit id corresponding to this version
 | |
| 	}
 | |
| 
 | |
| 	selectColumns := r.query.WorkflowMeta.Columns(r.query.WorkflowMeta.ALL)
 | |
| 	selectColumns = append(selectColumns, r.query.WorkflowVersion.Version.As("version"),
 | |
| 		r.query.WorkflowVersion.VersionDescription.As("version_description"),
 | |
| 		r.query.WorkflowVersion.Canvas.As("canvas"),
 | |
| 		r.query.WorkflowVersion.InputParams.As("input_params"),
 | |
| 		r.query.WorkflowVersion.OutputParams.As("output_params"),
 | |
| 		r.query.WorkflowVersion.CreatorID.As("version_creator_id"),
 | |
| 		r.query.WorkflowVersion.CreatedAt.As("version_created_at"),
 | |
| 		r.query.WorkflowVersion.CommitID.As("commit_id"),
 | |
| 	)
 | |
| 
 | |
| 	d := r.query.WorkflowMeta.Debug().WithContext(ctx).
 | |
| 		Join(r.query.WorkflowVersion, r.query.WorkflowVersion.WorkflowID.EqCol(r.query.WorkflowMeta.ID),
 | |
| 			r.query.WorkflowVersion.Version.EqCol(r.query.WorkflowMeta.LatestVersion)).
 | |
| 		Select(selectColumns...).
 | |
| 		Where(conditions...)
 | |
| 
 | |
| 	if q.NeedTotalNumber {
 | |
| 		totalCount, err = d.Count()
 | |
| 		if err != nil {
 | |
| 			return nil, 0, fmt.Errorf("failed to get workflow latest versions count for policy %+v: %w", policy, err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if q.DescByUpdate {
 | |
| 		d = d.Order(r.query.WorkflowMeta.LatestVersionTs.Desc())
 | |
| 	} else {
 | |
| 		d = d.Order(r.query.WorkflowMeta.LatestVersionTs.Asc())
 | |
| 	}
 | |
| 
 | |
| 	var combinedVersions []combinedVersion
 | |
| 	if q.Page != nil {
 | |
| 		_, err = d.ScanByPage(&combinedVersions, q.Page.Offset(), q.Page.Limit())
 | |
| 	} else {
 | |
| 		err = d.Scan(&combinedVersions)
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			return nil, 0, nil
 | |
| 		}
 | |
| 		return nil, 0, fmt.Errorf("failed to get workflow latest versions for policy %+v: %w", policy, err)
 | |
| 	}
 | |
| 
 | |
| 	result := make([]*entity.Workflow, len(combinedVersions))
 | |
| 	for i, version := range combinedVersions {
 | |
| 		url, err := r.tos.GetObjectUrl(ctx, version.IconURI)
 | |
| 		if err != nil {
 | |
| 			logs.Warnf("failed to get url for workflow meta %v", err)
 | |
| 		}
 | |
| 
 | |
| 		canvasInfo := &vo.CanvasInfo{
 | |
| 			Canvas:          version.Canvas,
 | |
| 			InputParamsStr:  version.InputParams,
 | |
| 			OutputParamsStr: version.OutputParams,
 | |
| 		}
 | |
| 		if err = canvasInfo.Unmarshal(); err != nil {
 | |
| 			return nil, 0, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 		}
 | |
| 
 | |
| 		wf := &entity.Workflow{
 | |
| 			ID:       version.ID,
 | |
| 			CommitID: version.CommitID,
 | |
| 			Meta: &vo.Meta{
 | |
| 				SpaceID:     version.SpaceID,
 | |
| 				CreatorID:   version.CreatorID,
 | |
| 				CreatedAt:   time.UnixMilli(version.CreatedAt),
 | |
| 				ContentType: entity.ContentType(version.ContentType),
 | |
| 				Name:        version.Name,
 | |
| 				Desc:        version.Description,
 | |
| 				IconURI:     version.IconURI,
 | |
| 				IconURL:     url,
 | |
| 				Mode:        entity.Mode(version.Mode),
 | |
| 			},
 | |
| 			VersionMeta: &vo.VersionMeta{
 | |
| 				Version:            version.Version,
 | |
| 				VersionDescription: version.VersionDescription,
 | |
| 				VersionCreatedAt:   time.UnixMilli(version.VersionCreatedAt),
 | |
| 				VersionCreatorID:   version.VersionCreatorID,
 | |
| 			},
 | |
| 			CanvasInfo: canvasInfo,
 | |
| 		}
 | |
| 
 | |
| 		if version.Tag != 0 {
 | |
| 			wf.Meta.Tag = ptr.Of(entity.Tag(version.Tag))
 | |
| 		}
 | |
| 		if version.AppID != 0 {
 | |
| 			wf.Meta.AppID = &version.AppID
 | |
| 		}
 | |
| 		if version.Status > 0 {
 | |
| 			wf.Meta.HasPublished = true
 | |
| 		}
 | |
| 		if version.LatestVersion != "" {
 | |
| 			wf.Meta.LatestPublishedVersion = &version.LatestVersion
 | |
| 		}
 | |
| 
 | |
| 		result[i] = wf
 | |
| 	}
 | |
| 
 | |
| 	return result, totalCount, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) MGetReferences(ctx context.Context, policy *vo.MGetReferencePolicy) (
 | |
| 	_ []*entity.WorkflowReference, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	if len(policy.ReferredIDs) == 0 {
 | |
| 		return nil, vo.WrapError(errno.ErrInternalBadRequest, errors.New("referred IDs cannot be empty when querying references"))
 | |
| 	}
 | |
| 
 | |
| 	var conds []gen.Condition
 | |
| 	if len(policy.ReferredIDs) == 1 {
 | |
| 		conds = append(conds, r.query.WorkflowReference.ReferredID.Eq(policy.ReferredIDs[0]))
 | |
| 	} else {
 | |
| 		conds = append(conds, r.query.WorkflowReference.ReferredID.In(policy.ReferredIDs...))
 | |
| 	}
 | |
| 
 | |
| 	if len(policy.ReferringIDs) == 1 {
 | |
| 		conds = append(conds, r.query.WorkflowReference.ReferringID.Eq(policy.ReferringIDs[0]))
 | |
| 	} else if len(policy.ReferringIDs) > 1 {
 | |
| 		conds = append(conds, r.query.WorkflowReference.ReferringID.In(policy.ReferringIDs...))
 | |
| 	}
 | |
| 
 | |
| 	if len(policy.ReferType) == 1 {
 | |
| 		conds = append(conds, r.query.WorkflowReference.ReferType.Eq(int32(policy.ReferType[0])))
 | |
| 	} else if len(policy.ReferType) > 1 {
 | |
| 		conds = append(conds, r.query.WorkflowReference.ReferType.In(
 | |
| 			slices.Transform(policy.ReferType, func(r vo.ReferType) int32 {
 | |
| 				return int32(r)
 | |
| 			})...))
 | |
| 	}
 | |
| 
 | |
| 	if len(policy.ReferringBizType) == 1 {
 | |
| 		conds = append(conds, r.query.WorkflowReference.ReferringBizType.Eq(int32(policy.ReferringBizType[0])))
 | |
| 	} else if len(policy.ReferringBizType) > 1 {
 | |
| 		conds = append(conds, r.query.WorkflowReference.ReferringBizType.In(
 | |
| 			slices.Transform(policy.ReferringBizType, func(r vo.ReferringBizType) int32 {
 | |
| 				return int32(r)
 | |
| 			})...))
 | |
| 	}
 | |
| 
 | |
| 	conds = append(conds, r.query.WorkflowReference.Status.Eq(1))
 | |
| 
 | |
| 	refs, err := r.query.WorkflowReference.WithContext(ctx).Where(conds...).Find()
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to query workflow references: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	result := make([]*entity.WorkflowReference, 0, len(refs))
 | |
| 	for _, ref := range refs {
 | |
| 		result = append(result, &entity.WorkflowReference{
 | |
| 			ID: ref.ID,
 | |
| 			WorkflowReferenceKey: entity.WorkflowReferenceKey{
 | |
| 				ReferredID:       ref.ReferredID,
 | |
| 				ReferringID:      ref.ReferringID,
 | |
| 				ReferType:        vo.ReferType(ref.ReferType),
 | |
| 				ReferringBizType: vo.ReferringBizType(ref.ReferringBizType),
 | |
| 			},
 | |
| 			CreatedAt: time.UnixMilli(ref.CreatedAt),
 | |
| 			Enabled:   ref.Status == 1,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return result, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) MGetMetas(ctx context.Context, query *vo.MetaQuery) (
 | |
| 	_ map[int64]*vo.Meta, _ int64, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	if len(query.IDs) == 0 && query.Page == nil && query.Name == nil && query.AppID == nil {
 | |
| 		return nil, 0, vo.WrapError(errno.ErrInternalBadRequest,
 | |
| 			fmt.Errorf("insufficient query parameters for workflow meta: %+v", query),
 | |
| 			errorx.KV("scene", "query workflow metas"))
 | |
| 	}
 | |
| 
 | |
| 	var conditions []gen.Condition
 | |
| 	if len(query.IDs) > 0 {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.ID.In(query.IDs...))
 | |
| 	}
 | |
| 
 | |
| 	if query.Name != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.Name.Like(`%%`+*query.Name+`%%`))
 | |
| 	}
 | |
| 
 | |
| 	if query.SpaceID != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.SpaceID.Eq(*query.SpaceID))
 | |
| 	}
 | |
| 
 | |
| 	if query.PublishStatus != nil {
 | |
| 		if *query.PublishStatus == vo.HasPublished {
 | |
| 			conditions = append(conditions, r.query.WorkflowMeta.Status.Eq(1))
 | |
| 		} else {
 | |
| 			conditions = append(conditions, r.query.WorkflowMeta.Status.Eq(0))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if query.AppID != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.AppID.Eq(*query.AppID))
 | |
| 	}
 | |
| 
 | |
| 	if query.LibOnly { // if AppID not specified, we can only query those within Library
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.AppID.Eq(0))
 | |
| 	}
 | |
| 
 | |
| 	if query.Mode != nil {
 | |
| 		conditions = append(conditions, r.query.WorkflowMeta.Mode.Eq(int32(*query.Mode)))
 | |
| 	}
 | |
| 
 | |
| 	var result []*model.WorkflowMeta
 | |
| 
 | |
| 	workflowMetaDo := r.query.WorkflowMeta.WithContext(ctx).Debug().Where(conditions...)
 | |
| 
 | |
| 	var total int64
 | |
| 	if query.NeedTotalNumber { // this is the total count
 | |
| 		total, err = workflowMetaDo.Count()
 | |
| 		if err != nil {
 | |
| 			return nil, 0, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if query.DescByUpdate {
 | |
| 		workflowMetaDo = workflowMetaDo.Order(r.query.WorkflowMeta.UpdatedAt.Desc())
 | |
| 	} else {
 | |
| 		workflowMetaDo = workflowMetaDo.Order(r.query.WorkflowMeta.CreatedAt.Desc())
 | |
| 	}
 | |
| 
 | |
| 	if query.Page != nil {
 | |
| 		result, _, err = workflowMetaDo.FindByPage(query.Page.Offset(), query.Page.Limit())
 | |
| 		if err != nil {
 | |
| 			return nil, 0, err
 | |
| 		}
 | |
| 	} else {
 | |
| 		if len(conditions) == 0 {
 | |
| 			return nil, 0, errors.New("no conditions provided")
 | |
| 		}
 | |
| 		result, err = workflowMetaDo.Find()
 | |
| 		if err != nil {
 | |
| 			return nil, 0, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	wfMap := make(map[int64]*vo.Meta, len(result))
 | |
| 	for _, meta := range result {
 | |
| 		converted, err := r.convertMeta(ctx, meta)
 | |
| 		if err != nil {
 | |
| 			return nil, 0, err
 | |
| 		}
 | |
| 		wfMap[meta.ID] = converted
 | |
| 	}
 | |
| 	return wfMap, total, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) GetLatestVersion(ctx context.Context, id int64) (*vo.VersionInfo, error) {
 | |
| 	version, err := r.query.WorkflowVersion.WithContext(ctx).Where(r.query.WorkflowVersion.WorkflowID.Eq(id)).
 | |
| 		Order(r.query.WorkflowVersion.CreatedAt.Desc()).First()
 | |
| 	if err != nil {
 | |
| 		if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			return nil, vo.WrapError(errno.ErrWorkflowNotFound,
 | |
| 				fmt.Errorf("workflow version not found for ID %d: %w", id, err),
 | |
| 				errorx.KV("id", strconv.FormatInt(id, 10)))
 | |
| 		}
 | |
| 		return nil, fmt.Errorf("failed to query workflow version for ID %d: %w", id, err)
 | |
| 	}
 | |
| 	return &vo.VersionInfo{
 | |
| 		VersionMeta: &vo.VersionMeta{
 | |
| 			Version:            version.Version,
 | |
| 			VersionDescription: version.VersionDescription,
 | |
| 			VersionCreatedAt:   time.UnixMilli(version.CreatedAt),
 | |
| 			VersionCreatorID:   version.CreatorID,
 | |
| 		},
 | |
| 		CanvasInfo: vo.CanvasInfo{
 | |
| 			Canvas:          version.Canvas,
 | |
| 			InputParamsStr:  version.InputParams,
 | |
| 			OutputParamsStr: version.OutputParams,
 | |
| 		},
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) CreateSnapshotIfNeeded(ctx context.Context, id int64, commitID string) error {
 | |
| 	latestSnapshot, err := r.query.WorkflowSnapshot.WithContext(ctx).Where(
 | |
| 		r.query.WorkflowSnapshot.WorkflowID.Eq(id),
 | |
| 		r.query.WorkflowSnapshot.CommitID.Eq(commitID),
 | |
| 	).First()
 | |
| 
 | |
| 	if err != nil {
 | |
| 		if !errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			logs.CtxErrorf(ctx, "query workflow snapshot failed err=%v", err)
 | |
| 		}
 | |
| 	} else if latestSnapshot != nil { // already have this snapshot, no need to create it
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	draft, err := r.query.WorkflowDraft.WithContext(ctx).Where(
 | |
| 		r.query.WorkflowDraft.ID.Eq(id),
 | |
| 		r.query.WorkflowDraft.CommitID.Eq(commitID),
 | |
| 	).First()
 | |
| 	if err != nil {
 | |
| 		if errors.Is(err, gorm.ErrRecordNotFound) {
 | |
| 			return vo.WrapError(errno.ErrWorkflowNotFound,
 | |
| 				fmt.Errorf("workflow draft not found for ID %d, commitID %s: %w", id, commitID, err),
 | |
| 				errorx.KV("id", strconv.FormatInt(id, 10)))
 | |
| 		}
 | |
| 		return vo.WrapError(errno.ErrDatabaseError,
 | |
| 			fmt.Errorf("failed to query workflow draft for ID %d, commitID %s: %w", id, commitID, err))
 | |
| 	}
 | |
| 
 | |
| 	return r.query.WorkflowSnapshot.WithContext(ctx).Save(&model.WorkflowSnapshot{
 | |
| 		// ID: auto_increment
 | |
| 		WorkflowID:   id,
 | |
| 		CommitID:     commitID,
 | |
| 		Canvas:       draft.Canvas,
 | |
| 		InputParams:  draft.InputParams,
 | |
| 		OutputParams: draft.OutputParams,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) WorkflowAsTool(ctx context.Context, policy vo.GetPolicy, wfToolConfig vo.WorkflowToolConfig) (workflow.ToolFromWorkflow, error) {
 | |
| 	var (
 | |
| 		canvas               vo.Canvas
 | |
| 		inputParamsCfg       = wfToolConfig.InputParametersConfig
 | |
| 		outputParamsCfg      = wfToolConfig.OutputParametersConfig
 | |
| 		inputParamsConfigMap = slices.ToMap(inputParamsCfg, func(w *workflow3.APIParameter) (string, *workflow3.APIParameter) {
 | |
| 			return w.Name, w
 | |
| 		})
 | |
| 	)
 | |
| 
 | |
| 	wfEntity, err := r.GetEntity(ctx, &policy)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if err = sonic.UnmarshalString(wfEntity.Canvas, &canvas); err != nil {
 | |
| 		return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 	}
 | |
| 
 | |
| 	name := fmt.Sprintf("ts_%s_%s", wfEntity.Name, wfEntity.Name)
 | |
| 	desc := wfEntity.Desc
 | |
| 
 | |
| 	var params map[string]*schema.ParameterInfo
 | |
| 
 | |
| 	for _, tInfo := range wfEntity.InputParams {
 | |
| 		if p, ok := inputParamsConfigMap[tInfo.Name]; ok && p.LocalDisable {
 | |
| 			continue
 | |
| 		}
 | |
| 		param, err := tInfo.ToParameterInfo()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		if params == nil {
 | |
| 			params = make(map[string]*schema.ParameterInfo)
 | |
| 		}
 | |
| 		params[tInfo.Name] = param
 | |
| 	}
 | |
| 
 | |
| 	toolInfo := &schema.ToolInfo{
 | |
| 		Name:        name,
 | |
| 		Desc:        desc,
 | |
| 		ParamsOneOf: schema.NewParamsOneOfByParams(params),
 | |
| 	}
 | |
| 
 | |
| 	workflowSC, err := adaptor.CanvasToWorkflowSchema(ctx, &canvas)
 | |
| 	if err != nil {
 | |
| 		return nil, vo.WrapError(errno.ErrSchemaConversionFail, err)
 | |
| 	}
 | |
| 
 | |
| 	var opts []compose.WorkflowOption
 | |
| 	opts = append(opts, compose.WithIDAsName(policy.ID),
 | |
| 		compose.WithParentRequireCheckpoint()) // always assumes the 'parent' may pass a checkpoint ID
 | |
| 	if s := execute.GetStaticConfig(); s != nil && s.MaxNodeCountPerWorkflow > 0 {
 | |
| 		opts = append(opts, compose.WithMaxNodeCount(s.MaxNodeCountPerWorkflow))
 | |
| 	}
 | |
| 
 | |
| 	wf, err := compose.NewWorkflow(ctx, workflowSC, opts...)
 | |
| 	if err != nil {
 | |
| 		return nil, vo.WrapError(errno.ErrWorkflowCompileFail, err)
 | |
| 	}
 | |
| 
 | |
| 	type streamFunc func(ctx context.Context, in map[string]any, opts ...einoCompose.Option) (*schema.StreamReader[map[string]any], error)
 | |
| 
 | |
| 	if wf.StreamRun() {
 | |
| 		convertStream := func(stream streamFunc) streamFunc {
 | |
| 			return func(ctx context.Context, in map[string]any, opts ...einoCompose.Option) (*schema.StreamReader[map[string]any], error) {
 | |
| 				if len(inputParamsConfigMap) == 0 {
 | |
| 					return stream(ctx, in, opts...)
 | |
| 				}
 | |
| 				input := make(map[string]any, len(in))
 | |
| 				for k, v := range in {
 | |
| 					if p, ok := inputParamsConfigMap[k]; ok {
 | |
| 						if p.LocalDisable {
 | |
| 							if p.LocalDefault != nil {
 | |
| 								input[k], err = transformDefaultValue(*p.LocalDefault, p)
 | |
| 								if err != nil {
 | |
| 									return nil, err
 | |
| 								}
 | |
| 							}
 | |
| 						} else {
 | |
| 							input[k] = v
 | |
| 						}
 | |
| 
 | |
| 					} else {
 | |
| 						input[k] = v
 | |
| 					}
 | |
| 				}
 | |
| 				return stream(ctx, input, opts...)
 | |
| 			}
 | |
| 		}
 | |
| 		return compose.NewStreamableWorkflow(
 | |
| 			toolInfo,
 | |
| 			convertStream(wf.Runner.Stream),
 | |
| 			wf.TerminatePlan(),
 | |
| 			wfEntity,
 | |
| 			workflowSC,
 | |
| 			r,
 | |
| 		), nil
 | |
| 	}
 | |
| 
 | |
| 	type invokeFunc func(ctx context.Context, in map[string]any, opts ...einoCompose.Option) (out map[string]any, err error)
 | |
| 	convertInvoke := func(invoke invokeFunc) invokeFunc {
 | |
| 		return func(ctx context.Context, in map[string]any, opts ...einoCompose.Option) (out map[string]any, err error) {
 | |
| 			if len(inputParamsCfg) == 0 && len(outputParamsCfg) == 0 {
 | |
| 				return invoke(ctx, in, opts...)
 | |
| 			}
 | |
| 			input := make(map[string]any, len(in))
 | |
| 			for k, v := range in {
 | |
| 				if p, ok := inputParamsConfigMap[k]; ok {
 | |
| 					if p.LocalDisable {
 | |
| 						if p.LocalDefault != nil {
 | |
| 							input[k], err = transformDefaultValue(*p.LocalDefault, p)
 | |
| 							if err != nil {
 | |
| 								return nil, fmt.Errorf("failed to transfer default value, default value=%v,value type=%v,err=%w", *p.LocalDefault, p.Type, err)
 | |
| 							}
 | |
| 						}
 | |
| 					} else {
 | |
| 						input[k] = v
 | |
| 					}
 | |
| 				} else {
 | |
| 					input[k] = v
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			out, err = invoke(ctx, input, opts...)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 
 | |
| 			if wf.TerminatePlan() == vo.ReturnVariables && len(outputParamsCfg) > 0 {
 | |
| 				return filterDisabledAPIParameters(outputParamsCfg, out), nil
 | |
| 			}
 | |
| 
 | |
| 			return out, nil
 | |
| 
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return compose.NewInvokableWorkflow(
 | |
| 		toolInfo,
 | |
| 		convertInvoke(wf.Runner.Invoke),
 | |
| 		wf.TerminatePlan(),
 | |
| 		wfEntity,
 | |
| 		workflowSC,
 | |
| 		r,
 | |
| 	), nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) CopyWorkflow(ctx context.Context, workflowID int64, policy vo.CopyWorkflowPolicy) (
 | |
| 	_ *entity.Workflow, err error) {
 | |
| 	const (
 | |
| 		copyWorkflowRedisKeyPrefix         = "copy_workflow_redis_key_prefix"
 | |
| 		copyWorkflowRedisKeyExpireInterval = time.Hour * 24 * 7
 | |
| 	)
 | |
| 
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	var (
 | |
| 		copiedID      int64
 | |
| 		workflowMeta  = r.query.WorkflowMeta
 | |
| 		workflowDraft = r.query.WorkflowDraft
 | |
| 	)
 | |
| 
 | |
| 	copiedID, err = r.IDGenerator.GenID(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, vo.WrapError(errno.ErrIDGenError, err)
 | |
| 	}
 | |
| 
 | |
| 	var copiedWorkflow *entity.Workflow
 | |
| 	wfMeta, err := workflowMeta.WithContext(ctx).Where(workflowMeta.ID.Eq(workflowID)).First()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	wfDraft, err := workflowDraft.WithContext(ctx).Where(workflowDraft.ID.Eq(workflowID)).First()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	commitID, err := r.IDGenerator.GenID(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	var copiedWorkflowName string
 | |
| 	if policy.ShouldModifyWorkflowName {
 | |
| 		copiedWorkflowRedisKey := fmt.Sprintf("%s:%d:%d", copyWorkflowRedisKeyPrefix, workflowID, ctxutil.MustGetUIDFromCtx(ctx))
 | |
| 		copiedNameSuffix, err := r.redis.Incr(ctx, copiedWorkflowRedisKey).Result()
 | |
| 		if err != nil {
 | |
| 			return nil, vo.WrapError(errno.ErrRedisError, err)
 | |
| 		}
 | |
| 		err = r.redis.Expire(ctx, copiedWorkflowRedisKey, copyWorkflowRedisKeyExpireInterval).Err()
 | |
| 		if err != nil {
 | |
| 			logs.Warnf("failed to set the rediskey %v expiration time, err=%v", copiedWorkflowRedisKey, err)
 | |
| 		}
 | |
| 		copiedWorkflowName = fmt.Sprintf("%s_%d", wfMeta.Name, copiedNameSuffix)
 | |
| 	} else {
 | |
| 		copiedWorkflowName = wfMeta.Name
 | |
| 	}
 | |
| 
 | |
| 	err = r.query.Transaction(func(tx *query.Query) error {
 | |
| 		wfMeta.Name = copiedWorkflowName
 | |
| 		wfMeta.SourceID = workflowID
 | |
| 		wfMeta.Status = 0
 | |
| 		wfMeta.ID = copiedID
 | |
| 		wfMeta.CreatedAt = 0
 | |
| 		wfMeta.UpdatedAt = 0
 | |
| 		wfMeta.LatestVersion = ""
 | |
| 		if policy.TargetSpaceID != nil {
 | |
| 			wfMeta.SpaceID = *policy.TargetSpaceID
 | |
| 		}
 | |
| 		if policy.TargetAppID != nil {
 | |
| 			wfMeta.AppID = *policy.TargetAppID
 | |
| 		}
 | |
| 		wfMeta.CreatorID = ctxutil.MustGetUIDFromCtx(ctx)
 | |
| 		err = workflowMeta.WithContext(ctx).Create(wfMeta)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		wfDraft.ID = copiedID
 | |
| 		// copy workflow are treated as modified and not tested run
 | |
| 		wfDraft.TestRunSuccess = false
 | |
| 		wfDraft.Modified = true
 | |
| 		wfDraft.UpdatedAt = 0
 | |
| 		wfDraft.CommitID = strconv.FormatInt(commitID, 10)
 | |
| 		if policy.ModifiedCanvasSchema != nil {
 | |
| 			wfDraft.Canvas = *policy.ModifiedCanvasSchema
 | |
| 		}
 | |
| 		err = workflowDraft.WithContext(ctx).Create(wfDraft)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		return nil
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	copiedWorkflow = &entity.Workflow{
 | |
| 		ID:       copiedID,
 | |
| 		CommitID: wfDraft.CommitID,
 | |
| 		Meta: &vo.Meta{
 | |
| 			SpaceID:   wfMeta.SpaceID,
 | |
| 			Name:      wfMeta.Name,
 | |
| 			CreatorID: wfMeta.CreatorID,
 | |
| 			IconURI:   wfMeta.IconURI,
 | |
| 			Desc:      wfMeta.Description,
 | |
| 			AppID:     ternary.IFElse(wfMeta.AppID == 0, (*int64)(nil), ptr.Of(wfMeta.AppID)),
 | |
| 			Mode:      workflowModel.WorkflowMode(wfMeta.Mode),
 | |
| 		},
 | |
| 		CanvasInfo: &vo.CanvasInfo{
 | |
| 			Canvas:          wfDraft.Canvas,
 | |
| 			InputParamsStr:  wfDraft.InputParams,
 | |
| 			OutputParamsStr: wfDraft.OutputParams,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	return copiedWorkflow, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) GetDraftWorkflowsByAppID(ctx context.Context, AppID int64) (
 | |
| 	_ map[int64]*vo.DraftInfo, _ map[int64]string, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			err = vo.WrapIfNeeded(errno.ErrDatabaseError, err)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	var (
 | |
| 		workflowMeta  = r.query.WorkflowMeta
 | |
| 		workflowDraft = r.query.WorkflowDraft
 | |
| 	)
 | |
| 
 | |
| 	wfMetas, err := workflowMeta.WithContext(ctx).Where(workflowMeta.AppID.Eq(AppID)).Find()
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	draftIDs := slices.Transform(wfMetas, func(a *model.WorkflowMeta) int64 {
 | |
| 		return a.ID
 | |
| 	})
 | |
| 
 | |
| 	wfDrafts, err := workflowDraft.WithContext(ctx).Where(workflowDraft.ID.In(draftIDs...)).Find()
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	result := make(map[int64]*vo.DraftInfo, len(wfDrafts))
 | |
| 	for _, d := range wfDrafts {
 | |
| 		result[d.ID] = &vo.DraftInfo{
 | |
| 			Canvas:          d.Canvas,
 | |
| 			InputParamsStr:  d.InputParams,
 | |
| 			OutputParamsStr: d.OutputParams,
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	wid2Named := slices.ToMap(wfMetas, func(e *model.WorkflowMeta) (int64, string) {
 | |
| 		return e.ID, e.Name
 | |
| 	})
 | |
| 	return result, wid2Named, nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) BatchCreateConnectorWorkflowVersion(ctx context.Context, appID, connectorID int64, workflowIDs []int64, version string) error {
 | |
| 	objects := make([]*model.ConnectorWorkflowVersion, 0, len(workflowIDs))
 | |
| 	for idx := range workflowIDs {
 | |
| 		workflowID := workflowIDs[idx]
 | |
| 		objects = append(objects, &model.ConnectorWorkflowVersion{
 | |
| 			AppID:       appID,
 | |
| 			ConnectorID: connectorID,
 | |
| 			Version:     version,
 | |
| 			WorkflowID:  workflowID,
 | |
| 		})
 | |
| 	}
 | |
| 	err := r.query.ConnectorWorkflowVersion.WithContext(ctx).CreateInBatches(objects, batchCreateSize)
 | |
| 	if err != nil {
 | |
| 		return vo.WrapError(errno.ErrDatabaseError, err)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) GetKnowledgeRecallChatModel() cm.BaseChatModel {
 | |
| 	return r.builtinModel
 | |
| }
 | |
| 
 | |
| func (r *RepositoryImpl) GetObjectUrl(ctx context.Context, objectKey string, opts ...storage.GetOptFn) (string, error) {
 | |
| 	return r.tos.GetObjectUrl(ctx, objectKey, opts...)
 | |
| }
 | |
| 
 | |
| func filterDisabledAPIParameters(parametersCfg []*workflow3.APIParameter, m map[string]any) map[string]any {
 | |
| 	result := make(map[string]any, len(m))
 | |
| 	responseParameterMap := slices.ToMap(parametersCfg, func(p *workflow3.APIParameter) (string, *workflow3.APIParameter) {
 | |
| 		return p.Name, p
 | |
| 	})
 | |
| 	for key, value := range m {
 | |
| 		if parameter, ok := responseParameterMap[key]; ok {
 | |
| 			if parameter.LocalDisable {
 | |
| 				continue
 | |
| 			}
 | |
| 			if parameter.Type == workflow3.ParameterType_Object && len(parameter.SubParameters) > 0 {
 | |
| 				val := filterDisabledAPIParameters(parameter.SubParameters, value.(map[string]interface{}))
 | |
| 				result[key] = val
 | |
| 			} else {
 | |
| 				result[key] = value
 | |
| 			}
 | |
| 		} else {
 | |
| 			result[key] = value
 | |
| 		}
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func transformDefaultValue(value string, p *workflow3.APIParameter) (any, error) {
 | |
| 	switch p.Type {
 | |
| 	default:
 | |
| 		return value, nil
 | |
| 	case workflow3.ParameterType_String:
 | |
| 		return value, nil
 | |
| 	case workflow3.ParameterType_Object:
 | |
| 		ret := make(map[string]any)
 | |
| 		err := sonic.UnmarshalString(value, &ret)
 | |
| 		if err != nil {
 | |
| 			return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 		}
 | |
| 		return ret, nil
 | |
| 	case workflow3.ParameterType_Bool:
 | |
| 		b, err := strconv.ParseBool(value)
 | |
| 		if err != nil {
 | |
| 			return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 		}
 | |
| 		return b, nil
 | |
| 	case workflow3.ParameterType_Number:
 | |
| 		f, err := strconv.ParseFloat(value, 64)
 | |
| 		if err != nil {
 | |
| 			return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 		}
 | |
| 		return f, nil
 | |
| 	case workflow3.ParameterType_Integer:
 | |
| 		i, err := strconv.ParseInt(value, 10, 64)
 | |
| 		if err != nil {
 | |
| 			return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 		}
 | |
| 		return i, nil
 | |
| 	case workflow3.ParameterType_Array:
 | |
| 		ret := make([]any, 0)
 | |
| 		err := sonic.UnmarshalString(value, &ret)
 | |
| 		if err != nil {
 | |
| 			return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
 | |
| 		}
 | |
| 		return ret, nil
 | |
| 	}
 | |
| }
 |