未验证 提交 2d8e2ae8 编写于 作者: Mr.奇淼('s avatar Mr.奇淼( 提交者: GitHub

Merge pull request #585 from flipped-aurora/gva_gormv2_dev

新增自动化代码回滚功能
......@@ -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")
......
......@@ -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"
)
......
......@@ -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
)
......@@ -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.
)
......
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 {
......
package request
type SysAutoHistory struct {
PageInfo
}
type AutoHistoryByID struct {
ID uint `json:"id"`
}
type DBReq struct {
Database string `json:"database" gorm:"column:database"`
}
......
......@@ -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
......
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 // 表示对应状态 0 代表创建, 1 代表回滚 ...
}
......@@ -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) // 获取指定表所有字段信息
}
}
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
}
......@@ -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
}
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,
......
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
}
......@@ -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
......
......@@ -86,6 +86,9 @@ 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"},
}
//@author: [SliverHorn](https://github.com/SliverHorn)
......
......@@ -39,6 +39,8 @@ var authorityMenus = []AuthorityMenus{
{"888", 21},
{"888", 22},
{"888", 23},
{"888", 24},
{"888", 25},
{"8881", 1},
{"8881", 2},
{"8881", 8},
......
......@@ -86,6 +86,9 @@ 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: "8881", V1: "/base/login", V2: "POST"},
{PType: "p", V0: "8881", V1: "/user/register", V2: "POST"},
{PType: "p", V0: "8881", V1: "/api/createApi", V2: "POST"},
......
......@@ -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/autoCodeAdmin/index.vue", Sort: 0, Meta: model.Meta{Title: "自动化代码(复用)", Icon: "s-finance"}},
}
//@author: [SliverHorn](https://github.com/SliverHorn)
......
......@@ -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: 去除结构体空格
......
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("未找到内容")
}
......@@ -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
......
......@@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%=htmlWebpackPlugin.options.title%></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){ %>
<script type="text/javascript" src="<%= item.js %>"></script>
<% } }) %>
......
......@@ -60,3 +60,27 @@ 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
})
}
......@@ -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)
}
}
}
}
......
<template>
<div>
<div class="search-term">
<el-form :inline="true" :model="searchInfo" class="demo-form-inline">
<el-form-item label="表名">
<el-input v-model="searchInfo.tableName" placeholder="表名" />
</el-form-item>
<el-form-item label="结构体名称">
<el-input v-model="searchInfo.structName" placeholder="结构体名称" />
</el-form-item>
<el-form-item>
<el-button size="mini" type="primary" icon="el-icon-plus" @click="goAutoCode(null)">新增</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData" border stripe>
<el-table-column
type="selection"
width="55"
/>
<el-table-column label="id" width="60" prop="ID" />
<el-table-column label="日期" width="180">
<template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
</el-table-column>
<el-table-column label="结构体名" min-width="150" prop="structName" />
<el-table-column label="结构体描述" min-width="150" prop="structCNName" />
<el-table-column label="表名称" min-width="150" prop="tableName" />
<el-table-column label="回滚标记" min-width="150" prop="flag">
<template slot-scope="scope">
<el-tag
v-if="scope.row.flag"
type="danger"
size="mini"
effect="dark"
>
已回滚
</el-tag>
<el-tag
v-else
size="mini"
type="success"
effect="dark"
>
未回滚
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" min-width="180">
<template slot-scope="scope">
<div>
<el-button size="mini" type="primary" @click="rollback(scope.row)">回滚</el-button>
<el-button size="mini" type="success" @click="goAutoCode(scope.row)">复用</el-button>
<el-button size="mini" type="warning" @click="deleteRow(scope.row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="page"
:page-size="pageSize"
:page-sizes="[10, 30, 50, 100]"
:style="{float:'right',padding:'20px'}"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</template>
<script>
// 获取列表内容封装在mixins内部 getTableData方法 初始化已封装完成 条件搜索时候 请把条件安好后台定制的结构体字段 放到 this.searchInfo 中即可实现条件搜索
import { getSysHistory, rollback } from '@/api/autoCode.js'
import { formatTimeToStr } from '@/utils/date'
import infoList from '@/mixins/infoList'
export default {
name: 'Api',
filters: {
formatDate: function(time) {
if (time !== null && time !== '') {
var date = new Date(time)
return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
} else {
return ''
}
},
formatBoolean: function(bool) {
if (bool !== null) {
return bool ? '' : ''
} else {
return ''
}
}
},
mixins: [infoList],
data() {
return {
listApi: getSysHistory
}
},
created() {
this.getTableData()
},
methods: {
async rollback(row) {
this.$confirm('此操作将删除自动创建的文件和api, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
const res = await rollback({ id: Number(row.ID) })
if (res.code === 0) {
this.$message.success('回滚成功')
this.getTableData()
}
})
},
goAutoCode(row) {
if (row) {
this.$router.push({ name: 'autoCodeEdit', params: {
id: row.ID
}})
} else {
this.$router.push({ name: 'autoCode' })
}
}
}
}
</script>
<style scoped lang="scss">
.button-box {
padding: 10px 20px;
.el-button {
float: right;
}
}
.el-tag--mini {
margin-left: 5px;
}
.warning {
color: #dc143c;
}
</style>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册