Skip to Content

التحفيز

تربط وحدة التحفيز (WP-14-BE) كلَّ مكافأة مباشرةً بحدث تعلُّم قابل للقياس. لا يُكتسب XP بمجرد تسجيل الدخول أو إتمام جلسات أو قضاء وقت في التطبيق. الشيء الوحيد الذي يمكنه سكّ XP أو فتح وسام أو تقدُّم سلسلة مكافآت هو صف mastery_event تكتبه قناة القياس (V-8). يضمن ذلك أن ما تعكسه قائمة المتصدرين هو نموٌّ حقيقي لا مجرد تفاعل.

يحمل ملف التحفيز للطالب XP الخاص به فحسب، إلى جانب الصورة الرمزية وسلسلة المكافآت والأوسمة. لا تصل أي بيانات عن الترتيب بين الأقران أو مجاميع الصف أو المقارنة بالمرجعية إلى أي سطح يراه الطالب (SPEC-GAM-10). تحصل رموز الطالب على 403 في كل مسار لقائمة المتصدرين؛ ولا توجد نقطة نهاية لقائمة متصدرين خاصة بالطلاب.

المسارات

الطريقةالمسارمن يمكنه الاستدعاء
GET/api/students/{id}/gamification/profileالطالب نفسه، المعلم الذي يدرّس الصف، وليّ الأمر، المدير، المسؤول
GET/api/leaderboards/class/{classId}المعلم، المدير، المسؤول (رمز الطالب: 403)
GET/api/leaderboards/teacher/{teacherUserId}المعلم نفسه، المدير، المسؤول (رمز الطالب: 403)
GET/api/leaderboards/school/{schoolId}المدير والمسؤول فقط (المعلم: 403)
POST/api/teachers/badges/{studentBadgeId}/rescindالمعلم (محدودًا بـ ClassTeacher)
GET/api/admin/gamification/auditالمسؤول فقط
POST/api/admin/gamification/replayالمسؤول فقط

جميع معرّفات الطلاب والصفوف أعداد صحيحة موجبة. أي معرّف خارج النطاق أو ينتمي لمؤسسة أخرى يُعيد 404 (عدم الكشف عن الوجود). يتطلب كل مسار رمز جلسة صالحًا وسياق مستأجر organizationId.

GET /api/students/{id}/gamification/profile

يُعيد رصيد XP للطالب وطبقة الصورة الرمزية وحالة سلسلة المكافآت وقائمة الأوسمة. بالنسبة للمستدعين البالغين (المعلم، وليّ الأمر، المدير، المسؤول)، يحمل كل وسام أيضًا studentBadgeId وstatus حتى يتمكن المعلم من استدعاء نافذة سحب الوسام. أما في استجابة الطالب نفسه فيغيب هذان الحقلان؛ لا يرى الطالب إلا اسم الوسام وتاريخ منحه.

curl https://localhost:3000/api/students/42/gamification/profile \ -H "Authorization: Bearer <accessToken>"
{ "studentId": 42, "xpBalance": 340, "avatarTier": 3, "streak": { "current": 4, "longest": 11, "lastTickDate": "2026-06-26" }, "badges": [ { "code": "BADGE_JOURNEY_START", "nameAr": "بداية الرحلة", "descriptionAr": "...", "awardedAt": "2026-06-10T08:00:00.000Z" } ] }

xpBalance يُحسَب دائمًا عند القراءة (بعملية SUM على xp_transaction) ولا يُخزَّن أبدًا كعمود (V-6). avatarTier عدد صحيح من 1 إلى 5. يظهر studentBadgeId وstatus للمستدعين البالغين فقط.

اشتقاق XP

لا يُخزَّن XP كرصيد متراكم قائم بذاته. كل استدعاء يُعيد xpBalance يحسبه من سجل xp_transaction ذي الإلحاق فقط. تُكتب الرصيدات بواسطة المنسّق بعد كل حدث إتقان، مع حدٍّ أقصى للجلسة (القيمة الافتراضية الهندسية: 200 XP للجلسة؛ هذا الحدّ قيمة في مجموعة قواعد إصدار مُعيَّن لا ثابت مُضمَّن في الكود).

