package audit import ( "context" stderrors "errors" "strconv" "go.uber.org/zap" "gorm.io/gorm" "github.com/break/junhong_cmp_fiber/pkg/auditcontext" "github.com/break/junhong_cmp_fiber/pkg/auditfailure" "github.com/break/junhong_cmp_fiber/pkg/constants" pkgerrors "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/logger" ) // RecordFailure 在业务回滚后使用独立短事务记录失败或拒绝事实。 func (w *Writer) RecordFailure(ctx context.Context, db *gorm.DB, input AppendInput, originalErr error) { fillFailureInput(&input, originalErr) if w == nil || db == nil { recordFailureWriteError(ctx, input, pkgerrors.New(pkgerrors.CodeInvalidStatus, "统一审计失败记录接缝未配置")) return } if err := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { return w.Append(ctx, tx, input) }); err != nil { recordFailureWriteError(ctx, input, err) } } func fillFailureInput(input *AppendInput, originalErr error) { var appErr *pkgerrors.AppError if stderrors.As(originalErr, &appErr) && appErr != nil { if input.Result == "" { input.Result = constants.AuditResultDenied if appErr.Code == pkgerrors.CodeDatabaseError || appErr.Code == pkgerrors.CodeInternalError { input.Result = constants.AuditResultFailed } } input.ErrorCode = strconv.Itoa(appErr.Code) input.ErrorSummary = appErr.Message return } if input.Result == "" { input.Result = constants.AuditResultFailed } input.ErrorCode = strconv.Itoa(pkgerrors.CodeInternalError) input.ErrorSummary = "业务操作失败" } func recordFailureWriteError(ctx context.Context, input AppendInput, err error) { recordSecondaryWriteFailure(ctx, input.ActionCode, primaryResourceKey(input), input.ErrorCode, err) } func recordBusinessAppendFailure(ctx context.Context, input AppendInput, err error) { recordBusinessWriteFailure(ctx, input.ActionCode, primaryResourceKey(input), err) } func recordBusinessWriteFailure(ctx context.Context, actionCode, resourceKey string, err error) { linkage := auditcontext.From(ctx) logger.GetAppLogger().Error( "业务审计写入失败,已降级", zap.String("action", actionCode), zap.String("resource_key", resourceKey), zap.String("request_id", linkage.RequestID), zap.String("correlation_id", linkage.CorrelationID), zap.Error(err), ) recordSecondaryWriteFailure(ctx, actionCode, resourceKey, "", err) } func recordSecondaryWriteFailure(ctx context.Context, actionCode, resourceKey, originalErrorCode string, err error) { linkage := auditcontext.From(ctx) auditfailure.RecordSecondaryWriteFailure( actionCode, resourceKey, linkage.RequestID, linkage.CorrelationID, originalErrorCode, err, ) } func primaryResourceKey(input AppendInput) string { for _, resource := range input.Resources { if resource.Relation == constants.AuditResourceRelationPrimary { return resource.Key } } return "" }