Skip to content

Commit 6e1aae7

Browse files
committed
✨ Implement activity enrollment and waiting list
1 parent cc46757 commit 6e1aae7

21 files changed

Lines changed: 1571 additions & 48 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace TaleEngine.API.Contracts.Dtos.Requests
2+
{
3+
/// <summary>
4+
/// Request to enroll in an activity
5+
/// </summary>
6+
public class ActivityEnrollmentRequest
7+
{
8+
/// <summary>
9+
/// Gets or sets the activity identifier.
10+
/// </summary>
11+
public int ActivityId { get; set; }
12+
13+
/// <summary>
14+
/// Gets or sets the user identifier.
15+
/// </summary>
16+
public int UserId { get; set; }
17+
}
18+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
namespace TaleEngine.API.Contracts.Dtos.Results
2+
{
3+
/// <summary>
4+
/// Result of an activity enrollment operation
5+
/// </summary>
6+
public class ActivityEnrollmentResult
7+
{
8+
/// <summary>
9+
/// Gets or sets the activity identifier.
10+
/// </summary>
11+
public int ActivityId { get; set; }
12+
13+
/// <summary>
14+
/// Gets or sets the user identifier.
15+
/// </summary>
16+
public int UserId { get; set; }
17+
18+
/// <summary>
19+
/// Gets or sets whether the user was enrolled directly or added to waiting list.
20+
/// </summary>
21+
public bool IsWaitingList { get; set; }
22+
23+
/// <summary>
24+
/// Gets or sets the position in waiting list (null if enrolled directly).
25+
/// </summary>
26+
public int? PositionInWaitingList { get; set; }
27+
28+
/// <summary>
29+
/// Gets or sets the number of available places.
30+
/// </summary>
31+
public int AvailablePlaces { get; set; }
32+
33+
/// <summary>
34+
/// Gets or sets the total places for the activity.
35+
/// </summary>
36+
public int TotalPlaces { get; set; }
37+
38+
/// <summary>
39+
/// Gets or sets the success message.
40+
/// </summary>
41+
public string Message { get; set; }
42+
43+
/// <summary>
44+
/// Gets or sets whether the operation was successful.
45+
/// </summary>
46+
public bool Success { get; set; }
47+
}
48+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.Collections.Generic;
2+
3+
namespace TaleEngine.API.Contracts.Dtos.Results
4+
{
5+
/// <summary>
6+
/// Waiting list for an activity
7+
/// </summary>
8+
public class WaitingListResult
9+
{
10+
/// <summary>
11+
/// Gets or sets the activity identifier.
12+
/// </summary>
13+
public int ActivityId { get; set; }
14+
15+
/// <summary>
16+
/// Gets or sets the activity title.
17+
/// </summary>
18+
public string ActivityTitle { get; set; }
19+
20+
/// <summary>
21+
/// Gets or sets the total number of users in waiting list.
22+
/// </summary>
23+
public int TotalWaiting { get; set; }
24+
25+
/// <summary>
26+
/// Gets or sets the list of users in waiting list.
27+
/// </summary>
28+
public List<WaitingListUserDto> UsersInWaitingList { get; set; }
29+
}
30+
31+
/// <summary>
32+
/// User in waiting list
33+
/// </summary>
34+
public class WaitingListUserDto
35+
{
36+
/// <summary>
37+
/// Gets or sets the user identifier.
38+
/// </summary>
39+
public int UserId { get; set; }
40+
41+
/// <summary>
42+
/// Gets or sets the username.
43+
/// </summary>
44+
public string Username { get; set; }
45+
46+
/// <summary>
47+
/// Gets or sets the position in waiting list.
48+
/// </summary>
49+
public int Position { get; set; }
50+
}
51+
}

TaleEngine/TaleEngine.Application.Contracts/IActivityService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,11 @@ public interface IActivityService
1717
int CreateActivity(int editionId, Activity activity);
1818
int UpdateActivity(int id, Activity activity);
1919

20+
// Waiting List methods
21+
bool EnrollUserInActivity(int activityId, int userId);
22+
bool RemoveUserFromActivity(int activityId, int userId);
23+
List<UserEntity> GetWaitingList(int activityId);
24+
int? GetUserPositionInWaitingList(int activityId, int userId);
25+
bool PromoteFromWaitingList(int activityId);
2026
}
2127
}

TaleEngine/TaleEngine.Application/ActivityService.cs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,151 @@ private int Update(ActivityEntity activityEntity)
241241
return 1;
242242
}
243243

