package refundchannel import ( "context" "strconv" "github.com/bytedance/sonic" "gorm.io/gorm" "github.com/break/junhong_cmp_fiber/internal/infrastructure/messaging/outbox" "github.com/break/junhong_cmp_fiber/pkg/auditcontext" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/outboxid" ) // EventRefundChannelRefund 是退款进入渠道原路处理中后的执行事件。 const EventRefundChannelRefund = "refund.channel.refund.requested" // refundChannelPayloadVersion 是渠道原路退款事件的载荷版本。 const refundChannelPayloadVersion = 1 // Payload 是渠道原路退款事件的载荷。 type Payload struct { RefundID uint `json:"refund_id"` OrderID uint `json:"order_id"` } // AppendRefundChannelRefund 在企微通过事务内幂等写入渠道原路退款执行事件。 // 同一退款申请使用稳定事件 ID,重复投递不会重复创建事实。 func AppendRefundChannelRefund(ctx context.Context, tx *gorm.DB, repository *outbox.Repository, refundID, orderID uint) error { if repository == nil { return gorm.ErrInvalidDB } value := strconv.FormatUint(uint64(refundID), 10) _, err := repository.AppendIdempotent(ctx, tx, outbox.Envelope{ EventID: outboxid.Stable(EventRefundChannelRefund+":", value), EventType: EventRefundChannelRefund, PayloadVersion: refundChannelPayloadVersion, AggregateType: "refund", AggregateID: value, ResourceType: "refund", ResourceID: value, BusinessKey: EventRefundChannelRefund + ":" + value, Payload: Payload{RefundID: refundID, OrderID: orderID}, }) return err } // Consumer 把渠道原路退款事件转成一次性资金动作。 type Consumer struct { service *Service } // NewConsumer 创建渠道原路退款事件消费者。 func NewConsumer(service *Service) *Consumer { return &Consumer{service: service} } // Consume 幂等执行渠道原路退款;重复投递由退款申请状态与渠道请求号共同兜住。 func (c *Consumer) Consume(ctx context.Context, envelope outbox.DeliveryEnvelope) error { var payload Payload if err := sonic.Unmarshal(envelope.Payload, &payload); err != nil { return outbox.Permanent(err) } if envelope.EventType != EventRefundChannelRefund || envelope.PayloadVersion != refundChannelPayloadVersion || payload.RefundID == 0 { return outbox.Permanent(gorm.ErrInvalidData) } if c == nil || c.service == nil { return errors.New(errors.CodeServiceUnavailable, "渠道原路退款执行能力未配置") } ctx = auditcontext.With(ctx, auditcontext.Context{CorrelationID: envelope.CorrelationID, ParentEventID: envelope.EventID}) return c.service.Execute(ctx, payload.RefundID) } // 编译期断言:渠道原路退款消费者满足公共 Outbox 的消费边界。 var _ outbox.EventConsumer = (*Consumer)(nil)