Files
tyapi-server/scripts/migrate_repository.md
2025-07-20 20:53:26 +08:00

11 KiB
Raw Blame History

Repository 迁移指南

从传统模式迁移到 BaseRepositoryImpl 模式

步骤 1修改结构体定义

之前:

type GormExampleRepository struct {
    db     *gorm.DB
    logger *zap.Logger
}

之后:

type GormExampleRepository struct {
    *database.BaseRepositoryImpl // 嵌入基础Repository实现
}

步骤 2修改构造函数

之前:

func NewGormExampleRepository(db *gorm.DB, logger *zap.Logger) ExampleRepository {
    return &GormExampleRepository{
        db:     db,
        logger: logger,
    }
}

之后:

func NewGormExampleRepository(db *gorm.DB, logger *zap.Logger) ExampleRepository {
    return &GormExampleRepository{
        BaseRepositoryImpl: database.NewBaseRepositoryImpl(db, logger),
    }
}

步骤 3删除 getDB 方法

删除这样的代码:

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 - 之前:

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 - 之后:

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 - 之前:

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 - 之后:

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 - 之前:

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 - 之后:

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

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

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

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

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

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

func (r *GormExampleRepository) Exists(ctx context.Context, id string) (bool, error) {
    return r.BaseRepositoryImpl.Exists(ctx, id, &Entity{})
}

Count

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

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

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

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

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

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

func (r *GormExampleRepository) CountByStatus(ctx context.Context, status string) (int64, error) {
    return r.CountWhere(ctx, &Entity{}, "status = ?", status)
}

使用 ExistsWhere

func (r *GormExampleRepository) ExistsByCode(ctx context.Context, code string) (bool, error) {
    return r.ExistsWhere(ctx, &Entity{}, "code = ?", code)
}

步骤 7复杂查询仍使用 GetDB

对于复杂查询,继续使用 r.GetDB(ctx) 来获取数据库连接:

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字段