# Phân Hệ Quản Lý Danh Sách Học Viên Đến Lịch Thi (Web Exam Schedule)
## 01. Use Case & Luồng Hoạt Động (Use Cases & Activity Workflows)

---

### 1. Tổng Quan Phân Hệ (Overview)

Phân hệ **Quản lý danh sách học viên đến lịch thi** (`web_exam_schedule`) là một trong những chức năng trọng tâm thuộc khối **Vận Hành Thi** trên hệ thống LMS. 

Phân hệ này cho phép:
- Quản trị viên, cán bộ vận hành thi và giáo vụ theo dõi danh sách tất cả học viên đã đến hạn hoặc được thiết lập lịch thi các bài kiểm tra/mocktest.
- Tạo lịch thi bổ sung (thi lại, thi sớm) cho từng học viên hoặc hàng loạt (Bulk actions).
- Đổi lịch thi (Re-schedule) với cơ chế đóng lịch cũ và khởi tạo lịch mới có ghi nhận đầy đủ lịch sử thay đổi (Audit trail).
- Gia hạn thời hạn nộp bài (Extend deadline) và đồng bộ thông báo nhắc thi (Mocktest Reminding) sang hệ thống Zeus / ICL.
- Xóa lịch thi (dành cho nhóm quyền đặc biệt) để dọn dẹp dữ liệu kiểm thử hoặc xử lý ngoại lệ.
- Xuất báo cáo dữ liệu lịch thi quy mô lớn dưới dạng file nén CSV/ZIP chạy nền qua Message Queue (Job Batching).

---

### 2. Danh Sách Tác Nhân (Actors)

| Tác nhân (Actor) | Vai trò & Trách nhiệm |
| :--- | :--- |
| **Học viên (Student)** | Đối tượng được gán lịch thi; nhận thông báo thời gian mở/đóng bài và làm bài thi theo lịch. |
| **Cán bộ Vận hành Thi / Admin** | Tra cứu, lọc danh sách, tạo mới lịch thi, đổi lịch thi, gia hạn hạn nộp và xuất báo cáo dữ liệu. |
| **Super Admin / Authorized Emails** | Nhóm người dùng có email đặc thù được phép thực hiện thao tác xóa lịch thi và dữ liệu hoàn thành liên quan. |
| **Hệ thống Zeus / ICL (External Service)** | Hệ thống backend quản trị học tập đối tác; đồng bộ lịch thi ban đầu (`zeus_start_date`, `zeus_deadline`) và nhận payload nhắc nhở lịch thi (`sendMocktestRemindingManualWithLog`). |
| **Hệ thống Moodle Core / LMS Engine** | Nơi lưu trữ cấu trúc cây học trình (`api_moodle`), quản lý lượt làm bài (`user_quiz_attempts`) và chấm điểm (`student_score`). |

---

### 3. Use Case Diagram (Sơ đồ Use Case Tổng Thể)

```mermaid
usecaseDiagram
    actor "Cán bộ Vận hành / Admin" as Operator
    actor "Super Admin / Quyền đặc biệt" as SuperAdmin
    actor "Hệ thống Zeus / ICL" as Zeus

    package "Phân Hệ Quản Lý DSHV Đến Lịch Thi (web_exam_schedule)" {
        usecase "UC01: Tra cứu & Lọc Danh Sách Lịch Thi" as UC01
        usecase "UC02: Tạo Lịch Thi Mới (Đơn lẻ / Hàng loạt)" as UC02
        usecase "UC03: Đổi Lịch Thi (Re-schedule)" as UC03
        usecase "UC04: Gia Hạn Thời Hạn Nộp Bài" as UC04
        usecase "UC05: Xóa Lịch Thi" as UC05
        usecase "UC06: Xuất Dữ Liệu Lịch Thi (Async Export)" as UC06
        usecase "UC07: Đồng Bộ Nhắc Thi Sang Zeus" as UC07
        usecase "UC08: Cấp Thêm Lượt Thi (Reset Allowance)" as UC08
    }

    Operator --> UC01
    Operator --> UC02
    Operator --> UC03
    Operator --> UC04
    Operator --> UC06

    SuperAdmin --> UC05

    UC02 ..> UC07 : <<include>>
    UC02 ..> UC08 : <<extend>> (Khi là Thi lại)
    UC03 ..> UC08 : <<extend>> (Khi là Thi lại)
    UC04 ..> UC07 : <<include>>
    UC07 --> Zeus
```

---

