Spaces:
Sleeping
Sleeping
File size: 8,095 Bytes
4433399 da13799 068485b 1a7e978 068485b da13799 7fe61c9 068485b da13799 1a7e978 068485b da13799 068485b 1a7e978 068485b 7fe61c9 25f925e 068485b da13799 068485b da13799 b8cc5e6 da13799 7fe61c9 b8cc5e6 7fe61c9 25f925e 7fe61c9 b8cc5e6 7fe61c9 b8cc5e6 25f925e 7fe61c9 b8cc5e6 7fe61c9 b8cc5e6 7fe61c9 da13799 068485b 25f925e 068485b 25f925e 068485b 25f925e 068485b 25f925e 068485b 25f925e 068485b 25f925e 068485b 1a7e978 068485b 7fe61c9 068485b 7fe61c9 068485b 4433399 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | using LibraryManagement.Shared.Models;
using DbConnect.Data;
using DbConnect.Entities;
using Microsoft.EntityFrameworkCore;
using Backend.Features.Loyalty;
namespace Backend.Features.Subscriptions
{
public interface ISubscriptionService
{
Task<IEnumerable<MembershipDto>> GetMembershipsAsync();
Task<SubscriptionDto?> GetUserSubscriptionAsync(Guid userId);
Task<IEnumerable<SubscriptionDto>> GetUserAllSubscriptionsAsync(Guid userId);
Task<bool> HandleLoyaltyRedemptionAsync(Guid userId, string rewardId, string rewardName, string redemptionId);
Task<SubscriptionDto> SubscribeUserAsync(Guid userId, int membershipId);
Task<IEnumerable<SubscriptionDto>> GetAllSubscriptionsAsync();
}
public class SubscriptionService : ISubscriptionService
{
private readonly AppDbContext _context;
private readonly ILoyaltyService _loyaltyService;
public SubscriptionService(AppDbContext context, ILoyaltyService loyaltyService)
{
_context = context;
_loyaltyService = loyaltyService;
}
public async Task<IEnumerable<MembershipDto>> GetMembershipsAsync()
{
return await _context.Memberships
.Select(m => new MembershipDto
{
Id = m.Id,
Type = m.Type,
MaxBooks = m.MaxBooks,
BorrowingDays = m.BorrowingDays,
Price = m.Price,
DurationMonths = m.DurationMonths,
RewardId = m.RewardId
}).ToListAsync();
}
public async Task<SubscriptionDto?> GetUserSubscriptionAsync(Guid userId)
{
var subscription = await _context.UserSubscriptions
.Include(s => s.Membership)
.Where(s => s.UserId == userId && s.IsActive)
.OrderByDescending(s => s.StartDate)
.FirstOrDefaultAsync();
if (subscription == null) return null;
return MapToDto(subscription);
}
public async Task<IEnumerable<SubscriptionDto>> GetUserAllSubscriptionsAsync(Guid userId)
{
var subscriptions = await _context.UserSubscriptions
.Include(s => s.Membership)
.Include(s => s.User)
.Where(s => s.UserId == userId && s.IsActive)
.OrderBy(s => s.StartDate)
.ToListAsync();
return subscriptions.Select(MapToDto);
}
public async Task<bool> HandleLoyaltyRedemptionAsync(Guid userId, string rewardId, string rewardName, string redemptionId)
{
try
{
// Load all memberships into memory (small collection)
var allMemberships = await _context.Memberships.ToListAsync();
Membership? membership = null;
// 1. First try exact match by RewardId (most precise)
if (!string.IsNullOrEmpty(rewardId))
{
membership = allMemberships.FirstOrDefault(m => m.RewardId == rewardId);
}
// 2. Fallback: in-memory string matching on rewardName vs membership Type
// e.g. "Free Monthly Basic Membership" contains words "Basic" and "Monthly"
if (membership == null && !string.IsNullOrEmpty(rewardName))
{
var normalizedRewardName = rewardName.ToLowerInvariant();
membership = allMemberships.FirstOrDefault(m =>
{
var typeWords = m.Type.ToLowerInvariant().Split(' ', StringSplitOptions.RemoveEmptyEntries);
return typeWords.All(w => normalizedRewardName.Contains(w));
});
}
if (membership == null)
{
System.Diagnostics.Debug.WriteLine($"No membership matched for rewardId='{rewardId}', rewardName='{rewardName}'");
return false;
}
System.Diagnostics.Debug.WriteLine($"Matched membership '{membership.Type}' (Id={membership.Id}) for rewardName='{rewardName}'");
// 3. Grant the membership (will be queued if they already have one)
await SubscribeUserAsync(userId, membership.Id);
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"HandleLoyaltyRedemptionAsync error: {ex.Message}");
return false;
}
}
public async Task<SubscriptionDto> SubscribeUserAsync(Guid userId, int membershipId)
{
var membership = await _context.Memberships.FindAsync(membershipId);
if (membership == null) throw new Exception("Membership plan not found.");
// Find the latest expiry date for this user to enable queuing
// We look at all active or future subscriptions
var latestSubscription = await _context.UserSubscriptions
.Where(s => s.UserId == userId && s.IsActive)
.OrderByDescending(s => s.ExpiryDate)
.FirstOrDefaultAsync();
DateTime startDate = DateTime.UtcNow;
// If there's an existing subscription that expires in the future, start after it
if (latestSubscription != null && latestSubscription.ExpiryDate > startDate)
{
startDate = latestSubscription.ExpiryDate;
}
var subscription = new UserSubscription
{
UserId = userId,
MembershipId = membershipId,
StartDate = startDate,
ExpiryDate = startDate.AddMonths(membership.DurationMonths),
IsActive = true
};
_context.UserSubscriptions.Add(subscription);
await _context.SaveChangesAsync();
// Explicitly load context for DTO
await _context.Entry(subscription).Reference(s => s.Membership).LoadAsync();
await _context.Entry(subscription).Reference(s => s.User).LoadAsync();
// Loyalty Integration: Send SUBSCRIBE event
string externalUserId = userId.ToString();
string userMobile = subscription.User?.PhoneNumber ?? "0000000000";
string userEmail = subscription.User?.Email ?? "No Email";
await _loyaltyService.ProcessEventAsync(
externalUserId: externalUserId,
eventKey: "SUBSCRIBE",
eventValue: (double)membership.Price,
referenceId: $"SUB-{subscription.Id}",
description: $"Purchased Membership: {membership.Type}",
email: userEmail,
mobile: userMobile
);
return MapToDto(subscription);
}
public async Task<IEnumerable<SubscriptionDto>> GetAllSubscriptionsAsync()
{
var subscriptions = await _context.UserSubscriptions
.Include(s => s.Membership)
.Include(s => s.User)
.OrderByDescending(s => s.StartDate)
.ToListAsync();
return subscriptions.Select(MapToDto);
}
private static SubscriptionDto MapToDto(UserSubscription subscription)
{
return new SubscriptionDto
{
Id = subscription.Id,
UserId = subscription.UserId,
MembershipId = subscription.MembershipId,
MembershipType = subscription.Membership.Type,
UserEmail = subscription.User?.Email ?? "Unknown",
UserName = subscription.User?.FullName ?? "Unknown",
StartDate = subscription.StartDate,
ExpiryDate = subscription.ExpiryDate,
IsActive = subscription.IsActive
};
}
}
}
|