معمل الجُماعي كاد كام

توثيق الربط مع أنظمة العيادات

توثيق واجهة الربط: أرسل الحالات من نظام عيادتك إلى معمل الجُماعي كاد كام، وتابع مراحل التنفيذ والأسعار والرصيد تلقائياً.

https://cadcam.lovable.app/api/public/partner نسخة نصية للمساعد الذكي OpenAPI

٠. الربط بضغطة زر (بدون لصق مفتاح)

الطريقة المفضّلة: صاحب العيادة يضغط «اربط مع المعمل» داخل نظامه، فيُفتح له في المعمل صفحة موافقة، يسجّل دخوله بحسابه في المعمل ويوافق — ثم يعود إلى نظامه مرتبطاً. المفتاح لا يمر عبر متصفحه أبداً.

  • يسجّل المعمل نظامك مرة واحدة ويعطيك: app_id وapp_secret ونطاق العودة المسموح (نطاق موقع نظامك).
  • لكل مستخدم: وجّهه إلى صفحة الموافقة بـ app_id وredirect_uri وstate من عندك.
  • بعد الموافقة يعود المتصفح إلى redirect_uri?code=…&state=… — الرمز صالح ٥ دقائق ولمرة واحدة.
  • خادمك يستبدل الرمز بالمفتاح عبر POST /connect/exchange ويحفظ المفتاح مشفَّراً لهذا المستخدم.
  • الرفض يعود بـ redirect_uri?error=access_denied.
  • حساب غير معتمد في المعمل يرى «حسابك بانتظار اعتماد المعمل» ولا يُنشأ له ربط.
١) صفحة الموافقة (وجّه متصفح صاحب العيادة إليها)
https://cadcam.algomaei.com/connect?app_id=<APP_ID>&redirect_uri=https%3A%2F%2Fclinic-hub.example.com%2Flab%2Freturn&state=<RANDOM>
٢) استبدال الرمز بالمفتاح (من خادمك فقط)
curl -X POST https://cadcam.lovable.app/api/public/partner/connect/exchange \
  -H "x-app-id: <APP_ID>" -H "x-app-secret: <APP_SECRET>" \
  -H "Content-Type: application/json" \
  -d '{ "code": "<CODE_FROM_REDIRECT>" }'

# { "api_key": "lab_live_…", "account": { "id": "…", "name": "عيادة النور", "type": "clinic" } }
٣) فصل الربط (بالمفتاح نفسه)
curl -X POST https://cadcam.lovable.app/api/public/partner/connect/revoke -H "Authorization: Bearer lab_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
تحقّق من مطابقة state عند العودة، ولا ترسل app_secret ولا المفتاح إلى المتصفح. إن أردت الطريقة اليدوية القديمة (مفتاح يعطيه لك المعمل مباشرة) فهي تعمل كما هي وبقية التوثيق ينطبق عليها.

١. نظرة عامة

واجهة الربط تسمح لأي نظام إدارة عيادات (مثل clinic-hub) بالعمل مع معمل الجُماعي كاد كام مباشرة: قراءة كتالوج الأعمال بأسعار حساب العيادة نفسها، إرسال الحالة من ملف المريض بضغطة واحدة، إرفاق ملفات السكان والتصميم، متابعة مراحل التنفيذ، وقراءة كشف الحساب.

  • الحالة المُرسَلة تُنشأ داخل المعمل كأي حالة عادية: رقم رسمي، تسعير من كتالوج المعمل، مراحل إنتاج، وإشعار للاستقبال.
  • كل مفتاح ربط مرتبط بحساب عيادة واحد، ولا يرى أو يكتب أي بيانات لحساب آخر.
  • الأسعار والخصومات وحالات الإنتاج لا يمكن تغييرها من الخارج — التسعير من المعمل دائماً.
  • كل نداء يُسجَّل في المعمل (الوقت، النداء، النتيجة) ويظهر للمدير.
