Website bán máy tính và linh kiện PC tích hợp công cụ kiểm tra tương thích cấu hình và chatbot AI tư vấn build PC.
Khóa luận tốt nghiệp — Trần Đức Khánh (A45039) — GVHD: ThS. Trần Văn Nam — Trường Đại học Thăng Long.
| Thành phần | Công nghệ |
|---|---|
| Frontend | React 18, Vite, React Router, Axios — phục vụ bằng nginx |
| Backend | Node.js 20, Express 5, mysql2, JWT (jsonwebtoken), bcryptjs, zod |
| Cơ sở dữ liệu | MySQL 8.0 (utf8mb4) |
| Hạ tầng | Docker, Docker Compose |
| Kiểm thử | Jest (unit test), Postman/Newman (API test) |
Yêu cầu: Docker Desktop (Docker Compose v2).
docker compose up -d --build| Dịch vụ | Địa chỉ |
|---|---|
| Website (frontend) | http://localhost:3000 |
| REST API | http://localhost:5000/api |
| MySQL | localhost:3307 (user tkpc / tkpc_secret, database trankhanhpc) |
Lần chạy đầu tiên MySQL tự nạp schema và dữ liệu trong database/init/. Các script này chỉ chạy khi volume còn trống; sau khi sửa schema hoặc seed, nạp lại từ đầu:
docker compose down -v # xóa cả volume dữ liệu MySQL
docker compose up -d --buildCó thể ghi đè cấu hình (cổng, mật khẩu, JWT secret...) bằng cách sao chép .env.example thành .env. Không có .env thì dùng giá trị mặc định cho môi trường dev.
| Vai trò | Mật khẩu | |
|---|---|---|
| Quản trị viên | admin@trankhanhpc.vn | Admin@123 |
| Nhân viên | staff@trankhanhpc.vn | Staff@123 |
| Khách hàng | customer@trankhanhpc.vn | Customer@123 |
docker compose up -d mysql # chỉ chạy MySQL
cd backend && cp .env.example .env && npm install && npm run dev
cd frontend && npm install && npm run dev # http://localhost:5173, proxy /api → :5000Trình duyệt ──► nginx (frontend:80)
├── / → React SPA (static)
└── /api/* → backend:5000 (Express) ──► MySQL 8
Backend tổ chức theo module, mỗi module tách lớp routes → controller → service → repository:
backend/src
├── app.js, server.js, routes.js
├── config/ env, MySQL pool, transaction helper
├── middlewares/ authenticate / authorize (RBAC), validate (zod), errorHandler
├── constants/ roles
├── utils/ ApiError, pagination, response, slugify
└── modules/
├── auth/ đăng ký, đăng nhập, refresh token, đăng xuất, đổi mật khẩu
├── users/ quản lý tài khoản & phân quyền (Admin)
├── catalog/ danh mục 2 cấp, thương hiệu, specRegistry, metadata bộ lọc động
├── products/ tìm kiếm/lọc/phân trang, chi tiết, quản lý sản phẩm & máy tính bộ
├── wishlist/ sản phẩm yêu thích
└── compatibility/ Rule Engine kiểm tra tương thích (đọc luật từ CSDL)
Frontend (React + Vite) dùng chung một hệ thống giao diện (font Be Vietnam Pro, token màu trong styles/base.css):
| Trang | Đường dẫn | Ghi chú |
|---|---|---|
| Trang chủ | / |
Hero, Flash Sale (sản phẩm đang giảm giá, đếm ngược tới 24h), PC bán chạy theo tầm giá, Build PC theo tựa game, dải giới thiệu danh mục |
| Danh mục / tìm kiếm | /danh-muc/:slug, /tim-kiem?q= |
Bộ lọc động theo thông số, đồng bộ trên URL |
| Chi tiết sản phẩm | /san-pham/:slug |
Đối chiếu tương thích với cấu hình Build PC đang soạn; máy bộ thì kiểm tra chính cấu hình của máy |
| Build PC | /build-pc (?chon=cpu mở sẵn cửa sổ chọn) |
8 vị trí linh kiện, mẫu cấu hình từ máy bộ đang bán, kết quả Rule Engine, công suất, xuất CSV / in / sao chép |
| Giỏ hàng → Thông tin đặt hàng | /gio-hang, /thanh-toan |
Giỏ lưu trên trình duyệt; thanh toán khi nhận hàng (COD). Tạo đơn hàng sẽ nối API đơn hàng ở giai đoạn sau |
| Yêu thích | /yeu-thich |
Cần đăng nhập |
| Trợ lý AI | /tro-ly-ai (?q= hỏi luôn) |
Giao diện toàn màn hình. Chưa kết nối Gemini: hiện gợi ý theo ngân sách/nhu cầu từ kho hàng thật (utils/assistant.js) |
| Quản trị / Nhân viên | /admin, /staff |
Theo vai trò |
Ảnh sản phẩm để trống (image_url NULL) và hiển thị khung "Chưa có hình ảnh" theo loại linh kiện cho tới khi được thêm qua trang quản trị.
- Access token (JWT, mặc định 15 phút) gửi qua header
Authorization: Bearer <token>. - Refresh token (JWT, mặc định 7 ngày) lưu dạng SHA-256 trong bảng
refresh_tokens, xoay vòng mỗi lần refresh. Dùng lại token đã thu hồi → thu hồi toàn bộ phiên của người dùng. - Mỗi request xác thực đều nạp lại user từ CSDL nên khóa tài khoản / đổi vai trò có hiệu lực ngay.
- Vai trò lưu ở bảng
roles(customer,staff,admin), mỗi người dùng có đúng một vai trò (users.role_id). Middlewareauthorize(...roles)kiểm soát từng endpoint.
Schema đầy đủ có chú thích trong database/init/01_schema.sql. Các nhóm bảng:
| Nhóm | Bảng |
|---|---|
| Người dùng | roles, users, refresh_tokens |
| Danh mục & sản phẩm | categories (2 cấp), brands, products, product_components (linh kiện cấu thành máy tính bộ) |
| Thông số linh kiện | cpu_specs, mainboard_specs, ram_specs, gpu_specs, psu_specs, storage_specs, cooler_specs, case_specs |
| Kho hàng | inventory, inventory_transactions, inventory_transaction_items |
| Yêu thích | wishlist_items |
| PC Builder | pc_builds, pc_build_items |
| Kiểm tra tương thích | compatibility_rules, compatibility_rule_conditions, compatibility_checks, compatibility_check_results |
| Giỏ hàng & đơn hàng | cart_items, orders, order_items, order_status_history |
| Chatbot AI | chat_sessions, chat_messages, ai_build_requests, ai_build_recommendations |
Thông số được lưu trong bảng riêng theo loại linh kiện với cột có kiểu dữ liệu rõ ràng (cpu_specs.socket VARCHAR, gpu_specs.length_mm SMALLINT...). Nhờ vậy Rule Engine đối chiếu trực tiếp các cột, ví dụ CPU.socket = MAINBOARD.socket, GPU.length_mm <= CASE.gpu_max_length_mm.
Các cột đó được mô tả trong backend/src/modules/catalog/specRegistry.js (nhãn, kiểu, đơn vị, có lọc được, bắt buộc hay không). Bộ lọc động, API tạo/sửa sản phẩm và bảng thông số trên giao diện đều đọc từ sổ đăng ký này, nên thêm một thông số mới chỉ cần thêm cột vào bảng *_specs và một dòng vào registry.
Cột nhiều giá trị (socket tản nhiệt hỗ trợ, chuẩn mainboard case hỗ trợ...) lưu dạng danh sách phân cách dấu phẩy không có khoảng trắng (LGA1700,AM4,AM5) và lọc bằng FIND_IN_SET.
Máy tính bộ là products thuộc nhóm danh mục Máy tính bộ (PC Gaming, PC AMD Gaming, PC Workstation, PC Mini, PC Văn phòng, PC AI); linh kiện cấu thành lưu ở product_components. Vì linh kiện lẫn máy bộ đều là products nên giỏ hàng, đơn hàng và yêu thích dùng chung một cách tham chiếu.
Bảng compatibility_rules + compatibility_rule_conditions chứa 18 luật khởi tạo (socket CPU–mainboard–tản nhiệt, loại/bus/dung lượng/số thanh RAM, kích thước mainboard/GPU/tản nhiệt/radiator/nguồn so với case, công suất và đầu cấp nguồn PSU, giao tiếp ổ cứng, tồn kho). Điều kiện có dạng <linh kiện>.<cột> <toán tử> <linh kiện>.<cột>; Rule Engine (backend/src/modules/compatibility/ruleEngine.js) đọc bảng này nên Admin cấu hình được luật mà không sửa code; mỗi luật trả về PASS / FAIL / WARNING / INFO / SKIPPED.
| File | Nội dung |
|---|---|
database/init/01_schema.sql |
Schema |
database/init/02_reference_data.sql |
Vai trò, 2 + 14 danh mục, 14 thương hiệu, 18 luật tương thích |
database/init/03_seed_data.sql |
Sinh tự động — 160 linh kiện (8 nhóm × 20), 7 máy tính bộ, tồn kho, 3 tài khoản demo, yêu thích mẫu |
Muốn thêm/sửa sản phẩm: sửa database/seed/source_data.py rồi chạy python database/seed/build_seed.py để sinh lại 03_seed_data.sql.
Phản hồi thống nhất:
| Method | Endpoint | Quyền | Mô tả |
|---|---|---|---|
| GET | /api/health |
Công khai | Kiểm tra API và CSDL |
| POST | /api/auth/register |
Công khai | Đăng ký (vai trò Khách hàng) |
| POST | /api/auth/login |
Công khai | Đăng nhập, trả access + refresh token |
| POST | /api/auth/refresh |
Công khai | Cấp lại token (xoay vòng refresh token) |
| POST | /api/auth/logout |
Công khai | Thu hồi refresh token |
| GET | /api/auth/me |
Đã đăng nhập | Thông tin người dùng hiện tại |
| PUT | /api/auth/change-password |
Đã đăng nhập | Đổi mật khẩu (đăng xuất mọi thiết bị) |
| GET | /api/users |
Admin | Danh sách người dùng (role, isActive, q, page, limit) |
| POST | /api/users |
Admin | Tạo tài khoản với vai trò bất kỳ |
| GET | /api/users/:id |
Admin | Chi tiết người dùng |
| PATCH | /api/users/:id/role |
Admin | Đổi vai trò |
| PATCH | /api/users/:id/status |
Admin | Khóa / mở khóa |
| GET | /api/categories |
Công khai | Cây danh mục 2 cấp kèm số sản phẩm |
| GET | /api/categories/:slug |
Công khai | Chi tiết danh mục, danh mục con, định nghĩa thông số |
| GET | /api/categories/:slug/filters |
Công khai | Metadata bộ lọc động (hãng, khoảng giá, giá trị thông số + số lượng) |
| GET | /api/brands?category= |
Công khai | Thương hiệu (theo danh mục) |
| GET | /api/products |
Công khai | Tìm kiếm, lọc, sắp xếp, phân trang |
| GET | /api/products/:idOrSlug |
Công khai | Chi tiết sản phẩm: bảng thông số, hoặc linh kiện cấu thành nếu là máy bộ |
| POST | /api/products |
Nhân viên, Admin | Tạo linh kiện (specs) hoặc máy tính bộ (components) |
| PATCH | /api/products/:id |
Nhân viên, Admin | Cập nhật một phần; specs/components thay toàn bộ |
| DELETE | /api/products/:id |
Admin | Ẩn sản phẩm (xóa mềm) |
| GET | /api/wishlist |
Đã đăng nhập | Danh sách yêu thích (items, productIds) |
| POST | /api/wishlist/:productId |
Đã đăng nhập | Thêm vào yêu thích (idempotent) |
| DELETE | /api/wishlist/:productId |
Đã đăng nhập | Bỏ yêu thích |
| POST | /api/compatibility/check |
Công khai | Kiểm tra cấu hình { items: [{ productId, quantity }] }: tóm tắt, kết quả từng luật, công suất ước tính |
| POST | /api/compatibility/candidates |
Công khai | Chấm điểm danh sách sản phẩm cho một vị trí (componentType, productIds) so với cấu hình hiện tại |
| Tham số | Ví dụ | Ý nghĩa |
|---|---|---|
q |
q=ryzen 7600 |
Mỗi từ khóa phải có trong tên, SKU hoặc thương hiệu |
category |
category=cpu / category=pc |
Slug danh mục; danh mục cha lấy cả danh mục con. Bắt buộc khi dùng specs |
brand |
brand=intel,amd |
Slug thương hiệu, nhiều giá trị cách nhau dấu phẩy |
minPrice, maxPrice |
minPrice=2000000 |
Khoảng giá bán thực tế (đã tính khuyến mãi) |
inStock |
inStock=true |
Chỉ còn hàng / hết hàng (theo inventory) |
onSale |
onSale=true |
Chỉ sản phẩm đang có giá khuyến mãi |
ids |
ids=1003,2001 |
Lấy theo danh sách id (tối đa 50) – dùng cho giỏ hàng, Build PC |
sort |
price_asc |
newest (mặc định), price_asc, price_desc, name_asc, name_desc |
page, limit |
page=2&limit=12 |
Phân trang (limit tối đa 100) |
status |
status=all |
Chỉ Nhân viên/Admin: active (mặc định), inactive, discontinued, all |
specs[...] |
xem bên dưới | Lọc theo thông số kỹ thuật |
Lọc thông số kỹ thuật — key là tên cột trong bảng *_specs, danh sách hợp lệ lấy từ GET /api/categories/:slug/filters:
specs[socket]=AM5,LGA1700 # text: khớp một trong các giá trị
specs[vram_gb]=8,12 # number: khớp một trong các giá trị
specs[cores][min]=6&specs[cores][max]=12 # number: khoảng (có thể chỉ min hoặc max)
specs[wifi]=true # boolean
specs[supported_sockets]=AM5 # cột đa giá trị: danh sách có chứa giá trị
Trong một thông số các giá trị kết hợp bằng OR, giữa các thông số kết hợp bằng AND. Key không thuộc danh mục trả về 400 kèm danh sách allowedKeys.
Ví dụ: tản nước AIO hỗ trợ AM5, giá ≤ 3 triệu
GET /api/products?category=cooler&specs[cooler_type]=AIO&specs[supported_sockets]=AM5&maxPrice=3000000
# Unit test (bộ dựng truy vấn lọc, chuẩn hóa thông số, specRegistry, slugify)
cd backend && npm test
# API test: chạy toàn bộ Postman collection (cần hệ thống đang chạy)
npx newman run postman/TranKhanhPC.postman_collection.json -e postman/TranKhanhPC.local.postman_environment.jsonHoặc import 2 file trong postman/ vào Postman và chạy bằng Collection Runner theo thứ tự — các request đăng nhập tự lưu token vào collection variables. Collection được sinh từ postman/generate.js (node postman/generate.js).
Endpoint đăng nhập/đăng ký giới hạn 50 request / 15 phút / IP. Nếu chạy collection nhiều lần liên tiếp bị 429, chạy
docker compose restart backend.
Theo Conventional Commits: feat, fix, test, docs, chore, refactor + phạm vi (auth, products, database, frontend...).