feat: complete the thirteen-stage financial learning path
Backend CI / type-check, build, tests (push) Successful in 1m16s

Server grades levels 7-13 and keeps castle attempt results separate from stored progress.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-22 19:41:24 +03:30
co-authored by Cursor
parent dfb20ca92c
commit 866be22395
52 changed files with 3840 additions and 30 deletions
+57
View File
@@ -0,0 +1,57 @@
# وضعیت مسیر آموزشی ۱۳ مرحله
تاریخ بررسی: ۲۰۲۶-۱۰-۲۲. این سند وضعیت محلی commit‌نشده است. موفقیت CI قبلی با SHA قدیمی backend این تغییرات را تأیید نمی‌کند.
## وضعیت مراحل
هر ۱۳ مرحله در هر دو فهرست `gold-land/levels` و `progression/levels` فعال است. مرحله ۱۴ وجود ندارد.
| مرحله | عنوان | نوع | کلید |
| ---: | --- | --- | --- |
| ۱ | پول چیه؟ | lesson | what-is-money |
| ۲ | پس‌انداز | lesson | saving |
| ۳ | نیاز و خواسته | lesson | needs-and-wants |
| ۴ | طلا چیه؟ | lesson | what-is-gold |
| ۵ | بازار طلا | lesson | gold-market |
| ۶ | تورم | lesson | inflation |
| ۷ | بودجه‌بندی | game | budgeting |
| ۸ | ریسک و بازده | challenge | risk-return |
| ۹ | سبد سرمایه‌گذاری | game | portfolio |
| ۱۰ | سود مرکب | lesson | compound-interest |
| ۱۱ | خرید هوشمند | game | smart-shop |
| ۱۲ | هدف مالی | challenge | financial-goal |
| ۱۳ | آزمون قلعه | final_exam | castle-exam |
کارت «چالش آموزشی» خانه و مسیر `/challenge` چالش روزانه کاتالوگ است و آزمون نقشه نیست.
## اجرای بررسی
Node مورد انتظار در تنظیمات `22.18.0` است. اگر همان نسخه نصب نباشد، نسخه واقعی را گزارش کن و `.nvmrc`، engines و CI را عوض نکن.
```text
cd backend-nest
npm run type-check
npm run build
npm test
cd frontend
npm test
npm run type-check
npm run build
$env:PLAYWRIGHT_CHROME_CHANNEL='chrome'
npx playwright test -c e2e/playwright.config.ts
```
`npm test` در backend شامل suiteهای مراحل ۷ تا ۱۳ است. `frontend/test/run-all-tests.mjs` هم همان‌ها را دارد. Playwright همه فایل‌های `e2e/*.spec.ts` را، از جمله `full-path.spec.ts`، اجرا می‌کند. journey کامل فقط روی پروژه desktop است تا زمان کنترل شود. تست‌های موبایل و دسکتاپ اختصاصی مراحل جدا مانده‌اند.
تست‌ها باید با دیتابیس و حساب موقت باشند.
## نتیجه تلاش و بهترین پیشرفت
امتیاز پاسخ complete، نتیجه همان تلاش است. ستاره ذخیره‌شده، بهترین امتیاز و `completed_at` فقط وقتی تلاش به ستاره برسد به‌روز می‌شوند.
تلاش زیر ۶۰ در مراحل ۱ تا ۱۲ رد می‌شود. در آزمون قلعه همان شکل پاسخ برمی‌گردد، ولی completion و پاداش ثبت نمی‌شود. اگر قبلاً قبول شده باشد، تلاش ناموفق بعدی ستاره، بهترین امتیاز و تاریخ تکمیل قبلی را نگه می‌دارد و پاداش صفر است. پیام «آفرین» فقط وقتی نتیجه همین تلاش معتبر است نشان داده می‌شود، نه به‌خاطر ستاره قبلی.
## کارهای باز
Docker آزمایشی، انتشار، چالش روزانه واقعی، ارزیابی بازی عمومی، و Friends هنوز انجام نشده‌اند. `pinnedRef` در قرارداد E2E هنوز SHA قدیمی backend است.
+48
View File
@@ -0,0 +1,48 @@
# مرحله ۱۰ — سود مرکب
## هدف
کودک فرق رشد بر مبلغ اولیه و رشد بر موجودی جدید را در یک مثال ریاضی ببیند. این مرحله وعده سود واقعی یا پیشنهاد سرمایه‌گذاری نیست. موجودی شبیه‌سازی از کیف پول کاربر جداست.
## فرض‌ها
- مبلغ اولیه ۱۰۰ سکه آموزشی.
- نرخ فرضی ثابت ۱۰٪ در هر دوره.
- هزینه، زیان و تورم در محاسبه مثال وارد نمی‌شوند و در متن توضیح داده می‌شوند.
- واحد زمان «دوره فرضی» است، نه سال یا ماه.
- احتمال و قرعه‌کشی وجود ندارد.
## فرمول
مبالغ با واحد صحیح یک‌دهم سکه نگه داشته می‌شوند تا ۱۳۳٫۱ به اعشار شناور خراب نشود. ۱۰۰ سکه = ۱۰۰۰ واحد.
رشد ساده در دوره `n` (از ۱):
`شروع = ۱۰۰۰ + ۱۰۰ × (n − ۱)` و `رشد = ۱۰۰` و `پایان = شروع + رشد`
رشد مرکب:
`رشد = شروع × ۱۰ / ۱۰۰` و `پایان = شروع + رشد` و شروع دوره بعد برابر پایان همین دوره است.
| دوره | ساده | مرکب |
| --- | ---: | ---: |
| ۰ | ۱۰۰ | ۱۰۰ |
| ۱ | ۱۱۰ | ۱۱۰ |
| ۲ | ۱۲۰ | ۱۲۱ |
| ۳ | ۱۳۰ | ۱۳۳٫۱ |
دوره دوم مرکب: ۱۱۰ + ۱۱ = ۱۲۱. دوره سوم مرکب: ۱۲۱ + ۱۲٫۱ = ۱۳۳٫۱.
مرور دکمه‌های دوره فقط تعداد ردیف‌های دیده‌شده را عوض می‌کند و مبلغ‌ها را تغییر نمی‌دهد.
## نمره
پنج سؤال، هرکدام ۲۰. زیر ۶۰ تکمیل نمی‌شود. سرعت و کلیک امتیاز نیست. پاسخ درست و توضیح در GET عمومی نیست؛ توضیح بعد از complete می‌آید.
1. `compound-q1` = `on-balance`
2. `compound-q2` = `121`
3. `compound-q3` = `from-110`
4. `compound-q4` = `compound`
5. `compound-q5` = `false`
پاداش پایه ۴۵ XP و ۱۸ سکه است.
+35
View File
@@ -0,0 +1,35 @@
# مرحله ۱۱ — خرید هوشمند
## هدف
کودک خرید را با نیاز، هزینه نهایی، اطلاعات کافی و بودجه مقایسه کند. ارزان‌ترین عدد روی کارت یا بزرگ‌ترین تخفیف همیشه بهترین انتخاب نیست. فروشگاه، کالا و سکه خیالی‌اند و از کیف پول بازی کم نمی‌شوند.
## هزینه‌ها
هزینه نهایی فقط وقتی هر دو مبلغ معلوم باشند: `قیمت + تحویل`. مبلغ نامشخص صفر نیست و جمع نهایی هم نامشخص می‌ماند.
| پیشنهاد | قیمت | تحویل | جمع |
| --- | ---: | ---: | ---: |
| دفتر ب | ۴۰ | ۵ | ۴۵ |
| دفتر ج | ۳۸ | ۱۲ | ۵۰ |
| مداد با تخفیف ۲۵٪ | ۳۰ | رایگان | ۳۰ |
| مداد بدون تخفیف | ۲۸ | رایگان | ۲۸ |
| وسیله با اندازه نامشخص | ۴۵ | نامشخص | نامشخص |
انتخاب مناسب دفتر، پیشنهاد ب است. برای دو مداد یکسان، ۲۸ از ۳۰ کمتر است. اگر اندازه یا تحویل نامشخص باشد، خرید فوری مناسب نیست.
## نمره
شش پاسخ با وزن برابر:
`score = Math.round(correctCount × 100 / 6)`
| درست | ۰ | ۱ | ۲ | ۳ | ۴ | ۵ | ۶ |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| امتیاز | ۰ | ۱۷ | ۳۳ | ۵۰ | ۶۷ | ۸۳ | ۱۰۰ |
زیر ۶۰ تکمیل نمی‌شود. ۴ درست یک ستاره، ۵ درست دو ستاره، ۶ درست سه ستاره. پاداش پایه ۴۵ XP و ۱۸ سکه است. سرعت و تعداد باز کردن کارت‌ها امتیاز نیست.
شناسه‌ها: `notebook-choice=b`، `notebook-why=fits-and-cheaper`، `discount-choice=b`، `discount-why=pay-amount`، `info-choice=ask`، `info-why=incomplete`.
ارسال فقط `{ questionId, answerId }` است. مبلغ، تخفیف، امتیاز و پاداش کلاینت پذیرفته نمی‌شود.
+36
View File
@@ -0,0 +1,36 @@
# مرحله ۱۲ — هدف مالی
## هدف
کودک یک خواسته را به هدف مشخص تبدیل کند، مبلغ باقی‌مانده و زمان لازم را حساب کند و بعد از تغییر شرایط برنامه را اصلاح کند. مبالغ خیالی‌اند و از کیف پول بازی کم نمی‌شوند. بدهی، درآمد تضمینی و حذف نیاز ضروری راه‌حل نیستند.
## سناریو
بسته ساخت کاردستی قلعه ۱۲۰ سکه است. موجودی ۳۰، پول هفتگی ۳۰ و نیاز ضروری عادی ۱۰ سکه است. پس‌انداز قابل انتخاب در حالت عادی ۵، ۱۰، ۱۵ یا ۲۰ است. خرج اختیاری = ۳۰ − ۱۰ − پس‌انداز. صفر بودن آن خطا نیست.
مبلغ باقی‌مانده ۹۰ است. تعداد هفته = `ceil(۹۰ / پس‌انداز هفتگی)`.
| پس‌انداز | هفته |
| ---: | ---: |
| ۵ | ۱۸ |
| ۱۰ | ۹ |
| ۱۵ | ۶ |
| ۲۰ | ۵ |
در هفته دوم نیاز ضروری ۲۰ سکه می‌شود. پس‌انداز همان هفته ۰، ۵ یا ۱۰ است. هفته اول با نرخ اولیه ثبت شده می‌ماند. از هفته سوم نرخ ادامه دوباره یکی از ۵، ۱۰، ۱۵ یا ۲۰ است و خودکار پر نمی‌شود.
`موجودی پس از هفته دوم = ۳۰ + نرخ اولیه + نرخ هفته دوم`
`هفته‌های باقی‌مانده = ceil(max(0, ۱۲۰ − موجودی) / نرخ ادامه)`
`کل زمان = ۲ + هفته‌های باقی‌مانده`
مثال ۱۵ / ۵ / ۱۵: موجودی ۵۰، پنج هفته دیگر، کل ۷ هفته، موجودی نهایی ۱۲۵.
## نمره
پنج سؤال ۲۰ امتیازی: ۰، ۴۰، ۶۰، ۸۰، ۱۰۰. سرعت برنامه نمره نیست. زیر ۶۰ تکمیل نمی‌شود. پاداش پایه ۵۰ XP و ۲۰ سکه است.
شناسه‌ها: `q-goal=specific`، `q-left=90`، `q-weeks=6`، `q-need=adjust`، `q-change=update`.
ارسال: `{ questionId: "plan", plan: { initialWeekly, exceptionWeekly, continueWeekly } }` به‌علاوه پنج `{ questionId, answerId }`. زمان و موجودی کلاینت پذیرفته نمی‌شود.
+32
View File
@@ -0,0 +1,32 @@
# مرحله ۱۳ — آزمون قلعه
## هدف
آزمون، آموخته‌های مراحل ۱ تا ۱۲ را مرور می‌کند و مفهوم تازه یاد نمی‌دهد. مثال‌ها خیالی و جدا از کیف پول‌اند. سرعت و حفظ اصطلاح نمره نیست. شکست پایان بازی نیست.
## نگاشت سؤال‌ها
| سؤال | مرحله | پاسخ |
| --- | ---: | --- |
| castle-q1 | ۱ | exchange |
| castle-q2 | ۲ | regular |
| castle-q3 | ۳ | water |
| castle-q4 | ۴ | moves |
| castle-q5 | ۵ | no |
| castle-q6 | ۶ | less |
| castle-q7 | ۷ | valid |
| castle-q8 | ۸ | keep |
| castle-q9 | ۹ | no |
| castle-q10 | ۱۰ | 121 |
| castle-q11 | ۱۱ | b |
| castle-q12 | ۱۲ | 6 |
## امتیاز
`score = Math.round(correctCount × 100 / 12)`
۰، ۸، ۱۷، ۲۵، ۳۳، ۴۲، ۵۰، ۵۸، ۶۷، ۷۵، ۸۳، ۹۲، ۱۰۰.
کمتر از ۶۰ تکمیل نمی‌شود و پاداش ندارد، ولی توضیح سؤال‌ها برمی‌گردد. ۸ درست یک ستاره، ۹ یا ۱۰ دو ستاره، ۱۱ یا ۱۲ سه ستاره. پاداش پایه ۱۰۰ XP و ۵۰ سکه است. بعد از مرحله ۱۳ مرحله دیگری باز نمی‌شود.
ارسال فقط دوازده `{ questionId, answerId }` است. امتیاز و ستاره کلاینت پذیرفته نمی‌شود. پاسخ درست در GET عمومی نیست.
+35
View File
@@ -0,0 +1,35 @@
# مرحله ۷ — مأموریت بودجه‌بندی
- گروه سنی: ۹ تا ۱۲ سال.
- مدت هدف: حدود ۵ تا ۸ دقیقه، بدون محدودیت زمانی.
- لحن: فارسی ساده و صمیمی.
- راهنما: تصویر موجود `professor` با عنوان «راهنما». شخصیت جدید ساخته نشده است.
- نوع فعالیت: `game` با کلید `budgeting`. این مرحله درس پنج‌سؤالی نیست.
## هدف آموزشی
کودک ۱۰۰ سکهٔ آموزشی را بین نیاز سفر (رفت‌وآمد و آب و غذا)، هدف پس‌انداز، و تفریح اختیاری تقسیم می‌کند و بعد از یک تغییر مشخص، برنامه را اصلاح می‌کند. این سکه‌ها از کیف پول کاربر کم نمی‌شوند.
## سناریو
- رفت‌وآمد: ابتدا حداقل ۲۰، بعد از اتفاق حداقل ۳۰.
- آب و غذا: حداقل ۲۵.
- پس‌انداز برای هدف: هدف ۲۰. حداقل ۱۰ برای بخشی از امتیاز.
- تفریح: اختیاری و بدون امتیاز اضافه.
- ذخیره: باقی‌ماندهٔ تخصیص‌نشده. صفر بودن آن خطا نیست و تنها راه درست معرفی نمی‌شود.
- گام تغییر: ۵ سکه.
- اتفاق ثابت: «هزینه رفت‌وآمد ۱۰ سکه بیشتر شد؛ حالا ۳۰ سکه لازم داری.» معرفی فقط می‌گوید ممکن است شرایط تغییر کند.
## Rubric
هر کدام از برنامهٔ اولیه و برنامهٔ نهایی پنج معیار ۱۰امتیازی دارد. مجموع ۱۰۰ است.
1. مجموع تخصیص حداکثر ۱۰۰.
2. تأمین حداقل رفت‌وآمد همان مرحله.
3. تأمین آب و غذا.
4. حداقل ۱۰ سکه پس‌انداز.
5. رسیدن پس‌انداز به ۲۰.
ورودی منفی، غیرصحیح، خارج از گام ۵، مجموع بیش از ۱۰۰، یا فیلد ناشناخته با خطای ورودی رد می‌شود. برنامهٔ اول بدون حداقل ۲۰ رفت‌وآمد و ۲۵ آب‌وغذا، و برنامهٔ نهایی بدون حداقل ۳۰ رفت‌وآمد و ۲۵ آب‌وغذا، با خطای جداگانهٔ نیاز ضروری رد می‌شوند و امتیاز نمی‌گیرند. پس‌انداز، تفریح و ذخیره شرط تأیید نیستند.
پاداش پایهٔ ۴۵ XP و ۱۸ سکه و پاداش هر ستارهٔ تازه (۵ XP و ۳ سکه) فقط از سرور و دفتر پاداش است. امتیاز زیر ۶۰ تکمیل نمی‌شود. مرحلهٔ ۸ غیرفعال می‌ماند.
+31
View File
@@ -0,0 +1,31 @@
# مرحله ۸ — ریسک و بازده
- گروه سنی: ۹ تا ۱۲ سال.
- مدت هدف: حدود ۵ تا ۸ دقیقه، بدون محدودیت زمانی.
- راهنما: تصویر موجود `professor`.
- نوع: `challenge` با کلید `risk-return`.
## هدف آموزشی
کودک تفاوت نتیجهٔ ممکن و نتیجهٔ تضمینی را می‌فهمد، احتمال زیان را تشخیص می‌دهد و انتخاب را با هدف و زمان نیاز به پول هماهنگ می‌کند. امتیاز برای فهم مفهوم و دلیل انتخاب است، نه برای انتخاب پرریسک یا سود اتفاقی.
## مثال ساختگی
برای ۲۰ سکهٔ آموزشی که از کیف پول بازی کم نمی‌شود:
- پیشنهاد الف، چهار پیامد هم‌احتمال: ۲۰، ۲۰، ۲۲، ۲۲.
- پیشنهاد ب، چهار پیامد هم‌احتمال: ۸، ۱۶، ۲۸، ۳۶.
در این مثال هر کارت شانس یکسان دارد. در دنیای واقعی همیشه احتمال‌ها را دقیق نمی‌دانیم. نبود زیان اسمی در پیشنهاد الف فقط مربوط به همین مثال است و تضمین عمومی سرمایه‌گذاری نیست. هیچ دارایی واقعی توصیه نمی‌شود.
## Rubric
پنج فعالیت، هر کدام ۲۰ امتیاز. جمع ۱۰۰.
1. امکان برگشت کمتر از ۲۰: پیشنهاد ب.
2. دریافت قطعی ۳۶ در پیشنهاد ب: خیر.
3. نیاز ضروری تا دو روز دیگر: پول را وارد پیشنهادهای نامطمئن نکن.
4. مقایسه: پیشنهاد ب امکان نتیجهٔ بالاتر و زیان بیشتر دارد؛ سود بیشتر قطعی نیست.
5. وعدهٔ سود زیاد بدون ریسک: تضمین معتبر فرض نکن، بررسی کن و از بزرگسال قابل اعتماد کمک بگیر.
امتیاز به قرعه وابسته نیست. پاسخ درست در محتوای عمومی سؤال نیست. پاداش پایه ۵۰ XP و ۲۰ سکه است. مرحلهٔ ۹ غیرفعال می‌ماند.
+49
View File
@@ -0,0 +1,49 @@
# مرحله ۹ — سبد سرمایه‌گذاری
## هدف آموزشی
کودک ببیند تقسیم ۱۰۰ سکهٔ آموزشی بین گزینه‌هایی با رفتار متفاوت می‌تواند وابستگی به یک گزینه را کم کند. این تقسیم زیان را حذف نمی‌کند و سود را تضمین نمی‌کند. زیاد بودن تعداد اسم‌ها به‌تنهایی تنوع مؤثر نیست.
صندوق‌های «آفتاب»، «باران» و «باد» خیالی‌اند و نمایندهٔ دارایی واقعی نیستند. سکه‌ها از کیف پول بازی کم نمی‌شوند.
## فرض شبیه‌سازی
- فقط سه سناریوی ثابت نشان داده می‌شود. قرعه‌کشی و احتمال وجود ندارد.
- سناریوها مثال‌اند، نه پیش‌بینی و نه مجموعهٔ کامل حالت‌های واقعی.
- هیچ سبدی به‌خاطر بهتر بودن در یک سناریو «بهترین» معرفی نمی‌شود.
| سناریو | آفتاب | باران | باد |
| --- | ---: | ---: | ---: |
| هوای آفتابی | +۲۰٪ | −۱۰٪ | ۰٪ |
| روز بارانی | −۲۰٪ | +۱۰٪ | ۰٪ |
| افت همگانی | −۱۰٪ | −۱۰٪ | −۱۰٪ |
سبد مرجع همیشه ۱۰۰ سکه فقط در آفتاب است و برای مقایسه نمایش داده می‌شود.
## فرمول
مبالغ مضرب ۱۰ و درصدها مضرب ۱۰ هستند. نتیجه با حساب صحیح این فرمول دقیق است و اعشار ندارد:
`ارزش = مبلغ × (۱۰۰ + درصد) / ۱۰۰`
تغییر نسبت به ۱۰۰ با متن «سود N سکه»، «زیان N سکه» یا «بدون تغییر» نوشته می‌شود.
نمونه‌ها:
- ۱۰۰ / ۰ / ۰: آفتابی ۱۲۰ (سود ۲۰)، بارانی ۸۰ (زیان ۲۰)، افت ۹۰ (زیان ۱۰).
- ۴۰ / ۳۰ / ۳۰: آفتابی ۱۰۵ (سود ۵)، بارانی ۹۵ (زیان ۵)، افت ۹۰ (زیان ۱۰).
- هر ترکیب معتبری در افت همگانی ۹۰ سکه است.
## نمره
امتیاز فقط از پنج سؤال، هرکدام ۲۰، می‌آید. ترکیب سبد و سود یا زیان شبیه‌سازی نمره نیستند. زیر ۶۰ تکمیل نمی‌شود. پاسخ درست در GET عمومی نیست.
1. `q-one` = `depend`
2. `q-loss` = `no`
3. `q-name` = `no`
4. `q-best` = `no`
5. `q-approach` = `balanced`
ارسال: `{ questionId: 'portfolio', holdings: { sun, rain, wind } }` به‌علاوهٔ پنج `{ questionId, answerId }`. سرور ترکیب و پاسخ‌ها را خودش اعتبارسنجی و محاسبه می‌کند.
پاداش پایه ۵۰ XP و ۲۰ سکه است و از complete همان مرحلهٔ Gold Land می‌آید، نه از کارت کاتالوگ `portfolio`.