383 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			383 lines
		
	
	
		
			12 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"
 | |
| 	"sort"
 | |
| 
 | |
| 	model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
 | |
| 	searchModel "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/search"
 | |
| 	pluginCommon "github.com/coze-dev/coze-studio/backend/api/model/plugin_develop/common"
 | |
| 	resCommon "github.com/coze-dev/coze-studio/backend/api/model/resource/common"
 | |
| 	crosssearch "github.com/coze-dev/coze-studio/backend/crossdomain/contract/search"
 | |
| 	pluginConf "github.com/coze-dev/coze-studio/backend/domain/plugin/conf"
 | |
| 	"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) GetOnlinePlugin(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, err error) {
 | |
| 	pl, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, pluginID)
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", pluginID)
 | |
| 	}
 | |
| 	if !exist {
 | |
| 		return nil, errorx.New(errno.ErrPluginRecordNotFound)
 | |
| 	}
 | |
| 
 | |
| 	return pl, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) MGetOnlinePlugins(ctx context.Context, pluginIDs []int64) (plugins []*entity.PluginInfo, err error) {
 | |
| 	plugins, err = p.pluginRepo.MGetOnlinePlugins(ctx, pluginIDs)
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "MGetOnlinePlugins failed, pluginIDs=%v", pluginIDs)
 | |
| 	}
 | |
| 
 | |
| 	res := make([]*model.PluginInfo, 0, len(plugins))
 | |
| 	for _, pl := range plugins {
 | |
| 		res = append(res, pl.PluginInfo)
 | |
| 	}
 | |
| 
 | |
| 	return plugins, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) GetOnlineTool(ctx context.Context, toolID int64) (tool *entity.ToolInfo, err error) {
 | |
| 	tool, exist, err := p.toolRepo.GetOnlineTool(ctx, toolID)
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "GetOnlineTool failed, toolID=%d", toolID)
 | |
| 	}
 | |
| 	if !exist {
 | |
| 		return nil, errorx.New(errno.ErrPluginRecordNotFound)
 | |
| 	}
 | |
| 
 | |
| 	return tool, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) MGetOnlineTools(ctx context.Context, toolIDs []int64) (tools []*entity.ToolInfo, err error) {
 | |
| 	tools, err = p.toolRepo.MGetOnlineTools(ctx, toolIDs)
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "MGetOnlineTools failed, toolIDs=%v", toolIDs)
 | |
| 	}
 | |
| 
 | |
| 	return tools, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) MGetVersionTools(ctx context.Context, versionTools []entity.VersionTool) (tools []*entity.ToolInfo, err error) {
 | |
| 	tools, err = p.toolRepo.MGetVersionTools(ctx, versionTools)
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "MGetVersionTools failed, versionTools=%v", versionTools)
 | |
| 	}
 | |
| 
 | |
| 	return tools, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) ListPluginProducts(ctx context.Context, req *ListPluginProductsRequest) (resp *ListPluginProductsResponse, err error) {
 | |
| 	plugins := slices.Transform(pluginConf.GetAllPluginProducts(), func(p *pluginConf.PluginInfo) *entity.PluginInfo {
 | |
| 		return entity.NewPluginInfo(p.Info)
 | |
| 	})
 | |
| 	sort.Slice(plugins, func(i, j int) bool {
 | |
| 		return plugins[i].GetRefProductID() < plugins[j].GetRefProductID()
 | |
| 	})
 | |
| 
 | |
| 	return &ListPluginProductsResponse{
 | |
| 		Plugins: plugins,
 | |
| 		Total:   int64(len(plugins)),
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) GetPluginProductAllTools(ctx context.Context, pluginID int64) (tools []*entity.ToolInfo, err error) {
 | |
| 	res, err := p.toolRepo.GetPluginAllOnlineTools(ctx, pluginID)
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "GetPluginAllOnlineTools failed, pluginID=%d", pluginID)
 | |
| 	}
 | |
| 
 | |
| 	return res, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) DeleteAPPAllPlugins(ctx context.Context, appID int64) (pluginIDs []int64, err error) {
 | |
| 	return p.pluginRepo.DeleteAPPAllPlugins(ctx, appID)
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) GetAPPAllPlugins(ctx context.Context, appID int64) (plugins []*entity.PluginInfo, err error) {
 | |
| 	plugins, err = p.pluginRepo.GetAPPAllDraftPlugins(ctx, appID)
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "GetAPPAllDraftPlugins failed, appID=%d", appID)
 | |
| 	}
 | |
| 
 | |
| 	return plugins, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) MGetVersionPlugins(ctx context.Context, versionPlugins []entity.VersionPlugin) (plugins []*entity.PluginInfo, err error) {
 | |
| 	plugins, err = p.pluginRepo.MGetVersionPlugins(ctx, versionPlugins)
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "MGetVersionPlugins failed, versionPlugins=%v", versionPlugins)
 | |
| 	}
 | |
| 
 | |
