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

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
+8 -5
View File
@@ -204,24 +204,27 @@ type flowServices struct {
func newFlowServices(db *gorm.DB) flowServices {
listingRepo := listing.NewRepository(db)
orderRepo := order.NewRepository(db)
walletRepo := wallet.NewRepository(db)
configRepo := paymentconfig.NewRepository(db, &paymentconfig.MockEncryptor{})
paymentRepo := payment.NewRepository(db, configRepo, orderRepo, walletRepo)
disputeRepo := dispute.NewRepository(db)
orderRepo.SetRefundFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
var paymentRepo *payment.Repository
orderRepo := order.NewRepository(db, order.Dependencies{
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)
if err != nil {
return "", err
}
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)
disputeRepo := dispute.NewRepository(db, dispute.Dependencies{
RefundStarter: dispute.RefundStarterFunc(func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
refund, err := paymentRepo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark)
if err != nil {
return "", err
}
return refund.Status, nil
}),
})
return flowServices{
@@ -256,7 +256,7 @@ func (r *Repository) prepareRefund(order *model.RentalOrder, amountCent int64, b
if amountCent <= 0 {
return nil, nil
}
if r.refundFunc == nil {
if r.refundStarter == nil {
return nil, ErrDependencyUnavailable
}
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) {
if action == nil || r.refundFunc == nil {
if action == nil || r.refundStarter == nil {
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) {
+19 -9
View File
@@ -9,13 +9,23 @@ import (
type Repository struct {
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 RefundFunc func(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 Dependencies struct {
RefundStarter RefundStarter
}
type refundAction struct {
OrderID uint64
@@ -24,12 +34,12 @@ type refundAction struct {
Remark string
}
func NewRepository(db *gorm.DB) *Repository {
return &Repository{db: db}
func NewRepository(db *gorm.DB, deps ...Dependencies) *Repository {
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 {
@@ -172,14 +172,14 @@ func (r *Repository) AdminRefund(ctx context.Context, orderID uint64) (*RefundSt
if order.RefundStatus == refundStatusRefunded {
return r.buildRefundStatusDTO(&order), nil
}
if r.refundFunc == nil {
if r.refundStarter == nil {
return nil, ErrDependencyUnavailable
}
totalCent := order.RentAmountCent + order.DepositAmountCent
if totalCent <= 0 {
return nil, ErrInvalidCheckoutAmount
}
status, err := r.refundFunc(ctx, orderID, totalCent, refundBizAdmin, "后台人工退款")
status, err := r.refundStarter.StartRefund(ctx, orderID, totalCent, refundBizAdmin, "后台人工退款")
if err != nil {
return nil, err
}
+2 -2
View File
@@ -195,8 +195,8 @@ func (r *Repository) ConfirmPaidFromChannel(ctx context.Context, orderID uint64,
if err != nil {
return err
}
if newConvID > 0 && r.chatRepo != nil {
r.chatRepo.NotifyNewConversation(newConvID)
if newConvID > 0 && r.chatNotifier != nil {
r.chatNotifier.NotifyNewConversation(newConvID)
}
return nil
}
+3 -3
View File
@@ -11,7 +11,7 @@ func (r *Repository) prepareRefund(order *model.RentalOrder, amountCent int64, b
if amountCent <= 0 {
return nil, nil
}
if r.refundFunc == nil {
if r.refundStarter == nil {
return nil, ErrDependencyUnavailable
}
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) {
if action == nil || r.refundFunc == nil {
if action == nil || r.refundStarter == nil {
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)
}
}
+28 -15
View File
@@ -3,13 +3,29 @@ package order
import (
"context"
"hfb_sys/backend/internal/modules/chat"
"gorm.io/gorm"
)
// RefundFunc 由 payment 模块注入,避免 order payment 形成循环依赖
type RefundFunc func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error)
// RefundStarter 由支付模块适配实现,避免 order 直接依赖 payment。
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 {
OrderID uint64
RefundAmountCent int64
@@ -19,20 +35,17 @@ type refundAction struct {
type Repository struct {
db *gorm.DB
chatRepo *chat.Repository
refundFunc RefundFunc
chatNotifier OrderChatNotifier
refundStarter RefundStarter
}
const defaultPendingPaymentTimeoutMinutes = 15
func NewRepository(db *gorm.DB) *Repository {
return &Repository{db: db}
func NewRepository(db *gorm.DB, deps ...Dependencies) *Repository {
repo := &Repository{db: db}
if len(deps) > 0 {
repo.chatNotifier = deps[0].ChatNotifier
repo.refundStarter = deps[0].RefundStarter
}
func (r *Repository) SetChatRepo(cr *chat.Repository) {
r.chatRepo = cr
}
func (r *Repository) SetRefundFunc(fn RefundFunc) {
r.refundFunc = fn
return repo
}
+27 -26
View File
@@ -109,9 +109,30 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
if deps.DB != nil {
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
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)
orderHandler := order.NewHandler(orderService)
@@ -156,56 +177,36 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
paymentConfigHandler = paymentconfig.NewHandler(paymentConfigService)
}
var paymentRepo *payment.Repository
if deps.DB != nil {
paymentRepo = payment.NewRepository(deps.DB, paymentConfigRepo, orderRepo, walletRepo)
}
paymentService := payment.NewService(paymentRepo, cfg.AppEnv)
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
if deps.DB != nil {
notificationRepo = notification.NewRepository(deps.DB)
}
notificationService := notification.NewService(notificationRepo)
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)
chatHandler := chat.NewHandler(chatService)
var chatHubHandler *chathub.Handler
if chatHub != nil {
chatHubHandler = chathub.NewHandler(chatHub)
}
if orderRepo != nil && chatRepo != nil {
orderRepo.SetChatRepo(chatRepo)
}
var disputeRepo *dispute.Repository
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 paymentRepo == nil {
return "", dispute.ErrDependencyUnavailable
}
if disputeRepo != nil && paymentRepo != nil {
disputeRepo.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
}),
})
}
disputeService := dispute.NewService(disputeRepo)