文件结构调整,支持插件自动化 (#1824)
* Update index.vue 多个详情页之间切换tab,页面没有重新渲染 * feature:新增同步API功能 * feature: 同步表数据 * feature:新增同步API功能 * feature: 增加V2插件注册 * feature:给Enter的依赖结构增加单独的New 方便引用 * feature: 调整关联属性的选择模式 * feature: 增加component组件和name的映射插件,防止keepalive的懒加载失效。 * update: PluginInitializeRouter && 修复TypePluginInitializeMenu ast 类型错误 * update: 测试文件的astType 类型错误 * feature: 文件变更自动同步componentName.json。 * feature: 文件变更自动同步componentName.json。 * feat: UI美化 * feat: 自动化页面顺序调整 * feature:修改404页面 * update: PluginInitializeMenu * update: Plugin template * fixed systemApi 重复声明 * api.vue:update:修改API分组为下拉列表 * update: import添加注释 * update: plugin_enter_test.go 增加测试用例 * update: ast 预览文件路径 * update: config Autocode 新增Module字段以及如果为空的情况下自动获取运行目录下的go.mod文件 * update: auto_code_package.go 完善调用ast工具类的封装使用 * update: auto_code_template.go Create方法和修正SysAutoCodeHistory * feat:调整自动化package为模板,增加初始化配置信息,调整页面信息。 * update: ast PreviewPath MkdirAll * update: ast type错误, PluginEnter and PackageModuleEnter add TemplatePath模版路径 * update: autoCodePackage and autoCodeTemplate bug修正 * update: PackageInitializeRouter 传入两个路由组 * update: PackageModuleEnter 处理空变量时与type冲突注入 * update: Package 模版更新 * update: utils/ast 优化统一 * update: 注入内容修复错误 * fix: 修复注释错误 * update: plugin 模版 完成 * feature: 文件watch功能只在development下开启 * update: viper.go.template 因为viper不区分配置的key的大小写所以用package * update: ast 测试代码规范化 * update: package 删除api和router多余导包 * update: plugin template * update: auto_code_package 问题修复 * update: ast 测试插件的预览功能 * update: gorm_biz 更新注册方式 * update: go.mod tidy * remove: plugin template gen main.go.template * update: ast 重构, 分离读取和写入步骤支持 * update: AutoCodePackageApi 传入参数错误修复 * rename: sys_autocode_history.go => sys_auto_code_history.go * update: 预览无需落盘, 创建落盘,抽离公共参数 * update: api.go.tpl 导包位置fmt 和package js位置存放错误 * update: 测试用例修复 and PackageInitializeGorm 重构 * update: ast 新增相对路径, 代码生成器历史回滚功能 * update: ast 工具类回滚失败修复以及测试文件 * update: 代码生成器历史 回滚问题修复 * update: 代码生成器模版忽略.DS_Store * featute: 自动化GORM结构的注入和剔除 * feature: 插件模板调整 * feature: 增加公告插件示例,调整代码模板。 * feature: 自动注册插件V2。 --------- Co-authored-by: zayn <972858472@qq.com> Co-authored-by: SliverHorn <sliver_horn@qq.com> Co-authored-by: krank <emosick@qq.com> Co-authored-by: cjk <wlicjk@126.com> Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com> Co-authored-by: maxwell <zhong.maxwell@gmail.com>
This commit is contained in:
@@ -3,10 +3,12 @@ package ast
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
)
|
||||
|
||||
// 增加 import 方法
|
||||
// AddImport 增加 import 方法
|
||||
func AddImport(astNode ast.Node, imp string) {
|
||||
impStr := fmt.Sprintf("\"%s\"", imp)
|
||||
ast.Inspect(astNode, func(node ast.Node) bool {
|
||||
@@ -31,7 +33,7 @@ func AddImport(astNode ast.Node, imp string) {
|
||||
})
|
||||
}
|
||||
|
||||
// 查询特定function方法
|
||||
// FindFunction 查询特定function方法
|
||||
func FindFunction(astNode ast.Node, FunctionName string) *ast.FuncDecl {
|
||||
var funcDeclP *ast.FuncDecl
|
||||
ast.Inspect(astNode, func(node ast.Node) bool {
|
||||
@@ -45,3 +47,25 @@ func FindFunction(astNode ast.Node, FunctionName string) *ast.FuncDecl {
|
||||
})
|
||||
return funcDeclP
|
||||
}
|
||||
|
||||
// 检查是否存在Import
|
||||
func CheckImport(file *ast.File, importPath string) bool {
|
||||
for _, imp := range file.Imports {
|
||||
// Remove quotes around the import path
|
||||
path := imp.Path.Value[1 : len(imp.Path.Value)-1]
|
||||
|
||||
if path == importPath {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func CreateStmt(statement string) *ast.ExprStmt {
|
||||
expr, err := parser.ParseExpr(statement)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return &ast.ExprStmt{X: expr}
|
||||
}
|
||||
|
11
server/utils/ast/ast_init_test.go
Normal file
11
server/utils/ast/ast_init_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func init() {
|
||||
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("../../../")
|
||||
global.GVA_CONFIG.AutoCode.Server = "server"
|
||||
}
|
32
server/utils/ast/ast_test.go
Normal file
32
server/utils/ast/ast_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAst(t *testing.T) {
|
||||
filename := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "plugin.go")
|
||||
fileSet := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fileSet, filename, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = ast.Print(fileSet, file)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = printer.Fprint(os.Stdout, token.NewFileSet(), file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
53
server/utils/ast/ast_type.go
Normal file
53
server/utils/ast/ast_type.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package 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" // server/api/v1/enter.go
|
||||
TypePackageRouterEnter = "PackageRouterEnter" // server/router/enter.go
|
||||
TypePackageServiceEnter = "PackageServiceEnter" // server/service/enter.go
|
||||
TypePackageApiModuleEnter = "PackageApiModuleEnter" // server/api/v1/{package}/enter.go
|
||||
TypePackageRouterModuleEnter = "PackageRouterModuleEnter" // server/router/{package}/enter.go
|
||||
TypePackageServiceModuleEnter = "PackageServiceModuleEnter" // server/service/{package}/enter.go
|
||||
TypePackageInitializeGorm = "PackageInitializeGorm" // server/initialize/gorm_biz.go
|
||||
TypePackageInitializeRouter = "PackageInitializeRouter" // server/initialize/router_biz.go
|
||||
TypePluginGen = "PluginGen" // server/plugin/{package}/gen/main.go
|
||||
TypePluginApiEnter = "PluginApiEnter" // server/plugin/{package}/enter.go
|
||||
TypePluginInitializeV1 = "PluginInitializeV1" // server/initialize/plugin_biz_v1.go
|
||||
TypePluginInitializeV2 = "PluginInitializeV2" // server/initialize/plugin_biz_v2.go
|
||||
TypePluginRouterEnter = "PluginRouterEnter" // server/plugin/{package}/enter.go
|
||||
TypePluginServiceEnter = "PluginServiceEnter" // server/plugin/{package}/enter.go
|
||||
TypePluginInitializeApi = "PluginInitializeApi" // server/plugin/{package}/initialize/api.go
|
||||
TypePluginInitializeGorm = "PluginInitializeGorm" // server/plugin/{package}/initialize/gorm.go
|
||||
TypePluginInitializeMenu = "PluginInitializeMenu" // server/plugin/{package}/initialize/menu.go
|
||||
TypePluginInitializeRouter = "PluginInitializeRouter" // server/plugin/{package}/initialize/router.go
|
||||
)
|
93
server/utils/ast/import.go
Normal file
93
server/utils/ast/import.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Import struct {
|
||||
Base
|
||||
ImportPath string // 导包路径
|
||||
}
|
||||
|
||||
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 && v2.Path.Value == a.ImportPath {
|
||||
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:]...)
|
||||
} // 如果没有import声明,就删除, 如果不删除则会出现import()
|
||||
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 && v2.Path.Value == a.ImportPath {
|
||||
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...)
|
||||
} // 如果没有import声明,就创建一个, 主要要放在第一个
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Import) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
17
server/utils/ast/interfaces.go
Normal file
17
server/utils/ast/interfaces.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Ast interface {
|
||||
// Parse 解析文件/代码
|
||||
Parse(filename string, writer io.Writer) (file *ast.File, err error)
|
||||
// Rollback 回滚
|
||||
Rollback(file *ast.File) error
|
||||
// Injection 注入
|
||||
Injection(file *ast.File) error
|
||||
// Format 格式化输出
|
||||
Format(filename string, writer io.Writer, file *ast.File) error
|
||||
}
|
75
server/utils/ast/interfaces_base.go
Normal file
75
server/utils/ast/interfaces_base.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/pkg/errors"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Base struct{}
|
||||
|
||||
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)
|
||||
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(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server)
|
||||
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(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server)
|
||||
keys := strings.Split(filePath, "/")
|
||||
filePath = filepath.Join(keys...)
|
||||
filePath = filepath.Join(server, filePath)
|
||||
return filePath
|
||||
}
|
102
server/utils/ast/package_enter.go
Normal file
102
server/utils/ast/package_enter.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PackageEnter 模块化入口
|
||||
type PackageEnter struct {
|
||||
Base
|
||||
Type Type // 类型
|
||||
Path string // 文件路径
|
||||
ImportPath string // 导包路径
|
||||
StructName string // 结构体名称
|
||||
PackageName string // 包名
|
||||
RelativePath string // 相对路径
|
||||
PackageStructName string // 包结构体名称
|
||||
}
|
||||
|
||||
func (a *PackageEnter) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
if filename == "" {
|
||||
if a.RelativePath == "" {
|
||||
filename = a.Path
|
||||
a.RelativePath = a.Base.RelativePath(a.Path)
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
a.Path = a.Base.AbsolutePath(a.RelativePath)
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
|
||||
func (a *PackageEnter) Rollback(file *ast.File) error {
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.GenDecl)
|
||||
if o1 {
|
||||
for j := 0; j < len(v1.Specs); j++ {
|
||||
v2, o2 := v1.Specs[j].(*ast.TypeSpec)
|
||||
if o2 {
|
||||
if v2.Name.Name != a.Type.Group() {
|
||||
continue
|
||||
}
|
||||
v3, o3 := v2.Type.(*ast.StructType)
|
||||
if o3 {
|
||||
for k := 0; k < len(v3.Fields.List); k++ {
|
||||
if len(v3.Fields.List[k].Names) >= 1 && v3.Fields.List[k].Names[0].Name == a.StructName {
|
||||
_ = NewImport(a.ImportPath).Rollback(file)
|
||||
v3.Fields.List = append(v3.Fields.List[:k], v3.Fields.List[k+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PackageEnter) Injection(file *ast.File) error {
|
||||
_ = NewImport(a.ImportPath).Injection(file)
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.GenDecl)
|
||||
if o1 {
|
||||
for j := 0; j < len(v1.Specs); j++ {
|
||||
v2, o2 := v1.Specs[j].(*ast.TypeSpec)
|
||||
if o2 {
|
||||
if v2.Name.Name != a.Type.Group() {
|
||||
continue
|
||||
}
|
||||
v3, o3 := v2.Type.(*ast.StructType)
|
||||
if o3 {
|
||||
var has bool
|
||||
for k := 0; k < len(v3.Fields.List); k++ {
|
||||
if len(v3.Fields.List[k].Names) == 1 && v3.Fields.List[k].Names[0].Name == a.StructName {
|
||||
has = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
field := &ast.Field{
|
||||
Names: []*ast.Ident{{Name: a.StructName}},
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.PackageStructName},
|
||||
},
|
||||
}
|
||||
v3.Fields.List = append(v3.Fields.List, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PackageEnter) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
if filename == "" {
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
154
server/utils/ast/package_enter_test.go
Normal file
154
server/utils/ast/package_enter_test.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPackageEnter_Rollback(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
PackageName string
|
||||
PackageStructName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试ExampleApiGroup回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageApiEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/api/v1/example"`,
|
||||
StructName: "ExampleApiGroup",
|
||||
PackageName: "example",
|
||||
PackageStructName: "ApiGroup",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试ExampleRouterGroup回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageRouterEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router/example"`,
|
||||
StructName: "Example",
|
||||
PackageName: "example",
|
||||
PackageStructName: "RouterGroup",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试ExampleServiceGroup回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageServiceEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/service/example"`,
|
||||
StructName: "ExampleServiceGroup",
|
||||
PackageName: "example",
|
||||
PackageStructName: "ServiceGroup",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PackageEnter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
PackageStructName: tt.fields.PackageStructName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Rollback(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackageEnter_Injection(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
PackageName string
|
||||
PackageStructName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试ExampleApiGroup注入",
|
||||
fields: fields{
|
||||
Type: TypePackageApiEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/api/v1/example"`,
|
||||
StructName: "ExampleApiGroup",
|
||||
PackageName: "example",
|
||||
PackageStructName: "ApiGroup",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试ExampleRouterGroup注入",
|
||||
fields: fields{
|
||||
Type: TypePackageRouterEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router/example"`,
|
||||
StructName: "Example",
|
||||
PackageName: "example",
|
||||
PackageStructName: "RouterGroup",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试ExampleServiceGroup注入",
|
||||
fields: fields{
|
||||
Type: TypePackageServiceEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/service/example"`,
|
||||
StructName: "ExampleServiceGroup",
|
||||
PackageName: "example",
|
||||
PackageStructName: "ServiceGroup",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PackageEnter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
PackageStructName: tt.fields.PackageStructName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Injection(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Format() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
208
server/utils/ast/package_initialize_gorm.go
Normal file
208
server/utils/ast/package_initialize_gorm.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PackageInitializeGorm 包初始化gorm
|
||||
type PackageInitializeGorm struct {
|
||||
Base
|
||||
Type Type // 类型
|
||||
Path string // 文件路径
|
||||
ImportPath string // 导包路径
|
||||
Business string // 业务库 gva => gva, 不要传"gva"
|
||||
StructName string // 结构体名称
|
||||
PackageName string // 包名
|
||||
RelativePath string // 相对路径
|
||||
IsNew bool // 是否使用new关键字 true: new(PackageName.StructName) false: &PackageName.StructName{}
|
||||
}
|
||||
|
||||
func (a *PackageInitializeGorm) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
if filename == "" {
|
||||
if a.RelativePath == "" {
|
||||
filename = a.Path
|
||||
a.RelativePath = a.Base.RelativePath(a.Path)
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
a.Path = a.Base.AbsolutePath(a.RelativePath)
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
|
||||
func (a *PackageInitializeGorm) Rollback(file *ast.File) error {
|
||||
cutAutoMigrateFuncVisitor := cutAutoMigrateFunc{
|
||||
pkgInitGorm: a,
|
||||
}
|
||||
ast.Walk(cutAutoMigrateFuncVisitor, file)
|
||||
|
||||
if cutAutoMigrateFuncVisitor.PackageNameNum == 0 {
|
||||
_ = NewImport(a.ImportPath).Rollback(file)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PackageInitializeGorm) Injection(file *ast.File) error {
|
||||
_ = NewImport(a.ImportPath).Injection(file)
|
||||
bizModelDecl := FindFunction(file, "bizModel")
|
||||
if bizModelDecl != nil {
|
||||
a.addDbVar(bizModelDecl.Body)
|
||||
}
|
||||
|
||||
addAutoMigrateVisitor := addAutoMigrateFunc{
|
||||
pkgInitGorm: a,
|
||||
}
|
||||
|
||||
ast.Walk(addAutoMigrateVisitor, file)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PackageInitializeGorm) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
if filename == "" {
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
||||
|
||||
type addAutoMigrateFunc struct {
|
||||
pkgInitGorm *PackageInitializeGorm
|
||||
}
|
||||
|
||||
func (v addAutoMigrateFunc) Visit(n ast.Node) ast.Visitor {
|
||||
// 总调用的db变量根据business来决定
|
||||
varDB := v.pkgInitGorm.Business + "db"
|
||||
|
||||
callExpr, ok := n.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return v
|
||||
}
|
||||
|
||||
// 检查是不是 db.AutoMigrate() 方法
|
||||
selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
|
||||
if !ok || selExpr.Sel.Name != "AutoMigrate" {
|
||||
return v
|
||||
}
|
||||
|
||||
// 检查调用方是不是 db
|
||||
ident, ok := selExpr.X.(*ast.Ident)
|
||||
if !ok || ident.Name != varDB {
|
||||
return v
|
||||
}
|
||||
|
||||
// 添加结构体参数
|
||||
callExpr.Args = append(callExpr.Args, &ast.CompositeLit{
|
||||
Type: &ast.SelectorExpr{
|
||||
X: ast.NewIdent(v.pkgInitGorm.PackageName),
|
||||
Sel: ast.NewIdent(v.pkgInitGorm.StructName),
|
||||
},
|
||||
})
|
||||
return v
|
||||
}
|
||||
|
||||
type cutAutoMigrateFunc struct {
|
||||
pkgInitGorm *PackageInitializeGorm
|
||||
PackageNameNum int
|
||||
}
|
||||
|
||||
func (v cutAutoMigrateFunc) Visit(n ast.Node) ast.Visitor {
|
||||
// 总调用的db变量根据business来决定
|
||||
varDB := v.pkgInitGorm.Business + "Db"
|
||||
|
||||
callExpr, ok := n.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return v
|
||||
}
|
||||
|
||||
// 检查是不是 db.AutoMigrate() 方法
|
||||
selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
|
||||
if !ok || selExpr.Sel.Name != "AutoMigrate" {
|
||||
return v
|
||||
}
|
||||
|
||||
// 检查调用方是不是 db
|
||||
ident, ok := selExpr.X.(*ast.Ident)
|
||||
if !ok || ident.Name != varDB {
|
||||
return v
|
||||
}
|
||||
|
||||
// 删除结构体参数
|
||||
for i := range callExpr.Args {
|
||||
if com, ok := callExpr.Args[i].(*ast.CompositeLit); ok {
|
||||
if selector, ok := com.Type.(*ast.SelectorExpr); ok {
|
||||
if x, ok := selector.X.(*ast.Ident); ok {
|
||||
if x.Name == v.pkgInitGorm.PackageName {
|
||||
v.PackageNameNum++
|
||||
if selector.Sel.Name == v.pkgInitGorm.StructName {
|
||||
callExpr.Args = append(callExpr.Args[:i], callExpr.Args[i+1:]...)
|
||||
v.PackageNameNum--
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 创建businessDB变量
|
||||
func (a *PackageInitializeGorm) addDbVar(astBody *ast.BlockStmt) {
|
||||
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 == "db" || ident.Name == a.Business+"Db" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加 businessDb := global.GetGlobalDBByDBName("business") 变量
|
||||
assignNode := &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{
|
||||
Name: a.Business + "Db",
|
||||
},
|
||||
},
|
||||
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: fmt.Sprintf("\"%s\"", a.Business),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// 添加 businessDb.AutoMigrate() 方法
|
||||
autoMigrateCall := &ast.ExprStmt{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
Name: a.Business + "Db",
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
Name: "AutoMigrate",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
returnNode := astBody.List[len(astBody.List)-1]
|
||||
astBody.List = append(astBody.List[:len(astBody.List)-1], assignNode, autoMigrateCall, returnNode)
|
||||
}
|
171
server/utils/ast/package_initialize_gorm_test.go
Normal file
171
server/utils/ast/package_initialize_gorm_test.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPackageInitializeGorm_Injection(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
PackageName string
|
||||
IsNew bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 &example.ExaFileUploadAndDownload{} 注入",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`,
|
||||
StructName: "ExaFileUploadAndDownload",
|
||||
PackageName: "example",
|
||||
IsNew: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 &example.ExaCustomer{} 注入",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`,
|
||||
StructName: "ExaCustomer",
|
||||
PackageName: "example",
|
||||
IsNew: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 new(example.ExaFileUploadAndDownload) 注入",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`,
|
||||
StructName: "ExaFileUploadAndDownload",
|
||||
PackageName: "example",
|
||||
IsNew: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 new(example.ExaCustomer) 注入",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`,
|
||||
StructName: "ExaCustomer",
|
||||
PackageName: "example",
|
||||
IsNew: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PackageInitializeGorm{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
IsNew: tt.fields.IsNew,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Injection(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackageInitializeGorm_Rollback(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
PackageName string
|
||||
IsNew bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 &example.ExaFileUploadAndDownload{} 回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`,
|
||||
StructName: "ExaFileUploadAndDownload",
|
||||
PackageName: "example",
|
||||
IsNew: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 &example.ExaCustomer{} 回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`,
|
||||
StructName: "ExaCustomer",
|
||||
PackageName: "example",
|
||||
IsNew: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 new(example.ExaFileUploadAndDownload) 回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`,
|
||||
StructName: "ExaFileUploadAndDownload",
|
||||
PackageName: "example",
|
||||
IsNew: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 new(example.ExaCustomer) 回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`,
|
||||
StructName: "ExaCustomer",
|
||||
PackageName: "example",
|
||||
IsNew: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PackageInitializeGorm{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
IsNew: tt.fields.IsNew,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Rollback(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
223
server/utils/ast/package_initialize_router.go
Normal file
223
server/utils/ast/package_initialize_router.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PackageInitializeRouter 包初始化路由
|
||||
// ModuleName := PackageName.AppName.GroupName
|
||||
// ModuleName.FunctionName(RouterGroupName)
|
||||
type PackageInitializeRouter struct {
|
||||
Base
|
||||
Type Type // 类型
|
||||
Path string // 文件路径
|
||||
ImportPath string // 导包路径
|
||||
RelativePath string // 相对路径
|
||||
AppName string // 应用名称
|
||||
GroupName string // 分组名称
|
||||
ModuleName string // 模块名称
|
||||
PackageName string // 包名
|
||||
FunctionName string // 函数名
|
||||
RouterGroupName string // 路由分组名称
|
||||
LeftRouterGroupName string // 左路由分组名称
|
||||
RightRouterGroupName string // 右路由分组名称
|
||||
}
|
||||
|
||||
func (a *PackageInitializeRouter) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
if filename == "" {
|
||||
if a.RelativePath == "" {
|
||||
filename = a.Path
|
||||
a.RelativePath = a.Base.RelativePath(a.Path)
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
a.Path = a.Base.AbsolutePath(a.RelativePath)
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
|
||||
func (a *PackageInitializeRouter) Rollback(file *ast.File) error {
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.FuncDecl)
|
||||
if o1 {
|
||||
if v1.Name.Name != "initBizRouter" {
|
||||
continue
|
||||
}
|
||||
for j := 0; j < len(v1.Body.List); j++ {
|
||||
v2, o2 := v1.Body.List[j].(*ast.BlockStmt)
|
||||
if o2 {
|
||||
for k := 0; k < len(v2.List); k++ {
|
||||
if k == 0 {
|
||||
v3, o3 := v2.List[0].(*ast.AssignStmt)
|
||||
if o3 {
|
||||
if len(v3.Lhs) == 1 && len(v3.Rhs) == 1 {
|
||||
v4, o4 := v3.Lhs[0].(*ast.Ident)
|
||||
v5, o5 := v3.Rhs[0].(*ast.SelectorExpr)
|
||||
v6, o6 := v5.X.(*ast.SelectorExpr)
|
||||
v7, o7 := v6.X.(*ast.Ident)
|
||||
if o4 && o5 && o6 && o7 {
|
||||
if v4.Name != a.ModuleName && v7.Name != a.PackageName && v6.Sel.Name != a.AppName && v5.Sel.Name != a.GroupName {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // 判断是否有路由组和作用域
|
||||
v3, o3 := v2.List[k].(*ast.ExprStmt)
|
||||
if o3 {
|
||||
v4, o4 := v3.X.(*ast.CallExpr)
|
||||
if o4 {
|
||||
v5, o5 := v4.Fun.(*ast.SelectorExpr)
|
||||
if o5 {
|
||||
v6, o6 := v5.X.(*ast.Ident)
|
||||
if o6 {
|
||||
if v6.Name == a.ModuleName && v5.Sel.Name == a.FunctionName {
|
||||
v2.List = append(v2.List[:k], v2.List[k+1:]...)
|
||||
length := len(v2.List)
|
||||
if length == 1 {
|
||||
v1.Body.List = append(v1.Body.List[:j], v1.Body.List[j+1:]...)
|
||||
// TODO 删除作用域之后会出现两种情况需要删除空行, 中间模块被删除和最后的模块被删除
|
||||
// if j < len(v1.Body.List) {
|
||||
// v2, o2 = v1.Body.List[j].(*ast.BlockStmt)
|
||||
// if o2 {
|
||||
// v2.Lbrace -= 3
|
||||
// // v2.Rbrace -= 2
|
||||
// }
|
||||
// } // 中间模块被删除
|
||||
// if j == len(v1.Body.List) {
|
||||
// v1.Body.Rbrace -= 10
|
||||
// } // 最后的模块被删除
|
||||
break
|
||||
} // 无调用路由初始化函数 => 删除局部变量 && 删除作用域 && 导包
|
||||
if k < length-1 {
|
||||
v3, o3 = v2.List[k].(*ast.ExprStmt)
|
||||
if o3 {
|
||||
v4, o4 = v3.X.(*ast.CallExpr)
|
||||
if o4 {
|
||||
v5, o5 = v4.Fun.(*ast.SelectorExpr)
|
||||
if o5 {
|
||||
v6, o6 = v5.X.(*ast.Ident)
|
||||
if o6 {
|
||||
v6.NamePos -= 10
|
||||
}
|
||||
v5.Sel.NamePos -= 20
|
||||
}
|
||||
}
|
||||
}
|
||||
} // 删除空行
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PackageInitializeRouter) Injection(file *ast.File) error {
|
||||
_ = NewImport(a.ImportPath).Injection(file)
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.FuncDecl)
|
||||
if o1 {
|
||||
if v1.Name.Name != "initBizRouter" {
|
||||
continue
|
||||
}
|
||||
var (
|
||||
hasInit bool
|
||||
blockStmt *ast.BlockStmt
|
||||
)
|
||||
for j := 0; j < len(v1.Body.List); j++ {
|
||||
v2, o2 := v1.Body.List[j].(*ast.BlockStmt)
|
||||
if o2 {
|
||||
for k := 0; k < len(v2.List); k++ {
|
||||
if k == 0 {
|
||||
v3, o3 := v2.List[0].(*ast.AssignStmt)
|
||||
if o3 {
|
||||
if len(v3.Lhs) == 1 && len(v3.Rhs) == 1 {
|
||||
v4, o4 := v3.Lhs[0].(*ast.Ident)
|
||||
v5, o5 := v3.Rhs[0].(*ast.SelectorExpr)
|
||||
v6, o6 := v5.X.(*ast.SelectorExpr)
|
||||
v7, o7 := v6.X.(*ast.Ident)
|
||||
if o4 && o5 && o6 && o7 {
|
||||
if v4.Name == a.ModuleName && v7.Name == a.PackageName && v6.Sel.Name == a.AppName && v5.Sel.Name == a.GroupName {
|
||||
blockStmt = v2
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // 判断是否有路由组和作用域
|
||||
v3, o3 := v2.List[k].(*ast.ExprStmt)
|
||||
if o3 {
|
||||
v4, o4 := v3.X.(*ast.CallExpr)
|
||||
if o4 {
|
||||
v5, o5 := v4.Fun.(*ast.SelectorExpr)
|
||||
if o5 {
|
||||
v6, o6 := v5.X.(*ast.Ident)
|
||||
if o6 {
|
||||
if v6.Name == a.ModuleName && v5.Sel.Name == a.FunctionName {
|
||||
hasInit = true
|
||||
continue
|
||||
} // 判断是否存在调用函数
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if hasInit {
|
||||
continue
|
||||
}
|
||||
stmt := &ast.ExprStmt{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.ModuleName},
|
||||
Sel: &ast.Ident{Name: a.FunctionName},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.Ident{Name: a.LeftRouterGroupName},
|
||||
&ast.Ident{Name: a.RightRouterGroupName},
|
||||
},
|
||||
},
|
||||
}
|
||||
if blockStmt == nil {
|
||||
blockStmt = &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{&ast.Ident{Name: a.ModuleName, Obj: ast.NewObj(ast.Var, a.ModuleName)}},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.AppName},
|
||||
},
|
||||
Sel: &ast.Ident{Name: a.GroupName},
|
||||
},
|
||||
},
|
||||
},
|
||||
stmt,
|
||||
},
|
||||
}
|
||||
v1.Body.List = append(v1.Body.List, blockStmt)
|
||||
break
|
||||
} // 创建作用域 && 创建路由组 && 调用路由初始化函数
|
||||
blockStmt.List = append(blockStmt.List, stmt)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PackageInitializeRouter) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
if filename == "" {
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
156
server/utils/ast/package_initialize_router_test.go
Normal file
156
server/utils/ast/package_initialize_router_test.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPackageInitializeRouter_Injection(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
AppName string
|
||||
GroupName string
|
||||
ModuleName string
|
||||
PackageName string
|
||||
FunctionName string
|
||||
RouterGroupName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 InitCustomerRouter 注入",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router"`,
|
||||
AppName: "RouterGroupApp",
|
||||
GroupName: "Example",
|
||||
ModuleName: "exampleRouter",
|
||||
PackageName: "router",
|
||||
FunctionName: "InitCustomerRouter",
|
||||
RouterGroupName: "privateGroup",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 InitFileUploadAndDownloadRouter 注入",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router"`,
|
||||
AppName: "RouterGroupApp",
|
||||
GroupName: "Example",
|
||||
ModuleName: "exampleRouter",
|
||||
PackageName: "router",
|
||||
FunctionName: "InitFileUploadAndDownloadRouter",
|
||||
RouterGroupName: "privateGroup",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PackageInitializeRouter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
AppName: tt.fields.AppName,
|
||||
GroupName: tt.fields.GroupName,
|
||||
ModuleName: tt.fields.ModuleName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
FunctionName: tt.fields.FunctionName,
|
||||
RouterGroupName: tt.fields.RouterGroupName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Injection(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackageInitializeRouter_Rollback(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
AppName string
|
||||
GroupName string
|
||||
ModuleName string
|
||||
PackageName string
|
||||
FunctionName string
|
||||
RouterGroupName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
|
||||
{
|
||||
name: "测试 InitCustomerRouter 回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router"`,
|
||||
AppName: "RouterGroupApp",
|
||||
GroupName: "Example",
|
||||
ModuleName: "exampleRouter",
|
||||
PackageName: "router",
|
||||
FunctionName: "InitCustomerRouter",
|
||||
RouterGroupName: "privateGroup",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 InitFileUploadAndDownloadRouter 回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router"`,
|
||||
AppName: "RouterGroupApp",
|
||||
GroupName: "Example",
|
||||
ModuleName: "exampleRouter",
|
||||
PackageName: "router",
|
||||
FunctionName: "InitFileUploadAndDownloadRouter",
|
||||
RouterGroupName: "privateGroup",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PackageInitializeRouter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
AppName: tt.fields.AppName,
|
||||
GroupName: tt.fields.GroupName,
|
||||
ModuleName: tt.fields.ModuleName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
FunctionName: tt.fields.FunctionName,
|
||||
RouterGroupName: tt.fields.RouterGroupName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Rollback(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
180
server/utils/ast/package_module_enter.go
Normal file
180
server/utils/ast/package_module_enter.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PackageModuleEnter 模块化入口
|
||||
// ModuleName := PackageName.AppName.GroupName.ServiceName
|
||||
type PackageModuleEnter struct {
|
||||
Base
|
||||
Type Type // 类型
|
||||
Path string // 文件路径
|
||||
ImportPath string // 导包路径
|
||||
RelativePath string // 相对路径
|
||||
StructName string // 结构体名称
|
||||
AppName string // 应用名称
|
||||
GroupName string // 分组名称
|
||||
ModuleName string // 模块名称
|
||||
PackageName string // 包名
|
||||
ServiceName string // 服务名称
|
||||
}
|
||||
|
||||
func (a *PackageModuleEnter) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
if filename == "" {
|
||||
if a.RelativePath == "" {
|
||||
filename = a.Path
|
||||
a.RelativePath = a.Base.RelativePath(a.Path)
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
a.Path = a.Base.AbsolutePath(a.RelativePath)
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
|
||||
func (a *PackageModuleEnter) Rollback(file *ast.File) error {
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.GenDecl)
|
||||
if o1 {
|
||||
for j := 0; j < len(v1.Specs); j++ {
|
||||
v2, o2 := v1.Specs[j].(*ast.TypeSpec)
|
||||
if o2 {
|
||||
if v2.Name.Name != a.Type.Group() {
|
||||
continue
|
||||
}
|
||||
v3, o3 := v2.Type.(*ast.StructType)
|
||||
if o3 {
|
||||
for k := 0; k < len(v3.Fields.List); k++ {
|
||||
v4, o4 := v3.Fields.List[k].Type.(*ast.Ident)
|
||||
if o4 && v4.Name == a.StructName {
|
||||
v3.Fields.List = append(v3.Fields.List[:k], v3.Fields.List[k+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if a.Type == TypePackageServiceModuleEnter {
|
||||
continue
|
||||
}
|
||||
v3, o3 := v1.Specs[j].(*ast.ValueSpec)
|
||||
if o3 {
|
||||
if len(v3.Names) == 1 && v3.Names[0].Name == a.ModuleName {
|
||||
v1.Specs = append(v1.Specs[:j], v1.Specs[j+1:]...)
|
||||
}
|
||||
}
|
||||
if v1.Tok == token.VAR && len(v1.Specs) == 0 {
|
||||
_ = NewImport(a.ImportPath).Rollback(file)
|
||||
if i == len(file.Decls) {
|
||||
file.Decls = append(file.Decls[:i-1])
|
||||
break
|
||||
} // 空的var(), 如果不删除则会影响的注入变量, 因为识别不到*ast.ValueSpec
|
||||
file.Decls = append(file.Decls[:i], file.Decls[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PackageModuleEnter) Injection(file *ast.File) error {
|
||||
_ = NewImport(a.ImportPath).Injection(file)
|
||||
var hasValue bool
|
||||
var hasVariables bool
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.GenDecl)
|
||||
if o1 {
|
||||
if v1.Tok == token.VAR {
|
||||
hasVariables = true
|
||||
}
|
||||
for j := 0; j < len(v1.Specs); j++ {
|
||||
if a.Type == TypePackageServiceModuleEnter {
|
||||
hasValue = true
|
||||
}
|
||||
v2, o2 := v1.Specs[j].(*ast.TypeSpec)
|
||||
if o2 {
|
||||
if v2.Name.Name != a.Type.Group() {
|
||||
continue
|
||||
}
|
||||
v3, o3 := v2.Type.(*ast.StructType)
|
||||
if o3 {
|
||||
var hasStruct bool
|
||||
for k := 0; k < len(v3.Fields.List); k++ {
|
||||
v4, o4 := v3.Fields.List[k].Type.(*ast.Ident)
|
||||
if o4 && v4.Name == a.StructName {
|
||||
hasStruct = true
|
||||
}
|
||||
}
|
||||
if !hasStruct {
|
||||
field := &ast.Field{Type: &ast.Ident{Name: a.StructName}}
|
||||
v3.Fields.List = append(v3.Fields.List, field)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
v3, o3 := v1.Specs[j].(*ast.ValueSpec)
|
||||
if o3 {
|
||||
hasVariables = true
|
||||
if len(v3.Names) == 1 && v3.Names[0].Name == a.ModuleName {
|
||||
hasValue = true
|
||||
}
|
||||
}
|
||||
if v1.Tok == token.VAR && len(v1.Specs) == 0 {
|
||||
hasVariables = false
|
||||
} // 说明是空var()
|
||||
if hasVariables && !hasValue {
|
||||
spec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{Name: a.ModuleName}},
|
||||
Values: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.AppName},
|
||||
},
|
||||
Sel: &ast.Ident{Name: a.GroupName},
|
||||
},
|
||||
Sel: &ast.Ident{Name: a.ServiceName},
|
||||
},
|
||||
},
|
||||
}
|
||||
v1.Specs = append(v1.Specs, spec)
|
||||
hasValue = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasValue && !hasVariables {
|
||||
decl := &ast.GenDecl{
|
||||
Tok: token.VAR,
|
||||
Specs: []ast.Spec{
|
||||
&ast.ValueSpec{
|
||||
Names: []*ast.Ident{{Name: a.ModuleName}},
|
||||
Values: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.AppName},
|
||||
},
|
||||
Sel: &ast.Ident{Name: a.GroupName},
|
||||
},
|
||||
Sel: &ast.Ident{Name: a.ServiceName},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
file.Decls = append(file.Decls, decl)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PackageModuleEnter) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
if filename == "" {
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
185
server/utils/ast/package_module_enter_test.go
Normal file
185
server/utils/ast/package_module_enter_test.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPackageModuleEnter_Rollback(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
AppName string
|
||||
GroupName string
|
||||
ModuleName string
|
||||
PackageName string
|
||||
ServiceName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 FileUploadAndDownloadRouter 回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageRouterModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", "example", "enter.go"),
|
||||
ImportPath: `api "github.com/flipped-aurora/gin-vue-admin/server/api/v1"`,
|
||||
StructName: "FileUploadAndDownloadRouter",
|
||||
AppName: "ApiGroupApp",
|
||||
GroupName: "ExampleApiGroup",
|
||||
ModuleName: "exaFileUploadAndDownloadApi",
|
||||
PackageName: "api",
|
||||
ServiceName: "FileUploadAndDownloadApi",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 FileUploadAndDownloadApi 回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageApiModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", "example", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/service"`,
|
||||
StructName: "FileUploadAndDownloadApi",
|
||||
AppName: "ServiceGroupApp",
|
||||
GroupName: "ExampleServiceGroup",
|
||||
ModuleName: "fileUploadAndDownloadService",
|
||||
PackageName: "service",
|
||||
ServiceName: "FileUploadAndDownloadService",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 FileUploadAndDownloadService 回滚",
|
||||
fields: fields{
|
||||
Type: TypePackageServiceModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", "example", "enter.go"),
|
||||
ImportPath: ``,
|
||||
StructName: "FileUploadAndDownloadService",
|
||||
AppName: "",
|
||||
GroupName: "",
|
||||
ModuleName: "",
|
||||
PackageName: "",
|
||||
ServiceName: "",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PackageModuleEnter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
AppName: tt.fields.AppName,
|
||||
GroupName: tt.fields.GroupName,
|
||||
ModuleName: tt.fields.ModuleName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
ServiceName: tt.fields.ServiceName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Rollback(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackageModuleEnter_Injection(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
AppName string
|
||||
GroupName string
|
||||
ModuleName string
|
||||
PackageName string
|
||||
ServiceName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 FileUploadAndDownloadRouter 注入",
|
||||
fields: fields{
|
||||
Type: TypePackageRouterModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", "example", "enter.go"),
|
||||
ImportPath: `api "github.com/flipped-aurora/gin-vue-admin/server/api/v1"`,
|
||||
StructName: "FileUploadAndDownloadRouter",
|
||||
AppName: "ApiGroupApp",
|
||||
GroupName: "ExampleApiGroup",
|
||||
ModuleName: "exaFileUploadAndDownloadApi",
|
||||
PackageName: "api",
|
||||
ServiceName: "FileUploadAndDownloadApi",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 FileUploadAndDownloadApi 注入",
|
||||
fields: fields{
|
||||
Type: TypePackageApiModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", "example", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/service"`,
|
||||
StructName: "FileUploadAndDownloadApi",
|
||||
AppName: "ServiceGroupApp",
|
||||
GroupName: "ExampleServiceGroup",
|
||||
ModuleName: "fileUploadAndDownloadService",
|
||||
PackageName: "service",
|
||||
ServiceName: "FileUploadAndDownloadService",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 FileUploadAndDownloadService 注入",
|
||||
fields: fields{
|
||||
Type: TypePackageServiceModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", "example", "enter.go"),
|
||||
ImportPath: ``,
|
||||
StructName: "FileUploadAndDownloadService",
|
||||
AppName: "",
|
||||
GroupName: "",
|
||||
ModuleName: "",
|
||||
PackageName: "",
|
||||
ServiceName: "",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PackageModuleEnter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
AppName: tt.fields.AppName,
|
||||
GroupName: tt.fields.GroupName,
|
||||
ModuleName: tt.fields.ModuleName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
ServiceName: tt.fields.ServiceName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Injection(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
162
server/utils/ast/plugin_enter.go
Normal file
162
server/utils/ast/plugin_enter.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PluginEnter 插件化入口
|
||||
// ModuleName := PackageName.GroupName.ServiceName
|
||||
type PluginEnter struct {
|
||||
Base
|
||||
Type Type // 类型
|
||||
Path string // 文件路径
|
||||
ImportPath string // 导包路径
|
||||
RelativePath string // 相对路径
|
||||
StructName string // 结构体名称
|
||||
StructCamelName string // 结构体小驼峰名称
|
||||
ModuleName string // 模块名称
|
||||
GroupName string // 分组名称
|
||||
PackageName string // 包名
|
||||
ServiceName string // 服务名称
|
||||
}
|
||||
|
||||
func (a *PluginEnter) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
if filename == "" {
|
||||
if a.RelativePath == "" {
|
||||
filename = a.Path
|
||||
a.RelativePath = a.Base.RelativePath(a.Path)
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
a.Path = a.Base.AbsolutePath(a.RelativePath)
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
|
||||
func (a *PluginEnter) Rollback(file *ast.File) error {
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.GenDecl)
|
||||
if o1 {
|
||||
for j := 0; j < len(v1.Specs); j++ {
|
||||
v2, o2 := v1.Specs[j].(*ast.ValueSpec)
|
||||
if o2 {
|
||||
for k := 0; k < len(v2.Values); k++ {
|
||||
v3, o3 := v2.Values[k].(*ast.CallExpr)
|
||||
if o3 {
|
||||
for l := 0; l < len(v3.Args); l++ {
|
||||
v4, o4 := v3.Args[l].(*ast.Ident)
|
||||
if o4 {
|
||||
v5, o5 := v4.Obj.Decl.(*ast.TypeSpec)
|
||||
if o5 {
|
||||
if v5.Name.Name != a.Type.Group() {
|
||||
break
|
||||
}
|
||||
v6, o6 := v5.Type.(*ast.StructType)
|
||||
if o6 {
|
||||
for m := 0; m < len(v6.Fields.List); m++ {
|
||||
v7, o7 := v6.Fields.List[m].Type.(*ast.Ident)
|
||||
if len(v6.Fields.List[m].Names) >= 1 && v6.Fields.List[m].Names[0].Name == a.StructName && o7 && v7.Name == a.StructCamelName {
|
||||
v6.Fields.List = append(v6.Fields.List[:m], v6.Fields.List[m+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if a.Type == TypePluginServiceEnter {
|
||||
continue
|
||||
}
|
||||
if len(v2.Names) >= 1 && v2.Names[0].Name == a.ModuleName {
|
||||
v1.Specs = append(v1.Specs[:j], v1.Specs[j+1:]...)
|
||||
if len(v1.Specs) <= 1 {
|
||||
_ = NewImport(a.ImportPath).Rollback(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginEnter) Injection(file *ast.File) error {
|
||||
_ = NewImport(a.ImportPath).Injection(file)
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.GenDecl)
|
||||
if o1 {
|
||||
for j := 0; j < len(v1.Specs); j++ {
|
||||
v2, o2 := v1.Specs[j].(*ast.ValueSpec)
|
||||
if o2 {
|
||||
for k := 0; k < len(v2.Values); k++ {
|
||||
v3, o3 := v2.Values[k].(*ast.CallExpr)
|
||||
if o3 {
|
||||
for l := 0; l < len(v3.Args); l++ {
|
||||
v4, o4 := v3.Args[l].(*ast.Ident)
|
||||
if o4 {
|
||||
v5, o5 := v4.Obj.Decl.(*ast.TypeSpec)
|
||||
if o5 {
|
||||
if v5.Name.Name != a.Type.Group() {
|
||||
continue
|
||||
}
|
||||
v6, o6 := v5.Type.(*ast.StructType)
|
||||
if o6 {
|
||||
var has bool
|
||||
for m := 0; m < len(v6.Fields.List); m++ {
|
||||
v7, o7 := v6.Fields.List[m].Type.(*ast.Ident)
|
||||
if len(v6.Fields.List[m].Names) >= 1 && v6.Fields.List[m].Names[0].Name == a.StructName && o7 && v7.Name == a.StructCamelName {
|
||||
has = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
field := &ast.Field{
|
||||
Names: []*ast.Ident{{Name: a.StructName}},
|
||||
Type: &ast.Ident{Name: a.StructCamelName},
|
||||
}
|
||||
v6.Fields.List = append(v6.Fields.List, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if a.Type == TypePluginServiceEnter {
|
||||
continue
|
||||
}
|
||||
var has bool
|
||||
if len(v2.Names) >= 1 && v2.Names[0].Name == a.ModuleName {
|
||||
has = true
|
||||
}
|
||||
if !has {
|
||||
spec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{Name: a.ModuleName}},
|
||||
Values: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.GroupName},
|
||||
},
|
||||
Sel: &ast.Ident{Name: a.ServiceName},
|
||||
},
|
||||
},
|
||||
}
|
||||
v1.Specs = append(v1.Specs, spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginEnter) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
if filename == "" {
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
200
server/utils/ast/plugin_enter_test.go
Normal file
200
server/utils/ast/plugin_enter_test.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPluginEnter_Injection(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
StructCamelName string
|
||||
ModuleName string
|
||||
GroupName string
|
||||
PackageName string
|
||||
ServiceName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 Gva插件UserApi 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginApiEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "api", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/service"`,
|
||||
StructName: "User",
|
||||
StructCamelName: "user",
|
||||
ModuleName: "serviceUser",
|
||||
GroupName: "Service",
|
||||
PackageName: "service",
|
||||
ServiceName: "User",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 Gva插件UserRouter 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginRouterEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "router", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/api"`,
|
||||
StructName: "User",
|
||||
StructCamelName: "user",
|
||||
ModuleName: "userApi",
|
||||
GroupName: "Api",
|
||||
PackageName: "api",
|
||||
ServiceName: "User",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 Gva插件UserService 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginServiceEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "service", "enter.go"),
|
||||
ImportPath: "",
|
||||
StructName: "User",
|
||||
StructCamelName: "user",
|
||||
ModuleName: "",
|
||||
GroupName: "",
|
||||
PackageName: "",
|
||||
ServiceName: "",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 gva的User 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginServiceEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "service", "enter.go"),
|
||||
ImportPath: "",
|
||||
StructName: "User",
|
||||
StructCamelName: "user",
|
||||
ModuleName: "",
|
||||
GroupName: "",
|
||||
PackageName: "",
|
||||
ServiceName: "",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PluginEnter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
StructCamelName: tt.fields.StructCamelName,
|
||||
ModuleName: tt.fields.ModuleName,
|
||||
GroupName: tt.fields.GroupName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
ServiceName: tt.fields.ServiceName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Injection(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginEnter_Rollback(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
StructCamelName string
|
||||
ModuleName string
|
||||
GroupName string
|
||||
PackageName string
|
||||
ServiceName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 Gva插件UserRouter 回滚",
|
||||
fields: fields{
|
||||
Type: TypePluginRouterEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "router", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/api"`,
|
||||
StructName: "User",
|
||||
StructCamelName: "user",
|
||||
ModuleName: "userApi",
|
||||
GroupName: "Api",
|
||||
PackageName: "api",
|
||||
ServiceName: "User",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 Gva插件UserApi 回滚",
|
||||
fields: fields{
|
||||
Type: TypePluginApiEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "api", "enter.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/service"`,
|
||||
StructName: "User",
|
||||
StructCamelName: "user",
|
||||
ModuleName: "serviceUser",
|
||||
GroupName: "Service",
|
||||
PackageName: "service",
|
||||
ServiceName: "User",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 Gva插件UserService 回滚",
|
||||
fields: fields{
|
||||
Type: TypePluginServiceEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "service", "enter.go"),
|
||||
ImportPath: "",
|
||||
StructName: "User",
|
||||
StructCamelName: "user",
|
||||
ModuleName: "",
|
||||
GroupName: "",
|
||||
PackageName: "",
|
||||
ServiceName: "",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PluginEnter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
StructCamelName: tt.fields.StructCamelName,
|
||||
ModuleName: tt.fields.ModuleName,
|
||||
GroupName: tt.fields.GroupName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
ServiceName: tt.fields.ServiceName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Rollback(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
189
server/utils/ast/plugin_gen.go
Normal file
189
server/utils/ast/plugin_gen.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
)
|
||||
|
||||
type PluginGen struct {
|
||||
Base
|
||||
Type Type // 类型
|
||||
Path string // 文件路径
|
||||
ImportPath string // 导包路径
|
||||
RelativePath string // 相对路径
|
||||
StructName string // 结构体名称
|
||||
PackageName string // 包名
|
||||
IsNew bool // 是否使用new关键字
|
||||
}
|
||||
|
||||
func (a *PluginGen) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
if filename == "" {
|
||||
if a.RelativePath == "" {
|
||||
filename = a.Path
|
||||
a.RelativePath = a.Base.RelativePath(a.Path)
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
a.Path = a.Base.AbsolutePath(a.RelativePath)
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
func (a *PluginGen) Rollback(file *ast.File) error {
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.FuncDecl)
|
||||
if o1 {
|
||||
for j := 0; j < len(v1.Body.List); j++ {
|
||||
v2, o2 := v1.Body.List[j].(*ast.ExprStmt)
|
||||
if o2 {
|
||||
v3, o3 := v2.X.(*ast.CallExpr)
|
||||
if o3 {
|
||||
v4, o4 := v3.Fun.(*ast.SelectorExpr)
|
||||
if o4 {
|
||||
if v4.Sel.Name != "ApplyBasic" {
|
||||
continue
|
||||
}
|
||||
for k := 0; k < len(v3.Args); k++ {
|
||||
v5, o5 := v3.Args[k].(*ast.CallExpr)
|
||||
if o5 {
|
||||
v6, o6 := v5.Fun.(*ast.Ident)
|
||||
if o6 {
|
||||
if v6.Name != "new" {
|
||||
continue
|
||||
}
|
||||
for l := 0; l < len(v5.Args); l++ {
|
||||
v7, o7 := v5.Args[l].(*ast.SelectorExpr)
|
||||
if o7 {
|
||||
v8, o8 := v7.X.(*ast.Ident)
|
||||
if o8 {
|
||||
if v8.Name == a.PackageName && v7.Sel.Name == a.StructName {
|
||||
v3.Args = append(v3.Args[:k], v3.Args[k+1:]...)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if k >= len(v3.Args) {
|
||||
break
|
||||
}
|
||||
v6, o6 := v3.Args[k].(*ast.CompositeLit)
|
||||
if o6 {
|
||||
v7, o7 := v6.Type.(*ast.SelectorExpr)
|
||||
if o7 {
|
||||
v8, o8 := v7.X.(*ast.Ident)
|
||||
if o8 {
|
||||
if v8.Name == a.PackageName && v7.Sel.Name == a.StructName {
|
||||
v3.Args = append(v3.Args[:k], v3.Args[k+1:]...)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(v3.Args) == 0 {
|
||||
_ = NewImport(a.ImportPath).Rollback(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginGen) Injection(file *ast.File) error {
|
||||
_ = NewImport(a.ImportPath).Injection(file)
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.FuncDecl)
|
||||
if o1 {
|
||||
for j := 0; j < len(v1.Body.List); j++ {
|
||||
v2, o2 := v1.Body.List[j].(*ast.ExprStmt)
|
||||
if o2 {
|
||||
v3, o3 := v2.X.(*ast.CallExpr)
|
||||
if o3 {
|
||||
v4, o4 := v3.Fun.(*ast.SelectorExpr)
|
||||
if o4 {
|
||||
if v4.Sel.Name != "ApplyBasic" {
|
||||
continue
|
||||
}
|
||||
var has bool
|
||||
for k := 0; k < len(v3.Args); k++ {
|
||||
v5, o5 := v3.Args[k].(*ast.CallExpr)
|
||||
if o5 {
|
||||
v6, o6 := v5.Fun.(*ast.Ident)
|
||||
if o6 {
|
||||
if v6.Name != "new" {
|
||||
continue
|
||||
}
|
||||
for l := 0; l < len(v5.Args); l++ {
|
||||
v7, o7 := v5.Args[l].(*ast.SelectorExpr)
|
||||
if o7 {
|
||||
v8, o8 := v7.X.(*ast.Ident)
|
||||
if o8 {
|
||||
if v8.Name == a.PackageName && v7.Sel.Name == a.StructName {
|
||||
has = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
v6, o6 := v3.Args[k].(*ast.CompositeLit)
|
||||
if o6 {
|
||||
v7, o7 := v6.Type.(*ast.SelectorExpr)
|
||||
if o7 {
|
||||
v8, o8 := v7.X.(*ast.Ident)
|
||||
if o8 {
|
||||
if v8.Name == a.PackageName && v7.Sel.Name == a.StructName {
|
||||
has = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
if a.IsNew {
|
||||
arg := &ast.CallExpr{
|
||||
Fun: &ast.Ident{Name: "\n\t\tnew"},
|
||||
Args: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.StructName},
|
||||
},
|
||||
},
|
||||
}
|
||||
v3.Args = append(v3.Args, arg)
|
||||
v3.Args = append(v3.Args, &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: "\n",
|
||||
})
|
||||
break
|
||||
}
|
||||
arg := &ast.CompositeLit{
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.StructName},
|
||||
},
|
||||
}
|
||||
v3.Args = append(v3.Args, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginGen) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
if filename == "" {
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
127
server/utils/ast/plugin_gen_test.go
Normal file
127
server/utils/ast/plugin_gen_test.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPluginGenModel_Injection(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
PackageName string
|
||||
StructName string
|
||||
IsNew bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 GvaUser 结构体注入",
|
||||
fields: fields{
|
||||
Type: TypePluginGen,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "gen", "main.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`,
|
||||
PackageName: "model",
|
||||
StructName: "User",
|
||||
IsNew: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 GvaUser 结构体注入",
|
||||
fields: fields{
|
||||
Type: TypePluginGen,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "gen", "main.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`,
|
||||
PackageName: "model",
|
||||
StructName: "User",
|
||||
IsNew: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PluginGen{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
PackageName: tt.fields.PackageName,
|
||||
StructName: tt.fields.StructName,
|
||||
IsNew: tt.fields.IsNew,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Injection(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginGenModel_Rollback(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
PackageName string
|
||||
StructName string
|
||||
IsNew bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 GvaUser 回滚",
|
||||
fields: fields{
|
||||
Type: TypePluginGen,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "gen", "main.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`,
|
||||
PackageName: "model",
|
||||
StructName: "User",
|
||||
IsNew: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 GvaUser 回滚",
|
||||
fields: fields{
|
||||
Type: TypePluginGen,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "gen", "main.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`,
|
||||
PackageName: "model",
|
||||
StructName: "User",
|
||||
IsNew: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PluginGen{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
PackageName: tt.fields.PackageName,
|
||||
StructName: tt.fields.StructName,
|
||||
IsNew: tt.fields.IsNew,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Rollback(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
243
server/utils/ast/plugin_initialize_gorm.go
Normal file
243
server/utils/ast/plugin_initialize_gorm.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
)
|
||||
|
||||
type PluginInitializeGorm struct {
|
||||
Base
|
||||
Type Type // 类型
|
||||
Path string // 文件路径
|
||||
ImportPath string // 导包路径
|
||||
RelativePath string // 相对路径
|
||||
StructName string // 结构体名称
|
||||
PackageName string // 包名
|
||||
IsNew bool // 是否使用new关键字 true: new(PackageName.StructName) false: &PackageName.StructName{}
|
||||
}
|
||||
|
||||
func (a *PluginInitializeGorm) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
if filename == "" {
|
||||
if a.RelativePath == "" {
|
||||
filename = a.Path
|
||||
a.RelativePath = a.Base.RelativePath(a.Path)
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
a.Path = a.Base.AbsolutePath(a.RelativePath)
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
|
||||
func (a *PluginInitializeGorm) Rollback(file *ast.File) error {
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.FuncDecl)
|
||||
if o1 {
|
||||
if v1.Name.Name != "Gorm" {
|
||||
continue
|
||||
}
|
||||
for j := 0; j < len(v1.Body.List); j++ {
|
||||
v2, o2 := v1.Body.List[j].(*ast.AssignStmt)
|
||||
if o2 {
|
||||
for k := 0; k < len(v2.Rhs); k++ {
|
||||
v3, o3 := v2.Rhs[k].(*ast.CallExpr)
|
||||
if o3 {
|
||||
v4, o4 := v3.Fun.(*ast.SelectorExpr)
|
||||
if o4 {
|
||||
if v4.Sel.Name != "AutoMigrate" {
|
||||
continue
|
||||
}
|
||||
length := len(v3.Args)
|
||||
var removeStruct bool
|
||||
var hasImport bool
|
||||
for l := 0; l < length; l++ {
|
||||
if a.IsNew {
|
||||
v5, o5 := v3.Args[l].(*ast.CallExpr)
|
||||
if o5 {
|
||||
for m := 0; m < len(v5.Args); m++ {
|
||||
v6, o6 := v5.Args[m].(*ast.SelectorExpr)
|
||||
if o6 {
|
||||
v7, o7 := v6.X.(*ast.Ident)
|
||||
if o7 {
|
||||
if v7.Name == a.PackageName && v6.Sel.Name == a.StructName {
|
||||
v3.Args = append(v3.Args[:l], v3.Args[l+1:]...)
|
||||
length--
|
||||
removeStruct = true
|
||||
continue
|
||||
} // TODO 优化 空行未删除
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if l >= length {
|
||||
break
|
||||
} // 防止越界
|
||||
v6, o6 := v3.Args[l].(*ast.UnaryExpr)
|
||||
if o6 {
|
||||
v7, o7 := v6.X.(*ast.CompositeLit)
|
||||
if o7 {
|
||||
v8, o8 := v7.Type.(*ast.SelectorExpr)
|
||||
if o8 {
|
||||
v9, o9 := v8.X.(*ast.Ident)
|
||||
if o9 {
|
||||
if v6.Op == token.AND && v9.Name == a.PackageName && v8.Sel.Name == a.StructName {
|
||||
v3.Args = append(v3.Args[:l], v3.Args[l+1:]...)
|
||||
length--
|
||||
removeStruct = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for l := 0; l < length; l++ {
|
||||
v5, o5 := v3.Args[l].(*ast.CallExpr)
|
||||
if o5 {
|
||||
for m := 0; m < len(v5.Args); m++ {
|
||||
v6, o6 := v5.Args[m].(*ast.SelectorExpr)
|
||||
if o6 {
|
||||
v7, o7 := v6.X.(*ast.Ident)
|
||||
if o7 {
|
||||
if removeStruct {
|
||||
if v7.Name == a.PackageName {
|
||||
hasImport = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // 判断new关键字的package是否有其他结构体使用
|
||||
}
|
||||
v6, o6 := v3.Args[l].(*ast.UnaryExpr)
|
||||
if o6 {
|
||||
v7, o7 := v6.X.(*ast.CompositeLit)
|
||||
if o7 {
|
||||
v8, o8 := v7.Type.(*ast.SelectorExpr)
|
||||
if o8 {
|
||||
v9, o9 := v8.X.(*ast.Ident)
|
||||
if o9 {
|
||||
if v9.Name == a.PackageName {
|
||||
hasImport = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // 判断&关键字的package是否有其他结构体使用
|
||||
}
|
||||
if removeStruct && !hasImport {
|
||||
_ = NewImport(a.ImportPath).Rollback(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginInitializeGorm) Injection(file *ast.File) error {
|
||||
_ = NewImport(a.ImportPath).Injection(file)
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.FuncDecl)
|
||||
if o1 {
|
||||
if v1.Name.Name != "Gorm" {
|
||||
continue
|
||||
}
|
||||
for j := 0; j < len(v1.Body.List); j++ {
|
||||
v2, o2 := v1.Body.List[j].(*ast.AssignStmt)
|
||||
if o2 {
|
||||
for k := 0; k < len(v2.Rhs); k++ {
|
||||
v3, o3 := v2.Rhs[k].(*ast.CallExpr)
|
||||
if o3 {
|
||||
v4, o4 := v3.Fun.(*ast.SelectorExpr)
|
||||
if o4 {
|
||||
if v4.Sel.Name != "AutoMigrate" {
|
||||
continue
|
||||
}
|
||||
var has bool
|
||||
|
||||
for l := 0; l < len(v3.Args); l++ {
|
||||
if a.IsNew {
|
||||
v5, o5 := v3.Args[l].(*ast.CallExpr)
|
||||
if o5 {
|
||||
for m := 0; m < len(v5.Args); m++ {
|
||||
v6, o6 := v5.Args[m].(*ast.SelectorExpr)
|
||||
if o6 {
|
||||
v7, o7 := v6.X.(*ast.Ident)
|
||||
if o7 {
|
||||
if v7.Name == a.PackageName && v6.Sel.Name == a.StructName {
|
||||
has = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
v6, o6 := v3.Args[l].(*ast.UnaryExpr)
|
||||
if o6 {
|
||||
v7, o7 := v6.X.(*ast.CompositeLit)
|
||||
if o7 {
|
||||
v8, o8 := v7.Type.(*ast.SelectorExpr)
|
||||
if o8 {
|
||||
v9, o9 := v8.X.(*ast.Ident)
|
||||
if o9 {
|
||||
if v6.Op == token.AND && v9.Name == a.PackageName && v8.Sel.Name == a.StructName {
|
||||
has = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
if a.IsNew {
|
||||
arg := &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
Name: "\n\t\tnew",
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.StructName},
|
||||
},
|
||||
},
|
||||
}
|
||||
v3.Args = append(v3.Args, arg)
|
||||
v3.Args = append(v3.Args, ast.NewIdent("\n"))
|
||||
} else { // TODO 程序可用,但是格式并没有优雅回车
|
||||
arg := &ast.UnaryExpr{
|
||||
Op: token.AND,
|
||||
X: &ast.CompositeLit{
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.StructName},
|
||||
},
|
||||
},
|
||||
}
|
||||
v3.Args = append(v3.Args, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginInitializeGorm) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
if filename == "" {
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
138
server/utils/ast/plugin_initialize_gorm_test.go
Normal file
138
server/utils/ast/plugin_initialize_gorm_test.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPluginInitializeGorm_Injection(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
PackageName string
|
||||
IsNew bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 &model.User{} 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`,
|
||||
StructName: "User",
|
||||
PackageName: "model",
|
||||
IsNew: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 new(model.ExaCustomer) 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`,
|
||||
StructName: "User",
|
||||
PackageName: "model",
|
||||
IsNew: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 new(model.SysUsers) 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`,
|
||||
StructName: "SysUser",
|
||||
PackageName: "model",
|
||||
IsNew: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PluginInitializeGorm{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
IsNew: tt.fields.IsNew,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Injection(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginInitializeGorm_Rollback(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
StructName string
|
||||
PackageName string
|
||||
IsNew bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 &model.User{} 回滚",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`,
|
||||
StructName: "User",
|
||||
PackageName: "model",
|
||||
IsNew: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "测试 new(model.ExaCustomer) 回滚",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`,
|
||||
StructName: "User",
|
||||
PackageName: "model",
|
||||
IsNew: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PluginInitializeGorm{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: tt.fields.StructName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
IsNew: tt.fields.IsNew,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Rollback(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
249
server/utils/ast/plugin_initialize_router.go
Normal file
249
server/utils/ast/plugin_initialize_router.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PluginInitializeRouter 插件初始化路由
|
||||
// PackageName.AppName.GroupName.FunctionName()
|
||||
type PluginInitializeRouter struct {
|
||||
Base
|
||||
Type Type // 类型
|
||||
Path string // 文件路径
|
||||
ImportPath string // 导包路径
|
||||
ImportGlobalPath string // 导包全局变量路径
|
||||
ImportMiddlewarePath string // 导包中间件路径
|
||||
RelativePath string // 相对路径
|
||||
AppName string // 应用名称
|
||||
GroupName string // 分组名称
|
||||
PackageName string // 包名
|
||||
FunctionName string // 函数名
|
||||
LeftRouterGroupName string // 左路由分组名称
|
||||
RightRouterGroupName string // 右路由分组名称
|
||||
}
|
||||
|
||||
func (a *PluginInitializeRouter) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
if filename == "" {
|
||||
if a.RelativePath == "" {
|
||||
filename = a.Path
|
||||
a.RelativePath = a.Base.RelativePath(a.Path)
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
a.Path = a.Base.AbsolutePath(a.RelativePath)
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
|
||||
func (a *PluginInitializeRouter) Rollback(file *ast.File) error {
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.FuncDecl)
|
||||
if o1 {
|
||||
for j := 0; j < len(v1.Body.List); j++ {
|
||||
v2, o2 := v1.Body.List[j].(*ast.ExprStmt)
|
||||
if o2 {
|
||||
v3, o3 := v2.X.(*ast.CallExpr)
|
||||
if o3 {
|
||||
v4, o4 := v3.Fun.(*ast.SelectorExpr)
|
||||
if o4 {
|
||||
v5, o5 := v4.X.(*ast.SelectorExpr)
|
||||
if o5 {
|
||||
v6, o6 := v5.X.(*ast.SelectorExpr)
|
||||
if o6 {
|
||||
v7, o7 := v6.X.(*ast.Ident)
|
||||
if o7 {
|
||||
if v7.Name == a.PackageName && v6.Sel.Name == a.AppName && v5.Sel.Name == a.GroupName && v4.Sel.Name == a.FunctionName {
|
||||
v1.Body.List = append(v1.Body.List[:j], v1.Body.List[j+1:]...)
|
||||
if len(v1.Body.List) == 3 {
|
||||
if a.ImportMiddlewarePath != "" {
|
||||
_ = NewImport(a.ImportMiddlewarePath).Rollback(file)
|
||||
}
|
||||
if a.ImportGlobalPath != "" {
|
||||
_ = NewImport(a.ImportGlobalPath).Rollback(file)
|
||||
}
|
||||
_ = NewImport(a.ImportPath).Rollback(file)
|
||||
v1.Body.List = nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginInitializeRouter) Injection(file *ast.File) error {
|
||||
_ = NewImport(a.ImportPath).Injection(file)
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.FuncDecl)
|
||||
if o1 {
|
||||
var has bool
|
||||
for j := 0; j < len(v1.Body.List); j++ {
|
||||
v2, o2 := v1.Body.List[j].(*ast.ExprStmt)
|
||||
if o2 {
|
||||
v3, o3 := v2.X.(*ast.CallExpr)
|
||||
if o3 {
|
||||
v4, o4 := v3.Fun.(*ast.SelectorExpr)
|
||||
if o4 {
|
||||
v5, o5 := v4.X.(*ast.SelectorExpr)
|
||||
if o5 {
|
||||
v6, o6 := v5.X.(*ast.SelectorExpr)
|
||||
if o6 {
|
||||
v7, o7 := v6.X.(*ast.Ident)
|
||||
if o7 {
|
||||
if v7.Name == a.PackageName && v6.Sel.Name == a.AppName && v5.Sel.Name == a.GroupName && v4.Sel.Name == a.FunctionName {
|
||||
has = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
if v1.Body == nil {
|
||||
v1.Body = new(ast.BlockStmt)
|
||||
}
|
||||
if v1.Body.List == nil {
|
||||
a.ImportMiddlewarePath = fmt.Sprintf(`"%s/middleware"`, global.GVA_CONFIG.AutoCode.Module)
|
||||
_ = NewImport(a.ImportMiddlewarePath).Injection(file)
|
||||
a.ImportGlobalPath = fmt.Sprintf(`"%s/global"`, global.GVA_CONFIG.AutoCode.Module)
|
||||
_ = NewImport(a.ImportGlobalPath).Injection(file)
|
||||
v1.Body.List = make([]ast.Stmt, 0, 3)
|
||||
public := &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{Name: a.LeftRouterGroupName, Obj: ast.NewObj(ast.Var, a.LeftRouterGroupName)},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "engine"},
|
||||
Sel: &ast.Ident{Name: "Group"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: `global.GVA_CONFIG.System.RouterPrefix`,
|
||||
},
|
||||
},
|
||||
},
|
||||
Sel: &ast.Ident{Name: "Group"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: `""`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
private := &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{Name: a.RightRouterGroupName, Obj: ast.NewObj(ast.Var, a.RightRouterGroupName)},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "engine"},
|
||||
Sel: &ast.Ident{Name: "Group"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: `global.GVA_CONFIG.System.RouterPrefix`,
|
||||
},
|
||||
},
|
||||
},
|
||||
Sel: &ast.Ident{Name: "Group"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: `""`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
useMiddleware := &ast.ExprStmt{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.RightRouterGroupName},
|
||||
Sel: &ast.Ident{Name: "Use"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "middleware"},
|
||||
Sel: &ast.Ident{Name: "JWTAuth"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Sel: &ast.Ident{Name: "Use"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "middleware"},
|
||||
Sel: &ast.Ident{Name: "CasbinHandler"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
v1.Body.List = append(v1.Body.List, public)
|
||||
v1.Body.List = append(v1.Body.List, private)
|
||||
v1.Body.List = append(v1.Body.List, useMiddleware)
|
||||
}
|
||||
body := &ast.ExprStmt{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: a.PackageName},
|
||||
Sel: &ast.Ident{Name: a.AppName},
|
||||
},
|
||||
Sel: &ast.Ident{Name: a.GroupName},
|
||||
},
|
||||
Sel: &ast.Ident{Name: a.FunctionName},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.Ident{Name: a.LeftRouterGroupName},
|
||||
&ast.Ident{Name: a.RightRouterGroupName},
|
||||
},
|
||||
},
|
||||
}
|
||||
v1.Body.List = append(v1.Body.List, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginInitializeRouter) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
if filename == "" {
|
||||
filename = a.Path
|
||||
}
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
155
server/utils/ast/plugin_initialize_router_test.go
Normal file
155
server/utils/ast/plugin_initialize_router_test.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPluginInitializeRouter_Injection(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
AppName string
|
||||
GroupName string
|
||||
PackageName string
|
||||
FunctionName string
|
||||
LeftRouterGroupName string
|
||||
RightRouterGroupName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 Gva插件User 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "router.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/router"`,
|
||||
AppName: "Router",
|
||||
GroupName: "User",
|
||||
PackageName: "router",
|
||||
FunctionName: "Init",
|
||||
LeftRouterGroupName: "public",
|
||||
RightRouterGroupName: "private",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 中文 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "router.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/router"`,
|
||||
AppName: "Router",
|
||||
GroupName: "U中文",
|
||||
PackageName: "router",
|
||||
FunctionName: "Init",
|
||||
LeftRouterGroupName: "public",
|
||||
RightRouterGroupName: "private",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PluginInitializeRouter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
AppName: tt.fields.AppName,
|
||||
GroupName: tt.fields.GroupName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
FunctionName: tt.fields.FunctionName,
|
||||
LeftRouterGroupName: tt.fields.LeftRouterGroupName,
|
||||
RightRouterGroupName: tt.fields.RightRouterGroupName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Injection(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginInitializeRouter_Rollback(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
ImportPath string
|
||||
AppName string
|
||||
GroupName string
|
||||
PackageName string
|
||||
FunctionName string
|
||||
LeftRouterGroupName string
|
||||
RightRouterGroupName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 Gva插件User 回滚",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "router.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/router"`,
|
||||
AppName: "Router",
|
||||
GroupName: "User",
|
||||
PackageName: "router",
|
||||
FunctionName: "Init",
|
||||
LeftRouterGroupName: "public",
|
||||
RightRouterGroupName: "private",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 中文 注入",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "router.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/router"`,
|
||||
AppName: "Router",
|
||||
GroupName: "U中文",
|
||||
PackageName: "router",
|
||||
FunctionName: "Init",
|
||||
LeftRouterGroupName: "public",
|
||||
RightRouterGroupName: "private",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &PluginInitializeRouter{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
AppName: tt.fields.AppName,
|
||||
GroupName: tt.fields.GroupName,
|
||||
PackageName: tt.fields.PackageName,
|
||||
FunctionName: tt.fields.FunctionName,
|
||||
LeftRouterGroupName: tt.fields.LeftRouterGroupName,
|
||||
RightRouterGroupName: tt.fields.RightRouterGroupName,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Rollback(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
52
server/utils/ast/plugin_initialize_v2.go
Normal file
52
server/utils/ast/plugin_initialize_v2.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"io"
|
||||
)
|
||||
|
||||
type PluginInitializeV2 struct {
|
||||
Base
|
||||
Type Type // 类型
|
||||
Path string // 文件路径
|
||||
PluginPath string // 插件路径
|
||||
RelativePath string // 相对路径
|
||||
ImportPath string // 导包路径
|
||||
StructName string // 结构体名称
|
||||
PackageName string // 包名
|
||||
}
|
||||
|
||||
func (a *PluginInitializeV2) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
if filename == "" {
|
||||
if a.RelativePath == "" {
|
||||
filename = a.PluginPath
|
||||
a.RelativePath = a.Base.RelativePath(a.PluginPath)
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
a.PluginPath = a.Base.AbsolutePath(a.RelativePath)
|
||||
filename = a.PluginPath
|
||||
}
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
|
||||
func (a *PluginInitializeV2) Injection(file *ast.File) error {
|
||||
if !CheckImport(file, a.ImportPath) {
|
||||
NewImport(a.ImportPath).Injection(file)
|
||||
funcDecl := FindFunction(file, "bizPluginV2")
|
||||
stmt := CreateStmt(fmt.Sprintf("PluginInitV2(engine, %s.Plugin)", a.PackageName))
|
||||
funcDecl.Body.List = append(funcDecl.Body.List, stmt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginInitializeV2) Rollback(file *ast.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PluginInitializeV2) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
if filename == "" {
|
||||
filename = a.PluginPath
|
||||
}
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
100
server/utils/ast/plugin_initialize_v2_test.go
Normal file
100
server/utils/ast/plugin_initialize_v2_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPluginInitialize_Injection(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
PluginPath string
|
||||
ImportPath string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 Gva插件 注册注入",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeV2,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "plugin_biz_v2.go"),
|
||||
PluginPath: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "plugin.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva"`,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := PluginInitializeV2{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
PluginPath: tt.fields.PluginPath,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Injection(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginInitialize_Rollback(t *testing.T) {
|
||||
type fields struct {
|
||||
Type Type
|
||||
Path string
|
||||
PluginPath string
|
||||
ImportPath string
|
||||
PluginName string
|
||||
StructName string
|
||||
PackageName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 Gva插件 回滚",
|
||||
fields: fields{
|
||||
Type: TypePluginInitializeV2,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "plugin_biz_v2.go"),
|
||||
PluginPath: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "plugin.go"),
|
||||
ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva"`,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := PluginInitializeV2{
|
||||
Type: tt.fields.Type,
|
||||
Path: tt.fields.Path,
|
||||
PluginPath: tt.fields.PluginPath,
|
||||
ImportPath: tt.fields.ImportPath,
|
||||
StructName: "Plugin",
|
||||
PackageName: "gva",
|
||||
}
|
||||
file, err := a.Parse(a.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
a.Rollback(file)
|
||||
err = a.Format(a.Path, nil, file)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user