public async Task HandleAsync(RefundPointsCommand command, CancellationToken cancellationToken)
        {
            await commandValidator.ValidateAndThrowAsync(command, cancellationToken : cancellationToken).ConfigureAwait(false);

            try
            {
                using (var context = dbContextFactory.CreateDbContext())
                {
                    var consumptionEvent = await context.PointsConsumedEvents.FirstAsync(e => e.TransactionId == command.TransactionId).ConfigureAwait(false);

                    var points        = consumptionEvent.PointChange;
                    var transactionId = command.TransactionId;

                    context.PointsRefundedEvents.Add(new PointsRefundedEvent
                    {
                        PointChange         = points,
                        Reason              = $"Refunded {points} points consumed by transaction {transactionId}.",
                        UtcDateTimeRecorded = DateTime.UtcNow,
                        UserId              = consumptionEvent.UserId,
                        TransactionId       = command.TransactionId
                    });

                    await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
                }
            }
            catch (DbUpdateException ex) when(ex.IsUniqueConstraintViolationError())
            {
                // This means this consumption has already been refunded, can be ignored.
                return;
            }
        }
Exemple #2
0
        public async Task HandleAsync(EarnPointsCommand command, CancellationToken cancellationToken)
        {
            commandValidator.ValidateAndThrow(command);

            try
            {
                using (var context = dbContextFactory.CreateDbContext())
                {
                    context.PointsEarnedEvents.Add(new PointsEarnedEvent
                    {
                        PointChange         = command.Points,
                        Reason              = EarnPointsReason,
                        UtcDateTimeRecorded = DateTime.UtcNow,
                        UserId              = command.UserId,
                        TransactionId       = command.TransactionId
                    });

                    await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
                }
            }
            catch (DbUpdateException ex) when(ex.IsUniqueConstraintViolationError())
            {
                // This means this earning has already been registered, can be ignored.
                return;
            }
        }
Exemple #3
0
        public async Task HandleAsync(ConsumePointsCommand command, CancellationToken cancellationToken)
        {
            await commandValidator.ValidateAndThrowAsync(command, cancellationToken : cancellationToken).ConfigureAwait(false);

            try
            {
                using (var context = dbContextFactory.CreateDbContext())
                {
                    context.PointsConsumedEvents.Add(new PointsConsumedEvent
                    {
                        PointChange         = command.Points,
                        UtcDateTimeRecorded = DateTime.UtcNow,
                        UserId        = command.UserId,
                        TransactionId = command.TransactionId,
                        Reason        = ConsumePointsReason,
                    });

                    // Assume optimistic concurrency so that points aren't changed between retrieving the sum and adding the consume record.
                    await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
                }
            }
            catch (DbUpdateException ex) when(ex.IsUniqueConstraintViolationError())
            {
                // This means this consumption has already been registered, can be ignored.
                return;
            }
        }
        public async Task <int> CalculateTotalAsync(int userId, CancellationToken cancellationToken)
        {
            using (var context = dbContextFactory.CreateDbContext())
            {
                var totalEarned = await context.PointsEarnedEvents.Where(evt => evt.UserId == userId).SumAsync(e => e.PointChange).ConfigureAwait(false);

                var totalRefunded = await context.PointsRefundedEvents.Where(evt => evt.UserId == userId).SumAsync(e => e.PointChange).ConfigureAwait(false);

                var totalConsumed = await context.PointsConsumedEvents.Where(evt => evt.UserId == userId).SumAsync(e => e.PointChange).ConfigureAwait(false);

                var total = totalEarned + totalRefunded - totalConsumed;

                return(total);
            }
        }
        public RefundPointsCommandValidator(ILoyaltyDbContextFactory dbContextFactory)
        {
            RuleFor(cmd => cmd.TransactionId)
            .NotEmpty()
            .WithMessage(CommonValidationMessages.CannotBeNullOrEmpty)
            .DependentRules(() =>
            {
                RuleFor(cmd => cmd.TransactionId)
                .MustAsync(async(string transactionId, CancellationToken cancellationToken) =>
                {
                    using (var context = dbContextFactory.CreateDbContext())
                    {
                        var consumptionEvent = await context
                                               .PointsConsumedEvents
                                               .FirstOrDefaultAsync(e => e.TransactionId == transactionId).ConfigureAwait(false);

                        return(consumptionEvent != null);
                    }
                })
                .WithMessage(ValidationMessages.PointsConsumptionNotFound);
            });
        }