Merge pull request #2065 from flipped-aurora/2.8.5

支持多模块mcp,精细化api和menu的创建规则。
This commit is contained in:
PiexlMax(奇淼
2025-08-05 18:43:21 +08:00
committed by GitHub
8 changed files with 977 additions and 425 deletions

View File

@@ -27,8 +27,6 @@ import (
func (b *BaseApi) Login(c *gin.Context) { func (b *BaseApi) Login(c *gin.Context) {
var l systemReq.Login var l systemReq.Login
err := c.ShouldBindJSON(&l) err := c.ShouldBindJSON(&l)
key := c.ClientIP()
if err != nil { if err != nil {
response.FailWithMessage(err.Error(), c) response.FailWithMessage(err.Error(), c)
return return
@@ -39,6 +37,7 @@ func (b *BaseApi) Login(c *gin.Context) {
return return
} }
key := c.ClientIP()
// 判断验证码是否开启 // 判断验证码是否开启
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数 openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间 openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
@@ -48,30 +47,30 @@ func (b *BaseApi) Login(c *gin.Context) {
} }
var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v) var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v)
if oc && !store.Verify(l.CaptchaId, l.Captcha, true) {
if !oc || (l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true)) { // 验证码次数+1
u := &system.SysUser{Username: l.Username, Password: l.Password} global.BlackCache.Increment(key, 1)
user, err := userService.Login(u) response.FailWithMessage("验证码错误", c)
if err != nil {
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户名不存在或者密码错误", c)
return
}
if user.Enable != 1 {
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户被禁止登录", c)
return
}
b.TokenNext(c, *user)
return return
} }
// 验证码次数+1
global.BlackCache.Increment(key, 1) u := &system.SysUser{Username: l.Username, Password: l.Password}
response.FailWithMessage("验证码错误", c) user, err := userService.Login(u)
if err != nil {
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户名不存在或者密码错误", c)
return
}
if user.Enable != 1 {
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户被禁止登录", c)
return
}
b.TokenNext(c, *user)
} }
// TokenNext 登录以后签发jwt // TokenNext 登录以后签发jwt

View File

