إنتقل إلى المحتوى الرئيسي

البحث عن عميل

الخلفية​

على Shukah كان التطبيق يبحث عن عميل برقم الهاتف عبر GET /maeda/client?contact_phone=…. وفي خدمة المصادقة بحث مماثل عند GET /api/v1/clients. يُرجع الحقول الأربعة نفسها، لكنه يحتاج إلى JWT من Locate ويُخفي جزءًا من اسم العائلة (masking).

الميزة​

أرسل رقم هاتف واحصل على من يملكه: معرّف العميل وهاتفه واسمه الأول واسم عائلته المقنَّع. والإجابة مصفوفة بصف واحد، أو مصفوفة فارغة عندما لا يكون لأي عميل نشط هذا الهاتف.

في نسخة التطبيق اللحظية بتاريخ 2026-02-26، يستخدمه التطبيق فقط لإيجاد مستلم هدية نقاط، من نموذج الهدية ومرة أخرى عند إتمام شراء الهدية (checkout). ويقرأ الصف الأول فقط، وتتحول المصفوفة الفارغة إلى خطأ "User not found" في التطبيق.

يحتاج الاستدعاء إلى جلسة مسجَّلة الدخول، لكن الهاتف الذي تبحث عنه لا يلزم أن يكون هاتفك. فالـ token يحدد المستدعي فقط. أما الهاتف في query فيحدد من يعود.

GET /api/v1/clients​

يستبدل ShukahGET /maeda/client✅ تم الاستبدال
يحتاج تعديل التطبيق1 breaking · 3 للمراقبة
  • 🔴 يحصل token من Shukah على 401 "Unauthorization"؛ أرسل JWT من Locate (من exchange-token؛ واحتفظ بـ token من Shukah).
  • 🟡 تقنّع Locate الحقل last_name ("User" تصبح "U**r")؛ وتقنيع Shukah غير معروف (اللقطة منقّحة).
  • 🟡 الهاتف غير المعروف يعطي 200 [] على Locate (بحسب الكود)؛ ويحوّل التطبيق [] إلى "User not found" (user.service.ts:49-51).
  • 🟡 body الـ 401 في Locate هو {status,message} وليس body Laravel؛ ويقرأ التطبيق message فقط.

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <locate-jwt>، أي token المستدعي. ويُرفض token Shukah بـ 401.جلسة المستدعي.
contact_phonequerystringنعمتُحذف المسافات. ويُطابَق مع الهاتف المخزَّن بوجود + في البداية أو بدونه. الفارغ أو المفقود يعطي 400. وعلامة + الحرفية في URL تُقرأ مسافة وتُحذف، لذا فإن +966… غير المرمَّزة (unencoded) تظل تطابق.الهاتف المراد البحث عنه. ويمكن أن يعود لأي شخص، لا للمستدعي فقط.
key وlanguage وأي مفتاح query آخرquerystringلايُقبل ويُتجاهل.يُبقى حتى تظل سلسلة query القديمة لـ Shukah تعمل.

الاستجابة (Response)​

200 مع مصفوفة من صفر أو صف واحد:

الحقلالنوعيقبل nullالصيغة / القيودالوصف
client_idintegerلا—معرّف العميل الذي عُثر عليه.
contact_phonestringنعمالهاتف كما خُزِّن، غير مقنَّع.هاتف العميل.
first_namestringنعمتُحذف المسافات الزائدة. والفارغ يصبح null. غير مقنَّع.الاسم الأول.
last_namestringنعممقنَّع: يُحتفظ بالحرف الأول والأخير ويتحول كل حرف بينهما إلى * (User تصبح U**r). والاسم المكوَّن من حرف أو حرفين يُرجَع كما هو. والفارغ يصبح null.اسم العائلة، مقنَّع.

عندما لا يكون لأي عميل نشط وغير محذوف هذا الهاتف، تكون الإجابة 200 مع []. والعميل الموقوف أو المحظور أو المعلّق أو المنتهي يحصل على [] أيضًا.

[
{
"client_id": 356259,
"contact_phone": "+966500000000",
"first_name": "Test",
"last_name": "User"
}
]

التُقط على dev، 2026-10-01 (العميل 356259)؛ واستُبدلت الحقول الشخصية. وقد استبدل الالتقاط last_name بـ User، فلا يظهر هنا الشكل المقنَّع للقيمة الحقيقية. وقاعدة التقنيع (mask) مصدرها الشيفرة واختبار الوحدة الخاص بها: فـ User ستصبح U**r.

الأخطاء​

