LMS Maritime - Sổ tay học code và bảo vệ

Tài liệu này dùng để học nhanh codebase trước ngày báo cáo: hiểu kiến trúc, nắm các luồng chính, biết chạy local, biết sửa code có kiểm chứng, và trả lời được các câu hỏi thường gặp về hệ thống LMS Hàng Hải.

Ngày tạo: 13/06/2026 Frontend: Angular 20.3 + Signals Backend: Spring Boot 3.2 + Java 21 Database: PostgreSQL 16 + Flyway Runtime: Docker + Caddy + Cloudflare R2

1. Học Nhanh Trong 5 Phút

Đây là hệ thống LMS phục vụ đào tạo hàng hải. Hệ thống có bốn vai trò chính: ADMIN, ORG_ADMIN, TEACHERSTUDENT. Frontend viết bằng Angular, backend viết bằng Spring Boot, dữ liệu lưu trong PostgreSQL.

Câu giới thiệu ngắn khi thầy hỏi

"Dự án của em là nền tảng LMS cho đào tạo hàng hải, bao gồm quản lý khóa học, bài học, lớp học, ghi danh, quiz, bài tập, tiến độ học tập, thanh toán, PWA offline và video adaptive. Backend theo Clean Architecture/DDD, frontend theo Angular Signals và lazy-loaded routes."

Một request đi như thế nào?

  1. Người dùng thao tác trên Angular component.
  2. Component gọi service hoặc API client.
  3. Interceptor gắn JWT và base URL.
  4. Spring Controller nhận request.
  5. UseCase xử lý nghiệp vụ.
  6. Repository Adapter đọc/ghi PostgreSQL qua JPA Entity.
  7. DTO trả về UI, Angular signal cập nhật màn hình.

Thông tin cần nhớ

MụcGiá trị
Local frontendhttp://localhost:4200
Local backendhttp://localhost:8088
Swaggerhttp://localhost:8088/swagger-ui
Productionhttps://holilihu.online
Production mediahttps://media.holilihu.online
Tài khoản teacherteacher@maritime.edu / redacted-local-seed
Tài khoản studentstudent@maritime.edu / redacted-local-seed
Tài khoản adminadmin@maritime.edu / redacted-local-seed

2. Cách Chạy Local

Cách khuyến nghị là chạy PostgreSQL và backend bằng Docker, còn frontend chạy local bằng Node để sửa UI nhanh hơn.

2.1. Kiểm tra trước khi chạy

git status --short --branch
docker --version
node -v
npm -v
java -version
Khi học hoặc demo trên Windows, nên dùng PowerShell. Nhiều lệnh trong repo và tài liệu đang được viết theo thói quen PowerShell/Bash.

2.2. Chạy backend và database bằng Docker

cd E:\Sach\Sua\LMS_hohulili
Copy-Item .env.dev.example .env
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d db backend

2.3. Kiểm tra backend đã chạy

curl.exe -s http://localhost:8088/actuator/health
curl.exe -s "http://localhost:8088/api/v3/courses?page=0&size=1"

Kết quả health mong muốn:

{"status":"UP"}

2.4. Chạy frontend

cd E:\Sach\Sua\LMS_hohulili\fe
npm install
npm start

Mở trình duyệt tại:

http://localhost:4200

2.5. Chạy toàn bộ stack bằng Docker

Cách này phù hợp khi cần môi trường gần production hơn, nhưng sửa UI sẽ chậm hơn.

cd E:\Sach\Sua\LMS_hohulili
Copy-Item .env.dev.example .env
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build --wait

2.6. Tắt local stack

cd E:\Sach\Sua\LMS_hohulili
docker compose -f docker-compose.yml -f docker-compose.dev.yml down
Không dùng lệnh xóa volume nếu chưa muốn reset database local. Lệnh xóa volume sẽ làm mất dữ liệu local: docker compose ... down -v.

