نظام التزويد
تستخدم OmniCRM Ansible لأتمتة عملية التزويد، والتكوين، وإلغاء تزويد خدمات العملاء. تم تصميم نظام التزويد ليكون مرنًا، مما يسمح بتدفقات عمل معقدة مع الحفاظ على الاتساق والموثوقية.

::: note ::: title ملاحظة :::
للحصول على جولة كاملة حول رحلة المنتج إلى الخدمة مع أمثلة تفصيلية على كتيبات Ansible، واستراتيجيات التسعير، وسيناريوهات العالم الحقيقي، راجع دليل دورة حياة المنتج الكامل. :::
نظرة عامة
عندما يتم طلب منتج أو يحتاج خدمة إلى التكوين، تقوم OmniCRM بإنشاء وظيفة تزويد تنفذ واحدًا أو أكثر من كتيبات Ansible. تتفاعل هذه الكتيبات مع أنظمة خلفية متنوعة (OCS/CGRateS، مع��ات الشبكة، APIs، إلخ) لتزويد الخدمة بالكامل.
يدعم نظام التزويد تدفقين رئيسيين:
- التزويد القياسي - يتم تفعيله بواسطة الموظفين أو العملاء من خلال واجهة المستخدم/API
- التزويد البسيط - يتم تفعيله بواسطة أنظمة خارجية مثل OCS للعمليات الآلية
قيم حالة التزويد
يمكن أن تحتوي وظائف التزويد والمهام الفردية على الحالات التالية:
- الحالة 0 (نجاح) - اكتملت وظيفة التزويد بنجاح
- الحالة 1 (جارٍ) - وظيفة التزويد أو المهمة قيد التنفيذ حاليًا
- الحالة 2 (فشل - حرج) - حدث فشل حرج تسبب في فشل التزويد
- الحالة 3 (فشل - تم تجاهله) - فشلت مهمة ولكن كانت تحتوي على
ignore_errors: true، لذا استمر التزويد
عندما تفشل وظيفة التزويد، ترسل OmniCRM إشعارات عبر البريد الإلكتروني إلى قائمة إشعار الفشل المكونة مسبقًا مع معلومات خطأ مفصلة.
كيف تدفع المنتجات التزويد
تعريف المنتج هو المخطط لما يتم تزويده وكيف. عندما يختار المستخدم منتجًا للتزويد، يقرأ النظام عدة حقول رئيسية من تعريف المنتج لتحديد ما يجب القيام به.
حقول المنتج المستخدمة في التزويد
يحتوي تعريف المنتج على:
provisioning_play- اسم كتيب Ansible الذي سيتم تنفيذه (بدون امتداد .yaml)provisioning_json_vars- سلسلة JSON تحتوي على المتغيرات الافتراضية التي سيتم تمريرها إلى Ansibleinventory_items_list- قائمة بأنواع المخزون التي يجب تعيينها (مثل،['SIM Card', 'Mobile Number'])product_id،product_name، حقول التسعير - يتم تمريرها تلقائيًا إلى كتيب العمل
مثال على تعريف المنتج
{
"product_id": 1,
"product_slug": "Mobile-SIM",
"product_name": "Mobile SIM Only",
"provisioning_play": "play_psim_only",
"provisioning_json_vars": "{\"iccid\": \"\", \"msisdn\": \"\"}",
"inventory_items_list": "['SIM Card', 'Mobile Number']",
"retail_cost": 0,
"retail_setup_cost": 0,
"wholesale_cost": 3,
"wholesale_setup_cost": 1
}
من المنتج إلى وظيفة التزويد
عند بدء عملية التزويد، يقوم النظام:
-
تحميل كتيب العمل المحدد في
provisioning_playيبحث النظام عن
OmniCRM-API/Provisioners/plays/play_psim_only.yaml -
دمج المتغيرات من مصادر متعددة في
extra_vars:a. من provisioning_json_vars:
{"iccid": "", "msisdn": ""}b. من جسم الطلب: أي متغيرات إضافية يقدمها المستخدم/API c. من حقول المنتج:product_id،customer_id، إلخ. d. من ال��صادقة:access_tokenأو إعداد لـrefresh_token -
تعيين المخزون بناءً على
inventory_items_listقبل تشغيل كتيب العمل، تطلب واجهة المستخدم/API اختيار المخزون:
- SIM Card - يختار المستخدم SIM متاح من المخزون
- Mobile Number - يختار المستخدم رقم هاتف متاح
تتم إضافة معرفات المخزون المحددة إلى
extra_varsمع نوع المخزون كمفتاح:extra_vars = {
"product_id": 1,
"customer_id": 456,
"SIM Card": 789, # inventory_id of selected SIM
"Mobile Number": 101, # inventory_id of selected phone number
"iccid": "", # From provisioning_json_vars
"msisdn": "", # From provisioning_json_vars
"access_token": "eyJ..."
} -
تمرير كل شيء إلى Ansible عبر
hostvars[inventory_hostname]داخل كتيب العمل، يمكن الوصول إلى المتغيرات على النحو التالي:
- name: Get inventory_id for SIM Card
set_fact:
inventory_id_sim_card: "{{ hostvars[inventory_hostname]['SIM Card'] | int }}"
when: "'SIM Card' in hostvars[inventory_hostname]"
كيف تستخدم كتيبات العمل متغيرات المخزون
بمجرد أن تحتوي كتيب العمل على معرفات المخزون، فإنه يسترجع تفاصيل المخزون الكاملة من API:
- name: Get SIM Card Details from Inventory
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: sim_card_response
- name: Extract ICCID and IMSI from inventory
set_fact:
iccid: "{{ sim_card_response.json.iccid }}"
imsi: "{{ sim_card_response.json.imsi }}"
- name: Get Phone Number Details from Inventory
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_phone_number }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: phone_number_response
- name: Extract MSISDN
set_fact:
msisdn: "{{ phone_number_response.json.msisdn }}"
يمكن بعد ذلك استخدام هذه القيم لـ:
- تزويد بطاقة SIM على HSS مع IMSI
- تكوين رقم الهاتف في نظام الفوترة
- ��عيين عناصر المخزون للعميل
- إنشاء سجل الخدمة مع هذه التفاصيل
مثال من العالم الحقيقي: تزويد بطاقة SIM المحمولة
من play_psim_only.yaml، إليك كيف يستخدم بيانات المنتج والمخزون:
- name: Get Product information from CRM API
uri:
url: "{{ crm_config.crm.base_url }}/crm/product/product_id/{{ product_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_product
- name: Set package facts from product
set_fact:
package_name: "{{ api_response_product.json.product_name }}"
package_comment: "{{ api_response_product.json.comment }}"
setup_cost: "{{ api_response_product.json.retail_setup_cost }}"
monthly_cost: "{{ api_response_product.json.retail_cost }}"
- name: Set inventory_id_sim_card if SIM Card was selected
set_fact:
inventory_id_sim_card: "{{ hostvars[inventory_hostname]['SIM Card'] | int }}"
when: "'SIM Card' in hostvars[inventory_hostname]"
- name: Set inventory_id_phone_number if Mobile Number was selected
set_fact:
inventory_id_phone_number: "{{ hostvars[inventory_hostname]['Mobile Number'] | int }}"
when: "'Mobile Number' in hostvars[inventory_hostname]"
- name: Get SIM Card details from inventory
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: sim_inventory_response
- name: Get Phone Number details from inventory
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_phone_number }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: phone_inventory_response
- name: Extract values from inventory
set_fact:
iccid: "{{ sim_inventory_response.json.iccid }}"
imsi: "{{ sim_inventory_response.json.imsi }}"
msisdn: "{{ phone_inventory_response.json.msisdn }}"
ki: "{{ sim_inventory_response.json.ki }}"
opc: "{{ sim_inventory_response.json.opc }}"
- name: Provision subscriber on HSS
uri:
url: "http://{{ hss_server }}/subscriber/{{ imsi }}"
method: PUT
body_format: json
body:
{
"imsi": "{{ imsi }}",
"msisdn": "{{ msisdn }}",
"ki": "{{ ki }}",
"opc": "{{ opc }}",
"enabled": true
}
status_code: 200
- name: Assign inventory to customer
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
{
"customer_id": {{ customer_id }},
"item_state": "Assigned"
}
status_code: 200
هذا يوضح التدفق الكامل:
- يحدد تعريف المنتج
provisioning_play: "play_psim_only" - يتطلب المنتج
inventory_items_list: ['SIM Card', 'Mobile Number'] - يختار المستخدم عناصر المخزون أثناء التزويد
- يتم تمرير معرفات المخزون إلى كتيب العمل كـ
extra_vars - يسترجع كتيب العمل تفاصيل المخزون الكاملة من API
- يستخدم كتيب العمل بيانات المخزون لتكوين معدات الشبكة
- يحدد كتيب العمل المخزون على أنه مخصص للعميل
التراجع والتنظيف: نمط أفضل الممارسات
أفضل ممارسة حرجة: يجب أن يتعامل نفس كتيب العمل مع كل من التراجع عن فشل التزويد وإلغاء التزويد المتعمد باستخدام هيكل block و rescue في Ansible.
هيكل كتيب الع��ل
من play_psim_only.yaml:
- name: OmniCore Service Provisioning 2024
hosts: localhost
gather_facts: no
become: False
tasks:
- name: Main block
block:
# --- مهام التزويد ---
- name: Get Product information
uri: ...
- name: Create account in OCS
uri: ...
- name: Provision subscriber on HSS
uri: ...
- name: Create service record
uri: ...
# ... العديد من مهام التزويد الأخرى ...
rescue:
# --- مهام التنظيف ---
# يتم تشغيل هذا القسم عندما:
# 1. تفشل أي مهمة في الكتلة (التراجع)
# 2. action == "deprovision" (تنظيف متعمد)
- name: Get Inventory items linked to this service
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/customer_id/{{ customer_id }}"
method: GET
register: inventory_api_response
ignore_errors: True
- name: Return inventory to pool
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ item.inventory_id }}"
method: PATCH
body_format: json
body:
service_id: null
customer_id: null
item_state: "Used"
with_items: "{{ inventory_api_response.json.data }}"
ignore_errors: True
- name: Delete Account from Charging
uri:
url: "http://{{ crm_config.ocs.OCS }}/jsonrpc"
method: POST
body:
{
"method": "ApierV1.RemoveAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}"
}]
}
ignore_errors: True
- name: Delete Attribute Profile
uri:
url: "http://{{ crm_config.ocs.OCS }}/jsonrpc"
method: POST
body:
{
"method": "APIerSv1.RemoveAttributeProfile",
"params": [{
"ID": "ATTR_ACCOUNT_{{ service_uuid }}"
}]
}
ignore_errors: True
- name: Remove Resource Profile
uri: ...
ignore_errors: True
- name: Remove Filters
uri: ...
ignore_errors: True
- name: Deprovision Subscriber from HSS
uri:
url: "{{ item.key }}/subscriber/{{ item.value.subscriber_id }}"
method: DELETE
loop: "{{ hss_subscriber_data | dict2items }}"
ignore_errors: True
when:
- deprovision_subscriber | bool == true
- name: Patch Subscriber to Dormant State
uri:
url: "{{ item.key }}/subscriber/{{ item.value.subscriber_id }}"
method: PATCH
body:
{
"enabled": true,
"msisdn": "9999{{ imsi[-10:] }}", # رقم وهمي
"ue_ambr_dl": 9999999, # مرتفع بشكل غير قابل للاستخدام
"ue_ambr_ul": 9999999
}
loop: "{{ hss_subscriber_data | dict2items }}"
when:
- deprovision_subscriber | default(false) | bool == false
# تؤكد العبارة النهائية النجاح أو الفشل
- name: Set status to "Success" if Manual deprovision / Fail if failed provision
assert:
that:
- action == "deprovision"
لماذا يعتبر هذا النمط أفضل ممارسة
1. لا تكرار في الكود
تتعامل نفس مهام التنظيف مع كلا السيناريوهين:
- فشل التزويد (التراجع): إذا فشلت أي مه��ة في
block، يتم تنفيذ قسمrescueتلقائيًا - إلغاء التزويد المتعمد: عند استدعائه مع
action: "deprovision"، ينتقل كتيب العمل مباشرة إلىrescue
2. ضمان التنظيف الكامل
عندما يفشل التزويد في منتصف الطريق، يضمن قسم الإنقاذ:
- حذف جميع حسابات OCS التي تم إنشاؤها
- إزالة جميع إدخالات معدات الشبكة المكونة
- إعادة المخزون المعين إلى المجموعة
- حذف المشتركين في HSS أو تعيينهم إلى حالة خاملة
- عدم بقاء أي تزويد جزئي في أي نظام
هذا يمنع الموارد "اليتيمة" التي:
- تستهلك المخزون دون أن يتم تتبعها
- تنشئ حسابات فوترة غير مرتبطة بالخدمات
- تسبب الارتباك أثناء استكشاف الأخطاء وإصلاحها
- تهدر موارد الشبكة
3. معالجة الفشل بشكل سلس مع ignore_errors
لاحظ أن كل مهمة تنظيف تستخدم ignore_errors: True. هذا مقصود لأنه:
- أثناء التراجع، قد لا تكون بعض الموارد قد تم إنشاؤها بعد
- نريد محاولة جميع مهام التنظيف حتى لو فشلت بعضها
- تحدد العبارة النهائية النجاح/الفشل العام
على سبيل المثال، إذا فشل التزويد عند "إنشاء حساب في OCS"، ستقوم عملية التنظيف بمحاولة:
- حذف حساب OCS (ستفشل، ولكن سيتم تجاهلها)
- إزالة ملفات تعريف السمات (ستفشل، ولكن سيتم تجاهلها)
- إعادة المخزون (تنجح)
- حذف المشترك في HSS (قد لا يوجد، يتم تجاهله)
4. تمييز إلغاء التزويد عن التراجع
العبارة النهائية في نهاية rescue ذكية:
- name: Set status to "Success" if Manual deprovision / Fail if failed provision
assert:
that:
- action == "deprovision"
هذا يعني:
- إذا كان ``action == "deprovision"``: تمر العبارة، ينجح كتيب العمل (الحالة 0)
- إذا لم يتم تعيين ``action`` أو != "deprovision": تفشل العبارة، يفشل كتيب العمل (الحالة 2)
لذا فإن نفس كود التنظيف ينتج عنه حالات وظائف تزويد مختلفة اعتمادًا ��لى النية.
5. تنظيف مشروط بناءً على نوع الخدمة
تستخدم بعض مهام التنظيف شروطًا للتعامل مع سيناريوهات مختلفة:
- name: Deprovision Subscriber from HSS
uri: ...
when:
- deprovision_subscriber | bool == true
- name: Patch Subscriber to Dormant State
uri: ...
when:
- deprovision_subscriber | default(false) | bool == false
هذا يسمح بتنظيف مرن:
- الحذف الكامل: عندما تكون بطاقات SIM مخصصة للعملاء
(
deprovision_subscriber: true) - الحالة الخاملة: عندما تكون بطاقات SIM قابلة لإعادة الاستخدام ويجب أن تبقى في HSS
(
deprovision_subscriber: false)
كيفية استخدام هذا النمط
للتزويد:
{
"product_id": 1,
"customer_id": 456,
"provisioning_play": "play_psim_only"
}
إذا فشل التزويد، يحدث التراجع التلقائي عبر rescue.
لإلغاء التزويد:
{
"service_id": 123,
"service_uuid": "Service_abc123",
"action": "deprovision",
"provisioning_play": "play_psim_only"
}
يتخطى كتيب العمل مباشرة إلى قسم rescue، وينفذ جميع مهام التنظيف، وينجح.
ملخص الفوائد
✅ مصدر واحد للحقيقة: يتعامل كتيب عمل واحد مع التزويد وإلغاء التزويد ✅ عمليات ذرية: إما أن يتم تزويدها بالكامل أو تنظيفها بالكامل ✅ لا موارد يتيمة: لا تترك التزويدات الفاشلة أي أثر ✅ صيانة أسهل: التغييرات في منطق التزويد تنطبق تلقائيًا على التنظيف ✅ تقليل الأخطاء: لا فرصة لعدم تزامن كود التزويد وإلغاء التزويد ✅ قابل للاختبار: يمكن اختبار منطق إلغاء التزويد عن طريق التشغيل مع action: "deprovision"
يجب اتباع هذا النمط في جميع كتيبات التزويد لضمان الموثوقية والاتساق.
تجاوز متغيرات المنتج
يمكن تجاوز provisioning_json_vars في وقت التزويد. على سبيل المثال، قد يعرف المنتج:
{
"provisioning_json_vars": "{\"monthly_cost\": 50, \"data_limit_gb\": 100}"
}
ولكن عند التزويد، يمكنك تجاوز هذه القيم:
{
"product_id": 1,
"customer_id": 456,
"monthly_cost": 45,
"data_limit_gb": 150
}
ستستخدم extra_vars المدمجة القيم المتجاوزة. هذا يسمح بـ:
- تسعير مخصص لعملاء محددين
- حدود بيانات مختلفة بناءً على العروض الترويجية
- اختبار مع معلمات مختلفة دون تعديل المنتج
المنتجات بدون مخزون
لا تتطلب جميع المنتجات مخزونًا. على سبيل المثال، قد يحتوي إضافة بيانات أو تبديل ميزة على:
{
"product_id": 10,
"product_name": "Extra 10GB Data",
"provisioning_play": "play_local_data_addon",
"provisioning_json_vars": "{\"data_gb\": 10}",
"inventory_items_list": "[]"
}
في هذه الحالة، يتلقى كتيب العمل:
extra_vars = {
"product_id": 10,
"customer_id": 456,
"service_id": 123, # الخدمة التي سيتم إضافة البيانات إليها
"data_gb": 10,
"access_token": "eyJ..."
}
يضيف كتيب العمل ببساطة البيانات إلى الخدمة الحالية دون الحاجة إلى أي عناصر مخزون.
تدفق التزويد القياسي
يتم بدء التزويد القياسي عندما:
- يضيف موظف خدمة إلى عميل من واجهة المستخدم
- يطلب عميل خدمة من خلال بوابة الخدمة الذاتية
- يتم استدعاء API مباشرة مع
PUT /crm/provision/
عندما تضغط على "تزويد"
إليك التدفق الكامل الذي يحدث عندما ينقر المستخدم على زر "تزويد":
1. تعرض واجهة المستخدم اختيار المنتج
يختار المستخدم منتجًا من كتالوج المنتجات. يحتوي المنتج على:
provisioning_play- أي كتيب Ansible يجب تشغيلهinventory_items_list- المخزون المطلوب (مثل،['SIM Card', 'Mobile Number'])provisioning_json_vars- المتغيرات الافتراضية
2. مختار المخزون (إذا لزم الأمر)
إذا كانت inventory_items_list ليست فارغة، تظهر نافذة منبثقة تعرض قوائم منسدلة لكل نوع من أنواع المخزون. يجب على المستخدم اختيار عناصر المخزون المتاحة قبل المتابعة.
3. تم النقر على زر التزويد
ترسل JavaScript طلب PUT /crm/provision/:
PUT /crm/provision/
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"product_id": 42,
"customer_id": 123,
"SIM Card": 5001,
"Mobile Number": 5002
}
4. يستقبل API الطلب
نقطة نهاية التزويد (routes/provisioning.py):
- يتحقق من صحة المصادقة (رمز Bearer، مفتاح API، أو قائمة بيضاء IP)
- يتحقق من أن المستخدم لديه إذن
CREATE_PROVISION - يستخرج
initiating_userمن الرمز - يحمل تعريف المنتج من قاعدة البيانات
- يسترجع مسار كتيب العمل:
OmniCRM-API/Provisioners/plays/play_psim_only.yaml
5. دمج المتغيرات
يجمع النظام المتغيرات من مصادر متعددة:
# من المنتج
product_vars = json.loads(product['provisioning_json_vars'])
# من جسم الطلب
request_vars = request.json
# تمت إضافتها بواسطة النظام
system_vars = {
'product_id': 42,
'customer_id': 123,
'access_token': g.access_token, # انظر قسم المصادقة أدناه
'initiating_user': 7
}
# المدمجة النهائية
extra_vars = {**product_vars, **request_vars, **system_vars}
6. إنشاء سجل التزويد
تم إنشاء سجل قاعدة البيانات مع الحالة 1 (جارٍ):
provision = {
'provision_id': 456,
'customer_id': 123,
'product_id': 42,
'provisioning_play': 'play_psim_only',
'provisioning_json_vars': json.dumps(extra_vars),
'provisioning_status': 1, # جارٍ
'task_count': 85,
'initiating_user': 7,
'created': '2025-01-10T14:30:00Z'
}
7. تم إنشاء خيط خلفي
run_playbook_in_background(
playbook='plays/play_psim_only.yaml',
extra_vars=extra_vars,
provision_id=456,
refresh_token=refresh_token # لتحديث الرمز أثناء التنفيذ
)
8. تعيد API الاستجابة على الفور
تُعاد الاستجابة إلى واجهة المستخدم مع provision_id:
{
"provision_id": 456,
"provisioning_status": 1,
"message": "Provisioning job created"
}
9. تقوم واجهة المستخدم بالاستعلام عن التحديثات
تبدأ واجهة المستخدم في الاستعلام عن GET /crm/provision/provision_id/456 كل 3 ثوانٍ للتحقق من الحالة. تتضمن الاستجابة:
{
"provision_id": 456,
"provisioning_status": 1,
"task_count": 12,
"provisioning_result_json": [
{
"event_number": 1,
"event_name": "Get Product information from CRM API",
"provisioning_status": 0,
"timestamp": "2024-01-15T10:30:05"
},
{
"event_number": 2,
"event_name": "Assign SIM Card from inventory",
"provisioning_status": 1,
"timestamp": "2024-01-15T10:30:07"
}
]
}
10. تنفذ Ansible في الخلفية
يتم تشغيل كتيب العمل للمهام بشكل متسلسل:
- كل إكمال مهمة ينشئ سجل
Provision_Eventفي قاعدة البيانات - تتضمن الحدث: اسم المهمة، الحالة (0=نجاح، 2=فشل، 3=فشل ولكن تم تجاهله)، نتيجة JSON
- تعرض واجهة المستخدم التقدم في الوقت الحقيقي مع عرض المهام المكتملة والمهمة الحالية التي يتم تشغيلها
- تظهر المهام الفاشلة رسائل خطأ في تفاصيل الحدث
التتبع في واجهة المستخدم:
أثناء تشغيل التزويد (الحالة 1)، يمكن للمستخدمين عرض:
- صفحة تفاصيل الخدمة - تعرض شارة حالة التزويد (جارٍ/نجاح/فشل)
- سجل النشاط - يسرد جميع أحداث التزويد مع الطوابع الزمنية
- عرض تفاصيل التزويد - يظهر تقدم المهمة بمهمة مع إمكانية التوسيع/الطي للتفاصيل
عرض مثال:
حالة التزويد: جارٍ (8 من 12 مهمة مكتملة)
✓ الحصول على معلومات المنتج من واجهة برمجة تطبيقات CRM ✓ استرداد تفاصيل العميل ✓ تعيين بطاقة SIM من المخزون (ICCID: 8991101200003204510) ✓ تعيين رقم الهاتف (555-0123) ⟳ إنشاء حساب في OCS/CGRateS (قيد التقدم...) ⏺ تكوين سياسات الشبكة ⏺ إنشاء سجل الخدمة ...
11. يكتمل التزويد
تعيين الحالة النهائية:
provisioning_status: 0- نجاحprovisioning_status: 2- فشل (خطأ حرج)
تتوقف واجهة المستخدم عن الاستعلام وتعرض النتيجة:
- نجاح: علامة تحقق خضراء، الخدمة محددة نشطة، يمكن للمستخدم عرض تفاصيل الخدمة
- فشل: علامة X حمراء، يتم عرض رسالة خطأ، خيار لإعادة المحاولة أو الاتصال بالدعم
- إشعار عبر البريد الإلكتروني: إذا فشل، يتم إرسال بريد إلكتروني إلى
provisioning.failure_listفي التكوين
المصادقة والتفويض
تتبع المستخدم
تتبع كل وظيفة تزويد المستخدم الذي بدأها:
- مبادرة المستخدم: يتم تعيين حقل
initiating_userإلى معرف المستخدم من رمز JWT الخاص بهم - مصادقة مفتاح API: يستخدم معرف أول مستخدم إداري
- مصادقة قائمة بيضاء IP: يستخدم معرف أول مستخدم إداري
التحقق من الأذونات
يتحقق النظام من الأذونات قبل السماح بالتزويد:
- يحتاج الموظفون إلى إذن
CREATE_PROVISION - يمكن للعملاء فقط تزويد الخدمات لحساباتهم الخاصة
(إذن
VIEW_OWN_PROVISION)
كيف تقوم Ansible بالمصادقة مع واجهة برمجة تطبيقات CRM
تحتاج كتيبات Ansible إلى إجراء مكالمات API مصادق عليها مرة أخرى إلى CRM (لاسترداد تفاصيل المنتج، إنشاء الخدمات، تحديث المخزون، إلخ). تتم معالجة المصادقة من خلال رموز Bearer المرسلة إلى كتيب العمل.
يعتمد مصدر access_token على طريقة المصادقة المستخدمة لاستدعاء واجهة برمجة تطبيقات التزويد:
الطريقة 1: تسجيل دخول المستخدم (رمز Bearer)
عندما يقوم المستخدم بتسجيل الدخول عبر واجهة الويب:
- يقوم المستخدم بالمصادقة:
POST /crm/auth/login - يتلقى رمز JWT
access_token(قصير الأجل، 15-30 دقيقة) وrefresh_token(طويل الأجل) - يقدم طلب التزويد مع رمز Bearer في الرأس
- تستخرج واجهة برمجة تطبيقات التزويد الرمز من رأس
Authorization: Bearer ... - يتم تخزينه في
g.access_token(سياق طلب Flask) - يتم تمريره إلى Ansible كمتغير
access_token
الكود (permissions.py):
# استخراج رمز Bearer من الرأس
auth_header = request.headers.get('Authorization', '')
if auth_header.startswith('Bearer '):
bearer_token = auth_header[7:]
# التحقق من الصحة وفك التشفير
decoded_token = jwt.decode(bearer_token, secret_key, algorithms=['HS256'])
# التخزين للتزويد
g.access_token = bearer_token
ال��ود (provisioning.py):
if "access_token" in g:
json_data['access_token'] = g.access_token
run_playbook(playbook_path, extra_vars=json_data, provision_id=provision_id)
الطريقة 2: مفتاح API (رأس X-API-KEY)
بالنسبة للأنظمة الآلية التي تستخدم مفاتيح API:
- يقوم النظام بإجراء الطلب:
PUT /crm/provision/معX-API-KEY: your-api-key...في الرأس - تتحقق واجهة برمجة تطبيقات التزويد من مفتاح API مقابل
crm_config.yaml - تولد رمز JWT جديد في الوقت الفعلي لأول مستخدم إداري
- يتم تخزينه في
g.access_token - يتم تمريره إلى Ansible
لماذا توليد رمز؟
مفاتيح API هي سلاسل، وليست JWTs. تتصل كتيبات العمل بنقاط نهاية API التي تتوقع المصادقة باستخدام JWT. لذا:
- تحقق من مفتاح API
- إذا كان صالحًا وله دور
admin، قم بتوليد JWT مؤقت - استخدم معرف أول مستخدم إداري كموضوع JWT
- يسمح الرمز لكتيب العمل بإجراء مكالمات API مصادق عليها
الكود (permissions.py):
def handle_api_key_auth(f, api_key, *args, **kwargs):
if not secure_compare_api_key(api_key):
return {'message': 'Invalid API key'}, 401
API_KEYS = yaml_config.get('api_keys', {})
if api_key in API_KEYS:
if 'admin' in API_KEYS[api_key].get('roles', []):
admin_user_id = retrieve_first_admin_user_id()
access_token = create_access_token(identity=str(admin_user_id))
g.access_token = access_token
الطريقة 3: قائمة بيضاء IP
بالنسبة للأنظمة الداخلية الموثوقة على الشبكات الخاصة:
- يقوم النظام بإجراء الطلب من IP مدرج في القائمة البيضاء (مثل، 192.168.1.100)
- تتحقق واجهة برمجة تطبيقات التزويد من عنوان IP الخاص بالعميل مقابل
ip_whitelistفيcrm_config.yaml - إذا كانت مدرجة في القائمة البيضاء، تولد رمز JWT جديد لأول مستخدم إداري
- يتم تخزينه في
g.access_token - يتم تمريره إلى Ansible
الكود (permissions.py):
def handle_ip_auth(f, *args, **kwargs):
client_ip = get_real_client_ip()
if not is_ip_whitelisted(client_ip):
return {'message': 'Access denied'}, 403
admin_user_id = retrieve_first_admin_user_id()
access_token = create_access_token(identity=str(admin_user_id))
g.access_token = access_token
استخدام الرمز في كتيبات العمل
تتضمن كل مكالمة API في كتيب العمل الرمز:
- name: Get Product Details
uri:
url: "http://localhost:5000/crm/product/product_id/{{ product_id }}"
headers:
Authorization: "Bearer {{ access_token }}"
- name: Create Service Record
uri:
url: "http://localhost:5000/crm/service/"
method: PUT
headers:
Authorization: "Bearer {{ access_token }}"
body:
customer_id: "{{ customer_id }}"
service_name: "Mobile Service"
انتهاء صلاحية الرمز وتجديده
قد تتجاوز كتيبات العمل التي تعمل لفترات طويلة (5-10 دقائق) access_token
(15-30 دقيقة انتهاء الصلاحية). بالنسبة للتزويدات التي يقوم بها المستخدم، يمرر النظام
كلاً من access_token و refresh_token:
refresh_token = request.cookies.get('refresh_token')
run_playbook(playbook_path, extra_vars, provision_id, refresh_token=refresh_token)
إذا انتهت صلاحية access_token، يمكن لمشغل كتيب العمل:
- اكتشاف استجابة 401 غير مصرح بها
- استدعاء
POST /crm/auth/refreshمعrefresh_token - استلام
access_tokenجديد - إعادة محاولة الطلب الفاشل
بالنسبة لمصادقة مفتاح API/قائمة بيضاء IP، يمكن أن تحتوي الرموز المولدة على انتهاء صلاحية أطول (1-2 ساعة) نظرًا لأن هذه أنظمة آلية موثوقة.
عملية التزويد
-
إنشاء الوظيفة
عند استلام طلب التزويد، يقوم النظام:
- يتحقق من صحة الطلب ويتحقق من الأذونات
- يحمل كتيب Ansible المحدد في تعريف المنتج
- ينشئ سجل
Provisionفي قاعدة البيانات مع الحالة 1 (جارٍ) - يستخرج المتغيرات من تعريف المنتج وجسم الطلب
- يلتقط رموز المصادقة للوصول إلى API
-
معالجة الرموز
تحتاج كتيبات Ansible إلى المصادقة مع واجهة برمجة تطبيقات CRM لاسترداد البيانات وإجراء التغييرات. تتعامل نظام التزويد مع ذلك بطريقتين:
- رمز Bearer (JWT): بالنسبة للتزويد الذي يقوم به المستخدم، يتم استخدام
refresh_tokenمن الطلب لتوليد رموز وصول جديدة أثناء تنفيذ كتيب العمل - مفتاح API/مصادقة IP: بالنسبة للأنظمة الآلية، يتم تمرير
access_tokenمباشرة إلى كتيب العمل عبرg.access_token
- رمز Bearer (JWT): بالنسبة للتزويد الذي يقوم به المستخدم، يتم استخدام
-
التنفيذ في الخلفية
يتم تشغيل كتيب العمل في خيط خلفي باستخدام
playbook_runner_v2. وهذا يسمح لواجهة API بالعودة على الفور بينما يستمر التزويد بشكل غير متزامن.أثناء التنفيذ:
- كل إكمال/فشل مهمة ينشئ سجل
Provision_Event - يراقب معالج الحدث الفشل الحرج مقابل الفشل المتجاهل
- يتم كتابة تحديثات الحالة في الوقت الحقيقي إلى قاعدة البيانات
- يمكن لواجهة المستخدم الاستعلام عن التحديثات عبر
GET /crm/provision/provision_id/<id>
- كل إكمال/فشل مهمة ينشئ سجل
-
تنفيذ كتيب العمل
عادةً ما يقوم كتيب العمل Ansible بتنفيذ هذه العمليات:
- يسترجع معلومات المنتج من واجهة برمجة التطبيقات
- يسترجع معلومات العميل من واجهة برمجة التطبيقات
- يعين عناصر المخزون (بطاقات SIM، عناوين IP، أرقام الهواتف، إلخ)
- ينشئ حسابات في OCS/OCS
- يقوم بتكوين معدات الشبكة
- ينشئ سجل الخدمة في واجهة برمجة تطبيقات CRM
- يضيف معاملات تكاليف الإعداد
- يرسل رسائل البريد الإلكتروني/SMS الترحيبية للعملاء
-
معالجة الأخطاء
تستخدم كتيبات Ansible أقسام
blockوrescueللتراجع:- إذا فشلت مهمة حاسمة، يزيل قسم الإنقاذ التزويد الجزئي
- يتم وضع علامة على المهام التي تحتوي على
ignore_errors: trueكحالة 3 ولا تفشل الوظيفة - تؤدي الأخطاء الفادحة (أخطاء بناء جملة YAML، فشل الاتصال) إلى إنشاء حدث خطأ خاص مع معلومات تصحيح الأخطاء
مثال: كتيب تزويد قياسي
إليك مثال من play_simple_service.yaml:
- name: Simple Provisioning Play
hosts: localhost
gather_facts: no
become: False
tasks:
- name: Main block
block:
- name: Get Product information from CRM API
uri:
url: "http://localhost:5000/crm/product/product_id/{{ product_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
validate_certs: no
register: api_response_product
- name: Set package facts
set_fact:
package_name: "{{ api_response_product.json.product_name }}"
setup_cost: "{{ api_response_product.json.retail_setup_cost }}"
monthly_cost: "{{ api_response_product.json.retail_cost }}"
- name: Generate Service UUID
set_fact:
service_uuid: "Service_{{ 99999999 | random | to_uuid }}"
- name: Create account in OCS
uri:
url: "http://{{ crm_config.ocs.OCS }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ActionPlanIds": [],
"ExtraOptions": { "AllowNegative": false, "Disabled": false }
}]
}
status_code: 200
register: response
- name: Add Service via API
uri:
url: "http://localhost:5000/crm/service/"
method: PUT
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"customer_id": "{{ customer_id }}",
"product_id": "{{ product_id }}",
"service_name": "Service: {{ service_uuid }}",
"service_uuid": "{{ service_uuid }}",
"service_status": "Active",
"retail_cost": "{{ monthly_cost | float }}"
}
status_code: 200
register: service_creation_response
- name: Add Setup Cost Transaction
uri:
url: "http://localhost:5000/crm/transaction/"
method: PUT
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
{
"customer_id": {{ customer_id | int }},
"service_id": {{ service_creation_response.json.service_id | int }},
"title": "{{ package_name }} - Setup Costs",
"retail_cost": "{{ setup_cost | float }}"
}
register: api_response_transaction
rescue:
- name: Remove account in OCS
uri:
url: "http://{{ crm_config.ocs.OCS }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV2.RemoveAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}"
}]
}
status_code: 200
- name: Fail the provision
assert:
that:
- false
يظهر هذا كتيب العمل التدفق النموذجي:
- استرجاع تفاصيل المنتج من واجهة برمجة تطبيقات CRM
- توليد UUID خدمة فريدة
- إنشاء حساب الفوترة في OCS
- إنشاء سجل الخدمة عبر واجهة برمجة التطبيقات CRM
- إضافة معاملات تكاليف الإعداد
- إذا فشل أي شيء، يقوم قسم
rescueبإزالة حساب OCS
تدفق التزويد البسيط
تم تصميم التزويد البسيط للأنظمة الآلية التي تحتاج إلى تحفيز التزويد دون تفاعل المستخدم. الحالة الأكثر شيوعًا هي تحفيز OCS لتز��يد الإضافات عبر ActionPlans.
نقاط نهاية التزويد البسيط
تقدم OmniCRM نقطتي تزويد بسيطتين:
-
POST /crm/provision/simple_provision_addon/service_id/<id>/product_id/<id>للتزويد الآلي للإضافات (مثل الرسوم المتكررة، الشحنات التلقائية)
-
POST /crm/provision/simple_provision_addon_recharge/service_id/<id>/product_id/<id>لعمليات الشحن السريعة التي تحتاج إلى تغذية راجعة فورية
المصادقة لتزويد بسيط
تستخدم نقاط نهاية التزويد البسيط قائمة بيضاء IP أو مفاتيح API للمصادقة:
- يتم التحقق من عنوان IP المصدر للطلب مقابل
ip_whitelistفيcrm_config.yaml - أو يمكن تقديم مفتاح API من
api_keysفيcrm_config.yaml - يتم توليد رمز وصول ويتم تمريره إلى كتيب العمل عبر
g.access_token
مثال: رد اتصال ActionPlan من OCS
يمكن تكوين OCS لاستدعاء نقطة نهاية التزويد البسيط عند تنفيذ الإجراءات المتكررة:
{
"method": "ApierV1.SetActionPlan",
"params": [{
"Id": "ActionPlan_Service123_Monthly_Charge",
"ActionsId": "Action_Service123_Add_Monthly_Data",
"Timing": {
"Years": [],
"Months": [],
"MonthDays": [1],
"Time": "00:00:00Z"
},
"Weight": 10,
"ActionTriggers": [
{
"ThresholdType": "*min_event_counter",
"ThresholdValue": 1,
"ActionsID": "Action_Service123_HTTP_Callback"
}
]
}]
}
تقوم الإجراء بعمل POST HTTP إلى:
هذا يحفز كتيب العمل المرتبط (مثل،
play_topup_no_charge.yaml) الذي يضيف بيانات/رصيد إلى الخدمة.
مثال: كتيب الشحن البسيط
من play_topup_monetary.yaml:
- name: Mobile Topup Monetary - 2024
hosts: localhost
gather_facts: no
become: False
tasks:
- name: Get Product information from CRM API
uri:
url: "http://localhost:5000/crm/product/product_id/{{ product_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_product
- name: Get Service information from CRM API
uri:
url: "http://localhost:5000/crm/service/service_id/{{ service_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_service
- name: Set service facts
set_fact:
service_uuid: "{{ api_response_service.json.service_uuid }}"
customer_id: "{{ api_response_service.json.customer_id }}"
package_name: "{{ api_response_product.json.product_name }}"
monthly_cost: "{{ api_response_product.json.retail_cost }}"
- name: Get Customer Payment Method
uri:
url: "http://localhost:5000/crm/stripe/customer_id/{{ customer_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_stripe
- name: Charge customer
uri:
url: "http://localhost:5000/crm/stripe/charge_card/{{ customer_stripe_id }}"
method: POST
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
{
"retail_cost": "{{ monthly_cost }}",
"description": "{{ package_name }} topup",
"customer_id": "{{ customer_id | int }}",
"service_id": "{{ service_id | int }}"
}
register: api_response_stripe
- name: Add monetary balance to OCS
uri:
url: "http://{{ crm_config.ocs.OCS }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"BalanceType": "*monetary",
"Balance": {
"Value": "{{ monthly_cost | float * 100 }}",
"ExpiryTime": "+4320h"
}
}]
}
status_code: 200
- name: Add Transaction to CRM
uri:
url: "http://localhost:5000/crm/transaction/"
method: PUT
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
{
"customer_id": {{ customer_id | int }},
"service_id": {{ service_id | int }},
"title": "{{ package_name }}",
"retail_cost": "{{ monthly_cost | float }}"
}
- name: Send Notification SMS
uri:
url: "http://sms-gateway/SMS/plaintext/{{ api_key }}"
method: POST
body_format: json
body:
{
"source_msisdn": "YourCompany",
"destinatination_msisdn": "{{ customer_phone }}",
"message_body": "Thanks for topping up {{ monthly_cost }}!"
}
status_code: 201
ignore_errors: True
يقوم هذا الكتيب بـ:
- الحصول على تفاصيل الخدمة والمنتج من واجهة برمجة التطبيقات
- استرداد طريقة الدفع الخاصة بالعميل
- شحن العميل عبر واجهة برمجة تطبيقات Stripe
- إضافة رصيد نقدي إلى OCS
- تسجيل المعاملة في CRM
- إرسال رسالة SMS تأكيد (مع
ignore_errors: Trueبحيث لا تفشل الأخطاء المهمة)
سلاسل التزويد
بالنسبة للمنتجات المعقدة التي تتطلب خطوات تزويد متعددة، تدعم OmniCRM سلاسل التزويد. تقوم السلسلة بتنفيذ عدة كتيبات عمل بشكل متسلسل، مع تمرير السياق بينها.
مثال على حالة الاستخدام: خدمة مجمعة تقوم بتزويد:
- خدمة الإنترنت الأساسية (تخلق سجل الخدمة الرئيسي)
- إضافة IPTV (تستخدم
service_idمن الخطوة 1) - إضافة IP ثابت (تستخدم
service_idمن الخطوة 1)
تقوم خدمة التزويد تل��ائيًا:
- استعلام قاعدة البيانات عن
service_idالذي تم إنشاؤه بواسطة كتيب العمل الأول - حقنها في
extra_varsلكتيبات العمل اللاحقة - تتبع كل كتيب عمل كسجل
Provisionمنفصل
أسباب الفشل وتصحيح الأخطاء
عندما يفشل التزويد، يقوم النظام بالتقاط معلومات مفصلة للمساعدة في تشخيص المشكلة.
سيناريوهات الفشل الشائعة
فشل المهام الحرجة (الحالة 2)
تسبب هذه الفشل في وظيفة التزويد بالكامل:
- مكالمات API التي تعيد رموز حالة غير متوقعة
- فشل التأكيدات (مثل،
assert: that: response.status == 200) - العناصر المطلوبة المفقودة من المخزون
- معدات الشبكة غير قابلة للوصول
- بيانات اعتماد غير صالحة أو رموز منتهية الصلاحية
- أخطاء OCS/OCS
الفشل المتجاهل (الحالة 3)
تُسجل هذه ولكن لا تفشل المهمة:
- فشل إشعارات SMS/البريد الإلكتروني الاختيارية
- عمليات البحث عن البيانات غير الحر��ة (المعلمة بـ
ignore_errors: True) - عمليات التنظيف أثناء إلغاء التزويد
الأخطاء الفادحة
تمنع هذه تشغيل كتيب العمل على الإطلاق:
- أخطاء بناء جملة YAML في كتيب العمل
- متغيرات غير معرفة في كتيب العمل
- ملفات كتيب العمل المفقودة
- فشل الاتصال بوحدة التحكم Ansible
عندما يحدث خطأ فادح، ينشئ النظام حدث خطأ خاص يحتوي على:
- رمز خروج Ansible
- stdout الكامل (يحتوي على تفاصيل خطأ البناء)
- stderr الكامل (يحتوي على أخطاء وقت التشغيل)
- قائمة بالأسباب الشائعة لهذا النوع من الفشل
- جميع المتغيرات المرسلة إلى كتيب العمل
رسائل البريد الإلكتروني لإشعار الأخطاء
عندما يفشل التزويد (الحالة 2)، يتم إرسال بريد إلكتروني تلقائيًا إلى
قائمة إشعار الفشل المكونة مسبقًا (provisioning.failure_list في
crm_config.yaml).
يتضمن البريد الإلكتروني:
- معلومات العميل
- تفاصيل المنتج/الخدمة
- نتائج المهام الملونة:
- أخضر: المهام الناجحة
- برتقالي: المهام الفاشلة ولكن تم تجاهلها
- أحمر: الفشل الحرج
- بالنسبة للفشل الحرج: إخراج تصحيح كامل بما في ذلك أجسام الطلب/الاستجابة
- بالنسبة للأخطاء الفادحة: إخراج Ansible، رسائل الأخطاء، والأسباب الشائعة
مراقبة وظائف التزويد
واجهة برمجة تطبيقات حالة التزويد
للتحقق من حالة وظيفة التزويد:
GET /crm/provision/provision_id/<id>
Authorization: Bearer <token>
تتضمن الاستجابة:
{
"provision_id": 123,
"customer_id": 456,
"customer_name": "John Smith",
"product_id": 10,
"provisioning_status": 0,
"provisioning_play": "play_psim_only",
"playbook_description": "OmniCore Service Provisioning 2024",
"task_count": 85,
"provisioning_result_json": [
{
"event_number": 1,
"event_name": "Get Product information from CRM API",
"provisioning_status": 1,
"provisioning_result_json": "{...}"
},
{
"event_number": 2,
"event_name": "Create account in OCS",
"provisioning_status": 1,
"provisioning_result_json": "{...}"
}
]
}
سرد وظائف التزويد
للحصول على قائمة مجزأة من جميع وظائف التزويد:
GET /crm/provision/?page=1&per_page=20&sort=provision_id&order=desc
Authorization: Bearer <token>
يدعم التصفية:
GET /crm/provision/?filters={"provisioning_status":[2]}&search=Mobile
Authorization: Bearer <token>
يعيد هذا الوظائف الفاشلة فقط (الحالة 2) حيث تحتوي الوصف على "Mobile".
أفضل الممارسات
تصميم كتيب العمل
- استخدم دائمًا block/rescue: تأكد من إمكانية التراجع عن التزويد الجزئي
- استخدم ignore_errors بحذر: فقط للعمليات الاختيارية حقًا
- سجل المتغيرات المهمة: استخدم مهام
debugلتسجيل القيم الرئيسية لأغراض تصحيح الأخطاء - تحقق من الاستجابات: استخدم
assertللتحقق من أن استجابات API كما هو متوقع - التماثل: صمم كتيبات العمل لتكون قابلة للتشغيل بأمان مرة أخرى
المصادقة
- التزويد الذي يقوم به المستخدم: استخدم دائمًا
refresh_tokenلكتيبات العمل طويلة الأمد - التزويد الآلي: استخدم قائمة بيضاء IP أو مفاتيح API مع رموز وصول مولدة
- انتهاء صلاحية الرمز: يضمن
refresh_tokenتجديد رموز الوصول حسب الحاجة
معالجة الأخطاء
- قدم السياق: قم بتضمين
customer_id،service_id، وتفاصيل العملية في رسائل الأخطاء - أبلغ بشكل مناسب: تؤدي الأخطاء الحرجة إلى إشعارات عبر البريد الإلكتروني، ولكن لا تزعج للأخطاء المتوقعة
- معلومات تصحيح الأخطاء: قم بالتقاط أجسام الطلب/الاستجابة الكاملة في سجلات
Provision_Event
الأمان
- تحقق من المدخلات: تحقق من
customer_id،product_id،service_idقبل التزويد - تحقق من الأذونات: تحقق من أن المستخدمين يمكنهم فقط التزويد للعملاء المصرح لهم
- بيانات حساسة: استخدم نظام الحذف لإزالة كلمات المرور/المفاتيح من السجلات
- قائمة بيضاء IP: قيد نقاط نهاية
simple_provisionعلى الأنظمة ال��وثوقة فقط
الأداء
- التنفيذ في الخلفية: لا تحظر استجابات API في انتظار التزويد
- فترات الاستعلام: يجب أن تستعلم واجهة المستخدم عن تحديثات الحالة كل 2-5 ثوانٍ
- المهام المتوازية: استخدم التوازي الأصلي لـ Ansible للعمليات المستقلة
- تحديثات قاعدة البيانات: يقوم معالج الأحداث بتحديث قاعدة البيانات في الوقت الحقيقي، ولا حاجة للاستعلام أثناء التنفيذ
الوثائق ذات الصلة
concepts_ansible- مفاهيم التزويد العامة في Ansibleconcepts_api- مصادقة واستخدام واجهة برمجة تطبيقات CRMconcepts_products_and_services- تعريفات المنتجات والخدماتadministration_inventory- إدارة المخزون للتزويد