Overview
Once proposals are approved by administrators, they become Events visible to students. The event system supports browsing, registration, search, pagination, and organizer management.
Public Browsing Students can browse all approved events
Search & Filter Full-text search across event titles and descriptions
Pagination Efficient pagination for large event lists
Organizer Dashboard Organizers can view and manage their events
Event Lifecycle
Proposal Approved
Admin approves a proposal, creating an Event entity
Published
Event becomes visible to students with OPEN status
Registration Period
Students register until capacity is reached
Ongoing
Event status changes to ONGOING on event date
Completed
Event status changes to COMPLETED after end time
Event Entity
Database Structure
@ Entity
@ Table ( name = "events" )
@ Data
@ Builder
@ NoArgsConstructor
@ AllArgsConstructor
public class Event {
@ Id
@ GeneratedValue ( strategy = GenerationType . IDENTITY )
@ Column ( name = "event_id" )
private Long eventID ;
@ Column ( nullable = false )
private String title ;
@ Column ( columnDefinition = "TEXT" )
private String description ;
@ Column ( name = "event_date" , nullable = false )
private LocalDate eventDate ;
@ Column ( name = "start_time" , nullable = false )
private LocalTime startTime ;
@ Column ( name = "end_time" , nullable = false )
private LocalTime endTime ;
@ Column ( nullable = false )
private String venue ;
@ Column ( nullable = false )
private Integer capacity ;
@ Column ( name = "current_registrations" )
@ Builder . Default
private Integer currentRegistrations = 0 ;
@ Enumerated ( EnumType . STRING )
@ Column ( nullable = false )
private EventStatus status ;
@ Enumerated ( EnumType . STRING )
@ Column ( name = "approval_status" )
@ Builder . Default
private ApprovalStatus approvalStatus = ApprovalStatus . APPROVED ;
@ Enumerated ( EnumType . STRING )
@ Column ( name = "organization_type" , nullable = false )
private OrganizationType organizationType ;
@ ManyToOne ( fetch = FetchType . LAZY )
@ JoinColumn (
name = "organizer_id" ,
nullable = false ,
foreignKey = @ ForeignKey ( name = "fk_events_organizer" )
)
private EventOrganizer organizer ;
@ Column ( name = "created_at" )
@ Builder . Default
private LocalDateTime createdAt = LocalDateTime . now ();
@ Column ( name = "updated_at" )
private LocalDateTime updatedAt ;
// OPTIMISTIC LOCKING
@ Version
private Long version ;
}
Key Fields
Primary key, auto-generated
Detailed event description (TEXT column)
Event start time (required)
Event end time (required)
Event location (required)
Maximum number of participants (required)
Current number of registrations (defaults to 0)
One of: OPEN, ONGOING, COMPLETED, CANCELLED
One of: PENDING, APPROVED, REJECTED (defaults to APPROVED)
One of: ACADEMIC, CULTURAL, SPORTS, SOCIAL, CAREER, VOLUNTEER
Foreign key to the organizer (lazy-loaded)
Optimistic locking version for concurrency control
The version field is critical for preventing race conditions during registration. See Registrations for details.
Browse Events
Endpoint
GET / api / events ? page = 0 & size = 9 & search = workshop
Query Parameters
Number of events per page
Search query (searches title and description)
Implementation
EventController.java:27-45
@ GetMapping
public ResponseEntity < Response < Page < EventDTO >>> getAllEvents (
@ RequestParam ( defaultValue = "0" ) int page,
@ RequestParam ( defaultValue = "9" ) int size,
@ RequestParam ( required = false ) String search,
@ AuthenticationPrincipal CustomUserDetails userDetails) {
Pageable pageable = PageRequest . of (page, size);
Long currentUserId = userDetails != null ? userDetails . getUser (). getUserID () : null ;
Page < EventDTO > events = eventService . getAllApprovedEvents (pageable, search, currentUserId);
Response < Page < EventDTO >> response = Response. < Page < EventDTO >> builder ()
. statusCode ( HttpStatus . OK . value ())
. message ( "Events retrieved successfully" )
. data (events)
. build ();
return ResponseEntity . ok (response);
}
This endpoint is accessible to both authenticated and unauthenticated users. Authentication is optional but provides additional context (e.g., isRegistered field).
Response Example
{
"statusCode" : 200 ,
"message" : "Events retrieved successfully" ,
"data" : {
"content" : [
{
"eventID" : 456 ,
"title" : "React Workshop" ,
"description" : "Learn React fundamentals" ,
"eventDate" : "2024-04-15" ,
"startTime" : "14:00:00" ,
"endTime" : "16:00:00" ,
"venue" : "Room 301" ,
"capacity" : 50 ,
"currentRegistrations" : 32 ,
"status" : "OPEN" ,
"organizationType" : "ACADEMIC" ,
"organizerName" : "Computer Science Club" ,
"organizationName" : "CS Club" ,
"isRegistered" : true ,
"isOwner" : false ,
"createdAt" : "2024-03-15T10:30:00"
}
],
"pageable" : {
"pageNumber" : 0 ,
"pageSize" : 9 ,
"sort" : { "sorted" : false },
"offset" : 0 ,
"paged" : true ,
"unpaged" : false
},
"totalPages" : 3 ,
"totalElements" : 25 ,
"last" : false ,
"first" : true ,
"number" : 0 ,
"numberOfElements" : 9 ,
"size" : 9 ,
"empty" : false
},
"timestamp" : "2024-03-15T10:35:00"
}
Array of events for the current page
Total number of events across all pages
Current page number (0-indexed)
True if this is the first page
True if this is the last page
Get Event by ID
Endpoint
GET / api / events / {eventID}
Implementation
EventController.java:47-60
@ GetMapping ( "/{eventID}" )
public ResponseEntity < Response < EventDTO >> getEventByID (@ PathVariable Long eventID,
@ AuthenticationPrincipal CustomUserDetails userDetails) {
Long currentUserId = userDetails != null ? userDetails . getUser (). getUserID () : null ;
EventDTO event = eventService . getEventByID (eventID, currentUserId);
Response < EventDTO > response = Response. < EventDTO > builder ()
. statusCode ( HttpStatus . OK . value ())
. message ( "Event retrieved successfully" )
. data (event)
. build ();
return ResponseEntity . ok (response);
}
Response Example
{
"statusCode" : 200 ,
"message" : "Event retrieved successfully" ,
"data" : {
"eventID" : 456 ,
"title" : "React Workshop" ,
"description" : "Learn React fundamentals including hooks, state management, and best practices." ,
"eventDate" : "2024-04-15" ,
"startTime" : "14:00:00" ,
"endTime" : "16:00:00" ,
"venue" : "Room 301, Engineering Building" ,
"capacity" : 50 ,
"currentRegistrations" : 32 ,
"status" : "OPEN" ,
"approvalStatus" : "APPROVED" ,
"organizationType" : "ACADEMIC" ,
"organizerID" : 789 ,
"organizerName" : "John Smith" ,
"organizationName" : "Computer Science Club" ,
"isRegistered" : false ,
"isOwner" : false ,
"createdAt" : "2024-03-15T10:30:00" ,
"updatedAt" : "2024-03-16T09:15:00"
},
"timestamp" : "2024-03-15T10:35:00"
}
EventDTO Fields
True if the current user is registered for this event (requires authentication)
True if the current user is the organizer of this event
The isRegistered and isOwner fields provide client-side context for displaying appropriate UI (e.g., “Register” vs “Already Registered” buttons).
Get Managed Events (Organizers)
Endpoint
Authorization
EventController.java:62-63
@ GetMapping ( "/managed" )
@ PreAuthorize ( "hasRole('ORGANIZER')" )
Only users with ORGANIZER role can access this endpoint.
Implementation
EventController.java:64-77
public ResponseEntity < Response < List < EventDTO >>> getManagedEvents (
@ AuthenticationPrincipal CustomUserDetails userDetails) {
Long organizerId = userDetails . getUser (). getUserID ();
List < EventDTO > managedEvents = eventService . getEventsByOrganizer (organizerId);
Response < List < EventDTO >> response = Response. < List < EventDTO >> builder ()
. statusCode ( HttpStatus . OK . value ())
. message ( "Managed events retrieved successfully" )
. data (managedEvents)
. build ();
return ResponseEntity . ok (response);
}
Response Example
{
"statusCode" : 200 ,
"message" : "Managed events retrieved successfully" ,
"data" : [
{
"eventID" : 456 ,
"title" : "React Workshop" ,
"status" : "OPEN" ,
"currentRegistrations" : 32 ,
"capacity" : 50 ,
"eventDate" : "2024-04-15" ,
"isOwner" : true
},
{
"eventID" : 457 ,
"title" : "Career Fair 2024" ,
"status" : "ONGOING" ,
"currentRegistrations" : 150 ,
"capacity" : 200 ,
"eventDate" : "2024-03-20" ,
"isOwner" : true
}
],
"timestamp" : "2024-03-15T10:35:00"
}
This endpoint returns all events organized by the authenticated user, regardless of status (OPEN, ONGOING, COMPLETED, CANCELLED).
Event Statuses
OPEN
ONGOING
COMPLETED
CANCELLED
Description: Event is accepting registrationsConditions:
Event date is in the future
Current registrations < capacity
Actions Available:
Students can register
Organizer can post updates
Admin can cancel event
Description: Event is currently happeningConditions:
Current time is between start and end time on event date
Actions Available:
No new registrations
Organizer can view participants
Attendance can be tracked
Description: Event has finishedConditions:
Current time is after event end time
Actions Available:
Organizer submits post-event report
Students can provide feedback
No modifications allowed
Description: Event was cancelledConditions:
Admin or organizer cancelled the event
Actions Available:
All registrations are cancelled
Students are notified
Event remains visible in history
Search Functionality
Full-Text Search
The search parameter performs full-text search across:
Event title
Event description
// Example: Search for "workshop"
GET / api / events ? search = workshop
// Example: Search with pagination
GET / api / events ? search = react & page = 0 & size = 12
Case-Insensitive Search is case-insensitive
Partial Matching Supports partial word matching
Multiple Words Searches for any matching word
Fast Performance Database-level search with indexes
Optimistic Locking
Concurrency Control
The Event entity uses optimistic locking to prevent race conditions:
// OPTIMISTIC LOCKING
@ Version
private Long version ;
Optimistic locking is critical for preventing overbooking during high-traffic registration periods.
How It Works
Read Event
Client reads event with version = 5
Modify Event
Client increments currentRegistrations
Save with Version Check
JPA checks if database version is still 5
Success or Retry
If version matches: Save succeeds, version incremented to 6
If version doesn’t match: ObjectOptimisticLockingFailureException thrown
See Registrations for a detailed example of optimistic locking in action.
Organization Types
Events are categorized by organization type:
ACADEMIC Workshops, seminars, lectures
CULTURAL Art shows, performances, exhibitions
SPORTS Tournaments, fitness activities
SOCIAL Networking, social gatherings
CAREER Career fairs, recruitment events
VOLUNTEER Community service, volunteer work
The Wrapper Rule
All event endpoints follow the Wrapper Rule: ResponseEntity<Response<T>>
Consistent Structure
Response < List < EventDTO >> response = Response. < List < EventDTO >> builder ()
. statusCode ( HttpStatus . OK . value ())
. message ( "Events retrieved successfully" )
. data (managedEvents)
. build ();
return ResponseEntity . ok (response);
This ensures:
Consistent error handling
Standardized response format
Client-friendly error messages
Automatic timestamp inclusion
Error Handling
{
"statusCode" : 404 ,
"message" : "Event not found with ID: 456" ,
"timestamp" : "2024-03-15T10:30:00"
}
{
"statusCode" : 403 ,
"message" : "Access Denied" ,
"timestamp" : "2024-03-15T10:30:00"
}
{
"statusCode" : 400 ,
"message" : "Invalid page number" ,
"timestamp" : "2024-03-15T10:30:00"
}
Best Practices
Use Pagination
Always use pagination for event lists to improve performance and user experience.
Implement Search
Use the search parameter for client-side search functionality.
Check isRegistered
Use the isRegistered field to prevent duplicate registrations on the client side.
Handle Empty Results
Check the empty field in pagination response to handle empty result sets.
Proposals Learn about proposal submission before events
Registrations Understand how students register for events