Thiết kế mã nguồn Java cho nền tảng quản lý đăng ký thi đấu bida

Dưới đây là giải pháp thiết kế mã nguồn cho nền tảng quản lý đăng ký thi đấu bida dựa trên Java + Spring Boot, bao gồm các module chức năng cốt lõi, thiết kế cơ sở dữ liệu, triển khai mã quan trọng và lựa chọn công nghệ, phù hợp cho các kịch bản quản lý giải đấu quy mô vừa và nhỏ.

I. Thiết kế kiến trúc hệ thống

1. Công nghệ sử dụng
  • Framework phía sau: Spring Boot 3.0 + Spring Security 6.0 (kiểm soát quyền truy cập)
  • Cơ sở dữ liệu: MySQL 8.0 (replication chủ-slave) + Redis 7.0 (cache session/mã xác thực)
  • Hàng đợi tin nhắn: RocketMQ 5.1 (xử lý bất đồng bộ gửi tin nhắn SMS)
  • Phía trước: Vue 3 + Element Plus (trang quản trị) + UniApp (mobile H5)
  • Triển khai: Docker + Kubernetes (cluster high-availability)
2. Phân chia module
Module Mô tả chức năng
**Module người dùng** Đăng ký/đăng nhập, quản lý thông tin cá nhân, đa vai trò (vận động viên/trọng tài/quản trị viên)
**Module giải đấu** Tạo giải đấu, cấu hình quy tắc, bốc thăm chia bảng, tạo lịch thi đấu, nhập kết quả
**Module đăng ký** Đăng ký thi đấu, kiểm tra điều kiện, thanh toán phí, danh sách chờ
**Module thông báo** Gửi tin nhắn SMS/email/thông báo nội bộ (kết quả đăng ký, thay đổi lịch, công bố kết quả)
**Module thống kê** Thống kê dữ liệu tham gia, phân tích tỷ lệ thắng, bảng xếp hạng giải đấu

II. Triển khai chức năng cốt lõi

1. Thiết kế cơ sở dữ liệu (các bảng quan trọng)

sql