@@ -41,7 +41,7 @@ type ApiCreator struct{}
// New 创建API创建工具 // New 创建API创建工具
func (a *ApiCreator) New() mcp.Tool { func (a *ApiCreator) New() mcp.Tool {
return mcp.NewTool("create_api", return mcp.NewTool("create_api",
mcp.WithDescription("创建后端API记录用于在生成后端接口时自动创建对应的API权限记录,只要创建API层,router下的文件产生路径变化都需要调用此mcp。"), mcp.WithDescription("创建后端API记录用于AI编辑器自动添加API接口时自动创建对应的API权限记录。注意使用gva_auto_generate创建的包和模块会自动创建API权限无需调用此工具。仅在AI编辑器自动添加API或router下的文件产生路径变化时使用。"),
mcp.WithString("path", mcp.WithString("path",
mcp.Required(), mcp.Required(),
mcp.Description("API路径/user/create"), mcp.Description("API路径/user/create"),

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"` // 路径信息
} }
``` ```
@@ -86,20 +85,32 @@ type AutoCodeField struct {
Clearable bool `json:"clearable"` // 是否可清空 Clearable bool `json:"clearable"` // 是否可清空
Sort bool `json:"sort"` // 是否支持排序 Sort bool `json:"sort"` // 是否支持排序
PrimaryKey bool `json:"primaryKey"` // 是否主键 PrimaryKey bool `json:"primaryKey"` // 是否主键
DataSource *DataSource `json:"dataSource"` // 数据源 DataSource *DataSource `json:"dataSource"` // 数据源配置(用于关联其他表)
CheckDataSource bool `json:"checkDataSource"` // 是否检查数据源 CheckDataSource bool `json:"checkDataSource"` // 是否检查数据源
FieldIndexType string `json:"fieldIndexType"` // 索引类型 FieldIndexType string `json:"fieldIndexType"` // 索引类型
} }
``` ```
### 4. DataSource 结构体(关联表配置)
```go
type DataSource struct {
DBName string `json:"dbName"` // 关联的数据库名称
Table string `json:"table"` // 关联的表名
Label string `json:"label"` // 用于显示的字段名如name、title等
Value string `json:"value"` // 用于存储的值字段名通常是id
Association int `json:"association"` // 关联关系1=一对一2=一对多
HasDeletedAt bool `json:"hasDeletedAt"` // 关联表是否有软删除字段
}
```
## 使用示例 ## 使用示例
### 示例1创建新包和模块 ### 示例1创建新包和批量创建多个模块
```json ```json
{ {
"packageName": "user", "packageName": "user",
"moduleName": "User",
"packageType": "package", "packageType": "package",
"needCreatedPackage": true, "needCreatedPackage": true,
"needCreatedModules": true, "needCreatedModules": true,
@@ -109,141 +120,404 @@ 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": {
"fieldIndexType": "" "dbName": "gva",
}, "table": "sys_users",
{ "label": "username",
"fieldName": "Email", "value": "id",
"fieldDesc": "邮箱", "association": 2,
"fieldType": "string", "hasDeletedAt": true
"fieldJson": "email", },
"dataTypeLong": "100", "checkDataSource": true,
"comment": "邮箱地址", "fieldIndexType": ""
"columnName": "email", },
"fieldSearchType": "EQ", {
"fieldSearchHide": false, "fieldName": "Email",
"dictType": "", "fieldDesc": "邮箱",
"form": true, "fieldType": "string",
"table": true, "fieldJson": "email",
"desc": true, "dataTypeLong": "100",
"excel": true, "comment": "邮箱地址",
"require": true, "columnName": "email",
"defaultValue": "", "fieldSearchType": "EQ",
"errorText": "请输入邮箱", "fieldSearchHide": false,
"clearable": true, "dictType": "",
"sort": false, "form": true,
"primaryKey": false, "table": true,
"dataSource": null, "desc": true,
"checkDataSource": false, "excel": true,
"fieldIndexType": "index" "require": true,
} "defaultValue": "",
] "errorText": "请输入邮箱",
} "clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": null,
"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
}
]
}
]
} }
``` ```
### 示例3模块关联关系配置详解
以下示例展示了如何配置不同类型的关联关系:
```json
{
"packageName": "order",
"packageType": "package",
"needCreatedPackage": true,
"needCreatedModules": true,
"packageInfo": {
"desc": "订单管理模块",
"label": "订单管理",
"template": "package",
"packageName": "order"
},
"modulesInfo": [
{
"package": "order",
"tableName": "orders",
"structName": "Order",
"packageName": "order",
"description": "订单",
"abbreviation": "order",
"humpPackageName": "order",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "UserID",
"fieldDesc": "下单用户",
"fieldType": "uint",
"fieldJson": "userId",
"columnName": "user_id",
"fieldSearchType": "EQ",
"form": true,
"table": true,
"desc": true,
"require": true,
"dataSource": {
"dbName": "gva",
"table": "sys_users",
"label": "username",
"value": "id",
"association": 2,
"hasDeletedAt": true
},
"checkDataSource": true
},
{
"fieldName": "ProductID",
"fieldDesc": "商品",
"fieldType": "uint",
"fieldJson": "productId",
"columnName": "product_id",
"fieldSearchType": "EQ",
"form": true,
"table": true,
"desc": true,
"require": true,
"dataSource": {
"dbName": "gva",
"table": "products",
"label": "name",
"value": "id",
"association": 2,
"hasDeletedAt": false
},
"checkDataSource": true
},
{
"fieldName": "Status",
"fieldDesc": "订单状态",
"fieldType": "int",
"fieldJson": "status",
"columnName": "status",
"fieldSearchType": "EQ",
"form": true,
"table": true,
"desc": true,
"require": true,
"dictType": "order_status"
}
]
}
]
}
```
## DataSource 配置说明
### 关联关系类型
- **association: 1** - 一对一关联(如用户与用户档案)
- **association: 2** - 一对多关联(如用户与订单)
### 配置要点
1. **dbName**: 通常为 "gva"(默认数据库)
2. **table**: 关联表的实际表名
3. **label**: 用于前端显示的字段(如用户名、商品名称)
4. **value**: 用于存储关联ID的字段通常是 "id"
5. **hasDeletedAt**: 关联表是否支持软删除
6. **checkDataSource**: 建议设为true会验证关联表是否存在
### 常见关联场景
- 用户关联:`{"table": "sys_users", "label": "username", "value": "id"}`
- 角色关联:`{"table": "sys_authorities", "label": "authorityName", "value": "authorityId"}`
- 部门关联:`{"table": "sys_departments", "label": "name", "value": "id"}`
- 分类关联:`{"table": "categories", "label": "name", "value": "id"}`
## 重要注意事项 ## 重要注意事项
1. **PackageType**: 只能是 "plugin" 或 "package" 1. **PackageType**: 只能是 "plugin" 或 "package"
2. **NeedCreatedPackage**: 当为true时PackageInfo必须提供 2. **NeedCreatedPackage**: 当为true时PackageInfo必须提供
3. **NeedCreatedModules**: 当为true时ModulesInfo必须提供 3. **NeedCreatedModules**: 当为true时ModulesInfo必须提供
4. **字段类型**: FieldType支持的类型包括string, int, int64, float64, bool, time.Time, enum, picture, video, file, pictures, array, richtext, json等 4. **字段类型**: FieldType支持的类型包括
- string字符串
- richtext富文本
- int整型
- bool布尔值
- float64浮点型
- time.Time时间
- enum枚举
- picture单图片字符串
- pictures多图片json字符串
- video视频字符串
- file文件json字符串
- jsonJSON
- array数组
5. **搜索类型**: FieldSearchType支持EQ, NE, GT, GE, LT, LE, LIKE, BETWEEN等 5. **搜索类型**: FieldSearchType支持EQ, NE, GT, GE, LT, LE, LIKE, BETWEEN等
6. **索引类型**: FieldIndexType支持index, unique等 6. **索引类型**: FieldIndexType支持index, unique等
7. **GvaModel**: 设置为true时会自动包含ID、CreatedAt、UpdatedAt、DeletedAt字段 7. **GvaModel**: 设置为true时会自动包含ID、CreatedAt、UpdatedAt、DeletedAt字段
8. **关联配置**: 使用dataSource时确保关联表已存在建议开启checkDataSource验证
## 常见错误避免 ## 常见错误避免

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,138 @@
"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": ""
] },
} {
"fieldName": "AuthorID",
"fieldDesc": "作者",
"fieldType": "uint",
"fieldJson": "authorId",
"dataTypeLong": "",
"comment": "作者ID",
"columnName": "author_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": {
"dbName": "gva",
"table": "library_authors",
"label": "name",
"value": "id",
"association": 2,
"hasDeletedAt": true
},
"checkDataSource": true,
"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": ""
}
]
}
]
} }
} }
``` ```
@@ -119,4 +201,5 @@
1. 必须先调用`confirm`来获取确认信息 1. 必须先调用`confirm`来获取确认信息
2.`execute`时必须提供相应的确认参数 2.`execute`时必须提供相应的确认参数
3. 确认参数的值必须是"yes"或"no" 3. 确认参数的值必须是"yes"或"no"
4. 如果不需要创建包或模块,则不需要提供对应的确认参数 4. 如果不需要创建包或模块,则不需要提供对应的确认参数
5. 字段类型支持string字符串,richtext富文本,int整型,bool布尔值,float64浮点型,time.Time时间,enum枚举,picture单图片字符串,pictures多图片json字符串,video视频字符串,file文件json字符串,jsonJSON,array数组

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)",
@@ -185,18 +189,24 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
"clearable": "是否可清空(bool)", "clearable": "是否可清空(bool)",
"sort": "是否排序(bool)", "sort": "是否排序(bool)",
"primaryKey": "是否主键(bool)", "primaryKey": "是否主键(bool)",
"dataSource": "数据源(object)", "dataSource": "数据源配置(object) - 用于配置字段的关联表信息,结构:{\"dbName\":\"数据库名\",\"table\":\"关联表名\",\"label\":\"显示字段\",\"value\":\"值字段\",\"association\":1或2(1=一对一,2=一对多),\"hasDeletedAt\":true/false}。\n\n**获取表名提示:**\n- 可在 server/model 和 plugin/xxx/model 目录下查看对应模块的 TableName() 接口实现获取实际表名\n- 例如SysUser 的表名为 \"sys_users\"ExaFileUploadAndDownload 的表名为 \"exa_file_upload_and_downloads\"\n- 插件模块示例Info 的表名为 \"gva_announcements_info\"\n\n**获取数据库名提示:**\n- 主数据库:通常使用 \"gva\"(默认数据库标识)\n- 多数据库:可在 config.yaml 的 db-list 配置中查看可用数据库的 alias-name 字段\n- 如果用户未提及关联多数据库信息 则使用默认数据库 默认数据库的情况下 dbName此处填写为空",
"checkDataSource": "检查数据源(bool)", "checkDataSource": "是否检查数据源(bool) - 启用后会验证关联表的存在性",
"fieldIndexType": "索引类型(string)" "fieldIndexType": "索引类型(string)"
}] }]
} }, {
"package": "包名(string)",
"tableName": "第二个模块的表名(string)",
"structName": "第二个模块的结构体名(string)",
"description": "第二个模块的描述(string)",
"...": "更多模块配置..."
}]
} }
注意: 注意:
1. needCreatedPackage=true时packageInfo必需 1. needCreatedPackage=true时packageInfo必需
2. needCreatedModules=true时modulesInfo必需 2. needCreatedModules=true时modulesInfo必需
3. packageType只能是"package"或"plugin" 3. packageType只能是"package"或"plugin"
4. 字段类型支持string,int,int64,float64,bool,time.Time,enum,picture,video,file,pictures,array,richtext,json 4. 字段类型支持string(字符串),richtext富文本,int整型,bool布尔值,float64(浮点型),time.Time(时间),enum(枚举),picture(单图片,字符串),pictures多图片json字符串,video视频字符串,file文件json字符串,jsonJSON,array数组
5. 搜索类型支持:=,!=,>,>=,<,<=,NOT BETWEEN/LIKE/BETWEEN/IN/NOT IN 5. 搜索类型支持:=,!=,>,>=,<,<=,NOT BETWEEN/LIKE/BETWEEN/IN/NOT IN
6. gvaModel=true时自动包含ID,CreatedAt,UpdatedAt,DeletedAt字段 6. gvaModel=true时自动包含ID,CreatedAt,UpdatedAt,DeletedAt字段
7. **重要**当gvaModel=false时必须有一个字段的primaryKey=true否则会导致PrimaryField为nil错误 7. **重要**当gvaModel=false时必须有一个字段的primaryKey=true否则会导致PrimaryField为nil错误
@@ -204,10 +214,19 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
9. 智能字典创建功能:当字段使用字典类型(DictType)时,系统会: 9. 智能字典创建功能:当字段使用字典类型(DictType)时,系统会:
- 自动检查字典是否存在,如果不存在则创建字典 - 自动检查字典是否存在,如果不存在则创建字典
- 根据字典类型和字段描述智能生成默认选项,支持状态、性别、类型、等级、优先级、审批、角色、布尔值、订单、颜色、尺寸等常见场景 - 根据字典类型和字段描述智能生成默认选项,支持状态、性别、类型、等级、优先级、审批、角色、布尔值、订单、颜色、尺寸等常见场景
- 为无法识别的字典类型提供通用默认选项`), - 为无法识别的字典类型提供通用默认选项
10. **模块关联配置**当需要配置模块间的关联关系时使用dataSource字段
- **dbName**: 关联的数据库名称
- **table**: 关联的表名
- **label**: 用于显示的字段名如name、title等
- **value**: 用于存储的值字段名通常是id
- **association**: 关联关系类型1=一对一关联2=一对多关联)
- **hasDeletedAt**: 关联表是否有软删除字段
- **checkDataSource**: 设为true时会验证关联表的存在性
- 示例:{"dbName":"gva","table":"sys_users","label":"username","value":"id","association":2,"hasDeletedAt":true}`),
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 +533,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 +543,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 +559,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 +568,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 +592,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 +609,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 +623,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 +662,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 +673,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 +683,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 +694,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 +709,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 +718,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 +767,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 +779,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 +825,13 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
"checkDataSource": false, "checkDataSource": false,
"fieldIndexType": "" "fieldIndexType": ""
}] }]
} }, {
"package": "包名",
"tableName": "第二个模块的表名",
"structName": "第二个模块的结构体名",
"description": "第二个模块的描述",
"...": "更多模块配置..."
}]
} }
**重要提醒**ExecutionPlan必须严格按照以下格式和验证规则 **重要提醒**ExecutionPlan必须严格按照以下格式和验证规则
@@ -835,13 +859,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 +896,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 +923,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 +1077,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 +1170,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 +1200,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 +1338,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 +1377,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 +1498,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 +1517,7 @@ func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (sugge
pluginMatches++ pluginMatches++
} }
} }
// 检测包关键词 // 检测包关键词
packageMatches := 0 packageMatches := 0
for _, keyword := range packageKeywords { for _, keyword := range packageKeywords {
@@ -1468,7 +1525,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 +1534,7 @@ func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (sugge
return "plugin", true, "中" return "plugin", true, "中"
} }
} }
// 默认返回package // 默认返回package
return "package", false, "低" return "package", false, "低"
} }
@@ -1492,7 +1549,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 +1557,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 +1594,7 @@ func (t *AutomationModuleAnalyzer) isPackageFolderEmpty(packageName, template st
break break
} }
} }
// 如果没有 .go 文件,认为是空包 // 如果没有 .go 文件,认为是空包
return !hasGoFiles, nil return !hasGoFiles, nil
} }
@@ -1545,7 +1602,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 +1617,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 +1641,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 +1656,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 +1691,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

