diff --git a/server/api/v1/sys_auto_code.go b/server/api/v1/sys_auto_code.go index 09ac2418..e48a2fbe 100644 --- a/server/api/v1/sys_auto_code.go +++ b/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,89 @@ import ( "go.uber.org/zap" ) +// @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/delSysHistory [post] +func DelSysHistory(c *gin.Context) { + var id request.AutoHistoryByID + _ = c.ShouldBindJSON(&id) + err := service.DeletePage(id.ID) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Any("err", err)) + response.FailWithMessage("获取失败", c) + } + response.OkWithMessage("删除成功", c) + +} + +// @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/getSysHistory [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/rollback [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 +// @accept application/json +// @Produce application/json +// @Param data body request.AutoHistoryByID true "获取meta信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /autoCode/getMeta [post] +func GetMeta(c *gin.Context) { + var id request.AutoHistoryByID + _ = c.ShouldBindJSON(&id) + if v, err := service.GetMeta(id.ID); err != nil { + response.FailWithMessage(err.Error(), c) + return + } else { + response.OkWithDetailed(gin.H{"meta": v}, "获取成功", c) + } + +} + // @Tags AutoCode // @Summary 预览创建后的代码 // @Security ApiKeyAuth @@ -54,15 +138,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") diff --git a/server/global/global.go b/server/global/global.go index 829714b0..404bb8ae 100644 --- a/server/global/global.go +++ b/server/global/global.go @@ -9,7 +9,7 @@ import ( "gin-vue-admin/config" - "github.com/go-redis/redis" + "github.com/go-redis/redis/v8" "github.com/spf13/viper" "gorm.io/gorm" ) diff --git a/server/go.mod b/server/go.mod index 6eb7497d..df07108b 100644 --- a/server/go.mod +++ b/server/go.mod @@ -20,8 +20,8 @@ require ( github.com/go-openapi/swag v0.19.8 // indirect github.com/go-playground/validator/v10 v10.3.0 // indirect github.com/go-redis/redis v6.15.7+incompatible + github.com/go-redis/redis/v8 v8.11.0 github.com/go-sql-driver/mysql v1.5.0 - github.com/golang/protobuf v1.4.2 // indirect github.com/gookit/color v1.3.1 github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect github.com/jordan-wright/email v0.0.0-20200824153738-3f5bafa1cd84 @@ -31,8 +31,6 @@ require ( github.com/mailru/easyjson v0.7.1 // indirect github.com/mitchellh/mapstructure v1.2.2 // indirect github.com/mojocn/base64Captcha v1.3.1 - github.com/onsi/ginkgo v1.7.0 // indirect - github.com/onsi/gomega v1.4.3 // indirect github.com/pelletier/go-toml v1.6.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/qiniu/api.v7/v7 v7.4.1 @@ -52,10 +50,8 @@ require ( go.uber.org/zap v1.10.0 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/tools v0.0.0-20200324003944-a576cf524670 // indirect google.golang.org/protobuf v1.24.0 // indirect gopkg.in/ini.v1 v1.55.0 // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect gorm.io/driver/mysql v1.0.1 gorm.io/gorm v1.20.7 ) diff --git a/server/initialize/gorm.go b/server/initialize/gorm.go index c3d997c8..39bd7020 100644 --- a/server/initialize/gorm.go +++ b/server/initialize/gorm.go @@ -48,7 +48,7 @@ func MysqlTables(db *gorm.DB) { model.ExaSimpleUploader{}, model.ExaCustomer{}, model.SysOperationRecord{}, - + model.SysAutoCodeHistory{}, // Code generated by gin-vue-admin Begin; DO NOT EDIT. // Code generated by gin-vue-admin End; DO NOT EDIT. ) diff --git a/server/initialize/redis.go b/server/initialize/redis.go index f9571ddf..6ecc5a8e 100644 --- a/server/initialize/redis.go +++ b/server/initialize/redis.go @@ -1,8 +1,10 @@ package initialize import ( + "context" "gin-vue-admin/global" - "github.com/go-redis/redis" + + "github.com/go-redis/redis/v8" "go.uber.org/zap" ) @@ -13,7 +15,7 @@ func Redis() { Password: redisCfg.Password, // no password set DB: redisCfg.DB, // use default DB }) - pong, err := client.Ping().Result() + pong, err := client.Ping(context.Background()).Result() if err != nil { global.GVA_LOG.Error("redis connect ping failed, err:", zap.Any("err", err)) } else { diff --git a/server/model/request/sys_autocode.go b/server/model/request/sys_autocode.go index d97be350..2e64eb9a 100644 --- a/server/model/request/sys_autocode.go +++ b/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"` } diff --git a/server/model/sys_auto_code.go b/server/model/sys_auto_code.go index 9e030f5d..73b68f43 100644 --- a/server/model/sys_auto_code.go +++ b/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 diff --git a/server/model/sys_autocode_history.go b/server/model/sys_autocode_history.go new file mode 100644 index 00000000..45c5568f --- /dev/null +++ b/server/model/sys_autocode_history.go @@ -0,0 +1,18 @@ +package model + +import "gin-vue-admin/global" + +// 自动迁移代码记录,用于回滚,重放使用 + +type SysAutoCodeHistory struct { + global.GVA_MODEL + TableName string `json:"tableName"` + RequestMeta string `gorm:"type:text" json:"requestMeta,omitempty"` // 前端传入的结构化信息 + AutoCodePath string `gorm:"type:text" json:"autoCodePath,omitempty"` // 其他meta信息 path;path + InjectionMeta string `gorm:"type:text" json:"injectionMeta,omitempty"` // 注入的内容 RouterPath@functionName@RouterString; + StructName string `json:"structName"` + StructCNName string `json:"structCNName"` + ApiIDs string `json:"apiIDs,omitempty"` // api表注册内容 + Flag int `json:"flag"` // 表示对应状态 0 代表创建, 1 代表回滚 ... + +} diff --git a/server/router/sys_auto_code.go b/server/router/sys_auto_code.go index 723471d0..ee0f02f0 100644 --- a/server/router/sys_auto_code.go +++ b/server/router/sys_auto_code.go @@ -8,10 +8,14 @@ import ( func InitAutoCodeRouter(Router *gin.RouterGroup) { AutoCodeRouter := Router.Group("autoCode") { - AutoCodeRouter.POST("preview", v1.PreviewTemp) // 获取自动创建代码预览 - AutoCodeRouter.POST("createTemp", v1.CreateTemp) // 创建自动化代码 - AutoCodeRouter.GET("getTables", v1.GetTables) // 获取对应数据库的表 - AutoCodeRouter.GET("getDB", v1.GetDB) // 获取数据库 - AutoCodeRouter.GET("getColumn", v1.GetColumn) // 获取指定表所有字段信息 + AutoCodeRouter.POST("delSysHistory", v1.DelSysHistory) // 删除回滚记录 + AutoCodeRouter.POST("getMeta", v1.GetMeta) // 根据id获取meta信息 + 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) // 获取对应数据库的表 + AutoCodeRouter.GET("getDB", v1.GetDB) // 获取数据库 + AutoCodeRouter.GET("getColumn", v1.GetColumn) // 获取指定表所有字段信息 } } diff --git a/server/service/jwt_black_list.go b/server/service/jwt_black_list.go index 507a76ff..9b242875 100644 --- a/server/service/jwt_black_list.go +++ b/server/service/jwt_black_list.go @@ -1,11 +1,13 @@ package service import ( + "context" "errors" "gin-vue-admin/global" "gin-vue-admin/model" - "gorm.io/gorm" "time" + + "gorm.io/gorm" ) //@author: [piexlmax](https://github.com/piexlmax) @@ -38,7 +40,7 @@ func IsBlacklist(jwt string) bool { //@return: err error, redisJWT string func GetRedisJWT(userName string) (err error, redisJWT string) { - redisJWT, err = global.GVA_REDIS.Get(userName).Result() + redisJWT, err = global.GVA_REDIS.Get(context.Background(), userName).Result() return err, redisJWT } @@ -51,6 +53,6 @@ func GetRedisJWT(userName string) (err error, redisJWT string) { func SetRedisJWT(jwt string, userName string) (err error) { // 此处过期时间等于jwt过期时间 timer := time.Duration(global.GVA_CONFIG.JWT.ExpiresTime) * time.Second - err = global.GVA_REDIS.Set(userName, jwt, timer).Err() + err = global.GVA_REDIS.Set(context.Background(), userName, jwt, timer).Err() return err } diff --git a/server/service/sys_api.go b/server/service/sys_api.go index 52e8682d..55ff5998 100644 --- a/server/service/sys_api.go +++ b/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 +} diff --git a/server/service/sys_auto_code.go b/server/service/sys_auto_code.go index 679120af..a4db85fc 100644 --- a/server/service/sys_auto_code.go +++ b/server/service/sys_auto_code.go @@ -1,7 +1,9 @@ package service import ( + "encoding/json" "errors" + "fmt" "gin-vue-admin/global" "gin-vue-admin/model" "gin-vue-admin/model/request" @@ -9,6 +11,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strconv" "strings" "text/template" @@ -98,11 +101,12 @@ 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 } + meta, _ := json.Marshal(autoCode) // 写入文件前,先创建文件夹 if err = utils.CreateDir(needMkdir...); err != nil { return err @@ -125,6 +129,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 +157,61 @@ 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.AutoMoveFile || autoCode.AutoCreateApiToSql { + if autoCode.TableName != "" { + err = CreateAutoCodeHistory( + string(meta), + autoCode.StructName, + autoCode.Description, + bf.String(), + injectionCodeMeta.String(), + autoCode.TableName, + idBf.String(), + ) + } else { + err = CreateAutoCodeHistory( + string(meta), + autoCode.StructName, + autoCode.Description, + 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 +269,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 +325,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 +365,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 +422,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, diff --git a/server/service/sys_autocode_history.go b/server/service/sys_autocode_history.go new file mode 100644 index 00000000..4fd94686 --- /dev/null +++ b/server/service/sys_autocode_history.go @@ -0,0 +1,87 @@ +package service + +import ( + "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(meta, structName, structCNName, autoCodePath string, injectionMeta string, tableName string, apiIds string) error { + return global.GVA_DB.Create(&model.SysAutoCodeHistory{ + RequestMeta: meta, + AutoCodePath: autoCodePath, + InjectionMeta: injectionMeta, + StructName: structName, + StructCNName: structCNName, + 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.AutoCodePath, ";") { + _ = utils.DeLFile(path) + } + // 清除注入 + for _, v := range strings.Split(md.InjectionMeta, ";") { + // RouterPath@functionName@RouterString + meta := strings.Split(v, "@") + if len(meta) == 3 { + _ = utils.AutoClearCode(meta[0], meta[2]) + } + } + md.Flag = 1 + return global.GVA_DB.Save(&md).Error +} + +func GetMeta(id uint) (string, error) { + var meta string + return meta, global.GVA_DB.Model(model.SysAutoCodeHistory{}).Select("request_meta").First(&meta, id).Error +} + +// GetSysHistoryPage 获取系统历史数据 +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").Select("id,created_at,updated_at,struct_name,struct_cn_name,flag,table_name").Find(&fileLists).Error + return err, fileLists, total +} + +// DeletePage 删除历史数据 +func DeletePage(id uint) error { + return global.GVA_DB.Delete(model.SysAutoCodeHistory{}, id).Error +} diff --git a/server/service/sys_initdb.go b/server/service/sys_initdb.go index b84a0868..22901099 100644 --- a/server/service/sys_initdb.go +++ b/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 diff --git a/server/source/api.go b/server/source/api.go index f3c1cffe..23d40f12 100644 --- a/server/source/api.go +++ b/server/source/api.go @@ -86,6 +86,10 @@ var apis = []model.SysApi{ {global.GVA_MODEL{ID: 83, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/excel/exportExcel", "导出excel", "excel", "POST"}, {global.GVA_MODEL{ID: 84, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/excel/downloadTemplate", "下载excel模板", "excel", "GET"}, {global.GVA_MODEL{ID: 85, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/api/deleteApisByIds", "批量删除api", "api", "DELETE"}, + {global.GVA_MODEL{ID: 86, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/autoCode/getSysHistory", "查询回滚记录", "autoCode", "POST"}, + {global.GVA_MODEL{ID: 87, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/autoCode/rollback", "回滚自动生成代码", "autoCode", "POST"}, + {global.GVA_MODEL{ID: 88, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/autoCode/getMeta", "获取meta信息", "autoCode", "POST"}, + {global.GVA_MODEL{ID: 89, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/autoCode/delSysHistory", "删除回滚记录", "autoCode", "POST"}, } //@author: [SliverHorn](https://github.com/SliverHorn) diff --git a/server/source/authorities_menus.go b/server/source/authorities_menus.go index 932ef0c3..0d0bc030 100644 --- a/server/source/authorities_menus.go +++ b/server/source/authorities_menus.go @@ -39,6 +39,8 @@ var authorityMenus = []AuthorityMenus{ {"888", 21}, {"888", 22}, {"888", 23}, + {"888", 24}, + {"888", 25}, {"8881", 1}, {"8881", 2}, {"8881", 8}, diff --git a/server/source/casbin.go b/server/source/casbin.go index a7b642c5..59036c24 100644 --- a/server/source/casbin.go +++ b/server/source/casbin.go @@ -86,6 +86,10 @@ var carbines = []gormadapter.CasbinRule{ {PType: "p", V0: "888", V1: "/excel/exportExcel", V2: "POST"}, {PType: "p", V0: "888", V1: "/excel/downloadTemplate", V2: "GET"}, {PType: "p", V0: "888", V1: "/api/deleteApisByIds", V2: "DELETE"}, + {PType: "p", V0: "888", V1: "/autoCode/getSysHistory", V2: "POST"}, + {PType: "p", V0: "888", V1: "/autoCode/rollback", V2: "POST"}, + {PType: "p", V0: "888", V1: "/autoCode/getMeta", V2: "POST"}, + {PType: "p", V0: "888", V1: "/autoCode/delSysHistory", V2: "POST"}, {PType: "p", V0: "8881", V1: "/base/login", V2: "POST"}, {PType: "p", V0: "8881", V1: "/user/register", V2: "POST"}, {PType: "p", V0: "8881", V1: "/api/createApi", V2: "POST"}, diff --git a/server/source/menu.go b/server/source/menu.go index 673e7c2c..9d219b70 100644 --- a/server/source/menu.go +++ b/server/source/menu.go @@ -37,6 +37,8 @@ var menus = []model.SysBaseMenu{ {GVA_MODEL: global.GVA_MODEL{ID: 21, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "9", Path: "simpleUploader", Name: "simpleUploader", Component: "view/example/simpleUploader/simpleUploader", Sort: 6, Meta: model.Meta{Title: "断点续传(插件版)", Icon: "upload"}}, {GVA_MODEL: global.GVA_MODEL{ID: 22, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Hidden: false, Component: "/", Sort: 0, Meta: model.Meta{Title: "官方网站", Icon: "s-home"}}, {GVA_MODEL: global.GVA_MODEL{ID: 23, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "state", Name: "state", Hidden: false, Component: "view/system/state.vue", Sort: 6, Meta: model.Meta{Title: "服务器状态", Icon: "cloudy"}}, + {GVA_MODEL: global.GVA_MODEL{ID: 24, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "14", Path: "autoCodeAdmin", Name: "autoCodeAdmin", Hidden: false, Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 1, Meta: model.Meta{Title: "自动化代码管理", Icon: "s-finance"}}, + {GVA_MODEL: global.GVA_MODEL{ID: 25, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "14", Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Hidden: true, Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: model.Meta{Title: "自动化代码(复用)", Icon: "s-finance"}}, } //@author: [SliverHorn](https://github.com/SliverHorn) diff --git a/server/utils/file_operations.go b/server/utils/file_operations.go index 36115c69..0e747773 100644 --- a/server/utils/file_operations.go +++ b/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: 去除结构体空格 diff --git a/server/utils/injectionCode.go b/server/utils/injectionCode.go index 5bc66c4f..5b12f11d 100644 --- a/server/utils/injectionCode.go +++ b/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("未找到内容") +} diff --git a/web/Dockerfile b/web/Dockerfile index d616262d..b40db4ac 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -3,8 +3,7 @@ FROM node:12.16.1 WORKDIR /gva_web/ COPY . . -RUN npm install -g cnpm --registry=https://registry.npm.taobao.org -RUN cnpm install || npm install +RUN npm install RUN npm run build FROM nginx:alpine diff --git a/web/public/index.html b/web/public/index.html index 75281b94..20222d92 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -8,7 +8,7 @@ <%=htmlWebpackPlugin.options.title%> - <% if(process.env.NODE_ENV!=='development'){ %> + <% if(process.env.NODE_ENV!=='development' && htmlWebpackPlugin.options.cdns){ %> <% htmlWebpackPlugin.options.cdns.forEach(function(item){ if(item.js){ %> <% } }) %> diff --git a/web/src/api/autoCode.js b/web/src/api/autoCode.js index ad7f40bd..fc732919 100644 --- a/web/src/api/autoCode.js +++ b/web/src/api/autoCode.js @@ -60,3 +60,35 @@ export const getColumn = (params) => { params }) } + +export const getSysHistory = (data) => { + return service({ + url: '/autoCode/getSysHistory', + method: 'post', + data + }) +} + +export const rollback = (data) => { + return service({ + url: '/autoCode/rollback', + method: 'post', + data + }) +} + +export const getMeta = (data) => { + return service({ + url: '/autoCode/getMeta', + method: 'post', + data + }) +} + +export const delSysHistory = (data) => { + return service({ + url: '/autoCode/delSysHistory', + method: 'post', + data + }) +} diff --git a/web/src/view/systemTools/autoCode/index.vue b/web/src/view/systemTools/autoCode/index.vue index 3bc55e50..f02059b8 100644 --- a/web/src/view/systemTools/autoCode/index.vue +++ b/web/src/view/systemTools/autoCode/index.vue @@ -165,7 +165,7 @@ const fieldTemplate = { import FieldDialog from '@/view/systemTools/autoCode/component/fieldDialog.vue' import PreviewCodeDialg from '@/view/systemTools/autoCode/component/previewCodeDialg.vue' import { toUpperCase, toHump, toSQLLine } from '@/utils/stringFun' -import { createTemp, getDB, getTable, getColumn, preview } from '@/api/autoCode' +import { createTemp, getDB, getTable, getColumn, preview, getMeta } from '@/api/autoCode' import { getDict } from '@/utils/dictionary' export default { @@ -223,6 +223,10 @@ export default { created() { this.getDb() this.setFdMap() + const id = this.$route.params.id + if (id) { + this.getAutoCodeJson(id) + } }, methods: { editAndAddField(item) { @@ -305,7 +309,6 @@ export default { return false } this.form.humpPackageName = toSQLLine(this.form.packageName) - debugger if (isPreview) { const data = await preview(this.form) this.preViewCode = data.data.autoCode @@ -396,6 +399,12 @@ export default { this.fdMap[item.label] = fdtype }) }) + }, + async getAutoCodeJson(id) { + const res = await getMeta({ id: Number(id) }) + if (res.code === 0) { + this.form = JSON.parse(res.data.meta) + } } } } diff --git a/web/src/view/systemTools/autoCodeAdmin/index.vue b/web/src/view/systemTools/autoCodeAdmin/index.vue new file mode 100644 index 00000000..4f0345ae --- /dev/null +++ b/web/src/view/systemTools/autoCodeAdmin/index.vue @@ -0,0 +1,159 @@ + + + + +