🎨 移除多余模块
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -72,7 +72,7 @@ log
|
||||
uploads
|
||||
upload
|
||||
server/config.yaml
|
||||
|
||||
dist
|
||||
# ---> AI
|
||||
.claude
|
||||
.codex
|
||||
|
||||
13
server/api/v1/common/enter.go
Normal file
13
server/api/v1/common/enter.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package common
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
|
||||
type ApiGroup struct {
|
||||
AttachmentCategoryApi
|
||||
FileUploadAndDownloadApi
|
||||
}
|
||||
|
||||
var (
|
||||
attachmentCategoryService = service.ServiceGroupApp.CommonServiceGroup.AttachmentCategoryService
|
||||
fileUploadAndDownloadService = service.ServiceGroupApp.CommonServiceGroup.FileUploadAndDownloadService
|
||||
)
|
||||
@@ -1,10 +1,10 @@
|
||||
package example
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
commonModel "github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -16,7 +16,7 @@ type AttachmentCategoryApi struct{}
|
||||
// @Summary 媒体库分类列表
|
||||
// @Security AttachmentCategory
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=example.ExaAttachmentCategory,msg=string} "媒体库分类列表"
|
||||
// @Success 200 {object} response.Response{data=commonModel.ExaAttachmentCategory,msg=string} "媒体库分类列表"
|
||||
// @Router /attachmentCategory/getCategoryList [get]
|
||||
func (a *AttachmentCategoryApi) GetCategoryList(c *gin.Context) {
|
||||
res, err := attachmentCategoryService.GetCategoryList()
|
||||
@@ -34,10 +34,10 @@ func (a *AttachmentCategoryApi) GetCategoryList(c *gin.Context) {
|
||||
// @Security AttachmentCategory
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaAttachmentCategory true "媒体库分类数据"// @Success 200 {object} response.Response{msg=string} "添加媒体库分类"
|
||||
// @Param data body commonModel.ExaAttachmentCategory true "媒体库分类数据"// @Success 200 {object} response.Response{msg=string} "添加媒体库分类"
|
||||
// @Router /attachmentCategory/addCategory [post]
|
||||
func (a *AttachmentCategoryApi) AddCategory(c *gin.Context) {
|
||||
var req example.ExaAttachmentCategory
|
||||
var req commonModel.ExaAttachmentCategory
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
global.GVA_LOG.Error("参数错误!", zap.Error(err))
|
||||
response.FailWithMessage("参数错误", c)
|
||||
@@ -1,4 +1,4 @@
|
||||
package example
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -7,11 +7,10 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
commonModel "github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response"
|
||||
commonRes "github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
@@ -84,7 +83,7 @@ func (b *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) {
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "Find the file, 查找文件"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.FileResponse,msg=string} "查找文件,返回包括文件详情"
|
||||
// @Success 200 {object} response.Response{data=commonRes.FileResponse,msg=string} "查找文件,返回包括文件详情"
|
||||
// @Router /fileUploadAndDownload/findFile [get]
|
||||
func (b *FileUploadAndDownloadApi) FindFile(c *gin.Context) {
|
||||
fileMd5 := c.Query("fileMd5")
|
||||
@@ -95,7 +94,7 @@ func (b *FileUploadAndDownloadApi) FindFile(c *gin.Context) {
|
||||
global.GVA_LOG.Error("查找失败!", zap.Error(err))
|
||||
response.FailWithMessage("查找失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(exampleRes.FileResponse{File: file}, "查找成功", c)
|
||||
response.OkWithDetailed(commonRes.FileResponse{File: file}, "查找成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +105,7 @@ func (b *FileUploadAndDownloadApi) FindFile(c *gin.Context) {
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "上传文件完成"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.FilePathResponse,msg=string} "创建文件,返回包括文件路径"
|
||||
// @Success 200 {object} response.Response{data=commonRes.FilePathResponse,msg=string} "创建文件,返回包括文件路径"
|
||||
// @Router /fileUploadAndDownload/findFile [post]
|
||||
func (b *FileUploadAndDownloadApi) BreakpointContinueFinish(c *gin.Context) {
|
||||
fileMd5 := c.Query("fileMd5")
|
||||
@@ -114,9 +113,9 @@ func (b *FileUploadAndDownloadApi) BreakpointContinueFinish(c *gin.Context) {
|
||||
filePath, err := utils.MakeFile(fileName, fileMd5)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("文件创建失败!", zap.Error(err))
|
||||
response.FailWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建失败", c)
|
||||
response.FailWithDetailed(commonRes.FilePathResponse{FilePath: filePath}, "文件创建失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建成功", c)
|
||||
response.OkWithDetailed(commonRes.FilePathResponse{FilePath: filePath}, "文件创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +129,7 @@ func (b *FileUploadAndDownloadApi) BreakpointContinueFinish(c *gin.Context) {
|
||||
// @Success 200 {object} response.Response{msg=string} "删除切片"
|
||||
// @Router /fileUploadAndDownload/removeChunk [post]
|
||||
func (b *FileUploadAndDownloadApi) RemoveChunk(c *gin.Context) {
|
||||
var file example.ExaFile
|
||||
var file commonModel.ExaFile
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
@@ -1,11 +1,11 @@
|
||||
package example
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
commonModel "github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
commonReq "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example/request"
|
||||
exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response"
|
||||
commonRes "github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"strconv"
|
||||
@@ -20,10 +20,10 @@ type FileUploadAndDownloadApi struct{}
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "上传文件示例"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.ExaFileResponse,msg=string} "上传文件示例,返回包括文件详情"
|
||||
// @Success 200 {object} response.Response{data=commonRes.ExaFileResponse,msg=string} "上传文件示例,返回包括文件详情"
|
||||
// @Router /fileUploadAndDownload/upload [post]
|
||||
func (b *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
var file commonModel.ExaFileUploadAndDownload
|
||||
noSave := c.DefaultQuery("noSave", "0")
|
||||
_, header, err := c.Request.FormFile("file")
|
||||
classId, _ := strconv.Atoi(c.DefaultPostForm("classId", "0"))
|
||||
@@ -38,12 +38,12 @@ func (b *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
|
||||
response.FailWithMessage("上传文件失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(exampleRes.ExaFileResponse{File: file}, "上传成功", c)
|
||||
response.OkWithDetailed(commonRes.ExaFileResponse{File: file}, "上传成功", c)
|
||||
}
|
||||
|
||||
// EditFileName 编辑文件名或者备注
|
||||
func (b *FileUploadAndDownloadApi) EditFileName(c *gin.Context) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
var file commonModel.ExaFileUploadAndDownload
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
@@ -63,11 +63,11 @@ func (b *FileUploadAndDownloadApi) EditFileName(c *gin.Context) {
|
||||
// @Summary 删除文件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaFileUploadAndDownload true "传入文件里面id即可"
|
||||
// @Param data body commonModel.ExaFileUploadAndDownload true "传入文件里面id即可"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除文件"
|
||||
// @Router /fileUploadAndDownload/deleteFile [post]
|
||||
func (b *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
var file commonModel.ExaFileUploadAndDownload
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
@@ -87,11 +87,11 @@ func (b *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExaAttachmentCategorySearch true "页码, 每页大小, 分类id"
|
||||
// @Param data body commonReq.ExaAttachmentCategorySearch true "页码, 每页大小, 分类id"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页文件列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /fileUploadAndDownload/getFileList [post]
|
||||
func (b *FileUploadAndDownloadApi) GetFileList(c *gin.Context) {
|
||||
var pageInfo request.ExaAttachmentCategorySearch
|
||||
var pageInfo commonReq.ExaAttachmentCategorySearch
|
||||
err := c.ShouldBindJSON(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
@@ -116,11 +116,11 @@ func (b *FileUploadAndDownloadApi) GetFileList(c *gin.Context) {
|
||||
// @Summary 导入URL
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaFileUploadAndDownload true "对象"
|
||||
// @Param data body commonModel.ExaFileUploadAndDownload true "对象"
|
||||
// @Success 200 {object} response.Response{msg=string} "导入URL"
|
||||
// @Router /fileUploadAndDownload/importURL [post]
|
||||
func (b *FileUploadAndDownloadApi) ImportURL(c *gin.Context) {
|
||||
var file []example.ExaFileUploadAndDownload
|
||||
var file []commonModel.ExaFileUploadAndDownload
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
@@ -1,13 +1,13 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/example"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/common"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/system"
|
||||
)
|
||||
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
|
||||
type ApiGroup struct {
|
||||
SystemApiGroup system.ApiGroup
|
||||
ExampleApiGroup example.ApiGroup
|
||||
SystemApiGroup system.ApiGroup
|
||||
CommonApiGroup common.ApiGroup
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package example
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
|
||||
type ApiGroup struct {
|
||||
CustomerApi
|
||||
|
||||
AttachmentCategoryApi
|
||||
FileUploadAndDownloadApi
|
||||
}
|
||||
|
||||
var (
|
||||
customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
|
||||
|
||||
attachmentCategoryService = service.ServiceGroupApp.ExampleServiceGroup.AttachmentCategoryService
|
||||
fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
|
||||
)
|
||||
@@ -1,176 +0,0 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type CustomerApi struct{}
|
||||
|
||||
// CreateExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 创建客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户用户名, 客户手机号码"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建客户"
|
||||
// @Router /customer/customer [post]
|
||||
func (e *CustomerApi) CreateExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
err := c.ShouldBindJSON(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer, utils.CustomerVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
customer.SysUserID = utils.GetUserID(c)
|
||||
customer.SysUserAuthorityID = utils.GetUserAuthorityId(c)
|
||||
err = customerService.CreateExaCustomer(customer)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 删除客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除客户"
|
||||
// @Router /customer/customer [delete]
|
||||
func (e *CustomerApi) DeleteExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
err := c.ShouldBindJSON(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = customerService.DeleteExaCustomer(customer)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 更新客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户ID, 客户信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新客户信息"
|
||||
// @Router /customer/customer [put]
|
||||
func (e *CustomerApi) UpdateExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
err := c.ShouldBindJSON(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer, utils.CustomerVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = customerService.UpdateExaCustomer(&customer)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// GetExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 获取单一客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query example.ExaCustomer true "客户ID"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.ExaCustomerResponse,msg=string} "获取单一客户信息,返回包括客户详情"
|
||||
// @Router /customer/customer [get]
|
||||
func (e *CustomerApi) GetExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
err := c.ShouldBindQuery(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
data, err := customerService.GetExaCustomer(customer.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(exampleRes.ExaCustomerResponse{Customer: data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetExaCustomerList
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 分页获取权限客户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取权限客户列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /customer/customerList [get]
|
||||
func (e *CustomerApi) GetExaCustomerList(c *gin.Context) {
|
||||
var pageInfo request.PageInfo
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(pageInfo, utils.PageInfoVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
customerList, total, err := customerService.GetCustomerInfoList(utils.GetUserAuthorityId(c), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: customerList,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
commonReq "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AIWorkflowSessionApi struct{}
|
||||
|
||||
func (a *AIWorkflowSessionApi) Save(c *gin.Context) {
|
||||
var info systemReq.SysAIWorkflowSessionUpsert
|
||||
if err := c.ShouldBindJSON(&info); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := aiWorkflowSessionService.Save(c.Request.Context(), utils.GetUserID(c), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("保存 AI 工作流会话失败", zap.Error(err))
|
||||
response.FailWithMessage("保存会话失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(gin.H{"session": session}, "保存成功", c)
|
||||
}
|
||||
|
||||
func (a *AIWorkflowSessionApi) GetList(c *gin.Context) {
|
||||
var info systemReq.SysAIWorkflowSessionSearch
|
||||
if err := c.ShouldBindJSON(&info); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
list, total, err := aiWorkflowSessionService.GetList(c.Request.Context(), utils.GetUserID(c), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取 AI 工作流会话列表失败", zap.Error(err))
|
||||
response.FailWithMessage("获取会话列表失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: info.Page,
|
||||
PageSize: info.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (a *AIWorkflowSessionApi) GetDetail(c *gin.Context) {
|
||||
var info commonReq.GetById
|
||||
if err := c.ShouldBindJSON(&info); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := aiWorkflowSessionService.GetDetail(c.Request.Context(), utils.GetUserID(c), info.Uint())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取 AI 工作流会话详情失败", zap.Error(err))
|
||||
response.FailWithMessage("获取会话详情失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(gin.H{"session": session}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (a *AIWorkflowSessionApi) Delete(c *gin.Context) {
|
||||
var info commonReq.GetById
|
||||
if err := c.ShouldBindJSON(&info); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := aiWorkflowSessionService.Delete(c.Request.Context(), utils.GetUserID(c), info.Uint()); err != nil {
|
||||
global.GVA_LOG.Error("删除 AI 工作流会话失败", zap.Error(err))
|
||||
response.FailWithMessage("删除会话失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
func (a *AIWorkflowSessionApi) DumpMarkdown(c *gin.Context) {
|
||||
var info systemReq.SysAIWorkflowMarkdownDump
|
||||
if err := c.ShouldBindJSON(&info); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := aiWorkflowSessionService.DumpMarkdown(c.Request.Context(), utils.GetUserID(c), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("AI 工作流 Markdown 落盘失败", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(gin.H{"result": result}, "落盘成功", c)
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
request "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeHistoryApi struct{}
|
||||
|
||||
// First
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取meta信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "请求参数"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取meta信息"
|
||||
// @Router /autoCode/getMeta [post]
|
||||
func (a *AutoCodeHistoryApi) First(c *gin.Context) {
|
||||
var info common.GetById
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
data, err := autoCodeHistoryService.First(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"meta": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// Delete
|
||||
// @Tags AutoCode
|
||||
// @Summary 删除回滚记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "请求参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除回滚记录"
|
||||
// @Router /autoCode/delSysHistory [post]
|
||||
func (a *AutoCodeHistoryApi) Delete(c *gin.Context) {
|
||||
var info common.GetById
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodeHistoryService.Delete(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// RollBack
|
||||
// @Tags AutoCode
|
||||
// @Summary 回滚自动生成代码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAutoHistoryRollBack true "请求参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "回滚自动生成代码"
|
||||
// @Router /autoCode/rollback [post]
|
||||
func (a *AutoCodeHistoryApi) RollBack(c *gin.Context) {
|
||||
var info request.SysAutoHistoryRollBack
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodeHistoryService.RollBack(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("回滚成功", c)
|
||||
}
|
||||
|
||||
// GetList
|
||||
// @Tags AutoCode
|
||||
// @Summary 查询回滚记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body common.PageInfo true "请求参数"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "查询回滚记录,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /autoCode/getSysHistory [post]
|
||||
func (a *AutoCodeHistoryApi) GetList(c *gin.Context) {
|
||||
var info common.PageInfo
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := autoCodeHistoryService.GetList(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: info.Page,
|
||||
PageSize: info.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AutoCodePackageApi struct{}
|
||||
|
||||
// Create
|
||||
// @Tags AutoCodePackage
|
||||
// @Summary 创建package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAutoCodePackageCreate true "创建package"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
|
||||
// @Router /autoCode/createPackage [post]
|
||||
func (a *AutoCodePackageApi) Create(c *gin.Context) {
|
||||
var info request.SysAutoCodePackageCreate
|
||||
_ = c.ShouldBindJSON(&info)
|
||||
if err := utils.Verify(info, utils.AutoPackageVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if strings.Contains(info.PackageName, "\\") || strings.Contains(info.PackageName, "/") || strings.Contains(info.PackageName, "..") {
|
||||
response.FailWithMessage("包名不合法", c)
|
||||
return
|
||||
} // PackageName可能导致路径穿越的问题 / 和 \ 都要防止
|
||||
err := autoCodePackageService.Create(c.Request.Context(), &info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// Delete
|
||||
// @Tags AutoCode
|
||||
// @Summary 删除package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body common.GetById true "创建package"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "删除package成功"
|
||||
// @Router /autoCode/delPackage [post]
|
||||
func (a *AutoCodePackageApi) Delete(c *gin.Context) {
|
||||
var info common.GetById
|
||||
_ = c.ShouldBindJSON(&info)
|
||||
err := autoCodePackageService.Delete(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// All
|
||||
// @Tags AutoCodePackage
|
||||
// @Summary 获取package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
|
||||
// @Router /autoCode/getPackage [post]
|
||||
func (a *AutoCodePackageApi) All(c *gin.Context) {
|
||||
data, err := autoCodePackageService.All(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"pkgs": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// Templates
|
||||
// @Tags AutoCodePackage
|
||||
// @Summary 获取package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
|
||||
// @Router /autoCode/getTemplates [get]
|
||||
func (a *AutoCodePackageApi) Templates(c *gin.Context) {
|
||||
data, err := autoCodePackageService.Templates(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(data, "获取成功", c)
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodePluginApi struct{}
|
||||
|
||||
// Install
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 安装插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param plug formData file true "this is a test file"
|
||||
// @Success 200 {object} response.Response{data=[]interface{},msg=string} "安装插件成功"
|
||||
// @Router /autoCode/installPlugin [post]
|
||||
func (a *AutoCodePluginApi) Install(c *gin.Context) {
|
||||
header, err := c.FormFile("plug")
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
web, server, err := autoCodePluginService.Install(header)
|
||||
webStr := "web插件安装成功"
|
||||
serverStr := "server插件安装成功"
|
||||
if web == -1 {
|
||||
webStr = "web端插件未成功安装,请按照文档自行解压安装,如果为纯后端插件请忽略此条提示"
|
||||
}
|
||||
if server == -1 {
|
||||
serverStr = "server端插件未成功安装,请按照文档自行解压安装,如果为纯前端插件请忽略此条提示"
|
||||
}
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData([]interface{}{
|
||||
gin.H{
|
||||
"code": web,
|
||||
"msg": webStr,
|
||||
},
|
||||
gin.H{
|
||||
"code": server,
|
||||
"msg": serverStr,
|
||||
}}, c)
|
||||
}
|
||||
|
||||
// Packaged
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param plugName query string true "插件名称"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/pubPlug [post]
|
||||
func (a *AutoCodePluginApi) Packaged(c *gin.Context) {
|
||||
plugName := c.Query("plugName")
|
||||
zipPath, err := autoCodePluginService.PubPlug(plugName)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("打包失败!", zap.Error(err))
|
||||
response.FailWithMessage("打包失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage(fmt.Sprintf("打包成功,文件路径为:%s", zipPath), c)
|
||||
}
|
||||
|
||||
// InitMenu
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/initMenu [post]
|
||||
func (a *AutoCodePluginApi) InitMenu(c *gin.Context) {
|
||||
var menuInfo request.InitMenu
|
||||
err := c.ShouldBindJSON(&menuInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodePluginService.InitMenu(menuInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建初始化Menu失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建初始化Menu失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("文件变更成功", c)
|
||||
}
|
||||
|
||||
// InitAPI
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/initAPI [post]
|
||||
func (a *AutoCodePluginApi) InitAPI(c *gin.Context) {
|
||||
var apiInfo request.InitApi
|
||||
err := c.ShouldBindJSON(&apiInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodePluginService.InitAPI(apiInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建初始化API失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建初始化API失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("文件变更成功", c)
|
||||
}
|
||||
|
||||
// InitDictionary
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/initDictionary [post]
|
||||
func (a *AutoCodePluginApi) InitDictionary(c *gin.Context) {
|
||||
var dictInfo request.InitDictionary
|
||||
err := c.ShouldBindJSON(&dictInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodePluginService.InitDictionary(dictInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建初始化Dictionary失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建初始化Dictionary失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("文件变更成功", c)
|
||||
}
|
||||
|
||||
// GetPluginList
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 获取插件列表
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=[]systemRes.PluginInfo} "获取插件列表成功"
|
||||
// @Router /autoCode/getPluginList [get]
|
||||
func (a *AutoCodePluginApi) GetPluginList(c *gin.Context) {
|
||||
serverDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin")
|
||||
webDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin")
|
||||
|
||||
serverEntries, _ := os.ReadDir(serverDir)
|
||||
webEntries, _ := os.ReadDir(webDir)
|
||||
|
||||
configMap := make(map[string]string)
|
||||
|
||||
for _, entry := range serverEntries {
|
||||
if entry.IsDir() {
|
||||
configMap[entry.Name()] = "server"
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range webEntries {
|
||||
if entry.IsDir() {
|
||||
if val, ok := configMap[entry.Name()]; ok {
|
||||
if val == "server" {
|
||||
configMap[entry.Name()] = "full"
|
||||
}
|
||||
} else {
|
||||
configMap[entry.Name()] = "web"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var list []systemRes.PluginInfo
|
||||
for k, v := range configMap {
|
||||
apis, menus, dicts := utils.GetPluginData(k)
|
||||
list = append(list, systemRes.PluginInfo{
|
||||
PluginName: k,
|
||||
PluginType: v,
|
||||
Apis: apis,
|
||||
Menus: menus,
|
||||
Dictionaries: dicts,
|
||||
})
|
||||
}
|
||||
|
||||
response.OkWithDetailed(list, "获取成功", c)
|
||||
}
|
||||
|
||||
// Remove
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 删除插件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param pluginName query string true "插件名称"
|
||||
// @Param pluginType query string true "插件类型"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除插件成功"
|
||||
// @Router /autoCode/removePlugin [post]
|
||||
func (a *AutoCodePluginApi) Remove(c *gin.Context) {
|
||||
pluginName := c.Query("pluginName")
|
||||
pluginType := c.Query("pluginType")
|
||||
err := autoCodePluginService.Remove(pluginName, pluginType)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeTemplateApi struct{}
|
||||
|
||||
// Preview
|
||||
// @Tags AutoCodeTemplate
|
||||
// @Summary 预览创建后的代码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoCode true "预览创建代码"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "预览创建后的代码"
|
||||
// @Router /autoCode/preview [post]
|
||||
func (a *AutoCodeTemplateApi) Preview(c *gin.Context) {
|
||||
var info request.AutoCode
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(info, utils.AutoCodeVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = info.Pretreatment()
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
info.PackageT = utils.FirstUpper(info.Package)
|
||||
autoCode, err := autoCodeTemplateService.Preview(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error(err.Error(), zap.Error(err))
|
||||
response.FailWithMessage("预览失败:"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"autoCode": autoCode}, "预览成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// Create
|
||||
// @Tags AutoCodeTemplate
|
||||
// @Summary 自动代码模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoCode true "创建自动代码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/createTemp [post]
|
||||
func (a *AutoCodeTemplateApi) Create(c *gin.Context) {
|
||||
var info request.AutoCode
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(info, utils.AutoCodeVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = info.Pretreatment()
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodeTemplateService.Create(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// AddFunc
|
||||
// @Tags AddFunc
|
||||
// @Summary 增加方法
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoCode true "增加方法"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/addFunc [post]
|
||||
func (a *AutoCodeTemplateApi) AddFunc(c *gin.Context) {
|
||||
var info request.AutoFunc
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
var tempMap map[string]string
|
||||
if info.IsPreview {
|
||||
info.Router = "填充router"
|
||||
info.FuncName = "填充funcName"
|
||||
info.Method = "填充method"
|
||||
info.Description = "填充description"
|
||||
tempMap, err = autoCodeTemplateService.GetApiAndServer(info)
|
||||
} else {
|
||||
err = autoCodeTemplateService.AddFunc(info)
|
||||
}
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("注入失败!", zap.Error(err))
|
||||
response.FailWithMessage("注入失败", c)
|
||||
} else {
|
||||
if info.IsPreview {
|
||||
response.OkWithDetailed(tempMap, "注入成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("注入成功", c)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ type ApiGroup struct {
|
||||
BaseApi
|
||||
SystemApi
|
||||
CasbinApi
|
||||
AutoCodeApi
|
||||
SystemApiApi
|
||||
AuthorityApi
|
||||
DictionaryApi
|
||||
@@ -17,43 +16,32 @@ type ApiGroup struct {
|
||||
DictionaryDetailApi
|
||||
AuthorityBtnApi
|
||||
SysExportTemplateApi
|
||||
AutoCodePluginApi
|
||||
AutoCodePackageApi
|
||||
AutoCodeHistoryApi
|
||||
AutoCodeTemplateApi
|
||||
McpApi
|
||||
SysParamsApi
|
||||
SysVersionApi
|
||||
SysErrorApi
|
||||
LoginLogApi
|
||||
ApiTokenApi
|
||||
SkillsApi
|
||||
AIWorkflowSessionApi
|
||||
}
|
||||
|
||||
var (
|
||||
apiService = service.ServiceGroupApp.SystemServiceGroup.ApiService
|
||||
jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
|
||||
menuService = service.ServiceGroupApp.SystemServiceGroup.MenuService
|
||||
userService = service.ServiceGroupApp.SystemServiceGroup.UserService
|
||||
initDBService = service.ServiceGroupApp.SystemServiceGroup.InitDBService
|
||||
casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
|
||||
baseMenuService = service.ServiceGroupApp.SystemServiceGroup.BaseMenuService
|
||||
authorityService = service.ServiceGroupApp.SystemServiceGroup.AuthorityService
|
||||
dictionaryService = service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
authorityBtnService = service.ServiceGroupApp.SystemServiceGroup.AuthorityBtnService
|
||||
systemConfigService = service.ServiceGroupApp.SystemServiceGroup.SystemConfigService
|
||||
sysParamsService = service.ServiceGroupApp.SystemServiceGroup.SysParamsService
|
||||
operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
|
||||
dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
|
||||
autoCodeService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeService
|
||||
aiWorkflowSessionService = service.ServiceGroupApp.SystemServiceGroup.AIWorkflowSession
|
||||
autoCodePluginService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePlugin
|
||||
autoCodePackageService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePackage
|
||||
autoCodeHistoryService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeHistory
|
||||
autoCodeTemplateService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate
|
||||
sysVersionService = service.ServiceGroupApp.SystemServiceGroup.SysVersionService
|
||||
sysErrorService = service.ServiceGroupApp.SystemServiceGroup.SysErrorService
|
||||
loginLogService = service.ServiceGroupApp.SystemServiceGroup.LoginLogService
|
||||
apiTokenService = service.ServiceGroupApp.SystemServiceGroup.ApiTokenService
|
||||
skillsService = service.ServiceGroupApp.SystemServiceGroup.SkillsService
|
||||
apiService = service.ServiceGroupApp.SystemServiceGroup.ApiService
|
||||
jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
|
||||
menuService = service.ServiceGroupApp.SystemServiceGroup.MenuService
|
||||
userService = service.ServiceGroupApp.SystemServiceGroup.UserService
|
||||
initDBService = service.ServiceGroupApp.SystemServiceGroup.InitDBService
|
||||
casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
|
||||
baseMenuService = service.ServiceGroupApp.SystemServiceGroup.BaseMenuService
|
||||
authorityService = service.ServiceGroupApp.SystemServiceGroup.AuthorityService
|
||||
dictionaryService = service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
authorityBtnService = service.ServiceGroupApp.SystemServiceGroup.AuthorityBtnService
|
||||
systemConfigService = service.ServiceGroupApp.SystemServiceGroup.SystemConfigService
|
||||
sysParamsService = service.ServiceGroupApp.SystemServiceGroup.SysParamsService
|
||||
operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
|
||||
dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
|
||||
sysVersionService = service.ServiceGroupApp.SystemServiceGroup.SysVersionService
|
||||
mcpService = service.ServiceGroupApp.SystemServiceGroup.McpService
|
||||
sysErrorService = service.ServiceGroupApp.SystemServiceGroup.SysErrorService
|
||||
loginLogService = service.ServiceGroupApp.SystemServiceGroup.LoginLogService
|
||||
apiTokenService = service.ServiceGroupApp.SystemServiceGroup.ApiTokenService
|
||||
)
|
||||
|
||||
@@ -7,19 +7,21 @@ import (
|
||||
mcpTool "github.com/flipped-aurora/gin-vue-admin/server/mcp"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/mcp/client"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
func (a *AutoCodeTemplateApi) MCP(c *gin.Context) {
|
||||
var info request.AutoMcpTool
|
||||
type McpApi struct{}
|
||||
|
||||
func (a *McpApi) CreateTool(c *gin.Context) {
|
||||
var info systemReq.McpToolTemplateRequest
|
||||
if err := c.ShouldBindJSON(&info); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
toolFilePath, err := autoCodeTemplateService.CreateMcp(c.Request.Context(), info)
|
||||
toolFilePath, err := mcpService.CreateToolTemplate(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
response.FailWithMessage("创建失败", c)
|
||||
global.GVA_LOG.Error(err.Error())
|
||||
@@ -28,14 +30,14 @@ func (a *AutoCodeTemplateApi) MCP(c *gin.Context) {
|
||||
response.OkWithMessage("创建成功,MCP Tool路径:"+toolFilePath, c)
|
||||
}
|
||||
|
||||
func (a *AutoCodeTemplateApi) MCPStatus(c *gin.Context) {
|
||||
func (a *McpApi) Status(c *gin.Context) {
|
||||
response.OkWithData(gin.H{
|
||||
"status": mcpTool.GetManagedStandaloneStatus(c.Request.Context()),
|
||||
"mcpServerConfig": buildMCPServerConfig(),
|
||||
}, c)
|
||||
}
|
||||
|
||||
func (a *AutoCodeTemplateApi) MCPStart(c *gin.Context) {
|
||||
func (a *McpApi) Start(c *gin.Context) {
|
||||
status, err := mcpTool.StartManagedStandalone(c.Request.Context())
|
||||
if err != nil {
|
||||
response.FailWithDetailed(gin.H{
|
||||
@@ -51,7 +53,7 @@ func (a *AutoCodeTemplateApi) MCPStart(c *gin.Context) {
|
||||
}, "MCP独立服务已启动", c)
|
||||
}
|
||||
|
||||
func (a *AutoCodeTemplateApi) MCPStop(c *gin.Context) {
|
||||
func (a *McpApi) Stop(c *gin.Context) {
|
||||
status, err := mcpTool.StopManagedStandalone(c.Request.Context())
|
||||
if err != nil {
|
||||
response.FailWithDetailed(gin.H{
|
||||
@@ -67,9 +69,8 @@ func (a *AutoCodeTemplateApi) MCPStop(c *gin.Context) {
|
||||
}, "MCP独立服务已停用", c)
|
||||
}
|
||||
|
||||
func (a *AutoCodeTemplateApi) MCPList(c *gin.Context) {
|
||||
baseURL := mcpTool.ResolveMCPServiceURL()
|
||||
testClient, err := client.NewClient(baseURL, "testClient", "v1.0.0", mcpServerName(), incomingMCPHeaders(c))
|
||||
func (a *McpApi) List(c *gin.Context) {
|
||||
testClient, err := client.NewClient(mcpTool.ResolveMCPServiceURL(), "testClient", "v1.0.0", mcpServerName(), incomingMCPHeaders(c))
|
||||
if err != nil {
|
||||
response.FailWithDetailed(gin.H{
|
||||
"status": mcpTool.GetManagedStandaloneStatus(c.Request.Context()),
|
||||
@@ -95,13 +96,7 @@ func (a *AutoCodeTemplateApi) MCPList(c *gin.Context) {
|
||||
}, c)
|
||||
}
|
||||
|
||||
func (a *AutoCodeTemplateApi) MCPRoutes(c *gin.Context) {
|
||||
response.OkWithData(gin.H{
|
||||
"routes": global.GVA_ROUTERS,
|
||||
}, c)
|
||||
}
|
||||
|
||||
func (a *AutoCodeTemplateApi) MCPTest(c *gin.Context) {
|
||||
func (a *McpApi) Test(c *gin.Context) {
|
||||
var testRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Arguments map[string]interface{} `json:"arguments" binding:"required"`
|
||||
@@ -111,8 +106,7 @@ func (a *AutoCodeTemplateApi) MCPTest(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
baseURL := mcpTool.ResolveMCPServiceURL()
|
||||
testClient, err := client.NewClient(baseURL, "testClient", "v1.0.0", mcpServerName(), incomingMCPHeaders(c))
|
||||
testClient, err := client.NewClient(mcpTool.ResolveMCPServiceURL(), "testClient", "v1.0.0", mcpServerName(), incomingMCPHeaders(c))
|
||||
if err != nil {
|
||||
response.FailWithMessage("连接MCP服务失败:"+err.Error(), c)
|
||||
return
|
||||
@@ -1,219 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/goccy/go-json"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeApi struct{}
|
||||
|
||||
func (autoApi *AutoCodeApi) GetDB(c *gin.Context) {
|
||||
businessDB := c.Query("businessDB")
|
||||
dbs, err := autoCodeService.Database(businessDB).GetDB(businessDB)
|
||||
var dbList []map[string]interface{}
|
||||
for _, db := range global.GVA_CONFIG.DBList {
|
||||
item := map[string]interface{}{
|
||||
"aliasName": db.AliasName,
|
||||
"dbName": db.Dbname,
|
||||
"disable": db.Disable,
|
||||
"dbtype": db.Type,
|
||||
}
|
||||
dbList = append(dbList, item)
|
||||
}
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"dbs": dbs, "dbList": dbList}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
|
||||
dbName := c.Query("dbName")
|
||||
businessDB := c.Query("businessDB")
|
||||
if dbName == "" {
|
||||
dbName = *global.GVA_ACTIVE_DBNAME
|
||||
if businessDB != "" {
|
||||
for _, db := range global.GVA_CONFIG.DBList {
|
||||
if db.AliasName == businessDB {
|
||||
dbName = db.Dbname
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tables, err := autoCodeService.Database(businessDB).GetTables(businessDB, dbName)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询table失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询table失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"tables": tables}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (autoApi *AutoCodeApi) GetColumn(c *gin.Context) {
|
||||
businessDB := c.Query("businessDB")
|
||||
dbName := c.Query("dbName")
|
||||
if dbName == "" {
|
||||
dbName = *global.GVA_ACTIVE_DBNAME
|
||||
if businessDB != "" {
|
||||
for _, db := range global.GVA_CONFIG.DBList {
|
||||
if db.AliasName == businessDB {
|
||||
dbName = db.Dbname
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tableName := c.Query("tableName")
|
||||
columns, err := autoCodeService.Database(businessDB).GetColumn(businessDB, tableName, dbName)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"columns": columns}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (autoApi *AutoCodeApi) LLMAuto(c *gin.Context) {
|
||||
var llm common.JSONMap
|
||||
if err := c.ShouldBindJSON(&llm); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if shouldStreamLLM(c, llm) {
|
||||
if err := autoApi.proxyLLMStream(c, llm); err != nil {
|
||||
global.GVA_LOG.Error("大模型流式代理失败!", zap.Error(err))
|
||||
if c.Writer.Written() {
|
||||
writeLLMStreamError(c, err)
|
||||
return
|
||||
}
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data, err := autoCodeService.LLMAuto(c.Request.Context(), llm)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("大模型生成失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(data, c)
|
||||
}
|
||||
|
||||
func shouldStreamLLM(c *gin.Context, llm common.JSONMap) bool {
|
||||
responseMode := strings.ToLower(strings.TrimSpace(fmt.Sprintf("%v", llm["response_mode"])))
|
||||
if responseMode == "streaming" || responseMode == "sse" {
|
||||
return true
|
||||
}
|
||||
if stream, ok := llm["stream"].(bool); ok && stream {
|
||||
return true
|
||||
}
|
||||
return strings.Contains(strings.ToLower(c.GetHeader("Accept")), "text/event-stream")
|
||||
}
|
||||
|
||||
func (autoApi *AutoCodeApi) proxyLLMStream(c *gin.Context, llm common.JSONMap) error {
|
||||
res, err := autoCodeService.LLMAutoStream(c.Request.Context(), llm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
||||
body, readErr := io.ReadAll(res.Body)
|
||||
if readErr != nil {
|
||||
return fmt.Errorf("上游大模型流式服务返回非 2xx: status=%d content-type=%s read-body-err=%w", res.StatusCode, res.Header.Get("Content-Type"), readErr)
|
||||
}
|
||||
return fmt.Errorf("上游大模型流式服务返回非 2xx: status=%d content-type=%s body=%s", res.StatusCode, res.Header.Get("Content-Type"), previewResponseBody(body))
|
||||
}
|
||||
|
||||
flusher, ok := c.Writer.(http.Flusher)
|
||||
if !ok {
|
||||
return errors.New("当前响应不支持流式输出")
|
||||
}
|
||||
|
||||
copyLLMStreamHeaders(c.Writer.Header(), res.Header)
|
||||
if c.Writer.Header().Get("Content-Type") == "" {
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
|
||||
}
|
||||
if c.Writer.Header().Get("Cache-Control") == "" {
|
||||
c.Writer.Header().Set("Cache-Control", "no-cache")
|
||||
}
|
||||
c.Writer.Header().Set("Connection", "keep-alive")
|
||||
c.Writer.Header().Set("X-Accel-Buffering", "no")
|
||||
c.Status(res.StatusCode)
|
||||
flusher.Flush()
|
||||
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
n, readErr := res.Body.Read(buf)
|
||||
if n > 0 {
|
||||
if _, writeErr := c.Writer.Write(buf[:n]); writeErr != nil {
|
||||
return fmt.Errorf("向客户端写入流式响应失败: %w", writeErr)
|
||||
}
|
||||
flusher.Flush()
|
||||
}
|
||||
if readErr != nil {
|
||||
if errors.Is(readErr, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("读取上游流式响应失败: %w", readErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func copyLLMStreamHeaders(dst, src http.Header) {
|
||||
for _, key := range []string{
|
||||
"Content-Type",
|
||||
"Cache-Control",
|
||||
"Content-Encoding",
|
||||
"Content-Language",
|
||||
"X-Accel-Buffering",
|
||||
} {
|
||||
if value := src.Get(key); value != "" {
|
||||
dst.Set(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeLLMStreamError(c *gin.Context, err error) {
|
||||
payload, marshalErr := json.Marshal(gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
if marshalErr != nil {
|
||||
payload = []byte(`{"message":"流式代理失败"}`)
|
||||
}
|
||||
_, _ = c.Writer.WriteString("event: error\n")
|
||||
_, _ = c.Writer.WriteString("data: ")
|
||||
_, _ = c.Writer.Write(payload)
|
||||
_, _ = c.Writer.WriteString("\n\n")
|
||||
if flusher, ok := c.Writer.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func previewResponseBody(body []byte) string {
|
||||
text := strings.TrimSpace(string(body))
|
||||
text = strings.ReplaceAll(text, "\r", " ")
|
||||
text = strings.ReplaceAll(text, "\n", " ")
|
||||
text = strings.Join(strings.Fields(text), " ")
|
||||
if text == "" {
|
||||
return "<empty>"
|
||||
}
|
||||
runes := []rune(text)
|
||||
if len(runes) > 300 {
|
||||
return string(runes[:300]) + "..."
|
||||
}
|
||||
return text
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/gin-contrib/sse"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/goccy/go-json"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (autoApi *AutoCodeApi) LLMAutoSSE(c *gin.Context) {
|
||||
var llm common.JSONMap
|
||||
if err := c.ShouldBindJSON(&llm); err != nil {
|
||||
global.GVA_LOG.Error("LLMAutoSSE 参数绑定失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if llm == nil {
|
||||
llm = common.JSONMap{}
|
||||
}
|
||||
llm["response_mode"] = "streaming"
|
||||
global.GVA_LOG.Info("LLMAutoSSE 收到请求", zap.Any("mode", llm["mode"]))
|
||||
|
||||
if err := autoApi.streamLLMAsSSE(c, llm); err != nil {
|
||||
global.GVA_LOG.Error("大模型 SSE 代理失败!", zap.Error(err))
|
||||
if c.Writer.Written() {
|
||||
writeLLMStreamError(c, err)
|
||||
return
|
||||
}
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
}
|
||||
}
|
||||
|
||||
func (autoApi *AutoCodeApi) streamLLMAsSSE(c *gin.Context, llm common.JSONMap) error {
|
||||
res, err := autoCodeService.LLMAutoStream(c.Request.Context(), llm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("调用上游大模型失败: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
||||
body, readErr := io.ReadAll(res.Body)
|
||||
if readErr != nil {
|
||||
return fmt.Errorf("上游大模型流式服务返回非 2xx: status=%d content-type=%s read-body-err=%w", res.StatusCode, res.Header.Get("Content-Type"), readErr)
|
||||
}
|
||||
return fmt.Errorf("上游大模型流式服务返回非 2xx: status=%d content-type=%s body=%s", res.StatusCode, res.Header.Get("Content-Type"), previewResponseBody(body))
|
||||
}
|
||||
|
||||
ct := res.Header.Get("Content-Type")
|
||||
global.GVA_LOG.Info("LLMAutoSSE 上游返回成功,开始 SSE 流式转发",
|
||||
zap.Int("status", res.StatusCode),
|
||||
zap.String("content-type", ct))
|
||||
|
||||
// 如果上游返回的不是 SSE 流(可能是 blocking 模式返回的 JSON),直接读取并转发
|
||||
if !strings.Contains(ct, "text/event-stream") && !strings.Contains(ct, "text/plain") {
|
||||
body, readErr := io.ReadAll(res.Body)
|
||||
if readErr != nil {
|
||||
return fmt.Errorf("读取上游非流式响应失败: %w", readErr)
|
||||
}
|
||||
global.GVA_LOG.Warn("LLMAutoSSE 上游返回非 SSE 流,Content-Type: "+ct+", 将以单次事件转发",
|
||||
zap.String("body_preview", previewResponseBody(body)))
|
||||
|
||||
flusher, ok := c.Writer.(http.Flusher)
|
||||
if !ok {
|
||||
return errors.New("当前响应不支持流式输出")
|
||||
}
|
||||
prepareSSEHeaders(c)
|
||||
c.Status(http.StatusOK)
|
||||
|
||||
var payload any
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
payload = string(body)
|
||||
}
|
||||
if err := renderSSE(c, sse.Event{Event: "message", Data: payload}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := renderSSE(c, sse.Event{Event: "done", Data: gin.H{"done": true}}); err != nil {
|
||||
return err
|
||||
}
|
||||
flusher.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
flusher, ok := c.Writer.(http.Flusher)
|
||||
if !ok {
|
||||
return errors.New("当前响应不支持流式输出")
|
||||
}
|
||||
|
||||
prepareSSEHeaders(c)
|
||||
c.Status(http.StatusOK)
|
||||
flusher.Flush()
|
||||
|
||||
reader := bufio.NewReader(res.Body)
|
||||
lines := make([]string, 0, 8)
|
||||
blockCount := 0
|
||||
|
||||
global.GVA_LOG.Info("LLMAutoSSE 开始读取上游流数据...")
|
||||
|
||||
for {
|
||||
global.GVA_LOG.Debug("LLMAutoSSE 等待读取下一行...")
|
||||
line, readErr := reader.ReadString('\n')
|
||||
if readErr != nil && !errors.Is(readErr, io.EOF) {
|
||||
global.GVA_LOG.Error("LLMAutoSSE 读取上游流失败", zap.Int("已转发块数", blockCount), zap.Error(readErr))
|
||||
return fmt.Errorf("读取上游流式响应失败: %w", readErr)
|
||||
}
|
||||
|
||||
line = strings.TrimRight(line, "\r\n")
|
||||
if line == "" {
|
||||
if len(lines) > 0 {
|
||||
blockCount++
|
||||
if blockCount <= 3 {
|
||||
global.GVA_LOG.Debug("LLMAutoSSE 转发 SSE 块", zap.Int("block", blockCount), zap.Strings("lines", lines))
|
||||
}
|
||||
}
|
||||
if err := emitSSEBlock(c, lines); err != nil {
|
||||
return err
|
||||
}
|
||||
lines = lines[:0]
|
||||
} else {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
|
||||
if errors.Is(readErr, io.EOF) {
|
||||
if err := emitSSEBlock(c, lines); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := renderSSE(c, sse.Event{
|
||||
Event: "done",
|
||||
Data: gin.H{"done": true},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
flusher.Flush()
|
||||
global.GVA_LOG.Info("LLMAutoSSE 流式转发完成", zap.Int("总块数", blockCount))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func prepareSSEHeaders(c *gin.Context) {
|
||||
header := c.Writer.Header()
|
||||
header.Set("Content-Type", "text/event-stream; charset=utf-8")
|
||||
header.Set("Cache-Control", "no-cache, no-transform")
|
||||
header.Set("Connection", "keep-alive")
|
||||
header.Set("X-Accel-Buffering", "no")
|
||||
}
|
||||
|
||||
func emitSSEBlock(c *gin.Context, lines []string) error {
|
||||
if len(lines) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
eventName := "message"
|
||||
eventID := ""
|
||||
dataLines := make([]string, 0, len(lines))
|
||||
|
||||
for _, line := range lines {
|
||||
switch {
|
||||
case strings.HasPrefix(line, "event:"):
|
||||
eventName = strings.TrimSpace(strings.TrimPrefix(line, "event:"))
|
||||
case strings.HasPrefix(line, "id:"):
|
||||
eventID = strings.TrimSpace(strings.TrimPrefix(line, "id:"))
|
||||
case strings.HasPrefix(line, "data:"):
|
||||
dataLines = append(dataLines, strings.TrimSpace(strings.TrimPrefix(line, "data:")))
|
||||
}
|
||||
}
|
||||
|
||||
rawData := strings.TrimSpace(strings.Join(dataLines, "\n"))
|
||||
if rawData == "" {
|
||||
return nil
|
||||
}
|
||||
if rawData == "[DONE]" {
|
||||
return renderSSE(c, sse.Event{
|
||||
Id: eventID,
|
||||
Event: "done",
|
||||
Data: gin.H{"done": true},
|
||||
})
|
||||
}
|
||||
|
||||
var payload interface{}
|
||||
if err := json.Unmarshal([]byte(rawData), &payload); err != nil {
|
||||
payload = rawData
|
||||
}
|
||||
|
||||
return renderSSE(c, sse.Event{
|
||||
Id: eventID,
|
||||
Event: eventName,
|
||||
Data: payload,
|
||||
})
|
||||
}
|
||||
|
||||
func renderSSE(c *gin.Context, event sse.Event) error {
|
||||
if err := event.Render(c.Writer); err != nil {
|
||||
return fmt.Errorf("写入 SSE 事件失败: %w", err)
|
||||
}
|
||||
if flusher, ok := c.Writer.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SkillsApi struct{}
|
||||
|
||||
func (s *SkillsApi) GetTools(c *gin.Context) {
|
||||
data, err := skillsService.Tools(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取工具列表失败", zap.Error(err))
|
||||
response.FailWithMessage("获取工具列表失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"tools": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetSkillList(c *gin.Context) {
|
||||
var req request.SkillToolRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
data, err := skillsService.List(c.Request.Context(), req.Tool)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取技能列表失败", zap.Error(err))
|
||||
response.FailWithMessage("获取技能列表失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"skills": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetSkillDetail(c *gin.Context) {
|
||||
var req request.SkillDetailRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
data, err := skillsService.Detail(c.Request.Context(), req.Tool, req.Skill)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取技能详情失败", zap.Error(err))
|
||||
response.FailWithMessage("获取技能详情失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"detail": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveSkill(c *gin.Context) {
|
||||
var req request.SkillSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.Save(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存技能失败", zap.Error(err))
|
||||
response.FailWithMessage("保存技能失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) DeleteSkill(c *gin.Context) {
|
||||
var req request.SkillDeleteRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.Delete(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("删除技能失败", zap.Error(err))
|
||||
response.FailWithMessage("删除技能失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) CreateScript(c *gin.Context) {
|
||||
var req request.SkillScriptCreateRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
fileName, content, err := skillsService.CreateScript(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建脚本失败", zap.Error(err))
|
||||
response.FailWithMessage("创建脚本失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"fileName": fileName, "content": content}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetScript(c *gin.Context) {
|
||||
var req request.SkillFileRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, err := skillsService.GetScript(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取脚本失败", zap.Error(err))
|
||||
response.FailWithMessage("读取脚本失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveScript(c *gin.Context) {
|
||||
var req request.SkillFileSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveScript(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存脚本失败", zap.Error(err))
|
||||
response.FailWithMessage("保存脚本失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) CreateResource(c *gin.Context) {
|
||||
var req request.SkillResourceCreateRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
fileName, content, err := skillsService.CreateResource(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建资源失败", zap.Error(err))
|
||||
response.FailWithMessage("创建资源失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"fileName": fileName, "content": content}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetResource(c *gin.Context) {
|
||||
var req request.SkillFileRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, err := skillsService.GetResource(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取资源失败", zap.Error(err))
|
||||
response.FailWithMessage("读取资源失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveResource(c *gin.Context) {
|
||||
var req request.SkillFileSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveResource(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存资源失败", zap.Error(err))
|
||||
response.FailWithMessage("保存资源失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) CreateReference(c *gin.Context) {
|
||||
var req request.SkillReferenceCreateRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
fileName, content, err := skillsService.CreateReference(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建参考失败", zap.Error(err))
|
||||
response.FailWithMessage("创建参考失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"fileName": fileName, "content": content}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetReference(c *gin.Context) {
|
||||
var req request.SkillFileRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, err := skillsService.GetReference(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取参考失败", zap.Error(err))
|
||||
response.FailWithMessage("读取参考失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveReference(c *gin.Context) {
|
||||
var req request.SkillFileSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveReference(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存参考失败", zap.Error(err))
|
||||
response.FailWithMessage("保存参考失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) CreateTemplate(c *gin.Context) {
|
||||
var req request.SkillTemplateCreateRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
fileName, content, err := skillsService.CreateTemplate(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建模板失败", zap.Error(err))
|
||||
response.FailWithMessage("创建模板失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"fileName": fileName, "content": content}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetTemplate(c *gin.Context) {
|
||||
var req request.SkillFileRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, err := skillsService.GetTemplate(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取模板失败", zap.Error(err))
|
||||
response.FailWithMessage("读取模板失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveTemplate(c *gin.Context) {
|
||||
var req request.SkillFileSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveTemplate(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存模板失败", zap.Error(err))
|
||||
response.FailWithMessage("保存模板失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetGlobalConstraint(c *gin.Context) {
|
||||
var req request.SkillToolRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, exists, err := skillsService.GetGlobalConstraint(c.Request.Context(), req.Tool)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取全局约束失败", zap.Error(err))
|
||||
response.FailWithMessage("读取全局约束失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content, "exists": exists}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveGlobalConstraint(c *gin.Context) {
|
||||
var req request.SkillGlobalConstraintSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveGlobalConstraint(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存全局约束失败", zap.Error(err))
|
||||
response.FailWithMessage("保存全局约束失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) PackageSkill(c *gin.Context) {
|
||||
var req request.SkillPackageRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
|
||||
fileName, data, err := skillsService.Package(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("打包技能失败", zap.Error(err))
|
||||
response.FailWithMessage("打包技能失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "application/zip")
|
||||
c.Header("Content-Disposition", "attachment; filename=\""+fileName+"\"")
|
||||
c.Data(http.StatusOK, "application/zip", data)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) DownloadOnlineSkill(c *gin.Context) {
|
||||
var req request.DownloadOnlineSkillReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage("参数错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := skillsService.DownloadOnlineSkill(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("下载在线技能失败", zap.Error(err))
|
||||
response.FailWithMessage("下载在线技能失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("下载成功", c)
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -16,8 +15,7 @@ import (
|
||||
)
|
||||
|
||||
type standaloneConfig struct {
|
||||
MCP config.MCP `yaml:"mcp"`
|
||||
AutoCode config.Autocode `yaml:"autocode"`
|
||||
MCP config.MCP `yaml:"mcp"`
|
||||
}
|
||||
|
||||
func loadStandaloneConfig() (string, error) {
|
||||
@@ -39,7 +37,6 @@ func loadStandaloneConfig() (string, error) {
|
||||
applyStandaloneDefaults(configPath, &cfg)
|
||||
|
||||
global.GVA_CONFIG.MCP = cfg.MCP
|
||||
global.GVA_CONFIG.AutoCode = cfg.AutoCode
|
||||
|
||||
return configPath, nil
|
||||
}
|
||||
@@ -122,68 +119,6 @@ func applyStandaloneDefaults(configPath string, cfg *standaloneConfig) {
|
||||
cfg.MCP.BaseURL = fmt.Sprintf("http://127.0.0.1:%d%s", cfg.MCP.Addr, cfg.MCP.Path)
|
||||
}
|
||||
|
||||
configDir := filepath.Dir(configPath)
|
||||
if cfg.AutoCode.Server == "" {
|
||||
cfg.AutoCode.Server = "server"
|
||||
}
|
||||
if cfg.AutoCode.Web == "" {
|
||||
cfg.AutoCode.Web = "web/src"
|
||||
}
|
||||
if cfg.AutoCode.Root == "" {
|
||||
if root, err := detectProjectRoot(configDir); err == nil {
|
||||
cfg.AutoCode.Root = root
|
||||
}
|
||||
} else if !filepath.IsAbs(cfg.AutoCode.Root) {
|
||||
cfg.AutoCode.Root = filepath.Clean(filepath.Join(configDir, cfg.AutoCode.Root))
|
||||
}
|
||||
|
||||
if cfg.AutoCode.Module == "" && cfg.AutoCode.Root != "" {
|
||||
goModPath := filepath.Join(cfg.AutoCode.Root, cfg.AutoCode.Server, "go.mod")
|
||||
if module, err := detectGoModule(goModPath); err == nil {
|
||||
cfg.AutoCode.Module = module
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func detectProjectRoot(startDir string) (string, error) {
|
||||
dir := startDir
|
||||
for {
|
||||
serverDir := filepath.Join(dir, "server")
|
||||
webDir := filepath.Join(dir, "web")
|
||||
if isDir(serverDir) && isDir(webDir) {
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
parent := filepath.Dir(dir)
|
||||
if parent == dir {
|
||||
break
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
|
||||
return "", errors.New("未能自动识别项目根目录,请在 MCP 配置中设置 autocode.root")
|
||||
}
|
||||
|
||||
func detectGoModule(goModPath string) (string, error) {
|
||||
file, err := os.Open(goModPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if strings.HasPrefix(line, "module ") {
|
||||
return strings.TrimSpace(strings.TrimPrefix(line, "module ")), nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "", errors.New("go.mod 中未找到 module 定义")
|
||||
}
|
||||
|
||||
func isDir(path string) bool {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Autocode struct {
|
||||
Web string `mapstructure:"web" json:"web" yaml:"web"`
|
||||
Root string `mapstructure:"root" json:"root" yaml:"root"`
|
||||
Server string `mapstructure:"server" json:"server" yaml:"server"`
|
||||
Module string `mapstructure:"module" json:"module" yaml:"module"`
|
||||
AiPath string `mapstructure:"ai-path" json:"ai-path" yaml:"ai-path"`
|
||||
}
|
||||
|
||||
func (a *Autocode) WebRoot() string {
|
||||
webs := strings.Split(a.Web, "/")
|
||||
if len(webs) == 0 {
|
||||
webs = strings.Split(a.Web, "\\")
|
||||
}
|
||||
return filepath.Join(webs...)
|
||||
}
|
||||
@@ -9,8 +9,6 @@ type Server struct {
|
||||
Email Email `mapstructure:"email" json:"email" yaml:"email"`
|
||||
System System `mapstructure:"system" json:"system" yaml:"system"`
|
||||
Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
|
||||
// auto
|
||||
AutoCode Autocode `mapstructure:"autocode" json:"autocode" yaml:"autocode"`
|
||||
// gorm
|
||||
Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
|
||||
Mssql Mssql `mapstructure:"mssql" json:"mssql" yaml:"mssql"`
|
||||
|
||||
@@ -36,8 +36,6 @@ func RunServer() {
|
||||
fmt.Printf(`
|
||||
欢迎使用 gin-vue-admin
|
||||
当前版本:%s
|
||||
项目地址:https://github.com/flipped-aurora/gin-vue-admin
|
||||
插件市场:https://plugin.gin-vue-admin.com
|
||||
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
|
||||
MCP 独立服务请手动启动: go run ./cmd/mcp -config ./cmd/mcp/config.yaml
|
||||
默认MCP StreamHTTP地址:%s
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/core/internal"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
@@ -36,8 +35,6 @@ func Viper() *viper.Viper {
|
||||
panic(fmt.Errorf("fatal error unmarshal config: %w", err))
|
||||
}
|
||||
|
||||
// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
|
||||
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
|
||||
return v
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package initialize
|
||||
import (
|
||||
"context"
|
||||
adapter "github.com/casbin/gorm-adapter/v3"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
commonModel "github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
|
||||
"gorm.io/gorm"
|
||||
@@ -41,14 +41,11 @@ func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error
|
||||
sysModel.SysAuthority{},
|
||||
sysModel.JwtBlacklist{},
|
||||
sysModel.SysDictionary{},
|
||||
sysModel.SysAutoCodeHistory{},
|
||||
sysModel.SysAIWorkflowSession{},
|
||||
sysModel.SysOperationRecord{},
|
||||
sysModel.SysDictionaryDetail{},
|
||||
sysModel.SysBaseMenuParameter{},
|
||||
sysModel.SysBaseMenuBtn{},
|
||||
sysModel.SysAuthorityBtn{},
|
||||
sysModel.SysAutoCodePackage{},
|
||||
sysModel.SysExportTemplate{},
|
||||
sysModel.Condition{},
|
||||
sysModel.JoinTemplate{},
|
||||
@@ -59,11 +56,10 @@ func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error
|
||||
sysModel.SysApiToken{},
|
||||
adapter.CasbinRule{},
|
||||
|
||||
example.ExaFile{},
|
||||
example.ExaCustomer{},
|
||||
example.ExaFileChunk{},
|
||||
example.ExaFileUploadAndDownload{},
|
||||
example.ExaAttachmentCategory{},
|
||||
commonModel.ExaFile{},
|
||||
commonModel.ExaFileChunk{},
|
||||
commonModel.ExaFileUploadAndDownload{},
|
||||
commonModel.ExaAttachmentCategory{},
|
||||
}
|
||||
for _, t := range tables {
|
||||
_ = db.AutoMigrate(&t)
|
||||
@@ -85,25 +81,21 @@ func (e *ensureTables) TableCreated(ctx context.Context) bool {
|
||||
sysModel.SysAuthority{},
|
||||
sysModel.JwtBlacklist{},
|
||||
sysModel.SysDictionary{},
|
||||
sysModel.SysAutoCodeHistory{},
|
||||
sysModel.SysAIWorkflowSession{},
|
||||
sysModel.SysOperationRecord{},
|
||||
sysModel.SysDictionaryDetail{},
|
||||
sysModel.SysBaseMenuParameter{},
|
||||
sysModel.SysBaseMenuBtn{},
|
||||
sysModel.SysAuthorityBtn{},
|
||||
sysModel.SysAutoCodePackage{},
|
||||
sysModel.SysExportTemplate{},
|
||||
sysModel.Condition{},
|
||||
sysModel.JoinTemplate{},
|
||||
|
||||
adapter.CasbinRule{},
|
||||
|
||||
example.ExaFile{},
|
||||
example.ExaCustomer{},
|
||||
example.ExaFileChunk{},
|
||||
example.ExaFileUploadAndDownload{},
|
||||
example.ExaAttachmentCategory{},
|
||||
commonModel.ExaFile{},
|
||||
commonModel.ExaFileChunk{},
|
||||
commonModel.ExaFileUploadAndDownload{},
|
||||
commonModel.ExaAttachmentCategory{},
|
||||
}
|
||||
yes := true
|
||||
for _, t := range tables {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
commonModel "github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@@ -51,12 +51,10 @@ func RegisterTables() {
|
||||
system.SysAuthority{},
|
||||
system.SysDictionary{},
|
||||
system.SysOperationRecord{},
|
||||
system.SysAutoCodeHistory{},
|
||||
system.SysDictionaryDetail{},
|
||||
system.SysBaseMenuParameter{},
|
||||
system.SysBaseMenuBtn{},
|
||||
system.SysAuthorityBtn{},
|
||||
system.SysAutoCodePackage{},
|
||||
system.SysExportTemplate{},
|
||||
system.Condition{},
|
||||
system.JoinTemplate{},
|
||||
@@ -66,11 +64,10 @@ func RegisterTables() {
|
||||
system.SysApiToken{},
|
||||
system.SysLoginLog{},
|
||||
|
||||
example.ExaFile{},
|
||||
example.ExaCustomer{},
|
||||
example.ExaFileChunk{},
|
||||
example.ExaFileUploadAndDownload{},
|
||||
example.ExaAttachmentCategory{},
|
||||
commonModel.ExaFile{},
|
||||
commonModel.ExaFileChunk{},
|
||||
commonModel.ExaFileUploadAndDownload{},
|
||||
commonModel.ExaAttachmentCategory{},
|
||||
)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("register table failed", zap.Error(err))
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/songzhibin97/gkit/cache/local_cache"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
@@ -23,10 +20,4 @@ func OtherInit() {
|
||||
global.BlackCache = local_cache.NewCache(
|
||||
local_cache.SetDefaultExpire(dr),
|
||||
)
|
||||
file, err := os.Open("go.mod")
|
||||
if err == nil && global.GVA_CONFIG.AutoCode.Module == "" {
|
||||
scanner := bufio.NewScanner(file)
|
||||
scanner.Scan()
|
||||
global.GVA_CONFIG.AutoCode.Module = strings.TrimPrefix(scanner.Text(), "module ")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InstallPlugin(PrivateGroup *gin.RouterGroup, PublicRouter *gin.RouterGroup, engine *gin.Engine) {
|
||||
if global.GVA_DB == nil {
|
||||
global.GVA_LOG.Info("项目暂未初始化,跳过可选扩展安装")
|
||||
return
|
||||
}
|
||||
bizPluginV1(PrivateGroup, PublicRouter)
|
||||
_ = engine
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils/plugin"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PluginInit(group *gin.RouterGroup, Plugin ...plugin.Plugin) {
|
||||
for i := range Plugin {
|
||||
fmt.Println(Plugin[i].RouterPath(), "注册开始!")
|
||||
PluginGroup := group.Group(Plugin[i].RouterPath())
|
||||
Plugin[i].Register(PluginGroup)
|
||||
fmt.Println(Plugin[i].RouterPath(), "注册成功!")
|
||||
}
|
||||
}
|
||||
|
||||
func bizPluginV1(group ...*gin.RouterGroup) {
|
||||
private := group[0]
|
||||
public := group[1]
|
||||
// 添加跟角色挂钩权限的插件 示例 本地示例模式于在线仓库模式注意上方的import 可以自行切换 效果相同
|
||||
PluginInit(private, email.CreateEmailPlug(
|
||||
global.GVA_CONFIG.Email.To,
|
||||
global.GVA_CONFIG.Email.From,
|
||||
global.GVA_CONFIG.Email.Host,
|
||||
global.GVA_CONFIG.Email.Secret,
|
||||
global.GVA_CONFIG.Email.Nickname,
|
||||
global.GVA_CONFIG.Email.Port,
|
||||
global.GVA_CONFIG.Email.IsSSL,
|
||||
global.GVA_CONFIG.Email.IsLoginAuth,
|
||||
))
|
||||
holder(public, private)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
_ "github.com/flipped-aurora/gin-vue-admin/server/plugin"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils/plugin/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PluginInitV2(group *gin.Engine, plugins ...plugin.Plugin) {
|
||||
for i := 0; i < len(plugins); i++ {
|
||||
plugins[i].Register(group)
|
||||
}
|
||||
}
|
||||
func bizPluginV2(engine *gin.Engine) {
|
||||
PluginInitV2(engine, plugin.Registered()...)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
_ "github.com/flipped-aurora/gin-vue-admin/server/source/example"
|
||||
_ "github.com/flipped-aurora/gin-vue-admin/server/source/common"
|
||||
_ "github.com/flipped-aurora/gin-vue-admin/server/source/system"
|
||||
)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ func Routers() *gin.Engine {
|
||||
}
|
||||
|
||||
systemRouter := router.RouterGroupApp.System
|
||||
exampleRouter := router.RouterGroupApp.Example
|
||||
commonRouter := router.RouterGroupApp.Common
|
||||
// 如果想要不使用nginx代理前端网页,可以修改 web/.env.production 下的
|
||||
// VUE_APP_BASE_API = /
|
||||
// VUE_APP_BASE_PATH = http://localhost
|
||||
@@ -74,39 +74,33 @@ func Routers() *gin.Engine {
|
||||
})
|
||||
}
|
||||
{
|
||||
systemRouter.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权
|
||||
systemRouter.InitInitRouter(PublicGroup) // 自动初始化相关
|
||||
systemRouter.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权
|
||||
systemRouter.InitInitRouter(PublicGroup) // 自动初始化相关
|
||||
}
|
||||
|
||||
{
|
||||
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
|
||||
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
|
||||
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
|
||||
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
|
||||
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
|
||||
systemRouter.InitSysVersionRouter(PrivateGroup) // 发版相关路由
|
||||
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
|
||||
systemRouter.InitAutoCodeRouter(PrivateGroup, PublicGroup) // 创建自动化代码
|
||||
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
|
||||
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
|
||||
systemRouter.InitAutoCodeHistoryRouter(PrivateGroup) // 自动化代码历史
|
||||
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
|
||||
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
|
||||
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理
|
||||
systemRouter.InitSysExportTemplateRouter(PrivateGroup, PublicGroup) // 导出模板
|
||||
systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理
|
||||
systemRouter.InitSysErrorRouter(PrivateGroup, PublicGroup) // 错误日志
|
||||
systemRouter.InitLoginLogRouter(PrivateGroup) // 登录日志
|
||||
systemRouter.InitApiTokenRouter(PrivateGroup) // apiToken签发
|
||||
systemRouter.InitSkillsRouter(PrivateGroup, PublicGroup) // Skills 定义器
|
||||
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
|
||||
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
|
||||
exampleRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类
|
||||
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
|
||||
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
|
||||
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
|
||||
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
|
||||
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
|
||||
systemRouter.InitSysVersionRouter(PrivateGroup) // 发版相关路由
|
||||
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
|
||||
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
|
||||
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
|
||||
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
|
||||
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
|
||||
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理
|
||||
systemRouter.InitSysExportTemplateRouter(PrivateGroup, PublicGroup) // 导出模板
|
||||
systemRouter.InitMcpRouter(PrivateGroup) // MCP 管理
|
||||
systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理
|
||||
systemRouter.InitSysErrorRouter(PrivateGroup, PublicGroup) // 错误日志
|
||||
systemRouter.InitLoginLogRouter(PrivateGroup) // 登录日志
|
||||
systemRouter.InitApiTokenRouter(PrivateGroup) // apiToken签发
|
||||
commonRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
|
||||
commonRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类
|
||||
}
|
||||
|
||||
//插件路由安装
|
||||
InstallPlugin(PrivateGroup, PublicGroup, Router)
|
||||
|
||||
// 注册业务路由
|
||||
initBizRouter(PrivateGroup, PublicGroup)
|
||||
|
||||
|
||||
@@ -16,4 +16,5 @@ func initBizRouter(routers ...*gin.RouterGroup) {
|
||||
publicGroup := routers[1]
|
||||
|
||||
holder(publicGroup, privateGroup)
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package mcpTool
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
@@ -29,10 +29,6 @@ type ApiListResponse struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
}
|
||||
|
||||
type mcpRoutesResponse struct {
|
||||
Routes gin.RoutesInfo `json:"routes"`
|
||||
}
|
||||
|
||||
type ApiLister struct{}
|
||||
|
||||
func (a *ApiLister) New() mcp.Tool {
|
||||
@@ -59,11 +55,6 @@ func (a *ApiLister) Handle(ctx context.Context, _ mcp.CallToolRequest) (*mcp.Cal
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routeResp, err := postUpstream[mcpRoutesResponse](ctx, "/autoCode/mcpRoutes", map[string]any{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databaseApis := make([]ApiInfo, 0, len(apiResp.Data.Apis))
|
||||
for _, api := range apiResp.Data.Apis {
|
||||
databaseApis = append(databaseApis, ApiInfo{
|
||||
@@ -76,8 +67,8 @@ func (a *ApiLister) Handle(ctx context.Context, _ mcp.CallToolRequest) (*mcp.Cal
|
||||
})
|
||||
}
|
||||
|
||||
ginApis := make([]ApiInfo, 0, len(routeResp.Data.Routes))
|
||||
for _, route := range routeResp.Data.Routes {
|
||||
ginApis := make([]ApiInfo, 0, len(global.GVA_ROUTERS))
|
||||
for _, route := range global.GVA_ROUTERS {
|
||||
ginApis = append(ginApis, ApiInfo{
|
||||
Path: route.Path,
|
||||
Method: route.Method,
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
commonReq "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
)
|
||||
|
||||
func fetchAutoCodePackages(ctx context.Context) ([]model.SysAutoCodePackage, error) {
|
||||
resp, err := postUpstream[map[string][]model.SysAutoCodePackage](ctx, "/autoCode/getPackage", map[string]any{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Data["pkgs"], nil
|
||||
}
|
||||
|
||||
func fetchAutoCodeHistories(ctx context.Context) ([]model.SysAutoCodeHistory, error) {
|
||||
resp, err := postUpstream[pageResultData[[]model.SysAutoCodeHistory]](ctx, "/autoCode/getSysHistory", commonReq.PageInfo{
|
||||
Page: 1,
|
||||
PageSize: 10000,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Data.List, nil
|
||||
}
|
||||
|
||||
func createAutoCodePackage(ctx context.Context, info *systemReq.SysAutoCodePackageCreate) error {
|
||||
_, err := postUpstream[map[string]any](ctx, "/autoCode/createPackage", info)
|
||||
return err
|
||||
}
|
||||
|
||||
func createAutoCodeModule(ctx context.Context, info systemReq.AutoCode) error {
|
||||
_, err := postUpstream[map[string]any](ctx, "/autoCode/createTemp", info)
|
||||
return err
|
||||
}
|
||||
|
||||
func deleteAutoCodePackage(ctx context.Context, id uint) error {
|
||||
_, err := postUpstream[map[string]any](ctx, "/autoCode/delPackage", commonReq.GetById{ID: int(id)})
|
||||
return err
|
||||
}
|
||||
|
||||
func deleteAutoCodeHistory(ctx context.Context, id uint) error {
|
||||
_, err := postUpstream[map[string]any](ctx, "/autoCode/delSysHistory", commonReq.GetById{ID: int(id)})
|
||||
return err
|
||||
}
|
||||
@@ -1,494 +0,0 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&GVAAnalyzer{})
|
||||
}
|
||||
|
||||
// GVAAnalyzer GVA分析器 - 用于分析当前功能是否需要创建独立的package和module
|
||||
type GVAAnalyzer struct{}
|
||||
|
||||
// AnalyzeRequest 分析请求结构体
|
||||
type AnalyzeRequest struct {
|
||||
Requirement string `json:"requirement" binding:"required"` // 用户需求描述
|
||||
}
|
||||
|
||||
// AnalyzeResponse 分析响应结构体
|
||||
type AnalyzeResponse struct {
|
||||
ExistingPackages []PackageInfo `json:"existingPackages"` // 现有包信息
|
||||
PredesignedModules []PredesignedModuleInfo `json:"predesignedModules"` // 预设计模块信息
|
||||
Dictionaries []DictionaryPre `json:"dictionaries"` // 字典信息
|
||||
CleanupInfo *CleanupInfo `json:"cleanupInfo"` // 清理信息(如果有)
|
||||
}
|
||||
|
||||
// ModuleInfo 模块信息
|
||||
type ModuleInfo struct {
|
||||
ModuleName string `json:"moduleName"` // 模块名称
|
||||
PackageName string `json:"packageName"` // 包名
|
||||
Template string `json:"template"` // 模板类型
|
||||
StructName string `json:"structName"` // 结构体名称
|
||||
TableName string `json:"tableName"` // 表名
|
||||
Description string `json:"description"` // 描述
|
||||
FilePaths []string `json:"filePaths"` // 相关文件路径
|
||||
}
|
||||
|
||||
// PackageInfo 包信息
|
||||
type PackageInfo struct {
|
||||
PackageName string `json:"packageName"` // 包名
|
||||
Template string `json:"template"` // 模板类型
|
||||
Label string `json:"label"` // 标签
|
||||
Desc string `json:"desc"` // 描述
|
||||
Module string `json:"module"` // 模块
|
||||
IsEmpty bool `json:"isEmpty"` // 是否为空包
|
||||
}
|
||||
|
||||
// PredesignedModuleInfo 预设计模块信息
|
||||
type PredesignedModuleInfo struct {
|
||||
ModuleName string `json:"moduleName"` // 模块名称
|
||||
PackageName string `json:"packageName"` // 包名
|
||||
Template string `json:"template"` // 模板类型
|
||||
FilePaths []string `json:"filePaths"` // 文件路径列表
|
||||
Description string `json:"description"` // 描述
|
||||
}
|
||||
|
||||
// CleanupInfo 清理信息
|
||||
type CleanupInfo struct {
|
||||
DeletedPackages []string `json:"deletedPackages"` // 已删除的包
|
||||
DeletedModules []string `json:"deletedModules"` // 已删除的模块
|
||||
CleanupMessage string `json:"cleanupMessage"` // 清理消息
|
||||
}
|
||||
|
||||
// New 创建GVA分析器工具
|
||||
func (g *GVAAnalyzer) New() mcp.Tool {
|
||||
return mcp.NewTool("gva_analyze",
|
||||
mcp.WithDescription("返回当前系统中有效的包和模块信息,并分析用户需求是否需要创建新的包、模块和字典。同时检查并清理空包,确保系统整洁。"),
|
||||
mcp.WithString("requirement",
|
||||
mcp.Description("用户需求描述,用于分析是否需要创建新的包和模块"),
|
||||
mcp.Required(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理分析请求
|
||||
func (g *GVAAnalyzer) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 解析请求参数
|
||||
requirementStr, ok := request.GetArguments()["requirement"].(string)
|
||||
if !ok || requirementStr == "" {
|
||||
return nil, errors.New("参数错误:requirement 必须是非空字符串")
|
||||
}
|
||||
|
||||
// 创建分析请求
|
||||
analyzeReq := AnalyzeRequest{
|
||||
Requirement: requirementStr,
|
||||
}
|
||||
|
||||
// 执行分析逻辑
|
||||
response, err := g.performAnalysis(ctx, analyzeReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("分析失败: %v", err)
|
||||
}
|
||||
|
||||
// 序列化响应
|
||||
responseJSON, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(string(responseJSON)),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// performAnalysis 执行分析逻辑
|
||||
func (g *GVAAnalyzer) performAnalysis(ctx context.Context, req AnalyzeRequest) (*AnalyzeResponse, error) {
|
||||
_ = req
|
||||
|
||||
packages, err := fetchAutoCodePackages(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取包信息失败: %v", err)
|
||||
}
|
||||
|
||||
histories, err := fetchAutoCodeHistories(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取历史记录失败: %v", err)
|
||||
}
|
||||
|
||||
cleanupInfo := &CleanupInfo{
|
||||
DeletedPackages: []string{},
|
||||
DeletedModules: []string{},
|
||||
}
|
||||
|
||||
validPackages := make([]PackageInfo, 0, len(packages))
|
||||
var emptyPackageHistoryIDs []uint
|
||||
|
||||
for _, pkg := range packages {
|
||||
isEmpty, err := g.isPackageFolderEmpty(pkg.PackageName, pkg.Template)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("检查包 %s 是否为空时出错: %v", pkg.PackageName, err))
|
||||
continue
|
||||
}
|
||||
|
||||
if isEmpty {
|
||||
if err := g.removeEmptyPackageFolder(pkg.PackageName, pkg.Template); err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("删除空包文件夹 %s 失败: %v", pkg.PackageName, err))
|
||||
} else {
|
||||
cleanupInfo.DeletedPackages = append(cleanupInfo.DeletedPackages, pkg.PackageName)
|
||||
}
|
||||
|
||||
if err := deleteAutoCodePackage(ctx, pkg.ID); err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("删除包数据库记录 %s 失败: %v", pkg.PackageName, err))
|
||||
}
|
||||
|
||||
for _, history := range histories {
|
||||
if history.Package == pkg.PackageName {
|
||||
emptyPackageHistoryIDs = append(emptyPackageHistoryIDs, history.ID)
|
||||
cleanupInfo.DeletedModules = append(cleanupInfo.DeletedModules, history.StructName)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
validPackages = append(validPackages, PackageInfo{
|
||||
PackageName: pkg.PackageName,
|
||||
Template: pkg.Template,
|
||||
Label: pkg.Label,
|
||||
Desc: pkg.Desc,
|
||||
Module: pkg.Module,
|
||||
IsEmpty: false,
|
||||
})
|
||||
}
|
||||
|
||||
var dirtyHistoryIDs []uint
|
||||
for _, history := range histories {
|
||||
for _, emptyID := range emptyPackageHistoryIDs {
|
||||
if history.ID == emptyID {
|
||||
dirtyHistoryIDs = append(dirtyHistoryIDs, history.ID)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(dirtyHistoryIDs) > 0 {
|
||||
deletedCount := 0
|
||||
for _, historyID := range dirtyHistoryIDs {
|
||||
if err := deleteAutoCodeHistory(ctx, historyID); err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("删除脏历史记录失败: %v", err))
|
||||
continue
|
||||
}
|
||||
deletedCount++
|
||||
}
|
||||
if deletedCount > 0 {
|
||||
global.GVA_LOG.Info(fmt.Sprintf("成功删除 %d 条脏历史记录", deletedCount))
|
||||
}
|
||||
|
||||
if err := g.cleanupRelatedApiAndMenus(dirtyHistoryIDs); err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("清理相关API和菜单记录失败: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
predesignedModules, err := g.scanPredesignedModules()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描预设计模块失败: %v", err))
|
||||
predesignedModules = []PredesignedModuleInfo{}
|
||||
}
|
||||
|
||||
filteredModules := []PredesignedModuleInfo{}
|
||||
for _, module := range predesignedModules {
|
||||
isDeleted := false
|
||||
for _, deletedPkg := range cleanupInfo.DeletedPackages {
|
||||
if module.PackageName == deletedPkg {
|
||||
isDeleted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isDeleted {
|
||||
filteredModules = append(filteredModules, module)
|
||||
}
|
||||
}
|
||||
|
||||
dictionaries := []DictionaryPre{}
|
||||
dictEntities, err := fetchDictionaryList(ctx, "")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("获取字典信息失败: %v", err))
|
||||
} else {
|
||||
for _, dictionary := range dictEntities {
|
||||
dictionaries = append(dictionaries, DictionaryPre{
|
||||
Type: dictionary.Type,
|
||||
Desc: dictionary.Desc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var cleanupResult *CleanupInfo
|
||||
if len(cleanupInfo.DeletedPackages) > 0 || len(cleanupInfo.DeletedModules) > 0 {
|
||||
var message strings.Builder
|
||||
message.WriteString("**系统清理完成**\n\n")
|
||||
if len(cleanupInfo.DeletedPackages) > 0 {
|
||||
message.WriteString(fmt.Sprintf("- 删除了 %d 个空包: %s\n", len(cleanupInfo.DeletedPackages), strings.Join(cleanupInfo.DeletedPackages, ", ")))
|
||||
}
|
||||
if len(cleanupInfo.DeletedModules) > 0 {
|
||||
message.WriteString(fmt.Sprintf("- 删除了 %d 个相关模块: %s\n", len(cleanupInfo.DeletedModules), strings.Join(cleanupInfo.DeletedModules, ", ")))
|
||||
}
|
||||
cleanupInfo.CleanupMessage = message.String()
|
||||
cleanupResult = cleanupInfo
|
||||
}
|
||||
|
||||
response := &AnalyzeResponse{
|
||||
ExistingPackages: validPackages,
|
||||
PredesignedModules: filteredModules,
|
||||
Dictionaries: dictionaries,
|
||||
CleanupInfo: cleanupResult,
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// isPackageFolderEmpty 检查包文件夹是否为空
|
||||
func (g *GVAAnalyzer) isPackageFolderEmpty(packageName, template string) (bool, error) {
|
||||
// 根据模板类型确定基础路径
|
||||
var basePath string
|
||||
if template == "plugin" {
|
||||
basePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", packageName)
|
||||
} else {
|
||||
basePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", packageName)
|
||||
}
|
||||
|
||||
// 检查文件夹是否存在
|
||||
if _, err := os.Stat(basePath); os.IsNotExist(err) {
|
||||
return true, nil // 文件夹不存在,认为空
|
||||
} else if err != nil {
|
||||
return false, err // 其他错误
|
||||
}
|
||||
// 递归检查是否有.go文件
|
||||
return g.hasGoFilesRecursive(basePath)
|
||||
}
|
||||
|
||||
// hasGoFilesRecursive 递归检查目录及其子目录中是否有.go文件
|
||||
func (g *GVAAnalyzer) hasGoFilesRecursive(dirPath string) (bool, error) {
|
||||
entries, err := os.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return true, err // 读取失败,返回空
|
||||
}
|
||||
|
||||
// 检查当前目录下的.go文件
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".go") {
|
||||
return false, nil // 找到.go文件,不为空
|
||||
}
|
||||
}
|
||||
|
||||
// 递归检查子目录
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
subDirPath := filepath.Join(dirPath, entry.Name())
|
||||
isEmpty, err := g.hasGoFilesRecursive(subDirPath)
|
||||
if err != nil {
|
||||
continue // 忽略子目录的错误,继续检查其他目录
|
||||
}
|
||||
if !isEmpty {
|
||||
return false, nil // 子目录中找到.go文件,不为空
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil // 没有找到.go文件,为空
|
||||
}
|
||||
|
||||
// removeEmptyPackageFolder 删除空包文件夹
|
||||
func (g *GVAAnalyzer) removeEmptyPackageFolder(packageName, template string) error {
|
||||
var basePath string
|
||||
if template == "plugin" {
|
||||
basePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", packageName)
|
||||
} else {
|
||||
// 对于package类型,需要删除多个目录
|
||||
paths := []string{
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", packageName),
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "model", packageName),
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", packageName),
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", packageName),
|
||||
}
|
||||
for _, path := range paths {
|
||||
if err := g.removeDirectoryIfExists(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return g.removeDirectoryIfExists(basePath)
|
||||
}
|
||||
|
||||
// removeDirectoryIfExists 删除目录(如果存在)
|
||||
func (g *GVAAnalyzer) removeDirectoryIfExists(dirPath string) error {
|
||||
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
|
||||
return nil // 目录不存在,无需删除
|
||||
} else if err != nil {
|
||||
return err // 其他错误
|
||||
}
|
||||
|
||||
// 检查目录中是否包含go文件
|
||||
noGoFiles, err := g.hasGoFilesRecursive(dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// hasGoFilesRecursive 返回 false 表示发现了 go 文件
|
||||
if noGoFiles {
|
||||
return os.RemoveAll(dirPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanupRelatedApiAndMenus 清理相关的API和菜单记录
|
||||
func (g *GVAAnalyzer) cleanupRelatedApiAndMenus(historyIDs []uint) error {
|
||||
if len(historyIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 这里可以根据需要实现具体的API和菜单清理逻辑
|
||||
// 由于涉及到具体的业务逻辑,这里只做日志记录
|
||||
global.GVA_LOG.Info(fmt.Sprintf("清理历史记录ID %v 相关的API和菜单记录", historyIDs))
|
||||
|
||||
// 可以调用service层的相关方法进行清理
|
||||
// 例如:service.ServiceGroupApp.SystemApiService.DeleteApisByIds(historyIDs)
|
||||
// 例如:service.ServiceGroupApp.MenuService.DeleteMenusByIds(historyIDs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// scanPredesignedModules 扫描预设计模块
|
||||
func (g *GVAAnalyzer) scanPredesignedModules() ([]PredesignedModuleInfo, error) {
|
||||
// 获取autocode配置路径
|
||||
autocodeRoot := global.GVA_CONFIG.AutoCode.Root
|
||||
if autocodeRoot == "" {
|
||||
return nil, errors.New("autocode根路径未配置")
|
||||
}
|
||||
|
||||
var modules []PredesignedModuleInfo
|
||||
|
||||
// 扫描plugin目录
|
||||
pluginModules, err := g.scanPluginModules(filepath.Join(autocodeRoot, global.GVA_CONFIG.AutoCode.Server, "plugin"))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描plugin模块失败: %v", err))
|
||||
} else {
|
||||
modules = append(modules, pluginModules...)
|
||||
}
|
||||
|
||||
// 扫描model目录
|
||||
modelModules, err := g.scanModelModules(filepath.Join(autocodeRoot, global.GVA_CONFIG.AutoCode.Server, "model"))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描model模块失败: %v", err))
|
||||
} else {
|
||||
modules = append(modules, modelModules...)
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
// scanPluginModules 扫描插件模块
|
||||
func (g *GVAAnalyzer) scanPluginModules(pluginDir string) ([]PredesignedModuleInfo, error) {
|
||||
var modules []PredesignedModuleInfo
|
||||
|
||||
if _, err := os.Stat(pluginDir); os.IsNotExist(err) {
|
||||
return modules, nil // 目录不存在,返回空列表
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(pluginDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
pluginName := entry.Name()
|
||||
pluginPath := filepath.Join(pluginDir, pluginName)
|
||||
|
||||
// 查找model目录
|
||||
modelDir := filepath.Join(pluginPath, "model")
|
||||
if _, err := os.Stat(modelDir); err == nil {
|
||||
// 扫描model目录下的模块
|
||||
pluginModules, err := g.scanModulesInDirectory(modelDir, pluginName, "plugin")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描插件 %s 的模块失败: %v", pluginName, err))
|
||||
continue
|
||||
}
|
||||
modules = append(modules, pluginModules...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
// scanModelModules 扫描模型模块
|
||||
func (g *GVAAnalyzer) scanModelModules(modelDir string) ([]PredesignedModuleInfo, error) {
|
||||
var modules []PredesignedModuleInfo
|
||||
|
||||
if _, err := os.Stat(modelDir); os.IsNotExist(err) {
|
||||
return modules, nil // 目录不存在,返回空列表
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(modelDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
packageName := entry.Name()
|
||||
packagePath := filepath.Join(modelDir, packageName)
|
||||
|
||||
// 扫描包目录下的模块
|
||||
packageModules, err := g.scanModulesInDirectory(packagePath, packageName, "package")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描包 %s 的模块失败: %v", packageName, err))
|
||||
continue
|
||||
}
|
||||
modules = append(modules, packageModules...)
|
||||
}
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
// scanModulesInDirectory 扫描目录中的模块
|
||||
func (g *GVAAnalyzer) scanModulesInDirectory(dir, packageName, template string) ([]PredesignedModuleInfo, error) {
|
||||
var modules []PredesignedModuleInfo
|
||||
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".go") {
|
||||
moduleName := strings.TrimSuffix(entry.Name(), ".go")
|
||||
filePath := filepath.Join(dir, entry.Name())
|
||||
|
||||
module := PredesignedModuleInfo{
|
||||
ModuleName: moduleName,
|
||||
PackageName: packageName,
|
||||
Template: template,
|
||||
FilePaths: []string{filePath},
|
||||
Description: fmt.Sprintf("%s模块中的%s", packageName, moduleName),
|
||||
}
|
||||
modules = append(modules, module)
|
||||
}
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
@@ -1,750 +0,0 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&GVAExecutor{})
|
||||
}
|
||||
|
||||
// GVAExecutor GVA代码生成器
|
||||
type GVAExecutor struct{}
|
||||
|
||||
// ExecuteRequest 执行请求结构体
|
||||
type ExecuteRequest struct {
|
||||
ExecutionPlan ExecutionPlan `json:"executionPlan"` // 执行计划
|
||||
Requirement string `json:"requirement"` // 原始需求(可选,用于日志记录)
|
||||
}
|
||||
|
||||
// ExecuteResponse 执行响应结构体
|
||||
type ExecuteResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
PackageID uint `json:"packageId,omitempty"`
|
||||
HistoryID uint `json:"historyId,omitempty"`
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
GeneratedPaths []string `json:"generatedPaths,omitempty"`
|
||||
NextActions []string `json:"nextActions,omitempty"`
|
||||
}
|
||||
|
||||
// ExecutionPlan 执行计划结构体
|
||||
type ExecutionPlan struct {
|
||||
PackageName string `json:"packageName"`
|
||||
PackageType string `json:"packageType"` // "plugin" 或 "package"
|
||||
NeedCreatedPackage bool `json:"needCreatedPackage"`
|
||||
NeedCreatedModules bool `json:"needCreatedModules"`
|
||||
NeedCreatedDictionaries bool `json:"needCreatedDictionaries"`
|
||||
PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"`
|
||||
ModulesInfo []*request.AutoCode `json:"modulesInfo,omitempty"`
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
DictionariesInfo []*DictionaryGenerateRequest `json:"dictionariesInfo,omitempty"`
|
||||
}
|
||||
|
||||
// New 创建GVA代码生成执行器工具
|
||||
func (g *GVAExecutor) New() mcp.Tool {
|
||||
return mcp.NewTool("gva_execute",
|
||||
mcp.WithDescription(`**GVA代码生成执行器:直接执行代码生成,无需确认步骤**
|
||||
|
||||
**核心功能:**
|
||||
根据需求分析和当前的包信息判断是否调用,直接生成代码。支持批量创建多个模块、自动创建包、模块、字典等。
|
||||
|
||||
**使用场景:**
|
||||
在gva_analyze获取了当前的包信息和字典信息之后,如果已经包含了可以使用的包和模块,那就不要调用本mcp。根据分析结果直接生成代码,适用于自动化代码生成流程。
|
||||
|
||||
**重要提示:**
|
||||
- 当needCreatedModules=true时,模块创建会自动生成API和菜单,不应再调用api_creator和menu_creator工具
|
||||
- 字段使用字典类型时,系统会自动检查并创建字典
|
||||
- 字典创建会在模块创建之前执行
|
||||
- 当字段配置了dataSource且association=2(一对多关联)时,系统会自动将fieldType修改为'array'`),
|
||||
mcp.WithObject("executionPlan",
|
||||
mcp.Description("执行计划,包含包信息、模块与字典信息"),
|
||||
mcp.Required(),
|
||||
mcp.Properties(map[string]interface{}{
|
||||
"packageName": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "包名(小写开头)",
|
||||
},
|
||||
"packageType": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "package 或 plugin,如果用户提到了使用插件则创建plugin,如果用户没有特定说明则一律选用package",
|
||||
"enum": []string{"package", "plugin"},
|
||||
},
|
||||
"needCreatedPackage": map[string]interface{}{
|
||||
"type": "boolean",
|
||||
"description": "是否需要创建包,为true时packageInfo必需",
|
||||
},
|
||||
"needCreatedModules": map[string]interface{}{
|
||||
"type": "boolean",
|
||||
"description": "是否需要创建模块,为true时modulesInfo必需",
|
||||
},
|
||||
"needCreatedDictionaries": map[string]interface{}{
|
||||
"type": "boolean",
|
||||
"description": "是否需要创建字典,为true时dictionariesInfo必需",
|
||||
},
|
||||
"packageInfo": map[string]interface{}{
|
||||
"type": "object",
|
||||
"description": "包创建信息,当needCreatedPackage=true时必需",
|
||||
"properties": map[string]interface{}{
|
||||
"desc": map[string]interface{}{"type": "string", "description": "包描述"},
|
||||
"label": map[string]interface{}{"type": "string", "description": "展示名"},
|
||||
"template": map[string]interface{}{"type": "string", "description": "package 或 plugin,如果用户提到了使用插件则创建plugin,如果用户没有特定说明则一律选用package", "enum": []string{"package", "plugin"}},
|
||||
"packageName": map[string]interface{}{"type": "string", "description": "包名"},
|
||||
},
|
||||
},
|
||||
"modulesInfo": map[string]interface{}{
|
||||
"type": "array",
|
||||
"description": "模块配置列表,支持批量创建多个模块",
|
||||
"items": map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"package": map[string]interface{}{"type": "string", "description": "包名(小写开头,示例: userInfo)"},
|
||||
"tableName": map[string]interface{}{"type": "string", "description": "数据库表名(蛇形命名法,示例:user_info)"},
|
||||
"businessDB": map[string]interface{}{"type": "string", "description": "业务数据库(可留空表示默认)"},
|
||||
"structName": map[string]interface{}{"type": "string", "description": "结构体名(大驼峰示例:UserInfo)"},
|
||||
"packageName": map[string]interface{}{"type": "string", "description": "文件名称"},
|
||||
"description": map[string]interface{}{"type": "string", "description": "中文描述"},
|
||||
"abbreviation": map[string]interface{}{"type": "string", "description": "简称"},
|
||||
"humpPackageName": map[string]interface{}{"type": "string", "description": "文件名称(小驼峰),一般是结构体名的小驼峰示例:userInfo"},
|
||||
"gvaModel": map[string]interface{}{"type": "boolean", "description": "是否使用GVA模型(固定为true),自动包含ID、CreatedAt、UpdatedAt、DeletedAt字段"},
|
||||
"autoMigrate": map[string]interface{}{"type": "boolean", "description": "是否自动迁移数据库"},
|
||||
"autoCreateResource": map[string]interface{}{"type": "boolean", "description": "是否创建资源(默认为false)"},
|
||||
"autoCreateApiToSql": map[string]interface{}{"type": "boolean", "description": "是否创建API(默认为true)"},
|
||||
"autoCreateMenuToSql": map[string]interface{}{"type": "boolean", "description": "是否创建菜单(默认为true)"},
|
||||
"autoCreateBtnAuth": map[string]interface{}{"type": "boolean", "description": "是否创建按钮权限(默认为false)"},
|
||||
"onlyTemplate": map[string]interface{}{"type": "boolean", "description": "是否仅模板(默认为false)"},
|
||||
"isTree": map[string]interface{}{"type": "boolean", "description": "是否树形结构(默认为false)"},
|
||||
"treeJson": map[string]interface{}{"type": "string", "description": "树形JSON字段"},
|
||||
"isAdd": map[string]interface{}{"type": "boolean", "description": "是否新增(固定为false)"},
|
||||
"generateWeb": map[string]interface{}{"type": "boolean", "description": "是否生成前端代码"},
|
||||
"generateServer": map[string]interface{}{"type": "boolean", "description": "是否生成后端代码"},
|
||||
"fields": map[string]interface{}{
|
||||
"type": "array",
|
||||
"description": "字段列表",
|
||||
"items": map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"fieldName": map[string]interface{}{"type": "string", "description": "字段名(必须大写开头示例:UserName)"},
|
||||
"fieldDesc": map[string]interface{}{"type": "string", "description": "字段描述"},
|
||||
"fieldType": map[string]interface{}{"type": "string", "description": "字段类型:string(字符串)、richtext(富文本)、int(整型)、bool(布尔值)、float64(浮点型)、time.Time(时间)、enum(枚举)、picture(单图片)、pictures(多图片)、video(视频)、file(文件)、json(JSON)、array(数组)"},
|
||||
"fieldJson": map[string]interface{}{"type": "string", "description": "JSON标签,示例: userName"},
|
||||
"dataTypeLong": map[string]interface{}{"type": "string", "description": "数据长度"},
|
||||
"comment": map[string]interface{}{"type": "string", "description": "注释"},
|
||||
"columnName": map[string]interface{}{"type": "string", "description": "数据库列名,示例: user_name"},
|
||||
"fieldSearchType": map[string]interface{}{"type": "string", "description": "搜索类型:=、!=、>、>=、<、<=、LIKE、BETWEEN、IN、NOT IN、NOT BETWEEN"},
|
||||
"fieldSearchHide": map[string]interface{}{"type": "boolean", "description": "是否隐藏搜索"},
|
||||
"dictType": map[string]interface{}{"type": "string", "description": "字典类型,使用字典类型时系统会自动检查并创建字典"},
|
||||
"form": map[string]interface{}{"type": "boolean", "description": "表单显示"},
|
||||
"table": map[string]interface{}{"type": "boolean", "description": "表格显示"},
|
||||
"desc": map[string]interface{}{"type": "boolean", "description": "详情显示"},
|
||||
"excel": map[string]interface{}{"type": "boolean", "description": "导入导出"},
|
||||
"require": map[string]interface{}{"type": "boolean", "description": "是否必填"},
|
||||
"defaultValue": map[string]interface{}{"type": "string", "description": "默认值"},
|
||||
"errorText": map[string]interface{}{"type": "string", "description": "错误提示"},
|
||||
"clearable": map[string]interface{}{"type": "boolean", "description": "是否可清空"},
|
||||
"sort": map[string]interface{}{"type": "boolean", "description": "是否排序"},
|
||||
"primaryKey": map[string]interface{}{"type": "boolean", "description": "是否主键(gvaModel=false时必须有一个字段为true)"},
|
||||
"dataSource": map[string]interface{}{
|
||||
"type": "object",
|
||||
"description": "数据源配置,用于配置字段的关联表信息。获取表名提示:可在 server/model 和 plugin/xxx/model 目录下查看对应模块的 TableName() 接口实现获取实际表名(如 SysUser 的表名为 sys_users)。获取数据库名提示:主数据库通常使用 gva(默认数据库标识),多数据库可在 config.yaml 的 db-list 配置中查看可用数据库的 alias-name 字段,如果用户未提及关联多数据库信息则使用默认数据库,默认数据库的情况下 dbName填写为空",
|
||||
"properties": map[string]interface{}{
|
||||
"dbName": map[string]interface{}{"type": "string", "description": "关联的数据库名称(默认数据库留空)"},
|
||||
"table": map[string]interface{}{"type": "string", "description": "关联的表名"},
|
||||
"label": map[string]interface{}{"type": "string", "description": "用于显示的字段名(如name、title等)"},
|
||||
"value": map[string]interface{}{"type": "string", "description": "用于存储的值字段名(通常是id)"},
|
||||
"association": map[string]interface{}{"type": "integer", "description": "关联关系类型:1=一对一关联,2=一对多关联。一对一和一对多的前面的一是当前的实体,如果他只能关联另一个实体的一个则选用一对一,如果他需要关联多个他的关联实体则选用一对多"},
|
||||
"hasDeletedAt": map[string]interface{}{"type": "boolean", "description": "关联表是否有软删除字段"},
|
||||
},
|
||||
},
|
||||
"checkDataSource": map[string]interface{}{"type": "boolean", "description": "是否检查数据源,启用后会验证关联表的存在性"},
|
||||
"fieldIndexType": map[string]interface{}{"type": "string", "description": "索引类型"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"paths": map[string]interface{}{
|
||||
"type": "object",
|
||||
"description": "生成的文件路径映射",
|
||||
"additionalProperties": map[string]interface{}{"type": "string"},
|
||||
},
|
||||
"dictionariesInfo": map[string]interface{}{
|
||||
"type": "array",
|
||||
"description": "字典创建信息,字典创建会在模块创建之前执行",
|
||||
"items": map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"dictType": map[string]interface{}{"type": "string", "description": "字典类型,用于标识字典的唯一性"},
|
||||
"dictName": map[string]interface{}{"type": "string", "description": "字典名称,必须生成,字典的中文名称"},
|
||||
"description": map[string]interface{}{"type": "string", "description": "字典描述,字典的用途说明"},
|
||||
"status": map[string]interface{}{"type": "boolean", "description": "字典状态:true启用,false禁用"},
|
||||
"fieldDesc": map[string]interface{}{"type": "string", "description": "字段描述,用于AI理解字段含义并生成合适的选项"},
|
||||
"options": map[string]interface{}{
|
||||
"type": "array",
|
||||
"description": "字典选项列表(可选,如果不提供将根据fieldDesc自动生成默认选项)",
|
||||
"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": "排序号,数字越小越靠前"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
mcp.AdditionalProperties(false),
|
||||
),
|
||||
mcp.WithString("requirement",
|
||||
mcp.Description("原始需求描述(可选,用于日志记录)"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理执行请求(移除确认步骤)
|
||||
func (g *GVAExecutor) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
executionPlanData, ok := request.GetArguments()["executionPlan"]
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:executionPlan 必须提供")
|
||||
}
|
||||
|
||||
// 解析执行计划
|
||||
planJSON, err := json.Marshal(executionPlanData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析执行计划失败: %v", err)
|
||||
}
|
||||
|
||||
var plan ExecutionPlan
|
||||
err = json.Unmarshal(planJSON, &plan)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析执行计划失败: %v\n\n请确保ExecutionPlan格式正确,参考工具描述中的结构体格式要求", err)
|
||||
}
|
||||
|
||||
// 验证执行计划的完整性
|
||||
if err := g.validateExecutionPlan(&plan); err != nil {
|
||||
return nil, fmt.Errorf("执行计划验证失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取原始需求(可选)
|
||||
var originalRequirement string
|
||||
if reqData, ok := request.GetArguments()["requirement"]; ok {
|
||||
if reqStr, ok := reqData.(string); ok {
|
||||
originalRequirement = reqStr
|
||||
}
|
||||
}
|
||||
|
||||
// 直接执行创建操作(无确认步骤)
|
||||
result := g.executeCreation(ctx, &plan)
|
||||
|
||||
// 如果执行成功且有原始需求,提供代码复检建议
|
||||
var reviewMessage string
|
||||
if result.Success && originalRequirement != "" {
|
||||
global.GVA_LOG.Info("执行完成,返回生成的文件路径供AI进行代码复检...")
|
||||
|
||||
// 构建文件路径信息供AI使用
|
||||
var pathsInfo []string
|
||||
for _, path := range result.GeneratedPaths {
|
||||
pathsInfo = append(pathsInfo, fmt.Sprintf("- %s", path))
|
||||
}
|
||||
|
||||
reviewMessage = fmt.Sprintf("\n\n📁 已生成以下文件:\n%s\n\n💡 提示:可以检查生成的代码是否满足原始需求。", strings.Join(pathsInfo, "\n"))
|
||||
} else if originalRequirement == "" {
|
||||
reviewMessage = "\n\n💡 提示:如需代码复检,请提供原始需求描述。"
|
||||
}
|
||||
|
||||
// 序列化响应
|
||||
response := ExecuteResponse{
|
||||
Success: result.Success,
|
||||
Message: result.Message,
|
||||
PackageID: result.PackageID,
|
||||
HistoryID: result.HistoryID,
|
||||
Paths: result.Paths,
|
||||
GeneratedPaths: result.GeneratedPaths,
|
||||
NextActions: result.NextActions,
|
||||
}
|
||||
|
||||
responseJSON, err := json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("执行结果:\n\n%s%s", string(responseJSON), reviewMessage)),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// validateExecutionPlan 验证执行计划的完整性
|
||||
func (g *GVAExecutor) validateExecutionPlan(plan *ExecutionPlan) error {
|
||||
if plan.PackageName == "" {
|
||||
return errors.New("packageName 不能为空")
|
||||
}
|
||||
if plan.PackageType != "package" && plan.PackageType != "plugin" {
|
||||
return errors.New("packageType 必须是 'package' 或 'plugin'")
|
||||
}
|
||||
|
||||
if plan.NeedCreatedPackage && plan.PackageInfo != nil && plan.PackageType != plan.PackageInfo.Template {
|
||||
return errors.New("packageType 和 packageInfo.template 必须保持一致")
|
||||
}
|
||||
|
||||
if plan.NeedCreatedPackage {
|
||||
if plan.PackageInfo == nil {
|
||||
return errors.New("当 needCreatedPackage=true 时,packageInfo 不能为空")
|
||||
}
|
||||
if plan.PackageInfo.PackageName == "" {
|
||||
return errors.New("packageInfo.packageName 不能为空")
|
||||
}
|
||||
if plan.PackageInfo.Template != "package" && plan.PackageInfo.Template != "plugin" {
|
||||
return errors.New("packageInfo.template 必须是 'package' 或 'plugin'")
|
||||
}
|
||||
if plan.PackageInfo.Label == "" {
|
||||
return errors.New("packageInfo.label 不能为空")
|
||||
}
|
||||
if plan.PackageInfo.Desc == "" {
|
||||
return errors.New("packageInfo.desc 不能为空")
|
||||
}
|
||||
}
|
||||
|
||||
if plan.NeedCreatedModules {
|
||||
if len(plan.ModulesInfo) == 0 {
|
||||
return errors.New("当 needCreatedModules=true 时,modulesInfo 不能为空")
|
||||
}
|
||||
|
||||
for moduleIndex, moduleInfo := range plan.ModulesInfo {
|
||||
if moduleInfo.Package == "" {
|
||||
return fmt.Errorf("模块 %d 的 package 不能为空", moduleIndex+1)
|
||||
}
|
||||
if moduleInfo.StructName == "" {
|
||||
return fmt.Errorf("模块 %d 的 structName 不能为空", moduleIndex+1)
|
||||
}
|
||||
if moduleInfo.TableName == "" {
|
||||
return fmt.Errorf("模块 %d 的 tableName 不能为空", moduleIndex+1)
|
||||
}
|
||||
if moduleInfo.Description == "" {
|
||||
return fmt.Errorf("模块 %d 的 description 不能为空", moduleIndex+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)
|
||||
}
|
||||
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 len(field.FieldName) > 0 {
|
||||
firstChar := string(field.FieldName[0])
|
||||
if firstChar >= "a" && firstChar <= "z" {
|
||||
moduleInfo.Fields[i].FieldName = strings.ToUpper(firstChar) + field.FieldName[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)
|
||||
}
|
||||
|
||||
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 !validType {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldType '%s' 不支持", moduleIndex+1, i+1, field.FieldType)
|
||||
}
|
||||
|
||||
if field.FieldSearchType != "" {
|
||||
validSearchTypes := []string{"=", "!=", ">", ">=", "<", "<=", "LIKE", "BETWEEN", "IN", "NOT IN"}
|
||||
validSearchType := false
|
||||
for _, validSearchTypeValue := range validSearchTypes {
|
||||
if field.FieldSearchType == validSearchTypeValue {
|
||||
validSearchType = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validSearchType {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldSearchType '%s' 不支持", moduleIndex+1, i+1, field.FieldSearchType)
|
||||
}
|
||||
}
|
||||
|
||||
if field.DataSource != nil {
|
||||
associationValue := field.DataSource.Association
|
||||
if associationValue == 2 && field.FieldType != "array" {
|
||||
global.GVA_LOG.Info(fmt.Sprintf("module %d field %d association=2, force fieldType to array", moduleIndex+1, i+1))
|
||||
moduleInfo.Fields[i].FieldType = "array"
|
||||
}
|
||||
if associationValue != 1 && associationValue != 2 {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 dataSource.association 必须是 1 或 2", moduleIndex+1, i+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !moduleInfo.GvaModel {
|
||||
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 {
|
||||
for i, field := range moduleInfo.Fields {
|
||||
if field.PrimaryKey {
|
||||
return fmt.Errorf("模块 %d:当 gvaModel=true 时,字段 %d 的 primaryKey 应该为 false", moduleIndex+1, i+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// executeCreation 执行创建操作
|
||||
func (g *GVAExecutor) executeCreation(ctx context.Context, plan *ExecutionPlan) *ExecuteResponse {
|
||||
result := &ExecuteResponse{
|
||||
Success: false,
|
||||
Paths: make(map[string]string),
|
||||
GeneratedPaths: []string{}, // 初始化生成文件路径列表
|
||||
}
|
||||
|
||||
// 无论如何都先构建目录结构信息,确保paths始终返回
|
||||
result.Paths = g.buildDirectoryStructure(plan)
|
||||
|
||||
// 记录预期生成的文件路径
|
||||
result.GeneratedPaths = g.collectExpectedFilePaths(plan)
|
||||
|
||||
if !plan.NeedCreatedModules {
|
||||
result.Success = true
|
||||
result.Message += "已列出当前功能所涉及的目录结构信息; 请在paths中查看; 并且在对应指定文件中实现相关的业务逻辑; "
|
||||
return result
|
||||
}
|
||||
|
||||
// 创建包(如果需要)
|
||||
if plan.NeedCreatedPackage && plan.PackageInfo != nil {
|
||||
err := createAutoCodePackage(ctx, plan.PackageInfo)
|
||||
if err != nil {
|
||||
result.Message = fmt.Sprintf("创建包失败: %v", err)
|
||||
// 即使创建包失败,也要返回paths信息
|
||||
return result
|
||||
}
|
||||
result.Message += "包创建成功; "
|
||||
}
|
||||
|
||||
// 创建指定字典(如果需要)
|
||||
if plan.NeedCreatedDictionaries && len(plan.DictionariesInfo) > 0 {
|
||||
dictResult := g.createDictionariesFromInfo(ctx, plan.DictionariesInfo)
|
||||
result.Message += dictResult
|
||||
}
|
||||
|
||||
// 批量创建字典和模块(如果需要)
|
||||
if plan.NeedCreatedModules && len(plan.ModulesInfo) > 0 {
|
||||
// 遍历所有模块进行创建
|
||||
for _, moduleInfo := range plan.ModulesInfo {
|
||||
|
||||
// 创建模块
|
||||
err := moduleInfo.Pretreatment()
|
||||
if err != nil {
|
||||
result.Message += fmt.Sprintf("模块 %s 信息预处理失败: %v; ", moduleInfo.StructName, err)
|
||||
continue // 继续处理下一个模块
|
||||
}
|
||||
|
||||
err = createAutoCodeModule(ctx, *moduleInfo)
|
||||
if err != nil {
|
||||
result.Message += fmt.Sprintf("创建模块 %s 失败: %v; ", moduleInfo.StructName, err)
|
||||
continue // 继续处理下一个模块
|
||||
}
|
||||
result.Message += fmt.Sprintf("模块 %s 创建成功; ", moduleInfo.StructName)
|
||||
}
|
||||
|
||||
result.Message += fmt.Sprintf("批量创建完成,共处理 %d 个模块; ", len(plan.ModulesInfo))
|
||||
|
||||
// 添加重要提醒:不要使用其他MCP工具
|
||||
result.Message += "\n\n⚠️ 重要提醒:\n"
|
||||
result.Message += "模块创建已完成,API和菜单已自动生成。请不要再调用以下MCP工具:\n"
|
||||
result.Message += "- api_creator:API权限已在模块创建时自动生成\n"
|
||||
result.Message += "- menu_creator:前端菜单已在模块创建时自动生成\n"
|
||||
result.Message += "如需修改API或菜单,请直接在系统管理界面中进行配置。\n"
|
||||
}
|
||||
|
||||
result.Message += "已构建目录结构信息; "
|
||||
result.Success = true
|
||||
|
||||
if result.Message == "" {
|
||||
result.Message = "执行计划完成"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// buildDirectoryStructure 构建目录结构信息
|
||||
func (g *GVAExecutor) buildDirectoryStructure(plan *ExecutionPlan) map[string]string {
|
||||
paths := make(map[string]string)
|
||||
|
||||
// 获取配置信息
|
||||
autoCodeConfig := global.GVA_CONFIG.AutoCode
|
||||
|
||||
// 构建基础路径
|
||||
rootPath := autoCodeConfig.Root
|
||||
serverPath := autoCodeConfig.Server
|
||||
webPath := autoCodeConfig.Web
|
||||
moduleName := autoCodeConfig.Module
|
||||
|
||||
// 如果计划中有包名,使用计划中的包名,否则使用默认
|
||||
packageName := "example"
|
||||
if plan.PackageName != "" {
|
||||
packageName = plan.PackageName
|
||||
}
|
||||
|
||||
// 如果计划中有模块信息,获取第一个模块的结构名作为默认值
|
||||
structName := "ExampleStruct"
|
||||
if len(plan.ModulesInfo) > 0 && plan.ModulesInfo[0].StructName != "" {
|
||||
structName = plan.ModulesInfo[0].StructName
|
||||
}
|
||||
|
||||
// 根据包类型构建不同的路径结构
|
||||
packageType := plan.PackageType
|
||||
if packageType == "" {
|
||||
packageType = "package" // 默认为package模式
|
||||
}
|
||||
|
||||
// 构建服务端路径
|
||||
if serverPath != "" {
|
||||
serverBasePath := fmt.Sprintf("%s/%s", rootPath, serverPath)
|
||||
|
||||
if packageType == "plugin" {
|
||||
// Plugin 模式:所有文件都在 /plugin/packageName/ 目录中
|
||||
plugingBasePath := fmt.Sprintf("%s/plugin/%s", serverBasePath, packageName)
|
||||
|
||||
// API 路径
|
||||
paths["api"] = fmt.Sprintf("%s/api", plugingBasePath)
|
||||
|
||||
// Service 路径
|
||||
paths["service"] = fmt.Sprintf("%s/service", plugingBasePath)
|
||||
|
||||
// Model 路径
|
||||
paths["model"] = fmt.Sprintf("%s/model", plugingBasePath)
|
||||
|
||||
// Router 路径
|
||||
paths["router"] = fmt.Sprintf("%s/router", plugingBasePath)
|
||||
|
||||
// Request 路径
|
||||
paths["request"] = fmt.Sprintf("%s/model/request", plugingBasePath)
|
||||
|
||||
// Response 路径
|
||||
paths["response"] = fmt.Sprintf("%s/model/response", plugingBasePath)
|
||||
|
||||
// Plugin 特有文件
|
||||
paths["plugin_main"] = fmt.Sprintf("%s/main.go", plugingBasePath)
|
||||
paths["plugin_config"] = fmt.Sprintf("%s/plugin.go", plugingBasePath)
|
||||
paths["plugin_initialize"] = fmt.Sprintf("%s/initialize", plugingBasePath)
|
||||
} else {
|
||||
// Package 模式:传统的目录结构
|
||||
// API 路径
|
||||
paths["api"] = fmt.Sprintf("%s/api/v1/%s", serverBasePath, packageName)
|
||||
|
||||
// Service 路径
|
||||
paths["service"] = fmt.Sprintf("%s/service/%s", serverBasePath, packageName)
|
||||
|
||||
// Model 路径
|
||||
paths["model"] = fmt.Sprintf("%s/model/%s", serverBasePath, packageName)
|
||||
|
||||
// Router 路径
|
||||
paths["router"] = fmt.Sprintf("%s/router/%s", serverBasePath, packageName)
|
||||
|
||||
// Request 路径
|
||||
paths["request"] = fmt.Sprintf("%s/model/%s/request", serverBasePath, packageName)
|
||||
|
||||
// Response 路径
|
||||
paths["response"] = fmt.Sprintf("%s/model/%s/response", serverBasePath, packageName)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建前端路径(两种模式相同)
|
||||
if webPath != "" {
|
||||
webBasePath := fmt.Sprintf("%s/%s", rootPath, webPath)
|
||||
|
||||
if packageType == "plugin" {
|
||||
// Plugin 模式:前端文件也在 /plugin/packageName/ 目录中
|
||||
pluginWebBasePath := fmt.Sprintf("%s/plugin/%s", webBasePath, packageName)
|
||||
|
||||
// Vue 页面路径
|
||||
paths["vue_page"] = fmt.Sprintf("%s/view", pluginWebBasePath)
|
||||
|
||||
// API 路径
|
||||
paths["vue_api"] = fmt.Sprintf("%s/api", pluginWebBasePath)
|
||||
} else {
|
||||
// Package 模式:传统的目录结构
|
||||
// Vue 页面路径
|
||||
paths["vue_page"] = fmt.Sprintf("%s/view/%s", webBasePath, packageName)
|
||||
|
||||
// API 路径
|
||||
paths["vue_api"] = fmt.Sprintf("%s/api/%s", webBasePath, packageName)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加模块信息
|
||||
paths["module"] = moduleName
|
||||
paths["package_name"] = packageName
|
||||
paths["package_type"] = packageType
|
||||
paths["struct_name"] = structName
|
||||
paths["root_path"] = rootPath
|
||||
paths["server_path"] = serverPath
|
||||
paths["web_path"] = webPath
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
// collectExpectedFilePaths 收集预期生成的文件路径
|
||||
func (g *GVAExecutor) collectExpectedFilePaths(plan *ExecutionPlan) []string {
|
||||
var paths []string
|
||||
|
||||
// 获取目录结构
|
||||
dirPaths := g.buildDirectoryStructure(plan)
|
||||
|
||||
// 如果需要创建模块,添加预期的文件路径
|
||||
if plan.NeedCreatedModules && len(plan.ModulesInfo) > 0 {
|
||||
for _, moduleInfo := range plan.ModulesInfo {
|
||||
structName := moduleInfo.StructName
|
||||
|
||||
// 后端文件
|
||||
if apiPath, ok := dirPaths["api"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", apiPath, strings.ToLower(structName)))
|
||||
}
|
||||
if servicePath, ok := dirPaths["service"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", servicePath, strings.ToLower(structName)))
|
||||
}
|
||||
if modelPath, ok := dirPaths["model"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", modelPath, strings.ToLower(structName)))
|
||||
}
|
||||
if routerPath, ok := dirPaths["router"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", routerPath, strings.ToLower(structName)))
|
||||
}
|
||||
if requestPath, ok := dirPaths["request"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", requestPath, strings.ToLower(structName)))
|
||||
}
|
||||
if responsePath, ok := dirPaths["response"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", responsePath, strings.ToLower(structName)))
|
||||
}
|
||||
|
||||
// 前端文件
|
||||
if vuePage, ok := dirPaths["vue_page"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.vue", vuePage, strings.ToLower(structName)))
|
||||
}
|
||||
if vueApi, ok := dirPaths["vue_api"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.js", vueApi, strings.ToLower(structName)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
// checkDictionaryExists 检查字典是否存在
|
||||
func (g *GVAExecutor) checkDictionaryExists(dictType string) (bool, error) {
|
||||
dictionary, err := findDictionaryByType(context.Background(), dictType)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return dictionary != nil, nil
|
||||
}
|
||||
|
||||
// createDictionariesFromInfo 根据 DictionariesInfo 创建字典
|
||||
func (g *GVAExecutor) createDictionariesFromInfo(ctx context.Context, dictionariesInfo []*DictionaryGenerateRequest) string {
|
||||
var messages []string
|
||||
|
||||
messages = append(messages, fmt.Sprintf("开始创建 %d 个指定字典: ", len(dictionariesInfo)))
|
||||
|
||||
for _, dictInfo := range dictionariesInfo {
|
||||
exists, err := g.checkDictionaryExists(dictInfo.DictType)
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("检查字典 %s 时出错: %v; ", dictInfo.DictType, err))
|
||||
continue
|
||||
}
|
||||
|
||||
if !exists {
|
||||
err = createDictionary(ctx, system.SysDictionary{
|
||||
Name: dictInfo.DictName,
|
||||
Type: dictInfo.DictType,
|
||||
Status: enabledBoolPointer(),
|
||||
Desc: dictInfo.Description,
|
||||
})
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("创建字典 %s 失败: %v; ", dictInfo.DictType, err))
|
||||
continue
|
||||
}
|
||||
|
||||
messages = append(messages, fmt.Sprintf("成功创建字典 %s (%s); ", dictInfo.DictType, dictInfo.DictName))
|
||||
|
||||
createdDict, err := findDictionaryByType(ctx, dictInfo.DictType)
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("获取创建的字典失败: %v; ", err))
|
||||
continue
|
||||
}
|
||||
if createdDict == nil {
|
||||
messages = append(messages, fmt.Sprintf("获取创建的字典失败: %s; ", dictInfo.DictType))
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dictInfo.Options) > 0 {
|
||||
successCount := 0
|
||||
for _, option := range dictInfo.Options {
|
||||
dictionaryDetail := system.SysDictionaryDetail{
|
||||
Label: option.Label,
|
||||
Value: option.Value,
|
||||
Status: enabledBoolPointer(),
|
||||
Sort: option.Sort,
|
||||
SysDictionaryID: int(createdDict.ID),
|
||||
}
|
||||
|
||||
err = createDictionaryDetail(ctx, dictionaryDetail)
|
||||
if err == nil {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
messages = append(messages, fmt.Sprintf("创建了 %d 个字典选项; ", successCount))
|
||||
}
|
||||
} else {
|
||||
messages = append(messages, fmt.Sprintf("字典 %s 已存在,跳过创建; ", dictInfo.DictType))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(messages, "")
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
// GVAReviewer GVA代码审查工具
|
||||
type GVAReviewer struct{}
|
||||
|
||||
// init 注册工具
|
||||
func init() {
|
||||
RegisterTool(&GVAReviewer{})
|
||||
}
|
||||
|
||||
// ReviewRequest 审查请求结构
|
||||
type ReviewRequest struct {
|
||||
UserRequirement string `json:"userRequirement"` // 经过requirement_analyze后的用户需求
|
||||
GeneratedFiles []string `json:"generatedFiles"` // gva_execute创建的文件列表
|
||||
}
|
||||
|
||||
// ReviewResponse 审查响应结构
|
||||
type ReviewResponse struct {
|
||||
Success bool `json:"success"` // 是否审查成功
|
||||
Message string `json:"message"` // 审查结果消息
|
||||
AdjustmentPrompt string `json:"adjustmentPrompt"` // 调整代码的提示
|
||||
ReviewDetails string `json:"reviewDetails"` // 详细的审查结果
|
||||
}
|
||||
|
||||
// New 创建GVA代码审查工具
|
||||
func (g *GVAReviewer) New() mcp.Tool {
|
||||
return mcp.NewTool("gva_review",
|
||||
mcp.WithDescription(`**GVA代码审查工具 - 在gva_execute调用后使用**
|
||||
|
||||
**核心功能:**
|
||||
- 接收经过requirement_analyze处理的用户需求和gva_execute生成的文件列表
|
||||
- 分析生成的代码是否满足用户的原始需求
|
||||
- 检查是否涉及到关联、交互等复杂功能
|
||||
- 如果代码不满足需求,提供调整建议和新的prompt
|
||||
|
||||
**使用场景:**
|
||||
- 在gva_execute成功执行后调用
|
||||
- 用于验证生成的代码是否完整满足用户需求
|
||||
- 检查模块间的关联关系是否正确实现
|
||||
- 发现缺失的交互功能或业务逻辑
|
||||
|
||||
**工作流程:**
|
||||
1. 接收用户原始需求和生成的文件列表
|
||||
2. 分析需求中的关键功能点
|
||||
3. 检查生成的文件是否覆盖所有功能
|
||||
4. 识别缺失的关联关系、交互功能等
|
||||
5. 生成调整建议和新的开发prompt
|
||||
|
||||
**输出内容:**
|
||||
- 审查结果和是否需要调整
|
||||
- 详细的缺失功能分析
|
||||
- 针对性的代码调整建议
|
||||
- 可直接使用的开发prompt
|
||||
|
||||
**重要提示:**
|
||||
- 本工具专门用于代码质量审查,不执行实际的代码修改
|
||||
- 重点关注模块间关联、用户交互、业务流程完整性
|
||||
- 提供的调整建议应该具体可执行`),
|
||||
mcp.WithString("userRequirement",
|
||||
mcp.Description("经过requirement_analyze处理后的用户需求描述,包含详细的功能要求和字段信息"),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString("generatedFiles",
|
||||
mcp.Description("gva_execute创建的文件列表,JSON字符串格式,包含所有生成的后端和前端文件路径"),
|
||||
mcp.Required(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理审查请求
|
||||
func (g *GVAReviewer) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 获取用户需求
|
||||
userRequirementData, ok := request.GetArguments()["userRequirement"]
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:userRequirement 必须提供")
|
||||
}
|
||||
|
||||
userRequirement, ok := userRequirementData.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:userRequirement 必须是字符串类型")
|
||||
}
|
||||
|
||||
// 获取生成的文件列表
|
||||
generatedFilesData, ok := request.GetArguments()["generatedFiles"]
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:generatedFiles 必须提供")
|
||||
}
|
||||
|
||||
generatedFilesStr, ok := generatedFilesData.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:generatedFiles 必须是JSON字符串")
|
||||
}
|
||||
|
||||
// 解析JSON字符串为字符串数组
|
||||
var generatedFiles []string
|
||||
err := json.Unmarshal([]byte(generatedFilesStr), &generatedFiles)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析generatedFiles失败: %v", err)
|
||||
}
|
||||
|
||||
if len(generatedFiles) == 0 {
|
||||
return nil, errors.New("参数错误:generatedFiles 不能为空")
|
||||
}
|
||||
|
||||
// 直接生成调整提示,不进行复杂分析
|
||||
adjustmentPrompt := g.generateAdjustmentPrompt(userRequirement, generatedFiles)
|
||||
|
||||
// 构建简化的审查详情
|
||||
reviewDetails := fmt.Sprintf("📋 **代码审查报告**\n\n **用户原始需求:**\n%s\n\n **已生成文件数量:** %d\n\n **建议进行代码优化和完善**", userRequirement, len(generatedFiles))
|
||||
|
||||
// 构建审查结果
|
||||
reviewResult := &ReviewResponse{
|
||||
Success: true,
|
||||
Message: "代码审查完成",
|
||||
AdjustmentPrompt: adjustmentPrompt,
|
||||
ReviewDetails: reviewDetails,
|
||||
}
|
||||
|
||||
// 序列化响应
|
||||
responseJSON, err := json.MarshalIndent(reviewResult, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化审查结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("代码审查结果:\n\n%s", string(responseJSON))),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generateAdjustmentPrompt 生成调整代码的提示
|
||||
func (g *GVAReviewer) generateAdjustmentPrompt(userRequirement string, generatedFiles []string) string {
|
||||
var prompt strings.Builder
|
||||
|
||||
prompt.WriteString("🔧 **代码调整指导 Prompt:**\n\n")
|
||||
prompt.WriteString(fmt.Sprintf("**用户的原始需求为:** %s\n\n", userRequirement))
|
||||
prompt.WriteString("**经过GVA生成后的文件有如下内容:**\n")
|
||||
for _, file := range generatedFiles {
|
||||
prompt.WriteString(fmt.Sprintf("- %s\n", file))
|
||||
}
|
||||
prompt.WriteString("\n")
|
||||
|
||||
prompt.WriteString("**请帮我优化和完善代码,确保:**\n")
|
||||
prompt.WriteString("1. 代码完全满足用户的原始需求\n")
|
||||
prompt.WriteString("2. 完善模块间的关联关系,确保数据一致性\n")
|
||||
prompt.WriteString("3. 实现所有必要的用户交互功能\n")
|
||||
prompt.WriteString("4. 保持代码的完整性和可维护性\n")
|
||||
prompt.WriteString("5. 遵循GVA框架的开发规范和最佳实践\n")
|
||||
prompt.WriteString("6. 确保前后端功能完整对接\n")
|
||||
prompt.WriteString("7. 添加必要的错误处理和数据验证\n\n")
|
||||
prompt.WriteString("8. 如果需要vue路由跳转,请使用 menu_lister获取完整路由表,并且路由跳转使用 router.push({\"name\":从menu_lister中获取的name})\n\n")
|
||||
prompt.WriteString("9. 如果当前所有的vue页面内容无法满足需求,则自行书写vue文件,并且调用 menu_creator创建菜单记录\n\n")
|
||||
prompt.WriteString("10. 如果需要API调用,请使用 api_lister获取api表,根据需求调用对应接口\n\n")
|
||||
prompt.WriteString("11. 如果当前所有API无法满足则自行书写接口,补全前后端代码,并使用 api_creator创建api记录\n\n")
|
||||
prompt.WriteString("12. 无论前后端都不要随意删除import的内容\n\n")
|
||||
prompt.WriteString("**请基于用户需求和现有文件,提供完整的代码优化方案。**")
|
||||
|
||||
return prompt.String()
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
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工具的首选入口,请优先使用!**
|
||||
|
||||
** 核心能力:**
|
||||
作为资深系统架构师,智能分析用户需求并自动设计完整的模块架构
|
||||
|
||||
** 核心功能:**
|
||||
1. **智能需求解构**:深度分析用户需求,识别核心业务实体、业务流程、数据关系
|
||||
2. **自动模块设计**:基于需求分析,智能确定需要多少个模块及各模块功能
|
||||
3. **字段智能推导**:为每个模块自动设计详细字段,包含数据类型、关联关系、字典需求
|
||||
4. **架构优化建议**:提供模块拆分、关联设计、扩展性等专业建议
|
||||
|
||||
** 输出内容:**
|
||||
- 模块数量和架构设计
|
||||
- 每个模块的详细字段清单
|
||||
- 数据类型和关联关系设计
|
||||
- 字典需求和类型定义
|
||||
- 模块间关系图和扩展建议
|
||||
|
||||
** 适用场景:**
|
||||
- 用户需求描述不完整,需要智能补全
|
||||
- 复杂业务系统的模块架构设计
|
||||
- 需要专业的数据库设计建议
|
||||
- 想要快速搭建生产级业务系统
|
||||
|
||||
** 推荐工作流:**
|
||||
requirement_analyzer → gva_analyze → gva_execute → 其他辅助工具
|
||||
|
||||
`),
|
||||
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提示词 - 智能分析需求并确定模块结构
|
||||
func (t *RequirementAnalyzer) generateAIPrompt(userRequirement string) string {
|
||||
prompt := fmt.Sprintf(`# 智能需求分析与模块设计任务
|
||||
|
||||
## 用户原始需求
|
||||
%s
|
||||
|
||||
## 核心任务
|
||||
你需要作为一个资深的系统架构师,深度分析用户需求,智能设计出完整的模块架构。
|
||||
|
||||
## 分析步骤
|
||||
|
||||
### 第一步:需求解构分析
|
||||
请仔细分析用户需求,识别出:
|
||||
1. **核心业务实体**(如:用户、商品、订单、疫苗、宠物等)
|
||||
2. **业务流程**(如:注册、购买、记录、管理等)
|
||||
3. **数据关系**(实体间的关联关系)
|
||||
4. **功能模块**(需要哪些独立的管理模块)
|
||||
|
||||
### 第二步:模块架构设计
|
||||
基于需求分析,设计出模块架构,格式如下:
|
||||
|
||||
**模块1:[模块名称]**
|
||||
- 功能描述:[该模块的核心功能]
|
||||
- 主要字段:[列出关键字段,注明数据类型]
|
||||
- 关联关系:[与其他模块的关系,明确一对一/一对多]
|
||||
- 字典需求:[需要哪些字典类型]
|
||||
|
||||
**模块2:[模块名称]**
|
||||
- 功能描述:[该模块的核心功能]
|
||||
- 主要字段:[列出关键字段,注明数据类型]
|
||||
- 关联关系:[与其他模块的关系]
|
||||
- 字典需求:[需要哪些字典类型]
|
||||
|
||||
**...**
|
||||
|
||||
### 第三步:字段详细设计
|
||||
为每个模块详细设计字段:
|
||||
|
||||
#### 模块1字段清单:
|
||||
- 字段名1 (数据类型) - 字段描述 [是否必填] [关联信息/字典类型]
|
||||
- 字段名2 (数据类型) - 字段描述 [是否必填] [关联信息/字典类型]
|
||||
- ...
|
||||
|
||||
#### 模块2字段清单:
|
||||
- 字段名1 (数据类型) - 字段描述 [是否必填] [关联信息/字典类型]
|
||||
- ...
|
||||
|
||||
## 智能分析指导原则
|
||||
|
||||
### 模块拆分原则
|
||||
1. **单一职责**:每个模块只负责一个核心业务实体
|
||||
2. **数据完整性**:相关数据应该在同一模块中
|
||||
3. **业务独立性**:模块应该能够独立完成特定业务功能
|
||||
4. **扩展性考虑**:为未来功能扩展预留空间
|
||||
|
||||
### 字段设计原则
|
||||
1. **必要性**:只包含业务必需的字段
|
||||
2. **规范性**:遵循数据库设计规范
|
||||
3. **关联性**:正确识别实体间关系
|
||||
4. **字典化**:状态、类型等枚举值使用字典
|
||||
|
||||
### 关联关系识别
|
||||
- **一对一**:一个实体只能关联另一个实体的一个记录
|
||||
- **一对多**:一个实体可以关联另一个实体的多个记录
|
||||
- **多对多**:通过中间表实现复杂关联
|
||||
|
||||
## 特殊场景处理
|
||||
|
||||
### 复杂实体识别
|
||||
当用户提到某个概念时,要判断它是否需要独立模块:
|
||||
- **字典处理**:简单的常见的状态、类型(如:开关、性别、完成状态等)
|
||||
- **独立模块**:复杂实体(如:疫苗管理、宠物档案、注射记录)
|
||||
|
||||
## 输出要求
|
||||
|
||||
### 必须包含的信息
|
||||
1. **模块数量**:明确需要几个模块
|
||||
2. **模块关系图**:用文字描述模块间关系
|
||||
3. **核心字段**:每个模块的关键字段(至少5-10个)
|
||||
4. **数据类型**:string、int、bool、time.Time、float64等
|
||||
5. **关联设计**:明确哪些字段是关联字段
|
||||
6. **字典需求**:列出需要创建的字典类型
|
||||
|
||||
### 严格遵循用户输入
|
||||
- 如果用户提供了具体字段,**必须使用**用户提供的字段
|
||||
- 如果用户提供了SQL文件,**严格按照**SQL结构设计
|
||||
- **不要**随意发散,**不要**添加用户未提及的功能
|
||||
---
|
||||
|
||||
**现在请开始深度分析用户需求:"%s"**
|
||||
|
||||
请按照上述框架进行系统性分析,确保输出的模块设计既满足当前需求,又具备良好的扩展性。`, userRequirement, userRequirement)
|
||||
|
||||
return prompt
|
||||
}
|
||||
@@ -353,21 +353,13 @@ func ensureManagedBinary(serverRoot string) (string, error) {
|
||||
}
|
||||
|
||||
func resolveMCPServerRoot() string {
|
||||
root := strings.TrimSpace(global.GVA_CONFIG.AutoCode.Root)
|
||||
serverDir := strings.TrimSpace(global.GVA_CONFIG.AutoCode.Server)
|
||||
if serverDir == "" {
|
||||
serverDir = "server"
|
||||
}
|
||||
|
||||
candidates := []string{}
|
||||
if root != "" {
|
||||
candidates = append(candidates, filepath.Join(root, filepath.FromSlash(serverDir)))
|
||||
candidates = append(candidates, root)
|
||||
}
|
||||
|
||||
if cwd, err := os.Getwd(); err == nil {
|
||||
candidates = append(candidates, cwd)
|
||||
candidates = append(candidates, filepath.Join(cwd, "server"))
|
||||
if filepath.Base(cwd) == "server" {
|
||||
candidates = append(candidates, filepath.Dir(cwd))
|
||||
}
|
||||
}
|
||||
|
||||
for _, candidate := range candidates {
|
||||
@@ -407,11 +399,6 @@ func ensureMCPRuntimeDir() (string, error) {
|
||||
}
|
||||
|
||||
func resolveMCPProjectRoot() string {
|
||||
root := strings.TrimSpace(global.GVA_CONFIG.AutoCode.Root)
|
||||
if root != "" {
|
||||
return root
|
||||
}
|
||||
|
||||
serverRoot := resolveMCPServerRoot()
|
||||
if serverRoot == "" {
|
||||
return "."
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email/utils"
|
||||
utils2 "github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func ErrorToEmail() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var username string
|
||||
claims, _ := utils2.GetClaims(c)
|
||||
if claims.Username != "" {
|
||||
username = claims.Username
|
||||
} else {
|
||||
id, _ := strconv.Atoi(c.Request.Header.Get("x-user-id"))
|
||||
var u system.SysUser
|
||||
err := global.GVA_DB.Where("id = ?", id).First(&u).Error
|
||||
if err != nil {
|
||||
username = "Unknown"
|
||||
}
|
||||
username = u.Username
|
||||
}
|
||||
body, _ := io.ReadAll(c.Request.Body)
|
||||
// 再重新写回请求体body中,ioutil.ReadAll会清空c.Request.Body中的数据
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
|
||||
record := system.SysOperationRecord{
|
||||
Ip: c.ClientIP(),
|
||||
Method: c.Request.Method,
|
||||
Path: c.Request.URL.Path,
|
||||
Agent: c.Request.UserAgent(),
|
||||
Body: string(body),
|
||||
}
|
||||
now := time.Now()
|
||||
|
||||
c.Next()
|
||||
|
||||
latency := time.Since(now)
|
||||
status := c.Writer.Status()
|
||||
record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
|
||||
str := "接收到的请求为" + record.Body + "\n" + "请求方式为" + record.Method + "\n" + "报错信息如下" + record.ErrorMessage + "\n" + "耗时" + latency.String() + "\n"
|
||||
if status != 200 {
|
||||
subject := username + "" + record.Ip + "调用了" + record.Path + "报错了"
|
||||
if err := utils.ErrorToEmail(subject, str); err != nil {
|
||||
global.GVA_LOG.Error("ErrorToEmail Failed, err:", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package example
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
@@ -1,4 +1,4 @@
|
||||
package example
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
@@ -1,4 +1,4 @@
|
||||
package example
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
@@ -1,10 +1,6 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
)
|
||||
|
||||
type ExaAttachmentCategorySearch struct {
|
||||
ClassId int `json:"classId" form:"classId"`
|
||||
request.PageInfo
|
||||
PageInfo
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package response
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
|
||||
type FilePathResponse struct {
|
||||
FilePath string `json:"filePath"`
|
||||
}
|
||||
|
||||
type FileResponse struct {
|
||||
File example.ExaFile `json:"file"`
|
||||
File common.ExaFile `json:"file"`
|
||||
}
|
||||
7
server/model/common/response/exa_file_upload_download.go
Normal file
7
server/model/common/response/exa_file_upload_download.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package response
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
|
||||
type ExaFileResponse struct {
|
||||
File common.ExaFileUploadAndDownload `json:"file"`
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
)
|
||||
|
||||
type ExaCustomer struct {
|
||||
global.GVA_MODEL
|
||||
CustomerName string `json:"customerName" form:"customerName" gorm:"comment:客户名"` // 客户名
|
||||
CustomerPhoneData string `json:"customerPhoneData" form:"customerPhoneData" gorm:"comment:客户手机号"` // 客户手机号
|
||||
SysUserID uint `json:"sysUserId" form:"sysUserId" gorm:"comment:管理ID"` // 管理ID
|
||||
SysUserAuthorityID uint `json:"sysUserAuthorityID" form:"sysUserAuthorityID" gorm:"comment:管理角色ID"` // 管理角色ID
|
||||
SysUser system.SysUser `json:"sysUser" form:"sysUser" gorm:"comment:管理详情"` // 管理详情
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package response
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
|
||||
type ExaCustomerResponse struct {
|
||||
Customer example.ExaCustomer `json:"customer"`
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package response
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
|
||||
type ExaFileResponse struct {
|
||||
File example.ExaFileUploadAndDownload `json:"file"`
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package request
|
||||
|
||||
type AutoMcpTool struct {
|
||||
type McpToolTemplateRequest struct {
|
||||
Name string `json:"name" form:"name" binding:"required"`
|
||||
Description string `json:"description" form:"description" binding:"required"`
|
||||
Params []struct {
|
||||
Name string `json:"name" form:"name" binding:"required"`
|
||||
Description string `json:"description" form:"description" binding:"required"`
|
||||
Type string `json:"type" form:"type" binding:"required"` // string, number, boolean, object, array
|
||||
Type string `json:"type" form:"type" binding:"required"`
|
||||
Required bool `json:"required" form:"required"`
|
||||
Default string `json:"default" form:"default"`
|
||||
} `json:"params" form:"params"`
|
||||
Response []struct {
|
||||
Type string `json:"type" form:"type" binding:"required"` // text, image
|
||||
Type string `json:"type" form:"type" binding:"required"`
|
||||
} `json:"response" form:"response"`
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
commonReq "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
system "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
)
|
||||
|
||||
type SysAIWorkflowSessionUpsert struct {
|
||||
ID uint `json:"id"`
|
||||
Tab string `json:"tab"`
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
ConversationID string `json:"conversationId"`
|
||||
MessageID string `json:"messageId"`
|
||||
CurrentNodeID string `json:"currentNodeId"`
|
||||
Settings common.JSONMap `json:"settings"`
|
||||
FormData common.JSONMap `json:"formData"`
|
||||
ResultData common.JSONMap `json:"resultData"`
|
||||
Messages []system.AIWorkflowMessage `json:"messages"`
|
||||
}
|
||||
|
||||
type SysAIWorkflowSessionSearch struct {
|
||||
commonReq.PageInfo
|
||||
Tab string `json:"tab" form:"tab"`
|
||||
}
|
||||
|
||||
type SysAIWorkflowMarkdownDump struct {
|
||||
ID uint `json:"id"`
|
||||
Tab string `json:"tab"`
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
ConversationID string `json:"conversationId"`
|
||||
MessageID string `json:"messageId"`
|
||||
CurrentNodeID string `json:"currentNodeId"`
|
||||
Settings common.JSONMap `json:"settings"`
|
||||
FormData common.JSONMap `json:"formData"`
|
||||
ResultData common.JSONMap `json:"resultData"`
|
||||
Messages []system.AIWorkflowMessage `json:"messages"`
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/pkg/errors"
|
||||
"go/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AutoCode struct {
|
||||
Package string `json:"package"`
|
||||
PackageT string `json:"-"`
|
||||
TableName string `json:"tableName" example:"表名"` // 表名
|
||||
BusinessDB string `json:"businessDB" example:"业务数据库"` // 业务数据库
|
||||
StructName string `json:"structName" example:"Struct名称"` // Struct名称
|
||||
PackageName string `json:"packageName" example:"文件名称"` // 文件名称
|
||||
Description string `json:"description" example:"Struct中文名称"` // Struct中文名称
|
||||
Abbreviation string `json:"abbreviation" example:"Struct简称"` // Struct简称
|
||||
HumpPackageName string `json:"humpPackageName" example:"go文件名称"` // go文件名称
|
||||
GvaModel bool `json:"gvaModel" example:"false"` // 是否使用gva默认Model
|
||||
AutoMigrate bool `json:"autoMigrate" example:"false"` // 是否自动迁移表结构
|
||||
AutoCreateResource bool `json:"autoCreateResource" example:"false"` // 是否自动创建资源标识
|
||||
AutoCreateApiToSql bool `json:"autoCreateApiToSql" example:"false"` // 是否自动创建api
|
||||
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql" example:"false"` // 是否自动创建menu
|
||||
AutoCreateBtnAuth bool `json:"autoCreateBtnAuth" example:"false"` // 是否自动创建按钮权限
|
||||
OnlyTemplate bool `json:"onlyTemplate" example:"false"` // 是否只生成模板
|
||||
IsTree bool `json:"isTree" example:"false"` // 是否树形结构
|
||||
TreeJson string `json:"treeJson" example:"展示的树json字段"` // 展示的树json字段
|
||||
IsAdd bool `json:"isAdd" example:"false"` // 是否新增
|
||||
Fields []*AutoCodeField `json:"fields"`
|
||||
GenerateWeb bool `json:"generateWeb" example:"true"` // 是否生成web
|
||||
GenerateServer bool `json:"generateServer" example:"true"` // 是否生成server
|
||||
Module string `json:"-"`
|
||||
DictTypes []string `json:"-"`
|
||||
PrimaryField *AutoCodeField `json:"primaryField"`
|
||||
DataSourceMap map[string]*DataSource `json:"-"`
|
||||
HasPic bool `json:"-"`
|
||||
HasFile bool `json:"-"`
|
||||
HasTimer bool `json:"-"`
|
||||
NeedSort bool `json:"-"`
|
||||
NeedJSON bool `json:"-"`
|
||||
HasRichText bool `json:"-"`
|
||||
HasDataSource bool `json:"-"`
|
||||
HasSearchTimer bool `json:"-"`
|
||||
HasArray bool `json:"-"`
|
||||
HasExcel bool `json:"-"`
|
||||
}
|
||||
|
||||
type DataSource struct {
|
||||
DBName string `json:"dbName"`
|
||||
Table string `json:"table"`
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
Association int `json:"association"` // 关联关系 1 一对一 2 一对多
|
||||
HasDeletedAt bool `json:"hasDeletedAt"`
|
||||
}
|
||||
|
||||
func (r *AutoCode) Apis() []model.SysApi {
|
||||
return []model.SysApi{
|
||||
{
|
||||
Path: "/" + r.Abbreviation + "/" + "create" + r.StructName,
|
||||
Description: "新增" + r.Description,
|
||||
ApiGroup: r.Description,
|
||||
Method: "POST",
|
||||
},
|
||||
{
|
||||
Path: "/" + r.Abbreviation + "/" + "delete" + r.StructName,
|
||||
Description: "删除" + r.Description,
|
||||
ApiGroup: r.Description,
|
||||
Method: "DELETE",
|
||||
},
|
||||
{
|
||||
Path: "/" + r.Abbreviation + "/" + "delete" + r.StructName + "ByIds",
|
||||
Description: "批量删除" + r.Description,
|
||||
ApiGroup: r.Description,
|
||||
Method: "DELETE",
|
||||
},
|
||||
{
|
||||
Path: "/" + r.Abbreviation + "/" + "update" + r.StructName,
|
||||
Description: "更新" + r.Description,
|
||||
ApiGroup: r.Description,
|
||||
Method: "PUT",
|
||||
},
|
||||
{
|
||||
Path: "/" + r.Abbreviation + "/" + "find" + r.StructName,
|
||||
Description: "根据ID获取" + r.Description,
|
||||
ApiGroup: r.Description,
|
||||
Method: "GET",
|
||||
},
|
||||
{
|
||||
Path: "/" + r.Abbreviation + "/" + "get" + r.StructName + "List",
|
||||
Description: "获取" + r.Description + "列表",
|
||||
ApiGroup: r.Description,
|
||||
Method: "GET",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *AutoCode) Menu(template string) model.SysBaseMenu {
|
||||
component := fmt.Sprintf("view/%s/%s/%s.vue", r.Package, r.PackageName, r.PackageName)
|
||||
if template != "package" {
|
||||
component = fmt.Sprintf("plugin/%s/view/%s.vue", r.Package, r.PackageName)
|
||||
}
|
||||
return model.SysBaseMenu{
|
||||
ParentId: 0,
|
||||
Path: r.Abbreviation,
|
||||
Name: r.Abbreviation,
|
||||
Component: component,
|
||||
Meta: model.Meta{
|
||||
Title: r.Description,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Pretreatment 预处理
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (r *AutoCode) Pretreatment() error {
|
||||
r.Module = global.GVA_CONFIG.AutoCode.Module
|
||||
if token.IsKeyword(r.Abbreviation) {
|
||||
r.Abbreviation = r.Abbreviation + "_"
|
||||
} // go 关键字处理
|
||||
if strings.HasSuffix(r.HumpPackageName, "test") {
|
||||
r.HumpPackageName = r.HumpPackageName + "_"
|
||||
} // test
|
||||
length := len(r.Fields)
|
||||
dict := make(map[string]string, length)
|
||||
r.DataSourceMap = make(map[string]*DataSource, length)
|
||||
for i := 0; i < length; i++ {
|
||||
if r.Fields[i].Excel {
|
||||
r.HasExcel = true
|
||||
}
|
||||
if r.Fields[i].DictType != "" {
|
||||
dict[r.Fields[i].DictType] = ""
|
||||
}
|
||||
if r.Fields[i].Sort {
|
||||
r.NeedSort = true
|
||||
}
|
||||
switch r.Fields[i].FieldType {
|
||||
case "file":
|
||||
r.HasFile = true
|
||||
r.NeedJSON = true
|
||||
case "json":
|
||||
r.NeedJSON = true
|
||||
case "array":
|
||||
r.NeedJSON = true
|
||||
r.HasArray = true
|
||||
case "video":
|
||||
r.HasPic = true
|
||||
case "richtext":
|
||||
r.HasRichText = true
|
||||
case "picture":
|
||||
r.HasPic = true
|
||||
case "pictures":
|
||||
r.HasPic = true
|
||||
r.NeedJSON = true
|
||||
case "time.Time":
|
||||
r.HasTimer = true
|
||||
if r.Fields[i].FieldSearchType != "" && r.Fields[i].FieldSearchType != "BETWEEN" && r.Fields[i].FieldSearchType != "NOT BETWEEN" {
|
||||
r.HasSearchTimer = true
|
||||
}
|
||||
}
|
||||
if r.Fields[i].DataSource != nil {
|
||||
if r.Fields[i].DataSource.Table != "" && r.Fields[i].DataSource.Label != "" && r.Fields[i].DataSource.Value != "" {
|
||||
r.HasDataSource = true
|
||||
r.Fields[i].CheckDataSource = true
|
||||
r.DataSourceMap[r.Fields[i].FieldJson] = r.Fields[i].DataSource
|
||||
}
|
||||
}
|
||||
if !r.GvaModel && r.PrimaryField == nil && r.Fields[i].PrimaryKey {
|
||||
r.PrimaryField = r.Fields[i]
|
||||
} // 自定义主键
|
||||
}
|
||||
{
|
||||
for key := range dict {
|
||||
r.DictTypes = append(r.DictTypes, key)
|
||||
}
|
||||
} // DictTypes => 字典
|
||||
{
|
||||
if r.GvaModel {
|
||||
r.PrimaryField = &AutoCodeField{
|
||||
FieldName: "ID",
|
||||
FieldType: "uint",
|
||||
FieldDesc: "ID",
|
||||
FieldJson: "ID",
|
||||
DataTypeLong: "20",
|
||||
Comment: "主键ID",
|
||||
ColumnName: "id",
|
||||
}
|
||||
}
|
||||
} // GvaModel
|
||||
{
|
||||
if r.IsAdd && r.PrimaryField == nil {
|
||||
r.PrimaryField = new(AutoCodeField)
|
||||
}
|
||||
} // 新增字段模式下不关注主键
|
||||
if r.Package == "" {
|
||||
return errors.New("Package为空!")
|
||||
} // 增加判断:Package不为空
|
||||
packages := []rune(r.Package)
|
||||
if len(packages) > 0 {
|
||||
if packages[0] >= 97 && packages[0] <= 122 {
|
||||
packages[0] = packages[0] - 32
|
||||
}
|
||||
r.PackageT = string(packages)
|
||||
} // PackageT 是 Package 的首字母大写
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *AutoCode) History() SysAutoHistoryCreate {
|
||||
bytes, _ := json.Marshal(r)
|
||||
return SysAutoHistoryCreate{
|
||||
Table: r.TableName,
|
||||
Package: r.Package,
|
||||
Request: string(bytes),
|
||||
StructName: r.StructName,
|
||||
BusinessDB: r.BusinessDB,
|
||||
Description: r.Description,
|
||||
}
|
||||
}
|
||||
|
||||
type AutoCodeField struct {
|
||||
FieldName string `json:"fieldName"` // Field名
|
||||
FieldDesc string `json:"fieldDesc"` // 中文名
|
||||
FieldType string `json:"fieldType"` // Field数据类型
|
||||
FieldJson string `json:"fieldJson"` // FieldJson
|
||||
DataTypeLong string `json:"dataTypeLong"` // 数据库字段长度
|
||||
Comment string `json:"comment"` // 数据库字段描述
|
||||
ColumnName string `json:"columnName"` // 数据库字段
|
||||
FieldSearchType string `json:"fieldSearchType"` // 搜索条件
|
||||
FieldSearchHide bool `json:"fieldSearchHide"` // 是否隐藏查询条件
|
||||
DictType string `json:"dictType"` // 字典
|
||||
//Front bool `json:"front"` // 是否前端可见
|
||||
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"` // 索引类型
|
||||
}
|
||||
|
||||
type AutoFunc struct {
|
||||
Package string `json:"package"`
|
||||
FuncName string `json:"funcName"` // 方法名称
|
||||
Router string `json:"router"` // 路由名称
|
||||
FuncDesc string `json:"funcDesc"` // 方法介绍
|
||||
BusinessDB string `json:"businessDB"` // 业务库
|
||||
StructName string `json:"structName"` // Struct名称
|
||||
PackageName string `json:"packageName"` // 文件名称
|
||||
Description string `json:"description"` // Struct中文名称
|
||||
Abbreviation string `json:"abbreviation"` // Struct简称
|
||||
HumpPackageName string `json:"humpPackageName"` // go文件名称
|
||||
Method string `json:"method"` // 方法
|
||||
IsPlugin bool `json:"isPlugin"` // 是否插件
|
||||
IsAuth bool `json:"isAuth"` // 是否鉴权
|
||||
IsPreview bool `json:"isPreview"` // 是否预览
|
||||
IsAi bool `json:"isAi"` // 是否AI
|
||||
ApiFunc string `json:"apiFunc"` // API方法
|
||||
ServerFunc string `json:"serverFunc"` // 服务方法
|
||||
JsFunc string `json:"jsFunc"` // JS方法
|
||||
}
|
||||
|
||||
type InitMenu struct {
|
||||
PlugName string `json:"plugName"`
|
||||
ParentMenu string `json:"parentMenu"`
|
||||
Menus []uint `json:"menus"`
|
||||
}
|
||||
|
||||
type InitApi struct {
|
||||
PlugName string `json:"plugName"`
|
||||
APIs []uint `json:"apis"`
|
||||
}
|
||||
|
||||
type InitDictionary struct {
|
||||
PlugName string `json:"plugName"`
|
||||
Dictionaries []uint `json:"dictionaries"`
|
||||
}
|
||||
|
||||
type LLMAutoCode struct {
|
||||
Prompt string `json:"prompt" form:"prompt" gorm:"column:prompt;comment:提示语;type:text;"` //提示语
|
||||
Mode string `json:"mode" form:"mode" gorm:"column:mode;comment:模式;type:text;"` //模式
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
)
|
||||
|
||||
type SysAutoCodePackageCreate struct {
|
||||
Desc string `json:"desc" example:"描述"`
|
||||
Label string `json:"label" example:"展示名"`
|
||||
Template string `json:"template" example:"模版"`
|
||||
PackageName string `json:"packageName" example:"包名"`
|
||||
Module string `json:"-" example:"模块"`
|
||||
}
|
||||
|
||||
func (r *SysAutoCodePackageCreate) AutoCode() AutoCode {
|
||||
return AutoCode{
|
||||
Package: r.PackageName,
|
||||
Module: global.GVA_CONFIG.AutoCode.Module,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SysAutoCodePackageCreate) Create() model.SysAutoCodePackage {
|
||||
return model.SysAutoCodePackage{
|
||||
Desc: r.Desc,
|
||||
Label: r.Label,
|
||||
Template: r.Template,
|
||||
PackageName: r.PackageName,
|
||||
Module: global.GVA_CONFIG.AutoCode.Module,
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
)
|
||||
|
||||
type SysAutoHistoryCreate struct {
|
||||
Table string // 表名
|
||||
Package string // 模块名/插件名
|
||||
Request string // 前端传入的结构化信息
|
||||
StructName string // 结构体名称
|
||||
BusinessDB string // 业务库
|
||||
Description string // Struct中文名称
|
||||
Injections map[string]string // 注入路径
|
||||
Templates map[string]string // 模板信息
|
||||
ApiIDs []uint // api表注册内容
|
||||
MenuID uint // 菜单ID
|
||||
ExportTemplateID uint // 导出模板ID
|
||||
}
|
||||
|
||||
func (r *SysAutoHistoryCreate) Create() model.SysAutoCodeHistory {
|
||||
entity := model.SysAutoCodeHistory{
|
||||
Package: r.Package,
|
||||
Request: r.Request,
|
||||
Table: r.Table,
|
||||
StructName: r.StructName,
|
||||
Abbreviation: r.StructName,
|
||||
BusinessDB: r.BusinessDB,
|
||||
Description: r.Description,
|
||||
Injections: r.Injections,
|
||||
Templates: r.Templates,
|
||||
ApiIDs: r.ApiIDs,
|
||||
MenuID: r.MenuID,
|
||||
ExportTemplateID: r.ExportTemplateID,
|
||||
}
|
||||
if entity.Table == "" {
|
||||
entity.Table = r.StructName
|
||||
}
|
||||
return entity
|
||||
}
|
||||
|
||||
type SysAutoHistoryRollBack struct {
|
||||
common.GetById
|
||||
DeleteApi bool `json:"deleteApi" form:"deleteApi"` // 是否删除接口
|
||||
DeleteMenu bool `json:"deleteMenu" form:"deleteMenu"` // 是否删除菜单
|
||||
DeleteTable bool `json:"deleteTable" form:"deleteTable"` // 是否删除表
|
||||
}
|
||||
|
||||
func (r *SysAutoHistoryRollBack) ApiIds(entity model.SysAutoCodeHistory) common.IdsReq {
|
||||
length := len(entity.ApiIDs)
|
||||
ids := make([]int, 0)
|
||||
for i := 0; i < length; i++ {
|
||||
ids = append(ids, int(entity.ApiIDs[i]))
|
||||
}
|
||||
return common.IdsReq{Ids: ids}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package request
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
|
||||
type SkillToolRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
}
|
||||
|
||||
type SkillDetailRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
}
|
||||
|
||||
type SkillDeleteRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
}
|
||||
|
||||
type SkillPackageRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
}
|
||||
|
||||
type SkillSaveRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
Meta system.SkillMeta `json:"meta"`
|
||||
Markdown string `json:"markdown"`
|
||||
SyncTools []string `json:"syncTools"`
|
||||
}
|
||||
|
||||
type SkillScriptCreateRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
FileName string `json:"fileName"`
|
||||
ScriptType string `json:"scriptType"`
|
||||
}
|
||||
|
||||
type SkillResourceCreateRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
FileName string `json:"fileName"`
|
||||
}
|
||||
|
||||
type SkillReferenceCreateRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
FileName string `json:"fileName"`
|
||||
}
|
||||
|
||||
type SkillTemplateCreateRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
FileName string `json:"fileName"`
|
||||
}
|
||||
|
||||
type SkillFileRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
FileName string `json:"fileName"`
|
||||
}
|
||||
|
||||
type SkillFileSaveRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
FileName string `json:"fileName"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type SkillGlobalConstraintSaveRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Content string `json:"content"`
|
||||
SyncTools []string `json:"syncTools"`
|
||||
}
|
||||
|
||||
type DownloadOnlineSkillReq struct {
|
||||
Tool string `json:"tool" binding:"required"`
|
||||
ID uint `json:"id" binding:"required"`
|
||||
Version string `json:"version" binding:"required"`
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package response
|
||||
|
||||
import "time"
|
||||
|
||||
type SysAIWorkflowSessionListItem struct {
|
||||
ID uint `json:"ID"`
|
||||
CreatedAt time.Time `json:"CreatedAt"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
Tab string `json:"tab"`
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
ConversationID string `json:"conversationId"`
|
||||
CurrentNodeID string `json:"currentNodeId"`
|
||||
}
|
||||
|
||||
type AIWorkflowMarkdownDumpResult struct {
|
||||
FileName string `json:"fileName"`
|
||||
FilePath string `json:"filePath"`
|
||||
RelativePath string `json:"relativePath"`
|
||||
Directory string `json:"directory"`
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package response
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
|
||||
type Db struct {
|
||||
Database string `json:"database" gorm:"column:database"`
|
||||
}
|
||||
|
||||
type Table struct {
|
||||
TableName string `json:"tableName" gorm:"column:table_name"`
|
||||
}
|
||||
|
||||
type Column struct {
|
||||
DataType string `json:"dataType" gorm:"column:data_type"`
|
||||
ColumnName string `json:"columnName" gorm:"column:column_name"`
|
||||
DataTypeLong string `json:"dataTypeLong" gorm:"column:data_type_long"`
|
||||
ColumnComment string `json:"columnComment" gorm:"column:column_comment"`
|
||||
PrimaryKey bool `json:"primaryKey" gorm:"column:primary_key"`
|
||||
}
|
||||
|
||||
type PluginInfo struct {
|
||||
PluginName string `json:"pluginName"`
|
||||
PluginType string `json:"pluginType"` // web, server, full
|
||||
Apis []system.SysApi `json:"apis"`
|
||||
Menus []system.SysBaseMenu `json:"menus"`
|
||||
Dictionaries []system.SysDictionary `json:"dictionaries"`
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
)
|
||||
|
||||
type AIWorkflowMessage struct {
|
||||
ID string `json:"id"`
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
Snapshot common.JSONMap `json:"snapshot"`
|
||||
ConversationID string `json:"conversationId"`
|
||||
MessageID string `json:"messageId"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
type SysAIWorkflowSession struct {
|
||||
global.GVA_MODEL
|
||||
UserID uint `json:"userId" gorm:"column:user_id;index;comment:用户ID"`
|
||||
Tab string `json:"tab" gorm:"column:tab;size:32;index;comment:会话类型"`
|
||||
Title string `json:"title" gorm:"column:title;size:255;comment:会话标题"`
|
||||
Summary string `json:"summary" gorm:"column:summary;type:text;comment:摘要"`
|
||||
ConversationID string `json:"conversationId" gorm:"column:conversation_id;size:255;comment:Dify会话ID"`
|
||||
MessageID string `json:"messageId" gorm:"column:message_id;size:255;comment:Dify消息ID"`
|
||||
CurrentNodeID string `json:"currentNodeId" gorm:"column:current_node_id;size:64;comment:当前选中节点ID"`
|
||||
Settings common.JSONMap `json:"settings" gorm:"column:settings;type:longtext;comment:页面设置"`
|
||||
FormData common.JSONMap `json:"formData" gorm:"column:form_data;type:longtext;comment:表单数据"`
|
||||
ResultData common.JSONMap `json:"resultData" gorm:"column:result_data;type:longtext;comment:当前展示结果"`
|
||||
Messages []AIWorkflowMessage `json:"messages" gorm:"column:messages;serializer:json;type:longtext;comment:会话消息"`
|
||||
}
|
||||
|
||||
func (s *SysAIWorkflowSession) TableName() string {
|
||||
return "sys_ai_workflow_sessions"
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SysAutoCodeHistory 自动迁移代码记录,用于回滚,重放使用
|
||||
type SysAutoCodeHistory struct {
|
||||
global.GVA_MODEL
|
||||
Table string `json:"tableName" gorm:"column:table_name;comment:表名"`
|
||||
Package string `json:"package" gorm:"column:package;comment:模块名/插件名"`
|
||||
Request string `json:"request" gorm:"type:text;column:request;comment:前端传入的结构化信息"`
|
||||
StructName string `json:"structName" gorm:"column:struct_name;comment:结构体名称"`
|
||||
Abbreviation string `json:"abbreviation" gorm:"column:abbreviation;comment:结构体名称缩写"`
|
||||
BusinessDB string `json:"businessDb" gorm:"column:business_db;comment:业务库"`
|
||||
Description string `json:"description" gorm:"column:description;comment:Struct中文名称"`
|
||||
Templates map[string]string `json:"template" gorm:"serializer:json;type:text;column:templates;comment:模板信息"`
|
||||
Injections map[string]string `json:"injections" gorm:"serializer:json;type:text;column:Injections;comment:注入路径"`
|
||||
Flag int `json:"flag" gorm:"column:flag;comment:[0:创建,1:回滚]"`
|
||||
ApiIDs []uint `json:"apiIDs" gorm:"serializer:json;column:api_ids;comment:api表注册内容"`
|
||||
MenuID uint `json:"menuId" gorm:"column:menu_id;comment:菜单ID"`
|
||||
ExportTemplateID uint `json:"exportTemplateID" gorm:"column:export_template_id;comment:导出模板ID"`
|
||||
AutoCodePackage SysAutoCodePackage `json:"autoCodePackage" gorm:"foreignKey:ID;references:PackageID"`
|
||||
PackageID uint `json:"packageID" gorm:"column:package_id;comment:包ID"`
|
||||
}
|
||||
|
||||
func (s *SysAutoCodeHistory) BeforeCreate(db *gorm.DB) error {
|
||||
templates := make(map[string]string, len(s.Templates))
|
||||
for key, value := range s.Templates {
|
||||
server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server)
|
||||
{
|
||||
hasServer := strings.Index(key, server)
|
||||
if hasServer != -1 {
|
||||
key = strings.TrimPrefix(key, server)
|
||||
keys := strings.Split(key, string(os.PathSeparator))
|
||||
key = path.Join(keys...)
|
||||
}
|
||||
} // key
|
||||
web := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot())
|
||||
hasWeb := strings.Index(value, web)
|
||||
if hasWeb != -1 {
|
||||
value = strings.TrimPrefix(value, web)
|
||||
values := strings.Split(value, string(os.PathSeparator))
|
||||
value = path.Join(values...)
|
||||
templates[key] = value
|
||||
continue
|
||||
}
|
||||
hasServer := strings.Index(value, server)
|
||||
if hasServer != -1 {
|
||||
value = strings.TrimPrefix(value, server)
|
||||
values := strings.Split(value, string(os.PathSeparator))
|
||||
value = path.Join(values...)
|
||||
templates[key] = value
|
||||
continue
|
||||
}
|
||||
}
|
||||
s.Templates = templates
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SysAutoCodeHistory) TableName() string {
|
||||
return "sys_auto_code_histories"
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
)
|
||||
|
||||
type SysAutoCodePackage struct {
|
||||
global.GVA_MODEL
|
||||
Desc string `json:"desc" gorm:"comment:描述"`
|
||||
Label string `json:"label" gorm:"comment:展示名"`
|
||||
Template string `json:"template" gorm:"comment:模版"`
|
||||
PackageName string `json:"packageName" gorm:"comment:包名"`
|
||||
Module string `json:"-" example:"模块"`
|
||||
}
|
||||
|
||||
func (s *SysAutoCodePackage) TableName() string {
|
||||
return "sys_auto_code_packages"
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package system
|
||||
|
||||
type SkillMeta struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Description string `json:"description" yaml:"description"`
|
||||
AllowedTools string `json:"allowedTools" yaml:"allowed-tools,omitempty"`
|
||||
Context string `json:"context" yaml:"context,omitempty"`
|
||||
Agent string `json:"agent" yaml:"agent,omitempty"`
|
||||
}
|
||||
|
||||
type SkillDetail struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
Meta SkillMeta `json:"meta"`
|
||||
Markdown string `json:"markdown"`
|
||||
Scripts []string `json:"scripts"`
|
||||
Resources []string `json:"resources"`
|
||||
References []string `json:"references"`
|
||||
Templates []string `json:"templates"`
|
||||
}
|
||||
|
||||
type SkillTool struct {
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package api
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/service"
|
||||
|
||||
var (
|
||||
Api = new(api)
|
||||
serviceInfo = service.Service.Info
|
||||
)
|
||||
|
||||
type api struct{ Info info }
|
||||
@@ -1,183 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var Info = new(info)
|
||||
|
||||
type info struct{}
|
||||
|
||||
// CreateInfo 创建公告
|
||||
// @Tags Info
|
||||
// @Summary 创建公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.Info true "创建公告"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /info/createInfo [post]
|
||||
func (a *info) CreateInfo(c *gin.Context) {
|
||||
var info model.Info
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = serviceInfo.CreateInfo(&info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteInfo 删除公告
|
||||
// @Tags Info
|
||||
// @Summary 删除公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.Info true "删除公告"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /info/deleteInfo [delete]
|
||||
func (a *info) DeleteInfo(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := serviceInfo.DeleteInfo(ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteInfoByIds 批量删除公告
|
||||
// @Tags Info
|
||||
// @Summary 批量删除公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /info/deleteInfoByIds [delete]
|
||||
func (a *info) DeleteInfoByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
if err := serviceInfo.DeleteInfoByIds(IDs); err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateInfo 更新公告
|
||||
// @Tags Info
|
||||
// @Summary 更新公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.Info true "更新公告"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /info/updateInfo [put]
|
||||
func (a *info) UpdateInfo(c *gin.Context) {
|
||||
var info model.Info
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = serviceInfo.UpdateInfo(info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindInfo 用id查询公告
|
||||
// @Tags Info
|
||||
// @Summary 用id查询公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query model.Info true "用id查询公告"
|
||||
// @Success 200 {object} response.Response{data=model.Info,msg=string} "查询成功"
|
||||
// @Router /info/findInfo [get]
|
||||
func (a *info) FindInfo(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
reinfo, err := serviceInfo.GetInfo(ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(reinfo, c)
|
||||
}
|
||||
|
||||
// GetInfoList 分页获取公告列表
|
||||
// @Tags Info
|
||||
// @Summary 分页获取公告列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.InfoSearch true "分页获取公告列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /info/getInfoList [get]
|
||||
func (a *info) GetInfoList(c *gin.Context) {
|
||||
var pageInfo request.InfoSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := serviceInfo.GetInfoInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetInfoDataSource 获取Info的数据源
|
||||
// @Tags Info
|
||||
// @Summary 获取Info的数据源
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "查询成功"
|
||||
// @Router /info/getInfoDataSource [get]
|
||||
func (a *info) GetInfoDataSource(c *gin.Context) {
|
||||
// 此接口为获取数据源定义的数据
|
||||
dataSource, err := serviceInfo.GetInfoDataSource()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(dataSource, c)
|
||||
}
|
||||
|
||||
// GetInfoPublic 不需要鉴权的公告接口
|
||||
// @Tags Info
|
||||
// @Summary 不需要鉴权的公告接口
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.InfoSearch true "分页获取公告列表"
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /info/getInfoPublic [get]
|
||||
func (a *info) GetInfoPublic(c *gin.Context) {
|
||||
// 此接口不需要鉴权 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑
|
||||
response.OkWithDetailed(gin.H{"info": "不需要鉴权的公告接口信息"}, "获取成功", c)
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package main
|
||||
|
||||
//go:generate go mod tidy
|
||||
//go:generate go mod download
|
||||
//go:generate go run gen.go
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model"
|
||||
"gorm.io/gen"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g := gen.NewGenerator(gen.Config{OutPath: filepath.Join("..", "..", "..", "announcement", "blender", "model", "dao"), Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface})
|
||||
g.ApplyBasic(
|
||||
new(model.Info),
|
||||
)
|
||||
g.Execute()
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils"
|
||||
)
|
||||
|
||||
func Api(ctx context.Context) {
|
||||
entities := []model.SysApi{
|
||||
{
|
||||
Path: "/info/createInfo",
|
||||
Description: "新建公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "POST",
|
||||
},
|
||||
{
|
||||
Path: "/info/deleteInfo",
|
||||
Description: "删除公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "DELETE",
|
||||
},
|
||||
{
|
||||
Path: "/info/deleteInfoByIds",
|
||||
Description: "批量删除公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "DELETE",
|
||||
},
|
||||
{
|
||||
Path: "/info/updateInfo",
|
||||
Description: "更新公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "PUT",
|
||||
},
|
||||
{
|
||||
Path: "/info/findInfo",
|
||||
Description: "根据ID获取公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "GET",
|
||||
},
|
||||
{
|
||||
Path: "/info/getInfoList",
|
||||
Description: "获取公告列表",
|
||||
ApiGroup: "公告",
|
||||
Method: "GET",
|
||||
},
|
||||
}
|
||||
utils.RegisterApis(entities...)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils"
|
||||
)
|
||||
|
||||
func Dictionary(ctx context.Context) {
|
||||
entities := []model.SysDictionary{}
|
||||
utils.RegisterDictionaries(entities...)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Gorm(ctx context.Context) {
|
||||
err := global.GVA_DB.WithContext(ctx).AutoMigrate(
|
||||
new(model.Info),
|
||||
)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "注册表失败!")
|
||||
zap.L().Error(fmt.Sprintf("%+v", err))
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils"
|
||||
)
|
||||
|
||||
func Menu(ctx context.Context) {
|
||||
entities := []model.SysBaseMenu{
|
||||
{
|
||||
ParentId: 9,
|
||||
Path: "anInfo",
|
||||
Name: "anInfo",
|
||||
Hidden: false,
|
||||
Component: "plugin/announcement/view/info.vue",
|
||||
Sort: 5,
|
||||
Meta: model.Meta{Title: "公告管理", Icon: "box"},
|
||||
},
|
||||
}
|
||||
utils.RegisterMenus(entities...)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/router"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Router(engine *gin.Engine) {
|
||||
public := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("")
|
||||
private := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("")
|
||||
private.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
|
||||
router.Router.Info.Init(public, private)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/plugin"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Viper() {
|
||||
err := global.GVA_VP.UnmarshalKey("announcement", &plugin.Config)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "初始化配置文件失败!")
|
||||
zap.L().Error(fmt.Sprintf("%+v", err))
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
// Info 公告 结构体
|
||||
type Info struct {
|
||||
global.GVA_MODEL
|
||||
Title string `json:"title" form:"title" gorm:"column:title;comment:公告标题;"` //标题
|
||||
Content string `json:"content" form:"content" gorm:"column:content;comment:公告内容;type:text;"` //内容
|
||||
UserID *int `json:"userID" form:"userID" gorm:"column:user_id;comment:发布者;"` //作者
|
||||
Attachments datatypes.JSON `json:"attachments" form:"attachments" gorm:"column:attachments;comment:相关附件;" swaggertype:"array,object"` //附件
|
||||
}
|
||||
|
||||
// TableName 公告 Info自定义表名 gva_announcements_info
|
||||
func (Info) TableName() string {
|
||||
return "gva_announcements_info"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"time"
|
||||
)
|
||||
|
||||
type InfoSearch struct {
|
||||
StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
request.PageInfo
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package announcement
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/initialize"
|
||||
interfaces "github.com/flipped-aurora/gin-vue-admin/server/utils/plugin/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var _ interfaces.Plugin = (*plugin)(nil)
|
||||
|
||||
var Plugin = new(plugin)
|
||||
|
||||
type plugin struct{}
|
||||
|
||||
func init() {
|
||||
interfaces.Register(Plugin)
|
||||
}
|
||||
|
||||
func (p *plugin) Register(group *gin.Engine) {
|
||||
ctx := context.Background()
|
||||
// 如果需要配置文件,请到config.Config中填充配置结构,且到下方发放中填入其在config.yaml中的key
|
||||
// initialize.Viper()
|
||||
// 安装插件时候自动注册的api数据请到下方法.Api方法中实现
|
||||
initialize.Api(ctx)
|
||||
// 安装插件时候自动注册的Menu数据请到下方法.Menu方法中实现
|
||||
initialize.Menu(ctx)
|
||||
// 安装插件时候自动注册的Dictionary数据请到下方法.Dictionary方法中实现
|
||||
initialize.Dictionary(ctx)
|
||||
initialize.Gorm(ctx)
|
||||
initialize.Router(group)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package plugin
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/config"
|
||||
|
||||
var Config config.Config
|
||||
@@ -1,10 +0,0 @@
|
||||
package router
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/api"
|
||||
|
||||
var (
|
||||
Router = new(router)
|
||||
apiInfo = api.Api.Info
|
||||
)
|
||||
|
||||
type router struct{ Info info }
|
||||
@@ -1,31 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var Info = new(info)
|
||||
|
||||
type info struct{}
|
||||
|
||||
// Init 初始化 公告 路由信息
|
||||
func (r *info) Init(public *gin.RouterGroup, private *gin.RouterGroup) {
|
||||
{
|
||||
group := private.Group("info").Use(middleware.OperationRecord())
|
||||
group.POST("createInfo", apiInfo.CreateInfo) // 新建公告
|
||||
group.DELETE("deleteInfo", apiInfo.DeleteInfo) // 删除公告
|
||||
group.DELETE("deleteInfoByIds", apiInfo.DeleteInfoByIds) // 批量删除公告
|
||||
group.PUT("updateInfo", apiInfo.UpdateInfo) // 更新公告
|
||||
}
|
||||
{
|
||||
group := private.Group("info")
|
||||
group.GET("findInfo", apiInfo.FindInfo) // 根据ID获取公告
|
||||
group.GET("getInfoList", apiInfo.GetInfoList) // 获取公告列表
|
||||
}
|
||||
{
|
||||
group := public.Group("info")
|
||||
group.GET("getInfoDataSource", apiInfo.GetInfoDataSource) // 获取公告数据源
|
||||
group.GET("getInfoPublic", apiInfo.GetInfoPublic) // 获取公告列表
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package service
|
||||
|
||||
var Service = new(service)
|
||||
|
||||
type service struct{ Info info }
|
||||
@@ -1,78 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model/request"
|
||||
)
|
||||
|
||||
var Info = new(info)
|
||||
|
||||
type info struct{}
|
||||
|
||||
// CreateInfo 创建公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) CreateInfo(info *model.Info) (err error) {
|
||||
err = global.GVA_DB.Create(info).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteInfo 删除公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) DeleteInfo(ID string) (err error) {
|
||||
err = global.GVA_DB.Delete(&model.Info{}, "id = ?", ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteInfoByIds 批量删除公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) DeleteInfoByIds(IDs []string) (err error) {
|
||||
err = global.GVA_DB.Delete(&[]model.Info{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateInfo 更新公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) UpdateInfo(info model.Info) (err error) {
|
||||
err = global.GVA_DB.Model(&model.Info{}).Where("id = ?", info.ID).Updates(&info).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetInfo 根据ID获取公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) GetInfo(ID string) (info model.Info, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", ID).First(&info).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetInfoInfoList 分页获取公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) GetInfoInfoList(info request.InfoSearch) (list []model.Info, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.Model(&model.Info{})
|
||||
var infos []model.Info
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
err = db.Find(&infos).Error
|
||||
return infos, total, err
|
||||
}
|
||||
func (s *info) GetInfoDataSource() (res map[string][]map[string]any, err error) {
|
||||
res = make(map[string][]map[string]any)
|
||||
|
||||
userID := make([]map[string]any, 0)
|
||||
global.GVA_DB.Table("sys_users").Select("nick_name as label,id as value").Scan(&userID)
|
||||
res["userID"] = userID
|
||||
return
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
## GVA 邮件发送功能插件
|
||||
#### 开发者:GIN-VUE-ADMIN 官方
|
||||
|
||||
### 使用步骤
|
||||
|
||||
#### 1. 前往GVA主程序下的initialize/router.go 在Routers 方法最末尾按照你需要的及安全模式添加本插件
|
||||
例:
|
||||
本插件可以采用gva的配置文件 也可以直接写死内容作为配置 建议为gva添加配置文件结构 然后将配置传入
|
||||
PluginInit(PrivateGroup, email.CreateEmailPlug(
|
||||
global.GVA_CONFIG.Email.To,
|
||||
global.GVA_CONFIG.Email.From,
|
||||
global.GVA_CONFIG.Email.Host,
|
||||
global.GVA_CONFIG.Email.Secret,
|
||||
global.GVA_CONFIG.Email.Nickname,
|
||||
global.GVA_CONFIG.Email.Port,
|
||||
global.GVA_CONFIG.Email.IsSSL,
|
||||
global.GVA_CONFIG.Email.IsLoginAuth,
|
||||
))
|
||||
|
||||
同样也可以再传入时写死
|
||||
|
||||
PluginInit(PrivateGroup, email.CreateEmailPlug(
|
||||
"a@qq.com",
|
||||
"b@qq.com",
|
||||
"smtp.qq.com",
|
||||
"global.GVA_CONFIG.Email.Secret",
|
||||
"登录密钥",
|
||||
465,
|
||||
true,
|
||||
true,
|
||||
))
|
||||
|
||||
### 2. 配置说明
|
||||
|
||||
#### 2-1 全局配置结构体说明
|
||||
//其中 Form 和 Secret 通常来说就是用户名和密码
|
||||
|
||||
type Email struct {
|
||||
To string // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 此处配置主要用于发送错误监控邮件
|
||||
From string // 发件人 你自己要发邮件的邮箱
|
||||
Host string // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
|
||||
Secret string // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
|
||||
Nickname string // 昵称 发件人昵称 自定义即可 可以不填
|
||||
Port int // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
|
||||
IsSSL bool // 是否SSL 是否开启SSL
|
||||
IsLoginAuth bool // 是否LoginAuth 是否使用LoginAuth认证方式(适用于IBM、微软邮箱服务器等)
|
||||
}
|
||||
#### 2-2 入参结构说明
|
||||
//其中 Form 和 Secret 通常来说就是用户名和密码
|
||||
|
||||
type Email struct {
|
||||
To string `json:"to"` // 邮件发送给谁
|
||||
Subject string `json:"subject"` // 邮件标题
|
||||
Body string `json:"body"` // 邮件内容
|
||||
}
|
||||
|
||||
|
||||
### 3. 方法API
|
||||
|
||||
utils.EmailTest(邮件标题,邮件主体) 发送测试邮件
|
||||
例:utils.EmailTest("测试邮件","测试邮件")
|
||||
utils.ErrorToEmail(邮件标题,邮件主体) 错误监控
|
||||
例:utils.ErrorToEmail("测试邮件","测试邮件")
|
||||
utils.Email(目标邮箱多个的话用逗号分隔,邮件标题,邮件主体) 发送测试邮件
|
||||
例:utils.Email(”a.qq.com,b.qq.com“,"测试邮件","测试邮件")
|
||||
|
||||
### 4. 可直接调用的接口
|
||||
|
||||
测试接口: /email/emailTest [post] 已配置swagger
|
||||
|
||||
发送邮件接口接口: /email/emailSend [post] 已配置swagger
|
||||
入参:
|
||||
type Email struct {
|
||||
To string `json:"to"` // 邮件发送给谁
|
||||
Subject string `json:"subject"` // 邮件标题
|
||||
Body string `json:"body"` // 邮件内容
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package api
|
||||
|
||||
type ApiGroup struct {
|
||||
EmailApi
|
||||
}
|
||||
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
@@ -1,53 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
email_response "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/model/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type EmailApi struct{}
|
||||
|
||||
// EmailTest
|
||||
// @Tags System
|
||||
// @Summary 发送测试邮件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
|
||||
// @Router /email/emailTest [post]
|
||||
func (s *EmailApi) EmailTest(c *gin.Context) {
|
||||
err := service.ServiceGroupApp.EmailTest()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("发送失败!", zap.Error(err))
|
||||
response.FailWithMessage("发送失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("发送成功", c)
|
||||
}
|
||||
|
||||
// SendEmail
|
||||
// @Tags System
|
||||
// @Summary 发送邮件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body email_response.Email true "发送邮件必须的参数"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
|
||||
// @Router /email/sendEmail [post]
|
||||
func (s *EmailApi) SendEmail(c *gin.Context) {
|
||||
var email email_response.Email
|
||||
err := c.ShouldBindJSON(&email)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = service.ServiceGroupApp.SendEmail(email.To, email.Subject, email.Body)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("发送失败!", zap.Error(err))
|
||||
response.FailWithMessage("发送失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("发送成功", c)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package config
|
||||
|
||||
type Email struct {
|
||||
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
|
||||
From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱
|
||||
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
|
||||
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
|
||||
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱
|
||||
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
|
||||
IsSSL bool `mapstructure:"is-ssl" json:"isSSL" yaml:"is-ssl"` // 是否SSL 是否开启SSL
|
||||
IsLoginAuth bool `mapstructure:"is-loginauth" json:"is-loginauth" yaml:"is-loginauth"` // 是否LoginAuth 是否使用LoginAuth认证
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package global
|
||||
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/config"
|
||||
|
||||
var GlobalConfig = new(config.Email)
|
||||
@@ -1,29 +0,0 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email/router"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type emailPlugin struct{}
|
||||
|
||||
func CreateEmailPlug(To, From, Host, Secret, Nickname string, Port int, IsSSL bool, IsLoginAuth bool) *emailPlugin {
|
||||
global.GlobalConfig.To = To
|
||||
global.GlobalConfig.From = From
|
||||
global.GlobalConfig.Host = Host
|
||||
global.GlobalConfig.Secret = Secret
|
||||
global.GlobalConfig.Nickname = Nickname
|
||||
global.GlobalConfig.Port = Port
|
||||
global.GlobalConfig.IsSSL = IsSSL
|
||||
global.GlobalConfig.IsLoginAuth = IsLoginAuth
|
||||
return &emailPlugin{}
|
||||
}
|
||||
|
||||
func (*emailPlugin) Register(group *gin.RouterGroup) {
|
||||
router.RouterGroupApp.InitEmailRouter(group)
|
||||
}
|
||||
|
||||
func (*emailPlugin) RouterPath() string {
|
||||
return "email"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package response
|
||||
|
||||
type Email struct {
|
||||
To string `json:"to"` // 邮件发送给谁
|
||||
Subject string `json:"subject"` // 邮件标题
|
||||
Body string `json:"body"` // 邮件内容
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package router
|
||||
|
||||
type RouterGroup struct {
|
||||
EmailRouter
|
||||
}
|
||||
|
||||
var RouterGroupApp = new(RouterGroup)
|
||||
@@ -1,19 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type EmailRouter struct{}
|
||||
|
||||
func (s *EmailRouter) InitEmailRouter(Router *gin.RouterGroup) {
|
||||
emailRouter := Router.Use(middleware.OperationRecord())
|
||||
EmailApi := api.ApiGroupApp.EmailApi.EmailTest
|
||||
SendEmail := api.ApiGroupApp.EmailApi.SendEmail
|
||||
{
|
||||
emailRouter.POST("emailTest", EmailApi) // 发送测试邮件
|
||||
emailRouter.POST("sendEmail", SendEmail) // 发送邮件
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package service
|
||||
|
||||
type ServiceGroup struct {
|
||||
EmailService
|
||||
}
|
||||
|
||||
var ServiceGroupApp = new(ServiceGroup)
|
||||
@@ -1,32 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email/utils"
|
||||
)
|
||||
|
||||
type EmailService struct{}
|
||||
|
||||
//@author: [maplepie](https://github.com/maplepie)
|
||||
//@function: EmailTest
|
||||
//@description: 发送邮件测试
|
||||
//@return: err error
|
||||
|
||||
func (e *EmailService) EmailTest() (err error) {
|
||||
subject := "test"
|
||||
body := "test"
|
||||
err = utils.EmailTest(subject, body)
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [maplepie](https://github.com/maplepie)
|
||||
//@function: EmailTest
|
||||
//@description: 发送邮件测试
|
||||
//@return: err error
|
||||
//@params to string 收件人
|
||||
//@params subject string 标题(主题)
|
||||
//@params body string 邮件内容
|
||||
|
||||
func (e *EmailService) SendEmail(to, subject, body string) (err error) {
|
||||
err = utils.Email(to, subject, body)
|
||||
return err
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email/global"
|
||||
|
||||
"github.com/jordan-wright/email"
|
||||
)
|
||||
|
||||
//@author: [maplepie](https://github.com/maplepie)
|
||||
//@function: Email
|
||||
//@description: Email发送方法
|
||||
//@param: subject string, body string
|
||||
//@return: error
|
||||
|
||||
func Email(To, subject string, body string) error {
|
||||
to := strings.Split(To, ",")
|
||||
return send(to, subject, body)
|
||||
}
|
||||
|
||||
//@author: [SliverHorn](https://github.com/SliverHorn)
|
||||
//@function: ErrorToEmail
|
||||
//@description: 给email中间件错误发送邮件到指定邮箱
|
||||
//@param: subject string, body string
|
||||
//@return: error
|
||||
|
||||
func ErrorToEmail(subject string, body string) error {
|
||||
to := strings.Split(global.GlobalConfig.To, ",")
|
||||
if to[len(to)-1] == "" { // 判断切片的最后一个元素是否为空,为空则移除
|
||||
to = to[:len(to)-1]
|
||||
}
|
||||
return send(to, subject, body)
|
||||
}
|
||||
|
||||
//@author: [maplepie](https://github.com/maplepie)
|
||||
//@function: EmailTest
|
||||
//@description: Email测试方法
|
||||
//@param: subject string, body string
|
||||
//@return: error
|
||||
|
||||
func EmailTest(subject string, body string) error {
|
||||
to := []string{global.GlobalConfig.To}
|
||||
return send(to, subject, body)
|
||||
}
|
||||
|
||||
//@author: [maplepie](https://github.com/maplepie)
|
||||
//@function: send
|
||||
//@description: Email发送方法
|
||||
//@param: subject string, body string
|
||||
//@return: error
|
||||
|
||||
func send(to []string, subject string, body string) error {
|
||||
from := global.GlobalConfig.From
|
||||
nickname := global.GlobalConfig.Nickname
|
||||
secret := global.GlobalConfig.Secret
|
||||
host := global.GlobalConfig.Host
|
||||
port := global.GlobalConfig.Port
|
||||
isSSL := global.GlobalConfig.IsSSL
|
||||
isLoginAuth := global.GlobalConfig.IsLoginAuth
|
||||
|
||||
var auth smtp.Auth
|
||||
if isLoginAuth {
|
||||
auth = LoginAuth(from, secret)
|
||||
} else {
|
||||
auth = smtp.PlainAuth("", from, secret, host)
|
||||
}
|
||||
e := email.NewEmail()
|
||||
if nickname != "" {
|
||||
e.From = fmt.Sprintf("%s <%s>", nickname, from)
|
||||
} else {
|
||||
e.From = from
|
||||
}
|
||||
e.To = to
|
||||
e.Subject = subject
|
||||
e.HTML = []byte(body)
|
||||
var err error
|
||||
hostAddr := fmt.Sprintf("%s:%d", host, port)
|
||||
if isSSL {
|
||||
err = e.SendWithTLS(hostAddr, auth, &tls.Config{ServerName: host})
|
||||
} else {
|
||||
err = e.Send(hostAddr, auth)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// LoginAuth 用于IBM、微软邮箱服务器的LOGIN认证方式
|
||||
type loginAuth struct {
|
||||
username, password string
|
||||
}
|
||||
|
||||
func LoginAuth(username, password string) smtp.Auth {
|
||||
return &loginAuth{username, password}
|
||||
}
|
||||
|
||||
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||
return "LOGIN", []byte{}, nil
|
||||
}
|
||||
|
||||
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
if more {
|
||||
switch string(fromServer) {
|
||||
case "Username:":
|
||||
return []byte(a.username), nil
|
||||
case "Password:":
|
||||
return []byte(a.password), nil
|
||||
default:
|
||||
// 邮箱服务器可能发送的其他提示信息
|
||||
prompt := strings.ToLower(string(fromServer))
|
||||
if strings.Contains(prompt, "username") || strings.Contains(prompt, "user") {
|
||||
return []byte(a.username), nil
|
||||
}
|
||||
if strings.Contains(prompt, "password") || strings.Contains(prompt, "pass") {
|
||||
return []byte(a.password), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
)
|
||||
|
||||
var (
|
||||
ApiMap = make(map[string][]system.SysApi)
|
||||
MenuMap = make(map[string][]system.SysBaseMenu)
|
||||
DictMap = make(map[string][]system.SysDictionary)
|
||||
rw sync.Mutex
|
||||
)
|
||||
|
||||
func getPluginName() string {
|
||||
_, file, _, ok := runtime.Caller(2)
|
||||
pluginName := ""
|
||||
if ok {
|
||||
file = filepath.ToSlash(file)
|
||||
const key = "server/plugin/"
|
||||
if idx := strings.Index(file, key); idx != -1 {
|
||||
remain := file[idx+len(key):]
|
||||
parts := strings.Split(remain, "/")
|
||||
if len(parts) > 0 {
|
||||
pluginName = parts[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return pluginName
|
||||
}
|
||||
|
||||
func RegisterApis(apis ...system.SysApi) {
|
||||
name := getPluginName()
|
||||
if name != "" {
|
||||
rw.Lock()
|
||||
ApiMap[name] = apis
|
||||
rw.Unlock()
|
||||
}
|
||||
|
||||
err := global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
for _, api := range apis {
|
||||
err := tx.Model(system.SysApi{}).Where("path = ? AND method = ? AND api_group = ? ", api.Path, api.Method, api.ApiGroup).FirstOrCreate(&api).Error
|
||||
if err != nil {
|
||||
zap.L().Error("注册API失败", zap.Error(err), zap.String("api", api.Path), zap.String("method", api.Method), zap.String("apiGroup", api.ApiGroup))
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
zap.L().Error("注册API失败", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterMenus(menus ...system.SysBaseMenu) {
|
||||
name := getPluginName()
|
||||
if name != "" {
|
||||
rw.Lock()
|
||||
MenuMap[name] = menus
|
||||
rw.Unlock()
|
||||
}
|
||||
|
||||
parentMenu := menus[0]
|
||||
otherMenus := menus[1:]
|
||||
err := global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
err := tx.Model(system.SysBaseMenu{}).Where("name = ? ", parentMenu.Name).FirstOrCreate(&parentMenu).Error
|
||||
if err != nil {
|
||||
zap.L().Error("注册菜单失败", zap.Error(err))
|
||||
return errors.Wrap(err, "注册菜单失败")
|
||||
}
|
||||
pid := parentMenu.ID
|
||||
for i := range otherMenus {
|
||||
otherMenus[i].ParentId = pid
|
||||
err = tx.Model(system.SysBaseMenu{}).Where("name = ? ", otherMenus[i].Name).FirstOrCreate(&otherMenus[i]).Error
|
||||
if err != nil {
|
||||
zap.L().Error("注册菜单失败", zap.Error(err))
|
||||
return errors.Wrap(err, "注册菜单失败")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
zap.L().Error("注册菜单失败", zap.Error(err))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func RegisterDictionaries(dictionaries ...system.SysDictionary) {
|
||||
name := getPluginName()
|
||||
if name != "" {
|
||||
rw.Lock()
|
||||
DictMap[name] = dictionaries
|
||||
rw.Unlock()
|
||||
}
|
||||
|
||||
err := global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
for _, dict := range dictionaries {
|
||||
details := dict.SysDictionaryDetails
|
||||
dict.SysDictionaryDetails = nil
|
||||
err := tx.Model(system.SysDictionary{}).Where("type = ?", dict.Type).FirstOrCreate(&dict).Error
|
||||
if err != nil {
|
||||
zap.L().Error("注册字典失败", zap.Error(err), zap.String("type", dict.Type))
|
||||
return err
|
||||
}
|
||||
for _, detail := range details {
|
||||
detail.SysDictionaryID = int(dict.ID)
|
||||
err = tx.Model(system.SysDictionaryDetail{}).Where("sys_dictionary_id = ? AND value = ?", dict.ID, detail.Value).FirstOrCreate(&detail).Error
|
||||
if err != nil {
|
||||
zap.L().Error("注册字典详情失败", zap.Error(err), zap.String("value", detail.Value))
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
zap.L().Error("注册字典失败", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func Pointer[T any](in T) *T {
|
||||
return &in
|
||||
}
|
||||
|
||||
func GetPluginData(pluginName string) ([]system.SysApi, []system.SysBaseMenu, []system.SysDictionary) {
|
||||
rw.Lock()
|
||||
defer rw.Unlock()
|
||||
return ApiMap[pluginName], MenuMap[pluginName], DictMap[pluginName]
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
_ "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement"
|
||||
)
|
||||
@@ -1,26 +1,3 @@
|
||||
{{if .IsPlugin}}
|
||||
// {{.FuncName}} {{.FuncDesc}}
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary {{.FuncDesc}}
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /{{.Abbreviation}}/{{.Router}} [{{.Method}}]
|
||||
func (a *{{.Abbreviation}}) {{.FuncName}}(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
// 请添加自己的业务逻辑
|
||||
err := service{{ .StructName }}.{{.FuncName}}(ctx)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("失败!", zap.Error(err))
|
||||
response.FailWithMessage("失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData("返回数据",c)
|
||||
}
|
||||
|
||||
{{- else -}}
|
||||
|
||||
// {{.FuncName}} {{.FuncDesc}}
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary {{.FuncDesc}}
|
||||
@@ -41,4 +18,3 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api){{.FuncName}}(c *gin.Context) {
|
||||
}
|
||||
response.OkWithData("返回数据",c)
|
||||
}
|
||||
{{end}}
|
||||
|
||||
@@ -1,20 +1,3 @@
|
||||
{{if .IsPlugin}}
|
||||
// {{.FuncName}} {{.FuncDesc}}
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary {{.FuncDesc}}
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /{{.Abbreviation}}/{{.Router}} [{{.Method}}]
|
||||
export const {{.Router}} = () => {
|
||||
return service({
|
||||
url: '/{{.Abbreviation}}/{{.Router}}',
|
||||
method: '{{.Method}}'
|
||||
})
|
||||
}
|
||||
|
||||
{{- else -}}
|
||||
|
||||
// {{.FuncName}} {{.FuncDesc}}
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary {{.FuncDesc}}
|
||||
@@ -28,5 +11,3 @@ export const {{.Router}} = () => {
|
||||
method: '{{.Method}}'
|
||||
})
|
||||
}
|
||||
|
||||
{{- end -}}
|
||||
|
||||
@@ -4,16 +4,6 @@
|
||||
{{- else}}
|
||||
{{- $db = printf "global.MustGetGlobalDBByDBName(\"%s\")" .BusinessDB }}
|
||||
{{- end}}
|
||||
{{if .IsPlugin}}
|
||||
|
||||
// {{.FuncName}} {{.FuncDesc}}
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (s *{{.Abbreviation}}) {{.FuncName}}(ctx context.Context) (err error) {
|
||||
db := {{$db}}.Model(&model.{{.StructName}}{})
|
||||
return db.Error
|
||||
}
|
||||
|
||||
{{- else -}}
|
||||
|
||||
// {{.FuncName}} {{.FuncDesc}}
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
@@ -22,4 +12,3 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service){{.FuncName}}(ctx context
|
||||
db := {{$db}}.Model(&{{.Package}}.{{.StructName}}{})
|
||||
return db.Error
|
||||
}
|
||||
{{end}}
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
{{if not .OnlyTemplate}}
|
||||
"{{.Module}}/global"
|
||||
"{{.Module}}/model/common/response"
|
||||
"{{.Module}}/plugin/{{.Package}}/model"
|
||||
{{- if not .IsTree}}
|
||||
"{{.Module}}/plugin/{{.Package}}/model/request"
|
||||
{{- end }}
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
{{- if .AutoCreateResource}}
|
||||
"{{.Module}}/utils"
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
"{{.Module}}/model/common/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
var {{.StructName}} = new({{.Abbreviation}})
|
||||
|
||||
type {{.Abbreviation}} struct {}
|
||||
{{if not .OnlyTemplate}}
|
||||
// Create{{.StructName}} 创建{{.Description}}
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary 创建{{.Description}}
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.{{.StructName}} true "创建{{.Description}}"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /{{.Abbreviation}}/create{{.StructName}} [post]
|
||||
func (a *{{.Abbreviation}}) Create{{.StructName}}(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var info model.{{.StructName}}
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
{{- if .AutoCreateResource }}
|
||||
info.CreatedBy = utils.GetUserID(c)
|
||||
{{- end }}
|
||||
err = service{{ .StructName }}.Create{{.StructName}}(ctx,&info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败:" + err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// Delete{{.StructName}} 删除{{.Description}}
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary 删除{{.Description}}
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.{{.StructName}} true "删除{{.Description}}"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete]
|
||||
func (a *{{.Abbreviation}}) Delete{{.StructName}}(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
{{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}")
|
||||
{{- if .AutoCreateResource }}
|
||||
userID := utils.GetUserID(c)
|
||||
{{- end }}
|
||||
err := service{{ .StructName }}.Delete{{.StructName}}(ctx,{{.PrimaryField.FieldJson}} {{- if .AutoCreateResource -}},userID{{- end -}})
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败:" + err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// Delete{{.StructName}}ByIds 批量删除{{.Description}}
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary 批量删除{{.Description}}
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /{{.Abbreviation}}/delete{{.StructName}}ByIds [delete]
|
||||
func (a *{{.Abbreviation}}) Delete{{.StructName}}ByIds(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
{{.PrimaryField.FieldJson}}s := c.QueryArray("{{.PrimaryField.FieldJson}}s[]")
|
||||
{{- if .AutoCreateResource }}
|
||||
userID := utils.GetUserID(c)
|
||||
{{- end }}
|
||||
err := service{{ .StructName }}.Delete{{.StructName}}ByIds(ctx,{{.PrimaryField.FieldJson}}s{{- if .AutoCreateResource }},userID{{- end }})
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败:" + err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// Update{{.StructName}} 更新{{.Description}}
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary 更新{{.Description}}
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.{{.StructName}} true "更新{{.Description}}"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /{{.Abbreviation}}/update{{.StructName}} [put]
|
||||
func (a *{{.Abbreviation}}) Update{{.StructName}}(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var info model.{{.StructName}}
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
{{- if .AutoCreateResource }}
|
||||
info.UpdatedBy = utils.GetUserID(c)
|
||||
{{- end }}
|
||||
err = service{{ .StructName }}.Update{{.StructName}}(ctx,info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败:" + err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// Find{{.StructName}} 用id查询{{.Description}}
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary 用id查询{{.Description}}
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param {{.PrimaryField.FieldJson}} query {{.PrimaryField.FieldType}} true "用id查询{{.Description}}"
|
||||
// @Success 200 {object} response.Response{data=model.{{.StructName}},msg=string} "查询成功"
|
||||
// @Router /{{.Abbreviation}}/find{{.StructName}} [get]
|
||||
func (a *{{.Abbreviation}}) Find{{.StructName}}(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
{{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}")
|
||||
re{{.Abbreviation}}, err := service{{ .StructName }}.Get{{.StructName}}(ctx,{{.PrimaryField.FieldJson}})
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败:" + err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(re{{.Abbreviation}}, c)
|
||||
}
|
||||
|
||||
{{- if .IsTree }}
|
||||
// Get{{.StructName}}List 分页获取{{.Description}}列表
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary 分页获取{{.Description}}列表
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /{{.Abbreviation}}/get{{.StructName}}List [get]
|
||||
func (a *{{.Abbreviation}}) Get{{.StructName}}List(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
list, err := service{{ .StructName }}.Get{{.StructName}}InfoList(ctx)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败:" + err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(list, "获取成功", c)
|
||||
}
|
||||
{{- else }}
|
||||
// Get{{.StructName}}List 分页获取{{.Description}}列表
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary 分页获取{{.Description}}列表
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.{{.StructName}}Search true "分页获取{{.Description}}列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /{{.Abbreviation}}/get{{.StructName}}List [get]
|
||||
func (a *{{.Abbreviation}}) Get{{.StructName}}List(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var pageInfo request.{{.StructName}}Search
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := service{{ .StructName }}.Get{{.StructName}}InfoList(ctx,pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败:" + err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{- if .HasDataSource }}
|
||||
// Get{{.StructName}}DataSource 获取{{.StructName}}的数据源
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary 获取{{.StructName}}的数据源
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "查询成功"
|
||||
// @Router /{{.Abbreviation}}/get{{.StructName}}DataSource [get]
|
||||
func (a *{{.Abbreviation}}) Get{{.StructName}}DataSource(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 此接口为获取数据源定义的数据
|
||||
dataSource, err := service{{ .StructName }}.Get{{.StructName}}DataSource(ctx)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败:" + err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(dataSource, c)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
// Get{{.StructName}}Public 不需要鉴权的{{.Description}}接口
|
||||
// @Tags {{.StructName}}
|
||||
// @Summary 不需要鉴权的{{.Description}}接口
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /{{.Abbreviation}}/get{{.StructName}}Public [get]
|
||||
func (a *{{.Abbreviation}}) Get{{.StructName}}Public(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 此接口不需要鉴权 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑
|
||||
service{{ .StructName }}.Get{{.StructName}}Public(ctx)
|
||||
response.OkWithDetailed(gin.H{"info": "不需要鉴权的{{.Description}}接口信息"}, "获取成功", c)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package api
|
||||
|
||||
var Api = new(api)
|
||||
|
||||
type api struct {
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gorm.io/gen"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
//go:generate go mod tidy
|
||||
//go:generate go mod download
|
||||
//go:generate go run gen.go
|
||||
func main() {
|
||||
g := gen.NewGenerator(gen.Config{
|
||||
OutPath: filepath.Join("..", "..", "..", "{{ .Package }}", "blender", "model", "dao"),
|
||||
Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface,
|
||||
})
|
||||
g.ApplyBasic()
|
||||
g.Execute()
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user