feat: 代码生成

This commit is contained in:
Blizzard
2026-04-28 10:29:02 +08:00
parent 7e282b36d7
commit b343856b58
38 changed files with 2199 additions and 51 deletions
+211
View File
@@ -0,0 +1,211 @@
package codegen
import (
"fmt"
"os"
"path/filepath"
"strings"
codegenModel "sundynix-go/model/codegen"
)
// autoRegister 在代码生成后自动修改项目的注册文件
// 包括:initialize/gorm.go, initialize/router.go, api/v1/enter.go, router/enter.go, service/enter.go
// 以及子模块的 enter.go 文件(增量追加新 feature
func (s *CodegenService) autoRegister(outputDir string, config codegenModel.GenConfig) error {
for _, module := range config.Modules {
if strings.TrimSpace(module.PackageName) == "" || strings.TrimSpace(module.Name) == "" {
continue
}
pkg := module.PackageName
pascalModule := module.Name
// 判断是否新模块:检查 router/{pkg}/enter.go 是否已存在
routerEnterPath := filepath.Join(outputDir, "router", pkg, "enter.go")
isNewModule := !fileExistsOnDisk(routerEnterPath)
if isNewModule {
// 新模块:注册到 5 个顶级文件
if err := s.registerGorm(outputDir, module); err != nil {
return fmt.Errorf("注册 gorm 迁移失败: %w", err)
}
if err := s.registerTopEnter(outputDir, pkg, pascalModule); err != nil {
return fmt.Errorf("注册顶级 enter 失败: %w", err)
}
if err := s.registerRouter(outputDir, pkg, pascalModule, module.Features); err != nil {
return fmt.Errorf("注册路由初始化失败: %w", err)
}
} else {
// 已有模块:增量注册新 feature
if err := s.registerGormIncremental(outputDir, module); err != nil {
return fmt.Errorf("增量注册 gorm 迁移失败: %w", err)
}
if err := s.registerRouterIncremental(outputDir, pkg, pascalModule, module.Features); err != nil {
return fmt.Errorf("增量注册路由失败: %w", err)
}
}
}
return nil
}
// registerGorm 向 initialize/gorm.go 的 AutoMigrate 中追加所有 feature
func (s *CodegenService) registerGorm(outputDir string, module codegenModel.Module) error {
gormPath := filepath.Join(outputDir, "initialize", "gorm.go")
content, err := os.ReadFile(gormPath)
if err != nil {
return fmt.Errorf("读取 %s 失败: %w", gormPath, err)
}
text := string(content)
pkg := module.PackageName
// 1. 添加 import
importLine := fmt.Sprintf("\t\"sundynix-go/model/%s\"", pkg)
if !strings.Contains(text, importLine) {
text = strings.Replace(text, "\"go.uber.org/zap\"", importLine+"\n\n\t\"go.uber.org/zap\"", 1)
}
// 2. 在 AutoMigrate 闭合 ) 前插入 model
for _, feature := range module.Features {
if strings.TrimSpace(feature.Name) == "" {
continue
}
modelEntry := fmt.Sprintf("\t\t%s.%s{},", pkg, feature.Name)
if !strings.Contains(text, modelEntry) {
// 找最后一个 ) 之前的位置(AutoMigrate 结束)
closeIdx := strings.LastIndex(text, "\t)")
if closeIdx > 0 {
text = text[:closeIdx] + "\n" + modelEntry + "\n" + text[closeIdx:]
}
}
}
return os.WriteFile(gormPath, []byte(text), 0644)
}
// registerGormIncremental 增量模式:只追加新 feature 到 AutoMigrate
func (s *CodegenService) registerGormIncremental(outputDir string, module codegenModel.Module) error {
return s.registerGorm(outputDir, module) // 逻辑相同,Contains 检查保证幂等
}
// registerTopEnter 向 api/v1/enter.go、router/enter.go、service/enter.go 追加新模块
func (s *CodegenService) registerTopEnter(outputDir string, pkg, pascalModule string) error {
// --- api/v1/enter.go ---
apiEnterPath := filepath.Join(outputDir, "api", "v1", "enter.go")
if err := appendToEnterFile(apiEnterPath,
fmt.Sprintf("\t\"sundynix-go/api/v1/%s\"", pkg),
fmt.Sprintf("\t%sApiGroup %s.ApiGroup", pascalModule, pkg),
); err != nil {
return err
}
// --- router/enter.go ---
routerEnterPath := filepath.Join(outputDir, "router", "enter.go")
if err := appendToEnterFile(routerEnterPath,
fmt.Sprintf("\t\"sundynix-go/router/%s\"", pkg),
fmt.Sprintf("\t%s %s.RouterGroup", pascalModule, pkg),
); err != nil {
return err
}
// --- service/enter.go ---
svcEnterPath := filepath.Join(outputDir, "service", "enter.go")
if err := appendToEnterFile(svcEnterPath,
fmt.Sprintf("\t\"sundynix-go/service/%s\"", pkg),
fmt.Sprintf("\t%sServiceGroup %s.ServiceGroup", pascalModule, pkg),
); err != nil {
return err
}
return nil
}
// appendToEnterFile 向 enter.go 文件追加 import 和 struct 字段
func appendToEnterFile(filePath, importLine, structField string) error {
content, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("读取 %s 失败: %w", filePath, err)
}
text := string(content)
// 添加 import(如果不存在)
if !strings.Contains(text, importLine) {
// 在最后一个 import 之前插入
lastImportIdx := strings.LastIndex(text, "\t\"")
if lastImportIdx > 0 {
// 找这行的换行符
nlIdx := strings.Index(text[lastImportIdx:], "\n")
if nlIdx > 0 {
insertPos := lastImportIdx + nlIdx + 1
text = text[:insertPos] + importLine + "\n" + text[insertPos:]
}
}
}
// 添加 struct 字段(如果不存在)
if !strings.Contains(text, structField) {
// 在 struct 的 } 之前插入
closeBrace := strings.Index(text, "\n}")
if closeBrace > 0 {
text = text[:closeBrace] + "\n" + structField + text[closeBrace:]
}
}
return os.WriteFile(filePath, []byte(text), 0644)
}
// registerRouter 向 initialize/router.go 追加路由注册代码
func (s *CodegenService) registerRouter(outputDir, pkg, pascalModule string, features []codegenModel.Feature) error {
routerPath := filepath.Join(outputDir, "initialize", "router.go")
content, err := os.ReadFile(routerPath)
if err != nil {
return fmt.Errorf("读取 %s 失败: %w", routerPath, err)
}
text := string(content)
// 1. 添加变量声明:xxxRouter := router.GroupApp.Xxx
varLine := fmt.Sprintf("\t%sRouter := router.GroupApp.%s", lowerFirst(pkg), pascalModule)
if !strings.Contains(text, varLine) {
// 在 NeedAuthGroup 行之前插入
needAuthIdx := strings.Index(text, "NeedAuthGroup := Router.Group")
if needAuthIdx > 0 {
text = text[:needAuthIdx] + varLine + "\n\n\t" + text[needAuthIdx:]
}
}
// 2. 添加路由注册:xxxRouter.InitXxxRouter(NeedAuthGroup)
for _, feature := range features {
if strings.TrimSpace(feature.Name) == "" {
continue
}
routerCall := fmt.Sprintf("\t\t%sRouter.Init%sRouter(NeedAuthGroup)", lowerFirst(pkg), feature.Name)
if !strings.Contains(text, routerCall) {
// 在最后一个 Init...Router 行之后插入
lastInitIdx := strings.LastIndex(text, "Router(NeedAuthGroup)")
if lastInitIdx > 0 {
nlIdx := strings.Index(text[lastInitIdx:], "\n")
if nlIdx > 0 {
insertPos := lastInitIdx + nlIdx
comment := fmt.Sprintf(" //%s", feature.Comment)
if feature.Comment == "" {
comment = ""
}
text = text[:insertPos] + "\n" + routerCall + comment + text[insertPos:]
}
}
}
}
return os.WriteFile(routerPath, []byte(text), 0644)
}
// registerRouterIncremental 增量模式:只追加新 feature 的路由注册
func (s *CodegenService) registerRouterIncremental(outputDir, pkg, pascalModule string, features []codegenModel.Feature) error {
return s.registerRouter(outputDir, pkg, pascalModule, features) // Contains 检查保证幂等
}
// fileExistsOnDisk 检查文件是否已存在
func fileExistsOnDisk(path string) bool {
_, err := os.Stat(path)
return err == nil
}