| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Domain.Entities.Store.ValueObject;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel.Results;
- namespace Application.Features.Api.Store.Inventory.Use;
- internal sealed class Handler(IAppDbContext db) : ICommandHandler<Command, Result<Response>>
- {
- public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
- {
- var inv = await db.MemberInventory
- .Include(i => i.CouponCode).ThenInclude(c => c!.Coupon)
- .FirstOrDefaultAsync(i => i.ID == request.InventoryID, ct);
- if (inv is null)
- {
- return Result.Failure<Response>(Error.NotFound("Inventory.NotFound", "보관함 항목을 찾을 수 없습니다."));
- }
- // 본인 소유 검증 + 소프트 삭제 검증
- if (inv.MemberID != request.MemberID || inv.DeletedAt is not null)
- {
- return Result.Failure<Response>(Error.NotFound("Inventory.NotFound", "보관함 항목을 찾을 수 없습니다."));
- }
- if (inv.UsedAt is not null)
- {
- return Result.Failure<Response>(Error.Conflict("Inventory.AlreadyUsed", "이미 사용한 쿠폰입니다."));
- }
- if (inv.ExpiresAt is not null && inv.ExpiresAt.Value < DateTime.UtcNow)
- {
- return Result.Failure<Response>(Error.Problem("Inventory.Expired", "만료된 쿠폰입니다."));
- }
- if (inv.CouponCode is null || inv.CouponCode.Coupon is null)
- {
- return Result.Failure<Response>(Error.Problem("Inventory.InvalidState", "쿠폰 코드 정보를 찾을 수 없습니다."));
- }
- // 관리자에 의해 코드 풀에서 만료 처리된 코드는 사용 차단 (Defense in depth — 통상 ExpiresAt 가드에서 걸림)
- if (inv.CouponCode.Status == CouponCodeStatus.Expired)
- {
- return Result.Failure<Response>(Error.Problem("Inventory.Expired", "만료된 쿠폰입니다."));
- }
- inv.MarkUsed();
- inv.CouponCode.MarkUsed();
- inv.CouponCode.Coupon.IncreaseUsedCount();
- await db.SaveChangesAsync(ct);
- return Result.Success(new Response(inv.CouponCode.Code, inv.UsedAt!.Value));
- }
- }
|