المفتاح يصدره مدير المعمل من: لوحة النظام ← «الربط مع الأنظمة» ← «مفتاح ربط جديد». يظهر مرة واحدة فقط، فاحفظه في متغيرات البيئة لنظام العيادة (مثل LAB_API_KEY).

٢. التوثيق والرابط الأساسي

الرابط الأساسي لكل النقاط:

text
https://cadcam.lovable.app/api/public/partner

كل طلب يحمل المفتاح في ترويسة Authorization (أو x-lab-key إن كان العميل لا يسمح بتعديل Authorization):

http
Authorization: Bearer lab_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
  • شكل المفتاح: lab_live_ + ٤٨ حرفاً ستّ عشرياً.
  • المفتاح سرّي — يُستخدم من خادم نظام العيادة فقط، لا من المتصفح.
  • بدون مفتاح صالح أو مع مفتاح معطّل: الرد 401 مع { "error": "unauthorized" }.
  • كل النقاط تدعم OPTIONS (CORS) وترد بترويسات Access-Control-Allow-*.
  • كل الردود بصيغة JSON بترميز UTF-8 وبلا تخزين مؤقت.

٣. البداية السريعة (٣ خطوات)

  • اختبر المفتاح بـ GET /ping — يعيد اسم الحساب ونوعه.
  • اجلب GET /catalog واحفظ work_types[].id لعرضها في نموذج الإرسال داخل العيادة.
  • أرسل الحالة بـ POST /cases مع external_ref من رقم الزيارة عندك، ثم تابعها بـ GET /cases/{code}.
اختبار الاتصال
curl -H "Authorization: Bearer $LAB_API_KEY" https://cadcam.lovable.app/api/public/partner/ping
الردّ
{
  "ok": true,
  "lab": "معمل الجُماعي كاد كام",
  "account": { "id": "7f0c…", "name": "عيادة النور", "type": "clinic" }
}

٤. كتالوج الأعمال والأسعار — GET /catalog

يعيد مجموعات الأعمال وأنواعها وحقولها الإضافية، مع سعر حساب هذه العيادة تحديداً: حساب نوعه lab يأخذ سعر المعمل، وغيره يأخذ سعر الطبيب.

bash
curl -H "Authorization: Bearer $LAB_API_KEY" https://cadcam.lovable.app/api/public/partner/catalog
الردّ (مختصر)
{
  "groups": [{ "id": "…", "name": "الزيركون", "description": null, "sort_order": 1 }],
  "work_types": [
    {
      "id": "8c1f…",
      "group_id": "…",
      "name": "تاج زيركون",
      "case_type": "digital",
      "unit": "قطعة",
      "price": 9000,
      "currency": "YER",
      "fields": [
        { "id": "…", "label": "نوع الحافة", "field_type": "select", "required": true, "options": ["كتف", "شامفر"] }
      ]
    }
  ]
}
اجلب الكتالوج عند فتح شاشة الإرسال (أو خزّنه مؤقتاً لساعة). الأسعار قد تتغير من المعمل في أي وقت، ولا تُحسب من عندك — الحساب النهائي يتم في المعمل.

٥. إرسال حالة — POST /cases

حقول الجسم:

الحقلالنوعإلزاميالوصف
external_refstring ≤120مستحسن جداًمعرّفك عندك (رقم الزيارة/العمل). يمنع التكرار عند إعادة الإرسال.
patient_namestring ≤160نعماسم المريض.
patient_ageinteger 0–130لاالعمر.
patient_gender"male" | "female"لاالجنس.
case_type"digital" | "traditional"لارقمية (سكان) أو تقليدية (طبعة). الافتراضي: رقمية.
priority"normal" | "urgent"لاالأولوية.
due_dateYYYY-MM-DDلاتاريخ التسليم المطلوب.
notesstring ≤2000لاملاحظات الطبيب.
itemsarray 1–30نعمبنود العمل (انظر الجدول التالي).

