فترات زمن الوصول

تتيح Access Time Windows لعضو الشريك أن يحمل وصولًا مؤقتًا أو متكررًا يوفّره نظام خارجي. هذه الصفحة هي دليل المنتج المرجعي لكل من access_time_windows وaccess_time_windows_revision.

Access Time Windows أذونات إضافية للسماح فقط. يمكن للنافذة النشطة أن تسمح بالوصول، لكن النافذة غير النشطة أو المنتهية أو القديمة أو غير المعيَّنة لا تمنع وصولًا تسمح به بالفعل قاعدة عادية أو منحة دائمة. ويظل تعطيل المستخدم، والحظر الصريح للأبواب، ووضع الصيانة، وGeofence، وحدود المعدل، وتوفر وحدة التحكم سارية.

تطبيقات أنظمة المصدر المحددة هي محوّلات منفصلة. ويجب أن تترجم بيانات المصدر لديها إلى هذا العقد المحايد تجاه الموردين دون تغيير معاني حقوله.

حقول لقطة العضو

  • access_time_windows array اختياري

    لقطة Access Time Window الكاملة لهذا العضو ولمصدر الشريك هذا. ويمكن أن تجمع اللقطة بين نوافذ absolute ونوافذ weekly. احذف الحقل لترك اللقطة المخزنة دون تغيير؛ وأرسل [] لإلغاء كل النوافذ التي يملكها هذا المصدر.

  • access_time_windows_revision integer مطلوب مع access_time_windows

    عدد صحيح غير سالب ومتزايد باطّراد وآمن في JSON لتسلسل مراجعات هذا العضو/المصدر. وهو مطلوب كلما وُجد access_time_windows، ويجب ألا يتجاوز 9007199254740991.

تقبل كل لقطة 50 نافذة كحد أقصى. وتقبل external_id وscope_key وlabel ما يصل إلى 120 حرفًا. ويمكن أن تمتد النوافذ المطلقة 90 يومًا كحد أقصى. ويقبل اليوم الأسبوعي الواحد ثماني كتل غير متداخلة كحد أقصى. تُرفض البيانات غير الصالحة بدلًا من اقتطاعها أو تخمينها.

أشكال النوافذ

تحدد النافذة المطلقة فاصلًا زمنيًا دقيقًا واحدًا:

{
  "external_id": "access-4b1782",
  "kind": "absolute",
  "from": 1792053000,
  "to": 1792058400,
  "timezone": "Australia/Perth",
  "scope_key": "after_hours_entry",
  "label": "Temporary site access"
}

الحقول المطلوبة هي external_id وkind وfrom وto. أما timezone وscope_key وlabel فاختيارية.

تحدد النافذة الأسبوعية فواصل زمنية متكررة وفق التوقيت المحلي، ويمكن تقييدها اختياريًا بلحظات زمنية بتوقيت UTC:

{
  "external_id": "weekly-early-access",
  "kind": "weekly",
  "timezone": "America/New_York",
  "access_hours": {
    "sun": [],
    "mon": [["06:00", "08:30"]],
    "tue": [["06:00", "08:30"]],
    "wed": [["06:00", "08:30"]],
    "thu": [["06:00", "08:30"]],
    "fri": [["06:00", "08:30"]],
    "sat": []
  },
  "active_from": 1790812800,
  "active_until": 1796083200,
  "scope_key": "main_entry",
  "label": "Weekday early access"
}

الحقول المطلوبة هي external_id وkind وaccess_hours. أما timezone وactive_from وactive_until وscope_key وlabel فاختيارية.

طلب مختلط كامل

يستبدل هذا الطلب اللقطةَ السابقة للعضو التي يملكها المصدر بنافذة مطلقة واحدة ونافذة أسبوعية واحدة:

curl -X POST https://partner-api.performancehub.co/partner/v1/members \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "members": [
      {
        "member_id": "member_11111",
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        "first_name": "John",
        "last_name": "Smith",
        "access_time_windows_revision": 1842,
        "access_time_windows": [
          {
            "external_id": "access-4b1782",
            "kind": "absolute",
            "from": 1792053000,
            "to": 1792058400,
            "timezone": "Australia/Perth",
            "scope_key": "after_hours_entry",
            "label": "Temporary site access"
          },
          {
            "external_id": "weekly-early-access",
            "kind": "weekly",
            "timezone": "America/New_York",
            "access_hours": {
              "sun": [],
              "mon": [["06:00", "08:30"]],
              "tue": [["06:00", "08:30"]],
              "wed": [["06:00", "08:30"]],
              "thu": [["06:00", "08:30"]],
              "fri": [["06:00", "08:30"]],
              "sat": []
            },
            "active_from": 1790812800,
            "active_until": 1796083200,
            "scope_key": "main_entry",
            "label": "Weekday early access"
          }
        ]
      }
    ]
  }'