### 4. Chi Tiết Các Use Case Chính

#### 4.1. Use Case UC01: Tra cứu & Lọc Danh Sách Lịch Thi
- **Mã Use Case**: `UC_ES_01`
- **Mô tả**: Cho phép người dùng tìm kiếm, lọc danh sách học viên có lịch thi dựa trên đa tiêu chí kết hợp.
- **Tiền điều kiện**: Người dùng đăng nhập hệ thống và có quyền `exam-schedule.getExamSchedule`.
- **Dữ liệu đầu vào (Bộ lọc)**:
  - *Thời gian mở bài*: Từ ngày (`start_date`) - Đến ngày (`end_date`).
  - *Thời gian đóng bài*: Từ ngày (`close_start_date`) - Đến ngày (`close_end_date`).
  - *Thông tin học viên*: Số điện thoại (`phone`), Email (`email`), Mã học viên (`student_code` - mảng).
  - *Thông tin học trình*: Mã lớp (`class_code`), Sản phẩm / Category (`product`), Khóa học (`course`), Bài thi / Section (`exam`).
  - *Trạng thái*: Trạng thái thi (`exam_status`: 0 - Chờ nộp, 1 - Đã nộp, 2 - Không nộp), Trạng thái xử lý điểm (`point_status`: 0 - Đang xử lý, 1 - Hoàn tất, 2 - Giao chấm lại, 3 - Đã hủy), Yêu cầu (`requirement`: 0 - Đúng lộ trình, 1 - Thi lại, 2 - Thi sớm), Loại học viên (`is_vip`: 1 - VIP, 0 - Thường).
- **Luồng hoạt động chính**:
  1. Người dùng truy cập trang `exam-schedule.index`.
  2. Frontend gửi request AJAX `GET /exam-schedule/exam-schedule` kèm các query params.
  3. Controller kết hợp `tenantConnection`, dựng câu truy vấn qua `buildExamScheduleQuery` lọc bảng phẳng `student_exam_schedules` kết hợp join bảng `students` và subquery `course_student` kiểm tra VIP.
  4. Hệ thống phân trang (mặc định 10 bản ghi/trang), format dữ liệu khớp giao diện (bao gồm xác định root category qua `ApiMoodleService::getRootCategory`), kiểm tra các cờ phân quyền (`can_delete`, `can_update`, `can_extend`).
  5. Frontend render bảng danh sách kèm các badge trạng thái, action dropdown và phân trang.

---

#### 4.2. Use Case UC02: Tạo Lịch Thi Mới (Store Exam Schedule)
- **Mã Use Case**: `UC_ES_02`
- **Mô tả**: Tạo một hoặc nhiều lịch thi cho danh sách học viên và danh sách bài thi/section được chỉ định.
- **Tiền điều kiện**: Người dùng có quyền `exam-schedule.storeExamSchedule`.
- **Dữ liệu đầu vào**:
  - Danh sách ID học viên: `student_ids` (mảng số nguyên).
  - Danh sách ID Section: `exam_ids` (mảng số nguyên tương ứng section trong `api_moodle`).
  - Phân loại yêu cầu: `requirement_type` (1: Thi lại, 2: Thi sớm).
  - Thời gian: `start_date` (Y-m-d H:i:s), `deadline` (Y-m-d H:i:s).
- **Luồng hoạt động chính**:
  ```mermaid
  sequenceDiagram
      autonumber
      actor User as Cán bộ Vận hành
      participant FE as Frontend UI (Modal)
      participant Ctrl as ExamScheduleController
      participant DB as Database (Tenant)
      participant Zeus as Zeus / ICL Service

      User->>FE: Chọn Học viên, Section, Yêu cầu, Start Date & Deadline -> Bấm "Lưu"
      FE->>Ctrl: POST /exam-schedule/store-exam-schedule
      Ctrl->>Ctrl: Validate dữ liệu đầu vào
      Ctrl->>DB: Truy vấn cây Moodle (Section -> Course -> Category)
      Ctrl->>DB: Lấy thông tin sinh viên (lấy zeus_id)
      
      loop Với mỗi Student và mỗi Section
          Ctrl->>DB: Lấy student_score mới nhất (nếu có) để giữ nguyên trạng thái thi
          Ctrl->>DB: INSERT student_exam_schedules (status = 1)
          Ctrl->>DB: INSERT student_exam_schedule_histories (action = 'create')
          alt requirement_type == 1 (Thi lại)
              Ctrl->>DB: Tăng reset_allowance trong user_quiz_attempts (+1)
          end
          opt Học viên có zeus_id
              Ctrl->>Zeus: sendMocktestRemindingManualWithLog (Payload mocktest)
              Zeus-->>Ctrl: Kết quả & ghi log student_exam_schedule_icl_logs
          end
      end
      
      Ctrl-->>FE: HTTP 200 JSON {success: true, message: 'Tạo lịch thi hàng loạt thành công!'}
      FE->>User: Hiển thị SweetAlert2 thông báo & reload lại bảng danh sách
  ```

