Spaces:
Sleeping
Sleeping
File size: 9,535 Bytes
4433399 7fe61c9 ad67295 1a7e978 ad67295 1a7e978 7fe61c9 ad67295 1a7e978 ad67295 1a7e978 7fe61c9 ad67295 1a7e978 6f32ae8 ad67295 1a7e978 ad67295 1a7e978 6f32ae8 1a7e978 6f32ae8 25f925e 1a7e978 7fe61c9 ad67295 7fe61c9 ad67295 7fe61c9 ad67295 6f32ae8 ad67295 7fe61c9 ad67295 7fe61c9 ad67295 7fe61c9 ad67295 7fe61c9 ec76476 7fe61c9 ad67295 7fe61c9 da13799 7fe61c9 b8cc5e6 7fe61c9 b8cc5e6 25f925e 7fe61c9 1a7e978 ad67295 1a7e978 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 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | using LibraryManagement.Shared.Models;
using Backend.Features.Subscriptions;
using DbConnect.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
namespace Backend.Features.Loyalty
{
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class LoyaltyController : ControllerBase
{
private readonly ILoyaltyService _loyaltyService;
private readonly ISubscriptionService _subscriptionService;
private readonly AppDbContext _context;
public LoyaltyController(ILoyaltyService loyaltyService, ISubscriptionService subscriptionService, AppDbContext context)
{
_loyaltyService = loyaltyService;
_subscriptionService = subscriptionService;
_context = context;
}
[HttpGet("rewards")]
public async Task<IActionResult> GetRewards()
{
var rewards = await _loyaltyService.GetActiveRewardsAsync();
return Ok(rewards);
}
// ββ Member endpoints ββββββββββββββββββββββββββββββββββββββββββββββββββ
[HttpGet("my-account")]
public async Task<IActionResult> GetMyAccount()
{
var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdStr)) return Unauthorized();
// 2. Get user from DB to get Email/Mobile for potential registration
var user = await _context.Users.FindAsync(Guid.Parse(userIdStr));
if (user == null) return NotFound("User not found.");
// 3. Get Loyalty Account
var account = await _loyaltyService.GetUserAccountAsync(userIdStr);
if (account == null)
{
// Auto-register if account not found
var registered = await _loyaltyService.RegisterUserAsync(userIdStr, user.Email, user.PhoneNumber ?? "0000000000");
if (registered)
{
// Process a small signup bonus if it's their first time being linked
await _loyaltyService.ProcessEventAsync(userIdStr, "SIGNUP", 20, $"LINK-{user.Id}", "Account Auto-Linked Bonus", user.Email, user.PhoneNumber);
// Try lookup again
account = await _loyaltyService.GetUserAccountAsync(userIdStr);
}
}
if (account == null)
{
return Ok(null); // Will show "Account Not Linked" in UI
}
return Ok(account);
}
[HttpGet("my-redemptions")]
public async Task<IActionResult> GetMyRedemptions()
{
var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdStr)) return Unauthorized();
// Filter to this user's redemptions only
var redemptions = await _loyaltyService.GetUserRedemptionsAsync(userIdStr);
return Ok(redemptions);
}
[HttpGet("my-pending-redemptions")]
public async Task<IActionResult> GetMyPendingRedemptions()
{
var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdStr)) return Unauthorized();
var redemptions = await _loyaltyService.GetUserRedemptionsAsync(userIdStr);
var pending = redemptions.Where(r => string.Equals(r.Status, "Pending", StringComparison.OrdinalIgnoreCase));
return Ok(pending);
}
[HttpGet("my-points-history")]
public async Task<IActionResult> GetMyPointsHistory()
{
var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdStr)) return Unauthorized();
var account = await _loyaltyService.GetUserAccountAsync(userIdStr);
if (account == null) return NotFound("Loyalty account not found.");
var history = await _loyaltyService.GetPointsHistoryAsync(account.Id);
return Ok(history);
}
[HttpPost("claim")]
public async Task<IActionResult> ClaimReward([FromBody] ClaimRewardRequestDto request)
{
var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdStr)) return Unauthorized();
var (success, message) = await _loyaltyService.ClaimRewardAsync(userIdStr, request.RewardId, request.Notes ?? "Redeemed via Library Web Application");
if (success) return Ok(new { message });
return BadRequest(new { message });
}
// ββ Librarian endpoints βββββββββββββββββββββββββββββββββββββββββββββββ
[HttpGet("admin/redemptions/pending")]
[Authorize(Roles = "Librarian")]
public async Task<IActionResult> GetPendingRedemptions()
{
var redemptions = (await _loyaltyService.GetPendingRedemptionsAsync()).ToList();
// Map names
var guidList = redemptions
.Where(r => !string.IsNullOrEmpty(r.ExternalUserId) && Guid.TryParse(r.ExternalUserId, out _))
.Select(r => Guid.Parse(r.ExternalUserId))
.Distinct()
.ToList();
var users = await _context.Users
.Where(u => guidList.Contains(u.Id))
.Select(u => new { u.Id, u.FullName })
.ToListAsync();
var userMap = users.ToDictionary(u => u.Id.ToString(), u => u.FullName);
foreach (var r in redemptions)
{
if (!string.IsNullOrEmpty(r.ExternalUserId) && userMap.TryGetValue(r.ExternalUserId, out var name))
{
r.UserName = name;
}
else
{
r.UserName = "Unknown Member";
}
}
return Ok(redemptions);
}
[HttpGet("admin/all-points-history")]
[Authorize(Roles = "Librarian")]
public async Task<IActionResult> GetAllMembersPointsHistory()
{
// Get all users from DB
var users = await _context.Users
.Select(u => new { u.Id, u.FullName, u.Email })
.ToListAsync();
var results = new List<object>();
foreach (var user in users)
{
try
{
var account = await _loyaltyService.GetUserAccountAsync(user.Id.ToString());
if (account == null) continue;
var accountId = !string.IsNullOrWhiteSpace(account.Id) ? account.Id : (account.AccountId ?? string.Empty);
if (string.IsNullOrWhiteSpace(accountId)) continue;
var history = await _loyaltyService.GetPointsHistoryAsync(accountId);
var redemptions = await _loyaltyService.GetUserRedemptionsAsync(user.Id.ToString());
results.Add(new
{
userId = user.Id,
userName = user.FullName,
userEmail = user.Email,
accountId = accountId,
currentBalance = account.CurrentBalance,
tier = account.Tier,
history = history,
redemptions = redemptions
});
}
catch { /* Skip users not in loyalty system */ }
}
return Ok(results);
}
[HttpPost("admin/redemptions/{id}/fulfill")]
[Authorize(Roles = "Librarian")]
public async Task<IActionResult> FulfillRedemption(string id)
{
var pending = await _loyaltyService.GetPendingRedemptionsAsync();
var redemption = pending.FirstOrDefault(r => r.Id == id);
if (redemption == null)
return NotFound("Redemption record not found or already processed.");
var success = await _loyaltyService.UpdateRedemptionStatusAsync(id, "Fulfilled");
if (!success)
return BadRequest("Failed to update status in the loyalty system.");
bool membershipGranted = false;
if (Guid.TryParse(redemption.ExternalUserId?.Trim(), out Guid userId))
{
System.Diagnostics.Debug.WriteLine($"Fulfilling redemption {id} for user {userId}. RewardId: '{redemption.RewardId}', RewardName: '{redemption.RewardName}'");
membershipGranted = await _subscriptionService.HandleLoyaltyRedemptionAsync(userId, redemption.RewardId, redemption.RewardName, redemption.Id);
}
if (!membershipGranted)
{
var errorMsg = $"Redemption fulfilled in loyalty system, but failed to grant library membership. RewardName='{redemption.RewardName}', RewardId='{redemption.RewardId}' (Redemption ID: {id}).";
return BadRequest(new { message = errorMsg });
}
return Ok(new { message = $"Redemption fulfilled and {redemption.RewardName} membership granted successfully." });
}
}
}
|