Trang này là bản “đi theo request” để trả lời khi thầy hỏi: người dùng bấm ở UI thì code nào chạy, backend xử lý ra sao, dữ liệu nằm ở bảng nào, và điểm kiểm soát an toàn là gì.
Sơ đồ này không thay thế chi tiết bên dưới. Nó là bản nhớ nhanh: mỗi nghiệp vụ đều phải đi qua UI, API/client hoặc store, controller/use case, database và một invariant an toàn.
Roles, ownership, publication snapshot, payment truth
Auth và phân quyền
sequenceDiagram
autonumber
participant U as User
participant FE as Angular AuthService
participant API as AuthControllerV3
participant JWT as JWT service
participant DB as users / roles
U->>FE: Nhập email + mật khẩu
FE->>API: POST /auth/login
API->>DB: Kiểm tra user, role, trạng thái
API->>JWT: Tạo access token
JWT-->>API: JWT đã ký
API-->>FE: Token + profile
FE-->>U: Điều hướng theo role
Login/Register UI
-> AuthService / auth API client
-> AuthControllerV3
-> JWT service + user repository
-> JwtAuthenticationFilter on later requests
-> SecurityConfig + @PreAuthorize + role guards
UI guard chỉ là lớp tiện dụng; backend security mới là lớp quyết định.
Câu trả lời ngắn: LMS dùng JWT. Sau login, frontend lưu token và interceptor gắn token vào request. Backend đọc token qua JwtAuthenticationFilter, sau đó controller/use case bị khóa bằng role và ownership guard.
Teacher tạo và sửa khóa học
sequenceDiagram
autonumber
participant T as Teacher
participant UI as Course editor
participant Store as CourseEditorStore
participant API as Course authoring API
participant UC as Use case
participant DB as courses / chapters / lessons
T->>UI: Tạo hoặc sửa course draft
UI->>Store: Cập nhật form, curriculum, content blocks
Store->>API: Save chapter/lesson/section/content
API->>UC: Validate role + ownership + draft rules
UC->>DB: Ghi draft
DB-->>UC: Entity đã lưu
UC-->>Store: DTO mới nhất
Store-->>UI: Render trạng thái đã lưu
Teacher sửa draft. Học viên không đọc trực tiếp draft nếu course đã qua publication.
Điểm dễ nhầm: content_blocks là cột JSONB trong lessons, không phải bảng riêng.
Submit, review, approve và publication
sequenceDiagram
autonumber
participant T as Teacher
participant A as Admin/ORG_ADMIN
participant API as Course review API
participant Pub as CoursePublicationService
participant Draft as Draft tables
participant Snap as course_publications
participant L as Learner UI
T->>API: Submit draft for approval
API->>Draft: Validate course completeness
API-->>A: Course chờ duyệt
A->>API: Approve hoặc reject
API->>Pub: Nếu approve, tạo snapshot
Pub->>Draft: Đọc courses/chapters/lessons hiện tại
Pub->>Snap: Ghi publication version
L->>Snap: Đọc nội dung học viên thấy
Teacher submit-for-approval
-> backend validate draft
-> Admin/ORG_ADMIN review
-> approve/reject
-> CoursePublicationService tạo hoặc cập nhật snapshot
-> learner-facing page đọc course_publications
Phần
File/bảng chính
Frontend
Teacher course editor, admin course review pages
Backend
CoursePublicationService, CoursePublicationJpaEntity, admin course controller/use case
Database
course_publications, courses, learning_classes
Invariant
Sửa draft không tự động làm học viên thấy nội dung mới. Phải submit/approve và class phải trỏ đúng publication.
Câu trả lời ngắn: LMS tách draft và snapshot. course_publications là bản học viên đọc, giúp course đã duyệt ổn định dù teacher tiếp tục sửa draft.
Student xem course, enroll và học bài
sequenceDiagram
autonumber
participant S as Student
participant UI as Course detail / learning page
participant API as Enrollment + learning API
participant Pay as Payment truth
participant Pub as course_publications
participant Prog as progress tables
S->>UI: Mở course
UI->>API: Check entitlement
API->>Pay: Nếu paid course, kiểm tra giao dịch đã verified
API->>Pub: Lấy publication hợp lệ
UI-->>S: Mở bài học
S->>UI: Hoàn thành lesson/section
UI->>API: Mark progress
API->>Prog: Ghi student_lesson_progress
Student chỉ học khi có entitlement hợp lệ: free, enrolled, paid, hoặc được activate trong lớp.
Nếu hỏi “vì sao student đã trả tiền mà chưa học được”, kiểm tra payment_transactions, enrollment/activation và delivery mode.
Quiz, assignment và grading
sequenceDiagram
autonumber
participant T as Teacher
participant S as Student
participant UI as Assessment UI
participant API as Assessment controllers
participant UC as Assessment use cases
participant DB as quiz / assignment tables
participant Audit as grading_audit_log
T->>API: Tạo quiz/assignment/rubric
API->>DB: Lưu cấu hình đánh giá
S->>UI: Làm bài / nộp bài
UI->>API: Submit attempt/submission
API->>UC: Validate window, attempt, ownership
UC->>DB: Lưu kết quả
T->>API: Chấm hoặc phản hồi
API->>Audit: Ghi audit khi grade thay đổi
Teacher tạo quiz/assignment/rubric
-> Assessment controllers
-> questions / quizzes / assignments
-> Student attempt/submission
-> auto grade hoặc teacher grade
-> grading_audit_log + progress/analytics
Wiii/Pointy không được tự submit quiz hoặc grade. Grade cần role/ownership và audit.
Điểm cần nhớ: question rich content nằm ở questions.content_blocks JSONB; rubric data có ở assignment/rubric/submission tùy flow.
Upload tài liệu, file và video
sequenceDiagram
autonumber
participant U as Teacher/UI
participant FE as PresignedUploadService
participant API as Upload API
participant Store as R2/S3-compatible storage
participant DB as upload_sessions / files / video_assets
participant Worker as Video worker
U->>FE: Chọn Word/PDF/video
FE->>API: Init upload session
API->>DB: Tạo upload_sessions
API-->>FE: Presigned URL + session id
FE->>Store: Upload trực tiếp binary
FE->>API: Confirm/attach
API->>DB: Gắn file/video asset
API->>Worker: Nếu video, tạo ingest job
Worker->>Store: Package HLS/DASH
Worker->>DB: Cập nhật renditions/manifests
User chọn file
-> PresignedUploadService
-> backend tạo upload_sessions
-> browser upload lên storage
-> backend attach file/video asset
-> nếu video: ingest job -> Shaka package -> renditions/manifests
Phần
File/bảng chính
Frontend
presigned-upload.service.ts, upload adapter, course editor media UI
Upload lớn không đi qua web backend như binary stream dài; video ingest chạy tách khỏi request UI.
Video playback và offline/PWA
sequenceDiagram
autonumber
participant L as Learner
participant Player as AdaptiveVideoPlayer
participant API as Playback API
participant Edge as media.holilihu.online
participant Cache as IndexedDB/Dexie
participant DB as video_progress
L->>Player: Mở bài có video
Player->>API: Xin playback URL/token
API-->>Player: Signed manifest URL
Player->>Edge: Tải HLS/DASH manifest + segments
Player->>DB: Gửi progress khi học
L->>Player: Download offline nếu được phép
Player->>Cache: Lưu cache có kiểm soát
Cache-->>API: Sync progress khi online lại
Learning page
-> Adaptive video player
-> signed playback/media auth
-> HLS/DASH manifest + segments
-> progress tracking
-> optional offline download into IndexedDB/Dexie
-> offline sync queue khi online lại
Offline là cache có kiểm soát, không phải nguồn dữ liệu chính. Progress cuối cùng vẫn phải sync về backend.
Payment, refund và revenue
sequenceDiagram
autonumber
participant S as Student
participant FE as Payment UI
participant API as PaymentControllerV3
participant GW as VNPay/SePay
participant DB as payment_transactions
participant Enroll as Enrollment use case
S->>FE: Chọn thanh toán course
FE->>API: Tạo payment transaction
API->>GW: Tạo URL/QR theo gateway
GW-->>S: User thanh toán
GW->>API: IPN/webhook/callback
API->>GW: Verify chữ ký/nội dung
API->>DB: Mark COMPLETED nếu hợp lệ
API->>Enroll: Kích hoạt entitlement/enrollment
Message visibility phải đi theo class/course/user scope.
AI/Wiii và safe action
sequenceDiagram
autonumber
participant Page as LMS page
participant W as Wiii iframe
participant Ctx as WiiiContextService
participant Preview as Preview/diff UI
participant API as LMS backend
participant DB as Course draft tables
Page->>Ctx: Thu thập data-wiii-id + safe capabilities
Ctx->>W: postMessage context
W->>Page: Request point/click/action
Page->>Page: Chỉ cho click nếu data-wiii-click-safe=true
W->>Preview: Sinh lesson draft/patch
Preview-->>Page: Teacher xem diff
Page->>API: Apply khi teacher xác nhận
API->>DB: Mutate draft qua authorization/use case
LMS page context
-> Wiii iframe
-> WiiiContextService gửi context + safe capabilities
-> Wiii request action by data-wiii-id
-> LMS chỉ cho click nếu data-wiii-click-safe=true
-> preview/diff trước apply nội dung tạo bởi AI