3. Khi Thầy Bảo Sửa Code Ngay

Khi thầy yêu cầu sửa một lỗi nhỏ trong lúc báo cáo, cần làm theo quy trình gọn: xác định màn hình, tìm đúng file, sửa đúng phạm vi, build/test, rồi refresh browser.

3.1. Quy trình an toàn

  1. Ghi lại URL, màn hình và hành động gây lỗi.
  2. Chạy git status --short --branch để biết repo có file đang sửa không.
  3. Tìm route, component hoặc controller liên quan bằng rg.
  4. Sửa ít nhất có thể, không refactor lớn trong lúc demo.
  5. Chạy test hoặc build đúng phạm vi.
  6. Refresh browser và nói rõ bằng chứng đã kiểm chứng.

3.2. Lệnh tìm code nhanh

cd E:\Sach\Sua\LMS_hohulili
rg "teacher/courses" fe/src/app
rg "CourseEditor" fe/src/app/features/teacher
rg "@GetMapping|@PostMapping|@PutMapping|@PatchMapping" backend/src/main/java/com/example/lms
rg "api/v3/courses|teacher/courses|video-assets" backend/src/main/java/com/example/lms fe/src/app

3.3. Nếu sửa frontend

cd E:\Sach\Sua\LMS_hohulili\fe
npm run build

Các file hay dùng khi sửa UI:

Cần sửaFile/thư mục
Route tổngfe/src/app/app.routes.ts
Teacher routefe/src/app/features/teacher/teacher.routes.ts
Student routefe/src/app/features/student/student.routes.ts
Auth/JWT statefe/src/app/core/services/auth.service.ts
Gọi APIfe/src/app/api/client/fe/src/app/api/endpoints/
Course editorfe/src/app/features/teacher/course-editor/
Learning pagefe/src/app/features/learning/

3.4. Nếu sửa backend

cd E:\Sach\Sua\LMS_hohulili\backend
mvn -DskipTests compile -B
mvn test -B

Các file hay dùng khi sửa backend:

Cần sửaFile/thư mục
Security/JWTbackend/src/main/java/com/example/lms/config/
Auth/userbackend/src/main/java/com/example/lms/identity/
Khóa học/bài họcbackend/src/main/java/com/example/lms/course_authoring/
Lớp học/tiến độ/videobackend/src/main/java/com/example/lms/learning_delivery/
Quiz/bài tập/chấm điểmbackend/src/main/java/com/example/lms/assessment/
Upload/payment/sharedbackend/src/main/java/com/example/lms/shared/

3.5. Trước khi commit hoặc nộp PR

git status --short
git diff --stat
git diff
cd fe; npm run build
cd ..\backend; mvn -DskipTests compile -B
Không commit file .env*, secret, screenshot tạm, cache, build output hoặc Unity cache trong artifacts/. Khi cần commit, luôn xem kỹ git status --short -uall.

4. Kiến Trúc Tổng Quan

Hệ thống chia thành ba lớp lớn: frontend, backend, database/storage.

Angular UIComponent, signal, route, guard
API LayerApiClient, endpoint, interceptor
Spring ControllerREST endpoint, auth context
UseCaseNghiệp vụ, rule, transaction
PostgreSQL/R2JPA entity, migration, media object

Backend modules

ModuleVai tròVí dụ nghiệp vụ
identityNgười dùng, role, JWT, tổ chứcLogin, invite, org admin
course_authoringTạo và duyệt khóa họcCourse, chapter, lesson, publication
learning_deliveryHọc tập, lớp, tiến độ, videoEnrollment, progress, certificate, video asset
assessmentĐánh giáQuiz, question bank, assignment, grading
communicationGiao tiếpMessages, announcements
ai_assistantTrợ lý AI/WiiiChat sessions, token exchange, AI bridge
sharedDùng chungUpload, payment, audit log, outbox

Frontend modules

