From 7599146f243d2d2be7ec596f1aae1faa520e7cc6 Mon Sep 17 00:00:00 2001 From: Eg <1711788888@qq.com> Date: Wed, 8 Apr 2026 12:19:24 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- server/api/v1/common/enter.go | 13 + .../exa_attachment_category.go | 10 +- .../exa_breakpoint_continue.go | 19 +- .../exa_file_upload_download.go | 28 +- server/api/v1/enter.go | 6 +- server/api/v1/example/enter.go | 17 - server/api/v1/example/exa_customer.go | 176 ---- server/api/v1/system/ai_workflow_session.go | 102 --- server/api/v1/system/auto_code_history.go | 115 --- server/api/v1/system/auto_code_package.go | 100 --- server/api/v1/system/auto_code_plugin.go | 218 ----- server/api/v1/system/auto_code_template.go | 121 --- server/api/v1/system/enter.go | 52 +- .../v1/system/{auto_code_mcp.go => mcp.go} | 32 +- server/api/v1/system/sys_auto_code.go | 219 ----- server/api/v1/system/sys_auto_code_sse.go | 210 ----- server/api/v1/system/sys_skills.go | 263 ------ server/cmd/mcp/config.go | 67 +- server/config/auto_code.go | 22 - server/config/config.go | 2 - server/core/server.go | 2 - server/core/viper.go | 3 - server/initialize/ensure_tables.go | 26 +- server/initialize/gorm.go | 13 +- server/initialize/other.go | 9 - server/initialize/plugin.go | 15 - server/initialize/plugin_biz_v1.go | 36 - server/initialize/plugin_biz_v2.go | 16 - server/initialize/register_init.go | 2 +- server/initialize/router.go | 52 +- server/initialize/router_biz.go | 1 + server/mcp/api_lister.go | 15 +- server/mcp/autocode_http.go | 48 -- server/mcp/gva_analyze.go | 494 ----------- server/mcp/gva_execute.go | 750 ----------------- server/mcp/gva_review.go | 170 ---- server/mcp/requirement_analyzer.go | 199 ----- server/mcp/standalone_manager.go | 19 +- server/middleware/email.go | 58 -- .../exa_attachment_category.go | 2 +- .../exa_breakpoint_continue.go | 2 +- .../exa_file_upload_download.go | 2 +- .../request/exa_file_upload_and_downloads.go | 6 +- .../response/exa_breakpoint_continue.go | 4 +- .../response/exa_file_upload_download.go | 7 + server/model/example/exa_customer.go | 15 - server/model/example/response/exa_customer.go | 7 - .../response/exa_file_upload_download.go | 7 - .../request/{sys_auto_code_mcp.go => mcp.go} | 6 +- .../system/request/sys_ai_workflow_session.go | 40 - server/model/system/request/sys_auto_code.go | 291 ------- .../system/request/sys_auto_code_package.go | 31 - .../model/system/request/sys_auto_history.go | 57 -- server/model/system/request/sys_skills.go | 80 -- .../response/sys_ai_workflow_session.go | 21 - server/model/system/response/sys_auto_code.go | 27 - .../model/system/sys_ai_workflow_session.go | 35 - server/model/system/sys_auto_code_history.go | 68 -- server/model/system/sys_auto_code_package.go | 18 - server/model/system/sys_skills.go | 25 - server/plugin/announcement/api/enter.go | 10 - server/plugin/announcement/api/info.go | 183 ---- server/plugin/announcement/config/config.go | 4 - server/plugin/announcement/gen/gen.go | 19 - server/plugin/announcement/initialize/api.go | 49 -- .../announcement/initialize/dictionary.go | 12 - server/plugin/announcement/initialize/gorm.go | 20 - server/plugin/announcement/initialize/menu.go | 22 - .../plugin/announcement/initialize/router.go | 15 - .../plugin/announcement/initialize/viper.go | 17 - server/plugin/announcement/model/info.go | 20 - .../plugin/announcement/model/request/info.go | 12 - server/plugin/announcement/plugin.go | 32 - server/plugin/announcement/plugin/plugin.go | 5 - server/plugin/announcement/router/enter.go | 10 - server/plugin/announcement/router/info.go | 31 - server/plugin/announcement/service/enter.go | 5 - server/plugin/announcement/service/info.go | 78 -- server/plugin/email/README.MD | 78 -- server/plugin/email/api/enter.go | 7 - server/plugin/email/api/sys_email.go | 53 -- server/plugin/email/config/email.go | 12 - server/plugin/email/global/gloabl.go | 5 - server/plugin/email/main.go | 29 - server/plugin/email/model/response/email.go | 7 - server/plugin/email/router/enter.go | 7 - server/plugin/email/router/sys_email.go | 19 - server/plugin/email/service/enter.go | 7 - server/plugin/email/service/sys_email.go | 32 - server/plugin/email/utils/email.go | 122 --- server/plugin/plugin-tool/utils/check.go | 138 --- server/plugin/register.go | 5 - server/resource/function/api.go.tpl | 24 - server/resource/function/api.js.tpl | 19 - server/resource/function/server.go.tpl | 11 - server/resource/plugin/server/api/api.go.tpl | 255 ------ .../resource/plugin/server/api/enter.go.tpl | 6 - .../plugin/server/config/config.go.tpl | 4 - server/resource/plugin/server/gen/gen.go.tpl | 18 - .../plugin/server/initialize/api.go.tpl | 12 - .../server/initialize/dictionary.go.tpl | 12 - .../plugin/server/initialize/gorm.go.tpl | 17 - .../plugin/server/initialize/menu.go.tpl | 12 - .../plugin/server/initialize/router.go.tpl | 14 - .../plugin/server/initialize/viper.go.tpl | 17 - .../resource/plugin/server/model/model.go.tpl | 76 -- .../server/model/request/request.go.tpl | 38 - server/resource/plugin/server/plugin.go.tpl | 33 - .../plugin/server/plugin/plugin.go.tpl | 5 - .../plugin/server/router/enter.go.tpl | 6 - .../plugin/server/router/router.go.tpl | 46 - .../plugin/server/service/enter.go.tpl | 7 - .../plugin/server/service/service.go.tpl | 211 ----- server/resource/plugin/web/api/api.js.tpl | 127 --- server/resource/plugin/web/form/form.vue.tpl | 464 ----------- server/resource/plugin/web/view/view.vue.tpl | 689 --------------- server/router/common/enter.go | 15 + .../exa_attachment_category.go | 2 +- .../exa_file_upload_and_download.go | 2 +- server/router/enter.go | 6 +- server/router/example/enter.go | 19 - server/router/example/exa_customer.go | 22 - server/router/system/enter.go | 43 +- server/router/system/mcp.go | 17 + server/router/system/sys_auto_code.go | 46 - server/router/system/sys_auto_code_history.go | 17 - server/router/system/sys_skills.go | 35 - server/service/{example => common}/enter.go | 4 +- .../service/common/exa_attachment_category.go | 66 ++ .../exa_breakpoint_continue.go | 14 +- .../exa_file_upload_download.go | 34 +- server/service/enter.go | 6 +- .../example/exa_attachment_category.go | 66 -- server/service/example/exa_customer.go | 87 -- server/service/system/ai_workflow_markdown.go | 468 ----------- server/service/system/ai_workflow_session.go | 184 ---- server/service/system/auto_code_history.go | 217 ----- server/service/system/auto_code_llm.go | 125 --- server/service/system/auto_code_mcp.go | 45 - server/service/system/auto_code_package.go | 743 ----------------- .../service/system/auto_code_package_test.go | 108 --- server/service/system/auto_code_plugin.go | 512 ------------ server/service/system/auto_code_service.go | 3 - server/service/system/auto_code_template.go | 453 ---------- .../service/system/auto_code_template_test.go | 84 -- server/service/system/enter.go | 8 +- server/service/system/mcp.go | 93 +++ .../service/system/sys_auto_code_interface.go | 55 -- server/service/system/sys_auto_code_mssql.go | 83 -- server/service/system/sys_auto_code_mysql.go | 83 -- server/service/system/sys_auto_code_oracle.go | 72 -- server/service/system/sys_auto_code_pgsql.go | 135 --- server/service/system/sys_auto_code_sqlite.go | 84 -- server/service/system/sys_error.go | 32 +- server/service/system/sys_initdb_mssql.go | 2 - server/service/system/sys_initdb_mysql.go | 2 - server/service/system/sys_initdb_pgsql.go | 2 - server/service/system/sys_initdb_sqlite.go | 2 - server/service/system/sys_skills.go | 785 ------------------ .../file_upload_download.go | 16 +- server/source/system/api.go | 60 +- server/source/system/api_ignore.go | 2 - server/source/system/casbin.go | 83 +- server/source/system/menu.go | 23 +- server/utils/ast/ast_rollback.go | 5 +- server/utils/ast/ast_type.go | 16 - server/utils/ast/interfaces_base.go | 26 +- server/utils/ast/plugin_enter.go | 167 ---- server/utils/ast/plugin_gen.go | 189 ----- server/utils/ast/plugin_initialize_gorm.go | 111 --- server/utils/ast/plugin_initialize_router.go | 124 --- server/utils/ast/plugin_initialize_v2.go | 82 -- server/utils/autocode/template_funcs.go | 713 ---------------- server/utils/plugin/plugin.go | 18 - server/utils/plugin/v2/plugin.go | 11 - server/utils/plugin/v2/registry.go | 27 - server/utils/verify.go | 3 - web-admin/docs/system-inventory.md | 30 +- web-admin/src/App.tsx | 21 +- .../src/features/discovery/systemInventory.ts | 94 +-- .../features/menus/menuComponentCatalog.ts | 49 +- web-admin/src/lib/api.ts | 65 ++ .../router/pages/example/customer/page.tsx | 7 - web-admin/src/router/pages/example/page.tsx | 7 - .../src/router/pages/plugin/anInfo/page.tsx | 7 - .../pages/plugin/installPlugin/page.tsx | 7 - web-admin/src/router/pages/plugin/page.tsx | 7 - .../router/pages/plugin/plugin-email/page.tsx | 7 - .../src/router/pages/plugin/pubPlug/page.tsx | 7 - .../pages/systemTools/aiWorkflow/page.tsx | 7 - web-admin/src/types/system.ts | 73 ++ 192 files changed, 623 insertions(+), 13983 deletions(-) create mode 100644 server/api/v1/common/enter.go rename server/api/v1/{example => common}/exa_attachment_category.go (84%) rename server/api/v1/{example => common}/exa_breakpoint_continue.go (87%) rename server/api/v1/{example => common}/exa_file_upload_download.go (78%) delete mode 100644 server/api/v1/example/enter.go delete mode 100644 server/api/v1/example/exa_customer.go delete mode 100644 server/api/v1/system/ai_workflow_session.go delete mode 100644 server/api/v1/system/auto_code_history.go delete mode 100644 server/api/v1/system/auto_code_package.go delete mode 100644 server/api/v1/system/auto_code_plugin.go delete mode 100644 server/api/v1/system/auto_code_template.go rename server/api/v1/system/{auto_code_mcp.go => mcp.go} (80%) delete mode 100644 server/api/v1/system/sys_auto_code.go delete mode 100644 server/api/v1/system/sys_auto_code_sse.go delete mode 100644 server/api/v1/system/sys_skills.go delete mode 100644 server/config/auto_code.go delete mode 100644 server/initialize/plugin.go delete mode 100644 server/initialize/plugin_biz_v1.go delete mode 100644 server/initialize/plugin_biz_v2.go delete mode 100644 server/mcp/autocode_http.go delete mode 100644 server/mcp/gva_analyze.go delete mode 100644 server/mcp/gva_execute.go delete mode 100644 server/mcp/gva_review.go delete mode 100644 server/mcp/requirement_analyzer.go delete mode 100644 server/middleware/email.go rename server/model/{example => common}/exa_attachment_category.go (97%) rename server/model/{example => common}/exa_breakpoint_continue.go (96%) rename server/model/{example => common}/exa_file_upload_download.go (98%) rename server/model/{example => common}/request/exa_file_upload_and_downloads.go (51%) rename server/model/{example => common}/response/exa_breakpoint_continue.go (52%) create mode 100644 server/model/common/response/exa_file_upload_download.go delete mode 100644 server/model/example/exa_customer.go delete mode 100644 server/model/example/response/exa_customer.go delete mode 100644 server/model/example/response/exa_file_upload_download.go rename server/model/system/request/{sys_auto_code_mcp.go => mcp.go} (80%) delete mode 100644 server/model/system/request/sys_ai_workflow_session.go delete mode 100644 server/model/system/request/sys_auto_code.go delete mode 100644 server/model/system/request/sys_auto_code_package.go delete mode 100644 server/model/system/request/sys_auto_history.go delete mode 100644 server/model/system/request/sys_skills.go delete mode 100644 server/model/system/response/sys_ai_workflow_session.go delete mode 100644 server/model/system/response/sys_auto_code.go delete mode 100644 server/model/system/sys_ai_workflow_session.go delete mode 100644 server/model/system/sys_auto_code_history.go delete mode 100644 server/model/system/sys_auto_code_package.go delete mode 100644 server/model/system/sys_skills.go delete mode 100644 server/plugin/announcement/api/enter.go delete mode 100644 server/plugin/announcement/api/info.go delete mode 100644 server/plugin/announcement/config/config.go delete mode 100644 server/plugin/announcement/gen/gen.go delete mode 100644 server/plugin/announcement/initialize/api.go delete mode 100644 server/plugin/announcement/initialize/dictionary.go delete mode 100644 server/plugin/announcement/initialize/gorm.go delete mode 100644 server/plugin/announcement/initialize/menu.go delete mode 100644 server/plugin/announcement/initialize/router.go delete mode 100644 server/plugin/announcement/initialize/viper.go delete mode 100644 server/plugin/announcement/model/info.go delete mode 100644 server/plugin/announcement/model/request/info.go delete mode 100644 server/plugin/announcement/plugin.go delete mode 100644 server/plugin/announcement/plugin/plugin.go delete mode 100644 server/plugin/announcement/router/enter.go delete mode 100644 server/plugin/announcement/router/info.go delete mode 100644 server/plugin/announcement/service/enter.go delete mode 100644 server/plugin/announcement/service/info.go delete mode 100644 server/plugin/email/README.MD delete mode 100644 server/plugin/email/api/enter.go delete mode 100644 server/plugin/email/api/sys_email.go delete mode 100644 server/plugin/email/config/email.go delete mode 100644 server/plugin/email/global/gloabl.go delete mode 100644 server/plugin/email/main.go delete mode 100644 server/plugin/email/model/response/email.go delete mode 100644 server/plugin/email/router/enter.go delete mode 100644 server/plugin/email/router/sys_email.go delete mode 100644 server/plugin/email/service/enter.go delete mode 100644 server/plugin/email/service/sys_email.go delete mode 100644 server/plugin/email/utils/email.go delete mode 100644 server/plugin/plugin-tool/utils/check.go delete mode 100644 server/plugin/register.go delete mode 100644 server/resource/plugin/server/api/api.go.tpl delete mode 100644 server/resource/plugin/server/api/enter.go.tpl delete mode 100644 server/resource/plugin/server/config/config.go.tpl delete mode 100644 server/resource/plugin/server/gen/gen.go.tpl delete mode 100644 server/resource/plugin/server/initialize/api.go.tpl delete mode 100644 server/resource/plugin/server/initialize/dictionary.go.tpl delete mode 100644 server/resource/plugin/server/initialize/gorm.go.tpl delete mode 100644 server/resource/plugin/server/initialize/menu.go.tpl delete mode 100644 server/resource/plugin/server/initialize/router.go.tpl delete mode 100644 server/resource/plugin/server/initialize/viper.go.tpl delete mode 100644 server/resource/plugin/server/model/model.go.tpl delete mode 100644 server/resource/plugin/server/model/request/request.go.tpl delete mode 100644 server/resource/plugin/server/plugin.go.tpl delete mode 100644 server/resource/plugin/server/plugin/plugin.go.tpl delete mode 100644 server/resource/plugin/server/router/enter.go.tpl delete mode 100644 server/resource/plugin/server/router/router.go.tpl delete mode 100644 server/resource/plugin/server/service/enter.go.tpl delete mode 100644 server/resource/plugin/server/service/service.go.tpl delete mode 100644 server/resource/plugin/web/api/api.js.tpl delete mode 100644 server/resource/plugin/web/form/form.vue.tpl delete mode 100644 server/resource/plugin/web/view/view.vue.tpl create mode 100644 server/router/common/enter.go rename server/router/{example => common}/exa_attachment_category.go (96%) rename server/router/{example => common}/exa_file_upload_and_download.go (98%) delete mode 100644 server/router/example/enter.go delete mode 100644 server/router/example/exa_customer.go create mode 100644 server/router/system/mcp.go delete mode 100644 server/router/system/sys_auto_code.go delete mode 100644 server/router/system/sys_auto_code_history.go delete mode 100644 server/router/system/sys_skills.go rename server/service/{example => common}/enter.go (71%) create mode 100644 server/service/common/exa_attachment_category.go rename server/service/{example => common}/exa_breakpoint_continue.go (88%) rename server/service/{example => common}/exa_file_upload_download.go (70%) delete mode 100644 server/service/example/exa_attachment_category.go delete mode 100644 server/service/example/exa_customer.go delete mode 100644 server/service/system/ai_workflow_markdown.go delete mode 100644 server/service/system/ai_workflow_session.go delete mode 100644 server/service/system/auto_code_history.go delete mode 100644 server/service/system/auto_code_llm.go delete mode 100644 server/service/system/auto_code_mcp.go delete mode 100644 server/service/system/auto_code_package.go delete mode 100644 server/service/system/auto_code_package_test.go delete mode 100644 server/service/system/auto_code_plugin.go delete mode 100644 server/service/system/auto_code_service.go delete mode 100644 server/service/system/auto_code_template.go delete mode 100644 server/service/system/auto_code_template_test.go create mode 100644 server/service/system/mcp.go delete mode 100644 server/service/system/sys_auto_code_interface.go delete mode 100644 server/service/system/sys_auto_code_mssql.go delete mode 100644 server/service/system/sys_auto_code_mysql.go delete mode 100644 server/service/system/sys_auto_code_oracle.go delete mode 100644 server/service/system/sys_auto_code_pgsql.go delete mode 100644 server/service/system/sys_auto_code_sqlite.go delete mode 100644 server/service/system/sys_skills.go rename server/source/{example => common}/file_upload_download.go (72%) delete mode 100644 server/utils/ast/plugin_enter.go delete mode 100644 server/utils/ast/plugin_gen.go delete mode 100644 server/utils/ast/plugin_initialize_gorm.go delete mode 100644 server/utils/ast/plugin_initialize_router.go delete mode 100644 server/utils/ast/plugin_initialize_v2.go delete mode 100644 server/utils/autocode/template_funcs.go delete mode 100644 server/utils/plugin/plugin.go delete mode 100644 server/utils/plugin/v2/plugin.go delete mode 100644 server/utils/plugin/v2/registry.go delete mode 100644 web-admin/src/router/pages/example/customer/page.tsx delete mode 100644 web-admin/src/router/pages/example/page.tsx delete mode 100644 web-admin/src/router/pages/plugin/anInfo/page.tsx delete mode 100644 web-admin/src/router/pages/plugin/installPlugin/page.tsx delete mode 100644 web-admin/src/router/pages/plugin/page.tsx delete mode 100644 web-admin/src/router/pages/plugin/plugin-email/page.tsx delete mode 100644 web-admin/src/router/pages/plugin/pubPlug/page.tsx delete mode 100644 web-admin/src/router/pages/systemTools/aiWorkflow/page.tsx diff --git a/.gitignore b/.gitignore index eb2854d..f655248 100644 --- a/.gitignore +++ b/.gitignore @@ -72,7 +72,7 @@ log uploads upload server/config.yaml - +dist # ---> AI .claude .codex diff --git a/server/api/v1/common/enter.go b/server/api/v1/common/enter.go new file mode 100644 index 0000000..1537608 --- /dev/null +++ b/server/api/v1/common/enter.go @@ -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 +) diff --git a/server/api/v1/example/exa_attachment_category.go b/server/api/v1/common/exa_attachment_category.go similarity index 84% rename from server/api/v1/example/exa_attachment_category.go rename to server/api/v1/common/exa_attachment_category.go index 0436f78..ee4da81 100644 --- a/server/api/v1/example/exa_attachment_category.go +++ b/server/api/v1/common/exa_attachment_category.go @@ -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) diff --git a/server/api/v1/example/exa_breakpoint_continue.go b/server/api/v1/common/exa_breakpoint_continue.go similarity index 87% rename from server/api/v1/example/exa_breakpoint_continue.go rename to server/api/v1/common/exa_breakpoint_continue.go index 565ffe6..6012c78 100644 --- a/server/api/v1/example/exa_breakpoint_continue.go +++ b/server/api/v1/common/exa_breakpoint_continue.go @@ -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) diff --git a/server/api/v1/example/exa_file_upload_download.go b/server/api/v1/common/exa_file_upload_download.go similarity index 78% rename from server/api/v1/example/exa_file_upload_download.go rename to server/api/v1/common/exa_file_upload_download.go index 9497c6d..aa3d778 100644 --- a/server/api/v1/example/exa_file_upload_download.go +++ b/server/api/v1/common/exa_file_upload_download.go @@ -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) diff --git a/server/api/v1/enter.go b/server/api/v1/enter.go index 5c1dff4..47292f2 100644 --- a/server/api/v1/enter.go +++ b/server/api/v1/enter.go @@ -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 } diff --git a/server/api/v1/example/enter.go b/server/api/v1/example/enter.go deleted file mode 100644 index 4d753ec..0000000 --- a/server/api/v1/example/enter.go +++ /dev/null @@ -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 -) diff --git a/server/api/v1/example/exa_customer.go b/server/api/v1/example/exa_customer.go deleted file mode 100644 index 5d9ef1c..0000000 --- a/server/api/v1/example/exa_customer.go +++ /dev/null @@ -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) -} diff --git a/server/api/v1/system/ai_workflow_session.go b/server/api/v1/system/ai_workflow_session.go deleted file mode 100644 index 037fa8c..0000000 --- a/server/api/v1/system/ai_workflow_session.go +++ /dev/null @@ -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) -} diff --git a/server/api/v1/system/auto_code_history.go b/server/api/v1/system/auto_code_history.go deleted file mode 100644 index 065ddd8..0000000 --- a/server/api/v1/system/auto_code_history.go +++ /dev/null @@ -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) -} diff --git a/server/api/v1/system/auto_code_package.go b/server/api/v1/system/auto_code_package.go deleted file mode 100644 index 655f29a..0000000 --- a/server/api/v1/system/auto_code_package.go +++ /dev/null @@ -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) -} diff --git a/server/api/v1/system/auto_code_plugin.go b/server/api/v1/system/auto_code_plugin.go deleted file mode 100644 index c751b8b..0000000 --- a/server/api/v1/system/auto_code_plugin.go +++ /dev/null @@ -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) -} diff --git a/server/api/v1/system/auto_code_template.go b/server/api/v1/system/auto_code_template.go deleted file mode 100644 index 0f6041c..0000000 --- a/server/api/v1/system/auto_code_template.go +++ /dev/null @@ -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) - } -} diff --git a/server/api/v1/system/enter.go b/server/api/v1/system/enter.go index aceac88..d77fc00 100644 --- a/server/api/v1/system/enter.go +++ b/server/api/v1/system/enter.go @@ -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 ) diff --git a/server/api/v1/system/auto_code_mcp.go b/server/api/v1/system/mcp.go similarity index 80% rename from server/api/v1/system/auto_code_mcp.go rename to server/api/v1/system/mcp.go index 671b8d5..7eb597d 100644 --- a/server/api/v1/system/auto_code_mcp.go +++ b/server/api/v1/system/mcp.go @@ -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 diff --git a/server/api/v1/system/sys_auto_code.go b/server/api/v1/system/sys_auto_code.go deleted file mode 100644 index e0505d5..0000000 --- a/server/api/v1/system/sys_auto_code.go +++ /dev/null @@ -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 "" - } - runes := []rune(text) - if len(runes) > 300 { - return string(runes[:300]) + "..." - } - return text -} diff --git a/server/api/v1/system/sys_auto_code_sse.go b/server/api/v1/system/sys_auto_code_sse.go deleted file mode 100644 index 1acb96d..0000000 --- a/server/api/v1/system/sys_auto_code_sse.go +++ /dev/null @@ -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 -} diff --git a/server/api/v1/system/sys_skills.go b/server/api/v1/system/sys_skills.go deleted file mode 100644 index b0f24f5..0000000 --- a/server/api/v1/system/sys_skills.go +++ /dev/null @@ -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) -} diff --git a/server/cmd/mcp/config.go b/server/cmd/mcp/config.go index cc1d906..dd644c4 100644 --- a/server/cmd/mcp/config.go +++ b/server/cmd/mcp/config.go @@ -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 { diff --git a/server/config/auto_code.go b/server/config/auto_code.go deleted file mode 100644 index ade79a0..0000000 --- a/server/config/auto_code.go +++ /dev/null @@ -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...) -} diff --git a/server/config/config.go b/server/config/config.go index 3abac5a..f3cd1bb 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -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"` diff --git a/server/core/server.go b/server/core/server.go index 6c36f33..84a7f07 100644 --- a/server/core/server.go +++ b/server/core/server.go @@ -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 diff --git a/server/core/viper.go b/server/core/viper.go index d846c90..48b23e3 100644 --- a/server/core/viper.go +++ b/server/core/viper.go @@ -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 } diff --git a/server/initialize/ensure_tables.go b/server/initialize/ensure_tables.go index 817b542..3f78d66 100644 --- a/server/initialize/ensure_tables.go +++ b/server/initialize/ensure_tables.go @@ -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 { diff --git a/server/initialize/gorm.go b/server/initialize/gorm.go index b06f0f4..8546470 100644 --- a/server/initialize/gorm.go +++ b/server/initialize/gorm.go @@ -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)) diff --git a/server/initialize/other.go b/server/initialize/other.go index f272a81..5d23aeb 100644 --- a/server/initialize/other.go +++ b/server/initialize/other.go @@ -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 ") - } } diff --git a/server/initialize/plugin.go b/server/initialize/plugin.go deleted file mode 100644 index 87ac748..0000000 --- a/server/initialize/plugin.go +++ /dev/null @@ -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 -} diff --git a/server/initialize/plugin_biz_v1.go b/server/initialize/plugin_biz_v1.go deleted file mode 100644 index 1585db3..0000000 --- a/server/initialize/plugin_biz_v1.go +++ /dev/null @@ -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) -} diff --git a/server/initialize/plugin_biz_v2.go b/server/initialize/plugin_biz_v2.go deleted file mode 100644 index bde58fa..0000000 --- a/server/initialize/plugin_biz_v2.go +++ /dev/null @@ -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()...) -} diff --git a/server/initialize/register_init.go b/server/initialize/register_init.go index a249661..850e09e 100644 --- a/server/initialize/register_init.go +++ b/server/initialize/register_init.go @@ -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" ) diff --git a/server/initialize/router.go b/server/initialize/router.go index 48e885e..af1d9b6 100644 --- a/server/initialize/router.go +++ b/server/initialize/router.go @@ -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) diff --git a/server/initialize/router_biz.go b/server/initialize/router_biz.go index 279127d..99ad27c 100644 --- a/server/initialize/router_biz.go +++ b/server/initialize/router_biz.go @@ -16,4 +16,5 @@ func initBizRouter(routers ...*gin.RouterGroup) { publicGroup := routers[1] holder(publicGroup, privateGroup) + } diff --git a/server/mcp/api_lister.go b/server/mcp/api_lister.go index 0357a61..cdc3624 100644 --- a/server/mcp/api_lister.go +++ b/server/mcp/api_lister.go @@ -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, diff --git a/server/mcp/autocode_http.go b/server/mcp/autocode_http.go deleted file mode 100644 index 7e66dcb..0000000 --- a/server/mcp/autocode_http.go +++ /dev/null @@ -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 -} diff --git a/server/mcp/gva_analyze.go b/server/mcp/gva_analyze.go deleted file mode 100644 index 6b5f74f..0000000 --- a/server/mcp/gva_analyze.go +++ /dev/null @@ -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 -} diff --git a/server/mcp/gva_execute.go b/server/mcp/gva_execute.go deleted file mode 100644 index c44de47..0000000 --- a/server/mcp/gva_execute.go +++ /dev/null @@ -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, "") -} diff --git a/server/mcp/gva_review.go b/server/mcp/gva_review.go deleted file mode 100644 index a32a544..0000000 --- a/server/mcp/gva_review.go +++ /dev/null @@ -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() -} diff --git a/server/mcp/requirement_analyzer.go b/server/mcp/requirement_analyzer.go deleted file mode 100644 index 765b750..0000000 --- a/server/mcp/requirement_analyzer.go +++ /dev/null @@ -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 -} diff --git a/server/mcp/standalone_manager.go b/server/mcp/standalone_manager.go index a7ab497..3be32b0 100644 --- a/server/mcp/standalone_manager.go +++ b/server/mcp/standalone_manager.go @@ -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 "." diff --git a/server/middleware/email.go b/server/middleware/email.go deleted file mode 100644 index 1bc976c..0000000 --- a/server/middleware/email.go +++ /dev/null @@ -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)) - } - } - } -} diff --git a/server/model/example/exa_attachment_category.go b/server/model/common/exa_attachment_category.go similarity index 97% rename from server/model/example/exa_attachment_category.go rename to server/model/common/exa_attachment_category.go index a643b61..0162594 100644 --- a/server/model/example/exa_attachment_category.go +++ b/server/model/common/exa_attachment_category.go @@ -1,4 +1,4 @@ -package example +package common import ( "github.com/flipped-aurora/gin-vue-admin/server/global" diff --git a/server/model/example/exa_breakpoint_continue.go b/server/model/common/exa_breakpoint_continue.go similarity index 96% rename from server/model/example/exa_breakpoint_continue.go rename to server/model/common/exa_breakpoint_continue.go index 3c2924b..d500fa0 100644 --- a/server/model/example/exa_breakpoint_continue.go +++ b/server/model/common/exa_breakpoint_continue.go @@ -1,4 +1,4 @@ -package example +package common import ( "github.com/flipped-aurora/gin-vue-admin/server/global" diff --git a/server/model/example/exa_file_upload_download.go b/server/model/common/exa_file_upload_download.go similarity index 98% rename from server/model/example/exa_file_upload_download.go rename to server/model/common/exa_file_upload_download.go index 4fe342d..49a665e 100644 --- a/server/model/example/exa_file_upload_download.go +++ b/server/model/common/exa_file_upload_download.go @@ -1,4 +1,4 @@ -package example +package common import ( "github.com/flipped-aurora/gin-vue-admin/server/global" diff --git a/server/model/example/request/exa_file_upload_and_downloads.go b/server/model/common/request/exa_file_upload_and_downloads.go similarity index 51% rename from server/model/example/request/exa_file_upload_and_downloads.go rename to server/model/common/request/exa_file_upload_and_downloads.go index b9ff684..d64b2ed 100644 --- a/server/model/example/request/exa_file_upload_and_downloads.go +++ b/server/model/common/request/exa_file_upload_and_downloads.go @@ -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 } diff --git a/server/model/example/response/exa_breakpoint_continue.go b/server/model/common/response/exa_breakpoint_continue.go similarity index 52% rename from server/model/example/response/exa_breakpoint_continue.go rename to server/model/common/response/exa_breakpoint_continue.go index 54aa351..241bfdf 100644 --- a/server/model/example/response/exa_breakpoint_continue.go +++ b/server/model/common/response/exa_breakpoint_continue.go @@ -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"` } diff --git a/server/model/common/response/exa_file_upload_download.go b/server/model/common/response/exa_file_upload_download.go new file mode 100644 index 0000000..a9d64bd --- /dev/null +++ b/server/model/common/response/exa_file_upload_download.go @@ -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"` +} diff --git a/server/model/example/exa_customer.go b/server/model/example/exa_customer.go deleted file mode 100644 index e78dd09..0000000 --- a/server/model/example/exa_customer.go +++ /dev/null @@ -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:管理详情"` // 管理详情 -} diff --git a/server/model/example/response/exa_customer.go b/server/model/example/response/exa_customer.go deleted file mode 100644 index 7fd26f9..0000000 --- a/server/model/example/response/exa_customer.go +++ /dev/null @@ -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"` -} diff --git a/server/model/example/response/exa_file_upload_download.go b/server/model/example/response/exa_file_upload_download.go deleted file mode 100644 index c1b7931..0000000 --- a/server/model/example/response/exa_file_upload_download.go +++ /dev/null @@ -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"` -} diff --git a/server/model/system/request/sys_auto_code_mcp.go b/server/model/system/request/mcp.go similarity index 80% rename from server/model/system/request/sys_auto_code_mcp.go rename to server/model/system/request/mcp.go index a52ec7c..9d9e2a7 100644 --- a/server/model/system/request/sys_auto_code_mcp.go +++ b/server/model/system/request/mcp.go @@ -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"` } diff --git a/server/model/system/request/sys_ai_workflow_session.go b/server/model/system/request/sys_ai_workflow_session.go deleted file mode 100644 index 6f0c5ed..0000000 --- a/server/model/system/request/sys_ai_workflow_session.go +++ /dev/null @@ -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"` -} diff --git a/server/model/system/request/sys_auto_code.go b/server/model/system/request/sys_auto_code.go deleted file mode 100644 index 1990fdc..0000000 --- a/server/model/system/request/sys_auto_code.go +++ /dev/null @@ -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;"` //模式 -} diff --git a/server/model/system/request/sys_auto_code_package.go b/server/model/system/request/sys_auto_code_package.go deleted file mode 100644 index 679303a..0000000 --- a/server/model/system/request/sys_auto_code_package.go +++ /dev/null @@ -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, - } -} diff --git a/server/model/system/request/sys_auto_history.go b/server/model/system/request/sys_auto_history.go deleted file mode 100644 index e9e3243..0000000 --- a/server/model/system/request/sys_auto_history.go +++ /dev/null @@ -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} -} diff --git a/server/model/system/request/sys_skills.go b/server/model/system/request/sys_skills.go deleted file mode 100644 index 773605f..0000000 --- a/server/model/system/request/sys_skills.go +++ /dev/null @@ -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"` -} diff --git a/server/model/system/response/sys_ai_workflow_session.go b/server/model/system/response/sys_ai_workflow_session.go deleted file mode 100644 index 09eba73..0000000 --- a/server/model/system/response/sys_ai_workflow_session.go +++ /dev/null @@ -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"` -} diff --git a/server/model/system/response/sys_auto_code.go b/server/model/system/response/sys_auto_code.go deleted file mode 100644 index e1f1d8b..0000000 --- a/server/model/system/response/sys_auto_code.go +++ /dev/null @@ -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"` -} diff --git a/server/model/system/sys_ai_workflow_session.go b/server/model/system/sys_ai_workflow_session.go deleted file mode 100644 index 6b7236a..0000000 --- a/server/model/system/sys_ai_workflow_session.go +++ /dev/null @@ -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" -} diff --git a/server/model/system/sys_auto_code_history.go b/server/model/system/sys_auto_code_history.go deleted file mode 100644 index e41d553..0000000 --- a/server/model/system/sys_auto_code_history.go +++ /dev/null @@ -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" -} diff --git a/server/model/system/sys_auto_code_package.go b/server/model/system/sys_auto_code_package.go deleted file mode 100644 index 4099192..0000000 --- a/server/model/system/sys_auto_code_package.go +++ /dev/null @@ -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" -} diff --git a/server/model/system/sys_skills.go b/server/model/system/sys_skills.go deleted file mode 100644 index e7013f6..0000000 --- a/server/model/system/sys_skills.go +++ /dev/null @@ -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"` -} diff --git a/server/plugin/announcement/api/enter.go b/server/plugin/announcement/api/enter.go deleted file mode 100644 index 7fee6fc..0000000 --- a/server/plugin/announcement/api/enter.go +++ /dev/null @@ -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 } diff --git a/server/plugin/announcement/api/info.go b/server/plugin/announcement/api/info.go deleted file mode 100644 index dd0faa3..0000000 --- a/server/plugin/announcement/api/info.go +++ /dev/null @@ -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) -} diff --git a/server/plugin/announcement/config/config.go b/server/plugin/announcement/config/config.go deleted file mode 100644 index 809bc99..0000000 --- a/server/plugin/announcement/config/config.go +++ /dev/null @@ -1,4 +0,0 @@ -package config - -type Config struct { -} diff --git a/server/plugin/announcement/gen/gen.go b/server/plugin/announcement/gen/gen.go deleted file mode 100644 index d09800c..0000000 --- a/server/plugin/announcement/gen/gen.go +++ /dev/null @@ -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() -} diff --git a/server/plugin/announcement/initialize/api.go b/server/plugin/announcement/initialize/api.go deleted file mode 100644 index 6d0fed1..0000000 --- a/server/plugin/announcement/initialize/api.go +++ /dev/null @@ -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...) -} diff --git a/server/plugin/announcement/initialize/dictionary.go b/server/plugin/announcement/initialize/dictionary.go deleted file mode 100644 index 8ab340a..0000000 --- a/server/plugin/announcement/initialize/dictionary.go +++ /dev/null @@ -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...) -} diff --git a/server/plugin/announcement/initialize/gorm.go b/server/plugin/announcement/initialize/gorm.go deleted file mode 100644 index 3a88ff2..0000000 --- a/server/plugin/announcement/initialize/gorm.go +++ /dev/null @@ -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)) - } -} diff --git a/server/plugin/announcement/initialize/menu.go b/server/plugin/announcement/initialize/menu.go deleted file mode 100644 index 58144bc..0000000 --- a/server/plugin/announcement/initialize/menu.go +++ /dev/null @@ -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...) -} diff --git a/server/plugin/announcement/initialize/router.go b/server/plugin/announcement/initialize/router.go deleted file mode 100644 index e2c4f17..0000000 --- a/server/plugin/announcement/initialize/router.go +++ /dev/null @@ -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) -} diff --git a/server/plugin/announcement/initialize/viper.go b/server/plugin/announcement/initialize/viper.go deleted file mode 100644 index 68cfff6..0000000 --- a/server/plugin/announcement/initialize/viper.go +++ /dev/null @@ -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)) - } -} diff --git a/server/plugin/announcement/model/info.go b/server/plugin/announcement/model/info.go deleted file mode 100644 index 42d62fe..0000000 --- a/server/plugin/announcement/model/info.go +++ /dev/null @@ -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" -} diff --git a/server/plugin/announcement/model/request/info.go b/server/plugin/announcement/model/request/info.go deleted file mode 100644 index 35be3e0..0000000 --- a/server/plugin/announcement/model/request/info.go +++ /dev/null @@ -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 -} diff --git a/server/plugin/announcement/plugin.go b/server/plugin/announcement/plugin.go deleted file mode 100644 index 68e2464..0000000 --- a/server/plugin/announcement/plugin.go +++ /dev/null @@ -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) -} diff --git a/server/plugin/announcement/plugin/plugin.go b/server/plugin/announcement/plugin/plugin.go deleted file mode 100644 index 4058239..0000000 --- a/server/plugin/announcement/plugin/plugin.go +++ /dev/null @@ -1,5 +0,0 @@ -package plugin - -import "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/config" - -var Config config.Config diff --git a/server/plugin/announcement/router/enter.go b/server/plugin/announcement/router/enter.go deleted file mode 100644 index 543e0ff..0000000 --- a/server/plugin/announcement/router/enter.go +++ /dev/null @@ -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 } diff --git a/server/plugin/announcement/router/info.go b/server/plugin/announcement/router/info.go deleted file mode 100644 index 8de316b..0000000 --- a/server/plugin/announcement/router/info.go +++ /dev/null @@ -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) // 获取公告列表 - } -} diff --git a/server/plugin/announcement/service/enter.go b/server/plugin/announcement/service/enter.go deleted file mode 100644 index 988fbcd..0000000 --- a/server/plugin/announcement/service/enter.go +++ /dev/null @@ -1,5 +0,0 @@ -package service - -var Service = new(service) - -type service struct{ Info info } diff --git a/server/plugin/announcement/service/info.go b/server/plugin/announcement/service/info.go deleted file mode 100644 index b521553..0000000 --- a/server/plugin/announcement/service/info.go +++ /dev/null @@ -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 -} diff --git a/server/plugin/email/README.MD b/server/plugin/email/README.MD deleted file mode 100644 index 685cdd6..0000000 --- a/server/plugin/email/README.MD +++ /dev/null @@ -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"` // 邮件内容 - } - diff --git a/server/plugin/email/api/enter.go b/server/plugin/email/api/enter.go deleted file mode 100644 index 353404d..0000000 --- a/server/plugin/email/api/enter.go +++ /dev/null @@ -1,7 +0,0 @@ -package api - -type ApiGroup struct { - EmailApi -} - -var ApiGroupApp = new(ApiGroup) diff --git a/server/plugin/email/api/sys_email.go b/server/plugin/email/api/sys_email.go deleted file mode 100644 index fdc76ab..0000000 --- a/server/plugin/email/api/sys_email.go +++ /dev/null @@ -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) -} diff --git a/server/plugin/email/config/email.go b/server/plugin/email/config/email.go deleted file mode 100644 index 412b5a8..0000000 --- a/server/plugin/email/config/email.go +++ /dev/null @@ -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认证 -} diff --git a/server/plugin/email/global/gloabl.go b/server/plugin/email/global/gloabl.go deleted file mode 100644 index 13082d0..0000000 --- a/server/plugin/email/global/gloabl.go +++ /dev/null @@ -1,5 +0,0 @@ -package global - -import "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/config" - -var GlobalConfig = new(config.Email) diff --git a/server/plugin/email/main.go b/server/plugin/email/main.go deleted file mode 100644 index 37a6359..0000000 --- a/server/plugin/email/main.go +++ /dev/null @@ -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" -} diff --git a/server/plugin/email/model/response/email.go b/server/plugin/email/model/response/email.go deleted file mode 100644 index ed25475..0000000 --- a/server/plugin/email/model/response/email.go +++ /dev/null @@ -1,7 +0,0 @@ -package response - -type Email struct { - To string `json:"to"` // 邮件发送给谁 - Subject string `json:"subject"` // 邮件标题 - Body string `json:"body"` // 邮件内容 -} diff --git a/server/plugin/email/router/enter.go b/server/plugin/email/router/enter.go deleted file mode 100644 index e081a54..0000000 --- a/server/plugin/email/router/enter.go +++ /dev/null @@ -1,7 +0,0 @@ -package router - -type RouterGroup struct { - EmailRouter -} - -var RouterGroupApp = new(RouterGroup) diff --git a/server/plugin/email/router/sys_email.go b/server/plugin/email/router/sys_email.go deleted file mode 100644 index 1f9f07f..0000000 --- a/server/plugin/email/router/sys_email.go +++ /dev/null @@ -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) // 发送邮件 - } -} diff --git a/server/plugin/email/service/enter.go b/server/plugin/email/service/enter.go deleted file mode 100644 index e96e267..0000000 --- a/server/plugin/email/service/enter.go +++ /dev/null @@ -1,7 +0,0 @@ -package service - -type ServiceGroup struct { - EmailService -} - -var ServiceGroupApp = new(ServiceGroup) diff --git a/server/plugin/email/service/sys_email.go b/server/plugin/email/service/sys_email.go deleted file mode 100644 index 5704276..0000000 --- a/server/plugin/email/service/sys_email.go +++ /dev/null @@ -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 -} diff --git a/server/plugin/email/utils/email.go b/server/plugin/email/utils/email.go deleted file mode 100644 index dd732d8..0000000 --- a/server/plugin/email/utils/email.go +++ /dev/null @@ -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 -} diff --git a/server/plugin/plugin-tool/utils/check.go b/server/plugin/plugin-tool/utils/check.go deleted file mode 100644 index 70631d7..0000000 --- a/server/plugin/plugin-tool/utils/check.go +++ /dev/null @@ -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] -} - diff --git a/server/plugin/register.go b/server/plugin/register.go deleted file mode 100644 index 11399d0..0000000 --- a/server/plugin/register.go +++ /dev/null @@ -1,5 +0,0 @@ -package plugin - -import ( - _ "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement" -) diff --git a/server/resource/function/api.go.tpl b/server/resource/function/api.go.tpl index 35a1cd5..606eb9c 100644 --- a/server/resource/function/api.go.tpl +++ b/server/resource/function/api.go.tpl @@ -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}} diff --git a/server/resource/function/api.js.tpl b/server/resource/function/api.js.tpl index a07b102..07f82b3 100644 --- a/server/resource/function/api.js.tpl +++ b/server/resource/function/api.js.tpl @@ -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 -}} diff --git a/server/resource/function/server.go.tpl b/server/resource/function/server.go.tpl index 7327604..87f0476 100644 --- a/server/resource/function/server.go.tpl +++ b/server/resource/function/server.go.tpl @@ -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}} diff --git a/server/resource/plugin/server/api/api.go.tpl b/server/resource/plugin/server/api/api.go.tpl deleted file mode 100644 index e69ae82..0000000 --- a/server/resource/plugin/server/api/api.go.tpl +++ /dev/null @@ -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) -} diff --git a/server/resource/plugin/server/api/enter.go.tpl b/server/resource/plugin/server/api/enter.go.tpl deleted file mode 100644 index 989fb35..0000000 --- a/server/resource/plugin/server/api/enter.go.tpl +++ /dev/null @@ -1,6 +0,0 @@ -package api - -var Api = new(api) - -type api struct { -} diff --git a/server/resource/plugin/server/config/config.go.tpl b/server/resource/plugin/server/config/config.go.tpl deleted file mode 100644 index 809bc99..0000000 --- a/server/resource/plugin/server/config/config.go.tpl +++ /dev/null @@ -1,4 +0,0 @@ -package config - -type Config struct { -} diff --git a/server/resource/plugin/server/gen/gen.go.tpl b/server/resource/plugin/server/gen/gen.go.tpl deleted file mode 100644 index 5639d4a..0000000 --- a/server/resource/plugin/server/gen/gen.go.tpl +++ /dev/null @@ -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() -} diff --git a/server/resource/plugin/server/initialize/api.go.tpl b/server/resource/plugin/server/initialize/api.go.tpl deleted file mode 100644 index dfbea23..0000000 --- a/server/resource/plugin/server/initialize/api.go.tpl +++ /dev/null @@ -1,12 +0,0 @@ -package initialize - -import ( - "context" - model "{{.Module}}/model/system" - "{{.Module}}/plugin/plugin-tool/utils" -) - -func Api(ctx context.Context) { - entities := []model.SysApi{} - utils.RegisterApis(entities...) -} diff --git a/server/resource/plugin/server/initialize/dictionary.go.tpl b/server/resource/plugin/server/initialize/dictionary.go.tpl deleted file mode 100644 index e61b42c..0000000 --- a/server/resource/plugin/server/initialize/dictionary.go.tpl +++ /dev/null @@ -1,12 +0,0 @@ -package initialize - -import ( - "context" - model "{{.Module}}/model/system" - "{{.Module}}/plugin/plugin-tool/utils" -) - -func Dictionary(ctx context.Context) { - entities := []model.SysDictionary{} - utils.RegisterDictionaries(entities...) -} diff --git a/server/resource/plugin/server/initialize/gorm.go.tpl b/server/resource/plugin/server/initialize/gorm.go.tpl deleted file mode 100644 index 52c8183..0000000 --- a/server/resource/plugin/server/initialize/gorm.go.tpl +++ /dev/null @@ -1,17 +0,0 @@ -package initialize - -import ( - "context" - "fmt" - "{{.Module}}/global" - "github.com/pkg/errors" - "go.uber.org/zap" -) - -func Gorm(ctx context.Context) { - err := global.GVA_DB.WithContext(ctx).AutoMigrate() - if err != nil { - err = errors.Wrap(err, "注册表失败!") - zap.L().Error(fmt.Sprintf("%+v", err)) - } -} diff --git a/server/resource/plugin/server/initialize/menu.go.tpl b/server/resource/plugin/server/initialize/menu.go.tpl deleted file mode 100644 index 8774f35..0000000 --- a/server/resource/plugin/server/initialize/menu.go.tpl +++ /dev/null @@ -1,12 +0,0 @@ -package initialize - -import ( - "context" - model "{{.Module}}/model/system" - "{{.Module}}/plugin/plugin-tool/utils" -) - -func Menu(ctx context.Context) { - entities := []model.SysBaseMenu{} - utils.RegisterMenus(entities...) -} diff --git a/server/resource/plugin/server/initialize/router.go.tpl b/server/resource/plugin/server/initialize/router.go.tpl deleted file mode 100644 index fbf03a3..0000000 --- a/server/resource/plugin/server/initialize/router.go.tpl +++ /dev/null @@ -1,14 +0,0 @@ -package initialize - -import ( - "{{.Module}}/global" - "{{.Module}}/middleware" - "github.com/gin-gonic/gin" -) - -func Router(engine *gin.Engine) { - public := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("") - public.Use() - private := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("") - private.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) -} diff --git a/server/resource/plugin/server/initialize/viper.go.tpl b/server/resource/plugin/server/initialize/viper.go.tpl deleted file mode 100644 index e759ad6..0000000 --- a/server/resource/plugin/server/initialize/viper.go.tpl +++ /dev/null @@ -1,17 +0,0 @@ -package initialize - -import ( - "fmt" - "{{.Module}}/global" - "{{.Module}}/plugin/{{ .Package }}/plugin" - "github.com/pkg/errors" - "go.uber.org/zap" -) - -func Viper() { - err := global.GVA_VP.UnmarshalKey("{{ .Package }}", &plugin.Config) - if err != nil { - err = errors.Wrap(err, "初始化配置文件失败!") - zap.L().Error(fmt.Sprintf("%+v", err)) - } -} diff --git a/server/resource/plugin/server/model/model.go.tpl b/server/resource/plugin/server/model/model.go.tpl deleted file mode 100644 index 283841c..0000000 --- a/server/resource/plugin/server/model/model.go.tpl +++ /dev/null @@ -1,76 +0,0 @@ -{{- if .IsAdd}} -// 在结构体中新增如下字段 -{{- range .Fields}} - {{ GenerateField . }} -{{- end }} - -{{ else }} -package model - -{{- if not .OnlyTemplate}} -import ( - {{- if .GvaModel }} - "{{.Module}}/global" - {{- end }} - {{- if or .HasTimer }} - "time" - {{- end }} - {{- if .NeedJSON }} - "gorm.io/datatypes" - {{- end }} -) -{{- end }} - -// {{.StructName}} {{.Description}} 结构体 -type {{.StructName}} struct { -{{- if not .OnlyTemplate}} -{{- if .GvaModel }} - global.GVA_MODEL -{{- end }} -{{- range .Fields}} - {{ GenerateField . }} -{{- end }} - {{- if .AutoCreateResource }} - CreatedBy uint `gorm:"column:created_by;comment:创建者"` - UpdatedBy uint `gorm:"column:updated_by;comment:更新者"` - DeletedBy uint `gorm:"column:deleted_by;comment:删除者"` - {{- end }} - {{- if .IsTree }} - Children []*{{.StructName}} `json:"children" gorm:"-"` //子节点 - ParentID int `json:"parentID" gorm:"column:parent_id;comment:父节点"` - {{- end }} - {{- end }} -} - -{{ if .TableName }} -// TableName {{.Description}} {{.StructName}}自定义表名 {{.TableName}} -func ({{.StructName}}) TableName() string { - return "{{.TableName}}" -} -{{ end }} - - -{{if .IsTree }} -// GetChildren 实现TreeNode接口 -func (s *{{.StructName}}) GetChildren() []*{{.StructName}} { - return s.Children -} - -// SetChildren 实现TreeNode接口 -func (s *{{.StructName}}) SetChildren(children *{{.StructName}}) { - s.Children = append(s.Children, children) -} - -// GetID 实现TreeNode接口 -func (s *{{.StructName}}) GetID() int { - return int({{if not .GvaModel}}*{{- end }}s.{{.PrimaryField.FieldName}}) -} - -// GetParentID 实现TreeNode接口 -func (s *{{.StructName}}) GetParentID() int { - return s.ParentID -} -{{ end }} - - -{{ end }} diff --git a/server/resource/plugin/server/model/request/request.go.tpl b/server/resource/plugin/server/model/request/request.go.tpl deleted file mode 100644 index 60cf677..0000000 --- a/server/resource/plugin/server/model/request/request.go.tpl +++ /dev/null @@ -1,38 +0,0 @@ -{{- if .IsAdd}} -// 在结构体中新增如下字段 -{{- range .Fields}} - {{- if ne .FieldSearchType ""}} - {{ GenerateSearchField . }} - {{- end}} -{{- end }} -{{- if .NeedSort}} -Sort string `json:"sort" form:"sort"` -Order string `json:"order" form:"order"` -{{- end}} -{{- else }} -package request -{{- if not .OnlyTemplate}} -import ( - "{{.Module}}/model/common/request" - {{ if or .HasSearchTimer .GvaModel }}"time"{{ end }} -) -{{- end}} -type {{.StructName}}Search struct{ -{{- if not .OnlyTemplate}} - -{{- if .GvaModel }} - CreatedAtRange []time.Time `json:"createdAtRange" form:"createdAtRange[]"` -{{- end }} -{{- range .Fields}} - {{- if ne .FieldSearchType ""}} - {{ GenerateSearchField . }} - {{- end}} -{{- end }} - request.PageInfo - {{- if .NeedSort}} - Sort string `json:"sort" form:"sort"` - Order string `json:"order" form:"order"` - {{- end}} -{{- end }} -} -{{- end }} diff --git a/server/resource/plugin/server/plugin.go.tpl b/server/resource/plugin/server/plugin.go.tpl deleted file mode 100644 index 43ed056..0000000 --- a/server/resource/plugin/server/plugin.go.tpl +++ /dev/null @@ -1,33 +0,0 @@ -package {{ .Package }} - -import ( - "context" - "{{.Module}}/plugin/{{ .Package }}/initialize" - interfaces "{{.Module}}/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) -} - - -// 如果需要配置文件,请到config.Config中填充配置结构,且到下方发放中填入其在config.yaml中的key并添加如下方法 -// initialize.Viper() -// 安装插件时候自动注册的api数据请到下方法.Api方法中实现并添加如下方法 -// initialize.Api(ctx) -// 安装插件时候自动注册的api数据请到下方法.Menu方法中实现并添加如下方法 -// initialize.Menu(ctx) -// 安装插件时候自动注册的api数据请到下方法.Dictionary方法中实现并添加如下方法 -// initialize.Dictionary(ctx) -func (p *plugin) Register(group *gin.Engine) { - ctx := context.Background() - initialize.Gorm(ctx) - initialize.Router(group) -} diff --git a/server/resource/plugin/server/plugin/plugin.go.tpl b/server/resource/plugin/server/plugin/plugin.go.tpl deleted file mode 100644 index 7e25e07..0000000 --- a/server/resource/plugin/server/plugin/plugin.go.tpl +++ /dev/null @@ -1,5 +0,0 @@ -package plugin - -import "{{.Module}}/plugin/{{ .Package }}/config" - -var Config config.Config diff --git a/server/resource/plugin/server/router/enter.go.tpl b/server/resource/plugin/server/router/enter.go.tpl deleted file mode 100644 index 78517b3..0000000 --- a/server/resource/plugin/server/router/enter.go.tpl +++ /dev/null @@ -1,6 +0,0 @@ -package router - -var Router = new(router) - -type router struct { -} diff --git a/server/resource/plugin/server/router/router.go.tpl b/server/resource/plugin/server/router/router.go.tpl deleted file mode 100644 index 34bf4d8..0000000 --- a/server/resource/plugin/server/router/router.go.tpl +++ /dev/null @@ -1,46 +0,0 @@ -package router - -import ( - {{if .OnlyTemplate }} // {{end}}"{{.Module}}/middleware" - "github.com/gin-gonic/gin" -) - -var {{.StructName}} = new({{.Abbreviation}}) - -type {{.Abbreviation}} struct {} - -// Init 初始化 {{.Description}} 路由信息 -func (r *{{.Abbreviation}}) Init(public *gin.RouterGroup, private *gin.RouterGroup) { -{{- if not .OnlyTemplate }} - { - group := private.Group("{{.Abbreviation}}").Use(middleware.OperationRecord()) - group.POST("create{{.StructName}}", api{{.StructName}}.Create{{.StructName}}) // 新建{{.Description}} - group.DELETE("delete{{.StructName}}", api{{.StructName}}.Delete{{.StructName}}) // 删除{{.Description}} - group.DELETE("delete{{.StructName}}ByIds", api{{.StructName}}.Delete{{.StructName}}ByIds) // 批量删除{{.Description}} - group.PUT("update{{.StructName}}", api{{.StructName}}.Update{{.StructName}}) // 更新{{.Description}} - } - { - group := private.Group("{{.Abbreviation}}") - group.GET("find{{.StructName}}", api{{.StructName}}.Find{{.StructName}}) // 根据ID获取{{.Description}} - group.GET("get{{.StructName}}List", api{{.StructName}}.Get{{.StructName}}List) // 获取{{.Description}}列表 - } - { - group := public.Group("{{.Abbreviation}}") - {{- if .HasDataSource}} - group.GET("get{{.StructName}}DataSource", api{{.StructName}}.Get{{.StructName}}DataSource) // 获取{{.Description}}数据源 - {{- end}} - group.GET("get{{.StructName}}Public", api{{.StructName}}.Get{{.StructName}}Public) // {{.Description}}开放接口 - } -{{- else}} - // { - // group := private.Group("{{.Abbreviation}}").Use(middleware.OperationRecord()) - // } - // { - // group := private.Group("{{.Abbreviation}}") - // } - { - group := public.Group("{{.Abbreviation}}") - group.GET("get{{.StructName}}Public", api{{.StructName}}.Get{{.StructName}}Public) // {{.Description}}开放接口 - } -{{- end}} -} diff --git a/server/resource/plugin/server/service/enter.go.tpl b/server/resource/plugin/server/service/enter.go.tpl deleted file mode 100644 index 034facb..0000000 --- a/server/resource/plugin/server/service/enter.go.tpl +++ /dev/null @@ -1,7 +0,0 @@ -package service - -var Service = new(service) - -type service struct { -} - diff --git a/server/resource/plugin/server/service/service.go.tpl b/server/resource/plugin/server/service/service.go.tpl deleted file mode 100644 index 9743602..0000000 --- a/server/resource/plugin/server/service/service.go.tpl +++ /dev/null @@ -1,211 +0,0 @@ -{{- $db := "" }} -{{- if eq .BusinessDB "" }} - {{- $db = "global.GVA_DB" }} -{{- else}} - {{- $db = printf "global.MustGetGlobalDBByDBName(\"%s\")" .BusinessDB }} -{{- end}} - -{{- if .IsAdd}} - -// Get{{.StructName}}InfoList 新增搜索语句 - - {{ GenerateSearchConditions .Fields }} - -// Get{{.StructName}}InfoList 新增排序语句 请自行在搜索语句中添加orderMap内容 - {{- range .Fields}} - {{- if .Sort}} -orderMap["{{.ColumnName}}"] = true - {{- end}} - {{- end}} - - -{{- if .HasDataSource }} -// Get{{.StructName}}DataSource()方法新增关联语句 - {{range $key, $value := .DataSourceMap}} -{{$key}} := make([]map[string]any, 0) -{{$db}}.Table("{{$value.Table}}"){{- if $value.HasDeletedAt}}.Where("deleted_at IS NULL"){{ end }}.Select("{{$value.Label}} as label,{{$value.Value}} as value").Scan(&{{$key}}) -res["{{$key}}"] = {{$key}} - {{- end }} -{{- end }} -{{- else}} -package service - -import ( -{{- if not .OnlyTemplate }} - "context" - "{{.Module}}/global" - "{{.Module}}/plugin/{{.Package}}/model" - {{- if not .IsTree }} - "{{.Module}}/plugin/{{.Package}}/model/request" - {{- else }} - "errors" - {{- end }} - {{- if .AutoCreateResource }} - "gorm.io/gorm" - {{- end}} -{{- if .IsTree }} - "{{.Module}}/utils" -{{- end }} -{{- end }} -) - -var {{.StructName}} = new({{.Abbreviation}}) - -type {{.Abbreviation}} struct {} - -{{- $db := "" }} -{{- if eq .BusinessDB "" }} - {{- $db = "global.GVA_DB" }} -{{- else}} - {{- $db = printf "global.MustGetGlobalDBByDBName(\"%s\")" .BusinessDB }} -{{- end}} -{{- if not .OnlyTemplate }} -// Create{{.StructName}} 创建{{.Description}}记录 -// Author [yourname](https://github.com/yourname) -func (s *{{.Abbreviation}}) Create{{.StructName}}(ctx context.Context, {{.Abbreviation}} *model.{{.StructName}}) (err error) { - err = {{$db}}.Create({{.Abbreviation}}).Error - return err -} - -// Delete{{.StructName}} 删除{{.Description}}记录 -// Author [yourname](https://github.com/yourname) -func (s *{{.Abbreviation}}) Delete{{.StructName}}(ctx context.Context, {{.PrimaryField.FieldJson}} string{{- if .AutoCreateResource -}},userID uint{{- end -}}) (err error) { - - {{- if .IsTree }} - var count int64 - err = {{$db}}.Find(&model.{{.StructName}}{},"parent_id = ?",{{.PrimaryField.FieldJson}}).Count(&count).Error - if count > 0 { - return errors.New("此节点存在子节点不允许删除") - } - if err != nil { - return err - } - {{- end }} - - {{- if .AutoCreateResource }} - err = {{$db}}.Transaction(func(tx *gorm.DB) error { - if err := tx.Model(&model.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?", {{.PrimaryField.FieldJson}}).Update("deleted_by", userID).Error; err != nil { - return err - } - if err = tx.Delete(&model.{{.StructName}}{},"{{.PrimaryField.ColumnName}} = ?",{{.PrimaryField.FieldJson}}).Error; err != nil { - return err - } - return nil - }) - {{- else }} - err = {{$db}}.Delete(&model.{{.StructName}}{},"{{.PrimaryField.ColumnName}} = ?",{{.PrimaryField.FieldJson}}).Error - {{- end }} - return err -} - -// Delete{{.StructName}}ByIds 批量删除{{.Description}}记录 -// Author [yourname](https://github.com/yourname) -func (s *{{.Abbreviation}}) Delete{{.StructName}}ByIds(ctx context.Context, {{.PrimaryField.FieldJson}}s []string {{- if .AutoCreateResource }},deleted_by uint{{- end}}) (err error) { - {{- if .AutoCreateResource }} - err = {{$db}}.Transaction(func(tx *gorm.DB) error { - if err := tx.Model(&model.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} in ?", {{.PrimaryField.FieldJson}}s).Update("deleted_by", deleted_by).Error; err != nil { - return err - } - if err := tx.Where("{{.PrimaryField.ColumnName}} in ?", {{.PrimaryField.FieldJson}}s).Delete(&model.{{.StructName}}{}).Error; err != nil { - return err - } - return nil - }) - {{- else}} - err = {{$db}}.Delete(&[]model.{{.StructName}}{},"{{.PrimaryField.ColumnName}} in ?",{{.PrimaryField.FieldJson}}s).Error - {{- end}} - return err -} - -// Update{{.StructName}} 更新{{.Description}}记录 -// Author [yourname](https://github.com/yourname) -func (s *{{.Abbreviation}}) Update{{.StructName}}(ctx context.Context, {{.Abbreviation}} model.{{.StructName}}) (err error) { - err = {{$db}}.Model(&model.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?",{{.Abbreviation}}.{{.PrimaryField.FieldName}}).Updates(&{{.Abbreviation}}).Error - return err -} - -// Get{{.StructName}} 根据{{.PrimaryField.FieldJson}}获取{{.Description}}记录 -// Author [yourname](https://github.com/yourname) -func (s *{{.Abbreviation}}) Get{{.StructName}}(ctx context.Context, {{.PrimaryField.FieldJson}} string) ({{.Abbreviation}} model.{{.StructName}}, err error) { - err = {{$db}}.Where("{{.PrimaryField.ColumnName}} = ?", {{.PrimaryField.FieldJson}}).First(&{{.Abbreviation}}).Error - return -} - - -{{- if .IsTree }} -// Get{{.StructName}}InfoList 分页获取{{.Description}}记录,Tree模式下不添加分页和搜索 -// Author [yourname](https://github.com/yourname) -func (s *{{.Abbreviation}}) Get{{.StructName}}InfoList(ctx context.Context) (list []*model.{{.StructName}},err error) { - // 创建db - db := {{$db}}.Model(&model.{{.StructName}}{}) - var {{.Abbreviation}}s []*model.{{.StructName}} - - err = db.Find(&{{.Abbreviation}}s).Error - - return utils.BuildTree({{.Abbreviation}}s), err -} -{{- else }} -// Get{{.StructName}}InfoList 分页获取{{.Description}}记录 -// Author [yourname](https://github.com/yourname) -func (s *{{.Abbreviation}}) Get{{.StructName}}InfoList(ctx context.Context, info request.{{.StructName}}Search) (list []model.{{.StructName}}, total int64, err error) { - limit := info.PageSize - offset := info.PageSize * (info.Page - 1) - // 创建db - db := {{$db}}.Model(&model.{{.StructName}}{}) - var {{.Abbreviation}}s []model.{{.StructName}} - // 如果有条件搜索 下方会自动创建搜索语句 -{{- if .GvaModel }} - if len(info.CreatedAtRange) == 2 { - db = db.Where("created_at BETWEEN ? AND ?", info.CreatedAtRange[0], info.CreatedAtRange[1]) - } -{{- end }} - {{ GenerateSearchConditions .Fields }} - err = db.Count(&total).Error - if err!=nil { - return - } - {{- if .NeedSort}} - var OrderStr string - orderMap := make(map[string]bool) - {{- if .GvaModel }} - orderMap["id"] = true - orderMap["created_at"] = true - {{- end }} - {{- range .Fields}} - {{- if .Sort}} - orderMap["{{.ColumnName}}"] = true - {{- end}} - {{- end}} - if orderMap[info.Sort] { - OrderStr = info.Sort - if info.Order == "descending" { - OrderStr = OrderStr + " desc" - } - db = db.Order(OrderStr) - } - {{- end}} - - if limit != 0 { - db = db.Limit(limit).Offset(offset) - } - err = db.Find(&{{.Abbreviation}}s).Error - return {{.Abbreviation}}s, total, err -} -{{- end }} -{{- if .HasDataSource }} -func (s *{{.Abbreviation}})Get{{.StructName}}DataSource(ctx context.Context) (res map[string][]map[string]any, err error) { - res = make(map[string][]map[string]any) - {{range $key, $value := .DataSourceMap}} - {{$key}} := make([]map[string]any, 0) - {{$db}}.Table("{{$value.Table}}"){{- if $value.HasDeletedAt}}.Where("deleted_at IS NULL"){{ end }}.Select("{{$value.Label}} as label,{{$value.Value}} as value").Scan(&{{$key}}) - res["{{$key}}"] = {{$key}} - {{- end }} - return -} -{{- end }} -{{- end }} - -func (s *{{.Abbreviation}})Get{{.StructName}}Public(ctx context.Context) { - -} -{{- end }} diff --git a/server/resource/plugin/web/api/api.js.tpl b/server/resource/plugin/web/api/api.js.tpl deleted file mode 100644 index 0462fde..0000000 --- a/server/resource/plugin/web/api/api.js.tpl +++ /dev/null @@ -1,127 +0,0 @@ -import service from '@/utils/request' -{{- if not .OnlyTemplate}} -// @Tags {{.StructName}} -// @Summary 创建{{.Description}} -// @Security ApiKeyAuth -// @Accept application/json -// @Produce application/json -// @Param data body model.{{.StructName}} true "创建{{.Description}}" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" -// @Router /{{.Abbreviation}}/create{{.StructName}} [post] -export const create{{.StructName}} = (data) => { - return service({ - url: '/{{.Abbreviation}}/create{{.StructName}}', - method: 'post', - data - }) -} - -// @Tags {{.StructName}} -// @Summary 删除{{.Description}} -// @Security ApiKeyAuth -// @Accept application/json -// @Produce application/json -// @Param data body model.{{.StructName}} true "删除{{.Description}}" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" -// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete] -export const delete{{.StructName}} = (params) => { - return service({ - url: '/{{.Abbreviation}}/delete{{.StructName}}', - method: 'delete', - params - }) -} - -// @Tags {{.StructName}} -// @Summary 批量删除{{.Description}} -// @Security ApiKeyAuth -// @Accept application/json -// @Produce application/json -// @Param data body request.IdsReq true "批量删除{{.Description}}" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" -// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete] -export const delete{{.StructName}}ByIds = (params) => { - return service({ - url: '/{{.Abbreviation}}/delete{{.StructName}}ByIds', - method: 'delete', - params - }) -} - -// @Tags {{.StructName}} -// @Summary 更新{{.Description}} -// @Security ApiKeyAuth -// @Accept application/json -// @Produce application/json -// @Param data body model.{{.StructName}} true "更新{{.Description}}" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" -// @Router /{{.Abbreviation}}/update{{.StructName}} [put] -export const update{{.StructName}} = (data) => { - return service({ - url: '/{{.Abbreviation}}/update{{.StructName}}', - method: 'put', - data - }) -} - -// @Tags {{.StructName}} -// @Summary 用id查询{{.Description}} -// @Security ApiKeyAuth -// @Accept application/json -// @Produce application/json -// @Param data query model.{{.StructName}} true "用id查询{{.Description}}" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" -// @Router /{{.Abbreviation}}/find{{.StructName}} [get] -export const find{{.StructName}} = (params) => { - return service({ - url: '/{{.Abbreviation}}/find{{.StructName}}', - method: 'get', - params - }) -} - -// @Tags {{.StructName}} -// @Summary 分页获取{{.Description}}列表 -// @Security ApiKeyAuth -// @Accept application/json -// @Produce application/json -// @Param data query request.PageInfo true "分页获取{{.Description}}列表" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" -// @Router /{{.Abbreviation}}/get{{.StructName}}List [get] -export const get{{.StructName}}List = (params) => { - return service({ - url: '/{{.Abbreviation}}/get{{.StructName}}List', - method: 'get', - params - }) -} - -{{- if .HasDataSource}} -// @Tags {{.StructName}} -// @Summary 获取数据源 -// @Security ApiKeyAuth -// @Accept application/json -// @Produce application/json -// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" -// @Router /{{.Abbreviation}}/find{{.StructName}}DataSource [get] -export const get{{.StructName}}DataSource = () => { - return service({ - url: '/{{.Abbreviation}}/get{{.StructName}}DataSource', - method: 'get', - }) -} -{{- end}} -{{- end}} -// @Tags {{.StructName}} -// @Summary 不需要鉴权的{{.Description}}接口 -// @Accept application/json -// @Produce application/json -// @Param data query request.{{.StructName}}Search true "分页获取{{.Description}}列表" -// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" -// @Router /{{.Abbreviation}}/get{{.StructName}}Public [get] -export const get{{.StructName}}Public = () => { - return service({ - url: '/{{.Abbreviation}}/get{{.StructName}}Public', - method: 'get', - }) -} diff --git a/server/resource/plugin/web/form/form.vue.tpl b/server/resource/plugin/web/form/form.vue.tpl deleted file mode 100644 index 7d3406a..0000000 --- a/server/resource/plugin/web/form/form.vue.tpl +++ /dev/null @@ -1,464 +0,0 @@ -{{- if .IsAdd }} -// 新增表单中增加如下代码 -{{- range .Fields}} - {{- if .Form}} - - {{- if .CheckDataSource}} - - - - {{- else }} - {{- if eq .FieldType "bool" }} - - {{- end }} - {{- if eq .FieldType "string" }} - {{- if .DictType}} - - - - {{- else }} - - {{- end }} - {{- end }} - {{- if eq .FieldType "richtext" }} - - {{- end }} - {{- if eq .FieldType "json" }} - // 此字段为json结构,可以前端自行控制展示和数据绑定模式 需绑定json的key为 formData.{{.FieldJson}} 后端会按照json的类型进行存取 - {{"{{"}} formData.{{.FieldJson}} {{"}}"}} - {{- end }} - {{- if eq .FieldType "array" }} - - {{- end }} - {{- if eq .FieldType "int" }} - - {{- end }} - {{- if eq .FieldType "time.Time" }} - - {{- end }} - {{- if eq .FieldType "float64" }} - - {{- end }} - {{- if eq .FieldType "enum" }} - - - - {{- end }} - {{- if eq .FieldType "picture" }} - - {{- end }} - {{- if eq .FieldType "pictures" }} - - {{- end }} - {{- if eq .FieldType "video" }} - - {{- end }} - {{- if eq .FieldType "file" }} - - {{- end }} - {{- end }} - - {{- end }} - {{- end }} - -// 字典增加如下代码 - {{- range $index, $element := .DictTypes}} -const {{ $element }}Options = ref([]) - {{- end }} - -// init方法中增加如下调用 - -{{- range $index, $element := .DictTypes }} - {{ $element }}Options.value = await getDictFunc('{{$element}}') -{{- end }} - -// 基础formData结构增加如下字段 -{{- range .Fields}} - {{- if .Form}} - {{- if eq .FieldType "bool" }} -{{.FieldJson}}: false, - {{- end }} - {{- if eq .FieldType "string" }} -{{.FieldJson}}: '', - {{- end }} - {{- if eq .FieldType "richtext" }} -{{.FieldJson}}: '', - {{- end }} - {{- if eq .FieldType "int" }} -{{.FieldJson}}: {{- if or .DataSource}} undefined{{ else }} 0{{- end }}, - {{- end }} - {{- if eq .FieldType "time.Time" }} -{{.FieldJson}}: new Date(), - {{- end }} - {{- if eq .FieldType "float64" }} -{{.FieldJson}}: 0, - {{- end }} - {{- if eq .FieldType "picture" }} -{{.FieldJson}}: "", - {{- end }} - {{- if eq .FieldType "video" }} -{{.FieldJson}}: "", - {{- end }} - {{- if eq .FieldType "pictures" }} -{{.FieldJson}}: [], - {{- end }} - {{- if eq .FieldType "file" }} -{{.FieldJson}}: [], - {{- end }} - {{- if eq .FieldType "json" }} -{{.FieldJson}}: {}, - {{- end }} - {{- if eq .FieldType "array" }} -{{.FieldJson}}: [], - {{- end }} - {{- end }} - {{- end }} -// 验证规则中增加如下字段 - -{{- range .Fields }} - {{- if .Form }} - {{- if eq .Require true }} -{{.FieldJson }} : [{ - required: true, - message: '{{ .ErrorText }}', - trigger: ['input','blur'], -}, - {{- if eq .FieldType "string" }} -{ - whitespace: true, - message: '不能只输入空格', - trigger: ['input', 'blur'], -} - {{- end }} -], - {{- end }} - {{- end }} - {{- end }} - -{{- if .HasDataSource }} -// 请引用 -get{{.StructName}}DataSource, - -// 获取数据源 -const dataSource = ref([]) -const getDataSourceFunc = async()=>{ - const res = await get{{.StructName}}DataSource() - if (res.code === 0) { - dataSource.value = res.data - } -} -getDataSourceFunc() -{{- end }} -{{- else }} -{{- if not .OnlyTemplate }} - - - - - -{{- else }} - - - -{{- end }} -{{- end }} diff --git a/server/resource/plugin/web/view/view.vue.tpl b/server/resource/plugin/web/view/view.vue.tpl deleted file mode 100644 index 98b557a..0000000 --- a/server/resource/plugin/web/view/view.vue.tpl +++ /dev/null @@ -1,689 +0,0 @@ -{{- $global := . }} -{{- $templateID := printf "%s_%s" .Package .StructName }} -{{- if .IsAdd }} -// 请在搜索条件中增加如下代码 -{{- range .Fields}} - {{- if .FieldSearchType}} -{{ GenerateSearchFormItem .}} - {{ end }} -{{ end }} - - -// 表格增加如下列代码 - -{{- range .Fields}} - {{- if .Table}} - {{ GenerateTableColumn . }} - {{- end }} -{{- end }} - -// 新增表单中增加如下代码 -{{- range .Fields}} - {{- if .Form}} - {{ GenerateFormItem . }} - {{- end }} -{{- end }} - -// 查看抽屉中增加如下代码 - -{{- range .Fields}} - {{- if .Desc }} - {{ GenerateDescriptionItem . }} - {{- end }} - {{- end }} - -// 字典增加如下代码 - {{- range $index, $element := .DictTypes}} -const {{ $element }}Options = ref([]) - {{- end }} - -// setOptions方法中增加如下调用 - -{{- range $index, $element := .DictTypes }} - {{ $element }}Options.value = await getDictFunc('{{$element}}') -{{- end }} - -// 基础formData结构(变量处和关闭表单处)增加如下字段 -{{- range .Fields}} - {{- if .Form}} - {{ GenerateDefaultFormValue . }} - {{- end }} - {{- end }} -// 验证规则中增加如下字段 - -{{- range .Fields }} - {{- if .Form }} - {{- if eq .Require true }} -{{.FieldJson }} : [{ - required: true, - message: '{{ .ErrorText }}', - trigger: ['input','blur'], -}, - {{- if eq .FieldType "string" }} -{ - whitespace: true, - message: '不能只输入空格', - trigger: ['input', 'blur'], -} - {{- end }} -], - {{- end }} - {{- end }} - {{- end }} - - - -{{- if .HasDataSource }} -// 请引用 -get{{.StructName}}DataSource, - -// 获取数据源 -const dataSource = ref({}) -const getDataSourceFunc = async()=>{ - const res = await get{{.StructName}}DataSource() - if (res.code === 0) { - dataSource.value = res.data || [] - } -} -getDataSourceFunc() -{{- end }} - -{{- else }} - -{{- if not .OnlyTemplate}} - - - - - -{{- else}} - - - -{{- end }} - -{{- end }} diff --git a/server/router/common/enter.go b/server/router/common/enter.go new file mode 100644 index 0000000..c4c09df --- /dev/null +++ b/server/router/common/enter.go @@ -0,0 +1,15 @@ +package common + +import ( + api "github.com/flipped-aurora/gin-vue-admin/server/api/v1" +) + +type RouterGroup struct { + AttachmentCategoryRouter + FileUploadAndDownloadRouter +} + +var ( + attachmentCategoryApi = api.ApiGroupApp.CommonApiGroup.AttachmentCategoryApi + exaFileUploadAndDownloadApi = api.ApiGroupApp.CommonApiGroup.FileUploadAndDownloadApi +) diff --git a/server/router/example/exa_attachment_category.go b/server/router/common/exa_attachment_category.go similarity index 96% rename from server/router/example/exa_attachment_category.go rename to server/router/common/exa_attachment_category.go index 4900292..360f45f 100644 --- a/server/router/example/exa_attachment_category.go +++ b/server/router/common/exa_attachment_category.go @@ -1,4 +1,4 @@ -package example +package common import ( "github.com/gin-gonic/gin" diff --git a/server/router/example/exa_file_upload_and_download.go b/server/router/common/exa_file_upload_and_download.go similarity index 98% rename from server/router/example/exa_file_upload_and_download.go rename to server/router/common/exa_file_upload_and_download.go index 84f6ecd..c448f70 100644 --- a/server/router/example/exa_file_upload_and_download.go +++ b/server/router/common/exa_file_upload_and_download.go @@ -1,4 +1,4 @@ -package example +package common import ( "github.com/gin-gonic/gin" diff --git a/server/router/enter.go b/server/router/enter.go index 6e6d811..2bc76e7 100644 --- a/server/router/enter.go +++ b/server/router/enter.go @@ -1,13 +1,13 @@ package router import ( - "github.com/flipped-aurora/gin-vue-admin/server/router/example" + "github.com/flipped-aurora/gin-vue-admin/server/router/common" "github.com/flipped-aurora/gin-vue-admin/server/router/system" ) var RouterGroupApp = new(RouterGroup) type RouterGroup struct { - System system.RouterGroup - Example example.RouterGroup + System system.RouterGroup + Common common.RouterGroup } diff --git a/server/router/example/enter.go b/server/router/example/enter.go deleted file mode 100644 index 17cd3d5..0000000 --- a/server/router/example/enter.go +++ /dev/null @@ -1,19 +0,0 @@ -package example - -import ( - api "github.com/flipped-aurora/gin-vue-admin/server/api/v1" -) - -type RouterGroup struct { - CustomerRouter - - AttachmentCategoryRouter - FileUploadAndDownloadRouter -} - -var ( - exaCustomerApi = api.ApiGroupApp.ExampleApiGroup.CustomerApi - - attachmentCategoryApi = api.ApiGroupApp.ExampleApiGroup.AttachmentCategoryApi - exaFileUploadAndDownloadApi = api.ApiGroupApp.ExampleApiGroup.FileUploadAndDownloadApi -) diff --git a/server/router/example/exa_customer.go b/server/router/example/exa_customer.go deleted file mode 100644 index acdf3c7..0000000 --- a/server/router/example/exa_customer.go +++ /dev/null @@ -1,22 +0,0 @@ -package example - -import ( - "github.com/flipped-aurora/gin-vue-admin/server/middleware" - "github.com/gin-gonic/gin" -) - -type CustomerRouter struct{} - -func (e *CustomerRouter) InitCustomerRouter(Router *gin.RouterGroup) { - customerRouter := Router.Group("customer").Use(middleware.OperationRecord()) - customerRouterWithoutRecord := Router.Group("customer") - { - customerRouter.POST("customer", exaCustomerApi.CreateExaCustomer) // 创建客户 - customerRouter.PUT("customer", exaCustomerApi.UpdateExaCustomer) // 更新客户 - customerRouter.DELETE("customer", exaCustomerApi.DeleteExaCustomer) // 删除客户 - } - { - customerRouterWithoutRecord.GET("customer", exaCustomerApi.GetExaCustomer) // 获取单一客户信息 - customerRouterWithoutRecord.GET("customerList", exaCustomerApi.GetExaCustomerList) // 获取客户列表 - } -} diff --git a/server/router/system/enter.go b/server/router/system/enter.go index 7e5d23c..a5d6f1c 100644 --- a/server/router/system/enter.go +++ b/server/router/system/enter.go @@ -11,43 +11,36 @@ type RouterGroup struct { MenuRouter UserRouter CasbinRouter - AutoCodeRouter AuthorityRouter DictionaryRouter OperationRecordRouter DictionaryDetailRouter AuthorityBtnRouter SysExportTemplateRouter + McpRouter SysParamsRouter SysVersionRouter SysErrorRouter LoginLogRouter ApiTokenRouter - SkillsRouter } var ( - dbApi = api.ApiGroupApp.SystemApiGroup.DBApi - jwtApi = api.ApiGroupApp.SystemApiGroup.JwtApi - baseApi = api.ApiGroupApp.SystemApiGroup.BaseApi - casbinApi = api.ApiGroupApp.SystemApiGroup.CasbinApi - systemApi = api.ApiGroupApp.SystemApiGroup.SystemApi - sysParamsApi = api.ApiGroupApp.SystemApiGroup.SysParamsApi - autoCodeApi = api.ApiGroupApp.SystemApiGroup.AutoCodeApi - authorityApi = api.ApiGroupApp.SystemApiGroup.AuthorityApi - apiRouterApi = api.ApiGroupApp.SystemApiGroup.SystemApiApi - dictionaryApi = api.ApiGroupApp.SystemApiGroup.DictionaryApi - authorityBtnApi = api.ApiGroupApp.SystemApiGroup.AuthorityBtnApi - authorityMenuApi = api.ApiGroupApp.SystemApiGroup.AuthorityMenuApi - autoCodePluginApi = api.ApiGroupApp.SystemApiGroup.AutoCodePluginApi - autocodeHistoryApi = api.ApiGroupApp.SystemApiGroup.AutoCodeHistoryApi - operationRecordApi = api.ApiGroupApp.SystemApiGroup.OperationRecordApi - autoCodePackageApi = api.ApiGroupApp.SystemApiGroup.AutoCodePackageApi - dictionaryDetailApi = api.ApiGroupApp.SystemApiGroup.DictionaryDetailApi - autoCodeTemplateApi = api.ApiGroupApp.SystemApiGroup.AutoCodeTemplateApi - exportTemplateApi = api.ApiGroupApp.SystemApiGroup.SysExportTemplateApi - sysVersionApi = api.ApiGroupApp.SystemApiGroup.SysVersionApi - sysErrorApi = api.ApiGroupApp.SystemApiGroup.SysErrorApi - skillsApi = api.ApiGroupApp.SystemApiGroup.SkillsApi - aiWorkflowSessionApi = api.ApiGroupApp.SystemApiGroup.AIWorkflowSessionApi + dbApi = api.ApiGroupApp.SystemApiGroup.DBApi + jwtApi = api.ApiGroupApp.SystemApiGroup.JwtApi + baseApi = api.ApiGroupApp.SystemApiGroup.BaseApi + casbinApi = api.ApiGroupApp.SystemApiGroup.CasbinApi + systemApi = api.ApiGroupApp.SystemApiGroup.SystemApi + sysParamsApi = api.ApiGroupApp.SystemApiGroup.SysParamsApi + authorityApi = api.ApiGroupApp.SystemApiGroup.AuthorityApi + apiRouterApi = api.ApiGroupApp.SystemApiGroup.SystemApiApi + dictionaryApi = api.ApiGroupApp.SystemApiGroup.DictionaryApi + authorityBtnApi = api.ApiGroupApp.SystemApiGroup.AuthorityBtnApi + authorityMenuApi = api.ApiGroupApp.SystemApiGroup.AuthorityMenuApi + operationRecordApi = api.ApiGroupApp.SystemApiGroup.OperationRecordApi + dictionaryDetailApi = api.ApiGroupApp.SystemApiGroup.DictionaryDetailApi + exportTemplateApi = api.ApiGroupApp.SystemApiGroup.SysExportTemplateApi + mcpApi = api.ApiGroupApp.SystemApiGroup.McpApi + sysVersionApi = api.ApiGroupApp.SystemApiGroup.SysVersionApi + sysErrorApi = api.ApiGroupApp.SystemApiGroup.SysErrorApi ) diff --git a/server/router/system/mcp.go b/server/router/system/mcp.go new file mode 100644 index 0000000..473b01c --- /dev/null +++ b/server/router/system/mcp.go @@ -0,0 +1,17 @@ +package system + +import "github.com/gin-gonic/gin" + +type McpRouter struct{} + +func (r *McpRouter) InitMcpRouter(Router *gin.RouterGroup) { + mcpRouter := Router.Group("mcp") + { + mcpRouter.GET("status", mcpApi.Status) + mcpRouter.POST("start", mcpApi.Start) + mcpRouter.POST("stop", mcpApi.Stop) + mcpRouter.GET("tools", mcpApi.List) + mcpRouter.POST("test", mcpApi.Test) + mcpRouter.POST("createTool", mcpApi.CreateTool) + } +} diff --git a/server/router/system/sys_auto_code.go b/server/router/system/sys_auto_code.go deleted file mode 100644 index deca5cc..0000000 --- a/server/router/system/sys_auto_code.go +++ /dev/null @@ -1,46 +0,0 @@ -package system - -import "github.com/gin-gonic/gin" - -type AutoCodeRouter struct{} - -func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup, RouterPublic *gin.RouterGroup) { - autoCodeRouter := Router.Group("autoCode") - publicAutoCodeRouter := RouterPublic.Group("autoCode") - { - autoCodeRouter.GET("getDB", autoCodeApi.GetDB) - autoCodeRouter.GET("getTables", autoCodeApi.GetTables) - autoCodeRouter.GET("getColumn", autoCodeApi.GetColumn) - } - { - autoCodeRouter.POST("preview", autoCodeTemplateApi.Preview) - autoCodeRouter.POST("createTemp", autoCodeTemplateApi.Create) - autoCodeRouter.POST("addFunc", autoCodeTemplateApi.AddFunc) - } - { - autoCodeRouter.POST("mcp", autoCodeTemplateApi.MCP) - autoCodeRouter.POST("mcpStatus", autoCodeTemplateApi.MCPStatus) - autoCodeRouter.POST("mcpStart", autoCodeTemplateApi.MCPStart) - autoCodeRouter.POST("mcpStop", autoCodeTemplateApi.MCPStop) - autoCodeRouter.POST("mcpList", autoCodeTemplateApi.MCPList) - autoCodeRouter.POST("mcpRoutes", autoCodeTemplateApi.MCPRoutes) - autoCodeRouter.POST("mcpTest", autoCodeTemplateApi.MCPTest) - } - { - autoCodeRouter.POST("getPackage", autoCodePackageApi.All) - autoCodeRouter.POST("delPackage", autoCodePackageApi.Delete) - autoCodeRouter.POST("createPackage", autoCodePackageApi.Create) - autoCodeRouter.POST("saveAIWorkflowSession", aiWorkflowSessionApi.Save) - autoCodeRouter.POST("getAIWorkflowSessionList", aiWorkflowSessionApi.GetList) - autoCodeRouter.POST("getAIWorkflowSessionDetail", aiWorkflowSessionApi.GetDetail) - autoCodeRouter.POST("deleteAIWorkflowSession", aiWorkflowSessionApi.Delete) - autoCodeRouter.POST("dumpAIWorkflowMarkdown", aiWorkflowSessionApi.DumpMarkdown) - } - { - autoCodeRouter.GET("getTemplates", autoCodePackageApi.Templates) - } - { - publicAutoCodeRouter.POST("llmAuto", autoCodeApi.LLMAuto) - publicAutoCodeRouter.POST("llmAutoSSE", autoCodeApi.LLMAutoSSE) - } -} diff --git a/server/router/system/sys_auto_code_history.go b/server/router/system/sys_auto_code_history.go deleted file mode 100644 index 42a2bef..0000000 --- a/server/router/system/sys_auto_code_history.go +++ /dev/null @@ -1,17 +0,0 @@ -package system - -import ( - "github.com/gin-gonic/gin" -) - -type AutoCodeHistoryRouter struct{} - -func (s *AutoCodeRouter) InitAutoCodeHistoryRouter(Router *gin.RouterGroup) { - autoCodeHistoryRouter := Router.Group("autoCode") - { - autoCodeHistoryRouter.POST("getMeta", autocodeHistoryApi.First) // 根据id获取meta信息 - autoCodeHistoryRouter.POST("rollback", autocodeHistoryApi.RollBack) // 回滚 - autoCodeHistoryRouter.POST("delSysHistory", autocodeHistoryApi.Delete) // 删除回滚记录 - autoCodeHistoryRouter.POST("getSysHistory", autocodeHistoryApi.GetList) // 获取回滚记录分页 - } -} diff --git a/server/router/system/sys_skills.go b/server/router/system/sys_skills.go deleted file mode 100644 index d9f21bf..0000000 --- a/server/router/system/sys_skills.go +++ /dev/null @@ -1,35 +0,0 @@ -package system - -import "github.com/gin-gonic/gin" - -type SkillsRouter struct{} - -func (s *SkillsRouter) InitSkillsRouter(Router *gin.RouterGroup, pubRouter *gin.RouterGroup) { - skillsRouter := Router.Group("skills") - skillsRouterPub := pubRouter.Group("skills") - { - skillsRouter.GET("getTools", skillsApi.GetTools) - skillsRouter.POST("getSkillList", skillsApi.GetSkillList) - skillsRouter.POST("getSkillDetail", skillsApi.GetSkillDetail) - skillsRouter.POST("saveSkill", skillsApi.SaveSkill) - skillsRouter.POST("deleteSkill", skillsApi.DeleteSkill) - skillsRouter.POST("createScript", skillsApi.CreateScript) - skillsRouter.POST("getScript", skillsApi.GetScript) - skillsRouter.POST("saveScript", skillsApi.SaveScript) - skillsRouter.POST("createResource", skillsApi.CreateResource) - skillsRouter.POST("getResource", skillsApi.GetResource) - skillsRouter.POST("saveResource", skillsApi.SaveResource) - skillsRouter.POST("createReference", skillsApi.CreateReference) - skillsRouter.POST("getReference", skillsApi.GetReference) - skillsRouter.POST("saveReference", skillsApi.SaveReference) - skillsRouter.POST("createTemplate", skillsApi.CreateTemplate) - skillsRouter.POST("getTemplate", skillsApi.GetTemplate) - skillsRouter.POST("saveTemplate", skillsApi.SaveTemplate) - skillsRouter.POST("getGlobalConstraint", skillsApi.GetGlobalConstraint) - skillsRouter.POST("saveGlobalConstraint", skillsApi.SaveGlobalConstraint) - skillsRouter.POST("packageSkill", skillsApi.PackageSkill) - } - { - skillsRouterPub.POST("downloadOnlineSkill", skillsApi.DownloadOnlineSkill) - } -} diff --git a/server/service/example/enter.go b/server/service/common/enter.go similarity index 71% rename from server/service/example/enter.go rename to server/service/common/enter.go index 13f2557..95231ae 100644 --- a/server/service/example/enter.go +++ b/server/service/common/enter.go @@ -1,8 +1,6 @@ -package example +package common type ServiceGroup struct { - CustomerService - AttachmentCategoryService FileUploadAndDownloadService } diff --git a/server/service/common/exa_attachment_category.go b/server/service/common/exa_attachment_category.go new file mode 100644 index 0000000..ff0a6e4 --- /dev/null +++ b/server/service/common/exa_attachment_category.go @@ -0,0 +1,66 @@ +package common + +import ( + "errors" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common" + "gorm.io/gorm" +) + +type AttachmentCategoryService struct{} + +// AddCategory 创建/更新的分类 +func (a *AttachmentCategoryService) AddCategory(req *common.ExaAttachmentCategory) (err error) { + // 检查是否已存在相同名称的分类 + if (!errors.Is(global.GVA_DB.Take(&common.ExaAttachmentCategory{}, "name = ? and pid = ?", req.Name, req.Pid).Error, gorm.ErrRecordNotFound)) { + return errors.New("分类名称已存在") + } + if req.ID > 0 { + if err = global.GVA_DB.Model(&common.ExaAttachmentCategory{}).Where("id = ?", req.ID).Updates(&common.ExaAttachmentCategory{ + Name: req.Name, + Pid: req.Pid, + }).Error; err != nil { + return err + } + } else { + if err = global.GVA_DB.Create(&common.ExaAttachmentCategory{ + Name: req.Name, + Pid: req.Pid, + }).Error; err != nil { + return err + } + } + return nil +} + +// DeleteCategory 删除分类 +func (a *AttachmentCategoryService) DeleteCategory(id *int) error { + var childCount int64 + global.GVA_DB.Model(&common.ExaAttachmentCategory{}).Where("pid = ?", id).Count(&childCount) + if childCount > 0 { + return errors.New("请先删除子级") + } + return global.GVA_DB.Where("id = ?", id).Unscoped().Delete(&common.ExaAttachmentCategory{}).Error +} + +// GetCategoryList 分类列表 +func (a *AttachmentCategoryService) GetCategoryList() (res []*common.ExaAttachmentCategory, err error) { + var fileLists []common.ExaAttachmentCategory + err = global.GVA_DB.Model(&common.ExaAttachmentCategory{}).Find(&fileLists).Error + if err != nil { + return res, err + } + return a.getChildrenList(fileLists, 0), nil +} + +// getChildrenList 子类 +func (a *AttachmentCategoryService) getChildrenList(categories []common.ExaAttachmentCategory, parentID uint) []*common.ExaAttachmentCategory { + var tree []*common.ExaAttachmentCategory + for _, category := range categories { + if category.Pid == parentID { + category.Children = a.getChildrenList(categories, category.ID) + tree = append(tree, &category) + } + } + return tree +} diff --git a/server/service/example/exa_breakpoint_continue.go b/server/service/common/exa_breakpoint_continue.go similarity index 88% rename from server/service/example/exa_breakpoint_continue.go rename to server/service/common/exa_breakpoint_continue.go index ddb6227..8360478 100644 --- a/server/service/example/exa_breakpoint_continue.go +++ b/server/service/common/exa_breakpoint_continue.go @@ -1,10 +1,10 @@ -package example +package common import ( "errors" "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/example" + "github.com/flipped-aurora/gin-vue-admin/server/model/common" "gorm.io/gorm" ) @@ -18,8 +18,8 @@ var FileUploadAndDownloadServiceApp = new(FileUploadAndDownloadService) //@param: fileMd5 string, fileName string, chunkTotal int //@return: file model.ExaFile, err error -func (e *FileUploadAndDownloadService) FindOrCreateFile(fileMd5 string, fileName string, chunkTotal int) (file example.ExaFile, err error) { - var cfile example.ExaFile +func (e *FileUploadAndDownloadService) FindOrCreateFile(fileMd5 string, fileName string, chunkTotal int) (file common.ExaFile, err error) { + var cfile common.ExaFile cfile.FileMd5 = fileMd5 cfile.FileName = fileName cfile.ChunkTotal = chunkTotal @@ -41,7 +41,7 @@ func (e *FileUploadAndDownloadService) FindOrCreateFile(fileMd5 string, fileName //@return: error func (e *FileUploadAndDownloadService) CreateFileChunk(id uint, fileChunkPath string, fileChunkNumber int) error { - var chunk example.ExaFileChunk + var chunk common.ExaFileChunk chunk.FileChunkPath = fileChunkPath chunk.ExaFileID = id chunk.FileChunkNumber = fileChunkNumber @@ -56,8 +56,8 @@ func (e *FileUploadAndDownloadService) CreateFileChunk(id uint, fileChunkPath st //@return: error func (e *FileUploadAndDownloadService) DeleteFileChunk(fileMd5 string, filePath string) error { - var chunks []example.ExaFileChunk - var file example.ExaFile + var chunks []common.ExaFileChunk + var file common.ExaFile err := global.GVA_DB.Where("file_md5 = ?", fileMd5).First(&file). Updates(map[string]interface{}{ "IsFinish": true, diff --git a/server/service/example/exa_file_upload_download.go b/server/service/common/exa_file_upload_download.go similarity index 70% rename from server/service/example/exa_file_upload_download.go rename to server/service/common/exa_file_upload_download.go index 1a34262..2e4b82e 100644 --- a/server/service/example/exa_file_upload_download.go +++ b/server/service/common/exa_file_upload_download.go @@ -1,4 +1,4 @@ -package example +package common import ( "errors" @@ -6,8 +6,8 @@ import ( "strings" "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/example" - "github.com/flipped-aurora/gin-vue-admin/server/model/example/request" + "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/utils/upload" "gorm.io/gorm" ) @@ -18,7 +18,7 @@ import ( //@param: file model.ExaFileUploadAndDownload //@return: error -func (e *FileUploadAndDownloadService) Upload(file example.ExaFileUploadAndDownload) error { +func (e *FileUploadAndDownloadService) Upload(file common.ExaFileUploadAndDownload) error { return global.GVA_DB.Create(&file).Error } @@ -28,8 +28,8 @@ func (e *FileUploadAndDownloadService) Upload(file example.ExaFileUploadAndDownl //@param: id uint //@return: model.ExaFileUploadAndDownload, error -func (e *FileUploadAndDownloadService) FindFile(id uint) (example.ExaFileUploadAndDownload, error) { - var file example.ExaFileUploadAndDownload +func (e *FileUploadAndDownloadService) FindFile(id uint) (common.ExaFileUploadAndDownload, error) { + var file common.ExaFileUploadAndDownload err := global.GVA_DB.Where("id = ?", id).First(&file).Error return file, err } @@ -40,8 +40,8 @@ func (e *FileUploadAndDownloadService) FindFile(id uint) (example.ExaFileUploadA //@param: file model.ExaFileUploadAndDownload //@return: err error -func (e *FileUploadAndDownloadService) DeleteFile(file example.ExaFileUploadAndDownload) (err error) { - var fileFromDb example.ExaFileUploadAndDownload +func (e *FileUploadAndDownloadService) DeleteFile(file common.ExaFileUploadAndDownload) (err error) { + var fileFromDb common.ExaFileUploadAndDownload fileFromDb, err = e.FindFile(file.ID) if err != nil { return @@ -55,8 +55,8 @@ func (e *FileUploadAndDownloadService) DeleteFile(file example.ExaFileUploadAndD } // EditFileName 编辑文件名或者备注 -func (e *FileUploadAndDownloadService) EditFileName(file example.ExaFileUploadAndDownload) (err error) { - var fileFromDb example.ExaFileUploadAndDownload +func (e *FileUploadAndDownloadService) EditFileName(file common.ExaFileUploadAndDownload) (err error) { + var fileFromDb common.ExaFileUploadAndDownload return global.GVA_DB.Where("id = ?", file.ID).First(&fileFromDb).Update("name", file.Name).Error } @@ -66,10 +66,10 @@ func (e *FileUploadAndDownloadService) EditFileName(file example.ExaFileUploadAn //@param: info request.ExaAttachmentCategorySearch //@return: list interface{}, total int64, err error -func (e *FileUploadAndDownloadService) GetFileRecordInfoList(info request.ExaAttachmentCategorySearch) (list []example.ExaFileUploadAndDownload, total int64, err error) { +func (e *FileUploadAndDownloadService) GetFileRecordInfoList(info commonReq.ExaAttachmentCategorySearch) (list []common.ExaFileUploadAndDownload, total int64, err error) { limit := info.PageSize offset := info.PageSize * (info.Page - 1) - db := global.GVA_DB.Model(&example.ExaFileUploadAndDownload{}) + db := global.GVA_DB.Model(&common.ExaFileUploadAndDownload{}) if len(info.Keyword) > 0 { db = db.Where("name LIKE ?", "%"+info.Keyword+"%") @@ -93,14 +93,14 @@ func (e *FileUploadAndDownloadService) GetFileRecordInfoList(info request.ExaAtt //@param: header *multipart.FileHeader, noSave string //@return: file model.ExaFileUploadAndDownload, err error -func (e *FileUploadAndDownloadService) UploadFile(header *multipart.FileHeader, noSave string, classId int) (file example.ExaFileUploadAndDownload, err error) { +func (e *FileUploadAndDownloadService) UploadFile(header *multipart.FileHeader, noSave string, classId int) (file common.ExaFileUploadAndDownload, err error) { oss := upload.NewOss() filePath, key, uploadErr := oss.UploadFile(header) if uploadErr != nil { return file, uploadErr } s := strings.Split(header.Filename, ".") - f := example.ExaFileUploadAndDownload{ + f := common.ExaFileUploadAndDownload{ Url: filePath, Name: header.Filename, ClassId: classId, @@ -109,8 +109,8 @@ func (e *FileUploadAndDownloadService) UploadFile(header *multipart.FileHeader, } if noSave == "0" { // 检查是否已存在相同key的记录 - var existingFile example.ExaFileUploadAndDownload - err = global.GVA_DB.Where(&example.ExaFileUploadAndDownload{Key: key}).First(&existingFile).Error + var existingFile common.ExaFileUploadAndDownload + err = global.GVA_DB.Where(&common.ExaFileUploadAndDownload{Key: key}).First(&existingFile).Error if errors.Is(err, gorm.ErrRecordNotFound) { return f, e.Upload(f) } @@ -125,6 +125,6 @@ func (e *FileUploadAndDownloadService) UploadFile(header *multipart.FileHeader, //@param: file model.ExaFileUploadAndDownload //@return: error -func (e *FileUploadAndDownloadService) ImportURL(file *[]example.ExaFileUploadAndDownload) error { +func (e *FileUploadAndDownloadService) ImportURL(file *[]common.ExaFileUploadAndDownload) error { return global.GVA_DB.Create(&file).Error } diff --git a/server/service/enter.go b/server/service/enter.go index 4dc990e..f83855e 100644 --- a/server/service/enter.go +++ b/server/service/enter.go @@ -1,13 +1,13 @@ package service import ( - "github.com/flipped-aurora/gin-vue-admin/server/service/example" + "github.com/flipped-aurora/gin-vue-admin/server/service/common" "github.com/flipped-aurora/gin-vue-admin/server/service/system" ) var ServiceGroupApp = new(ServiceGroup) type ServiceGroup struct { - SystemServiceGroup system.ServiceGroup - ExampleServiceGroup example.ServiceGroup + SystemServiceGroup system.ServiceGroup + CommonServiceGroup common.ServiceGroup } diff --git a/server/service/example/exa_attachment_category.go b/server/service/example/exa_attachment_category.go deleted file mode 100644 index 1e74fc9..0000000 --- a/server/service/example/exa_attachment_category.go +++ /dev/null @@ -1,66 +0,0 @@ -package example - -import ( - "errors" - "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/example" - "gorm.io/gorm" -) - -type AttachmentCategoryService struct{} - -// AddCategory 创建/更新的分类 -func (a *AttachmentCategoryService) AddCategory(req *example.ExaAttachmentCategory) (err error) { - // 检查是否已存在相同名称的分类 - if (!errors.Is(global.GVA_DB.Take(&example.ExaAttachmentCategory{}, "name = ? and pid = ?", req.Name, req.Pid).Error, gorm.ErrRecordNotFound)) { - return errors.New("分类名称已存在") - } - if req.ID > 0 { - if err = global.GVA_DB.Model(&example.ExaAttachmentCategory{}).Where("id = ?", req.ID).Updates(&example.ExaAttachmentCategory{ - Name: req.Name, - Pid: req.Pid, - }).Error; err != nil { - return err - } - } else { - if err = global.GVA_DB.Create(&example.ExaAttachmentCategory{ - Name: req.Name, - Pid: req.Pid, - }).Error; err != nil { - return err - } - } - return nil -} - -// DeleteCategory 删除分类 -func (a *AttachmentCategoryService) DeleteCategory(id *int) error { - var childCount int64 - global.GVA_DB.Model(&example.ExaAttachmentCategory{}).Where("pid = ?", id).Count(&childCount) - if childCount > 0 { - return errors.New("请先删除子级") - } - return global.GVA_DB.Where("id = ?", id).Unscoped().Delete(&example.ExaAttachmentCategory{}).Error -} - -// GetCategoryList 分类列表 -func (a *AttachmentCategoryService) GetCategoryList() (res []*example.ExaAttachmentCategory, err error) { - var fileLists []example.ExaAttachmentCategory - err = global.GVA_DB.Model(&example.ExaAttachmentCategory{}).Find(&fileLists).Error - if err != nil { - return res, err - } - return a.getChildrenList(fileLists, 0), nil -} - -// getChildrenList 子类 -func (a *AttachmentCategoryService) getChildrenList(categories []example.ExaAttachmentCategory, parentID uint) []*example.ExaAttachmentCategory { - var tree []*example.ExaAttachmentCategory - for _, category := range categories { - if category.Pid == parentID { - category.Children = a.getChildrenList(categories, category.ID) - tree = append(tree, &category) - } - } - return tree -} diff --git a/server/service/example/exa_customer.go b/server/service/example/exa_customer.go deleted file mode 100644 index cf816f5..0000000 --- a/server/service/example/exa_customer.go +++ /dev/null @@ -1,87 +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/example" - "github.com/flipped-aurora/gin-vue-admin/server/model/system" - systemService "github.com/flipped-aurora/gin-vue-admin/server/service/system" -) - -type CustomerService struct{} - -var CustomerServiceApp = new(CustomerService) - -//@author: [piexlmax](https://github.com/piexlmax) -//@function: CreateExaCustomer -//@description: 创建客户 -//@param: e model.ExaCustomer -//@return: err error - -func (exa *CustomerService) CreateExaCustomer(e example.ExaCustomer) (err error) { - err = global.GVA_DB.Create(&e).Error - return err -} - -//@author: [piexlmax](https://github.com/piexlmax) -//@function: DeleteFileChunk -//@description: 删除客户 -//@param: e model.ExaCustomer -//@return: err error - -func (exa *CustomerService) DeleteExaCustomer(e example.ExaCustomer) (err error) { - err = global.GVA_DB.Delete(&e).Error - return err -} - -//@author: [piexlmax](https://github.com/piexlmax) -//@function: UpdateExaCustomer -//@description: 更新客户 -//@param: e *model.ExaCustomer -//@return: err error - -func (exa *CustomerService) UpdateExaCustomer(e *example.ExaCustomer) (err error) { - err = global.GVA_DB.Save(e).Error - return err -} - -//@author: [piexlmax](https://github.com/piexlmax) -//@function: GetExaCustomer -//@description: 获取客户信息 -//@param: id uint -//@return: customer model.ExaCustomer, err error - -func (exa *CustomerService) GetExaCustomer(id uint) (customer example.ExaCustomer, err error) { - err = global.GVA_DB.Where("id = ?", id).First(&customer).Error - return -} - -//@author: [piexlmax](https://github.com/piexlmax) -//@function: GetCustomerInfoList -//@description: 分页获取客户列表 -//@param: sysUserAuthorityID string, info request.PageInfo -//@return: list interface{}, total int64, err error - -func (exa *CustomerService) GetCustomerInfoList(sysUserAuthorityID uint, info request.PageInfo) (list interface{}, total int64, err error) { - limit := info.PageSize - offset := info.PageSize * (info.Page - 1) - db := global.GVA_DB.Model(&example.ExaCustomer{}) - var a system.SysAuthority - a.AuthorityId = sysUserAuthorityID - auth, err := systemService.AuthorityServiceApp.GetAuthorityInfo(a) - if err != nil { - return - } - var dataId []uint - for _, v := range auth.DataAuthorityId { - dataId = append(dataId, v.AuthorityId) - } - var CustomerList []example.ExaCustomer - err = db.Where("sys_user_authority_id in ?", dataId).Count(&total).Error - if err != nil { - return CustomerList, total, err - } else { - err = db.Limit(limit).Offset(offset).Preload("SysUser").Where("sys_user_authority_id in ?", dataId).Find(&CustomerList).Error - } - return CustomerList, total, err -} diff --git a/server/service/system/ai_workflow_markdown.go b/server/service/system/ai_workflow_markdown.go deleted file mode 100644 index 96214ba..0000000 --- a/server/service/system/ai_workflow_markdown.go +++ /dev/null @@ -1,468 +0,0 @@ -package system - -import ( - "context" - "fmt" - "os" - "path/filepath" - "strings" - "time" - "unicode" - - "github.com/flipped-aurora/gin-vue-admin/server/global" - common "github.com/flipped-aurora/gin-vue-admin/server/model/common" - system "github.com/flipped-aurora/gin-vue-admin/server/model/system" - systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" - systemResp "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" -) - -const ( - aiWorkflowMarkdownRootDir = "ai-workflow-docs" - aiWorkflowAnalysisDir = "analysis" - aiWorkflowPromptDir = "prompt-workflow" -) - -func (s *aiWorkflowSession) DumpMarkdown(ctx context.Context, userID uint, info systemReq.SysAIWorkflowMarkdownDump) (result systemResp.AIWorkflowMarkdownDumpResult, err error) { - if userID == 0 { - return result, fmt.Errorf("用户未登录") - } - if info.Tab != "analysis" && info.Tab != "workflow" { - return result, fmt.Errorf("不支持的会话类型") - } - - root := strings.TrimSpace(global.GVA_CONFIG.AutoCode.Root) - if root == "" { - return result, fmt.Errorf("autocode.root 未配置") - } - - session := system.SysAIWorkflowSession{ - GVA_MODEL: global.GVA_MODEL{ID: info.ID}, - UserID: userID, - Tab: info.Tab, - Title: strings.TrimSpace(info.Title), - Summary: strings.TrimSpace(info.Summary), - ConversationID: strings.TrimSpace(info.ConversationID), - MessageID: strings.TrimSpace(info.MessageID), - CurrentNodeID: strings.TrimSpace(info.CurrentNodeID), - Settings: cloneJSONMap(info.Settings), - FormData: cloneJSONMap(info.FormData), - ResultData: cloneJSONMap(info.ResultData), - Messages: sanitizeMessages(info.Messages), - } - if strings.TrimSpace(session.Title) == "" { - session.Title = s.titleFromMessages(session.Messages) - } - if strings.TrimSpace(session.Title) == "" { - session.Title = s.titleFromForm(systemReq.SysAIWorkflowSessionUpsert{ - Tab: info.Tab, - FormData: info.FormData, - }) - } - if strings.TrimSpace(session.Summary) == "" { - session.Summary = s.summaryFromResult(info.ResultData) - } - - markdown := buildAIWorkflowMarkdown(session) - if strings.TrimSpace(markdown) == "" { - return result, fmt.Errorf("没有可落盘的内容") - } - - targetDir := filepath.Join(root, aiWorkflowMarkdownRootDir, workflowMarkdownSubDir(info.Tab)) - if err := os.MkdirAll(targetDir, 0o755); err != nil { - return result, err - } - - fileName := buildWorkflowMarkdownFileName(info.Tab, session.Title, session.ID) - filePath := filepath.Join(targetDir, fileName) - if err := os.WriteFile(filePath, []byte(markdown), 0o644); err != nil { - return result, err - } - - relativePath, relErr := filepath.Rel(root, filePath) - if relErr != nil { - relativePath = filepath.Join(aiWorkflowMarkdownRootDir, workflowMarkdownSubDir(info.Tab), fileName) - } - - return systemResp.AIWorkflowMarkdownDumpResult{ - FileName: fileName, - FilePath: filePath, - RelativePath: relativePath, - Directory: targetDir, - }, nil -} - -func workflowMarkdownSubDir(tab string) string { - if tab == "analysis" { - return aiWorkflowAnalysisDir - } - return aiWorkflowPromptDir -} - -func buildWorkflowMarkdownFileName(tab, title string, sessionID uint) string { - prefix := "prompt-workflow" - if tab == "analysis" { - prefix = "analysis" - } - - stem := sanitizeWorkflowFileStem(title) - if stem == "" { - if sessionID > 0 { - stem = fmt.Sprintf("session-%d", sessionID) - } else { - stem = "session" - } - } - - return fmt.Sprintf( - "%s-%s-%s.md", - time.Now().Format("20060102-150405"), - prefix, - stem, - ) -} - -func sanitizeWorkflowFileStem(title string) string { - var builder strings.Builder - lastDash := false - - for _, r := range strings.TrimSpace(title) { - switch { - case unicode.IsLetter(r) || unicode.IsDigit(r): - builder.WriteRune(unicode.ToLower(r)) - lastDash = false - case r == '-' || r == '_' || unicode.IsSpace(r): - if !lastDash && builder.Len() > 0 { - builder.WriteByte('-') - lastDash = true - } - } - } - - return strings.Trim(builder.String(), "-") -} - -func buildAIWorkflowMarkdown(session system.SysAIWorkflowSession) string { - if session.Tab == "analysis" { - return buildAnalysisMarkdown(session) - } - return buildPromptWorkflowMarkdown(session) -} - -func buildAnalysisMarkdown(session system.SysAIWorkflowSession) string { - var builder strings.Builder - - writeMarkdownTitle(&builder, "# AI 需求分析") - writeMarkdownMeta(&builder, - "标题", firstNonEmpty(session.Title, "未命名需求"), - "摘要", firstNonEmpty(session.Summary, getString(session.ResultData, "summary")), - "会话类型", "analysis", - "会话ID", session.ConversationID, - "消息ID", session.MessageID, - "节点ID", session.CurrentNodeID, - ) - - writeMarkdownSection(&builder, "## 原始输入") - writeMarkdownKeyValue(&builder, - "原始需求", getString(session.FormData, "requirement"), - "目标形态", getString(session.FormData, "packageType"), - "业务场景", getString(session.FormData, "businessScene"), - "额外约束", getString(session.FormData, "extraConstraints"), - "是否有客户端页面", formatBool(getBool(session.FormData, "hasClientPage")), - "客户端页面说明", getString(session.FormData, "clientPageDescription"), - "客户端额外约束", getString(session.FormData, "clientPageConstraints"), - ) - - writeMarkdownSection(&builder, "## 整理后的需求") - writeMarkdownKeyValue(&builder, - "总结", getString(session.ResultData, "summary"), - "推荐形态", getString(session.ResultData, "recommendedPackageType"), - ) - - writeStringListSection(&builder, "### 待确认信息", getStringSlice(session.ResultData, "missingInfo")) - writeStringListSection(&builder, "### 建议事项", getStringSlice(session.ResultData, "suggestions")) - - modules := getMapSlice(session.ResultData, "modules") - if len(modules) > 0 { - writeMarkdownSection(&builder, "### 模块拆解") - for index, module := range modules { - builder.WriteString(fmt.Sprintf("#### %d. %s\n\n", index+1, firstNonEmpty(getString(module, "label"), getString(module, "name"), fmt.Sprintf("模块 %d", index+1)))) - writeMarkdownKeyValue(&builder, - "模块标识", getString(module, "name"), - "模块说明", getString(module, "description"), - ) - - fields := getMapSlice(module, "fields") - if len(fields) > 0 { - builder.WriteString("| 字段 | 标识 | 类型 | 必填 | 说明 |\n") - builder.WriteString("| --- | --- | --- | --- | --- |\n") - for _, field := range fields { - builder.WriteString(fmt.Sprintf( - "| %s | %s | %s | %s | %s |\n", - markdownCell(firstNonEmpty(getString(field, "label"), getString(field, "name"), "-")), - markdownCell(firstNonEmpty(getString(field, "name"), "-")), - markdownCell(firstNonEmpty(getString(field, "type"), "string")), - markdownCell(formatBool(getBool(field, "required"))), - markdownCell(firstNonEmpty(getString(field, "description"), "-")), - )) - } - builder.WriteString("\n") - } - } - } - - clientPages := getMapSlice(session.ResultData, "clientPages") - if len(clientPages) > 0 { - writeMarkdownSection(&builder, "### 客户端页面") - for index, page := range clientPages { - builder.WriteString(fmt.Sprintf("#### %d. %s\n\n", index+1, firstNonEmpty(getString(page, "label"), getString(page, "name"), fmt.Sprintf("页面 %d", index+1)))) - writeMarkdownKeyValue(&builder, - "页面标识", getString(page, "name"), - "页面类型", getString(page, "pageType"), - "页面说明", getString(page, "description"), - ) - writeStringListSection(&builder, "目标模块", getStringSlice(page, "targetModules")) - writeStringListSection(&builder, "交互行为", getStringSlice(page, "interactions")) - writeStringListSection(&builder, "字段关系", getStringSlice(page, "relations")) - } - } - - writeMarkdownAppendix(&builder, session.ResultData) - return strings.TrimSpace(builder.String()) + "\n" -} - -func buildPromptWorkflowMarkdown(session system.SysAIWorkflowSession) string { - var builder strings.Builder - - writeMarkdownTitle(&builder, "# Prompt Workflow") - writeMarkdownMeta(&builder, - "标题", firstNonEmpty(session.Title, "未命名工作流"), - "摘要", firstNonEmpty(session.Summary, getString(session.ResultData, "summary")), - "会话类型", "workflow", - "会话ID", session.ConversationID, - "消息ID", session.MessageID, - "节点ID", session.CurrentNodeID, - ) - - writeMarkdownSection(&builder, "## 输入上下文") - writeMarkdownKeyValue(&builder, - "来源需求", getString(session.FormData, "source"), - "工作流类型", getString(session.FormData, "flowType"), - "额外约束", getString(session.FormData, "extraConstraints"), - ) - - writeMarkdownSection(&builder, "## Prompt 工作流") - writeMarkdownKeyValue(&builder, "总结", getString(session.ResultData, "summary")) - - steps := getMapSlice(session.ResultData, "steps") - if len(steps) == 0 { - rawText := getString(session.ResultData, "rawText") - if strings.TrimSpace(rawText) != "" { - builder.WriteString(rawText) - builder.WriteString("\n\n") - } - } else { - for index, step := range steps { - builder.WriteString(fmt.Sprintf("### %d. %s\n\n", index+1, firstNonEmpty(getString(step, "title"), fmt.Sprintf("步骤 %d", index+1)))) - writeMarkdownKeyValue(&builder, - "目标", getString(step, "goal"), - "建议工具", getString(step, "suggestedTool"), - "可自动执行", formatBool(getBool(step, "autoExecutable")), - "预期输出", getString(step, "expectedOutput"), - ) - prompt := getString(step, "prompt") - if strings.TrimSpace(prompt) != "" { - builder.WriteString("#### Prompt\n\n") - builder.WriteString("```text\n") - builder.WriteString(prompt) - builder.WriteString("\n```\n\n") - } - } - } - - writeMarkdownAppendix(&builder, session.ResultData) - return strings.TrimSpace(builder.String()) + "\n" -} - -func writeMarkdownTitle(builder *strings.Builder, title string) { - builder.WriteString(title) - builder.WriteString("\n\n") -} - -func writeMarkdownMeta(builder *strings.Builder, pairs ...string) { - for i := 0; i+1 < len(pairs); i += 2 { - if strings.TrimSpace(pairs[i+1]) == "" { - continue - } - builder.WriteString(fmt.Sprintf("- **%s**: %s\n", pairs[i], pairs[i+1])) - } - builder.WriteString("\n") -} - -func writeMarkdownSection(builder *strings.Builder, title string) { - builder.WriteString(title) - builder.WriteString("\n\n") -} - -func writeMarkdownKeyValue(builder *strings.Builder, pairs ...string) { - hasValue := false - for i := 0; i+1 < len(pairs); i += 2 { - if strings.TrimSpace(pairs[i+1]) == "" { - continue - } - hasValue = true - builder.WriteString(fmt.Sprintf("- **%s**: %s\n", pairs[i], pairs[i+1])) - } - if hasValue { - builder.WriteString("\n") - } -} - -func writeStringListSection(builder *strings.Builder, title string, list []string) { - if len(list) == 0 { - return - } - builder.WriteString(title) - builder.WriteString("\n\n") - for _, item := range list { - if strings.TrimSpace(item) == "" { - continue - } - builder.WriteString("- ") - builder.WriteString(item) - builder.WriteString("\n") - } - builder.WriteString("\n") -} - -func writeMarkdownAppendix(builder *strings.Builder, resultData common.JSONMap) { - rawText := getString(resultData, "rawText") - rawJSON := getString(resultData, "rawJson") - - if strings.TrimSpace(rawText) != "" { - builder.WriteString("## 原始文本\n\n") - builder.WriteString("```text\n") - builder.WriteString(rawText) - builder.WriteString("\n```\n\n") - } - - if strings.TrimSpace(rawJSON) != "" { - builder.WriteString("## 原始结构化数据\n\n") - builder.WriteString("```json\n") - builder.WriteString(rawJSON) - builder.WriteString("\n```\n") - } -} - -func getString(data map[string]interface{}, key string) string { - if len(data) == 0 { - return "" - } - value, ok := data[key] - if !ok || value == nil { - return "" - } - switch typed := value.(type) { - case string: - return strings.TrimSpace(typed) - case fmt.Stringer: - return strings.TrimSpace(typed.String()) - default: - return strings.TrimSpace(fmt.Sprintf("%v", value)) - } -} - -func getBool(data map[string]interface{}, key string) bool { - if len(data) == 0 { - return false - } - value, ok := data[key] - if !ok || value == nil { - return false - } - switch typed := value.(type) { - case bool: - return typed - case string: - return strings.EqualFold(strings.TrimSpace(typed), "true") - default: - return false - } -} - -func getStringSlice(data map[string]interface{}, key string) []string { - value, ok := data[key] - if !ok || value == nil { - return nil - } - return toStringSlice(value) -} - -func getMapSlice(data map[string]interface{}, key string) []map[string]interface{} { - value, ok := data[key] - if !ok || value == nil { - return nil - } - return toMapSlice(value) -} - -func toStringSlice(value interface{}) []string { - switch typed := value.(type) { - case []string: - result := make([]string, 0, len(typed)) - for _, item := range typed { - if strings.TrimSpace(item) != "" { - result = append(result, strings.TrimSpace(item)) - } - } - return result - case []interface{}: - result := make([]string, 0, len(typed)) - for _, item := range typed { - text := strings.TrimSpace(fmt.Sprintf("%v", item)) - if text != "" { - result = append(result, text) - } - } - return result - default: - text := strings.TrimSpace(fmt.Sprintf("%v", value)) - if text == "" || text == "" { - return nil - } - return []string{text} - } -} - -func toMapSlice(value interface{}) []map[string]interface{} { - switch typed := value.(type) { - case []map[string]interface{}: - return typed - case []interface{}: - result := make([]map[string]interface{}, 0, len(typed)) - for _, item := range typed { - if row, ok := item.(map[string]interface{}); ok { - result = append(result, row) - } - } - return result - default: - return nil - } -} - -func formatBool(value bool) string { - if value { - return "是" - } - return "否" -} - -func markdownCell(value string) string { - text := strings.TrimSpace(value) - if text == "" { - return "-" - } - text = strings.ReplaceAll(text, "\n", "
") - text = strings.ReplaceAll(text, "|", "\\|") - return text -} diff --git a/server/service/system/ai_workflow_session.go b/server/service/system/ai_workflow_session.go deleted file mode 100644 index 4c6896e..0000000 --- a/server/service/system/ai_workflow_session.go +++ /dev/null @@ -1,184 +0,0 @@ -package system - -import ( - "context" - "errors" - "strings" - - "github.com/flipped-aurora/gin-vue-admin/server/global" - system "github.com/flipped-aurora/gin-vue-admin/server/model/system" - systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" - systemResp "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" - "gorm.io/gorm" -) - -type aiWorkflowSession struct{} - -var AIWorkflowSessionServiceApp = new(aiWorkflowSession) - -func (s *aiWorkflowSession) Save(ctx context.Context, userID uint, info systemReq.SysAIWorkflowSessionUpsert) (session system.SysAIWorkflowSession, err error) { - if userID == 0 { - return session, errors.New("用户未登录") - } - if info.Tab != "analysis" && info.Tab != "workflow" { - return session, errors.New("不支持的会话类型") - } - - db := global.GVA_DB.WithContext(ctx) - if info.ID != 0 { - err = db.Where("id = ? AND user_id = ?", info.ID, userID).First(&session).Error - if err != nil { - return session, err - } - } - - session.UserID = userID - session.Tab = info.Tab - session.Title = truncateText(firstNonEmpty(strings.TrimSpace(info.Title), s.titleFromMessages(info.Messages), s.titleFromForm(info)), 255) - session.Summary = strings.TrimSpace(firstNonEmpty(info.Summary, s.summaryFromResult(info.ResultData))) - session.ConversationID = strings.TrimSpace(info.ConversationID) - session.MessageID = strings.TrimSpace(info.MessageID) - session.Settings = cloneJSONMap(info.Settings) - session.FormData = cloneJSONMap(info.FormData) - session.ResultData = cloneJSONMap(info.ResultData) - session.Messages = sanitizeMessages(info.Messages) - session.CurrentNodeID = strings.TrimSpace(info.CurrentNodeID) - if session.CurrentNodeID == "" { - session.CurrentNodeID = lastAssistantMessageID(session.Messages) - } - - if session.ID == 0 { - err = db.Create(&session).Error - return session, err - } - err = db.Save(&session).Error - return session, err -} - -func (s *aiWorkflowSession) GetList(ctx context.Context, userID uint, info systemReq.SysAIWorkflowSessionSearch) (list []systemResp.SysAIWorkflowSessionListItem, total int64, err error) { - db := global.GVA_DB.WithContext(ctx).Model(&system.SysAIWorkflowSession{}).Where("user_id = ?", userID) - if tab := strings.TrimSpace(info.Tab); tab != "" { - db = db.Where("tab = ?", tab) - } - if keyword := strings.TrimSpace(info.Keyword); keyword != "" { - like := "%" + keyword + "%" - db = db.Where("title LIKE ? OR summary LIKE ?", like, like) - } - - err = db.Count(&total).Error - if err != nil { - return nil, 0, err - } - - err = db.Select("id", "created_at", "updated_at", "tab", "title", "summary", "conversation_id", "current_node_id"). - Scopes(info.Paginate()). - Order("updated_at desc"). - Find(&list).Error - return list, total, err -} - -func (s *aiWorkflowSession) GetDetail(ctx context.Context, userID uint, id uint) (session system.SysAIWorkflowSession, err error) { - err = global.GVA_DB.WithContext(ctx).Where("id = ? AND user_id = ?", id, userID).First(&session).Error - return session, err -} - -func (s *aiWorkflowSession) Delete(ctx context.Context, userID uint, id uint) error { - result := global.GVA_DB.WithContext(ctx).Where("id = ? AND user_id = ?", id, userID).Delete(&system.SysAIWorkflowSession{}) - if result.Error != nil { - return result.Error - } - if result.RowsAffected == 0 { - return gorm.ErrRecordNotFound - } - return nil -} - -func (s *aiWorkflowSession) titleFromMessages(messages []system.AIWorkflowMessage) string { - for _, item := range messages { - if item.Role == "user" && strings.TrimSpace(item.Content) != "" { - return strings.TrimSpace(item.Content) - } - } - return "" -} - -func (s *aiWorkflowSession) titleFromForm(info systemReq.SysAIWorkflowSessionUpsert) string { - if info.Tab == "analysis" { - if requirement, ok := info.FormData["requirement"].(string); ok { - return strings.TrimSpace(requirement) - } - } - if source, ok := info.FormData["source"].(string); ok { - return strings.TrimSpace(source) - } - return "" -} - -func (s *aiWorkflowSession) summaryFromResult(resultData map[string]interface{}) string { - if resultData == nil { - return "" - } - if summary, ok := resultData["summary"].(string); ok { - return strings.TrimSpace(summary) - } - return "" -} - -func sanitizeMessages(messages []system.AIWorkflowMessage) []system.AIWorkflowMessage { - if len(messages) == 0 { - return []system.AIWorkflowMessage{} - } - result := make([]system.AIWorkflowMessage, 0, len(messages)) - for _, item := range messages { - result = append(result, system.AIWorkflowMessage{ - ID: strings.TrimSpace(item.ID), - Role: strings.TrimSpace(item.Role), - Content: item.Content, - Snapshot: cloneJSONMap(item.Snapshot), - ConversationID: strings.TrimSpace(item.ConversationID), - MessageID: strings.TrimSpace(item.MessageID), - CreatedAt: strings.TrimSpace(item.CreatedAt), - }) - } - return result -} - -func cloneJSONMap(source map[string]interface{}) map[string]interface{} { - if len(source) == 0 { - return map[string]interface{}{} - } - target := make(map[string]interface{}, len(source)) - for key, value := range source { - target[key] = value - } - return target -} - -func lastAssistantMessageID(messages []system.AIWorkflowMessage) string { - for i := len(messages) - 1; i >= 0; i-- { - if messages[i].Role == "assistant" && strings.TrimSpace(messages[i].ID) != "" { - return messages[i].ID - } - } - return "" -} - -func firstNonEmpty(values ...string) string { - for _, value := range values { - if strings.TrimSpace(value) != "" { - return strings.TrimSpace(value) - } - } - return "" -} - -func truncateText(value string, size int) string { - if size <= 0 { - return "" - } - runes := []rune(strings.TrimSpace(value)) - if len(runes) <= size { - return string(runes) - } - return string(runes[:size]) -} diff --git a/server/service/system/auto_code_history.go b/server/service/system/auto_code_history.go deleted file mode 100644 index d9d1676..0000000 --- a/server/service/system/auto_code_history.go +++ /dev/null @@ -1,217 +0,0 @@ -package system - -import ( - "context" - "encoding/json" - "fmt" - "github.com/flipped-aurora/gin-vue-admin/server/utils/ast" - "github.com/pkg/errors" - "path" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/flipped-aurora/gin-vue-admin/server/global" - common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" - model "github.com/flipped-aurora/gin-vue-admin/server/model/system" - request "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" - "github.com/flipped-aurora/gin-vue-admin/server/utils" - - "go.uber.org/zap" -) - -var AutocodeHistory = new(autoCodeHistory) - -type autoCodeHistory struct{} - -// Create 创建代码生成器历史记录 -// Author [SliverHorn](https://github.com/SliverHorn) -// Author [songzhibin97](https://github.com/songzhibin97) -func (s *autoCodeHistory) Create(ctx context.Context, info request.SysAutoHistoryCreate) error { - create := info.Create() - err := global.GVA_DB.WithContext(ctx).Create(&create).Error - if err != nil { - return errors.Wrap(err, "创建失败!") - } - return nil -} - -// First 根据id获取代码生成器历史的数据 -// Author [SliverHorn](https://github.com/SliverHorn) -// Author [songzhibin97](https://github.com/songzhibin97) -func (s *autoCodeHistory) First(ctx context.Context, info common.GetById) (string, error) { - var meta string - err := global.GVA_DB.WithContext(ctx).Model(model.SysAutoCodeHistory{}).Where("id = ?", info.ID).Pluck("request", &meta).Error - if err != nil { - return "", errors.Wrap(err, "获取失败!") - } - return meta, nil -} - -// Repeat 检测重复 -// Author [SliverHorn](https://github.com/SliverHorn) -// Author [songzhibin97](https://github.com/songzhibin97) -func (s *autoCodeHistory) Repeat(businessDB, structName, abbreviation, Package string) bool { - var count int64 - global.GVA_DB.Model(&model.SysAutoCodeHistory{}).Where("business_db = ? and (struct_name = ? OR abbreviation = ?) and package = ? and flag = ?", businessDB, structName, abbreviation, Package, 0).Count(&count).Debug() - return count > 0 -} - -// RollBack 回滚 -// Author [SliverHorn](https://github.com/SliverHorn) -// Author [songzhibin97](https://github.com/songzhibin97) -func (s *autoCodeHistory) RollBack(ctx context.Context, info request.SysAutoHistoryRollBack) error { - var history model.SysAutoCodeHistory - err := global.GVA_DB.Where("id = ?", info.ID).First(&history).Error - if err != nil { - return err - } - if history.ExportTemplateID != 0 { - err = global.GVA_DB.Delete(&model.SysExportTemplate{}, "id = ?", history.ExportTemplateID).Error - if err != nil { - return err - } - } - if info.DeleteApi { - ids := info.ApiIds(history) - err = ApiServiceApp.DeleteApisByIds(ids) - if err != nil { - global.GVA_LOG.Error("ClearTag DeleteApiByIds:", zap.Error(err)) - } - } // 清除API表 - if info.DeleteMenu { - err = BaseMenuServiceApp.DeleteBaseMenu(int(history.MenuID)) - if err != nil { - return errors.Wrap(err, "删除菜单失败!") - } - } // 清除菜单表 - if info.DeleteTable { - err = s.DropTable(history.BusinessDB, history.Table) - if err != nil { - return errors.Wrap(err, "删除表失败!") - } - } // 删除表 - templates := make(map[string]string, len(history.Templates)) - for key, template := range history.Templates { - { - server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server) - keys := strings.Split(key, "/") - key = filepath.Join(keys...) - key = strings.TrimPrefix(key, server) - } // key - { - web := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot()) - server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server) - slices := strings.Split(template, "/") - template = filepath.Join(slices...) - ext := path.Ext(template) - switch ext { - case ".js", ".vue": - template = filepath.Join(web, template) - case ".go": - template = filepath.Join(server, template) - } - } // value - templates[key] = template - } - history.Templates = templates - for key, value := range history.Injections { - var injection ast.Ast - switch key { - case ast.TypePackageApiEnter, ast.TypePackageRouterEnter, ast.TypePackageServiceEnter: - - case ast.TypePackageApiModuleEnter, ast.TypePackageRouterModuleEnter, ast.TypePackageServiceModuleEnter: - var entity ast.PackageModuleEnter - _ = json.Unmarshal([]byte(value), &entity) - injection = &entity - case ast.TypePackageInitializeGorm: - var entity ast.PackageInitializeGorm - _ = json.Unmarshal([]byte(value), &entity) - injection = &entity - case ast.TypePackageInitializeRouter: - var entity ast.PackageInitializeRouter - _ = json.Unmarshal([]byte(value), &entity) - injection = &entity - case ast.TypePluginGen: - var entity ast.PluginGen - _ = json.Unmarshal([]byte(value), &entity) - injection = &entity - case ast.TypePluginApiEnter, ast.TypePluginRouterEnter, ast.TypePluginServiceEnter: - var entity ast.PluginEnter - _ = json.Unmarshal([]byte(value), &entity) - injection = &entity - case ast.TypePluginInitializeGorm: - var entity ast.PluginInitializeGorm - _ = json.Unmarshal([]byte(value), &entity) - injection = &entity - case ast.TypePluginInitializeRouter: - var entity ast.PluginInitializeRouter - _ = json.Unmarshal([]byte(value), &entity) - injection = &entity - } - if injection == nil { - continue - } - file, _ := injection.Parse("", nil) - if file != nil { - _ = injection.Rollback(file) - err = injection.Format("", nil, file) - if err != nil { - return err - } - fmt.Printf("[filepath:%s]回滚注入代码成功!\n", key) - } - } // 清除注入代码 - removeBasePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, "rm_file", strconv.FormatInt(int64(time.Now().Nanosecond()), 10)) - for _, value := range history.Templates { - if !filepath.IsAbs(value) { - continue - } - removePath := filepath.Join(removeBasePath, strings.TrimPrefix(value, global.GVA_CONFIG.AutoCode.Root)) - err = utils.FileMove(value, removePath) - if err != nil { - return errors.Wrapf(err, "[src:%s][dst:%s]文件移动失败!", value, removePath) - } - } // 移动文件 - err = global.GVA_DB.WithContext(ctx).Model(&model.SysAutoCodeHistory{}).Where("id = ?", info.ID).Update("flag", 1).Error - if err != nil { - return errors.Wrap(err, "更新失败!") - } - return nil -} - -// Delete 删除历史数据 -// Author [SliverHorn](https://github.com/SliverHorn) -// Author [songzhibin97](https://github.com/songzhibin97) -func (s *autoCodeHistory) Delete(ctx context.Context, info common.GetById) error { - err := global.GVA_DB.WithContext(ctx).Where("id = ?", info.Uint()).Delete(&model.SysAutoCodeHistory{}).Error - if err != nil { - return errors.Wrap(err, "删除失败!") - } - return nil -} - -// GetList 获取系统历史数据 -// Author [SliverHorn](https://github.com/SliverHorn) -// Author [songzhibin97](https://github.com/songzhibin97) -func (s *autoCodeHistory) GetList(ctx context.Context, info common.PageInfo) (list []model.SysAutoCodeHistory, total int64, err error) { - var entities []model.SysAutoCodeHistory - db := global.GVA_DB.WithContext(ctx).Model(&model.SysAutoCodeHistory{}) - err = db.Count(&total).Error - if err != nil { - return nil, total, err - } - err = db.Scopes(info.Paginate()).Order("updated_at desc").Find(&entities).Error - return entities, total, err -} - -// DropTable 获取指定数据库和指定数据表的所有字段名,类型值等 -// @author: [piexlmax](https://github.com/piexlmax) -func (s *autoCodeHistory) DropTable(BusinessDb, tableName string) error { - if BusinessDb != "" { - return global.MustGetGlobalDBByDBName(BusinessDb).Exec("DROP TABLE " + tableName).Error - } else { - return global.GVA_DB.Exec("DROP TABLE " + tableName).Error - } -} diff --git a/server/service/system/auto_code_llm.go b/server/service/system/auto_code_llm.go deleted file mode 100644 index fdb1d54..0000000 --- a/server/service/system/auto_code_llm.go +++ /dev/null @@ -1,125 +0,0 @@ -package system - -import ( - "context" - "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" - commonResp "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" - "github.com/flipped-aurora/gin-vue-admin/server/utils/request" - "github.com/goccy/go-json" -) - -func (s *AutoCodeService) LLMAuto(ctx context.Context, llm common.JSONMap) (interface{}, error) { - path, err := buildLLMAutoPath(llm) - if err != nil { - return nil, err - } - - res, err := request.HttpRequestWithContextAndTimeout( - ctx, - path, - http.MethodPost, - nil, - nil, - llm, - ) - if err != nil { - return nil, fmt.Errorf("调用上游大模型服务失败: %w", err) - } - defer res.Body.Close() - - body, err := io.ReadAll(res.Body) - if err != nil { - return nil, fmt.Errorf("读取大模型响应失败: %w", err) - } - - bodyPreview := previewResponseBody(body) - contentType := res.Header.Get("Content-Type") - if res.StatusCode < 200 || res.StatusCode >= 300 { - return nil, fmt.Errorf("上游大模型服务返回非 2xx: status=%d content-type=%s body=%s", res.StatusCode, contentType, bodyPreview) - } - - var resStruct commonResp.Response - if err = json.Unmarshal(body, &resStruct); err != nil { - return nil, fmt.Errorf("解析大模型响应失败: status=%d content-type=%s body=%s err=%w", res.StatusCode, contentType, bodyPreview, err) - } - - if resStruct.Code != commonResp.SUCCESS { - return nil, fmt.Errorf("大模型服务返回业务错误: code=%d msg=%s body=%s", resStruct.Code, resStruct.Msg, bodyPreview) - } - - return resStruct.Data, nil -} - -func (s *AutoCodeService) LLMAutoStream(ctx context.Context, llm common.JSONMap) (*http.Response, error) { - path, err := buildLLMAutoPath(llm) - if err != nil { - return nil, err - } - - payload := cloneLLMAutoJSONMap(llm) - responseMode := strings.ToLower(strings.TrimSpace(fmt.Sprintf("%v", payload["response_mode"]))) - if responseMode == "" { - payload["response_mode"] = "streaming" - } - - res, err := request.HttpRequestWithContextAndTimeout( - ctx, - path, - http.MethodPost, - map[string]string{ - "Accept": "text/event-stream", - "Accept-Encoding": "identity", // 禁止 gzip,避免 SSE 流被压缩导致缓冲卡住 - "Cache-Control": "no-cache", - }, - nil, - payload, - -1, // 不设置 client.Timeout,SSE 流的生命周期由 ctx 控制 - ) - if err != nil { - return nil, fmt.Errorf("调用上游大模型流式服务失败: %w", err) - } - return res, nil -} - -func buildLLMAutoPath(llm common.JSONMap) (string, error) { - if global.GVA_CONFIG.AutoCode.AiPath == "" { - return "", errors.New("请先前往插件市场个人中心获取 AiPath 并填写到 config.yaml 中") - } - - mode := strings.TrimSpace(fmt.Sprintf("%v", llm["mode"])) - if mode == "" { - return "", errors.New("llmAuto 缺少 mode 参数") - } - - return strings.ReplaceAll(global.GVA_CONFIG.AutoCode.AiPath, "{FUNC}", mode), nil -} - -func cloneLLMAutoJSONMap(src common.JSONMap) common.JSONMap { - dst := make(common.JSONMap, len(src)) - for key, value := range src { - dst[key] = value - } - return dst -} - -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 "" - } - runes := []rune(text) - if len(runes) > 300 { - return string(runes[:300]) + "..." - } - return text -} diff --git a/server/service/system/auto_code_mcp.go b/server/service/system/auto_code_mcp.go deleted file mode 100644 index 3b6eb84..0000000 --- a/server/service/system/auto_code_mcp.go +++ /dev/null @@ -1,45 +0,0 @@ -package system - -import ( - "context" - "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" - "github.com/flipped-aurora/gin-vue-admin/server/utils" - "github.com/flipped-aurora/gin-vue-admin/server/utils/autocode" - "os" - "path/filepath" - "text/template" -) - -func (s *autoCodeTemplate) CreateMcp(ctx context.Context, info request.AutoMcpTool) (toolFilePath string, err error) { - mcpTemplatePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "mcp", "tools.tpl") - mcpToolPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "mcp") - - var files *template.Template - - templateName := filepath.Base(mcpTemplatePath) - - files, err = template.New(templateName).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(mcpTemplatePath) - if err != nil { - return - } - - fileName := utils.HumpToUnderscore(info.Name) - - toolFilePath = filepath.Join(mcpToolPath, fileName+".go") - - f, err := os.Create(toolFilePath) - if err != nil { - return - } - defer f.Close() - - // 执行模板,将内容写入文件 - err = files.Execute(f, info) - if err != nil { - return - } - - return - -} diff --git a/server/service/system/auto_code_package.go b/server/service/system/auto_code_package.go deleted file mode 100644 index 763c56f..0000000 --- a/server/service/system/auto_code_package.go +++ /dev/null @@ -1,743 +0,0 @@ -package system - -import ( - "context" - "fmt" - "go/token" - "os" - "path/filepath" - "strings" - "text/template" - - "github.com/flipped-aurora/gin-vue-admin/server/global" - common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" - model "github.com/flipped-aurora/gin-vue-admin/server/model/system" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" - "github.com/flipped-aurora/gin-vue-admin/server/utils" - "github.com/flipped-aurora/gin-vue-admin/server/utils/ast" - "github.com/flipped-aurora/gin-vue-admin/server/utils/autocode" - "github.com/pkg/errors" - "gorm.io/gorm" -) - -var AutoCodePackage = new(autoCodePackage) - -type autoCodePackage struct{} - -// Create 创建包信息 -// @author: [piexlmax](https://github.com/piexlmax) -// @author: [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodePackage) Create(ctx context.Context, info *request.SysAutoCodePackageCreate) error { - switch { - case info.Template == "": - return errors.New("模板不能为空!") - case info.Template == "page": - return errors.New("page为表单生成器!") - case info.PackageName == "": - return errors.New("PackageName不能为空!") - case token.IsKeyword(info.PackageName): - return errors.Errorf("%s为go的关键字!", info.PackageName) - case info.Template == "package": - if info.PackageName == "system" || info.PackageName == "example" { - return errors.New("不能使用已保留的package name") - } - default: - break - } - if !errors.Is(global.GVA_DB.Where("package_name = ? and template = ?", info.PackageName, info.Template).First(&model.SysAutoCodePackage{}).Error, gorm.ErrRecordNotFound) { - return errors.New("存在相同PackageName") - } - create := info.Create() - return global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { - err := tx.Create(&create).Error - if err != nil { - return errors.Wrap(err, "创建失败!") - } - code := info.AutoCode() - _, asts, creates, err := s.templates(ctx, create, code, true) - if err != nil { - return err - } - for key, value := range creates { // key 为 模版绝对路径 - var files *template.Template - files, err = template.New(filepath.Base(key)).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(key) - if err != nil { - return errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", key) - } - err = os.MkdirAll(filepath.Dir(value), os.ModePerm) - if err != nil { - return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", value) - } - var file *os.File - file, err = os.Create(value) - if err != nil { - return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", value) - } - err = files.Execute(file, code) - _ = file.Close() - if err != nil { - return errors.Wrapf(err, "[filepath:%s]生成失败!", value) - } - fmt.Printf("[template:%s][filepath:%s]生成成功!\n", key, value) - } - for key, value := range asts { - keys := strings.Split(key, "=>") - if len(keys) == 2 { - switch keys[1] { - case ast.TypePluginInitializeV2, ast.TypePackageApiEnter, ast.TypePackageRouterEnter, ast.TypePackageServiceEnter: - file, _ := value.Parse("", nil) - if file != nil { - err = value.Injection(file) - if err != nil { - return err - } - err = value.Format("", nil, file) - if err != nil { - return err - } - } - fmt.Printf("[type:%s]注入成功!\n", key) - } - } - } - return nil - }) -} - -// Delete 删除包记录 -// @author: [piexlmax](https://github.com/piexlmax) -// @author: [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodePackage) Delete(ctx context.Context, info common.GetById) error { - err := global.GVA_DB.WithContext(ctx).Delete(&model.SysAutoCodePackage{}, info.Uint()).Error - if err != nil { - return errors.Wrap(err, "删除失败!") - } - return nil -} - -// DeleteByNames -// @author: [piexlmax](https://github.com/piexlmax) -// @author: [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodePackage) DeleteByNames(ctx context.Context, names []string) error { - if len(names) == 0 { - return nil - } - err := global.GVA_DB.WithContext(ctx).Where("package_name IN ?", names).Delete(&model.SysAutoCodePackage{}).Error - if err != nil { - return errors.Wrap(err, "删除失败!") - } - return nil -} - -// All 获取所有包 -// @author: [piexlmax](https://github.com/piexlmax) -// @author: [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodePackage) All(ctx context.Context) (entities []model.SysAutoCodePackage, err error) { - server := make([]model.SysAutoCodePackage, 0) - plugin := make([]model.SysAutoCodePackage, 0) - serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service") - pluginPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin") - serverDir, err := os.ReadDir(serverPath) - if err != nil { - return nil, errors.Wrap(err, "读取service文件夹失败!") - } - pluginDir, err := os.ReadDir(pluginPath) - if err != nil { - return nil, errors.Wrap(err, "读取plugin文件夹失败!") - } - for i := 0; i < len(serverDir); i++ { - if serverDir[i].IsDir() { - serverPackage := model.SysAutoCodePackage{ - PackageName: serverDir[i].Name(), - Template: "package", - Label: serverDir[i].Name() + "包", - Desc: "系统自动读取" + serverDir[i].Name() + "包", - Module: global.GVA_CONFIG.AutoCode.Module, - } - server = append(server, serverPackage) - } - } - for i := 0; i < len(pluginDir); i++ { - if pluginDir[i].IsDir() { - dirNameMap := map[string]bool{ - "api": true, - "config": true, - "initialize": true, - "plugin": true, - "router": true, - "service": true, - } - dir, e := os.ReadDir(filepath.Join(pluginPath, pluginDir[i].Name())) - if e != nil { - return nil, errors.Wrap(err, "读取plugin文件夹失败!") - } - //dir目录需要包含所有的dirNameMap - for k := 0; k < len(dir); k++ { - if dir[k].IsDir() { - if ok := dirNameMap[dir[k].Name()]; ok { - delete(dirNameMap, dir[k].Name()) - } - } - } - - var desc string - if len(dirNameMap) == 0 { - // 完全符合标准结构 - desc = "系统自动读取" + pluginDir[i].Name() + "插件,使用前请确认是否为v2版本插件" - } else { - // 缺少某些结构,生成警告描述 - var missingDirs []string - for dirName := range dirNameMap { - missingDirs = append(missingDirs, dirName) - } - desc = fmt.Sprintf("系统自动读取,但是缺少 %s 结构,不建议自动化和mcp使用", strings.Join(missingDirs, "、")) - } - - pluginPackage := model.SysAutoCodePackage{ - PackageName: pluginDir[i].Name(), - Template: "plugin", - Label: pluginDir[i].Name() + "插件", - Desc: desc, - Module: global.GVA_CONFIG.AutoCode.Module, - } - plugin = append(plugin, pluginPackage) - } - } - - err = global.GVA_DB.WithContext(ctx).Find(&entities).Error - if err != nil { - return nil, errors.Wrap(err, "获取所有包失败!") - } - entitiesMap := make(map[string]model.SysAutoCodePackage) - for i := 0; i < len(entities); i++ { - entitiesMap[entities[i].PackageName] = entities[i] - } - createEntity := []model.SysAutoCodePackage{} - for i := 0; i < len(server); i++ { - if _, ok := entitiesMap[server[i].PackageName]; !ok { - if server[i].Template == "package" { - createEntity = append(createEntity, server[i]) - } - } - } - for i := 0; i < len(plugin); i++ { - if _, ok := entitiesMap[plugin[i].PackageName]; !ok { - if plugin[i].Template == "plugin" { - createEntity = append(createEntity, plugin[i]) - } - } - } - - if len(createEntity) > 0 { - err = global.GVA_DB.WithContext(ctx).Create(&createEntity).Error - if err != nil { - return nil, errors.Wrap(err, "同步失败!") - } - entities = append(entities, createEntity...) - } - - // 处理数据库存在但实体文件不存在的情况 - 删除数据库中对应的数据 - existingPackageNames := make(map[string]bool) - // 收集所有存在的包名 - for i := 0; i < len(server); i++ { - existingPackageNames[server[i].PackageName] = true - } - for i := 0; i < len(plugin); i++ { - existingPackageNames[plugin[i].PackageName] = true - } - - // 找出需要删除的数据库记录 - deleteEntityIDs := []uint{} - for i := 0; i < len(entities); i++ { - if !existingPackageNames[entities[i].PackageName] { - deleteEntityIDs = append(deleteEntityIDs, entities[i].ID) - } - } - - // 删除数据库中不存在文件的记录 - if len(deleteEntityIDs) > 0 { - err = global.GVA_DB.WithContext(ctx).Delete(&model.SysAutoCodePackage{}, deleteEntityIDs).Error - if err != nil { - return nil, errors.Wrap(err, "删除不存在的包记录失败!") - } - // 从返回结果中移除已删除的记录 - filteredEntities := []model.SysAutoCodePackage{} - for i := 0; i < len(entities); i++ { - if existingPackageNames[entities[i].PackageName] { - filteredEntities = append(filteredEntities, entities[i]) - } - } - entities = filteredEntities - } - - return entities, nil -} - -// Templates 获取所有模版文件夹 -// @author: [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodePackage) Templates(ctx context.Context) ([]string, error) { - templates := make([]string, 0) - entries, err := os.ReadDir("resource") - if err != nil { - return nil, errors.Wrap(err, "读取模版文件夹失败!") - } - for i := 0; i < len(entries); i++ { - if entries[i].IsDir() { - if entries[i].Name() == "page" { - continue - } // page 为表单生成器 - if entries[i].Name() == "function" { - continue - } // function 为函数生成器 - if entries[i].Name() == "preview" { - continue - } // preview 为预览代码生成器的代码 - if entries[i].Name() == "mcp" { - continue - } // preview 为mcp生成器的代码 - templates = append(templates, entries[i].Name()) - } - } - return templates, nil -} - -func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCodePackage, info request.AutoCode, isPackage bool) (code map[string]string, asts map[string]ast.Ast, creates map[string]string, err error) { - code = make(map[string]string) - asts = make(map[string]ast.Ast) - creates = make(map[string]string) - templateDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", entity.Template) - templateDirs, err := os.ReadDir(templateDir) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", templateDir) - } - for i := 0; i < len(templateDirs); i++ { - second := filepath.Join(templateDir, templateDirs[i].Name()) - switch templateDirs[i].Name() { - case "server": - if !info.GenerateServer && !isPackage { - break - } - var secondDirs []os.DirEntry - secondDirs, err = os.ReadDir(second) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", second) - } - for j := 0; j < len(secondDirs); j++ { - if secondDirs[j].Name() == ".DS_Store" { - continue - } - three := filepath.Join(second, secondDirs[j].Name()) - if !secondDirs[j].IsDir() { - ext := filepath.Ext(secondDirs[j].Name()) - if ext != ".tpl" { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", three) - } - name := strings.TrimSuffix(secondDirs[j].Name(), ext) - if name == "main.go" || name == "plugin.go" { - pluginInitialize := &ast.PluginInitializeV2{ - Type: ast.TypePluginInitializeV2, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, name), - PluginPath: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "register.go"), - ImportPath: fmt.Sprintf(`"%s/plugin/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), - PackageName: entity.PackageName, - } - asts[pluginInitialize.PluginPath+"=>"+pluginInitialize.Type.String()] = pluginInitialize - creates[three] = pluginInitialize.Path - continue - } - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", three) - } - switch secondDirs[j].Name() { - case "api", "router", "service": - var threeDirs []os.DirEntry - threeDirs, err = os.ReadDir(three) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three) - } - for k := 0; k < len(threeDirs); k++ { - if threeDirs[k].Name() == ".DS_Store" { - continue - } - four := filepath.Join(three, threeDirs[k].Name()) - if threeDirs[k].IsDir() { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four) - } - ext := filepath.Ext(four) - if ext != ".tpl" { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four) - } - api := strings.Index(threeDirs[k].Name(), "api") - hasEnter := strings.Index(threeDirs[k].Name(), "enter") - router := strings.Index(threeDirs[k].Name(), "router") - service := strings.Index(threeDirs[k].Name(), "service") - if router == -1 && api == -1 && service == -1 && hasEnter == -1 { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four) - } - if entity.Template == "package" { - create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, info.HumpPackageName+".go") - if api != -1 { - create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", entity.PackageName, info.HumpPackageName+".go") - } - if hasEnter != -1 { - isApi := strings.Index(secondDirs[j].Name(), "api") - isRouter := strings.Index(secondDirs[j].Name(), "router") - isService := strings.Index(secondDirs[j].Name(), "service") - if isApi != -1 { - packageApiEnter := &ast.PackageEnter{ - Type: ast.TypePackageApiEnter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", "enter.go"), - ImportPath: fmt.Sprintf(`"%s/%s/%s/%s"`, global.GVA_CONFIG.AutoCode.Module, "api", "v1", entity.PackageName), - StructName: utils.FirstUpper(entity.PackageName) + "ApiGroup", - PackageName: entity.PackageName, - PackageStructName: "ApiGroup", - } - asts[packageApiEnter.Path+"=>"+packageApiEnter.Type.String()] = packageApiEnter - packageApiModuleEnter := &ast.PackageModuleEnter{ - Type: ast.TypePackageApiModuleEnter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", entity.PackageName, "enter.go"), - ImportPath: fmt.Sprintf(`"%s/service"`, global.GVA_CONFIG.AutoCode.Module), - StructName: info.StructName + "Api", - AppName: "ServiceGroupApp", - GroupName: utils.FirstUpper(entity.PackageName) + "ServiceGroup", - ModuleName: info.Abbreviation + "Service", - PackageName: "service", - ServiceName: info.StructName + "Service", - } - asts[packageApiModuleEnter.Path+"=>"+packageApiModuleEnter.Type.String()] = packageApiModuleEnter - creates[four] = packageApiModuleEnter.Path - } - if isRouter != -1 { - packageRouterEnter := &ast.PackageEnter{ - Type: ast.TypePackageRouterEnter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "enter.go"), - ImportPath: fmt.Sprintf(`"%s/%s/%s"`, global.GVA_CONFIG.AutoCode.Module, secondDirs[j].Name(), entity.PackageName), - StructName: utils.FirstUpper(entity.PackageName), - PackageName: entity.PackageName, - PackageStructName: "RouterGroup", - } - asts[packageRouterEnter.Path+"=>"+packageRouterEnter.Type.String()] = packageRouterEnter - packageRouterModuleEnter := &ast.PackageModuleEnter{ - Type: ast.TypePackageRouterModuleEnter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, "enter.go"), - ImportPath: fmt.Sprintf(`api "%s/api/v1"`, global.GVA_CONFIG.AutoCode.Module), - StructName: info.StructName + "Router", - AppName: "ApiGroupApp", - GroupName: utils.FirstUpper(entity.PackageName) + "ApiGroup", - ModuleName: info.Abbreviation + "Api", - PackageName: "api", - ServiceName: info.StructName + "Api", - } - creates[four] = packageRouterModuleEnter.Path - asts[packageRouterModuleEnter.Path+"=>"+packageRouterModuleEnter.Type.String()] = packageRouterModuleEnter - packageInitializeRouter := &ast.PackageInitializeRouter{ - Type: ast.TypePackageInitializeRouter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"), - ImportPath: fmt.Sprintf(`"%s/router"`, global.GVA_CONFIG.AutoCode.Module), - AppName: "RouterGroupApp", - GroupName: utils.FirstUpper(entity.PackageName), - ModuleName: entity.PackageName + "Router", - PackageName: "router", - FunctionName: "Init" + info.StructName + "Router", - LeftRouterGroupName: "privateGroup", - RightRouterGroupName: "publicGroup", - } - asts[packageInitializeRouter.Path+"=>"+packageInitializeRouter.Type.String()] = packageInitializeRouter - } - if isService != -1 { - path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)) - importPath := fmt.Sprintf(`"%s/service/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName) - packageServiceEnter := &ast.PackageEnter{ - Type: ast.TypePackageServiceEnter, - Path: path, - ImportPath: importPath, - StructName: utils.FirstUpper(entity.PackageName) + "ServiceGroup", - PackageName: entity.PackageName, - PackageStructName: "ServiceGroup", - } - asts[packageServiceEnter.Path+"=>"+packageServiceEnter.Type.String()] = packageServiceEnter - packageServiceModuleEnter := &ast.PackageModuleEnter{ - Type: ast.TypePackageServiceModuleEnter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, "enter.go"), - StructName: info.StructName + "Service", - } - asts[packageServiceModuleEnter.Path+"=>"+packageServiceModuleEnter.Type.String()] = packageServiceModuleEnter - creates[four] = packageServiceModuleEnter.Path - } - continue - } - code[four] = create - continue - } - if hasEnter != -1 { - isApi := strings.Index(secondDirs[j].Name(), "api") - isRouter := strings.Index(secondDirs[j].Name(), "router") - isService := strings.Index(secondDirs[j].Name(), "service") - if isRouter != -1 { - pluginRouterEnter := &ast.PluginEnter{ - Type: ast.TypePluginRouterEnter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), - ImportPath: fmt.Sprintf(`"%s/plugin/%s/api"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), - StructName: info.StructName, - StructCamelName: info.Abbreviation, - ModuleName: "api" + info.StructName, - GroupName: "Api", - PackageName: "api", - ServiceName: info.StructName, - } - asts[pluginRouterEnter.Path+"=>"+pluginRouterEnter.Type.String()] = pluginRouterEnter - creates[four] = pluginRouterEnter.Path - } - if isApi != -1 { - pluginApiEnter := &ast.PluginEnter{ - Type: ast.TypePluginApiEnter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), - ImportPath: fmt.Sprintf(`"%s/plugin/%s/service"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), - StructName: info.StructName, - StructCamelName: info.Abbreviation, - ModuleName: "service" + info.StructName, - GroupName: "Service", - PackageName: "service", - ServiceName: info.StructName, - } - asts[pluginApiEnter.Path+"=>"+pluginApiEnter.Type.String()] = pluginApiEnter - creates[four] = pluginApiEnter.Path - } - if isService != -1 { - pluginServiceEnter := &ast.PluginEnter{ - Type: ast.TypePluginServiceEnter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), - StructName: info.StructName, - StructCamelName: info.Abbreviation, - } - asts[pluginServiceEnter.Path+"=>"+pluginServiceEnter.Type.String()] = pluginServiceEnter - creates[four] = pluginServiceEnter.Path - } - continue - } // enter.go - create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), info.HumpPackageName+".go") - code[four] = create - } - case "gen", "config", "initialize", "plugin", "response": - if entity.Template == "package" { - continue - } // package模板不需要生成gen, config, initialize - var threeDirs []os.DirEntry - threeDirs, err = os.ReadDir(three) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three) - } - for k := 0; k < len(threeDirs); k++ { - if threeDirs[k].Name() == ".DS_Store" { - continue - } - four := filepath.Join(three, threeDirs[k].Name()) - if threeDirs[k].IsDir() { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four) - } - ext := filepath.Ext(four) - if ext != ".tpl" { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four) - } - gen := strings.Index(threeDirs[k].Name(), "gen") - api := strings.Index(threeDirs[k].Name(), "api") - menu := strings.Index(threeDirs[k].Name(), "menu") - viper := strings.Index(threeDirs[k].Name(), "viper") - plugin := strings.Index(threeDirs[k].Name(), "plugin") - config := strings.Index(threeDirs[k].Name(), "config") - router := strings.Index(threeDirs[k].Name(), "router") - hasGorm := strings.Index(threeDirs[k].Name(), "gorm") - response := strings.Index(threeDirs[k].Name(), "response") - dictionary := strings.Index(threeDirs[k].Name(), "dictionary") - if gen != -1 && api != -1 && menu != -1 && viper != -1 && plugin != -1 && config != -1 && router != -1 && hasGorm != -1 && response != -1 && dictionary != -1 { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four) - } - if api != -1 || menu != -1 || viper != -1 || response != -1 || plugin != -1 || config != -1 || dictionary != -1 { - creates[four] = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)) - } - if gen != -1 { - pluginGen := &ast.PluginGen{ - Type: ast.TypePluginGen, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), - ImportPath: fmt.Sprintf(`"%s/plugin/%s/model"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), - StructName: info.StructName, - PackageName: "model", - IsNew: true, - } - asts[pluginGen.Path+"=>"+pluginGen.Type.String()] = pluginGen - creates[four] = pluginGen.Path - } - if hasGorm != -1 { - pluginInitializeGorm := &ast.PluginInitializeGorm{ - Type: ast.TypePluginInitializeGorm, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), - ImportPath: fmt.Sprintf(`"%s/plugin/%s/model"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), - StructName: info.StructName, - PackageName: "model", - IsNew: true, - } - asts[pluginInitializeGorm.Path+"=>"+pluginInitializeGorm.Type.String()] = pluginInitializeGorm - creates[four] = pluginInitializeGorm.Path - } - if router != -1 { - pluginInitializeRouter := &ast.PluginInitializeRouter{ - Type: ast.TypePluginInitializeRouter, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), - ImportPath: fmt.Sprintf(`"%s/plugin/%s/router"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), - AppName: "Router", - GroupName: info.StructName, - PackageName: "router", - FunctionName: "Init", - LeftRouterGroupName: "public", - RightRouterGroupName: "private", - } - asts[pluginInitializeRouter.Path+"=>"+pluginInitializeRouter.Type.String()] = pluginInitializeRouter - creates[four] = pluginInitializeRouter.Path - } - } - case "model": - var threeDirs []os.DirEntry - threeDirs, err = os.ReadDir(three) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three) - } - for k := 0; k < len(threeDirs); k++ { - if threeDirs[k].Name() == ".DS_Store" { - continue - } - four := filepath.Join(three, threeDirs[k].Name()) - if threeDirs[k].IsDir() { - var fourDirs []os.DirEntry - fourDirs, err = os.ReadDir(four) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", four) - } - for l := 0; l < len(fourDirs); l++ { - if fourDirs[l].Name() == ".DS_Store" { - continue - } - five := filepath.Join(four, fourDirs[l].Name()) - if fourDirs[l].IsDir() { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", five) - } - ext := filepath.Ext(five) - if ext != ".tpl" { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", five) - } - hasRequest := strings.Index(fourDirs[l].Name(), "request") - if hasRequest == -1 { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", five) - } - create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), threeDirs[k].Name(), info.HumpPackageName+".go") - if entity.Template == "package" { - create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, threeDirs[k].Name(), info.HumpPackageName+".go") - } - code[five] = create - } - continue - } - ext := filepath.Ext(threeDirs[k].Name()) - if ext != ".tpl" { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four) - } - hasModel := strings.Index(threeDirs[k].Name(), "model") - if hasModel == -1 { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four) - } - create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), info.HumpPackageName+".go") - if entity.Template == "package" { - packageInitializeGorm := &ast.PackageInitializeGorm{ - Type: ast.TypePackageInitializeGorm, - Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), - ImportPath: fmt.Sprintf(`"%s/model/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), - Business: info.BusinessDB, - StructName: info.StructName, - PackageName: entity.PackageName, - IsNew: true, - } - code[four] = packageInitializeGorm.Path - asts[packageInitializeGorm.Path+"=>"+packageInitializeGorm.Type.String()] = packageInitializeGorm - create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, info.HumpPackageName+".go") - } - code[four] = create - } - default: - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", three) - } - } - case "web": - if !info.GenerateWeb && !isPackage { - break - } - var secondDirs []os.DirEntry - secondDirs, err = os.ReadDir(second) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", second) - } - for j := 0; j < len(secondDirs); j++ { - if secondDirs[j].Name() == ".DS_Store" { - continue - } - three := filepath.Join(second, secondDirs[j].Name()) - if !secondDirs[j].IsDir() { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", three) - } - switch secondDirs[j].Name() { - case "api", "form", "view", "table": - var threeDirs []os.DirEntry - threeDirs, err = os.ReadDir(three) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three) - } - for k := 0; k < len(threeDirs); k++ { - if threeDirs[k].Name() == ".DS_Store" { - continue - } - four := filepath.Join(three, threeDirs[k].Name()) - if threeDirs[k].IsDir() { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four) - } - ext := filepath.Ext(four) - if ext != ".tpl" { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four) - } - api := strings.Index(threeDirs[k].Name(), "api") - form := strings.Index(threeDirs[k].Name(), "form") - view := strings.Index(threeDirs[k].Name(), "view") - table := strings.Index(threeDirs[k].Name(), "table") - if api == -1 && form == -1 && view == -1 && table == -1 { - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four) - } - if entity.Template == "package" { - if view != -1 || table != -1 { - formPath := filepath.Join(three, "form.vue"+ext) - value, ok := code[formPath] - if ok { - value = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName, info.PackageName+"Form"+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext))) - code[formPath] = value - } - } - create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName, info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext))) - if api != -1 { - create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext))) - } - code[four] = create - continue - } - create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), "plugin", entity.PackageName, secondDirs[j].Name(), info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext))) - code[four] = create - } - default: - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", three) - } - } - case "readme.txt.tpl", "readme.txt.template": - continue - default: - if templateDirs[i].Name() == ".DS_Store" { - continue - } - return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", second) - } - } - return code, asts, creates, nil -} diff --git a/server/service/system/auto_code_package_test.go b/server/service/system/auto_code_package_test.go deleted file mode 100644 index d2a5473..0000000 --- a/server/service/system/auto_code_package_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package system - -import ( - "context" - "reflect" - "testing" - - model "github.com/flipped-aurora/gin-vue-admin/server/model/system" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" -) - -func Test_autoCodePackage_Create(t *testing.T) { - type args struct { - ctx context.Context - info *request.SysAutoCodePackageCreate - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "测试 package", - args: args{ - ctx: context.Background(), - info: &request.SysAutoCodePackageCreate{ - Template: "package", - PackageName: "gva", - }, - }, - wantErr: false, - }, - { - name: "测试 plugin", - args: args{ - ctx: context.Background(), - info: &request.SysAutoCodePackageCreate{ - Template: "plugin", - PackageName: "gva", - }, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - a := &autoCodePackage{} - if err := a.Create(tt.args.ctx, tt.args.info); (err != nil) != tt.wantErr { - t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func Test_autoCodePackage_templates(t *testing.T) { - type args struct { - ctx context.Context - entity model.SysAutoCodePackage - info request.AutoCode - isPackage bool - } - tests := []struct { - name string - args args - wantCode map[string]string - wantEnter map[string]map[string]string - wantErr bool - }{ - { - name: "测试1", - args: args{ - ctx: context.Background(), - entity: model.SysAutoCodePackage{ - Desc: "描述", - Label: "展示名", - Template: "plugin", - PackageName: "preview", - }, - info: request.AutoCode{ - Abbreviation: "user", - HumpPackageName: "user", - }, - isPackage: false, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := &autoCodePackage{} - gotCode, gotEnter, gotCreates, err := s.templates(tt.args.ctx, tt.args.entity, tt.args.info, tt.args.isPackage) - if (err != nil) != tt.wantErr { - t.Errorf("templates() error = %v, wantErr %v", err, tt.wantErr) - return - } - for key, value := range gotCode { - t.Logf("\n") - t.Logf(key) - t.Logf(value) - t.Logf("\n") - } - t.Log(gotCreates) - if !reflect.DeepEqual(gotEnter, tt.wantEnter) { - t.Errorf("templates() gotEnter = %v, want %v", gotEnter, tt.wantEnter) - } - }) - } -} diff --git a/server/service/system/auto_code_plugin.go b/server/service/system/auto_code_plugin.go deleted file mode 100644 index be3dff6..0000000 --- a/server/service/system/auto_code_plugin.go +++ /dev/null @@ -1,512 +0,0 @@ -package system - -import ( - "bytes" - "context" - "fmt" - goast "go/ast" - "go/parser" - "go/printer" - "go/token" - "io" - "mime/multipart" - "os" - "path/filepath" - "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" - pluginUtils "github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils" - "github.com/flipped-aurora/gin-vue-admin/server/utils" - ast "github.com/flipped-aurora/gin-vue-admin/server/utils/ast" - "github.com/mholt/archives" - cp "github.com/otiai10/copy" - "github.com/pkg/errors" - "go.uber.org/zap" -) - -var AutoCodePlugin = new(autoCodePlugin) - -type autoCodePlugin struct{} - -// Install 插件安装 -func (s *autoCodePlugin) Install(file *multipart.FileHeader) (web, server int, err error) { - const GVAPLUGPINATH = "./gva-plug-temp/" - defer os.RemoveAll(GVAPLUGPINATH) - _, err = os.Stat(GVAPLUGPINATH) - if os.IsNotExist(err) { - os.Mkdir(GVAPLUGPINATH, os.ModePerm) - } - - src, err := file.Open() - if err != nil { - return -1, -1, err - } - defer src.Close() - - // 在临时目录创建目标文件 - // 使用完整路径拼接的好处:明确文件位置,避免路径混乱 - out, err := os.Create(GVAPLUGPINATH + file.Filename) - if err != nil { - return -1, -1, err - } - - // 将上传的文件内容复制到临时文件 - // 使用io.Copy的好处:高效处理大文件,自动管理缓冲区,避免内存溢出 - _, err = io.Copy(out, src) - if err != nil { - out.Close() - return -1, -1, err - } - - // 立即关闭文件,确保数据写入磁盘并释放文件句柄 - // 必须在解压前关闭,否则在Windows系统上会导致文件被占用无法解压 - err = out.Close() - if err != nil { - return -1, -1, err - } - - paths, err := utils.Unzip(GVAPLUGPINATH+file.Filename, GVAPLUGPINATH) - paths = filterFile(paths) - var webIndex = -1 - var serverIndex = -1 - webPlugin := "" - serverPlugin := "" - serverPackage := "" - serverRootName := "" - - for i := range paths { - paths[i] = filepath.ToSlash(paths[i]) - pathArr := strings.Split(paths[i], "/") - ln := len(pathArr) - - if ln < 4 { - continue - } - if pathArr[2]+"/"+pathArr[3] == `server/plugin` { - if len(serverPlugin) == 0 { - serverPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3]) - } - if serverRootName == "" && ln > 1 && pathArr[1] != "" { - serverRootName = pathArr[1] - } - if ln > 4 && serverPackage == "" && pathArr[4] != "" { - serverPackage = pathArr[4] - } - } - if pathArr[2]+"/"+pathArr[3] == `web/plugin` && len(webPlugin) == 0 { - webPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3]) - } - } - if len(serverPlugin) == 0 && len(webPlugin) == 0 { - zap.L().Error("非标准插件,请按照文档自动迁移使用") - return webIndex, serverIndex, errors.New("非标准插件,请按照文档自动迁移使用") - } - - if len(serverPlugin) != 0 { - if serverPackage == "" { - serverPackage = serverRootName - } - err = installation(serverPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Server) - if err != nil { - return webIndex, serverIndex, err - } - err = ensurePluginRegisterImport(serverPackage) - if err != nil { - return webIndex, serverIndex, err - } - } - - if len(webPlugin) != 0 { - err = installation(webPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Web) - if err != nil { - return webIndex, serverIndex, err - } - } - - return 1, 1, err -} - -func installation(path string, formPath string, toPath string) error { - arr := strings.Split(filepath.ToSlash(path), "/") - ln := len(arr) - if ln < 3 { - return errors.New("arr") - } - name := arr[ln-3] - - var form = filepath.Join(global.GVA_CONFIG.AutoCode.Root, formPath, path) - var to = filepath.Join(global.GVA_CONFIG.AutoCode.Root, toPath, "plugin") - _, err := os.Stat(to + name) - if err == nil { - zap.L().Error("autoPath 已存在同名插件,请自行手动安装", zap.String("to", to)) - return errors.New(toPath + "已存在同名插件,请自行手动安装") - } - return cp.Copy(form, to, cp.Options{Skip: skipMacSpecialDocument}) -} - -func ensurePluginRegisterImport(packageName string) error { - module := strings.TrimSpace(global.GVA_CONFIG.AutoCode.Module) - if module == "" { - return errors.New("autocode module is empty") - } - if packageName == "" { - return errors.New("plugin package is empty") - } - - registerPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "register.go") - src, err := os.ReadFile(registerPath) - if err != nil { - return err - } - fileSet := token.NewFileSet() - astFile, err := parser.ParseFile(fileSet, registerPath, src, parser.ParseComments) - if err != nil { - return err - } - - importPath := fmt.Sprintf("%s/plugin/%s", module, packageName) - if ast.CheckImport(astFile, importPath) { - return nil - } - - importSpec := &goast.ImportSpec{ - Name: goast.NewIdent("_"), - Path: &goast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", importPath)}, - } - var importDecl *goast.GenDecl - for _, decl := range astFile.Decls { - genDecl, ok := decl.(*goast.GenDecl) - if !ok { - continue - } - if genDecl.Tok == token.IMPORT { - importDecl = genDecl - break - } - } - if importDecl == nil { - astFile.Decls = append([]goast.Decl{ - &goast.GenDecl{ - Tok: token.IMPORT, - Specs: []goast.Spec{importSpec}, - }, - }, astFile.Decls...) - } else { - importDecl.Specs = append(importDecl.Specs, importSpec) - } - - var out []byte - bf := bytes.NewBuffer(out) - printer.Fprint(bf, fileSet, astFile) - - return os.WriteFile(registerPath, bf.Bytes(), 0666) -} - -func filterFile(paths []string) []string { - np := make([]string, 0, len(paths)) - for _, path := range paths { - if ok, _ := skipMacSpecialDocument(nil, path, ""); ok { - continue - } - np = append(np, path) - } - return np -} - -func skipMacSpecialDocument(_ os.FileInfo, src, _ string) (bool, error) { - if strings.Contains(src, ".DS_Store") || strings.Contains(src, "__MACOSX") { - return true, nil - } - return false, nil -} - -func (s *autoCodePlugin) PubPlug(plugName string) (zipPath string, err error) { - if plugName == "" { - return "", errors.New("插件名称不能为空") - } - - // 防止路径穿越 - plugName = filepath.Clean(plugName) - - webPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", plugName) - serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", plugName) - // 创建一个新的zip文件 - - // 判断目录是否存在 - _, err = os.Stat(webPath) - if err != nil { - return "", errors.New("web路径不存在") - } - _, err = os.Stat(serverPath) - if err != nil { - return "", errors.New("server路径不存在") - } - - fileName := plugName + ".zip" - // 创建一个新的zip文件 - files, err := archives.FilesFromDisk(context.Background(), nil, map[string]string{ - webPath: plugName + "/web/plugin/" + plugName, - serverPath: plugName + "/server/plugin/" + plugName, - }) - - // create the output file we'll write to - out, err := os.Create(fileName) - if err != nil { - return - } - defer out.Close() - - // we can use the CompressedArchive type to gzip a tarball - // (compression is not required; you could use Tar directly) - format := archives.CompressedArchive{ - //Compression: archives.Gz{}, - Archival: archives.Zip{}, - } - - // create the archive - err = format.Archive(context.Background(), out, files) - if err != nil { - return - } - - return filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fileName), nil -} - -func (s *autoCodePlugin) InitMenu(menuInfo request.InitMenu) (err error) { - menuPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", menuInfo.PlugName, "initialize", "menu.go") - src, err := os.ReadFile(menuPath) - if err != nil { - fmt.Println(err) - } - fileSet := token.NewFileSet() - astFile, err := parser.ParseFile(fileSet, "", src, 0) - arrayAst := ast.FindArray(astFile, "model", "SysBaseMenu") - var menus []system.SysBaseMenu - - parentMenu := []system.SysBaseMenu{ - { - ParentId: 0, - Path: menuInfo.PlugName + "Menu", - Name: menuInfo.PlugName + "Menu", - Hidden: false, - Component: "view/routerHolder.vue", - Sort: 0, - Meta: system.Meta{ - Title: menuInfo.ParentMenu, - Icon: "school", - }, - }, - } - - // 查询菜单及其关联的参数和按钮 - err = global.GVA_DB.Preload("Parameters").Preload("MenuBtn").Find(&menus, "id in (?)", menuInfo.Menus).Error - if err != nil { - return err - } - menus = append(parentMenu, menus...) - menuExpr := ast.CreateMenuStructAst(menus) - arrayAst.Elts = *menuExpr - - var out []byte - bf := bytes.NewBuffer(out) - printer.Fprint(bf, fileSet, astFile) - - os.WriteFile(menuPath, bf.Bytes(), 0666) - return nil -} - -func (s *autoCodePlugin) InitAPI(apiInfo request.InitApi) (err error) { - apiPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", apiInfo.PlugName, "initialize", "api.go") - src, err := os.ReadFile(apiPath) - if err != nil { - fmt.Println(err) - } - fileSet := token.NewFileSet() - astFile, err := parser.ParseFile(fileSet, "", src, 0) - arrayAst := ast.FindArray(astFile, "model", "SysApi") - var apis []system.SysApi - err = global.GVA_DB.Find(&apis, "id in (?)", apiInfo.APIs).Error - if err != nil { - return err - } - apisExpr := ast.CreateApiStructAst(apis) - arrayAst.Elts = *apisExpr - - var out []byte - bf := bytes.NewBuffer(out) - printer.Fprint(bf, fileSet, astFile) - - os.WriteFile(apiPath, bf.Bytes(), 0666) - return nil -} - -func (s *autoCodePlugin) InitDictionary(dictInfo request.InitDictionary) (err error) { - dictPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", dictInfo.PlugName, "initialize", "dictionary.go") - src, err := os.ReadFile(dictPath) - if err != nil { - fmt.Println(err) - } - fileSet := token.NewFileSet() - astFile, err := parser.ParseFile(fileSet, "", src, 0) - arrayAst := ast.FindArray(astFile, "model", "SysDictionary") - var dictionaries []system.SysDictionary - err = global.GVA_DB.Preload("SysDictionaryDetails").Find(&dictionaries, "id in (?)", dictInfo.Dictionaries).Error - if err != nil { - return err - } - dictExpr := ast.CreateDictionaryStructAst(dictionaries) - arrayAst.Elts = *dictExpr - - var out []byte - bf := bytes.NewBuffer(out) - printer.Fprint(bf, fileSet, astFile) - - os.WriteFile(dictPath, bf.Bytes(), 0666) - return nil -} - -func (s *autoCodePlugin) Remove(pluginName string, pluginType string) (err error) { - // 1. 删除前端代码 - if pluginType == "web" || pluginType == "full" { - webDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", pluginName) - err = os.RemoveAll(webDir) - if err != nil { - return errors.Wrap(err, "删除前端插件目录失败") - } - } - - // 2. 删除后端代码 - if pluginType == "server" || pluginType == "full" { - serverDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", pluginName) - err = os.RemoveAll(serverDir) - if err != nil { - return errors.Wrap(err, "删除后端插件目录失败") - } - - // 移除注册 - removePluginRegisterImport(pluginName) - } - - // 通过utils 获取 api 菜单 字典 - apis, menus, dicts := pluginUtils.GetPluginData(pluginName) - - // 3. 删除菜单 (递归删除) - if len(menus) > 0 { - for _, menu := range menus { - var dbMenu system.SysBaseMenu - if err := global.GVA_DB.Where("name = ?", menu.Name).First(&dbMenu).Error; err == nil { - // 获取该菜单及其所有子菜单的ID - var menuIds []int - GetMenuIds(dbMenu, &menuIds) - // 逆序删除,先删除子菜单 - for i := len(menuIds) - 1; i >= 0; i-- { - err := BaseMenuServiceApp.DeleteBaseMenu(menuIds[i]) - if err != nil { - zap.L().Error("删除菜单失败", zap.Int("id", menuIds[i]), zap.Error(err)) - } - } - } - } - } - - // 4. 删除API - if len(apis) > 0 { - for _, api := range apis { - var dbApi system.SysApi - if err := global.GVA_DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&dbApi).Error; err == nil { - err := ApiServiceApp.DeleteApi(dbApi) - if err != nil { - zap.L().Error("删除API失败", zap.String("path", api.Path), zap.Error(err)) - } - } - } - } - - // 5. 删除字典 - if len(dicts) > 0 { - for _, dict := range dicts { - var dbDict system.SysDictionary - if err := global.GVA_DB.Where("type = ?", dict.Type).First(&dbDict).Error; err == nil { - err := DictionaryServiceApp.DeleteSysDictionary(dbDict) - if err != nil { - zap.L().Error("删除字典失败", zap.String("type", dict.Type), zap.Error(err)) - } - } - } - } - - return nil -} - -func GetMenuIds(menu system.SysBaseMenu, ids *[]int) { - *ids = append(*ids, int(menu.ID)) - var children []system.SysBaseMenu - global.GVA_DB.Where("parent_id = ?", menu.ID).Find(&children) - for _, child := range children { - // 先递归收集子菜单 - GetMenuIds(child, ids) - } -} - -func removePluginRegisterImport(packageName string) error { - module := strings.TrimSpace(global.GVA_CONFIG.AutoCode.Module) - if module == "" { - return errors.New("autocode module is empty") - } - if packageName == "" { - return errors.New("plugin package is empty") - } - - registerPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "register.go") - src, err := os.ReadFile(registerPath) - if err != nil { - return err - } - fileSet := token.NewFileSet() - astFile, err := parser.ParseFile(fileSet, registerPath, src, parser.ParseComments) - if err != nil { - return err - } - - importPath := fmt.Sprintf("%s/plugin/%s", module, packageName) - importLit := fmt.Sprintf("%q", importPath) - - // 移除 import - var newDecls []goast.Decl - for _, decl := range astFile.Decls { - genDecl, ok := decl.(*goast.GenDecl) - if !ok { - newDecls = append(newDecls, decl) - continue - } - if genDecl.Tok == token.IMPORT { - var newSpecs []goast.Spec - for _, spec := range genDecl.Specs { - importSpec, ok := spec.(*goast.ImportSpec) - if !ok { - newSpecs = append(newSpecs, spec) - continue - } - if importSpec.Path.Value != importLit { - newSpecs = append(newSpecs, spec) - } - } - // 如果还有其他import,保留该 decl - if len(newSpecs) > 0 { - genDecl.Specs = newSpecs - newDecls = append(newDecls, genDecl) - } - } else { - newDecls = append(newDecls, decl) - } - } - astFile.Decls = newDecls - - var out []byte - bf := bytes.NewBuffer(out) - printer.Fprint(bf, fileSet, astFile) - - return os.WriteFile(registerPath, bf.Bytes(), 0666) -} diff --git a/server/service/system/auto_code_service.go b/server/service/system/auto_code_service.go deleted file mode 100644 index 0c895b1..0000000 --- a/server/service/system/auto_code_service.go +++ /dev/null @@ -1,3 +0,0 @@ -package system - -type AutoCodeService struct{} diff --git a/server/service/system/auto_code_template.go b/server/service/system/auto_code_template.go deleted file mode 100644 index dffb376..0000000 --- a/server/service/system/auto_code_template.go +++ /dev/null @@ -1,453 +0,0 @@ -package system - -import ( - "context" - "encoding/json" - "fmt" - "github.com/flipped-aurora/gin-vue-admin/server/utils/autocode" - "go/ast" - "go/format" - "go/parser" - "go/token" - "os" - "path/filepath" - "strings" - "text/template" - - "github.com/flipped-aurora/gin-vue-admin/server/global" - model "github.com/flipped-aurora/gin-vue-admin/server/model/system" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" - utilsAst "github.com/flipped-aurora/gin-vue-admin/server/utils/ast" - "github.com/pkg/errors" - "gorm.io/gorm" -) - -var AutoCodeTemplate = new(autoCodeTemplate) - -type autoCodeTemplate struct{} - -func (s *autoCodeTemplate) checkPackage(Pkg string, template string) (err error) { - switch template { - case "package": - apiEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", Pkg, "enter.go") - _, err = os.Stat(apiEnter) - if err != nil { - return fmt.Errorf("package结构异常,缺少api/v1/%s/enter.go", Pkg) - } - serviceEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", Pkg, "enter.go") - _, err = os.Stat(serviceEnter) - if err != nil { - return fmt.Errorf("package结构异常,缺少service/%s/enter.go", Pkg) - } - routerEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", Pkg, "enter.go") - _, err = os.Stat(routerEnter) - if err != nil { - return fmt.Errorf("package结构异常,缺少router/%s/enter.go", Pkg) - } - case "plugin": - pluginEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", Pkg, "plugin.go") - _, err = os.Stat(pluginEnter) - if err != nil { - return fmt.Errorf("plugin结构异常,缺少plugin/%s/plugin.go", Pkg) - } - } - return nil -} - -// Create 创建生成自动化代码 -func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) error { - history := info.History() - var autoPkg model.SysAutoCodePackage - err := global.GVA_DB.WithContext(ctx).Where("package_name = ?", info.Package).First(&autoPkg).Error - if err != nil { - return errors.Wrap(err, "查询包失败!") - } - err = s.checkPackage(info.Package, autoPkg.Template) - if err != nil { - return err - } - // 增加判断: 重复创建struct 或者重复的简称 - if AutocodeHistory.Repeat(info.BusinessDB, info.StructName, info.Abbreviation, info.Package) { - return errors.New("已经创建过此数据结构,请勿重复创建!") - } - - generate, templates, injections, err := s.generate(ctx, info, autoPkg) - if err != nil { - return err - } - for key, builder := range generate { - err = os.MkdirAll(filepath.Dir(key), os.ModePerm) - if err != nil { - return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", key) - } - err = os.WriteFile(key, []byte(builder.String()), 0666) - if err != nil { - return errors.Wrapf(err, "[filepath:%s]写入文件失败!", key) - } - } - - // 自动创建api - if info.AutoCreateApiToSql && !info.OnlyTemplate { - apis := info.Apis() - err := global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { - for _, v := range apis { - var api model.SysApi - var id uint - err := tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error - if errors.Is(err, gorm.ErrRecordNotFound) { - if err = tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务 - return err - } - id = v.ID - } else { - id = api.ID - } - history.ApiIDs = append(history.ApiIDs, id) - } - return nil - }) - if err != nil { - return err - } - } - - // 自动创建menu - if info.AutoCreateMenuToSql { - var entity model.SysBaseMenu - var id uint - err := global.GVA_DB.WithContext(ctx).First(&entity, "name = ?", info.Abbreviation).Error - if err == nil { - id = entity.ID - } else { - entity = info.Menu(autoPkg.Template) - if info.AutoCreateBtnAuth && !info.OnlyTemplate { - entity.MenuBtn = []model.SysBaseMenuBtn{ - {SysBaseMenuID: entity.ID, Name: "add", Desc: "新增"}, - {SysBaseMenuID: entity.ID, Name: "batchDelete", Desc: "批量删除"}, - {SysBaseMenuID: entity.ID, Name: "delete", Desc: "删除"}, - {SysBaseMenuID: entity.ID, Name: "edit", Desc: "编辑"}, - {SysBaseMenuID: entity.ID, Name: "info", Desc: "详情"}, - } - if info.HasExcel { - excelBtn := []model.SysBaseMenuBtn{ - {SysBaseMenuID: entity.ID, Name: "exportTemplate", Desc: "导出模板"}, - {SysBaseMenuID: entity.ID, Name: "exportExcel", Desc: "导出Excel"}, - {SysBaseMenuID: entity.ID, Name: "importExcel", Desc: "导入Excel"}, - } - entity.MenuBtn = append(entity.MenuBtn, excelBtn...) - } - } - err = global.GVA_DB.WithContext(ctx).Create(&entity).Error - id = entity.ID - if err != nil { - return errors.Wrap(err, "创建菜单失败!") - } - } - history.MenuID = id - } - - if info.HasExcel { - dbName := info.BusinessDB - name := info.Package + "_" + info.StructName - tableName := info.TableName - fieldsMap := make(map[string]string, len(info.Fields)) - for _, field := range info.Fields { - if field.Excel { - fieldsMap[field.ColumnName] = field.FieldDesc - } - } - templateInfo, _ := json.Marshal(fieldsMap) - sysExportTemplate := model.SysExportTemplate{ - DBName: dbName, - Name: name, - TableName: tableName, - TemplateID: name, - TemplateInfo: string(templateInfo), - } - err = SysExportTemplateServiceApp.CreateSysExportTemplate(&sysExportTemplate) - if err != nil { - return err - } - history.ExportTemplateID = sysExportTemplate.ID - } - - // 创建历史记录 - history.Templates = templates - history.Injections = make(map[string]string, len(injections)) - for key, value := range injections { - bytes, _ := json.Marshal(value) - history.Injections[key] = string(bytes) - } - err = AutocodeHistory.Create(ctx, history) - if err != nil { - return err - } - return nil -} - -// Preview 预览自动化代码 -func (s *autoCodeTemplate) Preview(ctx context.Context, info request.AutoCode) (map[string]string, error) { - var entity model.SysAutoCodePackage - err := global.GVA_DB.WithContext(ctx).Where("package_name = ?", info.Package).First(&entity).Error - if err != nil { - return nil, errors.Wrap(err, "查询包失败!") - } - // 增加判断: 重复创建struct 或者重复的简称 - if AutocodeHistory.Repeat(info.BusinessDB, info.StructName, info.Abbreviation, info.Package) && !info.IsAdd { - return nil, errors.New("已经创建过此数据结构或重复简称,请勿重复创建!") - } - - preview := make(map[string]string) - codes, _, _, err := s.generate(ctx, info, entity) - if err != nil { - return nil, err - } - for key, writer := range codes { - if len(key) > len(global.GVA_CONFIG.AutoCode.Root) { - key, _ = filepath.Rel(global.GVA_CONFIG.AutoCode.Root, key) - } - // 获取key的后缀 取消. - suffix := filepath.Ext(key)[1:] - var builder strings.Builder - builder.WriteString("```" + suffix + "\n\n") - builder.WriteString(writer.String()) - builder.WriteString("\n\n```") - preview[key] = builder.String() - } - return preview, nil -} - -func (s *autoCodeTemplate) generate(ctx context.Context, info request.AutoCode, entity model.SysAutoCodePackage) (map[string]strings.Builder, map[string]string, map[string]utilsAst.Ast, error) { - templates, asts, _, err := AutoCodePackage.templates(ctx, entity, info, false) - if err != nil { - return nil, nil, nil, err - } - code := make(map[string]strings.Builder) - for key, create := range templates { - var files *template.Template - files, err = template.New(filepath.Base(key)).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(key) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "[filpath:%s]读取模版文件失败!", key) - } - var builder strings.Builder - err = files.Execute(&builder, info) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "[filpath:%s]生成文件失败!", create) - } - code[create] = builder - } // 生成文件 - injections := make(map[string]utilsAst.Ast, len(asts)) - for key, value := range asts { - keys := strings.Split(key, "=>") - if len(keys) == 2 { - if keys[1] == utilsAst.TypePluginInitializeV2 { - continue - } - if info.OnlyTemplate { - if keys[1] == utilsAst.TypePackageInitializeGorm || keys[1] == utilsAst.TypePluginInitializeGorm { - continue - } - } - if !info.AutoMigrate { - if keys[1] == utilsAst.TypePackageInitializeGorm || keys[1] == utilsAst.TypePluginInitializeGorm { - continue - } - } - var builder strings.Builder - parse, _ := value.Parse("", &builder) - if parse != nil { - _ = value.Injection(parse) - err = value.Format("", &builder, parse) - if err != nil { - return nil, nil, nil, err - } - code[keys[0]] = builder - injections[keys[1]] = value - fmt.Println(keys[0], "注入成功!") - } - } - } - // 注入代码 - return code, templates, injections, nil -} - -func (s *autoCodeTemplate) AddFunc(info request.AutoFunc) error { - autoPkg := model.SysAutoCodePackage{} - err := global.GVA_DB.First(&autoPkg, "package_name = ?", info.Package).Error - if err != nil { - return err - } - if autoPkg.Template != "package" { - info.IsPlugin = true - } - err = s.addTemplateToFile("api.go", info) - if err != nil { - return err - } - err = s.addTemplateToFile("server.go", info) - if err != nil { - return err - } - err = s.addTemplateToFile("api.js", info) - if err != nil { - return err - } - return s.addTemplateToAst("router", info) -} - -func (s *autoCodeTemplate) GetApiAndServer(info request.AutoFunc) (map[string]string, error) { - autoPkg := model.SysAutoCodePackage{} - err := global.GVA_DB.First(&autoPkg, "package_name = ?", info.Package).Error - if err != nil { - return nil, err - } - if autoPkg.Template != "package" { - info.IsPlugin = true - } - - apiStr, err := s.getTemplateStr("api.go", info) - if err != nil { - return nil, err - } - serverStr, err := s.getTemplateStr("server.go", info) - if err != nil { - return nil, err - } - jsStr, err := s.getTemplateStr("api.js", info) - if err != nil { - return nil, err - } - return map[string]string{"api": apiStr, "server": serverStr, "js": jsStr}, nil - -} - -func (s *autoCodeTemplate) getTemplateStr(t string, info request.AutoFunc) (string, error) { - tempPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "function", t+".tpl") - files, err := template.New(filepath.Base(tempPath)).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(tempPath) - if err != nil { - return "", errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", tempPath) - } - var builder strings.Builder - err = files.Execute(&builder, info) - if err != nil { - fmt.Println(err.Error()) - return "", errors.Wrapf(err, "[filpath:%s]生成文件失败!", tempPath) - } - return builder.String(), nil -} - -func (s *autoCodeTemplate) addTemplateToAst(t string, info request.AutoFunc) error { - tPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", info.Package, info.HumpPackageName+".go") - funcName := fmt.Sprintf("Init%sRouter", info.StructName) - - routerStr := "RouterWithoutAuth" - if info.IsAuth { - routerStr = "Router" - } - - stmtStr := fmt.Sprintf("%s%s.%s(\"%s\", %sApi.%s)", info.Abbreviation, routerStr, info.Method, info.Router, info.Abbreviation, info.FuncName) - if info.IsPlugin { - tPath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "router", info.HumpPackageName+".go") - stmtStr = fmt.Sprintf("group.%s(\"%s\", api%s.%s)", info.Method, info.Router, info.StructName, info.FuncName) - funcName = "Init" - } - - src, err := os.ReadFile(tPath) - if err != nil { - return err - } - - fileSet := token.NewFileSet() - astFile, err := parser.ParseFile(fileSet, "", src, 0) - if err != nil { - return err - } - funcDecl := utilsAst.FindFunction(astFile, funcName) - stmtNode := utilsAst.CreateStmt(stmtStr) - - if info.IsAuth { - for i := 0; i < len(funcDecl.Body.List); i++ { - st := funcDecl.Body.List[i] - // 使用类型断言来检查stmt是否是一个块语句 - if blockStmt, ok := st.(*ast.BlockStmt); ok { - // 如果是,插入代码 跳出 - blockStmt.List = append(blockStmt.List, stmtNode) - break - } - } - } else { - for i := len(funcDecl.Body.List) - 1; i >= 0; i-- { - st := funcDecl.Body.List[i] - // 使用类型断言来检查stmt是否是一个块语句 - if blockStmt, ok := st.(*ast.BlockStmt); ok { - // 如果是,插入代码 跳出 - blockStmt.List = append(blockStmt.List, stmtNode) - break - } - } - } - - // 创建一个新的文件 - f, err := os.Create(tPath) - if err != nil { - return err - } - defer f.Close() - - if err := format.Node(f, fileSet, astFile); err != nil { - return err - } - return err -} - -func (s *autoCodeTemplate) addTemplateToFile(t string, info request.AutoFunc) error { - getTemplateStr, err := s.getTemplateStr(t, info) - if err != nil { - return err - } - var target string - - switch t { - case "api.go": - if info.IsAi && info.ApiFunc != "" { - getTemplateStr = info.ApiFunc - } - target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", info.Package, info.HumpPackageName+".go") - case "server.go": - if info.IsAi && info.ServerFunc != "" { - getTemplateStr = info.ServerFunc - } - target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", info.Package, info.HumpPackageName+".go") - case "api.js": - if info.IsAi && info.JsFunc != "" { - getTemplateStr = info.JsFunc - } - target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "api", info.Package, info.PackageName+".js") - } - if info.IsPlugin { - switch t { - case "api.go": - target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "api", info.HumpPackageName+".go") - case "server.go": - target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "service", info.HumpPackageName+".go") - case "api.js": - target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", info.Package, "api", info.PackageName+".js") - } - } - - // 打开文件,如果不存在则返回错误 - file, err := os.OpenFile(target, os.O_WRONLY|os.O_APPEND, 0644) - if err != nil { - return err - } - defer file.Close() - - // 写入内容 - _, err = fmt.Fprintln(file, getTemplateStr) - if err != nil { - fmt.Printf("写入文件失败: %s\n", err.Error()) - return err - } - - return nil -} diff --git a/server/service/system/auto_code_template_test.go b/server/service/system/auto_code_template_test.go deleted file mode 100644 index 09d8191..0000000 --- a/server/service/system/auto_code_template_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package system - -import ( - "context" - "encoding/json" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" - "reflect" - "testing" -) - -func Test_autoCodeTemplate_Create(t *testing.T) { - type args struct { - ctx context.Context - info request.AutoCode - } - tests := []struct { - name string - args args - wantErr bool - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := &autoCodeTemplate{} - if err := s.Create(tt.args.ctx, tt.args.info); (err != nil) != tt.wantErr { - t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func Test_autoCodeTemplate_Preview(t *testing.T) { - type args struct { - ctx context.Context - info request.AutoCode - } - tests := []struct { - name string - args args - want map[string]string - wantErr bool - }{ - { - name: "测试 package", - args: args{ - ctx: context.Background(), - info: request.AutoCode{}, - }, - wantErr: false, - }, - { - name: "测试 plugin", - args: args{ - ctx: context.Background(), - info: request.AutoCode{}, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testJson := `{"structName":"SysUser","tableName":"sys_users","packageName":"sysUsers","package":"gva","abbreviation":"sysUsers","description":"sysUsers表","businessDB":"","autoCreateApiToSql":true,"autoCreateMenuToSql":true,"autoMigrate":true,"gvaModel":true,"autoCreateResource":false,"fields":[{"fieldName":"Uuid","fieldDesc":"用户UUID","fieldType":"string","dataType":"varchar","fieldJson":"uuid","primaryKey":false,"dataTypeLong":"191","columnName":"uuid","comment":"用户UUID","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Username","fieldDesc":"用户登录名","fieldType":"string","dataType":"varchar","fieldJson":"username","primaryKey":false,"dataTypeLong":"191","columnName":"username","comment":"用户登录名","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Password","fieldDesc":"用户登录密码","fieldType":"string","dataType":"varchar","fieldJson":"password","primaryKey":false,"dataTypeLong":"191","columnName":"password","comment":"用户登录密码","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"NickName","fieldDesc":"用户昵称","fieldType":"string","dataType":"varchar","fieldJson":"nickName","primaryKey":false,"dataTypeLong":"191","columnName":"nick_name","comment":"用户昵称","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"SideMode","fieldDesc":"用户侧边主题","fieldType":"string","dataType":"varchar","fieldJson":"sideMode","primaryKey":false,"dataTypeLong":"191","columnName":"side_mode","comment":"用户侧边主题","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"HeaderImg","fieldDesc":"用户头像","fieldType":"string","dataType":"varchar","fieldJson":"headerImg","primaryKey":false,"dataTypeLong":"191","columnName":"header_img","comment":"用户头像","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"BaseColor","fieldDesc":"基础颜色","fieldType":"string","dataType":"varchar","fieldJson":"baseColor","primaryKey":false,"dataTypeLong":"191","columnName":"base_color","comment":"基础颜色","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"AuthorityId","fieldDesc":"用户角色ID","fieldType":"int","dataType":"bigint","fieldJson":"authorityId","primaryKey":false,"dataTypeLong":"20","columnName":"authority_id","comment":"用户角色ID","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Phone","fieldDesc":"用户手机号","fieldType":"string","dataType":"varchar","fieldJson":"phone","primaryKey":false,"dataTypeLong":"191","columnName":"phone","comment":"用户手机号","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Email","fieldDesc":"用户邮箱","fieldType":"string","dataType":"varchar","fieldJson":"email","primaryKey":false,"dataTypeLong":"191","columnName":"email","comment":"用户邮箱","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Enable","fieldDesc":"用户是否被冻结 1正常 2冻结","fieldType":"int","dataType":"bigint","fieldJson":"enable","primaryKey":false,"dataTypeLong":"19","columnName":"enable","comment":"用户是否被冻结 1正常 2冻结","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}}],"humpPackageName":"sys_users"}` - err := json.Unmarshal([]byte(testJson), &tt.args.info) - if err != nil { - t.Error(err) - return - } - err = tt.args.info.Pretreatment() - if err != nil { - t.Error(err) - return - } - got, err := AutoCodeTemplate.Preview(tt.args.ctx, tt.args.info) - if (err != nil) != tt.wantErr { - t.Errorf("Preview() error = %+v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Preview() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/server/service/system/enter.go b/server/service/system/enter.go index 536fb1a..91a6935 100644 --- a/server/service/system/enter.go +++ b/server/service/system/enter.go @@ -7,7 +7,6 @@ type ServiceGroup struct { UserService CasbinService InitDBService - AutoCodeService BaseMenuService AuthorityService DictionaryService @@ -18,12 +17,7 @@ type ServiceGroup struct { SysExportTemplateService SysParamsService SysVersionService - SkillsService - AIWorkflowSession aiWorkflowSession - AutoCodePlugin autoCodePlugin - AutoCodePackage autoCodePackage - AutoCodeHistory autoCodeHistory - AutoCodeTemplate autoCodeTemplate + McpService SysErrorService LoginLogService ApiTokenService diff --git a/server/service/system/mcp.go b/server/service/system/mcp.go new file mode 100644 index 0000000..cea9fe2 --- /dev/null +++ b/server/service/system/mcp.go @@ -0,0 +1,93 @@ +package system + +import ( + "context" + "os" + "path/filepath" + "strings" + "text/template" + "unicode" + + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" +) + +type McpService struct{} + +func (s *McpService) CreateToolTemplate(_ context.Context, info systemReq.McpToolTemplateRequest) (string, error) { + serverRoot := resolveServiceServerRoot() + templatePath := filepath.Join(serverRoot, "resource", "mcp", "tools.tpl") + targetDir := filepath.Join(serverRoot, "mcp") + + files, err := template.New(filepath.Base(templatePath)).Funcs(template.FuncMap{ + "title": toToolStructName, + }).ParseFiles(templatePath) + if err != nil { + return "", err + } + + fileName := toToolFileName(info.Name) + targetPath := filepath.Join(targetDir, fileName+".go") + + f, err := os.Create(targetPath) + if err != nil { + return "", err + } + defer f.Close() + + if err := files.Execute(f, info); err != nil { + return "", err + } + + return targetPath, nil +} + +func resolveServiceServerRoot() string { + if cwd, err := os.Getwd(); err == nil { + if filepath.Base(cwd) == "server" { + return cwd + } + if _, err := os.Stat(filepath.Join(cwd, "server")); err == nil { + return filepath.Join(cwd, "server") + } + return cwd + } + return "server" +} + +func toToolFileName(name string) string { + var b strings.Builder + for index, r := range name { + if unicode.IsUpper(r) { + if index > 0 { + b.WriteByte('_') + } + b.WriteRune(unicode.ToLower(r)) + continue + } + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' { + b.WriteRune(unicode.ToLower(r)) + continue + } + b.WriteByte('_') + } + return strings.Trim(b.String(), "_") +} + +func toToolStructName(name string) string { + parts := strings.FieldsFunc(name, func(r rune) bool { + return !(unicode.IsLetter(r) || unicode.IsDigit(r)) + }) + var b strings.Builder + for _, part := range parts { + if part == "" { + continue + } + runes := []rune(strings.ToLower(part)) + runes[0] = unicode.ToUpper(runes[0]) + b.WriteString(string(runes)) + } + if b.Len() == 0 { + return "CustomTool" + } + return b.String() +} diff --git a/server/service/system/sys_auto_code_interface.go b/server/service/system/sys_auto_code_interface.go deleted file mode 100644 index 7939f14..0000000 --- a/server/service/system/sys_auto_code_interface.go +++ /dev/null @@ -1,55 +0,0 @@ -package system - -import ( - "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" -) - -type AutoCodeService struct{} - -type Database interface { - GetDB(businessDB string) (data []response.Db, err error) - GetTables(businessDB string, dbName string) (data []response.Table, err error) - GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) -} - -func (autoCodeService *AutoCodeService) Database(businessDB string) Database { - - if businessDB == "" { - switch global.GVA_CONFIG.System.DbType { - case "mysql": - return AutoCodeMysql - case "pgsql": - return AutoCodePgsql - case "mssql": - return AutoCodeMssql - case "oracle": - return AutoCodeOracle - case "sqlite": - return AutoCodeSqlite - default: - return AutoCodeMysql - } - } else { - for _, info := range global.GVA_CONFIG.DBList { - if info.AliasName == businessDB { - switch info.Type { - case "mysql": - return AutoCodeMysql - case "mssql": - return AutoCodeMssql - case "pgsql": - return AutoCodePgsql - case "oracle": - return AutoCodeOracle - case "sqlite": - return AutoCodeSqlite - default: - return AutoCodeMysql - } - } - } - return AutoCodeMysql - } - -} diff --git a/server/service/system/sys_auto_code_mssql.go b/server/service/system/sys_auto_code_mssql.go deleted file mode 100644 index 31bdc96..0000000 --- a/server/service/system/sys_auto_code_mssql.go +++ /dev/null @@ -1,83 +0,0 @@ -package system - -import ( - "fmt" - "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" -) - -var AutoCodeMssql = new(autoCodeMssql) - -type autoCodeMssql struct{} - -// GetDB 获取数据库的所有数据库名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodeMssql) GetDB(businessDB string) (data []response.Db, err error) { - var entities []response.Db - sql := "select name AS 'database' from sys.databases;" - if businessDB == "" { - err = global.GVA_DB.Raw(sql).Scan(&entities).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error - } - return entities, err -} - -// GetTables 获取数据库的所有表名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodeMssql) GetTables(businessDB string, dbName string) (data []response.Table, err error) { - var entities []response.Table - - sql := fmt.Sprintf(`select name as 'table_name' from %s.DBO.sysobjects where xtype='U'`, dbName) - if businessDB == "" { - err = global.GVA_DB.Raw(sql).Scan(&entities).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error - } - - return entities, err -} - -// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodeMssql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { - var entities []response.Column - sql := fmt.Sprintf(` -SELECT - sc.name AS column_name, - st.name AS data_type, - sc.max_length AS data_type_long, - CASE - WHEN pk.object_id IS NOT NULL THEN 1 - ELSE 0 - END AS primary_key, - sc.column_id -FROM - %s.sys.columns sc -JOIN - sys.types st ON sc.user_type_id=st.user_type_id -LEFT JOIN - %s.sys.objects so ON so.name='%s' AND so.type='U' -LEFT JOIN - %s.sys.indexes si ON si.object_id = so.object_id AND si.is_primary_key = 1 -LEFT JOIN - %s.sys.index_columns sic ON sic.object_id = si.object_id AND sic.index_id = si.index_id AND sic.column_id = sc.column_id -LEFT JOIN - %s.sys.key_constraints pk ON pk.object_id = si.object_id -WHERE - st.is_user_defined=0 AND sc.object_id = so.object_id -ORDER BY - sc.column_id -`, dbName, dbName, tableName, dbName, dbName, dbName) - - if businessDB == "" { - err = global.GVA_DB.Raw(sql).Scan(&entities).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error - } - - return entities, err -} diff --git a/server/service/system/sys_auto_code_mysql.go b/server/service/system/sys_auto_code_mysql.go deleted file mode 100644 index c7f0f1b..0000000 --- a/server/service/system/sys_auto_code_mysql.go +++ /dev/null @@ -1,83 +0,0 @@ -package system - -import ( - "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" -) - -var AutoCodeMysql = new(autoCodeMysql) - -type autoCodeMysql struct{} - -// GetDB 获取数据库的所有数据库名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodeMysql) GetDB(businessDB string) (data []response.Db, err error) { - var entities []response.Db - sql := "SELECT SCHEMA_NAME AS `database` FROM INFORMATION_SCHEMA.SCHEMATA;" - if businessDB == "" { - err = global.GVA_DB.Raw(sql).Scan(&entities).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error - } - return entities, err -} - -// GetTables 获取数据库的所有表名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodeMysql) GetTables(businessDB string, dbName string) (data []response.Table, err error) { - var entities []response.Table - sql := `select table_name as table_name from information_schema.tables where table_schema = ?` - if businessDB == "" { - err = global.GVA_DB.Raw(sql, dbName).Scan(&entities).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql, dbName).Scan(&entities).Error - } - - return entities, err -} - -// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodeMysql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { - var entities []response.Column - sql := ` - SELECT - c.COLUMN_NAME column_name, - c.DATA_TYPE data_type, - CASE c.DATA_TYPE - WHEN 'longtext' THEN c.CHARACTER_MAXIMUM_LENGTH - WHEN 'varchar' THEN c.CHARACTER_MAXIMUM_LENGTH - WHEN 'double' THEN CONCAT_WS(',', c.NUMERIC_PRECISION, c.NUMERIC_SCALE) - WHEN 'decimal' THEN CONCAT_WS(',', c.NUMERIC_PRECISION, c.NUMERIC_SCALE) - WHEN 'int' THEN c.NUMERIC_PRECISION - WHEN 'bigint' THEN c.NUMERIC_PRECISION - ELSE '' - END AS data_type_long, - c.COLUMN_COMMENT column_comment, - CASE WHEN kcu.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS primary_key, - c.ORDINAL_POSITION -FROM - INFORMATION_SCHEMA.COLUMNS c -LEFT JOIN - INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu -ON - c.TABLE_SCHEMA = kcu.TABLE_SCHEMA - AND c.TABLE_NAME = kcu.TABLE_NAME - AND c.COLUMN_NAME = kcu.COLUMN_NAME - AND kcu.CONSTRAINT_NAME = 'PRIMARY' -WHERE - c.TABLE_NAME = ? - AND c.TABLE_SCHEMA = ? -ORDER BY - c.ORDINAL_POSITION;` - if businessDB == "" { - err = global.GVA_DB.Raw(sql, tableName, dbName).Scan(&entities).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error - } - - return entities, err -} diff --git a/server/service/system/sys_auto_code_oracle.go b/server/service/system/sys_auto_code_oracle.go deleted file mode 100644 index fc3c43f..0000000 --- a/server/service/system/sys_auto_code_oracle.go +++ /dev/null @@ -1,72 +0,0 @@ -package system - -import ( - "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" -) - -var AutoCodeOracle = new(autoCodeOracle) - -type autoCodeOracle struct{} - -// GetDB 获取数据库的所有数据库名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodeOracle) GetDB(businessDB string) (data []response.Db, err error) { - var entities []response.Db - sql := `SELECT lower(username) AS "database" FROM all_users` - err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error - return entities, err -} - -// GetTables 获取数据库的所有表名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodeOracle) GetTables(businessDB string, dbName string) (data []response.Table, err error) { - var entities []response.Table - sql := `select lower(table_name) as "table_name" from all_tables where lower(owner) = ?` - - err = global.GVA_DBList[businessDB].Raw(sql, dbName).Scan(&entities).Error - return entities, err -} - -// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (s *autoCodeOracle) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { - var entities []response.Column - sql := ` - SELECT - lower(a.COLUMN_NAME) as "column_name", - (CASE WHEN a.DATA_TYPE = 'NUMBER' AND a.DATA_SCALE=0 THEN 'int' else lower(a.DATA_TYPE) end) as "data_type", - (CASE WHEN a.DATA_TYPE = 'NUMBER' THEN a.DATA_PRECISION else a.DATA_LENGTH end) as "data_type_long", - b.COMMENTS as "column_comment", - (CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END) as "primary_key", - a.COLUMN_ID -FROM - all_tab_columns a -JOIN - all_col_comments b ON a.OWNER = b.OWNER AND a.TABLE_NAME = b.TABLE_NAME AND a.COLUMN_NAME = b.COLUMN_NAME -LEFT JOIN - ( - SELECT - acc.OWNER, - acc.TABLE_NAME, - acc.COLUMN_NAME - FROM - all_cons_columns acc - JOIN - all_constraints ac ON acc.OWNER = ac.OWNER AND acc.CONSTRAINT_NAME = ac.CONSTRAINT_NAME - WHERE - ac.CONSTRAINT_TYPE = 'P' - ) pk ON a.OWNER = pk.OWNER AND a.TABLE_NAME = pk.TABLE_NAME AND a.COLUMN_NAME = pk.COLUMN_NAME -WHERE - lower(a.table_name) = ? - AND lower(a.OWNER) = ? -ORDER BY - a.COLUMN_ID -` - - err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error - return entities, err -} diff --git a/server/service/system/sys_auto_code_pgsql.go b/server/service/system/sys_auto_code_pgsql.go deleted file mode 100644 index fae16fb..0000000 --- a/server/service/system/sys_auto_code_pgsql.go +++ /dev/null @@ -1,135 +0,0 @@ -package system - -import ( - "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" -) - -var AutoCodePgsql = new(autoCodePgsql) - -type autoCodePgsql struct{} - -// GetDB 获取数据库的所有数据库名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (a *autoCodePgsql) GetDB(businessDB string) (data []response.Db, err error) { - var entities []response.Db - sql := `SELECT datname as database FROM pg_database WHERE datistemplate = false` - if businessDB == "" { - err = global.GVA_DB.Raw(sql).Scan(&entities).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error - } - - return entities, err -} - -// GetTables 获取数据库的所有表名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (a *autoCodePgsql) GetTables(businessDB string, dbName string) (data []response.Table, err error) { - var entities []response.Table - sql := `select table_name as table_name from information_schema.tables where table_catalog = ? and table_schema = ?` - - db := global.GVA_DB - if businessDB != "" { - db = global.GVA_DBList[businessDB] - } - - err = db.Raw(sql, dbName, "public").Scan(&entities).Error - return entities, err -} - -// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (a *autoCodePgsql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { - // todo 数据获取不全, 待完善sql - sql := ` -SELECT - psc.COLUMN_NAME AS COLUMN_NAME, - psc.udt_name AS data_type, - CASE - psc.udt_name - WHEN 'text' THEN - concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH ) - WHEN 'varchar' THEN - concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH ) - WHEN 'smallint' THEN - concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE ) - WHEN 'decimal' THEN - concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE ) - WHEN 'integer' THEN - concat_ws ( '', '', psc.NUMERIC_PRECISION ) - WHEN 'int4' THEN - concat_ws ( '', '', psc.NUMERIC_PRECISION ) - WHEN 'int8' THEN - concat_ws ( '', '', psc.NUMERIC_PRECISION ) - WHEN 'bigint' THEN - concat_ws ( '', '', psc.NUMERIC_PRECISION ) - WHEN 'timestamp' THEN - concat_ws ( '', '', psc.datetime_precision ) - ELSE '' - END AS data_type_long, - ( - SELECT - pd.description - FROM - pg_description pd - WHERE - (pd.objoid,pd.objsubid) in ( - SELECT pa.attrelid,pa.attnum - FROM - pg_attribute pa - WHERE pa.attrelid = ( SELECT oid FROM pg_class pc WHERE - pc.relname = psc.table_name - ) - and attname = psc.column_name - ) - ) AS column_comment, - ( - SELECT - COUNT(*) - FROM - pg_constraint - WHERE - contype = 'p' - AND conrelid = ( - SELECT - oid - FROM - pg_class - WHERE - relname = psc.table_name - ) - AND conkey::int[] @> ARRAY[( - SELECT - attnum::integer - FROM - pg_attribute - WHERE - attrelid = conrelid - AND attname = psc.column_name - )] - ) > 0 AS primary_key, - psc.ordinal_position -FROM - INFORMATION_SCHEMA.COLUMNS psc -WHERE - table_catalog = ? - AND table_schema = 'public' - AND TABLE_NAME = ? -ORDER BY - psc.ordinal_position; -` - var entities []response.Column - //sql = strings.ReplaceAll(sql, "@table_catalog", dbName) - //sql = strings.ReplaceAll(sql, "@table_name", tableName) - db := global.GVA_DB - if businessDB != "" { - db = global.GVA_DBList[businessDB] - } - - err = db.Raw(sql, dbName, tableName).Scan(&entities).Error - return entities, err -} diff --git a/server/service/system/sys_auto_code_sqlite.go b/server/service/system/sys_auto_code_sqlite.go deleted file mode 100644 index 59bcfce..0000000 --- a/server/service/system/sys_auto_code_sqlite.go +++ /dev/null @@ -1,84 +0,0 @@ -package system - -import ( - "fmt" - "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" - "path/filepath" - "strings" -) - -var AutoCodeSqlite = new(autoCodeSqlite) - -type autoCodeSqlite struct{} - -// GetDB 获取数据库的所有数据库名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (a *autoCodeSqlite) GetDB(businessDB string) (data []response.Db, err error) { - var entities []response.Db - sql := "PRAGMA database_list;" - var databaseList []struct { - File string `gorm:"column:file"` - } - if businessDB == "" { - err = global.GVA_DB.Raw(sql).Find(&databaseList).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql).Find(&databaseList).Error - } - for _, database := range databaseList { - if database.File != "" { - fileName := filepath.Base(database.File) - fileExt := filepath.Ext(fileName) - fileNameWithoutExt := strings.TrimSuffix(fileName, fileExt) - - entities = append(entities, response.Db{fileNameWithoutExt}) - } - } - // entities = append(entities, response.Db{global.GVA_CONFIG.Sqlite.Dbname}) - return entities, err -} - -// GetTables 获取数据库的所有表名 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (a *autoCodeSqlite) GetTables(businessDB string, dbName string) (data []response.Table, err error) { - var entities []response.Table - sql := `SELECT name FROM sqlite_master WHERE type='table'` - tabelNames := []string{} - if businessDB == "" { - err = global.GVA_DB.Raw(sql).Find(&tabelNames).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql).Find(&tabelNames).Error - } - for _, tabelName := range tabelNames { - entities = append(entities, response.Table{tabelName}) - } - return entities, err -} - -// GetColumn 获取指定数据表的所有字段名,类型值等 -// Author [piexlmax](https://github.com/piexlmax) -// Author [SliverHorn](https://github.com/SliverHorn) -func (a *autoCodeSqlite) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { - var entities []response.Column - sql := fmt.Sprintf("PRAGMA table_info(%s);", tableName) - var columnInfos []struct { - Name string `gorm:"column:name"` - Type string `gorm:"column:type"` - Pk int `gorm:"column:pk"` - } - if businessDB == "" { - err = global.GVA_DB.Raw(sql).Scan(&columnInfos).Error - } else { - err = global.GVA_DBList[businessDB].Raw(sql).Scan(&columnInfos).Error - } - for _, columnInfo := range columnInfos { - entities = append(entities, response.Column{ - ColumnName: columnInfo.Name, - DataType: columnInfo.Type, - PrimaryKey: columnInfo.Pk == 1, - }) - } - return entities, err -} diff --git a/server/service/system/sys_error.go b/server/service/system/sys_error.go index 065b624..acd7057 100644 --- a/server/service/system/sys_error.go +++ b/server/service/system/sys_error.go @@ -4,9 +4,9 @@ import ( "context" "fmt" "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/system" systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "strings" ) type SysErrorService struct{} @@ -96,7 +96,6 @@ func (sysErrorService *SysErrorService) GetSysErrorSolution(ctx context.Context, var se system.SysError _ = global.GVA_DB.Model(&system.SysError{}).Where("id = ?", id).First(&se).Error - // 构造 LLM 请求参数,使用管家模式(butler)根据错误信息生成解决方案 var form, info string if se.Form != nil { form = *se.Form @@ -105,22 +104,21 @@ func (sysErrorService *SysErrorService) GetSysErrorSolution(ctx context.Context, info = *se.Info } - llmReq := common.JSONMap{ - "mode": "solution", - "info": info, - "form": form, - } - - // 调用服务层 LLMAuto,忽略错误但尽量写入方案 - var solution string - if data, err := (&AutoCodeService{}).LLMAuto(context.Background(), llmReq); err == nil { - solution = fmt.Sprintf("%v", data.(map[string]interface{})["text"]) - _ = global.GVA_DB.Model(&system.SysError{}).Where("id = ?", id).Updates(map[string]interface{}{"status": "处理完成", "solution": solution}).Error - } else { - // 即使生成失败也标记为完成,避免任务卡住 - _ = global.GVA_DB.Model(&system.SysError{}).Where("id = ?", id).Update("status", "处理失败").Error - } + solution := buildSysErrorSolution(form, info) + _ = global.GVA_DB.Model(&system.SysError{}).Where("id = ?", id).Updates(map[string]interface{}{"status": "处理完成", "solution": solution}).Error }(ID) return nil } + +func buildSysErrorSolution(form string, info string) string { + parts := make([]string, 0, 3) + if form != "" { + parts = append(parts, fmt.Sprintf("来源模块:%s。", form)) + } + if info != "" { + parts = append(parts, fmt.Sprintf("错误信息:%s。", info)) + } + parts = append(parts, "建议先核对请求参数、服务日志和数据库状态,再根据错误发生时间定位对应操作记录。") + return strings.Join(parts, " ") +} diff --git a/server/service/system/sys_initdb_mssql.go b/server/service/system/sys_initdb_mssql.go index ad8d37c..3597bf7 100644 --- a/server/service/system/sys_initdb_mssql.go +++ b/server/service/system/sys_initdb_mssql.go @@ -11,7 +11,6 @@ import ( "github.com/gookit/color" "gorm.io/driver/sqlserver" "gorm.io/gorm" - "path/filepath" ) type MssqlInitHandler struct{} @@ -62,7 +61,6 @@ func (h MssqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (n return nil, err } - global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..") next = context.WithValue(next, "db", db) return next, err } diff --git a/server/service/system/sys_initdb_mysql.go b/server/service/system/sys_initdb_mysql.go index 2ef64d2..a9120dc 100644 --- a/server/service/system/sys_initdb_mysql.go +++ b/server/service/system/sys_initdb_mysql.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "path/filepath" "github.com/flipped-aurora/gin-vue-admin/server/config" "github.com/gookit/color" @@ -67,7 +66,6 @@ func (h MysqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (n }), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil { return ctx, err } - global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..") next = context.WithValue(next, "db", db) return next, err } diff --git a/server/service/system/sys_initdb_pgsql.go b/server/service/system/sys_initdb_pgsql.go index fa0a65a..b76afda 100644 --- a/server/service/system/sys_initdb_pgsql.go +++ b/server/service/system/sys_initdb_pgsql.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "path/filepath" "github.com/flipped-aurora/gin-vue-admin/server/config" "github.com/gookit/color" @@ -71,7 +70,6 @@ func (h PgsqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (n }), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil { return ctx, err } - global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..") next = context.WithValue(next, "db", db) return next, err } diff --git a/server/service/system/sys_initdb_sqlite.go b/server/service/system/sys_initdb_sqlite.go index a102ad3..09013f5 100644 --- a/server/service/system/sys_initdb_sqlite.go +++ b/server/service/system/sys_initdb_sqlite.go @@ -7,7 +7,6 @@ import ( "github.com/google/uuid" "github.com/gookit/color" "gorm.io/gorm" - "path/filepath" "github.com/flipped-aurora/gin-vue-admin/server/config" "github.com/flipped-aurora/gin-vue-admin/server/global" @@ -58,7 +57,6 @@ func (h SqliteInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) ( }); err != nil { return ctx, err } - global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..") next = context.WithValue(next, "db", db) return next, err } diff --git a/server/service/system/sys_skills.go b/server/service/system/sys_skills.go deleted file mode 100644 index c055dda..0000000 --- a/server/service/system/sys_skills.go +++ /dev/null @@ -1,785 +0,0 @@ -package system - -import ( - "archive/zip" - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "io/fs" - "net/http" - "os" - "path/filepath" - "sort" - "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" - "gopkg.in/yaml.v3" -) - -const ( - skillFileName = "SKILL.md" - globalConstraintFileName = "README.md" -) - -var skillToolOrder = []string{"copilot", "claude", "cursor", "trae", "codex"} - -var skillToolDirs = map[string]string{ - "copilot": ".aone_copilot", - "claude": ".claude", - "trae": ".trae", - "codex": ".codex", - "cursor": ".cursor", -} - -var skillToolLabels = map[string]string{ - "copilot": "Copilot", - "claude": "Claude", - "trae": "Trae", - "codex": "Codex", - "cursor": "Cursor", -} - -const defaultSkillMarkdown = "## 技能用途\n请在这里描述技能的目标、适用场景与限制条件。\n\n## 输入\n- 请补充输入格式与示例。\n\n## 输出\n- 请补充输出格式与示例。\n\n## 关键步骤\n1. 第一步\n2. 第二步\n\n## 示例\n在此补充一到两个典型示例。\n" - -const defaultResourceMarkdown = "# 资源说明\n请在这里补充资源内容。\n" - -const defaultReferenceMarkdown = "# 参考资料\n请在这里补充参考资料内容。\n" - -const defaultTemplateMarkdown = "# 模板\n请在这里补充模板内容。\n" - -const defaultGlobalConstraintMarkdown = "# 全局约束\n请在这里补充该工具的统一约束与使用规范。\n" - -type SkillsService struct{} - -func (s *SkillsService) Tools(_ context.Context) ([]system.SkillTool, error) { - tools := make([]system.SkillTool, 0, len(skillToolOrder)) - for _, key := range skillToolOrder { - if _, err := s.toolSkillsDir(key); err != nil { - return nil, err - } - tools = append(tools, system.SkillTool{Key: key, Label: skillToolLabels[key]}) - } - return tools, nil -} - -func (s *SkillsService) List(_ context.Context, tool string) ([]string, error) { - skillsDir, err := s.toolSkillsDir(tool) - if err != nil { - return nil, err - } - entries, err := os.ReadDir(skillsDir) - if err != nil { - return nil, err - } - var skills []string - for _, entry := range entries { - if entry.IsDir() { - skills = append(skills, entry.Name()) - } - } - sort.Strings(skills) - return skills, nil -} - -func (s *SkillsService) Detail(_ context.Context, tool, skill string) (system.SkillDetail, error) { - var detail system.SkillDetail - if !isSafeName(skill) { - return detail, errors.New("技能名称不合法") - } - detail.Tool = tool - detail.Skill = skill - - skillDir, err := s.skillDir(tool, skill) - if err != nil { - return detail, err - } - - skillFilePath := filepath.Join(skillDir, skillFileName) - content, err := os.ReadFile(skillFilePath) - if err != nil { - if !os.IsNotExist(err) { - return detail, err - } - detail.Meta = system.SkillMeta{Name: skill} - detail.Markdown = defaultSkillMarkdown - } else { - meta, body, parseErr := parseSkillContent(string(content)) - if parseErr != nil { - meta = system.SkillMeta{Name: skill} - body = string(content) - } - if meta.Name == "" { - meta.Name = skill - } - detail.Meta = meta - detail.Markdown = body - } - - detail.Scripts = listFiles(filepath.Join(skillDir, "scripts")) - detail.Resources = listFiles(filepath.Join(skillDir, "resources")) - detail.References = listFiles(filepath.Join(skillDir, "references")) - detail.Templates = listFiles(filepath.Join(skillDir, "templates")) - return detail, nil -} - -func (s *SkillsService) Save(_ context.Context, req request.SkillSaveRequest) error { - if !isSafeName(req.Skill) { - return errors.New("技能名称不合法") - } - skillDir, err := s.ensureSkillDir(req.Tool, req.Skill) - if err != nil { - return err - } - if req.Meta.Name == "" { - req.Meta.Name = req.Skill - } - content, err := buildSkillContent(req.Meta, req.Markdown) - if err != nil { - return err - } - if err := os.WriteFile(filepath.Join(skillDir, skillFileName), []byte(content), 0644); err != nil { - return err - } - - if len(req.SyncTools) > 0 { - for _, tool := range req.SyncTools { - if tool == req.Tool { - continue - } - targetDir, err := s.ensureSkillDir(tool, req.Skill) - if err != nil { - return err - } - if err := copySkillDir(skillDir, targetDir); err != nil { - return err - } - } - } - return nil -} - -func (s *SkillsService) Delete(_ context.Context, req request.SkillDeleteRequest) error { - if strings.TrimSpace(req.Tool) == "" { - return errors.New("工具类型不能为空") - } - if !isSafeName(req.Skill) { - return errors.New("技能名称不合法") - } - skillDir, err := s.skillDir(req.Tool, req.Skill) - if err != nil { - return err - } - info, err := os.Stat(skillDir) - if err != nil { - if os.IsNotExist(err) { - return errors.New("技能不存在") - } - return err - } - if !info.IsDir() { - return errors.New("技能目录异常") - } - return os.RemoveAll(skillDir) -} - -func (s *SkillsService) Package(_ context.Context, req request.SkillPackageRequest) (string, []byte, error) { - if strings.TrimSpace(req.Tool) == "" { - return "", nil, errors.New("工具类型不能为空") - } - if !isSafeName(req.Skill) { - return "", nil, errors.New("技能名称不合法") - } - - skillDir, err := s.skillDir(req.Tool, req.Skill) - if err != nil { - return "", nil, err - } - info, err := os.Stat(skillDir) - if err != nil { - if os.IsNotExist(err) { - return "", nil, errors.New("技能不存在") - } - return "", nil, err - } - if !info.IsDir() { - return "", nil, errors.New("技能目录异常") - } - - buf := bytes.NewBuffer(nil) - zw := zip.NewWriter(buf) - - walkErr := filepath.WalkDir(skillDir, func(path string, d fs.DirEntry, walkErr error) error { - if walkErr != nil { - return walkErr - } - rel, err := filepath.Rel(skillDir, path) - if err != nil { - return err - } - if rel == "." { - return nil - } - zipName := filepath.ToSlash(rel) - if d.IsDir() { - _, err = zw.Create(strings.TrimSuffix(zipName, "/") + "/") - return err - } - - fileInfo, err := d.Info() - if err != nil { - return err - } - header, err := zip.FileInfoHeader(fileInfo) - if err != nil { - return err - } - header.Name = zipName - header.Method = zip.Deflate - - writer, err := zw.CreateHeader(header) - if err != nil { - return err - } - content, err := os.ReadFile(path) - if err != nil { - return err - } - _, err = writer.Write(content) - return err - }) - if walkErr != nil { - _ = zw.Close() - return "", nil, walkErr - } - - if err = zw.Close(); err != nil { - return "", nil, err - } - - return req.Skill + ".zip", buf.Bytes(), nil -} - -func (s *SkillsService) CreateScript(_ context.Context, req request.SkillScriptCreateRequest) (string, string, error) { - if !isSafeName(req.Skill) { - return "", "", errors.New("技能名称不合法") - } - fileName, lang, err := buildScriptFileName(req.FileName, req.ScriptType) - if err != nil { - return "", "", err - } - if lang == "" { - return "", "", errors.New("脚本类型不支持") - } - skillDir, err := s.ensureSkillDir(req.Tool, req.Skill) - if err != nil { - return "", "", err - } - filePath := filepath.Join(skillDir, "scripts", fileName) - if _, err := os.Stat(filePath); err == nil { - return "", "", errors.New("脚本已存在") - } - if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { - return "", "", err - } - content := scriptTemplate(lang) - if err := os.WriteFile(filePath, []byte(content), 0644); err != nil { - return "", "", err - } - return fileName, content, nil -} - -func (s *SkillsService) GetScript(_ context.Context, req request.SkillFileRequest) (string, error) { - return s.readSkillFile(req.Tool, req.Skill, "scripts", req.FileName) -} - -func (s *SkillsService) SaveScript(_ context.Context, req request.SkillFileSaveRequest) error { - return s.writeSkillFile(req.Tool, req.Skill, "scripts", req.FileName, req.Content) -} - -func (s *SkillsService) CreateResource(_ context.Context, req request.SkillResourceCreateRequest) (string, string, error) { - return s.createMarkdownFile(req.Tool, req.Skill, "resources", req.FileName, defaultResourceMarkdown, "资源") -} - -func (s *SkillsService) GetResource(_ context.Context, req request.SkillFileRequest) (string, error) { - return s.readSkillFile(req.Tool, req.Skill, "resources", req.FileName) -} - -func (s *SkillsService) SaveResource(_ context.Context, req request.SkillFileSaveRequest) error { - return s.writeSkillFile(req.Tool, req.Skill, "resources", req.FileName, req.Content) -} - -func (s *SkillsService) CreateReference(_ context.Context, req request.SkillReferenceCreateRequest) (string, string, error) { - return s.createMarkdownFile(req.Tool, req.Skill, "references", req.FileName, defaultReferenceMarkdown, "参考") -} - -func (s *SkillsService) GetReference(_ context.Context, req request.SkillFileRequest) (string, error) { - return s.readSkillFile(req.Tool, req.Skill, "references", req.FileName) -} - -func (s *SkillsService) SaveReference(_ context.Context, req request.SkillFileSaveRequest) error { - return s.writeSkillFile(req.Tool, req.Skill, "references", req.FileName, req.Content) -} - -func (s *SkillsService) CreateTemplate(_ context.Context, req request.SkillTemplateCreateRequest) (string, string, error) { - return s.createMarkdownFile(req.Tool, req.Skill, "templates", req.FileName, defaultTemplateMarkdown, "模板") -} - -func (s *SkillsService) GetTemplate(_ context.Context, req request.SkillFileRequest) (string, error) { - return s.readSkillFile(req.Tool, req.Skill, "templates", req.FileName) -} - -func (s *SkillsService) SaveTemplate(_ context.Context, req request.SkillFileSaveRequest) error { - return s.writeSkillFile(req.Tool, req.Skill, "templates", req.FileName, req.Content) -} - -func (s *SkillsService) GetGlobalConstraint(_ context.Context, tool string) (string, bool, error) { - skillsDir, err := s.toolSkillsDir(tool) - if err != nil { - return "", false, err - } - filePath := filepath.Join(skillsDir, globalConstraintFileName) - content, err := os.ReadFile(filePath) - if err != nil { - if os.IsNotExist(err) { - return defaultGlobalConstraintMarkdown, false, nil - } - return "", false, err - } - return string(content), true, nil -} - -func (s *SkillsService) SaveGlobalConstraint(_ context.Context, req request.SkillGlobalConstraintSaveRequest) error { - if strings.TrimSpace(req.Tool) == "" { - return errors.New("工具类型不能为空") - } - writeConstraint := func(tool, content string) error { - skillsDir, err := s.toolSkillsDir(tool) - if err != nil { - return err - } - filePath := filepath.Join(skillsDir, globalConstraintFileName) - if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { - return err - } - return os.WriteFile(filePath, []byte(content), 0644) - } - if err := writeConstraint(req.Tool, req.Content); err != nil { - return err - } - if len(req.SyncTools) == 0 { - return nil - } - for _, tool := range req.SyncTools { - if tool == "" || tool == req.Tool { - continue - } - if err := writeConstraint(tool, req.Content); err != nil { - return err - } - } - return nil -} - -func (s *SkillsService) DownloadOnlineSkill(_ context.Context, req request.DownloadOnlineSkillReq) error { - skillsDir, err := s.toolSkillsDir(req.Tool) - if err != nil { - return err - } - - body, err := json.Marshal(map[string]interface{}{ - "plugin_id": req.ID, - "version": req.Version, - }) - if err != nil { - return fmt.Errorf("构建下载请求失败: %w", err) - } - - downloadReq, err := http.NewRequest(http.MethodPost, "https://plugin.gin-vue-admin.com/api/shopPlugin/downloadSkill", bytes.NewReader(body)) - if err != nil { - return fmt.Errorf("构建下载请求失败: %w", err) - } - downloadReq.Header.Set("Content-Type", "application/json") - - downloadResp, err := http.DefaultClient.Do(downloadReq) - if err != nil { - return fmt.Errorf("下载技能失败: %w", err) - } - defer downloadResp.Body.Close() - - if downloadResp.StatusCode != http.StatusOK { - return fmt.Errorf("下载技能失败, HTTP状态码: %d", downloadResp.StatusCode) - } - - metaBody, err := io.ReadAll(downloadResp.Body) - if err != nil { - return fmt.Errorf("读取下载结果失败: %w", err) - } - - var meta struct { - Data struct { - URL string `json:"url"` - } `json:"data"` - } - if err = json.Unmarshal(metaBody, &meta); err != nil { - return fmt.Errorf("解析下载结果失败: %w", err) - } - - realDownloadURL := strings.TrimSpace(meta.Data.URL) - if realDownloadURL == "" { - return errors.New("下载结果缺少 url") - } - - zipResp, err := http.Get(realDownloadURL) - if err != nil { - return fmt.Errorf("下载压缩包失败: %w", err) - } - defer zipResp.Body.Close() - - if zipResp.StatusCode != http.StatusOK { - return fmt.Errorf("下载压缩包失败, HTTP状态码: %d", zipResp.StatusCode) - } - - tmpFile, err := os.CreateTemp("", "gva-skill-*.zip") - if err != nil { - return fmt.Errorf("创建临时文件失败: %w", err) - } - tmpPath := tmpFile.Name() - defer os.Remove(tmpPath) - - if _, err = io.Copy(tmpFile, zipResp.Body); err != nil { - tmpFile.Close() - return fmt.Errorf("保存技能包失败: %w", err) - } - tmpFile.Close() - - if err = extractZipToDir(tmpPath, skillsDir); err != nil { - return fmt.Errorf("解压技能包失败: %w", err) - } - - return nil -} - -func extractZipToDir(zipPath, destDir string) error { - r, err := zip.OpenReader(zipPath) - if err != nil { - return err - } - defer r.Close() - - for _, f := range r.File { - name := filepath.FromSlash(f.Name) - if strings.Contains(name, "..") { - continue - } - - target := filepath.Join(destDir, name) - if !strings.HasPrefix(filepath.Clean(target), filepath.Clean(destDir)) { - continue - } - - if f.FileInfo().IsDir() { - if err := os.MkdirAll(target, os.ModePerm); err != nil { - return err - } - continue - } - - if err := os.MkdirAll(filepath.Dir(target), os.ModePerm); err != nil { - return err - } - - rc, err := f.Open() - if err != nil { - return err - } - - out, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) - if err != nil { - rc.Close() - return err - } - - _, err = io.Copy(out, rc) - rc.Close() - out.Close() - if err != nil { - return err - } - } - - return nil -} - -func (s *SkillsService) toolSkillsDir(tool string) (string, error) { - toolDir, ok := skillToolDirs[tool] - if !ok { - return "", errors.New("工具类型不支持") - } - root := strings.TrimSpace(global.GVA_CONFIG.AutoCode.Root) - if root == "" { - root = "." - } - skillsDir := filepath.Join(root, toolDir, "skills") - if err := os.MkdirAll(skillsDir, os.ModePerm); err != nil { - return "", err - } - return skillsDir, nil -} - -func (s *SkillsService) skillDir(tool, skill string) (string, error) { - skillsDir, err := s.toolSkillsDir(tool) - if err != nil { - return "", err - } - return filepath.Join(skillsDir, skill), nil -} - -func (s *SkillsService) ensureSkillDir(tool, skill string) (string, error) { - if !isSafeName(skill) { - return "", errors.New("技能名称不合法") - } - skillDir, err := s.skillDir(tool, skill) - if err != nil { - return "", err - } - if err := os.MkdirAll(skillDir, os.ModePerm); err != nil { - return "", err - } - return skillDir, nil -} - -func (s *SkillsService) createMarkdownFile(tool, skill, subDir, fileName, defaultContent, label string) (string, string, error) { - if !isSafeName(skill) { - return "", "", errors.New("技能名称不合法") - } - cleanName, err := buildResourceFileName(fileName) - if err != nil { - return "", "", err - } - skillDir, err := s.ensureSkillDir(tool, skill) - if err != nil { - return "", "", err - } - filePath := filepath.Join(skillDir, subDir, cleanName) - if _, err := os.Stat(filePath); err == nil { - if label == "" { - label = "文件" - } - return "", "", fmt.Errorf("%s已存在", label) - } - if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { - return "", "", err - } - content := defaultContent - if err := os.WriteFile(filePath, []byte(content), 0644); err != nil { - return "", "", err - } - return cleanName, content, nil -} - -func (s *SkillsService) readSkillFile(tool, skill, subDir, fileName string) (string, error) { - if !isSafeName(skill) { - return "", errors.New("技能名称不合法") - } - if !isSafeFileName(fileName) { - return "", errors.New("文件名不合法") - } - skillDir, err := s.skillDir(tool, skill) - if err != nil { - return "", err - } - filePath := filepath.Join(skillDir, subDir, fileName) - content, err := os.ReadFile(filePath) - if err != nil { - return "", err - } - return string(content), nil -} - -func (s *SkillsService) writeSkillFile(tool, skill, subDir, fileName, content string) error { - if !isSafeName(skill) { - return errors.New("技能名称不合法") - } - if !isSafeFileName(fileName) { - return errors.New("文件名不合法") - } - skillDir, err := s.ensureSkillDir(tool, skill) - if err != nil { - return err - } - filePath := filepath.Join(skillDir, subDir, fileName) - if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { - return err - } - return os.WriteFile(filePath, []byte(content), 0644) -} - -func parseSkillContent(content string) (system.SkillMeta, string, error) { - clean := strings.TrimPrefix(content, "\ufeff") - lines := strings.Split(clean, "\n") - if len(lines) == 0 || strings.TrimSpace(lines[0]) != "---" { - return system.SkillMeta{}, clean, nil - } - end := -1 - for i := 1; i < len(lines); i++ { - if strings.TrimSpace(lines[i]) == "---" { - end = i - break - } - } - if end == -1 { - return system.SkillMeta{}, clean, nil - } - yamlText := strings.Join(lines[1:end], "\n") - body := strings.Join(lines[end+1:], "\n") - var meta system.SkillMeta - if err := yaml.Unmarshal([]byte(yamlText), &meta); err != nil { - return system.SkillMeta{}, body, err - } - return meta, body, nil -} - -func buildSkillContent(meta system.SkillMeta, markdown string) (string, error) { - if meta.Name == "" { - return "", errors.New("name不能为空") - } - data, err := yaml.Marshal(meta) - if err != nil { - return "", err - } - yamlText := strings.TrimRight(string(data), "\n") - body := strings.TrimLeft(markdown, "\n") - if body != "" { - body = body + "\n" - } - return fmt.Sprintf("---\n%s\n---\n%s", yamlText, body), nil -} - -func listFiles(dir string) []string { - entries, err := os.ReadDir(dir) - if err != nil { - return []string{} - } - files := make([]string, 0, len(entries)) - for _, entry := range entries { - if entry.Type().IsRegular() { - files = append(files, entry.Name()) - } - } - sort.Strings(files) - return files -} - -func isSafeName(name string) bool { - if strings.TrimSpace(name) == "" { - return false - } - if strings.Contains(name, "..") { - return false - } - if strings.ContainsAny(name, "/\\") { - return false - } - return name == filepath.Base(name) -} - -func isSafeFileName(name string) bool { - if strings.TrimSpace(name) == "" { - return false - } - if strings.Contains(name, "..") { - return false - } - if strings.ContainsAny(name, "/\\") { - return false - } - return name == filepath.Base(name) -} - -func buildScriptFileName(fileName, scriptType string) (string, string, error) { - clean := strings.TrimSpace(fileName) - if clean == "" { - return "", "", errors.New("文件名不能为空") - } - if !isSafeFileName(clean) { - return "", "", errors.New("文件名不合法") - } - base := strings.TrimSuffix(clean, filepath.Ext(clean)) - if base == "" { - return "", "", errors.New("文件名不合法") - } - - switch strings.ToLower(scriptType) { - case "py", "python": - return base + ".py", "python", nil - case "js", "javascript", "script": - return base + ".js", "javascript", nil - case "sh", "shell", "bash": - return base + ".sh", "sh", nil - default: - return "", "", errors.New("脚本类型不支持") - } -} - -func buildResourceFileName(fileName string) (string, error) { - clean := strings.TrimSpace(fileName) - if clean == "" { - return "", errors.New("文件名不能为空") - } - if !isSafeFileName(clean) { - return "", errors.New("文件名不合法") - } - base := strings.TrimSuffix(clean, filepath.Ext(clean)) - if base == "" { - return "", errors.New("文件名不合法") - } - return base + ".md", nil -} - -func scriptTemplate(lang string) string { - switch lang { - case "python": - return "# -*- coding: utf-8 -*-\n# TODO: 在这里实现脚本逻辑\n" - case "javascript": - return "// TODO: 在这里实现脚本逻辑\n" - case "sh": - return "#!/usr/bin/env bash\nset -euo pipefail\n\n# TODO: 在这里实现脚本逻辑\n" - default: - return "" - } -} - -func copySkillDir(src, dst string) error { - return filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - rel, err := filepath.Rel(src, path) - if err != nil { - return err - } - if rel == "." { - return nil - } - target := filepath.Join(dst, rel) - if d.IsDir() { - return os.MkdirAll(target, os.ModePerm) - } - if !d.Type().IsRegular() { - return nil - } - data, err := os.ReadFile(path) - if err != nil { - return err - } - if err := os.MkdirAll(filepath.Dir(target), os.ModePerm); err != nil { - return err - } - return os.WriteFile(target, data, 0644) - }) -} diff --git a/server/source/example/file_upload_download.go b/server/source/common/file_upload_download.go similarity index 72% rename from server/source/example/file_upload_download.go rename to server/source/common/file_upload_download.go index 0f6f7a7..3d1c382 100644 --- a/server/source/example/file_upload_download.go +++ b/server/source/common/file_upload_download.go @@ -1,8 +1,8 @@ -package example +package common import ( "context" - "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/service/system" "github.com/pkg/errors" "gorm.io/gorm" @@ -22,7 +22,7 @@ func (i *initExaFileMysql) MigrateTable(ctx context.Context) (context.Context, e if !ok { return ctx, system.ErrMissingDBContext } - return ctx, db.AutoMigrate(&example.ExaFileUploadAndDownload{}) + return ctx, db.AutoMigrate(&commonModel.ExaFileUploadAndDownload{}) } func (i *initExaFileMysql) TableCreated(ctx context.Context) bool { @@ -30,11 +30,11 @@ func (i *initExaFileMysql) TableCreated(ctx context.Context) bool { if !ok { return false } - return db.Migrator().HasTable(&example.ExaFileUploadAndDownload{}) + return db.Migrator().HasTable(&commonModel.ExaFileUploadAndDownload{}) } func (i *initExaFileMysql) InitializerName() string { - return example.ExaFileUploadAndDownload{}.TableName() + return commonModel.ExaFileUploadAndDownload{}.TableName() } func (i *initExaFileMysql) InitializeData(ctx context.Context) (context.Context, error) { @@ -42,12 +42,12 @@ func (i *initExaFileMysql) InitializeData(ctx context.Context) (context.Context, if !ok { return ctx, system.ErrMissingDBContext } - entities := []example.ExaFileUploadAndDownload{ + entities := []commonModel.ExaFileUploadAndDownload{ {Name: "10.png", Url: "https://qmplusimg.henrongyi.top/gvalogo.png", Tag: "png", Key: "158787308910.png"}, {Name: "logo.png", Url: "https://qmplusimg.henrongyi.top/1576554439myAvatar.png", Tag: "png", Key: "1587973709logo.png"}, } if err := db.Create(&entities).Error; err != nil { - return ctx, errors.Wrap(err, example.ExaFileUploadAndDownload{}.TableName()+"表数据初始化失败!") + return ctx, errors.Wrap(err, commonModel.ExaFileUploadAndDownload{}.TableName()+"表数据初始化失败!") } return ctx, nil } @@ -57,7 +57,7 @@ func (i *initExaFileMysql) DataInserted(ctx context.Context) bool { if !ok { return false } - lookup := example.ExaFileUploadAndDownload{Name: "logo.png", Key: "1587973709logo.png"} + lookup := commonModel.ExaFileUploadAndDownload{Name: "logo.png", Key: "1587973709logo.png"} if errors.Is(db.First(&lookup, &lookup).Error, gorm.ErrRecordNotFound) { return false } diff --git a/server/source/system/api.go b/server/source/system/api.go index a1330c3..88165c2 100644 --- a/server/source/system/api.go +++ b/server/source/system/api.go @@ -116,60 +116,12 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) { {ApiGroup: "系统服务", Method: "POST", Path: "/system/getSystemConfig", Description: "获取配置文件内容"}, {ApiGroup: "系统服务", Method: "POST", Path: "/system/setSystemConfig", Description: "设置配置文件内容"}, - {ApiGroup: "skills", Method: "GET", Path: "/skills/getTools", Description: "获取技能工具列表"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/getSkillList", Description: "获取技能列表"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/getSkillDetail", Description: "获取技能详情"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/saveSkill", Description: "保存技能定义"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/deleteSkill", Description: "删除技能"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/createScript", Description: "创建技能脚本"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/getScript", Description: "读取技能脚本"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/saveScript", Description: "保存技能脚本"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/createResource", Description: "创建技能资源"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/getResource", Description: "读取技能资源"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/saveResource", Description: "保存技能资源"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/createReference", Description: "创建技能参考"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/getReference", Description: "读取技能参考"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/saveReference", Description: "保存技能参考"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/createTemplate", Description: "创建技能模板"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/getTemplate", Description: "读取技能模板"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/saveTemplate", Description: "保存技能模板"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/getGlobalConstraint", Description: "读取全局约束"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/saveGlobalConstraint", Description: "保存全局约束"}, - {ApiGroup: "skills", Method: "POST", Path: "/skills/packageSkill", Description: "打包技能"}, - - {ApiGroup: "客户", Method: "PUT", Path: "/customer/customer", Description: "更新客户"}, - {ApiGroup: "客户", Method: "POST", Path: "/customer/customer", Description: "创建客户"}, - {ApiGroup: "客户", Method: "DELETE", Path: "/customer/customer", Description: "删除客户"}, - {ApiGroup: "客户", Method: "GET", Path: "/customer/customer", Description: "获取单一客户"}, - {ApiGroup: "客户", Method: "GET", Path: "/customer/customerList", Description: "获取客户列表"}, - - {ApiGroup: "代码生成器", Method: "GET", Path: "/autoCode/getDB", Description: "获取所有数据库"}, - {ApiGroup: "代码生成器", Method: "GET", Path: "/autoCode/getTables", Description: "获取数据库表"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/createTemp", Description: "自动化代码"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/preview", Description: "预览自动化代码"}, - {ApiGroup: "代码生成器", Method: "GET", Path: "/autoCode/getColumn", Description: "获取所选table的所有字段"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcp", Description: "自动生成 MCP Tool 模板"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcpStatus", Description: "获取 MCP 独立服务状态"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcpStart", Description: "启动 MCP 独立服务"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcpStop", Description: "停用 MCP 独立服务"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcpTest", Description: "MCP Tool 管理"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcpList", Description: "获取 MCP ToolList"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/saveAIWorkflowSession", Description: "保存AI需求工作流会话"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/getAIWorkflowSessionList", Description: "获取AI需求工作流会话列表"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/getAIWorkflowSessionDetail", Description: "获取AI需求工作流会话详情"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/deleteAIWorkflowSession", Description: "删除AI需求工作流会话"}, - {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/dumpAIWorkflowMarkdown", Description: "AI需求工作流Markdown落盘"}, - - {ApiGroup: "模板配置", Method: "POST", Path: "/autoCode/createPackage", Description: "配置模板"}, - {ApiGroup: "模板配置", Method: "GET", Path: "/autoCode/getTemplates", Description: "获取模板文件"}, - {ApiGroup: "模板配置", Method: "POST", Path: "/autoCode/getPackage", Description: "获取所有模板"}, - {ApiGroup: "模板配置", Method: "POST", Path: "/autoCode/delPackage", Description: "删除模板"}, - - {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/getMeta", Description: "获取meta信息"}, - {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/rollback", Description: "回滚自动生成代码"}, - {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/getSysHistory", Description: "查询回滚记录"}, - {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/delSysHistory", Description: "删除回滚记录"}, - {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/addFunc", Description: "增加模板方法"}, + {ApiGroup: "MCP管理", Method: "GET", Path: "/mcp/status", Description: "获取 MCP 独立服务状态"}, + {ApiGroup: "MCP管理", Method: "POST", Path: "/mcp/start", Description: "启动 MCP 独立服务"}, + {ApiGroup: "MCP管理", Method: "POST", Path: "/mcp/stop", Description: "停用 MCP 独立服务"}, + {ApiGroup: "MCP管理", Method: "GET", Path: "/mcp/tools", Description: "获取 MCP 工具列表"}, + {ApiGroup: "MCP管理", Method: "POST", Path: "/mcp/test", Description: "调试 MCP 工具"}, + {ApiGroup: "MCP管理", Method: "POST", Path: "/mcp/createTool", Description: "生成 MCP Tool 模板"}, {ApiGroup: "系统字典详情", Method: "PUT", Path: "/sysDictionaryDetail/updateSysDictionaryDetail", Description: "更新字典内容"}, {ApiGroup: "系统字典详情", Method: "POST", Path: "/sysDictionaryDetail/createSysDictionaryDetail", Description: "新增字典内容"}, diff --git a/server/source/system/api_ignore.go b/server/source/system/api_ignore.go index e18aa7c..d11f277 100644 --- a/server/source/system/api_ignore.go +++ b/server/source/system/api_ignore.go @@ -48,8 +48,6 @@ func (i *initApiIgnore) InitializeData(ctx context.Context) (context.Context, er {Method: "GET", Path: "/uploads/file/*filepath"}, {Method: "GET", Path: "/health"}, {Method: "HEAD", Path: "/uploads/file/*filepath"}, - {Method: "POST", Path: "/autoCode/llmAuto"}, - {Method: "POST", Path: "/autoCode/llmAutoSSE"}, {Method: "POST", Path: "/system/reloadSystem"}, {Method: "POST", Path: "/base/login"}, {Method: "POST", Path: "/base/captcha"}, diff --git a/server/source/system/casbin.go b/server/source/system/casbin.go index 370e1d7..106055d 100644 --- a/server/source/system/casbin.go +++ b/server/source/system/casbin.go @@ -118,58 +118,12 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error {Ptype: "p", V0: "888", V1: "/system/setSystemConfig", V2: "POST"}, {Ptype: "p", V0: "888", V1: "/system/getServerInfo", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/getTools", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/skills/getSkillList", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/getSkillDetail", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/saveSkill", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/deleteSkill", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/createScript", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/getScript", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/saveScript", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/createResource", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/getResource", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/saveResource", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/createReference", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/getReference", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/saveReference", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/createTemplate", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/getTemplate", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/saveTemplate", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/getGlobalConstraint", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/saveGlobalConstraint", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/skills/packageSkill", V2: "POST"}, - - {Ptype: "p", V0: "888", V1: "/customer/customer", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/customer/customer", V2: "PUT"}, - {Ptype: "p", V0: "888", V1: "/customer/customer", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/customer/customer", V2: "DELETE"}, - {Ptype: "p", V0: "888", V1: "/customer/customerList", V2: "GET"}, - - {Ptype: "p", V0: "888", V1: "/autoCode/getDB", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/autoCode/getMeta", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/preview", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/getTables", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/autoCode/getColumn", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/autoCode/rollback", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/createTemp", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/delSysHistory", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/getSysHistory", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/createPackage", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/getTemplates", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/autoCode/getPackage", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/delPackage", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/addFunc", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/mcp", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/mcpStatus", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/mcpStart", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/mcpStop", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/mcpTest", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/mcpList", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/saveAIWorkflowSession", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/getAIWorkflowSessionList", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/getAIWorkflowSessionDetail", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/deleteAIWorkflowSession", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/autoCode/dumpAIWorkflowMarkdown", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/mcp/status", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/mcp/start", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/mcp/stop", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/mcp/tools", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/mcp/test", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/mcp/createTool", V2: "POST"}, {Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/findSysDictionaryDetail", V2: "GET"}, {Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/updateSysDictionaryDetail", V2: "PUT"}, @@ -280,11 +234,6 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error {Ptype: "p", V0: "8881", V1: "/jwt/jsonInBlacklist", V2: "POST"}, {Ptype: "p", V0: "8881", V1: "/system/getSystemConfig", V2: "POST"}, {Ptype: "p", V0: "8881", V1: "/system/setSystemConfig", V2: "POST"}, - {Ptype: "p", V0: "8881", V1: "/customer/customer", V2: "POST"}, - {Ptype: "p", V0: "8881", V1: "/customer/customer", V2: "PUT"}, - {Ptype: "p", V0: "8881", V1: "/customer/customer", V2: "DELETE"}, - {Ptype: "p", V0: "8881", V1: "/customer/customer", V2: "GET"}, - {Ptype: "p", V0: "8881", V1: "/customer/customerList", V2: "GET"}, {Ptype: "p", V0: "8881", V1: "/user/getUserInfo", V2: "GET"}, {Ptype: "p", V0: "9528", V1: "/user/admin_register", V2: "POST"}, @@ -324,20 +273,12 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error {Ptype: "p", V0: "9528", V1: "/jwt/jsonInBlacklist", V2: "POST"}, {Ptype: "p", V0: "9528", V1: "/system/getSystemConfig", V2: "POST"}, {Ptype: "p", V0: "9528", V1: "/system/setSystemConfig", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/customer/customer", V2: "PUT"}, - {Ptype: "p", V0: "9528", V1: "/customer/customer", V2: "GET"}, - {Ptype: "p", V0: "9528", V1: "/customer/customer", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/customer/customer", V2: "DELETE"}, - {Ptype: "p", V0: "9528", V1: "/customer/customerList", V2: "GET"}, - {Ptype: "p", V0: "9528", V1: "/autoCode/createTemp", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/autoCode/mcpStatus", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/autoCode/mcpStart", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/autoCode/mcpStop", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/autoCode/saveAIWorkflowSession", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/autoCode/getAIWorkflowSessionList", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/autoCode/getAIWorkflowSessionDetail", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/autoCode/deleteAIWorkflowSession", V2: "POST"}, - {Ptype: "p", V0: "9528", V1: "/autoCode/dumpAIWorkflowMarkdown", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/mcp/status", V2: "GET"}, + {Ptype: "p", V0: "9528", V1: "/mcp/start", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/mcp/stop", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/mcp/tools", V2: "GET"}, + {Ptype: "p", V0: "9528", V1: "/mcp/test", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/mcp/createTool", V2: "POST"}, {Ptype: "p", V0: "9528", V1: "/user/getUserInfo", V2: "GET"}, } if err := db.Create(&entities).Error; err != nil { diff --git a/server/source/system/menu.go b/server/source/system/menu.go index 814a1bf..c83f9c5 100644 --- a/server/source/system/menu.go +++ b/server/source/system/menu.go @@ -58,7 +58,6 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "admin", Name: "superAdmin", Component: "view/superAdmin/index.vue", Sort: 3, Meta: Meta{Title: "超级管理员", Icon: "user"}}, {MenuLevel: 0, Hidden: true, ParentId: 0, Path: "person", Name: "person", Component: "view/person/person.vue", Sort: 4, Meta: Meta{Title: "个人信息", Icon: "message"}}, {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "common", Name: "common", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "公共能力", Icon: "folder-opened"}}, - {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "example", Name: "example", Component: "view/example/index.vue", Sort: 7, Meta: Meta{Title: "示例文件", Icon: "management"}}, {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "systemTools", Name: "systemTools", Component: "view/systemTools/index.vue", Sort: 5, Meta: Meta{Title: "编程辅助", Icon: "tools"}}, {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}}, {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "state", Name: "state", Component: "view/system/state.vue", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "cloudy"}}, @@ -93,23 +92,13 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er // common子菜单 {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "upload", Name: "upload", Component: "view/example/upload/upload.vue", Sort: 1, Meta: Meta{Title: "媒体库(上传下载)", Icon: "upload"}}, - - // example子菜单 - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["example"], Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 6, Meta: Meta{Title: "断点续传", Icon: "upload-filled"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["example"], Path: "customer", Name: "customer", Component: "view/example/customer/customer.vue", Sort: 7, Meta: Meta{Title: "客户列表(资源示例)", Icon: "avatar"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 2, Meta: Meta{Title: "断点续传", Icon: "upload-filled"}}, // systemTools子菜单 - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "autoPkg", Name: "autoPkg", Component: "view/systemTools/autoPkg/autoPkg.vue", Sort: 0, Meta: Meta{Title: "模板配置", Icon: "folder"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "autoCode", Name: "autoCode", Component: "view/systemTools/autoCode/index.vue", Sort: 1, Meta: Meta{Title: "代码生成器", Icon: "cpu", KeepAlive: true}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "autoCodeAdmin", Name: "autoCodeAdmin", Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 2, Meta: Meta{Title: "自动化代码管理", Icon: "magic-stick"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 3, Meta: Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "aiWorkflow", Name: "aiWorkflow", Component: "view/systemTools/aiWrokflow/index.vue", Sort: 4, Meta: Meta{Title: "AI需求工作流", Icon: "magic-stick", KeepAlive: true}}, - {MenuLevel: 1, Hidden: true, ParentId: menuNameMap["systemTools"], Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: Meta{Title: "自动化代码-${id}", Icon: "magic-stick"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 5, Meta: Meta{Title: "导出模板", Icon: "reading"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTest", Name: "mcpTest", Component: "view/systemTools/autoCode/mcpTest.vue", Sort: 6, Meta: Meta{Title: "Mcp Tools管理", Icon: "partly-cloudy"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTool", Name: "mcpTool", Component: "view/systemTools/autoCode/mcp.vue", Sort: 7, Meta: Meta{Title: "Mcp Tools模板", Icon: "magnet"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "skills", Name: "skills", Component: "view/systemTools/skills/index.vue", Sort: 8, Meta: Meta{Title: "Skills管理", Icon: "document"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "picture", Name: "picture", Component: "view/systemTools/autoCode/picture.vue", Sort: 9, Meta: Meta{Title: "AI页面绘制", Icon: "picture-filled"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 1, Meta: Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 2, Meta: Meta{Title: "导出模板", Icon: "reading"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTest", Name: "mcpTest", Component: "view/systemTools/mcpTest/index.vue", Sort: 3, Meta: Meta{Title: "MCP Tools管理", Icon: "partly-cloudy"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTool", Name: "mcpTool", Component: "view/systemTools/mcpTool/index.vue", Sort: 4, Meta: Meta{Title: "MCP Tools模板", Icon: "magnet"}}, } // 创建子菜单 @@ -128,7 +117,7 @@ func (i *initMenu) DataInserted(ctx context.Context) bool { if !ok { return false } - if errors.Is(db.Where("path = ?", "autoPkg").First(&SysBaseMenu{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 + if errors.Is(db.Where("path = ?", "upload").First(&SysBaseMenu{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 return false } return true diff --git a/server/utils/ast/ast_rollback.go b/server/utils/ast/ast_rollback.go index daa8422..dfaf99d 100644 --- a/server/utils/ast/ast_rollback.go +++ b/server/utils/ast/ast_rollback.go @@ -3,7 +3,6 @@ package ast import ( "bytes" "fmt" - "github.com/flipped-aurora/gin-vue-admin/server/global" "go/ast" "go/parser" "go/printer" @@ -22,7 +21,7 @@ func RollGormBack(pk, model string) { // 首先分析存在多少个ttt作为调用方的node块 // 如果多个 仅仅删除对应块即可 // 如果单个 那么还需要剔除import - path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go") + path := filepath.Join(resolveServerRoot(), "initialize", "gorm_biz.go") src, err := os.ReadFile(path) if err != nil { fmt.Println(err) @@ -99,7 +98,7 @@ func RollRouterBack(pk, model string) { // 首先抓到所有的代码块结构 {} // 分析结构中是否存在一个变量叫做 pk+Router // 然后获取到代码块指针 对内部需要回滚的代码进行剔除 - path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go") + path := filepath.Join(resolveServerRoot(), "initialize", "router_biz.go") src, err := os.ReadFile(path) if err != nil { fmt.Println(err) diff --git a/server/utils/ast/ast_type.go b/server/utils/ast/ast_type.go index 43285c9..8e46048 100644 --- a/server/utils/ast/ast_type.go +++ b/server/utils/ast/ast_type.go @@ -20,12 +20,6 @@ func (r Type) Group() string { return "RouterGroup" case TypePackageServiceModuleEnter: return "ServiceGroup" - case TypePluginApiEnter: - return "api" - case TypePluginRouterEnter: - return "router" - case TypePluginServiceEnter: - return "service" default: return "" } @@ -40,14 +34,4 @@ const ( TypePackageServiceModuleEnter = "PackageServiceModuleEnter" // server/service/{package}/enter.go TypePackageInitializeGorm = "PackageInitializeGorm" // server/initialize/gorm_biz.go TypePackageInitializeRouter = "PackageInitializeRouter" // server/initialize/router_biz.go - TypePluginGen = "PluginGen" // server/plugin/{package}/gen/main.go - TypePluginApiEnter = "PluginApiEnter" // server/plugin/{package}/enter.go - TypePluginInitializeV1 = "PluginInitializeV1" // server/initialize/plugin_biz_v1.go - TypePluginInitializeV2 = "PluginInitializeV2" // server/plugin/register.go - TypePluginRouterEnter = "PluginRouterEnter" // server/plugin/{package}/enter.go - TypePluginServiceEnter = "PluginServiceEnter" // server/plugin/{package}/enter.go - TypePluginInitializeApi = "PluginInitializeApi" // server/plugin/{package}/initialize/api.go - TypePluginInitializeGorm = "PluginInitializeGorm" // server/plugin/{package}/initialize/gorm.go - TypePluginInitializeMenu = "PluginInitializeMenu" // server/plugin/{package}/initialize/menu.go - TypePluginInitializeRouter = "PluginInitializeRouter" // server/plugin/{package}/initialize/router.go ) diff --git a/server/utils/ast/interfaces_base.go b/server/utils/ast/interfaces_base.go index 9c80874..7f6fcc6 100644 --- a/server/utils/ast/interfaces_base.go +++ b/server/utils/ast/interfaces_base.go @@ -1,7 +1,6 @@ package ast import ( - "github.com/flipped-aurora/gin-vue-admin/server/global" "github.com/pkg/errors" "go/ast" "go/format" @@ -61,7 +60,7 @@ func (a *Base) Format(filename string, writer io.Writer, file *ast.File) error { // RelativePath 绝对路径转相对路径 func (a *Base) RelativePath(filePath string) string { - server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server) + server := resolveServerRoot() hasServer := strings.Index(filePath, server) if hasServer != -1 { filePath = strings.TrimPrefix(filePath, server) @@ -73,9 +72,30 @@ func (a *Base) RelativePath(filePath string) string { // AbsolutePath 相对路径转绝对路径 func (a *Base) AbsolutePath(filePath string) string { - server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server) + server := resolveServerRoot() keys := strings.Split(filePath, "/") filePath = filepath.Join(keys...) filePath = filepath.Join(server, filePath) return filePath } + +func resolveProjectRoot() string { + if cwd, err := os.Getwd(); err == nil { + if filepath.Base(cwd) == "server" { + return filepath.Dir(cwd) + } + if _, err := os.Stat(filepath.Join(cwd, "server")); err == nil { + return cwd + } + return cwd + } + return "." +} + +func resolveServerRoot() string { + root := resolveProjectRoot() + if filepath.Base(root) == "server" { + return root + } + return filepath.Join(root, "server") +} diff --git a/server/utils/ast/plugin_enter.go b/server/utils/ast/plugin_enter.go deleted file mode 100644 index df5bba4..0000000 --- a/server/utils/ast/plugin_enter.go +++ /dev/null @@ -1,167 +0,0 @@ -package ast - -import ( - "go/ast" - "go/token" - "io" -) - -// PluginEnter 插件化入口 -// ModuleName := PackageName.GroupName.ServiceName -type PluginEnter struct { - Base - Type Type // 类型 - Path string // 文件路径 - ImportPath string // 导包路径 - RelativePath string // 相对路径 - StructName string // 结构体名称 - StructCamelName string // 结构体小驼峰名称 - ModuleName string // 模块名称 - GroupName string // 分组名称 - PackageName string // 包名 - ServiceName string // 服务名称 -} - -func (a *PluginEnter) Parse(filename string, writer io.Writer) (file *ast.File, err error) { - if filename == "" { - if a.RelativePath == "" { - filename = a.Path - a.RelativePath = a.Base.RelativePath(a.Path) - return a.Base.Parse(filename, writer) - } - a.Path = a.Base.AbsolutePath(a.RelativePath) - filename = a.Path - } - return a.Base.Parse(filename, writer) -} - -func (a *PluginEnter) Rollback(file *ast.File) error { - //回滚结构体内内容 - var structType *ast.StructType - ast.Inspect(file, func(n ast.Node) bool { - switch x := n.(type) { - case *ast.TypeSpec: - if s, ok := x.Type.(*ast.StructType); ok { - structType = s - for i, field := range x.Type.(*ast.StructType).Fields.List { - if len(field.Names) > 0 && field.Names[0].Name == a.StructName { - s.Fields.List = append(s.Fields.List[:i], s.Fields.List[i+1:]...) - return false - } - } - } - } - return true - }) - - if len(structType.Fields.List) == 0 { - _ = NewImport(a.ImportPath).Rollback(file) - } - - if a.Type == TypePluginServiceEnter { - return nil - } - - //回滚变量内容 - ast.Inspect(file, func(n ast.Node) bool { - genDecl, ok := n.(*ast.GenDecl) - if ok && genDecl.Tok == token.VAR { - for i, spec := range genDecl.Specs { - valueSpec, vsok := spec.(*ast.ValueSpec) - if vsok { - for _, name := range valueSpec.Names { - if name.Name == a.ModuleName { - genDecl.Specs = append(genDecl.Specs[:i], genDecl.Specs[i+1:]...) - return false - } - } - } - } - } - return true - }) - - return nil -} - -func (a *PluginEnter) Injection(file *ast.File) error { - _ = NewImport(a.ImportPath).Injection(file) - - has := false - hasVar := false - var firstStruct *ast.StructType - var varSpec *ast.GenDecl - //寻找是否存在结构且定位 - ast.Inspect(file, func(n ast.Node) bool { - switch x := n.(type) { - case *ast.TypeSpec: - if s, ok := x.Type.(*ast.StructType); ok { - firstStruct = s - for _, field := range x.Type.(*ast.StructType).Fields.List { - if len(field.Names) > 0 && field.Names[0].Name == a.StructName { - has = true - return false - } - } - } - } - return true - }) - - if !has { - field := &ast.Field{ - Names: []*ast.Ident{{Name: a.StructName}}, - Type: &ast.Ident{Name: a.StructCamelName}, - } - firstStruct.Fields.List = append(firstStruct.Fields.List, field) - } - - if a.Type == TypePluginServiceEnter { - return nil - } - - //寻找是否存在变量且定位 - ast.Inspect(file, func(n ast.Node) bool { - genDecl, ok := n.(*ast.GenDecl) - if ok && genDecl.Tok == token.VAR { - for _, spec := range genDecl.Specs { - valueSpec, vsok := spec.(*ast.ValueSpec) - if vsok { - varSpec = genDecl - for _, name := range valueSpec.Names { - if name.Name == a.ModuleName { - hasVar = true - return false - } - } - } - } - } - return true - }) - - if !hasVar { - spec := &ast.ValueSpec{ - Names: []*ast.Ident{{Name: a.ModuleName}}, - Values: []ast.Expr{ - &ast.SelectorExpr{ - X: &ast.SelectorExpr{ - X: &ast.Ident{Name: a.PackageName}, - Sel: &ast.Ident{Name: a.GroupName}, - }, - Sel: &ast.Ident{Name: a.ServiceName}, - }, - }, - } - varSpec.Specs = append(varSpec.Specs, spec) - } - - return nil -} - -func (a *PluginEnter) Format(filename string, writer io.Writer, file *ast.File) error { - if filename == "" { - filename = a.Path - } - return a.Base.Format(filename, writer, file) -} diff --git a/server/utils/ast/plugin_gen.go b/server/utils/ast/plugin_gen.go deleted file mode 100644 index ed7d04f..0000000 --- a/server/utils/ast/plugin_gen.go +++ /dev/null @@ -1,189 +0,0 @@ -package ast - -import ( - "go/ast" - "go/token" - "io" -) - -type PluginGen struct { - Base - Type Type // 类型 - Path string // 文件路径 - ImportPath string // 导包路径 - RelativePath string // 相对路径 - StructName string // 结构体名称 - PackageName string // 包名 - IsNew bool // 是否使用new关键字 -} - -func (a *PluginGen) Parse(filename string, writer io.Writer) (file *ast.File, err error) { - if filename == "" { - if a.RelativePath == "" { - filename = a.Path - a.RelativePath = a.Base.RelativePath(a.Path) - return a.Base.Parse(filename, writer) - } - a.Path = a.Base.AbsolutePath(a.RelativePath) - filename = a.Path - } - return a.Base.Parse(filename, writer) -} -func (a *PluginGen) Rollback(file *ast.File) error { - for i := 0; i < len(file.Decls); i++ { - v1, o1 := file.Decls[i].(*ast.FuncDecl) - if o1 { - for j := 0; j < len(v1.Body.List); j++ { - v2, o2 := v1.Body.List[j].(*ast.ExprStmt) - if o2 { - v3, o3 := v2.X.(*ast.CallExpr) - if o3 { - v4, o4 := v3.Fun.(*ast.SelectorExpr) - if o4 { - if v4.Sel.Name != "ApplyBasic" { - continue - } - for k := 0; k < len(v3.Args); k++ { - v5, o5 := v3.Args[k].(*ast.CallExpr) - if o5 { - v6, o6 := v5.Fun.(*ast.Ident) - if o6 { - if v6.Name != "new" { - continue - } - for l := 0; l < len(v5.Args); l++ { - v7, o7 := v5.Args[l].(*ast.SelectorExpr) - if o7 { - v8, o8 := v7.X.(*ast.Ident) - if o8 { - if v8.Name == a.PackageName && v7.Sel.Name == a.StructName { - v3.Args = append(v3.Args[:k], v3.Args[k+1:]...) - continue - } - } - } - } - } - } - if k >= len(v3.Args) { - break - } - v6, o6 := v3.Args[k].(*ast.CompositeLit) - if o6 { - v7, o7 := v6.Type.(*ast.SelectorExpr) - if o7 { - v8, o8 := v7.X.(*ast.Ident) - if o8 { - if v8.Name == a.PackageName && v7.Sel.Name == a.StructName { - v3.Args = append(v3.Args[:k], v3.Args[k+1:]...) - continue - } - } - } - } - } - if len(v3.Args) == 0 { - _ = NewImport(a.ImportPath).Rollback(file) - } - } - } - } - } - } - } - return nil -} - -func (a *PluginGen) Injection(file *ast.File) error { - _ = NewImport(a.ImportPath).Injection(file) - for i := 0; i < len(file.Decls); i++ { - v1, o1 := file.Decls[i].(*ast.FuncDecl) - if o1 { - for j := 0; j < len(v1.Body.List); j++ { - v2, o2 := v1.Body.List[j].(*ast.ExprStmt) - if o2 { - v3, o3 := v2.X.(*ast.CallExpr) - if o3 { - v4, o4 := v3.Fun.(*ast.SelectorExpr) - if o4 { - if v4.Sel.Name != "ApplyBasic" { - continue - } - var has bool - for k := 0; k < len(v3.Args); k++ { - v5, o5 := v3.Args[k].(*ast.CallExpr) - if o5 { - v6, o6 := v5.Fun.(*ast.Ident) - if o6 { - if v6.Name != "new" { - continue - } - for l := 0; l < len(v5.Args); l++ { - v7, o7 := v5.Args[l].(*ast.SelectorExpr) - if o7 { - v8, o8 := v7.X.(*ast.Ident) - if o8 { - if v8.Name == a.PackageName && v7.Sel.Name == a.StructName { - has = true - break - } - } - } - } - } - } - v6, o6 := v3.Args[k].(*ast.CompositeLit) - if o6 { - v7, o7 := v6.Type.(*ast.SelectorExpr) - if o7 { - v8, o8 := v7.X.(*ast.Ident) - if o8 { - if v8.Name == a.PackageName && v7.Sel.Name == a.StructName { - has = true - break - } - } - } - } - } - if !has { - if a.IsNew { - arg := &ast.CallExpr{ - Fun: &ast.Ident{Name: "\n\t\tnew"}, - Args: []ast.Expr{ - &ast.SelectorExpr{ - X: &ast.Ident{Name: a.PackageName}, - Sel: &ast.Ident{Name: a.StructName}, - }, - }, - } - v3.Args = append(v3.Args, arg) - v3.Args = append(v3.Args, &ast.BasicLit{ - Kind: token.STRING, - Value: "\n", - }) - break - } - arg := &ast.CompositeLit{ - Type: &ast.SelectorExpr{ - X: &ast.Ident{Name: a.PackageName}, - Sel: &ast.Ident{Name: a.StructName}, - }, - } - v3.Args = append(v3.Args, arg) - } - } - } - } - } - } - } - return nil -} - -func (a *PluginGen) Format(filename string, writer io.Writer, file *ast.File) error { - if filename == "" { - filename = a.Path - } - return a.Base.Format(filename, writer, file) -} diff --git a/server/utils/ast/plugin_initialize_gorm.go b/server/utils/ast/plugin_initialize_gorm.go deleted file mode 100644 index e342251..0000000 --- a/server/utils/ast/plugin_initialize_gorm.go +++ /dev/null @@ -1,111 +0,0 @@ -package ast - -import ( - "go/ast" - "io" -) - -type PluginInitializeGorm struct { - Base - Type Type // 类型 - Path string // 文件路径 - ImportPath string // 导包路径 - RelativePath string // 相对路径 - StructName string // 结构体名称 - PackageName string // 包名 - IsNew bool // 是否使用new关键字 true: new(PackageName.StructName) false: &PackageName.StructName{} -} - -func (a *PluginInitializeGorm) Parse(filename string, writer io.Writer) (file *ast.File, err error) { - if filename == "" { - if a.RelativePath == "" { - filename = a.Path - a.RelativePath = a.Base.RelativePath(a.Path) - return a.Base.Parse(filename, writer) - } - a.Path = a.Base.AbsolutePath(a.RelativePath) - filename = a.Path - } - return a.Base.Parse(filename, writer) -} - -func (a *PluginInitializeGorm) Rollback(file *ast.File) error { - var needRollBackImport bool - ast.Inspect(file, func(n ast.Node) bool { - callExpr, ok := n.(*ast.CallExpr) - if !ok { - return true - } - - selExpr, seok := callExpr.Fun.(*ast.SelectorExpr) - if !seok || selExpr.Sel.Name != "AutoMigrate" { - return true - } - if len(callExpr.Args) <= 1 { - needRollBackImport = true - } - // 删除指定的参数 - for i, arg := range callExpr.Args { - compLit, cok := arg.(*ast.CompositeLit) - if !cok { - continue - } - - cselExpr, sok := compLit.Type.(*ast.SelectorExpr) - if !sok { - continue - } - - ident, idok := cselExpr.X.(*ast.Ident) - if idok && ident.Name == a.PackageName && cselExpr.Sel.Name == a.StructName { - // 删除参数 - callExpr.Args = append(callExpr.Args[:i], callExpr.Args[i+1:]...) - break - } - } - - return true - }) - - if needRollBackImport { - _ = NewImport(a.ImportPath).Rollback(file) - } - - return nil -} - -func (a *PluginInitializeGorm) Injection(file *ast.File) error { - _ = NewImport(a.ImportPath).Injection(file) - var call *ast.CallExpr - ast.Inspect(file, func(n ast.Node) bool { - callExpr, ok := n.(*ast.CallExpr) - if !ok { - return true - } - - selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) - if ok && selExpr.Sel.Name == "AutoMigrate" { - call = callExpr - return false - } - - return true - }) - - arg := &ast.CompositeLit{ - Type: &ast.SelectorExpr{ - X: &ast.Ident{Name: a.PackageName}, - Sel: &ast.Ident{Name: a.StructName}, - }, - } - - call.Args = append(call.Args, arg) - return nil -} - -func (a *PluginInitializeGorm) Format(filename string, writer io.Writer, file *ast.File) error { - if filename == "" { - filename = a.Path - } - return a.Base.Format(filename, writer, file) -} diff --git a/server/utils/ast/plugin_initialize_router.go b/server/utils/ast/plugin_initialize_router.go deleted file mode 100644 index 6550789..0000000 --- a/server/utils/ast/plugin_initialize_router.go +++ /dev/null @@ -1,124 +0,0 @@ -package ast - -import ( - "fmt" - "go/ast" - "io" -) - -// PluginInitializeRouter 插件初始化路由 -// PackageName.AppName.GroupName.FunctionName() -type PluginInitializeRouter struct { - Base - Type Type // 类型 - Path string // 文件路径 - ImportPath string // 导包路径 - ImportGlobalPath string // 导包全局变量路径 - ImportMiddlewarePath string // 导包中间件路径 - RelativePath string // 相对路径 - AppName string // 应用名称 - GroupName string // 分组名称 - PackageName string // 包名 - FunctionName string // 函数名 - LeftRouterGroupName string // 左路由分组名称 - RightRouterGroupName string // 右路由分组名称 -} - -func (a *PluginInitializeRouter) Parse(filename string, writer io.Writer) (file *ast.File, err error) { - if filename == "" { - if a.RelativePath == "" { - filename = a.Path - a.RelativePath = a.Base.RelativePath(a.Path) - return a.Base.Parse(filename, writer) - } - a.Path = a.Base.AbsolutePath(a.RelativePath) - filename = a.Path - } - return a.Base.Parse(filename, writer) -} - -func (a *PluginInitializeRouter) Rollback(file *ast.File) error { - funcDecl := FindFunction(file, "Router") - delI := 0 - routerNum := 0 - for i := len(funcDecl.Body.List) - 1; i >= 0; i-- { - stmt, ok := funcDecl.Body.List[i].(*ast.ExprStmt) - if !ok { - continue - } - - callExpr, ok := stmt.X.(*ast.CallExpr) - if !ok { - continue - } - - selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) - if !ok { - continue - } - - ident, ok := selExpr.X.(*ast.SelectorExpr) - - if ok { - if iExpr, ieok := ident.X.(*ast.SelectorExpr); ieok { - if iden, idok := iExpr.X.(*ast.Ident); idok { - if iden.Name == "router" { - routerNum++ - } - } - } - if ident.Sel.Name == a.GroupName && selExpr.Sel.Name == a.FunctionName { - // 删除语句 - delI = i - } - } - } - - funcDecl.Body.List = append(funcDecl.Body.List[:delI], funcDecl.Body.List[delI+1:]...) - - if routerNum <= 1 { - _ = NewImport(a.ImportPath).Rollback(file) - } - - return nil -} - -func (a *PluginInitializeRouter) Injection(file *ast.File) error { - _ = NewImport(a.ImportPath).Injection(file) - funcDecl := FindFunction(file, "Router") - - var exists bool - - ast.Inspect(funcDecl, func(n ast.Node) bool { - callExpr, ok := n.(*ast.CallExpr) - if !ok { - return true - } - - selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) - if !ok { - return true - } - - ident, ok := selExpr.X.(*ast.SelectorExpr) - if ok && ident.Sel.Name == a.GroupName && selExpr.Sel.Name == a.FunctionName { - exists = true - return false - } - return true - }) - - if !exists { - stmtStr := fmt.Sprintf("%s.%s.%s.%s(%s, %s)", a.PackageName, a.AppName, a.GroupName, a.FunctionName, a.LeftRouterGroupName, a.RightRouterGroupName) - stmt := CreateStmt(stmtStr) - funcDecl.Body.List = append(funcDecl.Body.List, stmt) - } - return nil -} - -func (a *PluginInitializeRouter) Format(filename string, writer io.Writer, file *ast.File) error { - if filename == "" { - filename = a.Path - } - return a.Base.Format(filename, writer, file) -} diff --git a/server/utils/ast/plugin_initialize_v2.go b/server/utils/ast/plugin_initialize_v2.go deleted file mode 100644 index 974f513..0000000 --- a/server/utils/ast/plugin_initialize_v2.go +++ /dev/null @@ -1,82 +0,0 @@ -package ast - -import ( - "go/ast" - "go/token" - "io" - "strconv" - "strings" -) - -type PluginInitializeV2 struct { - Base - Type Type // 类型 - Path string // 文件路径 - PluginPath string // 插件路径 - RelativePath string // 相对路径 - ImportPath string // 导包路径 - StructName string // 结构体名称 - PackageName string // 包名 -} - -func (a *PluginInitializeV2) Parse(filename string, writer io.Writer) (file *ast.File, err error) { - if filename == "" { - if a.RelativePath == "" { - filename = a.PluginPath - a.RelativePath = a.Base.RelativePath(a.PluginPath) - return a.Base.Parse(filename, writer) - } - a.PluginPath = a.Base.AbsolutePath(a.RelativePath) - filename = a.PluginPath - } - return a.Base.Parse(filename, writer) -} - -func (a *PluginInitializeV2) Injection(file *ast.File) error { - importPath := strings.TrimSpace(a.ImportPath) - if importPath == "" { - return nil - } - importPath = strings.Trim(importPath, "\"") - if importPath == "" || CheckImport(file, importPath) { - return nil - } - - importSpec := &ast.ImportSpec{ - Name: ast.NewIdent("_"), - Path: &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(importPath)}, - } - var importDecl *ast.GenDecl - for _, decl := range file.Decls { - genDecl, ok := decl.(*ast.GenDecl) - if !ok { - continue - } - if genDecl.Tok == token.IMPORT { - importDecl = genDecl - break - } - } - if importDecl == nil { - file.Decls = append([]ast.Decl{ - &ast.GenDecl{ - Tok: token.IMPORT, - Specs: []ast.Spec{importSpec}, - }, - }, file.Decls...) - return nil - } - importDecl.Specs = append(importDecl.Specs, importSpec) - return nil -} - -func (a *PluginInitializeV2) Rollback(file *ast.File) error { - return nil -} - -func (a *PluginInitializeV2) Format(filename string, writer io.Writer, file *ast.File) error { - if filename == "" { - filename = a.PluginPath - } - return a.Base.Format(filename, writer, file) -} diff --git a/server/utils/autocode/template_funcs.go b/server/utils/autocode/template_funcs.go deleted file mode 100644 index 2845aea..0000000 --- a/server/utils/autocode/template_funcs.go +++ /dev/null @@ -1,713 +0,0 @@ -package autocode - -import ( - "fmt" - systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" - "slices" - "strings" - "text/template" -) - -// GetTemplateFuncMap 返回模板函数映射,用于在模板中使用 -func GetTemplateFuncMap() template.FuncMap { - return template.FuncMap{ - "title": strings.Title, - "GenerateField": GenerateField, - "GenerateSearchField": GenerateSearchField, - "GenerateSearchConditions": GenerateSearchConditions, - "GenerateSearchFormItem": GenerateSearchFormItem, - "GenerateTableColumn": GenerateTableColumn, - "GenerateFormItem": GenerateFormItem, - "GenerateDescriptionItem": GenerateDescriptionItem, - "GenerateDefaultFormValue": GenerateDefaultFormValue, - } -} - -// 渲染Model中的字段 -func GenerateField(field systemReq.AutoCodeField) string { - // 构建gorm标签 - gormTag := `` - - if field.FieldIndexType != "" { - gormTag += field.FieldIndexType + ";" - } - - if field.PrimaryKey { - gormTag += "primarykey;" - } - - if field.DefaultValue != "" { - gormTag += fmt.Sprintf("default:%s;", field.DefaultValue) - } - - if field.Comment != "" { - gormTag += fmt.Sprintf("comment:%s;", field.Comment) - } - - gormTag += "column:" + field.ColumnName + ";" - - // 对于int类型,根据DataTypeLong决定具体的Go类型,不使用size标签 - if field.DataTypeLong != "" && field.FieldType != "enum" && field.FieldType != "int" { - gormTag += fmt.Sprintf("size:%s;", field.DataTypeLong) - } - - requireTag := ` binding:"required"` + "`" - - // 根据字段类型构建不同的字段定义 - var result string - switch field.FieldType { - case "enum": - result = fmt.Sprintf(`%s string `+"`"+`json:"%s" form:"%s" gorm:"%stype:enum(%s);"`+"`", - field.FieldName, field.FieldJson, field.FieldJson, gormTag, field.DataTypeLong) - case "picture", "video": - tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s"`, - field.FieldJson, field.FieldJson, gormTag) - - result = fmt.Sprintf(`%s string `+"`"+`%s`+"`"+``, field.FieldName, tagContent) - case "file", "pictures", "array": - tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s"`, - field.FieldJson, field.FieldJson, gormTag) - - result = fmt.Sprintf(`%s datatypes.JSON `+"`"+`%s swaggertype:"array,object"`+"`"+``, - field.FieldName, tagContent) - case "richtext": - tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s`, - field.FieldJson, field.FieldJson, gormTag) - - result = fmt.Sprintf(`%s *string `+"`"+`%stype:text;"`+"`"+``, - field.FieldName, tagContent) - case "json": - tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s"`, - field.FieldJson, field.FieldJson, gormTag) - - result = fmt.Sprintf(`%s datatypes.JSON `+"`"+`%s swaggertype:"object"`+"`"+``, - field.FieldName, tagContent) - default: - tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s"`, - field.FieldJson, field.FieldJson, gormTag) - - // 对于int类型,根据DataTypeLong决定具体的Go类型 - var fieldType string - if field.FieldType == "int" { - switch field.DataTypeLong { - case "1", "2", "3": - fieldType = "int8" - case "4", "5": - fieldType = "int16" - case "6", "7", "8", "9", "10": - fieldType = "int32" - case "11", "12", "13", "14", "15", "16", "17", "18", "19", "20": - fieldType = "int64" - default: - fieldType = "int64" - } - } else { - fieldType = field.FieldType - } - - result = fmt.Sprintf(`%s *%s `+"`"+`%s`+"`"+``, - field.FieldName, fieldType, tagContent) - } - - if field.Require { - result = result[0:len(result)-1] + requireTag - } - - // 添加字段描述 - if field.FieldDesc != "" { - result += fmt.Sprintf(" //%s", field.FieldDesc) - } - - return result -} - -// 格式化搜索条件语句 -func GenerateSearchConditions(fields []*systemReq.AutoCodeField) string { - var conditions []string - - for _, field := range fields { - if field.FieldSearchType == "" { - continue - } - - var condition string - - if slices.Contains([]string{"enum", "pictures", "picture", "video", "json", "richtext", "array"}, field.FieldType) { - if field.FieldType == "enum" { - if field.FieldSearchType == "LIKE" { - condition = fmt.Sprintf(` - if info.%s != "" { - db = db.Where("%s LIKE ?", "%%"+ info.%s+"%%") - }`, - field.FieldName, field.ColumnName, field.FieldName) - } else { - condition = fmt.Sprintf(` - if info.%s != "" { - db = db.Where("%s %s ?", info.%s) - }`, - field.FieldName, field.ColumnName, field.FieldSearchType, field.FieldName) - } - } else { - condition = fmt.Sprintf(` - if info.%s != "" { - // TODO 数据类型为复杂类型,请根据业务需求自行实现复杂类型的查询业务 - }`, field.FieldName) - } - - } else if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" { - if field.FieldType == "time.Time" { - condition = fmt.Sprintf(` - if len(info.%sRange) == 2 { - db = db.Where("%s %s ? AND ? ", info.%sRange[0], info.%sRange[1]) - }`, - field.FieldName, field.ColumnName, field.FieldSearchType, field.FieldName, field.FieldName) - } else { - condition = fmt.Sprintf(` - if info.Start%s != nil && info.End%s != nil { - db = db.Where("%s %s ? AND ? ", *info.Start%s, *info.End%s) - }`, - field.FieldName, field.FieldName, field.ColumnName, - field.FieldSearchType, field.FieldName, field.FieldName) - } - } else { - nullCheck := "info." + field.FieldName + " != nil" - if field.FieldType == "string" { - condition = fmt.Sprintf(` - if %s && *info.%s != "" {`, nullCheck, field.FieldName) - } else { - condition = fmt.Sprintf(` - if %s {`, nullCheck) - } - - if field.FieldSearchType == "LIKE" { - condition += fmt.Sprintf(` - db = db.Where("%s LIKE ?", "%%"+ *info.%s+"%%") - }`, - field.ColumnName, field.FieldName) - } else { - condition += fmt.Sprintf(` - db = db.Where("%s %s ?", *info.%s) - }`, - field.ColumnName, field.FieldSearchType, field.FieldName) - } - } - - conditions = append(conditions, condition) - } - - return strings.Join(conditions, "") -} - -// 格式化前端搜索条件 -func GenerateSearchFormItem(field systemReq.AutoCodeField) string { - // 开始构建表单项 - result := fmt.Sprintf(` -`, field.FieldDesc, field.FieldJson) - - // 根据字段属性生成不同的输入类型 - if field.FieldType == "bool" { - result += fmt.Sprintf(` -`, field.FieldJson) - result += ` -` - result += ` -` - result += ` -` - } else if field.DictType != "" { - multipleAttr := "" - if field.FieldType == "array" { - multipleAttr = "multiple " - } - result += fmt.Sprintf(` -`, - field.FieldJson, field.FieldDesc, field.DictType, field.Clearable, multipleAttr) - } else if field.CheckDataSource { - multipleAttr := "" - if field.DataSource.Association == 2 { - multipleAttr = "multiple " - } - result += fmt.Sprintf(` -`, - multipleAttr, field.FieldJson, field.FieldDesc, field.Clearable) - result += fmt.Sprintf(` -`, - field.FieldJson) - result += ` -` - } else if field.FieldType == "float64" || field.FieldType == "int" { - if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" { - result += fmt.Sprintf(` -`, field.FieldName) - result += ` — -` - result += fmt.Sprintf(` -`, field.FieldName) - } else { - result += fmt.Sprintf(` -`, field.FieldJson) - } - } else if field.FieldType == "time.Time" { - if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" { - result += ` -` - result += fmt.Sprintf(``, field.FieldJson) - } else { - result += fmt.Sprintf(``, field.FieldJson) - } - } else { - result += fmt.Sprintf(` -`, field.FieldJson) - } - - // 关闭表单项 - result += `` - - return result -} - -// GenerateTableColumn generates HTML for table column based on field properties -func GenerateTableColumn(field systemReq.AutoCodeField) string { - // Add sortable attribute if needed - sortAttr := "" - if field.Sort { - sortAttr = " sortable" - } - - // Handle different field types - if field.CheckDataSource { - result := fmt.Sprintf(` -`, - sortAttr, field.FieldDesc, field.FieldJson) - result += ` -` - result += `` - return result - } else if field.DictType != "" { - result := fmt.Sprintf(` -`, - sortAttr, field.FieldDesc, field.FieldJson) - result += ` -` - result += `` - return result - } else if field.FieldType == "bool" { - result := fmt.Sprintf(` -`, - sortAttr, field.FieldDesc, field.FieldJson) - result += fmt.Sprintf(` -`, field.FieldJson) - result += `` - return result - } else if field.FieldType == "time.Time" { - result := fmt.Sprintf(` -`, - sortAttr, field.FieldDesc, field.FieldJson) - result += fmt.Sprintf(` -`, field.FieldJson) - result += `` - return result - } else if field.FieldType == "picture" { - result := fmt.Sprintf(` -`, field.FieldDesc, field.FieldJson) - result += ` -` - result += `` - return result - } else if field.FieldType == "pictures" { - result := fmt.Sprintf(` -`, field.FieldDesc, field.FieldJson) - result += ` -` - result += `` - return result - } else if field.FieldType == "video" { - result := fmt.Sprintf(` -`, field.FieldDesc, field.FieldJson) - result += ` -` - result += `` - return result - } else if field.FieldType == "richtext" { - result := fmt.Sprintf(` -`, field.FieldDesc, field.FieldJson) - result += ` -` - result += `` - return result - } else if field.FieldType == "file" { - result := fmt.Sprintf(` -`, field.FieldDesc, field.FieldJson) - result += ` -` - result += `` - return result - } else if field.FieldType == "json" { - result := fmt.Sprintf(` -`, field.FieldDesc, field.FieldJson) - result += ` -` - result += `` - return result - } else if field.FieldType == "array" { - result := fmt.Sprintf(` -`, field.FieldDesc, field.FieldJson) - result += ` -` - result += `` - return result - } else { - return fmt.Sprintf(` -`, - sortAttr, field.FieldDesc, field.FieldJson) - } -} - -func GenerateFormItem(field systemReq.AutoCodeField) string { - // 开始构建表单项 - result := fmt.Sprintf(` -`, field.FieldDesc, field.FieldJson) - - // 处理不同字段类型 - if field.CheckDataSource { - multipleAttr := "" - if field.DataSource.Association == 2 { - multipleAttr = " multiple" - } - result += fmt.Sprintf(` -`, - multipleAttr, field.FieldJson, field.FieldDesc, field.Clearable) - result += fmt.Sprintf(` -`, - field.FieldJson) - result += ` -` - } else { - switch field.FieldType { - case "bool": - result += fmt.Sprintf(` -`, - field.FieldJson) - - case "string": - if field.DictType != "" { - result += fmt.Sprintf(` -`, - field.FieldJson, field.FieldDesc, field.DictType, field.Clearable) - } else { - result += fmt.Sprintf(` -`, - field.FieldJson, field.Clearable, field.FieldDesc) - } - - case "richtext": - result += fmt.Sprintf(` -`, field.FieldJson) - - case "json": - result += fmt.Sprintf(` // 此字段为json结构,可以前端自行控制展示和数据绑定模式 需绑定json的key为 formData.%s 后端会按照json的类型进行存取 -`, field.FieldJson) - result += fmt.Sprintf(` {{ formData.%s }} -`, field.FieldJson) - - case "array": - if field.DictType != "" { - result += fmt.Sprintf(` -`, - field.FieldJson, field.FieldDesc, field.Clearable) - result += fmt.Sprintf(` -`, - field.DictType) - result += ` -` - } else { - result += fmt.Sprintf(` -`, field.FieldJson) - } - - case "int": - result += fmt.Sprintf(` -`, - field.FieldJson, field.Clearable, field.FieldDesc) - - case "time.Time": - result += fmt.Sprintf(` -`, - field.FieldJson, field.Clearable) - - case "float64": - result += fmt.Sprintf(` -`, - field.FieldJson, field.Clearable) - - case "enum": - result += fmt.Sprintf(` -`, - field.FieldJson, field.FieldDesc, field.Clearable) - result += fmt.Sprintf(` -`, - field.DataTypeLong) - result += ` -` - - case "picture": - result += fmt.Sprintf(` -`, field.FieldJson) - - case "pictures": - result += fmt.Sprintf(` -`, field.FieldJson) - - case "video": - result += fmt.Sprintf(` -`, field.FieldJson) - - case "file": - result += fmt.Sprintf(` -`, field.FieldJson) - } - } - - // 关闭表单项 - result += `` - - return result -} - -func GenerateDescriptionItem(field systemReq.AutoCodeField) string { - // 开始构建描述项 - result := fmt.Sprintf(` -`, field.FieldDesc) - - if field.CheckDataSource { - result += ` -` - } else if field.FieldType != "picture" && field.FieldType != "pictures" && - field.FieldType != "file" && field.FieldType != "array" && - field.FieldType != "richtext" { - result += fmt.Sprintf(` {{ detailForm.%s }} -`, field.FieldJson) - } else { - switch field.FieldType { - case "picture": - result += fmt.Sprintf(` -`, - field.FieldJson, field.FieldJson) - case "array": - result += fmt.Sprintf(` -`, field.FieldJson) - case "pictures": - result += fmt.Sprintf(` -`, - field.FieldJson, field.FieldJson) - case "richtext": - result += fmt.Sprintf(` -`, field.FieldJson) - case "file": - result += fmt.Sprintf(`
-`, field.FieldJson) - result += ` -` - result += ` -` - result += ` {{ item.name }} -` - result += ` -` - result += `
-` - } - } - - // 关闭描述项 - result += `
` - - return result -} - -func GenerateDefaultFormValue(field systemReq.AutoCodeField) string { - // 根据字段类型确定默认值 - var defaultValue string - - switch field.FieldType { - case "bool": - defaultValue = "false" - case "string", "richtext": - defaultValue = "''" - case "int": - if field.DataSource != nil { // 检查数据源是否存在 - defaultValue = "undefined" - } else { - defaultValue = "0" - } - case "time.Time": - defaultValue = "new Date()" - case "float64": - defaultValue = "0" - case "picture", "video": - defaultValue = "\"\"" - case "pictures", "file", "array": - defaultValue = "[]" - case "json": - defaultValue = "{}" - default: - defaultValue = "null" - } - - // 返回格式化后的默认值字符串 - return fmt.Sprintf(`%s: %s,`, field.FieldJson, defaultValue) -} - -// GenerateSearchField 根据字段属性生成搜索结构体中的字段定义 -func GenerateSearchField(field systemReq.AutoCodeField) string { - var result string - - if field.FieldSearchType == "" { - return "" // 如果没有搜索类型,返回空字符串 - } - - if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" { - // 生成范围搜索字段 - // time 的情况 - if field.FieldType == "time.Time" { - result = fmt.Sprintf("%sRange []time.Time `json:\"%sRange\" form:\"%sRange[]\"`", - field.FieldName, field.FieldJson, field.FieldJson) - } else { - startField := fmt.Sprintf("Start%s *%s `json:\"start%s\" form:\"start%s\"`", - field.FieldName, field.FieldType, field.FieldName, field.FieldName) - endField := fmt.Sprintf("End%s *%s `json:\"end%s\" form:\"end%s\"`", - field.FieldName, field.FieldType, field.FieldName, field.FieldName) - result = startField + "\n" + endField - } - } else { - // 生成普通搜索字段 - if field.FieldType == "enum" || field.FieldType == "picture" || - field.FieldType == "pictures" || field.FieldType == "video" || - field.FieldType == "json" || field.FieldType == "richtext" || field.FieldType == "array" || field.FieldType == "file" { - result = fmt.Sprintf("%s string `json:\"%s\" form:\"%s\"` ", - field.FieldName, field.FieldJson, field.FieldJson) - } else { - result = fmt.Sprintf("%s *%s `json:\"%s\" form:\"%s\"` ", - field.FieldName, field.FieldType, field.FieldJson, field.FieldJson) - } - } - - return result -} diff --git a/server/utils/plugin/plugin.go b/server/utils/plugin/plugin.go deleted file mode 100644 index a59d5b5..0000000 --- a/server/utils/plugin/plugin.go +++ /dev/null @@ -1,18 +0,0 @@ -package plugin - -import ( - "github.com/gin-gonic/gin" -) - -const ( - OnlyFuncName = "Plugin" -) - -// Plugin 插件模式接口化 -type Plugin interface { - // Register 注册路由 - Register(group *gin.RouterGroup) - - // RouterPath 用户返回注册路由 - RouterPath() string -} diff --git a/server/utils/plugin/v2/plugin.go b/server/utils/plugin/v2/plugin.go deleted file mode 100644 index 4dac0ab..0000000 --- a/server/utils/plugin/v2/plugin.go +++ /dev/null @@ -1,11 +0,0 @@ -package plugin - -import ( - "github.com/gin-gonic/gin" -) - -// Plugin 插件模式接口化v2 -type Plugin interface { - // Register 注册路由 - Register(group *gin.Engine) -} diff --git a/server/utils/plugin/v2/registry.go b/server/utils/plugin/v2/registry.go deleted file mode 100644 index 4ec5fce..0000000 --- a/server/utils/plugin/v2/registry.go +++ /dev/null @@ -1,27 +0,0 @@ -package plugin - -import "sync" - -var ( - registryMu sync.RWMutex - registry []Plugin -) - -// Register records a plugin for auto initialization. -func Register(p Plugin) { - if p == nil { - return - } - registryMu.Lock() - registry = append(registry, p) - registryMu.Unlock() -} - -// Registered returns a snapshot of all registered plugins. -func Registered() []Plugin { - registryMu.RLock() - defer registryMu.RUnlock() - out := make([]Plugin, len(registry)) - copy(out, registry) - return out -} diff --git a/server/utils/verify.go b/server/utils/verify.go index cc2cb78..5f22869 100644 --- a/server/utils/verify.go +++ b/server/utils/verify.go @@ -8,9 +8,6 @@ var ( LoginVerify = Rules{"Username": {NotEmpty()}, "Password": {NotEmpty()}} RegisterVerify = Rules{"Username": {NotEmpty()}, "NickName": {NotEmpty()}, "Password": {NotEmpty()}, "AuthorityId": {NotEmpty()}} PageInfoVerify = Rules{"Page": {NotEmpty()}, "PageSize": {NotEmpty()}} - CustomerVerify = Rules{"CustomerName": {NotEmpty()}, "CustomerPhoneData": {NotEmpty()}} - AutoCodeVerify = Rules{"Abbreviation": {NotEmpty()}, "StructName": {NotEmpty()}, "PackageName": {NotEmpty()}} - AutoPackageVerify = Rules{"PackageName": {NotEmpty()}} AuthorityVerify = Rules{"AuthorityId": {NotEmpty()}, "AuthorityName": {NotEmpty()}} AuthorityIdVerify = Rules{"AuthorityId": {NotEmpty()}} OldAuthorityVerify = Rules{"OldAuthorityId": {NotEmpty()}} diff --git a/web-admin/docs/system-inventory.md b/web-admin/docs/system-inventory.md index d9bc780..a3adf03 100644 --- a/web-admin/docs/system-inventory.md +++ b/web-admin/docs/system-inventory.md @@ -26,28 +26,17 @@ - `sysVersion` 版本管理 - `sysError` 错误日志 -### 示例模块 - -- `breakpoint` 断点续传 -- `customer` 客户列表示例 - ### 公共模块 - `upload` 媒体库 +- `breakpoint` 断点续传 ### 编程辅助 -- `autoPkg` 模板配置 -- `autoCode` 代码生成器 -- `autoCodeAdmin` 自动化代码管理 - `formCreate` 表单生成器 -- `aiWorkflow` AI 需求工作流 - `exportTemplate` 导出模板 -- `mcpTest` Mcp Tools 管理 -- `mcpTool` Mcp Tools 模板 -- `skills` Skills 管理 -- `picture` AI 页面绘制 -- `autoCodeEdit/:id` 隐藏编辑路由 +- `mcpTest` MCP Tools 管理 +- `mcpTool` MCP Tools 模板 ## 主要接口分组 @@ -168,19 +157,12 @@ - 版本管理 - 媒体库 - 断点续传 -- 客户列表示例 -- 模板配置 -- 代码生成器 -- 自动化代码管理 - 表单生成器 -- AI 需求工作流 - 导出模板 -- Mcp Tools 管理 -- Mcp Tools 模板 -- Skills 管理 -- AI 页面绘制 +- MCP Tools 管理 +- MCP Tools 模板 ## 说明 - 新后台没有修改后端协议,仍然复用原有 token、菜单、权限和接口格式。 -- 当前仍有部分研发辅助类重模块保留为入口页,原因不是无法实现,而是这些模块交互面太大,适合单独拆阶段继续重构。 +- 当前仍有部分研发辅助模块保留为入口页,原因不是无法实现,而是这些模块交互面较大,适合单独拆阶段继续重构。 diff --git a/web-admin/src/App.tsx b/web-admin/src/App.tsx index 88138a2..627bf85 100644 --- a/web-admin/src/App.tsx +++ b/web-admin/src/App.tsx @@ -21,6 +21,10 @@ import { OperationLogPage } from '@/features/logs/OperationLogPage' import { SystemConfigPage } from '@/features/system/SystemConfigPage' import { ApiTokenPage } from '@/features/tokens/ApiTokenPage' import { ErrorLogPage } from '@/features/errors/ErrorLogPage' +import { MediaLibraryPage } from '@/features/media/MediaLibraryPage' +import { BreakpointPage } from '@/features/breakpoint/BreakpointPage' +import { McpTestPage } from '@/features/mcp/McpTestPage' +import { McpToolPage } from '@/features/mcp/McpToolPage' type AppRoute = { path: string @@ -45,22 +49,13 @@ const appRoutes: AppRoute[] = [ { path: 'admin/sysVersion', menuName: 'sysVersion', element: }, { path: 'admin/sysError', menuName: 'sysError', element: }, { path: 'common', menuName: 'common', element: }, - { path: 'common/upload', menuName: 'upload', element: }, - { path: 'example', menuName: 'example', element: }, - { path: 'example/breakpoint', menuName: 'breakpoint', element: }, - { path: 'example/customer', menuName: 'customer', element: }, + { path: 'common/upload', menuName: 'upload', element: }, + { path: 'common/breakpoint', menuName: 'breakpoint', element: }, { path: 'systemTools', menuName: 'systemTools', element: }, - { path: 'systemTools/autoPkg', menuName: 'autoPkg', element: }, - { path: 'systemTools/autoCode', menuName: 'autoCode', element: }, - { path: 'systemTools/autoCodeAdmin', menuName: 'autoCodeAdmin', element: }, { path: 'systemTools/formCreate', menuName: 'formCreate', element: }, - { path: 'systemTools/aiWorkflow', menuName: 'aiWorkflow', element: }, { path: 'systemTools/exportTemplate', menuName: 'exportTemplate', element: }, - { path: 'systemTools/mcpTest', menuName: 'mcpTest', element: }, - { path: 'systemTools/mcpTool', menuName: 'mcpTool', element: }, - { path: 'systemTools/skills', menuName: 'skills', element: }, - { path: 'systemTools/picture', menuName: 'picture', element: }, - { path: 'systemTools/autoCodeEdit/:id', menuName: 'autoCodeEdit', element: }, + { path: 'systemTools/mcpTest', menuName: 'mcpTest', element: }, + { path: 'systemTools/mcpTool', menuName: 'mcpTool', element: }, { path: 'person', menuName: 'person', element: }, { path: 'state', menuName: 'state', element: }, ] diff --git a/web-admin/src/features/discovery/systemInventory.ts b/web-admin/src/features/discovery/systemInventory.ts index 0db652b..7949cd2 100644 --- a/web-admin/src/features/discovery/systemInventory.ts +++ b/web-admin/src/features/discovery/systemInventory.ts @@ -142,18 +142,9 @@ export const moduleCatalog: Record = { group: '公共模块', status: 'partial', summary: '承载多端复用的公共功能,不再挂在示例目录下。', - features: ['公共上传能力', '后续 app 复用入口'], + features: ['媒体库', '断点续传', '后续 app 复用入口'], endpoints: ['/fileUploadAndDownload/getFileList', '/attachmentCategory/getCategoryList'], }, - example: { - name: 'example', - title: '示例文件', - group: '业务示例', - status: 'partial', - summary: '保留和业务演示直接相关的示例页面。', - features: ['断点续传', '客户资源示例'], - endpoints: ['/customer/customerList'], - }, upload: { name: 'upload', title: '媒体库', @@ -166,56 +157,20 @@ export const moduleCatalog: Record = { breakpoint: { name: 'breakpoint', title: '断点续传', - group: '业务示例', + group: '公共模块', status: 'partial', summary: '展示大文件切片上传流程。', features: ['文件校验', '分片上传', '合并完成'], endpoints: ['/fileUploadAndDownload/findFile', '/fileUploadAndDownload/breakpointContinue'], }, - customer: { - name: 'customer', - title: '客户列表', - group: '业务示例', - status: 'partial', - summary: '示例业务资源表,用于展示普通 CRUD 模式。', - features: ['客户列表', '增删改查'], - endpoints: ['/customer/customerList', '/customer/customer'], - }, systemTools: { name: 'systemTools', title: '编程辅助', group: '研发辅助', status: 'partial', - summary: '聚合代码生成、模板配置、技能管理和 AI 工作流。', - features: ['代码生成器', '模板配置', 'Skills 管理', 'AI 页面绘制'], - endpoints: ['/autoCode/getDB', '/skills/getSkillList'], - }, - autoPkg: { - name: 'autoPkg', - title: '模板配置', - group: '研发辅助', - status: 'partial', - summary: '维护自动代码生成模板。', - features: ['模板列表', '模板文件'], - endpoints: ['/autoCode/createPackage', '/autoCode/getPackage', '/autoCode/getTemplates'], - }, - autoCode: { - name: 'autoCode', - title: '代码生成器', - group: '研发辅助', - status: 'partial', - summary: '自动生成表单、接口、服务和路由代码。', - features: ['库表发现', '代码预览', '代码生成'], - endpoints: ['/autoCode/getDB', '/autoCode/getTables', '/autoCode/preview', '/autoCode/createTemp'], - }, - autoCodeAdmin: { - name: 'autoCodeAdmin', - title: '自动化代码管理', - group: '研发辅助', - status: 'partial', - summary: '管理自动化代码任务和函数模板。', - features: ['任务管理', '模板方法'], - endpoints: ['/autoCode/getMeta', '/autoCode/addFunc'], + summary: '聚合表单设计、导出模板和 MCP 管理等研发辅助能力。', + features: ['表单设计', '导出模板', 'MCP 管理'], + endpoints: ['/sysExportTemplate/getSysExportTemplateList', '/mcp/status'], }, formCreate: { name: 'formCreate', @@ -226,15 +181,6 @@ export const moduleCatalog: Record = { features: ['表单设计'], endpoints: [], }, - aiWorkflow: { - name: 'aiWorkflow', - title: 'AI 需求工作流', - group: '研发辅助', - status: 'partial', - summary: '管理 AI 会话和需求工作流结果。', - features: ['会话列表', '会话详情', 'Markdown 落盘'], - endpoints: ['/autoCode/getAIWorkflowSessionList', '/autoCode/getAIWorkflowSessionDetail'], - }, exportTemplate: { name: 'exportTemplate', title: '导出模板', @@ -246,39 +192,21 @@ export const moduleCatalog: Record = { }, mcpTest: { name: 'mcpTest', - title: 'Mcp Tools 管理', + title: 'MCP Tools 管理', group: '研发辅助', status: 'partial', - summary: '围绕 MCP Tool 的状态、测试和服务控制。', - features: ['服务状态', '测试调用'], - endpoints: ['/autoCode/mcpStatus', '/autoCode/mcpTest', '/autoCode/mcpList'], + summary: '围绕 MCP Tool 的状态、测试和独立服务控制。', + features: ['服务状态', '工具列表', '测试调用'], + endpoints: ['/mcp/status', '/mcp/tools', '/mcp/test'], }, mcpTool: { name: 'mcpTool', - title: 'Mcp Tools 模板', + title: 'MCP Tools 模板', group: '研发辅助', status: 'partial', summary: '生成 MCP Tool 模板代码。', features: ['模板生成'], - endpoints: ['/autoCode/mcp'], - }, - skills: { - name: 'skills', - title: 'Skills 管理', - group: '研发辅助', - status: 'partial', - summary: '管理技能定义、脚本、资源、模板和全局约束。', - features: ['技能列表', '脚本资源管理', '技能打包'], - endpoints: ['/skills/getSkillList', '/skills/saveSkill', '/skills/packageSkill'], - }, - picture: { - name: 'picture', - title: 'AI 页面绘制', - group: '研发辅助', - status: 'partial', - summary: '基于 AI 描述生成页面草图。', - features: ['图像工作台'], - endpoints: ['/autoCode/llmAuto'], + endpoints: ['/mcp/createTool'], }, person: { name: 'person', diff --git a/web-admin/src/features/menus/menuComponentCatalog.ts b/web-admin/src/features/menus/menuComponentCatalog.ts index 7ba5781..46a622d 100644 --- a/web-admin/src/features/menus/menuComponentCatalog.ts +++ b/web-admin/src/features/menus/menuComponentCatalog.ts @@ -29,8 +29,6 @@ const componentOptions: ComponentOption[] = [ { value: 'features/errors/ErrorLogPage', label: 'ErrorLogPage · 错误日志', routeName: 'sysError' }, { value: 'features/person/ProfilePage', label: 'ProfilePage · 个人中心', routeName: 'person' }, { value: 'features/server/ServerStatePage', label: 'ServerStatePage · 服务器状态', routeName: 'state' }, - { value: 'features/mcp/McpTestPage', label: 'McpTestPage · MCP Tools 管理', routeName: 'mcpTest' }, - { value: 'features/mcp/McpToolPage', label: 'McpToolPage · MCP Tools 模板', routeName: 'mcpTool' }, { value: 'features/discovery/ModuleLandingPage:superAdmin', label: 'ModuleLandingPage · 超级管理员', @@ -47,8 +45,8 @@ const componentOptions: ComponentOption[] = [ routeName: 'upload', }, { - value: 'features/discovery/ModuleLandingPage:breakpoint', - label: 'ModuleLandingPage · 断点续传', + value: 'features/breakpoint/BreakpointPage', + label: 'BreakpointPage · 断点续传', routeName: 'breakpoint', }, { @@ -56,51 +54,14 @@ const componentOptions: ComponentOption[] = [ label: 'ModuleLandingPage · 编程辅助', routeName: 'systemTools', }, - { - value: 'features/discovery/ModuleLandingPage:aiWorkflow', - label: 'ModuleLandingPage · AI 需求工作流', - routeName: 'aiWorkflow', - }, { value: 'features/discovery/ModuleLandingPage:exportTemplate', label: 'ModuleLandingPage · 导出模板', routeName: 'exportTemplate', }, - { - value: 'features/discovery/ModuleLandingPage:example', - label: 'ModuleLandingPage · 示例文件', - routeName: 'example', - }, - { - value: 'features/discovery/ModuleLandingPage:customer', - label: 'ModuleLandingPage · 客户列表示例', - routeName: 'customer', - }, - { - value: 'features/discovery/ModuleLandingPage:plugin', - label: 'ModuleLandingPage · 插件系统', - routeName: 'plugin', - }, - { - value: 'features/discovery/ModuleLandingPage:installPlugin', - label: 'ModuleLandingPage · 插件安装', - routeName: 'installPlugin', - }, - { - value: 'features/discovery/ModuleLandingPage:pubPlug', - label: 'ModuleLandingPage · 打包插件', - routeName: 'pubPlug', - }, - { - value: 'features/discovery/ModuleLandingPage:plugin-email', - label: 'ModuleLandingPage · 邮件插件', - routeName: 'plugin-email', - }, - { - value: 'features/discovery/ModuleLandingPage:anInfo', - label: 'ModuleLandingPage · 公告管理示例', - routeName: 'anInfo', - }, + { value: 'features/mcp/McpTestPage', label: 'McpTestPage · MCP Tools 管理', routeName: 'mcpTest' }, + { value: 'features/mcp/McpToolPage', label: 'McpToolPage · MCP Tools 模板', routeName: 'mcpTool' }, + { value: 'features/media/MediaLibraryPage', label: 'MediaLibraryPage · 媒体库', routeName: 'upload' }, ] const componentRouteNameMap = new Map(componentOptions.map((item) => [item.value, item.routeName])) diff --git a/web-admin/src/lib/api.ts b/web-admin/src/lib/api.ts index 0ac9148..dbe4b4c 100644 --- a/web-admin/src/lib/api.ts +++ b/web-admin/src/lib/api.ts @@ -2,12 +2,19 @@ import { http } from './http' import type { ApiRecord, ApiTokenRecord, + AttachmentCategory, Authority, CaptchaInfo, Dictionary, DictionaryDetail, LoginLog, LoginResult, + McpContent, + McpManagedStatus, + McpServerConfig, + McpToolDescriptor, + McpToolTemplateRequest, + MediaFileRecord, MenuNode, OperationRecord, PagePayload, @@ -273,3 +280,61 @@ export const inventoryApi = { return http.post<{ server: Record }>('/system/getServerInfo') }, } + +export const mediaLibraryApi = { + getCategoryList() { + return http.get('/attachmentCategory/getCategoryList') + }, + saveCategory(payload: Partial) { + return http.post>('/attachmentCategory/addCategory', payload) + }, + deleteCategory(id: number) { + return http.post>('/attachmentCategory/deleteCategory', { id }) + }, + getFileList(payload: Record) { + return http.post>('/fileUploadAndDownload/getFileList', payload) + }, + editFile(payload: Partial) { + return http.post>('/fileUploadAndDownload/editFileName', payload) + }, + deleteFile(id: number) { + return http.post>('/fileUploadAndDownload/deleteFile', { ID: id }) + }, +} + +export const breakpointApi = { + findFile(params: { fileMd5: string; fileName: string; chunkTotal: number }) { + return http.get<{ file: Record }>('/fileUploadAndDownload/findFile', { params }) + }, + finish(params: { fileMd5: string; fileName: string }) { + return http.post<{ filePath: string }>('/fileUploadAndDownload/breakpointContinueFinish', undefined, { params }) + }, + removeChunk(payload: { fileMd5: string; filePath: string }) { + return http.post>('/fileUploadAndDownload/removeChunk', payload) + }, +} + +export const mcpApi = { + getStatus() { + return http.get<{ status: McpManagedStatus; mcpServerConfig: McpServerConfig }>('/mcp/status') + }, + start() { + return http.post<{ status: McpManagedStatus; mcpServerConfig: McpServerConfig }>('/mcp/start') + }, + stop() { + return http.post<{ status: McpManagedStatus; mcpServerConfig: McpServerConfig }>('/mcp/stop') + }, + getList() { + return http.get<{ + status: McpManagedStatus + mcpServerConfig: McpServerConfig + list: { tools: McpToolDescriptor[] } + }>('/mcp/tools') + }, + test(payload: { name: string; arguments: Record }) { + return http.post('/mcp/test', payload) + }, + createTool(payload: McpToolTemplateRequest) { + return http.post>('/mcp/createTool', payload) + }, +} diff --git a/web-admin/src/router/pages/example/customer/page.tsx b/web-admin/src/router/pages/example/customer/page.tsx deleted file mode 100644 index f621df7..0000000 --- a/web-admin/src/router/pages/example/customer/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'customer', -} - -export default createModulePage('customer') diff --git a/web-admin/src/router/pages/example/page.tsx b/web-admin/src/router/pages/example/page.tsx deleted file mode 100644 index da08033..0000000 --- a/web-admin/src/router/pages/example/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'example', -} - -export default createModulePage('example') diff --git a/web-admin/src/router/pages/plugin/anInfo/page.tsx b/web-admin/src/router/pages/plugin/anInfo/page.tsx deleted file mode 100644 index 9296f08..0000000 --- a/web-admin/src/router/pages/plugin/anInfo/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'anInfo', -} - -export default createModulePage('anInfo') diff --git a/web-admin/src/router/pages/plugin/installPlugin/page.tsx b/web-admin/src/router/pages/plugin/installPlugin/page.tsx deleted file mode 100644 index e78090b..0000000 --- a/web-admin/src/router/pages/plugin/installPlugin/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'installPlugin', -} - -export default createModulePage('installPlugin') diff --git a/web-admin/src/router/pages/plugin/page.tsx b/web-admin/src/router/pages/plugin/page.tsx deleted file mode 100644 index 5bd1437..0000000 --- a/web-admin/src/router/pages/plugin/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'plugin', -} - -export default createModulePage('plugin') diff --git a/web-admin/src/router/pages/plugin/plugin-email/page.tsx b/web-admin/src/router/pages/plugin/plugin-email/page.tsx deleted file mode 100644 index 7811894..0000000 --- a/web-admin/src/router/pages/plugin/plugin-email/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'plugin-email', -} - -export default createModulePage('plugin-email') diff --git a/web-admin/src/router/pages/plugin/pubPlug/page.tsx b/web-admin/src/router/pages/plugin/pubPlug/page.tsx deleted file mode 100644 index 7f4076d..0000000 --- a/web-admin/src/router/pages/plugin/pubPlug/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'pubPlug', -} - -export default createModulePage('pubPlug') diff --git a/web-admin/src/router/pages/systemTools/aiWorkflow/page.tsx b/web-admin/src/router/pages/systemTools/aiWorkflow/page.tsx deleted file mode 100644 index 2c67cd2..0000000 --- a/web-admin/src/router/pages/systemTools/aiWorkflow/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'aiWorkflow', -} - -export default createModulePage('aiWorkflow') diff --git a/web-admin/src/types/system.ts b/web-admin/src/types/system.ts index c6f9456..53f364d 100644 --- a/web-admin/src/types/system.ts +++ b/web-admin/src/types/system.ts @@ -158,6 +158,79 @@ export type SysErrorRecord = BaseEntity & { status?: string } +export type AttachmentCategory = BaseEntity & { + name: string + pid: number + children?: AttachmentCategory[] +} + +export type MediaFileRecord = BaseEntity & { + name: string + classId: number + url: string + tag: string + key: string +} + +export type JsonSchema = { + type?: string + properties?: Record + items?: JsonSchema + enum?: unknown[] + default?: unknown +} + +export type McpContent = { + type: string + text?: string + data?: string + mimeType?: string +} + +export type McpManagedStatus = { + state: string + managed: boolean + reachable: boolean + starting: boolean + baseURL: string + healthURL: string + listenAddr: string + path: string + authHeader: string + pid?: number + logPath?: string + startedAt?: string + lastError?: string + message?: string +} + +export type McpServerConfig = { + mcpServers?: Record }> +} + +export type McpToolDescriptor = { + name: string + description?: string + inputSchema?: JsonSchema | Record +} + +export type McpToolTemplateRequest = { + name: string + description: string + params: Array<{ + name: string + description: string + type: string + required: boolean + default?: string + }> + response: Array<{ + type: string + }> +} + +export type AutoMcpToolRequest = McpToolTemplateRequest + export type ModuleStatus = 'ready' | 'partial' | 'planned' export type ModuleDescriptor = {