Quellcode durchsuchen

Merge remote-tracking branch 'origin/gva_gormv2_dev' into gva_gormv2_dev

pixel vor 3 Jahren
Ursprung
Commit
dcba08981d

+ 49 - 2
server/api/v1/sys_auto_code.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"gin-vue-admin/global"
 	"gin-vue-admin/model"
+	"gin-vue-admin/model/request"
 	"gin-vue-admin/model/response"
 	"gin-vue-admin/service"
 	"gin-vue-admin/utils"
@@ -15,6 +16,49 @@ import (
 	"go.uber.org/zap"
 )
 
+// @Tags AutoCode
+// @Summary 查询回滚记录
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.SysAutoHistory true "查询回滚记录"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /autoCode/preview [post]
+func GetSysHistory(c *gin.Context) {
+	var search request.SysAutoHistory
+	_ = c.ShouldBindJSON(&search)
+	err, list, total := service.GetSysHistoryPage(search.PageInfo)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Any("err", err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     search.Page,
+			PageSize: search.PageSize,
+		}, "获取成功", c)
+	}
+}
+
+// @Tags AutoCode
+// @Summary 回滚
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.AutoHistoryByID true "回滚自动生成代码"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"回滚成功"}"
+// @Router /autoCode/preview [post]
+func RollBack(c *gin.Context) {
+	var id request.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
@@ -54,15 +98,18 @@ func CreateTemp(c *gin.Context) {
 		response.FailWithMessage(err.Error(), c)
 		return
 	}
+	var apiIds []uint
 	if a.AutoCreateApiToSql {
-		if err := service.AutoCreateApi(&a); err != nil {
+		if ids, err := service.AutoCreateApi(&a); err != nil {
 			global.GVA_LOG.Error("自动化创建失败!请自行清空垃圾数据!", zap.Any("err", err))
 			c.Writer.Header().Add("success", "false")
 			c.Writer.Header().Add("msg", url.QueryEscape("自动化创建失败!请自行清空垃圾数据!"))
 			return
+		} else {
+			apiIds = ids
 		}
 	}
-	err := service.CreateTemp(a)
+	err := service.CreateTemp(a, apiIds...)
 	if err != nil {
 		if errors.Is(err, model.AutoMoveErr) {
 			c.Writer.Header().Add("success", "false")

+ 8 - 0
server/model/request/sys_autocode.go

@@ -1,5 +1,13 @@
 package request
 
+type SysAutoHistory struct {
+	PageInfo
+}
+
+type AutoHistoryByID struct {
+	ID uint `json:"id"`
+}
+
 type DBReq struct {
 	Database string `json:"database" gorm:"column:database"`
 }

+ 1 - 1
server/model/sys_auto_code.go

@@ -7,7 +7,7 @@ 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

+ 14 - 0
server/model/sys_autocode_history.go

@@ -0,0 +1,14 @@
+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;
+	ApiIDs        string // api表注册内容
+	Flag          int    // 表示对应状态 0 代表创建, 1 代表回滚 ...
+}

+ 2 - 0
server/router/sys_auto_code.go

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

+ 4 - 0
server/service/sys_api.go

@@ -141,3 +141,7 @@ func DeleteApisByIds(ids request.IdsReq) (err error) {
 	err = global.GVA_DB.Delete(&[]model.SysApi{}, "id in ?", ids.Ids).Error
 	return err
 }
+
+func DeleteApiByIds(ids []string) (err error) {
+	return global.GVA_DB.Delete(model.SysApi{}, ids).Error
+}

+ 59 - 10
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"
@@ -9,6 +10,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"text/template"
 
@@ -98,7 +100,7 @@ func PreviewTemp(autoCode model.AutoCodeStruct) (map[string]string, error) {
 //@param: model.AutoCodeStruct
 //@return: err error
 
-func CreateTemp(autoCode model.AutoCodeStruct) (err error) {
+func CreateTemp(autoCode model.AutoCodeStruct, ids ...uint) (err error) {
 	dataList, fileList, needMkdir, err := getNeedList(&autoCode)
 	if err != nil {
 		return err
@@ -125,6 +127,13 @@ func CreateTemp(autoCode model.AutoCodeStruct) (err error) {
 			return
 		}
 	}()
+	bf := strings.Builder{}
+	idBf := strings.Builder{}
+	injectionCodeMeta := strings.Builder{}
+	for _, id := range ids {
+		idBf.WriteString(strconv.Itoa(int(id)))
+		idBf.WriteString(";")
+	}
 	if autoCode.AutoMoveFile { // 判断是否需要自动转移
 		for index, _ := range dataList {
 			addAutoMoveFile(&dataList[index])
@@ -146,18 +155,51 @@ func CreateTemp(autoCode model.AutoCodeStruct) (err error) {
 		if err != nil {
 			return err
 		}
+
+		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)"))
+
+		// 保存生成信息
+		for _, data := range dataList {
+			if len(data.autoMoveFilePath) != 0 {
+				bf.WriteString(data.autoMoveFilePath)
+				bf.WriteString(";")
+			}
+		}
+
 		if global.GVA_CONFIG.AutoCode.TransferRestart {
 			go func() {
 				_ = utils.Reload()
 			}()
 		}
-		return errors.New("创建代码成功并移动文件成功")
+		//return errors.New("创建代码成功并移动文件成功")
 	} else { // 打包
-		if err := utils.ZipFiles("./ginvueadmin.zip", fileList, ".", "."); err != nil {
+		if err = utils.ZipFiles("./ginvueadmin.zip", fileList, ".", "."); err != nil {
 			return err
 		}
 	}
+	if autoCode.TableName != "" {
+		err = CreateAutoCodeHistory(bf.String(),
+			injectionCodeMeta.String(),
+			autoCode.TableName,
+			idBf.String(),
+		)
+	} else {
+		err = CreateAutoCodeHistory(bf.String(),
+			injectionCodeMeta.String(),
+			autoCode.StructName,
+			idBf.String(),
+		)
+	}
+	if err != nil {
+		return err
+	}
+	if autoCode.AutoMoveFile {
+		return errors.New("创建代码成功并移动文件成功")
+	}
 	return nil
+
 }
 
 //@author: [piexlmax](https://github.com/piexlmax)
@@ -215,6 +257,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
@@ -267,7 +313,7 @@ func addAutoMoveFile(data *tplData) {
 //@param: a *model.AutoCodeStruct
 //@return: err error
 
-func AutoCreateApi(a *model.AutoCodeStruct) (err error) {
+func AutoCreateApi(a *model.AutoCodeStruct) (ids []uint, err error) {
 	var apiList = []model.SysApi{
 		{
 			Path:        "/" + a.Abbreviation + "/" + "create" + a.StructName,
@@ -307,17 +353,20 @@ func AutoCreateApi(a *model.AutoCodeStruct) (err error) {
 		},
 	}
 	err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
+
 		for _, v := range apiList {
 			var api model.SysApi
 			if errors.Is(tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error, gorm.ErrRecordNotFound) {
-				if err := tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
+				if err = tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
 					return err
+				} else {
+					ids = append(ids, v.ID)
 				}
 			}
 		}
 		return nil
 	})
-	return err
+	return ids, err
 }
 
 func getNeedList(autoCode *model.AutoCodeStruct) (dataList []tplData, fileList []string, needMkdir []string, err error) {
@@ -361,10 +410,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,

+ 75 - 0
server/service/sys_autocode_history.go

@@ -0,0 +1,75 @@
+package service
+
+import (
+	"errors"
+	"gin-vue-admin/global"
+	"gin-vue-admin/model"
+	"gin-vue-admin/model/request"
+	"gin-vue-admin/utils"
+	"strings"
+
+	"go.uber.org/zap"
+)
+
+// CreateAutoCodeHistory RouterPath : RouterPath@RouterString;RouterPath2@RouterString2
+func CreateAutoCodeHistory(autoCodeMeta string, injectionMeta string, tableName string, apiIds string) error {
+	return global.GVA_DB.Create(&model.SysAutoCodeHistory{
+		AutoCodeMeta:  autoCodeMeta,
+		InjectionMeta: injectionMeta,
+		TableName:     tableName,
+		ApiIDs:        apiIds,
+	}).Error
+}
+
+// RollBack 回滚
+func RollBack(id uint) error {
+	md := model.SysAutoCodeHistory{}
+	if err := global.GVA_DB.First(&md, id).Error; err != nil {
+		return err
+	}
+	// 清除API表
+	err := DeleteApiByIds(strings.Split(md.ApiIDs, ";"))
+	if err != nil {
+		global.GVA_LOG.Error("ClearTag DeleteApiByIds:", zap.Error(err))
+	}
+	// 获取全部表名
+	err, dbNames := GetTables(global.GVA_CONFIG.Mysql.Dbname)
+	if err != nil {
+		global.GVA_LOG.Error("ClearTag GetTables:", zap.Error(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
+}
+
+func GetSysHistoryPage(info request.PageInfo) (err error, list interface{}, total int64) {
+	limit := info.PageSize
+	offset := info.PageSize * (info.Page - 1)
+	db := global.GVA_DB
+	var fileLists []model.SysAutoCodeHistory
+	err = db.Find(&fileLists).Count(&total).Error
+	err = db.Limit(limit).Offset(offset).Order("updated_at desc").Find(&fileLists).Error
+	return err, fileLists, total
+}

+ 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("未找到内容")
+}