# Phân Hệ Quản Lý Chấm Thủ Công (Web Exam Grading)
## 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ý chấm thủ công** (`web_exam_grading`) là không gian làm việc chuyên dụng dành cho **Giáo viên chấm thi** và **Cán bộ quản lý vận hành chấm** trên hệ thống LMS.

Phân hệ tập trung vào:
- **Theo dõi tiến độ chấm thi**: Quản lý danh sách các bài thi tự luận (Writing, Speaking) được phân công chấm lần 1 (`assign_type = 1`).
- **Phân quyền hiển thị theo vai trò**:
  - *Giáo viên thông thường*: Chỉ thấy các bài thi được gán trực tiếp cho tài khoản của mình.
  - *Quản trị viên / Cán bộ có quyền `exam-grading.viewAllGrading`*: Xem toàn bộ danh sách phân công chấm của tất cả giáo viên trong hệ thống.
- **Đối soát điểm Trước & Sau Review**: Hiển thị so sánh trực quan điểm số AI/hệ thống chấm ban đầu (`score_before_review`) và điểm giáo viên chấm hoàn tất (`score_after_review`).
- **Cảnh báo bất thường (Abnormal Status)**: Nhận diện và hiển thị lý do bài thi bị đánh dấu bất thường (chênh lệch điểm, thời gian làm bài bất thường, vi phạm quy chế).
- **Xuất báo cáo chấm chi tiết**: Xuất dữ liệu chấm thi đa chiều (đề bài, bài làm học viên, điểm AI, điểm GV, nhận xét, hạn chót chấm) qua Queue Background.

---

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

| Tác nhân (Actor) | Vai trò & Trách nhiệm |
| :--- | :--- |
| **Giáo viên chấm thi (Teacher)** | Theo dõi danh sách bài thi được phân công chấm, kiểm soát thời hạn chấm (`deadline`), mở giao diện chấm và hoàn tất bài chấm. |
| **Cán bộ Quản lý Chấm / Admin** | Giám sát toàn bộ tiến độ chấm của đội ngũ giáo viên, lọc theo giáo viên/kỹ năng/trạng thái, phát hiện bài trễ hạn để đôn đốc hoặc tái phân công. |
| **Hệ thống AI Hub / EMS Service** | Chấm điểm tự động ban đầu và cung cấp snapshot dữ liệu bài làm. |

---

### 3. Use Case Diagram (Sơ đồ Use Case)

```mermaid
usecaseDiagram
    actor "Giáo viên Chấm thi" as Teacher
    actor "Cán bộ Quản trị / Vận hành" as Admin
    actor "Hệ thống Background Queue" as Queue

    package "Phân Hệ Quản Lý Chấm Thủ Công (web_exam_grading)" {
        usecase "UC01: Tra cứu & Lọc Danh Sách Phân Công Chấm" as UC01
        usecase "UC02: Xem So Sánh Điểm Trước & Sau Review" as UC02
        usecase "UC03: Mở Giao Diện Chấm Bài Thi" as UC03
        usecase "UC04: Theo Dõi Hạn Chót Chấm (Deadline Tracking)" as UC04
        usecase "UC05: Kiểm Tra Trạng Thái Bất Thường (Abnormal Reason)" as UC05
        usecase "UC06: Xuất Báo Cáo Chấm Thi Ra CSV / ZIP" as UC06
    }

    Teacher --> UC01
    Teacher --> UC02
    Teacher --> UC03
    Teacher --> UC04

    Admin --> UC01
    Admin --> UC02
    Admin --> UC03
    Admin --> UC04
    Admin --> UC05
    Admin --> UC06

    UC06 --> Queue
```

---

### 4. Chi Tiết Luồng Hoạt Động Cốt Lõi (Activity Workflows)

#### 4.1. Luồng 1: Tra Cứu & Lấy Danh Sách Bài Chấm (`exam-grading.getExamGrading`)
- **Mã Use Case**: `UC_EG_01`
- **Mô tả**: Lấy danh sách các phân công chấm bài tự luận lần 1 (`tah.assign_type = 1`), áp dụng phân quyền nghiêm ngặt theo vai trò người dùng.
- **Sequence Diagram**:
  ```mermaid
  sequenceDiagram
      autonumber
      actor User as Giáo viên / Quản trị viên
      participant FE as Giao diện Quản lý Chấm (index-v2.blade.php / exam-grading-v2.js)
      participant Ctrl as ExamGradingController
      participant Perm as Permission Resolver
      participant DB as Database (Tenant Connection)

      User->>FE: Truy cập trang hoặc áp dụng Bộ lọc
      FE->>Ctrl: GET /exam-grading/list (Kèm query params)
      Ctrl->>Perm: checkPermissionUser('exam-grading.viewAllGrading')
      
      alt Không có quyền viewAllGrading (Là Giáo viên)
          Ctrl->>Ctrl: Gán điều kiện: tah.user_id = teacherId VÀ tah.id = MAX(tah.id)
      else Có quyền viewAllGrading (Là Admin)
          Ctrl->>Ctrl: Cho phép xem toàn bộ giáo viên và lọc theo teacher_id tùy chọn
      end
      
      Ctrl->>DB: Thực thi Query phân trang (tah JOIN seht JOIN ss JOIN students JOIN api_moodle)
      Ctrl->>DB: Batch Load StudentExamHistory & StudentScoreGradingHistory
      
      loop Với từng bản ghi bài chấm
          Ctrl->>Ctrl: Tính score_before_review qua IclScoreService
          Ctrl->>Ctrl: Parse score_after_review từ data_ems_snapshot
          Ctrl->>Ctrl: Định dạng Tên kỹ năng, Tên sản phẩm Root Category, Trạng thái Bất thường
      end
      
      Ctrl-->>FE: Trả về HTTP 200 JSON {data, can_view_all_grading}
      FE->>User: Render bảng dữ liệu, hiển thị Badges điểm số & Trạng thái chấm
  ```