HTTPBodyمتى
400{"name":"Bad Request","message":"Phone number cannot be blank.","code":0,"status":400,"type":"yii\\web\\BadRequestHttpException"}contact_phone مفقود أو فارغ.
401{"status":401,"message":"Unauthorization"}الـ token مفقود أو مشوَّه أو ليس JWT من Locate، أو عميل المستدعي غير موجود، أو عميل المستدعي غير نشط.

التُقط body الخاص بـ 401 على dev في 2026-10-01 دون token. أما body الخاص بـ 400 فله الشكل نفسه الذي التُقط لـ reset-otp (انظر تسجيل الدخول برمز OTP). ولم يُلتقط على هذا الـ route، لأن جولة الالتقاط لم تُجرِ أي استدعاء بحث بـ token وهاتف فارغ.

جرّبه​

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
export PHONE='<phone-to-look-up>' # the answer shows that client's first name and phone
curl -s -G "$AUTH_BASE_URL/api/v1/clients" --data-urlencode "contact_phone=$PHONE" -H "Authorization: Bearer $TOKEN"

المتوقع: 200 ومصفوفة بصف واحد، كما هو مبيّن تحت «الاستجابة». والهاتف الذي لا يملكه أحد يعطي [].

الفروق عن Shukah​

لـ body الخاص بـ 200 المفاتيح الأربعة نفسها على الجانبين: ففي 2026-10-01 أرجعت الخدمتان client_id وcontact_phone وfirst_name وlast_name للعميل 356259.

الجانبShukahLocateالأثر على التطبيق
الـ Tokentoken من Shukah (استخدمه الالتقاط).JWT من Locate فقط. وtoken من Shukah يحصل على 401 عند الحارس نفسه الخاص بـ verify-token.استبدل token Shukah بالـ JWT قبل التحوّل، واحتفظ بـ token Shukah لـ order-v2.
URLGET $SHUKAH_BASE_URL/maeda/client?contact_phone=…&key=maeda&language=en-GB (الاستدعاء الملتقط).GET $AUTH_BASE_URL/api/v1/clients?contact_phone=…. تتجاهل Locate key وlanguage.غيّر base URL والمسار. ويمكن للتطبيق مواصلة إرسال key وlanguage.
last_nameغير موثّق بعد: فقد استُبدل last_name في التقاط dev، لذا لا يمكن رؤية التقنيع.مقنَّع: يُحتفظ بالحرف الأول والأخير ويتحول ما بينهما إلى *.ينسخ التطبيق last_name من الصف الأول، وLocate تعطيه مقنَّعًا. افحص كيف يستخدم تدفق الهدية الاسم.
هاتف غير معروفغير موثّق بعد.200 مع []، وكذلك لعميل غير نشط.يحوّل التطبيق المصفوفة الفارغة بالفعل إلى "User not found"، لذا لا يحتاج [] إلى تغيير.
body الخاص بـ 401{"name":"Unauthorized","message":"Your request was made with invalid credentials.","code":0,"status":401,"type":"Illuminate\\Auth\\AuthenticationException"} (بلا token، التُقط على dev، 2026-10-01).{"status":401,"message":"Unauthorization"} (بلا token، التُقط على dev، 2026-10-01).يجب تغيير أي شيء يطابق نص الخطأ أو name أو type. وقيمة status هي 401 في الاثنين.

تنبيه​

  • يعود last_name مقنَّعًا (User تصبح U**r)، لكن first_name وcontact_phone يعودان كاملين.
  • الهاتف غير المعروف والعميل غير النشط كلاهما يحصل على 200 مع []، ويحوّلها التطبيق إلى خطأ "User not found"، فلا يستطيع التمييز بينهما.
  • الـ token والهاتف مستقلان: فأي مستدعٍ مسجَّل الدخول يستطيع البحث عن أي هاتف والحصول على الاسم الأول لذلك العميل وهاتفه.

التفاصيل (للـ backend)​

فُحص dev فقط: الـ host https://dev.authentication.locate.sa والبادئة /api/v1. أما الـ host والبادئة في UAT وproduction: غير موثّق بعد.

