Tài liệu dự án · Sách ôn thi Hybrid TNTHPT

#08 – Thiết kế API

Giai đoạn: Thiết kế hệ thống · Phiên bản 0.1 – 29/10/2026

Mức tổng quan để chốt hợp đồng giữa frontend và backend. Đặc tả chi tiết sẽ được sinh tự động dạng OpenAPI 3.1 từ mã nguồn (NFR-MAINT-01).

#1. Quy ước chung

Mục Quy ước
Kiểu REST + JSON, tiền tố /api/v1
Xác thực Cookie phiên (web) hoặc Authorization: Bearer <token>; CMS dùng tiền tố /api/v1/admin
Tên trường camelCase; thời gian ISO 8601 có múi giờ (2026-11-30T09:00:00+07:00)
Phân trang ?cursor=...&limit=20 → { items, nextCursor }
Idempotency Header Idempotency-Key cho POST /activations, POST /orders, POST /attempts/{id}/submit
Lỗi { "error": { "code": "CODE_NOT_FOUND", "message": "...", "details": {...} } } + mã HTTP phù hợp
Giới hạn tần suất Header RateLimit-Remaining, Retry-After khi 429
Phiên bản Thay đổi phá vỡ tương thích → /api/v2

#Mã lỗi nghiệp vụ chính

Mã HTTP Ý nghĩa
UNAUTHENTICATED 401 Chưa đăng nhập
FORBIDDEN 403 Không có vai trò phù hợp
NOT_ENTITLED 403 Chưa kích hoạt sách / chưa mua gói (kèm bookId để hiện nút kích hoạt)
CODE_NOT_FOUND 404 ID không tồn tại (kèm suggestions)
ACTIVATION_INVALID 422 Mã kích hoạt không tồn tại / đã thu hồi
ACTIVATION_USED 409 Mã đã được tài khoản khác dùng
ACTIVATION_LOCKED 429 Thử sai quá nhiều lần
ATTEMPT_CLOSED 409 Lượt đã nộp/hết giờ, không nhận câu trả lời
ATTEMPT_IN_PROGRESS 409 Đã có lượt đang làm (kèm attemptId)
VALIDATION_ERROR 400 Dữ liệu không hợp lệ

#2. API cho học sinh

#2.1 Tài khoản & kích hoạt

Phương thức Đường dẫn Mô tả FR
GET /auth/sso/authorize → /auth/sso/callback Đăng nhập Hoclieu FR-01.01
POST /auth/register, /auth/login, /auth/otp, /auth/password/reset Tài khoản riêng FR-01.01, 01.08
POST /auth/logout Đăng xuất (?all=true mọi thiết bị) FR-01.08
GET / PATCH /me Hồ sơ, đồng ý điều khoản FR-01.07
POST /activations/preview Kiểm tra mã, trả thông tin sách (không kích hoạt) FR-01.04
POST /activations Kích hoạt FR-01.02–04
GET /me/books Sách đã kích hoạt FR-01.05
POST /api/v1/activations
Idempotency-Key: 7c1f...
{ "code": "k7xq-m4pa-r9" }

201 Created
{
  "book": { "id": "b-toan-27", "title": "Ôn thi TNTHPT Toán", "edition": "2027", "subject": "MATH" },
  "entitlement": { "validTo": "2027-09-30T23:59:59+07:00" }
}

#2.2 Phân giải ID

Phương thức Đường dẫn Mô tả FR
GET /codes/{code}?edition=27 Phân giải ID → tài nguyên FR-02.02–05
GET /me/recent-codes ID đã mở gần đây FR-02.07
GET /api/v1/codes/tv012

200 OK
{ "code": "TV012", "kind": "EXAMPLE", "resourceId": "r-123", "bookId": "b-toan-27",
  "path": "/vi-du/r-123", "printedPage": 87 }

403 Forbidden
{ "error": { "code": "NOT_ENTITLED", "details": { "bookId": "b-toan-27", "bookTitle": "Ôn thi TNTHPT Toán 2027" } } }