---

#### 4.2. Luồng 2: Đối Soát Điểm Trước & Sau Review (`score_before_review` vs `score_after_review`)
- **Mã Use Case**: `UC_EG_02`
- **Mô tả**: Cung cấp cái nhìn đối sánh giữa điểm AI/hệ thống chấm ban đầu và điểm sau khi giáo viên hoàn thành chấm.
- **Activity Flowchart**:
  ```mermaid
  flowchart TD
      Start([Bắt đầu xử lý điểm hiển thị]) --> GetScoreBefore[1. Tính toán score_before_review]
      GetScoreBefore --> CheckExamHist{StudentExamHistory có dữ liệu?}
      CheckExamHist -- Có --> CalcInitial[Gọi IclScoreService::calculateInitialScoresWithData với manual_grading_status = 0]
      CheckExamHist -- Không --> SetNullBefore[score_before_review = null]
      
      CalcInitial --> GetScoreAfter[2. Trích xuất score_after_review]
      SetNullBefore --> GetScoreAfter
      
      GetScoreAfter --> CheckGradingHist{Có bản ghi StudentScoreGradingHistory assign_type = 1?}
      CheckGradingHist -- Có --> ParseEms[Gọi parseScoresFromDataEms từ data_ems_snapshot]
      CheckGradingHist -- Không --> SetNullAfter[score_after_review = null]
      
      ParseEms --> CheckOverall{Có overall_score trong snapshot?}
      CheckOverall -- Có --> BindOverall[Gán overall score chính xác]
      CheckOverall -- Không --> CalcMean[Tính trung bình các kỹ năng con]
      
      BindOverall --> FinishScore([Hoàn thành Format điểm cho UI])
      CalcMean --> FinishScore
      SetNullAfter --> FinishScore
  ```

---

#### 4.3. Luồng 3: Mở Modal Chấm Bài Trực Tiếp Từ Danh Sách
- **Mã Use Case**: `UC_EG_03`
- **Mô tả**: Giáo viên bấm vào nút "Chấm bài" trên một dòng phân công để mở trực tiếp modal chấm bài thi (`exam-detail-modal.edit.js`).
- **Luồng hoạt động**:
  1. Người dùng bấm biểu tượng chiếc bút / nút "Chấm bài" tại dòng bài thi có `student_score_id`.
  2. Frontend kích hoạt hàm `openTeacherExamDetail(studentScoreId)`.
  3. Modal khởi tạo `window._syncedWritingScores = new Set()`, hiển thị loading và gọi API `getTeacherAssignedOverview`.
  4. Hệ thống nạp bài làm học viên và mở đúng Part kỹ năng được phân công để giáo viên thực hiện chấm.

---

#### 4.4. Luồng 4: Xuất Báo Cáo Chấm Thi Quy Mô Lớn (`exam-grading.export`)
- **Mã Use Case**: `UC_EG_04`
- **Mô tả**: Xuất dữ liệu chấm bài (bao gồm đề bài, bài viết học viên, kết quả chấm, điểm AI, điểm GV, thời gian phân chấm, hạn chót và nhận xét) ra file CSV nén ZIP chạy nền qua Queue Job (`ExportExamGradingJob`).
- **Sequence Diagram**:
  ```mermaid
  sequenceDiagram
      autonumber
      actor User as Người dùng
      participant FE as Frontend (Polling Modal)
      participant Ctrl as ExamGradingController
      participant Cache as Redis Cache
      participant Job as ExportExamGradingJob (Queue)
      participant Disk as Disk Storage

      User->>FE: Bấm "Xuất dữ liệu"
      FE->>Ctrl: POST /exam-grading/export (Kèm bộ lọc)
      Ctrl->>Ctrl: Đếm tổng số bản ghi (batch_size = 5000)
      Ctrl->>Ctrl: Sinh export_id (UUID)
      Ctrl->>Cache: Khởi tạo export_progress_{exportId} status = processing
      Ctrl->>Job: Dispatch ExportExamGradingJob
      Ctrl-->>FE: Trả về {success: true, export_id}
      
      par Queue Worker xử lý nền
          loop Từng batch 5000 records
              Job->>Job: Query dữ liệu, format UTF-8 BOM, trích xuất đề & bài làm
              Job->>Disk: Ghi file exam_grading_part_X.csv
              Job->>Cache: Cập nhật tiến độ exported_batches
          end
          Job->>Disk: Nén các file CSV thành exam_grading_export.zip
          Job->>Cache: Cập nhật status = completed, file_url
      and Frontend Polling
          loop Mỗi 1.5s
              FE->>Ctrl: GET /exam-grading/export-progress/{export_id}
              Ctrl->>Cache: Lấy progress
              Ctrl-->>FE: Trả về % tiến độ
          end
      end
      
      FE->>User: Tải tự động file ZIP qua GET /exam-grading/download/{export_id}
  ```
