Files
tyapi-server/scripts/migrate_repository.md

372 lines
11 KiB
Markdown
Raw Normal View History

2025-07-20 20:53:26 +08:00
# Repository 迁移指南
## 从传统模式迁移到 BaseRepositoryImpl 模式
### 步骤 1修改结构体定义
**之前:**
```go
type GormExampleRepository struct {
db *gorm.DB
logger *zap.Logger
}
```
**之后:**
```go
type GormExampleRepository struct {
*database.BaseRepositoryImpl // 嵌入基础Repository实现
}
```
### 步骤 2修改构造函数
**之前:**
```go
func NewGormExampleRepository(db *gorm.DB, logger *zap.Logger) ExampleRepository {
return &GormExampleRepository{
db: db,
logger: logger,
}
}
```
**之后:**
```go
func NewGormExampleRepository(db *gorm.DB, logger *zap.Logger) ExampleRepository {
return &GormExampleRepository{
BaseRepositoryImpl: database.NewBaseRepositoryImpl(db, logger),
}
}
```
### 步骤 3删除 getDB 方法
**删除这样的代码:**
```go
func (r *GormExampleRepository) getDB(ctx context.Context) *gorm.DB {
if tx, ok := database.GetTx(ctx); ok {
return tx
}
return r.db
}
```
### 步骤 4实现 Repository[T] 接口方法
#### 基础 CRUD 操作
**Create - 之前:**
```go
func (r *GormExampleRepository) Create(ctx context.Context, entity Entity) (Entity, error) {
r.logger.Info("创建实体", zap.String("id", entity.ID))
err := r.getDB(ctx).WithContext(ctx).Create(&entity).Error
return entity, err
}
```
**Create - 之后:**
```go
func (r *GormExampleRepository) Create(ctx context.Context, entity Entity) (Entity, error) {
r.GetLogger().Info("创建实体", zap.String("id", entity.ID))
err := r.BaseRepositoryImpl.Create(ctx, &entity)
return entity, err
}
```
**GetByID - 之前:**
```go
func (r *GormExampleRepository) GetByID(ctx context.Context, id string) (Entity, error) {
var entity Entity
err := r.getDB(ctx).WithContext(ctx).Where("id = ?", id).First(&entity).Error
return entity, err
}
```
**GetByID - 之后:**
```go
func (r *GormExampleRepository) GetByID(ctx context.Context, id string) (Entity, error) {
var entity Entity
err := r.BaseRepositoryImpl.GetByID(ctx, id, &entity)
return entity, err
}
```
**Update - 之前:**
```go
func (r *GormExampleRepository) Update(ctx context.Context, entity Entity) error {
r.logger.Info("更新实体", zap.String("id", entity.ID))
return r.getDB(ctx).WithContext(ctx).Save(&entity).Error
}
```
**Update - 之后:**
```go
func (r *GormExampleRepository) Update(ctx context.Context, entity Entity) error {
r.GetLogger().Info("更新实体", zap.String("id", entity.ID))
return r.BaseRepositoryImpl.Update(ctx, &entity)
}
```
#### 批量操作
**CreateBatch**
```go
func (r *GormExampleRepository) CreateBatch(ctx context.Context, entities []Entity) error {
r.GetLogger().Info("批量创建实体", zap.Int("count", len(entities)))
return r.BaseRepositoryImpl.CreateBatch(ctx, &entities)
}
```
**GetByIDs**
```go
func (r *GormExampleRepository) GetByIDs(ctx context.Context, ids []string) ([]Entity, error) {
var entities []Entity
err := r.BaseRepositoryImpl.GetByIDs(ctx, ids, &entities)
return entities, err
}
```
**UpdateBatch**
```go
func (r *GormExampleRepository) UpdateBatch(ctx context.Context, entities []Entity) error {
r.GetLogger().Info("批量更新实体", zap.Int("count", len(entities)))
return r.BaseRepositoryImpl.UpdateBatch(ctx, &entities)
}
```
**DeleteBatch**
```go
func (r *GormExampleRepository) DeleteBatch(ctx context.Context, ids []string) error {
r.GetLogger().Info("批量删除实体", zap.Strings("ids", ids))
return r.BaseRepositoryImpl.DeleteBatch(ctx, ids, &Entity{})
}
```
### 步骤 5实现 BaseRepository 接口方法
#### 基础操作
**Delete**
```go
func (r *GormExampleRepository) Delete(ctx context.Context, id string) error {
r.GetLogger().Info("删除实体", zap.String("id", id))
return r.BaseRepositoryImpl.Delete(ctx, id, &Entity{})
}
```
**Exists**
```go
func (r *GormExampleRepository) Exists(ctx context.Context, id string) (bool, error) {
return r.BaseRepositoryImpl.Exists(ctx, id, &Entity{})
}
```
**Count**
```go
func (r *GormExampleRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
// 如果需要自定义搜索逻辑
if options.Search != "" {
return r.CountWhere(ctx, &Entity{}, "name LIKE ? OR description LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%")
}
return r.BaseRepositoryImpl.Count(ctx, &Entity{}, options)
}
```
**SoftDelete**
```go
func (r *GormExampleRepository) SoftDelete(ctx context.Context, id string) error {
r.GetLogger().Info("软删除实体", zap.String("id", id))
return r.BaseRepositoryImpl.SoftDelete(ctx, id, &Entity{})
}
```
**Restore**
```go
func (r *GormExampleRepository) Restore(ctx context.Context, id string) error {
r.GetLogger().Info("恢复实体", zap.String("id", id))
return r.BaseRepositoryImpl.Restore(ctx, id, &Entity{})
}
```
#### 列表查询
**List**
```go
func (r *GormExampleRepository) List(ctx context.Context, options interfaces.ListOptions) ([]Entity, error) {
var entities []Entity
// 如果需要自定义搜索逻辑
if options.Search != "" {
query := r.GetDB(ctx).Model(&Entity{})
// 应用筛选条件
if options.Filters != nil {
for key, value := range options.Filters {
query = query.Where(key+" = ?", value)
}
}
// 自定义搜索逻辑
query = query.Where("name LIKE ? OR description LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%")
// 应用预加载
for _, include := range options.Include {
query = query.Preload(include)
}
// 应用排序
if options.Sort != "" {
order := "ASC"
if options.Order == "desc" || options.Order == "DESC" {
order = "DESC"
}
query = query.Order(options.Sort + " " + order)
} else {
query = query.Order("created_at DESC")
}
// 应用分页
if options.Page > 0 && options.PageSize > 0 {
offset := (options.Page - 1) * options.PageSize
query = query.Offset(offset).Limit(options.PageSize)
}
return entities, query.Find(&entities).Error
}
// 使用基础实现
err := r.BaseRepositoryImpl.List(ctx, &Entity{}, &entities, options)
return entities, err
}
```
### 步骤 6业务方法使用辅助方法
**使用 FindOne**
```go
func (r *GormExampleRepository) GetByCode(ctx context.Context, code string) (*Entity, error) {
var entity Entity
err := r.FindOne(ctx, &entity, "code = ?", code)
if err != nil {
return nil, err
}
return &entity, nil
}
```
**使用 FindWhere**
```go
func (r *GormExampleRepository) GetByStatus(ctx context.Context, status string) ([]Entity, error) {
var entities []Entity
err := r.FindWhere(ctx, &entities, "status = ?", status)
return entities, err
}
```
**使用 CountWhere**
```go
func (r *GormExampleRepository) CountByStatus(ctx context.Context, status string) (int64, error) {
return r.CountWhere(ctx, &Entity{}, "status = ?", status)
}
```
**使用 ExistsWhere**
```go
func (r *GormExampleRepository) ExistsByCode(ctx context.Context, code string) (bool, error) {
return r.ExistsWhere(ctx, &Entity{}, "code = ?", code)
}
```
### 步骤 7复杂查询仍使用 GetDB
对于复杂查询,继续使用 `r.GetDB(ctx)` 来获取数据库连接:
```go
func (r *GormExampleRepository) ComplexQuery(ctx context.Context, params QueryParams) ([]Entity, error) {
var entities []Entity
query := r.GetDB(ctx).Model(&Entity{})
// 添加复杂的查询逻辑
if params.Status != "" {
query = query.Where("status = ?", params.Status)
}
if params.DateRange != nil {
query = query.Where("created_at BETWEEN ? AND ?", params.DateRange.Start, params.DateRange.End)
}
// 连接查询
query = query.Joins("LEFT JOIN related_table ON entities.related_id = related_table.id").
Where("related_table.active = ?", true)
return entities, query.Find(&entities).Error
}
```
## BaseRepositoryImpl 提供的方法
### 核心方法
- `GetDB(ctx)` - 获取数据库连接(自动支持事务)
- `GetLogger()` - 获取日志记录器
- `WithTx(tx)` - 创建事务版本的Repository
### 基础 CRUD
- `Create(ctx, entity)` - 创建实体
- `GetByID(ctx, id, entity)` - 根据ID获取
- `Update(ctx, entity)` - 更新实体
- `Delete(ctx, id, entity)` - 删除实体
- `Exists(ctx, id, entity)` - 检查存在
### 批量操作
- `CreateBatch(ctx, entities)` - 批量创建
- `GetByIDs(ctx, ids, entities)` - 批量获取
- `UpdateBatch(ctx, entities)` - 批量更新
- `DeleteBatch(ctx, ids, entity)` - 批量删除
### 查询方法
- `List(ctx, entity, entities, options)` - 列表查询
- `Count(ctx, entity, options)` - 计数查询
- `FindWhere(ctx, entities, condition, args...)` - 条件查询
- `FindOne(ctx, entity, condition, args...)` - 单个查询
- `CountWhere(ctx, entity, condition, args...)` - 条件计数
- `ExistsWhere(ctx, entity, condition, args...)` - 条件存在
### 软删除
- `SoftDelete(ctx, id, entity)` - 软删除
- `Restore(ctx, id, entity)` - 恢复
### 事务辅助
- `ExecuteInTransaction(ctx, fn)` - 执行事务
- `IsInTransaction(ctx)` - 检查事务状态
## 优势
1. **统一事务处理**所有Repository自动支持事务
2. **减少代码重复**移除重复的getDB方法和基础CRUD实现
3. **提高可维护性**:统一的事务逻辑在一个地方维护
4. **类型安全**:编译时检查,减少运行时错误
5. **更清晰的职责**Repository专注于业务逻辑基础功能由BaseRepository提供
6. **完整的接口支持**自动实现Repository[T]和BaseRepository的所有方法
## 迁移检查清单
- [ ] 修改结构体定义,嵌入 BaseRepositoryImpl
- [ ] 更新构造函数
- [ ] 删除 getDB 方法
- [ ] 实现 Repository[T] 接口的所有方法
- [ ] 实现 BaseRepository 接口的所有方法
- [ ] 使用辅助方法替换重复的查询逻辑
- [ ] 为特殊需求重写搜索逻辑
- [ ] 运行测试确保功能正常
- [ ] 更新相关文档
## 注意事项
1. **方法参数**BaseRepositoryImpl的方法使用`interface{}`,需要传递指针
2. **搜索逻辑**:默认搜索逻辑可能不适合所有实体,需要重写
3. **预加载**使用ListOptions的Include字段或直接调用GetDB()
4. **事务支持**:所有方法自动支持事务,无需额外处理
5. **日志记录**使用GetLogger()而不是直接访问logger字段