استخدم إما facility_id أو linking_identifier وفقًا لطريقة الربط المهيّأة لديك. لا ترسل كليهما.

قواعد الوقت والحدود

from وto وactive_from وactive_until أعداد صحيحة في JSON تمثّل ثواني Unix وتحدد لحظات زمنية بتوقيت UTC:

  • أرسل عددًا صحيحًا في JSON دون علامات اقتباس. تُرفض السلاسل النصية الرقمية، والأعداد العشرية، وسلاسل RFC 3339، والمللي ثانية وفق Unix.
  • كل فاصل زمني نصف مفتوح: [from, to). يمكن أن يبدأ الوصول عند from تمامًا؛ ولا يعود نشطًا عند to تمامًا.
  • يجب أن تكون from أقل من to. وعند وجود حدَّي النافذة الأسبوعية كليهما، يجب أن تكون active_from أقل من active_until.
  • لا تستخرج قيم epoch من تاريخ/وقت محلي دون منطقة زمنية. حوّل وقت المصدر أولًا إلى لحظة زمنية بتوقيت UTC، ثم اقسم المللي ثانية وفق Unix على 1000 وتحقّق من أن الناتج عدد صحيح.

تحويل epoch

يتحول 2026-10-15T08:30:00Z إلى 1792053000:

UTC instant                 Unix milliseconds   Unix seconds sent
2026-10-15T08:30:00Z        1792053000000        1792053000

تحويل مستقل عن لغة البرمجة:

instant = parse_source_timestamp_with_explicit_zone(source_timestamp)
milliseconds = instant.to_unix_milliseconds()
assert milliseconds modulo 1000 equals 0
seconds = milliseconds / 1000
assert seconds is a JSON-safe nonnegative integer

إذا كان المصدر يتضمن دقة أقل من الثانية، فحدّد سياسة تقريب مقصودة لبداية الفترات ونهايتها قبل اختبار التكامل. لا تمرّر المللي ثانية أبدًا معتمدًا على API لاستنتاج الوحدة.

المناطق الزمنية والتوقيت الصيفي

يجب أن تكون timezone منطقة زمنية صالحة وفق IANA مثل UTC أو Australia/Perth أو America/New_York. والقيمة الافتراضية عند حذفها هي UTC.

  • النوافذ المطلقة: تحدد from وto بالفعل لحظات زمنية دقيقة بتوقيت UTC. وtimezone مجرد بيانات وصفية للعرض والتدقيق؛ ولا يؤدي تغييرها أبدًا إلى إزاحة أي من اللحظتين.
  • النوافذ الأسبوعية: تحدد timezone التوقيت المحلي وقواعد التوقيت الصيفي المستخدمة لتقييم access_hours. لا تحوّل الكتل الأسبوعية إلى UTC مسبقًا.
  • أثناء تقديم الساعة، لا يحدث أبدًا الوقت المحلي الذي تتخطاه المنطقة الزمنية.
  • أثناء تأخير الساعة، تتطابق اللحظتان الفعليتان اللتان تحملان الوقت المحلي المتكرر كلتاهما مع الكتلة الأسبوعية.
  • تظل active_from وactive_until لحظات زمنية بتوقيت UTC حتى عندما تستخدم النافذة الأسبوعية منطقة زمنية غير UTC.

تستخدم access_hours الأيام بأحرف صغيرة من sun إلى sat. وكل قيمة مصفوفة من أزواج [start, end] بتنسيق 24 ساعة HH:mm مع أصفار بادئة. يجب أن تكون الكتل غير فارغة ومرتبة وغير متداخلة. وend غير مشمولة. لا يمكن للكتلة أن تتجاوز منتصف الليل؛ قسّم الفاصل الزمني الليلي في المصدر إلى كتلة متأخرة في اليوم الأول وكتلة مبكرة في اليوم التالي.

المعرّفات الثابتة وتعيين النطاقات

يجب أن يكون external_id ثابتًا طوال عمر النافذة المنطقية وفريدًا ضمن لقطة العضو. حافظ على ثباته عند تغيّر الأوقات أو التسميات حتى يتمكن المشغّلون وسجلات التدقيق من ربط المراجعات. ولا تُعِد استخدامه لنافذة غير ذات صلة.

scope_key هو هدف الوصول المنطقي الثابت الخاص بك. وقيمته الافتراضية default. يعيّن مسؤول المنشأة كل مفتاح مسموح به إلى أهداف في Performance Hub على مستوى المنشأة بأكملها، أو باب، أو نطاق الوصول:

Partner scope_key           Performance Hub mapping
default                     Facility's configured default target
main_entry                  Access Scope "Main entrances"
after_hours_entry           Doors approved for after-hours entry

