This commit is contained in:
2023-11-02 04:34:46 +08:00
commit c4548fe498
369 changed files with 40208 additions and 0 deletions

47
utils/ast/ast.go Normal file
View File

@@ -0,0 +1,47 @@
package ast
import (
"fmt"
"go/ast"
"go/token"
)
// 增加 import 方法
func AddImport(astNode ast.Node, imp string) {
impStr := fmt.Sprintf("\"%s\"", imp)
ast.Inspect(astNode, func(node ast.Node) bool {
if genDecl, ok := node.(*ast.GenDecl); ok {
if genDecl.Tok == token.IMPORT {
for i := range genDecl.Specs {
if impNode, ok := genDecl.Specs[i].(*ast.ImportSpec); ok {
if impNode.Path.Value == impStr {
return false
}
}
}
genDecl.Specs = append(genDecl.Specs, &ast.ImportSpec{
Path: &ast.BasicLit{
Kind: token.STRING,
Value: impStr,
},
})
}
}
return true
})
}
// 查询特定function方法
func FindFunction(astNode ast.Node, FunctionName string) *ast.FuncDecl {
var funcDeclP *ast.FuncDecl
ast.Inspect(astNode, func(node ast.Node) bool {
if funcDecl, ok := node.(*ast.FuncDecl); ok {
if funcDecl.Name.String() == FunctionName {
funcDeclP = funcDecl
return false
}
}
return true
})
return funcDeclP
}

View File

@@ -0,0 +1,47 @@
package ast
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"os"
)
func ImportForAutoEnter(path string, funcName string, code string) {
src, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, 0)
ast.Inspect(astFile, func(node ast.Node) bool {
if typeSpec, ok := node.(*ast.TypeSpec); ok {
if typeSpec.Name.Name == funcName {
if st, ok := typeSpec.Type.(*ast.StructType); ok {
for i := range st.Fields.List {
if t, ok := st.Fields.List[i].Type.(*ast.Ident); ok {
if t.Name == code {
return false
}
}
}
sn := &ast.Field{
Type: &ast.Ident{Name: code},
}
st.Fields.List = append(st.Fields.List, sn)
}
}
}
return true
})
var out []byte
bf := bytes.NewBuffer(out)
err = printer.Fprint(bf, fileSet, astFile)
if err != nil {
return
}
_ = os.WriteFile(path, bf.Bytes(), 0666)
}

View File

@@ -0,0 +1,7 @@
package ast
import "testing"
func TestImportForAutoEnter(t *testing.T) {
ImportForAutoEnter("D:\\gin-vue-admin\\server.exe.exe\\api\\v1\\test\\enter.go", "ApiGroup", "test")
}

181
utils/ast/ast_enter.go Normal file
View File

