Files
junhong_cmp_fiber/internal/infrastructure/wallet/credit_event.go
break 5e552d99bc 收口审计治理与套餐任务进展
Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展
Confidence: medium
Scope-risk: broad
Directive: 后续修改需保持审计事件与业务事务边界一致
Tested: git diff --cached --check
Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
2026-08-05 14:30:54 +08:00

78 lines
3.6 KiB
Go
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.
package wallet
import (
"context"
"fmt"
"strconv"
notificationapp "github.com/break/junhong_cmp_fiber/internal/application/notification"
walletapp "github.com/break/junhong_cmp_fiber/internal/application/wallet"
"github.com/break/junhong_cmp_fiber/internal/infrastructure/messaging/outbox"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"gorm.io/gorm"
)
// BalanceAdjustmentAuditWriter 在人工调整事务内追加统一 Audit Event。
type BalanceAdjustmentAuditWriter interface {
WriteAgentWalletBalanceAdjustment(context.Context, *gorm.DB, walletapp.CreditedEvent) error
}
// CreditEventWriter 将代理主钱包正向入账事实写入公共 Outbox并审计人工调整。
type CreditEventWriter struct {
outbox *outbox.Repository
audit BalanceAdjustmentAuditWriter
}
// NewCreditEventWriter 创建代理主钱包入账 Outbox Writer。
func NewCreditEventWriter(repository *outbox.Repository, auditWriter BalanceAdjustmentAuditWriter) *CreditEventWriter {
return &CreditEventWriter{outbox: repository, audit: auditWriter}
}
// Append 在调用方业务事务中追加代理主钱包入账事件及必要审计。
func (w *CreditEventWriter) Append(ctx context.Context, tx *gorm.DB, event walletapp.CreditedEvent) error {
if w == nil || w.outbox == nil || w.audit == nil {
return errors.New(errors.CodeInternalError, "代理主钱包入账 Outbox Writer 未配置")
}
_, err := w.outbox.Append(ctx, tx, outbox.Envelope{
EventID: event.EventID, EventType: constants.OutboxEventTypeAgentMainWalletCredited,
PayloadVersion: constants.AgentMainWalletCreditedPayloadVersionV1,
AggregateType: "agent_wallet", AggregateID: strconv.FormatUint(uint64(event.WalletID), 10),
ResourceType: event.ReferenceType, ResourceID: strconv.FormatUint(uint64(event.ReferenceID), 10),
BusinessKey: event.EventID, RequestID: event.RequestID, CorrelationID: event.CorrelationID, Payload: event,
})
if err != nil {
return err
}
if event.ReferenceType == constants.ReferenceTypeManualAdjustment && event.TransactionType == constants.AgentTransactionTypeAdjustment {
return w.audit.WriteAgentWalletBalanceAdjustment(ctx, tx, event)
}
if event.ReferenceType != constants.ReferenceTypeTopup || event.TransactionType != constants.AgentTransactionTypeRecharge {
return nil
}
rechargeID := strconv.FormatUint(uint64(event.ReferenceID), 10)
var shop model.Shop
if err := tx.WithContext(ctx).Select("shop_name").Where("id = ?", event.ShopID).Take(&shop).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值通知店铺失败")
}
notificationEventID := "agent-recharge:" + rechargeID + ":completed"
_, err = w.outbox.AppendIdempotent(ctx, tx, outbox.Envelope{
EventID: notificationEventID, EventType: constants.OutboxEventTypeAdminDynamicNotification,
PayloadVersion: constants.NotificationPayloadVersionV1,
AggregateType: "agent_recharge", AggregateID: rechargeID,
ResourceType: constants.NotificationRefTypeAgentRecharge, ResourceID: rechargeID,
BusinessKey: notificationEventID, RequestID: event.RequestID, CorrelationID: event.CorrelationID,
Payload: notificationapp.AdminDynamicPayload{
TargetKind: constants.NotificationTargetKindShop, TargetID: event.ShopID,
NotificationType: constants.NotificationTypeAgentRechargeCompleted,
TemplateData: map[string]string{
"shop_name": shop.ShopName,
"amount": fmt.Sprintf("%d.%02d 元", event.Amount/100, event.Amount%100),
},
RefType: constants.NotificationRefTypeAgentRecharge, RefID: rechargeID,
},
})
return err
}