قيم MasteryEventSourceKind المغلقة الثماني التي يمكنها سكّ XP هي:

النوعالوصف
subskill_state_upتنتقل مهارة فرعية إلى حالة أعلى في قناة SSE
cbm_trend_above_goalيتجاوز اتجاه طلاقة CBM خط الهدف صعودًا
bundle_step_goal_metتُكتمل خطوة فردية ضمن خطة الحزمة
band_progressionيتقدّم الطالب نطاقًا أو أكثر في مستوى الكفاءة على مجموعة عناصر
skill_status_transitionينتقل وضع المهارة إلى حالة أكثر إيجابية
domain_status_escalationينتقل وضع المجال إلى حالة أكثر إيجابية
bundle_completionتُكتمل خطة الحزمة بأكملها
scaffold_tier_step_upينتقل الطالب إلى طبقة دعم أقل ضمن الحزمة

لا يوجد نوع حدث آخر. القائمة مغلقة؛ يرفض CI lint (‏lint:gamification-award-rule) أي موقع استدعاء يحاول سكّ XP خارج هذه المجموعة. إشارة التوقف do_not_decide_yet أو partial_domain_profile تقطع المنسّق كليًّا؛ لا يُكتب شيء.

طبقات الصورة الرمزية

تتقدّم الصورة الرمزية عبر 5 طبقات مظهرية بناءً على عدد أحداث الإتقان التراكمية. الطبقات مظهرية فقط؛ لا تحمل مضاعفًا ولا إشارة مقارنة بالأقران.

الطبقةتُبلَغ عندما يكون العدد التراكمي
1أقل من 5 (طبقة البداية)
25 أو أكثر
315 أو أكثر
435 أو أكثر
575 أو أكثر

العتبات قيم افتراضية هندسية عاملة (مُعلَّمة لتأكيد الشريك). يضع تقدّم الطبقة حدثًا احتفاليًا غير حاجب للواجهة الأمامية؛ الحدث مثلي لكل (student, tier).

سلاسل المكافآت

تُحسَب سلسلة المكافآت مرة واحدة في كل يوم تقويمي يحقق فيه الطالب حدث إتقان واحدًا على الأقل. الأيام التي تجري فيها جلسات دون أي حدث إتقان لا تُحسَب. الحساب مثلي داخل اليوم التقويمي الواحد؛ إعادة التشغيل في اليوم ذاته بعد الحساب لا تُغيّر شيئًا.

نافذة السماح. عطلة نهاية الأسبوع (الجمعة والسبت) والإجازات المدرسية لا تُحتسَب في غياب. لن يقطع النظام سلسلة بسبب يوم يصادف عطلة نهاية أسبوع أو إجازة مُعرَّفة. نافذة السماح لأيام الدراسة العادية (غير عطل الأسبوع وغير الإجازات) التي لا تتضمن حدثًا تبلغ 7 أيام دراسية افتراضيًا.

سلوك القطع. عندما تتجاوز فجوة أيام الدراسة العادية (غير عطل الأسبوع وغير الإجازية) بلا أحداث نافذةَ السماح، تنقطع السلسلة السابقة. يبدأ اليوم الذي تجاوز النافذة سلسلة جديدة بقيمة current = 1 (يوم القطع نفسه يُعدّ اليوم الأول للسلسلة الجديدة لا يومًا صفريًا).

معالم سلسلة المكافآت عند 5 و10 و20 يومًا متتاليًا، وتُرسَل إشعارات إلى الواجهة الأمامية.

الأوسمة

تُبذَر 15 وسامًا من أحداث الإتقان حصرًا. كل وسام في الكتالوج له awardRule يستشهد بنوع واحد على الأقل من MasteryEventSourceKind؛ لا يمكن تعريف وسام دون محفّز تعلُّم قابل للقياس (V-8). يُقيِّم المنسّق كتالوج الأوسمة بعد كل حدث إتقان ويمنح وسامًا جديدًا واحدًا كحدٍّ أقصى في كل تقييم، حفاظًا على إمكانية إدارة واجهة الاحتفال.

