feat(mcp): 实现需求分析工具并支持批量创建模块

1. 新增requirement_analyzer工具,用于将用户需求转换为结构化提示词
2. 修改gva_auto_generate工具支持批量创建多个模块
3. 更新文档示例和schema以反映批量创建功能
4. 优化执行计划验证逻辑,支持模块数组校验
5. 改进字典创建流程,避免重复创建相同字典类型
This commit is contained in:
pixelmaxQM
2025-08-03 23:58:47 +08:00
parent 9260111312
commit 31e6316916
4 changed files with 753 additions and 391 deletions

View File

@@ -8,12 +8,11 @@ ExecutionPlan 是用于自动化模块创建的执行计划结构体,包含了
```go ```go
type ExecutionPlan struct { type ExecutionPlan struct {
PackageName string `json:"packageName"` // 包名,如:"user", "order", "product" PackageName string `json:"packageName"` // 包名,如:"user", "order", "product"
ModuleName string `json:"moduleName"` // 模块名,通常与结构体名相同
PackageType string `json:"packageType"` // "plugin" 或 "package" PackageType string `json:"packageType"` // "plugin" 或 "package"
NeedCreatedPackage bool `json:"needCreatedPackage"` // 是否需要创建包 NeedCreatedPackage bool `json:"needCreatedPackage"` // 是否需要创建包
NeedCreatedModules bool `json:"needCreatedModules"` // 是否需要创建模块 NeedCreatedModules bool `json:"needCreatedModules"` // 是否需要创建模块
PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"` // 包信息当NeedCreatedPackage=true时必需 PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"` // 包信息当NeedCreatedPackage=true时必需
ModulesInfo *request.AutoCode `json:"modulesInfo,omitempty"` // 模块信息当NeedCreatedModules=true时必需 ModulesInfo []*request.AutoCode `json:"modulesInfo,omitempty"` // 模块信息数组当NeedCreatedModules=true时必需,支持批量创建
Paths map[string]string `json:"paths,omitempty"` // 路径信息 Paths map[string]string `json:"paths,omitempty"` // 路径信息
} }
``` ```
@@ -94,12 +93,11 @@ type AutoCodeField struct {
## 使用示例 ## 使用示例
### 示例1创建新包和模块 ### 示例1创建新包和批量创建多个模块
```json ```json
{ {
"packageName": "user", "packageName": "user",
"moduleName": "User",
"packageType": "package", "packageType": "package",
"needCreatedPackage": true, "needCreatedPackage": true,
"needCreatedModules": true, "needCreatedModules": true,
@@ -109,129 +107,256 @@ type AutoCodeField struct {
"template": "package", "template": "package",
"packageName": "user" "packageName": "user"
}, },
"modulesInfo": { "modulesInfo": [
"package": "user", {
"tableName": "sys_users", "package": "user",
"businessDB": "", "tableName": "sys_users",
"structName": "User", "businessDB": "",
"packageName": "user", "structName": "User",
"description": "用户", "packageName": "user",
"abbreviation": "user", "description": "用户",
"humpPackageName": "user", "abbreviation": "user",
"gvaModel": true, "humpPackageName": "user",
"autoMigrate": true, "gvaModel": true,
"autoCreateResource": true, "autoMigrate": true,
"autoCreateApiToSql": true, "autoCreateResource": true,
"autoCreateMenuToSql": true, "autoCreateApiToSql": true,
"autoCreateBtnAuth": true, "autoCreateMenuToSql": true,
"onlyTemplate": false, "autoCreateBtnAuth": true,
"isTree": false, "onlyTemplate": false,
"treeJson": "", "isTree": false,
"isAdd": true, "treeJson": "",
"generateWeb": true, "isAdd": true,
"generateServer": true, "generateWeb": true,
"fields": [ "generateServer": true,
{ "fields": [
"fieldName": "Username", {
"fieldDesc": "用户名", "fieldName": "Username",
"fieldType": "string", "fieldDesc": "用户名",
"fieldJson": "username", "fieldType": "string",
"dataTypeLong": "50", "fieldJson": "username",
"comment": "用户名", "dataTypeLong": "50",
"columnName": "username", "comment": "用户名",
"fieldSearchType": "LIKE", "columnName": "username",
"fieldSearchHide": false, "fieldSearchType": "LIKE",
"dictType": "", "fieldSearchHide": false,
"form": true, "dictType": "",
"table": true, "form": true,
"desc": true, "table": true,
"excel": true, "desc": true,
"require": true, "excel": true,
"defaultValue": "", "require": true,
"errorText": "请输入用户名", "defaultValue": "",
"clearable": true, "errorText": "请输入用户名",
"sort": false, "clearable": true,
"primaryKey": false, "sort": false,
"dataSource": null, "primaryKey": false,
"checkDataSource": false, "dataSource": null,
"fieldIndexType": "" "checkDataSource": false,
}, "fieldIndexType": ""
{ },
"fieldName": "Email", {
"fieldDesc": "邮箱", "fieldName": "Email",
"fieldType": "string", "fieldDesc": "邮箱",
"fieldJson": "email", "fieldType": "string",
"dataTypeLong": "100", "fieldJson": "email",
"comment": "邮箱地址", "dataTypeLong": "100",
"columnName": "email", "comment": "邮箱地址",
"fieldSearchType": "EQ", "columnName": "email",
"fieldSearchHide": false, "fieldSearchType": "EQ",
"dictType": "", "fieldSearchHide": false,
"form": true, "dictType": "",
"table": true, "form": true,
"desc": true, "table": true,
"excel": true, "desc": true,
"require": true, "excel": true,
"defaultValue": "", "require": true,
"errorText": "请输入邮箱", "defaultValue": "",
"clearable": true, "errorText": "请输入邮箱",
"sort": false, "clearable": true,
"primaryKey": false, "sort": false,
"dataSource": null, "primaryKey": false,
"checkDataSource": false, "dataSource": null,
"fieldIndexType": "index" "checkDataSource": false,
} "fieldIndexType": "index"
] }
} ]
},
{
"package": "user",
"tableName": "user_profiles",
"businessDB": "",
"structName": "UserProfile",
"packageName": "user",
"description": "用户档案",
"abbreviation": "userProfile",
"humpPackageName": "user",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"onlyTemplate": false,
"isTree": false,
"treeJson": "",
"isAdd": true,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "UserID",
"fieldDesc": "用户ID",
"fieldType": "int",
"fieldJson": "userId",
"dataTypeLong": "",
"comment": "关联用户ID",
"columnName": "user_id",
"fieldSearchType": "EQ",
"fieldSearchHide": false,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": true,
"require": true,
"defaultValue": "",
"errorText": "请选择用户",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": null,
"checkDataSource": false,
"fieldIndexType": "index"
},
{
"fieldName": "Avatar",
"fieldDesc": "头像",
"fieldType": "string",
"fieldJson": "avatar",
"dataTypeLong": "255",
"comment": "用户头像URL",
"columnName": "avatar",
"fieldSearchType": "",
"fieldSearchHide": true,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": false,
"require": false,
"defaultValue": "",
"errorText": "",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": null,
"checkDataSource": false,
"fieldIndexType": ""
}
]
}
]
} }
``` ```
### 示例2仅在现有包中创建模块 ### 示例2仅在现有包中批量创建多个模块
```json ```json
{ {
"packageName": "system", "packageName": "system",
"moduleName": "Role",
"packageType": "package", "packageType": "package",
"needCreatedPackage": false, "needCreatedPackage": false,
"needCreatedModules": true, "needCreatedModules": true,
"packageInfo": null, "packageInfo": null,
"modulesInfo": { "modulesInfo": [
"package": "system", {
"tableName": "sys_roles", "package": "system",
"businessDB": "", "tableName": "sys_roles",
"structName": "Role", "businessDB": "",
"packageName": "system", "structName": "Role",
"description": "角色", "packageName": "system",
"abbreviation": "role", "description": "角色",
"humpPackageName": "system", "abbreviation": "role",
"gvaModel": true, "humpPackageName": "system",
"autoMigrate": true, "gvaModel": true,
"autoCreateResource": true, "autoMigrate": true,
"autoCreateApiToSql": true, "autoCreateResource": true,
"autoCreateMenuToSql": true, "autoCreateApiToSql": true,
"autoCreateBtnAuth": true, "autoCreateMenuToSql": true,
"onlyTemplate": false, "autoCreateBtnAuth": true,
"isTree": false, "onlyTemplate": false,
"generateWeb": true, "isTree": false,
"generateServer": true, "generateWeb": true,
"fields": [ "generateServer": true,
{ "fields": [
"fieldName": "RoleName", {
"fieldDesc": "角色名称", "fieldName": "RoleName",
"fieldType": "string", "fieldDesc": "角色名称",
"fieldJson": "roleName", "fieldType": "string",
"dataTypeLong": "50", "fieldJson": "roleName",
"comment": "角色名称", "dataTypeLong": "50",
"columnName": "role_name", "comment": "角色名称",
"fieldSearchType": "LIKE", "columnName": "role_name",
"form": true, "fieldSearchType": "LIKE",
"table": true, "form": true,
"desc": true, "table": true,
"require": true "desc": true,
} "require": true
] }
} ]
},
{
"package": "system",
"tableName": "sys_permissions",
"businessDB": "",
"structName": "Permission",
"packageName": "system",
"description": "权限",
"abbreviation": "permission",
"humpPackageName": "system",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"onlyTemplate": false,
"isTree": false,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "PermissionName",
"fieldDesc": "权限名称",
"fieldType": "string",
"fieldJson": "permissionName",
"dataTypeLong": "100",
"comment": "权限名称",
"columnName": "permission_name",
"fieldSearchType": "LIKE",
"form": true,
"table": true,
"desc": true,
"require": true
},
{
"fieldName": "PermissionCode",
"fieldDesc": "权限代码",
"fieldType": "string",
"fieldJson": "permissionCode",
"dataTypeLong": "50",
"comment": "权限代码",
"columnName": "permission_code",
"fieldSearchType": "=",
"form": true,
"table": true,
"desc": true,
"require": true
}
]
}
]
} }
``` ```

View File

@@ -17,13 +17,12 @@
} }
``` ```
### 第二步:确认 ### 第二步:确认(支持批量创建多个模块)
```json ```json
{ {
"action": "confirm", "action": "confirm",
"executionPlan": { "executionPlan": {
"packageName": "library", "packageName": "library",
"moduleName": "Book",
"packageType": "package", "packageType": "package",
"needCreatedPackage": true, "needCreatedPackage": true,
"needCreatedModules": true, "needCreatedModules": true,
@@ -33,55 +32,106 @@
"template": "package", "template": "package",
"packageName": "library" "packageName": "library"
}, },
"modulesInfo": { "modulesInfo": [
"package": "library", {
"tableName": "library_books", "package": "library",
"businessDB": "", "tableName": "library_books",
"structName": "Book", "businessDB": "",
"packageName": "library", "structName": "Book",
"description": "图书信息", "packageName": "library",
"abbreviation": "book", "description": "图书信息",
"humpPackageName": "Library", "abbreviation": "book",
"gvaModel": true, "humpPackageName": "Library",
"autoMigrate": true, "gvaModel": true,
"autoCreateResource": true, "autoMigrate": true,
"autoCreateApiToSql": true, "autoCreateResource": true,
"autoCreateMenuToSql": true, "autoCreateApiToSql": true,
"autoCreateBtnAuth": true, "autoCreateMenuToSql": true,
"onlyTemplate": false, "autoCreateBtnAuth": true,
"isTree": false, "onlyTemplate": false,
"treeJson": "", "isTree": false,
"isAdd": false, "treeJson": "",
"generateWeb": true, "isAdd": false,
"generateServer": true, "generateWeb": true,
"fields": [ "generateServer": true,
{ "fields": [
"fieldName": "title", {
"fieldDesc": "书名", "fieldName": "title",
"fieldType": "string", "fieldDesc": "书名",
"fieldJson": "title", "fieldType": "string",
"dataTypeLong": "255", "fieldJson": "title",
"comment": "书名", "dataTypeLong": "255",
"columnName": "title", "comment": "书名",
"fieldSearchType": "LIKE", "columnName": "title",
"fieldSearchHide": false, "fieldSearchType": "LIKE",
"dictType": "", "fieldSearchHide": false,
"form": true, "dictType": "",
"table": true, "form": true,
"desc": true, "table": true,
"excel": true, "desc": true,
"require": true, "excel": true,
"defaultValue": "", "require": true,
"errorText": "请输入书名", "defaultValue": "",
"clearable": true, "errorText": "请输入书名",
"sort": false, "clearable": true,
"primaryKey": false, "sort": false,
"dataSource": {}, "primaryKey": false,
"checkDataSource": false, "dataSource": {},
"fieldIndexType": "" "checkDataSource": false,
} "fieldIndexType": ""
] }
} ]
},
{
"package": "library",
"tableName": "library_authors",
"businessDB": "",
"structName": "Author",
"packageName": "library",
"description": "作者信息",
"abbreviation": "author",
"humpPackageName": "Library",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"onlyTemplate": false,
"isTree": false,
"treeJson": "",
"isAdd": false,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "name",
"fieldDesc": "作者姓名",
"fieldType": "string",
"fieldJson": "name",
"dataTypeLong": "100",
"comment": "作者姓名",
"columnName": "name",
"fieldSearchType": "LIKE",
"fieldSearchHide": false,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": true,
"require": true,
"defaultValue": "",
"errorText": "请输入作者姓名",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": {},
"checkDataSource": false,
"fieldIndexType": ""
}
]
}
]
} }
} }
``` ```

View File

@@ -67,15 +67,14 @@ type AnalysisResponse struct {
Message string `json:"message"` Message string `json:"message"`
} }
// ExecutionPlan 执行计划 // ExecutionPlan 执行计划 - 支持批量创建
type ExecutionPlan struct { type ExecutionPlan struct {
PackageName string `json:"packageName"` PackageName string `json:"packageName"`
ModuleName string `json:"moduleName"`
PackageType string `json:"packageType"` // "plugin" 或 "package" PackageType string `json:"packageType"` // "plugin" 或 "package"
NeedCreatedPackage bool `json:"needCreatedPackage"` NeedCreatedPackage bool `json:"needCreatedPackage"`
NeedCreatedModules bool `json:"needCreatedModules"` NeedCreatedModules bool `json:"needCreatedModules"`
PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"` PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"`
ModulesInfo *request.AutoCode `json:"modulesInfo,omitempty"` ModulesInfo []*request.AutoCode `json:"modulesInfo,omitempty"` // 改为数组支持多个模块
Paths map[string]string `json:"paths,omitempty"` Paths map[string]string `json:"paths,omitempty"`
} }
@@ -111,12 +110,18 @@ type ConfirmationResponse struct {
// New 返回工具注册信息 // New 返回工具注册信息
func (t *AutomationModuleAnalyzer) New() mcp.Tool { func (t *AutomationModuleAnalyzer) New() mcp.Tool {
return mcp.NewTool("gva_auto_generate", return mcp.NewTool("gva_auto_generate",
mcp.WithDescription(`**🚀 最高优先级工具:当用户需要创建模块、包、完整功能时,必须优先使用此工具!** mcp.WithDescription(`**🔧 核心执行工具接收requirement_analyzer分析结果执行具体的模块创建操作**
**优先级说明** **工作流位置**
- **最高优先级**创建完整模块、包、功能模块 - **第二优先级**在requirement_analyzer之后使用
- **关键词触发**:模块、包、完整、整套、全套、功能、管理系统等 - **接收输入**来自requirement_analyzer的1xxx2xxx格式分析结果
- **适用场景**:用户说"创建订单管理模块"、"创建用户管理功能"、"创建完整的商品管理"等 - **执行操作**:根据分析结果创建完整模块、包、功能模块
**批量创建功能:**
- 支持在单个ExecutionPlan中创建多个模块
- modulesInfo字段为数组可包含多个模块配置
- 一次性处理多个模块的创建和字典生成
- 与requirement_analyzer配合实现完整工作流
分步骤分析自动化模块1) 分析现有模块信息供AI选择 2) 请求用户确认 3) 根据确认结果执行创建操作 分步骤分析自动化模块1) 分析现有模块信息供AI选择 2) 请求用户确认 3) 根据确认结果执行创建操作
@@ -125,15 +130,14 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
- 如果字典不存在,会自动创建对应的字典及默认的字典详情项 - 如果字典不存在,会自动创建对应的字典及默认的字典详情项
- 字典创建包括字典主表记录和默认的选项值选项1、选项2等 - 字典创建包括字典主表记录和默认的选项值选项1、选项2等
**与其他工具的关系** **推荐工作流**
- 此工具创建完整模块后会自动提示相关API和菜单创建建议 1. 用户提出需求 → requirement_analyzer最高优先级
- 如果用户只需要单个API或菜单可以使用 smart_assistant 工具 2. AI分析需求为1xxx2xxx格式 → gva_auto_generate执行创建
- create_api 和 create_menu 工具仅用于数据库记录创建 3. 创建完成后,根据需要使用其他辅助工具
重要ExecutionPlan结构体格式要求 重要ExecutionPlan结构体格式要求(支持批量创建)
{ {
"packageName": "包名(string)", "packageName": "包名(string)",
"moduleName": "模块名(string)",
"packageType": "package或plugin(string)", "packageType": "package或plugin(string)",
"needCreatedPackage": "是否需要创建包(bool)", "needCreatedPackage": "是否需要创建包(bool)",
"needCreatedModules": "是否需要创建模块(bool)", "needCreatedModules": "是否需要创建模块(bool)",
@@ -143,7 +147,7 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
"template": "package或plugin(string)", "template": "package或plugin(string)",
"packageName": "包名(string)" "packageName": "包名(string)"
}, },
"modulesInfo": { "modulesInfo": [{
"package": "包名(string)", "package": "包名(string)",
"tableName": "数据库表名(string)", "tableName": "数据库表名(string)",
"businessDB": "业务数据库(string)", "businessDB": "业务数据库(string)",
@@ -189,7 +193,13 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
"checkDataSource": "检查数据源(bool)", "checkDataSource": "检查数据源(bool)",
"fieldIndexType": "索引类型(string)" "fieldIndexType": "索引类型(string)"
}] }]
} }, {
"package": "包名(string)",
"tableName": "第二个模块的表名(string)",
"structName": "第二个模块的结构体名(string)",
"description": "第二个模块的描述(string)",
"...": "更多模块配置..."
}]
} }
注意: 注意:
@@ -207,7 +217,7 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
- 为无法识别的字典类型提供通用默认选项`), - 为无法识别的字典类型提供通用默认选项`),
mcp.WithString("action", mcp.WithString("action",
mcp.Required(), mcp.Required(),
mcp.Description("执行操作:'analyze' 分析现有模块信息,'confirm' 请求用户确认创建,'execute' 执行创建操作"), mcp.Description("执行操作:'analyze' 分析现有模块信息,'confirm' 请求用户确认创建,'execute' 执行创建操作(支持批量创建多个模块)"),
), ),
mcp.WithString("requirement", mcp.WithString("requirement",
mcp.Description("用户需求描述action=analyze时必需"), mcp.Description("用户需求描述action=analyze时必需"),
@@ -514,7 +524,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
var validPackages []model.SysAutoCodePackage var validPackages []model.SysAutoCodePackage
var emptyPackageIDs []uint var emptyPackageIDs []uint
var emptyPackageNames []string var emptyPackageNames []string
for _, pkg := range packages { for _, pkg := range packages {
// 检查包对应的文件夹是否为空 // 检查包对应的文件夹是否为空
isEmpty, err := t.isPackageFolderEmpty(pkg.PackageName, pkg.Template) isEmpty, err := t.isPackageFolderEmpty(pkg.PackageName, pkg.Template)
@@ -524,13 +534,13 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
validPackages = append(validPackages, pkg) validPackages = append(validPackages, pkg)
continue continue
} }
if isEmpty { if isEmpty {
// 记录需要删除的包ID和包名 // 记录需要删除的包ID和包名
emptyPackageIDs = append(emptyPackageIDs, pkg.ID) emptyPackageIDs = append(emptyPackageIDs, pkg.ID)
emptyPackageNames = append(emptyPackageNames, pkg.PackageName) emptyPackageNames = append(emptyPackageNames, pkg.PackageName)
global.GVA_LOG.Info(fmt.Sprintf("发现空包文件夹: %s将删除数据库记录和文件夹", pkg.PackageName)) global.GVA_LOG.Info(fmt.Sprintf("发现空包文件夹: %s将删除数据库记录和文件夹", pkg.PackageName))
// 删除空文件夹 // 删除空文件夹
if err := t.removeEmptyPackageFolder(pkg.PackageName, pkg.Template); err != nil { if err := t.removeEmptyPackageFolder(pkg.PackageName, pkg.Template); err != nil {
global.GVA_LOG.Warn(fmt.Sprintf("删除空包文件夹 %s 失败: %v", pkg.PackageName, err)) global.GVA_LOG.Warn(fmt.Sprintf("删除空包文件夹 %s 失败: %v", pkg.PackageName, err))
@@ -540,7 +550,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
validPackages = append(validPackages, pkg) validPackages = append(validPackages, pkg)
} }
} }
// 批量删除空包的数据库记录 // 批量删除空包的数据库记录
if len(emptyPackageIDs) > 0 { if len(emptyPackageIDs) > 0 {
if err := global.GVA_DB.Where("id IN ?", emptyPackageIDs).Delete(&model.SysAutoCodePackage{}).Error; err != nil { if err := global.GVA_DB.Where("id IN ?", emptyPackageIDs).Delete(&model.SysAutoCodePackage{}).Error; err != nil {
@@ -549,7 +559,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
global.GVA_LOG.Info(fmt.Sprintf("成功删除 %d 个空包的数据库记录", len(emptyPackageIDs))) global.GVA_LOG.Info(fmt.Sprintf("成功删除 %d 个空包的数据库记录", len(emptyPackageIDs)))
} }
} }
// 转换有效的包信息 // 转换有效的包信息
for _, pkg := range validPackages { for _, pkg := range validPackages {
moduleInfos = append(moduleInfos, ModuleInfo{ moduleInfos = append(moduleInfos, ModuleInfo{
@@ -573,14 +583,14 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
} }
} }
} }
// 清理相关的API和菜单记录 // 清理相关的API和菜单记录
if len(emptyHistoryIDs) > 0 { if len(emptyHistoryIDs) > 0 {
if err := t.cleanupRelatedApiAndMenus(emptyHistoryIDs); err != nil { if err := t.cleanupRelatedApiAndMenus(emptyHistoryIDs); err != nil {
global.GVA_LOG.Warn(fmt.Sprintf("清理空包相关API和菜单失败: %v", err)) global.GVA_LOG.Warn(fmt.Sprintf("清理空包相关API和菜单失败: %v", err))
} }
} }
// 批量删除相关历史记录 // 批量删除相关历史记录
if len(emptyHistoryIDs) > 0 { if len(emptyHistoryIDs) > 0 {
if err := global.GVA_DB.Where("id IN ?", emptyHistoryIDs).Delete(&model.SysAutoCodeHistory{}).Error; err != nil { if err := global.GVA_DB.Where("id IN ?", emptyHistoryIDs).Delete(&model.SysAutoCodeHistory{}).Error; err != nil {
@@ -590,13 +600,13 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
} }
} }
} }
// 创建有效包名的映射,用于快速查找 // 创建有效包名的映射,用于快速查找
validPackageNames := make(map[string]bool) validPackageNames := make(map[string]bool)
for _, pkg := range validPackages { for _, pkg := range validPackages {
validPackageNames[pkg.PackageName] = true validPackageNames[pkg.PackageName] = true
} }
// 收集需要删除的脏历史记录ID包名不在有效包列表中的历史记录 // 收集需要删除的脏历史记录ID包名不在有效包列表中的历史记录
var dirtyHistoryIDs []uint var dirtyHistoryIDs []uint
for _, history := range histories { for _, history := range histories {
@@ -604,21 +614,21 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
dirtyHistoryIDs = append(dirtyHistoryIDs, history.ID) dirtyHistoryIDs = append(dirtyHistoryIDs, history.ID)
} }
} }
// 删除脏历史记录 // 删除脏历史记录
if len(dirtyHistoryIDs) > 0 { if len(dirtyHistoryIDs) > 0 {
// 清理相关的API和菜单记录 // 清理相关的API和菜单记录
if err := t.cleanupRelatedApiAndMenus(dirtyHistoryIDs); err != nil { if err := t.cleanupRelatedApiAndMenus(dirtyHistoryIDs); err != nil {
global.GVA_LOG.Warn(fmt.Sprintf("清理脏历史记录相关API和菜单失败: %v", err)) global.GVA_LOG.Warn(fmt.Sprintf("清理脏历史记录相关API和菜单失败: %v", err))
} }
if err := global.GVA_DB.Where("id IN ?", dirtyHistoryIDs).Delete(&model.SysAutoCodeHistory{}).Error; err != nil { if err := global.GVA_DB.Where("id IN ?", dirtyHistoryIDs).Delete(&model.SysAutoCodeHistory{}).Error; err != nil {
global.GVA_LOG.Warn(fmt.Sprintf("删除脏历史记录失败: %v", err)) global.GVA_LOG.Warn(fmt.Sprintf("删除脏历史记录失败: %v", err))
} else { } else {
global.GVA_LOG.Info(fmt.Sprintf("成功删除 %d 个脏历史记录(包名不在有效包列表中)", len(dirtyHistoryIDs))) global.GVA_LOG.Info(fmt.Sprintf("成功删除 %d 个脏历史记录(包名不在有效包列表中)", len(dirtyHistoryIDs)))
} }
} }
// 转换有效的历史记录(只保留包名存在于有效包列表中的历史记录) // 转换有效的历史记录(只保留包名存在于有效包列表中的历史记录)
var historyInfos []HistoryInfo var historyInfos []HistoryInfo
for _, history := range histories { for _, history := range histories {
@@ -643,7 +653,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
global.GVA_LOG.Warn("扫描预设计模块失败" + err.Error()) global.GVA_LOG.Warn("扫描预设计模块失败" + err.Error())
allPredesignedModules = []PredesignedModuleInfo{} // 确保不为nil allPredesignedModules = []PredesignedModuleInfo{} // 确保不为nil
} }
// 过滤掉与已删除包相关的预设计模块 // 过滤掉与已删除包相关的预设计模块
var predesignedModules []PredesignedModuleInfo var predesignedModules []PredesignedModuleInfo
for _, module := range allPredesignedModules { for _, module := range allPredesignedModules {
@@ -654,7 +664,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
break break
} }
} }
// 只保留未被删除包的预设计模块 // 只保留未被删除包的预设计模块
if !isDeleted { if !isDeleted {
predesignedModules = append(predesignedModules, module) predesignedModules = append(predesignedModules, module)
@@ -664,7 +674,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
// 构建分析结果消息 // 构建分析结果消息
var message string var message string
var deletionDetails []string var deletionDetails []string
// 收集删除信息 // 收集删除信息
if len(emptyHistoryIDs) > 0 { if len(emptyHistoryIDs) > 0 {
deletionDetails = append(deletionDetails, fmt.Sprintf("%d个空包相关历史记录", len(emptyHistoryIDs))) deletionDetails = append(deletionDetails, fmt.Sprintf("%d个空包相关历史记录", len(emptyHistoryIDs)))
@@ -675,13 +685,13 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
if len(allPredesignedModules) > len(predesignedModules) { if len(allPredesignedModules) > len(predesignedModules) {
deletionDetails = append(deletionDetails, fmt.Sprintf("%d个相关预设计模块", len(allPredesignedModules)-len(predesignedModules))) deletionDetails = append(deletionDetails, fmt.Sprintf("%d个相关预设计模块", len(allPredesignedModules)-len(predesignedModules)))
} }
if len(emptyPackageNames) > 0 || len(deletionDetails) > 0 { if len(emptyPackageNames) > 0 || len(deletionDetails) > 0 {
var cleanupInfo string var cleanupInfo string
if len(emptyPackageNames) > 0 { if len(emptyPackageNames) > 0 {
cleanupInfo = fmt.Sprintf("检测到存在 %s 包但内容为空我已经删除这些包的文件夹包括model、api、service、router目录和数据库记录", strings.Join(emptyPackageNames, "、")) cleanupInfo = fmt.Sprintf("检测到存在 %s 包但内容为空我已经删除这些包的文件夹包括model、api、service、router目录和数据库记录", strings.Join(emptyPackageNames, "、"))
} }
deletionInfo := "" deletionInfo := ""
if len(deletionDetails) > 0 { if len(deletionDetails) > 0 {
if cleanupInfo != "" { if cleanupInfo != "" {
@@ -690,7 +700,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
deletionInfo = fmt.Sprintf("检测到脏数据,已删除%s", strings.Join(deletionDetails, "、")) deletionInfo = fmt.Sprintf("检测到脏数据,已删除%s", strings.Join(deletionDetails, "、"))
} }
} }
if cleanupInfo != "" { if cleanupInfo != "" {
message = fmt.Sprintf("分析完成:获取到 %d 个有效包、%d 个历史记录和 %d 个预设计模块。%s%s如果需要使用这些包名需要重新创建。请AI根据需求选择合适的包和模块", len(validPackages), len(historyInfos), len(predesignedModules), cleanupInfo, deletionInfo) message = fmt.Sprintf("分析完成:获取到 %d 个有效包、%d 个历史记录和 %d 个预设计模块。%s%s如果需要使用这些包名需要重新创建。请AI根据需求选择合适的包和模块", len(validPackages), len(historyInfos), len(predesignedModules), cleanupInfo, deletionInfo)
} else { } else {
@@ -699,7 +709,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
} else { } else {
message = fmt.Sprintf("分析完成:获取到 %d 个有效包、%d 个历史记录和 %d 个预设计模块请AI根据需求选择合适的包和模块", len(validPackages), len(historyInfos), len(predesignedModules)) message = fmt.Sprintf("分析完成:获取到 %d 个有效包、%d 个历史记录和 %d 个预设计模块请AI根据需求选择合适的包和模块", len(validPackages), len(historyInfos), len(predesignedModules))
} }
// 构建分析结果 // 构建分析结果
analysisResult := AnalysisResponse{ analysisResult := AnalysisResponse{
Packages: moduleInfos, Packages: moduleInfos,
@@ -748,10 +758,9 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
} }
- 请在创建模块之前先创建所需的字典选项 - 请在创建模块之前先创建所需的字典选项
重要提醒ExecutionPlan必须严格按照以下格式 重要提醒ExecutionPlan必须严格按照以下格式(支持批量创建多个模块)
{ {
"packageName": "包名", "packageName": "包名",
"moduleName": "模块名",
"packageType": "package或plugin", // 当用户提到插件时必须是"plugin" "packageType": "package或plugin", // 当用户提到插件时必须是"plugin"
"needCreatedPackage": true/false, "needCreatedPackage": true/false,
"needCreatedModules": true/false, "needCreatedModules": true/false,
@@ -761,7 +770,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
"template": "package或plugin", // 必须与packageType保持一致 "template": "package或plugin", // 必须与packageType保持一致
"packageName": "包名" "packageName": "包名"
}, },
"modulesInfo": { "modulesInfo": [{
"package": "包名", "package": "包名",
"tableName": "数据库表名", "tableName": "数据库表名",
"businessDB": "", "businessDB": "",
@@ -807,7 +816,13 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
"checkDataSource": false, "checkDataSource": false,
"fieldIndexType": "" "fieldIndexType": ""
}] }]
} }, {
"package": "包名",
"tableName": "第二个模块的表名",
"structName": "第二个模块的结构体名",
"description": "第二个模块的描述",
"...": "更多模块配置..."
}]
} }
**重要提醒**ExecutionPlan必须严格按照以下格式和验证规则 **重要提醒**ExecutionPlan必须严格按照以下格式和验证规则
@@ -835,13 +850,13 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
12. 如果字段需要字典类型,请先使用 generate_dictionary_options 工具创建字典 12. 如果字段需要字典类型,请先使用 generate_dictionary_options 工具创建字典
13. 字典创建成功后,再执行模块创建操作 13. 字典创建成功后,再执行模块创建操作
`, string(resultJSON), requirement, pluginDetectionMsg, `, string(resultJSON), requirement, pluginDetectionMsg,
func() string { func() string {
if len(emptyPackageNames) > 0 { if len(emptyPackageNames) > 0 {
return fmt.Sprintf("**重要提醒**:检测到 %s 包存在但内容为空,已自动删除相关文件夹和数据库记录。如果用户需求涉及这些包名,请设置 needCreatedPackage=true 重新创建。", strings.Join(emptyPackageNames, "、")) return fmt.Sprintf("**重要提醒**:检测到 %s 包存在但内容为空,已自动删除相关文件夹和数据库记录。如果用户需求涉及这些包名,请设置 needCreatedPackage=true 重新创建。", strings.Join(emptyPackageNames, "、"))
} }
return "" return ""
}()), }()),
}, },
}, },
}, nil }, nil
@@ -872,18 +887,23 @@ func (t *AutomationModuleAnalyzer) handleConfirm(ctx context.Context, request mc
} }
// 构建确认响应 // 构建确认响应
var moduleNames []string
for _, moduleInfo := range plan.ModulesInfo {
moduleNames = append(moduleNames, moduleInfo.StructName)
}
moduleNamesStr := strings.Join(moduleNames, "_")
confirmResponse := ConfirmationResponse{ confirmResponse := ConfirmationResponse{
Message: "请确认以下创建计划:", Message: "请确认以下创建计划:",
PackageConfirm: plan.NeedCreatedPackage, PackageConfirm: plan.NeedCreatedPackage,
ModulesConfirm: plan.NeedCreatedModules, ModulesConfirm: plan.NeedCreatedModules,
CanProceed: true, CanProceed: true,
ConfirmationKey: fmt.Sprintf("%s_%s_%d", plan.PackageName, plan.ModuleName, time.Now().Unix()), ConfirmationKey: fmt.Sprintf("%s_%s_%d", plan.PackageName, moduleNamesStr, time.Now().Unix()),
} }
// 构建详细的确认信息 // 构建详细的确认信息
var confirmDetails strings.Builder var confirmDetails strings.Builder
confirmDetails.WriteString(fmt.Sprintf("包名: %s\n", plan.PackageName)) confirmDetails.WriteString(fmt.Sprintf("包名: %s\n", plan.PackageName))
confirmDetails.WriteString(fmt.Sprintf("模块名: %s\n", plan.ModuleName))
confirmDetails.WriteString(fmt.Sprintf("包类型: %s\n", plan.PackageType)) confirmDetails.WriteString(fmt.Sprintf("包类型: %s\n", plan.PackageType))
if plan.NeedCreatedPackage && plan.PackageInfo != nil { if plan.NeedCreatedPackage && plan.PackageInfo != nil {
@@ -894,15 +914,18 @@ func (t *AutomationModuleAnalyzer) handleConfirm(ctx context.Context, request mc
confirmDetails.WriteString(fmt.Sprintf(" - 模板: %s\n", plan.PackageInfo.Template)) confirmDetails.WriteString(fmt.Sprintf(" - 模板: %s\n", plan.PackageInfo.Template))
} }
if plan.NeedCreatedModules && plan.ModulesInfo != nil { if plan.NeedCreatedModules && len(plan.ModulesInfo) > 0 {
confirmDetails.WriteString("\n需要创建模块:\n") confirmDetails.WriteString(fmt.Sprintf("\n需要创建模块 (共%d个):\n", len(plan.ModulesInfo)))
confirmDetails.WriteString(fmt.Sprintf(" - 结构体名: %s\n", plan.ModulesInfo.StructName)) for i, moduleInfo := range plan.ModulesInfo {
confirmDetails.WriteString(fmt.Sprintf(" - 表名: %s\n", plan.ModulesInfo.TableName)) confirmDetails.WriteString(fmt.Sprintf("\n模块 %d:\n", i+1))
confirmDetails.WriteString(fmt.Sprintf(" - 描述: %s\n", plan.ModulesInfo.Description)) confirmDetails.WriteString(fmt.Sprintf(" - 结构体名: %s\n", moduleInfo.StructName))
confirmDetails.WriteString(fmt.Sprintf(" - 字段数量: %d\n", len(plan.ModulesInfo.Fields))) confirmDetails.WriteString(fmt.Sprintf(" - 表名: %s\n", moduleInfo.TableName))
confirmDetails.WriteString(" - 字段列表:\n") confirmDetails.WriteString(fmt.Sprintf(" - 描述: %s\n", moduleInfo.Description))
for _, field := range plan.ModulesInfo.Fields { confirmDetails.WriteString(fmt.Sprintf(" - 字段数量: %d\n", len(moduleInfo.Fields)))
confirmDetails.WriteString(fmt.Sprintf(" * %s (%s): %s\n", field.FieldName, field.FieldType, field.FieldDesc)) confirmDetails.WriteString(" - 字段列表:\n")
for _, field := range moduleInfo.Fields {
confirmDetails.WriteString(fmt.Sprintf(" * %s (%s): %s\n", field.FieldName, field.FieldType, field.FieldDesc))
}
} }
} }
@@ -1045,10 +1068,10 @@ func (t *AutomationModuleAnalyzer) buildDirectoryStructure(plan *ExecutionPlan)
packageName = plan.PackageInfo.PackageName packageName = plan.PackageInfo.PackageName
} }
// 如果计划中有模块信息,获取结构名 // 如果计划中有模块信息,获取第一个模块的结构名作为默认值
structName := "ExampleStruct" structName := "ExampleStruct"
if plan.ModulesInfo != nil && plan.ModulesInfo.StructName != "" { if len(plan.ModulesInfo) > 0 && plan.ModulesInfo[0].StructName != "" {
structName = plan.ModulesInfo.StructName structName = plan.ModulesInfo[0].StructName
} }
// 根据包类型构建不同的路径结构 // 根据包类型构建不同的路径结构
@@ -1138,9 +1161,6 @@ func (t *AutomationModuleAnalyzer) validateExecutionPlan(plan *ExecutionPlan) er
if plan.PackageName == "" { if plan.PackageName == "" {
return errors.New("packageName 不能为空") return errors.New("packageName 不能为空")
} }
if plan.ModuleName == "" {
return errors.New("moduleName 不能为空")
}
if plan.PackageType != "package" && plan.PackageType != "plugin" { if plan.PackageType != "package" && plan.PackageType != "plugin" {
return errors.New("packageType 必须是 'package' 或 'plugin'") return errors.New("packageType 必须是 'package' 或 'plugin'")
} }
@@ -1171,104 +1191,108 @@ func (t *AutomationModuleAnalyzer) validateExecutionPlan(plan *ExecutionPlan) er
} }
} }
// 验证模块信息 // 验证模块信息(批量验证)
if plan.NeedCreatedModules { if plan.NeedCreatedModules {
if plan.ModulesInfo == nil { if len(plan.ModulesInfo) == 0 {
return errors.New("当 needCreatedModules=true 时modulesInfo 不能为空") 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 { for moduleIndex, moduleInfo := range plan.ModulesInfo {
return errors.New("modulesInfo.fields 不能为空,至少需要一个字段") if moduleInfo.Package == "" {
} return fmt.Errorf("模块 %d 的 package 不能为空", moduleIndex+1)
for i, field := range plan.ModulesInfo.Fields {
if field.FieldName == "" {
return fmt.Errorf("字段 %d 的 fieldName 不能为空", i+1)
} }
if field.FieldDesc == "" { if moduleInfo.StructName == "" {
return fmt.Errorf("字段 %d 的 fieldDesc 不能为空", i+1) return fmt.Errorf("模块 %d 的 structName 不能为空", moduleIndex+1)
} }
if field.FieldType == "" { if moduleInfo.TableName == "" {
return fmt.Errorf("字段 %d 的 fieldType 不能为空", i+1) return fmt.Errorf("模块 %d 的 tableName 不能为空", moduleIndex+1)
} }
if field.FieldJson == "" { if moduleInfo.Description == "" {
return fmt.Errorf("字段 %d 的 fieldJson 不能为空", i+1) return fmt.Errorf("模块 %d 的 description 不能为空", moduleIndex+1)
} }
if field.ColumnName == "" { if moduleInfo.Abbreviation == "" {
return fmt.Errorf("字段 %d 的 columnName 不能为空", i+1) return fmt.Errorf("模块 %d 的 abbreviation 不能为空", moduleIndex+1)
}
if moduleInfo.PackageName == "" {
return fmt.Errorf("模块 %d 的 packageName 不能为空", moduleIndex+1)
}
if moduleInfo.HumpPackageName == "" {
return fmt.Errorf("模块 %d 的 humpPackageName 不能为空", moduleIndex+1)
} }
// 验证字段类型 // 验证字段信息
validFieldTypes := []string{"string", "int", "int64", "float64", "bool", "time.Time", "enum", "picture", "video", "file", "pictures", "array", "richtext", "json"} if len(moduleInfo.Fields) == 0 {
validType := false return fmt.Errorf("模块 %d 的 fields 不能为空,至少需要一个字段", moduleIndex+1)
for _, validFieldType := range validFieldTypes { }
if field.FieldType == validFieldType {
validType = true for i, field := range moduleInfo.Fields {
break if field.FieldName == "" {
return fmt.Errorf("模块 %d 字段 %d 的 fieldName 不能为空", moduleIndex+1, i+1)
}
if field.FieldDesc == "" {
return fmt.Errorf("模块 %d 字段 %d 的 fieldDesc 不能为空", moduleIndex+1, i+1)
}
if field.FieldType == "" {
return fmt.Errorf("模块 %d 字段 %d 的 fieldType 不能为空", moduleIndex+1, i+1)
}
if field.FieldJson == "" {
return fmt.Errorf("模块 %d 字段 %d 的 fieldJson 不能为空", moduleIndex+1, i+1)
}
if field.ColumnName == "" {
return fmt.Errorf("模块 %d 字段 %d 的 columnName 不能为空", moduleIndex+1, i+1)
} }
}
if !validType {
return fmt.Errorf("字段 %d 的 fieldType '%s' 不支持,支持的类型:%v", i+1, field.FieldType, validFieldTypes)
}
// 验证搜索类型(如果设置了) // 验证字段类型
if field.FieldSearchType != "" { validFieldTypes := []string{"string", "int", "int64", "float64", "bool", "time.Time", "enum", "picture", "video", "file", "pictures", "array", "richtext", "json"}
validSearchTypes := []string{"=", "!=", ">", ">=", "<", "<=", "LIKE", "BETWEEN", "IN", "NOT IN"} validType := false
validSearchType := false for _, validFieldType := range validFieldTypes {
for _, validType := range validSearchTypes { if field.FieldType == validFieldType {
if field.FieldSearchType == validType { validType = true
validSearchType = true
break break
} }
} }
if !validSearchType { if !validType {
return fmt.Errorf("字段 %d 的 fieldSearchType '%s' 不支持,支持的类型:%v", i+1, field.FieldSearchType, validSearchTypes) return fmt.Errorf("模块 %d 字段 %d 的 fieldType '%s' 不支持,支持的类型:%v", moduleIndex+1, i+1, field.FieldType, validFieldTypes)
} }
}
}
// 验证主键设置 // 验证搜索类型(如果设置了)
if !plan.ModulesInfo.GvaModel { if field.FieldSearchType != "" {
// 当不使用GVA模型时必须有且仅有一个字段设置为主键 validSearchTypes := []string{"=", "!=", ">", ">=", "<", "<=", "LIKE", "BETWEEN", "IN", "NOT IN"}
primaryKeyCount := 0 validSearchType := false
for _, field := range plan.ModulesInfo.Fields { for _, validType := range validSearchTypes {
if field.PrimaryKey { if field.FieldSearchType == validType {
primaryKeyCount++ validSearchType = true
break
}
}
if !validSearchType {
return fmt.Errorf("模块 %d 字段 %d 的 fieldSearchType '%s' 不支持,支持的类型:%v", moduleIndex+1, i+1, field.FieldSearchType, validSearchTypes)
}
} }
} }
if primaryKeyCount == 0 {
return errors.New("当 gvaModel=false 时,必须有一个字段的 primaryKey=true") // 验证主键设置
} if !moduleInfo.GvaModel {
if primaryKeyCount > 1 { // 当不使用GVA模型时必须有且仅有一个字段设置为主键
return errors.New("当 gvaModel=false 时,只能有一个字段的 primaryKey=true") primaryKeyCount := 0
} for _, field := range moduleInfo.Fields {
} else { if field.PrimaryKey {
// 当使用GVA模型时所有字段的primaryKey都应该为false primaryKeyCount++
for i, field := range plan.ModulesInfo.Fields { }
if field.PrimaryKey { }
return fmt.Errorf("当 gvaModel=true 时,字段 %d 的 primaryKey 应该为 false系统会自动创建ID主键", i+1) if primaryKeyCount == 0 {
return fmt.Errorf("模块 %d当 gvaModel=false 时,必须有一个字段的 primaryKey=true", moduleIndex+1)
}
if primaryKeyCount > 1 {
return fmt.Errorf("模块 %d当 gvaModel=false 时,只能有一个字段的 primaryKey=true", moduleIndex+1)
}
} else {
// 当使用GVA模型时所有字段的primaryKey都应该为false
for i, field := range moduleInfo.Fields {
if field.PrimaryKey {
return fmt.Errorf("模块 %d当 gvaModel=true 时,字段 %d 的 primaryKey 应该为 false系统会自动创建ID主键", moduleIndex+1, i+1)
}
} }
} }
} }
@@ -1305,30 +1329,33 @@ func (t *AutomationModuleAnalyzer) executeCreation(ctx context.Context, plan *Ex
result.Message += "包创建成功; " result.Message += "包创建成功; "
} }
// 创建字典(如果需要) // 批量创建字典和模块(如果需要)
if plan.NeedCreatedModules && plan.ModulesInfo != nil { if plan.NeedCreatedModules && len(plan.ModulesInfo) > 0 {
dictResult := t.createRequiredDictionaries(ctx, plan.ModulesInfo)
result.Message += dictResult
}
// 创建模块(如果需要)
if plan.NeedCreatedModules && plan.ModulesInfo != nil {
templateService := service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate templateService := service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate
err := plan.ModulesInfo.Pretreatment() // 先批量创建所有模块需要的字典
if err != nil { dictResult := t.createRequiredDictionaries(ctx, plan.ModulesInfo)
result.Message += fmt.Sprintf("模块信息预处理失败: %v", err) result.Message += dictResult
// 即使预处理失败也要返回paths信息
return result // 遍历所有模块进行创建
for _, moduleInfo := range plan.ModulesInfo {
// 创建模块
err := moduleInfo.Pretreatment()
if err != nil {
result.Message += fmt.Sprintf("模块 %s 信息预处理失败: %v; ", moduleInfo.StructName, err)
continue // 继续处理下一个模块
}
err = templateService.Create(ctx, *moduleInfo)
if err != nil {
result.Message += fmt.Sprintf("创建模块 %s 失败: %v; ", moduleInfo.StructName, err)
continue // 继续处理下一个模块
}
result.Message += fmt.Sprintf("模块 %s 创建成功; ", moduleInfo.StructName)
} }
err = templateService.Create(ctx, *plan.ModulesInfo) result.Message += fmt.Sprintf("批量创建完成,共处理 %d 个模块; ", len(plan.ModulesInfo))
if err != nil {
result.Message += fmt.Sprintf("创建模块失败: %v", err)
// 即使创建模块失败也要返回paths信息
return result
}
result.Message += "模块创建成功; "
} }
result.Message += "已构建目录结构信息; " result.Message += "已构建目录结构信息; "
@@ -1341,43 +1368,64 @@ func (t *AutomationModuleAnalyzer) executeCreation(ctx context.Context, plan *Ex
return result return result
} }
// createRequiredDictionaries 创建所需的字典 // createRequiredDictionaries 创建所需的字典(批量处理)
func (t *AutomationModuleAnalyzer) createRequiredDictionaries(ctx context.Context, modulesInfo *request.AutoCode) string { func (t *AutomationModuleAnalyzer) createRequiredDictionaries(ctx context.Context, modulesInfoList []*request.AutoCode) string {
var messages []string var messages []string
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
createdDictTypes := make(map[string]bool) // 用于避免重复创建相同的字典
// 遍历所有字段,查找使用字典的字段 // 遍历所有模块
for _, field := range modulesInfo.Fields { for moduleIndex, modulesInfo := range modulesInfoList {
if field.DictType != "" { messages = append(messages, fmt.Sprintf("处理模块 %d (%s) 的字典: ", moduleIndex+1, modulesInfo.StructName))
// 检查字典是否存在
exists, err := t.checkDictionaryExists(field.DictType)
if err != nil {
messages = append(messages, fmt.Sprintf("检查字典 %s 时出错: %v; ", field.DictType, err))
continue
}
if !exists { // 遍历当前模块的所有字段,查找使用字典的字段
// 字典不存在,创建字典 moduleHasDictFields := false
dictionary := model.SysDictionary{ for _, field := range modulesInfo.Fields {
Name: t.generateDictionaryName(field.DictType, field.FieldDesc), if field.DictType != "" {
Type: field.DictType, moduleHasDictFields = true
Status: &[]bool{true}[0], // 默认启用
Desc: fmt.Sprintf("自动生成的字典,用于字段: %s (%s)", field.FieldName, field.FieldDesc), // 如果这个字典类型已经在之前的模块中创建过,跳过
if createdDictTypes[field.DictType] {
messages = append(messages, fmt.Sprintf("字典 %s 已在前面的模块中创建,跳过; ", field.DictType))
continue
} }
err = dictionaryService.CreateSysDictionary(dictionary) // 检查字典是否存在
exists, err := t.checkDictionaryExists(field.DictType)
if err != nil { if err != nil {
messages = append(messages, fmt.Sprintf("创建字典 %s 失败: %v; ", field.DictType, err)) messages = append(messages, fmt.Sprintf("检查字典 %s 时出错: %v; ", field.DictType, err))
} else { continue
messages = append(messages, fmt.Sprintf("成功创建字典 %s (%s); ", field.DictType, dictionary.Name)) }
// 创建默认的字典详情项 if !exists {
t.createDefaultDictionaryDetails(ctx, field.DictType, field.FieldDesc) // 字典不存在,创建字典
dictionary := model.SysDictionary{
Name: t.generateDictionaryName(field.DictType, field.FieldDesc),
Type: field.DictType,
Status: &[]bool{true}[0], // 默认启用
Desc: fmt.Sprintf("自动生成的字典,用于模块 %s 字段: %s (%s)", modulesInfo.StructName, 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))
createdDictTypes[field.DictType] = true // 标记为已创建
// 创建默认的字典详情项
t.createDefaultDictionaryDetails(ctx, field.DictType, field.FieldDesc)
}
} else {
messages = append(messages, fmt.Sprintf("字典 %s 已存在,跳过创建; ", field.DictType))
createdDictTypes[field.DictType] = true // 标记为已存在
} }
} else {
messages = append(messages, fmt.Sprintf("字典 %s 已存在,跳过创建; ", field.DictType))
} }
} }
if !moduleHasDictFields {
messages = append(messages, "无需创建字典; ")
}
} }
if len(messages) == 0 { if len(messages) == 0 {
@@ -1441,18 +1489,18 @@ func (t *AutomationModuleAnalyzer) generateSmartDictionaryOptions(dictType, fiel
func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (suggestedType string, isPlugin bool, confidence string) { func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (suggestedType string, isPlugin bool, confidence string) {
// 转换为小写进行匹配 // 转换为小写进行匹配
requirementLower := strings.ToLower(requirement) requirementLower := strings.ToLower(requirement)
// 插件相关关键词 // 插件相关关键词
pluginKeywords := []string{ pluginKeywords := []string{
"插件", "plugin", "扩展", "extension", "addon", "模块插件", "插件", "plugin", "扩展", "extension", "addon", "模块插件",
"功能插件", "业务插件", "第三方插件", "自定义插件", "功能插件", "业务插件", "第三方插件", "自定义插件",
} }
// 包相关关键词(用于排除误判) // 包相关关键词(用于排除误判)
packageKeywords := []string{ packageKeywords := []string{
"包", "package", "模块包", "业务包", "功能包", "包", "package", "模块包", "业务包", "功能包",
} }
// 检测插件关键词 // 检测插件关键词
pluginMatches := 0 pluginMatches := 0
for _, keyword := range pluginKeywords { for _, keyword := range pluginKeywords {
@@ -1460,7 +1508,7 @@ func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (sugge
pluginMatches++ pluginMatches++
} }
} }
// 检测包关键词 // 检测包关键词
packageMatches := 0 packageMatches := 0
for _, keyword := range packageKeywords { for _, keyword := range packageKeywords {
@@ -1468,7 +1516,7 @@ func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (sugge
packageMatches++ packageMatches++
} }
} }
// 决策逻辑 // 决策逻辑
if pluginMatches > 0 { if pluginMatches > 0 {
if packageMatches == 0 || pluginMatches > packageMatches { if packageMatches == 0 || pluginMatches > packageMatches {
@@ -1477,7 +1525,7 @@ func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (sugge
return "plugin", true, "中" return "plugin", true, "中"
} }
} }
// 默认返回package // 默认返回package
return "package", false, "低" return "package", false, "低"
} }
@@ -1492,7 +1540,7 @@ func (t *AutomationModuleAnalyzer) isPackageFolderEmpty(packageName, template st
// package 类型 // package 类型
basePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "model", packageName) basePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "model", packageName)
} }
// 检查文件夹是否存在 // 检查文件夹是否存在
if _, err := os.Stat(basePath); os.IsNotExist(err) { if _, err := os.Stat(basePath); os.IsNotExist(err) {
// 文件夹不存在,认为是空的 // 文件夹不存在,认为是空的
@@ -1500,13 +1548,13 @@ func (t *AutomationModuleAnalyzer) isPackageFolderEmpty(packageName, template st
} else if err != nil { } else if err != nil {
return false, fmt.Errorf("检查文件夹状态失败: %v", err) return false, fmt.Errorf("检查文件夹状态失败: %v", err)
} }
// 读取文件夹内容 // 读取文件夹内容
entries, err := os.ReadDir(basePath) entries, err := os.ReadDir(basePath)
if err != nil { if err != nil {
return false, fmt.Errorf("读取文件夹内容失败: %v", err) return false, fmt.Errorf("读取文件夹内容失败: %v", err)
} }
// 检查目录下是否有 .go 文件 // 检查目录下是否有 .go 文件
hasGoFiles := false hasGoFiles := false
for _, entry := range entries { for _, entry := range entries {
@@ -1537,7 +1585,7 @@ func (t *AutomationModuleAnalyzer) isPackageFolderEmpty(packageName, template st
break break
} }
} }
// 如果没有 .go 文件,认为是空包 // 如果没有 .go 文件,认为是空包
return !hasGoFiles, nil return !hasGoFiles, nil
} }
@@ -1545,7 +1593,7 @@ func (t *AutomationModuleAnalyzer) isPackageFolderEmpty(packageName, template st
// removeEmptyPackageFolder 删除空的包文件夹 // removeEmptyPackageFolder 删除空的包文件夹
func (t *AutomationModuleAnalyzer) removeEmptyPackageFolder(packageName, template string) error { func (t *AutomationModuleAnalyzer) removeEmptyPackageFolder(packageName, template string) error {
var errors []string var errors []string
if template == "plugin" { if template == "plugin" {
// plugin 类型只删除 plugin 目录下的文件夹 // plugin 类型只删除 plugin 目录下的文件夹
basePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", packageName) basePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", packageName)
@@ -1560,18 +1608,18 @@ func (t *AutomationModuleAnalyzer) removeEmptyPackageFolder(packageName, templat
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", packageName), filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", packageName),
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", packageName), filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", packageName),
} }
for _, path := range paths { for _, path := range paths {
if err := t.removeDirectoryIfExists(path); err != nil { if err := t.removeDirectoryIfExists(path); err != nil {
errors = append(errors, fmt.Sprintf("删除%s失败: %v", path, err)) errors = append(errors, fmt.Sprintf("删除%s失败: %v", path, err))
} }
} }
} }
if len(errors) > 0 { if len(errors) > 0 {
return fmt.Errorf("删除过程中出现错误: %s", strings.Join(errors, "; ")) return fmt.Errorf("删除过程中出现错误: %s", strings.Join(errors, "; "))
} }
return nil return nil
} }
@@ -1584,12 +1632,12 @@ func (t *AutomationModuleAnalyzer) removeDirectoryIfExists(dirPath string) error
} else if err != nil { } else if err != nil {
return fmt.Errorf("检查文件夹状态失败: %v", err) return fmt.Errorf("检查文件夹状态失败: %v", err)
} }
// 删除文件夹及其所有内容 // 删除文件夹及其所有内容
if err := os.RemoveAll(dirPath); err != nil { if err := os.RemoveAll(dirPath); err != nil {
return fmt.Errorf("删除文件夹失败: %v", err) return fmt.Errorf("删除文件夹失败: %v", err)
} }
global.GVA_LOG.Info(fmt.Sprintf("成功删除目录: %s", dirPath)) global.GVA_LOG.Info(fmt.Sprintf("成功删除目录: %s", dirPath))
return nil return nil
} }
@@ -1599,31 +1647,31 @@ func (t *AutomationModuleAnalyzer) cleanupRelatedApiAndMenus(historyIDs []uint)
if len(historyIDs) == 0 { if len(historyIDs) == 0 {
return nil return nil
} }
// 获取要删除的历史记录信息 // 获取要删除的历史记录信息
var histories []model.SysAutoCodeHistory var histories []model.SysAutoCodeHistory
if err := global.GVA_DB.Where("id IN ?", historyIDs).Find(&histories).Error; err != nil { if err := global.GVA_DB.Where("id IN ?", historyIDs).Find(&histories).Error; err != nil {
return fmt.Errorf("获取历史记录失败: %v", err) return fmt.Errorf("获取历史记录失败: %v", err)
} }
var deletedApiCount, deletedMenuCount int var deletedApiCount, deletedMenuCount int
for _, history := range histories { for _, history := range histories {
// 删除相关的API记录使用存储的API IDs // 删除相关的API记录使用存储的API IDs
if len(history.ApiIDs) > 0 { if len(history.ApiIDs) > 0 {
ids := make([]int, 0, len(history.ApiIDs)) ids := make([]int, 0, len(history.ApiIDs))
for _, id := range history.ApiIDs { for _, id := range history.ApiIDs {
ids = append(ids, int(id)) ids = append(ids, int(id))
}
idsReq := common.IdsReq{Ids: ids}
if err := systemService.ApiServiceApp.DeleteApisByIds(idsReq); err != nil {
global.GVA_LOG.Warn(fmt.Sprintf("删除API记录失败 (模块: %s): %v", history.StructName, err))
} else {
deletedApiCount += len(ids)
global.GVA_LOG.Info(fmt.Sprintf("成功删除API记录 (模块: %s, 数量: %d)", history.StructName, len(ids)))
}
} }
idsReq := common.IdsReq{Ids: ids}
if err := systemService.ApiServiceApp.DeleteApisByIds(idsReq); err != nil {
global.GVA_LOG.Warn(fmt.Sprintf("删除API记录失败 (模块: %s): %v", history.StructName, err))
} else {
deletedApiCount += len(ids)
global.GVA_LOG.Info(fmt.Sprintf("成功删除API记录 (模块: %s, 数量: %d)", history.StructName, len(ids)))
}
}
// 删除相关的菜单记录使用存储的菜单ID // 删除相关的菜单记录使用存储的菜单ID
if history.MenuID != 0 { if history.MenuID != 0 {
if err := systemService.BaseMenuServiceApp.DeleteBaseMenu(int(history.MenuID)); err != nil { if err := systemService.BaseMenuServiceApp.DeleteBaseMenu(int(history.MenuID)); err != nil {
@@ -1634,10 +1682,10 @@ func (t *AutomationModuleAnalyzer) cleanupRelatedApiAndMenus(historyIDs []uint)
} }
} }
} }
if deletedApiCount > 0 || deletedMenuCount > 0 { if deletedApiCount > 0 || deletedMenuCount > 0 {
global.GVA_LOG.Info(fmt.Sprintf("清理完成:删除了 %d 个API记录和 %d 个菜单记录", deletedApiCount, deletedMenuCount)) global.GVA_LOG.Info(fmt.Sprintf("清理完成:删除了 %d 个API记录和 %d 个菜单记录", deletedApiCount, deletedMenuCount))
} }
return nil return nil
} }