244+
public bool EnrollUserInActivity(int activityId, int userId)
245+
{
246+
try
247+
{
248+
var activity = _unitOfWork.ActivityRepository.GetAllIncludeEnrollments(activityId)
249+
.FirstOrDefault(a => a.Id == activityId);
250+
var user = _unitOfWork.UserRepository.GetById(userId);
251+
252+
if (activity == null || user == null)
253+
return false;
254+
255+
// Check if user is already enrolled
256+
if (activity.UsersPlay != null && activity.UsersPlay.Any(u => u.Id == userId))
257+
return false;
258+
259+
// Check if user is already in waiting list
260+
if (activity.UsersWaitingList != null && activity.UsersWaitingList.Any(u => u.Id == userId))
261+
return false;
262+
263+
// Check if there are available places
264+
if (activity.UsersPlay == null || activity.UsersPlay.Count < activity.Places)
265+
{
266+
// Enroll directly
267+
if (activity.UsersPlay == null)
268+
activity.UsersPlay = new List<UserEntity>();
269+
270+
activity.UsersPlay.Add(user);
271+
}
272+
else
273+
{
274+
// Add to waiting list
275+
if (activity.UsersWaitingList == null)
276+
activity.UsersWaitingList = new List<UserEntity>();
277+
278+
activity.UsersWaitingList.Add(user);
279+
}
280+
281+
_unitOfWork.ActivityRepository.Update(activity);
282+
_unitOfWork.ActivityRepository.Save();
283+
return true;
284+
}
285+
catch (Exception)
286+
{
287+
return false;
288+
}
289+
}
290+
291+
public bool RemoveUserFromActivity(int activityId, int userId)
292+
{
293+
try
294+
{
295+
var activity = _unitOfWork.ActivityRepository.GetAllIncludeEnrollments(activityId)
296+
.FirstOrDefault(a => a.Id == activityId);
297+
var user = _unitOfWork.UserRepository.GetById(userId);
298+
299+
if (activity == null || user == null)
300+
return false;
301+
302+
bool wasEnrolled = false;
303+
304+
// Remove from enrolled users
305+
if (activity.UsersPlay != null && activity.UsersPlay.Any(u => u.Id == userId))
306+
{
307+
activity.UsersPlay.Remove(user);
308+
wasEnrolled = true;
309+
}
310+
311+
// Remove from waiting list
312+
if (activity.UsersWaitingList != null && activity.UsersWaitingList.Any(u => u.Id == userId))
313+
{
314+
activity.UsersWaitingList.Remove(user);
315+
}
316+
317+
_unitOfWork.ActivityRepository.Update(activity);
318+
_unitOfWork.ActivityRepository.Save();
319+
320+
// If user was enrolled, promote from waiting list
321+
if (wasEnrolled)
322+
{
323+
PromoteFromWaitingList(activityId);
324+
}
325+
326+
return true;
327+
}
328+
catch (Exception)
329+
{
330+
return false;
331+
}
332+
}
333+
334+
public List<UserEntity> GetWaitingList(int activityId)
335+
{
336+
var activity = _unitOfWork.ActivityRepository.GetAllIncludeWaitingList(activityId)
337+
.FirstOrDefault(a => a.Id == activityId);
338+
339+
return activity?.UsersWaitingList ?? new List<UserEntity>();
340+
}
341+
342+
public int? GetUserPositionInWaitingList(int activityId, int userId)
343+
{
344+
var activity = _unitOfWork.ActivityRepository.GetAllIncludeWaitingList(activityId)
345+
.FirstOrDefault(a => a.Id == activityId);
346+
347+
if (activity?.UsersWaitingList == null)
348+
return null;
349+
350+
var position = activity.UsersWaitingList.FindIndex(u => u.Id == userId);
351+
return position >= 0 ? position + 1 : (int?)null;
352+
}
353+
354+
public bool PromoteFromWaitingList(int activityId)
355+
{
356+
try
357+
{
358+
var activity = _unitOfWork.ActivityRepository.GetAllIncludeEnrollments(activityId)
359+
.FirstOrDefault(a => a.Id == activityId);
360+
361+
if (activity == null)
362+
return false;
363+
364+
// Check if there are available places and users in waiting list
365+
if (activity.UsersWaitingList != null && activity.UsersWaitingList.Any() &&
366+
(activity.UsersPlay == null || activity.UsersPlay.Count < activity.Places))
367+
{
368+
var firstWaitingUser = activity.UsersWaitingList.First();
369+
activity.UsersWaitingList.Remove(firstWaitingUser);
370+
371+
if (activity.UsersPlay == null)
372+
activity.UsersPlay = new List<UserEntity>();
373+
374+
activity.UsersPlay.Add(firstWaitingUser);
375+
376+
_unitOfWork.ActivityRepository.Update(activity);
377+
_unitOfWork.ActivityRepository.Save();
378+
return true;
379+
}
380+
381+
return false;
382+
}
383+
catch (Exception)
384+
{
385+
return false;
386+
}
387+
}
388+
244389
#endregion
245390
}
246391
}

