45 lines
1.7 KiB
C#
45 lines
1.7 KiB
C#
using MAX.Framework.Commons;
|
|
using MAX.Framework.Mediation;
|
|
using MediatR;
|
|
using Store.Users.Entities;
|
|
|
|
namespace Store.Users.Behaviors;
|
|
|
|
/// <summary>锁定积分账户并检查消费额度,不依赖商铺信息。</summary>
|
|
public sealed class AccountConsumptionBehavior<TRequest, TResponse>(
|
|
UnitOfWorkExecutionContext executionContext)
|
|
: IPipelineBehavior<TRequest, TResponse>
|
|
where TRequest : class, IRequest<TResponse>, IAccountConsumptionRequest
|
|
{
|
|
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
|
|
RequestHandlerDelegate<TResponse> next)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
if (string.IsNullOrWhiteSpace(request.UserName))
|
|
throw new AppException("用户名不能为空");
|
|
var userName = request.UserName.Trim();
|
|
var user = await executionContext.Orm.Select<UserEntity>().Where(a => a.UserName == userName)
|
|
.ForUpdate().FirstAsync(cancellationToken)
|
|
?? throw new AppException("不存在的用户");
|
|
var quote = request.CalculateConsumption(user);
|
|
if (quote.Amount <= 0 || quote.UnitPrice <= 0)
|
|
throw new AppException("消费金额必须大于0");
|
|
if (user.Points < quote.Amount)
|
|
throw new AppException("余额不足");
|
|
lock (request)
|
|
{
|
|
if (request.ConsumptionContext != null)
|
|
throw new InvalidOperationException("同一个消费命令不能同时执行");
|
|
request.ConsumptionContext = new AccountConsumptionContext(user, quote);
|
|
}
|
|
try
|
|
{
|
|
return await next();
|
|
}
|
|
finally
|
|
{
|
|
request.ConsumptionContext = null;
|
|
}
|
|
}
|
|
}
|