Museum Exhibition and Service Platform: A Full-Stack Implementation with SpringBoot and Vue.js
Technical Architecture Overview
The platform employs a modern full-stack architecture combining SpringBoot 2.7+ for backend services and Vue.js 3.x for the frontend interface. This separation enables independant development and deployment while maintaining seamless RESTful API communication.
Backend Framework: SpringBoot
SpringBoot's convention-over-configuration paradigm accelerates development by eliminating boilerplate setup. The embedded Tomcat server simplifies deployment, while Spring Security handles authentication and Spring Data JPA manages persistence layers. Maven coordinates dependencies and build lifecycle.
Frontend Framework: Vue.js
Vue.js provides a reactive component-based architecture. The Composition API enhances code organization for complex exhibition management features. Vue Router manages navigation between collection galleries and visitor services, while Pinia handles state management for user sessions and exhibition data.
Core Implementation
Exhibition Management Controller
@RestController
@RequestMapping("/api/exhibitions")
public class ExhibitionController {
@Autowired
private ExhibitionService exhibitService;
@Autowired
private AnalyticsService analyticsService;
@GetMapping("/search")
public ResponseEntity<PageResult<ArtifactDTO>> searchArtifacts(
@RequestParam String keyword,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page - 1, size);
return ResponseEntity.ok(exhibitService.findByKeyword(keyword, pageable));
}
@PostMapping("/analytics/visitor-flow")
public ResponseEntity<Map<String, Object>> analyzeVisitorFlow(
@RequestBody VisitorFlowRequest request) {
Map<String, Object> insights = analyticsService.calculatePeakHours(
request.getExhibitionId(),
request.getStartDate(),
request.getEndDate()
);
return ResponseEntity.ok(insights);
}
}
Visitor Authentication Service
@Service
public class VisitorAuthService {
private final VisitorRepository visitorRepo;
private final TokenProvider tokenProvider;
public AuthenticationResponse authenticateVisitor(LoginCredentials credentials) {
Visitor visitor = visitorRepo.findByEmail(credentials.getEmail())
.orElseThrow(() -> new ResourceNotFoundException("Visitor not registered"));
if (!PasswordEncoder.matches(credentials.getPassword(), visitor.getPasswordHash())) {
throw new AuthenticationFailedException("Invalid credentials");
}
String accessToken = tokenProvider.generateToken(
visitor.getId(),
visitor.getRole(),
Duration.ofHours(2)
);
return new AuthenticationResponse(accessToken, visitor.getFullName());
}
}
Data Persistence Layer
Primary Database Schema
CREATE TABLE `exhibitions` (
`exhibit_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`title` VARCHAR(255) NOT NULL COMMENT 'Exhibition Title',
`curator` VARCHAR(100) DEFAULT NULL,
`start_date` DATE NOT NULL,
`end_date` DATE NOT NULL,
`description` TEXT,
`status` ENUM('DRAFT', 'ACTIVE', 'CLOSED') DEFAULT 'DRAFT',
PRIMARY KEY (`exhibit_id`),
INDEX `idx_status_dates` (`status`, `start_date`, `end_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Exhibition Records';
CREATE TABLE `visitors` (
`visitor_id` BIGINT NOT NULL AUTO_INCREMENT,
`registered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`email` VARCHAR(255) NOT NULL UNIQUE,
`password_hash` VARCHAR(255) NOT NULL,
`full_name` VARCHAR(200) NOT NULL,
`membership_level` ENUM('BASIC', 'PREMIUM', 'VIP') DEFAULT 'BASIC',
`preferences` JSON DEFAULT NULL,
PRIMARY KEY (`visitor_id`),
UNIQUE KEY `unique_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Visitor Accounts';
CREATE TABLE `booking_entries` (
`booking_id` BIGINT NOT NULL AUTO_INCREMENT,
`booked_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`visitor_id` BIGINT NOT NULL,
`exhibit_id` BIGINT NOT NULL,
`visit_date` DATE NOT NULL,
`time_slot` VARCHAR(20) NOT NULL,
`ticket_count` INT NOT NULL DEFAULT 1,
`total_amount` DECIMAL(10,2) NOT NULL,
FOREIGN KEY (`visitor_id`) REFERENCES `visitors`(`visitor_id`),
FOREIGN KEY (`exhibit_id`) REFERENCES `exhibitions`(`exhibit_id`),
INDEX `idx_visitor_date` (`visitor_id`, `visit_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Exhibition Bookings';
System Validation
Authentication Endpoint Testing
| Test Scenario | Input | Expected Behavior | Result |
|---|---|---|---|
| Valid credentials | email: visitor@example.com, password: correctPass | HTTP 200 with JWT token | Pass |
| Invalid password | email: visitor@example.com, password: wrongPass | HTTP 401 with error message | Pass |
| Non-existent account | email: unknown@example.com | HTTP 404 with not found error | Pass |
| Missing email field | password: somePass | HTTP 400 with validation error | Pass |
Exhibition CRUD Operations
| Operation | Test Data | Expected Outcome | Status |
|---|---|---|---|
| Create exhibition | Complete valid data | Record persisted with generated ID | Pass |
| Update exhibition | Modify title and dates | Changes reflected in database | Pass |
| Delete exhibition | Valid exhibit_id | Soft delete marks as 'CLOSED' | Pass |
| Search by keyword | "Renaissance" | Returns matching exhibitions | Pass |
Performance Considerations
Caching strategies implemented with Redis reduce database load for frequently accessed exhibition data. Connection pooling configured with HikariCP maintains optimal database connection management. API responses are compressed using GZIP to minimize payload size for mobile visitors.
The frontend implemants lazy loading for high-resolution artifact images and virtual scrolling for large collection lists, ensuring smooth user experience across devices.