93 lines
3.7 KiB
C#
93 lines
3.7 KiB
C#
|
|
using FlatRender.IdentitySvc.Application.Services.Interfaces;
|
||
|
|
using FlatRender.IdentitySvc.Domain.Entities;
|
||
|
|
using FlatRender.IdentitySvc.Domain.Enums;
|
||
|
|
using FlatRender.IdentitySvc.Infrastructure.Data;
|
||
|
|
using FlatRender.IdentitySvc.Models.Requests;
|
||
|
|
using FlatRender.IdentitySvc.Models.Responses;
|
||
|
|
using Microsoft.EntityFrameworkCore;
|
||
|
|
|
||
|
|
namespace FlatRender.IdentitySvc.Application.Services;
|
||
|
|
|
||
|
|
public class DiscountService(IdentityDbContext db) : IDiscountService
|
||
|
|
{
|
||
|
|
public async Task<DiscountValidateResponse> ValidateAsync(Guid tenantId, string code, Guid? planId)
|
||
|
|
{
|
||
|
|
var discount = await db.Discounts.FirstOrDefaultAsync(d =>
|
||
|
|
d.TenantId == tenantId && d.Code == code && d.IsActive &&
|
||
|
|
(d.StartsAt == null || d.StartsAt <= DateTime.UtcNow) &&
|
||
|
|
(d.ExpiresAt == null || d.ExpiresAt >= DateTime.UtcNow) &&
|
||
|
|
(d.MaxUseCount == null || d.UsedCount < d.MaxUseCount));
|
||
|
|
|
||
|
|
if (discount == null)
|
||
|
|
return new DiscountValidateResponse(false, 0, "Unknown", 0);
|
||
|
|
|
||
|
|
if (planId.HasValue && discount.AppliesToPlanIds != null && discount.AppliesToPlanIds.Length > 0)
|
||
|
|
{
|
||
|
|
if (!discount.AppliesToPlanIds.Contains(planId.Value))
|
||
|
|
return new DiscountValidateResponse(false, 0, discount.Kind.ToString(), discount.Value);
|
||
|
|
}
|
||
|
|
|
||
|
|
long discountMinor = 0;
|
||
|
|
if (planId.HasValue)
|
||
|
|
{
|
||
|
|
var plan = await db.Plans.FindAsync(planId.Value);
|
||
|
|
if (plan != null)
|
||
|
|
{
|
||
|
|
discountMinor = discount.Kind == DiscountKind.Percentage
|
||
|
|
? (long)(plan.PriceMinor * (double)discount.Value / 100)
|
||
|
|
: (long)discount.Value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return new DiscountValidateResponse(true, discountMinor, discount.Kind.ToString(), discount.Value);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<PagedResponse<DiscountResponse>> ListAsync(Guid tenantId, int page, int pageSize)
|
||
|
|
{
|
||
|
|
var total = await db.Discounts.LongCountAsync(d => d.TenantId == tenantId);
|
||
|
|
var discounts = await db.Discounts
|
||
|
|
.Where(d => d.TenantId == tenantId)
|
||
|
|
.OrderByDescending(d => d.CreatedAt)
|
||
|
|
.Skip((page - 1) * pageSize)
|
||
|
|
.Take(pageSize)
|
||
|
|
.ToListAsync();
|
||
|
|
|
||
|
|
return new PagedResponse<DiscountResponse>(
|
||
|
|
discounts.Select(MapResponse).ToList(),
|
||
|
|
new PaginationMeta(page, pageSize, total, total > (long)page * pageSize)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<DiscountResponse> CreateAsync(Guid tenantId, CreateDiscountRequest request)
|
||
|
|
{
|
||
|
|
var exists = await db.Discounts.AnyAsync(d => d.TenantId == tenantId && d.Code == request.Code);
|
||
|
|
if (exists) throw new InvalidOperationException("Discount code already exists");
|
||
|
|
|
||
|
|
if (!Enum.TryParse<DiscountKind>(request.Kind, true, out var kind))
|
||
|
|
throw new ArgumentException("Invalid discount kind");
|
||
|
|
|
||
|
|
var discount = new Discount
|
||
|
|
{
|
||
|
|
TenantId = tenantId,
|
||
|
|
Name = request.Name,
|
||
|
|
Code = request.Code.ToUpper(),
|
||
|
|
Kind = kind,
|
||
|
|
Value = request.Value,
|
||
|
|
OwnerUserId = request.OwnerUserId,
|
||
|
|
OwnerProfitPercentage = request.OwnerProfitPercentage,
|
||
|
|
MaxUseCount = request.MaxUseCount,
|
||
|
|
AppliesToPlanIds = request.AppliesToPlanIds,
|
||
|
|
StartsAt = request.StartsAt,
|
||
|
|
ExpiresAt = request.ExpiresAt,
|
||
|
|
};
|
||
|
|
db.Discounts.Add(discount);
|
||
|
|
await db.SaveChangesAsync();
|
||
|
|
return MapResponse(discount);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static DiscountResponse MapResponse(Discount d) => new(
|
||
|
|
d.Id, d.Name, d.Code, d.Kind.ToString(), d.Value,
|
||
|
|
d.UsedCount, d.MaxUseCount, d.IsActive, d.ExpiresAt, d.CreatedAt
|
||
|
|
);
|
||
|
|
}
|