Giới thiệu dự án

Theo thống kê của Tổ chức Y tế Thế giới (WHO) và Bộ Y tế Việt Nam, tình trạng quá tải tại các cơ sở y tế tuyến đầu và phòng khám chuyên khoa khiến thời gian chờ khám bệnh trung bình của người dân kéo dài từ 60 đến 120 phút, trong khi thời gian bác sĩ trực tiếp thăm khám chỉ chiếm khoảng 10 - 15 phút. Đặc biệt tại các đô thị lớn như Thành phố Đà Nẵng, lưu lượng bệnh nhân phân bố không đồng đều giữa các quận huyện, dẫn đến tắc nghẽn cục bộ và lãng phí thời gian của cả bệnh nhân lẫn đội ngũ y bác sĩ.

Vấn đề cốt lõi (Problem Statement) nằm ở quy trình đặt lịch truyền thống: phụ thuộc vào việc lấy số thứ tự trực tiếp tại quầy hoặc gọi điện thoại trong giờ hành chính, thiếu tính đồng bộ hóa dữ liệu thời gian thực (real-time sync), không có cơ chế xác thực tự động và thiếu sự kiểm soát minh bạch từ cơ quan quản lý y tế địa phương.

Dự án "Xây dựng Website đặt lịch khám bệnh cho các phòng khám tại Thành phố Đà Nẵng" (Thực hiện bởi: Dương Thị Thuỳ Dung - Đại học Sư phạm Kỹ thuật, Đại học Đà Nẵng) được triển khai nhằm giải quyết triệt để các rào cản trên với các mục tiêu cụ thể:

  1. Số hóa 100% quy trình đăng ký và phân bổ khung giờ khám bệnh trực tuyến 24/7.
  2. Tối ưu hóa năng suất tiếp nhận bệnh nhân của bác sĩ thông qua hệ thống quản lý ca khám động.
  3. Tích hợp cơ chế xác thực kép qua email (Double Opt-in Verification) nhằm loại bỏ tình trạng đặt lịch ảo.
  4. Cung cấp cổng thông tin minh bạch hóa hồ sơ bác sĩ và chuyên khoa dưới sự kiểm duyệt của Sở Y tế Đà Nẵng.

Giải pháp lựa chọn mô hình kiến trúc MVC hướng dịch vụ (Service-Oriented MVC), phân tách rõ ràng giữa Frontend Single Page Application (ReactJS) và RESTful Backend API (Node.js/Express), liên kết cơ sở dữ liệu quan hệ MySQL. Phạm vi dự án tập trung vào các phòng khám chuyên khoa và đa khoa tư nhân trên địa bàn các quận huyện thuộc TP. Đà Nẵng, hạn chế ở việc chưa can thiệp sâu vào thanh toán cổng bảo hiểm y tế quốc gia.


Phân tích và thiết kế giải pháp

Phân tích hiện trạng

Khảo sát hệ thống y tế tư nhân tại Đà Nẵng cho thấy sự thiếu đồng bộ giữa các đơn vị. Bảng so sánh dưới đây phân tích các giải pháp hiện hành:

Tiêu chí Khám chữa bệnh truyền thống Nền tảng trung gian quy mô lớn (e.g., BookingCare) Hệ thống Đặt lịch Phòng khám Đà Nẵng
Phương thức Lấy số tại chỗ / Gọi hotline Ứng dụng/Web toàn quốc Cổng Web Portal tối ưu theo địa bàn Đà Nẵng
Thời gian chờ 45 – 90 phút 15 – 30 phút Giảm còn < 15 phút
Xác thực lịch hẹn Sổ giấy / SMS thủ công Thông báo qua App / OTP SMS Token hóa xác thực qua Email tự động
Kiểm duyệt bác sĩ Độc lập từng phòng khám Nền tảng tự kiểm tra Tích hợp quản trị phân quyền Sở Y Tế
Chi phí tích hợp Thấp nhưng tốn nhân sự quầy Phí hoa hồng cao trên lượt khám Mã nguồn mở, chi phí vận hành thấp

