arc42 §6b — تدفقات بيانات الميزات: المنصة الأساسية
الحالة: مسودة v0.1 · التاريخ: 2026-07-20
يغطي: SRC-BRD §7.1 — إدارة المستخدمين · التحقق من الحسابات · الاستشارات · القوالب · التدريب · التقييمات · إصدار الشهادات
الوثيقة المرافقة: 06c-feature-flows-commerce-admin.md · 06-runtime-view.md
فهرس الميزات ← التدفقات#
كل ميزة من ميزات الـ MVP في SRC-BRD §7.1، مربوطة بالتدفق الذي يصفها.
| ميزة BRD §7.1 | Module | التدفق | الوثيقة |
|---|---|---|---|
| تسجيل الفرد / الشركة / المستشار / المزوّد | MOD-IAM |
F-01 | هذه |
| Google Sign-In · التسجيل بالبريد · التسجيل بالهاتف | MOD-IAM |
Flow 1 | 06 |
| إدارة الملف الشخصي | MOD-IAM |
F-02 | هذه |
| حسابات الموظفين | MOD-IAM |
F-03 | هذه |
| رفع المستندات القانونية · مراجعة المسؤول · اعتماد/رفض/إعادة تقديم | MOD-ONB |
Flow 2 | 06 |
| آلة حالات تفعيل الحساب | MOD-ONB |
F-04 | هذه |
| دليل المستشارين · الملفات التعريفية | MOD-CONS |
F-05 | هذه |
| حجز الاستشارة · اعتماد الموعد · الحالة | MOD-CONS |
Flow 3 | 06 |
| الاستشارة المرئية | MOD-VID |
Flow 3 | 06 |
| الاستشارة الميدانية (on-site) | MOD-CONS |
F-06 | هذه |
| القوالب التشغيلية المجانية · التنزيلات · تصفّح التصنيفات | MOD-TPL |
F-07 | هذه |
| طلبات القوالب المخصّصة | MOD-TPL |
F-08 | هذه |
| المحتوى التعليمي · تعلّم الموظفين · إسناد الشركة · التقدّم | MOD-LRN |
F-09 | هذه |
| اختبارات MCQ · التصحيح الآلي · درجة النجاح · السجل | MOD-ASMT |
Flow 4 | 06 |
| توليد الشهادات · التنزيل · السجل | MOD-CERT |
Flow 4 | 06 |
| دليل الموردين / المزوّدين · البحث · الترشيح | MOD-MKT |
Flow 5 | 06 |
| الإدراجات المميّزة (featured) | MOD-MKT |
C-04 | 06c |
| كل تدفقات الدفع | MOD-PAY |
C-01…C-05 | 06c |
| الإدارة · الاعتمادات · التقارير · لوحة المعلومات | MOD-ADM |
C-06…C-08 | 06c |
| الإشعارات | MOD-NOTIF |
C-09 | 06c |
المستوى 0 — خريطة تدفق البيانات على مستوى النظام#
كيف تتحرك البيانات عبر مسارات معالجة (pipelines) المنصة. أربعة pipelines متمايزة بخصائص مختلفة في زمن الاستجابة والمتانة والأمن.
flowchart TB
subgraph SRC["Data Sources"]
U["👤 Users<br/>5 actor types"]
ADM["🛡️ Admins"]
EXT["🔌 External<br/>payment · video · SMS"]
end
subgraph P1["Pipeline 1 — Transactional (synchronous)"]
direction LR
API1["API"] --> VAL["Validate<br/>+ AuthZ<br/>+ Tenant scope"] --> TX[("PostgreSQL<br/>ACID")]
end
subgraph P2["Pipeline 2 — Document (async, untrusted)"]
direction LR
UP["Upload"] --> QUAR["Quarantine<br/>bucket"] --> SCAN["Malware<br/>scan"] --> PROM["Promote"] --> STORE[("Object Storage<br/>encrypted")]
end
subgraph P3["Pipeline 3 — Generation (async, burst)"]
direction LR
TRIG["Trigger"] --> JQ["Job queue"] --> REND["Render<br/>Arabic-capable"] --> ART[("Artifacts<br/>certificates · docs")]
end
subgraph P4["Pipeline 4 — Projection (async, eventually consistent)"]
direction LR
EVT["Domain<br/>events"] --> OUT["Outbox"] --> PROJ["Projectors"]
PROJ --> IDX[("Search index")]
PROJ --> RPT[("Reporting<br/>read model")]
PROJ --> NOT["Notifications"]
end
U --> API1
ADM --> API1
EXT -->|"webhooks<br/>signature-verified<br/>idempotent"| API1
U --> UP
TX -.->|"emits"| EVT
TX -.->|"triggers"| TRIG
STORE -.-> REND
ART --> STORE
style P1 fill:#e8f0fe,stroke:#1168bd
style P2 fill:#fde8e8,stroke:#c94a4a
style P3 fill:#e8f5e9,stroke:#2d8659
style P4 fill:#fff4e5,stroke:#d98c1f
التدفق: أربعة pipelines — معاملاتي متزامن، ومستندي لا متزامن للمدخلات غير الموثوقة، وتوليدي على دفعات، وإسقاطي (projection) متسق في النهاية يغذّي الفهرس والتقارير والإشعارات.
خصائص الـ Pipelines#
| Pipeline | زمن الاستجابة | الاتساق | سلوك الفشل | الوضع الأمني |
|---|---|---|---|---|
| 1 — معاملاتي | p95 < 300 ms | قوي (ACID) | فشل سريع يظهر للمستخدم | نطاق المستأجر وRLS يُفرضان هنا |
| 2 — مستندي | قبول < 1 ثانية، فحص p95 < 60 ثانية | يظهر في النهاية | الرفض عند الشك (fail closed) — لا ترقية لملف لم يُفحص أبدًا | الأعلى — مدخلات غير موثوقة، bucket معزول |
| 3 — توليدي | p95 < دقيقتين | متاح في النهاية | إعادة محاولة؛ السجل المصدر محفوظ بمتانة سلفًا | يُصيّر بيانات مستأجر — يجب إعادة تأسيس النطاق داخل المهمة |
| 4 — إسقاطي | ثوانٍ | متسق في النهاية | إعادة محاولة؛ ليس مرجعًا موثوقًا أبدًا | الفهرس للترتيب فقط؛ قاعدة البيانات هي الحقيقة |
القاعدة الحاكمة: يجوز أن تفشل الـ pipelines 2–4 دون فقدان عمل المستخدم، لأن الـ pipeline 1 قد أودع بالفعل وبشكل دائم السجلَّ الذي يهم. نتيجة التقييم تنجو من فشل توليد الـ PDF؛ والحجز ينجو من فشل الإشعار؛ والإدراج ينجو من فشل الفهرسة.
F-01 — التسجيل بحسب نوع الحساب#
SRC-BRD §7.1، §10 · SRC-MOM REQ-01 · SRC-UFC
أكثر تدفقات النظام تفرّعًا على الإطلاق. أربعة أنواع تُسجّل نفسها ذاتيًا ببيانات مطلوبة مختلفة ومسارات تفعيل مختلفة.
flowchart TB
START["User opens app"] --> CHOOSE{"Select<br/>account type"}
CHOOSE -->|Individual| I1["Collect: name, email/phone<br/>Optional: profession"]
CHOOSE -->|Company| C1["Collect: company name, CR number,<br/>contact person, city, business type"]
CHOOSE -->|Consultant| K1["Collect: name, specialties,<br/>credentials, bio"]
CHOOSE -->|Service Provider| S1["Collect: company name, CR,<br/>service categories, coverage area"]
I1 --> VERIFY["Verify identity channel<br/>OTP or Google ID token"]
C1 --> VERIFY
K1 --> VERIFY
S1 --> VERIFY
VERIFY --> CREATE["Create user + type-specific profile<br/><i>one transaction</i>"]
CREATE --> GATE{"Approval<br/>required?<br/><i>CON-04</i>"}
GATE -->|"Individual — No"| ACTIVE["✅ state: active<br/>Full access to<br/>individual capabilities"]
GATE -->|"Company / Consultant /<br/>Provider — Yes"| PENDING["⏳ state: pending_documents"]
PENDING --> DOCS["Prompt: upload legal documents<br/><i>→ Flow 2 in 06</i>"]
DOCS --> REVIEW["state: under_review"]
REVIEW --> DECIDE{"Admin<br/>decision"}
DECIDE -->|Approved| ACTIVE2["✅ state: active"]
DECIDE -->|Rejected| REJ["❌ state: rejected + reason"]
REJ --> RESUB["Resubmission allowed<br/><i>new document version</i>"]
RESUB --> REVIEW
ACTIVE --> CAP["Capabilities resolved from<br/>actor type + account state"]
ACTIVE2 --> CAP
style PENDING fill:#fff4e5,stroke:#d98c1f
style REJ fill:#fde8e8,stroke:#c94a4a
style ACTIVE fill:#e8f5e9,stroke:#2d8659
style ACTIVE2 fill:#e8f5e9,stroke:#2d8659
التدفق: الفرد يُفعَّل مباشرة؛ أما الشركة والمستشار والمزوّد فتمر بمسار مستندات ومراجعة إدارية، مع السماح بإعادة التقديم بعد الرفض.
البيانات المكتوبة#
| المخزن | البيانات |
|---|---|
users |
الهوية، بيانات الاعتماد، مميّز actor_type، اللغة/المنطقة |
{type}_profiles |
سمات خاصة بكل نوع — مملوكة للـ module المعني، لا مُلصَقة بـ users |
account_state_transitions |
سجل تدقيق غير قابل للتغيير — الفاعل، من، إلى، السبب، الطابع الزمني |
ملاحظة تصميمية (Design note). ستة أنواع فاعلين ليست ستة أدوار على جدول واحد. صف واحد في
usersيحمل الهوية وبيانات الاعتماد؛ وكل نوع فاعل يحصل على كيان ملف تعريفي مملوك لـ module الخاص به (Consultancy\ConsultantProfile،Directory\ProviderProfile). هذا يمنعMOD-IAMمن أن يصير مكبًّا تمتد إليه كل الـ modules — وهو إخفاق Laravel الكلاسيكي حيث يراكمUserعلاقات من كل مكان فيصبح غير قابل للاستخراج (R-08).
أسئلة مفتوحة (Open questions)#
OQ-02— مجموعة المستندات المطلوبة بدقة لكل نوع. غير معروف حاليًا هل تحتاج الشركة السجل التجاري فقط، أم أيضًا الرخصة البلدية وشهادة سلامة الغذاء. يعيق نموذج بياناتMOD-ONB.OQ-12— هل تُلتقط بيانات التسجيل بلغتين؟
F-02 — إدارة الملف الشخصي#
يبدو بسيطًا خادعًا؛ لكن التخويل هو المهم.
flowchart LR
subgraph EDIT["Edit request"]
REQ["PATCH /v1/profile"] --> AUTHZ{"Who is<br/>editing?"}
end
AUTHZ -->|"Self — Individual/Consultant/Provider"| FULL["Full profile fields<br/>editable"]
AUTHZ -->|"Self — Employee"| LIMITED["⚠ LIMITED fields only<br/><i>BRD §11: 'Limited'</i><br/>name, phone, password, locale"]
AUTHZ -->|"Company admin editing employee"| CO["Employment fields only<br/>NOT credentials"]
AUTHZ -->|"Platform admin"| ADMIN["All fields<br/>📝 audit-logged"]
FULL --> SENSITIVE{"Sensitive<br/>field?"}
LIMITED --> SENSITIVE
CO --> APPLY
ADMIN --> APPLY
SENSITIVE -->|"email / phone"| REVERIFY["Re-verify channel<br/>before applying"]
SENSITIVE -->|"password"| REVOKE["Apply + revoke<br/>ALL token families"]
SENSITIVE -->|"No"| APPLY["Apply change"]
REVERIFY --> APPLY
REVOKE --> APPLY
APPLY --> REINDEX["Emit ProfileUpdated<br/>→ reindex if listing-visible"]
style LIMITED fill:#fff4e5,stroke:#d98c1f
style REVOKE fill:#fde8e8,stroke:#c94a4a
الضوابط:
- تغيير قناة اتصال مُتحقَّق منها يجب أن يُعاد التحقق منه قبل أن تصير قناة الاسترداد — وإلا صار تعديل الملف الشخصي مسارًا للاستيلاء على الحساب.
- تغيير كلمة المرور يُبطل كل الجلسات (
QAS-SEC-04). - نطاق ملف الموظف ضيّق عن قصد بحسب
SRC-BRD§11 — يجب ألا يستطيع الموظف تعديل سمات متعلقة بالتوظيف يعتمد عليها صاحب العمل. - ⚠️ تعديلات ملف المستشار/المزوّد التي تمسّ الظهور في الدليل أو التسعير ينبغي أن تعود إلى المراجعة إذا غيّرت جوهريًا ما جرى اعتماده. غير محدَّد في المصادر ← يُرفع إلى الجهة الراعية.
F-03 — تزويد حسابات الموظفين#
SRC-BRD §10.2، §10.3 · CON-09
الموظفون لا يستطيعون التسجيل ذاتيًا — فهم لا يوجدون إلا تحت شركة معتمدة.
sequenceDiagram
autonumber
participant CO as 🏢 Company Admin
participant API as Qwizin API
participant DB as PostgreSQL
participant N as Notifications
participant EMP as 👷 Employee
CO->>API: POST /v1/company/employees {name, email/phone, job title}
API->>API: AuthZ: is company_admin AND company.state == active
Note over API: ⚠ CON-09: employee cannot exist<br/>under a non-approved company
API->>DB: BEGIN TX
API->>DB: Create user(actor_type: employee, state: invited)
API->>DB: Link employment(company_id, employee_id, job_title)
API->>DB: 📝 Audit: admin X created employee Y
API->>DB: COMMIT
API->>N: Dispatch invitation
N->>EMP: 🔔 Invitation + one-time activation link
EMP->>API: Activate {token, set password}
API->>API: Validate token (single-use, expiring)
API->>DB: user.state = active
API-->>EMP: {access_token, refresh_token}
rect rgb(255,240,240)
Note over CO,EMP: ⚠ Cascade — OQ-07 UNRESOLVED
CO->>API: Deactivate employee
API->>DB: employment.state = terminated
API->>DB: Revoke all token families
Note over DB: Open: what happens to<br/>in-flight assessments?<br/>Already-issued certificates?<br/>→ OQ-07 needs a sponsor ruling
end
مشكلة التتالي#
إيقاف الشركة أو رفضها يجب أن ينتشر إلى كل موظف. OQ-07 لا يزال دون حسم، لكن الآلية محسومة: تحديد سياق المستأجر يقع في مكان واحد، فتكون نقطة الانتشار واحدة لا متناثرة عبر الـ modules.
ثلاثة أسئلة فرعية تحتاج قرارًا:
| السؤال | الخيارات |
|---|---|
| محاولات التقييم الجارية | إبطال / تجميد / السماح بالإكمال |
| الشهادات الصادرة سلفًا | تبقى صالحة / تُوسَم كموقوفة / تُبطل |
| وصول الموظف الشخصي | فقدان كل وصول / الاحتفاظ بالشهادات الشخصية فقط |
نقطة نهاية التحقق من الشهادة تُعيد الحالة الحالية محسوبةً لحظيًا (
QAS-INT-03)، فلا بد أن تملك جوابًا. هذا سؤال سياسة أعمال ذو أثر تقني ظاهر مباشرةً.
F-04 — آلة حالات تفعيل الحساب#
CON-04 · SRC-UFC · QAS-CMP-02
stateDiagram-v2
[*] --> pending_documents: Registration complete<br/>(approval-gated types)
[*] --> active: Registration complete<br/>(Individual)
pending_documents --> under_review: All required documents<br/>uploaded + scanned clean
under_review --> active: ✅ Admin approves ALL<br/>required documents
under_review --> rejected: ❌ Admin rejects<br/>(reason required)
rejected --> pending_documents: User resubmits<br/>(new document version)
active --> suspended: Admin action /<br/>subscription lapse
suspended --> active: Reinstated
active --> closed: User or admin closes
suspended --> closed: Escalation
closed --> [*]
note right of under_review
Activation requires ALL required
documents approved — not the first.
Partial approval keeps the account
in under_review.
end note
note right of rejected
Resubmission creates document
version n+1. NEVER overwrites n —
the evidence behind the original
decision must survive (QAS-CMP-02).
end note
الأثر على التخويل#
«الحساب معتمد ونشط» هو شرط مسبق على الجلسة بكاملها، لا صلاحية. ونمذجته كصلاحية تعني أن كل نقطة نهاية جديدة تكون غير محمية افتراضيًا — وهو عكس المطلوب تمامًا في منصة قائمة على بوابة اعتماد.
يُنفَّذ بقائمة سماح صريحة: كل شيء مرفوض أثناء الانتظار، باستثناء قائمة قصيرة قابلة للمراجعة (عرض الملف الشخصي، رفع المستندات، إعادة التقديم، تصفّح الـ marketplace العام، تسجيل الخروج). وأي قدرة تُضاف العام القادم تكون مرفوضة افتراضيًا حتى يسمح بها أحدهم عن قصد.
⚠️ مزلق موثَّق في إطار العمل. التنفيذ البديهي — خطّاف
beforeعام يُعيدfalseللحسابات غير المعتمدة — يُعطّل كل policy في التطبيق، لأن أي قيمة عائدة غير null تختصر التخويل كله. كما أنه يُعطّل قدرة المسؤول على مراجعة الحسابات المعلّقة. يجب أن يُعيد الخطّافnullفي المسار السعيد، وأن تكون حالة الحساب في طبقة مخصّصة لتحديد القدرات لا في الخطّاف العام. واحجز الخطّاف العام لرفع صلاحية super-admin فقط.
F-05 — دليل المستشارين والملف التعريفي#
flowchart TB
subgraph IDX["Indexing — async, Pipeline 4"]
CE["Consultant approved /<br/>profile updated /<br/>availability changed"] --> DEB["Debounce"]
DEB --> IX[("Search index<br/>name_ar · name_en · specialties ·<br/>city · price_band · rating · languages")]
end
subgraph BROWSE["Browse — Pipeline 1"]
Q["GET /v1/consultants<br/>?specialty=&city=&type=&q="] --> NORM["Normalise Arabic query"]
NORM --> SR["Query index"]
SR --> HYD["Hydrate from DB by ID"]
HYD --> FILT["⚠ Post-filter:<br/>state == active<br/>AND approved"]
FILT --> RES["Ranked results"]
end
subgraph PROFILE["Consultant detail"]
RES --> DET["GET /v1/consultants/{id}"]
DET --> PUB["Public: bio, specialties,<br/>credentials, pricing,<br/>consultation types offered"]
DET --> AVAIL["Availability calendar<br/><i>⚠ OQ-03</i>"]
end
style FILT fill:#fde8e8,stroke:#c94a4a
style AVAIL fill:#fff4e5,stroke:#d98c1f
OQ-03 — نموذج الإتاحة دون حسم. ثلاثة تصاميم مرشّحة تتباين تعقيدًا تباينًا ملموسًا:
| النموذج | جهد المستشار | تعقيد النظام |
|---|---|---|
| إتاحة أسبوعية متكرّرة + استثناءات | يُضبط مرة واحدة | متوسط — توليد المواعيد، معالجة المناطق الزمنية |
| نشر مواعيد صريح | مستمر | منخفض |
| الطلب ثم التفاوض (بلا تقويم منشور) | تفاعلي | الأدنى، لكنه أسوأ تجربة استخدام ويتعارض مع تصميم انتهاء صلاحية الحجز في Flow 3 |
التوصية: الإتاحة المتكرّرة مع الاستثناءات. فهي تطابق طريقة عمل المهنيين فعليًا، كما أن قفل المواعيد في تدفق الحجز يفترض سلفًا وجود مواعيد منفصلة.
F-06 — طلب استشارة ميدانية (On-Site)#
SRC-BRD §7.1 · SRC-MOM FR-2
مختلف جوهريًا عن الاستشارة المرئية وغير قابل للتبادل معها — فهو ينطوي على سفر، وعنوان فعلي، وغياب أي إشارة إتمام يمكن للمنصة ملاحظتها.
sequenceDiagram
autonumber
participant C as 👤 Customer
participant API as Qwizin API
participant CON as 🎓 Consultant
participant PAY as 💳 Payment
participant N as Notifications
C->>API: POST /v1/bookings {type: on_site, consultant, preferred dates[], site address, scope notes}
API->>API: Validate: consultant offers on_site<br/>+ covers this city
Note over API: ⚠ Coverage area is a<br/>consultant profile attribute —<br/>on-site is geographically bounded
API->>DB: booking(type: on_site, state: pending_quote)
rect rgb(255,245,235)
Note over CON,PAY: Quotation — on-site pricing is not a fixed rate
API->>CON: 🔔 On-site request + scope
CON->>API: Provide quote {fee, travel cost, duration, date}
API->>C: 🔔 Quote received
alt Customer accepts
C->>API: Accept quote
API->>PAY: AUTHORIZE hold
API->>DB: booking(state: confirmed)
else Customer declines / no response
API->>DB: booking(state: expired)
end
end
rect rgb(240,255,240)
Note over C,N: Visit & completion — NO automatic signal
N->>C: 🔔 Reminder T-24h
N->>CON: 🔔 Reminder T-24h
Note over C,CON: Visit happens off-platform
CON->>API: Mark visit completed + upload report
C->>API: Confirm completion
API->>PAY: CAPTURE
Note over API,PAY: ⚠ Capture requires confirmation.<br/>Video sessions have provider webhooks;<br/>on-site has NOTHING — a human must<br/>assert it happened.
end
rect rgb(255,240,240)
alt Dispute — customer says visit did not occur
API->>API: Flag for admin resolution
Note over API: ⚠ Policy undefined in all sources<br/>→ OQ-05
end
end
التدفق: الطلب يمر بمرحلة تسعير (quote) قبل الحجز على المبلغ، ثم تقع الزيارة خارج المنصة، ولا يقع التحصيل إلا بإقرار بشري من الطرفين.
لماذا يحتاج هذا التدفق تصميمًا منفصلًا#
| الجانب | الاستشارة المرئية | الاستشارة الميدانية |
|---|---|---|
| التسعير | سعر مستشار ثابت | تسعير لكل طلب — سفر، نطاق، مدة |
| الجغرافيا | غير ذات صلة | منطقة التغطية تقيّد المطابقة |
| إشارة الإتمام | webhook من المزوّد (SessionEnded) |
لا شيء — إقرار بشري فقط |
| مُطلِق التحصيل | انتهاء الجلسة | تأكيد الطرفين |
| دليل النزاع | مدة الجلسة، أحداث الجودة | تقرير المستشار المرفوع فقط |
ذو دلالة معمارية: الاستشارات الميدانية بلا حقيقة يمكن للآلة ملاحظتها. كل انتقال حالة بعد التأكيد يعتمد على إقرار بشري، ومن ثمّ يحمل سجل التدقيق ومسار النزاع عبء النزاهة كاملًا. يعامل
SRC-BRDنوعَي الاستشارة كميزة واحدة؛ وهما ليسا كذلك.
F-07 — كتالوج القوالب والتنزيل المجاني#
SRC-BRD §7.1 · SRC-MOM REQ-04 · BR-RL-2 (القوالب مجانية بالكامل لجميع أنواع الحسابات)
flowchart TB
subgraph PUB["Publishing — admin, Pipeline 1+2"]
A["🛡️ Admin uploads template"] --> META["Capture structured metadata:<br/>title_ar/en · category · subcategory ·<br/>description · file format · version"]
META --> SCAN2["Malware scan"]
SCAN2 --> STORE[("Object Storage<br/>templates/")]
META --> DBT[("templates table")]
DBT --> IXT["→ Search index"]
end
subgraph BR["Browse — Pipeline 1"]
U["Any authenticated user<br/><i>all types — BR-RL-2</i>"] --> CAT["GET /v1/templates?category="]
CAT --> LIST["Category tree + listings"]
LIST --> DET2["GET /v1/templates/{id}"]
end
subgraph DL["Download"]
DET2 --> DLREQ["POST /v1/templates/{id}/download"]
DLREQ --> AUTHZ2["AuthZ: authenticated<br/><i>no payment gate</i>"]
AUTHZ2 --> LOG["📊 Record download event<br/><i>KPI: templates downloaded</i>"]
LOG --> SIGN["Issue signed URL, short TTL"]
SIGN --> FETCH["Client fetches DIRECTLY<br/>from object storage"]
end
style FETCH fill:#e8f5e9,stroke:#2d8659
نقطتا تصميم أهم مما تبدوان:
- الملف لا يمرّ عبر طبقة الـ API إطلاقًا. نقل البيانات الضخمة عبر عمّال PHP-FPM يستهلك عاملًا طوال مدة كل تنزيل — وهو أسرع طريق لاستنزاف طبقة الطلبات. الحل: signed URL، وجلب مباشر، وCDN في المقدمة.
- أحداث التنزيل مؤشر أداء من الدرجة الأولى (
SRC-MOM§13: "number of free templates downloaded and customised"). التقاطها لحظة التنزيل أمر تافه؛ أما إعادة بنائها لاحقًا من سجلات الوصول فليس كذلك.
SEAM-03 — نمذجة القوالب بشكل مُهيكل#
تتضمن ملفات العيّنة الستة في مجموعة المصادر (SRC-CNT) ثلاثة مصنّفات تدقيق هي قوائم فحص بصف لكل ضابط مع أعمدة لحالة الامتثال وللشخص المسؤول. وهذه البنية إشارة قوية إلى أن «ملف قابل للتنزيل» شكل انتقالي.
وعليه: تُنمذَج القوالب كيانات مُهيكلة بأقسام وعناصر مُصنّفة منذ اليوم الأول، وإن كان الـ MVP لا يقدّم سوى ملف. تكلفة الـ MVP هي مخطط بيانات أغنى خلف API لم يتغيّر؛ أما تكلفة التعديل اللاحق فهي إعادة نمذجة الـ module وترحيل كل سجل قائم (QAS-MOD-04).
erDiagram
TEMPLATE ||--o{ TEMPLATE_SECTION : contains
TEMPLATE_SECTION ||--o{ TEMPLATE_ITEM : contains
TEMPLATE ||--o{ TEMPLATE_FILE : "has renditions"
TEMPLATE {
id id
string title_ar
string title_en
string category
int version
enum kind "document | checklist | form"
}
TEMPLATE_SECTION {
id id
string title_ar
string title_en
int order
}
TEMPLATE_ITEM {
id id
text prompt_ar
text prompt_en
enum response_type "yes_no | na | text | numeric"
bool required
}
TEMPLATE_FILE {
id id
string format "xlsx | pdf | docx"
string storage_ref
}
المرحلة الثانية تضيف جدول
template_submissionsفيوجد منتج التدقيق. ولا يتغيّر شيء في الـ MVP.
F-08 — طلب قالب مخصّص 💰#
SRC-BRD §7.1، §9.3.2 · SRC-MOM FR-3 / REQ-04 · CON-11 (أقل عدد ممكن من خطوات الواجهة — هاجس معدل الارتداد)
تدفق مُدرّ للإيرادات يتضمن خطوة تنفيذ بشرية.
sequenceDiagram
autonumber
participant U as 👤 Customer
participant API as Qwizin API
participant PAY as 💳 Payment
participant ADM as 🛡️ Admin
participant SPC as ✍️ Specialist
participant OBJ as Object Storage
participant N as Notifications
rect rgb(240,240,255)
Note over U,API: Intake — CON-11: MINIMAL steps
U->>API: POST /v1/template-requests<br/>{base_template_id?, business_type, business_size,<br/>requirements_text, attachments?}
Note over U,API: ⚠ Deliberately short form.<br/>Every extra field costs conversion<br/>on a paid action.
API->>DB: request(state: submitted)
API->>API: Determine price<br/><i>fixed / tiered / quoted</i>
end
rect rgb(255,245,235)
Note over U,PAY: Payment
alt Fixed or tiered price
API-->>U: {price}
U->>API: Pay {idempotency_key}
API->>PAY: AUTHORIZE + CAPTURE
Note over API,PAY: Capture immediately — unlike bookings,<br/>there is no counterparty acceptance step
API->>DB: request(state: paid)
else Quote required
API->>ADM: 🔔 Quote needed
ADM->>API: Provide quote
API->>U: 🔔 Quote
U->>API: Accept + pay
API->>DB: request(state: paid)
end
end
rect rgb(240,255,240)
Note over ADM,OBJ: Fulfilment — HUMAN work, off-platform
API->>ADM: 🔔 New paid request in queue
ADM->>API: Assign to specialist
API->>DB: request(state: in_progress, assignee, due_date)
N->>U: 🔔 In progress, expected by {date}
SPC->>API: Upload deliverable
API->>API: Malware scan (Pipeline 2)
API->>OBJ: Store → deliverables/
API->>DB: request(state: delivered)
N->>U: 🔔 Ready for download
U->>API: Download via signed URL
API->>DB: 📊 Record delivery acceptance
end
rect rgb(255,240,240)
alt Revision requested
U->>API: Request revision {notes}
API->>DB: request(state: revision_requested)
Note over API: ⚠ How many revisions are included?<br/>Undefined in all sources → OQ-14
end
end
التدفق: استمارة إدخال قصيرة عن قصد، ثم دفع يُحصَّل فورًا (بخلاف الحجوزات)، ثم تنفيذ بشري خارج المنصة ينتهي بتسليم عبر signed URL.
الواقع التشغيلي الذي يكشفه هذا التدفق#
هذه ليست ميزة مؤتمتة. إنها سير عمل لتقديم خدمة يتوسّطه إنسان، وله خصائص لا تتناولها الوثائق المصدر:
| الفجوة | لماذا تهم | OQ جديد |
|---|---|---|
| SLA زمن الإنجاز | العميل دفع وينتظر. ولا التزام تسليم مُعلَن. | OQ-15 |
| سياسة التعديلات | تعديلات غير محدودة مقابل رسم ثابت التزام مفتوح بلا سقف | OQ-14 |
| طاقة التنفيذ | الطلب غير محدود؛ وطاقة المتخصصين محدودة. يلزم مؤشر على عمق الطابور وربما تقييد الإدخال. | OQ-16 |
| الاسترداد عند عدم التسليم | المال يُحصَّل مقدّمًا بلا ضمان تسليم مؤتمت | OQ-05 |
النتيجة المعمارية: لأن التنفيذ يدوي، فإن مهمة النظام هي إدارة الطوابير وتتبّع الـ SLA والاحتفاظ بالأدلة — لا توليد المستندات. وتصميمه كميزة توليد سيكون خطأً في الفئة.
F-09 — إسناد التدريب وتتبّع تقدّم التعلّم#
SRC-BRD §7.1، §10.2 · SRC-TRN · ⚠️ خاضع لـ CONF-01
⚠️ تعارض في النطاق — يُقرأ قبل التنفيذ. يستبعد
SRC-BRD§7.2 وSRC-MOM§14 التعلّم الإلكتروني بالفيديو، ومسارات التعلّم، وتحليلات التعلّم. بينما يحدّدSRC-TRNالفيديو كأسلوب تعلّم، ويضيف التقييمات العملية، ومقارنة الفروع، وتتبّع الدورات المتأخرة. والتدفق أدناه ينفّذ نطاق الـ BRD الأضيق. فإن كانSRC-TRNداخل النطاق، فسيتغيّر هذا التدفق تغيّرًا جوهريًا — إذ تصبح تخزين الفيديو، وتحويل الترميز (transcoding)، والـ CDN، ومسار تقييم يُصحَّح بشريًا، وهرمية شركة ← فرع، كلها مطلوبة.
flowchart TB
subgraph AUTHOR["Content authoring — Admin"]
A1["🛡️ Admin creates learning content"] --> A2["Structured: module → lessons<br/>title_ar/en · body_ar/en · order ·<br/>estimated_duration · category"]
A2 --> A3["Link assessment<br/><i>MOD-ASMT</i>"]
A3 --> A4["Publish → available to assign"]
end
subgraph ASSIGN["Assignment — Company Admin"]
B1["🏢 Company admin selects content"] --> B2["Select employees<br/><i>individual / all / by job title</i>"]
B2 --> B3["Set due date (optional)"]
B3 --> B4["Create assignment records<br/><i>one per employee</i>"]
B4 --> B5["🔔 Notify employees"]
end
subgraph LEARN["Learning — Employee"]
C1["👷 Employee opens assigned module"] --> C2["Read lesson content"]
C2 --> C3["Mark lesson complete"]
C3 --> C4{"All lessons<br/>complete?"}
C4 -->|No| C2
C4 -->|Yes| C5["Unlock assessment"]
C5 --> C6["→ Flow 4: MCQ assessment"]
end
subgraph TRACK["Progress — Company Admin"]
D1["Progress events"] --> D2[("Progress read model<br/>Pipeline 4")]
D2 --> D3["Company dashboard:<br/>assigned · started · completed ·<br/>passed · certificate issued"]
end
A4 --> B1
B5 --> C1
C3 -.->|"emits LessonCompleted"| D1
C6 -.->|"emits AssessmentPassed"| D1
style C6 fill:#e8f0fe,stroke:#1168bd
نموذج البيانات#
erDiagram
LEARNING_MODULE ||--o{ LESSON : contains
LEARNING_MODULE ||--o| ASSESSMENT : "gated by"
LEARNING_MODULE ||--o{ ASSIGNMENT : "assigned via"
ASSIGNMENT }o--|| EMPLOYEE : "to"
ASSIGNMENT ||--o{ LESSON_PROGRESS : tracks
ASSIGNMENT {
id id
id company_id "tenant scope"
id employee_id
id module_id
date due_date
enum state "assigned|in_progress|completed|overdue"
timestamp assigned_at
}
LESSON_PROGRESS {
id assignment_id
id lesson_id
timestamp completed_at
}
ملاحظة على نطاق المستأجر: يحمل ASSIGNMENT عمود company_id ويخضع لـ RLS. أما محتوى التعلّم فهو عام على مستوى المنصة وغير مقيّد بنطاق المستأجر — والخطأ الشائع في النمذجة هو تقييد المحتوى بالشركات، وهو ما يمنع مشاركة المواد التي تؤلّفها المنصة.
تتبّع التقدّم إسقاط (projection) لا استعلام#
لوحات معلومات الشركات تقرأ من projection مبني في Pipeline 4، لا بتجميع الجداول المعاملاتية لحظيًا. فمؤشر SRC-MOM KPI 3 ("% of employees passing assessments and receiving certificates") هو شأن تقاريري، وحسابه من assignments ⋈ attempts ⋈ certificates عند كل تحميل للوحة سيتدهور مع نمو أعداد الموظفين.
SEAM-04 وSEAM-06#
SEAM-04—MOD-LRNوMOD-ASMTوحدتان (modules) منفصلتان بينهما عقد، لأن نظام LMS الكامل هو أعلى القدرات المؤجلة صوتًا، وهو ينمو بسرعة إن نما أصلًا.SEAM-06— تُنمذَج أهداف الإسناد عبر مستوى من الوساطة (موظف، أو مجموعة موظفين) بدل تثبيت «الموظف فقط» في الكود. الأقسام مؤجلة (SRC-BRD§10.2) والفروع مُلمَّح إليها فيSRC-TRN؛ وإقحام الهرمية لاحقًا في نموذج إسناد مسطّح مكلف.
أسئلة مفتوحة جديدة أثارها هذا التحليل#
| ID | السؤال | التدفق | يُعيق؟ |
|---|---|---|---|
OQ-14 |
كم عدد التعديلات المشمولة في طلب القالب المخصّص؟ | F-08 | نعم — التزام مفتوح بلا سقف |
OQ-15 |
ما هو SLA التسليم للطلبات المخصّصة؟ | F-08 | نعم — العميل قد دفع |
OQ-16 |
هل هناك حد لطاقة التنفيذ / تقييد للإدخال؟ | F-08 | متوسط |
OQ-17 |
هل تعود تعديلات ملف المستشار المؤثرة على التسعير أو الظهور إلى المراجعة؟ | F-02 | متوسط |
OQ-18 |
الاستشارات الميدانية: هل التسعير لكل طلب أم سعر مستشار ثابت؟ | F-06 | نعم — يعيق نموذج التسعير |
OQ-19 |
من يتحمّل التكلفة عند وجود نزاع حول زيارة ميدانية؟ | F-06 | نعم — لا حقيقة يمكن للآلة ملاحظتها |
تابع إلى: 06c-feature-flows-commerce-admin.md — المدفوعات، الاشتراكات، الإدراجات المميّزة، بوابة الإدارة، الإشعارات، التقارير.