🎨 更新项目版本
This commit is contained in:
201
mcp/api_creator.go
Normal file
201
mcp/api_creator.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system"
|
||||
"git.echol.cn/loser/lckt/service"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&ApiCreator{})
|
||||
}
|
||||
|
||||
// ApiCreateRequest API创建请求结构
|
||||
type ApiCreateRequest struct {
|
||||
Path string `json:"path"` // API路径
|
||||
Description string `json:"description"` // API中文描述
|
||||
ApiGroup string `json:"apiGroup"` // API组
|
||||
Method string `json:"method"` // HTTP方法
|
||||
}
|
||||
|
||||
// ApiCreateResponse API创建响应结构
|
||||
type ApiCreateResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
ApiID uint `json:"apiId"`
|
||||
Path string `json:"path"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
// ApiCreator API创建工具
|
||||
type ApiCreator struct{}
|
||||
|
||||
// New 创建API创建工具
|
||||
func (a *ApiCreator) New() mcp.Tool {
|
||||
return mcp.NewTool("create_api",
|
||||
mcp.WithDescription(`创建后端API记录,用于AI编辑器自动添加API接口时自动创建对应的API权限记录。
|
||||
|
||||
**重要限制:**
|
||||
- 当使用gva_auto_generate工具且needCreatedModules=true时,模块创建会自动生成API权限,不应调用此工具
|
||||
- 仅在以下情况使用:1) 单独创建API(不涉及模块创建);2) AI编辑器自动添加API;3) router下的文件产生路径变化时`),
|
||||
mcp.WithString("path",
|
||||
mcp.Required(),
|
||||
mcp.Description("API路径,如:/user/create"),
|
||||
),
|
||||
mcp.WithString("description",
|
||||
mcp.Required(),
|
||||
mcp.Description("API中文描述,如:创建用户"),
|
||||
),
|
||||
mcp.WithString("apiGroup",
|
||||
mcp.Required(),
|
||||
mcp.Description("API组名称,用于分类管理,如:用户管理"),
|
||||
),
|
||||
mcp.WithString("method",
|
||||
mcp.Description("HTTP方法"),
|
||||
mcp.DefaultString("POST"),
|
||||
),
|
||||
mcp.WithString("apis",
|
||||
mcp.Description("批量创建API的JSON字符串,格式:[{\"path\":\"/user/create\",\"description\":\"创建用户\",\"apiGroup\":\"用户管理\",\"method\":\"POST\"}]"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理API创建请求
|
||||
func (a *ApiCreator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
|
||||
var apis []ApiCreateRequest
|
||||
|
||||
// 检查是否是批量创建
|
||||
if apisStr, ok := args["apis"].(string); ok && apisStr != "" {
|
||||
if err := json.Unmarshal([]byte(apisStr), &apis); err != nil {
|
||||
return nil, fmt.Errorf("apis 参数格式错误: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 单个API创建
|
||||
path, ok := args["path"].(string)
|
||||
if !ok || path == "" {
|
||||
return nil, errors.New("path 参数是必需的")
|
||||
}
|
||||
|
||||
description, ok := args["description"].(string)
|
||||
if !ok || description == "" {
|
||||
return nil, errors.New("description 参数是必需的")
|
||||
}
|
||||
|
||||
apiGroup, ok := args["apiGroup"].(string)
|
||||
if !ok || apiGroup == "" {
|
||||
return nil, errors.New("apiGroup 参数是必需的")
|
||||
}
|
||||
|
||||
method := "POST"
|
||||
if val, ok := args["method"].(string); ok && val != "" {
|
||||
method = val
|
||||
}
|
||||
|
||||
apis = append(apis, ApiCreateRequest{
|
||||
Path: path,
|
||||
Description: description,
|
||||
ApiGroup: apiGroup,
|
||||
Method: method,
|
||||
})
|
||||
}
|
||||
|
||||
if len(apis) == 0 {
|
||||
return nil, errors.New("没有要创建的API")
|
||||
}
|
||||
|
||||
// 创建API记录
|
||||
apiService := service.ServiceGroupApp.SystemServiceGroup.ApiService
|
||||
var responses []ApiCreateResponse
|
||||
successCount := 0
|
||||
|
||||
for _, apiReq := range apis {
|
||||
api := system.SysApi{
|
||||
Path: apiReq.Path,
|
||||
Description: apiReq.Description,
|
||||
ApiGroup: apiReq.ApiGroup,
|
||||
Method: apiReq.Method,
|
||||
}
|
||||
|
||||
err := apiService.CreateApi(api)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn("创建API失败",
|
||||
zap.String("path", apiReq.Path),
|
||||
zap.String("method", apiReq.Method),
|
||||
zap.Error(err))
|
||||
|
||||
responses = append(responses, ApiCreateResponse{
|
||||
Success: false,
|
||||
Message: fmt.Sprintf("创建API失败: %v", err),
|
||||
Path: apiReq.Path,
|
||||
Method: apiReq.Method,
|
||||
})
|
||||
} else {
|
||||
// 获取创建的API ID
|
||||
var createdApi system.SysApi
|
||||
err = global.GVA_DB.Where("path = ? AND method = ?", apiReq.Path, apiReq.Method).First(&createdApi).Error
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn("获取创建的API ID失败", zap.Error(err))
|
||||
}
|
||||
|
||||
responses = append(responses, ApiCreateResponse{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("成功创建API %s %s", apiReq.Method, apiReq.Path),
|
||||
ApiID: createdApi.ID,
|
||||
Path: apiReq.Path,
|
||||
Method: apiReq.Method,
|
||||
})
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
// 构建总体响应
|
||||
var resultMessage string
|
||||
if len(apis) == 1 {
|
||||
resultMessage = responses[0].Message
|
||||
} else {
|
||||
resultMessage = fmt.Sprintf("批量创建API完成,成功 %d 个,失败 %d 个", successCount, len(apis)-successCount)
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"success": successCount > 0,
|
||||
"message": resultMessage,
|
||||
"totalCount": len(apis),
|
||||
"successCount": successCount,
|
||||
"failedCount": len(apis) - successCount,
|
||||
"details": responses,
|
||||
}
|
||||
|
||||
resultJSON, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
// 添加权限分配提醒
|
||||
permissionReminder := "\n\n⚠️ 重要提醒:\n" +
|
||||
"API创建完成后,请前往【系统管理】->【角色管理】中为相关角色分配新创建的API权限," +
|
||||
"以确保用户能够正常访问新接口。\n" +
|
||||
"具体步骤:\n" +
|
||||
"1. 进入角色管理页面\n" +
|
||||
"2. 选择需要授权的角色\n" +
|
||||
"3. 在API权限中勾选新创建的API接口\n" +
|
||||
"4. 保存权限配置"
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("API创建结果:\n\n%s%s", string(resultJSON), permissionReminder),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
165
mcp/api_lister.go
Normal file
165
mcp/api_lister.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
// 注册工具将在enter.go中统一处理
|
||||
RegisterTool(&ApiLister{})
|
||||
}
|
||||
|
||||
// ApiInfo API信息结构
|
||||
type ApiInfo struct {
|
||||
ID uint `json:"id,omitempty"` // 数据库ID(仅数据库API有)
|
||||
Path string `json:"path"` // API路径
|
||||
Description string `json:"description,omitempty"` // API描述
|
||||
ApiGroup string `json:"apiGroup,omitempty"` // API组
|
||||
Method string `json:"method"` // HTTP方法
|
||||
Source string `json:"source"` // 来源:database 或 gin
|
||||
}
|
||||
|
||||
// ApiListResponse API列表响应结构
|
||||
type ApiListResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
DatabaseApis []ApiInfo `json:"databaseApis"` // 数据库中的API
|
||||
GinApis []ApiInfo `json:"ginApis"` // gin框架中的API
|
||||
TotalCount int `json:"totalCount"` // 总数量
|
||||
}
|
||||
|
||||
// ApiLister API列表工具
|
||||
type ApiLister struct{}
|
||||
|
||||
// New 创建API列表工具
|
||||
func (a *ApiLister) New() mcp.Tool {
|
||||
return mcp.NewTool("list_all_apis",
|
||||
mcp.WithDescription(`获取系统中所有的API接口,分为两组:
|
||||
|
||||
**功能说明:**
|
||||
- 返回数据库中已注册的API列表
|
||||
- 返回gin框架中实际注册的路由API列表
|
||||
- 帮助前端判断是使用现有API还是需要创建新的API,如果api在前端未使用且需要前端调用的时候,请到api文件夹下对应模块的js中添加方法并暴露给当前业务调用
|
||||
|
||||
**返回数据结构:**
|
||||
- databaseApis: 数据库中的API记录(包含ID、描述、分组等完整信息)
|
||||
- ginApis: gin路由中的API(仅包含路径和方法),需要AI根据路径自行揣摩路径的业务含义,例如:/api/user/:id 表示根据用户ID获取用户信息`),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理API列表请求
|
||||
func (a *ApiLister) Handle(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
|
||||
// 获取数据库中的API
|
||||
databaseApis, err := a.getDatabaseApis()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取数据库API失败", zap.Error(err))
|
||||
errorResponse := ApiListResponse{
|
||||
Success: false,
|
||||
Message: "获取数据库API失败: " + err.Error(),
|
||||
}
|
||||
resultJSON, _ := json.Marshal(errorResponse)
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: string(resultJSON),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 获取gin路由中的API
|
||||
ginApis, err := a.getGinApis()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取gin路由API失败", zap.Error(err))
|
||||
errorResponse := ApiListResponse{
|
||||
Success: false,
|
||||
Message: "获取gin路由API失败: " + err.Error(),
|
||||
}
|
||||
resultJSON, _ := json.Marshal(errorResponse)
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: string(resultJSON),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
response := ApiListResponse{
|
||||
Success: true,
|
||||
Message: "获取API列表成功",
|
||||
DatabaseApis: databaseApis,
|
||||
GinApis: ginApis,
|
||||
TotalCount: len(databaseApis) + len(ginApis),
|
||||
}
|
||||
|
||||
global.GVA_LOG.Info("API列表获取成功",
|
||||
zap.Int("数据库API数量", len(databaseApis)),
|
||||
zap.Int("gin路由API数量", len(ginApis)),
|
||||
zap.Int("总数量", response.TotalCount))
|
||||
|
||||
resultJSON, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: string(resultJSON),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getDatabaseApis 获取数据库中的所有API
|
||||
func (a *ApiLister) getDatabaseApis() ([]ApiInfo, error) {
|
||||
var apis []system.SysApi
|
||||
err := global.GVA_DB.Model(&system.SysApi{}).Order("api_group ASC, path ASC").Find(&apis).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 转换为ApiInfo格式
|
||||
var result []ApiInfo
|
||||
for _, api := range apis {
|
||||
result = append(result, ApiInfo{
|
||||
ID: api.ID,
|
||||
Path: api.Path,
|
||||
Description: api.Description,
|
||||
ApiGroup: api.ApiGroup,
|
||||
Method: api.Method,
|
||||
Source: "database",
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// getGinApis 获取gin路由中的所有API(包含被忽略的API)
|
||||
func (a *ApiLister) getGinApis() ([]ApiInfo, error) {
|
||||
// 从gin路由信息中获取所有API
|
||||
var result []ApiInfo
|
||||
for _, route := range global.GVA_ROUTERS {
|
||||
result = append(result, ApiInfo{
|
||||
Path: route.Path,
|
||||
Method: route.Method,
|
||||
Source: "gin",
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
39
mcp/client/client.go
Normal file
39
mcp/client/client.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
mcpClient "github.com/mark3labs/mcp-go/client"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
func NewClient(baseUrl, name, version, serverName string) (*mcpClient.Client, error) {
|
||||
client, err := mcpClient.NewSSEMCPClient(baseUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 启动client
|
||||
if err := client.Start(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 初始化
|
||||
initRequest := mcp.InitializeRequest{}
|
||||
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
|
||||
initRequest.Params.ClientInfo = mcp.Implementation{
|
||||
Name: name,
|
||||
Version: version,
|
||||
}
|
||||
|
||||
result, err := client.Initialize(ctx, initRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.ServerInfo.Name != serverName {
|
||||
return nil, errors.New("server name mismatch")
|
||||
}
|
||||
return client, nil
|
||||
}
|
132
mcp/client/client_test.go
Normal file
132
mcp/client/client_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 测试 MCP 客户端连接
|
||||
func TestMcpClientConnection(t *testing.T) {
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTools(t *testing.T) {
|
||||
t.Run("currentTime", func(t *testing.T) {
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
request := mcp.CallToolRequest{}
|
||||
request.Params.Name = "currentTime"
|
||||
request.Params.Arguments = map[string]interface{}{
|
||||
"timezone": "UTC+8",
|
||||
}
|
||||
|
||||
result, err := c.CallTool(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatalf("方法调用错误: %v", err)
|
||||
}
|
||||
|
||||
if len(result.Content) != 1 {
|
||||
t.Errorf("应该有且仅返回1条信息,但是现在有 %d", len(result.Content))
|
||||
}
|
||||
if content, ok := result.Content[0].(mcp.TextContent); ok {
|
||||
t.Logf("成功返回信息%s", content.Text)
|
||||
} else {
|
||||
t.Logf("返回为止类型信息%+v", content)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("getNickname", func(t *testing.T) {
|
||||
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// Initialize
|
||||
initRequest := mcp.InitializeRequest{}
|
||||
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
|
||||
initRequest.Params.ClientInfo = mcp.Implementation{
|
||||
Name: "test-client",
|
||||
Version: "1.0.0",
|
||||
}
|
||||
|
||||
_, err = c.Initialize(ctx, initRequest)
|
||||
if err != nil {
|
||||
t.Fatalf("初始化失败: %v", err)
|
||||
}
|
||||
|
||||
request := mcp.CallToolRequest{}
|
||||
request.Params.Name = "getNickname"
|
||||
request.Params.Arguments = map[string]interface{}{
|
||||
"username": "admin",
|
||||
}
|
||||
|
||||
result, err := c.CallTool(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatalf("方法调用错误: %v", err)
|
||||
}
|
||||
|
||||
if len(result.Content) != 1 {
|
||||
t.Errorf("应该有且仅返回1条信息,但是现在有 %d", len(result.Content))
|
||||
}
|
||||
if content, ok := result.Content[0].(mcp.TextContent); ok {
|
||||
t.Logf("成功返回信息%s", content.Text)
|
||||
} else {
|
||||
t.Logf("返回为止类型信息%+v", content)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTools(t *testing.T) {
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
toolsRequest := mcp.ListToolsRequest{}
|
||||
|
||||
toolListResult, err := c.ListTools(ctx, toolsRequest)
|
||||
if err != nil {
|
||||
t.Fatalf("获取工具列表失败: %v", err)
|
||||
}
|
||||
for i := range toolListResult.Tools {
|
||||
tool := toolListResult.Tools[i]
|
||||
fmt.Printf("工具名称: %s\n", tool.Name)
|
||||
fmt.Printf("工具描述: %s\n", tool.Description)
|
||||
|
||||
// 打印参数信息
|
||||
if tool.InputSchema.Properties != nil {
|
||||
fmt.Println("参数列表:")
|
||||
for paramName, prop := range tool.InputSchema.Properties {
|
||||
required := "否"
|
||||
// 检查参数是否在必填列表中
|
||||
for _, reqField := range tool.InputSchema.Required {
|
||||
if reqField == paramName {
|
||||
required = "是"
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Printf(" - %s (类型: %s, 描述: %s, 必填: %s)\n",
|
||||
paramName, prop.(map[string]any)["type"], prop.(map[string]any)["description"], required)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("该工具没有参数")
|
||||
}
|
||||
fmt.Println("-------------------")
|
||||
}
|
||||
}
|
310
mcp/dictionary_generator.go
Normal file
310
mcp/dictionary_generator.go
Normal file
@@ -0,0 +1,310 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system"
|
||||
"git.echol.cn/loser/lckt/service"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterTool(&DictionaryOptionsGenerator{})
|
||||
}
|
||||
|
||||
// DictionaryOptionsGenerator 字典选项生成器
|
||||
type DictionaryOptionsGenerator struct{}
|
||||
|
||||
// DictionaryGenerateRequest 字典生成请求
|
||||
type DictionaryGenerateRequest struct {
|
||||
DictType string `json:"dictType"` // 字典类型
|
||||
FieldDesc string `json:"fieldDesc"` // 字段描述
|
||||
Options []DictionaryOption `json:"options"` // AI生成的字典选项
|
||||
DictName string `json:"dictName"` // 字典名称(可选)
|
||||
Description string `json:"description"` // 字典描述(可选)
|
||||
}
|
||||
|
||||
// DictionaryGenerateResponse 字典生成响应
|
||||
type DictionaryGenerateResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
DictType string `json:"dictType"`
|
||||
OptionsCount int `json:"optionsCount"`
|
||||
}
|
||||
|
||||
// New 返回工具注册信息
|
||||
func (d *DictionaryOptionsGenerator) New() mcp.Tool {
|
||||
return mcp.NewTool("generate_dictionary_options",
|
||||
mcp.WithDescription("智能生成字典选项并自动创建字典和字典详情"),
|
||||
mcp.WithString("dictType",
|
||||
mcp.Required(),
|
||||
mcp.Description("字典类型,用于标识字典的唯一性"),
|
||||
),
|
||||
mcp.WithString("fieldDesc",
|
||||
mcp.Required(),
|
||||
mcp.Description("字段描述,用于AI理解字段含义"),
|
||||
),
|
||||
mcp.WithString("options",
|
||||
mcp.Required(),
|
||||
mcp.Description("字典选项JSON字符串,格式:[{\"label\":\"显示名\",\"value\":\"值\",\"sort\":1}]"),
|
||||
),
|
||||
mcp.WithString("dictName",
|
||||
mcp.Description("字典名称,如果不提供将自动生成"),
|
||||
),
|
||||
mcp.WithString("description",
|
||||
mcp.Description("字典描述"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Name 返回工具名称
|
||||
func (d *DictionaryOptionsGenerator) Name() string {
|
||||
return "generate_dictionary_options"
|
||||
}
|
||||
|
||||
// Description 返回工具描述
|
||||
func (d *DictionaryOptionsGenerator) Description() string {
|
||||
return `字典选项生成工具 - 让AI生成并创建字典选项
|
||||
|
||||
此工具允许AI根据字典类型和字段描述生成合适的字典选项,并自动创建字典和字典详情。
|
||||
|
||||
参数说明:
|
||||
- dictType: 字典类型(必填)
|
||||
- fieldDesc: 字段描述(必填)
|
||||
- options: AI生成的字典选项数组(必填)
|
||||
- label: 选项标签
|
||||
- value: 选项值
|
||||
- sort: 排序号
|
||||
- dictName: 字典名称(可选,默认根据fieldDesc生成)
|
||||
- description: 字典描述(可选)
|
||||
|
||||
使用场景:
|
||||
1. 在创建模块时,如果字段需要字典类型,AI可以根据字段描述智能生成合适的选项
|
||||
2. 支持各种业务场景的字典选项生成,如状态、类型、等级等
|
||||
3. 自动创建字典和字典详情,无需手动配置
|
||||
|
||||
示例调用:
|
||||
{
|
||||
"dictType": "user_status",
|
||||
"fieldDesc": "用户状态",
|
||||
"options": [
|
||||
{"label": "正常", "value": "1", "sort": 1},
|
||||
{"label": "禁用", "value": "0", "sort": 2}
|
||||
],
|
||||
"dictName": "用户状态字典",
|
||||
"description": "用于管理用户账户状态的字典"
|
||||
}`
|
||||
}
|
||||
|
||||
// InputSchema 返回输入参数的JSON Schema
|
||||
func (d *DictionaryOptionsGenerator) InputSchema() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"dictType": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "字典类型,用于标识字典的唯一性",
|
||||
},
|
||||
"fieldDesc": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "字段描述,用于生成字典名称和理解字典用途",
|
||||
},
|
||||
"options": map[string]interface{}{
|
||||
"type": "array",
|
||||
"description": "AI生成的字典选项数组",
|
||||
"items": map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"label": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "选项标签,显示给用户的文本",
|
||||
},
|
||||
"value": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "选项值,存储在数据库中的值",
|
||||
},
|
||||
"sort": map[string]interface{}{
|
||||
"type": "integer",
|
||||
"description": "排序号,用于控制选项显示顺序",
|
||||
},
|
||||
},
|
||||
"required": []string{"label", "value", "sort"},
|
||||
},
|
||||
},
|
||||
"dictName": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "字典名称,可选,默认根据fieldDesc生成",
|
||||
},
|
||||
"description": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "字典描述,可选",
|
||||
},
|
||||
},
|
||||
"required": []string{"dictType", "fieldDesc", "options"},
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 处理工具调用
|
||||
func (d *DictionaryOptionsGenerator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 解析请求参数
|
||||
args := request.GetArguments()
|
||||
|
||||
dictType, ok := args["dictType"].(string)
|
||||
if !ok || dictType == "" {
|
||||
return nil, errors.New("dictType 参数是必需的")
|
||||
}
|
||||
|
||||
fieldDesc, ok := args["fieldDesc"].(string)
|
||||
if !ok || fieldDesc == "" {
|
||||
return nil, errors.New("fieldDesc 参数是必需的")
|
||||
}
|
||||
|
||||
optionsStr, ok := args["options"].(string)
|
||||
if !ok || optionsStr == "" {
|
||||
return nil, errors.New("options 参数是必需的")
|
||||
}
|
||||
|
||||
// 解析options JSON字符串
|
||||
var options []DictionaryOption
|
||||
if err := json.Unmarshal([]byte(optionsStr), &options); err != nil {
|
||||
return nil, fmt.Errorf("options 参数格式错误: %v", err)
|
||||
}
|
||||
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("options 不能为空")
|
||||
}
|
||||
|
||||
// 可选参数
|
||||
dictName, _ := args["dictName"].(string)
|
||||
description, _ := args["description"].(string)
|
||||
|
||||
// 构建请求对象
|
||||
req := &DictionaryGenerateRequest{
|
||||
DictType: dictType,
|
||||
FieldDesc: fieldDesc,
|
||||
Options: options,
|
||||
DictName: dictName,
|
||||
Description: description,
|
||||
}
|
||||
|
||||
// 创建字典
|
||||
response, err := d.createDictionaryWithOptions(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
resultJSON, err := json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("字典选项生成结果:\n\n%s", string(resultJSON)),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// createDictionaryWithOptions 创建字典和字典选项
|
||||
func (d *DictionaryOptionsGenerator) createDictionaryWithOptions(ctx context.Context, req *DictionaryGenerateRequest) (*DictionaryGenerateResponse, error) {
|
||||
// 检查字典是否已存在
|
||||
exists, err := d.checkDictionaryExists(req.DictType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查字典是否存在失败: %v", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return &DictionaryGenerateResponse{
|
||||
Success: false,
|
||||
Message: fmt.Sprintf("字典 %s 已存在,跳过创建", req.DictType),
|
||||
DictType: req.DictType,
|
||||
OptionsCount: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 生成字典名称
|
||||
dictName := req.DictName
|
||||
if dictName == "" {
|
||||
dictName = d.generateDictionaryName(req.DictType, req.FieldDesc)
|
||||
}
|
||||
|
||||
// 创建字典
|
||||
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
dictionary := system.SysDictionary{
|
||||
Name: dictName,
|
||||
Type: req.DictType,
|
||||
Status: &[]bool{true}[0], // 默认启用
|
||||
Desc: req.Description,
|
||||
}
|
||||
|
||||
err = dictionaryService.CreateSysDictionary(dictionary)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建字典失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取刚创建的字典ID
|
||||
var createdDict system.SysDictionary
|
||||
err = global.GVA_DB.Where("type = ?", req.DictType).First(&createdDict).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取创建的字典失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建字典详情项
|
||||
dictionaryDetailService := service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
|
||||
successCount := 0
|
||||
|
||||
for _, option := range req.Options {
|
||||
dictionaryDetail := system.SysDictionaryDetail{
|
||||
Label: option.Label,
|
||||
Value: option.Value,
|
||||
Status: &[]bool{true}[0], // 默认启用
|
||||
Sort: option.Sort,
|
||||
SysDictionaryID: int(createdDict.ID),
|
||||
}
|
||||
|
||||
err = dictionaryDetailService.CreateSysDictionaryDetail(dictionaryDetail)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn("创建字典详情项失败", zap.Error(err))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
return &DictionaryGenerateResponse{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("成功创建字典 %s,包含 %d 个选项", req.DictType, successCount),
|
||||
DictType: req.DictType,
|
||||
OptionsCount: successCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// checkDictionaryExists 检查字典是否存在
|
||||
func (d *DictionaryOptionsGenerator) checkDictionaryExists(dictType string) (bool, error) {
|
||||
var dictionary system.SysDictionary
|
||||
err := global.GVA_DB.Where("type = ?", dictType).First(&dictionary).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return false, nil // 字典不存在
|
||||
}
|
||||
return false, err // 其他错误
|
||||
}
|
||||
return true, nil // 字典存在
|
||||
}
|
||||
|
||||
// generateDictionaryName 生成字典名称
|
||||
func (d *DictionaryOptionsGenerator) generateDictionaryName(dictType, fieldDesc string) string {
|
||||
if fieldDesc != "" {
|
||||
return fmt.Sprintf("%s字典", fieldDesc)
|
||||
}
|
||||
return fmt.Sprintf("%s字典", dictType)
|
||||
}
|
234
mcp/dictionary_query.go
Normal file
234
mcp/dictionary_query.go
Normal file
@@ -0,0 +1,234 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system"
|
||||
"git.echol.cn/loser/lckt/service"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&DictionaryQuery{})
|
||||
}
|
||||
|
||||
// DictionaryInfo 字典信息结构
|
||||
type DictionaryInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"` // 字典名(中)
|
||||
Type string `json:"type"` // 字典名(英)
|
||||
Status *bool `json:"status"` // 状态
|
||||
Desc string `json:"desc"` // 描述
|
||||
Details []DictionaryDetailInfo `json:"details"` // 字典详情
|
||||
}
|
||||
|
||||
// DictionaryDetailInfo 字典详情信息结构
|
||||
type DictionaryDetailInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Label string `json:"label"` // 展示值
|
||||
Value string `json:"value"` // 字典值
|
||||
Extend string `json:"extend"` // 扩展值
|
||||
Status *bool `json:"status"` // 启用状态
|
||||
Sort int `json:"sort"` // 排序标记
|
||||
}
|
||||
|
||||
// DictionaryQueryResponse 字典查询响应结构
|
||||
type DictionaryQueryResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Total int `json:"total"`
|
||||
Dictionaries []DictionaryInfo `json:"dictionaries"`
|
||||
}
|
||||
|
||||
// DictionaryQuery 字典查询工具
|
||||
type DictionaryQuery struct{}
|
||||
|
||||
// New 创建字典查询工具
|
||||
func (d *DictionaryQuery) New() mcp.Tool {
|
||||
return mcp.NewTool("query_dictionaries",
|
||||
mcp.WithDescription("查询系统中所有的字典和字典属性,用于AI生成逻辑时了解可用的字典选项"),
|
||||
mcp.WithString("dictType",
|
||||
mcp.Description("可选:指定字典类型进行精确查询,如果不提供则返回所有字典"),
|
||||
),
|
||||
mcp.WithBoolean("includeDisabled",
|
||||
mcp.Description("是否包含已禁用的字典和字典项,默认为false(只返回启用的)"),
|
||||
),
|
||||
mcp.WithBoolean("detailsOnly",
|
||||
mcp.Description("是否只返回字典详情信息(不包含字典基本信息),默认为false"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理字典查询请求
|
||||
func (d *DictionaryQuery) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
|
||||
// 获取参数
|
||||
dictType := ""
|
||||
if val, ok := args["dictType"].(string); ok {
|
||||
dictType = val
|
||||
}
|
||||
|
||||
includeDisabled := false
|
||||
if val, ok := args["includeDisabled"].(bool); ok {
|
||||
includeDisabled = val
|
||||
}
|
||||
|
||||
detailsOnly := false
|
||||
if val, ok := args["detailsOnly"].(bool); ok {
|
||||
detailsOnly = val
|
||||
}
|
||||
|
||||
// 获取字典服务
|
||||
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
|
||||
var dictionaries []DictionaryInfo
|
||||
var err error
|
||||
|
||||
if dictType != "" {
|
||||
// 查询指定类型的字典
|
||||
var status *bool
|
||||
if !includeDisabled {
|
||||
status = &[]bool{true}[0]
|
||||
}
|
||||
|
||||
sysDictionary, err := dictionaryService.GetSysDictionary(dictType, 0, status)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询字典失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf(`{"success": false, "message": "查询字典失败: %v", "total": 0, "dictionaries": []}`, err.Error())),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
dictInfo := DictionaryInfo{
|
||||
ID: sysDictionary.ID,
|
||||
Name: sysDictionary.Name,
|
||||
Type: sysDictionary.Type,
|
||||
Status: sysDictionary.Status,
|
||||
Desc: sysDictionary.Desc,
|
||||
}
|
||||
|
||||
// 获取字典详情
|
||||
for _, detail := range sysDictionary.SysDictionaryDetails {
|
||||
if includeDisabled || (detail.Status != nil && *detail.Status) {
|
||||
dictInfo.Details = append(dictInfo.Details, DictionaryDetailInfo{
|
||||
ID: detail.ID,
|
||||
Label: detail.Label,
|
||||
Value: detail.Value,
|
||||
Extend: detail.Extend,
|
||||
Status: detail.Status,
|
||||
Sort: detail.Sort,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dictionaries = append(dictionaries, dictInfo)
|
||||
} else {
|
||||
// 查询所有字典
|
||||
var sysDictionaries []system.SysDictionary
|
||||
db := global.GVA_DB.Model(&system.SysDictionary{})
|
||||
|
||||
if !includeDisabled {
|
||||
db = db.Where("status = ?", true)
|
||||
}
|
||||
|
||||
err = db.Preload("SysDictionaryDetails", func(db *gorm.DB) *gorm.DB {
|
||||
if includeDisabled {
|
||||
return db.Order("sort")
|
||||
} else {
|
||||
return db.Where("status = ?", true).Order("sort")
|
||||
}
|
||||
}).Find(&sysDictionaries).Error
|
||||
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询字典列表失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf(`{"success": false, "message": "查询字典列表失败: %v", "total": 0, "dictionaries": []}`, err.Error())),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
for _, dict := range sysDictionaries {
|
||||
dictInfo := DictionaryInfo{
|
||||
ID: dict.ID,
|
||||
Name: dict.Name,
|
||||
Type: dict.Type,
|
||||
Status: dict.Status,
|
||||
Desc: dict.Desc,
|
||||
}
|
||||
|
||||
// 获取字典详情
|
||||
for _, detail := range dict.SysDictionaryDetails {
|
||||
if includeDisabled || (detail.Status != nil && *detail.Status) {
|
||||
dictInfo.Details = append(dictInfo.Details, DictionaryDetailInfo{
|
||||
ID: detail.ID,
|
||||
Label: detail.Label,
|
||||
Value: detail.Value,
|
||||
Extend: detail.Extend,
|
||||
Status: detail.Status,
|
||||
Sort: detail.Sort,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dictionaries = append(dictionaries, dictInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果只需要详情信息,则提取所有详情
|
||||
if detailsOnly {
|
||||
var allDetails []DictionaryDetailInfo
|
||||
for _, dict := range dictionaries {
|
||||
allDetails = append(allDetails, dict.Details...)
|
||||
}
|
||||
|
||||
response := map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "查询字典详情成功",
|
||||
"total": len(allDetails),
|
||||
"details": allDetails,
|
||||
}
|
||||
|
||||
responseJSON, _ := json.Marshal(response)
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(string(responseJSON)),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
response := DictionaryQueryResponse{
|
||||
Success: true,
|
||||
Message: "查询字典成功",
|
||||
Total: len(dictionaries),
|
||||
Dictionaries: dictionaries,
|
||||
}
|
||||
|
||||
responseJSON, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("序列化响应失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf(`{"success": false, "message": "序列化响应失败: %v", "total": 0, "dictionaries": []}`, err.Error())),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(string(responseJSON)),
|
||||
},
|
||||
}, nil
|
||||
}
|
31
mcp/enter.go
Normal file
31
mcp/enter.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
// McpTool 定义了MCP工具必须实现的接口
|
||||
type McpTool interface {
|
||||
// Handle 返回工具调用信息
|
||||
Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)
|
||||
// New 返回工具注册信息
|
||||
New() mcp.Tool
|
||||
}
|
||||
|
||||
// 工具注册表
|
||||
var toolRegister = make(map[string]McpTool)
|
||||
|
||||
// RegisterTool 供工具在init时调用,将自己注册到工具注册表中
|
||||
func RegisterTool(tool McpTool) {
|
||||
mcpTool := tool.New()
|
||||
toolRegister[mcpTool.Name] = tool
|
||||
}
|
||||
|
||||
// RegisterAllTools 将所有注册的工具注册到MCP服务中
|
||||
func RegisterAllTools(mcpServer *server.MCPServer) {
|
||||
for _, tool := range toolRegister {
|
||||
mcpServer.AddTool(tool.New(), tool.Handle)
|
||||
}
|
||||
}
|
529
mcp/execution_plan_schema.md
Normal file
529
mcp/execution_plan_schema.md
Normal file
@@ -0,0 +1,529 @@
|
||||
# ExecutionPlan 结构体格式说明
|
||||
|
||||
## 概述
|
||||
ExecutionPlan 是用于自动化模块创建的执行计划结构体,包含了创建包和模块所需的所有信息。
|
||||
|
||||
## 完整结构体定义
|
||||
|
||||
```go
|
||||
type ExecutionPlan struct {
|
||||
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"` // 路径信息
|
||||
}
|
||||
```
|
||||
|
||||
## 子结构体详细说明
|
||||
|
||||
### 1. SysAutoCodePackageCreate 结构体
|
||||
|
||||
```go
|
||||
type SysAutoCodePackageCreate struct {
|
||||
Desc string `json:"desc"` // 描述,如:"用户管理模块"
|
||||
Label string `json:"label"` // 展示名,如:"用户管理"
|
||||
Template string `json:"template"` // 模板类型:"plugin" 或 "package"
|
||||
PackageName string `json:"packageName"` // 包名,如:"user"
|
||||
Module string `json:"-"` // 模块名(自动填充,无需设置)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. AutoCode 结构体(核心字段)
|
||||
|
||||
```go
|
||||
type AutoCode struct {
|
||||
Package string `json:"package"` // 包名
|
||||
TableName string `json:"tableName"` // 数据库表名
|
||||
BusinessDB string `json:"businessDB"` // 业务数据库名
|
||||
StructName string `json:"structName"` // 结构体名称
|
||||
PackageName string `json:"packageName"` // 文件名称
|
||||
Description string `json:"description"` // 结构体中文名称
|
||||
Abbreviation string `json:"abbreviation"` // 结构体简称
|
||||
HumpPackageName string `json:"humpPackageName"` // 驼峰命名的包名
|
||||
GvaModel bool `json:"gvaModel"` // 是否使用GVA默认Model
|
||||
AutoMigrate bool `json:"autoMigrate"` // 是否自动迁移表结构
|
||||
AutoCreateResource bool `json:"autoCreateResource"` // 是否自动创建资源标识
|
||||
AutoCreateApiToSql bool `json:"autoCreateApiToSql"` // 是否自动创建API
|
||||
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql"` // 是否自动创建菜单
|
||||
AutoCreateBtnAuth bool `json:"autoCreateBtnAuth"` // 是否自动创建按钮权限
|
||||
OnlyTemplate bool `json:"onlyTemplate"` // 是否只生成模板
|
||||
IsTree bool `json:"isTree"` // 是否树形结构
|
||||
TreeJson string `json:"treeJson"` // 树形结构JSON字段
|
||||
IsAdd bool `json:"isAdd"` // 是否新增
|
||||
Fields []*AutoCodeField `json:"fields"` // 字段列表
|
||||
GenerateWeb bool `json:"generateWeb"` // 是否生成前端代码
|
||||
GenerateServer bool `json:"generateServer"` // 是否生成后端代码
|
||||
Module string `json:"-"` // 模块(自动填充)
|
||||
DictTypes []string `json:"-"` // 字典类型(自动填充)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. AutoCodeField 结构体(字段定义)
|
||||
|
||||
```go
|
||||
type AutoCodeField struct {
|
||||
FieldName string `json:"fieldName"` // 字段名
|
||||
FieldDesc string `json:"fieldDesc"` // 字段中文描述
|
||||
FieldType string `json:"fieldType"` // 字段类型:string, int, bool, time.Time等
|
||||
FieldJson string `json:"fieldJson"` // JSON标签名
|
||||
DataTypeLong string `json:"dataTypeLong"` // 数据库字段长度
|
||||
Comment string `json:"comment"` // 数据库字段注释
|
||||
ColumnName string `json:"columnName"` // 数据库列名
|
||||
FieldSearchType string `json:"fieldSearchType"` // 搜索类型:EQ, LIKE, BETWEEN等
|
||||
FieldSearchHide bool `json:"fieldSearchHide"` // 是否隐藏查询条件
|
||||
DictType string `json:"dictType"` // 字典类型
|
||||
Form bool `json:"form"` // 是否在表单中显示
|
||||
Table bool `json:"table"` // 是否在表格中显示
|
||||
Desc bool `json:"desc"` // 是否在详情中显示
|
||||
Excel bool `json:"excel"` // 是否支持导入导出
|
||||
Require bool `json:"require"` // 是否必填
|
||||
DefaultValue string `json:"defaultValue"` // 默认值
|
||||
ErrorText string `json:"errorText"` // 校验失败提示
|
||||
Clearable bool `json:"clearable"` // 是否可清空
|
||||
Sort bool `json:"sort"` // 是否支持排序
|
||||
PrimaryKey bool `json:"primaryKey"` // 是否主键
|
||||
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:创建新包和批量创建多个模块
|
||||
|
||||
```json
|
||||
{
|
||||
"packageName": "user",
|
||||
"packageType": "package",
|
||||
"needCreatedPackage": true,
|
||||
"needCreatedModules": true,
|
||||
"packageInfo": {
|
||||
"desc": "用户管理模块",
|
||||
"label": "用户管理",
|
||||
"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": {
|
||||
"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:仅在现有包中批量创建多个模块
|
||||
|
||||
```json
|
||||
{
|
||||
"packageName": "system",
|
||||
"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
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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(字符串)
|
||||
- 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验证
|
||||
|
||||
## 常见错误避免
|
||||
|
||||
1. 确保PackageName和ModuleName符合Go语言命名规范
|
||||
2. 字段名使用大写开头的驼峰命名
|
||||
3. JSON标签使用小写开头的驼峰命名
|
||||
4. 数据库列名使用下划线分隔的小写命名
|
||||
5. 必填字段不要遗漏
|
||||
6. 字段类型要与实际需求匹配
|
205
mcp/gag_usage_example.md
Normal file
205
mcp/gag_usage_example.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# GAG工具使用示例 - 带用户确认流程
|
||||
|
||||
## 新的工作流程
|
||||
|
||||
现在GAG工具支持三步工作流程:
|
||||
1. `analyze` - 分析现有模块信息
|
||||
2. `confirm` - 请求用户确认创建计划
|
||||
3. `execute` - 执行创建操作(需要用户确认)
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 第一步:分析
|
||||
```json
|
||||
{
|
||||
"action": "analyze",
|
||||
"requirement": "创建一个图书管理功能"
|
||||
}
|
||||
```
|
||||
|
||||
### 第二步:确认(支持批量创建多个模块)
|
||||
```json
|
||||
{
|
||||
"action": "confirm",
|
||||
"executionPlan": {
|
||||
"packageName": "library",
|
||||
"packageType": "package",
|
||||
"needCreatedPackage": true,
|
||||
"needCreatedModules": true,
|
||||
"packageInfo": {
|
||||
"desc": "图书管理包",
|
||||
"label": "图书管理",
|
||||
"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": ""
|
||||
},
|
||||
{
|
||||
"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": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 第三步:执行(需要确认参数)
|
||||
```json
|
||||
{
|
||||
"action": "execute",
|
||||
"executionPlan": {
|
||||
// ... 同上面的executionPlan
|
||||
},
|
||||
"packageConfirm": "yes", // 确认创建包
|
||||
"modulesConfirm": "yes" // 确认创建模块
|
||||
}
|
||||
```
|
||||
|
||||
## 确认参数说明
|
||||
|
||||
- `packageConfirm`: 当`needCreatedPackage`为true时必需
|
||||
- "yes": 确认创建包
|
||||
- "no": 取消创建包(停止后续处理)
|
||||
|
||||
- `modulesConfirm`: 当`needCreatedModules`为true时必需
|
||||
- "yes": 确认创建模块
|
||||
- "no": 取消创建模块(停止后续处理)
|
||||
|
||||
## 取消操作的行为
|
||||
|
||||
1. 如果用户在`packageConfirm`中选择"no",系统将停止所有后续处理
|
||||
2. 如果用户在`modulesConfirm`中选择"no",系统将停止模块创建
|
||||
3. 任何取消操作都会返回相应的取消消息,不会执行任何创建操作
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 必须先调用`confirm`来获取确认信息
|
||||
2. 在`execute`时必须提供相应的确认参数
|
||||
3. 确认参数的值必须是"yes"或"no"
|
||||
4. 如果不需要创建包或模块,则不需要提供对应的确认参数
|
||||
5. 字段类型支持:string(字符串),richtext(富文本),int(整型),bool(布尔值),float64(浮点型),time.Time(时间),enum(枚举),picture(单图片,字符串),pictures(多图片,json字符串),video(视频,字符串),file(文件,json字符串),json(JSON),array(数组)
|
1755
mcp/gva_auto_generate.go
Normal file
1755
mcp/gva_auto_generate.go
Normal file
File diff suppressed because it is too large
Load Diff
287
mcp/menu_creator.go
Normal file
287
mcp/menu_creator.go
Normal file
@@ -0,0 +1,287 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system"
|
||||
"git.echol.cn/loser/lckt/service"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&MenuCreator{})
|
||||
}
|
||||
|
||||
// MenuCreateRequest 菜单创建请求结构
|
||||
type MenuCreateRequest struct {
|
||||
ParentId uint `json:"parentId"` // 父菜单ID,0表示根菜单
|
||||
Path string `json:"path"` // 路由path
|
||||
Name string `json:"name"` // 路由name
|
||||
Hidden bool `json:"hidden"` // 是否在列表隐藏
|
||||
Component string `json:"component"` // 对应前端文件路径
|
||||
Sort int `json:"sort"` // 排序标记
|
||||
Title string `json:"title"` // 菜单名
|
||||
Icon string `json:"icon"` // 菜单图标
|
||||
KeepAlive bool `json:"keepAlive"` // 是否缓存
|
||||
DefaultMenu bool `json:"defaultMenu"` // 是否是基础路由
|
||||
CloseTab bool `json:"closeTab"` // 自动关闭tab
|
||||
ActiveName string `json:"activeName"` // 高亮菜单
|
||||
Parameters []MenuParameterRequest `json:"parameters"` // 路由参数
|
||||
MenuBtn []MenuButtonRequest `json:"menuBtn"` // 菜单按钮
|
||||
}
|
||||
|
||||
// MenuParameterRequest 菜单参数请求结构
|
||||
type MenuParameterRequest struct {
|
||||
Type string `json:"type"` // 参数类型:params或query
|
||||
Key string `json:"key"` // 参数key
|
||||
Value string `json:"value"` // 参数值
|
||||
}
|
||||
|
||||
// MenuButtonRequest 菜单按钮请求结构
|
||||
type MenuButtonRequest struct {
|
||||
Name string `json:"name"` // 按钮名称
|
||||
Desc string `json:"desc"` // 按钮描述
|
||||
}
|
||||
|
||||
// MenuCreateResponse 菜单创建响应结构
|
||||
type MenuCreateResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
MenuID uint `json:"menuId"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// MenuCreator 菜单创建工具
|
||||
type MenuCreator struct{}
|
||||
|
||||
// New 创建菜单创建工具
|
||||
func (m *MenuCreator) New() mcp.Tool {
|
||||
return mcp.NewTool("create_menu",
|
||||
mcp.WithDescription(`创建前端菜单记录,用于AI编辑器自动添加前端页面时自动创建对应的菜单项。
|
||||
|
||||
**重要限制:**
|
||||
- 当使用gva_auto_generate工具且needCreatedModules=true时,模块创建会自动生成菜单项,不应调用此工具
|
||||
- 仅在以下情况使用:1) 单独创建菜单(不涉及模块创建);2) AI编辑器自动添加前端页面时`),
|
||||
mcp.WithNumber("parentId",
|
||||
mcp.Description("父菜单ID,0表示根菜单"),
|
||||
mcp.DefaultNumber(0),
|
||||
),
|
||||
mcp.WithString("path",
|
||||
mcp.Required(),
|
||||
mcp.Description("路由path,如:userList"),
|
||||
),
|
||||
mcp.WithString("name",
|
||||
mcp.Required(),
|
||||
mcp.Description("路由name,用于Vue Router,如:userList"),
|
||||
),
|
||||
mcp.WithBoolean("hidden",
|
||||
mcp.Description("是否在菜单列表中隐藏"),
|
||||
),
|
||||
mcp.WithString("component",
|
||||
mcp.Required(),
|
||||
mcp.Description("对应的前端Vue组件路径,如:view/user/list.vue"),
|
||||
),
|
||||
mcp.WithNumber("sort",
|
||||
mcp.Description("菜单排序号,数字越小越靠前"),
|
||||
mcp.DefaultNumber(1),
|
||||
),
|
||||
mcp.WithString("title",
|
||||
mcp.Required(),
|
||||
mcp.Description("菜单显示标题"),
|
||||
),
|
||||
mcp.WithString("icon",
|
||||
mcp.Description("菜单图标名称"),
|
||||
mcp.DefaultString("menu"),
|
||||
),
|
||||
mcp.WithBoolean("keepAlive",
|
||||
mcp.Description("是否缓存页面"),
|
||||
),
|
||||
mcp.WithBoolean("defaultMenu",
|
||||
mcp.Description("是否是基础路由"),
|
||||
),
|
||||
mcp.WithBoolean("closeTab",
|
||||
mcp.Description("是否自动关闭tab"),
|
||||
),
|
||||
mcp.WithString("activeName",
|
||||
mcp.Description("高亮菜单名称"),
|
||||
),
|
||||
mcp.WithString("parameters",
|
||||
mcp.Description("路由参数JSON字符串,格式:[{\"type\":\"params\",\"key\":\"id\",\"value\":\"1\"}]"),
|
||||
),
|
||||
mcp.WithString("menuBtn",
|
||||
mcp.Description("菜单按钮JSON字符串,格式:[{\"name\":\"add\",\"desc\":\"新增\"}]"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理菜单创建请求
|
||||
func (m *MenuCreator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 解析请求参数
|
||||
args := request.GetArguments()
|
||||
|
||||
// 必需参数
|
||||
path, ok := args["path"].(string)
|
||||
if !ok || path == "" {
|
||||
return nil, errors.New("path 参数是必需的")
|
||||
}
|
||||
|
||||
name, ok := args["name"].(string)
|
||||
if !ok || name == "" {
|
||||
return nil, errors.New("name 参数是必需的")
|
||||
}
|
||||
|
||||
component, ok := args["component"].(string)
|
||||
if !ok || component == "" {
|
||||
return nil, errors.New("component 参数是必需的")
|
||||
}
|
||||
|
||||
title, ok := args["title"].(string)
|
||||
if !ok || title == "" {
|
||||
return nil, errors.New("title 参数是必需的")
|
||||
}
|
||||
|
||||
// 可选参数
|
||||
parentId := uint(0)
|
||||
if val, ok := args["parentId"].(float64); ok {
|
||||
parentId = uint(val)
|
||||
}
|
||||
|
||||
hidden := false
|
||||
if val, ok := args["hidden"].(bool); ok {
|
||||
hidden = val
|
||||
}
|
||||
|
||||
sort := 1
|
||||
if val, ok := args["sort"].(float64); ok {
|
||||
sort = int(val)
|
||||
}
|
||||
|
||||
icon := "menu"
|
||||
if val, ok := args["icon"].(string); ok && val != "" {
|
||||
icon = val
|
||||
}
|
||||
|
||||
keepAlive := false
|
||||
if val, ok := args["keepAlive"].(bool); ok {
|
||||
keepAlive = val
|
||||
}
|
||||
|
||||
defaultMenu := false
|
||||
if val, ok := args["defaultMenu"].(bool); ok {
|
||||
defaultMenu = val
|
||||
}
|
||||
|
||||
closeTab := false
|
||||
if val, ok := args["closeTab"].(bool); ok {
|
||||
closeTab = val
|
||||
}
|
||||
|
||||
activeName := ""
|
||||
if val, ok := args["activeName"].(string); ok {
|
||||
activeName = val
|
||||
}
|
||||
|
||||
// 解析参数和按钮
|
||||
var parameters []system.SysBaseMenuParameter
|
||||
if parametersStr, ok := args["parameters"].(string); ok && parametersStr != "" {
|
||||
var paramReqs []MenuParameterRequest
|
||||
if err := json.Unmarshal([]byte(parametersStr), ¶mReqs); err != nil {
|
||||
return nil, fmt.Errorf("parameters 参数格式错误: %v", err)
|
||||
}
|
||||
for _, param := range paramReqs {
|
||||
parameters = append(parameters, system.SysBaseMenuParameter{
|
||||
Type: param.Type,
|
||||
Key: param.Key,
|
||||
Value: param.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var menuBtn []system.SysBaseMenuBtn
|
||||
if menuBtnStr, ok := args["menuBtn"].(string); ok && menuBtnStr != "" {
|
||||
var btnReqs []MenuButtonRequest
|
||||
if err := json.Unmarshal([]byte(menuBtnStr), &btnReqs); err != nil {
|
||||
return nil, fmt.Errorf("menuBtn 参数格式错误: %v", err)
|
||||
}
|
||||
for _, btn := range btnReqs {
|
||||
menuBtn = append(menuBtn, system.SysBaseMenuBtn{
|
||||
Name: btn.Name,
|
||||
Desc: btn.Desc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 构建菜单对象
|
||||
menu := system.SysBaseMenu{
|
||||
ParentId: parentId,
|
||||
Path: path,
|
||||
Name: name,
|
||||
Hidden: hidden,
|
||||
Component: component,
|
||||
Sort: sort,
|
||||
Meta: system.Meta{
|
||||
Title: title,
|
||||
Icon: icon,
|
||||
KeepAlive: keepAlive,
|
||||
DefaultMenu: defaultMenu,
|
||||
CloseTab: closeTab,
|
||||
ActiveName: activeName,
|
||||
},
|
||||
Parameters: parameters,
|
||||
MenuBtn: menuBtn,
|
||||
}
|
||||
|
||||
// 创建菜单
|
||||
menuService := service.ServiceGroupApp.SystemServiceGroup.MenuService
|
||||
err := menuService.AddBaseMenu(menu)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建菜单失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取创建的菜单ID
|
||||
var createdMenu system.SysBaseMenu
|
||||
err = global.GVA_DB.Where("name = ? AND path = ?", name, path).First(&createdMenu).Error
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn("获取创建的菜单ID失败", zap.Error(err))
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
response := &MenuCreateResponse{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("成功创建菜单 %s", title),
|
||||
MenuID: createdMenu.ID,
|
||||
Name: name,
|
||||
Path: path,
|
||||
}
|
||||
|
||||
resultJSON, err := json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
// 添加权限分配提醒
|
||||
permissionReminder := "\n\n⚠️ 重要提醒:\n" +
|
||||
"菜单创建完成后,请前往【系统管理】->【角色管理】中为相关角色分配新创建的菜单权限," +
|
||||
"以确保用户能够正常访问新菜单。\n" +
|
||||
"具体步骤:\n" +
|
||||
"1. 进入角色管理页面\n" +
|
||||
"2. 选择需要授权的角色\n" +
|
||||
"3. 在菜单权限中勾选新创建的菜单项\n" +
|
||||
"4. 保存权限配置"
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("菜单创建结果:\n\n%s%s", string(resultJSON), permissionReminder),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
111
mcp/menu_lister.go
Normal file
111
mcp/menu_lister.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
// 注册工具将在enter.go中统一处理
|
||||
RegisterTool(&MenuLister{})
|
||||
}
|
||||
|
||||
// MenuListResponse 菜单列表响应结构
|
||||
type MenuListResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Menus []system.SysBaseMenu `json:"menus"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// MenuLister 菜单列表工具
|
||||
type MenuLister struct{}
|
||||
|
||||
// New 创建菜单列表工具
|
||||
func (m *MenuLister) New() mcp.Tool {
|
||||
return mcp.NewTool("list_all_menus",
|
||||
mcp.WithDescription(`获取系统中所有菜单信息,包括菜单树结构、路由信息、组件路径等,用于前端编写vue-router时正确跳转
|
||||
|
||||
**功能说明:**
|
||||
- 返回完整的菜单树形结构
|
||||
- 包含路由配置信息(path、name、component)
|
||||
- 包含菜单元数据(title、icon、keepAlive等)
|
||||
- 包含菜单参数和按钮配置
|
||||
- 支持父子菜单关系展示
|
||||
|
||||
**使用场景:**
|
||||
- 前端路由配置:获取所有菜单信息用于配置vue-router
|
||||
- 菜单权限管理:了解系统中所有可用的菜单项
|
||||
- 导航组件开发:构建动态导航菜单
|
||||
- 系统架构分析:了解系统的菜单结构和页面组织`),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理菜单列表请求
|
||||
func (m *MenuLister) Handle(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 获取所有基础菜单
|
||||
allMenus, err := m.getAllMenus()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取菜单列表失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("获取菜单列表失败: %v", err),
|
||||
},
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 构建返回结果
|
||||
response := MenuListResponse{
|
||||
Success: true,
|
||||
Message: "获取菜单列表成功",
|
||||
Menus: allMenus,
|
||||
TotalCount: len(allMenus),
|
||||
Description: "系统中所有菜单信息的标准列表,包含路由配置和组件信息",
|
||||
}
|
||||
|
||||
// 序列化响应
|
||||
responseJSON, err := json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("序列化菜单响应失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("序列化响应失败: %v", err),
|
||||
},
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: string(responseJSON),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getAllMenus 获取所有基础菜单
|
||||
func (m *MenuLister) getAllMenus() ([]system.SysBaseMenu, error) {
|
||||
var menus []system.SysBaseMenu
|
||||
err := global.GVA_DB.Order("sort").Preload("Parameters").Preload("MenuBtn").Find(&menus).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return menus, nil
|
||||
}
|
137
mcp/requirement_analyzer.go
Normal file
137
mcp/requirement_analyzer.go
Normal 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. 第三步功能描述**
|
||||
**...**
|
||||
|
||||
## 📋 梳理要求
|
||||
- 将需求拆解为具体的功能步骤
|
||||
- 每个步骤用数字编号(1、2、3...)
|
||||
- 步骤描述要清晰、具体、可执行
|
||||
- 按照业务逻辑顺序排列
|
||||
- 考虑数据流和用户操作流程
|
||||
|
||||
## 🔄 后续流程
|
||||
梳理完成后,请使用 gva_auto_generate 工具进行代码生成:
|
||||
- gva_auto_generate 会根据梳理的逻辑步骤自动生成包名、模块名
|
||||
- gva_auto_generate 会设计数据表结构和API接口
|
||||
- gva_auto_generate 会生成完整的前后端代码
|
||||
|
||||
|
||||
现在请开始梳理用户需求:"%s"`, userRequirement, userRequirement)
|
||||
|
||||
return prompt
|
||||
}
|
Reference in New Issue
Block a user