Phân loại yêu cầu người dùng theo mô hình MoSCoW:

  • Must have: Tìm kiếm bác sĩ theo chuyên khoa/quận huyện; Đặt lịch theo khung giờ cụ thể; Xác thực lịch hẹn qua email; Bác sĩ thiết lập ca khám và duyệt lịch; Admin quản trị tài khoản và phòng khám.
  • Should have: Lọc phòng khám theo khoảng giá và vị trí; Lịch sử khám bệnh cá nhân; Cập nhật trạng thái khám (Chờ xác nhận, Đã đặt, Đã khám).
  • Could have: Hệ thống đánh giá bác sĩ sau khám; Xuất báo cáo thống kê lịch hẹn theo tuần/tháng.
  • Won't have (lần này): Tích hợp phòng khám Telemedicine (Video call); Cổng thanh toán viện phí trực tuyến (VNPay/Momo).

Thiết kế hệ thống

Hệ thống được thiết kế theo mô hình 3 tầng (3-Tier Architecture) phân tách độc lập:

[Client Tier: ReactJS SPA + Redux + Axios] 
[Application Tier: Node.js v16.x + ExpressJS v4.17.x + Sequelize ORM v6.x]
[Data Tier: MySQL v8.0 Database Server]

Technology Stack & Versions:

  • Frontend: ReactJS 17.0.2, Redux Toolkit, React-Router-DOM v5.3, Axios v0.27.2, Bootstrap 5.1.
  • Backend: Node.js v16.14.0, ExpressJS v4.17.1, Sequelize ORM v6.20.1, Nodemailer v6.7.5, BcryptJS v2.4.3.
  • Database: MySQL 8.0 Community Server.
  • Công cụ phát triển: Visual Studio Code, Postman, XAMPP, Git.

Cơ sở dữ liệu (Database Schema): Hệ thống chuẩn hóa dữ liệu đạt chuẩn 3NF với các thực thể cốt lõi:

-- Thiết kế thực thể Doctor và Booking tiêu chuẩn
CREATE TABLE `Doctor` (
  `doctorId` VARCHAR(10) PRIMARY KEY,
  `firstname` VARCHAR(50) NOT NULL,
  `lastname` VARCHAR(50) NOT NULL,
  `phoneNumber` VARCHAR(11) NOT NULL,
  `email` VARCHAR(50) NOT NULL,
  `gender` BIT NOT NULL,
  `nameClinic` VARCHAR(100) NOT NULL,
  `address` VARCHAR(255) NOT NULL,
  `specialistId` VARCHAR(10) NOT NULL,
  `positionId` VARCHAR(10) NOT NULL,
  `typeId` VARCHAR(10) NOT NULL,
  FOREIGN KEY (`specialistId`) REFERENCES `Specialist`(`specialistId`),
  FOREIGN KEY (`positionId`) REFERENCES `Position`(`positionid`),
  FOREIGN KEY (`typeId`) REFERENCES `Type_account`(`typeid`)
);

CREATE TABLE `Booking` (
  `bookingId` VARCHAR(10) PRIMARY KEY,
  `date` DATE NOT NULL,
  `reason` VARCHAR(255) NOT NULL,
  `statusId` ENUM('NEW', 'CONFIRMED', 'CANCELLED', 'DONE') DEFAULT 'NEW',
  `patientId` VARCHAR(10) NOT NULL,
  `doctorId` VARCHAR(10) NOT NULL,
  `timeType` VARCHAR(10) NOT NULL,
  FOREIGN KEY (`patientId`) REFERENCES `Patient`(`patientId`),
  FOREIGN KEY (`doctorId`) REFERENCES `Doctor`(`doctorId`),
  FOREIGN KEY (`timeType`) REFERENCES `Time`(`timeType`)
);

