263 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			263 lines
		
	
	
		
			8.7 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 service
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
 | |
| 	resourceCommon "github.com/coze-dev/coze-studio/backend/api/model/resource/common"
 | |
| 	crossplugin "github.com/coze-dev/coze-studio/backend/crossdomain/contract/plugin"
 | |
| 	crossworkflow "github.com/coze-dev/coze-studio/backend/crossdomain/contract/workflow"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/app/entity"
 | |
| 	"github.com/coze-dev/coze-studio/backend/domain/app/repository"
 | |
| 	"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/logs"
 | |
| 	commonConsts "github.com/coze-dev/coze-studio/backend/types/consts"
 | |
| 	"github.com/coze-dev/coze-studio/backend/types/errno"
 | |
| )
 | |
| 
 | |
| func (a *appServiceImpl) PublishAPP(ctx context.Context, req *PublishAPPRequest) (resp *PublishAPPResponse, err error) {
 | |
| 	err = a.checkCanPublishAPP(ctx, req)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	recordID, err := a.createPublishVersion(ctx, req)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	success, err := a.publishByConnectors(ctx, recordID, req)
 | |
| 	if err != nil {
 | |
| 		logs.CtxErrorf(ctx, "publish by connectors failed, recordID=%d, err=%v", recordID, err)
 | |
| 	}
 | |
| 
 | |
| 	resp = &PublishAPPResponse{
 | |
| 		Success:         success,
 | |
| 		PublishRecordID: recordID,
 | |
| 	}
 | |
| 
 | |
| 	return resp, nil
 | |
| }
 | |
| 
 | |
