开发指南
本文档为 Go-ES 项目的开发指南,介绍了如何设置开发环境、代码规范、测试方法以及如何贡献和扩展本项目。
开发环境设置
基础环境
- 安装 Go:使用 Go 1.21 或更高版本
- 安装 Git:版本控制工具
- 安装代码编辑器:推荐使用 VSCode 或 GoLand
- 安装 MySQL:用于数据存储
- 安装 Elasticsearch:用于搜索功能
推荐的 IDE 插件
若使用 VSCode,推荐安装以下插件:
- Go: 官方 Go 语言支持插件
- Go Test Explorer: Go 测试工具
- REST Client: API 测试工具
- YAML: YAML 文件支持
- GitLens: Git 增强工具
初始化开发环境
# 克隆仓库
git clone https://github.com/saixiaoxi/go-es.git
cd go-es
# 安装依赖
go mod tidy
# 创建配置文件
cp config.example.yaml config.yaml
# 编辑配置文件,设置开发环境参数
vim config.yaml
启动开发服务器
# 运行应用
go run cmd/api/main.go
# 或构建并运行
go build -o bin/go-es cmd/api/main.go
./bin/go-es
项目扩展指南
添加新实体
- 创建领域实体:在
domain/entity/
目录下创建新实体
// 例如,创建 category.go
package entity
import (
"time"
"github.com/google/uuid"
)
// Category 类别实体
type Category struct {
ID string `json:"id" gorm:"type:varchar(36);primary_key"`
Name string `json:"name" gorm:"type:varchar(100);not null;unique"`
Description string `json:"description" gorm:"type:text"`
ParentID string `json:"parent_id" gorm:"type:varchar(36);index"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
}
// NewCategory 创建新类别
func NewCategory(name, description, parentID string) *Category {
return &Category{
ID: uuid.New().String(),
Name: name,
Description: description,
ParentID: parentID,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
- 定义仓储接口:在
domain/repository/
目录下定义仓储接口
// 例如,创建 category_repository.go
package repository
import (
"context"
"github.com/saixiaoxi/go-es/domain/entity"
)
// CategoryRepository 类别仓储接口
type CategoryRepository interface {
Create(ctx context.Context, category *entity.Category) error
FindByID(ctx context.Context, id string) (*entity.Category, error)
FindAll(ctx context.Context) ([]*entity.Category, error)
Update(ctx context.Context, category *entity.Category) error
Delete(ctx context.Context, id string) error
}
- 实现仓储:在
infrastructure/repository/
目录下实现仓储接口
// 例如,创建 category_repository_impl.go
package repository
import (
"context"
"github.com/saixiaoxi/go-es/domain/entity"
"github.com/saixiaoxi/go-es/domain/repository"
"github.com/saixiaoxi/go-es/infrastructure/persistence"
)
// CategoryRepositoryImpl 类别仓储实现
type CategoryRepositoryImpl struct {
db *persistence.Database
}
// NewCategoryRepository 创建类别仓储
func NewCategoryRepository(db *persistence.Database) repository.CategoryRepository {
return &CategoryRepositoryImpl{
db: db,
}
}
// Create 创建类别
func (r *CategoryRepositoryImpl) Create(ctx context.Context, category *entity.Category) error {
return r.db.GetDB().Create(category).Error
}
// 实现其他方法...
创建领域服务:在
domain/service/
目录下创建领域服务创建应用服务:在
application/service/
目录下创建应用服务创建 DTO:在
interfaces/dto/
目录下定义 DTO创建 API 处理器:在
interfaces/api/handler/
目录下创建处理器注册路由:在
interfaces/api/router/router.go
中注册新路由
添加新功能模块
例如,添加一个用户认证模块:
- 创建用户实体和相关仓储
- 实现认证服务:在
domain/service/
目录下实现 - 创建中间件:在
interfaces/api/middleware/
目录下创建认证中间件 - 添加认证路由和处理器
代码规范
命名约定
- 包名:使用小写单词,不使用下划线或混合大小写
- 文件名:使用下划线分隔的小写单词
- 变量名:使用驼峰命名法(CamelCase)
- 常量名:使用驼峰命名法,如果是包级常量,首字母大写
- 接口名:通常以 “er” 结尾,如 Reader, Writer
- 结构体名:使用大写开头的驼峰命名法
注释规范
- 所有导出的类型、函数、变量和常量都应该有注释
- 使用完整的句子,以被描述的对象名称开始
- 使用
//
注释,而不是/* */
- 包注释应该位于 package 子句之前
错误处理
- 错误应该被显式检查和处理
- 避免使用 panic,除非在初始化阶段
- 使用自定义错误类型增强错误信息
- 考虑使用
errors.Wrap
来添加上下文
测试指南
单元测试
为关键组件编写单元测试:
// product_service_test.go
package service_test
import (
"context"
"testing"
"github.com/saixiaoxi/go-es/domain/entity"
"github.com/saixiaoxi/go-es/domain/service"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
// MockProductRepository 模拟产品仓储
type MockProductRepository struct {
mock.Mock
}
// 实现ProductRepository接口的方法...
func TestCreateProduct(t *testing.T) {
// 准备测试数据
mockRepo := new(MockProductRepository)
productService := service.NewProductService(mockRepo)
product := entity.NewProduct("Test Product", "Description", 100.0, "Category", []string{"tag1"})
// 设置Mock期望
mockRepo.On("Create", mock.Anything, product).Return(nil)
mockRepo.On("IndexProduct", mock.Anything, product).Return(nil)
// 执行测试
err := productService.CreateProduct(context.Background(), product)
// 断言
assert.NoError(t, err)
mockRepo.AssertExpectations(t)
}
集成测试
编写集成测试验证系统各部分之间的交互:
// 在 tests/integration 目录下
func TestProductAPI(t *testing.T) {
// 准备测试环境...
// 发送HTTP请求
resp, err := http.Post(
"http://localhost:8080/api/v1/products",
"application/json",
strings.NewReader(`{"name":"Test Product","price":100,"category":"Test"}`),
)
// 断言
assert.NoError(t, err)
assert.Equal(t, http.StatusCreated, resp.StatusCode)
// 解析响应...
}
运行测试
# 运行所有测试
go test ./...
# 运行指定包的测试
go test ./domain/service/...
# 运行带覆盖率的测试
go test -cover ./...
# 生成覆盖率报告
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
文档生成
Swagger 文档
项目使用 Swagger 自动生成 API 文档:
# 安装 swag 工具
go install github.com/swaggo/swag/cmd/swag@latest
# 生成文档
swag init -g ./cmd/api/main.go
Go Doc
生成 Go 代码文档:
# 启动本地文档服务器
godoc -http=:6060
# 访问 http://localhost:6060/pkg/github.com/saixiaoxi/go-es/ 查看文档
常见开发任务
添加新的 API 端点
- 在 DTO 包中定义请求和响应结构
- 在处理器中添加新方法
- 添加 Swagger 注解
- 在路由器中注册路由
修改数据模型
- 更新实体定义
- 添加数据库迁移逻辑
- 更新 Elasticsearch 映射
- 更新相关 DTO
添加第三方服务集成
- 在
infrastructure
层创建集成客户端 - 在领域服务中添加相关功能
- 更新配置文件
性能优化
数据库查询优化
- 检查并优化数据库索引
- 使用批量操作替代循环单个操作
- 只查询必要字段
Elasticsearch 查询优化
- 优化索引设计和映射
- 使用过滤器缓存
- 限制返回字段
- 使用 _source_includes 替代返回完整文档
应用性能优化
- 使用连接池管理数据库和 Elasticsearch 连接
- 缓存重复的计算结果
- 使用 goroutine 处理并行任务
故障排除
常见问题
- “no such file or directory”:路径问题,检查文件路径是否正确
- 导入循环依赖:重新设计包的层级结构,避免循环引用
- 连接超时:检查网络连接和服务状态
调试技巧
- 使用
log
包打印调试信息 - 使用
delve
调试器进行源码级调试 - 使用
curl
或 API 客户端(如 Postman)测试 API 端点
贡献指南
提交代码
- Fork 项目并创建功能分支
- 提交前运行测试确保通过
- 使用清晰的提交消息,格式如下:
[模块] 简短描述 详细描述,解释更改原因
- 提交 Pull Request
代码审查
提交 PR 后,代码审查将检查:
- 代码风格和质量
- 测试覆盖率
- 功能完整性
- 文档更新