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

372 lines
11 KiB
Markdown
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.

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