-
Notifications
You must be signed in to change notification settings - Fork 8
/
ClaimRewardCommand.cs
120 lines (101 loc) · 4.32 KB
/
ClaimRewardCommand.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using AutoMapper;
using MediatR;
using Microsoft.EntityFrameworkCore;
using SSW.Rewards.Application.Common.Interfaces;
using SSW.Rewards.Application.Reward.Queries.Common;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace SSW.Rewards.Application.Reward.Commands
{
public class ClaimRewardCommand : IRequest<ClaimRewardResult>
{
public string Code { get; set; }
public bool ClaimInPerson { get; set; } = true;
public class ClaimRewardCommandHandler : IRequestHandler<ClaimRewardCommand, ClaimRewardResult>
{
private readonly ICurrentUserService _currentUserService;
private readonly ISSWRewardsDbContext _context;
private readonly IMapper _mapper;
private readonly IDateTimeProvider _dateTimeProvider;
private readonly IRewardSender _rewardSender;
public ClaimRewardCommandHandler(
ICurrentUserService currentUserService,
ISSWRewardsDbContext context,
IMapper mapper,
IDateTimeProvider dateTimeProvider,
IRewardSender rewardSender)
{
_currentUserService = currentUserService;
_context = context;
_mapper = mapper;
_dateTimeProvider = dateTimeProvider;
_rewardSender = rewardSender;
}
public async Task<ClaimRewardResult> Handle(ClaimRewardCommand request, CancellationToken cancellationToken)
{
var reward = await _context
.Rewards
.Where(r => r.Code == request.Code)
.FirstOrDefaultAsync(cancellationToken);
if(reward == null)
{
return new ClaimRewardResult
{
status = RewardStatus.NotFound
};
}
var user = await _currentUserService.GetCurrentUserAsync(cancellationToken);
var userRewards = await _context
.UserRewards
.Where(ur => ur.UserId == user.Id)
.ToListAsync(cancellationToken);
int pointsUsed = userRewards.Sum(ur => ur.Reward.Cost);
int balance = user.Points - pointsUsed;
if(balance < reward.Cost)
{
return new ClaimRewardResult
{
status = RewardStatus.NotEnoughPoints
};
}
// TECH DEBT: the following logic is intended to 'debounce' reward
// claiming, to prevent users claiming the same reward twice
// within a 5 minute window. This workaround is only required on
// the current 'milestone' model for points. Once we move to the
// 'currency' model, this will not be required anymore.
// see: https://github.com/SSWConsulting/SSW.Rewards/issues/100
var userHasReward = userRewards
.Where(ur => ur.RewardId == reward.Id)
.FirstOrDefault();
if(userHasReward != null && userHasReward.AwardedAt >= _dateTimeProvider.Now.AddMinutes(-5))
{
return new ClaimRewardResult
{
status = RewardStatus.Duplicate
};
}
await _context
.UserRewards
.AddAsync(new Domain.Entities.UserReward
{
UserId = user.Id,
RewardId = reward.Id
}, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
var dbUser = await _context.Users
.Where(u => u.Id == user.Id)
.FirstAsync(cancellationToken);
await _rewardSender.SendRewardAsync(dbUser, reward, cancellationToken);
var rewardModel = _mapper.Map<RewardViewModel>(reward);
return new ClaimRewardResult
{
viewModel = rewardModel,
status = RewardStatus.Claimed
};
}
}
}
}