收口审计治理与套餐任务进展

Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展
Confidence: medium
Scope-risk: broad
Directive: 后续修改需保持审计事件与业务事务边界一致
Tested: git diff --cached --check
Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
This commit is contained in:
2026-08-05 14:30:54 +08:00
parent b3499adfca
commit 5e552d99bc
178 changed files with 16797 additions and 5674 deletions

View File

@@ -20,16 +20,17 @@ import (
type AgentRechargePaymentConsumer struct {
db *gorm.DB
posting *walletapp.PostingService
audit agentrecharge.RechargeAuditWriter
}
// NewAgentRechargePaymentConsumer 创建代理在线充值入账消费者。
func NewAgentRechargePaymentConsumer(db *gorm.DB, posting *walletapp.PostingService) *AgentRechargePaymentConsumer {
return &AgentRechargePaymentConsumer{db: db, posting: posting}
func NewAgentRechargePaymentConsumer(db *gorm.DB, posting *walletapp.PostingService, audit agentrecharge.RechargeAuditWriter) *AgentRechargePaymentConsumer {
return &AgentRechargePaymentConsumer{db: db, posting: posting, audit: audit}
}
// Consume 校验支付与充值权威事实后,在独立事务中完成唯一入账和充值终态。
func (c *AgentRechargePaymentConsumer) Consume(ctx context.Context, envelope outbox.DeliveryEnvelope) error {
if c == nil || c.db == nil || c.posting == nil {
if c == nil || c.db == nil || c.posting == nil || c.audit == nil {
return errors.New(errors.CodeInternalError, "代理在线充值入账消费者未配置")
}
if envelope.EventType != constants.OutboxEventTypeAgentRechargePaymentConfirmed ||
@@ -45,23 +46,24 @@ func (c *AgentRechargePaymentConsumer) Consume(ctx context.Context, envelope out
return errors.New(errors.CodeInvalidParam, "代理充值支付确认事件载荷不完整")
}
return c.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
recharge, _, err := lockCreditingFacts(ctx, tx, event)
recharge, payment, err := lockCreditingFacts(ctx, tx, event)
if err != nil {
return err
}
if recharge.Status != constants.RechargeStatusPaid && recharge.Status != constants.RechargeStatusCompleted {
return errors.New(errors.CodeInvalidStatus, "代理在线充值当前状态不可入账")
}
if _, err := c.posting.PostInTx(ctx, tx, walletapp.PostingCommand{
posting, err := c.posting.PostInTx(ctx, tx, walletapp.PostingCommand{
ShopID: recharge.ShopID, WalletID: recharge.AgentWalletID, Amount: recharge.Amount,
ReferenceType: constants.ReferenceTypeTopup, ReferenceID: recharge.ID,
TransactionType: constants.AgentTransactionTypeRecharge,
UserID: recharge.UserID, Creator: recharge.UserID, Remark: "代理在线扫码充值",
RequestID: envelope.RequestID, CorrelationID: envelope.CorrelationID,
}); err != nil {
})
if err != nil {
return err
}
if recharge.Status == constants.RechargeStatusCompleted {
if posting.AlreadyApplied && recharge.Status == constants.RechargeStatusCompleted {
return nil
}
completedAt := time.Now().UTC()
@@ -74,7 +76,25 @@ func (c *AgentRechargePaymentConsumer) Consume(ctx context.Context, envelope out
if update.RowsAffected != 1 {
return errors.New(errors.CodeConflict, "代理在线充值状态已变化")
}
return nil
var wallet model.AgentWallet
if err := tx.WithContext(ctx).First(&wallet, recharge.AgentWalletID).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询代理充值钱包审计快照失败")
}
var transaction model.AgentWalletTransaction
if err := tx.WithContext(ctx).Where("reference_type = ? AND reference_id = ? AND transaction_type = ? AND status = ?",
constants.ReferenceTypeTopup, recharge.ID, constants.AgentTransactionTypeRecharge, constants.TransactionStatusSuccess).
First(&transaction).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询代理充值入账流水审计快照失败")
}
after := *recharge
after.Status = constants.RechargeStatusCompleted
after.CompletedAt = &completedAt
return c.audit.WriteAgentRecharge(ctx, tx, agentrecharge.RechargeAudit{
ActionCode: constants.AuditActionAgentRechargeCredited, Summary: "代理在线充值已入账",
Record: &after, Payment: payment, Wallet: &wallet, Transaction: &transaction,
BeforeData: map[string]any{"status": recharge.Status},
AfterData: map[string]any{"status": after.Status, "completed_at": completedAt},
})
})
}