SDD-038 · Xếp hạng, membership và mở khoá
Một phần của SDD-038 Cổng IELTS. Phần đầu là đặc tả đang áp dụng, mỗi mục giữ số § cũ; diễn biến và lý do các vòng quyết định nằm ở mục Lịch sử quyết định cuối trang.
34. Bảng xếp hạng learner và ví Nemo (SRC-901, 2026-09-20)
Trang: learn.nemo12.com/ielts/leaderboard. Bốn quyết định chốt trong chat cùng ngày: hiện tên và avatar (không ẩn danh), giải tháng xếp theo tổng giờ trong tháng, ví Nemo làm đủ cả sổ giao dịch, và "gần như live" nghĩa là tự gọi lại mỗi 30 giây.
a. Hai chế độ đọc, và chúng cố ý khác nguồn
Thứ hạng tuần này tính được bằng một câu SUM trên learning_events, và trang làm đúng thế cho tuần đang chạy. Nhưng "nhiều năm sau vẫn xem lại được" là một yêu cầu khác hẳn: bảng của một tuần đã qua phải không đổi dù sau này ta sửa cách tính giờ, dù learner rời đi, dù một dòng sự kiện hỏng bị xoá. Một con số đã gắn với 100.000 đồng tiền thật thì không được phép tự đổi sau lưng người đã nhận nó.
| Nguồn | Khi nào | |
|---|---|---|
| Tuần/tháng đang chạy | learning_events, tính trực tiếp | gọi lại mỗi 30s |
| Tuần/tháng đã xong | leaderboard_weeks · leaderboard_months | ảnh chụp, không đổi nữa |
Chốt sổ chạy lúc 02:00 thứ Hai giờ VN, bám nhánh cron 0 19 * * * đã có (cùng nhịp thư tuần) nhưng trong withLock và try/catch RIÊNG: đây là lượt duy nhất trong tuần cộng tiền vào ví, nó không được chết theo một lá thư gửi hỏng. Tháng TRƯỚC được chốt lại ở mọi thứ Hai — tháng trước thì lúc nào cũng đã kết thúc nên chốt không bao giờ sớm, còn chốt lại thì vô hại vì mọi thao tác đều chống trùng. Cách tính "chỉ chốt khi tuần vừa đóng nằm trọn trong tháng trước" nghe hợp lý nhưng để lọt đúng những tháng có ngày cuối rơi vào giữa tuần.
b. Luật thưởng, và chỗ nó có thể sai thành tiền mặt
- +100 Nemo cho một tuần đạt cả hai vế: từ 12 giờ trở lên và đủ bảy ngày có ít nhất một lượt học. Thiếu một ngày thì không có gì, dù học 40 giờ.
- +300 / 200 / 100 Nemo cho hạng nhất/nhì/ba của tháng theo tổng giờ.
- 1 Nemo = 1.000đ (chốt với chủ dự án), nên thưởng tuần 100.000đ vào sổ là 100 Nemo và đứng cùng thang với giải tháng. Việc chuyển khoản diễn ra ngoài đời; hệ chỉ ghi lại rằng nó đã xong (
nemo_ledger.paid_at).
NGÀY HỌC BẮT ĐẦU LÚC 04:00 GIỜ VN, không lúc 00:00 (Audit #015 D-9). Vế "đủ bảy ngày" đếm COUNT(DISTINCT day) trên learning_events, và day từng là ngày trên tờ lịch. Nhóm learner luyện thi buổi tối lại là nhóm hay ngồi qua nửa đêm nhất: mở bài 23:40 Chủ nhật, đóng tab 00:15 thứ Hai. Theo tờ lịch thì buổi ấy thuộc thứ Hai, mà thứ Hai đã là TUẦN SAU, nên Chủ nhật trống trơn, tuần chỉ còn sáu ngày, và 100.000đ mất đi dù learner thật sự đã học đủ bảy ngày. Sai lệch ấy đi một chiều và lặp lại mỗi tuần với đúng những người chăm nhất.
Lằn ranh 04:00 phải muộn hơn giờ học khuya nhất và sớm hơn giờ dậy sớm nhất; đó là chỗ rộng nhất giữa hai mốc. Hàm là learningDay trong shared/time.ts, dùng cho CẢ đường ghi lẫn đường đọc: chỉ sửa chỗ ghi thì learner đóng bài lúc 00:30 sẽ thấy thẻ "hôm nay 0 phút" ngay sau khi vừa học nửa tiếng, tức là sửa một chỗ đúng lên mà làm một chỗ khác nói dối đi. vnDate/vnDay vẫn là ngày trên TỜ LỊCH và không đổi: hạn nộp, ngày thi, lịch buổi học đều đọc nó.
Hệ quả người dùng nhìn thấy: học lúc 02:00 sáng thứ Ba được tính vào thứ Hai. Thẻ "hôm nay", chuỗi ngày liên tiếp và thư phụ huynh đều theo luật này.
Chốt sổ phải chạy SAU lằn ranh, và đó là điểm hỏng im lặng nếu quên. Chốt tuần lúc 02:00 thứ Hai là chốt trong lúc ngày học Chủ nhật còn đang chạy: learningDay lúc ấy còn trả về Chủ nhật, phép trừ bảy ngày lùi mất một tuần, và sổ chốt NHẦM TUẦN mà không một dòng lỗi nào. Nên lượt chốt đã tách khỏi cron 02:00 của thư tuần sang cron riêng lúc 05:00 giờ VN (một tiếng dự phòng sau lằn ranh), hằng số cron nằm CẠNH hằng số lằn ranh trong shared/time.ts, và time.test.ts so thẳng hai con số — dời sai là test đỏ chứ không phải tiền vào ví người khác. Nhánh cron còn một phép gác chạy: chốt trước lằn ranh thì ném lỗi và bỏ tuần ấy, vì một tuần bỏ sót chốt tay lại được còn một tuần chốt nhầm thì không.
Dữ liệu cũ không được ghi lại. Các tuần đã chốt đều có ảnh chụp trong nemo_leaderboard_weeks và tiền đã vào sổ ví; tính lại day cho quá khứ chỉ làm ảnh chụp và sổ ví nói khác nhau mà không trả thêm cho ai đồng nào. Tuần đang mở vì thế có lẫn hai cách đóng dấu, và nó tự hết sau một tuần.
Năm tuần đầu là của RIÊNG từng learner, không phải của cả lớp: người vào sau vẫn có đủ năm tuần của mình. Và tuần mở màn lẻ không tính. Learner bắt đầu vào thứ Năm thì tuần lịch ấy chỉ còn bốn ngày, không cách nào đủ bảy — tính nó là "tuần 1" là lấy mất một trong năm tuần thưởng chỉ vì họ đã bắt đầu giữa tuần. Tuần ấy vẫn lên bảng, vẫn có thứ hạng, chỉ là chưa vào đồng hồ đếm.
Trả thưởng hai lần là chuyện sẽ xảy ra — cron chạy lại sau một lần deploy hỏng, hoặc có người chạy tay để vá một tuần bị lỡ. Nên chống trùng bằng cấu trúc dữ liệu chứ không bằng trí nhớ người viết cron: nemo_ledger.id = '<kind>:<ref>:<learner_id>' + INSERT OR IGNORE, và ví chỉ được cộng khi thực sự có dòng mới được ghi. Đảo thứ tự — cộng ví trước, ghi sổ sau — thì một lần chạy lại là một lần số dư lệch khỏi sổ, và không có cách nào biết điều đó đã xảy ra.
MỘT KỲ CHỈ CHỐT ĐƯỢC SAU KHI NÓ KẾT THÚC (Audit #015 D-8), và đó là mặt trái của chính cơ chế chống trùng ở trên. Ảnh chụp bảng sửa lại được vì nó ghi bằng INSERT OR REPLACE; sổ ví thì không, vì nó cố ý dùng INSERT OR IGNORE với khoá <kind>:<kỳ>:<learner>. Nên một lần bấm sớm là vĩnh viễn: chốt tháng 9 vào ngày mùng 10 thì người đang dẫn lúc ấy nhận 300 Nemo và không thu lại được; tới đầu tháng 10 chốt lại, người thật sự về nhất cũng được cộng 300. Tháng ấy trả giải nhất cho hai người, tổng giải vượt luật 300.000đ, và bảng hiển thị mâu thuẫn với sổ ví.
Đường tháng hở rộng hơn đường tuần, nên nó mới là chỗ đáng lo: luật tuần cần đủ bảy ngày nên gần như không đạt được giữa tuần, còn thứ hạng tháng thì có ngay từ mùng 2.
Phép gác nằm trong closeWeek/closeMonth chứ không ở route, vì cron và nút bấm tay dùng chung hai hàm ấy và một phép gác chỉ có ở một đường là một phép gác chờ bị đi vòng. Hai hàm nhận mốc thời gian làm tham số (cron truyền controller.scheduledTime xuống) nên luật này kiểm được mà không phải đụng vào đồng hồ máy. Route dịch lỗi ra 400 kèm ngày kỳ ấy đóng lại, không phải 500: người bấm cần biết mình phải đợi tới bao giờ. Nút bấm tay vẫn chạy bình thường cho kỳ đã xong, vì nó tồn tại đúng cho ngày cron lỡ một nhịp.
Bảng xếp hạng đọc theo NGÀY HỌC, cùng luật 04:00 với lúc ghi (§34b trên). Nếu đường đọc còn dùng ngày lịch thì trong khoảng 00:00 tới 04:00 trang sẽ bày tuần MỚI trong khi số giây học lúc ấy vẫn đang rơi vào tuần CŨ, và cờ "tuần này còn chạy" cũng nói sai theo.
c. Đồng hạng, và vì sao bằng giờ thì xét tiếp số ngày
Xếp theo giờ, nhiều hơn đứng trước. Bằng giờ thì người học rải đủ bảy ngày đứng trên người dồn vào hai ngày cuối tuần — đó chính là thói quen cả chương trình này đang dựng. Bằng nốt cả hai thì mới là đồng hạng thật, và khi ấy cả hai cùng số hạng, hạng kế tiếp nhảy cóc (1, 1, 3). Ở giải tháng, hai người đồng hạng nhất thì cả hai nhận đủ 300 và không còn ai nhận 200: chia đôi giải nhất cho hai người học bằng nhau là phạt họ vì đã ngang tài.
d. Phạm vi mở ra, đúng ba thứ
Mọi route khác chạm dữ liệu learner đều khoá theo từng learner. Bảng xếp hạng thì không thể — bản chất của nó là cho learner A thấy learner B. Nên phạm vi mở đúng bằng tên hiển thị · ảnh đại diện · số giờ, không email, không id gia đình, không điểm bài làm, không mục tiêu band; chưa đăng nhập thì không thấy gì. Ví thì vẫn khoá theo nhà như mọi dữ liệu learner khác (requireLearnerAccess): thấy thứ hạng của người khác là đúng ý chủ dự án, thấy số dư của người khác thì không.
Learner nghỉ cả tuần vẫn có mặt trên bảng với 0 giờ và đứng cuối. Giấu họ đi là xoá đúng cái thông tin họ cần nhất trong tuần ấy.
e. Còn mở
- Chưa có màn admin để đánh dấu "đã chuyển tiền": hiện chỉ có
POST /v1/admin/nemo-ledger/{ledgerId}/paidgọi tay. - Ví chưa có đường TIÊU Nemo. Sổ đã đỡ được khoản âm nên khi có thứ để đổi thì không phải sửa lược đồ.
- Chưa có thư báo "tuần này con được 100 Nemo". Lượt chốt sổ là chỗ tự nhiên để phát sự kiện ấy.
47. Bảng xếp hạng liệt kê mọi IELTS learner (SRC-924, 20.09.2026)
Nay điều kiện là có mặt trong mạch IELTS: có dòng learner_ielts_setup, learner_ielts_baselines, learner_ielts_goals, hoặc đã học một lượt. Learner không dính dáng gì tới IELTS vẫn không bị lôi lên bảng. Chưa học buổi nào thì learner_week đếm từ ngày vào (learners.created_at) thay vì từ buổi học đầu.
Không đổi: vẫn không lọc theo "có học trong tuần này" - nghỉ cả tuần thì đứng cuối với 0 giờ chứ không biến mất.
58. Mở khoá tuần tự bốn kỹ năng (SRC-859, 19.09.2026) - ⏳ chờ chủ dự án chốt
Phần này mô tả thiết kế; chưa hiện thực. Bốn điểm còn chờ chốt ở Q-154..Q-157.
58a. Đây là tầng thứ hai, nằm TRÊN luật mở khoá của §46
§46 (SRC-923) đã có một cái khoá: bên trong một kỹ năng, bậc nào mở và bài nào mở. Phần này là cái khoá giữa bốn kỹ năng: Reading mở trước, ba kỹ năng kia mở sau. Hai tầng độc lập, và một learner có thể đang mở Reading mà chỉ đi tới bậc 3 của nó.
Luật vẫn ở một chỗ: apps/learn/src/ielts/unlock.ts đã tồn tại và là hàm thuần, nên tầng này thêm hàm vào đó chứ không dựng file thứ hai. Nhưng có một khác biệt phải giữ rõ: §46 nói "đọc không được danh sách bài thì mở", vì đó là luật sư phạm. Tầng này thì không - nó là ranh giới quyền, có dính tới thời hạn dùng thử, nên server phải gác thật và lỗi mạng không được mở cửa.
58b. Ba cái khoá khác nhau, đừng gộp thành một
Câu chỉ đạo chứa ba luật độc lập, và gộp chúng vào một cột locked là cách chắc chắn nhất để sau này không ai giải thích nổi vì sao một learner cụ thể không mở được Listening:
| Khoá | Hỏi gì | Mở bằng gì |
|---|---|---|
| Thời hạn dùng thử | Hôm nay còn trong 30 ngày kể từ ngày hồ sơ mở không? | Thời gian trôi, hoặc một entitlement trả tiền |
| Thứ tự kỹ năng | Reading đã xong trọn vẹn chưa? | Làm đủ việc của chặng trước |
| Ý người thật | Dolphin có mở sớm cho em này không? | Một hành động có tên người và dấu thời gian |
Trạng thái learner nhìn thấy là kết quả AND của ba cái, nhưng lý do trả về phải nói rõ cái nào đang đóng: TRIAL_EXPIRED, PREREQUISITE_INCOMPLETE, NOT_YET_SCHEDULED. Một màn hình chỉ nói "chưa mở" buộc learner đi hỏi mentor, và mentor cũng phải đi tra, trong khi máy vốn biết.
58c. Ngó nghiêng vẫn phải được, và đó là ràng buộc chứ không phải ưu ái
"Đi vòng vòng và ngó nghiêng" nghĩa là mục lục luôn mở: tên kỹ năng, số cụm, số bài, mô tả ngắn đều hiện kể cả khi kỹ năng đang khoá. Thứ bị cắt là nội dung bài và quyền ghi tiến độ, đúng luật REQ-TRI-02 đã dựng cho curriculum: cắt ở server, không làm mờ ở giao diện. Một tấm thẻ mờ vẫn gửi đủ dữ liệu xuống máy learner, tức là cái khoá chỉ có trên màn hình.
Khác biệt với REQ-TRI: ở đó đơn vị khoá là lesson của một course; ở đây là một kỹ năng của sân luyện IELTS, gồm cả bài tập năm bậc (§5i), trang lý thuyết (§5e) và sổ bằng chứng (§5b-bis). Cùng một cơ chế kiểm quyền, khác đơn vị, nên là bảng riêng chứ không phải nhồi course_id giả.
58d. "Trọn vẹn" phải là một hàm, không phải một cảm giác
Điều kiện mở chặng sau đọc từ dữ liệu đã có, không thêm cột "đã hoàn thành": cùng nguyên tắc suy ra trạng thái của §2. Với Reading, một định nghĩa đọc được bằng máy là đủ ba thứ - hết trang lý thuyết của mọi năng lực nhỏ, mọi năng lực nhỏ đạt ít nhất một bậc tối thiểu trên thang năm bậc, và có bằng chứng ở ít nhất N buổi khác ngày (chống dồn một đêm). Ngưỡng cụ thể là Q-155.
Hàm ấy server dùng để gác và client dùng để vẽ thanh "còn thiếu gì" - hai bản chép tay sẽ lệch nhau đúng vào ngày learner đứng sát ngưỡng.
58e. Lịch theo tháng là một bảng, không phải một hằng số
"Hệ thống unlock tuần tự sau từng tháng" nghĩa là mỗi kỹ năng có một mốc unlock_after_days tính từ ngày hồ sơ IELTS mở. Để trong bảng cấu hình chứ không viết cứng, vì chính chủ dự án sẽ muốn đổi thứ tự và đổi khoảng cách sau khi nhìn learner thật; đổi một dòng dữ liệu khác hẳn đổi một hằng số rồi chờ deploy.
Lịch tự động và tay Dolphin cộng dồn chứ không đè: cái nào tới trước thì mở. Chiều ngược lại (Dolphin khoá lại một kỹ năng lịch đã mở) là Q-156 - nó có ích khi một learner đi quá nhanh và học rỗng, nhưng cũng là cái nút lấy lại thứ đã cho, nên cần chủ dự án quyết chứ không phải phiên tự chọn.
58f. Chỗ cấu hình trong dolphin.nemo12.com
Hai bề mặt, đừng trộn:
- Mặc định toàn hệ (
/ielts-unlock): bảng bốn kỹ năng, mỗi dòng có thứ tự, số ngày chờ, điều kiện tiên quyết. Đây là thứ áp cho mọi learner mới. Sửa được thì cũng phải ghi ai sửa lúc nào. - Từng learner (trong hồ sơ learner đang có): trạng thái bốn kỹ năng, lý do đang khoá, và nút mở sớm. Mở sớm bắt buộc một dòng lý do, vì đây là chỗ một người vượt qua cái cổng mà hệ đặt ra; sáu tháng sau không ai nhớ nổi vì sao em này được mở Writing từ tuần thứ hai.
Mọi thao tác mở/khoá ghi audit_log như mọi việc khác trong Dolphin (REQ-MEN-11). Giao diện dolphin viết tiếng Anh theo REQ-MEN-16; sân luyện learner cũng tiếng Anh theo §8a.
58g. Chỗ dễ sai
- Khoá xuất hiện sau khi learner đã học. Sân luyện đang mở hết cho người dùng hiện có; bật khoá là lấy đi thứ đã cho. Learner nào đã có bằng chứng ở một kỹ năng thì kỹ năng ấy coi như đã mở, một lần, lúc chạy migration.
- Trial hết hạn giữa một bài đang làm. Bài đang mở phải nộp được; cổng chặn ở lúc bắt đầu một việc mới, không ở lúc kết thúc việc đang dở, nếu không learner mất luôn phần vừa làm.
- Khoá và Learner Model. Bằng chứng không sinh ra ở kỹ năng đang khoá, nên mọi mức thành thạo của kỹ năng ấy là
INSUFFICIENT_RELIABLE_EVIDENCE(REQ-INT-22), không phải điểm thấp. Báo cáo cho bố mẹ phải phân biệt hai thứ đó.
88. Leaderboard: mười dòng, và learner luôn thấy mình (SRC-985, 22.09.2026)
Chỉ đạo: chỉ hiển thị 1 tới 10; learner phải nhận ra "à, đó là chính tôi"; bỏ cột số giờ; và ai không nằm trong top 10 thì có một bảng riêng với hai người trên, hai người dưới.
Vì sao mười dòng là đủ. Bản trước đổ ra cả lớp, và từ dòng thứ tư trở xuống mọi người cùng 0h: một danh sách dài toàn số không thì không xếp hạng ai cả, nó chỉ nói "hầu hết chưa học tuần này" bằng ba chục dòng.
Vì sao vẫn cần khối thứ hai. Một bảng xếp hạng chỉ có ích khi người đọc tìm thấy MÌNH trong đó. Cắt còn mười dòng mà learner đứng hạng 27 thì trang không còn nói gì với chính người đang mở nó. Khối "Where you are" lấy đúng năm dòng quanh họ, có kẹp hai đầu để learner đứng áp chót vẫn thấy đủ năm. Hai trên hai dưới, không nhiều hơn: thứ learner làm được gì đó với nó là khoảng cách tới người ngay trước mặt, không phải toàn cảnh cái đuôi bảng.
Vì sao bỏ cột giờ. Một cột số giờ cạnh tên từng người biến bảng thành chỗ so công sức của nhau, mà phần lớn các dòng lại là 0h - tức là nó chủ yếu công bố ai chưa học. Thứ tự đứng trong bảng đã nói đủ điều bảng cần nói.
Một ngoại lệ có chủ ý: câu "còn thiếu bao nhiêu" ở lại, nhưng chỉ trên dòng của chính learner. Đó là mốc thưởng của riêng họ (quyết định (3) ở đầu Leaderboard.tsx), không phải một cột để đọc ngang qua người khác. Nhãn +100 Nemo cũng ở lại vì nó là TIỀN, không phải giờ.
Nhãn YOU nay là một chip nền accent chữ trắng, không còn là một chữ nhỏ cùng cỡ với chữ xung quanh: trong mười dòng giống hệt nhau, một sắc chữ khác đi không đủ để mắt dừng lại.
Phép kiểm hours() (cắt xuống, không làm tròn lên - SRC-901) vẫn còn nguyên hiệu lực: nó chuyển sang đo con số trong câu "to go", vốn cùng hàm và cùng đúng cái mốc 43.200 giây.
95. Menu tài khoản chỉ còn ba lối, và Leaderboard có số thứ tự (SRC-999, SRC-1000, SRC-1001, 23.09.2026)
Menu góc phải (SRC-999, SRC-1000): chỉ còn Learning Plan, Learner Profile, Referral, cộng Sign out. Bốn mục cũ đi cả, mỗi mục một lý do:
Learning Setupnay chỉ là một cái vỏ dẫn tới đúng hai trang kia, nên vào thẳng là bớt một cú bấm.Settingstrùng việc với chính hai thẻ Current/Target trên trang Learning Plan.Profiletrỏ tới một trang hồ sơ thứ hai đứng cạnh Learner Profile - hai mục cùng nghĩa trong một menu là thứ learner phải thử mới biết khác nhau ở đâu.Accountmở một hộp thoại nói rằng tính năng đang được phát triển; một mục mở ra lời xin lỗi thì không đáng chiếm chỗ.
Sign out ở lại: bỏ nó là khoá learner trong phiên của mình. Các trang cũ (/ielts/setup, /ielts/profile, /ielts/settings) vẫn mở được qua đường dẫn, vì link learner đã lưu không được gãy.
AvatarMenu dùng chung với ba cổng khác, nên thay đổi đi qua một cờ onlyItems bật ở nơi GỌI, không sửa mặc định. Hai chỗ gọi trong sân IELTS (hub /ielts và mọi trang bên trong) dùng cùng một danh sách: hai nửa của cùng một sân mà mở ra hai menu khác nhau thì learner không học được nó.
Số thứ tự trên Leaderboard (SRC-1001): cột số nay là VỊ TRÍ liên tục 1..10, không còn là hạng đồng hạng của server. Sáu người cùng 0h đều mang hạng 5, nên bảng cũ đọc ra "4, 5, 5, 5, 5, 5" - mười dòng mà chỉ có năm con số - còn ba dòng đầu thì chỉ có huy chương, không có số nào. Huy chương nay đứng CẠNH số chứ không thay số: huy chương nói "top ba", số nói "thứ mấy". Trong khối "Where you are", số là vị trí thật trong cả bảng: learner đứng thứ 13 đọc thấy 13, không phải 3.
98. Trang Membership, Referral trong khung IELTS, và thông tin chuyển khoản (SRC-1010, SRC-1011, 23.09.2026)
Referral: đổi KHUNG, không đổi panel. ReferralPanel là bản sao Y HỆT giữa learn và marlins, và nó đã đọc màu từ token. Trang sáng chói không phải vì panel tô sai màu mà vì /referral sống trong khung SÁNG của cổng learn chung; tiêu đề "Rủ bạn cùng học" chìm mất vì đúng lẽ ấy. Nay có /ielts/referral đặt nguyên panel ấy vào PageBody của sân IELTS (.n12-deep), nên mọi token tự lật sang nền tối. /referral cũ vẫn sống cho cổng learn chung.
Membership (/ielts/membership) trả lời bốn câu theo đúng thứ tự learner hỏi: gói nào, còn bao lâu → tài khoản có bao nhiêu → học phí bao nhiêu → có ưu đãi gì, trả tiền thế nào. Không con số nào được tính ở trang: kỳ dùng thử do modules/ielts/membership.ts quyết, học phí và đợt giảm do showcase/membershipPricing.ts chọn, số dư đọc từ ví Nemo, ưu đãi giới thiệu đọc từ referral. Bốn nguồn độc lập: một nguồn hỏng thì chỉ thẻ của nó im lặng.
Gói dùng thử cần một bảng mới (learner_ielts_membership, migration 0286): repo chưa có chỗ nào lưu ai đang dùng thử hay đã là thành viên - curriculum_entitlements cấp quyền theo khoá học, còn membership_pricing chỉ nói giá. Kỳ dùng thử bắt đầu từ LẦN ĐẦU hệ thống ghi nhận nó, không lùi về ngày tạo tài khoản: learner đang học hôm nay chưa từng được hứa một kỳ hạn nào, và tính lùi thì phần lớn họ mở trang ra sẽ thấy chữ "hết hạn" cho một khái niệm vừa mới có. Ba mươi ngày tính CẢ ngày đầu (bắt đầu 01.10 thì hết hạn 30.10), ngày cuối vẫn "còn 1 ngày".
Số tài khoản tạm KHÔNG giống số thật (SRC-1011). Chỉ đạo là đăng tạm một số ngẫu nhiên; nhưng một dãy số ngẫu nhiên đủ dài có thể trùng tài khoản Techcombank có thật của một người lạ, và learner thấy nó trên trang học phí thì có lý do để chuyển tiền vào. Tiền chuyển nhầm không đòi lại được bằng một bản sửa mã. Nên toàn bộ thông tin chuyển khoản nằm ở MỘT chỗ (apps/learn/src/ielts/paymentConfig.ts) với cờ active; khi cờ còn tắt:
- số hiện ra dạng rõ ràng là số tạm (
0000 0000 1234), màu nhạt, không có nút chép; - ô QR là một khung chờ, không sinh mã tạm - một mã VietQR ngẫu nhiên vẫn mở ra được lệnh chuyển tiền thật trong app ngân hàng;
- một dòng cảnh báo nói thẳng: chưa chuyển khoản vào đây.
Khi có số thật: điền hai trường, bật active, và mọi chỗ hiển thị theo.
Menu góc phải thêm Membership và Referral nay trỏ tới /ielts/referral.
103. /referral cũ dẫn vào bản trong khung IELTS (SRC-1018, 24.09.2026)
Chủ dự án mở learn.nemo12.com/referral và thấy trang cũ (tiêu đề chìm trên nền tối). Bản mới nằm ở /ielts/referral (SRC-1010). /referral và bí danh /gioi-thieu nay mở thẳng bản mới; toast nhận ưu đãi cũng trỏ sang đó. Giữ đường cũ làm bí danh vì link đã nằm trong bookmark.
116. Trang Referral gọn, mở dần (SRC-1049, 25.09.2026)
Chủ dự án: "Trang này có quá nhiều thông tin, cần lược bớt 50%... chỉ hiển thị 30% lượng thông tin còn lại ngay từ đầu. Sau đó learner click vài cái để xem thêm." Bản riêng cho sân IELTS (ReferralSlim trong Referral.tsx; ReferralPanel dùng chung với Marlins giữ nguyên). Mở ra chỉ có một câu quyền lợi và link mời. Hai lớp sau: "Xem ai đã tham gia" và "Xem chính sách và ví dụ" (khối ấy tự gập thêm một lần). Đã bỏ: dòng mã riêng, hai dòng phụ dưới hai con số %, đoạn đối soát tay dài.
119. Thưởng học tập (SRC-1054, 25.09.2026)
Chủ dự án: "Nếu học đủ 10 hours, thì nhận một phần thưởng là 100000, trong mỗi đợt 3 tháng, bạn được nhận tối đa 10 lần... Hệ thống sẽ ghi nhận điểm vào tài khoản. Vào cuối mỗi 3 tháng, học sinh sẽ nhận được chuyển khoản toàn bộ số tiền đó... top 3 trong tuần cũng có phần thưởng."
- Tính ở server (
modules/ielts/studyRewards.ts,GET /v1/learners/{id}/ielts-study-rewards): đợt 3 tháng nối tiếp từlearner_ielts_membership.started_on; giờ học = tổnglearning_events.secondstrong đợt (cùng sổ với dashboard); số lần thưởng = min(10, floor(giờ / 10)); tiền = số lần x 100.000đ. Là phép tính từ sổ công sức, không có bảng tiền riêng để lệch. Chuyển khoản cuối đợt vẫn làm tay. - Hiện ở: thẻ "Study rewards" trên Membership (tiến độ tới lần thưởng kế tiếp); dòng "Top 3 each week win a prize." trên Leaderboard; cụm "Thưởng khi học chăm" trong nút hướng dẫn (trang chính, Membership, Leaderboard); khối "Học chăm được thưởng" trên trang học phí công khai (cả bản tĩnh).
- Số tiền thưởng top 3 tuần chưa được chốt, nên các trang chỉ nói "có phần thưởng". Mọi chỗ đều nói đây chưa phải chính sách duy nhất.
121. Thưởng xếp hạng tuần: số tiền và ngày bắt đầu (SRC-1058, 25.09.2026)
Chủ dự án chốt phần còn bỏ ngỏ ở §119: "Xếp hạng tuần sẽ bắt đầu tính từ đầu tháng 10 năm 2026. Nhất tuần được 300000, nhì được 200000, giải 3 của tuần được 100000." Cập nhật ở mọi chỗ nói về thưởng tuần: dòng trên Leaderboard, thẻ Study rewards (Membership), cụm "Thưởng khi học chăm" trong nút hướng dẫn, khối "Học chăm được thưởng" trên trang học phí công khai (cả bản tĩnh). Việc chi thưởng tuần vẫn làm tay theo bảng xếp hạng; hệ thống chưa tự ghi khoản thưởng tuần vào tài khoản.
130. Học miễn phí 14 ngày, đếm ngược và email mỗi 3 ngày (SRC-1087, 27.09.2026)
Chủ dự án: mỗi learner học free 100% trong 14 ngày; hệ thống cho biết còn bao nhiêu ngày nữa là hết free; gửi email thông báo sau mỗi 3 ngày.
TRIAL_DAYS30 → 14 (workers/api/src/modules/ielts/membership.ts). Chỉ áp cho kỳ dùng thử MỚI (tạo lúc learner mở sân lần đầu): dònglearner_ielts_membershipđã có giữends_on, vì rút ngắn một kỳ đã hứa là lấy lại lời hứa. Chủ dự án muốn áp cho cả người cũ thì cần một lượt cập nhật dữ liệu.- Thanh trên của sân luyện hiện "Free: N days left" (ở mọi bề ngang, bấm vào mở Membership) khi
plan='trial'; hết kỳ thì "Free trial ended". Thanh tiến độ ở trang Membership tính theo độ dài thật của từng kỳ thay cho số 30 ghi cứng. - Email
ielts-trial(lượt quét thư IELTS hằng ngày): ngày thứ 3, 6, 9, 12 kể từ ngày bắt đầu ("còn N ngày học miễn phí") và ngày đầu sau khi hết ("kỳ học miễn phí đã kết thúc", mời xem gói thành viên). Khoá chống trùng theo (learner, số ngày đã qua). Là thư giao dịch về tình trạng tài khoản nên miễn trần tuần của thư IELTS (2 thư/tuần, nâng lên 3 từ 07.10.2026 theo SRC-1289); vẫn chịu trần ngày chung (luật thư, memorynemo12-email-rules). Thành viên không nhận. Test:ieltsCampaigns.test.ts"kỳ học miễn phí 14 ngày".
126e. Lịch Speak together hằng tuần (SRC-1088, 27.09.2026)
Chủ dự án: lịch luyện Speaking hằng tuần, tối thứ Tư 20:00-21:30 và sáng Chủ nhật 10:00-11:30; learner đăng ký mọi buổi, chậm nhất 2 giờ trước (SRC-1124, trước đó 48 giờ); xem lại các buổi đã đăng ký và chi tiết từng buổi; buổi 1 My Family, buổi 2 Food; bỏ bài "A New Classmate".
- Buổi KHÔNG lưu trong D1:
workers/api/src/modules/speakPair/schedule.tstính ra từ mốc thứ Tư 30.09.2026 20:00 giờ VN, hai khung mỗi tuần, chủ đề xoay vòng theoTOPICS(family, favourite-food, weekend, shopping, ...). Mã buổi là ngày giờ VN (2026-09-30-2000). Chỉ bảngspeak_schedule_signups(migration 0303) lưu ai đăng ký buổi nào. - API:
GET /speak-schedule(8 buổi tới + buổi của tôi),GET/POST/DELETE /speak-schedule/{mã}. Đăng ký bị từ chối khi còn dưới 2 giờ (SRC-1124); huỷ được tới lúc buổi bắt đầu. - Giao diện (tiếng Anh đơn giản, SRC-1084): khối "Weekly sessions" trên trang Speak together với hai tab Upcoming / My sessions; trang chi tiết
/ielts/speaking/pair/sessions/<mã>có ảnh, bối cảnh, số người đã đăng ký, nút đăng ký hoặc huỷ, lối sang bài học. - Bỏ bài
new-classmate; 11 bài đánh số lại 1-11. Đổi thứ tựTOPICSlà đổi chủ đề mọi buổi tương lai.
126f. Link Zoom và email nhắc trước mỗi buổi (SRC-1090, 27.09.2026)
- Link Zoom cố định của lịch tuần đọc từ biến môi trường
SPEAK_ZOOM_URLcủa worker API. Chỉ người đã đăng ký và buổi chưa kết thúc mới nhậnzoom_url; trang chi tiết buổi có thẻ "Zoom", dòng buổi trong "My sessions" có link tắt. Chưa đặt biến thì trang và thư nói link sẽ có trước buổi. - Email
ielts-speak-reminder(speakPair/reminders.ts, gọi từ cron hằng giờ phút 10): gửi cho người đã đăng ký những buổi bắt đầu trong (60, 120] phút tới, tức 18:10 cho buổi 20:00 thứ Tư và 08:10 cho buổi 10:00 Chủ nhật; kèm giờ, chủ đề, link Zoom. Khoá chống trùng (buổi, learner); thư giao dịch nên miễn trần ngày. Test trongspeakPair/schedule.test.ts. - Việc còn chờ chủ dự án: link Zoom thật để đặt vào
SPEAK_ZOOM_URL(vars trongworkers/api/wrangler.jsonc, đi qua deploy GitHub).
135. Membership gọn một nửa (SRC-1115, 28.09.2026)
Chủ dự án: bớt 50% nội dung trên /ielts/membership, giấu hết, khi nào cần thì bấm để xem. Trên cùng chỉ còn hai thẻ learner hỏi mỗi lần vào (Your plan, Your money). Bốn phần còn lại thành bốn dòng gập (More trong IeltsMembership.tsx), mặc định đóng: School code, Study rewards, Price and offers, How to pay (kèm video). Không bỏ nội dung nào, chỉ đổi thứ hiện sẵn.
Lịch sử quyết định
Từ mục 34. Bảng xếp hạng learner và ví Nemo (SRC-901, 2026-09-20)
Chỉ đạo chủ dự án 2026-09-20: "Tạo ra 1 trang riêng, dạng như Leader Board, để liệt kê danh sách các IELTS Learner... có thể là theo giờ đã học. Trong 5 tuần đầu tiên, mỗi tuần mỗi learner học 12 hours trở lên, và không bỏ ngày nào (ngày nào cũng học tối thiểu 1 bài), thì được thưởng 100.000 VNĐ vào tài khoản của mỗi người... Nhất nhì và ba của tháng thì được thêm 300, 200 và 100 Nemo nữa. Bảng xếp hạng tuần sẽ được lưu lại dài hạn, để sau đó nhiều năm đều có thể xem lại. Mọi cá nhân đều biết thứ tự của chính mình, và thứ tự của những người khác, gần như Live."
Từ mục 47. Bảng xếp hạng liệt kê mọi IELTS learner (SRC-924, 20.09.2026)
Chỉ đạo chủ dự án 2026-09-20: "Cần liệt kê tất cả IELTS Learners ra đây, sao lại chỉ liệt kê mình tôi?"
§34 (SRC-901) đòi learner đã từng ghi được ít nhất một dòng learning_events. Ý định là đừng bày người chưa từng dùng sản phẩm; hệ quả là learner đã đăng nhập, đã khai Learning Setup, đã khai điểm nền mà chưa làm xong bài nào thì không tồn tại trên bảng, kể cả với chính họ - mà bài bỏ dở cố ý không ghi dòng nào (SRC-919).
Từ mục 58. Mở khoá tuần tự bốn kỹ năng (SRC-859, 19.09.2026) - ⏳ chờ chủ dự án chốt
Chỉ đạo chủ dự án 2026-09-19: "Khi mới vào hệ thống IELTS, Learner có thể đi vòng vòng và ngó nghiêng, và học thử một chút, ví dụ được dùng thử hệ thống trong 30 ngày. Các tính năng cần được unlocked tuần tự. Learner không thể unlocked nếu không đảm bảo làm đủ và trọn vẹn những gì trước đó. Learner được phép dùng tính năng READING đầu tiên. Các skills còn lại sẽ được unlocked bởi các Dolphin (các Mentors), hoặc hệ thống sẽ unlock tuần tự sau từng tháng. Cần có chỗ nào đó trong dolphin.nemo12.com để cấu hình việc lock hoặc unlock."
Từ mục 98. Trang Membership, Referral trong khung IELTS, và thông tin chuyển khoản (SRC-1010, SRC-1011, 23.09.2026)
Chỉ đạo: trang Referral phải cùng màu với sân IELTS, có header và footer; một trang mới cho learner thấy học phí, số dư, gói hiện tại (ví dụ Trial) và ngày hết hạn dùng thử (mặc định 30 ngày); một chỗ xem ưu đãi; và chỗ hiện ngân hàng (Techcombank), số tài khoản, mã QR để thanh toán - số thật gửi sau, "đăng tạm số ngẫu nhiên lên đã".