كل بند داخل items:

الحقلالنوعإلزاميالوصف
work_type_iduuidنعممن /catalog.
teetharray من أرقام 11–85لاالأسنان بنظام FDI (11 = الأيمن العلوي الأمامي).
bridgesarray of arraysلامجموعات الجسور، مثل [[11,12,13]].
quantityinteger 1–200لاعدد القطع. الافتراضي: عدد الأسنان أو 1.
shadestring ≤40لااللون، مثل A2.
notesstring ≤1000لاملاحظة البند.
field_valuesobjectلاقيم الحقول الإضافية للعمل من fields في الكتالوج.
bash
curl -X POST https://cadcam.lovable.app/api/public/partner/cases \
  -H "Authorization: Bearer $LAB_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "external_ref": "clinic-visit-8841",
    "patient_name": "أحمد محمد",
    "patient_age": 34,
    "patient_gender": "male",
    "case_type": "digital",
    "priority": "normal",
    "due_date": "2026-10-05",
    "notes": "اللون A2 — تسليم قبل الخميس",
    "items": [
      { "work_type_id": "8c1f…", "teeth": [11, 12], "shade": "A2" },
      { "work_type_id": "8c1f…", "bridges": [[14, 15, 16]], "quantity": 3 }
    ]
  }'
الردّ 201
{ "id": "b2d9…", "code": "25-1043", "duplicate": false }
  • code هو رقم الحالة الرسمي في المعمل — اعرضه للطبيب واستخدمه في المتابعة.
  • إعادة إرسال نفس external_ref تعيد نفس الحالة مع duplicate: true والرمز 200 — آمن لإعادة المحاولة بعد انقطاع الشبكة.
  • المعمل يمنع أيضاً تكرار نفس المريض + نفس نوع الحالة خلال دقيقتين، ويرد بخطأ واضح.
  • الأسعار تُحسب في المعمل من الكتالوج؛ أي سعر ترسله يُتجاهل.

٦. إرفاق ملف — POST /files

يُرفق ملف سكان أو تصميم أو صورة بحالة قائمة لنفس الحساب: STL / PLY / OBJ / HTML / صور / PDF. الحد الأقصى ٢٥ ميجابايت للملف الواحد، ويُرسل مُرمّزاً Base64.

الحقلالنوعإلزاميالوصف
casestringنعمرقم الحالة (code) أو معرّفها (id).
namestring ≤200نعماسم الملف مع الامتداد، مثل upper.stl.
content_base64stringنعممحتوى الملف Base64 (يُقبل أيضاً data:…;base64, كبادئة).
content_typestring ≤120لانوع المحتوى إن عرفته.
bash
curl -X POST https://cadcam.lovable.app/api/public/partner/files \
  -H "Authorization: Bearer $LAB_API_KEY" -H "Content-Type: application/json" \
  -d "{\"case\":\"25-1043\",\"name\":\"upper.stl\",\"content_base64\":\"$(base64 -w0 upper.stl)\"}"
الردّ 201
{
  "file": { "id": "…", "url": "/api/public/media/cases/partner/…stl", "name": "upper.stl", "kind": "partner", "created_at": "…" },
  "case": { "id": "b2d9…", "code": "25-1043" }
}

٧. المتابعة — GET /cases و GET /cases/{code}

GET /cases يعيد حالات هذا الحساب فقط. معاملات الاستعلام: status (حالة الإنتاج)، search (رقم الحالة أو اسم المريض)، limit (١–٢٠٠، الافتراضي ٥٠).

bash
curl -H "Authorization: Bearer $LAB_API_KEY" "https://cadcam.lovable.app/api/public/partner/cases?limit=20&search=أحمد"
curl -H "Authorization: Bearer $LAB_API_KEY" https://cadcam.lovable.app/api/public/partner/cases/25-1043

GET /cases/{code_or_id} يعيد التفاصيل الكاملة: البنود، مراحل الإنتاج وحالتها، الملفات، طلبات موافقة التصميم، الدفعات، والرصيد المتبقي على الحالة.

