refactor: introduce Wire dependency injection and interface-based architecture
- Add Google Wire for compile-time dependency injection - Replace concrete service types with interfaces across handlers - Remove global state (DB, cache) in favor of constructor injection - Split monolithic files into focused modules: - config: separate files for each config domain - dto: converters split by domain (user, post, message, group) - cache: separate metrics.go and redis_cache.go - Introduce unified apperrors package with string-based error codes - Add transaction support with context-aware repository methods - Upgrade golang.org/x/crypto from 0.17.0 to 0.26.0
This commit is contained in:
@@ -2,6 +2,7 @@ package repository
|
||||
|
||||
import (
|
||||
"carrot_bbs/internal/model"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -17,6 +18,14 @@ func NewPostRepository(db *gorm.DB) *PostRepository {
|
||||
return &PostRepository{db: db}
|
||||
}
|
||||
|
||||
// getDB 获取数据库连接(优先使用 context 中的事务)
|
||||
func (r *PostRepository) getDB(ctx context.Context) *gorm.DB {
|
||||
if tx := GetTxFromContext(ctx); tx != nil {
|
||||
return tx
|
||||
}
|
||||
return r.db
|
||||
}
|
||||
|
||||
// Create 创建帖子
|
||||
func (r *PostRepository) Create(post *model.Post, images []string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
@@ -419,3 +428,104 @@ func (r *PostRepository) GetByIDs(ids []string) ([]*model.Post, error) {
|
||||
|
||||
return ordered, nil
|
||||
}
|
||||
|
||||
// ========== Context-aware methods for transaction support ==========
|
||||
|
||||
// CreateWithContext 创建帖子(支持事务)
|
||||
func (r *PostRepository) CreateWithContext(ctx context.Context, post *model.Post, images []string) error {
|
||||
db := r.getDB(ctx)
|
||||
|
||||
// 如果 context 中有事务,直接使用
|
||||
if tx := GetTxFromContext(ctx); tx != nil {
|
||||
return r.createWithTx(tx, post, images)
|
||||
}
|
||||
|
||||
// 否则开启新事务
|
||||
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
return r.createWithTx(tx, post, images)
|
||||
})
|
||||
}
|
||||
|
||||
// createWithTx 使用指定事务创建帖子
|
||||
func (r *PostRepository) createWithTx(tx *gorm.DB, post *model.Post, images []string) error {
|
||||
// 创建帖子
|
||||
if err := tx.Create(post).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建图片记录
|
||||
for i, url := range images {
|
||||
image := &model.PostImage{
|
||||
PostID: post.ID,
|
||||
URL: url,
|
||||
SortOrder: i,
|
||||
}
|
||||
if err := tx.Create(image).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByIDWithContext 根据ID获取帖子(支持事务)
|
||||
func (r *PostRepository) GetByIDWithContext(ctx context.Context, id string) (*model.Post, error) {
|
||||
var post model.Post
|
||||
err := r.getDB(ctx).WithContext(ctx).Preload("User").Preload("Images").First(&post, "id = ?", id).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
// UpdateWithContext 更新帖子(支持事务)
|
||||
func (r *PostRepository) UpdateWithContext(ctx context.Context, post *model.Post) error {
|
||||
post.UpdatedAt = time.Now()
|
||||
return r.getDB(ctx).WithContext(ctx).Save(post).Error
|
||||
}
|
||||
|
||||
// DeleteWithContext 删除帖子(支持事务)
|
||||
func (r *PostRepository) DeleteWithContext(ctx context.Context, id string) error {
|
||||
db := r.getDB(ctx)
|
||||
|
||||
// 如果 context 中有事务,直接使用
|
||||
if tx := GetTxFromContext(ctx); tx != nil {
|
||||
return r.deleteWithTx(tx, id)
|
||||
}
|
||||
|
||||
// 否则开启新事务
|
||||
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
return r.deleteWithTx(tx, id)
|
||||
})
|
||||
}
|
||||
|
||||
// deleteWithTx 使用指定事务删除帖子
|
||||
func (r *PostRepository) deleteWithTx(tx *gorm.DB, id string) error {
|
||||
// 删除帖子图片
|
||||
if err := tx.Where("post_id = ?", id).Delete(&model.PostImage{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除帖子点赞记录
|
||||
if err := tx.Where("post_id = ?", id).Delete(&model.PostLike{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除帖子收藏记录
|
||||
if err := tx.Where("post_id = ?", id).Delete(&model.Favorite{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除评论点赞记录(子查询获取该帖子所有评论ID)
|
||||
if err := tx.Where("comment_id IN (SELECT id FROM comments WHERE post_id = ?)", id).Delete(&model.CommentLike{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除帖子评论
|
||||
if err := tx.Where("post_id = ?", id).Delete(&model.Comment{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 最后删除帖子本身(软删除)
|
||||
return tx.Delete(&model.Post{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
43
internal/repository/transaction.go
Normal file
43
internal/repository/transaction.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TransactionManager 事务管理器接口
|
||||
type TransactionManager interface {
|
||||
// RunInTransaction 在事务中执行函数
|
||||
RunInTransaction(ctx context.Context, fn func(ctx context.Context) error) error
|
||||
}
|
||||
|
||||
// gormTransactionManager GORM 事务管理器实现
|
||||
type gormTransactionManager struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewTransactionManager 创建事务管理器
|
||||
func NewTransactionManager(db *gorm.DB) TransactionManager {
|
||||
return &gormTransactionManager{db: db}
|
||||
}
|
||||
|
||||
// RunInTransaction 在事务中执行函数
|
||||
func (m *gormTransactionManager) RunInTransaction(ctx context.Context, fn func(ctx context.Context) error) error {
|
||||
return m.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// 将事务 TX 存入 context
|
||||
ctxWithTx := context.WithValue(ctx, txKey{}, tx)
|
||||
return fn(ctxWithTx)
|
||||
})
|
||||
}
|
||||
|
||||
// txKey 事务 context key
|
||||
type txKey struct{}
|
||||
|
||||
// GetTxFromContext 从 context 获取事务 TX
|
||||
func GetTxFromContext(ctx context.Context) *gorm.DB {
|
||||
if tx, ok := ctx.Value(txKey{}).(*gorm.DB); ok {
|
||||
return tx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user