Files
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

389 lines
7.5 KiB
Markdown

# Quick Start Guide
## 概述
本指南展示如何使用重构后的 pkg/ast/provider 包来解析 Go 源码中的 `@provider` 注释并生成依赖注入代码。
## 前置条件
- Go 1.24.0+
- 理解 Go AST 解析
- 了解依赖注入概念
## 基本用法
### 1. 解析单个文件
```go
package main
import (
"fmt"
"log"
"go.ipao.vip/atomctl/v2/pkg/ast/provider"
)
func main() {
// 解析单个文件
providers, err := provider.ParseFile("path/to/your/file.go")
if err != nil {
log.Fatal(err)
}
// 打印解析结果
for _, p := range providers {
fmt.Printf("Provider: %s\n", p.StructName)
fmt.Printf(" Mode: %s\n", p.Mode)
fmt.Printf(" Return Type: %s\n", p.ReturnType)
fmt.Printf(" Inject Params: %d\n", len(p.InjectParams))
}
}
```
### 2. 批量解析目录
```go
package main
import (
"fmt"
"log"
"go.ipao.vip/atomctl/v2/pkg/ast/provider"
)
func main() {
// 创建解析器配置
config := provider.ParserConfig{
StrictMode: true,
AllowTestFile: false,
IgnorePattern: "*.gen.go",
}
// 创建解析器
parser := provider.NewParser(config)
// 解析目录
providers, err := parser.ParseDir("./app")
if err != nil {
log.Fatal(err)
}
// 生成代码
for _, p := range providers {
err := provider.Render(p.ProviderFile, []Provider{p})
if err != nil {
log.Printf("Failed to render %s: %v", p.StructName, err)
}
}
}
```
## 支持的 Provider 注释格式
### 基本格式
```go
// @provider
type UserService struct {
// ...
}
```
### 带模式
```go
// @provider(grpc)
type UserService struct {
// ...
}
```
### 带注入模式
```go
// @provider:only
type UserService struct {
Repo *UserRepo `inject:"true"`
Log *Logger `inject:"true"`
}
```
### 完整格式
```go
// @provider(grpc):only contracts.Initial atom.GroupInitial
type UserService struct {
Repo *UserRepo `inject:"true"`
Log *Logger `inject:"true"`
}
```
## 测试指南
### 运行测试
```bash
# 运行所有测试
go test ./pkg/ast/provider/...
# 运行测试并显示覆盖率
go test -cover ./pkg/ast/provider/...
# 运行基准测试
go test -bench=. ./pkg/ast/provider/...
```
### 编写测试
```go
package provider_test
import (
"testing"
"go.ipao.vip/atomctl/v2/pkg/ast/provider"
"github.com/stretchr/testify/assert"
)
func TestParseProvider(t *testing.T) {
// 准备测试代码
source := `
package main
// @provider:only contracts.Initial
type TestService struct {
Repo *TestRepo `inject:"true"`
}
`
// 创建临时文件
tmpFile := createTempFile(t, source)
defer os.Remove(tmpFile)
// 解析
providers, err := provider.ParseFile(tmpFile)
// 验证
assert.NoError(t, err)
assert.Len(t, providers, 1)
p := providers[0]
assert.Equal(t, "TestService", p.StructName)
assert.Equal(t, "contracts.Initial", p.ReturnType)
assert.True(t, p.InjectMode.IsOnly())
}
```
## 重构指南
### 从旧版本迁移
1. **更新导入路径**
```go
// 旧版本
import "go.ipao.vip/atomctl/v2/pkg/ast/provider"
// 新版本(相同的导入路径)
import "go.ipao.vip/atomctl/v2/pkg/ast/provider"
```
2. **使用新的 API**
```go
// 旧版本
providers := provider.Parse("file.go")
// 新版本(向后兼容)
providers := provider.Parse("file.go") // 仍然支持
// 推荐的新方式
parser := provider.NewParser(provider.DefaultConfig())
providers, err := parser.ParseFile("file.go")
```
### 自定义扩展
#### 1. 自定义 Provider 模式
```go
// 实现自定义模式处理器
type CustomModeHandler struct{}
func (h *CustomModeHandler) Handle(ctx *provider.ParserContext, comment *provider.ProviderComment) (*provider.Provider, error) {
// 自定义处理逻辑
return &provider.Provider{
Mode: provider.ProviderMode("custom"),
// ...
}, nil
}
// 注册自定义模式
provider.RegisterProviderMode("custom", &CustomModeHandler{})
```
#### 2. 自定义验证器
```go
// 实现自定义验证器
type CustomValidator struct{}
func (v *CustomValidator) Validate(p *provider.Provider) []error {
var errors []error
// 自定义验证逻辑
if p.StructName == "" {
errors = append(errors, fmt.Errorf("struct name cannot be empty"))
}
return errors
}
// 添加到验证链
parser.AddValidator(&CustomValidator{})
```
## 性能优化
### 1. 并行解析
```go
// 使用并行解析提高性能
func ParseProjectParallel(root string) ([]*provider.Provider, error) {
files, err := findGoFiles(root)
if err != nil {
return nil, err
}
var wg sync.WaitGroup
providers := make([]*provider.Provider, 0, len(files))
errChan := make(chan error, len(files))
for _, file := range files {
wg.Add(1)
go func(f string) {
defer wg.Done()
ps, err := provider.ParseFile(f)
if err != nil {
errChan <- err
return
}
providers = append(providers, ps...)
}(file)
}
wg.Wait()
close(errChan)
// 检查错误
for err := range errChan {
if err != nil {
return nil, err
}
}
return providers, nil
}
```
### 2. 缓存机制
```go
// 使用缓存避免重复解析
type CachedParser struct {
cache map[string][]*provider.Provider
parser *provider.Parser
}
func NewCachedParser() *CachedParser {
return &CachedParser{
cache: make(map[string][]*provider.Provider),
parser: provider.NewParser(provider.DefaultConfig()),
}
}
func (cp *CachedParser) ParseFile(file string) ([]*provider.Provider, error) {
if providers, ok := cp.cache[file]; ok {
return providers, nil
}
providers, err := cp.parser.ParseFile(file)
if err != nil {
return nil, err
}
cp.cache[file] = providers
return providers, nil
}
```
## 故障排除
### 常见错误
1. **解析错误**
```
error: failed to parse provider comment: invalid mode format
```
解决方案:检查 @provider 注释格式是否正确
2. **导入错误**
```
error: cannot resolve import path "github.com/unknown/pkg"
```
解决方案:确保所有导入的包都存在
3. **验证错误**
```
error: provider struct has invalid return type
```
解决方案:确保返回类型是有效的 Go 类型
### 调试技巧
1. **启用详细日志**
```go
provider.SetLogLevel(logrus.DebugLevel)
```
2. **使用解析器上下文**
```go
ctx := provider.NewParserContext(provider.ParserConfig{
StrictMode: true,
ErrorHandler: func(err error) {
log.Printf("Parse error: %v", err)
},
})
```
3. **验证生成的代码**
```go
if err := provider.ValidateGeneratedCode(code); err != nil {
log.Printf("Generated code validation failed: %v", err)
}
```
## 最佳实践
1. **保持注释简洁**
```go
// 推荐
// @provider:only contracts.Initial
// 不推荐
// @provider:only contracts.Initial atom.GroupInitial // 这是一个复杂的服务
```
2. **使用明确的类型**
```go
// 推荐
type UserService struct {
Repo *UserRepository `inject:"true"`
}
// 不推荐
type UserService struct {
Repo interface{} `inject:"true"`
}
```
3. **合理组织代码**
```go
// 将相关的 provider 放在同一个文件中
// 使用明确的包名和结构名
// 避免循环依赖
```
## 下一步
- 查看 [data-model.md](data-model.md) 了解详细的数据模型
- 阅读 [research.md](research.md) 了解重构决策过程
- 查看 [contracts/](contracts/) 目录了解 API 契约