404 Not Found
{ "error": { "code": "CODE_NOT_FOUND", "details": { "normalized": "TV999", "suggestions": ["TV099"] } } }

#2.3 Học liệu

Phương thức Đường dẫn Mô tả FR
GET /resources/{id} Nội dung lý thuyết/ví dụ (rich text, video id, liên kết cùng chuyên đề) FR-03.01–02, 02.08
GET /videos/{id}/playback URL phát HLS ký số + vị trí đã xem FR-03.03–04
PUT /videos/{id}/progress Lưu vị trí xem FR-03.04
GET /books/{id}/tree Cây thư viện kèm trạng thái học FR-03.05–06
POST /content-reports Báo lỗi nội dung FR-03.07

#2.4 Làm bài & kết quả

Phương thức Đường dẫn Mô tả FR
GET /assessments/{id} Thông tin đề, cấu trúc phần, chế độ cho phép, lượt đang làm dở (không có đáp án) FR-04.02–05
POST /assessments/{id}/attempts Bắt đầu lượt { mode, timed, assignmentId? } FR-04.05, 04.09
GET /attempts/{id} Lấy lượt + câu hỏi + câu trả lời đã lưu (khôi phục) FR-04.06
PATCH /attempts/{id}/answers Lưu theo lô các câu trả lời FR-04.06
POST /attempts/{id}/uploads Tải ảnh bài viết tay (URL ký số tải lên) FR-04.11
POST /attempts/{id}/submit Nộp bài → kết quả FR-04.08, 05.01
GET /attempts/{id}/result Kết quả, điểm theo phần, từng câu + lời giải FR-05.01–03, 05.07
PUT /attempts/{id}/self-grades Tự chấm theo rubric FR-05.05
GET /assessments/{id}/answer-key Chế độ tra đáp án (cần entitlement) FR-05.06
GET /attempts/{id}/recommendations Đề xuất củng cố FR-06.01–04
PATCH /api/v1/attempts/a-789/answers
{
  "answers": [
    { "questionId": "q-1", "response": "B", "timeSpentMs": 41200, "clientUpdatedAt": "2026-11-28T20:15:03.120+07:00" },
    { "questionId": "q-14", "response": { "a": true, "b": false, "c": true, "d": true } },
    { "questionId": "q-19", "response": "-1,5", "flagged": true }
  ]
}

200 OK
{ "savedAt": "2026-11-28T20:15:03.410+07:00", "deadlineAt": "2026-11-28T21:30:00+07:00" }
POST /api/v1/attempts/a-789/submit

200 OK
{
  "score": 7.25, "maxScore": 10, "submitType": "MANUAL", "durationSec": 4680,
  "sections": [
    { "title": "Phần I", "score": 2.5, "max": 3 },
    { "title": "Phần II", "score": 2.75, "max": 4 },
    { "title": "Phần III", "score": 2.0, "max": 3 }
  ],
  "summary": { "correct": 14, "wrong": 6, "blank": 2 },
  "recommendations": [
    { "tag": { "id": "t-nh", "name": "Nguyên hàm – Tích phân" }, "errorRate": 0.67,
      "resources": [ { "kind": "THEORY", "code": "TL021", "resourceId": "r-21" },
                     { "kind": "EXERCISE_SET", "code": "TB021", "resourceId": "r-77" } ] }
  ]
}

#2.5 Lịch sử & tiến bộ

Phương thức Đường dẫn Mô tả FR
GET /me/attempts?subject=MATH&kind=MOCK_EXAM Lịch sử lượt làm FR-07.01
GET /me/progress?subject=MATH Chuỗi điểm theo thời gian, trung bình FR-07.03–04
GET /me/weak-topics?subject=MATH Điểm yếu tổng hợp FR-06.05
GET /me/assignments Bài được giao FR-09.03

#2.6 Thương mại (P3)

