# 08. BẪY NGẦM & HƯỚNG DẪN XỬ LÝ SỰ CỐ (GOTCHAS & TROUBLESHOOTING)

Tài liệu này tổng hợp các "bẫy ngầm" kỹ thuật (Gotchas), lỗi tiềm ẩn và checklist kiểm tra bắt buộc trước khi chỉnh sửa hoặc deploy tính năng lên môi trường Production.

---

## 1. Danh Sách Các Bẫy Ngầm Kỹ Thuật (Top Gotchas)

### ⚠️ Bẫy 1: Quên thiết lập Tenant Connection khi Query
- **Hiện tượng**: Query không ra dữ liệu hoặc báo lỗi `Table not found` / dữ liệu bị rỗng.
- **Nguyên nhân**: Laravel mặc định trỏ vào `mysql` (Master DB) thay vì cơ sở dữ liệu của Tenant.
- **Cách khắc phục**:
  ```php
  // ❌ SAI:
  $histories = StudentExamHistory::where('idHistoryContest', $id)->get();

  // ✅ ĐÚNG:
  $tenantConnection = HelperTenant::getCurrentTenantConnection();
  $histories = StudentExamHistory::on($tenantConnection)->where('idHistoryContest', $id)->get();
  ```

---

### ⚠️ Bẫy 2: Cấu trúc JSON bài thi dạng 1 cấp vs dạng Lồng nhóm 2 cấp
- **Hiện tượng**: Học sinh có làm bài nhưng giao diện Modal chi tiết hiển thị toàn bộ là *"Bỏ qua"*.
- **Nguyên nhân**: Trong `listQuestionGraded.userAnswer`, một số đoạn văn (Passage 1, Passage 3 Q17-20) lưu `group.answer` là mảng ID trực tiếp `[4091932]` (cấp 1), trong khi các cụm câu hỏi lại lưu lồng đối tượng `[{ idChildQuestion: ..., answer: ... }]` (cấp 2). Nếu dùng `.find()` trên mảng ID nguyên thủy sẽ bị lỗi/trả về undefined.
- **Cách khắc phục**: Sử dụng hàm `getReadingUserAnswer` chuẩn trong `exam-detail-modal-v3.js` hỗ trợ cả 2 cấp:
  ```javascript
  // 1. Kiểm tra trực tiếp cấp 1: group.idChildQuestion == targetId
  // 2. Nếu là mảng object con: group.answer.find(a => typeof a === 'object' && a.idChildQuestion == targetId)
  ```

---

### ⚠️ Bẫy 3: Trùng lặp gửi Email & Bỏ sót cờ `$force` khi Phúc khảo
- **Hiện tượng**: Học sinh nhận email nhiều lần cho cùng 1 bài thi, HOẶC sau khi QA sửa điểm phúc khảo học sinh lại không nhận được email điểm mới.
- **Nguyên nhân**:
  - Không check bảng `student_exam_result_mail_logs` $\rightarrow$ Gửi trùng lặp.
  - Không truyền `$force = true` khi QA hoàn tất chấm lại $\rightarrow$ Bị chặn gửi vì đã có log cũ.
- **Cách khắc phục**:
  ```php
  // Khi tự động kiểm tra đủ điểm: notifyThirdParty($idHistoryContest, false);
  // Khi QA chấm lại / Phúc khảo xong: notifyThirdParty($idHistoryContest, true);
  ```

---

### ⚠️ Bẫy 4: Lấy thời gian làm bài `timeFinish` không chuẩn
- **Hiện tượng**: Cột thời gian làm bài hiển thị sai hoặc rỗng.
- **Nguyên nhân**: Một bài thi có thể có nhiều row trong `student_exam_histories`, lấy nhầm row chưa nộp hoặc bản ghi cũ.
- **Cách khắc phục**: Luôn lấy bản ghi có `student_score_id` và được tạo muộn nhất (`orderBy('id', 'desc')->first()`) để lấy chính xác mốc `timeFinish`.

---

## 2. Checklist Kiểm Tra Trước Khi Deploy (Pre-deployment Checklist)

Trước khi commit và push code lên nhánh Production (`hocmai_prod`), hãy đảm bảo:

- [ ] Đã kiểm tra cú pháp PHP: `php -l <file_da_sua>.php`.
- [ ] Đã kiểm tra cú pháp JavaScript: `node -c <file_da_sua>.js`.
- [ ] Đã kiểm tra mọi truy vấn DB đều có `on($tenantConnection)` hoặc `connection($tenantConnection)`.
- [ ] Đã kiểm tra quyền hạn (Permission Route) trong `Helper.php` và bảng `permissions`.
- [ ] Nếu sửa logic bóc tách JSON, đã mở [`public/exam-json-tester.html`](file:///var/www/html/hocmai_testsite/public/exam-json-tester.html) để test thử các mẫu Reading / Listening / Vocab.
- [ ] Đã kiểm tra không làm ảnh hưởng đến các luồng API của bên thứ 3.