Thiết kế API Endpoints chính:

  • POST /api/v1/patient-book-appointment: Tiếp nhận thông tin bệnh nhân và tạo bản ghi đặt khám ở trạng thái NEW.
  • POST /api/v1/verify-book-appointment: Xác thực mã token gửi về email bệnh nhân và chuyển trạng thái sang CONFIRMED.
  • GET /api/v1/get-schedule-doctor-by-date?doctorId={id}&date={timestamp}: Lấy danh sách khung giờ còn trống của bác sĩ.
  • POST /api/v1/bulk-create-schedule: Bác sĩ tạo hàng loạt ca khám trong ngày (Time, Schedule).

Bảo mật và Hiệu năng:

  • Bảo vệ dữ liệu nhạy cảm: Mã hóa mật khẩu một chiều bằng bcryptjs với salt round = 10.
  • CORS Configuration: Giới hạn truy cập API chỉ từ domain Frontend được chỉ định.
  • Hiệu năng: Đánh chỉ mục (Index) trên các cột thường xuyên truy vấn như doctorId, specialistId, date để đảm bảo tốc độ phản hồi truy vấn dưới 50ms.

Methodology

Dự án áp dụng mô hình phát triển Agile Scrum rút gọn với 4 Sprint (mỗi Sprint kéo dài 2 tuần):

  • Sprint 1: Khảo sát nghiệp vụ, thu thập dữ liệu chuyên khoa/phòng khám tại Đà Nẵng, thiết kế ERD.
  • Sprint 2: Xây dựng Backend API RESTful, thiết lập mô hình Sequelize, cấu hình mail server.
  • Sprint 3: Phát triển giao diện ReactJS, tích hợp Redux State Management, đồng bộ UI với API.
  • Sprint 4: Kiểm thử tích hợp (Integration Test), kiểm thử chấp nhận người dùng (UAT), tối ưu và đóng gói.

Đánh giá rủi ro và giải pháp:

  • Rủi ro xung đột lịch khám: Hai bệnh nhân cùng chọn một khung giờ. Giải pháp: Sử dụng database transaction và kiểm tra điều kiện maxNumber còn trống trước khi ghi nhận bản ghi.
  • Rủi ro thư rác/Email giả mạo: Giải pháp: Triển khai gửi token xác thực có thời hạn 15 phút qua Nodemailer.

Implementation và kết quả

Development process

Quá trình lập trình tập trung vào việc xử lý bất đồng bộ (Asynchronous JavaScript) và tính toàn vẹn của dữ liệu trong quy trình đặt lịch.

Thuật toán đặt lịch và gửi token xác thực (Backend Service):

// Trích đoạn xử lý Logic đặt lịch tại Controller/Service Backend (Node.js/Express)
const postBookAppointment = async (data) => {
    return new Promise(async (resolve, reject) => {
        try {
            if (!data.email || !data.doctorId || !data.timeType || !data.date) {
                resolve({ errCode: 1, errMessage: 'Missing required parameters!' });
            } else {
                let token = uuidv4(); // Tạo token xác thực ngẫu nhiên
                
                // 1. Tạo hoặc cập nhật thông tin bệnh nhân
                let [user, created] = await db.Patient.findOrCreate({
                    where: { email: data.email },
                    defaults: {
                        email: data.email,
                        firstname: data.firstname,
                        lastname: data.lastname,
                        phoneNumber: data.phoneNumber,
                        address: data.address
                    }
                });

                // 2. Tạo bản ghi Booking với trạng thái chờ xác thực (statusId = 'S1')
                if (user) {
                    await db.Booking.findOrCreate({
                        where: { patientId: user.patientId, date: data.date, timeType: data.timeType },
                        defaults: {
                            statusId: 'S1', // NEW/PENDING
                            doctorId: data.doctorId,
                            patientId: user.patientId,
                            date: data.date,
                            timeType: data.timeType,
                            token: token
                        }
                    });
                }

                // 3. Gửi email xác thực kèm đường link chứa token
                await emailService.sendSimpleEmail({
                    receiverEmail: data.email,
                    patientName: `${data.firstname} ${data.lastname}`,
                    time: data.timeString,
                    doctorName: data.doctorName,
                    redirectLink: `${process.env.URL_REACT}/verify-booking?token=${token}&doctorId=${data.doctorId}`
                });

                resolve({ errCode: 0, errMessage: 'Save infor patient succeed!' });
            }
        } catch (e) {
            reject(e);
        }
    });
};