الردّ (مختصر)
{
  "case": { "id": "b2d9…", "code": "25-1043", "external_ref": "clinic-visit-8841", "status": "in_progress",
            "delivery_status": "pending", "due_date": "2026-10-05", "total_price": 18000, "discount": 0, "currency": "YER" },
  "items":  [{ "work_type_name": "تاج زيركون", "teeth": [11, 12], "quantity": 2, "unit_price": 9000 }],
  "stages": [{ "name": "التصميم", "department": "cad", "status": "done", "completed_at": "…" },
             { "name": "القص", "department": "milling", "status": "in_progress" }],
  "files":   [{ "name": "upper.stl", "url": "/api/public/media/…" }],
  "reviews": [{ "id": "…", "image_url": "…", "message": "يرجى الموافقة على التصميم", "status": "pending" }],
  "payments": [{ "amount": 10000, "currency": "YER", "receipt_no": "R-221" }],
  "balance": 8000
}
إذا كان في reviews عنصر بحالة pending فهناك تصميم ينتظر موافقة الطبيب — اعرضه كتنبيه في نظام العيادة.

٨. كشف الحساب — GET /statement

إجماليات مفصولة بالعملة (رصيد افتتاحي + مستحق − مدفوع = الرصيد)، مع قائمة الحالات والدفعات. معاملات اختيارية: from وto بصيغة تاريخ ISO.

bash
curl -H "Authorization: Bearer $LAB_API_KEY" "https://cadcam.lovable.app/api/public/partner/statement?from=2026-09-01"
الردّ (مختصر)
{
  "totals": { "YER": { "opening": 0, "billed": 480000, "paid": 300000, "balance": 180000 } },
  "cases": [{ "code": "25-1043", "patient_name": "أحمد محمد", "total_price": 18000, "currency": "YER" }],
  "payments": [{ "amount": 10000, "currency": "YER", "receipt_no": "R-221", "created_at": "…" }]
}

٩. الإشعار العكسي (Webhook)

يضيف مدير المعمل عنوان نظام العيادة (https فقط) لكل مفتاح، فيصل إليه إشعار فوري عند كل تغيّر مهم — بدون انتظار أو استعلام متكرر.

الحدثمتى يُرسلأهم الحقول
case.updatedتغيّر حالة الإنتاج أو حالة التسليمid, code, external_ref, status, delivery_status, patient_name, due_date, delivered_at
payment.recordedتسجيل دفعة على حالةorder_id, code, amount, currency, method, receipt_no, created_at
review.requestedطلب موافقة على التصميمorder_id, code, review_id, image_url, message, status
شكل الحمولة
{
  "event": "case.updated",
  "sent_at": "2026-09-19T08:14:22.104Z",
  "data": { "id": "b2d9…", "code": "25-1043", "external_ref": "clinic-visit-8841",
            "status": "ready", "delivery_status": "pending", "patient_name": "أحمد محمد" }
}
  • الترويسات: X-Lab-Event باسم الحدث، وX-Lab-Signature = HMAC SHA-256 (hex) لنص الجسم الخام بسر التوقيع.
  • سر التوقيع يظهر لمدير المعمل في صفحة «الربط مع الأنظمة» بجانب العنوان.
  • تحقّق من التوقيع دائماً على النص الخام قبل تحليل JSON، بمقارنة ثابتة الزمن.
  • ردّ بسرعة بـ 200؛ المعالجة الطويلة تكون بعد الرد. عالج الإشعار بشكل مُتحمّل للتكرار (اعتمد code + status).
التحقق من التوقيع (Node / Web Crypto)
import { createHmac, timingSafeEqual } from "crypto";

