Merge pull request #2065 from flipped-aurora/2.8.5
支持多模块mcp,精细化api和menu的创建规则。
This commit is contained in:
@@ -27,8 +27,6 @@ import (
|
||||
func (b *BaseApi) Login(c *gin.Context) {
|
||||
var l systemReq.Login
|
||||
err := c.ShouldBindJSON(&l)
|
||||
key := c.ClientIP()
|
||||
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
@@ -39,6 +37,7 @@ func (b *BaseApi) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
key := c.ClientIP()
|
||||
// 判断验证码是否开启
|
||||
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
|
||||
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)
|
||||
|
||||
if !oc || (l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true)) {
|
||||
u := &system.SysUser{Username: l.Username, Password: l.Password}
|
||||
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)
|
||||
if oc && !store.Verify(l.CaptchaId, l.Captcha, true) {
|
||||
// 验证码次数+1
|
||||
global.BlackCache.Increment(key, 1)
|
||||
response.FailWithMessage("验证码错误", c)
|
||||
return
|
||||
}
|
||||
// 验证码次数+1
|
||||
global.BlackCache.Increment(key, 1)
|
||||
response.FailWithMessage("验证码错误", c)
|
||||
|
||||
u := &system.SysUser{Username: l.Username, Password: l.Password}
|
||||
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
|
||||
|
@@ -41,7 +41,7 @@ type ApiCreator struct{}
|
||||
// New 创建API创建工具
|
||||
func (a *ApiCreator) New() mcp.Tool {
|
||||
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.Required(),
|
||||
mcp.Description("API路径,如:/user/create"),
|
||||
|
@@ -8,12 +8,11 @@ ExecutionPlan 是用于自动化模块创建的执行计划结构体,包含了
|
||||
```go
|
||||
type ExecutionPlan struct {
|
||||
PackageName string `json:"packageName"` // 包名,如:"user", "order", "product"
|
||||
ModuleName string `json:"moduleName"` // 模块名,通常与结构体名相同
|
||||
PackageType string `json:"packageType"` // "plugin" 或 "package"
|
||||
NeedCreatedPackage bool `json:"needCreatedPackage"` // 是否需要创建包
|
||||
NeedCreatedModules bool `json:"needCreatedModules"` // 是否需要创建模块
|
||||
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"` // 路径信息
|
||||
}
|
||||
```
|
||||
@@ -86,20 +85,32 @@ type AutoCodeField struct {
|
||||
Clearable bool `json:"clearable"` // 是否可清空
|
||||
Sort bool `json:"sort"` // 是否支持排序
|
||||
PrimaryKey bool `json:"primaryKey"` // 是否主键
|
||||
DataSource *DataSource `json:"dataSource"` // 数据源
|
||||
DataSource *DataSource `json:"dataSource"` // 数据源配置(用于关联其他表)
|
||||
CheckDataSource bool `json:"checkDataSource"` // 是否检查数据源
|
||||
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
|
||||
{
|
||||
"packageName": "user",
|
||||
"moduleName": "User",
|
||||
"packageType": "package",
|
||||
"needCreatedPackage": true,
|
||||
"needCreatedModules": true,
|
||||
@@ -109,141 +120,404 @@ type AutoCodeField struct {
|
||||
"template": "package",
|
||||
"packageName": "user"
|
||||
},
|
||||
"modulesInfo": {
|
||||
"package": "user",
|
||||
"tableName": "sys_users",
|
||||
"businessDB": "",
|
||||
"structName": "User",
|
||||
"packageName": "user",
|
||||
"description": "用户",
|
||||
"abbreviation": "user",
|
||||
"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": "Username",
|
||||
"fieldDesc": "用户名",
|
||||
"fieldType": "string",
|
||||
"fieldJson": "username",
|
||||
"dataTypeLong": "50",
|
||||
"comment": "用户名",
|
||||
"columnName": "username",
|
||||
"fieldSearchType": "LIKE",
|
||||
"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": ""
|
||||
},
|
||||
{
|
||||
"fieldName": "Email",
|
||||
"fieldDesc": "邮箱",
|
||||
"fieldType": "string",
|
||||
"fieldJson": "email",
|
||||
"dataTypeLong": "100",
|
||||
"comment": "邮箱地址",
|
||||
"columnName": "email",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
"modulesInfo": [
|
||||
{
|
||||
"package": "user",
|
||||
"tableName": "sys_users",
|
||||
"businessDB": "",
|
||||
"structName": "User",
|
||||
"packageName": "user",
|
||||
"description": "用户",
|
||||
"abbreviation": "user",
|
||||
"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": "Username",
|
||||
"fieldDesc": "用户名",
|
||||
"fieldType": "string",
|
||||
"fieldJson": "username",
|
||||
"dataTypeLong": "50",
|
||||
"comment": "用户名",
|
||||
"columnName": "username",
|
||||
"fieldSearchType": "LIKE",
|
||||
"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": "sys_users",
|
||||
"label": "username",
|
||||
"value": "id",
|
||||
"association": 2,
|
||||
"hasDeletedAt": true
|
||||
},
|
||||
"checkDataSource": true,
|
||||
"fieldIndexType": ""
|
||||
},
|
||||
{
|
||||
"fieldName": "Email",
|
||||
"fieldDesc": "邮箱",
|
||||
"fieldType": "string",
|
||||
"fieldJson": "email",
|
||||
"dataTypeLong": "100",
|
||||
"comment": "邮箱地址",
|
||||
"columnName": "email",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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
|
||||
{
|
||||
"packageName": "system",
|
||||
"moduleName": "Role",
|
||||
"packageType": "package",
|
||||
"needCreatedPackage": false,
|
||||
"needCreatedModules": true,
|
||||
"packageInfo": null,
|
||||
"modulesInfo": {
|
||||
"package": "system",
|
||||
"tableName": "sys_roles",
|
||||
"businessDB": "",
|
||||
"structName": "Role",
|
||||
"packageName": "system",
|
||||
"description": "角色",
|
||||
"abbreviation": "role",
|
||||
"humpPackageName": "system",
|
||||
"gvaModel": true,
|
||||
"autoMigrate": true,
|
||||
"autoCreateResource": true,
|
||||
"autoCreateApiToSql": true,
|
||||
"autoCreateMenuToSql": true,
|
||||
"autoCreateBtnAuth": true,
|
||||
"onlyTemplate": false,
|
||||
"isTree": false,
|
||||
"generateWeb": true,
|
||||
"generateServer": true,
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "RoleName",
|
||||
"fieldDesc": "角色名称",
|
||||
"fieldType": "string",
|
||||
"fieldJson": "roleName",
|
||||
"dataTypeLong": "50",
|
||||
"comment": "角色名称",
|
||||
"columnName": "role_name",
|
||||
"fieldSearchType": "LIKE",
|
||||
"form": true,
|
||||
"table": true,
|
||||
"desc": true,
|
||||
"require": true
|
||||
}
|
||||
]
|
||||
}
|
||||
"modulesInfo": [
|
||||
{
|
||||
"package": "system",
|
||||
"tableName": "sys_roles",
|
||||
"businessDB": "",
|
||||
"structName": "Role",
|
||||
"packageName": "system",
|
||||
"description": "角色",
|
||||
"abbreviation": "role",
|
||||
"humpPackageName": "system",
|
||||
"gvaModel": true,
|
||||
"autoMigrate": true,
|
||||
"autoCreateResource": true,
|
||||
"autoCreateApiToSql": true,
|
||||
"autoCreateMenuToSql": true,
|
||||
"autoCreateBtnAuth": true,
|
||||
"onlyTemplate": false,
|
||||
"isTree": false,
|
||||
"generateWeb": true,
|
||||
"generateServer": true,
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "RoleName",
|
||||
"fieldDesc": "角色名称",
|
||||
"fieldType": "string",
|
||||
"fieldJson": "roleName",
|
||||
"dataTypeLong": "50",
|
||||
"comment": "角色名称",
|
||||
"columnName": "role_name",
|
||||
"fieldSearchType": "LIKE",
|
||||
"form": true,
|
||||
"table": 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"
|
||||
2. **NeedCreatedPackage**: 当为true时,PackageInfo必须提供
|
||||
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字符串)
|
||||
- json(JSON)
|
||||
- array(数组)
|
||||
5. **搜索类型**: FieldSearchType支持:EQ, NE, GT, GE, LT, LE, LIKE, BETWEEN等
|
||||
6. **索引类型**: FieldIndexType支持:index, unique等
|
||||
7. **GvaModel**: 设置为true时会自动包含ID、CreatedAt、UpdatedAt、DeletedAt字段
|
||||
8. **关联配置**: 使用dataSource时,确保关联表已存在,建议开启checkDataSource验证
|
||||
|
||||
## 常见错误避免
|
||||
|
||||
|
@@ -17,13 +17,12 @@
|
||||
}
|
||||
```
|
||||
|
||||
### 第二步:确认
|
||||
### 第二步:确认(支持批量创建多个模块)
|
||||
```json
|
||||
{
|
||||
"action": "confirm",
|
||||
"executionPlan": {
|
||||
"packageName": "library",
|
||||
"moduleName": "Book",
|
||||
"packageType": "package",
|
||||
"needCreatedPackage": true,
|
||||
"needCreatedModules": true,
|
||||
@@ -33,55 +32,138 @@
|
||||
"template": "package",
|
||||
"packageName": "library"
|
||||
},
|
||||
"modulesInfo": {
|
||||
"package": "library",
|
||||
"tableName": "library_books",
|
||||
"businessDB": "",
|
||||
"structName": "Book",
|
||||
"packageName": "library",
|
||||
"description": "图书信息",
|
||||
"abbreviation": "book",
|
||||
"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": "title",
|
||||
"fieldDesc": "书名",
|
||||
"fieldType": "string",
|
||||
"fieldJson": "title",
|
||||
"dataTypeLong": "255",
|
||||
"comment": "书名",
|
||||
"columnName": "title",
|
||||
"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": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
"modulesInfo": [
|
||||
{
|
||||
"package": "library",
|
||||
"tableName": "library_books",
|
||||
"businessDB": "",
|
||||
"structName": "Book",
|
||||
"packageName": "library",
|
||||
"description": "图书信息",
|
||||
"abbreviation": "book",
|
||||
"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": "title",
|
||||
"fieldDesc": "书名",
|
||||
"fieldType": "string",
|
||||
"fieldJson": "title",
|
||||
"dataTypeLong": "255",
|
||||
"comment": "书名",
|
||||
"columnName": "title",
|
||||
"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": ""
|
||||
},
|
||||
{
|
||||
"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`来获取确认信息
|
||||
2. 在`execute`时必须提供相应的确认参数
|
||||
3. 确认参数的值必须是"yes"或"no"
|
||||
4. 如果不需要创建包或模块,则不需要提供对应的确认参数
|
||||
4. 如果不需要创建包或模块,则不需要提供对应的确认参数
|
||||
5. 字段类型支持:string(字符串),richtext(富文本),int(整型),bool(布尔值),float64(浮点型),time.Time(时间),enum(枚举),picture(单图片,字符串),pictures(多图片,json字符串),video(视频,字符串),file(文件,json字符串),json(JSON),array(数组)
|
@@ -67,15 +67,14 @@ type AnalysisResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ExecutionPlan 执行计划
|
||||
// ExecutionPlan 执行计划 - 支持批量创建
|
||||
type ExecutionPlan struct {
|
||||
PackageName string `json:"packageName"`
|
||||
ModuleName string `json:"moduleName"`
|
||||
PackageType string `json:"packageType"` // "plugin" 或 "package"
|
||||
NeedCreatedPackage bool `json:"needCreatedPackage"`
|
||||
NeedCreatedModules bool `json:"needCreatedModules"`
|
||||
PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"`
|
||||
ModulesInfo *request.AutoCode `json:"modulesInfo,omitempty"`
|
||||
ModulesInfo []*request.AutoCode `json:"modulesInfo,omitempty"` // 改为数组支持多个模块
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
}
|
||||
|
||||
@@ -111,12 +110,18 @@ type ConfirmationResponse struct {
|
||||
// New 返回工具注册信息
|
||||
func (t *AutomationModuleAnalyzer) New() mcp.Tool {
|
||||
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) 根据确认结果执行创建操作
|
||||
|
||||
@@ -125,15 +130,14 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
|
||||
- 如果字典不存在,会自动创建对应的字典及默认的字典详情项
|
||||
- 字典创建包括:字典主表记录和默认的选项值(选项1、选项2等)
|
||||
|
||||
**与其他工具的关系:**
|
||||
- 此工具创建完整模块后,会自动提示相关API和菜单创建建议
|
||||
- 如果用户只需要单个API或菜单,可以使用 smart_assistant 工具
|
||||
- create_api 和 create_menu 工具仅用于数据库记录创建
|
||||
**推荐工作流:**
|
||||
1. 用户提出需求 → requirement_analyzer(最高优先级)
|
||||
2. AI分析需求为1xxx2xxx格式 → gva_auto_generate(执行创建)
|
||||
3. 创建完成后,根据需要使用其他辅助工具
|
||||
|
||||
重要:ExecutionPlan结构体格式要求:
|
||||
重要:ExecutionPlan结构体格式要求(支持批量创建):
|
||||
{
|
||||
"packageName": "包名(string)",
|
||||
"moduleName": "模块名(string)",
|
||||
"packageType": "package或plugin(string)",
|
||||
"needCreatedPackage": "是否需要创建包(bool)",
|
||||
"needCreatedModules": "是否需要创建模块(bool)",
|
||||
@@ -143,7 +147,7 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
|
||||
"template": "package或plugin(string)",
|
||||
"packageName": "包名(string)"
|
||||
},
|
||||
"modulesInfo": {
|
||||
"modulesInfo": [{
|
||||
"package": "包名(string)",
|
||||
"tableName": "数据库表名(string)",
|
||||
"businessDB": "业务数据库(string)",
|
||||
@@ -185,18 +189,24 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
|
||||
"clearable": "是否可清空(bool)",
|
||||
"sort": "是否排序(bool)",
|
||||
"primaryKey": "是否主键(bool)",
|
||||
"dataSource": "数据源(object)",
|
||||
"checkDataSource": "检查数据源(bool)",
|
||||
"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) - 启用后会验证关联表的存在性",
|
||||
"fieldIndexType": "索引类型(string)"
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"package": "包名(string)",
|
||||
"tableName": "第二个模块的表名(string)",
|
||||
"structName": "第二个模块的结构体名(string)",
|
||||
"description": "第二个模块的描述(string)",
|
||||
"...": "更多模块配置..."
|
||||
}]
|
||||
}
|
||||
|
||||
注意:
|
||||
1. needCreatedPackage=true时packageInfo必需
|
||||
2. needCreatedModules=true时modulesInfo必需
|
||||
3. packageType只能是"package"或"plugin"
|
||||
4. 字段类型支持:string,int,int64,float64,bool,time.Time,enum,picture,video,file,pictures,array,richtext,json
|
||||
4. 字段类型支持:string(字符串),richtext(富文本),int(整型),bool(布尔值),float64(浮点型),time.Time(时间),enum(枚举),picture(单图片,字符串),pictures(多图片,json字符串),video(视频,字符串),file(文件,json字符串),json(JSON),array(数组)
|
||||
5. 搜索类型支持:=,!=,>,>=,<,<=,NOT BETWEEN/LIKE/BETWEEN/IN/NOT IN
|
||||
6. gvaModel=true时自动包含ID,CreatedAt,UpdatedAt,DeletedAt字段
|
||||
7. **重要**:当gvaModel=false时,必须有一个字段的primaryKey=true,否则会导致PrimaryField为nil错误
|
||||
@@ -204,10 +214,19 @@ func (t *AutomationModuleAnalyzer) New() mcp.Tool {
|
||||
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.Required(),
|
||||
mcp.Description("执行操作:'analyze' 分析现有模块信息,'confirm' 请求用户确认创建,'execute' 执行创建操作"),
|
||||
mcp.Description("执行操作:'analyze' 分析现有模块信息,'confirm' 请求用户确认创建,'execute' 执行创建操作(支持批量创建多个模块)"),
|
||||
),
|
||||
mcp.WithString("requirement",
|
||||
mcp.Description("用户需求描述(action=analyze时必需)"),
|
||||
@@ -514,7 +533,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
var validPackages []model.SysAutoCodePackage
|
||||
var emptyPackageIDs []uint
|
||||
var emptyPackageNames []string
|
||||
|
||||
|
||||
for _, pkg := range packages {
|
||||
// 检查包对应的文件夹是否为空
|
||||
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)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
if isEmpty {
|
||||
// 记录需要删除的包ID和包名
|
||||
emptyPackageIDs = append(emptyPackageIDs, pkg.ID)
|
||||
emptyPackageNames = append(emptyPackageNames, pkg.PackageName)
|
||||
global.GVA_LOG.Info(fmt.Sprintf("发现空包文件夹: %s,将删除数据库记录和文件夹", pkg.PackageName))
|
||||
|
||||
|
||||
// 删除空文件夹
|
||||
if err := t.removeEmptyPackageFolder(pkg.PackageName, pkg.Template); err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 批量删除空包的数据库记录
|
||||
if len(emptyPackageIDs) > 0 {
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 转换有效的包信息
|
||||
for _, pkg := range validPackages {
|
||||
moduleInfos = append(moduleInfos, ModuleInfo{
|
||||
@@ -573,14 +592,14 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 清理相关的API和菜单记录
|
||||
if len(emptyHistoryIDs) > 0 {
|
||||
if err := t.cleanupRelatedApiAndMenus(emptyHistoryIDs); err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("清理空包相关API和菜单失败: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 批量删除相关历史记录
|
||||
if len(emptyHistoryIDs) > 0 {
|
||||
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)
|
||||
for _, pkg := range validPackages {
|
||||
validPackageNames[pkg.PackageName] = true
|
||||
}
|
||||
|
||||
|
||||
// 收集需要删除的脏历史记录ID(包名不在有效包列表中的历史记录)
|
||||
var dirtyHistoryIDs []uint
|
||||
for _, history := range histories {
|
||||
@@ -604,21 +623,21 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
dirtyHistoryIDs = append(dirtyHistoryIDs, history.ID)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 删除脏历史记录
|
||||
if len(dirtyHistoryIDs) > 0 {
|
||||
// 清理相关的API和菜单记录
|
||||
if err := t.cleanupRelatedApiAndMenus(dirtyHistoryIDs); err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("清理脏历史记录相关API和菜单失败: %v", err))
|
||||
}
|
||||
|
||||
|
||||
if err := global.GVA_DB.Where("id IN ?", dirtyHistoryIDs).Delete(&model.SysAutoCodeHistory{}).Error; err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("删除脏历史记录失败: %v", err))
|
||||
} else {
|
||||
global.GVA_LOG.Info(fmt.Sprintf("成功删除 %d 个脏历史记录(包名不在有效包列表中)", len(dirtyHistoryIDs)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 转换有效的历史记录(只保留包名存在于有效包列表中的历史记录)
|
||||
var historyInfos []HistoryInfo
|
||||
for _, history := range histories {
|
||||
@@ -643,7 +662,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
global.GVA_LOG.Warn("扫描预设计模块失败" + err.Error())
|
||||
allPredesignedModules = []PredesignedModuleInfo{} // 确保不为nil
|
||||
}
|
||||
|
||||
|
||||
// 过滤掉与已删除包相关的预设计模块
|
||||
var predesignedModules []PredesignedModuleInfo
|
||||
for _, module := range allPredesignedModules {
|
||||
@@ -654,7 +673,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 只保留未被删除包的预设计模块
|
||||
if !isDeleted {
|
||||
predesignedModules = append(predesignedModules, module)
|
||||
@@ -664,7 +683,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
// 构建分析结果消息
|
||||
var message string
|
||||
var deletionDetails []string
|
||||
|
||||
|
||||
// 收集删除信息
|
||||
if len(emptyHistoryIDs) > 0 {
|
||||
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) {
|
||||
deletionDetails = append(deletionDetails, fmt.Sprintf("%d个相关预设计模块", len(allPredesignedModules)-len(predesignedModules)))
|
||||
}
|
||||
|
||||
|
||||
if len(emptyPackageNames) > 0 || len(deletionDetails) > 0 {
|
||||
var cleanupInfo string
|
||||
if len(emptyPackageNames) > 0 {
|
||||
cleanupInfo = fmt.Sprintf("检测到存在 %s 包但内容为空,我已经删除这些包的文件夹(包括model、api、service、router目录)和数据库记录", strings.Join(emptyPackageNames, "、"))
|
||||
}
|
||||
|
||||
|
||||
deletionInfo := ""
|
||||
if len(deletionDetails) > 0 {
|
||||
if cleanupInfo != "" {
|
||||
@@ -690,7 +709,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
deletionInfo = fmt.Sprintf("检测到脏数据,已删除%s", strings.Join(deletionDetails, "、"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if cleanupInfo != "" {
|
||||
message = fmt.Sprintf("分析完成:获取到 %d 个有效包、%d 个历史记录和 %d 个预设计模块。%s%s,如果需要使用这些包名,需要重新创建。请AI根据需求选择合适的包和模块", len(validPackages), len(historyInfos), len(predesignedModules), cleanupInfo, deletionInfo)
|
||||
} else {
|
||||
@@ -699,7 +718,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
} else {
|
||||
message = fmt.Sprintf("分析完成:获取到 %d 个有效包、%d 个历史记录和 %d 个预设计模块,请AI根据需求选择合适的包和模块", len(validPackages), len(historyInfos), len(predesignedModules))
|
||||
}
|
||||
|
||||
|
||||
// 构建分析结果
|
||||
analysisResult := AnalysisResponse{
|
||||
Packages: moduleInfos,
|
||||
@@ -748,10 +767,9 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
}
|
||||
- 请在创建模块之前先创建所需的字典选项
|
||||
|
||||
重要提醒:ExecutionPlan必须严格按照以下格式:
|
||||
重要提醒:ExecutionPlan必须严格按照以下格式(支持批量创建多个模块):
|
||||
{
|
||||
"packageName": "包名",
|
||||
"moduleName": "模块名",
|
||||
"packageType": "package或plugin", // 当用户提到插件时必须是"plugin"
|
||||
"needCreatedPackage": true/false,
|
||||
"needCreatedModules": true/false,
|
||||
@@ -761,7 +779,7 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
"template": "package或plugin", // 必须与packageType保持一致!
|
||||
"packageName": "包名"
|
||||
},
|
||||
"modulesInfo": {
|
||||
"modulesInfo": [{
|
||||
"package": "包名",
|
||||
"tableName": "数据库表名",
|
||||
"businessDB": "",
|
||||
@@ -807,7 +825,13 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
"checkDataSource": false,
|
||||
"fieldIndexType": ""
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"package": "包名",
|
||||
"tableName": "第二个模块的表名",
|
||||
"structName": "第二个模块的结构体名",
|
||||
"description": "第二个模块的描述",
|
||||
"...": "更多模块配置..."
|
||||
}]
|
||||
}
|
||||
|
||||
**重要提醒**:ExecutionPlan必须严格按照以下格式和验证规则:
|
||||
@@ -835,13 +859,13 @@ func (t *AutomationModuleAnalyzer) handleAnalyze(ctx context.Context, request mc
|
||||
12. 如果字段需要字典类型,请先使用 generate_dictionary_options 工具创建字典
|
||||
13. 字典创建成功后,再执行模块创建操作
|
||||
|
||||
`, string(resultJSON), requirement, pluginDetectionMsg,
|
||||
func() string {
|
||||
if len(emptyPackageNames) > 0 {
|
||||
return fmt.Sprintf("**重要提醒**:检测到 %s 包存在但内容为空,已自动删除相关文件夹和数据库记录。如果用户需求涉及这些包名,请设置 needCreatedPackage=true 重新创建。", strings.Join(emptyPackageNames, "、"))
|
||||
}
|
||||
return ""
|
||||
}()),
|
||||
`, string(resultJSON), requirement, pluginDetectionMsg,
|
||||
func() string {
|
||||
if len(emptyPackageNames) > 0 {
|
||||
return fmt.Sprintf("**重要提醒**:检测到 %s 包存在但内容为空,已自动删除相关文件夹和数据库记录。如果用户需求涉及这些包名,请设置 needCreatedPackage=true 重新创建。", strings.Join(emptyPackageNames, "、"))
|
||||
}
|
||||
return ""
|
||||
}()),
|
||||
},
|
||||
},
|
||||
}, 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{
|
||||
Message: "请确认以下创建计划:",
|
||||
PackageConfirm: plan.NeedCreatedPackage,
|
||||
ModulesConfirm: plan.NeedCreatedModules,
|
||||
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
|
||||
confirmDetails.WriteString(fmt.Sprintf("包名: %s\n", plan.PackageName))
|
||||
confirmDetails.WriteString(fmt.Sprintf("模块名: %s\n", plan.ModuleName))
|
||||
confirmDetails.WriteString(fmt.Sprintf("包类型: %s\n", plan.PackageType))
|
||||
|
||||
if plan.NeedCreatedPackage && plan.PackageInfo != nil {
|
||||
@@ -894,15 +923,18 @@ func (t *AutomationModuleAnalyzer) handleConfirm(ctx context.Context, request mc
|
||||
confirmDetails.WriteString(fmt.Sprintf(" - 模板: %s\n", plan.PackageInfo.Template))
|
||||
}
|
||||
|
||||
if plan.NeedCreatedModules && plan.ModulesInfo != nil {
|
||||
confirmDetails.WriteString("\n需要创建模块:\n")
|
||||
confirmDetails.WriteString(fmt.Sprintf(" - 结构体名: %s\n", plan.ModulesInfo.StructName))
|
||||
confirmDetails.WriteString(fmt.Sprintf(" - 表名: %s\n", plan.ModulesInfo.TableName))
|
||||
confirmDetails.WriteString(fmt.Sprintf(" - 描述: %s\n", plan.ModulesInfo.Description))
|
||||
confirmDetails.WriteString(fmt.Sprintf(" - 字段数量: %d\n", len(plan.ModulesInfo.Fields)))
|
||||
confirmDetails.WriteString(" - 字段列表:\n")
|
||||
for _, field := range plan.ModulesInfo.Fields {
|
||||
confirmDetails.WriteString(fmt.Sprintf(" * %s (%s): %s\n", field.FieldName, field.FieldType, field.FieldDesc))
|
||||
if plan.NeedCreatedModules && len(plan.ModulesInfo) > 0 {
|
||||
confirmDetails.WriteString(fmt.Sprintf("\n需要创建模块 (共%d个):\n", len(plan.ModulesInfo)))
|
||||
for i, moduleInfo := range plan.ModulesInfo {
|
||||
confirmDetails.WriteString(fmt.Sprintf("\n模块 %d:\n", i+1))
|
||||
confirmDetails.WriteString(fmt.Sprintf(" - 结构体名: %s\n", moduleInfo.StructName))
|
||||
confirmDetails.WriteString(fmt.Sprintf(" - 表名: %s\n", moduleInfo.TableName))
|
||||
confirmDetails.WriteString(fmt.Sprintf(" - 描述: %s\n", moduleInfo.Description))
|
||||
confirmDetails.WriteString(fmt.Sprintf(" - 字段数量: %d\n", len(moduleInfo.Fields)))
|
||||
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
|
||||
}
|
||||
|
||||
// 如果计划中有模块信息,获取结构名
|
||||
// 如果计划中有模块信息,获取第一个模块的结构名作为默认值
|
||||
structName := "ExampleStruct"
|
||||
if plan.ModulesInfo != nil && plan.ModulesInfo.StructName != "" {
|
||||
structName = plan.ModulesInfo.StructName
|
||||
if len(plan.ModulesInfo) > 0 && plan.ModulesInfo[0].StructName != "" {
|
||||
structName = plan.ModulesInfo[0].StructName
|
||||
}
|
||||
|
||||
// 根据包类型构建不同的路径结构
|
||||
@@ -1138,9 +1170,6 @@ func (t *AutomationModuleAnalyzer) validateExecutionPlan(plan *ExecutionPlan) er
|
||||
if plan.PackageName == "" {
|
||||
return errors.New("packageName 不能为空")
|
||||
}
|
||||
if plan.ModuleName == "" {
|
||||
return errors.New("moduleName 不能为空")
|
||||
}
|
||||
if plan.PackageType != "package" && plan.PackageType != "plugin" {
|
||||
return errors.New("packageType 必须是 'package' 或 'plugin'")
|
||||
}
|
||||
@@ -1171,104 +1200,108 @@ func (t *AutomationModuleAnalyzer) validateExecutionPlan(plan *ExecutionPlan) er
|
||||
}
|
||||
}
|
||||
|
||||
// 验证模块信息
|
||||
// 验证模块信息(批量验证)
|
||||
if plan.NeedCreatedModules {
|
||||
if plan.ModulesInfo == nil {
|
||||
if len(plan.ModulesInfo) == 0 {
|
||||
return errors.New("当 needCreatedModules=true 时,modulesInfo 不能为空")
|
||||
}
|
||||
if plan.ModulesInfo.Package == "" {
|
||||
return errors.New("modulesInfo.package 不能为空")
|
||||
}
|
||||
if plan.ModulesInfo.StructName == "" {
|
||||
return errors.New("modulesInfo.structName 不能为空")
|
||||
}
|
||||
if plan.ModulesInfo.TableName == "" {
|
||||
return errors.New("modulesInfo.tableName 不能为空")
|
||||
}
|
||||
if plan.ModulesInfo.Description == "" {
|
||||
return errors.New("modulesInfo.description 不能为空")
|
||||
}
|
||||
if plan.ModulesInfo.Abbreviation == "" {
|
||||
return errors.New("modulesInfo.abbreviation 不能为空")
|
||||
}
|
||||
if plan.ModulesInfo.PackageName == "" {
|
||||
return errors.New("modulesInfo.packageName 不能为空")
|
||||
}
|
||||
if plan.ModulesInfo.HumpPackageName == "" {
|
||||
return errors.New("modulesInfo.humpPackageName 不能为空")
|
||||
}
|
||||
|
||||
// 验证字段信息
|
||||
if len(plan.ModulesInfo.Fields) == 0 {
|
||||
return errors.New("modulesInfo.fields 不能为空,至少需要一个字段")
|
||||
}
|
||||
|
||||
for i, field := range plan.ModulesInfo.Fields {
|
||||
if field.FieldName == "" {
|
||||
return fmt.Errorf("字段 %d 的 fieldName 不能为空", i+1)
|
||||
// 遍历验证每个模块
|
||||
for moduleIndex, moduleInfo := range plan.ModulesInfo {
|
||||
if moduleInfo.Package == "" {
|
||||
return fmt.Errorf("模块 %d 的 package 不能为空", moduleIndex+1)
|
||||
}
|
||||
if field.FieldDesc == "" {
|
||||
return fmt.Errorf("字段 %d 的 fieldDesc 不能为空", i+1)
|
||||
if moduleInfo.StructName == "" {
|
||||
return fmt.Errorf("模块 %d 的 structName 不能为空", moduleIndex+1)
|
||||
}
|
||||
if field.FieldType == "" {
|
||||
return fmt.Errorf("字段 %d 的 fieldType 不能为空", i+1)
|
||||
if moduleInfo.TableName == "" {
|
||||
return fmt.Errorf("模块 %d 的 tableName 不能为空", moduleIndex+1)
|
||||
}
|
||||
if field.FieldJson == "" {
|
||||
return fmt.Errorf("字段 %d 的 fieldJson 不能为空", i+1)
|
||||
if moduleInfo.Description == "" {
|
||||
return fmt.Errorf("模块 %d 的 description 不能为空", moduleIndex+1)
|
||||
}
|
||||
if field.ColumnName == "" {
|
||||
return fmt.Errorf("字段 %d 的 columnName 不能为空", i+1)
|
||||
if moduleInfo.Abbreviation == "" {
|
||||
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"}
|
||||
validType := false
|
||||
for _, validFieldType := range validFieldTypes {
|
||||
if field.FieldType == validFieldType {
|
||||
validType = true
|
||||
break
|
||||
// 验证字段信息
|
||||
if len(moduleInfo.Fields) == 0 {
|
||||
return fmt.Errorf("模块 %d 的 fields 不能为空,至少需要一个字段", moduleIndex+1)
|
||||
}
|
||||
|
||||
for i, field := range moduleInfo.Fields {
|
||||
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 != "" {
|
||||
validSearchTypes := []string{"=", "!=", ">", ">=", "<", "<=", "LIKE", "BETWEEN", "IN", "NOT IN"}
|
||||
validSearchType := false
|
||||
for _, validType := range validSearchTypes {
|
||||
if field.FieldSearchType == validType {
|
||||
validSearchType = true
|
||||
// 验证字段类型
|
||||
validFieldTypes := []string{"string", "int", "int64", "float64", "bool", "time.Time", "enum", "picture", "video", "file", "pictures", "array", "richtext", "json"}
|
||||
validType := false
|
||||
for _, validFieldType := range validFieldTypes {
|
||||
if field.FieldType == validFieldType {
|
||||
validType = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validSearchType {
|
||||
return fmt.Errorf("字段 %d 的 fieldSearchType '%s' 不支持,支持的类型:%v", i+1, field.FieldSearchType, validSearchTypes)
|
||||
if !validType {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldType '%s' 不支持,支持的类型:%v", moduleIndex+1, i+1, field.FieldType, validFieldTypes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证主键设置
|
||||
if !plan.ModulesInfo.GvaModel {
|
||||
// 当不使用GVA模型时,必须有且仅有一个字段设置为主键
|
||||
primaryKeyCount := 0
|
||||
for _, field := range plan.ModulesInfo.Fields {
|
||||
if field.PrimaryKey {
|
||||
primaryKeyCount++
|
||||
// 验证搜索类型(如果设置了)
|
||||
if field.FieldSearchType != "" {
|
||||
validSearchTypes := []string{"=", "!=", ">", ">=", "<", "<=", "LIKE", "BETWEEN", "IN", "NOT IN"}
|
||||
validSearchType := false
|
||||
for _, validType := range validSearchTypes {
|
||||
if field.FieldSearchType == validType {
|
||||
validSearchType = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validSearchType {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldSearchType '%s' 不支持,支持的类型:%v", moduleIndex+1, i+1, field.FieldSearchType, validSearchTypes)
|
||||
}
|
||||
}
|
||||
}
|
||||
if primaryKeyCount == 0 {
|
||||
return errors.New("当 gvaModel=false 时,必须有一个字段的 primaryKey=true")
|
||||
}
|
||||
if primaryKeyCount > 1 {
|
||||
return errors.New("当 gvaModel=false 时,只能有一个字段的 primaryKey=true")
|
||||
}
|
||||
} else {
|
||||
// 当使用GVA模型时,所有字段的primaryKey都应该为false
|
||||
for i, field := range plan.ModulesInfo.Fields {
|
||||
if field.PrimaryKey {
|
||||
return fmt.Errorf("当 gvaModel=true 时,字段 %d 的 primaryKey 应该为 false,系统会自动创建ID主键", i+1)
|
||||
|
||||
// 验证主键设置
|
||||
if !moduleInfo.GvaModel {
|
||||
// 当不使用GVA模型时,必须有且仅有一个字段设置为主键
|
||||
primaryKeyCount := 0
|
||||
for _, field := range moduleInfo.Fields {
|
||||
if field.PrimaryKey {
|
||||
primaryKeyCount++
|
||||
}
|
||||
}
|
||||
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 += "包创建成功; "
|
||||
}
|
||||
|
||||
// 创建字典(如果需要)
|
||||
if plan.NeedCreatedModules && plan.ModulesInfo != nil {
|
||||
dictResult := t.createRequiredDictionaries(ctx, plan.ModulesInfo)
|
||||
result.Message += dictResult
|
||||
}
|
||||
|
||||
// 创建模块(如果需要)
|
||||
if plan.NeedCreatedModules && plan.ModulesInfo != nil {
|
||||
// 批量创建字典和模块(如果需要)
|
||||
if plan.NeedCreatedModules && len(plan.ModulesInfo) > 0 {
|
||||
templateService := service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate
|
||||
|
||||
err := plan.ModulesInfo.Pretreatment()
|
||||
if err != nil {
|
||||
result.Message += fmt.Sprintf("模块信息预处理失败: %v", err)
|
||||
// 即使预处理失败,也要返回paths信息
|
||||
return result
|
||||
// 先批量创建所有模块需要的字典
|
||||
dictResult := t.createRequiredDictionaries(ctx, plan.ModulesInfo)
|
||||
result.Message += dictResult
|
||||
|
||||
// 遍历所有模块进行创建
|
||||
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)
|
||||
if err != nil {
|
||||
result.Message += fmt.Sprintf("创建模块失败: %v", err)
|
||||
// 即使创建模块失败,也要返回paths信息
|
||||
return result
|
||||
}
|
||||
result.Message += "模块创建成功; "
|
||||
result.Message += fmt.Sprintf("批量创建完成,共处理 %d 个模块; ", len(plan.ModulesInfo))
|
||||
}
|
||||
|
||||
result.Message += "已构建目录结构信息; "
|
||||
@@ -1341,43 +1377,64 @@ func (t *AutomationModuleAnalyzer) executeCreation(ctx context.Context, plan *Ex
|
||||
return result
|
||||
}
|
||||
|
||||
// createRequiredDictionaries 创建所需的字典
|
||||
func (t *AutomationModuleAnalyzer) createRequiredDictionaries(ctx context.Context, modulesInfo *request.AutoCode) string {
|
||||
// createRequiredDictionaries 创建所需的字典(批量处理)
|
||||
func (t *AutomationModuleAnalyzer) createRequiredDictionaries(ctx context.Context, modulesInfoList []*request.AutoCode) string {
|
||||
var messages []string
|
||||
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
createdDictTypes := make(map[string]bool) // 用于避免重复创建相同的字典
|
||||
|
||||
// 遍历所有字段,查找使用字典的字段
|
||||
for _, field := range modulesInfo.Fields {
|
||||
if field.DictType != "" {
|
||||
// 检查字典是否存在
|
||||
exists, err := t.checkDictionaryExists(field.DictType)
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("检查字典 %s 时出错: %v; ", field.DictType, err))
|
||||
continue
|
||||
}
|
||||
// 遍历所有模块
|
||||
for moduleIndex, modulesInfo := range modulesInfoList {
|
||||
messages = append(messages, fmt.Sprintf("处理模块 %d (%s) 的字典: ", moduleIndex+1, modulesInfo.StructName))
|
||||
|
||||
if !exists {
|
||||
// 字典不存在,创建字典
|
||||
dictionary := model.SysDictionary{
|
||||
Name: t.generateDictionaryName(field.DictType, field.FieldDesc),
|
||||
Type: field.DictType,
|
||||
Status: &[]bool{true}[0], // 默认启用
|
||||
Desc: fmt.Sprintf("自动生成的字典,用于字段: %s (%s)", field.FieldName, field.FieldDesc),
|
||||
// 遍历当前模块的所有字段,查找使用字典的字段
|
||||
moduleHasDictFields := false
|
||||
for _, field := range modulesInfo.Fields {
|
||||
if field.DictType != "" {
|
||||
moduleHasDictFields = true
|
||||
|
||||
// 如果这个字典类型已经在之前的模块中创建过,跳过
|
||||
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 {
|
||||
messages = append(messages, fmt.Sprintf("创建字典 %s 失败: %v; ", field.DictType, err))
|
||||
} else {
|
||||
messages = append(messages, fmt.Sprintf("成功创建字典 %s (%s); ", field.DictType, dictionary.Name))
|
||||
|
||||
// 创建默认的字典详情项
|
||||
t.createDefaultDictionaryDetails(ctx, field.DictType, field.FieldDesc)
|
||||
messages = append(messages, fmt.Sprintf("检查字典 %s 时出错: %v; ", field.DictType, err))
|
||||
continue
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// 字典不存在,创建字典
|
||||
dictionary := model.SysDictionary{
|
||||
Name: t.generateDictionaryName(field.DictType, field.FieldDesc),
|
||||
Type: field.DictType,
|
||||
Status: &[]bool{true}[0], // 默认启用
|
||||
Desc: fmt.Sprintf("自动生成的字典,用于模块 %s 字段: %s (%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 {
|
||||
@@ -1441,18 +1498,18 @@ func (t *AutomationModuleAnalyzer) generateSmartDictionaryOptions(dictType, fiel
|
||||
func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (suggestedType string, isPlugin bool, confidence string) {
|
||||
// 转换为小写进行匹配
|
||||
requirementLower := strings.ToLower(requirement)
|
||||
|
||||
|
||||
// 插件相关关键词
|
||||
pluginKeywords := []string{
|
||||
"插件", "plugin", "扩展", "extension", "addon", "模块插件",
|
||||
"功能插件", "业务插件", "第三方插件", "自定义插件",
|
||||
}
|
||||
|
||||
|
||||
// 包相关关键词(用于排除误判)
|
||||
packageKeywords := []string{
|
||||
"包", "package", "模块包", "业务包", "功能包",
|
||||
}
|
||||
|
||||
|
||||
// 检测插件关键词
|
||||
pluginMatches := 0
|
||||
for _, keyword := range pluginKeywords {
|
||||
@@ -1460,7 +1517,7 @@ func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (sugge
|
||||
pluginMatches++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检测包关键词
|
||||
packageMatches := 0
|
||||
for _, keyword := range packageKeywords {
|
||||
@@ -1468,7 +1525,7 @@ func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (sugge
|
||||
packageMatches++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 决策逻辑
|
||||
if pluginMatches > 0 {
|
||||
if packageMatches == 0 || pluginMatches > packageMatches {
|
||||
@@ -1477,7 +1534,7 @@ func (t *AutomationModuleAnalyzer) detectPluginIntent(requirement string) (sugge
|
||||
return "plugin", true, "中"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 默认返回package
|
||||
return "package", false, "低"
|
||||
}
|
||||
@@ -1492,7 +1549,7 @@ func (t *AutomationModuleAnalyzer) isPackageFolderEmpty(packageName, template st
|
||||
// package 类型
|
||||
basePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "model", packageName)
|
||||
}
|
||||
|
||||
|
||||
// 检查文件夹是否存在
|
||||
if _, err := os.Stat(basePath); os.IsNotExist(err) {
|
||||
// 文件夹不存在,认为是空的
|
||||
@@ -1500,13 +1557,13 @@ func (t *AutomationModuleAnalyzer) isPackageFolderEmpty(packageName, template st
|
||||
} else if err != nil {
|
||||
return false, fmt.Errorf("检查文件夹状态失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
// 读取文件夹内容
|
||||
entries, err := os.ReadDir(basePath)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("读取文件夹内容失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
// 检查目录下是否有 .go 文件
|
||||
hasGoFiles := false
|
||||
for _, entry := range entries {
|
||||
@@ -1537,7 +1594,7 @@ func (t *AutomationModuleAnalyzer) isPackageFolderEmpty(packageName, template st
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果没有 .go 文件,认为是空包
|
||||
return !hasGoFiles, nil
|
||||
}
|
||||
@@ -1545,7 +1602,7 @@ func (t *AutomationModuleAnalyzer) isPackageFolderEmpty(packageName, template st
|
||||
// removeEmptyPackageFolder 删除空的包文件夹
|
||||
func (t *AutomationModuleAnalyzer) removeEmptyPackageFolder(packageName, template string) error {
|
||||
var errors []string
|
||||
|
||||
|
||||
if template == "plugin" {
|
||||
// plugin 类型只删除 plugin 目录下的文件夹
|
||||
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, "router", packageName),
|
||||
}
|
||||
|
||||
|
||||
for _, path := range paths {
|
||||
if err := t.removeDirectoryIfExists(path); err != nil {
|
||||
errors = append(errors, fmt.Sprintf("删除%s失败: %v", path, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf("删除过程中出现错误: %s", strings.Join(errors, "; "))
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1584,12 +1641,12 @@ func (t *AutomationModuleAnalyzer) removeDirectoryIfExists(dirPath string) error
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("检查文件夹状态失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
// 删除文件夹及其所有内容
|
||||
if err := os.RemoveAll(dirPath); err != nil {
|
||||
return fmt.Errorf("删除文件夹失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
global.GVA_LOG.Info(fmt.Sprintf("成功删除目录: %s", dirPath))
|
||||
return nil
|
||||
}
|
||||
@@ -1599,31 +1656,31 @@ func (t *AutomationModuleAnalyzer) cleanupRelatedApiAndMenus(historyIDs []uint)
|
||||
if len(historyIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// 获取要删除的历史记录信息
|
||||
var histories []model.SysAutoCodeHistory
|
||||
if err := global.GVA_DB.Where("id IN ?", historyIDs).Find(&histories).Error; err != nil {
|
||||
return fmt.Errorf("获取历史记录失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
var deletedApiCount, deletedMenuCount int
|
||||
|
||||
|
||||
for _, history := range histories {
|
||||
// 删除相关的API记录(使用存储的API IDs)
|
||||
if len(history.ApiIDs) > 0 {
|
||||
ids := make([]int, 0, len(history.ApiIDs))
|
||||
for _, id := range history.ApiIDs {
|
||||
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)))
|
||||
}
|
||||
if len(history.ApiIDs) > 0 {
|
||||
ids := make([]int, 0, len(history.ApiIDs))
|
||||
for _, id := range history.ApiIDs {
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
||||
// 删除相关的菜单记录(使用存储的菜单ID)
|
||||
if history.MenuID != 0 {
|
||||
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 {
|
||||
global.GVA_LOG.Info(fmt.Sprintf("清理完成:删除了 %d 个API记录和 %d 个菜单记录", deletedApiCount, deletedMenuCount))
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -64,7 +64,7 @@ type MenuCreator struct{}
|
||||
// New 创建菜单创建工具
|
||||
func (m *MenuCreator) New() mcp.Tool {
|
||||
return mcp.NewTool("create_menu",
|
||||
mcp.WithDescription("创建前端菜单记录,用于在生成前端页面时自动创建对应的菜单项,只要前端有页面生成,都需要调用此mcp。"),
|
||||
mcp.WithDescription("创建前端菜单记录,用于AI编辑器自动添加前端页面时自动创建对应的菜单项。注意:使用gva_auto_generate创建的包和模块会自动创建菜单项,无需调用此工具。仅在AI编辑器自动添加前端页面时使用。"),
|
||||
mcp.WithNumber("parentId",
|
||||
mcp.Description("父菜单ID,0表示根菜单"),
|
||||
mcp.DefaultNumber(0),
|
||||
|
139
server/mcp/requirement_analyzer.go
Normal file
139
server/mcp/requirement_analyzer.go
Normal 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
|
||||
}
|
@@ -5,7 +5,7 @@ var (
|
||||
ApiVerify = Rules{"Path": {NotEmpty()}, "Description": {NotEmpty()}, "ApiGroup": {NotEmpty()}, "Method": {NotEmpty()}}
|
||||
MenuVerify = Rules{"Path": {NotEmpty()}, "Name": {NotEmpty()}, "Component": {NotEmpty()}, "Sort": {Ge("0")}}
|
||||
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()}}
|
||||
PageInfoVerify = Rules{"Page": {NotEmpty()}, "PageSize": {NotEmpty()}}
|
||||
CustomerVerify = Rules{"CustomerName": {NotEmpty()}, "CustomerPhoneData": {NotEmpty()}}
|
||||
|
Reference in New Issue
Block a user