# Phân Hệ Quản Lý Chấm Thủ Công (Web Exam Grading)
## 02. Quy Tắc Nghiệp Vụ & Logic Chi Tiết (Business Logic & Rules)

---

### 1. Phân Định Lượt Chấm Lần 1 (`assign_type = 1`)

Bảng `teacher_assigned_histories` quản lý nhiều loại phân công chấm khác nhau. Phân hệ **Quản lý chấm thủ công** (`web_exam_grading`) được định nghĩa riêng biệt cho **Lượt chấm lần 1**:

$$\text{WHERE } tah.assign\_type = 1$$

- **`assign_type = 1` (Chấm lần 1)**: Lượt chấm tiêu chuẩn được phân công cho giáo viên chuyên môn sau khi học viên nộp bài.
- **`assign_type = 2` (Chấm phúc khảo)**: Lượt chấm lại hoặc kiểm định QA (thuộc phân hệ `web_exam_regrading`).

---

### 2. Quản Lý Trạng Thái Phân Công Chấm (`tah.status`)

Trạng thái phân công chấm của từng giáo viên cho từng Part bài thi được kiểm soát qua cột `status` trong bảng `teacher_assigned_histories`:

| Trạng Thái (`tah.status`) | Tên Hiển Thị | Ý Nghĩa & Hành Vi Hệ Thống |
| :---: | :--- | :--- |
| **0** | **Chờ chấm / Đang chấm** | Bài thi đã được gán cho giáo viên nhưng giáo viên chưa lưu kết quả chấm cuối cùng. |
| **1** | **Đã hoàn thành** | Giáo viên đã nhập điểm, nhận xét và bấm lưu hoàn tất bài chấm. |
| **2** | **Đã thu hồi / Chuyển giao** | Bài thi đã bị cán bộ vận hành thu hồi quyền chấm hoặc chuyển giao cho giáo viên khác. Giáo viên cũ sẽ không còn quyền mở sửa bài thi. |

---

### 3. Logic Phân Quyền Hiển Thị & Bảo Mật Danh Sách Bài Chấm

Hệ thống áp dụng cơ chế phân quyền bảo mật dữ liệu 2 cấp độ:

```mermaid
flowchart TD
    Start([Người dùng gọi getExamGrading]) --> CheckPerm{Có quyền exam-grading.viewAllGrading?}
    
    CheckPerm -- CÓ (Admin / Quản lý) --> AdminView[Cho phép xem danh sách phân công của TẤT CẢ giáo viên]
    AdminView --> FilterTeacher[Cho phép áp dụng bộ lọc theo teacher_id bất kỳ]
    
    CheckPerm -- KHÔNG (Giáo viên) --> TeacherView[Chỉ cho phép xem bài gán cho chính tài khoản đăng nhập]
    TeacherView --> ForceTeacherID[Bắt buộc: tah.user_id = currentTeacherId]
    ForceTeacherID --> FilterLatest[Bắt buộc: tah.id = MAX(tah.id) cho Part đó]
    
    FilterTeacher --> ExecQuery[Thực thi Query dữ liệu an toàn]
    FilterLatest --> ExecQuery
```

*Đoạn mã truy vấn phân quyền thực tế trong Controller:*
```php
$hasViewAllPermission = checkPermissionUser('exam-grading.viewAllGrading', $tenantConnection);

if (!$hasViewAllPermission) {
    $user = Auth::user();
    $userTenant = HelperTenant::getUserFromTenant($user->email, $tenantConnection);
    $teacherUserId = $userTenant ? $userTenant->id : 0;

    // Chỉ xem bài gán cho mình
    $query->where('tah.user_id', $teacherUserId);
    
    // Đảm bảo chỉ lấy bản ghi phân công mới nhất của Part đó
    $query->whereRaw('tah.id = (
        SELECT MAX(tah2.id) 
        FROM teacher_assigned_histories as tah2 
        WHERE tah2.student_exam_histories_teacher_id = seht.id
    )');
}
```

---

### 4. Logic Tính Toán & Đối Soát Điểm Trước / Sau Review

Hệ thống cung cấp cơ chế đối soát độc đáo giúp quản trị viên và giáo viên so sánh chất lượng chấm:

#### 4.1. Điểm trước Review (`score_before_review`):
- Là điểm số ban đầu do hệ thống AI Hub hoặc chấm tự động của LMS sinh ra trước khi có sự can thiệp thủ công của giáo viên.
- Được tính toán động qua `IclScoreService::calculateInitialScoresWithData` với tham số `manual_grading_status = 0` và danh sách `teacherRecords` rỗng.

#### 4.2. Điểm sau Review (`score_after_review`):
- Là điểm số chính thức sau khi giáo viên hoàn thành chấm và lưu kết quả.
- Được trích xuất từ bản ghi `StudentScoreGradingHistory` mới nhất có `assign_type = 1`.
- Controller thực hiện giải mã (JSON parse) trường `data_ems_snapshot`, sắp xếp các kỹ năng theo thứ tự chuẩn quốc tế:
  $$\text{Listening (1)} \rightarrow \text{Reading (2)} \rightarrow \text{Writing (3)} \rightarrow \text{Speaking (4)} \rightarrow \text{Vocabulary (5)} \rightarrow \text{Exercise (6)}$$
- Trích xuất điểm Overall chốt của bài thi.

---

### 5. Logic Nhận Diện Bài Thi Bất Thường (`abnormal`)

Hệ thống tự động phát hiện và cảnh báo các bài thi có dấu hiệu bất thường dựa trên trường `abnormal_reasons` trong bảng `student_score`:

```sql
CASE 
    WHEN ss.abnormal_reasons IS NULL 
      OR ss.abnormal_reasons = '[]' 
      OR ss.abnormal_reasons = '' 
    THEN 'Bình thường' 
    ELSE 'Bất thường' 
END as abnormal
```

- **Bình thường**: Bài thi diễn ra đúng quy trình, không vi phạm các ngưỡng cảnh báo.
- **Bất thường**: Bài thi rơi vào các trường hợp:
  - Điểm số chênh lệch đột biến giữa các kỹ năng (ví dụ: Reading 9.0 nhưng Writing 3.0).
  - Thời gian làm bài quá ngắn so với thời lượng tiêu chuẩn.
  - Có nghi vấn gian lận hoặc vi phạm quy chế thi.
  - Chuỗi JSON `abnormal_reasons` chứa chi tiết lý do phục vụ hiển thị Tooltip giải thích trên giao diện.

---

### 6. Logic Quản Lý Thời Hạn Chấm (Assigned Time & Deadline)

Mỗi lượt phân công chấm đều đi kèm 2 mốc thời gian quan trọng trong bảng `teacher_assigned_histories`:
1. **`assigned_at` (Thời gian phân chấm)**: Thời điểm bài thi được gán cho giáo viên.
2. **`expires_at` (Hạn chót chấm - Deadline)**: Thời hạn cuối cùng giáo viên phải hoàn thành bài chấm (thường được cấu hình tự động sau 24h hoặc 48h kể từ khi gán).
3. **Cảnh báo tiến độ**:
   - Nếu $\text{NOW} > \text{expires\_at}$ và `status = 0`: Bài thi được đánh dấu **Quá hạn chấm (Overdue)** trên giao diện để cán bộ vận hành đôn đốc hoặc thu hồi gán cho giáo viên khác.
