Tài liệu này đặc tả chi tiết các Use Case của hệ thống Website Thương mại Điện tử TechVN. Mỗi Use Case mô tả một tương tác hoàn chỉnh giữa Actor và hệ thống, bao gồm luồng chính, luồng thay thế, luồng ngoại lệ, tiền điều kiện, hậu điều kiện và quy tắc nghiệp vụ liên quan.
Người dùng (User) / \ / \ Khách vãng lai Người dùng (Guest) đã xác thực (Authenticated) / \ / \ Khách hàng Quản trị viên (Customer) (Admin) / | \ \ / | \ \ QL SP QL ĐH Marketing Super Admin
Quan hệ kế thừa: Khách hàng kế thừa toàn bộ quyền của Khách vãng lai. Super Admin kế thừa toàn bộ quyền của các Admin khác.
┌─────────────────────────────────────────────────────────┐│ PKG-04: SAU MUA HÀNG ││ ││ ┌─┐ ││ │C│──── UC-18: Theo dõi đơn hàng ││ │U│ ││ │S│──── UC-19: Hủy đơn hàng ││ │T│ ││ │O│──── UC-20: Đánh giá sản phẩm ││ │M│ ││ │E│──── UC-21: Yêu cầu hoàn trả ││ │R│ ││ └─┘ ││ │└─────────────────────────────────────────────────────────┘
Cho phép khách vãng lai tạo tài khoản mới bằng email hoặc số điện thoại để sử dụng các tính năng dành cho thành viên (đặt hàng, tích điểm, theo dõi đơn).
Actor chính
ACT-GUEST (Khách vãng lai)
Actor phụ
ACT-SMS (Hệ thống SMS), ACT-EMAIL (Hệ thống Email)
Mức ưu tiên
P1 — Critical
Độ phức tạp
★★☆
Tần suất sử dụng
~200 lần/ngày (dự kiến sau go-live 3 tháng)
SRS tham chiếu
FR-ACC-001
User Story
US-1.1, US-1.2
Release
R1 — MVP
Tiền điều kiện (Pre-conditions):
#
Điều kiện
PRE-01
Người dùng chưa đăng nhập (trạng thái Guest).
PRE-02
Hệ thống SMS/Email gateway hoạt động bình thường.
PRE-03
Người dùng có thiết bị kết nối Internet và trình duyệt tương thích.
Hậu điều kiện (Post-conditions):
#
Điều kiện
Loại
POST-01
Tài khoản mới được tạo trong cơ sở dữ liệu với trạng thái ACTIVE.
Thành công
POST-02
Người dùng được tự động đăng nhập (JWT token được tạo).
Thành công
POST-03
Email chào mừng được gửi đến người dùng.
Thành công
POST-04
Không có tài khoản nào được tạo nếu quá trình thất bại.
Thất bại
Luồng chính (Main Flow / Basic Flow):
Bước
Actor
Hệ thống
1
Người dùng truy cập trang Đăng ký (từ header hoặc khi được yêu cầu đăng nhập).
Hiển thị form đăng ký với 2 tab: "Email" (mặc định) và "Số điện thoại".
2
Người dùng chọn tab đăng ký (Email hoặc SĐT).
Hiển thị form tương ứng.
3
Người dùng nhập thông tin: Họ tên, Email (hoặc SĐT), Mật khẩu, Xác nhận mật khẩu.
Validate realtime từng trường khi blur: - Họ tên: 2-50 ký tự. - Email: format hợp lệ (RFC 5322). - SĐT: 10 số, bắt đầu bằng 0 (03x, 05x, 07x, 08x, 09x). - Mật khẩu: ≥ 8 ký tự, chứa chữ hoa, chữ thường, số. - Xác nhận mật khẩu: khớp với mật khẩu. Hiển thị indicator độ mạnh mật khẩu (Yếu/Trung bình/Mạnh).
4
Người dùng nhấn nút "Đăng ký".
4.1. Kiểm tra Email/SĐT chưa tồn tại trong DB. 4.2. Tạo mã OTP 6 số ngẫu nhiên. 4.3. Lưu OTP vào Redis (TTL = 300 giây). 4.4. Gửi OTP qua kênh tương ứng: - Email: "Mã xác thực TechVN: 123456. Hiệu lực 5 phút." - SMS: "Ma xac thuc TechVN: 123456. Hieu luc 5 phut." 4.5. Hiển thị form nhập OTP với đồng hồ đếm ngược 5:00.
5
Người dùng nhập mã OTP 6 số.
Hiển thị auto-focus vào 6 ô input riêng biệt.
6
Người dùng nhấn "Xác nhận" (hoặc tự động submit khi nhập đủ 6 số).
6.1. So khớp OTP nhập với OTP lưu trong Redis. 6.2. Tạo bản ghi users trong PostgreSQL: - id: UUID v4. - email / phone: giá trị đã nhập. - password_hash: bcrypt(password, cost=12). - full_name: giá trị đã nhập. - status: ACTIVE. - role: CUSTOMER. - loyalty_points: 0. - created_at: timestamp hiện tại. 6.3. Xóa OTP khỏi Redis. 6.4. Tạo JWT access token (TTL 15 phút) và refresh token (TTL 30 ngày). 6.5. Đưa email chào mừng vào hàng đợi SQS notifications. 6.6. Trả về response chứa tokens + user profile.
7
Chuyển hướng về trang chủ (hoặc trang trước đó nếu có redirect_url). Hiển thị toast: "Chào mừng bạn đến với TechVN!" và tên người dùng trên header.
Luồng thay thế (Alternative Flows):
Mã
Tên
Phân nhánh tại
Mô tả
AF-01
Đăng ký bằng SĐT thay vì Email
Bước 2
Người dùng chọn tab "Số điện thoại". Form hiển thị trường SĐT thay vì Email. OTP được gửi qua SMS thay vì Email. Các bước còn lại tương tự.
AF-02
Gửi lại OTP
Bước 5
Người dùng nhấn "Gửi lại mã" (hiển thị sau khi đếm ngược 60 giây). Hệ thống tạo OTP mới, hủy OTP cũ, gửi lại. Tối đa 3 lần gửi lại mỗi giờ cho cùng email/SĐT. Sau 3 lần: hiển thị "Bạn đã yêu cầu quá nhiều lần. Vui lòng thử lại sau 1 giờ."
AF-03
Chuyển sang Đăng nhập
Bước 1
Người dùng nhấn link "Đã có tài khoản? Đăng nhập". Hệ thống chuyển đến trang Đăng nhập (UC-03).
Luồng ngoại lệ (Exception Flows):
Mã
Tên
Phân nhánh tại
Điều kiện
Xử lý
EX-01
Email/SĐT đã tồn tại
Bước 4.1
SELECT trả về bản ghi tồn tại
Hiển thị inline error: "Email này đã được đăng ký." Hiển thị link: "Đăng nhập" và "Quên mật khẩu?". Không tiết lộ thông tin tài khoản hiện có. Luồng dừng lại, người dùng có thể sửa hoặc chuyển sang đăng nhập.
EX-02
Mật khẩu không đủ mạnh
Bước 3
Mật khẩu < 8 ký tự hoặc thiếu chữ hoa/thường/số
Hiển thị realtime bên dưới trường mật khẩu: ❌ Tối thiểu 8 ký tự / ✅ Chứa chữ hoa / ❌ Chứa số. Nút "Đăng ký" bị disable cho đến khi tất cả điều kiện đạt ✅.
EX-03
OTP hết hạn
Bước 6.1
OTP không tồn tại trong Redis (TTL hết)
Hiển thị: "Mã xác thực đã hết hạn." Hiển thị nút "Gửi mã mới" (quay về AF-02).
EX-04
OTP sai
Bước 6.1
OTP nhập ≠ OTP lưu
Hiển thị: "Mã xác thực không đúng. Còn X lần thử." Tăng bộ đếm otp_attempts trong Redis. Tối đa 5 lần sai.
EX-05
OTP sai quá 5 lần
Bước 6.1
otp_attempts ≥ 5
Xóa OTP khỏi Redis. Hiển thị: "Bạn đã nhập sai quá nhiều lần. Vui lòng thử lại sau 30 phút." Lưu IP vào Redis blacklist (TTL = 1800 giây). Luồng dừng.
EX-06
SMS/Email gateway lỗi
Bước 4.4
API trả về lỗi hoặc timeout
Retry tự động 2 lần (khoảng cách 3 giây). Nếu vẫn thất bại: "Không thể gửi mã xác thực. Vui lòng thử lại sau hoặc chọn phương thức khác (Email ↔ SMS)." Log error để ops team xử lý.
EX-07
Lỗi server nội bộ
Bước 6.2
Exception khi tạo user trong DB
Rollback transaction. Hiển thị: "Đã có lỗi xảy ra. Vui lòng thử lại sau." Log error chi tiết (không hiển thị cho người dùng).
Quy tắc nghiệp vụ (Business Rules):
Mã
Quy tắc
Chi tiết
BR-ACC-01
Một email chỉ liên kết 1 tài khoản
Unique constraint trên cột email.
BR-ACC-02
Một SĐT chỉ liên kết 1 tài khoản
Unique constraint trên cột phone.
BR-ACC-03
Mật khẩu tối thiểu 8 ký tự
Chứa ít nhất: 1 chữ hoa, 1 chữ thường, 1 số.
BR-ACC-04
OTP hiệu lực 5 phút
Lưu trong Redis với TTL = 300s.
BR-ACC-05
Giới hạn gửi OTP
Tối đa 3 lần/giờ cho cùng email/SĐT.
BR-ACC-06
Khóa đăng ký khi nhập sai OTP quá 5 lần
Khóa theo IP trong 30 phút.
BR-ACC-07
SĐT Việt Nam hợp lệ
10 số, bắt đầu 0, đầu số: 03x, 05x, 07x, 08x, 09x.
Activity Diagram:
┌─────────┐ │ Bắt đầu │ └────┬────┘ ▼ ┌────────────────┐ │ Hiển thị form │ │ đăng ký │ └────────┬───────┘ ▼ ┌────────────────┐ │ Nhập thông tin │ │ (Tên, Email/ │ │ SĐT, MK) │ └────────┬───────┘ ▼ ┌────────────────┐ Không hợp lệ ┌──────────────┐ │ Validate │─────────────────────>│ Hiển thị lỗi │ │ client-side │ │ inline │ └────────┬───────┘ └──────┬───────┘ │ Hợp lệ │ ▼ │ Sửa ┌────────────────┐ │ │ Gửi request │<──────────────────────────────┘ │ đăng ký │ └────────┬───────┘ ▼ ┌───────────┐ Đã tồn tại ┌───────────────┐ │ Email/SĐT │────────────────────>│ Thông báo + │ │ tồn tại? │ │ link Đăng nhập│ └─────┬─────┘ └───────────────┘ │ Chưa ▼ ┌────────────────┐ │ Tạo OTP │ │ Gửi SMS/Email │ └────────┬───────┘ ▼ ┌────────────────┐ │ Nhập OTP │ └────────┬───────┘ ▼ ┌───────────┐ Sai ┌───────────────┐ │ OTP đúng? │────────────────────>│ Hiển thị lỗi │ └─────┬─────┘ │ ≤5 lần: thử │ │ Đúng │ lại │ ▼ │ >5 lần: khóa │ ┌────────────────┐ └───────────────┘ │ Tạo tài khoản │ │ Tạo JWT token │ │ Gửi email │ │ chào mừng │ └────────┬───────┘ ▼ ┌────────────────┐ │ Chuyển hướng │ │ trang chủ │ └────────┬───────┘ ▼ ┌─────────┐ │ Kết thúc │ └─────────┘
Yêu cầu giao diện (UI Notes):
#
Yêu cầu
UI-01
Form nằm giữa trang, max-width 480px (Desktop), full-width padding 16px (Mobile).
UI-02
Tab Email/SĐT chuyển đổi mượt mà (không reload trang).
UI-03
Indicator độ mạnh mật khẩu: thanh progress bar (đỏ → vàng → xanh).
UI-04
OTP input: 6 ô riêng biệt, auto-focus sang ô tiếp theo, hỗ trợ paste.
UI-05
Đồng hồ đếm ngược OTP: format "MM:SS", đổi màu đỏ khi < 1 phút.
UI-06
Nút "Đăng ký" trạng thái: disabled (xám) → enabled (xanh primary) khi form hợp lệ.
UI-07
Hiển thị link: "Đã có tài khoản? Đăng nhập" và "Đăng ký bằng Google / Facebook".
Yêu cầu phi chức năng:
#
Yêu cầu
Target
NF-01
Thời gian phản hồi submit form
≤ 2 giây
NF-02
Thời gian gửi OTP (từ lúc request đến lúc user nhận)
Cho phép người dùng đã có tài khoản xác thực danh tính bằng email/SĐT + mật khẩu để truy cập các tính năng thành viên.
Actor chính
ACT-CUSTOMER
Mức ưu tiên
P1 — Critical
Độ phức tạp
★☆☆
SRS tham chiếu
FR-ACC-002
User Story
US-1.3, US-1.4
Tiền điều kiện:
#
Điều kiện
PRE-01
Người dùng chưa đăng nhập (trạng thái Guest).
PRE-02
Người dùng có tài khoản hợp lệ (status = ACTIVE).
Hậu điều kiện:
#
Điều kiện
Loại
POST-01
Session được tạo, JWT tokens được phát hành.
Thành công
POST-02
Giỏ hàng guest (localStorage) được merge với giỏ hàng server.
Thành công
POST-03
Bản ghi last_login_at được cập nhật.
Thành công
POST-04
Không có thay đổi trạng thái nếu đăng nhập thất bại.
Thất bại
Luồng chính:
Bước
Actor
Hệ thống
1
Người dùng truy cập trang Đăng nhập.
Hiển thị form đăng nhập: Email/SĐT, Mật khẩu, checkbox "Ghi nhớ đăng nhập", nút "Đăng nhập", link "Quên mật khẩu?", nút "Đăng nhập bằng Google / Facebook".
2
Người dùng nhập Email (hoặc SĐT) và Mật khẩu.
Validate realtime: Email/SĐT không để trống, Mật khẩu không để trống.
3
Người dùng nhấn "Đăng nhập".
3.1. Tìm user bằng email hoặc phone trong DB. 3.2. So sánh mật khẩu nhập với password_hash (bcrypt.compare). 3.3. Kiểm tra status = ACTIVE. 3.4. Kiểm tra bộ đếm login_attempts trong Redis. 3.5. Reset login_attempts về 0. 3.6. Cập nhật last_login_at. 3.7. Tạo JWT access token (TTL 15 phút) và refresh token. - Nếu "Ghi nhớ đăng nhập": refresh token TTL = 30 ngày. - Nếu không: refresh token TTL = 24 giờ. 3.8. Kiểm tra giỏ hàng guest (từ request body nếu có) và merge với giỏ server.
4
Chuyển hướng: - Nếu có redirect_url (VD: từ checkout): về trang đó. - Nếu không: về trang chủ. Hiển thị tên + avatar trên header.
Luồng thay thế:
Mã
Tên
Mô tả
AF-01
Đăng nhập bằng Google
Bước 1: Người dùng nhấn "Đăng nhập bằng Google". Hệ thống redirect đến Google OAuth consent screen. Google callback với authorization code. Hệ thống exchange code lấy user info (email, name, avatar). Nếu email đã tồn tại → đăng nhập (hoặc đề xuất liên kết tài khoản). Nếu email chưa tồn tại → tạo tài khoản mới (không cần mật khẩu), trạng thái ACTIVE.
AF-02
Đăng nhập bằng Facebook
Tương tự AF-01, sử dụng Facebook OAuth.
Luồng ngoại lệ:
Mã
Điều kiện
Xử lý
EX-01
Email/SĐT không tồn tại trong DB
Hiển thị: "Thông tin đăng nhập không chính xác." (Không phân biệt sai email hay sai mật khẩu — chống enumeration).
EX-02
Mật khẩu sai
Tăng login_attempts trong Redis (TTL 15 phút). Hiển thị: "Thông tin đăng nhập không chính xác. Còn X lần thử."
EX-03
Sai mật khẩu 5 lần liên tiếp
Đặt account_locked trong Redis (TTL = 900 giây / 15 phút). Hiển thị: "Tài khoản bị khóa tạm thời 15 phút. Sử dụng 'Quên mật khẩu' để đặt lại." Gửi email cảnh báo đến email tài khoản (qua SQS).
EX-04
Tài khoản bị vô hiệu hóa (status ≠ ACTIVE)
Hiển thị: "Tài khoản đã bị vô hiệu hóa. Vui lòng liên hệ CSKH: 1900-xxxx."
Quy tắc nghiệp vụ:
Mã
Quy tắc
BR-LGN-01
Không phân biệt lỗi sai email hay sai mật khẩu trong thông báo (chống enumeration attack).
BR-LGN-02
Khóa tạm thời 15 phút sau 5 lần sai mật khẩu liên tiếp.
BR-LGN-03
"Ghi nhớ đăng nhập" kéo dài session 30 ngày (refresh token), mặc định 24 giờ.
BR-LGN-04
Merge giỏ hàng: nếu SP đã tồn tại ở cả guest và server, lấy số lượng lớn hơn.
Hiển thị đầy đủ thông tin sản phẩm (hình ảnh, giá, thông số kỹ thuật, đánh giá, sản phẩm liên quan) giúp khách hàng đưa ra quyết định mua hàng.
Actor chính
ACT-GUEST, ACT-CUSTOMER
Mức ưu tiên
P1 — Critical
Độ phức tạp
★★☆
SRS tham chiếu
FR-PRD-002
User Story
US-2.2
Tiền điều kiện:
#
Điều kiện
PRE-01
Sản phẩm tồn tại trong DB với status = PUBLISHED.
PRE-02
URL hợp lệ: /[category-slug]/[product-slug].
Luồng chính:
Bước
Actor
Hệ thống
1
Người dùng truy cập URL sản phẩm (từ danh mục, tìm kiếm, hoặc link trực tiếp).
1.1. Truy vấn sản phẩm theo slug (cache Redis → DB fallback). 1.2. Truy vấn đánh giá (top 5, sắp xếp mới nhất). 1.3. Truy vấn sản phẩm liên quan (cùng danh mục, cùng tầm giá, limit 8). 1.4. Ghi nhận lượt xem (increment view_count, async). 1.5. Lưu vào "Sản phẩm đã xem" (Redis / localStorage).
2
Hiển thị trang chi tiết sản phẩm với layout: Khu vực trên (above the fold): - Bên trái (60%): Gallery ảnh (ảnh chính + thumbnail phụ). - Bên phải (40%): Tên SP, Rating, SKU, Giá, Trạng thái tồn kho, Bộ chọn variant (nếu có), Số lượng, Nút CTA. Khu vực dưới (tabbed content): - Tab "Thông số kỹ thuật": Bảng spec. - Tab "Mô tả chi tiết": HTML content. - Tab "Đánh giá (N)": Danh sách review + form viết review. Khu vực cuối: - Phụ kiện đi kèm, SP tương tự, SP đã xem.
3
Người dùng tương tác với gallery ảnh (click thumbnail, zoom).
Click thumbnail: ảnh chính thay đổi. Click ảnh chính (Desktop): mở lightbox zoom. Swipe (Mobile): chuyển ảnh theo gesture.
4
Người dùng chọn variant (nếu sản phẩm có nhiều phiên bản).
Cập nhật realtime: giá, thông số, trạng thái tồn kho, ảnh sản phẩm tương ứng variant. URL thêm ?variant=xxx (không reload trang).
5
Người dùng chọn số lượng và nhấn "Thêm vào giỏ hàng" hoặc "Mua ngay".
→ Chuyển sang UC-12 (Quản lý giỏ hàng).
Luồng thay thế:
Mã
Tên
Mô tả
AF-01
Thêm vào danh sách so sánh
Người dùng nhấn "So sánh". Hệ thống thêm SP vào thanh so sánh (sticky bar dưới cùng). Nếu đã có 4 SP → thông báo "Tối đa 4 sản phẩm".
AF-02
Thêm vào Wishlist
Người dùng nhấn icon ♡. Nếu đã đăng nhập: lưu vào wishlist trong DB. Nếu chưa đăng nhập: popup yêu cầu đăng nhập.
AF-03
Chia sẻ sản phẩm
Người dùng nhấn "Chia sẻ". Hiển thị popup: Copy link, Facebook, Zalo, Messenger.
Luồng ngoại lệ:
Mã
Điều kiện
Xử lý
EX-01
Sản phẩm không tồn tại hoặc status ≠ PUBLISHED
Trả về trang 404 với gợi ý sản phẩm phổ biến.
EX-02
Sản phẩm hết hàng (stock_quantity = 0)
Hiển thị badge "Hết hàng" (đỏ). Disable nút "Thêm vào giỏ" và "Mua ngay". Hiển thị nút "Thông báo khi có hàng" (nhập email/SĐT). Vẫn hiển thị đầy đủ thông tin SP.
EX-03
Variant đã chọn hết hàng
Hiển thị "(Hết hàng)" bên cạnh tên variant. Disable variant đó (không chọn được). Tự động chọn variant còn hàng gần nhất.
Quy tắc nghiệp vụ:
Mã
Quy tắc
BR-PDP-01
Giá hiển thị: Nếu có sale_price → hiển thị giá KM (đỏ, lớn) + giá gốc (gạch ngang) + badge "−X%".
BR-PDP-02
Badge sản phẩm: "Mới" nếu created_at < 14 ngày. "Bán chạy" nếu sold_count top 10 danh mục.
BR-PDP-03
Bảo hành: hiển thị theo brand (VD: ASUS 24 tháng, Dell 12 tháng Premium Support).
BR-PDP-04
Trả góp: hiển thị "Trả góp 0%" nếu giá ≥ 3.000.000 VNĐ và có chương trình đang chạy.
BR-PDP-05
SEO: title = "Tên SP — TechVN", description từ seo_description, canonical URL, Schema.org Product markup.
Người dùng đã đăng nhập (hoặc chuyển sang đăng nhập/đăng ký tại bước checkout).
PRE-02
Giỏ hàng có ít nhất 1 sản phẩm.
PRE-03
Tất cả sản phẩm trong giỏ hàng còn tồn kho.
Hậu điều kiện (thành công):
#
Điều kiện
POST-01
Đơn hàng mới được tạo trong DB với mã format TV-YYYYMMDD-XXXXX.
POST-02
Trạng thái đơn hàng: "Chờ xác nhận" (COD) hoặc "Chờ thanh toán" (online).
POST-03
Tồn kho bị trừ (tạm giữ 30 phút cho thanh toán online).
POST-04
Giỏ hàng được xóa.
POST-05
Email + SMS xác nhận đơn hàng được gửi cho khách.
POST-06
Thông báo đơn hàng mới được gửi cho Admin.
Luồng chính:
Bước
Actor
Hệ thống
Bước 1 — Thông tin giao hàng
1.1
Người dùng nhấn "Tiến hành đặt hàng" từ giỏ hàng.
Kiểm tra đăng nhập. Nếu chưa → redirect đến trang đăng nhập (với redirect_url=/thanh-toan). Nếu đã đăng nhập → tiếp tục.
1.2
Hiển thị bước 1 "Thông tin giao hàng": - Danh sách địa chỉ đã lưu (radio button, mặc định chọn địa chỉ default). - Nút "+ Thêm địa chỉ mới". - Ô "Ghi chú đơn hàng" (textarea, không bắt buộc). - Sidebar phải: Tóm tắt giỏ hàng (ảnh, tên, SL, giá).
1.3
Người dùng chọn địa chỉ giao hàng (hoặc nhập mới).
Validate địa chỉ: Họ tên, SĐT, Tỉnh/Thành, Quận/Huyện, Phường/Xã, Địa chỉ chi tiết (tất cả bắt buộc). Dropdown Tỉnh/Thành → auto-load Quận/Huyện → auto-load Phường/Xã (cascade).
1.4
Gọi API tính phí vận chuyển (song song GHTK + GHN): Request: địa chỉ nhận, tổng trọng lượng, kích thước kiện hàng. Response: danh sách phương thức + phí + thời gian giao dự kiến.
1.5
Hiển thị phương thức vận chuyển: ○ Giao tiêu chuẩn — 25.000đ — Dự kiến: Thứ 4, 14/05/2026 (3-5 ngày) ● Giao nhanh — 45.000đ — Dự kiến: Thứ 2, 12/05/2026 (1-2 ngày) (Mặc định chọn "Giao tiêu chuẩn").
1.6
Người dùng chọn phương thức vận chuyển.
Cập nhật phí ship và tổng cộng trên sidebar.
1.7
Người dùng nhấn "Tiếp tục".
Validate bước 1 hoàn tất. Lưu thông tin giao hàng vào session. Chuyển sang Bước 2. Progress bar cập nhật: ● Giao hàng ── ● Thanh toán ── ○ Xác nhận.
Bước 2 — Phương thức thanh toán
2.1
Hiển thị danh sách phương thức thanh toán: ● COD — Thanh toán khi nhận hàng (mặc định) ○ Chuyển khoản ngân hàng ○ VNPAY (ATM / Visa / Mastercard / QR Pay) ○ Ví MoMo ○ Trả góp (chỉ hiển thị khi tổng đơn ≥ 3.000.000đ)
2.2
Người dùng chọn phương thức thanh toán.
Nếu chọn "Chuyển khoản": hiển thị thông tin tài khoản NH (Vietcombank, BIDV). Nếu chọn "Trả góp": hiển thị bảng tính góp (→ extend UC-17).
2.3
Người dùng nhấn "Tiếp tục".
Lưu phương thức thanh toán vào session. Chuyển sang Bước 3.
Bước 3 — Xác nhận và Đặt hàng
3.1
Hiển thị trang xác nhận tổng hợp: Sản phẩm: bảng (ảnh, tên, phiên bản, đơn giá, SL, thành tiền). Giao hàng: tên người nhận, SĐT, địa chỉ, phương thức, phí ship. Thanh toán: phương thức đã chọn. Tổng: - Tạm tính: 25.990.000đ - Mã giảm giá (WELCOME10): −500.000đ - Phí vận chuyển: 25.000đ - TỔNG THANH TOÁN: 25.515.000đ (font lớn, đỏ, đậm) Checkbox: "Tôi đã đọc và đồng ý với Điều khoản sử dụng và Chính sách mua hàng" (bắt buộc tick). Nút "Đặt hàng".
3.2
Người dùng tick checkbox và nhấn "Đặt hàng".
Xử lý đặt hàng (trong transaction DB): 3.2.1. Kiểm tra tồn kho lần cuối cho tất cả SP (SELECT FOR UPDATE). 3.2.2. Kiểm tra mã giảm giá còn hiệu lực. 3.2.3. Tạo bản ghi orders: - order_code: generate TV-YYYYMMDD-XXXXX. - status: PENDING_CONFIRM (COD) / PENDING_PAYMENT (online). - Các trường khác theo thông tin đã nhập. 3.2.4. Tạo bản ghi order_items cho từng SP. 3.2.5. Trừ stock_quantity trong products / product_variants. 3.2.6. Nếu có mã giảm giá: tăng used_count trong coupons. 3.2.7. Tạo bản ghi payments (status = PENDING nếu online). 3.2.8. Xóa giỏ hàng của user. 3.2.9. Commit transaction.
3.3
Xử lý sau đặt hàng (async qua SQS): 3.3.1. Gửi email xác nhận đơn hàng cho khách. 3.3.2. Gửi SMS: "Đơn hàng TV-20260509-00001 đã được đặt thành công." 3.3.3. Gửi thông báo cho Admin (push notification / Slack). 3.3.4. Ghi log audit.
3.4
Phân luồng theo phương thức thanh toán: - COD: Hiển thị trang "Đặt hàng thành công" (→ bước 4). - VNPAY/MoMo: Redirect đến cổng thanh toán (→ UC-15). - Chuyển khoản: Hiển thị thông tin CK + mã đơn hàng làm nội dung CK.
Bước 4 — Đặt hàng thành công (COD)
4.1
Hiển thị trang xác nhận: - ✅ "Đặt hàng thành công!" - Mã đơn hàng: TV-20260509-00001 (copy-able). - Phương thức thanh toán: COD. - Thời gian giao dự kiến: Thứ 4, 14/05/2026. - Nút: "Theo dõi đơn hàng" → UC-18. - Nút: "Tiếp tục mua sắm" → Trang chủ.
Luồng ngoại lệ:
Mã
Điều kiện
Bước
Xử lý
EX-01
SP trong giỏ hết hàng khi checkout
3.2.1
Rollback transaction. Hiển thị: "Sản phẩm [tên] đã hết hàng." Highlight SP hết hàng. Nút: "Xóa và tiếp tục" hoặc "Quay lại giỏ hàng".
EX-02
SP thay đổi giá khi checkout
3.2
Hiển thị warning: "Giá sản phẩm [tên] đã thay đổi từ X → Y." Yêu cầu xác nhận lại đơn hàng với giá mới.
EX-03
Mã giảm giá hết hạn / hết lượt
3.2.2
Xóa mã giảm giá khỏi đơn. Hiển thị: "Mã giảm giá [code] không còn hiệu lực." Cập nhật tổng thanh toán. Yêu cầu xác nhận lại.
EX-04
Thanh toán online timeout (30 phút)
Sau 3.4
Scheduler: sau 30 phút đơn PENDING_PAYMENT chưa nhận callback → tự động hủy đơn, trả tồn kho. Gửi email: "Đơn hàng [mã] đã hết hạn thanh toán."
EX-05
Lỗi hệ thống khi tạo đơn
3.2
Rollback transaction. Hiển thị: "Đã có lỗi xảy ra. Vui lòng thử lại." Giỏ hàng giữ nguyên. Log error.
EX-06
API vận chuyển lỗi
1.4
Retry 2 lần. Nếu thất bại: hiển thị "Tạm thời không tính được phí vận chuyển. Vui lòng thử lại sau." Cho phép đặt hàng COD với phí ship cố định 30.000đ (fallback).
Quy tắc nghiệp vụ:
Mã
Quy tắc
BR-CHK-01
Mã đơn hàng: TV-YYYYMMDD-XXXXX (X = số tự tăng trong ngày, padding 5 chữ số).
BR-CHK-02
Tồn kho tạm giữ (hold) 30 phút cho đơn thanh toán online. Hết 30 phút không thanh toán → tự hủy + trả tồn kho.
BR-CHK-03
Mỗi đơn hàng chỉ áp dụng 1 mã giảm giá.
BR-CHK-04
Trả góp chỉ hiển thị khi tổng đơn ≥ 3.000.000 VNĐ.
BR-CHK-05
COD giới hạn 20.000.000 VNĐ cho khách hàng mới (< 2 đơn hoàn thành). Khách quen không giới hạn.
BR-CHK-06
Freeship cho đơn hàng ≥ 5.000.000 VNĐ (nội thành HCM, HN, ĐN).
BR-CHK-07
Checkbox đồng ý điều khoản bắt buộc tick trước khi nhấn "Đặt hàng".
Cho phép nhân viên xem danh sách, xác nhận, tạo vận đơn, xử lý hoàn tiền và quản lý trạng thái đơn hàng.
Actor chính
ACT-ADMIN-ORD (Quản lý đơn hàng)
Actor phụ
ACT-SHIPPING, ACT-PAYMENT, ACT-EMAIL
Mức ưu tiên
P1 — Critical
Độ phức tạp
★★★
SRS tham chiếu
FR-ADM-003
User Story
US-9.1
Tiền điều kiện:
#
Điều kiện
PRE-01
Admin đã đăng nhập với role có quyền manage_orders.
PRE-02
Đơn hàng tồn tại trong hệ thống.
Luồng chính — Xác nhận và giao hàng:
Bước
Actor
Hệ thống
1
Admin truy cập "Quản lý đơn hàng".
Hiển thị bảng đơn hàng: Mã đơn | Khách hàng | SĐT | Tổng tiền | Thanh toán | Trạng thái | Ngày đặt | Hành động. Mặc định lọc: trạng thái "Chờ xác nhận", sắp xếp "Mới nhất". Hiển thị badge đếm số đơn "Chờ xác nhận" trên tab.
2
Admin click vào đơn hàng cần xử lý.
Hiển thị trang chi tiết đơn hàng gồm: Thông tin đơn: mã, ngày đặt, trạng thái, kênh thanh toán, trạng thái thanh toán. Khách hàng: tên, SĐT (click-to-call), email, số đơn đã mua, điểm thành viên. Sản phẩm: bảng (ảnh, tên, SKU, đơn giá, SL, thành tiền). Giao hàng: địa chỉ, phương thức, phí, thời gian dự kiến. Thanh toán: phương thức, trạng thái, transaction ID (nếu online). Ghi chú khách hàng: nội dung ghi chú của khách. Ghi chú nội bộ: textarea cho admin (khách hàng không nhìn thấy). Timeline: lịch sử trạng thái đơn hàng (timestamp + người thao tác). Hành động: nút theo trạng thái hiện tại.
3
Admin review đơn hàng, thêm ghi chú nội bộ (nếu cần).
Lưu ghi chú nội bộ vào orders.internal_note.
4
Admin nhấn "Xác nhận đơn hàng".
4.1. Kiểm tra tồn kho lần cuối. 4.2. Cập nhật orders.status = CONFIRMED, confirmed_at = now(). 4.3. Ghi log audit: {action: "ORDER_CONFIRMED", admin_id, order_id, timestamp}. 4.4. Gửi email + push notification cho khách: "Đơn hàng [mã] đã được xác nhận." 4.5. Hiển thị nút mới: "Tạo đơn vận chuyển".
5
Admin nhấn "Tạo đơn vận chuyển".
5.1. Gọi API GHTK/GHN: createShipment(order_info). 5.2. Nhận mã vận đơn: tracking_code. 5.3. Cập nhật orders.tracking_code, orders.shipping_provider. 5.4. Cập nhật orders.status = SHIPPING, shipped_at = now(). 5.5. Hiển thị nút: "In phiếu giao hàng" + "In phiếu xuất kho". 5.6. Gửi SMS cho khách: "Đơn hàng [mã] đang được giao. Mã vận đơn: [tracking]."
6
Admin nhấn "In phiếu giao hàng".
Generate PDF phiếu giao hàng gồm: barcode, thông tin người gửi/nhận, danh sách SP, ghi chú. Mở print dialog.
7
(Hệ thống tự động)
Đơn vị vận chuyển callback webhook khi trạng thái thay đổi: - "Đã lấy hàng" → cập nhật status. - "Đang giao" → cập nhật status. - "Đã giao" → cập nhật status = DELIVERED, delivered_at = now(). Gửi email + push cho khách: "Đơn hàng [mã] đã được giao thành công." - "Giao thất bại" → cập nhật status, thông báo cho Admin xử lý.
Luồng thay thế:
Mã
Tên
Mô tả
AF-01
Hủy đơn hàng (Admin)
Admin nhấn "Hủy đơn hàng". Hệ thống hiển thị dialog chọn lý do: "Hết hàng", "Khách yêu cầu hủy", "Thông tin không xác thực", "Khác". Admin chọn lý do, xác nhận hủy. Hệ thống: cập nhật status = CANCELLED, trả lại tồn kho, trả lại mã giảm giá (nếu có). Nếu đã thanh toán online → kích hoạt refund (→ AF-02). Gửi email/SMS thông báo hủy cho khách.
AF-02
Xử lý hoàn tiền
Áp dụng khi đơn đã thanh toán online bị hủy hoặc khách yêu cầu hoàn trả. Admin nhấn "Hoàn tiền". Hệ thống hiển thị: số tiền hoàn (mặc định = tổng đơn, cho phép hoàn 1 phần). Admin nhập số tiền, lý do, xác nhận. Hệ thống gọi API refund (VNPAY/MoMo). Cập nhật payments.status = REFUNDED. Gửi email cho khách: "Hoàn tiền [số tiền] cho đơn [mã]. Dự kiến nhận trong 5-7 ngày làm việc."
AF-03
Tìm kiếm đơn hàng
Admin nhập mã đơn hàng, tên KH hoặc SĐT vào ô tìm kiếm. Hệ thống tìm kiếm realtime (debounce 300ms). Hiển thị kết quả khớp.
Luồng ngoại lệ:
Mã
Điều kiện
Xử lý
EX-01
Tồn kho không đủ khi xác nhận
Hiển thị warning: "Sản phẩm [tên] chỉ còn X trong kho (đơn yêu cầu Y)." Cho phép Admin: "Xác nhận với SL khả dụng" hoặc "Liên hệ khách hàng" hoặc "Hủy đơn".
EX-02
API vận chuyển lỗi khi tạo đơn
Hiển thị: "Không thể tạo đơn vận chuyển qua [GHTK/GHN]. Lỗi: [mô tả]." Nút: "Thử lại" hoặc "Chuyển sang [đơn vị khác]" hoặc "Tạo thủ công (nhập mã vận đơn)".
EX-03
API refund lỗi
Hiển thị: "Hoàn tiền thất bại. Mã lỗi: [code]." Tạo ticket nội bộ cho bộ phận tài chính xử lý thủ công. Ghi log chi tiết.
Mô tả tương tác hoàn chỉnh giữa Actor và hệ thống để đạt được một mục tiêu cụ thể
Actor
Thực thể bên ngoài tương tác với hệ thống (người dùng hoặc hệ thống khác)
Main Flow
Luồng chính — kịch bản thành công (happy path)
Alternative Flow
Luồng thay thế — cách khác để đạt cùng mục tiêu
Exception Flow
Luồng ngoại lệ — xử lý lỗi, trường hợp bất thường
Pre-condition
Điều kiện phải đúng trước khi Use Case bắt đầu
Post-condition
Trạng thái hệ thống sau khi Use Case hoàn tất
<<include>>
Quan hệ bắt buộc: UC A luôn gọi UC B
<<extend>>
Quan hệ tùy chọn: UC B mở rộng UC A trong điều kiện nhất định
CSPRNG
Cryptographically Secure Pseudo-Random Number Generator
Ghi chú: Tài liệu này đặc tả 4 Use Case đại diện ở mức chi tiết đầy đủ (UC-01, UC-03, UC-08, UC-14, UC-25). Các Use Case còn lại sẽ được đặc tả theo cùng format trong quá trình phát triển từng Sprint. Mọi thay đổi cần được Product Owner phê duyệt và ghi nhận trong phần Lịch sử thay đổi.