لا يرسل الشركاء معرّفات المنشأة أو الباب أو نطاق الوصول في Performance Hub داخل النافذة. وأي scope_key غير معروف أو معطّل أو غير معيَّن يخفق بشكل مغلق لتلك النافذة؛ ولا يوسّع نطاق الوصول. اتفق على التعيينات واختبرها قبل الإطلاق.

اللقطات الكاملة والمراجعات

تعامل مع الحقلين بوصفهما قسمًا ذا إصدارات مستقلة داخل عملية الإدراج أو التحديث للعضو:

الإدخالالنتيجة
حذف access_time_windowsتبقى اللقطة الحالية دون تغيير؛ ولا يلزم تقديم مراجعة
موجودة مع مراجعة أحدثتُستبدل اللقطة المخزنة بالكامل لهذا العضو/المصدر
المراجعة نفسها والحمولة الموحّدة نفسهاإعادة إرسال آمنة من التكرار؛ ولا تتغير اللقطة
المراجعة نفسها وحمولة موحّدة مختلفةتعارض؛ خصّص مراجعة جديدة بعد التسوية
مراجعة أقدميتم تخطي اللقطة القديمة؛ ولا يمكنها الكتابة فوق الحالة الأحدث
access_time_windows: [] مع مراجعة أحدثتلغي صراحةً جميع النوافذ التي يملكها المصدر

اللقطة كاملة وليست تصحيحًا جزئيًا. فإذا كانت المراجعة 1842 تتضمن النافذتين A وB، فإن المراجعة 1843 التي تتضمن B فقط تزيل A.

حافظ على تسلسل مراجعات منسّق واحد لكل شريك/عضو عبر عمليات معالجة webhook، والاستطلاع الدوري، وإعادة الإرسال، والتسوية الكاملة. ويُفضَّل استخدام تسلسل دائم في قاعدة البيانات. لا يمكن استخدام قيمة بالمللي ثانية وفق Unix بوصفها مراجعة إلا عندما تنسّق كل الجهات الكاتبة وتضمن ترتيبًا صارمًا وتفرّدًا؛ فهي قيمة مراجعة، وليست حقل epoch.

احتفظ بالطلب والمراجعة كما هما بالضبط حتى تُعرف استجابة API. أعِد محاولة اللقطة الموحَّدة نفسها بالمراجعة نفسها بعد انتهاء المهلة أو حدوث خطأ عابر. ولا تخصّص مراجعة جديدة لمجرد أن تسليم النقل غير مؤكد.

الإلغاء وانتهاء الصلاحية وإخفاقات المصدر

  • إلغاء نافذة واحدة: اجلب حالة المصدر الموثوقة بنجاح، وأزل تلك النافذة، وأرسل اللقطة الكاملة المتبقية بمراجعة أحدث.
  • إلغاء جميع النوافذ: أرسل access_time_windows: [] بمراجعة أحدث.
  • ترك نافذة مطلقة تنتهي: تتوقف عن السماح بالوصول عند to؛ وأرسل تسوية لاحقة دون الإدخال المنتهي للحفاظ على نظافة اللقطات.
  • إنهاء نافذة أسبوعية: أزلها في لقطة أحدث، أو استخدم active_until عندما يكون لدى المصدر لحظة انتهاء معروفة.
  • إخفاق جلب المصدر أو عدم اكتماله: لا ترسل [] ولا تنشر لقطة جزئية. أطلق تنبيهًا وأعِد محاولة الجلب. فتحويل إخفاق المصدر إلى لقطة فارغة يعني إلغاءً غير مقصود للوصول.
  • إخفاق طلب Partner API في التحقق: صحّح البيانات وأرسل مراجعة أحدث فقط إذا تغيّرت اللقطة الموحّدة. وأعِد محاولة حالات الإخفاق العابرة بالمراجعة الأصلية.

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

خوارزمية المنتج

استخدم مسار العمل التالي المستقل عن لغة البرمجة لكل تحديث للعضو:

