收口审计治理与套餐任务进展
Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展 Confidence: medium Scope-risk: broad Directive: 后续修改需保持审计事件与业务事务边界一致 Tested: git diff --cached --check Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
This commit is contained in:
@@ -2,6 +2,7 @@ package refund
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
|
||||
approvalapp "github.com/break/junhong_cmp_fiber/internal/application/approval"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/auditcontext"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
)
|
||||
@@ -32,8 +34,11 @@ func (s *Service) Handle(ctx context.Context, event approvalapp.TerminalDecision
|
||||
}
|
||||
|
||||
func (s *Service) applyApprovedDecision(ctx context.Context, event approvalapp.TerminalDecisionEvent) error {
|
||||
ctx = auditcontext.With(ctx, auditcontext.Context{CorrelationID: event.CorrelationID, ParentEventID: event.EventID})
|
||||
var refund model.RefundRequest
|
||||
var order model.Order
|
||||
changed := false
|
||||
err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var refund model.RefundRequest
|
||||
if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", event.BusinessID).First(&refund).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
@@ -46,15 +51,17 @@ func (s *Service) applyApprovedDecision(ctx context.Context, event approvalapp.T
|
||||
if refund.Status != model.RefundStatusPending && refund.Status != model.RefundStatusApproved {
|
||||
return errors.New(errors.CodeInvalidStatus, "退款申请状态不允许审批通过")
|
||||
}
|
||||
var order model.Order
|
||||
if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", refund.OrderID).First(&order).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "锁定退款关联订单失败")
|
||||
}
|
||||
beforeRefund := refundAuditState(&refund)
|
||||
beforeOrder := map[string]any{"payment_status": order.PaymentStatus}
|
||||
approvedAmount := refund.RequestedRefundAmount
|
||||
if err := validateApprovedRefundAmount(approvedAmount, refund.RequestedRefundAmount, &order); err != nil {
|
||||
return err
|
||||
}
|
||||
if refund.Status == model.RefundStatusPending {
|
||||
changed = true
|
||||
result := tx.WithContext(ctx).Model(&model.RefundRequest{}).
|
||||
Where("id = ? AND status = ?", refund.ID, model.RefundStatusPending).
|
||||
Updates(map[string]any{
|
||||
@@ -71,6 +78,7 @@ func (s *Service) applyApprovedDecision(ctx context.Context, event approvalapp.T
|
||||
}
|
||||
switch order.PaymentStatus {
|
||||
case model.PaymentStatusPaid:
|
||||
changed = true
|
||||
result := tx.WithContext(ctx).Model(&model.Order{}).
|
||||
Where("id = ? AND payment_status = ?", order.ID, model.PaymentStatusPaid).
|
||||
Updates(map[string]any{"payment_status": model.PaymentStatusRefunded, "updated_at": event.OccurredAt})
|
||||
@@ -88,22 +96,32 @@ func (s *Service) applyApprovedDecision(ctx context.Context, event approvalapp.T
|
||||
if err := s.refundWalletPayment(ctx, tx, &refund, &order, approvedAmount, event.SubmitterAccountID); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.appendCompletedNotification(ctx, tx, &refund)
|
||||
if err := s.appendCompletedNotification(ctx, tx, &refund); err != nil {
|
||||
return err
|
||||
}
|
||||
if !changed {
|
||||
return nil
|
||||
}
|
||||
return s.appendRefundAudit(ctx, tx, refund.ID, constants.AuditActionRefundApproved, "通过退款审批",
|
||||
"refund:"+strconv.FormatUint(uint64(refund.ID), 10)+":approved", beforeRefund, beforeOrder, "退款已通过")
|
||||
})
|
||||
if err != nil {
|
||||
s.recordRefundFailure(ctx, constants.AuditActionRefundApproved, "通过退款审批失败", &refund, &order, err)
|
||||
return err
|
||||
}
|
||||
return s.ensureApprovedPostProcessing(ctx, event.BusinessID)
|
||||
}
|
||||
|
||||
func (s *Service) applyClosedDecision(ctx context.Context, event approvalapp.TerminalDecisionEvent) error {
|
||||
ctx = auditcontext.With(ctx, auditcontext.Context{CorrelationID: event.CorrelationID, ParentEventID: event.EventID})
|
||||
reason := map[string]string{
|
||||
constants.ApprovalDecisionRejected: "企业微信审批已拒绝",
|
||||
constants.ApprovalDecisionCancelled: "企业微信审批已撤销",
|
||||
constants.ApprovalDecisionDeleted: "企业微信审批已删除",
|
||||
}[event.Decision]
|
||||
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var refund model.RefundRequest
|
||||
var refund model.RefundRequest
|
||||
var order model.Order
|
||||
err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", event.BusinessID).First(&refund).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "锁定退款申请失败")
|
||||
}
|
||||
@@ -116,6 +134,10 @@ func (s *Service) applyClosedDecision(ctx context.Context, event approvalapp.Ter
|
||||
if refund.Status != model.RefundStatusPending {
|
||||
return errors.New(errors.CodeInvalidStatus, "退款申请状态不允许结束审批")
|
||||
}
|
||||
if err := tx.WithContext(ctx).First(&order, refund.OrderID).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询退款关联订单失败")
|
||||
}
|
||||
beforeRefund := refundAuditState(&refund)
|
||||
result := tx.WithContext(ctx).Model(&model.RefundRequest{}).
|
||||
Where("id = ? AND status = ?", refund.ID, model.RefundStatusPending).
|
||||
Updates(map[string]any{
|
||||
@@ -128,8 +150,13 @@ func (s *Service) applyClosedDecision(ctx context.Context, event approvalapp.Ter
|
||||
if result.RowsAffected != 1 {
|
||||
return errors.New(errors.CodeConflict, "退款申请状态已变化")
|
||||
}
|
||||
return nil
|
||||
return s.appendRefundAudit(ctx, tx, refund.ID, constants.AuditActionRefundRejected, "拒绝退款审批",
|
||||
"refund:"+strconv.FormatUint(uint64(refund.ID), 10)+":rejected", beforeRefund, nil, "退款已拒绝")
|
||||
})
|
||||
if err != nil {
|
||||
s.recordRefundFailure(ctx, constants.AuditActionRefundRejected, "拒绝退款审批失败", &refund, &order, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Service) ensureApprovedPostProcessing(ctx context.Context, refundID uint) error {
|
||||
|
||||
Reference in New Issue
Block a user