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"
 | 
						|
	"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossplugin"
 | 
						|
	"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossworkflow"
 | 
						|
	"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
 | 
						|
}
 |