function publish_member(member_key):
    source_result = fetch_authoritative_access_state(member_key)

    if source_result.failed or source_result.incomplete:
        record_failure(member_key)
        schedule_retry(member_key)
        return without sending access_time_windows

    windows = []
    for source_window in source_result.current_windows:
        if source_window.is_absolute:
            windows.append({
                external_id: stable_window_id(source_window),
                kind: "absolute",
                from: to_integer_unix_seconds(source_window.start_instant),
                to: to_integer_unix_seconds(source_window.end_instant),
                timezone: source_window.display_timezone or "UTC",
                scope_key: stable_scope_key(source_window) or "default",
                label: safe_label(source_window)
            })
        else:
            windows.append({
                external_id: stable_window_id(source_window),
                kind: "weekly",
                timezone: valid_iana_zone(source_window.timezone) or "UTC",
                access_hours: split_validate_and_sort_weekly_blocks(source_window),
                active_from: optional_integer_unix_seconds(source_window.active_start),
                active_until: optional_integer_unix_seconds(source_window.active_end),
                scope_key: stable_scope_key(source_window) or "default",
                label: safe_label(source_window)
            })

    normalized_snapshot = validate_sort_and_deduplicate(windows)
    revision = next_durable_member_revision(member_key)
    request = member_upsert_with(normalized_snapshot, revision)
    persist_for_retry(member_key, request)

    response = post_request(request)
    if response.is_transient_or_unknown:
        retry_exact_request_with_same_revision(request)
    else if response.is_revision_conflict:
        reconcile_with_latest_committed_source_state(member_key)
    else if response.is_stale_skip:
        stop_replaying_this_revision_and_reconcile(member_key)

قائمة التحقق للاعتماد

أكمل هذه الفحوصات في منشأة بيئة الاختبار باستخدام التعيين النهائي لـ scope_key:

  • تُقبل نافذة مطلقة مع حذف المنطقة الزمنية وتُقيَّم بتوقيت UTC.
  • تحافظ نافذة مطلقة بمنطقة زمنية مخصصة وفق IANA على لحظتَي from/to نفسيهما.
  • تتبع نافذة أسبوعية بمنطقة زمنية مخصصة وفق IANA التوقيت المحلي عبر انتقالات تقديم الساعة وتأخيرها.
  • تستبدل لقطة مختلطة مطلقة/أسبوعية اللقطة السابقة بالكامل.
  • يبدأ الوصول عند from تمامًا ويتوقف عند to تمامًا؛ ونهايات الكتل الأسبوعية غير مشمولة كذلك.
  • يسري تحديث الأوقات ضمن external_id نفسه بمراجعة أحدث.
  • يؤدي حذف access_time_windows إلى تغيير حقول العضو الأخرى دون تغيير النوافذ.
  • إعادة إرسال المراجعة/الحمولة نفسها آمنة من التكرار.
  • تُرجع المراجعة نفسها مع حمولة مختلفة تعارضًا ولا تغيّر اللقطة.
  • يُبلَّغ عن مراجعة أقدم خارج الترتيب بأنها قديمة، ولا يمكنها إرجاع الحالة إلى الوراء.
  • تلغي [] الأحدث النوافذَ التي يملكها المصدر؛ ولا يُرسل أي شيء عند إخفاق الجلب من المصدر.
  • يخفق scope_key غير المعروف أو غير المعيَّن بشكل مغلق ويُنشئ تحذيرًا مرئيًا للمشغّل.
  • تعمل تنبيهات عمر المصدر وسياسة المصدر القديم المهيّأة عند العتبة المتفق عليها.
  • يتقارب مسارا webhook والتسوية الكاملة إلى اللقطة نفسها وترتيب المراجعات نفسه.
  • يصل التسليم المتسلسل إلى Door & Gate Access ضمن الهدف المتفق عليه.
  • يمكن للنافذة النشطة السماح بالباب الفعلي المعيَّن، لكن لا يمكنها تجاوز تعطيل المستخدم، أو blockAllAccess، أو الصيانة، أو Geofence.
  • قبل النافذة وخارجها وبعدها، تظل القواعد العادية والمنح الدائمة تسمح بوصول المستخدمين المؤهلين بخلاف ذلك.

الحد الأدنى من متجهات الاختبار

[
  {
    "name": "absolute-half-open",
    "window": {
      "external_id": "tv-absolute",
      "kind": "absolute",
      "from": 1792053000,
      "to": 1792058400
    },
    "expect": {
      "1792052999": "inactive",
      "1792053000": "active",
      "1792058399": "active",
      "1792058400": "inactive"
    }
  },
  {
    "name": "weekly-dst",
    "window": {
      "external_id": "tv-weekly-dst",
      "kind": "weekly",
      "timezone": "America/New_York",
      "access_hours": {
        "sun": [["01:00", "03:30"]],
        "mon": [],
        "tue": [],
        "wed": [],
        "thu": [],
        "fri": [],
        "sat": []
      }
    },
    "expect": "Evaluate local wall time: skipped times do not occur; both repeated 01:30 instants qualify."
  }
]

قدّم أيضًا متجهات سلبية للمللي ثانية وفق Unix، والسلاسل النصية الرقمية، والأعداد العشرية، والمناطق غير الصالحة وفق IANA، وfrom >= to، وexternal_id المكرر، والكتل المتداخلة، والكتل الليلية، وأكثر من ثماني كتل في اليوم، وأكثر من 50 نافذة، ومفاتيح النطاق غير المعيَّنة.