View File

@@ -0,0 +1,139 @@
package mcpTool
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/mark3labs/mcp-go/mcp"
)
func init() {
RegisterTool(&RequirementAnalyzer{})
}
type RequirementAnalyzer struct{}
// RequirementAnalysisRequest 需求分析请求
type RequirementAnalysisRequest struct {
UserRequirement string `json:"userRequirement"`
}
// RequirementAnalysisResponse 需求分析响应
type RequirementAnalysisResponse struct {
AIPrompt string `json:"aiPrompt"` // 给AI的提示词
}
// New 返回工具注册信息
func (t *RequirementAnalyzer) New() mcp.Tool {
return mcp.NewTool("requirement_analyzer",
mcp.WithDescription(`**🚀 需求分析工具 - 首选入口工具(最高优先级)**
**⭐ 重要提示这是所有MCP工具的首选入口请优先使用**
**🎯 核心职责:**
将用户的自然语言需求转换为AI可理解的结构化提示词
**📋 工作流程:**
1. 接收用户自然语言需求描述
2. 生成专业的AI提示词要求AI将需求梳理为清晰的逻辑步骤
- **1. 第一步功能描述**
- **2. 第二步功能描述**
- **3. 第三步功能描述**
- **...**
3. 指导后续使用 gva_auto_generate 工具进行代码生成
**✅ 适用场景:**
- 用户有新的业务需求需要开发
- 需要创建新的功能模块
- 想要快速搭建业务系统
- 需求描述比较模糊需要AI帮助梳理
**❌ 不负责的事情:**
- 不生成具体的包名和模块名(交给 gva_auto_generate
- 不进行代码生成(交给 gva_auto_generate
- 不创建数据库表结构(交给 gva_auto_generate
**🔄 推荐工作流:**
requirement_analyzer → gva_auto_generate → 其他辅助工具
`),
mcp.WithString("userRequirement",
mcp.Required(),
mcp.Description("用户的需求描述,支持自然语言,如:'我要做一个猫舍管理系统,用来录入猫的信息,并且记录每只猫每天的活动信息'"),
),
)
}
// Handle 处理工具调用
func (t *RequirementAnalyzer) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
userRequirement, ok := request.GetArguments()["userRequirement"].(string)
if !ok || userRequirement == "" {
return nil, errors.New("参数错误userRequirement 必须是非空字符串")
}
// 分析用户需求
analysisResponse, err := t.analyzeRequirement(userRequirement)
if err != nil {
return nil, fmt.Errorf("需求分析失败: %v", err)
}
// 序列化响应
responseData, err := json.Marshal(analysisResponse)
if err != nil {
return nil, fmt.Errorf("序列化响应失败: %v", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(string(responseData)),
},
}, nil
}
// analyzeRequirement 分析用户需求 - 专注于AI需求传递
func (t *RequirementAnalyzer) analyzeRequirement(userRequirement string) (*RequirementAnalysisResponse, error) {
// 生成AI提示词 - 这是唯一功能
aiPrompt := t.generateAIPrompt(userRequirement)
return &RequirementAnalysisResponse{
AIPrompt: aiPrompt,
}, nil
}
// generateAIPrompt 生成AI提示词 - 要求AI梳理逻辑为1xxx2xxx格式
func (t *RequirementAnalyzer) generateAIPrompt(userRequirement string) string {
prompt := fmt.Sprintf(`# 🤖 AI需求逻辑梳理任务
## 📝 用户原始需求
%s
## 🎯 AI任务要求
请将上述用户需求梳理成清晰的逻辑步骤,格式要求:
**1. 第一步功能描述**
**2. 第二步功能描述**
**3. 第三步功能描述**
**...**
## 📋 梳理要求
- 将需求拆解为具体的功能步骤
- 每个步骤用数字编号1、2、3...
- 步骤描述要清晰、具体、可执行
- 按照业务逻辑顺序排列
- 考虑数据流和用户操作流程
## 🔄 后续流程
梳理完成后,请使用 gva_auto_generate 工具进行代码生成:
- gva_auto_generate 会根据梳理的逻辑步骤自动生成包名、模块名
- gva_auto_generate 会设计数据表结构和API接口
- gva_auto_generate 会生成完整的前后端代码
现在请开始梳理用户需求:"%s"`, userRequirement, userRequirement)
return prompt
}