95 lines
4.0 KiB
C#
95 lines
4.0 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Tiku.Application.Commerce;
|
|
using Tiku.Domain.Commerce;
|
|
|
|
namespace Tiku.Infrastructure.Commerce;
|
|
|
|
internal sealed class CouponAdministrationService(CommerceAdministrationDependencies dependencies)
|
|
: CommerceAdministrationServiceBase(dependencies), ICouponAdministrationService
|
|
{
|
|
public async Task<TenantCouponList> GetCouponsAsync(
|
|
CommerceAdminActor actor,
|
|
CommerceAdminQuery query,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await AssertAdminAsync(actor, cancellationToken);
|
|
var coupons = dbContext.Coupons.AsNoTracking()
|
|
.Where(item => item.TenantId == actor.TenantId);
|
|
var items = await coupons
|
|
.OrderByDescending(item => item.CreatedAt)
|
|
.Take(Math.Clamp(query.Limit ?? 50, 1, 200))
|
|
.ToArrayAsync(cancellationToken);
|
|
return new TenantCouponList(items);
|
|
}
|
|
|
|
public async Task<Coupon> UpsertCouponAsync(
|
|
CommerceAdminActor actor,
|
|
UpsertCouponCommand command,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await AssertAdminAsync(actor, cancellationToken);
|
|
var coupon = command.Id.HasValue
|
|
? await dbContext.Coupons.SingleOrDefaultAsync(
|
|
item => item.TenantId == actor.TenantId && item.Id == command.Id.Value,
|
|
cancellationToken)
|
|
: await dbContext.Coupons.SingleOrDefaultAsync(
|
|
item => item.TenantId == actor.TenantId && item.Code == command.Code.Trim(),
|
|
cancellationToken);
|
|
if (coupon is null)
|
|
{
|
|
coupon = new Coupon { TenantId = actor.TenantId };
|
|
dbContext.Coupons.Add(coupon);
|
|
}
|
|
|
|
coupon.Code = command.Code.Trim();
|
|
coupon.PlanId = command.PlanId;
|
|
coupon.DiscountType = command.DiscountType;
|
|
coupon.DiscountValue = command.DiscountValue;
|
|
coupon.ValidFrom = command.ValidFrom;
|
|
coupon.ValidTo = command.ValidTo;
|
|
coupon.MaxUses = command.MaxUses;
|
|
coupon.Source = command.Source?.Trim();
|
|
coupon.Remark = command.Remark;
|
|
|
|
await dbContext.SaveChangesAsync(cancellationToken);
|
|
return coupon;
|
|
}
|
|
|
|
public async Task<TenantCouponRedemptionList> GetCouponRedemptionsAsync(
|
|
CommerceAdminActor actor,
|
|
CommerceAdminQuery query,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await AssertAdminAsync(actor, cancellationToken);
|
|
var redemptions = dbContext.CouponRedemptions.AsNoTracking()
|
|
.Where(item => item.TenantId == actor.TenantId);
|
|
if (!string.IsNullOrWhiteSpace(query.Status))
|
|
redemptions = redemptions.Where(item => item.Status == ParseCouponRedemptionStatus(query.Status));
|
|
|
|
var items = await redemptions
|
|
.OrderByDescending(item => item.CreatedAt)
|
|
.Take(Math.Clamp(query.Limit ?? 50, 1, 200))
|
|
.ToArrayAsync(cancellationToken);
|
|
return new TenantCouponRedemptionList(items);
|
|
}
|
|
|
|
public async Task<TenantCouponReport> GetCouponReportAsync(
|
|
CommerceAdminActor actor,
|
|
CommerceAdminQuery query,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await AssertAdminAsync(actor, cancellationToken);
|
|
var couponCount =
|
|
await dbContext.Coupons.CountAsync(item => item.TenantId == actor.TenantId, cancellationToken);
|
|
var redemptions = dbContext.CouponRedemptions.AsNoTracking()
|
|
.Where(item => item.TenantId == actor.TenantId);
|
|
var claimedCount = await redemptions.CountAsync(cancellationToken);
|
|
var usedCount =
|
|
await redemptions.CountAsync(item => item.Status == CouponRedemptionStatus.Used, cancellationToken);
|
|
var discountApplied = await redemptions
|
|
.Where(item => item.Status == CouponRedemptionStatus.Used)
|
|
.SumAsync(item => item.DiscountAppliedCents, cancellationToken) ?? 0;
|
|
return new TenantCouponReport(couponCount, claimedCount, usedCount, discountApplied);
|
|
}
|
|
}
|