解耦订单和申诉模块外部依赖

This commit is contained in:
yml2213
2026-06-10 15:41:31 +08:00
parent ca50b103ec
commit 28c4efc669
8 changed files with 112 additions and 85 deletions
+18 -15
View File
@@ -204,24 +204,27 @@ type flowServices struct {
func newFlowServices(db *gorm.DB) flowServices { func newFlowServices(db *gorm.DB) flowServices {
listingRepo := listing.NewRepository(db) listingRepo := listing.NewRepository(db)
orderRepo := order.NewRepository(db)
walletRepo := wallet.NewRepository(db) walletRepo := wallet.NewRepository(db)
configRepo := paymentconfig.NewRepository(db, &paymentconfig.MockEncryptor{}) configRepo := paymentconfig.NewRepository(db, &paymentconfig.MockEncryptor{})
paymentRepo := payment.NewRepository(db, configRepo, orderRepo, walletRepo) var paymentRepo *payment.Repository
disputeRepo := dispute.NewRepository(db) orderRepo := order.NewRepository(db, order.Dependencies{
orderRepo.SetRefundFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) { RefundStarter: order.RefundStarterFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
refund, err := paymentRepo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark) refund, err := paymentRepo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark)
if err != nil { if err != nil {
return "", err return "", err
} }
return refund.Status, nil return refund.Status, nil
}),
}) })
disputeRepo.SetRefundFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) { paymentRepo = payment.NewRepository(db, configRepo, orderRepo, walletRepo)
refund, err := paymentRepo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark) disputeRepo := dispute.NewRepository(db, dispute.Dependencies{
if err != nil { RefundStarter: dispute.RefundStarterFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
return "", err refund, err := paymentRepo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark)
} if err != nil {
return refund.Status, nil return "", err
}
return refund.Status, nil
}),
}) })
return flowServices{ return flowServices{
@@ -256,7 +256,7 @@ func (r *Repository) prepareRefund(order *model.RentalOrder, amountCent int64, b
if amountCent <= 0 { if amountCent <= 0 {
return nil, nil return nil, nil
} }
if r.refundFunc == nil { if r.refundStarter == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
order.RefundStatus = "pending" order.RefundStatus = "pending"
@@ -271,10 +271,10 @@ func (r *Repository) prepareRefund(order *model.RentalOrder, amountCent int64, b
} }
func (r *Repository) startRefundBestEffort(ctx context.Context, action *refundAction) { func (r *Repository) startRefundBestEffort(ctx context.Context, action *refundAction) {
if action == nil || r.refundFunc == nil { if action == nil || r.refundStarter == nil {
return return
} }
_, _ = r.refundFunc(ctx, action.OrderID, action.RefundAmountCent, action.BizType, action.Remark) _, _ = r.refundStarter.StartRefund(ctx, action.OrderID, action.RefundAmountCent, action.BizType, action.Remark)
} }
func renterFrozenBalance(tx *gorm.DB, renterID uint64) (int64, error) { func renterFrozenBalance(tx *gorm.DB, renterID uint64) (int64, error) {
+21 -11
View File
@@ -8,14 +8,24 @@ import (
) )
type Repository struct { type Repository struct {
db *gorm.DB db *gorm.DB
refundFunc RefundFunc refundStarter RefundStarter
} }
// RefundFunc 由 payment 模块注入,避免 dispute payment 形成循环依赖 // RefundStarter 由支付模块适配实现,避免 dispute 直接依赖 payment。
type RefundStarter interface {
StartRefund(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error)
}
// RefundFunc 由 payment 模块注入,避免 dispute 与 payment 形成循环依赖。 type RefundStarterFunc func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error)
type RefundFunc func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error)
func (fn RefundStarterFunc) StartRefund(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
return fn(ctx, orderID, refundAmountCent, bizType, remark)
}
type Dependencies struct {
RefundStarter RefundStarter
}
type refundAction struct { type refundAction struct {
OrderID uint64 OrderID uint64
@@ -24,12 +34,12 @@ type refundAction struct {
Remark string Remark string
} }
func NewRepository(db *gorm.DB) *Repository { func NewRepository(db *gorm.DB, deps ...Dependencies) *Repository {
return &Repository{db: db} repo := &Repository{db: db}
} if len(deps) > 0 {
repo.refundStarter = deps[0].RefundStarter
func (r *Repository) SetRefundFunc(fn RefundFunc) { }
r.refundFunc = fn return repo
} }
func IsNotFound(err error) bool { func IsNotFound(err error) bool {
@@ -172,14 +172,14 @@ func (r *Repository) AdminRefund(ctx context.Context, orderID uint64) (*RefundSt
if order.RefundStatus == refundStatusRefunded { if order.RefundStatus == refundStatusRefunded {
return r.buildRefundStatusDTO(&order), nil return r.buildRefundStatusDTO(&order), nil
} }
if r.refundFunc == nil { if r.refundStarter == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
totalCent := order.RentAmountCent + order.DepositAmountCent totalCent := order.RentAmountCent + order.DepositAmountCent
if totalCent <= 0 { if totalCent <= 0 {
return nil, ErrInvalidCheckoutAmount return nil, ErrInvalidCheckoutAmount
} }
status, err := r.refundFunc(ctx, orderID, totalCent, refundBizAdmin, "后台人工退款") status, err := r.refundStarter.StartRefund(ctx, orderID, totalCent, refundBizAdmin, "后台人工退款")
if err != nil { if err != nil {
return nil, err return nil, err
} }
+2 -2
View File
@@ -195,8 +195,8 @@ func (r *Repository) ConfirmPaidFromChannel(ctx context.Context, orderID uint64,
if err != nil { if err != nil {
return err return err
} }
if newConvID > 0 && r.chatRepo != nil { if newConvID > 0 && r.chatNotifier != nil {
r.chatRepo.NotifyNewConversation(newConvID) r.chatNotifier.NotifyNewConversation(newConvID)
} }
return nil return nil
} }
+3 -3
View File
@@ -11,7 +11,7 @@ func (r *Repository) prepareRefund(order *model.RentalOrder, amountCent int64, b
if amountCent <= 0 { if amountCent <= 0 {
return nil, nil return nil, nil
} }
if r.refundFunc == nil { if r.refundStarter == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
order.RefundStatus = refundStatusPending order.RefundStatus = refundStatusPending
@@ -26,10 +26,10 @@ func (r *Repository) prepareRefund(order *model.RentalOrder, amountCent int64, b
} }
func (r *Repository) startRefundBestEffort(ctx context.Context, action *refundAction) { func (r *Repository) startRefundBestEffort(ctx context.Context, action *refundAction) {
if action == nil || r.refundFunc == nil { if action == nil || r.refundStarter == nil {
return return
} }
if _, err := r.refundFunc(ctx, action.OrderID, action.RefundAmountCent, action.BizType, action.Remark); err != nil { if _, err := r.refundStarter.StartRefund(ctx, action.OrderID, action.RefundAmountCent, action.BizType, action.Remark); err != nil {
log.Printf("[order] start refund failed order_id=%d biz_type=%s amount_cent=%d err=%v", action.OrderID, action.BizType, action.RefundAmountCent, err) log.Printf("[order] start refund failed order_id=%d biz_type=%s amount_cent=%d err=%v", action.OrderID, action.BizType, action.RefundAmountCent, err)
} }
} }
+30 -17
View File
@@ -3,13 +3,29 @@ package order
import ( import (
"context" "context"
"hfb_sys/backend/internal/modules/chat"
"gorm.io/gorm" "gorm.io/gorm"
) )
// RefundFunc 由 payment 模块注入,避免 order payment 形成循环依赖 // RefundStarter 由支付模块适配实现,避免 order 直接依赖 payment。
type RefundFunc func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error) type RefundStarter interface {
StartRefund(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error)
}
type RefundStarterFunc func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error)
func (fn RefundStarterFunc) StartRefund(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
return fn(ctx, orderID, refundAmountCent, bizType, remark)
}
type OrderChatNotifier interface {
NotifyNewConversation(conversationID uint64)
}
type Dependencies struct {
ChatNotifier OrderChatNotifier
RefundStarter RefundStarter
}
type refundAction struct { type refundAction struct {
OrderID uint64 OrderID uint64
RefundAmountCent int64 RefundAmountCent int64
@@ -18,21 +34,18 @@ type refundAction struct {
} }
type Repository struct { type Repository struct {
db *gorm.DB db *gorm.DB
chatRepo *chat.Repository chatNotifier OrderChatNotifier
refundFunc RefundFunc refundStarter RefundStarter
} }
const defaultPendingPaymentTimeoutMinutes = 15 const defaultPendingPaymentTimeoutMinutes = 15
func NewRepository(db *gorm.DB) *Repository { func NewRepository(db *gorm.DB, deps ...Dependencies) *Repository {
return &Repository{db: db} repo := &Repository{db: db}
} if len(deps) > 0 {
repo.chatNotifier = deps[0].ChatNotifier
func (r *Repository) SetChatRepo(cr *chat.Repository) { repo.refundStarter = deps[0].RefundStarter
r.chatRepo = cr }
} return repo
func (r *Repository) SetRefundFunc(fn RefundFunc) {
r.refundFunc = fn
} }
+33 -32
View File
@@ -109,9 +109,30 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
if deps.DB != nil { if deps.DB != nil {
listingRepo = listing.NewRepository(deps.DB) listingRepo = listing.NewRepository(deps.DB)
} }
var chatHub *chathub.Hub
if deps.DB != nil {
chatHub = chathub.NewHub(deps.DB)
}
var chatRepo *chat.Repository
if deps.DB != nil {
chatRepo = chat.NewRepository(deps.DB, chatHub)
}
var paymentRepo *payment.Repository
var orderRepo *order.Repository var orderRepo *order.Repository
if deps.DB != nil { if deps.DB != nil {
orderRepo = order.NewRepository(deps.DB) orderRepo = order.NewRepository(deps.DB, order.Dependencies{
ChatNotifier: chatRepo,
RefundStarter: order.RefundStarterFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
if paymentRepo == nil {
return "", order.ErrDependencyUnavailable
}
dto, err := paymentRepo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark)
if err != nil {
return "", err
}
return dto.Status, nil
}),
})
} }
orderService := order.NewService(orderRepo) orderService := order.NewService(orderRepo)
orderHandler := order.NewHandler(orderService) orderHandler := order.NewHandler(orderService)
@@ -156,56 +177,36 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
paymentConfigHandler = paymentconfig.NewHandler(paymentConfigService) paymentConfigHandler = paymentconfig.NewHandler(paymentConfigService)
} }
var paymentRepo *payment.Repository
if deps.DB != nil { if deps.DB != nil {
paymentRepo = payment.NewRepository(deps.DB, paymentConfigRepo, orderRepo, walletRepo) paymentRepo = payment.NewRepository(deps.DB, paymentConfigRepo, orderRepo, walletRepo)
} }
paymentService := payment.NewService(paymentRepo, cfg.AppEnv) paymentService := payment.NewService(paymentRepo, cfg.AppEnv)
paymentHandler := payment.NewHandler(paymentService) paymentHandler := payment.NewHandler(paymentService)
// Inject refund function into order repo to avoid circular dependency
if orderRepo != nil && paymentRepo != nil {
orderRepo.SetRefundFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
dto, err := paymentRepo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark)
if err != nil {
return "", err
}
return dto.Status, nil
})
}
var notificationRepo *notification.Repository var notificationRepo *notification.Repository
if deps.DB != nil { if deps.DB != nil {
notificationRepo = notification.NewRepository(deps.DB) notificationRepo = notification.NewRepository(deps.DB)
} }
notificationService := notification.NewService(notificationRepo) notificationService := notification.NewService(notificationRepo)
notificationHandler := notification.NewHandler(notificationService) notificationHandler := notification.NewHandler(notificationService)
var chatHub *chathub.Hub
if deps.DB != nil {
chatHub = chathub.NewHub(deps.DB)
}
var chatRepo *chat.Repository
if deps.DB != nil {
chatRepo = chat.NewRepository(deps.DB, chatHub)
}
chatService := chat.NewService(chatRepo) chatService := chat.NewService(chatRepo)
chatHandler := chat.NewHandler(chatService) chatHandler := chat.NewHandler(chatService)
var chatHubHandler *chathub.Handler var chatHubHandler *chathub.Handler
if chatHub != nil { if chatHub != nil {
chatHubHandler = chathub.NewHandler(chatHub) chatHubHandler = chathub.NewHandler(chatHub)
} }
if orderRepo != nil && chatRepo != nil {
orderRepo.SetChatRepo(chatRepo)
}
var disputeRepo *dispute.Repository var disputeRepo *dispute.Repository
if deps.DB != nil { if deps.DB != nil {
disputeRepo = dispute.NewRepository(deps.DB) disputeRepo = dispute.NewRepository(deps.DB, dispute.Dependencies{
} RefundStarter: dispute.RefundStarterFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
if disputeRepo != nil && paymentRepo != nil { if paymentRepo == nil {
disputeRepo.SetRefundFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) { return "", dispute.ErrDependencyUnavailable
dto, err := paymentRepo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark) }
if err != nil { dto, err := paymentRepo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark)
return "", err if err != nil {
} return "", err
return dto.Status, nil }
return dto.Status, nil
}),
}) })
} }
disputeService := dispute.NewService(disputeRepo) disputeService := dispute.NewService(disputeRepo)