---

#### 4.3. Use Case UC03: Đổi Lịch Thi (Update / Re-schedule)
- **Mã Use Case**: `UC_ES_03`
- **Mô tả**: Thay đổi khung thời gian làm bài của học viên cho một bài thi cụ thể. Hệ thống thực hiện nguyên tắc bất biến (Immutability): Đóng bản ghi lịch cũ (`status = 0`) và tạo bản ghi lịch mới (`status = 1`) để bảo toàn lịch sử.
- **Tiền điều kiện**: Người dùng có quyền `exam-schedule.updateExamSchedule`.
- **Ràng buộc nghiệp vụ quan trọng**:
  - `new_start_date` **phải lớn hơn** thời gian đóng bài (`deadline` hoặc `zeus_deadline`) của lịch thi hiện tại.
- **Luồng hoạt động chính**:
  ```mermaid
  sequenceDiagram
      autonumber
      actor User as Cán bộ Vận hành
      participant FE as Frontend (Modal Đổi lịch)
      participant Ctrl as ExamScheduleController
      participant DB as Database (Tenant)

      User->>FE: Nhập ngày mở mới & ngày đóng mới -> Xác nhận Đổi lịch
      FE->>Ctrl: POST /exam-schedule/update-exam-schedule
      Ctrl->>Ctrl: Validate đầu vào (student_ids[], course_id, section_id, start_date, deadline)
      Ctrl->>DB: Query các bản ghi lịch thi gốc (StudentExamSchedule)
      
      Ctrl->>DB: Bắt đầu Database Transaction
      loop Với từng studentId
          Ctrl->>Ctrl: Kiểm tra new_start_date > timeClose (deadline cũ)
          alt Vi phạm điều kiện thời gian
              Ctrl-->>FE: HTTP 400 (Ngày bắt đầu phải lớn hơn thời gian đóng hiện tại)
          end
          Ctrl->>DB: UPDATE student_exam_schedules (Bản ghi cũ: status = 0)
          Ctrl->>DB: INSERT student_exam_schedules (Bản ghi mới: status = 1, exam_status = 0, ...)
          Ctrl->>DB: INSERT student_exam_schedule_histories (action = 'update', ghi old -> new)
      end
      Ctrl->>DB: Commit Transaction
      
      alt requirement_type == 1 (Thi lại)
          Ctrl->>DB: Cấp thêm lượt thi trong user_quiz_attempts (reset_allowance + 1)
      end
      
      Ctrl-->>FE: HTTP 200 JSON {success: true}
      FE->>User: Cập nhật lại giao diện danh sách
  ```

---

#### 4.4. Use Case UC04: Gia Hạn Thời Hạn Nộp Bài (Extend Deadline)
- **Mã Use Case**: `UC_ES_04`
- **Mô tả**: Cho phép kéo dài thời gian nộp bài (`deadline`) cho học viên khi chưa hoàn thành bài thi.
- **Tiền điều kiện**: Người dùng có quyền `exam-schedule.extendExamSchedule`.
- **Ràng buộc nghiệp vụ**:
  - `new_deadline` **không được nhỏ hơn** thời gian đóng bài hiện tại (`deadline` hoặc `zeus_deadline`).
  - Ngày bắt đầu (`start_date`) được giữ nguyên, trạng thái lịch được đảm bảo mở (`status = 1`).
- **Luồng hoạt động chính**:
  1. Người dùng chọn 1 hoặc nhiều học viên cần gia hạn, mở Modal Gia hạn.
  2. Chọn `new_deadline` và bấm **Xác nhận gia hạn**.
  3. Controller validate dữ liệu, mở Transaction.
  4. Với mỗi học viên:
     - Ghi nhận lịch sử vào `student_exam_schedule_histories` (`action = 'extend'`).
     - Cập nhật trực tiếp `deadline = $newDeadline`, `status = 1` trên bản ghi lịch thi hiện tại.
     - Đồng bộ thời hạn mới sang Zeus qua `sendMocktestRemindingManualWithLog`.
  5. Commit Transaction và trả về thông báo thành công.

