using MAX.Framework.Security; using Store.Users.Services; using MAX.Framework.Commons; using MAX.Framework.Mediation; using MediatR; using Store.Users.Entities; namespace Store.Users.Behaviors; /// 从登录上下文确认操作者,校验目标账户归属后锁定积分账户并检查消费额度。 public sealed class AccountConsumptionBehavior( UnitOfWorkExecutionContext executionContext, ICurrentUser currentUser) : IPipelineBehavior where TRequest : class, IRequest, IAccountConsumptionRequest { public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) { cancellationToken.ThrowIfCancellationRequested(); if (!currentUser.IsAuthenticated || string.IsNullOrWhiteSpace(currentUser.UserName)) throw new UnauthorizedAccessException(); var orm = executionContext.Orm; var actor = await orm.Select().Where(u => u.UserName == currentUser.UserName) .FirstAsync(cancellationToken) ?? throw new UnauthorizedAccessException("登录账户不存在"); var userName = string.IsNullOrWhiteSpace(request.UserName) ? actor.UserName : request.UserName.Trim(); var user = await orm.Select().Where(u => u.UserName == userName) .ForUpdate().FirstAsync(cancellationToken) ?? throw new AppException("用户不存在"); if (!UserAccessPolicy.CanUseSelfService(actor, user)) throw new AppException("无权为该账户续费", 403); 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, actor, quote); } try { return await next(); } finally { request.ConsumptionContext = null; } } }