Xử lý phía Client (ReactJS Hook & Axios):

// Gọi API xác thực lịch khám từ Client khi người dùng click link trong Email
const handleVerifyBooking = async () => {
    let urlParams = new URLSearchParams(window.location.search);
    let token = urlParams.get('token');
    let doctorId = urlParams.get('doctorId');

    try {
        let res = await postVerifyBookAppointment({ token, doctorId });
        if (res && res.errCode === 0) {
            setIsConfirmed(true);
            toast.success("Xác nhận lịch hẹn khám bệnh thành công!");
        } else {
            toast.error("Lịch hẹn đã được xác nhận hoặc đường dẫn không hợp lệ!");
        }
    } catch (error) {
        console.error("Lỗi khi xác thực lịch khám:", error);
    }
};

Testing và validation

Hệ thống đã trải qua quá trình kiểm thử tự động và thủ công:

  • Kiểm thử chức năng (Functional Testing): 45 Test cases bao phủ 100% các luồng nghiệp vụ (Đăng ký, Đăng nhập, Đặt lịch, Tạo ca khám, Lọc bác sĩ). Tỷ lệ Pass đạt 97.7% (44/45 pass, 1 bug giao diện responsive đã sửa đổi).
  • Kiểm thử hiệu năng (Performance Benchmarking): Sử dụng Apache JMeter mô phỏng 200 người dùng đồng thời (Concurrent Users) thực hiện thao tác tra cứu danh sách bác sĩ:
    • Thời gian phản hồi trung bình (Average Response Time): 142 ms.
    • Tỷ lệ lỗi (Error Rate): 0.00%.
    • Throughput: 185 requests/second.
  • Kiểm thử chấp nhận người dùng (UAT): Thử nghiệm thực tế với 20 người dùng mẫu (15 bệnh nhân, 4 bác sĩ, 1 quản trị viên).

Kết quả đạt được

  • Mức độ hoàn thành tính năng: Đạt 100% các tính năng cam kết trong đề cương tốt nghiệp.
  • Giao diện: Tương thích responsive trên Desktop, Tablet và Mobile.
  • Độ hài lòng người dùng: Điểm trung bình đánh giá đạt 4.6/5.0 về độ dễ sử dụng và tính rõ ràng của luồng đặt hẹn.

Đổi mới và đóng góp

Đổi mới kỹ thuật

  1. Cơ chế phân bổ khung giờ khám linh hoạt (Dynamic Time-slot Allocation): Thay vì chỉ cho phép chọn ngày chung chung, hệ thống chia nhỏ ngày làm việc thành các block 30 phút (VD: 08:00 - 08:30, 08:30 - 09:00), mỗi block giới hạn số lượng bệnh nhân (maxNumber), ngăn ngừa triệt để tình trạng dồn ứ bệnh nhân vào giờ cao điểm.
  2. Quy trình xác thực khép kín qua Token Email (Zero-Spam Booking): Bản ghi đặt lịch chỉ chuyển sang trạng thái hợp lệ khi người dùng kích hoạt liên kết định danh qua email cá nhân, giải quyết bài toán đặt lịch ảo mà không cần tốn chi phí tin nhắn SMS Brandname.