TaleEngine/TaleEngine.Bussiness/Commands/ActivityCommands.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using System;
2+
using System.Linq;
23
using TaleEngine.Aggregates.ActivityAggregate;
34
using TaleEngine.API.Contracts.Dtos;
5+
using TaleEngine.API.Contracts.Dtos.Requests;
6+
using TaleEngine.API.Contracts.Dtos.Results;
47
using TaleEngine.CQRS.Contracts;
58
using TaleEngine.CQRS.Mappers;
69
using TaleEngine.Cross.Enums;
@@ -83,5 +86,59 @@ public void ChangeActivityStatusCommand(int activityId, int statusId)
8386

8487
_activityService.UpdateActivity(activityId, model);
8588
}
89+
90+
public ActivityEnrollmentResult EnrollInActivityCommand(ActivityEnrollmentRequest request)
91+
{
92+
var activity = _activityService.GetById(request.ActivityId);
93+
94+
if (activity == null)
95+
{
96+
return new ActivityEnrollmentResult
97+
{
98+
Success = false,
99+
Message = "Activity not found",
100+
ActivityId = request.ActivityId,
101+
UserId = request.UserId
102+
};
103+
}
104+
105+
var success = _activityService.EnrollUserInActivity(request.ActivityId, request.UserId);
106+
107+
if (!success)
108+
{
109+
return new ActivityEnrollmentResult
110+
{
111+
Success = false,
112+
Message = "Enrollment failed. User may already be enrolled or in waiting list.",
113+
ActivityId = request.ActivityId,
114+
UserId = request.UserId
115+
};
116+
}
117+
118+
// Get updated activity to check enrollment status
119+
var updatedActivity = _activityService.GetById(request.ActivityId);
120+
var enrolledUsers = updatedActivity.UsersPlay?.Count ?? 0;
121+
var isWaitingList = enrolledUsers >= activity.Places;
122+
var position = isWaitingList ? _activityService.GetUserPositionInWaitingList(request.ActivityId, request.UserId) : null;
123+
124+
return new ActivityEnrollmentResult
125+
{
126+
Success = true,
127+
ActivityId = request.ActivityId,
128+
UserId = request.UserId,
129+
IsWaitingList = isWaitingList,
130+
PositionInWaitingList = position,
131+
AvailablePlaces = activity.Places - enrolledUsers,
132+
TotalPlaces = activity.Places,
133+
Message = isWaitingList
134+
? $"Added to waiting list at position {position}"
135+
: "Successfully enrolled in activity"
136+
};
137+
}
138+
139+
public bool LeaveActivityCommand(ActivityEnrollmentRequest request)
140+
{
141+
return _activityService.RemoveUserFromActivity(request.ActivityId, request.UserId);
142+
}
86143
}
87144
}

TaleEngine/TaleEngine.Bussiness/Contracts/IActivityCommands.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using TaleEngine.API.Contracts.Dtos;
2+
using TaleEngine.API.Contracts.Dtos.Requests;
3+
using TaleEngine.API.Contracts.Dtos.Results;
24

35
namespace TaleEngine.CQRS.Contracts
46
{
@@ -8,5 +10,7 @@ public interface IActivityCommands
810
void CreateCommand(int editionId, ActivityDto activityDto);
911
void UpdateCommand(ActivityDto activityDto);
1012
void ChangeActivityStatusCommand(int activityId, int statusId);
13+
ActivityEnrollmentResult EnrollInActivityCommand(ActivityEnrollmentRequest request);
14+
bool LeaveActivityCommand(ActivityEnrollmentRequest request);
1115
}
1216
}

TaleEngine/TaleEngine.Bussiness/Contracts/IActivityQueries.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,7 @@ public interface IActivityQueries
1111
List<ActivityDto> PendingActivitiesQuery(int editionId);
1212
ActivityFilteredResult ActiveActivitiesFilteredQuery(ActivityFilterRequest activityFilterRequest, int userId = default);
1313
List<ActivityDto> LastThreeActivitiesQuery(int editionId);
14+
WaitingListResult GetWaitingListQuery(int activityId);
15+
int? GetUserPositionInWaitingListQuery(int activityId, int userId);
1416
}
1517
}

0 commit comments

Comments
 (0)