@@ -0,0 +1,181 @@
package ast
import (
"bytes"
"go/ast"
"go/format"
"go/parser"
"go/token"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"log"
"os"
"strconv"
"strings"
)
type Visitor struct {
ImportCode string
StructName string
PackageName string
GroupName string
}
func (vi *Visitor) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
case *ast.GenDecl:
// 查找有没有import context包
// Notice没有考虑没有import任何包的情况
if n.Tok == token.IMPORT && vi.ImportCode != "" {
vi.addImport(n)
// 不需要再遍历子树
return nil
}
if n.Tok == token.TYPE && vi.StructName != "" && vi.PackageName != "" && vi.GroupName != "" {
vi.addStruct(n)
return nil
}
case *ast.FuncDecl:
if n.Name.Name == "Routers" {
vi.addFuncBodyVar(n)
return nil
}
}
return vi
}
func (vi *Visitor) addStruct(genDecl *ast.GenDecl) ast.Visitor {
for i := range genDecl.Specs {
switch n := genDecl.Specs[i].(type) {
case *ast.TypeSpec:
if strings.Index(n.Name.Name, "Group") > -1 {
switch t := n.Type.(type) {
case *ast.StructType:
f := &ast.Field{
Names: []*ast.Ident{
{
Name: vi.StructName,
Obj: &ast.Object{
Kind: ast.Var,
Name: vi.StructName,
},
},
},
Type: &ast.SelectorExpr{
X: &ast.Ident{
Name: vi.PackageName,
},
Sel: &ast.Ident{
Name: vi.GroupName,
},
},
}
t.Fields.List = append(t.Fields.List, f)
}
}
}
}
return vi
}
func (vi *Visitor) addImport(genDecl *ast.GenDecl) ast.Visitor {
// 是否已经import
hasImported := false
for _, v := range genDecl.Specs {
importSpec := v.(*ast.ImportSpec)
// 如果已经包含
if importSpec.Path.Value == strconv.Quote(vi.ImportCode) {
hasImported = true
}
}
if !hasImported {
genDecl.Specs = append(genDecl.Specs, &ast.ImportSpec{
Path: &ast.BasicLit{
Kind: token.STRING,
Value: strconv.Quote(vi.ImportCode),
},
})
}
return vi
}
func (vi *Visitor) addFuncBodyVar(funDecl *ast.FuncDecl) ast.Visitor {
hasVar := false
for _, v := range funDecl.Body.List {
switch varSpec := v.(type) {
case *ast.AssignStmt:
for i := range varSpec.Lhs {
switch nn := varSpec.Lhs[i].(type) {
case *ast.Ident:
if nn.Name == vi.PackageName+"Router" {
hasVar = true
}
}
}
}
}
if !hasVar {
assignStmt := &ast.AssignStmt{
Lhs: []ast.Expr{
&ast.Ident{
Name: vi.PackageName + "Router",
Obj: &ast.Object{
Kind: ast.Var,
Name: vi.PackageName + "Router",
},
},
},
Tok: token.DEFINE,
Rhs: []ast.Expr{
&ast.SelectorExpr{
X: &ast.SelectorExpr{
X: &ast.Ident{
Name: "router",
},
Sel: &ast.Ident{
Name: "RouterGroupApp",
},
},
Sel: &ast.Ident{
Name: cases.Title(language.English).String(vi.PackageName),
},
},
},
}
funDecl.Body.List = append(funDecl.Body.List, funDecl.Body.List[1])
index := 1
copy(funDecl.Body.List[index+1:], funDecl.Body.List[index:])
funDecl.Body.List[index] = assignStmt
}
return vi
}
func ImportReference(filepath, importCode, structName, packageName, groupName string) error {
fSet := token.NewFileSet()
fParser, err := parser.ParseFile(fSet, filepath, nil, parser.ParseComments)
if err != nil {
return err
}
importCode = strings.TrimSpace(importCode)
v := &Visitor{
ImportCode: importCode,
StructName: structName,
PackageName: packageName,
GroupName: groupName,
}
if importCode == "" {
ast.Print(fSet, fParser)
}
ast.Walk(v, fParser)
var output []byte
buffer := bytes.NewBuffer(output)
err = format.Node(buffer, fSet, fParser)
if err != nil {
log.Fatal(err)
}
// 写回数据
return os.WriteFile(filepath, buffer.Bytes(), 0o600)
}

166
utils/ast/ast_gorm.go Normal file
View File

@@ -0,0 +1,166 @@
package ast
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"os"
)
// 自动为 gorm.go 注册一个自动迁移
func AddRegisterTablesAst(path, funcName, pk, varName, dbName, model string) {
modelPk := fmt.Sprintf("miniapp/model/%s", pk)
src, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, 0)
if err != nil {
fmt.Println(err)
}
AddImport(astFile, modelPk)
FuncNode := FindFunction(astFile, funcName)
if FuncNode != nil {
ast.Print(fileSet, FuncNode)
}
addDBVar(FuncNode.Body, varName, dbName)
addAutoMigrate(FuncNode.Body, varName, pk, model)
var out []byte
bf := bytes.NewBuffer(out)
printer.Fprint(bf, fileSet, astFile)
os.WriteFile(path, bf.Bytes(), 0666)
}
// 增加一个 db库变量
func addDBVar(astBody *ast.BlockStmt, varName, dbName string) {
if dbName == "" {
return
}
dbStr := fmt.Sprintf("\"%s\"", dbName)
for i := range astBody.List {
if assignStmt, ok := astBody.List[i].(*ast.AssignStmt); ok {
if ident, ok := assignStmt.Lhs[0].(*ast.Ident); ok {
if ident.Name == varName {
return
}
}
}
}
assignNode := &ast.AssignStmt{
Lhs: []ast.Expr{
&ast.Ident{
Name: varName,
},
},
Tok: token.DEFINE,
Rhs: []ast.Expr{
&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: &ast.Ident{
Name: "global",
},
Sel: &ast.Ident{
Name: "GetGlobalDBByDBName",
},
},
Args: []ast.Expr{
&ast.BasicLit{
Kind: token.STRING,
Value: dbStr,
},
},
},
},
}
astBody.List = append([]ast.Stmt{assignNode}, astBody.List...)
}
// 为db库变量增加 AutoMigrate 方法
func addAutoMigrate(astBody *ast.BlockStmt, dbname string, pk string, model string) {
if dbname == "" {
dbname = "db"
}
flag := true
ast.Inspect(astBody, func(node ast.Node) bool {
// 首先判断需要加入的方法调用语句是否存在 不存在则直接走到下方逻辑
switch n := node.(type) {
case *ast.CallExpr:
// 判断是否找到了AutoMigrate语句
if s, ok := n.Fun.(*ast.SelectorExpr); ok {
if x, ok := s.X.(*ast.Ident); ok {
if s.Sel.Name == "AutoMigrate" && x.Name == dbname {
flag = false
if !NeedAppendModel(n, pk, model) {
return false
}
// 判断已经找到了AutoMigrate语句
n.Args = append(n.Args, &ast.CompositeLit{
Type: &ast.SelectorExpr{
X: &ast.Ident{
Name: pk,
},
Sel: &ast.Ident{
Name: model,
},
},
})
return false
}
}
}
}
return true
//然后判断 pk.model是否存在 如果存在直接跳出 如果不存在 则向已经找到的方法调用语句的node里面push一条
})
if flag {
exprStmt := &ast.ExprStmt{
X: &ast.CallExpr{
Fun: &ast.SelectorExpr{
X: &ast.Ident{
Name: dbname,
},
Sel: &ast.Ident{
Name: "AutoMigrate",
},
},
Args: []ast.Expr{
&ast.CompositeLit{
Type: &ast.SelectorExpr{
X: &ast.Ident{
Name: pk,
},
Sel: &ast.Ident{
Name: model,
},
},
},
},
}}
astBody.List = append(astBody.List, exprStmt)
}
}
// 为automigrate增加实参
func NeedAppendModel(callNode ast.Node, pk string, model string) bool {
flag := true
ast.Inspect(callNode, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.SelectorExpr:
if x, ok := n.X.(*ast.Ident); ok {
if n.Sel.Name == model && x.Name == pk {
flag = false
return false
}
}
}
return true
})
return flag
}

