增加Mediat Pip特性,并用实例来做一个demo
This commit is contained in:
1 parent
292cb8638c
commit
c9ac7412ab
19 files changed
+682
-108
No files matched your search
@@ -0,0 +1,44 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Store.Users.Entities;
|
||||
using YourNamespace;
|
||||
|
||||
namespace Store.Users.Behaviors;
|
||||
|
||||
/// <summary>需要账户积分消费检查的请求。</summary>
|
||||
public interface IAccountConsumptionRequest
|
||||
{
|
||||
string UserName { get; }
|
||||
AccountConsumptionContext? ConsumptionContext { get; set; }
|
||||
ConsumptionQuote CalculateConsumption(UserEntity user);
|
||||
}
|
||||
|
||||
/// <summary>需要检查商铺归属的请求,与是否消费无关。</summary>
|
||||
public interface IShopOwnershipRequest
|
||||
{
|
||||
string UserName { get; }
|
||||
string ShopId { get; }
|
||||
ShopEntity? OwnedShop { get; set; }
|
||||
}
|
||||
|
||||
public sealed record ConsumptionQuote(decimal Amount, decimal UnitPrice);
|
||||
public sealed record AccountConsumptionContext(UserEntity User, ConsumptionQuote Quote);
|
||||
@@ -0,0 +1,43 @@
|
||||
using MAX.Framework.Commons;
|
||||
using MAX.Framework.Mediation;
|
||||
using MediatR;
|
||||
using YourNamespace;
|
||||
|
||||
namespace Store.Users.Behaviors;
|
||||
|
||||
/// <summary>独立检查商铺归属;事务中执行时锁定商铺,读取场景可不启用事务。</summary>
|
||||
public sealed class ShopOwnershipBehavior<TRequest, TResponse>(
|
||||
IFreeSql freeSql, UnitOfWorkExecutionContext executionContext) : IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : class, IRequest<TResponse>, IShopOwnershipRequest
|
||||
{
|
||||
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
|
||||
RequestHandlerDelegate<TResponse> next)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (string.IsNullOrWhiteSpace(request.UserName) || string.IsNullOrWhiteSpace(request.ShopId))
|
||||
throw new AppException("用户名和商铺ID不能为空");
|
||||
var userName = request.UserName.Trim();
|
||||
var shopId = request.ShopId.Trim();
|
||||
var orm = executionContext.IsActive ? executionContext.Orm : freeSql;
|
||||
var query = orm.Select<ShopEntity>().Where(a => a.ShopId == shopId && a.UserName == userName);
|
||||
if (executionContext.IsActive) query = query.ForUpdate();
|
||||
var shop = await query.FirstAsync(cancellationToken)
|
||||
?? throw new AppException("不存在的商铺或该商铺不属于当前用户");
|
||||
if (!string.Equals(shop.UserName, userName, StringComparison.OrdinalIgnoreCase))
|
||||
throw new AppException("该商铺不属于当前用户");
|
||||
lock (request)
|
||||
{
|
||||
if (request.OwnedShop != null)
|
||||
throw new InvalidOperationException("同一个商铺操作命令不能同时执行");
|
||||
request.OwnedShop = shop;
|
||||
}
|
||||
try
|
||||
{
|
||||
return await next();
|
||||
}
|
||||
finally
|
||||
{
|
||||
request.OwnedShop = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user