@@ -64,7 +64,7 @@ type MenuCreator struct{}
// New 创建菜单创建工具 // New 创建菜单创建工具
func (m *MenuCreator) New() mcp.Tool { func (m *MenuCreator) New() mcp.Tool {
return mcp.NewTool("create_menu", return mcp.NewTool("create_menu",
mcp.WithDescription("创建前端菜单记录,用于在生成前端页面时自动创建对应的菜单项,只要前端页面生成都需要调用此mcp。"), mcp.WithDescription("创建前端菜单记录,用于AI编辑器自动添加前端页面时自动创建对应的菜单项。注意使用gva_auto_generate创建的包和模块会自动创建菜单项无需调用此工具。仅在AI编辑器自动添加前端页面时使用。"),
mcp.WithNumber("parentId", mcp.WithNumber("parentId",
mcp.Description("父菜单ID0表示根菜单"), mcp.Description("父菜单ID0表示根菜单"),
mcp.DefaultNumber(0), mcp.DefaultNumber(0),

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
}

View File

@@ -5,7 +5,7 @@ var (
ApiVerify = Rules{"Path": {NotEmpty()}, "Description": {NotEmpty()}, "ApiGroup": {NotEmpty()}, "Method": {NotEmpty()}} ApiVerify = Rules{"Path": {NotEmpty()}, "Description": {NotEmpty()}, "ApiGroup": {NotEmpty()}, "Method": {NotEmpty()}}
MenuVerify = Rules{"Path": {NotEmpty()}, "Name": {NotEmpty()}, "Component": {NotEmpty()}, "Sort": {Ge("0")}} MenuVerify = Rules{"Path": {NotEmpty()}, "Name": {NotEmpty()}, "Component": {NotEmpty()}, "Sort": {Ge("0")}}
MenuMetaVerify = Rules{"Title": {NotEmpty()}} MenuMetaVerify = Rules{"Title": {NotEmpty()}}
LoginVerify = Rules{"CaptchaId": {NotEmpty()}, "Username": {NotEmpty()}, "Password": {NotEmpty()}} LoginVerify = Rules{"CaptchaId": {NotEmpty()}, "Captcha": {NotEmpty()}, "Username": {NotEmpty()}, "Password": {NotEmpty()}}
RegisterVerify = Rules{"Username": {NotEmpty()}, "NickName": {NotEmpty()}, "Password": {NotEmpty()}, "AuthorityId": {NotEmpty()}} RegisterVerify = Rules{"Username": {NotEmpty()}, "NickName": {NotEmpty()}, "Password": {NotEmpty()}, "AuthorityId": {NotEmpty()}}
PageInfoVerify = Rules{"Page": {NotEmpty()}, "PageSize": {NotEmpty()}} PageInfoVerify = Rules{"Page": {NotEmpty()}, "PageSize": {NotEmpty()}}
CustomerVerify = Rules{"CustomerName": {NotEmpty()}, "CustomerPhoneData": {NotEmpty()}} CustomerVerify = Rules{"CustomerName": {NotEmpty()}, "CustomerPhoneData": {NotEmpty()}}