View File

@@ -0,0 +1,18 @@
package ast
import (
"miniapp/global"
"miniapp/model/example"
"testing"
)
const A = 123
func TestAddRegisterTablesAst(t *testing.T) {
AddRegisterTablesAst("D:\\gin-vue-admin\\server.exe.exe\\utils\\ast_test.go", "Register", "test", "testDB", "testModel")
}
func Register() {
test := global.GetGlobalDBByDBName("test")
test.AutoMigrate(example.ExaFile{})
}

157
utils/ast/ast_rollback.go Normal file
View File

@@ -0,0 +1,157 @@
package ast
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"miniapp/global"
"os"
"path/filepath"
)
func RollBackAst(pk, model string) {
RollGormBack(pk, model)
RollRouterBack(pk, model)
}
func RollGormBack(pk, model string) {
// 首先分析存在多少个ttt作为调用方的node块
// 如果多个 仅仅删除对应块即可
// 如果单个 那么还需要剔除import
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm.go")
src, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, 0)
if err != nil {
fmt.Println(err)
}
var n *ast.CallExpr
var k int = -1
var pkNum = 0
ast.Inspect(astFile, func(node ast.Node) bool {
if node, ok := node.(*ast.CallExpr); ok {
for i := range node.Args {
pkOK := false
modelOK := false
ast.Inspect(node.Args[i], func(item ast.Node) bool {
if ii, ok := item.(*ast.Ident); ok {
if ii.Name == pk {
pkOK = true
pkNum++
}
if ii.Name == model {
modelOK = true
}
}
if pkOK && modelOK {
n = node
k = i
}
return true
})
}
}
return true
})
if k > 0 {
n.Args = append(append([]ast.Expr{}, n.Args[:k]...), n.Args[k+1:]...)
}
if pkNum == 1 {
var imI int = -1
var gp *ast.GenDecl
ast.Inspect(astFile, func(node ast.Node) bool {
if gen, ok := node.(*ast.GenDecl); ok {
for i := range gen.Specs {
if imspec, ok := gen.Specs[i].(*ast.ImportSpec); ok {
if imspec.Path.Value == "\"miniapp/model/"+pk+"\"" {
gp = gen
imI = i
return false
}
}
}
}
return true
})
if imI > -1 {
gp.Specs = append(append([]ast.Spec{}, gp.Specs[:imI]...), gp.Specs[imI+1:]...)
}
}
var out []byte
bf := bytes.NewBuffer(out)
printer.Fprint(bf, fileSet, astFile)
os.Remove(path)
os.WriteFile(path, bf.Bytes(), 0666)
}
func RollRouterBack(pk, model string) {
// 首先抓到所有的代码块结构 {}
// 分析结构中是否存在一个变量叫做 pk+Router
// 然后获取到代码块指针 对内部需要回滚的代码进行剔除
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router.go")
src, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, 0)
if err != nil {
fmt.Println(err)
}
var block *ast.BlockStmt
ast.Inspect(astFile, func(node ast.Node) bool {
if n, ok := node.(*ast.BlockStmt); ok {
ast.Inspect(n, func(bNode ast.Node) bool {
if in, ok := bNode.(*ast.Ident); ok {
if in.Name == pk+"Router" {
block = n
return false
}
}
return true
})
return true
}
return true
})
var k int
for i := range block.List {
if stmtNode, ok := block.List[i].(*ast.ExprStmt); ok {
ast.Inspect(stmtNode, func(node ast.Node) bool {
if n, ok := node.(*ast.Ident); ok {
if n.Name == "Init"+model+"Router" {
k = i
return false
}
}
return true
})
}
}
block.List = append(append([]ast.Stmt{}, block.List[:k]...), block.List[k+1:]...)
if len(block.List) == 1 {
// 说明这个块就没任何意义了
block.List = nil
// TODO 删除空的{}
}
var out []byte
bf := bytes.NewBuffer(out)
printer.Fprint(bf, fileSet, astFile)
os.Remove(path)
os.WriteFile(path, bf.Bytes(), 0666)
}

