This commit is contained in:
yvan 2025-08-12 11:29:13 +08:00
parent 7e2bcc029d
commit 0cee6b045b
11 changed files with 964 additions and 556 deletions

3
.gitignore vendored
View File

@ -43,4 +43,5 @@ web/pnpm-lock.yaml
yudao-module-mp
yudao-ui-admin-vue3-master
live2d-vue-app
./web/node_modules
./web/node_modules
wechat

View File

@ -1,76 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at 303176530@qq.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@ -1,19 +0,0 @@
### Contributing Guide
#### 1 Issue Guidelines
- Issues are exclusively for bug reports, feature requests and design-related topics. Other questions may be closed directly. If any questions come up when you are using Element, please hit [Gitter](https://gitter.im/element-en/Lobby) for help.
- Before submitting an issue, please check if similar problems have already been issued.
#### 2 Pull Request Guidelines
- Fork this repository to your own account. Do not create branches here.
- Commit info should be formatted as `[File Name]: Info about commit.` (e.g. `README.md: Fix xxx bug`)
- <font color=red>Make sure PRs are created to `develop` branch instead of `master` branch.</font>
- If your PR fixes a bug, please provide a description about the related bug.
- Merging a PR takes two maintainers: one approves the changes after reviewing, and then the other reviews and merges.

View File

@ -1,49 +0,0 @@
{
"folders": [
{
"path": "server",
"name": "backend"
},
{
"path": "web",
"name": "frontend"
},
{
"path": ".",
"name": "root"
}
],
"settings": {
"go.toolsEnvVars": {
"GOPROXY": "https://goproxy.cn,direct",
"GONOPROXY": "none;"
}
},
"launch": {
"version": "0.2.0",
"configurations": [
{
"type": "go",
"request": "launch",
"name": "Backend",
"cwd": "${workspaceFolder:backend}",
"program": "${workspaceFolder:backend}/"
},
{
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder:frontend}",
"name": "Frontend",
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "serve"]
}
],
"compounds": [
{
"name": "Both (Backend & Frontend)",
"configurations": ["Backend", "Frontend"],
"stopAll": true
}
]
}
}

View File

@ -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"),

View File

@ -7,14 +7,13 @@ 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时必需
Paths map[string]string `json:"paths,omitempty"` // 路径信息
PackageName string `json:"packageName"` // 包名,如:"user", "order", "product"
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时必需支持批量创建
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字符串
- jsonJSON
- 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验证
## 常见错误避免

View File

@ -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": ""
}
]
}
]
}
}
```
@ -101,12 +183,12 @@
## 确认参数说明
- `packageConfirm`: 当`needCreatedPackage`为true时必需
- "yes": 确认创建包
- "no": 取消创建包(停止后续处理)
- "yes": 确认创建包
- "no": 取消创建包(停止后续处理)
- `modulesConfirm`: 当`needCreatedModules`为true时必需
- "yes": 确认创建模块
- "no": 取消创建模块(停止后续处理)
- "yes": 确认创建模块
- "no": 取消创建模块(停止后续处理)
## 取消操作的行为
@ -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字符串,jsonJSON,array数组

View File

@ -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字符串,jsonJSON,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**: 用于显示的字段名如nametitle等
- **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
}

View File

@ -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("父菜单ID0表示根菜单"),
mcp.DefaultNumber(0),

View File

@ -0,0 +1,137 @@
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. 第三步功能描述**
**...**
## 📋 梳理要求
- 将需求拆解为具体的功能步骤
- 每个步骤用数字编号123...
- 步骤描述要清晰具体可执行
- 按照业务逻辑顺序排列
- 考虑数据流和用户操作流程
## 🔄 后续流程
梳理完成后请使用 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()}}
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()}}