مقایسه نام کاربر با نام ثبتشده در استعلام هویتی
نام واردشده و نام ثبتشده را با علامت مساوی مقایسه نکنید؛ ی و ک عربی، نیمفاصله و اعراب کاربر درست را رد میکنند و نرمالسازی پیش از مقایسه لازم است.
نام ثبتشده در استعلام هویتی و نامی که کاربر خودش تایپ کرده را نباید با علامت مساوی مقایسه کرد. «محمّد» و «محمد»، «علی» با ی فارسی و همان نام با ی عربی، «عبدالله» و «عبد الله»، همه یک ناماند و برابر نیستند. مقایسهٔ خام، کاربر درست را رد میکند.
این نوشته برای تیمی است که استعلام اطلاعات هویتی را گرفته تا نام اظهارشده را با نام ثبتشده بسنجد، و میخواهد بداند مقایسه را چطور بنویسد که نه شل باشد نه سختگیر بیدلیل.
ورودی و خروجی، در یک نگاه
ورودی: کد ملی و تاریخ تولد شمسی. خروجی وقتی تطابق باشد: first_name، last_name، father_name، national_code و دو نشانگر وضعیت حیات (alive و is_dead). وقتی تطابق نباشد: matched نادرست و بقیهٔ فیلدها null.
دقت کنید که چیزی به نام «درصد شباهت» در پاسخ نیست. اگر چنین چیزی میخواهید، سرویس جداگانهای دارد که در همین دسته است و با نام و نام پدر کار میکند؛ اما اگر استعلام هویتی را انتخاب کردهاید، مقایسه با شماست.
چرا مقایسهٔ مساوی کار نمیکند
سه دسته تفاوت میان دو رشتهٔ فارسی هست که انسان نمیبیند و ماشین میبیند.
نویسههای همشکل. ی فارسی (U+06CC) و ی عربی (U+064A)، ک فارسی (U+06A9) و ک عربی (U+0643). کاربرانی که با صفحهکلید عربی یا برخی گوشیها تایپ میکنند، ناخواسته نویسهٔ دیگری میفرستند. چشم تفاوتی نمیبیند.
فاصله و نیمفاصله. «عبدالرضا»، «عبد الرضا» و «عبدالرضا» (با نیمفاصله). نام خانوادگی مرکب هم همینطور.
اعراب و کشیده. تشدید، تنوین، «ـ» کشیده. بعضی نامها در ثبت اعراب ندارند و کاربر اضافه میکند، یا برعکس.
اولین کار، پیش از هر مقایسه، نرمالسازی هر دو طرف است. یک تابع کوچک کافی است:
js const norm = (s) => s .replace(/[\u064A\u0649]/g, '\u06CC') .replace(/\u0643/g, '\u06A9') .replace(/[ً-ٟـ]/g, '') // اعراب و کشیده .replace(/[]/g, ' ') // نیمفاصله به فاصله .replace(/\s+/g, ' ') .trim();
بعد از آن، خیلی از «ناهمخوانی»های ظاهری از بین میروند.
سه سطح مقایسه
بعد از نرمالسازی، سه سطح داریم و شما باید انتخاب کنید کجا بایستید.
- برابری کامل. سادهترین، سختترین. برای سندی که نام در آن عیناً چاپ میشود (قرارداد، بیمهنامه) شاید لازم باشد.
- برابری بدون فاصله. هر دو رشته را بدون فاصله مقایسه میکنید. «عبدالله» و «عبد الله» برابر میشوند. اغلب فرمها همینجا باید باشند.
- شباهت تقریبی. فاصلهٔ ویرایشی (Levenshtein) کوچک را بپذیرید. این خطرناکتر است: «حسین» و «حسن» دو نام متفاوتاند و فاصلهٔ ویرایشیشان کم است.
سطح سوم برای هشدار خوب است، نه برای پذیرفتن. یعنی نام را بپذیرید ولی برای بررسی انسانی علامت بزنید، یا کاربر را با «آیا منظورتان … بود؟» روبهرو کنید. به نظر من مقایسهای که خودکار تصمیم قطعی بر پایهٔ شباهت تقریبی بگیرد، دیر یا زود یک نفر را اشتباه میپذیرد.
نام و نام خانوادگی را جدا مقایسه کنید
کاربر در یک کادر «نام و نام خانوادگی» مینویسد؛ ثبتشده دو فیلد جدا دارد. پیش از مقایسه باید کادر را بشکنید و اینجا گیر میکنند: نامهای دوکلمهای («محمدرضا»، «علیاکبر») و نام خانوادگیهای دوکلمهای («تقوی نژاد»). شکستن بر اساس آخرین فاصله خطا میکند.
راه امنتر: دو فیلد جدا در فرم بگیرید. اگر مجبورید یک کادر داشته باشید، رشتهٔ ترکیبی را با first_name + ' ' + last_name بسازید و کل را با کل مقایسه کنید، نه بخشها را.
وقتی نتیجه نمیخواند، به کاربر چه بگویید
دو حالت مختلف را قاطی نکنید:
- تطابق انجام نشد (
matchedنادرست). یعنی کد ملی و تاریخ تولد با هم نخواندهاند. اینجا هیچ نامی در دست ندارید که مقایسه کنید. به کاربر بگویید تاریخ تولد یا کد ملی را بررسی کند. بیش از این چیزی نگویید. - تطابق انجام شد ولی نام نمیخواند. اینجا نام ثبتشده را دارید. پیام بد: «نام شما با ثبتها نمیخواند». پیام بهتر: «نامی که وارد کردید با اطلاعات ثبتشده فرق دارد. دوباره بررسی کنید.» و نام ثبتشده را نشان ندهید.
مورد آخر مهم است. اگر فرم شما نام ثبتشده را در پیام خطا بنویسد، هر کسی با داشتن یک جفت کد ملی و تاریخ تولد میتواند از فرم شما یک ابزار جستوجوی نام بسازد. کل منطق «تاریخ تولد اجباری است» برای جلوگیری از همین بود.
ورودی را درست بسازید
تاریخ تولد شمسی است و سال چهاررقمی. 1370/01/05، 1370-01-05 و 13700105 هر سه پذیرفته میشوند. اگر تاریخ تولد میلادی بفرستید یا روزی که در آن ماه وجود ندارد (مثلاً سیام اسفند در سال غیرکبیسه)، با invalid_input برمیگردد و هزینهای ندارد. مستندات API جزئیات این فیلدها را دارد.
نام پدر و وضعیت حیات
دو فیلد دیگر هم هست که ارزش دارند.
نام پدر را میشود بهعنوان سیگنال دوم مقایسه به کار برد، وقتی نام شخص رایج است. «محمد رضایی» در هر شهری دهها نفر است. ولی توجه: نام پدر هم باید همان نرمالسازی را بگیرد، و اگر فرم شما نام پدر نمیگیرد، افزودنش فقط برای این مقایسه بار اضافه بر کاربر است. من آن را فقط وقتی میخواهم که سند حقوقی صادر میشود.
وضعیت حیات (alive و is_dead) را در جریانهای مالی نادیده نگیرید. کد ملی کسی که فوت کرده هنوز از نظر ساختاری معتبر است و تطابق با تاریخ تولدش هم انجام میشود. اگر is_dead درست بود و کاربری با این هویت دارد ثبتنام میکند، یا کسی با هویت یک متوفی کار میکند یا خانوادهای حساب را به نام او باز کرده. هر دو مورد، رد خودکار نیست؛ بررسی انسانی لازم دارد. این را سمت سیستم خودتان تصمیم میگیرید.
یک جدول برای تست
پیش از راهاندازی، این موارد را در مجموعهٔ آزمون خودتان بگذارید (با دادههای ساختگی):
| ورودی کاربر | نام ثبتشده | نتیجهٔ مطلوب |
|---|---|---|
| «علی» با ی عربی | «علی» | پذیرفته شود |
| «عبد الله» | «عبدالله» | پذیرفته شود |
| «محمدرضا» | «محمد رضا» | پذیرفته شود |
| «حسین» | «حسن» | با هشدار، نه پذیرش خودکار |
| نام فرد دیگر | «علی» | رد شود |
اینها را مثل دادهی نمونهٔ خودتان بدانید؛ نه خروجی واقعی سرویس. هدف جدول این است که منطق مقایسه را پیش از اتصال به API امتحان کنید، چون هر پرسش آزمایشی به API هزینه دارد.
آنچه این سرویس نمیکند
- نشانی، شماره شناسنامه، محل تولد یا جنسیت برنمیگرداند.
- درباره سابقه یا تغییر نام چیزی نمیگوید.
- مقایسه را برای شما انجام نمیدهد؛ فقط داده میدهد.
- پاسخ را نباید در لاگ خام بنویسید. نام و نام پدر داده شخصیاند.
خلاصه برای مرور بعدی
در استعلام اطلاعات هویتی نرمالسازی کنید، بدونفاصله مقایسه کنید، شباهت تقریبی را فقط برای هشدار نگه دارید، نام ثبتشده را نشان ندهید. با همین چهار کار، جریان شما هم دقیق است هم کاربر را بیدلیل رد نمیکند.
وبسرویس مرتبط با این راهنما
API استعلام اطلاعات هویتی
اطلاعات هویتی با کد ملی و تاریخ تولد.
راهنماهای مرتبط
پرسش و تجربه کاربران
اگر این راهنما جوابتان را نداد، همینجا بپرسید. پرسشها را تیمملی ایپیآی پاسخ میدهد و هر نظر پیش از انتشار بررسی میشود.
هنوز پرسشی ثبت نشده، اولین نفر باشید.
