后端重构

This commit is contained in:
Yvan
2026-01-14 14:34:49 +08:00
parent f5f77b32fe
commit 30842b42e5
274 changed files with 39754 additions and 1005 deletions
+423
View File
@@ -0,0 +1,423 @@
package ast
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"log"
)
// AddImport 增加 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
})
}
// FindFunction 查询特定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
}
// FindArray 查询特定数组方法
func FindArray(astNode ast.Node, identName, selectorExprName string) *ast.CompositeLit {
var assignStmt *ast.CompositeLit
ast.Inspect(astNode, func(n ast.Node) bool {
switch node := n.(type) {
case *ast.AssignStmt:
for _, expr := range node.Rhs {
if exprType, ok := expr.(*ast.CompositeLit); ok {
if arrayType, ok := exprType.Type.(*ast.ArrayType); ok {
sel, ok1 := arrayType.Elt.(*ast.SelectorExpr)
x, ok2 := sel.X.(*ast.Ident)
if ok1 && ok2 && x.Name == identName && sel.Sel.Name == selectorExprName {
assignStmt = exprType
return false
}
}
}
}
}
return true
})
return assignStmt
}
// CheckImport 检查是否存在Import
func CheckImport(file *ast.File, importPath string) bool {
for _, imp := range file.Imports {
path := imp.Path.Value[1 : len(imp.Path.Value)-1]
if path == importPath {
return true
}
}
return false
}
// ClearPosition 清除AST节点位置信息
func ClearPosition(astNode ast.Node) {
ast.Inspect(astNode, func(n ast.Node) bool {
switch node := n.(type) {
case *ast.Ident:
node.NamePos = token.NoPos
case *ast.CallExpr:
node.Lparen = token.NoPos
node.Rparen = token.NoPos
case *ast.BasicLit:
node.ValuePos = token.NoPos
case *ast.SelectorExpr:
node.Sel.NamePos = token.NoPos
case *ast.BinaryExpr:
node.OpPos = token.NoPos
case *ast.UnaryExpr:
node.OpPos = token.NoPos
case *ast.StarExpr:
node.Star = token.NoPos
}
return true
})
}
// CreateStmt 创建语句
func CreateStmt(statement string) *ast.ExprStmt {
expr, err := parser.ParseExpr(statement)
if err != nil {
log.Fatal(err)
}
ClearPosition(expr)
return &ast.ExprStmt{X: expr}
}
// IsBlockStmt 判断是否为块语句
func IsBlockStmt(node ast.Node) bool {
_, ok := node.(*ast.BlockStmt)
return ok
}
// VariableExistsInBlock 检查变量是否存在于块中
func VariableExistsInBlock(block *ast.BlockStmt, varName string) bool {
exists := false
ast.Inspect(block, func(n ast.Node) bool {
switch node := n.(type) {
case *ast.AssignStmt:
for _, expr := range node.Lhs {
if ident, ok := expr.(*ast.Ident); ok && ident.Name == varName {
exists = true
return false
}
}
}
return true
})
return exists
}
// RemoveImport 移除 import
func RemoveImport(file *ast.File, importPath string) {
impStr := fmt.Sprintf("\"%s\"", importPath)
for i := 0; i < len(file.Decls); i++ {
if genDecl, ok := file.Decls[i].(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
for j := 0; j < len(genDecl.Specs); j++ {
if impSpec, ok := genDecl.Specs[j].(*ast.ImportSpec); ok {
if impSpec.Path.Value == impStr {
genDecl.Specs = append(genDecl.Specs[:j], genDecl.Specs[j+1:]...)
if len(genDecl.Specs) == 0 {
file.Decls = append(file.Decls[:i], file.Decls[i+1:]...)
}
return
}
}
}
}
}
}
// AddStructField 添加结构体字段
func AddStructField(file *ast.File, structName, fieldName, fieldType string) {
ast.Inspect(file, func(node ast.Node) bool {
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == structName {
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
// 检查字段是否已存在
for _, field := range structType.Fields.List {
for _, name := range field.Names {
if name.Name == fieldName {
return false
}
}
}
// 添加新字段
newField := &ast.Field{
Names: []*ast.Ident{{Name: fieldName}},
Type: &ast.Ident{Name: fieldType},
}
structType.Fields.List = append(structType.Fields.List, newField)
return false
}
}
return true
})
}
// RemoveStructField 移除结构体字段
func RemoveStructField(file *ast.File, structName, fieldName string) {
ast.Inspect(file, func(node ast.Node) bool {
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == structName {
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
for i, field := range structType.Fields.List {
for _, name := range field.Names {
if name.Name == fieldName {
structType.Fields.List = append(structType.Fields.List[:i], structType.Fields.List[i+1:]...)
return false
}
}
}
}
}
return true
})
}
// AddFuncCall 在函数体中添加函数调用
func AddFuncCall(file *ast.File, funcName, callExpr string) {
funcDecl := FindFunction(file, funcName)
if funcDecl == nil || funcDecl.Body == nil {
return
}
stmt := CreateStmt(callExpr)
funcDecl.Body.List = append(funcDecl.Body.List, stmt)
}
// RemoveFuncCall 从函数体中移除函数调用
func RemoveFuncCall(file *ast.File, funcName, targetFunc string) {
funcDecl := FindFunction(file, funcName)
if funcDecl == nil || funcDecl.Body == nil {
return
}
for i := 0; i < len(funcDecl.Body.List); i++ {
if exprStmt, ok := funcDecl.Body.List[i].(*ast.ExprStmt); ok {
if callExpr, ok := exprStmt.X.(*ast.CallExpr); ok {
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
if selExpr.Sel.Name == targetFunc {
funcDecl.Body.List = append(funcDecl.Body.List[:i], funcDecl.Body.List[i+1:]...)
return
}
}
}
}
}
}
// AddAutoMigrateModel 添加 AutoMigrate 模型
func AddAutoMigrateModel(file *ast.File, modelExpr string) {
ast.Inspect(file, func(node ast.Node) bool {
if callExpr, ok := node.(*ast.CallExpr); ok {
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
if selExpr.Sel.Name == "AutoMigrate" {
// 检查是否已存在
for _, arg := range callExpr.Args {
if unaryExpr, ok := arg.(*ast.UnaryExpr); ok {
if compLit, ok := unaryExpr.X.(*ast.CompositeLit); ok {
if selType, ok := compLit.Type.(*ast.SelectorExpr); ok {
existingModel := fmt.Sprintf("&%s.%s{}", selType.X.(*ast.Ident).Name, selType.Sel.Name)
if existingModel == modelExpr {
return false
}
}
}
}
}
// 添加新模型
expr, err := parser.ParseExpr(modelExpr)
if err == nil {
ClearPosition(expr)
callExpr.Args = append(callExpr.Args, expr)
}
return false
}
}
}
return true
})
}
// RemoveAutoMigrateModel 移除 AutoMigrate 模型
func RemoveAutoMigrateModel(file *ast.File, modelName string) {
ast.Inspect(file, func(node ast.Node) bool {
if callExpr, ok := node.(*ast.CallExpr); ok {
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
if selExpr.Sel.Name == "AutoMigrate" {
for i := 0; i < len(callExpr.Args); i++ {
if unaryExpr, ok := callExpr.Args[i].(*ast.UnaryExpr); ok {
if compLit, ok := unaryExpr.X.(*ast.CompositeLit); ok {
if selType, ok := compLit.Type.(*ast.SelectorExpr); ok {
existingModel := fmt.Sprintf("%s.%s", selType.X.(*ast.Ident).Name, selType.Sel.Name)
if existingModel == modelName {
callExpr.Args = append(callExpr.Args[:i], callExpr.Args[i+1:]...)
return false
}
}
}
}
}
}
}
}
return true
})
}
// AddGormGenModel 向 GORM Gen 的 g.ApplyBasic 调用中添加新模型
// 用于自动更新 cmd/gen/main.go 文件
func AddGormGenModel(file *ast.File, tableName string) {
ast.Inspect(file, func(node ast.Node) bool {
if callExpr, ok := node.(*ast.CallExpr); ok {
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
// 查找 g.ApplyBasic 调用
if ident, ok := selExpr.X.(*ast.Ident); ok && ident.Name == "g" && selExpr.Sel.Name == "ApplyBasic" {
// 检查是否已存在该表
for _, arg := range callExpr.Args {
if call, ok := arg.(*ast.CallExpr); ok {
if sel, ok := call.Fun.(*ast.SelectorExpr); ok {
if sel.Sel.Name == "GenerateModel" && len(call.Args) > 0 {
if lit, ok := call.Args[0].(*ast.BasicLit); ok {
existingTable := lit.Value[1 : len(lit.Value)-1] // 去掉引号
if existingTable == tableName {
return false // 已存在,不需要添加
}
}
}
}
}
}
// 添加新模型: g.GenerateModel("tableName", fieldOpts...)
newArg := &ast.CallExpr{
Fun: &ast.SelectorExpr{
X: &ast.Ident{Name: "g"},
Sel: &ast.Ident{Name: "GenerateModel"},
},
Args: []ast.Expr{
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", tableName)},
&ast.Ident{Name: "fieldOpts..."},
},
}
callExpr.Args = append(callExpr.Args, newArg)
return false
}
}
}
return true
})
}
// RemoveGormGenModel 从 GORM Gen 的 g.ApplyBasic 调用中移除模型
func RemoveGormGenModel(file *ast.File, tableName string) {
ast.Inspect(file, func(node ast.Node) bool {
if callExpr, ok := node.(*ast.CallExpr); ok {
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
if ident, ok := selExpr.X.(*ast.Ident); ok && ident.Name == "g" && selExpr.Sel.Name == "ApplyBasic" {
newArgs := make([]ast.Expr, 0, len(callExpr.Args))
for _, arg := range callExpr.Args {
if call, ok := arg.(*ast.CallExpr); ok {
if sel, ok := call.Fun.(*ast.SelectorExpr); ok {
if sel.Sel.Name == "GenerateModel" && len(call.Args) > 0 {
if lit, ok := call.Args[0].(*ast.BasicLit); ok {
existingTable := lit.Value[1 : len(lit.Value)-1]
if existingTable == tableName {
continue // 跳过要移除的模型
}
}
}
}
}
newArgs = append(newArgs, arg)
}
callExpr.Args = newArgs
return false
}
}
}
return true
})
}
// AddGormGenModelToSlice 向 models 切片中添加新模型
// 用于处理 cmd/gen/main.go 中的 models = append(models, g.GenerateModel(...)) 模式
func AddGormGenModelToSlice(file *ast.File, tableName string) bool {
var found bool
ast.Inspect(file, func(node ast.Node) bool {
// 查找 for 循环中的 models = append(models, ...) 语句
if forStmt, ok := node.(*ast.ForStmt); ok {
ast.Inspect(forStmt.Body, func(n ast.Node) bool {
if assignStmt, ok := n.(*ast.AssignStmt); ok {
for _, rhs := range assignStmt.Rhs {
if callExpr, ok := rhs.(*ast.CallExpr); ok {
if ident, ok := callExpr.Fun.(*ast.Ident); ok && ident.Name == "append" {
// 找到 append 调用,检查是否是 models 切片
if len(callExpr.Args) >= 1 {
if argIdent, ok := callExpr.Args[0].(*ast.Ident); ok && argIdent.Name == "models" {
found = true
return false
}
}
}
}
}
}
return true
})
}
return true
})
return found
}
// CheckGormGenModelExists 检查 GORM Gen 模型是否已存在
func CheckGormGenModelExists(file *ast.File, tableName string) bool {
var exists bool
ast.Inspect(file, func(node ast.Node) bool {
if callExpr, ok := node.(*ast.CallExpr); ok {
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
if selExpr.Sel.Name == "GenerateModel" && len(callExpr.Args) > 0 {
if lit, ok := callExpr.Args[0].(*ast.BasicLit); ok {
existingTable := lit.Value[1 : len(lit.Value)-1]
if existingTable == tableName {
exists = true
return false
}
}
}
}
}
return true
})
return exists
}
+518
View File
@@ -0,0 +1,518 @@
package ast
import (
"go/ast"
"go/parser"
"go/token"
"strings"
"testing"
)
// TestAddImport 测试添加导入
func TestAddImport(t *testing.T) {
testCode := `package main
import "fmt"
func main() {}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 添加新导入
AddImport(file, "kra/internal/biz/article")
// 验证导入是否添加
var found bool
ast.Inspect(file, func(node ast.Node) bool {
if genDecl, ok := node.(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
for _, spec := range genDecl.Specs {
if impSpec, ok := spec.(*ast.ImportSpec); ok {
if impSpec.Path.Value == "\"kra/internal/biz/article\"" {
found = true
return false
}
}
}
}
return true
})
if !found {
t.Error("应该添加 kra/internal/biz/article 导入")
}
}
// TestAddImport_Duplicate 测试重复添加导入
func TestAddImport_Duplicate(t *testing.T) {
testCode := `package main
import (
"fmt"
"kra/internal/biz/article"
)
func main() {}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 统计初始导入数量
initialCount := 0
ast.Inspect(file, func(node ast.Node) bool {
if genDecl, ok := node.(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
initialCount = len(genDecl.Specs)
return false
}
return true
})
// 尝试添加已存在的导入
AddImport(file, "kra/internal/biz/article")
// 验证导入数量没有增加
finalCount := 0
ast.Inspect(file, func(node ast.Node) bool {
if genDecl, ok := node.(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
finalCount = len(genDecl.Specs)
return false
}
return true
})
if finalCount != initialCount {
t.Errorf("重复添加导入后数量应保持 %d, 实际为 %d", initialCount, finalCount)
}
}
// TestRemoveImport 测试移除导入
func TestRemoveImport(t *testing.T) {
testCode := `package main
import (
"fmt"
"kra/internal/biz/article"
)
func main() {}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 移除导入
RemoveImport(file, "kra/internal/biz/article")
// 验证导入是否移除
var found bool
ast.Inspect(file, func(node ast.Node) bool {
if genDecl, ok := node.(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
for _, spec := range genDecl.Specs {
if impSpec, ok := spec.(*ast.ImportSpec); ok {
if impSpec.Path.Value == "\"kra/internal/biz/article\"" {
found = true
return false
}
}
}
}
return true
})
if found {
t.Error("应该移除 kra/internal/biz/article 导入")
}
}
// TestAddStructField 测试添加结构体字段
func TestAddStructField(t *testing.T) {
testCode := `package main
type MyStruct struct {
Name string
}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 添加新字段
AddStructField(file, "MyStruct", "Age", "int")
// 验证字段是否添加
var found bool
ast.Inspect(file, func(node ast.Node) bool {
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == "MyStruct" {
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
for _, field := range structType.Fields.List {
for _, name := range field.Names {
if name.Name == "Age" {
found = true
return false
}
}
}
}
}
return true
})
if !found {
t.Error("应该添加 Age 字段")
}
}
// TestAddStructField_Duplicate 测试重复添加结构体字段
func TestAddStructField_Duplicate(t *testing.T) {
testCode := `package main
type MyStruct struct {
Name string
Age int
}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 统计初始字段数量
initialCount := 0
ast.Inspect(file, func(node ast.Node) bool {
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == "MyStruct" {
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
initialCount = len(structType.Fields.List)
return false
}
}
return true
})
// 尝试添加已存在的字段
AddStructField(file, "MyStruct", "Age", "int")
// 验证字段数量没有增加
finalCount := 0
ast.Inspect(file, func(node ast.Node) bool {
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == "MyStruct" {
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
finalCount = len(structType.Fields.List)
return false
}
}
return true
})
if finalCount != initialCount {
t.Errorf("重复添加字段后数量应保持 %d, 实际为 %d", initialCount, finalCount)
}
}
// TestRemoveStructField 测试移除结构体字段
func TestRemoveStructField(t *testing.T) {
testCode := `package main
type MyStruct struct {
Name string
Age int
}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 移除字段
RemoveStructField(file, "MyStruct", "Age")
// 验证字段是否移除
var found bool
ast.Inspect(file, func(node ast.Node) bool {
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == "MyStruct" {
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
for _, field := range structType.Fields.List {
for _, name := range field.Names {
if name.Name == "Age" {
found = true
return false
}
}
}
}
}
return true
})
if found {
t.Error("应该移除 Age 字段")
}
}
// TestFindFunction 测试查找函数
func TestFindFunction(t *testing.T) {
testCode := `package main
func Hello() {}
func World() {}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 查找存在的函数
funcDecl := FindFunction(file, "Hello")
if funcDecl == nil {
t.Error("应该找到 Hello 函数")
}
// 查找不存在的函数
funcDecl = FindFunction(file, "NotExist")
if funcDecl != nil {
t.Error("不应该找到 NotExist 函数")
}
}
// TestCheckImport 测试检查导入是否存在
func TestCheckImport(t *testing.T) {
testCode := `package main
import (
"fmt"
"kra/internal/biz/article"
)
func main() {}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 检查存在的导入
if !CheckImport(file, "fmt") {
t.Error("应该找到 fmt 导入")
}
// 检查存在的导入(完整路径)
if !CheckImport(file, "kra/internal/biz/article") {
t.Error("应该找到 kra/internal/biz/article 导入")
}
// 检查不存在的导入
if CheckImport(file, "not/exist") {
t.Error("不应该找到 not/exist 导入")
}
}
// TestAddFuncCall 测试在函数体中添加函数调用
func TestAddFuncCall(t *testing.T) {
testCode := `package main
func initRouter() {
fmt.Println("init")
}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 添加函数调用
AddFuncCall(file, "initRouter", "router.Article.InitArticleRouter(group)")
// 验证函数调用是否添加
funcDecl := FindFunction(file, "initRouter")
if funcDecl == nil || funcDecl.Body == nil {
t.Fatal("找不到 initRouter 函数")
}
// 检查函数体中是否包含新的调用
if len(funcDecl.Body.List) < 2 {
t.Error("应该添加新的函数调用")
}
}
// TestRemoveFuncCall 测试从函数体中移除函数调用
func TestRemoveFuncCall(t *testing.T) {
testCode := `package main
func initRouter() {
fmt.Println("init")
router.Article.InitArticleRouter(group)
}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 移除函数调用
RemoveFuncCall(file, "initRouter", "InitArticleRouter")
// 验证函数调用是否移除
funcDecl := FindFunction(file, "initRouter")
if funcDecl == nil || funcDecl.Body == nil {
t.Fatal("找不到 initRouter 函数")
}
// 检查函数体中是否还包含该调用
for _, stmt := range funcDecl.Body.List {
if exprStmt, ok := stmt.(*ast.ExprStmt); ok {
if callExpr, ok := exprStmt.X.(*ast.CallExpr); ok {
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
if selExpr.Sel.Name == "InitArticleRouter" {
t.Error("应该移除 InitArticleRouter 调用")
}
}
}
}
}
}
// TestCreateStmt 测试创建语句
func TestCreateStmt(t *testing.T) {
stmt := CreateStmt("fmt.Println(\"hello\")")
if stmt == nil {
t.Error("应该创建语句")
}
if stmt.X == nil {
t.Error("语句表达式不应为空")
}
}
// TestVariableExistsInBlock 测试检查变量是否存在于块中
func TestVariableExistsInBlock(t *testing.T) {
testCode := `package main
func test() {
x := 1
y := 2
}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
funcDecl := FindFunction(file, "test")
if funcDecl == nil || funcDecl.Body == nil {
t.Fatal("找不到 test 函数")
}
// 检查存在的变量
if !VariableExistsInBlock(funcDecl.Body, "x") {
t.Error("应该找到变量 x")
}
// 检查不存在的变量
if VariableExistsInBlock(funcDecl.Body, "z") {
t.Error("不应该找到变量 z")
}
}
// TestBase_RelativePath 测试相对路径转换
func TestBase_RelativePath(t *testing.T) {
base := &Base{
AutoCodeRoot: "/home/user/project",
AutoCodeServer: "server",
}
testCases := []struct {
input string
expected string
}{
{"/home/user/project/server/internal/biz/article.go", "internal/biz/article.go"},
{"/other/path/file.go", "/other/path/file.go"},
}
for _, tc := range testCases {
result := base.RelativePath(tc.input)
// 由于路径分隔符可能不同,只检查是否包含关键部分
if !strings.Contains(result, "article.go") && tc.input != "/other/path/file.go" {
t.Errorf("RelativePath(%s) 结果不正确: %s", tc.input, result)
}
}
}
// TestAddAutoMigrateModel 测试添加 AutoMigrate 模型
func TestAddAutoMigrateModel(t *testing.T) {
testCode := `package main
import "gorm.io/gorm"
func migrate(db *gorm.DB) {
db.AutoMigrate(&User{})
}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 添加新模型
AddAutoMigrateModel(file, "&article.Article{}")
// 验证模型是否添加
var found bool
ast.Inspect(file, func(node ast.Node) bool {
if callExpr, ok := node.(*ast.CallExpr); ok {
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
if selExpr.Sel.Name == "AutoMigrate" {
// 检查参数数量是否增加
if len(callExpr.Args) > 1 {
found = true
return false
}
}
}
}
return true
})
if !found {
t.Error("应该添加新的 AutoMigrate 模型")
}
}
// TestCheckGormGenModelExists 测试检查 GORM Gen 模型是否存在
func TestCheckGormGenModelExists(t *testing.T) {
testCode := `package main
func main() {
g.ApplyBasic(
g.GenerateModel("users"),
g.GenerateModel("articles"),
)
}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
if err != nil {
t.Fatalf("解析测试代码失败: %v", err)
}
// 检查存在的模型
if !CheckGormGenModelExists(file, "users") {
t.Error("应该找到 users 模型")
}
// 检查不存在的模型
if CheckGormGenModelExists(file, "orders") {
t.Error("不应该找到 orders 模型")
}
}
+217
View File
@@ -0,0 +1,217 @@
package ast
import (
"bytes"
"go/ast"
"go/format"
"go/parser"
"go/token"
"log"
"os"
"strconv"
"strings"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
// Visitor AST访问器
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:
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 {
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
}
// ImportReference 导入引用
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)
}
// ImportForAutoEnter 自动导入
func ImportForAutoEnter(path string, funcName string, code string) {
src, err := os.ReadFile(path)
if err != nil {
return
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, 0)
if err != nil {
return
}
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 = format.Node(bf, fileSet, astFile)
if err != nil {
return
}
_ = os.WriteFile(path, bf.Bytes(), 0666)
}
+140
View File
@@ -0,0 +1,140 @@
package ast
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"os"
)
// AddRegisterTablesAst 自动为 gorm.go 注册一个自动迁移
func AddRegisterTablesAst(path, funcName, pk, varName, dbName, model, modelImportPath string) {
src, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
return
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, 0)
if err != nil {
fmt.Println(err)
return
}
AddImport(astFile, modelImportPath)
FuncNode := FindFunction(astFile, funcName)
if FuncNode != nil {
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)
}
// addDBVar 增加一个 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...)
}
// addAutoMigrate 为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:
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
}
n.Args = append(n.Args, &ast.CompositeLit{
Type: &ast.SelectorExpr{
X: &ast.Ident{Name: pk},
Sel: &ast.Ident{Name: model},
},
})
return false
}
}
}
}
return true
})
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)
}
}
// NeedAppendModel 为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
}
+96
View File
@@ -0,0 +1,96 @@
package ast
import (
"go/ast"
"go/token"
"io"
"strings"
)
// Import 导入操作
type Import struct {
Base
ImportPath string
}
// NewImport 创建导入操作
func NewImport(importPath string) *Import {
return &Import{ImportPath: importPath}
}
func (a *Import) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
return a.Base.Parse(filename, writer)
}
func (a *Import) Rollback(file *ast.File) error {
if a.ImportPath == "" {
return nil
}
for i := 0; i < len(file.Decls); i++ {
v1, o1 := file.Decls[i].(*ast.GenDecl)
if o1 {
if v1.Tok != token.IMPORT {
break
}
for j := 0; j < len(v1.Specs); j++ {
v2, o2 := v1.Specs[j].(*ast.ImportSpec)
if o2 && strings.HasSuffix(a.ImportPath, v2.Path.Value) {
v1.Specs = append(v1.Specs[:j], v1.Specs[j+1:]...)
if len(v1.Specs) == 0 {
file.Decls = append(file.Decls[:i], file.Decls[i+1:]...)
}
break
}
}
}
}
return nil
}
func (a *Import) Injection(file *ast.File) error {
if a.ImportPath == "" {
return nil
}
var has bool
for i := 0; i < len(file.Decls); i++ {
v1, o1 := file.Decls[i].(*ast.GenDecl)
if o1 {
if v1.Tok != token.IMPORT {
break
}
for j := 0; j < len(v1.Specs); j++ {
v2, o2 := v1.Specs[j].(*ast.ImportSpec)
if o2 && strings.HasSuffix(a.ImportPath, v2.Path.Value) {
has = true
break
}
}
if !has {
spec := &ast.ImportSpec{
Path: &ast.BasicLit{Kind: token.STRING, Value: a.ImportPath},
}
v1.Specs = append(v1.Specs, spec)
return nil
}
}
}
if !has {
decls := file.Decls
file.Decls = make([]ast.Decl, 0, len(file.Decls)+1)
decl := &ast.GenDecl{
Tok: token.IMPORT,
Specs: []ast.Spec{
&ast.ImportSpec{
Path: &ast.BasicLit{Kind: token.STRING, Value: a.ImportPath},
},
},
}
file.Decls = append(file.Decls, decl)
file.Decls = append(file.Decls, decls...)
}
return nil
}
func (a *Import) Format(filename string, writer io.Writer, file *ast.File) error {
return a.Base.Format(filename, writer, file)
}
+88
View File
@@ -0,0 +1,88 @@
package ast
import (
"go/ast"
"go/format"
"go/parser"
"go/token"
"io"
"os"
"path"
"path/filepath"
"strings"
"github.com/pkg/errors"
)
// Ast AST操作接口
type Ast interface {
Parse(filename string, writer io.Writer) (file *ast.File, err error)
Rollback(file *ast.File) error
Injection(file *ast.File) error
Format(filename string, writer io.Writer, file *ast.File) error
}
// Base 基础AST操作
type Base struct {
AutoCodeRoot string
AutoCodeServer string
}
func (a *Base) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
fileSet := token.NewFileSet()
if writer != nil {
file, err = parser.ParseFile(fileSet, filename, nil, parser.ParseComments)
} else {
file, err = parser.ParseFile(fileSet, filename, writer, parser.ParseComments)
}
if err != nil {
return nil, errors.Wrapf(err, "[filepath:%s]打开/解析文件失败!", filename)
}
return file, nil
}
func (a *Base) Rollback(file *ast.File) error {
return nil
}
func (a *Base) Injection(file *ast.File) error {
return nil
}
func (a *Base) Format(filename string, writer io.Writer, file *ast.File) error {
fileSet := token.NewFileSet()
if writer == nil {
open, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, 0666)
defer open.Close()
if err != nil {
return errors.Wrapf(err, "[filepath:%s]打开文件失败!", filename)
}
writer = open
}
err := format.Node(writer, fileSet, file)
if err != nil {
return errors.Wrapf(err, "[filepath:%s]注入失败!", filename)
}
return nil
}
// RelativePath 绝对路径转相对路径
func (a *Base) RelativePath(filePath string) string {
server := filepath.Join(a.AutoCodeRoot, a.AutoCodeServer)
hasServer := strings.Index(filePath, server)
if hasServer != -1 {
filePath = strings.TrimPrefix(filePath, server)
keys := strings.Split(filePath, string(filepath.Separator))
filePath = path.Join(keys...)
}
return filePath
}
// AbsolutePath 相对路径转绝对路径
func (a *Base) AbsolutePath(filePath string) string {
server := filepath.Join(a.AutoCodeRoot, a.AutoCodeServer)
keys := strings.Split(filePath, "/")
filePath = filepath.Join(keys...)
filePath = filepath.Join(server, filePath)
return filePath
}
+135
View File
@@ -0,0 +1,135 @@
package ast
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"os"
"strings"
)
// AppendNodeToList 向语句列表中插入节点
func AppendNodeToList(stmts []ast.Stmt, stmt ast.Stmt, index int) []ast.Stmt {
return append(stmts[:index], append([]ast.Stmt{stmt}, stmts[index:]...)...)
}
// AddRouterCode 添加路由代码
func AddRouterCode(path, funcName, pk, model string) {
src, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
return
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, parser.ParseComments)
if err != nil {
fmt.Println(err)
return
}
FuncNode := FindFunction(astFile, funcName)
if FuncNode == nil {
return
}
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
}
}
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)-1)
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"},
&ast.Ident{Name: "publicGroup"},
},
},
})
}
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
}
+255
View File
@@ -0,0 +1,255 @@
package ast
import (
"fmt"
"go/ast"
"go/token"
)
// Type AST类型
type Type string
func (r Type) String() string {
return string(r)
}
func (r Type) Group() string {
switch r {
case TypePackageApiEnter:
return "ApiGroup"
case TypePackageRouterEnter:
return "RouterGroup"
case TypePackageServiceEnter:
return "ServiceGroup"
case TypePackageApiModuleEnter:
return "ApiGroup"
case TypePackageRouterModuleEnter:
return "RouterGroup"
case TypePackageServiceModuleEnter:
return "ServiceGroup"
case TypePluginApiEnter:
return "api"
case TypePluginRouterEnter:
return "router"
case TypePluginServiceEnter:
return "service"
default:
return ""
}
}
const (
TypePackageApiEnter = "PackageApiEnter"
TypePackageRouterEnter = "PackageRouterEnter"
TypePackageServiceEnter = "PackageServiceEnter"
TypePackageApiModuleEnter = "PackageApiModuleEnter"
TypePackageRouterModuleEnter = "PackageRouterModuleEnter"
TypePackageServiceModuleEnter = "PackageServiceModuleEnter"
TypePackageInitializeGorm = "PackageInitializeGorm"
TypePackageInitializeRouter = "PackageInitializeRouter"
TypePluginGen = "PluginGen"
TypePluginApiEnter = "PluginApiEnter"
TypePluginInitializeV1 = "PluginInitializeV1"
TypePluginInitializeV2 = "PluginInitializeV2"
TypePluginRouterEnter = "PluginRouterEnter"
TypePluginServiceEnter = "PluginServiceEnter"
TypePluginInitializeApi = "PluginInitializeApi"
TypePluginInitializeGorm = "PluginInitializeGorm"
TypePluginInitializeMenu = "PluginInitializeMenu"
TypePluginInitializeRouter = "PluginInitializeRouter"
)
// SysBaseMenu 菜单结构(用于AST生成)
type SysBaseMenu struct {
ParentId uint
Path string
Name string
Hidden bool
Component string
Sort int
Title string
Icon string
Parameters []SysBaseMenuParameter
MenuBtn []SysBaseMenuBtn
}
// SysBaseMenuParameter 菜单参数
type SysBaseMenuParameter struct {
Type string
Key string
Value string
}
// SysBaseMenuBtn 菜单按钮
type SysBaseMenuBtn struct {
Name string
Desc string
}
// SysApi API结构
type SysApi struct {
Path string
Description string
ApiGroup string
Method string
}
// CreateMenuStructAst 创建菜单结构AST
func CreateMenuStructAst(menus []SysBaseMenu) *[]ast.Expr {
var menuElts []ast.Expr
for i := range menus {
elts := []ast.Expr{
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "ParentId"},
Value: &ast.BasicLit{Kind: token.INT, Value: "0"},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Path"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Path)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Name"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Name)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Hidden"},
Value: &ast.Ident{Name: "false"},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Component"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Component)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Sort"},
Value: &ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("%d", menus[i].Sort)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Meta"},
Value: &ast.CompositeLit{
Type: &ast.SelectorExpr{
X: &ast.Ident{Name: "model"},
Sel: &ast.Ident{Name: "Meta"},
},
Elts: []ast.Expr{
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Title"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Title)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Icon"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Icon)},
},
},
},
},
}
if len(menus[i].Parameters) > 0 {
var paramElts []ast.Expr
for _, param := range menus[i].Parameters {
paramElts = append(paramElts, &ast.CompositeLit{
Type: &ast.SelectorExpr{
X: &ast.Ident{Name: "model"},
Sel: &ast.Ident{Name: "SysBaseMenuParameter"},
},
Elts: []ast.Expr{
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Type"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", param.Type)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Key"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", param.Key)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Value"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", param.Value)},
},
},
})
}
elts = append(elts, &ast.KeyValueExpr{
Key: &ast.Ident{Name: "Parameters"},
Value: &ast.CompositeLit{
Type: &ast.ArrayType{
Elt: &ast.SelectorExpr{
X: &ast.Ident{Name: "model"},
Sel: &ast.Ident{Name: "SysBaseMenuParameter"},
},
},
Elts: paramElts,
},
})
}
if len(menus[i].MenuBtn) > 0 {
var btnElts []ast.Expr
for _, btn := range menus[i].MenuBtn {
btnElts = append(btnElts, &ast.CompositeLit{
Type: &ast.SelectorExpr{
X: &ast.Ident{Name: "model"},
Sel: &ast.Ident{Name: "SysBaseMenuBtn"},
},
Elts: []ast.Expr{
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Name"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", btn.Name)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Desc"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", btn.Desc)},
},
},
})
}
elts = append(elts, &ast.KeyValueExpr{
Key: &ast.Ident{Name: "MenuBtn"},
Value: &ast.CompositeLit{
Type: &ast.ArrayType{
Elt: &ast.SelectorExpr{
X: &ast.Ident{Name: "model"},
Sel: &ast.Ident{Name: "SysBaseMenuBtn"},
},
},
Elts: btnElts,
},
})
}
menuElts = append(menuElts, &ast.CompositeLit{
Type: nil,
Elts: elts,
})
}
return &menuElts
}
// CreateApiStructAst 创建API结构AST
func CreateApiStructAst(apis []SysApi) *[]ast.Expr {
var apiElts []ast.Expr
for i := range apis {
elts := []ast.Expr{
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Path"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Path)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Description"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Description)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "ApiGroup"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].ApiGroup)},
},
&ast.KeyValueExpr{
Key: &ast.Ident{Name: "Method"},
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Method)},
},
}
apiElts = append(apiElts, &ast.CompositeLit{
Type: nil,
Elts: elts,
})
}
return &apiElts
}