706 lines
24 KiB
Go
706 lines
24 KiB
Go
package system
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"go/ast"
|
||
"go/format"
|
||
"go/parser"
|
||
"go/token"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"text/template"
|
||
|
||
"github.com/go-kratos/kratos/v2/log"
|
||
"github.com/pkg/errors"
|
||
"gorm.io/gorm"
|
||
|
||
"kra/internal/conf"
|
||
"kra/pkg/utils"
|
||
"kra/pkg/utils/autocode"
|
||
)
|
||
|
||
// AutoCodeTemplateRepo 自动代码模板仓储接口
|
||
type AutoCodeTemplateRepo interface {
|
||
GetPackageByName(ctx context.Context, packageName string) (*SysAutoCodePackage, error)
|
||
CreateApis(ctx context.Context, apis []SysApi) ([]uint, error)
|
||
GetMenuByName(ctx context.Context, name string) (*SysBaseMenu, error)
|
||
CreateMenu(ctx context.Context, menu *SysBaseMenu) error
|
||
CreateExportTemplate(ctx context.Context, tpl *SysExportTemplate) error
|
||
Repeat(ctx context.Context, businessDB, structName, abbreviation, pkg string) bool
|
||
}
|
||
|
||
// AutoCodeTemplateUsecase 自动代码模板用例
|
||
type AutoCodeTemplateUsecase struct {
|
||
repo AutoCodeTemplateRepo
|
||
historyRepo AutoCodeHistoryRepo
|
||
packageRepo AutoCodePackageRepo
|
||
config *conf.AutoCodeConfig
|
||
log *log.Helper
|
||
db *gorm.DB
|
||
}
|
||
|
||
// NewAutoCodeTemplateUsecase 创建自动代码模板用例
|
||
func NewAutoCodeTemplateUsecase(
|
||
repo AutoCodeTemplateRepo,
|
||
historyRepo AutoCodeHistoryRepo,
|
||
packageRepo AutoCodePackageRepo,
|
||
config *conf.AutoCodeConfig,
|
||
logger log.Logger,
|
||
db *gorm.DB,
|
||
) *AutoCodeTemplateUsecase {
|
||
return &AutoCodeTemplateUsecase{
|
||
repo: repo,
|
||
historyRepo: historyRepo,
|
||
packageRepo: packageRepo,
|
||
config: config,
|
||
log: log.NewHelper(logger),
|
||
db: db,
|
||
}
|
||
}
|
||
|
||
// checkPackage 检查包结构是否完整
|
||
// 支持 Kratos DDD 层次结构检查:
|
||
// - biz/{package}/enter.go - 业务逻辑层入口
|
||
// - data/{package}/enter.go - 数据访问层入口
|
||
// - service/{package}/enter.go - 服务层入口
|
||
// - server/handler/{package}/enter.go - HTTP Handler 层入口
|
||
// - server/router/{package}/enter.go - 路由层入口
|
||
func (uc *AutoCodeTemplateUsecase) checkPackage(pkg string, template string) error {
|
||
switch template {
|
||
case "package":
|
||
// Kratos DDD 架构目录检查
|
||
// 检查 Biz 层
|
||
bizEnter := filepath.Join(uc.config.Root, uc.config.Server, "internal", "biz", pkg, "enter.go")
|
||
if _, err := os.Stat(bizEnter); err != nil {
|
||
return fmt.Errorf("package结构异常,缺少internal/biz/%s/enter.go", pkg)
|
||
}
|
||
// 检查 Data 层
|
||
dataEnter := filepath.Join(uc.config.Root, uc.config.Server, "internal", "data", pkg, "enter.go")
|
||
if _, err := os.Stat(dataEnter); err != nil {
|
||
return fmt.Errorf("package结构异常,缺少internal/data/%s/enter.go", pkg)
|
||
}
|
||
// 检查 Service 层
|
||
serviceEnter := filepath.Join(uc.config.Root, uc.config.Server, "internal", "service", pkg, "enter.go")
|
||
if _, err := os.Stat(serviceEnter); err != nil {
|
||
return fmt.Errorf("package结构异常,缺少internal/service/%s/enter.go", pkg)
|
||
}
|
||
// 检查 Handler 层
|
||
handlerEnter := filepath.Join(uc.config.Root, uc.config.Server, "internal", "server", "handler", pkg, "enter.go")
|
||
if _, err := os.Stat(handlerEnter); err != nil {
|
||
return fmt.Errorf("package结构异常,缺少internal/server/handler/%s/enter.go", pkg)
|
||
}
|
||
// 检查 Router 层
|
||
routerEnter := filepath.Join(uc.config.Root, uc.config.Server, "internal", "server", "router", pkg, "enter.go")
|
||
if _, err := os.Stat(routerEnter); err != nil {
|
||
return fmt.Errorf("package结构异常,缺少internal/server/router/%s/enter.go", pkg)
|
||
}
|
||
case "plugin":
|
||
pluginEnter := filepath.Join(uc.config.Root, uc.config.Server, "internal", "plugin", pkg, "plugin.go")
|
||
if _, err := os.Stat(pluginEnter); err != nil {
|
||
return fmt.Errorf("plugin结构异常,缺少internal/plugin/%s/plugin.go", pkg)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Create 创建生成自动化代码
|
||
func (uc *AutoCodeTemplateUsecase) Create(ctx context.Context, info *AutoCodeInfo) error {
|
||
// 查询包信息
|
||
autoPkg, err := uc.repo.GetPackageByName(ctx, info.Package)
|
||
if err != nil {
|
||
return errors.Wrap(err, "查询包失败!")
|
||
}
|
||
|
||
// 检查包结构
|
||
if err = uc.checkPackage(info.Package, autoPkg.Template); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 检查重复创建
|
||
if uc.repo.Repeat(ctx, info.BusinessDB, info.StructName, info.Abbreviation, info.Package) {
|
||
return errors.New("已经创建过此数据结构,请勿重复创建!")
|
||
}
|
||
|
||
// 生成代码
|
||
generate, templates, injections, err := uc.generate(ctx, info, autoPkg)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 写入文件
|
||
for key, builder := range generate {
|
||
if err = os.MkdirAll(filepath.Dir(key), os.ModePerm); err != nil {
|
||
return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", key)
|
||
}
|
||
if err = os.WriteFile(key, []byte(builder.String()), 0666); err != nil {
|
||
return errors.Wrapf(err, "[filepath:%s]写入文件失败!", key)
|
||
}
|
||
}
|
||
|
||
// 创建历史记录
|
||
history := &SysAutoCodeHistory{
|
||
Table: info.TableName,
|
||
Package: info.Package,
|
||
StructName: info.StructName,
|
||
Abbreviation: info.Abbreviation,
|
||
BusinessDB: info.BusinessDB,
|
||
Description: info.Description,
|
||
Templates: templates,
|
||
}
|
||
|
||
// 自动创建API
|
||
if info.AutoCreateApiToSql && !info.OnlyTemplate {
|
||
apis := uc.buildApis(info)
|
||
apiIDs, err := uc.repo.CreateApis(ctx, apis)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
history.ApiIDs = apiIDs
|
||
}
|
||
|
||
// 自动创建菜单
|
||
if info.AutoCreateMenuToSql {
|
||
menu, err := uc.repo.GetMenuByName(ctx, info.Abbreviation)
|
||
if err == nil && menu != nil {
|
||
history.MenuID = menu.ID
|
||
} else {
|
||
newMenu := uc.buildMenu(info, autoPkg.Template)
|
||
if info.AutoCreateBtnAuth && !info.OnlyTemplate {
|
||
newMenu.MenuBtn = uc.buildMenuBtns(info)
|
||
}
|
||
if err = uc.repo.CreateMenu(ctx, newMenu); err != nil {
|
||
return errors.Wrap(err, "创建菜单失败!")
|
||
}
|
||
history.MenuID = newMenu.ID
|
||
}
|
||
}
|
||
|
||
// 处理Excel导出模板
|
||
if info.HasExcel {
|
||
exportTpl := uc.buildExportTemplate(info)
|
||
if err = uc.repo.CreateExportTemplate(ctx, exportTpl); err != nil {
|
||
return err
|
||
}
|
||
history.ExportTemplateID = exportTpl.ID
|
||
}
|
||
|
||
// 保存注入信息
|
||
history.Injections = make(map[string]string, len(injections))
|
||
for key, value := range injections {
|
||
bytes, _ := json.Marshal(value)
|
||
history.Injections[key] = string(bytes)
|
||
}
|
||
|
||
// 创建历史记录
|
||
return uc.historyRepo.Create(ctx, history)
|
||
}
|
||
|
||
// Preview 预览自动化代码
|
||
func (uc *AutoCodeTemplateUsecase) Preview(ctx context.Context, info *AutoCodeInfo) (map[string]string, error) {
|
||
// 查询包信息
|
||
entity, err := uc.repo.GetPackageByName(ctx, info.Package)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "查询包失败!")
|
||
}
|
||
|
||
// 检查重复创建
|
||
if uc.repo.Repeat(ctx, info.BusinessDB, info.StructName, info.Abbreviation, info.Package) && !info.IsAdd {
|
||
return nil, errors.New("已经创建过此数据结构或重复简称,请勿重复创建!")
|
||
}
|
||
|
||
preview := make(map[string]string)
|
||
codes, _, _, err := uc.generate(ctx, info, entity)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
for key, writer := range codes {
|
||
if len(key) > len(uc.config.Root) {
|
||
key, _ = filepath.Rel(uc.config.Root, key)
|
||
}
|
||
suffix := filepath.Ext(key)[1:]
|
||
var builder strings.Builder
|
||
builder.WriteString("```" + suffix + "\n\n")
|
||
builder.WriteString(writer.String())
|
||
builder.WriteString("\n\n```")
|
||
preview[key] = builder.String()
|
||
}
|
||
return preview, nil
|
||
}
|
||
|
||
// generate 生成代码
|
||
func (uc *AutoCodeTemplateUsecase) generate(ctx context.Context, info *AutoCodeInfo, entity *SysAutoCodePackage) (map[string]strings.Builder, map[string]string, map[string]interface{}, error) {
|
||
dataList, asts, fileList, err := uc.packageRepo.Templates(ctx, entity, info, false)
|
||
if err != nil {
|
||
return nil, nil, nil, err
|
||
}
|
||
|
||
code := make(map[string]strings.Builder)
|
||
for templatePath := range dataList {
|
||
// 获取目标文件路径
|
||
targetPath, ok := fileList[templatePath]
|
||
if !ok {
|
||
continue
|
||
}
|
||
// 解析并执行模板
|
||
files, err := template.New(filepath.Base(templatePath)).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(templatePath)
|
||
if err != nil {
|
||
return nil, nil, nil, errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", templatePath)
|
||
}
|
||
var builder strings.Builder
|
||
if err = files.Execute(&builder, info); err != nil {
|
||
return nil, nil, nil, errors.Wrapf(err, "[filepath:%s]生成文件失败!", targetPath)
|
||
}
|
||
code[targetPath] = builder
|
||
}
|
||
|
||
injections := make(map[string]interface{}, len(asts))
|
||
for key, value := range asts {
|
||
keys := strings.Split(key, "=>")
|
||
if len(keys) == 2 {
|
||
if keys[1] == TypePluginInitializeV2 {
|
||
continue
|
||
}
|
||
if info.OnlyTemplate {
|
||
if keys[1] == TypePackageInitializeGorm || keys[1] == TypePluginInitializeGorm {
|
||
continue
|
||
}
|
||
}
|
||
if !info.AutoMigrate {
|
||
if keys[1] == TypePackageInitializeGorm || keys[1] == TypePluginInitializeGorm {
|
||
continue
|
||
}
|
||
}
|
||
var builder strings.Builder
|
||
parse, _ := value.Parse("", &builder)
|
||
if parse != nil {
|
||
_ = value.Injection(parse)
|
||
if err = value.Format("", &builder, parse); err != nil {
|
||
return nil, nil, nil, err
|
||
}
|
||
code[keys[0]] = builder
|
||
injections[keys[1]] = value
|
||
fmt.Println(keys[0], "注入成功!")
|
||
}
|
||
}
|
||
}
|
||
return code, fileList, injections, nil
|
||
}
|
||
|
||
// AddFunc 添加函数
|
||
func (uc *AutoCodeTemplateUsecase) AddFunc(info *AutoFuncInfo) error {
|
||
autoPkg, err := uc.packageRepo.FindByPackageName(context.Background(), info.Package)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if autoPkg.Template != "package" {
|
||
info.IsPlugin = true
|
||
}
|
||
|
||
if err = uc.addTemplateToFile("api.go", info); err != nil {
|
||
return err
|
||
}
|
||
if err = uc.addTemplateToFile("server.go", info); err != nil {
|
||
return err
|
||
}
|
||
if err = uc.addTemplateToFile("api.js", info); err != nil {
|
||
return err
|
||
}
|
||
return uc.addTemplateToAst("router", info)
|
||
}
|
||
|
||
// GetApiAndServer 获取API和Server代码
|
||
func (uc *AutoCodeTemplateUsecase) GetApiAndServer(info *AutoFuncInfo) (map[string]string, error) {
|
||
autoPkg, err := uc.packageRepo.FindByPackageName(context.Background(), info.Package)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if autoPkg.Template != "package" {
|
||
info.IsPlugin = true
|
||
}
|
||
|
||
apiStr, err := uc.getTemplateStr("api.go", info)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
serverStr, err := uc.getTemplateStr("server.go", info)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
jsStr, err := uc.getTemplateStr("api.js", info)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return map[string]string{"api": apiStr, "server": serverStr, "js": jsStr}, nil
|
||
}
|
||
|
||
// getTemplateStr 获取模板字符串
|
||
func (uc *AutoCodeTemplateUsecase) getTemplateStr(t string, info *AutoFuncInfo) (string, error) {
|
||
tempPath := filepath.Join(uc.config.Root, uc.config.Server, "resource", "function", t+".tpl")
|
||
files, err := template.New(filepath.Base(tempPath)).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(tempPath)
|
||
if err != nil {
|
||
return "", errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", tempPath)
|
||
}
|
||
var builder strings.Builder
|
||
if err = files.Execute(&builder, info); err != nil {
|
||
return "", errors.Wrapf(err, "[filepath:%s]生成文件失败!", tempPath)
|
||
}
|
||
return builder.String(), nil
|
||
}
|
||
|
||
// addTemplateToAst 添加模板到AST
|
||
// 支持 Kratos DDD 架构的路由注入
|
||
func (uc *AutoCodeTemplateUsecase) addTemplateToAst(t string, info *AutoFuncInfo) error {
|
||
// Kratos DDD 架构:路由文件在 internal/server/router/{package}/
|
||
tPath := filepath.Join(uc.config.Root, uc.config.Server, "internal", "server", "router", info.Package, info.HumpPackageName+".go")
|
||
funcName := fmt.Sprintf("Init%sRouter", info.StructName)
|
||
|
||
routerStr := "RouterWithoutAuth"
|
||
if info.IsAuth {
|
||
routerStr = "Router"
|
||
}
|
||
|
||
stmtStr := fmt.Sprintf("%s%s.%s(\"%s\", %sApi.%s)", info.Abbreviation, routerStr, info.Method, info.Router, info.Abbreviation, info.FuncName)
|
||
if info.IsPlugin {
|
||
tPath = filepath.Join(uc.config.Root, uc.config.Server, "internal", "plugin", info.Package, "router", info.HumpPackageName+".go")
|
||
stmtStr = fmt.Sprintf("group.%s(\"%s\", api%s.%s)", info.Method, info.Router, info.StructName, info.FuncName)
|
||
funcName = "Init"
|
||
}
|
||
|
||
src, err := os.ReadFile(tPath)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
fileSet := token.NewFileSet()
|
||
astFile, err := parser.ParseFile(fileSet, "", src, 0)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
funcDecl := utils.FindFunction(astFile, funcName)
|
||
stmtNode := utils.CreateStmt(stmtStr)
|
||
|
||
if info.IsAuth {
|
||
for i := 0; i < len(funcDecl.Body.List); i++ {
|
||
st := funcDecl.Body.List[i]
|
||
if blockStmt, ok := st.(*ast.BlockStmt); ok {
|
||
blockStmt.List = append(blockStmt.List, stmtNode)
|
||
break
|
||
}
|
||
}
|
||
} else {
|
||
for i := len(funcDecl.Body.List) - 1; i >= 0; i-- {
|
||
st := funcDecl.Body.List[i]
|
||
if blockStmt, ok := st.(*ast.BlockStmt); ok {
|
||
blockStmt.List = append(blockStmt.List, stmtNode)
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
f, err := os.Create(tPath)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer f.Close()
|
||
|
||
return format.Node(f, fileSet, astFile)
|
||
}
|
||
|
||
// addTemplateToFile 添加模板到文件
|
||
// 支持 Kratos DDD 架构的文件路径:
|
||
// - handler: internal/server/handler/{package}/
|
||
// - service: internal/service/{package}/
|
||
// - api.ts: web/src/services/kratos/
|
||
func (uc *AutoCodeTemplateUsecase) addTemplateToFile(t string, info *AutoFuncInfo) error {
|
||
getTemplateStr, err := uc.getTemplateStr(t, info)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
var target string
|
||
switch t {
|
||
case "api.go":
|
||
// Kratos DDD 架构:Handler 在 internal/server/handler/{package}/
|
||
if info.IsAi && info.ApiFunc != "" {
|
||
getTemplateStr = info.ApiFunc
|
||
}
|
||
target = filepath.Join(uc.config.Root, uc.config.Server, "internal", "server", "handler", info.Package, info.HumpPackageName+".go")
|
||
case "server.go":
|
||
// Kratos DDD 架构:Service 在 internal/service/{package}/
|
||
if info.IsAi && info.ServerFunc != "" {
|
||
getTemplateStr = info.ServerFunc
|
||
}
|
||
target = filepath.Join(uc.config.Root, uc.config.Server, "internal", "service", info.Package, info.HumpPackageName+".go")
|
||
case "api.js":
|
||
// Kratos DDD 架构:前端 API 在 web/src/services/kratos/
|
||
if info.IsAi && info.JsFunc != "" {
|
||
getTemplateStr = info.JsFunc
|
||
}
|
||
target = filepath.Join(uc.config.Root, uc.config.Web, "src", "services", "kratos", info.PackageName+".ts")
|
||
}
|
||
|
||
if info.IsPlugin {
|
||
switch t {
|
||
case "api.go":
|
||
target = filepath.Join(uc.config.Root, uc.config.Server, "internal", "plugin", info.Package, "api", info.HumpPackageName+".go")
|
||
case "server.go":
|
||
target = filepath.Join(uc.config.Root, uc.config.Server, "internal", "plugin", info.Package, "service", info.HumpPackageName+".go")
|
||
case "api.js":
|
||
target = filepath.Join(uc.config.Root, uc.config.Web, "src", "plugin", info.Package, "api", info.PackageName+".ts")
|
||
}
|
||
}
|
||
|
||
file, err := os.OpenFile(target, os.O_WRONLY|os.O_APPEND, 0644)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer file.Close()
|
||
|
||
_, err = fmt.Fprintln(file, getTemplateStr)
|
||
return err
|
||
}
|
||
|
||
// buildApis 构建API列表
|
||
func (uc *AutoCodeTemplateUsecase) buildApis(info *AutoCodeInfo) []SysApi {
|
||
apis := []SysApi{
|
||
{Path: "/" + info.Abbreviation + "/" + "create" + info.StructName, Description: "新增" + info.Description, ApiGroup: info.Description, Method: "POST"},
|
||
{Path: "/" + info.Abbreviation + "/" + "delete" + info.StructName, Description: "删除" + info.Description, ApiGroup: info.Description, Method: "DELETE"},
|
||
{Path: "/" + info.Abbreviation + "/" + "delete" + info.StructName + "ByIds", Description: "批量删除" + info.Description, ApiGroup: info.Description, Method: "DELETE"},
|
||
{Path: "/" + info.Abbreviation + "/" + "update" + info.StructName, Description: "更新" + info.Description, ApiGroup: info.Description, Method: "PUT"},
|
||
{Path: "/" + info.Abbreviation + "/" + "find" + info.StructName, Description: "根据ID获取" + info.Description, ApiGroup: info.Description, Method: "GET"},
|
||
{Path: "/" + info.Abbreviation + "/" + "get" + info.StructName + "List", Description: "获取" + info.Description + "列表", ApiGroup: info.Description, Method: "GET"},
|
||
}
|
||
if info.HasExcel {
|
||
apis = append(apis, []SysApi{
|
||
{Path: "/" + info.Abbreviation + "/" + "export" + info.StructName + "Excel", Description: "导出" + info.Description + "Excel", ApiGroup: info.Description, Method: "GET"},
|
||
{Path: "/" + info.Abbreviation + "/" + "import" + info.StructName + "Excel", Description: "导入" + info.Description + "Excel", ApiGroup: info.Description, Method: "POST"},
|
||
{Path: "/" + info.Abbreviation + "/" + "download" + info.StructName + "Template", Description: "下载" + info.Description + "模板", ApiGroup: info.Description, Method: "GET"},
|
||
}...)
|
||
}
|
||
return apis
|
||
}
|
||
|
||
// buildMenu 构建菜单
|
||
// 支持 Kratos DDD 架构的前端组件路径:
|
||
// - package: pages/{package}/{entity}/index
|
||
// - plugin: plugin/{package}/view/{entity}
|
||
func (uc *AutoCodeTemplateUsecase) buildMenu(info *AutoCodeInfo, template string) *SysBaseMenu {
|
||
// Kratos DDD 架构:React 页面在 pages/{package}/{entity}/
|
||
component := "pages/" + info.Package + "/" + info.PackageName + "/index"
|
||
if template == "plugin" {
|
||
component = "plugin/" + info.Package + "/view/" + info.PackageName
|
||
}
|
||
return &SysBaseMenu{
|
||
ParentId: 0,
|
||
Path: info.Abbreviation,
|
||
Name: info.Abbreviation,
|
||
Hidden: false,
|
||
Component: component,
|
||
Sort: 0,
|
||
Meta: Meta{
|
||
Title: info.Description,
|
||
Icon: "menu",
|
||
},
|
||
}
|
||
}
|
||
|
||
// buildMenuBtns 构建菜单按钮
|
||
func (uc *AutoCodeTemplateUsecase) buildMenuBtns(info *AutoCodeInfo) []SysBaseMenuBtn {
|
||
btns := []SysBaseMenuBtn{
|
||
{Name: "add", Desc: "新增"},
|
||
{Name: "batchDelete", Desc: "批量删除"},
|
||
{Name: "delete", Desc: "删除"},
|
||
{Name: "edit", Desc: "编辑"},
|
||
{Name: "info", Desc: "详情"},
|
||
}
|
||
if info.HasExcel {
|
||
btns = append(btns, []SysBaseMenuBtn{
|
||
{Name: "exportTemplate", Desc: "导出模板"},
|
||
{Name: "exportExcel", Desc: "导出Excel"},
|
||
{Name: "importExcel", Desc: "导入Excel"},
|
||
}...)
|
||
}
|
||
return btns
|
||
}
|
||
|
||
// buildExportTemplate 构建导出模板
|
||
func (uc *AutoCodeTemplateUsecase) buildExportTemplate(info *AutoCodeInfo) *SysExportTemplate {
|
||
fieldsMap := make(map[string]string, len(info.Fields))
|
||
for _, field := range info.Fields {
|
||
if field.Excel {
|
||
fieldsMap[field.ColumnName] = field.FieldDesc
|
||
}
|
||
}
|
||
templateInfo, _ := json.Marshal(fieldsMap)
|
||
return &SysExportTemplate{
|
||
DBName: info.BusinessDB,
|
||
Name: info.Package + "_" + info.StructName,
|
||
Table: info.TableName,
|
||
TemplateID: info.Package + "_" + info.StructName,
|
||
TemplateInfo: string(templateInfo),
|
||
}
|
||
}
|
||
|
||
// AST类型常量
|
||
const (
|
||
TypePackageApiEnter = "PackageApiEnter"
|
||
TypePackageRouterEnter = "PackageRouterEnter"
|
||
TypePackageServiceEnter = "PackageServiceEnter"
|
||
TypePackageInitializeGorm = "PackageInitializeGorm"
|
||
TypePackageInitializeRouter = "PackageInitializeRouter"
|
||
TypePackageGormGen = "PackageGormGen"
|
||
TypePluginGen = "PluginGen"
|
||
TypePluginApiEnter = "PluginApiEnter"
|
||
TypePluginInitializeV2 = "PluginInitializeV2"
|
||
TypePluginRouterEnter = "PluginRouterEnter"
|
||
TypePluginServiceEnter = "PluginServiceEnter"
|
||
TypePluginInitializeGorm = "PluginInitializeGorm"
|
||
TypePluginInitializeRouter = "PluginInitializeRouter"
|
||
)
|
||
|
||
// AutoCodeInfo 相关实体类型
|
||
type AutoCodeInfo struct {
|
||
Package string
|
||
PackageT string
|
||
TableName string
|
||
BusinessDB string
|
||
StructName string
|
||
PackageName string
|
||
Description string
|
||
Abbreviation string
|
||
HumpPackageName string
|
||
GvaModel bool
|
||
AutoMigrate bool
|
||
AutoCreateResource bool
|
||
AutoCreateApiToSql bool
|
||
AutoCreateMenuToSql bool
|
||
AutoCreateBtnAuth bool
|
||
OnlyTemplate bool
|
||
IsTree bool
|
||
TreeJson string
|
||
IsAdd bool
|
||
Fields []*AutoCodeField
|
||
GenerateWeb bool
|
||
GenerateServer bool
|
||
Module string
|
||
DictTypes []string
|
||
PrimaryField *AutoCodeField
|
||
DataSourceMap map[string]*DataSource
|
||
HasPic bool
|
||
HasFile bool
|
||
HasTimer bool
|
||
NeedSort bool
|
||
NeedJSON bool
|
||
HasRichText bool
|
||
HasDataSource bool
|
||
HasSearchTimer bool
|
||
HasArray bool
|
||
HasExcel bool
|
||
CustomMethods []*CustomMethod // 自定义查询方法
|
||
HasCustomMethods bool // 是否有自定义查询方法
|
||
}
|
||
|
||
// CustomMethod 自定义查询方法
|
||
// 用于在 GORM Gen 生成的代码中添加自定义查询方法
|
||
type CustomMethod struct {
|
||
Name string `json:"name"` // 方法名称 (如: FindByName, GetActiveUsers)
|
||
Description string `json:"description"` // 方法描述
|
||
ReturnType string `json:"returnType"` // 返回类型: single(单个), list(列表), count(计数), exists(存在检查)
|
||
Params []*CustomMethodParam `json:"params"` // 方法参数
|
||
Conditions []*CustomCondition `json:"conditions"` // 查询条件
|
||
OrderBy string `json:"orderBy"` // 排序字段 (如: created_at DESC)
|
||
Limit int `json:"limit"` // 限制数量 (0表示不限制)
|
||
}
|
||
|
||
// CustomMethodParam 自定义方法参数
|
||
type CustomMethodParam struct {
|
||
Name string `json:"name"` // 参数名称
|
||
Type string `json:"type"` // 参数类型 (string, int, uint, bool, time.Time等)
|
||
FieldName string `json:"fieldName"` // 对应的字段名 (用于生成查询条件)
|
||
}
|
||
|
||
// CustomCondition 自定义查询条件
|
||
type CustomCondition struct {
|
||
FieldName string `json:"fieldName"` // 字段名
|
||
Operator string `json:"operator"` // 操作符: eq, neq, gt, gte, lt, lte, like, in, between, isNull, isNotNull
|
||
ParamName string `json:"paramName"` // 参数名 (对应 CustomMethodParam.Name)
|
||
}
|
||
|
||
// DataSource 数据源
|
||
type DataSource struct {
|
||
DBName string `json:"dbName"`
|
||
Table string `json:"table"`
|
||
Label string `json:"label"`
|
||
Value string `json:"value"`
|
||
Association int `json:"association"`
|
||
HasDeletedAt bool `json:"hasDeletedAt"`
|
||
}
|
||
|
||
// AutoCodeField 自动代码字段
|
||
type AutoCodeField struct {
|
||
FieldName string `json:"fieldName"`
|
||
FieldDesc string `json:"fieldDesc"`
|
||
FieldType string `json:"fieldType"`
|
||
FieldJson string `json:"fieldJson"`
|
||
DataTypeLong string `json:"dataTypeLong"`
|
||
Comment string `json:"comment"`
|
||
ColumnName string `json:"columnName"`
|
||
FieldSearchType string `json:"fieldSearchType"`
|
||
FieldSearchHide bool `json:"fieldSearchHide"`
|
||
DictType string `json:"dictType"`
|
||
Form bool `json:"form"`
|
||
Table bool `json:"table"`
|
||
Desc bool `json:"desc"`
|
||
Excel bool `json:"excel"`
|
||
Require bool `json:"require"`
|
||
DefaultValue string `json:"defaultValue"`
|
||
ErrorText string `json:"errorText"`
|
||
Clearable bool `json:"clearable"`
|
||
Sort bool `json:"sort"`
|
||
PrimaryKey bool `json:"primaryKey"`
|
||
DataSource *DataSource `json:"dataSource"`
|
||
CheckDataSource bool `json:"checkDataSource"`
|
||
FieldIndexType string `json:"fieldIndexType"`
|
||
}
|
||
|
||
// AutoFuncInfo 自动函数信息
|
||
type AutoFuncInfo struct {
|
||
Package string
|
||
FuncName string
|
||
Router string
|
||
FuncDesc string
|
||
BusinessDB string
|
||
StructName string
|
||
PackageName string
|
||
Description string
|
||
Abbreviation string
|
||
HumpPackageName string
|
||
Method string
|
||
IsPlugin bool
|
||
IsAuth bool
|
||
IsPreview bool
|
||
IsAi bool
|
||
ApiFunc string
|
||
ServerFunc string
|
||
JsFunc string
|
||
}
|
||
|
||
// SysExportTemplate 导出模板
|
||
type SysExportTemplate struct {
|
||
ID uint `json:"ID" gorm:"primarykey"`
|
||
DBName string `json:"dbName" gorm:"column:db_name;comment:数据库名"`
|
||
Name string `json:"name" gorm:"column:name;comment:模板名称"`
|
||
Table string `json:"tableName" gorm:"column:table_name;comment:表名"`
|
||
TemplateID string `json:"templateId" gorm:"column:template_id;comment:模板ID"`
|
||
TemplateInfo string `json:"templateInfo" gorm:"column:template_info;type:text;comment:模板信息"`
|
||
}
|
||
|
||
func (SysExportTemplate) TableName() string {
|
||
return "sys_export_templates"
|
||
}
|