الـ Route والبحث​

  • يُسجَّل GET /clients قبل أي route بمعاملات (parameter route)، خلف الـ middleware المسمّى client، لذا يحتاج إلى JWT من Locate لعميل نشط كما يفعل verify-token (src/routes/client.js:14-16، src/middlewares/client.js:8-42).
  • يقرأ الـ handler قيمة contact_phone ويجيب بـ 400 عبر خطأ Yii عندما تكون فارغة (src/controllers/client/lookup/index.js:33-34).
  • البحث هو findClientByPhone، نفسه في reset-otp: الهاتف المخزَّن مع + اختيارية، غير محذوف، بحالة ACTIVE، بأحدث client_id أولًا، دون cache (src/controllers/client/reset-otp/handler.js:24-30، src/shared/helpers/filter.js:1-4). ويُرجع أفضل تطابق واحد، لذا فللمصفوفة صف واحد على الأكثر (src/controllers/client/lookup/index.js:36-40).
  • علامة + الحرفية في URL تُفكّ ترميزها إلى مسافة، ثم تحذفها normalizePhone، لذا فإن +966… غير المرمَّزة تظل تطابق (src/sync/lib/casts.js:40-41؛ قراءة شيفرة).
  • تذكر وثيقة التصميم تدفقات التاجر (merchant) التي تحمل JWT عميل من Locate بوصفها حالة الاستخدام (docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:9-11). ولا تستدعيه نسخة التطبيق اللحظية إلا لتحديد مستلم هدية (انظر Sources).
  • الاستدعاء للقراءة فقط، دون cache خاص به ودون ترقيم صفحات (pagination) ودون آثار جانبية (docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:25-28).

تحويل الصف والتقنيع​

تبني toLookupRow كل صف (src/controllers/client/lookup/index.js:23-28):

  • client_id هو client_id المخزَّن. وcontact_phone هو الهاتف المخزَّن، أو null.
  • first_name تُحذف مسافاته الزائدة، والفارغ يصبح null (src/controllers/client/lookup/index.js:17-21).
  • يمر last_name عبر maskLastName: تُحذف المسافات الزائدة، وnull أو الفارغ يعطي null، والطول اثنان أو أقل يبقى دون تغيير، وإلا فالحرف الأول ثم length - 2 نجمة ثم الحرف الأخير (src/controllers/client/lookup/index.js:8-15). ويثبّت اختبار الوحدة User على U**r (src/controllers/client/__tests__/lookup.test.js:32).
  • تقنيع first_name أو contact_phone خارج النطاق في وثيقة التصميم (docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:93). ولذلك يحصل أي مستدعٍ مسجَّل الدخول عليهما كاملين لأي هاتف، ولا يستطيع الاستدعاء التمييز بين هاتف غير معروف وآخر غير نشط.

غير موثّق بعد​

  • هل تقنّع Shukah last_name: فقد استُبدلت القيمة في الالتقاط.
  • ماذا تجيب Shukah عن هاتف غير معروف أو هاتف فارغ: لم يُلتقط أي منهما.
  • هل يقبل بحث Shukah أيضًا مرشّحات القوائم. تدرج وثيقة التصميم "Pagination / status / page list filters from upstream" خارج النطاق بالنسبة إلى Locate (docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:91)، ولم يستخدم الالتقاط إلا contact_phone وkey وlanguage.
  • هل في أي بيئة هذا الـ route: لا يسجّله origin/production عند 4f3bc96 (2026-09-21) (ملف src/routes/client.js فيه لا يستورد lookup)، وهل يعمل أي شيء من ذلك الـ ref غير معروف.

حيث تختلف الوثائق عن الشيفرة (القول للشيفرة)​

  • مثال التقنيع في وثيقة التصميم فيه 13 نجمة (U*************) لـ User (500…000)) (docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:30). أما الشيفرة واختبار الوحدة فتعطيان 12 (src/controllers/client/__tests__/lookup.test.js:33)، لأن القاعدة هي length - 2. والفرق شكلي.

Sources​

  • locate-authentication-service@6757aa4: src/routes/client.js:14-16، src/middlewares/client.js:8-42, src/controllers/client/lookup/index.js:8-41، src/controllers/client/__tests__/lookup.test.js:32-33، src/controllers/client/reset-otp/handler.js:24-30, src/shared/helpers/filter.js:1-4، src/sync/lib/casts.js:40-41, src/shared/errors/yii-bad-request-error.js:12-23، src/middlewares/error-handler.js:10, src/shared/constant/error-message.js:59
  • وثائق locate-authentication-service@6757aa4: docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:9-11,25-30,56-59,90-93 (2026-08-04)
  • نسخة الجوال اللحظية mobile/locate-client@e960c3a (الفرع development، 2026-02-26؛ شيفرة التطبيق نفسه، وليست من Locate): src/entities/user/model/api/services/user.service.ts:38-47,49-51,58-60، src/entities/user/model/api/dto/user.dto.ts:126-155، src/widgets/SendPointGiftForm/model/hooks/sendPointGiftForm.ts:64، src/widgets/PointGiftsCheckout/model/hooks/usePointGiftsCheckout.ts:376
  • التقاطات dev، 2026-10-01: locate-client-lookup وshukah-client-lookup و probe-locate-client-lookup وprobe-shukah-client-lookup وprobe-locate-reset-otp
  • دليل ثانوي، ليس الـ sha المثبَّت: locate-authentication-service origin/production عند 4f3bc96 (2026-09-21)، src/routes/client.js