Thư mụcVai trò
fe/src/app/apiKhai báo endpoint, DTO type, API client, interceptor
fe/src/app/coreService dùng chung: auth, PWA, upload, SEO, notification
fe/src/app/featuresMàn hình theo domain: teacher, student, admin, learning, courses
fe/src/app/sharedComponent UI dùng lại
fe/src/app/stateGlobal state, course state và class state

5. Frontend Angular

Frontend dùng Angular 20.3, standalone component mặc định, Signals, inject(), @if/@forChangeDetectionStrategy.OnPush.

Route map quan trọng

URLÝ nghĩaFile route
/Trang chủ publicfe/src/app/app.routes.ts
/coursesDanh sách khóa học publicfe/src/app/app.routes.ts
/courses/:idChi tiết khóa học publicfe/src/app/app.routes.ts
/teacher/coursesTeacher dashboardfe/src/app/features/teacher/teacher.routes.ts
/teacher/courses/:id/editorCourse editorfe/src/app/features/teacher/course-editor/course-editor.routes.ts
/student/coursesKhóa học của học viênfe/src/app/features/student/student.routes.ts
/student/learnLearning shellfe/src/app/features/learning/learning.routes.ts
/adminQuản trị hệ thốngfe/src/app/features/admin/admin.routes.ts

Pattern component cần nắm

