Przeglądaj źródła

添加代码生成中route和gorm的自动注入

LeonardWang 4 lat temu
rodzic
commit
2a1235a9a6

+ 16 - 0
server/config.yaml

@@ -68,6 +68,22 @@ mysql:
 local:
   path: 'uploads/file'
 
+# autocode configuration
+autocode:
+  root: ""
+  server: /server
+  server-api: /api/v1
+  server-initialize: /initialize
+  server-model: /model
+  server-request: /model/request/
+  server-router: /router
+  server-service: /service
+  web: /web/src
+  web-api: /api
+  web-flow: /view
+  web-form: /view
+  web-table: /view
+
 # qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket �?域名地址)
 qiniu:
   zone: 'ZoneHuadong'

+ 13 - 12
server/config/auto_code.go

@@ -1,16 +1,17 @@
 package config
 
 type Autocode struct {
-	Root     string `mapstructure:"root" json:"root" yaml:"root"`
-	Server   string `mapstructure:"server" json:"server" yaml:"server"`
-	SApi     string `mapstructure:"server-api" json:"serverApi" yaml:"server-api"`
-	SModel   string `mapstructure:"server-model" json:"serverModel" yaml:"server-model"`
-	SRequest string `mapstructure:"server-request" json:"serverRequest"  yaml:"server-request"`
-	SRouter  string `mapstructure:"server-router" json:"serverRouter" yaml:"server-router"`
-	SService string `mapstructure:"server-service" json:"serverService" yaml:"server-service"`
-	Web      string `mapstructure:"web" json:"web" yaml:"web"`
-	WApi     string `mapstructure:"web-api" json:"webApi" yaml:"web-api"`
-	WForm    string `mapstructure:"web-form" json:"webForm" yaml:"web-form"`
-	WTable   string `mapstructure:"web-table" json:"webTable" yaml:"web-table"`
-	WFlow    string `mapstructure:"web-flow" json:"webFlow" yaml:"web-flow"`
+	Root        string `mapstructure:"root" json:"root" yaml:"root"`
+	Server      string `mapstructure:"server" json:"server" yaml:"server"`
+	SApi        string `mapstructure:"server-api" json:"serverApi" yaml:"server-api"`
+	SInitialize string `mapstructure:"server-initialize" json:"serverInitialize" yaml:"server-initialize"`
+	SModel      string `mapstructure:"server-model" json:"serverModel" yaml:"server-model"`
+	SRequest    string `mapstructure:"server-request" json:"serverRequest"  yaml:"server-request"`
+	SRouter     string `mapstructure:"server-router" json:"serverRouter" yaml:"server-router"`
+	SService    string `mapstructure:"server-service" json:"serverService" yaml:"server-service"`
+	Web         string `mapstructure:"web" json:"web" yaml:"web"`
+	WApi        string `mapstructure:"web-api" json:"webApi" yaml:"web-api"`
+	WForm       string `mapstructure:"web-form" json:"webForm" yaml:"web-form"`
+	WTable      string `mapstructure:"web-table" json:"webTable" yaml:"web-table"`
+	WFlow       string `mapstructure:"web-flow" json:"webFlow" yaml:"web-flow"`
 }

+ 1 - 1
server/config/config.go