| 	return plugins, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) ListCustomOnlinePlugins(ctx context.Context, spaceID int64, pageInfo entity.PageInfo) (plugins []*entity.PluginInfo, total int64, err error) {
 | |
| 	if pageInfo.Name == nil || *pageInfo.Name == "" {
 | |
| 		plugins, total, err = p.pluginRepo.ListCustomOnlinePlugins(ctx, spaceID, pageInfo)
 | |
| 		if err != nil {
 | |
| 			return nil, 0, errorx.Wrapf(err, "ListCustomOnlinePlugins failed, spaceID=%d", spaceID)
 | |
| 		}
 | |
| 		return plugins, total, nil
 | |
| 	}
 | |
| 
 | |
| 	res, err := crosssearch.DefaultSVC().SearchResources(ctx, &searchModel.SearchResourcesRequest{
 | |
| 		SpaceID:  spaceID,
 | |
| 		Name:     *pageInfo.Name,
 | |
| 		OrderAsc: false,
 | |
| 		ResTypeFilter: []resCommon.ResType{
 | |
| 			resCommon.ResType_Plugin,
 | |
| 		},
 | |
| 		OrderFiledName: func() string {
 | |
| 			if pageInfo.SortBy == nil || *pageInfo.SortBy != entity.SortByCreatedAt {
 | |
| 				return searchModel.FieldOfUpdateTime
 | |
| 			}
 | |
| 			return searchModel.FieldOfCreateTime
 | |
| 		}(),
 | |
| 		Page:  ptr.Of(int32(pageInfo.Page)),
 | |
| 		Limit: int32(pageInfo.Size),
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, 0, errorx.Wrapf(err, "SearchResources failed, spaceID=%d", spaceID)
 | |
| 	}
 | |
| 
 | |
| 	plugins = make([]*entity.PluginInfo, 0, len(res.Data))
 | |
| 	for _, pl := range res.Data {
 | |
| 		draftPlugin, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, pl.ResID)
 | |
| 		if err != nil {
 | |
| 			return nil, 0, errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", pl.ResID)
 | |
| 		}
 | |
| 		if !exist {
 | |
| 			logs.CtxWarnf(ctx, "online plugin not exist, pluginID=%d", pl.ResID)
 | |
| 			continue
 | |
| 		}
 | |
| 		plugins = append(plugins, draftPlugin)
 | |
| 	}
 | |
| 
 | |
| 	if res.TotalHits != nil {
 | |
| 		total = *res.TotalHits
 | |
| 	}
 | |
| 
 | |
| 	return plugins, total, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) MGetPluginLatestVersion(ctx context.Context, pluginIDs []int64) (resp *MGetPluginLatestVersionResponse, err error) {
 | |
| 	plugins, err := p.pluginRepo.MGetOnlinePlugins(ctx, pluginIDs,
 | |
| 		repository.WithPluginID(),
 | |
| 		repository.WithPluginVersion())
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "MGetOnlinePlugins failed, pluginIDs=%v", pluginIDs)
 | |
| 	}
 | |
| 
 | |
| 	versions := make(map[int64]string, len(plugins))
 | |
| 	for _, pl := range plugins {
 | |
| 		versions[pl.ID] = pl.GetVersion()
 | |
| 	}
 | |
| 
 | |
| 	resp = &MGetPluginLatestVersionResponse{
 | |
| 		Versions: versions,
 | |
| 	}
 | |
| 
 | |