| func (a *appServiceImpl) publishByConnectors(ctx context.Context, recordID int64, req *PublishAPPRequest) (success bool, err error) {
 | |
| 	defer func() {
 | |
| 		if err != nil {
 | |
| 			updateErr := a.APPRepo.UpdateAPPPublishStatus(ctx, &repository.UpdateAPPPublishStatusRequest{
 | |
| 				RecordID:      recordID,
 | |
| 				PublishStatus: entity.PublishStatusOfPackFailed,
 | |
| 			})
 | |
| 			if updateErr != nil {
 | |
| 				logs.CtxErrorf(ctx, "UpdateAPPPublishStatus failed, recordID=%d, err=%v", recordID, updateErr)
 | |
| 			}
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	connectorIDs := make([]int64, 0, len(req.ConnectorPublishConfigs))
 | |
| 	for cid := range req.ConnectorPublishConfigs {
 | |
| 		connectorIDs = append(connectorIDs, cid)
 | |
| 	}
 | |
| 	failedResources, err := a.packResources(ctx, req.APPID, req.Version, connectorIDs)
 | |
| 	if err != nil {
 | |
| 		return false, err
 | |
| 	}
 | |
| 	if len(failedResources) > 0 {
 | |
| 		logs.CtxWarnf(ctx, "packResources failed, recordID=%d, len=%d", recordID, len(failedResources))
 | |
| 
 | |
| 		processErr := a.packResourcesFailedPostProcess(ctx, recordID, failedResources)
 | |
| 		if processErr != nil {
 | |
| 			logs.CtxErrorf(ctx, "packResourcesFailedPostProcess failed, recordID=%d, err=%v", recordID, processErr)
 | |
| 		}
 | |
| 
 | |
| 		return false, nil
 | |
| 	}
 | |
| 
 | |
| 	for cid := range req.ConnectorPublishConfigs {
 | |
| 		switch cid {
 | |
| 		case commonConsts.APIConnectorID:
 | |
| 			updateSuccessErr := a.APPRepo.UpdateConnectorPublishStatus(ctx, recordID, entity.ConnectorPublishStatusOfSuccess)
 | |
| 			if updateSuccessErr == nil {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			logs.CtxErrorf(ctx, "failed to update connector '%d' publish status to success, err=%v", cid, updateSuccessErr)
 | |
| 
 | |
| 			updateFailedErr := a.APPRepo.UpdateAPPPublishStatus(ctx, &repository.UpdateAPPPublishStatusRequest{
 | |
| 				RecordID:      recordID,
 | |
| 				PublishStatus: entity.PublishStatusOfPackFailed,
 | |
| 			})
 | |
| 
 | |
| 			if updateFailedErr != nil {
 | |
| 				logs.CtxWarnf(ctx, "failed to update connector '%d' publish status to failed, err=%v", cid, updateFailedErr)
 | |
| 			}
 | |
| 
 | |
| 		default:
 | |
| 			continue
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	err = a.APPRepo.UpdateAPPPublishStatus(ctx, &repository.UpdateAPPPublishStatusRequest{
 | |
| 		RecordID:      recordID,
 | |
| 		PublishStatus: entity.PublishStatusOfPublishDone,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return false, errorx.Wrapf(err, "UpdateAPPPublishStatus failed, recordID=%d", recordID)
 | |
| 	}
 | |
| 
 | |
| 	return true, nil
 | |
| }
 | |
| 
 | |
| func (a *appServiceImpl) checkCanPublishAPP(ctx context.Context, req *PublishAPPRequest) (err error) {
 | |
| 	exist, err := a.APPRepo.CheckAPPVersionExist(ctx, req.APPID, req.Version)
 | |
| 	if err != nil {
 | |
| 		return errorx.Wrapf(err, "CheckAPPVersionExist failed, appID=%d, version=%s", req.APPID, req.Version)
 | |
| 	}
 | |
| 	if exist {
 | |
| 		return errorx.New(errno.ErrAppRecordNotFound)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (a *appServiceImpl) createPublishVersion(ctx context.Context, req *PublishAPPRequest) (recordID int64, err error) {
 | |
| 	draftAPP, exist, err := a.APPRepo.GetDraftAPP(ctx, req.APPID)
 | |
| 	if err != nil {
 | |
| 		return 0, errorx.Wrapf(err, "GetDraftAPP failed, appID=%d", req.APPID)
 | |
| 	}
 | |
| 	if !exist {
 | |
| 		return 0, errorx.New(errno.ErrAppRecordNotFound)
 | |
| 	}
 | |
| 
 | |
| 	draftAPP.PublishedAtMS = ptr.Of(time.Now().UnixMilli())
 | |
| 	draftAPP.Version = &req.Version
 | |
| 	draftAPP.VersionDesc = &req.VersionDesc
 | |
| 
 | |
| 	publishRecords := make([]*entity.ConnectorPublishRecord, 0, len(req.ConnectorPublishConfigs))
 | |
| 
 | |
| 	for cid, conf := range req.ConnectorPublishConfigs {
 | |
| 		publishRecords = append(publishRecords, &entity.ConnectorPublishRecord{
 | |
| 			ConnectorID:   cid,
 | |
| 			PublishStatus: entity.ConnectorPublishStatusOfDefault,
 | |
| 			PublishConfig: conf,
 | |
| 		})
 | |
| 		draftAPP.ConnectorIDs = append(draftAPP.ConnectorIDs, cid)
 | |
| 	}
 | |
| 
 | |
| 	recordID, err = a.APPRepo.CreateAPPPublishRecord(ctx, &entity.PublishRecord{
 | |
| 		APP:                     draftAPP,
 | |
| 		ConnectorPublishRecords: publishRecords,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return 0, errorx.Wrapf(err, "CreateAPPPublishRecord failed, appID=%d", req.APPID)
 | |
| 	}
 | |
| 
 | |
| 	return recordID, nil
 | |
| }
 | |
| 
 | |
| func (a *appServiceImpl) packResources(ctx context.Context, appID int64, version string, connectorIDs []int64) (failedResources []*entity.PackResourceFailedInfo, err error) {
 | |
| 	failedPlugins, allDraftPlugins, err := a.packPlugins(ctx, appID, version)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	workflowFailedInfoList, err := a.packWorkflows(ctx, appID, version,
 | |
| 		slices.Transform(allDraftPlugins, func(a *plugin.PluginInfo) int64 {
 | |
| 			return a.ID
 | |
| 		}), connectorIDs)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	length := len(failedPlugins) + len(workflowFailedInfoList)
 | |
| 	if length == 0 {
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 
 | |
| 	failedResources = append(failedResources, failedPlugins...)
 | |
| 	failedResources = append(failedResources, workflowFailedInfoList...)
 | |
| 
 | |
| 	return failedResources, nil
 | |
| }
 | |
| 
 | |
| func (a *appServiceImpl) packPlugins(ctx context.Context, appID int64, version string) (failedInfo []*entity.PackResourceFailedInfo, allDraftPlugins []*plugin.PluginInfo, err error) {
 | |
| 	res, err := crossplugin.DefaultSVC().PublishAPPPlugins(ctx, &plugin.PublishAPPPluginsRequest{
 | |
| 		APPID:   appID,
 | |
| 		Version: version,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, nil, errorx.Wrapf(err, "PublishAPPPlugins failed, appID=%d, version=%s", appID, version)
 | |
| 	}
 | |
| 
 | |
| 	failedInfo = make([]*entity.PackResourceFailedInfo, 0, len(res.FailedPlugins))
 | |
| 	for _, p := range res.FailedPlugins {
 | |
| 		failedInfo = append(failedInfo, &entity.PackResourceFailedInfo{
 | |
| 			ResID:   p.ID,
 | |
| 			ResType: resourceCommon.ResType_Plugin,
 | |
| 			ResName: p.GetName(),
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return failedInfo, res.AllDraftPlugins, nil
 | |
| 
 | |
| }
 | |
| 
 | |
| func (a *appServiceImpl) packWorkflows(ctx context.Context, appID int64, version string, allDraftPluginIDs []int64, connectorIDs []int64) (workflowFailedInfoList []*entity.PackResourceFailedInfo, err error) {
 | |
| 	issues, err := crossworkflow.DefaultSVC().ReleaseApplicationWorkflows(ctx, appID, &crossworkflow.ReleaseWorkflowConfig{
 | |
| 		Version:      version,
 | |
| 		PluginIDs:    allDraftPluginIDs,
 | |
| 		ConnectorIDs: connectorIDs,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "ReleaseApplicationWorkflows failed, appID=%d, version=%s", appID, version)
 | |
| 	}
 | |
| 
 | |
| 	if len(issues) == 0 {
 | |
| 		return workflowFailedInfoList, nil
 | |
| 	}
 | |
| 
 | |
| 	workflowFailedInfoList = make([]*entity.PackResourceFailedInfo, 0, len(issues))
 | |
| 	for _, issue := range issues {
 | |
| 		workflowFailedInfoList = append(workflowFailedInfoList, &entity.PackResourceFailedInfo{
 | |
| 			ResID:   issue.WorkflowID,
 | |
| 			ResType: resourceCommon.ResType_Workflow,
 | |
| 			ResName: issue.WorkflowName,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return workflowFailedInfoList, nil
 | |
| }
 | |
| 
 | |
| func (a *appServiceImpl) packResourcesFailedPostProcess(ctx context.Context, recordID int64, packFailedInfo []*entity.PackResourceFailedInfo) (err error) {
 | |
| 	publishFailedInfo := &entity.PublishRecordExtraInfo{
 | |
| 		PackFailedInfo: packFailedInfo,
 | |
| 	}
 | |
| 	err = a.APPRepo.UpdateAPPPublishStatus(ctx, &repository.UpdateAPPPublishStatusRequest{
 | |
| 		RecordID:               recordID,
 | |
| 		PublishStatus:          entity.PublishStatusOfPackFailed,
 | |
| 		PublishRecordExtraInfo: publishFailedInfo,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return errorx.Wrapf(err, "UpdateAPPPublishStatus failed, recordID=%d", recordID)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 |