Hệ thống Quản lý Hoạt động Câu lạc bộ Sinh viên trên nền tảng WeChat với Spring Boot 3, Vue 3 và MyBatis

Trong bối cảnh chuyển đổi số ngày càng sâu rộng tại các cơ sở giáo dục, việc quản lý hoạt động câu lạc bộ sinh viên đang dịch chuyển mạnh mẽ từ phương thức thủ công sang mô hình kỹ thuật số dựa trên nền tảng di động. Với sự gia tăng nhanh chóng về số lượng câu lạc bộ và tính đa dạng trong hình thức tổ chức sự kiện, các giải pháp quản lý truyền thống bộc lộ nhiều hạn chế — đặc biệt ở khâu đăng ký tham gia, theo dõi thành viên, thông báo sự kiện và kiểm soát điểm danh.

Hệ thống được thiết kế nhằm tận dụng tiềm năng của WeChat — nền tảng nhắn tin và dịch vụ tích hợp phổ biến nhất tại Trung Quốc — để xây dựng một hệ sinh thái quản lý liền mạch, dễ tiếp cận và có khả năng mở rộng. Người dùng tương tác chủ yếu qua giao diện WeChat Mini Program (hoặc webview tích hợp), trong khi toàn bộ logic nghiệp vụ và lưu trữ dữ liệu được xử lý bởi một kiến trúc phân tầng hiện đại.

Hệ thống áp dụng mô hình phân tách frontend – backend, trong đó:

  • Frontend được phát triển bằng Vue 3 cùng Composition API và hỗ trợ đầy đủ TypeScript, sử dụng thư viện UI hiện đại như Element Plus để đảm bảo trải nghiệm người dùng mượt mà trên mọi thiết bị.
  • Backend được xây dựng trên nền tảng Spring Boot 3.x, tích hợp Spring Security cho xác thực, Spring Validation cho kiểm tra đầu vào, và MyBatis-Plus 3.5+ để tối ưu hóa thao tác với cơ sở dữ liệu.
  • Cơ sở dữ liệu sử dụng MySQL 8.0 với thiết kế chuẩn hóa, hỗ trợ ràng buộc khóa ngoại và chỉ mục tối ưu cho các truy vấn thường xuyên như tìm kiếm sự kiện theo thời gian, đếm số người đăng ký hay phân tích trạng thái tham gia.

Các chức năng cốt lõi bao gồm: đăng tải và quản lý sự kiện câu lạc bộ, quản lý hồ sơ thành viên (kèm phân quyền vai trò), đăng ký – hủy đăng ký – điểm danh trực tuyến, gửi thông báo tức thì qua WeChat Official Account hoặc Mini Program, và tổng quan thống kê hoạt động theo thời gian thực.

Bảng dữ liệu chính

Bảng activity — Lưu trữ thông tin sự kiện

Tên cột Kiểu dữ liệu Mô tả
id BIGINT UNSIGNED PK Khóa chính tự tăng
title VARCHAR(64) Tiêu đề sự kiện
description TEXT Mô tả chi tiết nội dung
scheduled_at DATETIME Thời gian dự kiến bắt đầu
venue VARCHAR(128) Địa điểm tổ chức
capacity SMALLINT UNSIGNED Số lượng chỗ tối đa
creator_openid VARCHAR(64) Mã định danh WeChat của người tạo
created_at DATETIME DEFAULT CURRENT_TIMESTAMP Thời điểm tạo bản ghi
status TINYINT DEFAULT 0 Trạng thái: 0 = Chưa diễn ra, 1 = Đang diễn ra, 2 = Đã kết thúc

Bảng member — Hồ sơ thành viên câu lạc bộ

Tên cột Kiểu dữ liệu Mô tả
id BIGINT UNSIGNED PK Khóa chính tự tăng
openid VARCHAR(64) UNIQUE Mã định danh duy nhất từ WeChat
full_name VARCHAR(32) Họ và tên đầy đủ
student_id VARCHAR(16) UNIQUE Mã sinh viên
joined_at DATETIME DEFAULT CURRENT_TIMESTAMP Thời điểm gia nhập câu lạc bộ
role_level TINYINT DEFAULT 0 Vai trò: 0 = Thành viên thường, 1 = Quản trị viên câu lạc bộ
club_id BIGINT UNSIGNED Khóa ngoại tham chiếu đến bảng club

Bảng registration — Ghi nhận đăng ký sự kiện

Tên cột Kiểu dữ liệu Mô tả
id BIGINT UNSIGNED PK Khóa chính tự tăng
activity_id BIGINT UNSIGNED Khóa ngoại tới activity.id
member_id BIGINT UNSIGNED Khóa ngoại tới member.id
registered_at DATETIME DEFAULT CURRENT_TIMESTAMP Thời điểm đăng ký
checkin_time DATETIME NULL Thời điểm điểm danh (NULL nếu chưa điểm danh)
feedback_text TEXT NULL Phản hồi sau sự kiện (tùy chọn)

Mẫu mã nguồn tiêu biểu

1. Lớp khởi tạo ứng dụng Spring Boot

@SpringBootApplication
@MapperScan("com.example.club.mapper")
public class ClubManagementApplication {

    public static void main(String[] args) {
        SpringApplication.run(ClubManagementApplication.class, args);
    }
}

2. Controller quản lý thành viên với xác thực JWT

@RestController
@RequestMapping("/api/v1/members")
@RequiredArgsConstructor
public class MemberController {

    private final MemberService memberService;
    private final JwtTokenProvider tokenProvider;

    @PostMapping("/login")
    public ResponseEntity<ApiResponse<AuthResponse>> login(
            @RequestBody @Valid LoginRequest request,
            HttpServletRequest httpServletRequest) {

        MemberEntity authenticated = memberService.authenticate(request.getOpenid(), request.getPassword());
        String jwtToken = tokenProvider.generateToken(authenticated);

        return ResponseEntity.ok(ApiResponse.success(new AuthResponse(jwtToken)));
    }

    @GetMapping("/profile")
    @PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
    public ResponseEntity<ApiResponse<MemberProfileDto>> getProfile(
            @AuthenticationPrincipal JwtUserDetails userDetails) {

        MemberProfileDto profile = memberService.fetchProfile(userDetails.getOpenid());
        return ResponseEntity.ok(ApiResponse.success(profile));
    }

    @PutMapping("/update")
    @PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
    public ResponseEntity<ApiResponse<Void>> updateProfile(
            @AuthenticationPrincipal JwtUserDetails userDetails,
            @RequestBody @Valid MemberUpdateDto updateDto) {

        memberService.updateProfile(userDetails.getOpenid(), updateDto);
        return ResponseEntity.ok(ApiResponse.success(null));
    }
}

3. Giao diện Vue 3 sử dụng Composition API để hiển thị danh sách sự kiện

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { fetchActivities } from '@/api/activity';

const activities = ref<ActivityItem[]>([]);
const loading = ref(true);

onMounted(async () => {
  try {
    const data = await fetchActivities({ status: 'upcoming', limit: 6 });
    activities.value = data;
  } finally {
    loading.value = false;
  }
});
</script>

<template>
  <div class="activity-list">
    <h2>Sự kiện sắp tới</h2>
    <el-skeleton v-if="loading" :rows="3" animated />
    <div v-else class="grid-container">
      <ActivityCard 
        v-for="item in activities" 
        :key="item.id" 
        :activity="item" 
      />
    </div>
  </div>
</template>

Thẻ: spring-boot Vue3 mybatis mysql WeChat-Mini-Program

Đăng vào ngày 4 tháng 6 lúc 05:39