Phương thức Đường dẫn Mô tả FR
GET /packages?subject= Danh sách gói FR-08.01
POST /orders Tạo đơn, trả URL thanh toán FR-08.02
GET /orders/{id}, /me/orders Trạng thái, lịch sử FR-08.04
POST /payments/{provider}/ipn Nhận IPN (không cần đăng nhập, kiểm chữ ký) FR-08.03

#2.7 Sự kiện

Phương thức Đường dẫn Mô tả FR
POST /events Gửi theo lô ≤ 50 sự kiện, chấp nhận khách ẩn danh FR-11.01

#3. API cho giáo viên

Phương thức Đường dẫn Mô tả FR
GET /teacher/classes Lớp (đồng bộ LMS hoặc tự tạo) FR-09.01
POST /teacher/classes, /classes/join Tạo lớp / học sinh vào bằng mã lớp (dự phòng) FR-09.01
GET /teacher/assessments?bookId= Đề có thể giao FR-09.02
POST /teacher/classes/{id}/assignments Giao bài { assessmentId, openAt, dueAt, modes } FR-09.02
GET /teacher/assignments/{id}/report Tiến độ, phổ điểm, bảng điểm FR-09.04–05
GET /teacher/assignments/{id}/item-analysis Câu và chuyên đề sai nhiều FR-09.06
GET /teacher/assignments/{id}/export Xuất Excel FR-09.07
POST /teacher/assignments/{id}/reminders Nhắc học sinh chưa nộp FR-09.04

#4. API cho CMS & quản trị (/admin)

Nhóm Đường dẫn chính FR
Sách & cây nội dung /admin/books, /admin/books/{id}/nodes, /admin/resources FR-10.01
Câu hỏi /admin/questions (CRUD, lọc theo tag/trạng thái), /admin/passages, /admin/rubrics FR-10.02, 10.10
Import POST /admin/imports (tải file) → GET /admin/imports/{id} (xem trước, lỗi) → POST /admin/imports/{id}/commit FR-10.03
Video POST /admin/videos (URL tải lên trực tiếp tới dịch vụ video), webhook /admin/videos/webhook FR-10.04
Đề /admin/assessments, /admin/assessments/{id}/sections FR-10.07
ID POST /admin/books/{id}/codes/generate, POST /admin/codes/{code}/assign, POST /admin/books/{id}/codes/lock, `GET /admin/books/{id}/codes/export?format=xlsx zip`
Kiểm tra liên kết GET /admin/books/{id}/link-check FR-10.14
Ma trận /admin/taxonomy, PUT /admin/questions/{id}/tags, /admin/recommendation-settings FR-10.06, 10.12
Duyệt POST /admin/resources/{id}/submit-review, /approve, /reject, GET /admin/resources/{id}/versions FR-10.08
Mã kích hoạt POST /admin/activation-batches, GET /admin/activation-batches/{id}/export, GET /admin/activation-codes/lookup, POST /admin/activation-codes/{id}/revoke FR-10.09
Người dùng CMS /admin/staff, /admin/roles FR-10.13
Báo cáo /admin/reports/overview, /admin/reports/content, /admin/reports/commerce, ?from=&to=&subject=&format=csv FR-11.02–05
Hỗ trợ /admin/users?query=, /admin/users/{id}, /admin/users/{id}/lock FR-11.06
Gói đề /admin/packages FR-08.06

#5. Tích hợp ngoài (cần xác nhận với đối tác)

Tích hợp Hướng Dữ liệu Câu hỏi
Hoclieu SSO SYS → Hoclieu OIDC: sub, họ tên, email/SĐT, vai trò Q02
LMS Hoclieu SYS ↔ LMS Lớp, thành viên, bài giao, điểm Q12
Dịch vụ video SYS ↔ Video Tải lên, webhook trạng thái, token phát Q13
Cổng thanh toán SYS ↔ Cổng Tạo giao dịch, IPN, truy vấn trạng thái Q11
OTP/Thông báo SYS → Nhà cung cấp Số điện thoại, nội dung mẫu Q02