بالنسبة للمستدعين البالغين، يتضمن DTO الوسام studentBadgeId (معرّف صف قاعدة البيانات) وstatus (‏active أو rescinded). هذان الحقلان غائبان في استجابة الطالب نفسه.

قوائم المتصدرين

ثلاثة نطاقات لقوائم المتصدرين: الصف، والمعلم (تجميع متعدد الصفوف)، والمدرسة. الثلاثة أسطح مخصصة للبالغين فقط.

تحصل رموز الطالب على 403 في كل مسار لقائمة المتصدرين. لا توجد قائمة متصدرين يراها الطالب، ولا يُخطَّط لإتاحتها (V-8 / Vision §5 ميزة محظورة بشكل صارم).

قائمة صف المتصدرين (GET /api/leaderboards/class/{classId}) تُعيد مدخلات لجميع الطلاب في الصف. يجب أن يكون المستدعي ClassTeacher لذلك الصف، أو مدير المدرسة، أو مسؤولًا. يُعيد صف معلم آخر 404.

قائمة معلم المتصدرين (GET /api/leaderboards/teacher/{teacherUserId}) تُعيد تجميعًا متعدد الصفوف لجميع الصفوف التي يدرّسها المعلم، مع مجاميع لكل صف. يمكن للمعلم طلب teacherUserId الخاص به فقط؛ ويمكن للمدير أو المسؤول طلب أي معلم ضمن نطاقه.

قائمة مدرسة المتصدرين (GET /api/leaderboards/school/{schoolId}) للمدير والمسؤول فقط. يُعيد رمز المعلم 403 بصرف النظر عن انتمائه للمدرسة.

تقرأ جميع قوائم المتصدرين من mastery_event مباشرة. لا تقرأ xp_balance أو أعداد الجلسات أو أي عداد تفاعل. تختار معامل الاستعلام view بين by_mastery_events (الافتراضي) وby_band_progression. يُقيّد معامل windowDays (من 1 إلى 90، الافتراضي 14) العدد لأحداث ضمن النافذة الزمنية.

تحمل الاستجابة framing.topLabelAr وframing.bottomLabelAr: نصوص عربية بلغة نموّ تعرضها الواجهة الأمامية حرفيًّا. هذه النصوص تجتاز تصفية أمان اللغة ولا تستخدم لغة مقارنة أو ترتيب (V-12).

curl "https://localhost:3000/api/leaderboards/class/cls_001?view=by_mastery_events&windowDays=14" \ -H "Authorization: Bearer <accessToken>"
{ "scope": "class", "view": "by_mastery_events", "windowDays": 14, "framing": { "topLabelAr": "...", "bottomLabelAr": "..." }, "entries": [ { "studentId": 42, "masteryEventCount": 18, "bandProgressionCount": 3, "sourceKindBreakdown": { "subskill_state_up": 12, "band_progression": 3, "bundle_step_goal_met": 3 } } ] }

POST /api/teachers/badges/{studentBadgeId}/rescind

يمكن للمعلم سحب وسام إن مُنح خطأً. يحمل جسم الطلب overrideCode فحسب؛ لا يوجد حقل نص حرّ في هذا المسار (B.3).

يجب أن يكون overrideCode هو Gam_Award_Invalid_05، الرمز الوحيد لسحب وسام التحفيز من OverrideCodeCatalog ذي التسعة رموز. يُثبّت المعالج الرمز في النطاق applicableScope = 'gamification_rescind' وقت التشغيل؛ رمز مفقود أو رمز من نطاق مختلف (كرمز خروج حزمة) يُعيد 400. يجب أن يكون المعلم ClassTeacher لطالب الوسام؛ أي معلم آخر يحصل على 403. وسام مسحوب مسبقًا يُعيد 409.

curl -X POST https://localhost:3000/api/teachers/badges/8124/rescind \ -H "Authorization: Bearer <accessToken>" \ -H "Content-Type: application/json" \ -d '{ "overrideCode": "Gam_Award_Invalid_05" }'
{ "studentBadgeId": 8124, "status": "rescinded", "compensatingXpTransactionId": 9201, "reversalDelta": -50 }

يُضيف السحب صف xp_transaction تعويضيًا (الرجوع). لا يُحذف شيء؛ يبقى السجل الكامل بما فيه الرصيد الأصلي والرجوع مرئيًا في مسار التدقيق.

