235 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			7.4 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"
 | 
						|
	"fmt"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"golang.org/x/mod/semver"
 | 
						|
 | 
						|
	model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
 | 
						|
	common "github.com/coze-dev/coze-studio/backend/api/model/plugin_develop_common"
 | 
						|
	"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
 | 
						|
	"github.com/coze-dev/coze-studio/backend/domain/plugin/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"
 | 
						|
	"github.com/coze-dev/coze-studio/backend/types/errno"
 | 
						|
)
 | 
						|
 | 
						|
func (p *pluginServiceImpl) GetPluginNextVersion(ctx context.Context, pluginID int64) (version string, err error) {
 | 
						|
	const defaultVersion = "v1.0.0"
 | 
						|
 | 
						|
	pl, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, pluginID)
 | 
						|
	if err != nil {
 | 
						|
		return "", errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", pluginID)
 | 
						|
	}
 | 
						|
	if !exist {
 | 
						|
		return defaultVersion, nil
 | 
						|
	}
 | 
						|
 | 
						|
	parts := strings.Split(pl.GetVersion(), ".") // Remove the 'v' and split
 | 
						|
	if len(parts) < 3 {
 | 
						|
		logs.CtxWarnf(ctx, "invalid version format '%s'", pl.GetVersion())
 | 
						|
		return defaultVersion, nil
 | 
						|
	}
 | 
						|
 | 
						|
	patch, err := strconv.ParseInt(parts[2], 10, 64)
 | 
						|
	if err != nil {
 | 
						|
		logs.CtxWarnf(ctx, "invalid version format '%s'", pl.GetVersion())
 | 
						|
		return defaultVersion, nil
 | 
						|
	}
 | 
						|
 | 
						|
	parts[2] = strconv.FormatInt(patch+1, 10)
 | 
						|
	nextVersion := strings.Join(parts, ".")
 | 
						|
 | 
						|
	return nextVersion, nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *pluginServiceImpl) PublishPlugin(ctx context.Context, req *PublishPluginRequest) (err error) {
 | 
						|
	draftPlugin, exist, err := p.pluginRepo.GetDraftPlugin(ctx, req.PluginID)
 | 
						|
	if err != nil {
 | 
						|
		return errorx.Wrapf(err, "GetDraftPlugin failed, pluginID=%d", req.PluginID)
 | 
						|
	}
 | 
						|
	if !exist {
 | 
						|
		return errorx.New(errno.ErrPluginRecordNotFound)
 | 
						|
	}
 | 
						|
 | 
						|
	err = p.checkToolsDebugStatus(ctx, req.PluginID)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	onlinePlugin, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, req.PluginID)
 | 
						|
	if err != nil {
 | 
						|
		return errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", req.PluginID)
 | 
						|
	}
 | 
						|
	if exist && onlinePlugin.Version != nil {
 | 
						|
		if semver.Compare(req.Version, *onlinePlugin.Version) != 1 {
 | 
						|
			return errorx.New(errno.ErrPluginInvalidParamCode,
 | 
						|
				errorx.KVf(errno.PluginMsgKey, "version must be greater than the online version '%s' and format like 'v1.0.0'",
 | 
						|
					*onlinePlugin.Version))
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	draftPlugin.Version = &req.Version
 | 
						|
	draftPlugin.VersionDesc = &req.VersionDesc
 | 
						|
 | 
						|
	err = p.pluginRepo.PublishPlugin(ctx, draftPlugin)
 | 
						|
	if err != nil {
 | 
						|
		return errorx.Wrapf(err, "PublishPlugin failed, pluginID=%d", req.PluginID)
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *pluginServiceImpl) PublishAPPPlugins(ctx context.Context, req *PublishAPPPluginsRequest) (resp *PublishAPPPluginsResponse, err error) {
 | 
						|
	resp = &PublishAPPPluginsResponse{}
 | 
						|
 | 
						|
	draftPlugins, err := p.pluginRepo.GetAPPAllDraftPlugins(ctx, req.APPID)
 | 
						|
	if err != nil {
 | 
						|
		return nil, errorx.Wrapf(err, "GetAPPAllDraftPlugins failed, appID=%d", req.APPID)
 | 
						|
	}
 | 
						|
 | 
						|
	failedPluginIDs, err := p.checkCanPublishAPPPlugins(ctx, req.Version, draftPlugins)
 | 
						|
	if err != nil {
 | 
						|
		return nil, errorx.Wrapf(err, "checkCanPublishAPPPlugins failed, appID=%d, appVerion=%s", req.APPID, req.Version)
 | 
						|
	}
 | 
						|
 | 
						|
	for _, draftPlugin := range draftPlugins {
 | 
						|
		draftPlugin.Version = &req.Version
 | 
						|
		draftPlugin.VersionDesc = ptr.Of(fmt.Sprintf("publish %s", req.Version))
 | 
						|
		resp.AllDraftPlugins = append(resp.AllDraftPlugins, draftPlugin.PluginInfo)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(failedPluginIDs) > 0 {
 | 
						|
		draftPluginMap := slices.ToMap(draftPlugins, func(plugin *entity.PluginInfo) (int64, *entity.PluginInfo) {
 | 
						|
			return plugin.ID, plugin
 | 
						|
		})
 | 
						|
 | 
						|
		failedPlugins := make([]*entity.PluginInfo, 0, len(failedPluginIDs))
 | 
						|
		for _, failedPluginID := range failedPluginIDs {
 | 
						|
			failedPlugins = append(failedPlugins, draftPluginMap[failedPluginID])
 | 
						|
		}
 | 
						|
		for _, failedPlugin := range failedPlugins {
 | 
						|
			resp.FailedPlugins = append(resp.FailedPlugins, failedPlugin.PluginInfo)
 | 
						|
		}
 | 
						|
 | 
						|
		return resp, nil
 | 
						|
	}
 | 
						|
 | 
						|
	err = p.pluginRepo.PublishPlugins(ctx, draftPlugins)
 | 
						|
	if err != nil {
 | 
						|
		return nil, errorx.Wrapf(err, "PublishPlugins failed, appID=%d", req.APPID)
 | 
						|
	}
 | 
						|
 | 
						|
	return resp, nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *pluginServiceImpl) checkCanPublishAPPPlugins(ctx context.Context, version string, draftPlugins []*entity.PluginInfo) (failedPluginIDs []int64, err error) {
 | 
						|
	failedPluginIDs = make([]int64, 0, len(draftPlugins))
 | 
						|
 | 
						|
	draftPluginIDs := slices.Transform(draftPlugins, func(plugin *entity.PluginInfo) int64 {
 | 
						|
		return plugin.ID
 | 
						|
	})
 | 
						|
 | 
						|
	// 1. check version
 | 
						|
	onlinePlugins, err := p.pluginRepo.MGetOnlinePlugins(ctx, draftPluginIDs,
 | 
						|
		repository.WithPluginID(),
 | 
						|
		repository.WithPluginVersion())
 | 
						|
	if err != nil {
 | 
						|
		return nil, errorx.Wrapf(err, "MGetOnlinePlugins failed, pluginIDs=%v", draftPluginIDs)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(onlinePlugins) > 0 {
 | 
						|
		for _, onlinePlugin := range onlinePlugins {
 | 
						|
			if onlinePlugin.Version == nil {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			if semver.Compare(version, *onlinePlugin.Version) != 1 {
 | 
						|
				failedPluginIDs = append(failedPluginIDs, onlinePlugin.ID)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if len(failedPluginIDs) > 0 {
 | 
						|
			logs.CtxErrorf(ctx, "invalid version of plugins '%v'", failedPluginIDs)
 | 
						|
			return failedPluginIDs, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// 2. check debug status
 | 
						|
	for _, draftPlugin := range draftPlugins {
 | 
						|
		err = p.checkToolsDebugStatus(ctx, draftPlugin.ID)
 | 
						|
		if err != nil {
 | 
						|
			failedPluginIDs = append(failedPluginIDs, draftPlugin.ID)
 | 
						|
			logs.CtxErrorf(ctx, "checkToolsDebugStatus failed, pluginID=%d, err=%s", draftPlugin.ID, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(failedPluginIDs) > 0 {
 | 
						|
		return failedPluginIDs, nil
 | 
						|
	}
 | 
						|
 | 
						|
	return failedPluginIDs, nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *pluginServiceImpl) checkToolsDebugStatus(ctx context.Context, pluginID int64) (err error) {
 | 
						|
	res, err := p.toolRepo.GetPluginAllDraftTools(ctx, pluginID,
 | 
						|
		repository.WithToolID(),
 | 
						|
		repository.WithToolDebugStatus(),
 | 
						|
		repository.WithToolActivatedStatus(),
 | 
						|
	)
 | 
						|
	if err != nil {
 | 
						|
		return errorx.Wrapf(err, "GetPluginAllDraftTools failed, pluginID=%d", pluginID)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(res) == 0 {
 | 
						|
		return errorx.New(errno.ErrPluginToolsCheckFailed, errorx.KVf(errno.PluginMsgKey,
 | 
						|
			"at least one activated tool is required in plugin"))
 | 
						|
	}
 | 
						|
 | 
						|
	activatedTools := make([]*entity.ToolInfo, 0, len(res))
 | 
						|
	for _, tool := range res {
 | 
						|
		if tool.GetActivatedStatus() == model.DeactivateTool {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		activatedTools = append(activatedTools, tool)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(activatedTools) == 0 {
 | 
						|
		return errorx.New(errno.ErrPluginToolsCheckFailed, errorx.KVf(errno.PluginMsgKey,
 | 
						|
			"at least one activated tool is required in plugin"))
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tool := range activatedTools {
 | 
						|
		if tool.GetDebugStatus() != common.APIDebugStatus_DebugWaiting {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		return errorx.New(errno.ErrPluginToolsCheckFailed, errorx.KVf(errno.PluginMsgKey,
 | 
						|
			"tools in plugin have not debugged yet"))
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *pluginServiceImpl) CheckPluginToolsDebugStatus(ctx context.Context, pluginID int64) (err error) {
 | 
						|
	return p.checkToolsDebugStatus(ctx, pluginID)
 | 
						|
}
 |