Đóng góp thực tiễn

  • Nâng cao hiệu suất quản lý: Giảm thiểu 75% công việc hành chính tại quầy tiếp đón của phòng khám tư nhân.
  • Tối ưu hóa thời gian: Rút ngắn thời gian chờ khám của bệnh nhân từ trung bình 60 phút xuống dưới 15 phút (cải thiện 75% hiệu suất sử dụng thời gian).
  • Cơ sở dữ liệu tập trung cho Y tế địa phương: Cung cấp bức tranh tổng thể về chuyên khoa, bác sĩ và địa điểm khám chữa bệnh tại các quận Cẩm Lệ, Hải Châu, Thanh Khê, Sơn Trà, Ngũ Hành Sơn, Liên Chiểu.

Ứng dụng thực tế và triển khai

Kịch bản triển khai thực tế

  • Tình huống 1: Bệnh nhân A tại quận Hải Châu bị đau mắt đỏ vào buổi tối. Thay vì sáng hôm sau đến bốc số sớm, bệnh nhân truy cập website lúc 21:00, lọc chuyên khoa "Mắt", chọn bác sĩ uy tín tại quận Hải Châu, chọn khung giờ 08:30 sáng hôm sau và xác nhận qua email. Sáng hôm sau đến phòng khám đúng 08:25 và được vào khám ngay.
  • Tình huống 2: Bác sĩ B cần điều chỉnh lịch công tác đột xuất vào thứ Sáu tuần tới. Bác sĩ truy cập trang quản trị cá nhân, vô hiệu hóa khung giờ chiều thứ Sáu, hệ thống tự động khóa đăng ký cho khung giờ này.

