Browse Source

feat:回滚自动化生产代码

songzhibin97 3 years ago
parent
commit
dac4e733fc

+ 18 - 0
server/api/v1/sys_auto_code.go

@@ -15,6 +15,24 @@ import (
 	"go.uber.org/zap"
 )
 
+// @Tags AutoCode
+// @Summary 回滚
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body uint true "回滚自动生成代码"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"回滚成功"}"
+// @Router /autoCode/preview [post]
+func RollBack(c *gin.Context) {
+	var id model.AutoHistoryByID
+	_ = c.ShouldBindJSON(&id)
+	if err := service.RollBack(id.ID); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	response.OkWithMessage("回滚成功", c)
+}
+
 // @Tags AutoCode
 // @Summary 预览创建后的代码
 // @Security ApiKeyAuth

+ 5 - 1
server/model/sys_auto_code.go

@@ -2,12 +2,16 @@ package model
 
 import "errors"
 
+type AutoHistoryByID struct {
+	ID uint `json:"id"`
+}
+
 // 初始版本自动化代码工具
 type AutoCodeStruct struct {
 	StructName         string   `json:"structName"`         // Struct名称
 	TableName          string   `json:"tableName"`          // 表名
 	PackageName        string   `json:"packageName"`        // 文件名称
-	HumpPackageName    string   `json:"humpPackageName"`        // go文件名称
+	HumpPackageName    string   `json:"humpPackageName"`    // go文件名称
 	Abbreviation       string   `json:"abbreviation"`       // Struct简称
 	Description        string   `json:"description"`        // Struct中文名称
 	AutoCreateApiToSql bool     `json:"autoCreateApiToSql"` // 是否自动创建api

+ 13 - 0
server/model/sys_autocode_history.go

@@ -0,0 +1,13 @@
+package model
+
+import "gin-vue-admin/global"
+
+// 自动迁移代码记录,用于回滚,重放使用
+
+type SysAutoCodeHistory struct {
+	global.GVA_MODEL
+	TableName     string
+	AutoCodeMeta  string `gorm:"type:text"` // 其他meta信息 path;path
+	InjectionMeta string `gorm:"type:text"` // 注入的内容 RouterPath@functionName@RouterString;
+	Flag          int    // 表示对应状态 0 代表创建, 1 代表回滚 ...
+}

+ 1 - 0
server/router/sys_auto_code.go

@@ -8,6 +8,7 @@ import (
 func InitAutoCodeRouter(Router *gin.RouterGroup) {
 	AutoCodeRouter := Router.Group("autoCode")
 	{
+		AutoCodeRouter.POST("rollback", v1.RollBack)     // 回滚
 		AutoCodeRouter.POST("preview", v1.PreviewTemp)   // 获取自动创建代码预览
 		AutoCodeRouter.POST("createTemp", v1.CreateTemp) // 创建自动化代码
 		AutoCodeRouter.GET("getTables", v1.GetTables)    // 获取对应数据库的表

+ 38 - 4
server/service/sys_auto_code.go

@@ -2,6 +2,7 @@ package service
 
 import (
 	"errors"
+	"fmt"
 	"gin-vue-admin/global"
 	"gin-vue-admin/model"
 	"gin-vue-admin/model/request"
@@ -143,6 +144,35 @@ func CreateTemp(autoCode model.AutoCodeStruct) (err error) {
 			return err
 		}
 		err = utils.AutoInjectionCode(initializeRouterFilePath, "Routers", "router.Init"+autoCode.StructName+"Router(PrivateGroup)")
+		if err != nil {
+			return err
+		}
+		injectionCodeMeta := strings.Builder{}
+		injectionCodeMeta.WriteString(fmt.Sprintf("%s@%s@%s", initializeGormFilePath, "MysqlTables", "model."+autoCode.StructName+"{},"))
+		injectionCodeMeta.WriteString(";")
+		injectionCodeMeta.WriteString(fmt.Sprintf("%s@%s@%s", initializeRouterFilePath, "Routers", "router.Init"+autoCode.StructName+"Router(PrivateGroup)"))
+
+		// 保存生成信息
+		bf := strings.Builder{}
+		for _, data := range dataList {
+			if len(data.autoMoveFilePath) != 0 {
+				bf.WriteString(data.autoMoveFilePath)
+				bf.WriteString(";")
+			}
+		}
+
+		if autoCode.TableName != "" {
+			err = CreateAutoCodeHistory(bf.String(),
+				injectionCodeMeta.String(),
+				autoCode.TableName,
+			)
+		} else {
+			err = CreateAutoCodeHistory(bf.String(),
+				injectionCodeMeta.String(),
+				autoCode.StructName,
+			)
+		}
+
 		if err != nil {
 			return err
 		}
@@ -215,6 +245,10 @@ func GetColumn(tableName string, dbName string) (err error, Columns []request.Co
 	return err, Columns
 }
 
+func DropTable(tableName string) error {
+	return global.GVA_DB.Exec("DROP TABLE " + tableName).Error
+}
+
 //@author: [SliverHorn](https://github.com/SliverHorn)
 //@author: [songzhibin97](https://github.com/songzhibin97)
 //@function: addAutoMoveFile
@@ -361,10 +395,10 @@ func getNeedList(autoCode *model.AutoCodeStruct) (dataList []tplData, fileList [
 			firstDot := strings.Index(origFileName, ".")
 			if firstDot != -1 {
 				var fileName string
-				if origFileName[firstDot:] !=".go"{
-					fileName = autoCode.PackageName+origFileName[firstDot:]
-				}else{
-					fileName = autoCode.HumpPackageName+origFileName[firstDot:]
+				if origFileName[firstDot:] != ".go" {
+					fileName = autoCode.PackageName + origFileName[firstDot:]
+				} else {
+					fileName = autoCode.HumpPackageName + origFileName[firstDot:]
 				}
 
 				dataList[index].autoCodePath = filepath.Join(autoPath, trimBase[:lastSeparator], autoCode.PackageName,

+ 58 - 0
server/service/sys_autocode_history.go

@@ -0,0 +1,58 @@
+package service
+
+import (
+	"errors"
+	"gin-vue-admin/global"
+	"gin-vue-admin/model"
+	"gin-vue-admin/utils"
+	"strings"
+
+	"go.uber.org/zap"
+)
+
+// CreateAutoCodeHistory RouterPath : RouterPath@RouterString;RouterPath2@RouterString2
+func CreateAutoCodeHistory(autoCodeMeta string, injectionMeta string, tableName string) error {
+	return global.GVA_DB.Create(&model.SysAutoCodeHistory{
+		AutoCodeMeta:  autoCodeMeta,
+		InjectionMeta: injectionMeta,
+		TableName:     tableName,
+	}).Error
+}
+
+func RollBack(id uint) error {
+	md := model.SysAutoCodeHistory{}
+	if err := global.GVA_DB.First(&md, id).Error; err != nil {
+		return err
+	}
+	// 切分数据
+	err, dbNames := GetTables(global.GVA_CONFIG.Mysql.Dbname)
+	if err != nil {
+		return err
+	}
+	// 删除表
+	for _, name := range dbNames {
+		if strings.Contains(strings.ToUpper(strings.Replace(name.TableName, "_", "", -1)), strings.ToUpper(md.TableName)) {
+			// 删除表
+			if err = DropTable(name.TableName); err != nil {
+				global.GVA_LOG.Error("ClearTag DropTable:", zap.Error(err))
+
+			}
+		}
+	}
+	// 删除文件
+
+	for _, path := range strings.Split(md.AutoCodeMeta, ";") {
+		_ = utils.DeLFile(path)
+	}
+	// 清除注入
+	for _, v := range strings.Split(md.InjectionMeta, ";") {
+		// RouterPath@functionName@RouterString
+		meta := strings.Split(v, "@")
+		if len(meta) != 3 {
+			return errors.New("split InjectionMeta Err")
+		}
+		_ = utils.AutoClearCode(meta[0], meta[2])
+	}
+	md.Flag = 1
+	return global.GVA_DB.Save(&md).Error
+}

+ 1 - 0
server/service/sys_initdb.go

@@ -131,6 +131,7 @@ func InitDB(conf request.InitDB) error {
 		model.ExaSimpleUploader{},
 		model.ExaCustomer{},
 		model.SysOperationRecord{},
+		model.SysAutoCodeHistory{},
 	)
 	if err != nil {
 		global.GVA_DB = nil

+ 4 - 0
server/utils/file_operations.go

@@ -42,6 +42,10 @@ Redirect:
 	return os.Rename(src, dst)
 }
 
+func DeLFile(filePath string) error {
+	return os.RemoveAll(filePath)
+}
+
 //@author: [songzhibin97](https://github.com/songzhibin97)
 //@function: TrimSpace
 //@description: 去除结构体空格

+ 39 - 2
server/utils/injectionCode.go

@@ -1,6 +1,7 @@
 package utils
 
 import (
+	"errors"
 	"fmt"
 	"go/ast"
 	"go/parser"
@@ -15,9 +16,18 @@ import (
 //@param: filepath string, funcName string, codeData string
 //@return: error
 
+const (
+	startComment = "Code generated by gin-vue-admin Begin; DO NOT EDIT."
+	endComment   = "Code generated by gin-vue-admin End; DO NOT EDIT."
+)
+
+//@author: [LeonardWang](https://github.com/WangLeonard)
+//@function: AutoInjectionCode
+//@description: 向文件中固定注释位置写入代码
+//@param: filepath string, funcName string, codeData string
+//@return: 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
@@ -141,3 +151,30 @@ func checkExist(srcData *[]byte, startPos int, endPos int, blockStmt *ast.BlockS
 	}
 	return false
 }
+
+func AutoClearCode(filepath string, codeData string) error {
+	srcData, err := ioutil.ReadFile(filepath)
+	if err != nil {
+		return err
+	}
+	srcData, err = cleanCode(codeData, string(srcData))
+	if err != nil {
+		return err
+	}
+	return ioutil.WriteFile(filepath, srcData, 0600)
+}
+
+func cleanCode(clearCode string, srcData string) ([]byte, error) {
+	bf := make([]rune, 0, 1024)
+	for i, v := range srcData {
+		if v == '\n' {
+			if strings.TrimSpace(string(bf)) == clearCode {
+				return append([]byte(srcData[:i-len(bf)]), []byte(srcData[i+1:])...), nil
+			}
+			bf = (bf)[:0]
+			continue
+		}
+		bf = append(bf, v)
+	}
+	return []byte(srcData), errors.New("未找到内容")
+}