Files
gva/server/mcp/gva_auto_generate.go
PiexlMax(奇淼 273d7bd50e V2.8.4Beta 极致融合AI编辑器 让开发速度更进一步 (#2060)
* fix(style): 修复 border 额外的 reset 导致 tailwind border 属性生效异常的问题

* feat: 添加错误预览组件并优化请求错误处理逻辑

* optimize: select and update necessary fields in `ChangePassword` method

- Simplify `ChangePassword` method signature by removing unnecessary return type.
- Use `Select()` to fetch only the necessary fields (`id` and `password`) from the database.
- Replace `Save()` with `Update()` for more efficient password update operation.

Note: use `Save(&user)` to update the whole user record, which will cover other unchanged fields as well, causing data inconsistency when data race conditions.

* feat(menu): 版本更新为2.8.4,给菜单增加按钮和参数的预制打包

* feat(menu): 恢复空白的配置文件

* Remove unused `SideMode` field from `ChangeUserInfo` struct

Remove unused and deprecated `SideMode` field from user request model.

* feat(automation): 增加可以自动生成CURD和续写方法的MCP

* fix(mcp): 确保始终返回目录结构信息

* fix(mcp): 当不需要创建模块时提前返回目录结构信息

* feat(automation): 增加可以自动生成CURD和续写方法的MCP

* feat(mcp): 添加GAG工具用户确认流程和自动字典创建功能

实现三步工作流程:分析、确认、执行
新增自动字典创建功能,当字段使用字典类型时自动检查并创建字典
添加用户确认机制,确保创建操作前获得用户明确确认

* feat(version): 新增版本管理功能,支持创建、导入、导出和下载版本数据

新增版本管理模块,包含以下功能:
1. 版本数据的增删改查
2. 版本创建功能,可选择关联菜单和API
3. 版本导入导出功能
4. 版本JSON数据下载
5. 相关前端页面和接口实现

* refactor(version): 简化版本管理删除逻辑并移除无用字段

移除版本管理中的状态、创建者、更新者和删除者字段
简化删除和批量删除方法的实现,去除事务和用户ID参数
更新自动生成配置的默认值说明

* feat(版本管理): 新增版本管理功能模块

* fix(menu): 修复递归创建菜单时关联数据未正确处理的问题

* feat(mcp): 添加预设计模块扫描功能以支持代码自动生成

在自动化模块分析器中添加对预设计模块的扫描功能,包括:
- 新增PredesignedModuleInfo结构体存储模块信息
- 实现scanPredesignedModules方法扫描plugin和model目录
- 在分析响应中添加predesignedModules字段
- 更新帮助文档说明预设计模块的使用方式

这些修改使系统能够识别并利用现有的预设计模块,提高代码生成效率并减少重复工作。

* feat(mcp): 新增API、菜单和字典生成工具并优化自动生成模块

* docs(mcp): 更新菜单和API创建工具的描述信息

* feat(mcp): 添加字典查询工具用于AI生成逻辑时了解可用字典选项

* feat: 在创建菜单/API/模块结果中添加权限分配提醒

为菜单创建、API创建和模块创建的结果消息添加权限分配提醒,帮助用户了解后续需要进行的权限配置步骤

* refactor(mcp): 统一使用WithBoolean替换WithBool并优化错误处理

* docs(mcp): 更新API创建工具的说明和错误处理日志

* feat(mcp): 添加插件意图检测功能并增强验证逻辑

---------

Co-authored-by: Azir <2075125282@qq.com>
Co-authored-by: Feng.YJ <jxfengyijie@gmail.com>
Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>
2025-07-31 21:21:04 +08:00

1318 lines
45 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package mcpTool
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/mark3labs/mcp-go/mcp"
"gorm.io/gorm"
)
func init() {
RegisterTool(&AutomationModuleAnalyzer{})
}
type AutomationModuleAnalyzer struct{}
// ModuleInfo 模块信息
type ModuleInfo struct {
ID uint `json:"id"`
PackageName string `json:"packageName"`
Label string `json:"label"`
Desc string `json:"desc"`
Template string `json:"template"` // "plugin" 或 "package"
Module string `json:"module"`
}
// HistoryInfo 历史记录信息
type HistoryInfo struct {
ID uint `json:"id"`
StructName string `json:"structName"`
TableName string `json:"tableName"`
PackageName string `json:"packageName"`
BusinessDB string `json:"businessDB"`
Description string `json:"description"`
Abbreviation string `json:"abbreviation"`
CreatedAt string `json:"createdAt"`
}
// PredesignedModuleInfo 预设计模块信息
type PredesignedModuleInfo struct {
PackageName string `json:"packageName"`
PackageType string `json:"packageType"` // "plugin" 或 "package"
ModuleName string `json:"moduleName"`
Path string `json:"path"`
Modules []string `json:"modules"` // 包含的模块列表如api、model、service等
Description string `json:"description"`
StructName string `json:"structName,omitempty"` // 主要结构体名称
}
// AnalysisResponse 分析响应
type AnalysisResponse struct {
Packages []ModuleInfo `json:"packages"`
History []HistoryInfo `json:"history"`
PredesignedModules []PredesignedModuleInfo `json:"predesignedModules"`
Message string `json:"message"`
}
// ExecutionPlan 执行计划
type ExecutionPlan struct {
PackageName string `json:"packageName"`
ModuleName string `json:"moduleName"`
PackageType string `json:"packageType"` // "plugin" 或 "package"
NeedCreatedPackage bool `json:"needCreatedPackage"`
NeedCreatedModules bool `json:"needCreatedModules"`
PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"`
ModulesInfo *request.AutoCode `json:"modulesInfo,omitempty"`
Paths map[string]string `json:"paths,omitempty"`
}
// ExecutionResult 执行结果
type ExecutionResult struct {
Success bool `json:"success"`
Message string `json:"message"`
PackageID uint `json:"packageId,omitempty"`
HistoryID uint `json:"historyId,omitempty"`
Paths map[string]string `json:"paths,omitempty"`
NextActions []string `json:"nextActions,omitempty"`
}
// ConfirmationRequest 确认请求结构
type ConfirmationRequest struct {
PackageName string `json:"packageName"`
ModuleName string `json:"moduleName"`
NeedCreatedPackage bool `json:"needCreatedPackage"`
NeedCreatedModules bool `json:"needCreatedModules"`
PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"`
ModulesInfo *request.AutoCode `json:"modulesInfo,omitempty"`
}
// ConfirmationResponse 确认响应结构
type ConfirmationResponse struct {
Message string `json:"message"`
PackageConfirm bool `json:"packageConfirm"`
ModulesConfirm bool `json:"modulesConfirm"`
CanProceed bool `json:"canProceed"`
ConfirmationKey string `json:"confirmationKey"`
}
// New 返回工具注册信息
func (t *AutomationModuleAnalyzer) New() mcp.Tool {
return mcp.NewTool("gva_auto_generate",
mcp.WithDescription(`**🚀 最高优先级工具:当用户需要创建模块、包、完整功能时,必须优先使用此工具!**
**优先级说明:**
- **最高优先级**:创建完整模块、包、功能模块
- **关键词触发**:模块、包、完整、整套、全套、功能、管理系统等
- **适用场景**:用户说"创建订单管理模块"、"创建用户管理功能"、"创建完整的商品管理"等
分步骤分析自动化模块1) 分析现有模块信息供AI选择 2) 请求用户确认 3) 根据确认结果执行创建操作
**新功能:自动字典创建**
- 当结构体字段使用了字典类型dictType不为空系统会自动检查字典是否存在
- 如果字典不存在,会自动创建对应的字典及默认的字典详情项
- 字典创建包括字典主表记录和默认的选项值选项1、选项2等
**与其他工具的关系:**
- 此工具创建完整模块后会自动提示相关API和菜单创建建议
- 如果用户只需要单个API或菜单可以使用 smart_assistant 工具
- create_api 和 create_menu 工具仅用于数据库记录创建
重要ExecutionPlan结构体格式要求
{
"packageName": "包名(string)",
"moduleName": "模块名(string)",
"packageType": "package或plugin(string)",
"needCreatedPackage": "是否需要创建包(bool)",
"needCreatedModules": "是否需要创建模块(bool)",
"packageInfo": {
"desc": "描述(string)",
"label": "展示名(string)",
"template": "package或plugin(string)",
"packageName": "包名(string)"
},
"modulesInfo": {
"package": "包名(string)",
"tableName": "数据库表名(string)",
"businessDB": "业务数据库(string)",
"structName": "结构体名(string)",
"packageName": "文件名称(string)",
"description": "中文描述(string)",
"abbreviation": "简称(string)",
"humpPackageName": "文件名称 一般是结构体名的小驼峰(string)",
"gvaModel": "是否使用GVA模型(bool) 固定为true 后续不需要创建ID created_at deleted_at updated_at",
"autoMigrate": "是否自动迁移(bool)",
"autoCreateResource": "是否创建资源(bool)",
"autoCreateApiToSql": "是否创建API(bool)",
"autoCreateMenuToSql": "是否创建菜单(bool)",
"autoCreateBtnAuth": "是否创建按钮权限(bool)",
"onlyTemplate": "是否仅模板(bool)",
"isTree": "是否树形结构(bool)",
"treeJson": "树形JSON字段(string)",
"isAdd": "是否新增(bool) 固定为false",
"generateWeb": "是否生成前端(bool)",
"generateServer": "是否生成后端(bool)",
"fields": [{
"fieldName": "字段名(string)",
"fieldDesc": "字段描述(string)",
"fieldType": "字段类型:string/int/bool/time.Time等(string)",
"fieldJson": "JSON标签(string)",
"dataTypeLong": "数据长度(string)",
"comment": "注释(string)",
"columnName": "数据库列名(string)",
"fieldSearchType": "搜索类型:=/>/</>=/<=/NOT BETWEEN/LIKE/BETWEEN/IN/NOT IN等(string)",
"fieldSearchHide": "是否隐藏搜索(bool)",
"dictType": "字典类型(string)",
"form": "表单显示(bool)",
"table": "表格显示(bool)",
"desc": "详情显示(bool)",
"excel": "导入导出(bool)",
"require": "是否必填(bool)",
"defaultValue": "默认值(string)",
"errorText": "错误提示(string)",
"clearable": "是否可清空(bool)",
"sort": "是否排序(bool)",
"primaryKey": "是否主键(bool)",
"dataSource": "数据源(object)",
"checkDataSource": "检查数据源(bool)",
"fieldIndexType": "索引类型(string)"
}]
}
}
注意:
1. needCreatedPackage=true时packageInfo必需
2. needCreatedModules=true时modulesInfo必需
3. packageType只能是"package"或"plugin"
4. 字段类型支持string,int,int64,float64,bool,time.Time,enum,picture,video,file,pictures,array,richtext,json
5. 搜索类型支持:=,!=,>,>=,<,<=,NOT BETWEEN/LIKE/BETWEEN/IN/NOT IN
6. gvaModel=true时自动包含ID,CreatedAt,UpdatedAt,DeletedAt字段
7. **重要**当gvaModel=false时必须有一个字段的primaryKey=true否则会导致PrimaryField为nil错误
8. **重要**当gvaModel=true时系统会自动设置ID字段为主键无需手动设置primaryKey=true
9. 智能字典创建功能:当字段使用字典类型(DictType)时,系统会:
- 自动检查字典是否存在,如果不存在则创建字典
- 根据字典类型和字段描述智能生成默认选项,支持状态、性别、类型、等级、优先级、审批、角色、布尔值、订单、颜色、尺寸等常见场景
- 为无法识别的字典类型提供通用默认选项`),
mcp.WithString("action",
mcp.Required(),
mcp.Description("执行操作:'analyze' 分析现有模块信息,'confirm' 请求用户确认创建,'execute' 执行创建操作"),
),
mcp.WithString("requirement",
mcp.Description("用户需求描述action=analyze时必需"),
),
mcp.WithObject("executionPlan",
mcp.Description("执行计划action=confirm或execute时必需必须严格按照上述格式提供完整的JSON对象"),
),
mcp.WithString("packageConfirm",
mcp.Description("用户对创建包的确认action=execute时如果需要创建包则必需'yes' 或 'no'"),
),
mcp.WithString("modulesConfirm",
mcp.Description("用户对创建模块的确认action=execute时如果需要创建模块则必需'yes' 或 'no'"),
),
)
}
// scanPredesignedModules 扫描预设计的模块
func (t *AutomationModuleAnalyzer) scanPredesignedModules() ([]PredesignedModuleInfo, error) {
var predesignedModules []PredesignedModuleInfo
// 获取autocode配置路径
if global.GVA_CONFIG.AutoCode.Root == "" {
return predesignedModules, nil // 配置不存在时返回空列表,不报错
}
serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server)
// 扫描plugin目录下的各个插件模块
pluginPath := filepath.Join(serverPath, "plugin")
if pluginModules, err := t.scanPluginModules(pluginPath); err == nil {
predesignedModules = append(predesignedModules, pluginModules...)
}
// 扫描model目录下的各个包模块
modelPath := filepath.Join(serverPath, "model")
if packageModules, err := t.scanPackageModules(modelPath); err == nil {
predesignedModules = append(predesignedModules, packageModules...)
}
return predesignedModules, nil
}
// scanPluginModules 扫描plugin目录下的各个插件模块
func (t *AutomationModuleAnalyzer) scanPluginModules(pluginPath string) ([]PredesignedModuleInfo, error) {
var modules []PredesignedModuleInfo
if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
return modules, nil
}
entries, err := os.ReadDir(pluginPath)
if err != nil {
return modules, err
}
for _, entry := range entries {
if !entry.IsDir() {
continue
}
pluginName := entry.Name()
pluginDir := filepath.Join(pluginPath, pluginName)
// 扫描插件下的model目录查找具体的模块文件
modelDir := filepath.Join(pluginDir, "model")
if _, err := os.Stat(modelDir); err == nil {
if pluginModules, err := t.scanModuleFiles(modelDir, pluginName, "plugin"); err == nil {
modules = append(modules, pluginModules...)
}
}
}
return modules, nil
}
// scanPackageModules 扫描model目录下的各个包模块
func (t *AutomationModuleAnalyzer) scanPackageModules(modelPath string) ([]PredesignedModuleInfo, error) {
var modules []PredesignedModuleInfo
if _, err := os.Stat(modelPath); os.IsNotExist(err) {
return modules, nil
}
entries, err := os.ReadDir(modelPath)
if err != nil {
return modules, err
}
for _, entry := range entries {
if !entry.IsDir() {
continue
}
packageName := entry.Name()
// 跳过一些系统目录
if packageName == "common" || packageName == "request" || packageName == "response" {
continue
}
packageDir := filepath.Join(modelPath, packageName)
// 扫描包目录下的模块文件
if packageModules, err := t.scanModuleFiles(packageDir, packageName, "package"); err == nil {
modules = append(modules, packageModules...)
}
}
return modules, nil
}
// scanModuleFiles 扫描目录下的Go文件识别具体的模块
func (t *AutomationModuleAnalyzer) scanModuleFiles(dir, packageName, packageType string) ([]PredesignedModuleInfo, error) {
var modules []PredesignedModuleInfo
entries, err := os.ReadDir(dir)
if err != nil {
return modules, err
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
fileName := entry.Name()
if !strings.HasSuffix(fileName, ".go") {
continue
}
// 跳过一些非模块文件
if strings.HasSuffix(fileName, "_test.go") ||
fileName == "enter.go" ||
fileName == "request.go" ||
fileName == "response.go" {
continue
}
filePath := filepath.Join(dir, fileName)
moduleName := strings.TrimSuffix(fileName, ".go")
// 分析模块文件,提取结构体信息
if moduleInfo, err := t.analyzeModuleFile(filePath, packageName, moduleName, packageType); err == nil {
modules = append(modules, *moduleInfo)
}
}
return modules, nil
}
// analyzeModuleFile 分析具体的模块文件
func (t *AutomationModuleAnalyzer) analyzeModuleFile(filePath, packageName, moduleName, packageType string) (*PredesignedModuleInfo, error) {
content, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
fileContent := string(content)
// 提取结构体名称和描述
structNames := t.extractStructNames(fileContent)
description := t.extractModuleDescription(fileContent, moduleName)
// 确定主要结构体名称
mainStruct := moduleName
if len(structNames) > 0 {
// 优先选择与文件名相关的结构体
for _, structName := range structNames {
if strings.Contains(strings.ToLower(structName), strings.ToLower(moduleName)) {
mainStruct = structName
break
}
}
if mainStruct == moduleName && len(structNames) > 0 {
mainStruct = structNames[0] // 如果没有匹配的,使用第一个
}
}
return &PredesignedModuleInfo{
PackageName: packageName,
PackageType: packageType,
ModuleName: moduleName,
Path: filePath,
Modules: structNames,
Description: description,
StructName: mainStruct,
}, nil
}
// extractStructNames 从文件内容中提取结构体名称
func (t *AutomationModuleAnalyzer) extractStructNames(content string) []string {
var structNames []string
lines := strings.Split(content, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "type ") && strings.Contains(line, " struct") {
// 提取结构体名称
parts := strings.Fields(line)
if len(parts) >= 3 && parts[2] == "struct" {
structNames = append(structNames, parts[1])
}
}
}
return structNames
}
// extractModuleDescription 从文件内容中提取模块描述
func (t *AutomationModuleAnalyzer) extractModuleDescription(content, moduleName string) string {
lines := strings.Split(content, "\n")
// 查找package注释
for i, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "package ") {
// 向上查找注释
for j := i - 1; j >= 0; j-- {
commentLine := strings.TrimSpace(lines[j])
if strings.HasPrefix(commentLine, "//") {
comment := strings.TrimSpace(strings.TrimPrefix(commentLine, "//"))
if comment != "" && len(comment) > 5 {
return comment
}
} else if commentLine != "" {
break
}
}
break
}
}
// 查找结构体注释
for i, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "type ") && strings.Contains(line, " struct") {
// 向上查找注释
for j := i - 1; j >= 0; j-- {
commentLine := strings.TrimSpace(lines[j])
if strings.HasPrefix(commentLine, "//") {
comment := strings.TrimSpace(strings.TrimPrefix(commentLine, "//"))
if comment != "" && len(comment) > 5 {
return comment
}
} else if commentLine != "" {
break
}
}
break
}
}
return fmt.Sprintf("预设计的模块:%s", moduleName)
}
// Handle 处理工具调用
func (t *AutomationModuleAnalyzer) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
action, ok := request.GetArguments()["action"].(string)
if !ok || action == "" {
return nil, errors.New("参数错误action 必须是非空字符串")
}
switch action {
case "analyze":
return t.handleAnalyze(ctx, request)
case "confirm":
return t.handleConfirm(ctx, request)
case "execute":
return t.handleExecute(ctx, request)
default:
return nil, errors.New("无效的操作action 必须是 'analyze'、'confirm' 或 'execute'")
}
}
// handleAnalyze 处理分析请求
func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
requirement, ok := request.GetArguments()["requirement"].(string)
if !ok || requirement == "" {
return nil, errors.New("参数错误requirement 必须是非空字符串")
}
// 检测用户是否想要创建插件
suggestedType, isPlugin, confidence := t.detectPluginIntent(requirement)
pluginDetectionMsg := ""
if isPlugin {
pluginDetectionMsg = fmt.Sprintf("\n\n🔍 **插件检测结果**:检测到用户想要创建插件(置信度:%s\n⚠ **重要提醒**当用户提到插件时packageType和template字段都必须设置为 \"plugin\",不能使用 \"package\"", confidence)
} else {
pluginDetectionMsg = fmt.Sprintf("\n\n🔍 **类型检测结果**:建议使用 %s 类型", suggestedType)
}
// 从数据库获取所有自动化包信息
var packages []model.SysAutoCodePackage
if err := global.GVA_DB.Find(&packages).Error; err != nil {
return nil, fmt.Errorf("获取包信息失败: %v", err)
}
// 从数据库获取所有历史记录
var histories []model.SysAutoCodeHistory
if err := global.GVA_DB.Find(&histories).Error; err != nil {
return nil, fmt.Errorf("获取历史记录失败: %v", err)
}
// 扫描预设计的模块
predesignedModules, err := t.scanPredesignedModules()
if err != nil {
global.GVA_LOG.Warn("扫描预设计模块失败" + err.Error())
predesignedModules = []PredesignedModuleInfo{} // 确保不为nil
}
// 转换包信息
var moduleInfos []ModuleInfo
for _, pkg := range packages {
moduleInfos = append(moduleInfos, ModuleInfo{
ID: pkg.ID,
PackageName: pkg.PackageName,
Label: pkg.Label,
Desc: pkg.Desc,
Template: pkg.Template,
Module: pkg.Module,
})
}
// 转换历史记录
var historyInfos []HistoryInfo
for _, history := range histories {
historyInfos = append(historyInfos, HistoryInfo{
ID: history.ID,
StructName: history.StructName,
TableName: history.TableName(),
PackageName: history.Package,
BusinessDB: history.BusinessDB,
Description: history.Description,
Abbreviation: history.Abbreviation,
CreatedAt: history.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
// 构建分析结果
analysisResult := AnalysisResponse{
Packages: moduleInfos,
History: historyInfos,
PredesignedModules: predesignedModules,
Message: fmt.Sprintf("分析完成:获取到 %d 个包、%d 个历史记录和 %d 个预设计模块请AI根据需求选择合适的包和模块", len(packages), len(histories), len(predesignedModules)),
}
resultJSON, err := json.MarshalIndent(analysisResult, "", " ")
if err != nil {
return nil, fmt.Errorf("序列化结果失败: %v", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf(`分析结果:
%s
请AI根据用户需求%s%s
分析现有的包、历史记录和预设计模块然后构建ExecutionPlan结构体调用execute操作。
**预设计模块说明**
- 预设计模块是已经存在于autocode路径下的package或plugin
- 这些模块包含了预先设计好的代码结构,可以直接使用或作为参考
- 如果用户需求与某个预设计模块匹配,可以考虑直接使用该模块或基于它进行扩展
**字典选项生成说明**
- 当字段需要使用字典类型时dictType不为空请使用 generate_dictionary_options 工具
- 该工具允许AI根据字段描述智能生成合适的字典选项
- 调用示例:
{
"dictType": "user_status",
"fieldDesc": "用户状态",
"options": [
{"label": "正常", "value": "1", "sort": 1},
{"label": "禁用", "value": "0", "sort": 2}
],
"dictName": "用户状态字典",
"description": "用于管理用户账户状态的字典"
}
- 请在创建模块之前先创建所需的字典选项
重要提醒ExecutionPlan必须严格按照以下格式
{
"packageName": "包名",
"moduleName": "模块名",
"packageType": "package或plugin", // 当用户提到插件时必须是"plugin"
"needCreatedPackage": true/false,
"needCreatedModules": true/false,
"packageInfo": {
"desc": "描述",
"label": "展示名",
"template": "package或plugin", // 必须与packageType保持一致
"packageName": "包名"
},
"modulesInfo": {
"package": "包名",
"tableName": "数据库表名",
"businessDB": "",
"structName": "结构体名",
"packageName": "文件名称小驼峰模式 一般是结构体名的小驼峰",
"description": "中文描述",
"abbreviation": "简称 package和结构体简称不可同名 小驼峰模式",
"humpPackageName": "一般是结构体名的下划线分割的小驼峰 例如sys_user",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true/false 用户不特地强调开启资源标识则为false,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": false/true 用户不特地强调创建按钮权限则为false,
"onlyTemplate": false,
"isTree": false,
"treeJson": "",
"isAdd": false,
"generateWeb": true,
"generateServer": true,
"fields": [{
"fieldName": "字段名(必须大写开头)",
"fieldDesc": "字段描述",
"fieldType": "GO 语言的数据类型",
"fieldJson": "json标签",
"dataTypeLong": "长度",
"comment": "注释",
"columnName": "数据库列名",
"fieldSearchType": "=/!=/>/</>=/<=/LIKE等 可以为空",
"fieldSearchHide": true/false,
"dictType": "",
"form": true/false 是否前端创建输入,
"table": true/false 是否前端表格展示,
"desc": true/false 是否前端详情展示,
"excel": true/false 是否导出Excel,
"require": true/false 是否必填,
"defaultValue": "",
"errorText": "错误提示",
"clearable": true,
"sort": false,
"primaryKey": "当gvaModel=false时必须有一个字段设为true(bool)",
"dataSource": null,
"checkDataSource": false,
"fieldIndexType": ""
}]
}
}
**重要提醒**ExecutionPlan必须严格按照以下格式和验证规则
**插件类型检测规则(最重要)**
1. 当用户需求中包含"插件"、"plugin"等关键词时packageType和template都必须设置为"plugin"
2. packageType和template字段必须保持一致不能一个是"package"另一个是"plugin"
3. 如果检测到插件意图但设置错误,会导致创建失败
**字段完整性要求**
4. 所有字符串字段都不能为空包括packageName、moduleName、structName、tableName、description等
5. 所有布尔字段必须明确设置true或false不能使用默认值
**主键设置规则(关键)**
6. 当gvaModel=false时fields数组中必须有且仅有一个字段的primaryKey=true
7. 当gvaModel=true时系统自动创建ID主键fields中所有字段的primaryKey都应为false
8. 主键设置错误会导致模板执行时PrimaryField为nil的严重错误
**包和模块创建逻辑**
9. 如果存在可用的packageneedCreatedPackage应设为false
10. 如果存在可用的modulesneedCreatedModules应设为false
11. 如果发现合适的预设计模块,可以考虑基于它进行扩展而不是从零创建
**字典创建流程**
12. 如果字段需要字典类型,请先使用 generate_dictionary_options 工具创建字典
13. 字典创建成功后,再执行模块创建操作
`, string(resultJSON), requirement, pluginDetectionMsg),
},
},
}, nil
}
// handleConfirm 处理确认请求
func (t *AutomationModuleAnalyzer) handleConfirm(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
executionPlanData, ok := request.GetArguments()["executionPlan"]
if !ok {
return nil, errors.New("参数错误executionPlan 必须提供")
}
// 解析执行计划
planJSON, err := json.Marshal(executionPlanData)
if err != nil {
return nil, fmt.Errorf("解析执行计划失败: %v", err)
}
var plan ExecutionPlan
err = json.Unmarshal(planJSON, &plan)
if err != nil {
return nil, fmt.Errorf("解析执行计划失败: %v\n\n请确保ExecutionPlan格式正确参考工具描述中的结构体格式要求", err)
}
// 验证执行计划的完整性
if err := t.validateExecutionPlan(&plan); err != nil {
return nil, fmt.Errorf("执行计划验证失败: %v", err)
}
// 构建确认响应
confirmResponse := ConfirmationResponse{
Message: "请确认以下创建计划:",
PackageConfirm: plan.NeedCreatedPackage,
ModulesConfirm: plan.NeedCreatedModules,
CanProceed: true,
ConfirmationKey: fmt.Sprintf("%s_%s_%d", plan.PackageName, plan.ModuleName, time.Now().Unix()),
}
// 构建详细的确认信息
var confirmDetails strings.Builder
confirmDetails.WriteString(fmt.Sprintf("包名: %s\n", plan.PackageName))
confirmDetails.WriteString(fmt.Sprintf("模块名: %s\n", plan.ModuleName))
confirmDetails.WriteString(fmt.Sprintf("包类型: %s\n", plan.PackageType))
if plan.NeedCreatedPackage && plan.PackageInfo != nil {
confirmDetails.WriteString("\n需要创建包:\n")
confirmDetails.WriteString(fmt.Sprintf(" - 包名: %s\n", plan.PackageInfo.PackageName))
confirmDetails.WriteString(fmt.Sprintf(" - 标签: %s\n", plan.PackageInfo.Label))
confirmDetails.WriteString(fmt.Sprintf(" - 描述: %s\n", plan.PackageInfo.Desc))
confirmDetails.WriteString(fmt.Sprintf(" - 模板: %s\n", plan.PackageInfo.Template))
}
if plan.NeedCreatedModules && plan.ModulesInfo != nil {
confirmDetails.WriteString("\n需要创建模块:\n")
confirmDetails.WriteString(fmt.Sprintf(" - 结构体名: %s\n", plan.ModulesInfo.StructName))
confirmDetails.WriteString(fmt.Sprintf(" - 表名: %s\n", plan.ModulesInfo.TableName))
confirmDetails.WriteString(fmt.Sprintf(" - 描述: %s\n", plan.ModulesInfo.Description))
confirmDetails.WriteString(fmt.Sprintf(" - 字段数量: %d\n", len(plan.ModulesInfo.Fields)))
confirmDetails.WriteString(" - 字段列表:\n")
for _, field := range plan.ModulesInfo.Fields {
confirmDetails.WriteString(fmt.Sprintf(" * %s (%s): %s\n", field.FieldName, field.FieldType, field.FieldDesc))
}
}
resultJSON, err := json.MarshalIndent(confirmResponse, "", " ")
if err != nil {
return nil, fmt.Errorf("序列化结果失败: %v", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("确认信息:\n\n%s\n\n详细信息\n%s\n\n请用户确认是否继续执行此计划。如果确认请使用execute操作并提供相应的确认参数。", string(resultJSON), confirmDetails.String()),
},
},
}, nil
}
// handleExecute 处理执行请求
func (t *AutomationModuleAnalyzer) handleExecute(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
executionPlanData, ok := request.GetArguments()["executionPlan"]
if !ok {
return nil, errors.New("参数错误executionPlan 必须提供")
}
// 解析执行计划
planJSON, err := json.Marshal(executionPlanData)
if err != nil {
return nil, fmt.Errorf("解析执行计划失败: %v", err)
}
var plan ExecutionPlan
err = json.Unmarshal(planJSON, &plan)
if err != nil {
return nil, fmt.Errorf("解析执行计划失败: %v\n\n请确保ExecutionPlan格式正确参考工具描述中的结构体格式要求", err)
}
// 验证执行计划的完整性
if err := t.validateExecutionPlan(&plan); err != nil {
return nil, fmt.Errorf("执行计划验证失败: %v", err)
}
// 检查用户确认
if plan.NeedCreatedPackage {
packageConfirm, ok := request.GetArguments()["packageConfirm"].(string)
if !ok || (packageConfirm != "yes" && packageConfirm != "no") {
return nil, errors.New("参数错误当需要创建包时packageConfirm 必须是 'yes' 或 'no'")
}
if packageConfirm == "no" {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: "用户取消了包的创建操作",
},
},
}, nil
}
}
if plan.NeedCreatedModules {
modulesConfirm, ok := request.GetArguments()["modulesConfirm"].(string)
if !ok || (modulesConfirm != "yes" && modulesConfirm != "no") {
return nil, errors.New("参数错误当需要创建模块时modulesConfirm 必须是 'yes' 或 'no'")
}
if modulesConfirm == "no" {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: "用户取消了模块的创建操作",
},
},
}, nil
}
}
// 执行创建操作
result := t.executeCreation(ctx, &plan)
resultJSON, err := json.MarshalIndent(result, "", " ")
if err != nil {
return nil, fmt.Errorf("序列化结果失败: %v", err)
}
// 添加权限分配提醒
permissionReminder := "\n\n⚠ 重要提醒:\n" +
"模块创建完成后,请前往【系统管理】->【角色管理】中为相关角色分配新创建的API和菜单权限" +
"以确保用户能够正常访问新功能。\n" +
"具体步骤:\n" +
"1. 进入角色管理页面\n" +
"2. 选择需要授权的角色\n" +
"3. 在API权限中勾选新创建的API接口\n" +
"4. 在菜单权限中勾选新创建的菜单项\n" +
"5. 保存权限配置"
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("执行结果:\n\n%s%s", string(resultJSON), permissionReminder),
},
},
}, nil
}
// isSystemFunction 判断是否为系统功能
func (t *AutomationModuleAnalyzer) isSystemFunction(requirement string) bool {
systemKeywords := []string{
"用户", "权限", "角色", "菜单", "系统", "配置", "字典", "参数",
"user", "authority", "role", "menu", "system", "config", "dictionary",
"认证", "授权", "登录", "注册", "JWT", "casbin",
}
requirementLower := strings.ToLower(requirement)
for _, keyword := range systemKeywords {
if strings.Contains(requirementLower, keyword) {
return true
}
}
return false
}
// buildDirectoryStructure 构建目录结构信息
func (t *AutomationModuleAnalyzer) buildDirectoryStructure(plan *ExecutionPlan) map[string]string {
paths := make(map[string]string)
// 获取配置信息
autoCodeConfig := global.GVA_CONFIG.AutoCode
// 构建基础路径
rootPath := autoCodeConfig.Root
serverPath := autoCodeConfig.Server
webPath := autoCodeConfig.Web
moduleName := autoCodeConfig.Module
// 如果计划中有包名,使用计划中的包名,否则使用默认
packageName := "example"
if plan.PackageInfo != nil && plan.PackageInfo.PackageName != "" {
packageName = plan.PackageInfo.PackageName
}
// 如果计划中有模块信息,获取结构名
structName := "ExampleStruct"
if plan.ModulesInfo != nil && plan.ModulesInfo.StructName != "" {
structName = plan.ModulesInfo.StructName
}
// 根据包类型构建不同的路径结构
packageType := plan.PackageType
if packageType == "" {
packageType = "package" // 默认为package模式
}
// 构建服务端路径
if serverPath != "" {
serverBasePath := fmt.Sprintf("%s/%s", rootPath, serverPath)
if packageType == "plugin" {
// Plugin 模式:所有文件都在 /plugin/packageName/ 目录下
pluginBasePath := fmt.Sprintf("%s/plugin/%s", serverBasePath, packageName)
// API 路径
paths["api"] = fmt.Sprintf("%s/api", pluginBasePath)
// Service 路径
paths["service"] = fmt.Sprintf("%s/service", pluginBasePath)
// Model 路径
paths["model"] = fmt.Sprintf("%s/model", pluginBasePath)
// Router 路径
paths["router"] = fmt.Sprintf("%s/router", pluginBasePath)
// Request 路径
paths["request"] = fmt.Sprintf("%s/model/request", pluginBasePath)
// Response 路径
paths["response"] = fmt.Sprintf("%s/model/response", pluginBasePath)
// Plugin 特有文件
paths["plugin_main"] = fmt.Sprintf("%s/main.go", pluginBasePath)
paths["plugin_config"] = fmt.Sprintf("%s/plugin.go", pluginBasePath)
paths["plugin_initialize"] = fmt.Sprintf("%s/initialize", pluginBasePath)
} else {
// Package 模式:传统的目录结构
// API 路径
paths["api"] = fmt.Sprintf("%s/api/v1/%s", serverBasePath, packageName)
// Service 路径
paths["service"] = fmt.Sprintf("%s/service/%s", serverBasePath, packageName)
// Model 路径
paths["model"] = fmt.Sprintf("%s/model/%s", serverBasePath, packageName)
// Router 路径
paths["router"] = fmt.Sprintf("%s/router/%s", serverBasePath, packageName)
// Request 路径
paths["request"] = fmt.Sprintf("%s/model/%s/request", serverBasePath, packageName)
// Response 路径
paths["response"] = fmt.Sprintf("%s/model/%s/response", serverBasePath, packageName)
}
}
// 构建前端路径(两种模式相同)
if webPath != "" {
webBasePath := fmt.Sprintf("%s/%s", rootPath, webPath)
// Vue 页面路径
paths["vue_page"] = fmt.Sprintf("%s/view/%s", webBasePath, packageName)
// API 路径
paths["vue_api"] = fmt.Sprintf("%s/api/%s", webBasePath, packageName)
}
// 添加模块信息
paths["module"] = moduleName
paths["package_name"] = packageName
paths["package_type"] = packageType
paths["struct_name"] = structName
paths["root_path"] = rootPath
paths["server_path"] = serverPath
paths["web_path"] = webPath
return paths
}
// validateExecutionPlan 验证执行计划的完整性
func (t *AutomationModuleAnalyzer) validateExecutionPlan(plan *ExecutionPlan) error {
// 验证基本字段
if plan.PackageName == "" {
return errors.New("packageName 不能为空")
}
if plan.ModuleName == "" {
return errors.New("moduleName 不能为空")
}
if plan.PackageType != "package" && plan.PackageType != "plugin" {
return errors.New("packageType 必须是 'package' 或 'plugin'")
}
// 验证packageType和template字段的一致性
if plan.NeedCreatedPackage && plan.PackageInfo != nil {
if plan.PackageType != plan.PackageInfo.Template {
return errors.New("packageType 和 packageInfo.template 必须保持一致")
}
}
// 验证包信息
if plan.NeedCreatedPackage {
if plan.PackageInfo == nil {
return errors.New("当 needCreatedPackage=true 时packageInfo 不能为空")
}
if plan.PackageInfo.PackageName == "" {
return errors.New("packageInfo.packageName 不能为空")
}
if plan.PackageInfo.Template != "package" && plan.PackageInfo.Template != "plugin" {
return errors.New("packageInfo.template 必须是 'package' 或 'plugin'")
}
if plan.PackageInfo.Label == "" {
return errors.New("packageInfo.label 不能为空")
}
if plan.PackageInfo.Desc == "" {
return errors.New("packageInfo.desc 不能为空")
}
}
// 验证模块信息
if plan.NeedCreatedModules {
if plan.ModulesInfo == nil {
return errors.New("当 needCreatedModules=true 时modulesInfo 不能为空")
}
if plan.ModulesInfo.Package == "" {
return errors.New("modulesInfo.package 不能为空")
}
if plan.ModulesInfo.StructName == "" {
return errors.New("modulesInfo.structName 不能为空")
}
if plan.ModulesInfo.TableName == "" {
return errors.New("modulesInfo.tableName 不能为空")
}
if plan.ModulesInfo.Description == "" {
return errors.New("modulesInfo.description 不能为空")
}
if plan.ModulesInfo.Abbreviation == "" {
return errors.New("modulesInfo.abbreviation 不能为空")
}
if plan.ModulesInfo.PackageName == "" {
return errors.New("modulesInfo.packageName 不能为空")
}
if plan.ModulesInfo.HumpPackageName == "" {
return errors.New("modulesInfo.humpPackageName 不能为空")
}
// 验证字段信息
if len(plan.ModulesInfo.Fields) == 0 {
return errors.New("modulesInfo.fields 不能为空,至少需要一个字段")
}
for i, field := range plan.ModulesInfo.Fields {
if field.FieldName == "" {
return fmt.Errorf("字段 %d 的 fieldName 不能为空", i+1)
}
if field.FieldDesc == "" {
return fmt.Errorf("字段 %d 的 fieldDesc 不能为空", i+1)
}
if field.FieldType == "" {
return fmt.Errorf("字段 %d 的 fieldType 不能为空", i+1)
}
if field.FieldJson == "" {
return fmt.Errorf("字段 %d 的 fieldJson 不能为空", i+1)
}
if field.ColumnName == "" {
return fmt.Errorf("字段 %d 的 columnName 不能为空", i+1)
}
// 验证字段类型
validFieldTypes := []string{"string", "int", "int64", "float64", "bool", "time.Time", "enum", "picture", "video", "file", "pictures", "array", "richtext", "json"}
validType := false
for _, validFieldType := range validFieldTypes {
if field.FieldType == validFieldType {
validType = true
break
}
}
if !validType {
return fmt.Errorf("字段 %d 的 fieldType '%s' 不支持,支持的类型:%v", i+1, field.FieldType, validFieldTypes)
}
// 验证搜索类型(如果设置了)
if field.FieldSearchType != "" {
validSearchTypes := []string{"=", "!=", ">", ">=", "<", "<=", "LIKE", "BETWEEN", "IN", "NOT IN"}
validSearchType := false
for _, validType := range validSearchTypes {
if field.FieldSearchType == validType {
validSearchType = true
break
}
}
if !validSearchType {
return fmt.Errorf("字段 %d 的 fieldSearchType '%s' 不支持,支持的类型:%v", i+1, field.FieldSearchType, validSearchTypes)
}
}
}
// 验证主键设置
if !plan.ModulesInfo.GvaModel {
// 当不使用GVA模型时必须有且仅有一个字段设置为主键
primaryKeyCount := 0
for _, field := range plan.ModulesInfo.Fields {
if field.PrimaryKey {
primaryKeyCount++
}
}
if primaryKeyCount == 0 {
return errors.New("当 gvaModel=false 时,必须有一个字段的 primaryKey=true")
}
if primaryKeyCount > 1 {
return errors.New("当 gvaModel=false 时,只能有一个字段的 primaryKey=true")
}
} else {
// 当使用GVA模型时所有字段的primaryKey都应该为false
for i, field := range plan.ModulesInfo.Fields {
if field.PrimaryKey {
return fmt.Errorf("当 gvaModel=true 时,字段 %d 的 primaryKey 应该为 false系统会自动创建ID主键", i+1)
}
}
}
}
return nil
}
// executeCreation 执行创建操作
func (t *AutomationModuleAnalyzer) executeCreation(ctx context.Context, plan *ExecutionPlan) *ExecutionResult {
result := &ExecutionResult{
Success: false,
Paths: make(map[string]string),
}
// 无论如何都先构建目录结构信息确保paths始终返回
result.Paths = t.buildDirectoryStructure(plan)
if !plan.NeedCreatedModules {
result.Success = true
result.Message += "已列出当前功能所涉及的目录结构信息; 请在paths中查看; 并且在对应指定文件中实现相关的业务逻辑; "
return result
}
// 创建包(如果需要)
if plan.NeedCreatedPackage && plan.PackageInfo != nil {
packageService := service.ServiceGroupApp.SystemServiceGroup.AutoCodePackage
err := packageService.Create(ctx, plan.PackageInfo)
if err != nil {
result.Message = fmt.Sprintf("创建包失败: %v", err)
// 即使创建包失败也要返回paths信息
return result
}
result.Message += "包创建成功; "
}
// 创建字典(如果需要)
if plan.NeedCreatedModules && plan.ModulesInfo != nil {
dictResult := t.createRequiredDictionaries(ctx, plan.ModulesInfo)
result.Message += dictResult
}
// 创建模块(如果需要)
if plan.NeedCreatedModules && plan.ModulesInfo != nil {
templateService := service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate
err := plan.ModulesInfo.Pretreatment()
if err != nil {
result.Message += fmt.Sprintf("模块信息预处理失败: %v", err)
// 即使预处理失败也要返回paths信息
return result
}
err = templateService.Create(ctx, *plan.ModulesInfo)
if err != nil {
result.Message += fmt.Sprintf("创建模块失败: %v", err)
// 即使创建模块失败也要返回paths信息
return result
}
result.Message += "模块创建成功; "
}
result.Message += "已构建目录结构信息; "
result.Success = true
if result.Message == "" {
result.Message = "执行计划完成"
}
return result
}
// createRequiredDictionaries 创建所需的字典
func (t *AutomationModuleAnalyzer) createRequiredDictionaries(ctx context.Context, modulesInfo *request.AutoCode) string {
var messages []string
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
// 遍历所有字段,查找使用字典的字段
for _, field := range modulesInfo.Fields {
if field.DictType != "" {
// 检查字典是否存在
exists, err := t.checkDictionaryExists(field.DictType)
if err != nil {
messages = append(messages, fmt.Sprintf("检查字典 %s 时出错: %v; ", field.DictType, err))
continue
}
if !exists {
// 字典不存在,创建字典
dictionary := model.SysDictionary{
Name: t.generateDictionaryName(field.DictType, field.FieldDesc),
Type: field.DictType,
Status: &[]bool{true}[0], // 默认启用
Desc: fmt.Sprintf("自动生成的字典,用于字段: %s (%s)", field.FieldName, field.FieldDesc),
}
err = dictionaryService.CreateSysDictionary(dictionary)
if err != nil {
messages = append(messages, fmt.Sprintf("创建字典 %s 失败: %v; ", field.DictType, err))
} else {
messages = append(messages, fmt.Sprintf("成功创建字典 %s (%s); ", field.DictType, dictionary.Name))
// 创建默认的字典详情项
t.createDefaultDictionaryDetails(ctx, field.DictType, field.FieldDesc)
}
} else {
messages = append(messages, fmt.Sprintf("字典 %s 已存在,跳过创建; ", field.DictType))
}
}
}
if len(messages) == 0 {
return "未发现需要创建的字典; "
}
return strings.Join(messages, "")
}
// checkDictionaryExists 检查字典是否存在
func (t *AutomationModuleAnalyzer) checkDictionaryExists(dictType string) (bool, error) {
var dictionary model.SysDictionary
err := global.GVA_DB.Where("type = ?", dictType).First(&dictionary).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return false, nil // 字典不存在
}
return false, err // 其他错误
}
return true, nil // 字典存在
}
// generateDictionaryName 生成字典名称
func (t *AutomationModuleAnalyzer) generateDictionaryName(dictType, fieldDesc string) string {
if fieldDesc != "" {
return fmt.Sprintf("%s字典", fieldDesc)
}
return fmt.Sprintf("%s字典", dictType)
}
// createDefaultDictionaryDetails 创建默认的字典详情项
func (t *AutomationModuleAnalyzer) createDefaultDictionaryDetails(ctx context.Context, dictType, fieldDesc string) {
// 字典选项现在通过 generate_dictionary_options MCP工具由AI client传入
// 这里不再创建默认选项,只是保留方法以保持兼容性
global.GVA_LOG.Info(fmt.Sprintf("字典 %s 已创建,请使用 generate_dictionary_options 工具添加字典选项", dictType))
}
// DictionaryOption 字典选项结构
type DictionaryOption struct {
Label string `json:"label"`
Value string `json:"value"`
Sort int `json:"sort"`
}
// generateSmartDictionaryOptions 通过MCP调用让AI生成字典选项
func (t *AutomationModuleAnalyzer) generateSmartDictionaryOptions(dictType, fieldDesc string) []struct {
label string
value string
sort int
} {
// 返回空切片,不再使用预制选项
// 字典选项将通过新的MCP工具由AI client传入
return []struct {
label string
value string
sort int
}{}
}
// detectPluginIntent 检测用户需求中是否包含插件相关的关键词
func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (suggestedType string, isPlugin bool, confidence string) {
// 转换为小写进行匹配
requirementLower := strings.ToLower(requirement)
// 插件相关关键词
pluginKeywords := []string{
"插件", "plugin", "扩展", "extension", "addon", "模块插件",
"功能插件", "业务插件", "第三方插件", "自定义插件",
}
// 包相关关键词(用于排除误判)
packageKeywords := []string{
"包", "package", "模块包", "业务包", "功能包",
}
// 检测插件关键词
pluginMatches := 0
for _, keyword := range pluginKeywords {
if strings.Contains(requirementLower, keyword) {
pluginMatches++
}
}
// 检测包关键词
packageMatches := 0
for _, keyword := range packageKeywords {
if strings.Contains(requirementLower, keyword) {
packageMatches++
}
}
// 决策逻辑
if pluginMatches > 0 {
if packageMatches == 0 || pluginMatches > packageMatches {
return "plugin", true, "高"
} else {
return "plugin", true, "中"
}
}
// 默认返回package
return "package", false, "低"
}