@Component({
  selector: 'app-example',
  imports: [CommonModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    @if (isLoading()) {
      <p>Đang tải</p>
    } @else {
      @for (item of items(); track item.id) {
        <div>{{ item.name }}</div>
      }
    }
  `
})
export class ExampleComponent {
  private service = inject(MyService);
  items = signal<Item[]>([]);
  itemCount = computed(() => this.items().length);
}
Nếu thầy hỏi vì sao dùng Signals: Signals giúp state rõ ràng hơn, UI cập nhật theo dữ liệu, OnPush tối ưu render, và giảm nhu cầu tự quản lý subscription thủ công.

6. Backend Clean Architecture / DDD

Backend không để controller thao tác trực tiếp với database. Controller gọi UseCase, UseCase dùng repository port, còn Infrastructure adapter mới nối với JPA.

ControllerREST API, validate request
UseCaseBusiness logic
DomainEntity, value object, rule
Repository PortInterface trong domain
Adapter/JPAJpaEntity, mapper, PostgreSQL

Quy tắc vàng

Spring Data JPA repository phải trỏ tới *JpaEntity, không trỏ tới domain model. Nếu sai sẽ gây lỗi Not a managed type.

Ví dụ câu trả lời

"Em tách domain model và JPA entity để domain không bị phụ thuộc framework. Nếu sau này đổi persistence, business rule vẫn nằm trong domain/use case. Infrastructure chỉ là lớp chuyển đổi và đọc ghi database."

7. Database

Database là PostgreSQL 16, migration bằng Flyway trong backend/src/main/resources/db/migration/. Các bảng chính gồm: schema hiện có 78 application tables2 materialized views theo catalog sinh từ migration.

Nhóm bảngBảng tiêu biểu
Người dùng/tổ chứcusers, organizations, organization_invites, user_external_identities
Khóa họccourses, chapters, lessons, sections, course_publications
Lớp họclearning_classes, class_teachers, enrollments, student_lesson_progress
Đánh giáquizzes, questions, quiz_attempts, assignments, assignment_submissions
Video/uploadupload_sessions, file_attachments, video_assets, video_renditions, video_progress
Thanh toánpayment_transactions, revenue_splits, payout_requests, teacher_bank_accounts

Lệnh xem database local

docker compose -f docker-compose.yml -f docker-compose.dev.yml exec db psql -U lms -d lms
\dt
select count(*) from users;
select count(*) from courses;

Nếu dùng DBeaver local: host localhost, port 5432, database lms, user lms, password lms khi chạy dev compose mặc định.

8. Các Luồng Chính Cần Thuộc

8.1. Đăng nhập và phân quyền

Login UIfeatures/auth
AuthServiceLưu token/user
InterceptorGắn JWT
Backend AuthKiểm tra thông tin đăng nhập
Role GuardADMIN/TEACHER/STUDENT

8.2. Teacher tạo khóa học

Course Creation/teacher/course-creation
Course EditorInfo, curriculum, classes
Store/Servicecourse-editor.store.ts
UseCasecourse_authoring
DBcourses, chapters, lessons

8.3. Admin duyệt khóa học

SubmitTeacher gửi duyệt
ReviewAdmin xem preview
ApproveTạo bản công bố
StudentĐọc bản đã công bố
RuleDraft khác learner snapshot

8.4. Student học bài

My Courses/student/courses
Learning Shellfeatures/learning
Lesson ContentText/video/quiz
Progress APIHoàn thành section
DBstudent_lesson_progress

8.5. Quiz và assignment

LuồngTeacherStudentBackend
Quiz Tạo quiz, question, gắn vào lesson/class Làm attempt, nộp đáp án, xem kết quả assessment: quiz, questions, attempts
Assignment Tạo bài tập, rubric, xem submission, chấm điểm Nộp bài, xem feedback/điểm assessment: assignments, submissions, grading audit

9. Upload, Video, Offline

9.1. Upload tài liệu, ảnh, video

Upload hiện đại không đẩy file lớn qua backend trực tiếp. Hệ thống dùng presigned upload:

initBackend tạo upload session
direct uploadBrowser đẩy file lên storage
confirmBackend xác nhận file
attachGắn vào lesson/course/video
cleanupDọn session quá hạn

9.2. Video adaptive

Video nội bộ dùng Cloudflare R2 private + Shaka Packager để tạo HLS/DASH. Production có dedicated video worker để ingest video, tránh để web backend bị nghẽn.

Thành phầnVai trò
video_assetsBản ghi video gốc và metadata
video_renditionsCác bản encode/chất lượng
video_ingest_jobsHàng đợi xử lý video
media.holilihu.onlineMedia domain qua Cloudflare Worker edge auth

9.3. Offline/PWA

Offline là dữ liệu cục bộ trên từng thiết bị. Dữ liệu học offline nằm trong IndexedDB/Dexie, tiến độ sync theo hướng cộng dồn và tiến lên, không ghi đè lùi tiến độ trên server.

File nên đọc:

  • fe/src/app/core/db/lms-offline.db.ts
  • fe/src/app/core/services/course-download.service.ts
  • fe/src/app/core/services/offline-sync.service.ts
  • fe/src/app/api/interceptors/offline.interceptor.ts

10. Bảo Mật Và Phân Quyền

Bảo mật chính gồm JWT, route guards, backend method security, CORS/CSP, rate limiting và tách role theo quyền.

Vai tròQuyền chính
ADMINQuản trị hệ thống, settings, logs, xóa user/course
ORG_ADMINDuyệt khóa học, quản lý teacher/student, analytics vận hành
TEACHERTạo khóa học, lớp học, bài tập, quiz, chấm điểm
STUDENTHọc, làm quiz/bài tập, thanh toán, xem kết quả

Files quan trọng

  • backend/src/main/java/com/example/lms/config/SecurityConfig.java
  • backend/src/main/java/com/example/lms/config/JwtAuthenticationFilter.java
  • fe/src/app/core/guards/role.guard.ts
  • fe/src/app/api/interceptors/auth.interceptor.ts

11. Debug Nhanh

Backend không lên

docker compose -f docker-compose.yml -f docker-compose.dev.yml logs backend --tail=100
docker compose -f docker-compose.yml -f docker-compose.dev.yml ps
  • Not a managed type: JPA repository đang trỏ tới domain model thay vì *JpaEntity.
  • DB connection fail: kiểm tra container db có healthy không.
  • R2/access key fail ở dev: kiểm tra config dev và biến môi trường dev.

Frontend không gọi được API

curl.exe -s http://localhost:8088/actuator/health
rg "apiUrl|baseUrl|localhost:8088" fe/src/environments fe/src/app

Port bị chiếm

netstat -ano | findstr :4200
netstat -ano | findstr :8088

Build frontend lỗi

cd E:\Sach\Sua\LMS_hohulili\fe
npm run build

Test backend quá chậm

cd E:\Sach\Sua\LMS_hohulili\backend
mvn -DskipTests compile -B

Khi sửa logic quan trọng, cần chạy test liên quan hoặc full mvn test -B.

12. Mở Code Để Sửa Khi Cần

Cách học dự án nhanh nhất là mở theo luồng người dùng, không mở ngẫu nhiên từng file. Khi thầy chỉ một màn hình bất kỳ, hãy truy ngược theo chuỗi: route → component → service/store → API endpoint → controller → use case → repository → migration/database.

12.1. Mở project bằng VS Code

cd E:\Sach\Sua\LMS_hohulili
code .

Nên mở 3 terminal trong VS Code:

TerminalDùng để làm gìLệnh thường dùng
Terminal 1Backend, Docker, databasedocker compose -f docker-compose.yml -f docker-compose.dev.yml up -d db backend
Terminal 2Frontend Angularcd fe && npm start
Terminal 3Tìm code, build, testrg "course-editor|video-assets|submit-for-approval" fe/src backend/src
Không nên mở hoặc format thư mục artifacts/. Đây là thư mục lớn, có thể chứa build/cache/tài nguyên tạm. Khi commit hoặc nộp PR, chỉ stage đúng file mình sửa.

12.2. Mở backend bằng IntelliJ IDEA

Backend là Maven project. Có thể mở thẳng thư mục backend/ bằng IntelliJ để IDE nhận Java 21, Spring Boot, Maven dependency và test runner. Nếu mở bằng VS Code vẫn được, nhưng IntelliJ dễ đọc luồng Controller → UseCase → Repository hơn.

cd E:\Sach\Sua\LMS_hohulili\backend
mvn -DskipTests compile -B

12.3. Mở database bằng DBeaver

DBeaver phù hợp để xem sơ đồ tổng, bảng, khóa ngoại và dữ liệu mẫu. Với local Docker, kết nối thường là:

TrườngGiá trị local thường dùng
Hostlocalhost
Port5432
Databaselms
Userlms
PasswordLấy từ .env local, không đưa vào tài liệu hoặc commit.

12.4. Cách tìm đúng file trong Angular

cd E:\Sach\Sua\LMS_hohulili
rg "path: 'curriculum'|course-editor|teacher-dashboard" fe/src/app
rg "updateCourseInfo|submitForApproval|getCourseDraft" fe/src/app/features/teacher
rg "#0056D2|--c-primary|--c-accent|bg-\[" fe/src

Các màn teacher/course editor quan trọng nằm ở:

  • fe/src/app/features/teacher/teacher.routes.ts: route lớn của teacher.
  • fe/src/app/features/teacher/course-editor/course-editor.routes.ts: route con info, curriculum, competency, settings, classes.
  • fe/src/app/features/teacher/course-editor/store/course-editor.store.ts: state, cache, readiness checklist, optimistic UI.
  • fe/src/app/features/teacher/course-editor/services/course-authoring.service.ts: DTO và các API gọi backend.

12.5. Cách tìm đúng file trong backend

rg "@RequestMapping|@GetMapping|@PostMapping|@PutMapping|@PatchMapping|@DeleteMapping" backend/src/main/java/com/example/lms
rg "submitForApproval|createCourse|updateCourse" backend/src/main/java/com/example/lms/course_authoring
rg "video-assets|adaptive|playback" backend/src/main/java/com/example/lms/learning_delivery

Backend đi theo Clean Architecture/DDD. Nếu sửa nghiệp vụ, ưu tiên sửa ở application/usecase hoặc domain/model. Controller chỉ nhận request, kiểm quyền, validate nhẹ và gọi use case.

13. Kịch Bản Sửa Code Hay Gặp

13.1. Đổi màu hoặc giao diện

Muốn đổiSửa ở đâuCách kiểm tra
Màu chủ đạo toàn appfe/src/styles.scssfe/src/styles/_variables.scsscd fe && npm run build, mở http://localhost:4200
Màu Angular Materialmat.theme(...) trong fe/src/styles.scssMở các dialog/input/material control.
Màu riêng một nút/cardFile .html hoặc .scss của component đóTìm bằng rg "#0056D2|bg-\[|text-\[" fe/src/app
Tránh nháy layout lúc loadfe/src/styles/critical.scssReload hard refresh và quan sát màn đầu.

13.2. Đổi logic frontend

Ví dụ thầy yêu cầu: "đổi điều kiện cho phép publish course". Cách lần:

  1. Tìm chữ đang hiển thị hoặc action button trong template: rg "publish|submitForApproval|Gửi duyệt" fe/src/app.
  2. Mở component hoặc store liên quan, thường là CourseEditorStore hoặc page trong course-editor/pages/.
  3. Nếu logic là state UI, sửa signal/computed trong frontend.
  4. Nếu logic là nghiệp vụ thật, sửa backend use case để không bị bypass bằng API.
  5. Chạy cd fe && npm run build; nếu có backend đổi thì chạy thêm cd backend && mvn -DskipTests compile -B.
Quy tắc Angular của dự án: dùng Signals, inject(), input()/output(), control flow @if/@for, ChangeDetectionStrategy.OnPush, không thêm standalone: true vì Angular 20 đã mặc định standalone.

13.3. Đổi logic backend

Ví dụ thầy yêu cầu: "khóa học trả phí phải có giá lớn hơn 0". Luồng sửa đúng:

ControllerTeacherCoursesControllerV3
DTOCourseDTOs
UseCaseCourseAuthoringUseCase
DomainCourse
DBMigration nếu đổi schema

Nếu chỉ đổi rule, thường sửa ở application/usecase hoặc domain/model. Nếu thêm cột/bảng, tạo migration mới trong backend/src/main/resources/db/migration/, ví dụ V136__add_course_field.sql. Không dựa vào ddl-auto để production tự sinh schema.

13.4. Thêm trường mới vào Course

BướcFile cần xem
1. Thêm cột DBbackend/src/main/resources/db/migration/Vxxx__...
2. Thêm JPA fieldcourse_authoring/infrastructure/persistence/entity/*Course*JpaEntity.java
3. Map domain/DTOinfrastructure/persistence/mapper/application/dto/
4. Sửa use caseCourseAuthoringUseCase, GetCourseDraftUseCase
5. Sửa Angular DTO/servicecourse-authoring.service.ts
6. Sửa UI formcourse-editor/pages/course-info hoặc page liên quan.

13.5. Sửa tiếng Việt và i18n/ARB

Dự án hiện là Angular, không dùng file .arb kiểu Flutter. Phần lớn chữ tiếng Việt đang nằm trực tiếp trong template/component, route title, toast message và backend response message. Khi cần sửa chữ:

rg "Từ cần sửa|Không thể|Gửi duyệt|Khóa học" fe/src backend/src

Nếu sau này muốn i18n chuẩn, nên thiết kế riêng bằng Angular i18n hoặc JSON translation files. Không tự thêm lẻ tẻ một hệ ARB vào giữa dự án khi chưa có quyết định kiến trúc.

13.6. Wiii/Pointy và metadata DOM an toàn

LMS đã có các target như data-wiii-id, data-wiii-click-safe, data-wiii-click-kind ở teacher dashboard, course creation, course editor sidebar, section editor và preview dialog. Chỉ gắn data-wiii-click-safe="true" cho thao tác điều hướng, mở panel hoặc form step an toàn. Không gắn safe cho submit quiz, delete, publish, enroll, grade, payment.

rg "data-wiii-id|data-wiii-click-safe|data-wiii-click-kind" fe/src/app/features/teacher fe/src/app/features/ai-chat

14. Shaka, ABR, Media Và Upload

14.1. Phân biệt ARB và ABR

Nếu hỏi về ARB theo nghĩa file dịch ngôn ngữ thì dự án hiện không dùng ARB. Nếu hỏi về ABR, đó là Adaptive Bitrate: trình phát tự đổi chất lượng video theo băng thông và tình trạng buffer.

14.2. Frontend Shaka Player nằm ở đâu?

FileVai trò
fe/src/app/features/learning/components/adaptive-video-player/adaptive-video-player.component.tsKhởi tạo Shaka, load HLS/DASH, cấu hình ABR, chọn chất lượng thủ công.
fe/src/app/core/utils/video-stream-recovery.tsQuyết định khi nào refresh playback URL sau lỗi 401/403/404/5xx.
fe/src/app/core/utils/video-offline-policy.tsPhân biệt video nội bộ có thể offline và video external/YouTube online-only.

14.3. Các dòng ABR quan trọng

rg "abr:|defaultBandwidthEstimate|switchInterval|selectQuality|resolveAdaptivePlayUrl" fe/src/app/features/learning/components/adaptive-video-player
Thiết lậpÝ nghĩa
abr.enabled: trueBật tự động chọn chất lượng.
defaultBandwidthEstimate: 900_000Ước lượng ban đầu khoảng 900 Kbps để tránh nhảy ngay lên chất lượng quá cao.
switchInterval: 8Khoảng thời gian giữa các lần cân nhắc đổi chất lượng.
selectQuality(null)Quay lại chế độ tự động.
selectQuality(q)Tắt ABR và chọn track theo độ phân giải.

14.4. Backend đóng gói video bằng Shaka như thế nào?

UploadPresigned upload lên storage
Assetvideo_assets
IngestVideoAssetIngestService
EncodeFFmpeg tạo rendition
PackageShaka tạo HLS/DASH
File/configÝ nghĩa
backend/DockerfileCài FFmpeg, libx264 và tải Shaka Packager binary.
backend/src/main/resources/application.ymlCác biến app.video.*: token, segment TTL, profile, media domain.
VideoAssetIngestServiceChọn profile SAVER, STANDARD, HIGH, gọi FFmpeg và Shaka.
ShakaPackagerServiceGọi command packager, tạo manifest HLS/DASH và kiểm output.
AdaptiveVideoPlaybackControllerServe manifest/object qua token playback.

14.5. Muốn đổi chất lượng encode hoặc profile

Có hai lớp cần phân biệt:

  • Profile tạo file: chỉnh VIDEO_ADAPTIVE_PROFILES, VIDEO_OFFLINE_PROFILES, VIDEO_ADAPTIVE_SEGMENT_DURATION_SECONDS, VIDEO_FFMPEG_PRESET.
  • Hành vi phát trên trình duyệt: chỉnh ABR trong adaptive-video-player.component.ts.
rg "VIDEO_ADAPTIVE_PROFILES|VIDEO_OFFLINE_PROFILES|VIDEO_FFMPEG_PRESET|adaptive-segment" backend docker-compose*.yml docs

14.6. Smoke test video local

  1. Chạy backend và frontend local.
  2. Đăng nhập teacher, upload video vào lesson hoặc section.
  3. Kiểm tra asset có trạng thái xử lý xong.
  4. Đăng nhập student, mở bài học có video.
  5. Mở DevTools Network, kiểm tra có request master.m3u8 hoặc manifest.mpd.
  6. Thử menu chất lượng: Tự động, 360p, 720p nếu có.

15. Kiểm Thử Trước Khi Nộp Hoặc Báo Cáo

15.1. Baseline nhanh

cd E:\Sach\Sua\LMS_hohulili\fe
npm run build

cd E:\Sach\Sua\LMS_hohulili\backend
mvn -DskipTests compile -B

cd E:\Sach\Sua\LMS_hohulili
docker compose -f docker-compose.yml -f docker-compose.dev.yml config -q
curl.exe -s http://localhost:8088/actuator/health

15.2. Khi sửa frontend

  • Chạy cd fe && npm run build.
  • Mở http://localhost:4200, đăng nhập đúng role và bấm flow vừa sửa.
  • Nếu sửa PWA/offline/video/payment, cân nhắc chạy cd fe && npm run test:e2e:smoke.

15.3. Khi sửa backend

  • Chạy cd backend && mvn -DskipTests compile -B trước để bắt lỗi compile.
  • Nếu sửa use case hoặc rule, chạy test liên quan bằng Maven.
  • Nếu sửa schema, chạy lại Docker DB local từ trạng thái sạch hoặc kiểm Flyway migration.

15.4. Checklist trước khi nói "done"

Cần kiểmBằng chứng nên có
Build frontendnpm run build pass.
Compile/backend testmvn -DskipTests compile -B hoặc test liên quan pass.
Runtime local/actuator/health trả {"status":"UP"}.
Flow UIẢnh chụp hoặc mô tả màn đã smoke: login, tạo course, upload, học bài.
Không commit rácgit status --short chỉ có file cần nộp, không có .env, cache, build output.

16. Câu Hỏi Bảo Vệ Thường Gặp

Vì sao tách frontend và backend?

Để UI và business API độc lập. Frontend Angular tối ưu trải nghiệm người dùng, backend Spring Boot đảm nhận security, transaction và nghiệp vụ.

Vì sao dùng Clean Architecture/DDD?

Vì hệ thống LMS có nhiều nghiệp vụ: course, learning, assessment, payment, video. Tách domain/use case/infrastructure giúp code dễ bảo trì, dễ test, và tránh việc controller hoặc JPA bị trộn với business rule.

Vì sao dùng publication snapshot?

Teacher có thể sửa bản nháp mà không ảnh hưởng học viên đang học. Học viên đọc nội dung đã công bố/approved, còn bản nháp mới chỉ ra ngoài sau khi submit và admin duyệt.

Vì sao upload file qua presigned URL?

File lớn không nên đi qua backend web request. Presigned upload cho phép browser upload trực tiếp lên storage, backend chỉ quản lý session, quyền truy cập và metadata.

Vì sao video có worker riêng?

Encode/packaging video tốn CPU và thời gian. Tách dedicated video worker giúp backend web vẫn phục vụ user ổn định khi có nhiều video đang ingest.

Nếu thầy hỏi "em đóng góp phần nào?"

Hãy trả lời theo module/luồng mình nắm chắc. Ví dụ: "Em nắm luồng teacher tạo khóa học, student học bài, upload/video, và cách backend DDD xử lý từ controller đến database."

17. Lịch Học Tới Ngày 20

NgàyNội dungKết quả cần đạt
Ngày 1Kiến trúc tổng quan + chạy localNói được request flow và mở được app local
Ngày 2Auth/role/JWT + route guardsGiải thích được phân quyền bốn role
Ngày 3Teacher course editorChỉ được file UI, store, API, backend course use case
Ngày 4Student learning + progressGiải thích được publication/progress/offline
Ngày 5Quiz/assignment/gradingNắm bảng DB và backend module assessment
Ngày 6Video/upload/payment/adminTrả lời được các câu hỏi khó về runtime
Ngày 7Luyện demo và sửa lỗi nhỏChạy build/test, sửa UI/API nhỏ tự tin
Cách học tốt nhất: mỗi ngày chọn một luồng, mở UI local, bấm thao tác, rồi truy ngược code: route -> component -> service -> API client -> controller -> use case -> repository -> database.