@@ -8,7 +8,7 @@ type Server struct {
 	Casbin  Casbin  `mapstructure:"casbin" json:"casbin" yaml:"casbin"`
 	System  System  `mapstructure:"system" json:"system" yaml:"system"`
 	Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
-	// aoto
+	// auto
 	AutoCode Autocode `mapstructure:"autoCode" json:"autoCode" yaml:"autoCode"`
 	// gorm
 	Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`

+ 3 - 0
server/initialize/gorm.go

@@ -48,6 +48,9 @@ func MysqlTables(db *gorm.DB) {
 		model.ExaSimpleUploader{},
 		model.ExaCustomer{},
 		model.SysOperationRecord{},
+
+		// Code generated by gin-vue-admin Begin; DO NOT EDIT.
+		// Code generated by gin-vue-admin End; DO NOT EDIT.
 	)
 	if err != nil {
 		global.GVA_LOG.Error("register table failed", zap.Any("err", err))

+ 3 - 0
server/initialize/router.go

@@ -49,6 +49,9 @@ func Routers() *gin.Engine {
 		router.InitSysDictionaryDetailRouter(PrivateGroup)   // 字典详情管理
 		router.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
 		router.InitExcelRouter(PrivateGroup)                 // 表格导入导出
+
+		// Code generated by gin-vue-admin Begin; DO NOT EDIT.
+		// Code generated by gin-vue-admin End; DO NOT EDIT.
 	}
 	global.GVA_LOG.Info("router register success")
 	return Router

+ 12 - 0
server/service/sys_auto_code.go

@@ -134,6 +134,18 @@ func CreateTemp(autoCode model.AutoCodeStruct) (err error) {
 				return err
 			}
 		}
+		initializeGormFilePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
+			global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "gorm.go")
+		initializeRouterFilePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
+			global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "router.go")
+		err = utils.AutoInjectionCode(initializeGormFilePath, "MysqlTables", "model."+autoCode.StructName+"{},")
+		if err != nil {
+			return err
+		}
+		err = utils.AutoInjectionCode(initializeRouterFilePath, "Routers", "router.Init"+autoCode.StructName+"Router(PrivateGroup)")
+		if err != nil {
+			return err
+		}
 		return errors.New("创建代码成功并移动文件成功")
 	} else { // 打包
 		if err := utils.ZipFiles("./ginvueadmin.zip", fileList, ".", "."); err != nil {

+ 2 - 0
server/service/sys_initdb.go

@@ -10,6 +10,7 @@ import (
 	"github.com/spf13/viper"
 	"gorm.io/driver/mysql"
 	"gorm.io/gorm"
+	"path/filepath"
 )
 
 //@author: [songzhibin97](https://github.com/songzhibin97)
@@ -154,5 +155,6 @@ func InitDB(conf request.InitDB) error {
 		_ = writeConfig(global.GVA_VP, baseSetting)
 		return err
 	}
+	global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
 	return nil
 }

+ 128 - 0
server/utils/injectionCode.go

@@ -0,0 +1,128 @@
+package utils
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"io/ioutil"
+	"strings"
+)
+
+//@author: [LeonardWang](https://github.com/WangLeonard)
+//@function: AutoInjectionCode
+//@description: 向文件中固定注释位置写入代码
+//@param: filepath string, funcName string, codeData string
+//@return: err error
+
+func AutoInjectionCode(filepath string, funcName string, codeData string) error {
+	startComment := "Code generated by gin-vue-admin Begin; DO NOT EDIT."
+	endComment := "Code generated by gin-vue-admin End; DO NOT EDIT."
+	srcData, err := ioutil.ReadFile(filepath)
+	if err != nil {
+		return err
+	}
+	srcDataLen := len(srcData)
+	fset := token.NewFileSet()
+	fparser, err := parser.ParseFile(fset, filepath, srcData, parser.ParseComments)
+	if err != nil {
+		return err
+	}
+	codeData = strings.TrimSpace(codeData)
+	var codeStartPos = -1
+	var codeEndPos = srcDataLen
+	var expectedFunction *ast.FuncDecl
+
+	var startCommentPos = -1
+	var endCommentPos = srcDataLen
+
+	// 如果指定了函数名,先寻找对应函数
+	if funcName != "" {
+		for _, decl := range fparser.Decls {
+			if funDecl, ok := decl.(*ast.FuncDecl); ok && funDecl.Name.Name == funcName {
+				expectedFunction = funDecl
+				codeStartPos = int(funDecl.Body.Lbrace)
+				codeEndPos = int(funDecl.Body.Rbrace)
+				break
+			}
+		}
+	}
+
+	// 遍历所有注释
+	for _, comment := range fparser.Comments {
+		if int(comment.Pos()) > codeStartPos && int(comment.End()) <= codeEndPos {
+			if startComment != "" && strings.Contains(comment.Text(), startComment) {
+				startCommentPos = int(comment.Pos()) // Note: Pos is the second '/'
+			}
+			if endComment != "" && strings.Contains(comment.Text(), endComment) {
+				endCommentPos = int(comment.Pos()) // Note: Pos is the second '/'
+			}
+		}
+	}
+
+	if endCommentPos == srcDataLen {
+		return fmt.Errorf("comment:%s not found", endComment)
+	}
+
+	// 在指定函数名,且函数中startComment和endComment都存在时,进行区间查重
+	if (codeStartPos != -1 && codeEndPos != srcDataLen) && (startCommentPos != -1 && endCommentPos != srcDataLen) && expectedFunction != nil {
+		if exist := checkExist(&srcData, startCommentPos, endCommentPos, expectedFunction.Body, codeData); exist {
+			fmt.Println("已存在")
+			return nil // 这里不需要返回错误?
+		}
+	}
+
+	// 两行注释中间没有换行时,会被认为是一条Comment
+	if startCommentPos == endCommentPos {
+		endCommentPos = startCommentPos + strings.Index(string(srcData[startCommentPos:]), endComment)
+		for srcData[endCommentPos] != '/' {
+			endCommentPos--
+		}
+	}
+
+	// 记录"//"之前的空字符,保持写入后的格式一致
+	tmpSpace := make([]byte, 0, 10)
+	for tmp := endCommentPos - 2; tmp >= 0; tmp-- {
+		if srcData[tmp] != '\n' {
+			tmpSpace = append(tmpSpace, srcData[tmp])
+		} else {
+			break
+		}
+	}
+
+	reverseSpace := make([]byte, 0, len(tmpSpace))
+	for index := len(tmpSpace) - 1; index >= 0; index-- {
+		reverseSpace = append(reverseSpace, tmpSpace[index])
+	}
+
+	// 插入数据
+	indexPos := endCommentPos - 1
+	insertData := []byte(append([]byte(codeData+"\n"), reverseSpace...))
+
+	remainData := append([]byte{}, srcData[indexPos:]...)
+	srcData = append(append(srcData[:indexPos], insertData...), remainData...)
+
+	// 写回数据
+	return ioutil.WriteFile(filepath, srcData, 0600)
+}
+
+func checkExist(srcData *[]byte, startPos int, endPos int, blockStmt *ast.BlockStmt, target string) bool {
+	for _, list := range blockStmt.List {
+		switch stmt := list.(type) {
+		case *ast.ExprStmt:
+			if callExpr, ok := stmt.X.(*ast.CallExpr); ok &&
+				int(callExpr.Pos()) > startPos && int(callExpr.End()) < endPos {
+				text := string((*srcData)[int(callExpr.Pos()-1):int(callExpr.End())])
+				key := strings.TrimSpace(text)
+				if key == target {
+					return true
+				}
+			}
+		case *ast.BlockStmt:
+			if checkExist(srcData, startPos, endPos, stmt, target) {
+				return true
+			}
+		}
+	}
+	return false
+}