export async function handleLabWebhook(request: Request) {
  const raw = await request.text();                       // النص الخام أولاً
  const sent = request.headers.get("x-lab-signature") ?? "";
  const expected = createHmac("sha256", process.env.LAB_WEBHOOK_SECRET!)
    .update(raw)
    .digest("hex");

  const a = Buffer.from(sent, "utf8");
  const b = Buffer.from(expected, "utf8");
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
    return new Response("invalid signature", { status: 401 });
  }

  const { event, data } = JSON.parse(raw);
  // حدّث العمل عندك بحسب data.external_ref أو data.code
  return new Response("ok");
}

١٠. الأخطاء ومعالجتها

الرمزerrorالمعنى وما تفعله
401unauthorizedالمفتاح ناقص أو خاطئ أو معطّل — راجع الترويسة، أو اطلب مفتاحاً جديداً من المعمل.
400invalid_jsonالجسم ليس JSON صالحاً.
400invalid_bodyحقول ناقصة أو غير صحيحة — التفاصيل في issues[] (مسار الحقل والسبب).
400create_failedرفض من المعمل (مثل نوع عمل غير موجود أو حالة مكررة) — الرسالة في message بالعربية.
404not_foundلا توجد حالة بهذا الرقم لهذا الحساب.
413file_too_largeالملف يتجاوز ٢٥ ميجابايت — قسّمه أو اضغطه.
500read_failed / upload_failed / attach_failedخطأ مؤقت — أعد المحاولة بتأخير متزايد.
  • أعد المحاولة عند 429/500/503 بتأخير متزايد (1s، 4s، 15s).
  • لا تُعد إنشاء الحالة بعد انقطاع دون external_ref — معه الإعادة آمنة تماماً.
  • سجّل رمز الحالة وmessage عندك لتشخيص أسرع؛ المدير يرى نفس النداءات في المعمل.

١١. للمساعد الذكي (AI agent)

إن كنت تبني الربط داخل نظام العيادات بمساعدة وكيل ذكي، أعطه هذه الروابط مباشرة:

text
التوثيق كنص خام (Markdown): https://cadcam.algomaei.com/docs/integration.md
وصف الواجهة (OpenAPI 3.1): https://cadcam.algomaei.com/api/public/partner/openapi.json

وهذه المهمة الجاهزة للصق في الوكيل:

text
اقرأ https://cadcam.algomaei.com/docs/integration.md ثم أضف في هذا المشروع:
1) شاشة «إعدادات الربط بالمعمل»: حفظ مفتاح lab_live_ في أسرار الخادم + زر اختبار الاتصال عبر GET /ping.
2) في ملف المريض زر «إرسال للمعمل»: يجلب /catalog ويعرض أنواع الأعمال وأسعارها، يختار الأسنان بنظام FDI،
   ثم POST /cases مع external_ref = معرّف الزيارة، ثم POST /files لكل ملف سكان.
3) شاشة «أعمالي في المعمل»: GET /cases و GET /cases/{code} مع تنبيه عند وجود طلب موافقة تصميم، و GET /statement للرصيد.
4) نقطة استقبال إشعارات: تتحقق من X-Lab-Signature (HMAC SHA-256 على النص الخام) قبل التحديث.
كل النداءات من الخادم فقط، والمفتاح لا يظهر في المتصفح.

١٢. قواعد وحدود

  • المفتاح يقرأ ويكتب بيانات حسابه فقط — لا وصول لأي حالة أو حساب آخر.
  • لا يمكن تعديل الأسعار ولا الخصومات ولا مراحل الإنتاج من الخارج.
  • تعطيل المفتاح من المعمل يوقف الربط فوراً دون أي أثر على الحالات السابقة.
  • الأسنان بنظام FDI؛ الجسور مجموعات أرقام متصلة.
  • العملات كما هي في المعمل (YER / USD) ولا تُحوَّل تلقائياً.
  • حد الملف ٢٥ ميجابايت؛ حد بنود الحالة ٣٠ بنداً.
  • الملفات تُخزَّن في المعمل وتظهر للفنيين مع الحالة.