Triển Khai Cổng Thông Tin Đào Tạo Đại Học Tích Hợp Spring Boot Và Vue.js

Kiến Trúc Tổng Thể Của Hệ Thống

Hệ thống cổng thông tin học thuật được kiến trúc hóa theo mô hình microservices-lite, tách biệt rõ ràng giữa lớp hiển thị và lớp xử lý nghiệp vụ. Frontend vận hành trên Vue.js 3 kết hợp Vite build tool để tối ưu cold start time và tree-shaking, trong khi backend sử dụng Spring Boot 3.x phục vụ RESTful API. Cơ sở dữ liệu quan hệ MySQL lưu trữ cấu hình hệ thống, hồ sơ giảng viên, danh mục học thuật cùng các bản ghi nghiệp vụ động. Quy trình xác thực và phân quyền tuân theo mô hình RBAC (Role-Based Access Control), cho phép kiểm soát chi tiết đến từng thao tác giao diện (button-level ACL), đáp ứng yêu cầu bảo mật cao cho môi trường giáo dục.

Cấu Trúc Module Nghiệp Vụ Trọng Tâm

  • Mô-đun Tổ Chức Học Thuật: Quản lý phân cấp các khoa/phòng ban, hiển thị thông tin liên lạc đa kênh và chân dung chuyên gia theo mảng nghiên cứu. Dữ liệu được cache ngắn hạn qua Redis nhằm giảm tải truy vấn lặp lại.
  • Module Đăng Ký Cuộc Thi Chuyên Ngành: Hệ thống thu thập hồ sơ trực tuyến tích hợp cổng thanh toán third-party. Backend thực hiện sanitize đầu vào, validate định dạng email và hash dữ liệu nhận dạng cá nhân (PII) trước khi write-down storage.
  • Nền Tảng Quản Lý Giảng Dạy: Trung tâm hóa việc phân phối giáo trình, kho ngân hàng câu hỏi trắc nghiệm và chấm thi tự động. Dashboard báo cáo giúp ban giám đốc học thuật phát hiện điểm nghẽn trong quy trình sư phạm và điều chỉnh curriculum kịp thời.
  • Kênh Kết Nối Tuyển Sinh – Việc Làm: Sync dữ liệu job requisition từ doanh nghiệp đối tác với profiles sinh viên tốt nghiệp. Trigger automated match algorithm để push notification khi profile người dùng khớp tiêu chí nhà tuyển dụng.
  • Hệ Thống Thông Báo Time-To-Live: Sử dụng Server-Sent Events (SSE) fallback WebSocket để broadcast alert khẩn cấp (lịch học thay đổi, cảnh báo an ninh) xuống client, không phụ thuộc vào polling cycle truyền thống.

Thiết Kế Sơ Đồ Database & Mô Hình Đối Tượng

Schema được thiết kế tuân thủ chuẩn Normal Form 3, đảm bảo toàn vẹn tham chiếu (Foreign Key Constraints) và tránh redundancy:

  • tbl_academic_units: Metadata khoa, mã số phòng ban, địa chỉ vật lý/trực tuyến, flag trạng thái kích hoạt.
  • tbl_party_building_posts: Nội dung tuyên truyền, danh mục duyệt bởi admin/editor, metadata publish date.
  • tbl_competition_entries: Hồ sơ dự thi, mapping tới bảng sys_users, bao gồm payment_status và submission_hash.
  • tbl_learning_resources: Tài nguyên số phân loại theo học phần, hỗ trợ full-text index tìm kiếm.
  • tbl_recruitment_campaigns: Tin tuyển sinh và thông báo việc làm, correlation_id liên kết doanh nghiệp.
  • tbl_realtime_alerts: Event log hệ thống, partitioned theo alert_priority và valid_until để cleanup tự động.

Triển Khai Xử Lý API & Business Logic Backend

Đoạn mã dưới đây thể hiện phương pháp tiếp cận lập trình hướng interface, áp dụng Builder Pattern cho filter clause và Transaction Management để đảm bảo atomicity khi persist dữ liệu.

Truy Vấn Danh Mục Khoa Cục Bộ Phân Trang

@RestController
@RequestMapping("/api/departments")
public class DepartmentController {

    private final IDepartmentService deptService;

    public DepartmentController(IDepartmentService deptService) {
        this.deptService = deptService;
    }

    @GetMapping("/list")
    public ResultMessage<IPage<DepartmentDTO>> fetchPaginated(
            @RequestParam(required = false) String searchKeyword,
            @ModelAttribute PageParamDTO param) {
        
        LambdaQueryWrapper<Department> queryBuilder = Wrappers.lambdaQuery(Department.class)
                .like(StringUtils.hasText(searchKeyword), Department::getTitle, searchKeyword)
                .eq(Department::getStatus, "ACTIVE");

        Page<Department> mpPage = PageUtil.buildMPPage(param);
        IPage<Department> pageResult = deptService.page(mpPage, queryBuilder);
        return ResultMessage.success(DepartmentConverter.toPageDTO(pageResult));
    }
}

Ghi Nhận Record Đăng Ký Cuộc Thi Với Auto-Ordering

@PostMapping("/competitions/register")
@Transactional(rollbackFor = DataAccessException.class)
public ResultMessage<CompetitionEntryDTO> submitEntry(@Validated @RequestBody EntrySubmissionDTO dto, Authentication currentAuth) {
    AppPrincipal user = SecurityContextHolder.extract(currentAuth);
    
    CompetitionEntry entity = new CompetitionEntry();
    entity.setParticipantName(user.getDisplayNickname());
    entity.setEmail(user.getEmail());
    entity.setStatus(EntryStatus.WAITING_CONFIRMATION);
    
    // Cập nhật thứ tự hiển thị dựa trên epoch timestamp thay vì COUNT(*)
    entity.setSortOrder(System.currentTimeMillis() / 1000L);
    
    CompetitionEntry persisted = entryRepository.save(entity);
    return ResultMessage.success(EntryConverter.mapToDTO(persisted));
}

Xử Lý Verify Thông Tin Xác Thực Frontend

@PostMapping("/auth/web-login")
public ResponseEntity<TokenResponse> performWebLogin(
        @RequestParam String inputUsername, 
        @RequestParam String inputPassword) {
    
    Optional<SysUser> userOpt = userRepository.findByAccount(inputUsername);
    if (userOpt.isEmpty()) {
        throw new AuthenticationRejectedException("Tài khoản không thuộc danh sách hệ thống.");
    }

    SysUser validUser = userOpt.get();
    PasswordEncoder bCryptEncoder = new BCryptPasswordEncoder();
    if (!bCryptEncoder.matches(inputPassword, validUser.getPasswordHash())) {
        throw new AuthenticationRejectedException("Chuỗi xác thực không trùng khớp.");
    }

    // Generate JWT Payload & Bind Security Context
    String signedToken = JwtTokenFactory.createSignedToken(validUser.getAccount(), validUser.getRoles());
    UsernamePasswordAuthenticationToken securityObj = new UsernamePasswordAuthenticationToken(
        PrincipalWrapper.wrap(validUser), null, validUser.getGrantedAuthority()
    );
    SecurityContextHolder.getContext().setAuthentication(securityObj);

    TokenResponse payload = new TokenResponse(signedToken, validUser.getUserId(), Duration.ofHours(8));
    return ResponseEntity.ok(payload);
}

Thẻ: spring-boot MyBatis-Plus vuejs3 mysql rbac-jwt

Đăng vào ngày 2 tháng 10 lúc 03:29