View File

@@ -0,0 +1,11 @@
package ast
import "testing"
func TestRollRouterBack(t *testing.T) {
RollRouterBack("ttt", "Testttt")
}
func TestRollGormBack(t *testing.T) {
RollGormBack("ttt", "Testttt")
}

132
utils/ast/ast_router.go Normal file
View File

@@ -0,0 +1,132 @@
package ast
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"os"
"strings"
)
func AppendNodeToList(stmts []ast.Stmt, stmt ast.Stmt, index int) []ast.Stmt {
return append(stmts[:index], append([]ast.Stmt{stmt}, stmts[index:]...)...)
}
func AddRouterCode(path, funcName, pk, model string) {
src, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, parser.ParseComments)
if err != nil {
fmt.Println(err)
}
FuncNode := FindFunction(astFile, funcName)
pkName := strings.ToUpper(pk[:1]) + pk[1:]
routerName := fmt.Sprintf("%sRouter", pk)
modelName := fmt.Sprintf("Init%sRouter", model)
var bloctPre *ast.BlockStmt
for i := len(FuncNode.Body.List) - 1; i >= 0; i-- {
if block, ok := FuncNode.Body.List[i].(*ast.BlockStmt); ok {
bloctPre = block
}
}
ast.Print(fileSet, FuncNode)
if ok, b := needAppendRouter(FuncNode, pk); ok {
routerNode :=
&ast.BlockStmt{
List: []ast.Stmt{
&ast.AssignStmt{
Lhs: []ast.Expr{
&ast.Ident{Name: routerName},
},
Tok: token.DEFINE,
Rhs: []ast.Expr{
&ast.SelectorExpr{
X: &ast.SelectorExpr{
X: &ast.Ident{Name: "router"},
Sel: &ast.Ident{Name: "RouterGroupApp"},
},
Sel: &ast.Ident{Name: pkName},
},
},
},
},
}
FuncNode.Body.List = AppendNodeToList(FuncNode.Body.List, routerNode, len(FuncNode.Body.List)-2)
bloctPre = routerNode
} else {
bloctPre = b
}
if needAppendInit(FuncNode, routerName, modelName) {
bloctPre.List = append(bloctPre.List,
&ast.ExprStmt{
X: &ast.CallExpr{
Fun: &ast.SelectorExpr{
X: &ast.Ident{Name: routerName},
Sel: &ast.Ident{Name: modelName},
},
Args: []ast.Expr{
&ast.Ident{
Name: "PrivateGroup",
},
},
},
})
}
var out []byte
bf := bytes.NewBuffer(out)
printer.Fprint(bf, fileSet, astFile)
os.WriteFile(path, bf.Bytes(), 0666)
}
func needAppendRouter(funcNode ast.Node, pk string) (bool, *ast.BlockStmt) {
flag := true
var block *ast.BlockStmt
ast.Inspect(funcNode, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.BlockStmt:
for i := range n.List {
if assignNode, ok := n.List[i].(*ast.AssignStmt); ok {
if identNode, ok := assignNode.Lhs[0].(*ast.Ident); ok {
if identNode.Name == fmt.Sprintf("%sRouter", pk) {
flag = false
block = n
return false
}
}
}
}
}
return true
})
return flag, block
}
func needAppendInit(funcNode ast.Node, routerName string, modelName string) bool {
flag := true
ast.Inspect(funcNode, func(node ast.Node) bool {
switch n := funcNode.(type) {
case *ast.CallExpr:
if selectNode, ok := n.Fun.(*ast.SelectorExpr); ok {
x, xok := selectNode.X.(*ast.Ident)
if xok && x.Name == routerName && selectNode.Sel.Name == modelName {
flag = false
return false
}
}
}
return true
})
return flag
}

View File

@@ -0,0 +1,9 @@
package ast
import (
"testing"
)
func TestAddRouterCode(t *testing.T) {
AddRouterCode("D:\\gin-vue-admin\\server.exe.exe\\utils\\ast\\ast_router_test.go", "Routers", "testRouter", "GVAStruct")
}