文件结构调整,支持插件自动化 (#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:
217
server/service/system/auto_code_history.go
Normal file
217
server/service/system/auto_code_history.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils/ast"
|
||||
"github.com/pkg/errors"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
request "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var AutocodeHistory = new(autoCodeHistory)
|
||||
|
||||
type autoCodeHistory struct{}
|
||||
|
||||
// Create 创建代码生成器历史记录
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) Create(ctx context.Context, info request.SysAutoHistoryCreate) error {
|
||||
create := info.Create()
|
||||
err := global.GVA_DB.WithContext(ctx).Create(&create).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "创建失败!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// First 根据id获取代码生成器历史的数据
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) First(ctx context.Context, info common.GetById) (string, error) {
|
||||
var meta string
|
||||
err := global.GVA_DB.WithContext(ctx).Model(model.SysAutoCodeHistory{}).Where("id = ?", info.ID).Pluck("request", &meta).Error
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "获取失败!")
|
||||
}
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
// Repeat 检测重复
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) Repeat(businessDB, structName, Package string) bool {
|
||||
var count int64
|
||||
global.GVA_DB.Model(&model.SysAutoCodeHistory{}).Where("business_db = ? and struct_name = ? and package = ? and flag = 0", businessDB, structName, Package).Count(&count)
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// RollBack 回滚
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) RollBack(ctx context.Context, info request.SysAutoHistoryRollBack) error {
|
||||
var history model.SysAutoCodeHistory
|
||||
err := global.GVA_DB.Where("id = ?", info.ID).First(&history).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.DeleteApi {
|
||||
ids := info.ApiIds(history)
|
||||
err = ApiServiceApp.DeleteApisByIds(ids)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("ClearTag DeleteApiByIds:", zap.Error(err))
|
||||
}
|
||||
} // 清除API表
|
||||
if info.DeleteMenu {
|
||||
err = BaseMenuServiceApp.DeleteBaseMenu(int(history.MenuID))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "删除菜单失败!")
|
||||
}
|
||||
} // 清除菜单表
|
||||
if info.DeleteTable {
|
||||
err = s.DropTable(history.BusinessDB, history.Table)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "删除表失败!")
|
||||
}
|
||||
} // 删除表
|
||||
templates := make(map[string]string, len(history.Templates))
|
||||
for key, template := range history.Templates {
|
||||
{
|
||||
server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server)
|
||||
keys := strings.Split(key, "/")
|
||||
key = filepath.Join(keys...)
|
||||
key = strings.TrimPrefix(key, server)
|
||||
} // key
|
||||
{
|
||||
web := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot())
|
||||
server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server)
|
||||
slices := strings.Split(template, "/")
|
||||
template = filepath.Join(slices...)
|
||||
ext := path.Ext(template)
|
||||
switch ext {
|
||||
case ".js", ".vue":
|
||||
template = filepath.Join(web, template)
|
||||
case ".go":
|
||||
template = filepath.Join(server, template)
|
||||
}
|
||||
} // value
|
||||
templates[key] = template
|
||||
}
|
||||
history.Templates = templates
|
||||
for key, value := range history.Injections {
|
||||
var injection ast.Ast
|
||||
switch key {
|
||||
case ast.TypePackageApiEnter, ast.TypePackageRouterEnter, ast.TypePackageServiceEnter:
|
||||
var entity ast.PackageEnter
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePackageApiModuleEnter, ast.TypePackageRouterModuleEnter, ast.TypePackageServiceModuleEnter:
|
||||
var entity ast.PackageModuleEnter
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePackageInitializeGorm:
|
||||
var entity ast.PackageInitializeGorm
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePackageInitializeRouter:
|
||||
var entity ast.PackageInitializeRouter
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePluginGen:
|
||||
var entity ast.PluginGen
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePluginApiEnter, ast.TypePluginRouterEnter, ast.TypePluginServiceEnter:
|
||||
var entity ast.PluginEnter
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePluginInitializeV2:
|
||||
var entity ast.PluginInitializeV2
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePluginInitializeGorm:
|
||||
var entity ast.PluginInitializeGorm
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePluginInitializeRouter:
|
||||
var entity ast.PluginInitializeRouter
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
}
|
||||
if injection == nil {
|
||||
continue
|
||||
}
|
||||
file, _ := injection.Parse("", nil)
|
||||
if file != nil {
|
||||
_ = injection.Rollback(file)
|
||||
err = injection.Format("", nil, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("[filepath:%s]回滚注入代码成功!\n", key)
|
||||
}
|
||||
} // 清除注入代码
|
||||
removeBasePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, "rm_file", strconv.FormatInt(int64(time.Now().Nanosecond()), 10))
|
||||
for _, value := range history.Templates {
|
||||
if !filepath.IsAbs(value) {
|
||||
continue
|
||||
}
|
||||
removePath := filepath.Join(removeBasePath, strings.TrimPrefix(value, global.GVA_CONFIG.AutoCode.Root))
|
||||
err = utils.FileMove(value, removePath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[src:%s][dst:%s]文件移动失败!", value, removePath)
|
||||
}
|
||||
} // 移动文件
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&model.SysAutoCodeHistory{}).Where("id = ?", info.ID).Update("flag", 1).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "更新失败!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除历史数据
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) Delete(ctx context.Context, info common.GetById) error {
|
||||
err := global.GVA_DB.WithContext(ctx).Where("id = ?", info.Uint()).Delete(&model.SysAutoCodeHistory{}).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "删除失败!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetList 获取系统历史数据
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) GetList(ctx context.Context, info common.PageInfo) (list []model.SysAutoCodeHistory, total int64, err error) {
|
||||
var entities []model.SysAutoCodeHistory
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&model.SysAutoCodeHistory{})
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
err = db.Scopes(info.Paginate()).Order("updated_at desc").Find(&entities).Error
|
||||
return entities, total, err
|
||||
}
|
||||
|
||||
// DropTable 获取指定数据库和指定数据表的所有字段名,类型值等
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
func (s *autoCodeHistory) DropTable(BusinessDb, tableName string) error {
|
||||
if BusinessDb != "" {
|
||||
return global.MustGetGlobalDBByDBName(BusinessDb).Exec("DROP TABLE " + tableName).Error
|
||||
} else {
|
||||
return global.GVA_DB.Exec("DROP TABLE " + tableName).Error
|
||||
}
|
||||
}
|
579
server/service/system/auto_code_package.go
Normal file
579
server/service/system/auto_code_package.go
Normal file
@@ -0,0 +1,579 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils/ast"
|
||||
"github.com/pkg/errors"
|
||||
"go/token"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var AutoCodePackage = new(autoCodePackage)
|
||||
|
||||
type autoCodePackage struct{}
|
||||
|
||||
// Create 创建包信息
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodePackage) Create(ctx context.Context, info *request.SysAutoCodePackageCreate) error {
|
||||
switch {
|
||||
case info.Template == "":
|
||||
return errors.New("模板不能为空!")
|
||||
case info.Template == "page":
|
||||
return errors.New("page为表单生成器!")
|
||||
case info.PackageName == "":
|
||||
return errors.New("PackageName不能为空!")
|
||||
case token.IsKeyword(info.PackageName):
|
||||
return errors.Errorf("%s为go的关键字!", info.PackageName)
|
||||
case info.Template == "package":
|
||||
if info.PackageName == "system" || info.PackageName == "example" {
|
||||
return errors.New("不能使用已保留的package name")
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
if !errors.Is(global.GVA_DB.Where("package_name = ? and template = ?", info.PackageName, info.Template).First(&model.SysAutoCodePackage{}).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("存在相同PackageName")
|
||||
}
|
||||
create := info.Create()
|
||||
return global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
err := tx.Create(&create).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "创建失败!")
|
||||
}
|
||||
code := info.AutoCode()
|
||||
_, asts, creates, err := s.templates(ctx, create, code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, value := range creates { // key 为 模版绝对路径
|
||||
var files *template.Template
|
||||
files, err = template.ParseFiles(key)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", key)
|
||||
}
|
||||
err = os.MkdirAll(filepath.Dir(value), os.ModePerm)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", value)
|
||||
}
|
||||
var file *os.File
|
||||
file, err = os.Create(value)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", value)
|
||||
}
|
||||
err = files.Execute(file, code)
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]生成失败!", value)
|
||||
}
|
||||
fmt.Printf("[template:%s][filepath:%s]生成成功!\n", key, value)
|
||||
}
|
||||
for key, value := range asts {
|
||||
keys := strings.Split(key, "=>")
|
||||
if len(keys) == 2 {
|
||||
if keys[1] == ast.TypePluginInitializeV2 {
|
||||
file, _ := value.Parse("", nil)
|
||||
if file != nil {
|
||||
err = value.Injection(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = value.Format("", nil, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fmt.Printf("[type:%s]注入成功!\n", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除包记录
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodePackage) Delete(ctx context.Context, info common.GetById) error {
|
||||
err := global.GVA_DB.WithContext(ctx).Delete(&model.SysAutoCodePackage{}, info.Uint()).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "删除失败!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// All 获取所有包
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodePackage) All(ctx context.Context) (entities []model.SysAutoCodePackage, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Find(&entities).Error
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "获取所有包失败!")
|
||||
}
|
||||
return entities, nil
|
||||
}
|
||||
|
||||
// Templates 获取所有模版文件夹
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodePackage) Templates(ctx context.Context) ([]string, error) {
|
||||
templates := make([]string, 0)
|
||||
entries, err := os.ReadDir("resource")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "读取模版文件夹失败!")
|
||||
}
|
||||
for i := 0; i < len(entries); i++ {
|
||||
if entries[i].IsDir() {
|
||||
if entries[i].Name() == "page" {
|
||||
continue
|
||||
} // page 为表单生成器
|
||||
if entries[i].Name() == "preview" {
|
||||
continue
|
||||
} // preview 为预览代码生成器的代码
|
||||
templates = append(templates, entries[i].Name())
|
||||
}
|
||||
}
|
||||
return templates, nil
|
||||
}
|
||||
|
||||
func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCodePackage, info request.AutoCode) (code map[string]string, asts map[string]ast.Ast, creates map[string]string, err error) {
|
||||
code = make(map[string]string)
|
||||
asts = make(map[string]ast.Ast)
|
||||
creates = make(map[string]string)
|
||||
templateDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", entity.Template)
|
||||
templateDirs, err := os.ReadDir(templateDir)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", templateDir)
|
||||
}
|
||||
for i := 0; i < len(templateDirs); i++ {
|
||||
second := filepath.Join(templateDir, templateDirs[i].Name())
|
||||
switch templateDirs[i].Name() {
|
||||
case "server":
|
||||
var secondDirs []os.DirEntry
|
||||
secondDirs, err = os.ReadDir(second)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", second)
|
||||
}
|
||||
for j := 0; j < len(secondDirs); j++ {
|
||||
if secondDirs[j].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
three := filepath.Join(second, secondDirs[j].Name())
|
||||
if !secondDirs[j].IsDir() {
|
||||
ext := filepath.Ext(secondDirs[j].Name())
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", three)
|
||||
}
|
||||
name := strings.TrimSuffix(secondDirs[j].Name(), ext)
|
||||
if name == "main.go" || name == "plugin.go" {
|
||||
pluginInitialize := &ast.PluginInitializeV2{
|
||||
Type: ast.TypePluginInitializeV2,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, name),
|
||||
PluginPath: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "plugin_biz_v2.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
PackageName: entity.PackageName,
|
||||
}
|
||||
asts[pluginInitialize.PluginPath+"=>"+pluginInitialize.Type.String()] = pluginInitialize
|
||||
creates[three] = pluginInitialize.Path
|
||||
continue
|
||||
}
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", three)
|
||||
}
|
||||
switch secondDirs[j].Name() {
|
||||
case "api", "router", "service":
|
||||
var threeDirs []os.DirEntry
|
||||
threeDirs, err = os.ReadDir(three)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three)
|
||||
}
|
||||
for k := 0; k < len(threeDirs); k++ {
|
||||
if threeDirs[k].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
four := filepath.Join(three, threeDirs[k].Name())
|
||||
if threeDirs[k].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four)
|
||||
}
|
||||
ext := filepath.Ext(four)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
api := strings.Index(threeDirs[k].Name(), "api")
|
||||
hasEnter := strings.Index(threeDirs[k].Name(), "enter")
|
||||
router := strings.Index(threeDirs[k].Name(), "router")
|
||||
service := strings.Index(threeDirs[k].Name(), "service")
|
||||
if router == -1 && api == -1 && service == -1 && hasEnter == -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four)
|
||||
}
|
||||
if entity.Template == "package" {
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, info.HumpPackageName+".go")
|
||||
if api != -1 {
|
||||
create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", entity.PackageName, info.HumpPackageName+".go")
|
||||
}
|
||||
if hasEnter != -1 {
|
||||
isApi := strings.Index(secondDirs[j].Name(), "api")
|
||||
isRouter := strings.Index(secondDirs[j].Name(), "router")
|
||||
isService := strings.Index(secondDirs[j].Name(), "service")
|
||||
if isApi != -1 {
|
||||
packageApiEnter := &ast.PackageEnter{
|
||||
Type: ast.TypePackageApiEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", "enter.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/%s/%s/%s"`, global.GVA_CONFIG.AutoCode.Module, "api", "v1", entity.PackageName),
|
||||
StructName: info.PackageT + "ApiGroup",
|
||||
PackageName: entity.PackageName,
|
||||
PackageStructName: "ApiGroup",
|
||||
}
|
||||
asts[packageApiEnter.Path+"=>"+packageApiEnter.Type.String()] = packageApiEnter
|
||||
packageApiModuleEnter := &ast.PackageModuleEnter{
|
||||
Type: ast.TypePackageApiModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", entity.PackageName, "enter.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/service"`, global.GVA_CONFIG.AutoCode.Module),
|
||||
StructName: info.StructName + "Api",
|
||||
AppName: "ServiceGroupApp",
|
||||
GroupName: info.PackageT + "ServiceGroup",
|
||||
ModuleName: info.Abbreviation + "Service",
|
||||
PackageName: "service",
|
||||
ServiceName: info.StructName + "Service",
|
||||
}
|
||||
asts[packageApiModuleEnter.Path+"=>"+packageApiModuleEnter.Type.String()] = packageApiModuleEnter
|
||||
creates[four] = packageApiModuleEnter.Path
|
||||
}
|
||||
if isRouter != -1 {
|
||||
packageRouterEnter := &ast.PackageEnter{
|
||||
Type: ast.TypePackageRouterEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "enter.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/%s/%s"`, global.GVA_CONFIG.AutoCode.Module, secondDirs[j].Name(), entity.PackageName),
|
||||
StructName: info.PackageT,
|
||||
PackageName: entity.PackageName,
|
||||
PackageStructName: "RouterGroup",
|
||||
}
|
||||
asts[packageRouterEnter.Path+"=>"+packageRouterEnter.Type.String()] = packageRouterEnter
|
||||
packageRouterModuleEnter := &ast.PackageModuleEnter{
|
||||
Type: ast.TypePackageRouterModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, "enter.go"),
|
||||
ImportPath: fmt.Sprintf(`api "%s/api/v1"`, global.GVA_CONFIG.AutoCode.Module),
|
||||
StructName: info.StructName + "Router",
|
||||
AppName: "ApiGroupApp",
|
||||
GroupName: info.PackageT + "ApiGroup",
|
||||
ModuleName: info.Abbreviation + "Api",
|
||||
PackageName: "api",
|
||||
ServiceName: info.StructName + "Api",
|
||||
}
|
||||
creates[four] = packageRouterModuleEnter.Path
|
||||
asts[packageRouterModuleEnter.Path+"=>"+packageRouterModuleEnter.Type.String()] = packageRouterModuleEnter
|
||||
packageInitializeRouter := &ast.PackageInitializeRouter{
|
||||
Type: ast.TypePackageInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/router"`, global.GVA_CONFIG.AutoCode.Module),
|
||||
AppName: "RouterGroupApp",
|
||||
GroupName: info.PackageT,
|
||||
ModuleName: entity.PackageName + "Router",
|
||||
PackageName: "router",
|
||||
FunctionName: "Init" + info.StructName + "Router",
|
||||
LeftRouterGroupName: "privateGroup",
|
||||
RightRouterGroupName: "publicGroup",
|
||||
}
|
||||
asts[packageInitializeRouter.Path+"=>"+packageInitializeRouter.Type.String()] = packageInitializeRouter
|
||||
}
|
||||
if isService != -1 {
|
||||
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext))
|
||||
importPath := fmt.Sprintf(`"%s/service/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName)
|
||||
packageServiceEnter := &ast.PackageEnter{
|
||||
Type: ast.TypePackageServiceEnter,
|
||||
Path: path,
|
||||
ImportPath: importPath,
|
||||
StructName: info.PackageT + "ServiceGroup",
|
||||
PackageName: entity.PackageName,
|
||||
PackageStructName: "ServiceGroup",
|
||||
}
|
||||
asts[packageServiceEnter.Path+"=>"+packageServiceEnter.Type.String()] = packageServiceEnter
|
||||
packageServiceModuleEnter := &ast.PackageModuleEnter{
|
||||
Type: ast.TypePackageServiceModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, "enter.go"),
|
||||
StructName: info.StructName + "Service",
|
||||
}
|
||||
asts[packageServiceModuleEnter.Path+"=>"+packageServiceModuleEnter.Type.String()] = packageServiceModuleEnter
|
||||
creates[four] = packageServiceModuleEnter.Path
|
||||
}
|
||||
continue
|
||||
}
|
||||
code[four] = create
|
||||
continue
|
||||
}
|
||||
if hasEnter != -1 {
|
||||
isApi := strings.Index(secondDirs[j].Name(), "api")
|
||||
isRouter := strings.Index(secondDirs[j].Name(), "router")
|
||||
isService := strings.Index(secondDirs[j].Name(), "service")
|
||||
if isRouter != -1 {
|
||||
pluginRouterEnter := &ast.PluginEnter{
|
||||
Type: ast.TypePluginRouterEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/api"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
StructName: info.StructName,
|
||||
StructCamelName: info.Abbreviation,
|
||||
ModuleName: "api" + info.StructName,
|
||||
GroupName: "Api",
|
||||
PackageName: "api",
|
||||
ServiceName: info.StructName,
|
||||
}
|
||||
asts[pluginRouterEnter.Path+"=>"+pluginRouterEnter.Type.String()] = pluginRouterEnter
|
||||
creates[four] = pluginRouterEnter.Path
|
||||
}
|
||||
if isApi != -1 {
|
||||
pluginApiEnter := &ast.PluginEnter{
|
||||
Type: ast.TypePluginApiEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/service"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
StructName: info.StructName,
|
||||
StructCamelName: info.Abbreviation,
|
||||
ModuleName: "service" + info.StructName,
|
||||
GroupName: "Service",
|
||||
PackageName: "service",
|
||||
ServiceName: info.StructName,
|
||||
}
|
||||
asts[pluginApiEnter.Path+"=>"+pluginApiEnter.Type.String()] = pluginApiEnter
|
||||
creates[four] = pluginApiEnter.Path
|
||||
}
|
||||
if isService != -1 {
|
||||
pluginServiceEnter := &ast.PluginEnter{
|
||||
Type: ast.TypePluginServiceEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
StructName: info.StructName,
|
||||
StructCamelName: info.Abbreviation,
|
||||
}
|
||||
asts[pluginServiceEnter.Path+"=>"+pluginServiceEnter.Type.String()] = pluginServiceEnter
|
||||
creates[four] = pluginServiceEnter.Path
|
||||
}
|
||||
continue
|
||||
} // enter.go
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), info.HumpPackageName+".go")
|
||||
code[four] = create
|
||||
}
|
||||
case "gen", "config", "initialize", "plugin", "response":
|
||||
if entity.Template == "package" {
|
||||
continue
|
||||
} // package模板不需要生成gen, config, initialize
|
||||
var threeDirs []os.DirEntry
|
||||
threeDirs, err = os.ReadDir(three)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three)
|
||||
}
|
||||
for k := 0; k < len(threeDirs); k++ {
|
||||
if threeDirs[k].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
four := filepath.Join(three, threeDirs[k].Name())
|
||||
if threeDirs[k].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four)
|
||||
}
|
||||
ext := filepath.Ext(four)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
gen := strings.Index(threeDirs[k].Name(), "gen")
|
||||
api := strings.Index(threeDirs[k].Name(), "api")
|
||||
menu := strings.Index(threeDirs[k].Name(), "menu")
|
||||
viper := strings.Index(threeDirs[k].Name(), "viper")
|
||||
plugin := strings.Index(threeDirs[k].Name(), "plugin")
|
||||
config := strings.Index(threeDirs[k].Name(), "config")
|
||||
router := strings.Index(threeDirs[k].Name(), "router")
|
||||
hasGorm := strings.Index(threeDirs[k].Name(), "gorm")
|
||||
response := strings.Index(threeDirs[k].Name(), "response")
|
||||
if gen != -1 && api != -1 && menu != -1 && viper != -1 && plugin != -1 && config != -1 && router != -1 && hasGorm != -1 && response != -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four)
|
||||
}
|
||||
if api != -1 || menu != -1 || viper != -1 || response != -1 || plugin != -1 || config != -1 {
|
||||
creates[four] = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext))
|
||||
}
|
||||
if gen != -1 {
|
||||
pluginGen := &ast.PluginGen{
|
||||
Type: ast.TypePluginGen,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/model"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
StructName: info.StructName,
|
||||
PackageName: "model",
|
||||
IsNew: true,
|
||||
}
|
||||
asts[pluginGen.Path+"=>"+pluginGen.Type.String()] = pluginGen
|
||||
creates[four] = pluginGen.Path
|
||||
}
|
||||
if hasGorm != -1 {
|
||||
pluginInitializeGorm := &ast.PluginInitializeGorm{
|
||||
Type: ast.TypePluginInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/model"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
StructName: info.StructName,
|
||||
PackageName: "model",
|
||||
IsNew: true,
|
||||
}
|
||||
asts[pluginInitializeGorm.Path+"=>"+pluginInitializeGorm.Type.String()] = pluginInitializeGorm
|
||||
creates[four] = pluginInitializeGorm.Path
|
||||
}
|
||||
if router != -1 {
|
||||
pluginInitializeRouter := &ast.PluginInitializeRouter{
|
||||
Type: ast.TypePluginInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/router"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
AppName: "Router",
|
||||
GroupName: info.StructName,
|
||||
PackageName: "router",
|
||||
FunctionName: "Init",
|
||||
LeftRouterGroupName: "public",
|
||||
RightRouterGroupName: "private",
|
||||
}
|
||||
asts[pluginInitializeRouter.Path+"=>"+pluginInitializeRouter.Type.String()] = pluginInitializeRouter
|
||||
creates[four] = pluginInitializeRouter.Path
|
||||
}
|
||||
}
|
||||
case "model":
|
||||
var threeDirs []os.DirEntry
|
||||
threeDirs, err = os.ReadDir(three)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three)
|
||||
}
|
||||
for k := 0; k < len(threeDirs); k++ {
|
||||
if threeDirs[k].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
four := filepath.Join(three, threeDirs[k].Name())
|
||||
if threeDirs[k].IsDir() {
|
||||
var fourDirs []os.DirEntry
|
||||
fourDirs, err = os.ReadDir(four)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", four)
|
||||
}
|
||||
for l := 0; l < len(fourDirs); l++ {
|
||||
if fourDirs[l].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
five := filepath.Join(four, fourDirs[l].Name())
|
||||
if fourDirs[l].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", five)
|
||||
}
|
||||
ext := filepath.Ext(five)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", five)
|
||||
}
|
||||
hasRequest := strings.Index(fourDirs[l].Name(), "request")
|
||||
if hasRequest == -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", five)
|
||||
}
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), threeDirs[k].Name(), info.HumpPackageName+".go")
|
||||
if entity.Template == "package" {
|
||||
create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, threeDirs[k].Name(), info.HumpPackageName+".go")
|
||||
}
|
||||
code[five] = create
|
||||
}
|
||||
continue
|
||||
}
|
||||
ext := filepath.Ext(threeDirs[k].Name())
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
hasModel := strings.Index(threeDirs[k].Name(), "model")
|
||||
if hasModel == -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four)
|
||||
}
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), info.HumpPackageName+".go")
|
||||
if entity.Template == "package" {
|
||||
packageInitializeGorm := &ast.PackageInitializeGorm{
|
||||
Type: ast.TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/model/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
Business: info.BusinessDB,
|
||||
StructName: info.StructName,
|
||||
PackageName: entity.PackageName,
|
||||
IsNew: true,
|
||||
}
|
||||
code[four] = packageInitializeGorm.Path
|
||||
asts[packageInitializeGorm.Path+"=>"+packageInitializeGorm.Type.String()] = packageInitializeGorm
|
||||
create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, info.HumpPackageName+".go")
|
||||
}
|
||||
code[four] = create
|
||||
}
|
||||
default:
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", three)
|
||||
}
|
||||
}
|
||||
case "web":
|
||||
var secondDirs []os.DirEntry
|
||||
secondDirs, err = os.ReadDir(second)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", second)
|
||||
}
|
||||
for j := 0; j < len(secondDirs); j++ {
|
||||
if secondDirs[j].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
three := filepath.Join(second, secondDirs[j].Name())
|
||||
if !secondDirs[j].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", three)
|
||||
}
|
||||
switch secondDirs[j].Name() {
|
||||
case "api", "form", "view", "table":
|
||||
var threeDirs []os.DirEntry
|
||||
threeDirs, err = os.ReadDir(three)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three)
|
||||
}
|
||||
for k := 0; k < len(threeDirs); k++ {
|
||||
if threeDirs[k].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
four := filepath.Join(three, threeDirs[k].Name())
|
||||
if threeDirs[k].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four)
|
||||
}
|
||||
ext := filepath.Ext(four)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
api := strings.Index(threeDirs[k].Name(), "api")
|
||||
form := strings.Index(threeDirs[k].Name(), "form")
|
||||
view := strings.Index(threeDirs[k].Name(), "view")
|
||||
table := strings.Index(threeDirs[k].Name(), "table")
|
||||
if api == -1 && form == -1 && view == -1 && table == -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four)
|
||||
}
|
||||
if entity.Template == "package" {
|
||||
if view != -1 || table != -1 {
|
||||
formPath := filepath.Join(three, "form.vue"+ext)
|
||||
value, ok := code[formPath]
|
||||
if ok {
|
||||
value = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName, info.Abbreviation+"Form"+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
|
||||
code[formPath] = value
|
||||
}
|
||||
}
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName, info.Abbreviation+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
|
||||
if api != -1 {
|
||||
create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.Abbreviation+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
|
||||
}
|
||||
code[four] = create
|
||||
continue
|
||||
}
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), "plugin", entity.PackageName, secondDirs[j].Name(), info.Abbreviation+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
|
||||
code[four] = create
|
||||
}
|
||||
default:
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", three)
|
||||
}
|
||||
}
|
||||
case "readme.txt.tpl", "readme.txt.template":
|
||||
continue
|
||||
default:
|
||||
if templateDirs[i].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", second)
|
||||
}
|
||||
}
|
||||
return code, asts, creates, nil
|
||||
}
|
105
server/service/system/auto_code_package_test.go
Normal file
105
server/service/system/auto_code_package_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_autoCodePackage_Create(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
info *request.SysAutoCodePackageCreate
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 package",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
info: &request.SysAutoCodePackageCreate{
|
||||
Template: "package",
|
||||
PackageName: "gva",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 plugin",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
info: &request.SysAutoCodePackageCreate{
|
||||
Template: "plugin",
|
||||
PackageName: "gva",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &autoCodePackage{}
|
||||
if err := a.Create(tt.args.ctx, tt.args.info); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_autoCodePackage_templates(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
entity model.SysAutoCodePackage
|
||||
info request.AutoCode
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantCode map[string]string
|
||||
wantEnter map[string]map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试1",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
entity: model.SysAutoCodePackage{
|
||||
Desc: "描述",
|
||||
Label: "展示名",
|
||||
Template: "plugin",
|
||||
PackageName: "preview",
|
||||
},
|
||||
info: request.AutoCode{
|
||||
Abbreviation: "user",
|
||||
HumpPackageName: "user",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &autoCodePackage{}
|
||||
gotCode, gotEnter, gotCreates, err := s.templates(tt.args.ctx, tt.args.entity, tt.args.info)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("templates() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
for key, value := range gotCode {
|
||||
t.Logf("\n")
|
||||
t.Logf(key)
|
||||
t.Logf(value)
|
||||
t.Logf("\n")
|
||||
}
|
||||
t.Log(gotCreates)
|
||||
if !reflect.DeepEqual(gotEnter, tt.wantEnter) {
|
||||
t.Errorf("templates() gotEnter = %v, want %v", gotEnter, tt.wantEnter)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
174
server/service/system/auto_code_plugin.go
Normal file
174
server/service/system/auto_code_plugin.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/mholt/archiver/v4"
|
||||
cp "github.com/otiai10/copy"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var AutoCodePlugin = new(autoCodePlugin)
|
||||
|
||||
type autoCodePlugin struct{}
|
||||
|
||||
// Install 插件安装
|
||||
func (s *autoCodePlugin) Install(file *multipart.FileHeader) (web, server int, err error) {
|
||||
const GVAPLUGPINATH = "./gva-plug-temp/"
|
||||
defer os.RemoveAll(GVAPLUGPINATH)
|
||||
_, err = os.Stat(GVAPLUGPINATH)
|
||||
if os.IsNotExist(err) {
|
||||
os.Mkdir(GVAPLUGPINATH, os.ModePerm)
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
out, err := os.Create(GVAPLUGPINATH + file.Filename)
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, src)
|
||||
|
||||
paths, err := utils.Unzip(GVAPLUGPINATH+file.Filename, GVAPLUGPINATH)
|
||||
paths = filterFile(paths)
|
||||
var webIndex = -1
|
||||
var serverIndex = -1
|
||||
webPlugin := ""
|
||||
serverPlugin := ""
|
||||
|
||||
for i := range paths {
|
||||
paths[i] = filepath.ToSlash(paths[i])
|
||||
pathArr := strings.Split(paths[i], "/")
|
||||
ln := len(pathArr)
|
||||
|
||||
if ln < 4 {
|
||||
continue
|
||||
}
|
||||
if pathArr[2]+"/"+pathArr[3] == `server/plugin` && len(serverPlugin) == 0 {
|
||||
serverPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3])
|
||||
}
|
||||
if pathArr[2]+"/"+pathArr[3] == `web/plugin` && len(webPlugin) == 0 {
|
||||
webPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3])
|
||||
}
|
||||
}
|
||||
if len(serverPlugin) == 0 && len(webPlugin) == 0 {
|
||||
zap.L().Error("非标准插件,请按照文档自动迁移使用")
|
||||
return webIndex, serverIndex, errors.New("非标准插件,请按照文档自动迁移使用")
|
||||
}
|
||||
|
||||
if len(serverPlugin) != 0 {
|
||||
err = installation(serverPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Server)
|
||||
if err != nil {
|
||||
return webIndex, serverIndex, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(webPlugin) != 0 {
|
||||
err = installation(webPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Web)
|
||||
if err != nil {
|
||||
return webIndex, serverIndex, err
|
||||
}
|
||||
}
|
||||
|
||||
return 1, 1, err
|
||||
}
|
||||
|
||||
func installation(path string, formPath string, toPath string) error {
|
||||
arr := strings.Split(filepath.ToSlash(path), "/")
|
||||
ln := len(arr)
|
||||
if ln < 3 {
|
||||
return errors.New("arr")
|
||||
}
|
||||
name := arr[ln-3]
|
||||
|
||||
var form = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + formPath + "/" + path)
|
||||
var to = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + toPath + "/plugin/")
|
||||
_, err := os.Stat(to + name)
|
||||
if err == nil {
|
||||
zap.L().Error("autoPath 已存在同名插件,请自行手动安装", zap.String("to", to))
|
||||
return errors.New(toPath + "已存在同名插件,请自行手动安装")
|
||||
}
|
||||
return cp.Copy(form, to, cp.Options{Skip: skipMacSpecialDocument})
|
||||
}
|
||||
|
||||
func filterFile(paths []string) []string {
|
||||
np := make([]string, 0, len(paths))
|
||||
for _, path := range paths {
|
||||
if ok, _ := skipMacSpecialDocument(path); ok {
|
||||
continue
|
||||
}
|
||||
np = append(np, path)
|
||||
}
|
||||
return np
|
||||
}
|
||||
|
||||
func skipMacSpecialDocument(src string) (bool, error) {
|
||||
if strings.Contains(src, ".DS_Store") || strings.Contains(src, "__MACOSX") {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *autoCodePlugin) PubPlug(plugName string) (zipPath string, err error) {
|
||||
if plugName == "" {
|
||||
return "", errors.New("插件名称不能为空")
|
||||
}
|
||||
|
||||
// 防止路径穿越
|
||||
plugName = filepath.Clean(plugName)
|
||||
|
||||
webPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", plugName)
|
||||
serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", plugName)
|
||||
// 创建一个新的zip文件
|
||||
|
||||
// 判断目录是否存在
|
||||
_, err = os.Stat(webPath)
|
||||
if err != nil {
|
||||
return "", errors.New("web路径不存在")
|
||||
}
|
||||
_, err = os.Stat(serverPath)
|
||||
if err != nil {
|
||||
return "", errors.New("server路径不存在")
|
||||
}
|
||||
|
||||
fileName := plugName + ".zip"
|
||||
// 创建一个新的zip文件
|
||||
files, err := archiver.FilesFromDisk(nil, map[string]string{
|
||||
webPath: plugName + "/web/plugin/" + plugName,
|
||||
serverPath: plugName + "/server/plugin/" + plugName,
|
||||
})
|
||||
|
||||
// create the output file we'll write to
|
||||
out, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// we can use the CompressedArchive type to gzip a tarball
|
||||
// (compression is not required; you could use Tar directly)
|
||||
format := archiver.CompressedArchive{
|
||||
Archival: archiver.Zip{},
|
||||
}
|
||||
|
||||
// create the archive
|
||||
err = format.Archive(context.Background(), out, files)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fileName), nil
|
||||
}
|
171
server/service/system/auto_code_template.go
Normal file
171
server/service/system/auto_code_template.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils/ast"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var AutoCodeTemplate = new(autoCodeTemplate)
|
||||
|
||||
type autoCodeTemplate struct{}
|
||||
|
||||
// Create 创建生成自动化代码
|
||||
func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) error {
|
||||
history := info.History()
|
||||
var autoPkg model.SysAutoCodePackage
|
||||
err := global.GVA_DB.WithContext(ctx).Where("package_name = ?", info.Package).First(&autoPkg).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "查询包失败!")
|
||||
}
|
||||
|
||||
// 增加判断: 重复创建struct
|
||||
if AutocodeHistory.Repeat(info.BusinessDB, info.StructName, info.Package) {
|
||||
return errors.New("已经创建过此数据结构,请勿重复创建!")
|
||||
}
|
||||
|
||||
// 自动创建api
|
||||
if info.AutoCreateApiToSql {
|
||||
apis := info.Apis()
|
||||
err := global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for _, v := range apis {
|
||||
var api model.SysApi
|
||||
err := tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error
|
||||
if err == nil {
|
||||
return errors.New("存在相同的API,请关闭自动创建API功能")
|
||||
}
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if err = tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
|
||||
return err
|
||||
}
|
||||
history.ApiIDs = append(history.ApiIDs, v.ID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 自动创建menu
|
||||
if info.AutoCreateMenuToSql {
|
||||
var entity model.SysBaseMenu
|
||||
err := global.GVA_DB.WithContext(ctx).First(&entity, "name = ?", info.Abbreviation).Error
|
||||
if err == nil {
|
||||
return errors.New("存在相同的菜单路由,请关闭自动创建菜单功能")
|
||||
}
|
||||
entity = info.Menu(autoPkg.Template)
|
||||
err = global.GVA_DB.WithContext(ctx).Create(&entity).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "创建菜单失败!")
|
||||
}
|
||||
history.MenuID = entity.ID
|
||||
}
|
||||
|
||||
generate, templates, injections, err := s.generate(ctx, info, autoPkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, builder := range generate {
|
||||
err = os.MkdirAll(filepath.Dir(key), os.ModePerm)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", key)
|
||||
}
|
||||
err = os.WriteFile(key, []byte(builder.String()), 0666)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]写入文件失败!", key)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建历史记录
|
||||
history.Templates = templates
|
||||
history.Injections = make(map[string]string, len(injections))
|
||||
for key, value := range injections {
|
||||
bytes, _ := json.Marshal(value)
|
||||
history.Injections[key] = string(bytes)
|
||||
}
|
||||
err = AutocodeHistory.Create(ctx, history)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Preview 预览自动化代码
|
||||
func (s *autoCodeTemplate) Preview(ctx context.Context, info request.AutoCode) (map[string]string, error) {
|
||||
var entity model.SysAutoCodePackage
|
||||
err := global.GVA_DB.WithContext(ctx).Where("package_name = ?", info.Package).First(&entity).Error
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "查询包失败!")
|
||||
}
|
||||
codes := make(map[string]strings.Builder)
|
||||
preview := make(map[string]string)
|
||||
codes, _, _, err = s.generate(ctx, info, entity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for key, writer := range codes {
|
||||
if len(key) > len(global.GVA_CONFIG.AutoCode.Root) {
|
||||
key, _ = filepath.Rel(global.GVA_CONFIG.AutoCode.Root, key)
|
||||
}
|
||||
var builder strings.Builder
|
||||
builder.WriteString("```\n\n")
|
||||
builder.WriteString(writer.String())
|
||||
builder.WriteString("\n\n```")
|
||||
preview[key] = builder.String()
|
||||
}
|
||||
return preview, nil
|
||||
}
|
||||
|
||||
func (s *autoCodeTemplate) generate(ctx context.Context, info request.AutoCode, entity model.SysAutoCodePackage) (map[string]strings.Builder, map[string]string, map[string]ast.Ast, error) {
|
||||
templates, asts, _, err := AutoCodePackage.templates(ctx, entity, info)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
code := make(map[string]strings.Builder)
|
||||
for key, create := range templates {
|
||||
var files *template.Template
|
||||
files, err = template.ParseFiles(key)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "[filpath:%s]读取模版文件失败!", key)
|
||||
}
|
||||
var builder strings.Builder
|
||||
err = files.Execute(&builder, info)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "[filpath:%s]生成文件失败!", create)
|
||||
}
|
||||
code[create] = builder
|
||||
} // 生成文件
|
||||
injections := make(map[string]ast.Ast, len(asts))
|
||||
if info.AutoMigrate {
|
||||
for key, value := range asts {
|
||||
keys := strings.Split(key, "=>")
|
||||
if len(keys) == 2 {
|
||||
var builder strings.Builder
|
||||
parse, _ := value.Parse("", &builder)
|
||||
if parse != nil {
|
||||
_ = value.Injection(parse)
|
||||
err = value.Format("", &builder, parse)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
code[keys[0]] = builder
|
||||
injections[keys[1]] = value
|
||||
fmt.Println(keys[0], "注入成功!")
|
||||
}
|
||||
}
|
||||
}
|
||||
} // 注入代码
|
||||
return code, templates, injections, nil
|
||||
}
|
84
server/service/system/auto_code_template_test.go
Normal file
84
server/service/system/auto_code_template_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_autoCodeTemplate_Create(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
info request.AutoCode
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &autoCodeTemplate{}
|
||||
if err := s.Create(tt.args.ctx, tt.args.info); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_autoCodeTemplate_Preview(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
info request.AutoCode
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 package",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
info: request.AutoCode{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 plugin",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
info: request.AutoCode{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
testJson := `{"structName":"SysUser","tableName":"sys_users","packageName":"sysUsers","package":"gva","abbreviation":"sysUsers","description":"sysUsers表","businessDB":"","autoCreateApiToSql":true,"autoCreateMenuToSql":true,"autoMigrate":true,"gvaModel":true,"autoCreateResource":false,"fields":[{"fieldName":"Uuid","fieldDesc":"用户UUID","fieldType":"string","dataType":"varchar","fieldJson":"uuid","primaryKey":false,"dataTypeLong":"191","columnName":"uuid","comment":"用户UUID","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Username","fieldDesc":"用户登录名","fieldType":"string","dataType":"varchar","fieldJson":"username","primaryKey":false,"dataTypeLong":"191","columnName":"username","comment":"用户登录名","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Password","fieldDesc":"用户登录密码","fieldType":"string","dataType":"varchar","fieldJson":"password","primaryKey":false,"dataTypeLong":"191","columnName":"password","comment":"用户登录密码","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"NickName","fieldDesc":"用户昵称","fieldType":"string","dataType":"varchar","fieldJson":"nickName","primaryKey":false,"dataTypeLong":"191","columnName":"nick_name","comment":"用户昵称","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"SideMode","fieldDesc":"用户侧边主题","fieldType":"string","dataType":"varchar","fieldJson":"sideMode","primaryKey":false,"dataTypeLong":"191","columnName":"side_mode","comment":"用户侧边主题","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"HeaderImg","fieldDesc":"用户头像","fieldType":"string","dataType":"varchar","fieldJson":"headerImg","primaryKey":false,"dataTypeLong":"191","columnName":"header_img","comment":"用户头像","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"BaseColor","fieldDesc":"基础颜色","fieldType":"string","dataType":"varchar","fieldJson":"baseColor","primaryKey":false,"dataTypeLong":"191","columnName":"base_color","comment":"基础颜色","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"AuthorityId","fieldDesc":"用户角色ID","fieldType":"int","dataType":"bigint","fieldJson":"authorityId","primaryKey":false,"dataTypeLong":"20","columnName":"authority_id","comment":"用户角色ID","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Phone","fieldDesc":"用户手机号","fieldType":"string","dataType":"varchar","fieldJson":"phone","primaryKey":false,"dataTypeLong":"191","columnName":"phone","comment":"用户手机号","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Email","fieldDesc":"用户邮箱","fieldType":"string","dataType":"varchar","fieldJson":"email","primaryKey":false,"dataTypeLong":"191","columnName":"email","comment":"用户邮箱","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Enable","fieldDesc":"用户是否被冻结 1正常 2冻结","fieldType":"int","dataType":"bigint","fieldJson":"enable","primaryKey":false,"dataTypeLong":"19","columnName":"enable","comment":"用户是否被冻结 1正常 2冻结","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}}],"humpPackageName":"sys_users"}`
|
||||
err := json.Unmarshal([]byte(testJson), &tt.args.info)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = tt.args.info.Pretreatment()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
got, err := AutoCodeTemplate.Preview(tt.args.ctx, tt.args.info)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Preview() error = %+v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Preview() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -12,9 +12,13 @@ type ServiceGroup struct {
|
||||
AuthorityService
|
||||
DictionaryService
|
||||
SystemConfigService
|
||||
AutoCodeHistoryService
|
||||
OperationRecordService
|
||||
DictionaryDetailService
|
||||
AuthorityBtnService
|
||||
SysExportTemplateService
|
||||
|
||||
AutoCodePlugin autoCodePlugin
|
||||
AutoCodePackage autoCodePackage
|
||||
AutoCodeHistory autoCodeHistory
|
||||
AutoCodeTemplate autoCodeTemplate
|
||||
}
|
||||
|
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
type JwtService struct{}
|
||||
|
||||
var JwtServiceApp = new(JwtService)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: JsonInBlacklist
|
||||
//@description: 拉黑jwt
|
||||
|
@@ -215,7 +215,7 @@ func (apiService *ApiService) GetAPIInfoList(api system.SysApi, info request.Pag
|
||||
//@return: apis []model.SysApi, err error
|
||||
|
||||
func (apiService *ApiService) GetAllApis() (apis []system.SysApi, err error) {
|
||||
err = global.GVA_DB.Find(&apis).Error
|
||||
err = global.GVA_DB.Order("id desc").Find(&apis).Error
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,8 @@ import (
|
||||
|
||||
type AuthorityBtnService struct{}
|
||||
|
||||
var AuthorityBtnServiceApp = new(AuthorityBtnService)
|
||||
|
||||
func (a *AuthorityBtnService) GetAuthorityBtn(req request.SysAuthorityBtnReq) (res response.SysAuthorityBtnRes, err error) {
|
||||
var authorityBtn []system.SysAuthorityBtn
|
||||
err = global.GVA_DB.Find(&authorityBtn, "authority_id = ? and sys_menu_id = ?", req.AuthorityId, req.MenuID).Error
|
||||
|
@@ -1,956 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/mholt/archiver/v4"
|
||||
|
||||
ast2 "github.com/flipped-aurora/gin-vue-admin/server/utils/ast"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/resource/autocode_template/subcontract"
|
||||
cp "github.com/otiai10/copy"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
autoPath = "autocode_template/"
|
||||
autocodePath = "resource/autocode_template"
|
||||
plugServerPath = "resource/plug_template/server"
|
||||
plugWebPath = "resource/plug_template/web"
|
||||
packageService = "service/%s/enter.go"
|
||||
packageServiceName = "service"
|
||||
packageRouter = "router/%s/enter.go"
|
||||
packageRouterName = "router"
|
||||
packageAPI = "api/v1/%s/enter.go"
|
||||
packageAPIName = "api/v1"
|
||||
)
|
||||
|
||||
type autoPackage struct {
|
||||
path string
|
||||
temp string
|
||||
name string
|
||||
}
|
||||
|
||||
var (
|
||||
packageInjectionMap map[string]astInjectionMeta
|
||||
injectionPaths []injectionMeta
|
||||
)
|
||||
|
||||
func Init(Package string) {
|
||||
injectionPaths = []injectionMeta{
|
||||
{
|
||||
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SApi, Package), "enter.go"),
|
||||
funcName: "ApiGroup",
|
||||
structNameF: "%sApi",
|
||||
},
|
||||
{
|
||||
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRouter, Package), "enter.go"),
|
||||
funcName: "RouterGroup",
|
||||
structNameF: "%sRouter",
|
||||
},
|
||||
{
|
||||
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SService, Package), "enter.go"),
|
||||
funcName: "ServiceGroup",
|
||||
structNameF: "%sService",
|
||||
},
|
||||
}
|
||||
|
||||
packageInjectionMap = map[string]astInjectionMeta{
|
||||
packageServiceName: {
|
||||
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, "service", "enter.go"),
|
||||
importCodeF: "github.com/flipped-aurora/gin-vue-admin/server/%s/%s",
|
||||
packageNameF: "%s",
|
||||
groupName: "ServiceGroup",
|
||||
structNameF: "%sServiceGroup",
|
||||
},
|
||||
packageRouterName: {
|
||||
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, "router", "enter.go"),
|
||||
importCodeF: "github.com/flipped-aurora/gin-vue-admin/server/%s/%s",
|
||||
packageNameF: "%s",
|
||||
groupName: "RouterGroup",
|
||||
structNameF: "%s",
|
||||
},
|
||||
packageAPIName: {
|
||||
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, "api/v1", "enter.go"),
|
||||
importCodeF: "github.com/flipped-aurora/gin-vue-admin/server/%s/%s",
|
||||
packageNameF: "%s",
|
||||
groupName: "ApiGroup",
|
||||
structNameF: "%sApiGroup",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type injectionMeta struct {
|
||||
path string
|
||||
funcName string
|
||||
structNameF string // 带格式化的
|
||||
}
|
||||
|
||||
type astInjectionMeta struct {
|
||||
path string
|
||||
importCodeF string
|
||||
structNameF string
|
||||
packageNameF string
|
||||
groupName string
|
||||
}
|
||||
|
||||
type tplData struct {
|
||||
template *template.Template
|
||||
autoPackage string
|
||||
locationPath string
|
||||
autoCodePath string
|
||||
autoMoveFilePath string
|
||||
}
|
||||
|
||||
type AutoCodeService struct{}
|
||||
|
||||
var AutoCodeServiceApp = new(AutoCodeService)
|
||||
|
||||
// @author: [songzhibin97](https://github.com/songzhibin97)
|
||||
// @function: PreviewTemp
|
||||
// @description: 预览创建代码
|
||||
// @param: model.AutoCodeStruct
|
||||
// @return: map[string]string, error
|
||||
|
||||
func (autoCodeService *AutoCodeService) PreviewTemp(autoCode system.AutoCodeStruct) (map[string]string, error) {
|
||||
fmtField(&autoCode)
|
||||
dataList, _, needMkdir, err := autoCodeService.getNeedList(&autoCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 写入文件前,先创建文件夹
|
||||
if err = utils.CreateDir(needMkdir...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建map
|
||||
ret := make(map[string]string)
|
||||
|
||||
// 生成map
|
||||
for _, value := range dataList {
|
||||
ext := ""
|
||||
if ext = filepath.Ext(value.autoCodePath); ext == ".txt" {
|
||||
continue
|
||||
}
|
||||
f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0o755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = value.template.Execute(f, autoCode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = f.Close()
|
||||
f, err = os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_RDONLY, 0o755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder := strings.Builder{}
|
||||
builder.WriteString("```")
|
||||
|
||||
if ext != "" && strings.Contains(ext, ".") {
|
||||
builder.WriteString(strings.Replace(ext, ".", "", -1))
|
||||
}
|
||||
builder.WriteString("\n\n")
|
||||
data, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.Write(data)
|
||||
builder.WriteString("\n\n```")
|
||||
|
||||
pathArr := strings.Split(value.autoCodePath, string(os.PathSeparator))
|
||||
ret[pathArr[1]+"-"+pathArr[3]] = builder.String()
|
||||
_ = f.Close()
|
||||
|
||||
}
|
||||
defer func() { // 移除中间文件
|
||||
if err := os.RemoveAll(autoPath); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func makeDictTypes(autoCode *system.AutoCodeStruct) {
|
||||
DictTypeM := make(map[string]string)
|
||||
for _, v := range autoCode.Fields {
|
||||
if v.DictType != "" {
|
||||
DictTypeM[v.DictType] = ""
|
||||
}
|
||||
}
|
||||
|
||||
for k := range DictTypeM {
|
||||
autoCode.DictTypes = append(autoCode.DictTypes, k)
|
||||
}
|
||||
}
|
||||
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @function: CreateTemp
|
||||
// @description: 创建代码
|
||||
// @param: model.AutoCodeStruct
|
||||
// @return: err error
|
||||
|
||||
func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruct, menuID uint, ids ...uint) (err error) {
|
||||
fmtField(&autoCode)
|
||||
// 增加判断: 重复创建struct
|
||||
if AutoCodeHistoryServiceApp.Repeat(autoCode.BusinessDB, autoCode.StructName, autoCode.Package) {
|
||||
return RepeatErr
|
||||
}
|
||||
dataList, _, needMkdir, err := autoCodeService.getNeedList(&autoCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
meta, _ := json.Marshal(autoCode)
|
||||
|
||||
// 增加判断:Package不为空
|
||||
if autoCode.Package == "" {
|
||||
return errors.New("Package为空\n")
|
||||
}
|
||||
|
||||
// 写入文件前,先创建文件夹
|
||||
if err = utils.CreateDir(needMkdir...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 生成文件
|
||||
for _, value := range dataList {
|
||||
f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = value.template.Execute(f, autoCode); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = f.Close()
|
||||
}
|
||||
|
||||
defer func() { // 移除中间文件
|
||||
if err := os.RemoveAll(autoPath); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
bf := strings.Builder{}
|
||||
idBf := strings.Builder{}
|
||||
injectionCodeMeta := strings.Builder{}
|
||||
for _, id := range ids {
|
||||
idBf.WriteString(strconv.Itoa(int(id)))
|
||||
idBf.WriteString(";")
|
||||
}
|
||||
Init(autoCode.Package)
|
||||
for index := range dataList {
|
||||
autoCodeService.addAutoMoveFile(&dataList[index])
|
||||
}
|
||||
// 判断目标文件是否都可以移动
|
||||
for _, value := range dataList {
|
||||
if utils.FileExist(value.autoMoveFilePath) {
|
||||
return errors.New(fmt.Sprintf("目标文件已存在:%s\n", value.autoMoveFilePath))
|
||||
}
|
||||
}
|
||||
for _, value := range dataList { // 移动文件
|
||||
if err := utils.FileMove(value.autoCodePath, value.autoMoveFilePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if autoCode.AutoMigrate {
|
||||
// 在gorm.go 注入 自动迁移
|
||||
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "gorm_biz.go")
|
||||
varDB := utils.MaheHump(autoCode.BusinessDB)
|
||||
ast2.AddRegisterTablesAst(path, "bizModel", autoCode.Package, varDB, autoCode.BusinessDB, autoCode.StructName)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// router.go 注入 自动迁移
|
||||
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "router_biz.go")
|
||||
ast2.AddRouterCode(path, "initBizRouter", autoCode.Package, autoCode.StructName)
|
||||
}
|
||||
// 给各个enter进行注入
|
||||
err = injectionCode(autoCode.StructName, &injectionCodeMeta)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 保存生成信息
|
||||
for _, data := range dataList {
|
||||
if len(data.autoMoveFilePath) != 0 {
|
||||
bf.WriteString(data.autoMoveFilePath)
|
||||
bf.WriteString(";")
|
||||
}
|
||||
}
|
||||
if autoCode.TableName != "" {
|
||||
err = AutoCodeHistoryServiceApp.CreateAutoCodeHistory(
|
||||
string(meta),
|
||||
autoCode.StructName,
|
||||
autoCode.Description,
|
||||
bf.String(),
|
||||
injectionCodeMeta.String(),
|
||||
autoCode.TableName,
|
||||
idBf.String(),
|
||||
autoCode.Package,
|
||||
autoCode.BusinessDB,
|
||||
menuID,
|
||||
)
|
||||
} else {
|
||||
err = AutoCodeHistoryServiceApp.CreateAutoCodeHistory(
|
||||
string(meta),
|
||||
autoCode.StructName,
|
||||
autoCode.Description,
|
||||
bf.String(),
|
||||
injectionCodeMeta.String(),
|
||||
autoCode.StructName,
|
||||
idBf.String(),
|
||||
autoCode.Package,
|
||||
autoCode.BusinessDB,
|
||||
menuID,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @function: GetAllTplFile
|
||||
// @description: 获取 pathName 文件夹下所有 tpl 文件
|
||||
// @param: pathName string, fileList []string
|
||||
// @return: []string, error
|
||||
|
||||
func (autoCodeService *AutoCodeService) GetAllTplFile(pathName string, fileList []string) ([]string, error) {
|
||||
files, err := os.ReadDir(pathName)
|
||||
for _, fi := range files {
|
||||
if fi.IsDir() {
|
||||
fileList, err = autoCodeService.GetAllTplFile(pathName+"/"+fi.Name(), fileList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if strings.HasSuffix(fi.Name(), ".tpl") {
|
||||
fileList = append(fileList, pathName+"/"+fi.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileList, err
|
||||
}
|
||||
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @function: GetDB
|
||||
// @description: 获取指定数据库和指定数据表的所有字段名,类型值等
|
||||
// @param: tableName string, dbName string
|
||||
// @return: err error, Columns []request.ColumnReq
|
||||
|
||||
func (autoCodeService *AutoCodeService) DropTable(BusinessDb, tableName string) error {
|
||||
if BusinessDb != "" {
|
||||
return global.MustGetGlobalDBByDBName(BusinessDb).Exec("DROP TABLE " + tableName).Error
|
||||
} else {
|
||||
return global.GVA_DB.Exec("DROP TABLE " + tableName).Error
|
||||
}
|
||||
}
|
||||
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
// @author: [songzhibin97](https://github.com/songzhibin97)
|
||||
// @function: addAutoMoveFile
|
||||
// @description: 生成对应的迁移文件路径
|
||||
// @param: *tplData
|
||||
// @return: null
|
||||
|
||||
func (autoCodeService *AutoCodeService) addAutoMoveFile(data *tplData) {
|
||||
base := filepath.Base(data.autoCodePath)
|
||||
fileSlice := strings.Split(data.autoCodePath, string(os.PathSeparator))
|
||||
n := len(fileSlice)
|
||||
if n <= 2 {
|
||||
return
|
||||
}
|
||||
if strings.Contains(fileSlice[1], "server") {
|
||||
if strings.Contains(fileSlice[n-2], "router") {
|
||||
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server,
|
||||
fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRouter, data.autoPackage), base)
|
||||
} else if strings.Contains(fileSlice[n-2], "api") {
|
||||
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SApi, data.autoPackage), base)
|
||||
} else if strings.Contains(fileSlice[n-2], "service") {
|
||||
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SService, data.autoPackage), base)
|
||||
} else if strings.Contains(fileSlice[n-2], "model") {
|
||||
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SModel, data.autoPackage), base)
|
||||
} else if strings.Contains(fileSlice[n-2], "request") {
|
||||
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRequest, data.autoPackage), base)
|
||||
}
|
||||
} else if strings.Contains(fileSlice[1], "web") {
|
||||
if strings.Contains(fileSlice[n-1], "js") {
|
||||
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WApi, data.autoPackage, base)
|
||||
} else if strings.Contains(fileSlice[n-2], "form") {
|
||||
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WForm, data.autoPackage, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), strings.TrimSuffix(base, filepath.Ext(base))+"Form.vue")
|
||||
} else if strings.Contains(fileSlice[n-2], "table") {
|
||||
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WTable, data.autoPackage, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), base)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
// @function: CreateApi
|
||||
// @description: 自动创建api数据,
|
||||
// @param: a *model.AutoCodeStruct
|
||||
// @return: err error
|
||||
|
||||
func (autoCodeService *AutoCodeService) AutoCreateApi(a *system.AutoCodeStruct) (ids []uint, err error) {
|
||||
apiList := []system.SysApi{
|
||||
{
|
||||
Path: "/" + a.Abbreviation + "/" + "create" + a.StructName,
|
||||
Description: "新增" + a.Description,
|
||||
ApiGroup: a.Description,
|
||||
Method: "POST",
|
||||
},
|
||||
{
|
||||
Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName,
|
||||
Description: "删除" + a.Description,
|
||||
ApiGroup: a.Description,
|
||||
Method: "DELETE",
|
||||
},
|
||||
{
|
||||
Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName + "ByIds",
|
||||
Description: "批量删除" + a.Description,
|
||||
ApiGroup: a.Description,
|
||||
Method: "DELETE",
|
||||
},
|
||||
{
|
||||
Path: "/" + a.Abbreviation + "/" + "update" + a.StructName,
|
||||
Description: "更新" + a.Description,
|
||||
ApiGroup: a.Description,
|
||||
Method: "PUT",
|
||||
},
|
||||
{
|
||||
Path: "/" + a.Abbreviation + "/" + "find" + a.StructName,
|
||||
Description: "根据ID获取" + a.Description,
|
||||
ApiGroup: a.Description,
|
||||
Method: "GET",
|
||||
},
|
||||
{
|
||||
Path: "/" + a.Abbreviation + "/" + "get" + a.StructName + "List",
|
||||
Description: "获取" + a.Description + "列表",
|
||||
ApiGroup: a.Description,
|
||||
Method: "GET",
|
||||
},
|
||||
}
|
||||
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
for _, v := range apiList {
|
||||
var api system.SysApi
|
||||
findErr := tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error
|
||||
if findErr == nil {
|
||||
return errors.New("存在相同的API,请关闭自动创建API功能")
|
||||
}
|
||||
if errors.Is(findErr, gorm.ErrRecordNotFound) {
|
||||
if err = tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
|
||||
return err
|
||||
} else {
|
||||
ids = append(ids, v.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return ids, err
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) AutoCreateMenu(a *system.AutoCodeStruct) (id uint, err error) {
|
||||
var menu system.SysBaseMenu
|
||||
err = global.GVA_DB.First(&menu, "name = ?", a.Abbreviation).Error
|
||||
if err == nil {
|
||||
return 0, errors.New("存在相同的菜单路由,请关闭自动创建菜单功能")
|
||||
}
|
||||
menu.ParentId = 0
|
||||
menu.Name = a.Abbreviation
|
||||
menu.Path = a.Abbreviation
|
||||
menu.Meta.Title = a.Description
|
||||
menu.Component = fmt.Sprintf("view/%s/%s/%s.vue", a.Package, a.PackageName, a.PackageName)
|
||||
err = global.GVA_DB.Create(&menu).Error
|
||||
return menu.ID, err
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) getNeedList(autoCode *system.AutoCodeStruct) (dataList []tplData, fileList []string, needMkdir []string, err error) {
|
||||
// 去除所有空格
|
||||
utils.TrimSpace(autoCode)
|
||||
for _, field := range autoCode.Fields {
|
||||
utils.TrimSpace(field)
|
||||
}
|
||||
// 获取 basePath 文件夹下所有tpl文件
|
||||
tplFileList, err := autoCodeService.GetAllTplFile(autocodePath, nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
dataList = make([]tplData, 0, len(tplFileList))
|
||||
fileList = make([]string, 0, len(tplFileList))
|
||||
needMkdir = make([]string, 0, len(tplFileList)) // 当文件夹下存在多个tpl文件时,改为map更合理
|
||||
// 根据文件路径生成 tplData 结构体,待填充数据
|
||||
for _, value := range tplFileList {
|
||||
dataList = append(dataList, tplData{locationPath: value, autoPackage: autoCode.Package})
|
||||
}
|
||||
// 生成 *Template, 填充 template 字段
|
||||
for index, value := range dataList {
|
||||
dataList[index].template, err = template.ParseFiles(value.locationPath)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
// 生成文件路径,填充 autoCodePath 字段,readme.txt.tpl不符合规则,需要特殊处理
|
||||
// resource/template/web/api.js.tpl -> autoCode/web/autoCode.PackageName/api/autoCode.PackageName.js
|
||||
// resource/template/readme.txt.tpl -> autoCode/readme.txt
|
||||
for index, value := range dataList {
|
||||
trimBase := strings.TrimPrefix(value.locationPath, autocodePath+"/")
|
||||
if trimBase == "readme.txt.tpl" {
|
||||
dataList[index].autoCodePath = autoPath + "readme.txt"
|
||||
continue
|
||||
}
|
||||
|
||||
if lastSeparator := strings.LastIndex(trimBase, "/"); lastSeparator != -1 {
|
||||
origFileName := strings.TrimSuffix(trimBase[lastSeparator+1:], ".tpl")
|
||||
firstDot := strings.Index(origFileName, ".")
|
||||
if firstDot != -1 {
|
||||
var fileName string
|
||||
if origFileName[firstDot:] != ".go" {
|
||||
fileName = autoCode.PackageName + origFileName[firstDot:]
|
||||
} else {
|
||||
fileName = autoCode.HumpPackageName + origFileName[firstDot:]
|
||||
}
|
||||
|
||||
dataList[index].autoCodePath = filepath.Join(autoPath, trimBase[:lastSeparator], autoCode.PackageName,
|
||||
origFileName[:firstDot], fileName)
|
||||
}
|
||||
}
|
||||
|
||||
if lastSeparator := strings.LastIndex(dataList[index].autoCodePath, string(os.PathSeparator)); lastSeparator != -1 {
|
||||
needMkdir = append(needMkdir, dataList[index].autoCodePath[:lastSeparator])
|
||||
}
|
||||
}
|
||||
for _, value := range dataList {
|
||||
fileList = append(fileList, value.autoCodePath)
|
||||
}
|
||||
return dataList, fileList, needMkdir, err
|
||||
}
|
||||
|
||||
// injectionCode 封装代码注入
|
||||
func injectionCode(structName string, bf *strings.Builder) error {
|
||||
for _, meta := range injectionPaths {
|
||||
code := fmt.Sprintf(meta.structNameF, structName)
|
||||
ast2.ImportForAutoEnter(meta.path, meta.funcName, code)
|
||||
bf.WriteString(fmt.Sprintf("%s@%s@%s;", meta.path, meta.funcName, code))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) CreateAutoCode(s *system.SysAutoCode) error {
|
||||
if s.PackageName == "autocode" || s.PackageName == "system" || s.PackageName == "example" || s.PackageName == "" {
|
||||
return errors.New("不能使用已保留的package name")
|
||||
}
|
||||
if !errors.Is(global.GVA_DB.Where("package_name = ?", s.PackageName).First(&system.SysAutoCode{}).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("存在相同PackageName")
|
||||
}
|
||||
if e := autoCodeService.CreatePackageTemp(s.PackageName); e != nil {
|
||||
return e
|
||||
}
|
||||
return global.GVA_DB.Create(&s).Error
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) GetPackage() (pkgList []system.SysAutoCode, err error) {
|
||||
err = global.GVA_DB.Find(&pkgList).Error
|
||||
return pkgList, err
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) DelPackage(a system.SysAutoCode) error {
|
||||
return global.GVA_DB.Delete(&a).Error
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) CreatePackageTemp(packageName string) error {
|
||||
Init(packageName)
|
||||
pendingTemp := []autoPackage{{
|
||||
path: packageService,
|
||||
name: packageServiceName,
|
||||
temp: string(subcontract.Server),
|
||||
}, {
|
||||
path: packageRouter,
|
||||
name: packageRouterName,
|
||||
temp: string(subcontract.Router),
|
||||
}, {
|
||||
path: packageAPI,
|
||||
name: packageAPIName,
|
||||
temp: string(subcontract.API),
|
||||
}}
|
||||
|
||||
webTemp := []string{
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WApi),
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WForm),
|
||||
}
|
||||
|
||||
for _, s := range webTemp {
|
||||
err := os.MkdirAll(filepath.Join(s, packageName), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for i, s := range pendingTemp {
|
||||
pendingTemp[i].path = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, filepath.Clean(fmt.Sprintf(s.path, packageName)))
|
||||
}
|
||||
// 选择模板
|
||||
for _, s := range pendingTemp {
|
||||
err := os.MkdirAll(filepath.Dir(s.path), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(s.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
temp, err := template.New("").Parse(s.temp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = temp.Execute(f, struct {
|
||||
PackageName string `json:"package_name"`
|
||||
}{packageName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// 创建完成后在对应的位置插入结构代码
|
||||
for _, v := range pendingTemp {
|
||||
meta := packageInjectionMap[v.name]
|
||||
if err := ast2.ImportReference(meta.path, fmt.Sprintf(meta.importCodeF, v.name, packageName), fmt.Sprintf(meta.structNameF, utils.FirstUpper(packageName)), fmt.Sprintf(meta.packageNameF, packageName), meta.groupName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePlug 自动创建插件模板
|
||||
func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) error {
|
||||
// 检查列表参数是否有效
|
||||
plug.CheckList()
|
||||
err := autoCodeService.createPluginServer(plug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = autoCodeService.createPluginWeb(plug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) createPluginServer(plug system.AutoPlugReq) error {
|
||||
tplFileList, _ := autoCodeService.GetAllTplFile(plugServerPath, nil)
|
||||
for _, tpl := range tplFileList {
|
||||
temp, err := template.ParseFiles(tpl)
|
||||
if err != nil {
|
||||
zap.L().Error("parse err", zap.String("tpl", tpl), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
pathArr := strings.SplitAfter(tpl, "/")
|
||||
if strings.Index(pathArr[3], "tpl") < 0 {
|
||||
dirPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+pathArr[3]))
|
||||
os.MkdirAll(dirPath, 0755)
|
||||
}
|
||||
file := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+tpl[len(plugServerPath):len(tpl)-4]))
|
||||
f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
zap.L().Error("open file", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = temp.Execute(f, plug)
|
||||
if err != nil {
|
||||
zap.L().Error("exec err", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) createPluginWeb(plug system.AutoPlugReq) error {
|
||||
tplFileList, _ := autoCodeService.GetAllTplFile(plugWebPath, nil)
|
||||
for _, tpl := range tplFileList {
|
||||
temp, err := template.ParseFiles(tpl)
|
||||
if err != nil {
|
||||
zap.L().Error("parse err", zap.String("tpl", tpl), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
pathArr := strings.SplitAfter(tpl, "/")
|
||||
if strings.Index(pathArr[3], "tpl") < 0 {
|
||||
dirPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+pathArr[3]))
|
||||
os.MkdirAll(dirPath, 0755)
|
||||
}
|
||||
file := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+tpl[len(plugWebPath):len(tpl)-4]))
|
||||
f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
zap.L().Error("open file", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = temp.Execute(f, plug)
|
||||
if err != nil {
|
||||
zap.L().Error("exec err", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) InstallPlugin(file *multipart.FileHeader) (web, server int, err error) {
|
||||
const GVAPLUGPINATH = "./gva-plug-temp/"
|
||||
defer os.RemoveAll(GVAPLUGPINATH)
|
||||
_, err = os.Stat(GVAPLUGPINATH)
|
||||
if os.IsNotExist(err) {
|
||||
os.Mkdir(GVAPLUGPINATH, os.ModePerm)
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
out, err := os.Create(GVAPLUGPINATH + file.Filename)
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, src)
|
||||
|
||||
paths, err := utils.Unzip(GVAPLUGPINATH+file.Filename, GVAPLUGPINATH)
|
||||
paths = filterFile(paths)
|
||||
var webIndex = -1
|
||||
var serverIndex = -1
|
||||
webPlugin := ""
|
||||
serverPlugin := ""
|
||||
|
||||
for i := range paths {
|
||||
paths[i] = filepath.ToSlash(paths[i])
|
||||
pathArr := strings.Split(paths[i], "/")
|
||||
ln := len(pathArr)
|
||||
|
||||
if ln < 4 {
|
||||
continue
|
||||
}
|
||||
if pathArr[2]+"/"+pathArr[3] == `server/plugin` && len(serverPlugin) == 0 {
|
||||
serverPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3])
|
||||
}
|
||||
if pathArr[2]+"/"+pathArr[3] == `web/plugin` && len(webPlugin) == 0 {
|
||||
webPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3])
|
||||
}
|
||||
}
|
||||
if len(serverPlugin) == 0 && len(webPlugin) == 0 {
|
||||
zap.L().Error("非标准插件,请按照文档自动迁移使用")
|
||||
return webIndex, serverIndex, errors.New("非标准插件,请按照文档自动迁移使用")
|
||||
}
|
||||
|
||||
if len(serverPlugin) != 0 {
|
||||
err = installation(serverPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Server)
|
||||
if err != nil {
|
||||
return webIndex, serverIndex, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(webPlugin) != 0 {
|
||||
err = installation(webPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Web)
|
||||
if err != nil {
|
||||
return webIndex, serverIndex, err
|
||||
}
|
||||
}
|
||||
|
||||
return 1, 1, err
|
||||
}
|
||||
|
||||
func installation(path string, formPath string, toPath string) error {
|
||||
arr := strings.Split(filepath.ToSlash(path), "/")
|
||||
ln := len(arr)
|
||||
if ln < 3 {
|
||||
return errors.New("arr")
|
||||
}
|
||||
name := arr[ln-3]
|
||||
|
||||
var form = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + formPath + "/" + path)
|
||||
var to = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + toPath + "/plugin/")
|
||||
_, err := os.Stat(to + name)
|
||||
if err == nil {
|
||||
zap.L().Error("autoPath 已存在同名插件,请自行手动安装", zap.String("to", to))
|
||||
return errors.New(toPath + "已存在同名插件,请自行手动安装")
|
||||
}
|
||||
return cp.Copy(form, to, cp.Options{Skip: skipMacSpecialDocument})
|
||||
}
|
||||
|
||||
func filterFile(paths []string) []string {
|
||||
np := make([]string, 0, len(paths))
|
||||
for _, path := range paths {
|
||||
if ok, _ := skipMacSpecialDocument(path); ok {
|
||||
continue
|
||||
}
|
||||
np = append(np, path)
|
||||
}
|
||||
return np
|
||||
}
|
||||
|
||||
func skipMacSpecialDocument(src string) (bool, error) {
|
||||
if strings.Contains(src, ".DS_Store") || strings.Contains(src, "__MACOSX") {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) PubPlug(plugName string) (zipPath string, err error) {
|
||||
if plugName == "" {
|
||||
return "", errors.New("插件名称不能为空")
|
||||
}
|
||||
|
||||
// 防止路径穿越
|
||||
plugName = filepath.Clean(plugName)
|
||||
|
||||
webPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", plugName)
|
||||
serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", plugName)
|
||||
// 创建一个新的zip文件
|
||||
|
||||
// 判断目录是否存在
|
||||
_, err = os.Stat(webPath)
|
||||
if err != nil {
|
||||
return "", errors.New("web路径不存在")
|
||||
}
|
||||
_, err = os.Stat(serverPath)
|
||||
if err != nil {
|
||||
return "", errors.New("server路径不存在")
|
||||
}
|
||||
|
||||
fileName := plugName + ".zip"
|
||||
// 创建一个新的zip文件
|
||||
files, err := archiver.FilesFromDisk(nil, map[string]string{
|
||||
webPath: plugName + "/web/plugin/" + plugName,
|
||||
serverPath: plugName + "/server/plugin/" + plugName,
|
||||
})
|
||||
|
||||
// create the output file we'll write to
|
||||
out, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// we can use the CompressedArchive type to gzip a tarball
|
||||
// (compression is not required; you could use Tar directly)
|
||||
format := archiver.CompressedArchive{
|
||||
Archival: archiver.Zip{},
|
||||
}
|
||||
|
||||
// create the archive
|
||||
err = format.Archive(context.Background(), out, files)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fileName), nil
|
||||
}
|
||||
|
||||
func fmtField(autoCode *system.AutoCodeStruct) {
|
||||
makeDictTypes(autoCode)
|
||||
autoCode.DataSourceMap = make(map[string]*system.DataSource)
|
||||
for i := range autoCode.Fields {
|
||||
|
||||
if autoCode.Fields[i].Front {
|
||||
autoCode.FrontFields = append(autoCode.FrontFields, autoCode.Fields[i])
|
||||
}
|
||||
|
||||
if autoCode.Fields[i].FieldType == "time.Time" {
|
||||
autoCode.HasTimer = true
|
||||
if autoCode.Fields[i].FieldSearchType != "" {
|
||||
autoCode.HasSearchTimer = true
|
||||
}
|
||||
}
|
||||
if autoCode.Fields[i].Sort {
|
||||
autoCode.NeedSort = true
|
||||
}
|
||||
if autoCode.Fields[i].FieldType == "picture" {
|
||||
autoCode.HasPic = true
|
||||
}
|
||||
if autoCode.Fields[i].FieldType == "video" {
|
||||
autoCode.HasPic = true
|
||||
}
|
||||
if autoCode.Fields[i].FieldType == "richtext" {
|
||||
autoCode.HasRichText = true
|
||||
}
|
||||
if autoCode.Fields[i].FieldType == "pictures" {
|
||||
autoCode.HasPic = true
|
||||
autoCode.NeedJSON = true
|
||||
}
|
||||
if autoCode.Fields[i].FieldType == "json" {
|
||||
autoCode.NeedJSON = true
|
||||
}
|
||||
if autoCode.Fields[i].FieldType == "array" {
|
||||
autoCode.NeedJSON = true
|
||||
}
|
||||
if autoCode.Fields[i].FieldType == "file" {
|
||||
autoCode.HasFile = true
|
||||
autoCode.NeedJSON = true
|
||||
}
|
||||
|
||||
if autoCode.Fields[i].DataSource != nil &&
|
||||
autoCode.Fields[i].DataSource.Table != "" &&
|
||||
autoCode.Fields[i].DataSource.Label != "" &&
|
||||
autoCode.Fields[i].DataSource.Value != "" {
|
||||
autoCode.HasDataSource = true
|
||||
autoCode.DataSourceMap[autoCode.Fields[i].FieldJson] = autoCode.Fields[i].DataSource
|
||||
autoCode.Fields[i].CheckDataSource = true
|
||||
}
|
||||
|
||||
if autoCode.GvaModel {
|
||||
autoCode.PrimaryField = &system.Field{
|
||||
FieldName: "ID",
|
||||
FieldType: "uint",
|
||||
FieldDesc: "ID",
|
||||
FieldJson: "ID",
|
||||
DataTypeLong: "20",
|
||||
Comment: "主键ID",
|
||||
ColumnName: "id",
|
||||
}
|
||||
}
|
||||
if !autoCode.GvaModel && autoCode.PrimaryField == nil && autoCode.Fields[i].PrimaryKey {
|
||||
autoCode.PrimaryField = autoCode.Fields[i]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -5,6 +5,8 @@ import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
|
||||
)
|
||||
|
||||
type AutoCodeService struct{}
|
||||
|
||||
type Database interface {
|
||||
GetDB(businessDB string) (data []response.Db, err error)
|
||||
GetTables(businessDB string, dbName string) (data []response.Table, err error)
|
||||
|
@@ -1,165 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils/ast"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var RepeatErr = errors.New("重复创建")
|
||||
|
||||
type AutoCodeHistoryService struct{}
|
||||
|
||||
var AutoCodeHistoryServiceApp = new(AutoCodeHistoryService)
|
||||
|
||||
// CreateAutoCodeHistory 创建代码生成器历史记录
|
||||
// RouterPath : RouterPath@RouterString;RouterPath2@RouterString2
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (autoCodeHistoryService *AutoCodeHistoryService) CreateAutoCodeHistory(meta, structName, structCNName, autoCodePath string, injectionMeta string, tableName string, apiIds string, Package string, BusinessDB string, menuID uint) error {
|
||||
return global.GVA_DB.Create(&system.SysAutoCodeHistory{
|
||||
Package: Package,
|
||||
RequestMeta: meta,
|
||||
AutoCodePath: autoCodePath,
|
||||
InjectionMeta: injectionMeta,
|
||||
StructName: structName,
|
||||
StructCNName: structCNName,
|
||||
TableName: tableName,
|
||||
ApiIDs: apiIds,
|
||||
BusinessDB: BusinessDB,
|
||||
MenuID: menuID,
|
||||
}).Error
|
||||
}
|
||||
|
||||
// First 根据id获取代码生成器历史的数据
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (autoCodeHistoryService *AutoCodeHistoryService) First(info *request.GetById) (string, error) {
|
||||
var meta string
|
||||
return meta, global.GVA_DB.Model(system.SysAutoCodeHistory{}).Select("request_meta").Where("id = ?", info.Uint()).First(&meta).Error
|
||||
}
|
||||
|
||||
// Repeat 检测重复
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (autoCodeHistoryService *AutoCodeHistoryService) Repeat(businessDB, structName, Package string) bool {
|
||||
var count int64
|
||||
global.GVA_DB.Model(&system.SysAutoCodeHistory{}).Where("business_db = ? and struct_name = ? and package = ? and flag = 0", businessDB, structName, Package).Count(&count)
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// RollBack 回滚
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (autoCodeHistoryService *AutoCodeHistoryService) RollBack(info *systemReq.RollBack) error {
|
||||
md := system.SysAutoCodeHistory{}
|
||||
if err := global.GVA_DB.Where("id = ?", info.ID).First(&md).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 清除API表
|
||||
var err error
|
||||
if info.DeleteApi {
|
||||
ids := request.IdsReq{}
|
||||
idsStr := strings.Split(md.ApiIDs, ";")
|
||||
for i := range idsStr[0 : len(idsStr)-1] {
|
||||
id, err := strconv.Atoi(idsStr[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ids.Ids = append(ids.Ids, id)
|
||||
}
|
||||
err = ApiServiceApp.DeleteApisByIds(ids)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("ClearTag DeleteApiByIds:", zap.Error(err))
|
||||
}
|
||||
}
|
||||
// 清除菜单表
|
||||
if info.DeleteMenu {
|
||||
err = BaseMenuServiceApp.DeleteBaseMenu(int(md.MenuID))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("ClearTag DeleteBaseMenu:", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// 删除表
|
||||
if info.DeleteTable {
|
||||
if err = AutoCodeServiceApp.DropTable(md.BusinessDB, md.TableName); err != nil {
|
||||
global.GVA_LOG.Error("ClearTag DropTable:", zap.Error(err))
|
||||
}
|
||||
}
|
||||
// 删除文件
|
||||
|
||||
for _, path := range strings.Split(md.AutoCodePath, ";") {
|
||||
|
||||
// 增加安全判断补丁:
|
||||
_path, err := filepath.Abs(path)
|
||||
if err != nil || _path != path {
|
||||
continue
|
||||
}
|
||||
|
||||
// 迁移
|
||||
nPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
|
||||
"rm_file", time.Now().Format("20060102"), filepath.Base(filepath.Dir(filepath.Dir(path))), filepath.Base(filepath.Dir(path)), filepath.Base(path))
|
||||
// 判断目标文件是否存在
|
||||
for utils.FileExist(nPath) {
|
||||
fmt.Println("文件已存在:", nPath)
|
||||
nPath += fmt.Sprintf("_%d", time.Now().Nanosecond())
|
||||
}
|
||||
err = utils.FileMove(path, nPath)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("file move err ", zap.Error(err))
|
||||
}
|
||||
//_ = utils.DeLFile(path)
|
||||
}
|
||||
// 清除注入
|
||||
for _, v := range strings.Split(md.InjectionMeta, ";") {
|
||||
// RouterPath@functionName@RouterString
|
||||
meta := strings.Split(v, "@")
|
||||
if len(meta) == 3 {
|
||||
_ = utils.AutoClearCode(meta[0], meta[2])
|
||||
}
|
||||
}
|
||||
|
||||
ast.RollBackAst(md.Package, md.StructName)
|
||||
|
||||
md.Flag = 1
|
||||
return global.GVA_DB.Save(&md).Error
|
||||
}
|
||||
|
||||
// Delete 删除历史数据
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (autoCodeHistoryService *AutoCodeHistoryService) Delete(info *request.GetById) error {
|
||||
return global.GVA_DB.Where("id = ?", info.Uint()).Delete(&system.SysAutoCodeHistory{}).Error
|
||||
}
|
||||
|
||||
// GetList 获取系统历史数据
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (autoCodeHistoryService *AutoCodeHistoryService) GetList(info request.PageInfo) (list []response.AutoCodeHistory, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
db := global.GVA_DB.Model(&system.SysAutoCodeHistory{})
|
||||
var entities []response.AutoCodeHistory
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
err = db.Limit(limit).Offset(offset).Order("updated_at desc").Find(&entities).Error
|
||||
return entities, total, err
|
||||
}
|
@@ -16,6 +16,8 @@ import (
|
||||
|
||||
type DictionaryService struct{}
|
||||
|
||||
var DictionaryServiceApp = new(DictionaryService)
|
||||
|
||||
func (dictionaryService *DictionaryService) CreateSysDictionary(sysDictionary system.SysDictionary) (err error) {
|
||||
if (!errors.Is(global.GVA_DB.First(&system.SysDictionary{}, "type = ?", sysDictionary.Type).Error, gorm.ErrRecordNotFound)) {
|
||||
return errors.New("存在相同的type,不允许创建")
|
||||
|
@@ -14,6 +14,8 @@ import (
|
||||
|
||||
type DictionaryDetailService struct{}
|
||||
|
||||
var DictionaryDetailServiceApp = new(DictionaryDetailService)
|
||||
|
||||
func (dictionaryDetailService *DictionaryDetailService) CreateSysDictionaryDetail(sysDictionaryDetail system.SysDictionaryDetail) (err error) {
|
||||
err = global.GVA_DB.Create(&sysDictionaryDetail).Error
|
||||
return err
|
||||
|
@@ -21,6 +21,8 @@ import (
|
||||
type SysExportTemplateService struct {
|
||||
}
|
||||
|
||||
var SysExportTemplateServiceApp = new(SysExportTemplateService)
|
||||
|
||||
// CreateSysExportTemplate 创建导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) CreateSysExportTemplate(sysExportTemplate *system.SysExportTemplate) (err error) {
|
||||
|
@@ -3,15 +3,16 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/config"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/gookit/color"
|
||||
"gorm.io/gorm"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/config"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
)
|
||||
|
||||
type SqliteInitHandler struct{}
|
||||
@@ -24,7 +25,7 @@ func NewSqliteInitHandler() *SqliteInitHandler {
|
||||
func (h SqliteInitHandler) WriteConfig(ctx context.Context) error {
|
||||
c, ok := ctx.Value("config").(config.Sqlite)
|
||||
if !ok {
|
||||
return errors.New("mysql config invalid")
|
||||
return errors.New("sqlite config invalid")
|
||||
}
|
||||
global.GVA_CONFIG.System.DbType = "sqlite"
|
||||
global.GVA_CONFIG.Sqlite = c
|
||||
|
@@ -15,6 +15,8 @@ import (
|
||||
|
||||
type OperationRecordService struct{}
|
||||
|
||||
var OperationRecordServiceApp = new(OperationRecordService)
|
||||
|
||||
func (operationRecordService *OperationRecordService) CreateSysOperationRecord(sysOperationRecord system.SysOperationRecord) (err error) {
|
||||
err = global.GVA_DB.Create(&sysOperationRecord).Error
|
||||
return err
|
||||
|
@@ -15,6 +15,8 @@ import (
|
||||
|
||||
type SystemConfigService struct{}
|
||||
|
||||
var SystemConfigServiceApp = new(SystemConfigService)
|
||||
|
||||
func (systemConfigService *SystemConfigService) GetSystemConfig() (conf config.Server, err error) {
|
||||
return global.GVA_CONFIG, nil
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ import (
|
||||
|
||||
type UserService struct{}
|
||||
|
||||
var UserServiceApp = new(UserService)
|
||||
|
||||
func (userService *UserService) Register(u system.SysUser) (userInter system.SysUser, err error) {
|
||||
var user system.SysUser
|
||||
if !errors.Is(global.GVA_DB.Where("username = ?", u.Username).First(&user).Error, gorm.ErrRecordNotFound) { // 判断用户名是否注册
|
||||
|
Reference in New Issue
Block a user