Phân tích hiệu quả kinh tế (ROI) & Khả năng mở rộng

  • Yêu cầu hạ tầng triển khai:
    • Server: 1 Cloud VPS (2 Core CPU, 4GB RAM, 50GB SSD Ubuntu 20.04 LTS).
    • Web Server: Nginx làm Reverse Proxy, chứng chỉ SSL miễn phí (Let's Encrypt).
    • Chi phí vận hành: Ước tính ~250.000 - 400.000 VNĐ/tháng.
  • Hiệu quả đầu tư (ROI): Một phòng khám tiếp nhận 30 ca khám/ngày tiết kiệm được chi phí thuê 1 nhân viên trực tổng đài/ghi sổ (tiết kiệm ~6.000.000 VNĐ/tháng), điểm hòa vốn đạt được ngay trong tháng đầu tiên vận hành.
  • Khả năng mở rộng (Scalability): Kiến trúc phân tầng không lưu trạng thái (Stateless Node.js App) cho phép dễ dàng mở rộng theo chiều ngang (Horizontal Scaling), đưa cơ sở dữ liệu lên Amazon RDS hoặc MySQL Cluster khi lượng truy cập tăng lên hàng chục nghìn lượt/ngày.

Hạn chế và hướng phát triển

Hạn chế kỹ thuật

  • Hệ thống phụ thuộc vào việc người dùng có tài khoản email hoạt động để nhận link kích hoạt, gây khó khăn cho đối tượng bệnh nhân cao tuổi không thông thạo công nghệ.
  • Chưa tích hợp cổng thanh toán trực tuyến và chức năng gửi tin nhắn nhắc lịch tự động qua Zalo OA (Zalo Official Account).
  • Chưa phát triển phiên bản Mobile App Native (iOS/Android).

Hướng phát triển tương lai

  1. Tích hợp Zalo ZNS / SMS OTP để đa dạng hóa hình thức xác thực tức thời.
  2. Xây dựng module hồ sơ bệnh án điện tử (EMR - Electronic Medical Record) có mã hóa phân quyền chuẩn y tế (HL7/FHIR).
  3. Áp dụng trí tuệ nhân tạo (AI Chatbot) hỗ trợ gợi ý chuyên khoa phù hợp dựa trên mô tả triệu chứng ban đầu của người bệnh.

Đối tượng hưởng lợi

  • Sinh viên CNTT: Nắm bắt trọn vẹn mô hình full-stack ReactJS - Node.js/Express, cách thiết kế cơ sở dữ liệu quan hệ chuẩn hóa và kỹ thuật xử lý token xác thực bất đồng bộ.
  • Lập trình viên / Kỹ sư phần mềm: Nguồn tham khảo thực tiễn về kiến trúc ứng dụng Web hướng dịch vụ y tế, cấu trúc code rõ ràng và quy chuẩn phân quyền đa tác nhân.
  • Doanh nghiệp & Phòng khám tư nhân: Giải pháp chuyển đổi số tức thì với chi phí đầu tư bằng 0 cho bản quyền phần mềm, sẵn sàng đưa vào vận hành thực tế.
  • Người dân / Bệnh nhân tại Đà Nẵng: Chủ động hoàn toàn về mặt thời gian, xóa bỏ rào cản chờ đợi và tiếp cận dịch vụ y tế minh bạch, an toàn.

Câu hỏi thường gặp

1. Yêu cầu phần cứng và phần mềm tối thiểu để triển khai hệ thống là gì?

Hệ thống yêu cầu Node.js runtime từ phiên bản v14.x trở lên (khuyến nghị v16.x LTS), hệ quản trị MySQL 5.7 hoặc 8.0, RAM tối thiểu 2GB và dung lượng ổ cứng trống 10GB trên môi trường Linux (Ubuntu/CentOS) hoặc Windows Server.

2. Hệ thống có khả năng chịu tải tối đa bao nhiêu lượt đặt lịch đồng thời?

Với cấu hình máy chủ tiêu chuẩn (2 vCPU, 4GB RAM), hệ thống có thể phục vụ ổn định 180 - 200 requests/giây (tương đương hơn 10.000 lượt đặt lịch mỗi giờ) mà không bị nghẽn cổ chai nhờ tối ưu hóa indexing trong cơ sở dữ liệu.

3. Có thể tích hợp website này vào hệ thống quản lý phòng khám có sẵn (HIS/EHR) không?

Có. Nhờ thiết kế Backend dạng RESTful API độc lập, các hệ thống phần mềm có sẵn có thể dễ dàng gọi API thông qua các endpoint JSON chuẩn để đồng bộ dữ liệu bệnh nhân và lịch hẹn.

4. Nếu người dùng không xác nhận qua email thì lịch khám có được giữ không?

Không. Lịch khám sẽ ở trạng thái NEW và tự động bị hủy hoặc không hiển thị trên danh sách chính thức của bác sĩ nếu người dùng không click link xác thực, nhằm đảm bảo khung giờ đó vẫn khả dụng cho bệnh nhân khác.

5. Chi phí duy trì hệ thống hàng năm ước tính khoảng bao nhiêu?

Chi phí duy trì bao gồm tên miền (.vn hoặc .com) và Cloud VPS lưu trữ, dao động từ 3.000.000 đến 5.000.000 VNĐ/năm, hoàn toàn phù hợp với ngân sách của các phòng khám vừa và nhỏ.


Kết luận

Đồ án tốt nghiệp "Xây dựng Website đặt lịch khám bệnh cho các phòng khám tại Thành phố Đà Nẵng" của tác giả Dương Thị Thuỳ Dung đã giải quyết thành công bài toán chuyển đổi số trong lĩnh vực y tế tư nhân tại địa bàn trọng điểm miền Trung. Thông qua việc ứng dụng các công nghệ web hiện đại gồm ReactJS, Node.js, Express và MySQL, hệ thống mang đến một giải pháp hoàn chỉnh, ổn định và tối ưu về mặt trải nghiệm người dùng lẫn hiệu suất quản trị. Dự án không chỉ dừng lại ở tính học thuật của một khóa luận đại học mà còn chứng minh giá trị ứng dụng thực tế cao, mở ra tiềm năng lớn trong việc hiện đại hóa dịch vụ chăm sóc sức khỏe cộng đồng.