| 	return resp, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) CopyPlugin(ctx context.Context, req *CopyPluginRequest) (resp *CopyPluginResponse, err error) {
 | |
| 	err = p.checkCanCopyPlugin(ctx, req.PluginID, req.CopyScene)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	plugin, tools, err := p.getCopySourcePluginAndTools(ctx, req.PluginID, req.CopyScene)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	p.changePluginAndToolsInfoForCopy(req, plugin, tools)
 | |
| 
 | |
| 	toolMap := make(map[int64]*entity.ToolInfo, len(tools))
 | |
| 	for _, tool := range tools {
 | |
| 		toolMap[tool.ID] = tool
 | |
| 	}
 | |
| 
 | |
| 	plugin, tools, err = p.pluginRepo.CopyPlugin(ctx, &repository.CopyPluginRequest{
 | |
| 		Plugin: plugin,
 | |
| 		Tools:  tools,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "CopyPlugin failed, pluginID=%d", req.PluginID)
 | |
| 	}
 | |
| 
 | |
| 	resp = &CopyPluginResponse{
 | |
| 		Plugin: plugin,
 | |
| 		Tools:  toolMap,
 | |
| 	}
 | |
| 
 | |
| 	return resp, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) changePluginAndToolsInfoForCopy(req *CopyPluginRequest, plugin *entity.PluginInfo, tools []*entity.ToolInfo) {
 | |
| 	plugin.Version = nil
 | |
| 	plugin.VersionDesc = nil
 | |
| 
 | |
| 	plugin.DeveloperID = req.UserID
 | |
| 
 | |
| 	if req.CopyScene != model.CopySceneOfAPPDuplicate {
 | |
| 		plugin.SetName(fmt.Sprintf("%s_copy", plugin.GetName()))
 | |
| 	}
 | |
| 
 | |
| 	if req.CopyScene == model.CopySceneOfToLibrary {
 | |
| 		const (
 | |
| 			defaultVersion     = "v0.0.1"
 | |
| 			defaultVersionDesc = "copy to library"
 | |
| 		)
 | |
| 
 | |
| 		plugin.APPID = nil
 | |
| 		plugin.Version = ptr.Of(defaultVersion)
 | |
| 		plugin.VersionDesc = ptr.Of(defaultVersionDesc)
 | |
| 
 | |
| 		for _, tool := range tools {
 | |
| 			tool.Version = ptr.Of(defaultVersion)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if req.CopyScene == model.CopySceneOfToAPP {
 | |
| 		plugin.APPID = req.TargetAPPID
 | |
| 
 | |
| 		for _, tool := range tools {
 | |
| 			tool.DebugStatus = ptr.Of(pluginCommon.APIDebugStatus_DebugPassed)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if req.CopyScene == model.CopySceneOfAPPDuplicate {
 | |
| 		plugin.APPID = req.TargetAPPID
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) checkCanCopyPlugin(ctx context.Context, pluginID int64, scene model.CopyScene) (err error) {
 | |
| 	switch scene {
 | |
| 	case model.CopySceneOfToAPP, model.CopySceneOfDuplicate, model.CopySceneOfAPPDuplicate:
 | |
| 		return nil
 | |
| 	case model.CopySceneOfToLibrary:
 | |
| 		return p.checkToolsDebugStatus(ctx, pluginID)
 | |
| 	default:
 | |
| 		return fmt.Errorf("unsupported copy scene '%s'", scene)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) getCopySourcePluginAndTools(ctx context.Context, pluginID int64, scene model.CopyScene) (plugin *entity.PluginInfo, tools []*entity.ToolInfo, err error) {
 | |
| 	switch scene {
 | |
| 	case model.CopySceneOfToAPP:
 | |
| 		return p.getOnlinePluginAndTools(ctx, pluginID)
 | |
| 	case model.CopySceneOfToLibrary, model.CopySceneOfDuplicate, model.CopySceneOfAPPDuplicate:
 | |
| 		return p.getDraftPluginAndTools(ctx, pluginID)
 | |
| 	default:
 | |
| 		return nil, nil, fmt.Errorf("unsupported copy scene '%s'", scene)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) getOnlinePluginAndTools(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, tools []*entity.ToolInfo, err error) {
 | |
| 	onlinePlugin, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, pluginID)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	if !exist {
 | |
| 		return nil, nil, errorx.New(errno.ErrPluginRecordNotFound)
 | |
| 	}
 | |
| 
 | |
| 	onlineTools, err := p.toolRepo.GetPluginAllOnlineTools(ctx, pluginID)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 
 | |
| 	return onlinePlugin, onlineTools, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) getDraftPluginAndTools(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, tools []*entity.ToolInfo, err error) {
 | |
| 	draftPlugin, exist, err := p.pluginRepo.GetDraftPlugin(ctx, pluginID)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	if !exist {
 | |
| 		return nil, nil, errorx.New(errno.ErrPluginRecordNotFound)
 | |
| 	}
 | |
| 
 | |
| 	draftTools, err := p.toolRepo.GetPluginAllDraftTools(ctx, pluginID)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 
 | |
| 	return draftPlugin, draftTools, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) MoveAPPPluginToLibrary(ctx context.Context, pluginID int64) (draftPlugin *entity.PluginInfo, err error) {
 | |
| 	draftPlugin, exist, err := p.pluginRepo.GetDraftPlugin(ctx, pluginID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if !exist {
 | |
| 		return nil, errorx.New(errno.ErrPluginRecordNotFound)
 | |
| 	}
 | |
| 
 | |
| 	err = p.checkToolsDebugStatus(ctx, pluginID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	draftTools, err := p.toolRepo.GetPluginAllDraftTools(ctx, pluginID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	p.changePluginAndToolsInfoForMove(draftPlugin, draftTools)
 | |
| 
 | |
| 	err = p.pluginRepo.MoveAPPPluginToLibrary(ctx, draftPlugin, draftTools)
 | |
| 	if err != nil {
 | |
| 		return nil, errorx.Wrapf(err, "MoveAPPPluginToLibrary failed, pluginID=%d", pluginID)
 | |
| 	}
 | |
| 
 | |
| 	return draftPlugin, nil
 | |
| }
 | |
| 
 | |
| func (p *pluginServiceImpl) changePluginAndToolsInfoForMove(plugin *entity.PluginInfo,
 | |
| 	tools []*entity.ToolInfo) {
 | |
| 
 | |
| 	const (
 | |
| 		defaultVersion     = "v0.0.1"
 | |
| 		defaultVersionDesc = "move to library"
 | |
| 	)
 | |
| 
 | |
| 	plugin.Version = ptr.Of(defaultVersion)
 | |
| 	plugin.VersionDesc = ptr.Of(defaultVersionDesc)
 | |
| 
 | |
| 	for _, tool := range tools {
 | |
| 		tool.Version = ptr.Of(defaultVersion)
 | |
| 	}
 | |
| 
 | |
| 	plugin.APPID = nil
 | |
| }
 |