المسؤول: التدقيق وإعادة الاشتقاق

GET /api/admin/gamification/audit

يُعيد تدفقات أحداث الإتقان وعمليات XP والأوسمة الخام لطالب. يستخدمه المسؤولون لفحص السجل الكامل قبل إعادة الاشتقاق أو بعدها. يقبل studentId (مطلوب) وفلتري from وto لتاريخ ووقت.

curl "https://localhost:3000/api/admin/gamification/audit?studentId=42" \ -H "Authorization: Bearer <adminAccessToken>"

POST /api/admin/gamification/replay

يُعيد اشتقاق رصيد XP للطالب ومجموعة أوسمته وطبقة صورته الرمزية من تدفق mastery_event غير المتغيَّر حتى نقطة زمنية محددة، ثم يقارن النتيجة بالقيم المخزَّنة (V-6). أي فارق غير صفري يعني انزياح قيمة مخزَّنة عن السجل؛ يُعلّم الرد كل بُعد بشكل منفصل.

curl -X POST https://localhost:3000/api/admin/gamification/replay \ -H "Authorization: Bearer <adminAccessToken>" \ -H "Content-Type: application/json" \ -d '{ "studentId": 42, "asOf": "2026-06-27T00:00:00.000Z" }'
{ "studentId": 42, "asOf": "2026-06-27T00:00:00.000Z", "derived": { "xpBalance": 340, "badgeCodes": ["BADGE_JOURNEY_START"], "avatarTier": 3 }, "stored": { "xpBalance": 340, "badgeCodes": ["BADGE_JOURNEY_START"], "avatarTier": 3 }, "drift": { "xpBalance": false, "badges": false, "avatarTier": false }, "hasDrift": false }

hasDrift: true يعني انزياح اللقطة المخزَّنة عن التدفق القابل للاشتقاق. هذه إشارة تدقيق V-6؛ حلّها يتطلب تحقيقًا من المسؤول لا تصحيحًا تلقائيًا.

الثوابت

  • V-8: كل مكافأة تعود لحدث مقيس. يحوي قيد MasteryEventSourceKind تمامًا 8 قيم. xp_transaction.masteryEventId هو NOT NULL في كل صف رصيد؛ student_badge.triggeringMasteryEventId هو NOT NULL في كل صف وسام. يرفض CI lint (‏lint:gamification-award-rule / AC-GAM-1) أي مسار كتابة يتجاوز هذه القيود.
  • V-6: رصيد XP دائمًا مشتقّ. xp_balance لا يُخزَّن أبدًا كعمود. إصدارات القواعد مُثبَّتة في كل صف رصيد حتى تُنتج إعادة الاشتقاق على نفس المدخلات نفس النتيجة.
  • إلحاق فقط (B.7). جداول mastery_event وxp_transaction وstudent_badge وstudent_badge_rescind إلحاقية فقط باتفاقية التطبيق و CI lint. لا يوجد مشغّل Postgres من نوع BEFORE في الموجة الأولى؛ التطبيق هو طبقة الإنفاذ.
  • لا نص حرّ (B.3). يقبل جسم السحب حقل overrideCode المُتحكَّم به فقط. لا يوجد حقل reason أو note أو comment في أي مسار كتابة للتحفيز.
  • لا قائمة متصدرين للطالب (SPEC-GAM-07). تحصل رموز الطالب على 403 في المسارات الثلاثة لقوائم المتصدرين. هذه قاعدة منتج صارمة لا تهيئة صلاحية.
  • لا نموذج لغوي (V-5). جميع منطق التحفيز حتمي ومكتوب بقواعد. لا تشارك أي نماذج لغوية في أي خطوة.
  • عزل المستأجر. organizationId محوسب ومفهرس في كل جدول؛ معرّف من مؤسسة أخرى يُعيد 404.
  • إشارة التوقف. إشارة do_not_decide_yet أو partial_domain_profile من المحرك الأعلى تقطع المنسّق كليًّا؛ لا يُكتب حدث إتقان أو رصيد XP أو وسام أو حساب سلسلة.

أدلة ذات صلة