---

#### 4.5. Use Case UC05: Xóa Lịch Thi (Delete Exam Schedule)
- **Mã Use Case**: `UC_ES_05`
- **Mô tả**: Xóa hoàn toàn một bản ghi lịch thi cùng các dữ liệu log, history và trạng thái phiên thi Zeus liên quan.
- **Tiền điều kiện**: Người dùng đăng nhập bằng email nằm trong whitelist được cấu hình (`$allowedDeleteEmails` bao gồm các email chỉ định của ban vận hành/dev).
- **Luồng xử lý xóa liên hoàn (Cascade Clean-up)**:
  ```mermaid
  flowchart TD
      Start([Bắt đầu Xóa Lịch Thi]) --> CheckAuth{Email trong whitelist?}
      CheckAuth -- Không --> Err403[Báo lỗi không có quyền]
      CheckAuth -- Có --> FindRecord[Tìm StudentExamSchedule theo schedule_id]
      FindRecord --> CheckExist{Tìm thấy?}
      CheckExist -- Không --> Err404[Báo lỗi 404 Không tìm thấy]
      CheckExist -- Có --> BeginTx[Bắt đầu DB Transaction]
      
      BeginTx --> DelZeus[1. Xóa zeus_session_completions theo course_id, section_id, zeus_id]
      DelZeus --> DelHist[2. Xóa student_exam_schedule_histories theo schedule_id]
      DelHist --> DelLogs[3. Xóa student_exam_schedule_icl_logs theo student_exam_schedule_id]
      DelLogs --> DelSchedule[4. Xóa chính bản ghi student_exam_schedules]
      
      DelSchedule --> CommitTx[Commit DB Transaction]
      CommitTx --> Success[Trả về HTTP 200 Thành công]
  ```

---

#### 4.6. Use Case UC06: Xuất Báo Cáo Dữ Liệu (Async Bulk Export)
- **Mã Use Case**: `UC_ES_06`
- **Mô tả**: Xuất toàn bộ dữ liệu lịch thi theo đúng các bộ lọc đang áp dụng ra file CSV (nén thành ZIP nếu dữ liệu lớn) chạy nền bằng Queue Job, không gây timeout hoặc tràn bộ nhớ web server.
- **Luồng hoạt động**:
  ```mermaid
  sequenceDiagram
      autonumber
      actor User as Cán bộ Vận hành
      participant FE as Frontend (JS Polling)
      participant Ctrl as ExamScheduleController
      participant Cache as Redis / Cache Store
      participant Queue as ExportExamScheduleJob (Queue Worker)
      participant Storage as Disk Storage

      User->>FE: Bấm "Xuất dữ liệu"
      FE->>Ctrl: POST /exam-schedule/export (Kèm toàn bộ bộ lọc hiện tại)
      Ctrl->>Ctrl: Đếm tổng số bản ghi (Count), tính số batch (1000 records/batch)
      Ctrl->>Ctrl: Sinh export_id (UUID)
      Ctrl->>Cache: Lưu trạng thái export_progress_{exportId} = {status: 'processing', total_batches, exported_batches: 0}
      Ctrl->>Queue: Dispatch ExportExamScheduleJob(exportId, filters, tenantConnection, batchSize, totalBatches)
      Ctrl-->>FE: Trả về {success: true, export_id}
      
      par Quá trình xuất nền (Queue)
          loop Với từng batch từ 0 -> totalBatches
              Queue->>Queue: Query 1000 bản ghi kèm format (UTF-8 BOM, Tiếng Việt)
              Queue->>Storage: Ghi file CSV part_X
              Queue->>Cache: Cập nhật exported_batches += 1
          end
          Queue->>Storage: Nén tất cả các file CSV part thành file exam_schedules_export.zip
          Queue->>Storage: Xóa các file CSV tạm
          Queue->>Cache: Cập nhật status = 'completed', file_url = download link
      and Quá trình Polling Frontend
          loop Mỗi 1-2 giây
              FE->>Ctrl: GET /exam-schedule/export-progress/{export_id}
              Ctrl->>Cache: Lấy progress
              Ctrl-->>FE: Trả về tiến độ hiện tại (%)
          end
      end
      
      FE->>FE: Tiến độ đạt 100% / completed
      FE->>User: Kích hoạt tải tự động window.location.href = file_url
  ```
