- Tổng quan dự án
- Kiến trúc hệ thống
- Tech Stack
- Yêu cầu hệ thống
- Hướng dẫn cài đặt & Khởi chạy
- Database Schema
- Tài liệu API
- Tính năng hệ thống
- Luồng hoạt động (Workflows)
- Tính toàn vẹn dữ liệu (ACID)
- Xử lý lỗi phổ biến (Troubleshooting)
Dự án Executive Unified Dashboard là giải pháp Tích hợp Hệ thống (System Integration) nhằm giải quyết bài toán phân mảnh dữ liệu giữa các hệ thống Legacy (HR và Payroll).
CEO Memo Core Requirement: "Single Data Entry, distributed to other databases in near real-time."
Hệ thống cung cấp một Dashboard duy nhất cho ban lãnh đạo (CEO) quản lý tổng thể. Khi có nhân sự mới, dữ liệu chỉ cần nhập MỘT LẦN tại Dashboard trung tâm, sau đó sẽ được tự động phân phối (Near Real-time) xuống 2 cơ sở dữ liệu cũ (SQL Server và MySQL) mà không làm gián đoạn hệ thống. Đồng thời, hệ thống liên tục cào (scrape) dữ liệu từ các web app cũ để đồng bộ ngược lại tạo thành một bức tranh toàn cảnh (Single Source of Truth).
graph TD
UI[CEO Dashboard UI] -->|1. POST| BFF(BFF Server Node.js)
UI <-->|GET APIs| BFF
subgraph SSOT [Single Source of Truth]
BFF -->|2. ACID Transaction| PG[(PostgreSQL)]
PG -->|Bang employees| PG
PG -->|Bang outbox_events| PG
end
BFF -->|3. Background Relay| RMQ((RabbitMQ Exchange))
RMQ -->|Queue: hr_legacy| W1(Worker 1)
RMQ -->|Queue: payroll_legacy| W2(Worker 2)
W1 -->|4a. INSERT| SQLS[(SQL Server - HR)]
W2 -->|4b. INSERT| MY[(MySQL - Payroll)]
subgraph Legacy [Legacy Systems]
HR_Web[HR Web App]
PR_Web[Payroll Web App]
end
BFF -->|5. Cronjob 30s| HR_Web
BFF -->|5. Cronjob 30s| PR_Web
HR_Web --> SQLS
PR_Web --> MY
| Công nghệ | Thành phần | Mục đích / Chức năng |
|---|---|---|
| Node.js + Express | BFF Server | API Gateway, xử lý logic nội suy, Cronjob scraping, phục vụ file tĩnh. |
| PostgreSQL | Central DB | Lưu trữ trung tâm (Single Source of Truth), quản lý Outbox pattern. |
| RabbitMQ | Message Broker | Đảm bảo giao nhận tin nhắn bất đồng bộ, chống mất mát dữ liệu. |
| SQL Server | Legacy HR DB | Nơi lưu trữ dữ liệu nhân sự cũ. |
| MySQL | Legacy Payroll DB | Nơi lưu trữ dữ liệu lương cũ. |
| Cheerio | Web Scraper | Cào (Parse HTML) từ 2 hệ thống Legacy cũ. |
| Vanilla JS/HTML/CSS | Frontend | Giao diện không framework, hiệu năng cao, render biểu đồ. |
Để chạy dự án, máy tính cần cài đặt:
- Node.js (v16+ khuyến nghị)
- PostgreSQL (v13+)
- Microsoft SQL Server & SSMS (SQL Server Management Studio)
- MySQL (XAMPP hoặc MySQL Workbench)
- RabbitMQ (Cài đặt qua Docker hoặc file cài .exe trên Windows)
- Mở SSMS, chuột phải vào Server Node ->
Properties->Security-> Chọn SQL Server and Windows Authentication mode (Bật Mixed Mode). - Vào
Security->Logins-> Chuột phải tài khoảnsa->Properties:- Đặt password là:
a123456* - Tab
Status: Chọn Enabled.
- Đặt password là:
- Mở SQL Server Configuration Manager:
SQL Server Network Configuration->Protocols-> Enable TCP/IP.- Chuột phải TCP/IP ->
Properties->IP Addresses-> Cuộn xuốngIPAll-> Xóa TCP Dynamic Ports để trống toàn bộ kể, đặtTCP Portlà 1433.
- Restart SQL Server Service.
- Tạo database
HRvà import file script (nếu có).
- Mở MySQL (port mặc định 3306).
- Tạo database
payroll. - Đảm bảo user là
rootvà password để trống.
- Mở pgAdmin hoặc psql (port mặc định 5432).
- Tạo database:
-- 1. Tạo Database mới cho hệ thống tích hợp
CREATE DATABASE acme_corp_db;
-- LƯU Ý QUAN TRỌNG: Bạn phải chuyển kết nối vào database "acme_corp_db" vừa tạo
-- trước khi bôi đen và chạy các lệnh CREATE TABLE dưới đây!
-- 2. Bảng lưu trữ dữ liệu gốc của nhân viên (Single Source of Truth)
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
ssn VARCHAR(50) NOT NULL,
gender VARCHAR(50),
ethnicity VARCHAR(100),
department VARCHAR(100),
emp_type VARCHAR(50),
shareholder VARCHAR(10),
benefit_plan VARCHAR(100),
salary_ytd NUMERIC,
vacation_days INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 3. Bảng quan trọng nhất: Hàng đợi sự kiện (Transactional Outbox Pattern)
CREATE TABLE outbox_events (
id SERIAL PRIMARY KEY,
aggregate_type VARCHAR(50) NOT NULL,
payload JSONB NOT NULL,
status VARCHAR(20) DEFAULT 'PENDING',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
- Đảm bảo user là
postgresvà password là123456. - (Quan trọng) Chạy lệnh sau để tránh lỗi trùng ID:
ALTER SEQUENCE employees_id_seq RESTART WITH 2500000;
Mở terminal tại thư mục dự án và chạy:
npm install express cors cheerio pg amqplib mssql mysql2 readlineThực hiện chạy theo đúng thứ tự sau:
Terminal 1 (Khởi động hệ thống Legacy):
# Đảm bảo 2 app Legacy đang chạy ở port 19335 (HR) và 8080 (Payroll)Terminal 2 (Chạy Worker Consumer):
node worker-legacy.jsTerminal 3 (Chạy BFF Server & UI):
node bff-server.jsTruy cập UI tại: http://localhost:3000
Hệ thống yêu cầu RabbitMQ để xử lý Message Queue. Cách nhanh nhất và sạch nhất là chạy qua Docker.
1. Cài đặt Docker Desktop:
- Tải và cài đặt Docker Desktop từ: https://www.docker.com/products/docker-desktop/
- Khởi động Docker Desktop và đảm bảo icon ở khay hệ thống hiển thị màu xanh (Engine running).
2. Chạy RabbitMQ Container: -Mở một Terminal mới (hoặc Command Prompt) và chạy lệnh sau để tải image và khởi động RabbitMQ kèm giao diện quản lý (Management Plugin):
docker run -d --name rabbitmq-server -p 5672:5672 -p 15672:15672 rabbitmq:3-management-Đăng nhập xem giao diện quản trị:
Truy cập trình duyệt: http://localhost:15672
Username: guest
Password: guest
Tại đây bạn có thể xem các Queue (hr_legacy_queue, payroll_legacy_queue) hoạt động realtime. -Cho những lần chạy dự án sau này: Bạn KHÔNG cần chạy lại lệnh docker run dài dòng phía trên nữa (nếu chạy sẽ bị lỗi trùng tên). Thay vào đó, chỉ cần đảm bảo đã khởi động docker, cmd gõ lệnh sau để bật lại container:
| HTTP Method | Endpoint | Chức năng | Body / Params |
|---|---|---|---|
POST |
/api/employees |
Tạo nhân viên mới + Lưu Outbox | JSON payload (14 fields) |
GET |
/api/employees/:mode |
Lấy dữ liệu phân trang | mode: unified / hr / payroll |
GET |
/api/dashboard/stats |
Lấy KPI tổng hợp | Không có |
GET |
/api/dashboard/charts |
Lấy data vẽ biểu đồ | Không có |
GET |
/api/sync |
Cào dữ liệu thủ công | Không có |
DASHBOARD (Unified Tab):
- KPI Cards: Tổng Headcount tự động xử lý Deduplication (HR + Payroll - Trùng lặp).
- Alert Cards (Drill-down): 4 thẻ cảnh báo màu sắc - Click để lọc trực tiếp vào bảng.
- Biểu đồ động (Charts): Phân tích Lương, Phép năm, Quyền lợi.
- Smart Table: 8 cột với Alert Tags trực quan (Sinh nhật, Kỷ niệm, Đổi Plan).
- Fuzzy Search & Phân trang: Tìm kiếm linh hoạt, Server-side pagination, Auto-refresh không làm gián đoạn UI.
- User điền Form 14 trường trên UI và submit.
- BFF Server mở
ACID Transactiontrong PostgreSQL. - Insert vào bảng
employeesvàoutbox_events(status: PENDING), sau đóCOMMIT. - Cronjob (3s) quét bảng outbox, lấy dòng
PENDINGđẩy lên RabbitMQ, update thànhPROCESSED. - Worker nhận Message và Insert song song vào SQL Server và MySQL.
- BFF chạy
syncFromLegacy()mỗi 30 giây. - Đọc trang 1, dùng Regex lấy "Tổng số trang".
- Dùng
Promise.allchạy thuật toán Head & Tail Scraping. - Chạy thuật toán Deduplication gộp các bản ghi trùng lặp.
- Ghi stream xuống file
.jsonlđể tránh tràn RAM.
Dự án áp dụng Transactional Outbox Pattern để giải bài toán Dual-Write. Bằng cách sử dụng Transaction của PostgreSQL để insert Data và Outbox message trong cùng 1 commit, hệ thống đảm bảo nếu RabbitMQ rớt mạng, dữ liệu vẫn an toàn ở trạng thái PENDING. Khi mạng phục hồi, Relay tự động đẩy tiếp, đảm bảo Eventual Consistency (Nhất quán cuối).
| # | Hiện tượng lỗi (Error) | Nguyên nhân | Cách khắc phục |
|---|---|---|---|
| 1 | Login failed for user 'sa' | Chưa bật Mixed Mode Authentication. | Mở SSMS > Security > Chọn SQL Server and Windows mode. |
| 2 | Violation of PRIMARY KEY | ID mới trùng với ID cũ trong Legacy. | Chạy ALTER SEQUENCE employees_id_seq RESTART WITH 3000000; trong Postgres. |
| 3 | Foreign Key Constraint | Hardcode ID = 1 khi insert. | Đổi code thành query động SELECT TOP 1... để lấy ID thực tế. |
| 4 | Unknown column PayRates_id | Sai tên cột trong câu Insert. | Sửa tên cột lại thành PayRates_id trong code worker. |
| 5 | Out of range value | Cột decimal(2,0) max là 99. | Thêm logic Math.min(salary, 99) vào code trước khi Insert. |
| 6 | UI Reload liên tục | Dùng Live Server. | Tắt Live Server, truy cập http://localhost:3000. |
| 7 | Ô input số trang bị reset | Auto-refresh 10s cập nhật DOM. | Update code JS: if (document.activeElement !== pageInput) |
| 8 | RabbitMQ Connection Refused | Service RabbitMQ chưa chạy. | Khởi động lại container Docker hoặc service RabbitMQ trên Windows. |