Files
atomctl/cmd/gen_route.go
Rogee e1f83ae469 feat: 重构 pkg/ast/provider 模块,优化代码组织逻辑和功能实现
## 主要改进

### 架构重构
- 将单体 provider.go 拆分为多个专门的模块文件
- 实现了清晰的职责分离和模块化设计
- 遵循 SOLID 原则,提高代码可维护性

### 新增功能
- **验证规则系统**: 实现了完整的 provider 验证框架
- **报告生成器**: 支持多种格式的验证报告 (JSON/HTML/Markdown/Text)
- **解析器优化**: 重新设计了解析流程,提高性能和可扩展性
- **错误处理**: 增强了错误处理和诊断能力

### 修复关键 Bug
- 修复 @provider(job) 注解缺失 __job 注入参数的问题
- 统一了 job 和 cronjob 模式的处理逻辑
- 确保了 provider 生成的正确性和一致性

### 代码质量提升
- 添加了完整的测试套件
- 引入了 golangci-lint 代码质量检查
- 优化了代码格式和结构
- 增加了详细的文档和规范

### 文件结构优化
```
pkg/ast/provider/
├── types.go              # 类型定义
├── parser.go             # 解析器实现
├── validator.go          # 验证规则
├── report_generator.go   # 报告生成
├── renderer.go           # 渲染器
├── comment_parser.go     # 注解解析
├── modes.go             # 模式定义
├── errors.go            # 错误处理
└── validator_test.go    # 测试文件
```

### 兼容性
- 保持向后兼容性
- 支持现有的所有 provider 模式
- 优化了 API 设计和用户体验

This completes the implementation of T025-T029 tasks following TDD principles,
including validation rules implementation and critical bug fixes.
2025-09-19 18:58:30 +08:00

117 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cmd
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"github.com/samber/lo"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"go.ipao.vip/atomctl/v2/pkg/ast/route"
"go.ipao.vip/atomctl/v2/pkg/utils/gomod"
)
func CommandGenRoute(root *cobra.Command) {
cmd := &cobra.Command{
Use: "route",
Short: "generate routes",
Long: `扫描项目控制器,解析注释生成 routes.gen.go。
用法与规则:
- 扫描根目录通过 --path 指定(默认 CWD会在 <path>/app/http 下递归搜索。
- 使用注释定义路由与参数绑定:
- @Router <path> [<method>] 例如:@Router /users/:id [get]
- @Bind <name> (<position>) key(<key>) [model(<field[:field_type]>)]
- position 枚举path|query|body|header|cookie|local|file
- model() 形式model() 默认 id:intmodel(id) 默认 intmodel(id:int) 显式类型
参数位置与类型建议:
- path标量或结合 model() 从路径值加载模型
- query/header标量或结构体
- cookie标量string 有快捷写法
- body结构体或标量
- file固定 multipart.FileHeader
- local任意类型上下文本地值
说明:生成完成后会自动运行 gen provider 以补全依赖注入。`,
RunE: commandGenRouteE,
PostRunE: commandGenProviderE,
}
cmd.Flags().String("path", ".", "Base path to scan (defaults to CWD)")
root.AddCommand(cmd)
}
// https://go.ipao.vip/atomctl/v2/pkg/swag?tab=readme-ov-file#api-operation
func commandGenRouteE(cmd *cobra.Command, args []string) error {
var err error
var path string
if len(args) > 0 {
path = args[0]
} else {
path, err = os.Getwd()
if err != nil {
return err
}
}
// allow overriding via --path flag
if f := cmd.Flag("path"); f != nil && f.Value.String() != "." && f.Value.String() != "" {
path = f.Value.String()
}
path, _ = filepath.Abs(path)
err = gomod.Parse(filepath.Join(path, "go.mod"))
if err != nil {
return err
}
routes := []route.RouteDefinition{}
modulePath := filepath.Join(path, "app/http")
if _, err := os.Stat(modulePath); os.IsNotExist(err) {
return fmt.Errorf("routes directory not found: %s (set --path to your project root)", modulePath)
}
// controllerPattern := regexp.MustCompile(`controller(_?\w+)?\.go`)
err = filepath.WalkDir(modulePath, func(path string, d fs.DirEntry, err error) error {
if d.IsDir() {
return nil
}
// if !controllerPattern.MatchString(d.Name()) {
// return nil
// }
if strings.HasSuffix(path, ".gen.go") {
return nil
}
if strings.HasSuffix(path, "_test.go") {
return nil
}
routes = append(routes, route.ParseFile(path)...)
return nil
})
if err != nil {
return err
}
routeGroups := lo.GroupBy(routes, func(item route.RouteDefinition) string {
return filepath.Dir(item.Path)
})
for path, routes := range routeGroups {
if err := route.Render(path, routes); err != nil {
log.WithError(err).WithField("path", path).Error("render route failed")
}
}
return nil
}