Hệ thống đặt vé rạp chiếu phim tích hợp Spring Boot và Vue với quản lý vai trò, phòng chiếu và lịch chiếu

Hệ thống đặt vé rạp chiếu phim được xây dựng trên kiến trúc phân tầng rõ ràng: backend dựa trên Spring Boot (Java 8+), sử dụng MySQL 5.7+ làm cơ sở dữ liệu quan hệ; frontend được phát triển bằng Vue 3 kết hợp Element Plus cho giao diện người dùng chuyên nghiệp. Dự án hỗ trợ đầy đủ chức năng quản trị và trải nghiệm người dùng thông qua hai cổng vào riêng biệt.

Môi trường triển khai:

  • Java JDK 8 trở lên
  • MySQL 5.7 hoặc mới hơn
  • Node.js 14+ (cho phần frontend)

Công cụ phát triển đề xuất:

  • Backend: IntelliJ IDEA, Eclipse hoặc VS Code với plugin Java
  • Frontend: VS Code hoặc WebStorm với hỗ trợ Vue và ESLint

Cấu trúc thư mục chính:

  • server/: Dự án Spring Boot (Maven), chứa lớp khởi động Application.java
  • client-admin/: Giao diện quản trị (Vue + Element Plus), chạy bằng npm run dev
  • client-user/: Giao diện người dùng cuối (Vue + Element Plus), chạy bằng npm run serve

Tài khoản mặc định:

  • Quản trị viên: admin / 123456
  • Người dùng thường: user / 123456

Một số đoạn mã logic đặc trưng đã được cải tiến:

@RestController
@RequestMapping("/api/roles")
public class RoleManagementController {

    @Autowired
    private RoleService roleService;

    @GetMapping("/{roleId}")
    public ResponseEntity<ApiResponse> fetchRoleDetails(@PathVariable Long roleId) {
        return ResponseEntity.ok(ApiResponse.success(roleService.retrieveById(roleId)));
    }

    @PostMapping
    public ResponseEntity<ApiResponse> createNewRole(@Valid @RequestBody RoleForm form) {
        return ResponseEntity.ok(ApiResponse.success(roleService.create(form)));
    }

    @PatchMapping
    public ResponseEntity<ApiResponse> modifyRole(@Valid @RequestBody RoleForm form) {
        return ResponseEntity.ok(ApiResponse.success(roleService.update(form)));
    }

    @DeleteMapping("/batch")
    public ResponseEntity<ApiResponse> removeRoles(@RequestBody Long[] roleIds) {
        roleService.deleteBatch(roleIds);
        return ResponseEntity.ok(ApiResponse.success("Xóa thành công"));
    }

    @PostMapping("/{roleId}/permissions")
    public ResponseEntity<ApiResponse> assignPermissions(
            @PathVariable Long roleId,
            @RequestBody Set<Long> permissionIds) {
        roleService.bindPermissions(roleId, permissionIds);
        return ResponseEntity.ok(ApiResponse.success("Phân quyền hoàn tất"));
    }
}
@RestController
@RequestMapping("/api/sessions")
public class ScreeningController {

    @Autowired
    private ScreeningService screeningService;

    @GetMapping
    public ResponseEntity<ApiResponse> listScreenings(@ModelAttribute ScreeningQuery query) {
        Pageable pageable = PageRequest.of(
                query.getPage() - 1,
                query.getSize(),
                Sort.by(query.getSortField()).descending()
        );
        return ResponseEntity.ok(ApiResponse.success(screeningService.search(query, pageable)));
    }

    @PostMapping
    public ResponseEntity<ApiResponse> scheduleNewScreening(@Valid @RequestBody ScreeningDto dto) {
        Screening saved = screeningService.schedule(dto);
        return ResponseEntity.status(HttpStatus.CREATED)
                .body(ApiResponse.success(saved));
    }

    @PutMapping("/{id}")
    public ResponseEntity<ApiResponse> updateScreening(
            @PathVariable Long id,
            @Valid @RequestBody ScreeningDto dto) {
        dto.setId(id);
        return ResponseEntity.ok(ApiResponse.success(screeningService.update(dto)));
    }
}
@RestController
@RequestMapping("/api/upload")
public class MediaUploadController {

    private static final Map<String, String> UPLOAD_PATHS = Map.of(
            "avatar", "static/uploads/users/",
            "poster", "static/uploads/movies/",
            "theater", "static/uploads/theaters/",
            "actor", "static/uploads/actors/"
    );

    @PostMapping("/{type}")
    public ResponseEntity<ApiResponse> uploadMedia(
            @PathVariable String type,
            @RequestParam MultipartFile file) throws IOException {

        if (!UPLOAD_PATHS.containsKey(type)) {
            throw new IllegalArgumentException("Loại tệp không được hỗ trợ: " + type);
        }

        String baseDir = ResourceUtils.getFile("classpath:").getAbsolutePath() +
                File.separator + UPLOAD_PATHS.get(type);

        Path uploadPath = Paths.get(baseDir);
        if (!Files.exists(uploadPath)) {
            Files.createDirectories(uploadPath);
        }

        String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
        Path targetPath = uploadPath.resolve(fileName);
        file.transferTo(targetPath);

        String publicUrl = "/uploads/" + type + "/" + fileName;
        return ResponseEntity.ok(ApiResponse.success(Map.of("url", publicUrl)));
    }
}

Hệ thống áp dụng xác thực JWT để kiểm soát truy cập, tích hợp cơ chế CORS toàn cục nhằm đảm bảo tương thích khi triển khai tách biệt frontend/backend. Các thao tác thanh toán và hủy vé được xử lý đồng bộ với trạng thái chỗ ngồi trong từng suất chiếu, đảm bảo tính nhất quán dữ liệu thời gian thực.

Thẻ: spring-boot vuejs mysql JWT element-plus

Đăng vào ngày 4 tháng 7 lúc 05:42