`-- Bảng người dùngCREATE TABLE `member` (  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,  `username` VARCHAR(50) NOT NULL UNIQUE,  `password` VARCHAR(100) NOT NULL, -- Mã hóa BCrypt  `role` ENUM('PLAYER', 'REFEREE', 'ADMIN') DEFAULT 'PLAYER',  `phone` VARCHAR(20),  `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP);-- Bảng giải đấuCREATE TABLE `tournament` (  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,  `name` VARCHAR(100) NOT NULL,  `type` ENUM('SINGLE', 'DOUBLE', 'TEAM') NOT NULL, -- Đơn/Đôi/Đội  `start_date` DATETIME NOT NULL,  `end_date` DATETIME NOT NULL,  `max_participants` INT DEFAULT 64,  `status` ENUM('DRAFT', 'PUBLISHED', 'CANCELLED', 'FINISHED') DEFAULT 'DRAFT');-- Bảng đăng kýCREATE TABLE `enrollment` (  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,  `member_id` BIGINT NOT NULL,  `tournament_id` BIGINT NOT NULL,  `status` ENUM('PENDING', 'APPROVED', 'REJECTED', 'PAID', 'CANCELLED') DEFAULT 'PENDING',  `fee_amount` DECIMAL(10,2) DEFAULT 0,  `registered_at` DATETIME DEFAULT CURRENT_TIMESTAMP,  FOREIGN KEY (`member_id`) REFERENCES `member`(`id`),  FOREIGN KEY (`tournament_id`) REFERENCES `tournament`(`id`));`
2. Triển khai mã quan trọng
(1) Tạo giải đấu mới (RESTful API)

java

`@RestController@RequestMapping("/api/tournaments")public class TournamentController {    @Autowired    private TournamentManagementService tournamentService;    @PostMapping    @PreAuthorize("hasRole('ADMIN')")    public ResponseEntity<ApiResponse> createTournament(@Valid @RequestBody TournamentRequest request) {        Tournament tournament = tournamentService.createNewTournament(request);        return ResponseEntity.ok(ApiResponse.success("Tạo giải đấu thành công", tournament));    }}@Servicepublic class TournamentManagementService {    @Transactional    public Tournament createNewTournament(TournamentRequest request) {        Tournament tournament = new Tournament();        BeanUtils.copyProperties(request, tournament);        tournament.setStatus(TournamentStatus.DRAFT);        return tournamentRepository.save(tournament);    }}`
(2) Đăng ký thi đấu của vận động viên (chống gửi trùng lặp + khóa phân tán)

java

`@Servicepublic class EnrollmentService {    @Autowired    private RedissonClient redisClient;    @Transactional    public ApiResponse enrollPlayer(Long memberId, Long tournamentId) {        String lockKey = "enroll:" + tournamentId + ":" + memberId;        RLock lock = redisClient.getLock(lockKey);        try {            if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {                // Kiểm tra đã đăng ký chưa                if (enrollmentRepository.existsByMemberIdAndTournamentId(memberId, tournamentId)) {                    return ApiResponse.error("Bạn đã đăng ký giải đấu này");                }                // Kiểm tra giải đấu có nhận đăng ký không                Tournament tournament = tournamentRepository.findById(tournamentId)                    .orElseThrow(() -> new RuntimeException("Giải đấu không tồn tại"));                if (!TournamentStatus.PUBLISHED.equals(tournament.getStatus())) {                    return ApiResponse.error("Giải đấu chưa mở đăng ký");                }                // Tạo bản ghi đăng ký                Enrollment enrollment = new Enrollment();                enrollment.setMemberId(memberId);                enrollment.setTournamentId(tournamentId);                enrollment.setStatus(EnrollmentStatus.PENDING);                enrollmentRepository.save(enrollment);                return ApiResponse.success("Đăng ký thành công, vui lòng chờ duyệt");            }        } finally {            lock.unlock();        }        return ApiResponse.error("Hệ thống đang bận, vui lòng thử lại sau");    }}`
(3) Thuật toán tạo lịch thi đấu (bốc thăm chia bảng)

java

`@Servicepublic class ScheduleService {    public List<MatchGame> generateMatchSchedule(List<Participant> participants, int groupCount) {        // 1. Xáo trộn ngẫu nhiên        Collections.shuffle(participants);        int participantsPerGroup = participants.size() / groupCount;        List<List<Participant>> groupedList = new ArrayList<>();        for (int i = 0; i < groupCount; i++) {            int startIndex = i * participantsPerGroup;            int endIndex = (i == groupCount - 1) ? participants.size() : startIndex + participantsPerGroup;            groupedList.add(participants.subList(startIndex, endIndex));        }        // 2. Đấu vòng tròn trong từng bảng (tạo cặp đấu)        List<MatchGame> matchGames = new ArrayList<>();        for (List<Participant> group : groupedList) {            for (int i = 0; i < group.size(); i++) {                for (int j = i + 1; j < group.size(); j++) {                    matchGames.add(new MatchGame(group.get(i), group.get(j)));                }            }        }        return matchGames;    }}`

III. Thiết kế high-concurrency và bảo mật

1. Kiểm soát concurrency
  • Redis distributed lock: Chống đăng ký trùng lặp, overselling (giới hạn số lượng đăng ký giải đấu).
  • Optimistic lock: Thêm trường version trong bảng đăng ký, giải quyết xung đột duyệt đồng thời.
  • Xử lý bất đồng bộ: Gửi tin nhắn SMS, thống kê kết quả và các tác vụ không real-time được xử lý qua RocketMQ.
2. Bảo vệ bảo mật
  • Phòng chống SQL injection: Sử dụng JPA/Hibernate tự động escape.
  • Phòng chống XSS: Phía trước sử dụng DOMPurify lọc, phía sau escape các tham số đầu vào.
  • Phòng chống CSRF: Spring Security tự động tạo XSRF-TOKEN.
  • Ẩn dữ liệu nhạy cảm: Số điện thoại hiển thị dạng 138****1234.

IV. Triển khai và mở rộng

1. Triển khai Docker

dockerfile

`# Dịch vụ phía sauFROM openjdk:17-jdk-alpineVOLUME /tmpARG JAR_FILE=target/billiard-tournament.jarCOPY ${JAR_FILE} app.jarENTRYPOINT ["java","-jar","/app.jar"]# Khởi tạo database (tùy chọn)# COPY docker-entrypoint-initdb.d /docker-entrypoint-initdb.d`
2. Thiết kế mở rộng
  • Hỗ trợ multi-tenant: Cách ly dữ liệu giải đấu khác nhau qua Schema.
  • Plugin rule engine: Hỗ trợ quy tắc giải đấu tùy chỉnh (như chấp điểm, thắng bao nhiêu ván).
  • Đa ngôn ngữ: Phía trước và sau hỗ trợ chuyển đổi tiếng Anh/trung việt.

Thẻ: spring-boot Java mysql Redis rest-api

Đăng vào ngày 18 tháng 7 lúc 16:52