6.8 KiB
بازمحاسبهٔ «آخرین بازدید» مشتریان
چرا؟
فیلد Customer.last_bazdid_dt یعنی «آخرین بازدیدی که برای مشتری انجام شده» و صفحهٔ
/panel/entezar/ بر پایهٔ آن مشتریانِ معوق (بیش از N ماه بازدید نشده) را لیست
میکند.
قاعدهٔ درست (منبع حقیقت): این مقدار باید برابر بزرگترین finishDateTime میان
اقدامهای actionType='bazdid' و status='finish' همان مشتری باشد — دقیقاً همان
چیزی که ویوهای customerNewAction و customerEditAction مینویسند:
if newAction.status == 'finish' and newAction.actionType == 'bazdid':
customer_obj.last_bazdid_dt = newAction.finishDateTime
customer_obj.save()
پس اگر رکوردی از راهی غیر از این ویوها وارد شود (اسکریپت، ایمپورت، ویرایش دستی
دیتابیس، اجرای دستی gt_esh و…)، این فیلد عقب میماند و مشتریِ واقعاً
بازدیدشده، اشتباهاً «معوق» شمرده میشود. این دستور آن واگرایی (drift) را از روی
جدول Action ترمیم میکند.
دستور idempotent است؛ اجرای دوبارهاش هیچ تغییری ایجاد نمیکند.
دستور
نام دستور منیجمنت: recalc_last_bazdid
مسیر فایل: admin_panel/management/commands/recalc_last_bazdid.py
۱) اول «شبیهسازی» — بدون هیچ تغییری
فقط گزارش میدهد چه چیزی تغییر میکند. قبل از هر اجرای واقعی این را بزنید.
از WSL:
.venv/Scripts/python.exe manage.py recalc_last_bazdid --dry-run
از Windows:
.venv\Scripts\python.exe manage.py recalc_last_bazdid --dry-run
۲) اجرای واقعی
.venv/Scripts/python.exe manage.py recalc_last_bazdid
گزینهها
| گزینه | پیشفرض | کار |
|---|---|---|
--dry-run |
خاموش | فقط گزارش؛ هیچ رکوردی ذخیره نمیشود. اول این را بزنید. |
--clear-orphans |
خاموش | مشتریانی که مقدار دارند ولی هیچ اقدام بازدیدِ تمامشدهای پشتشان نیست را هم NULL میکند. بهطور پیشفرض دستنخورده میمانند. فقط اگر مطمئنید این ستون آلوده است. |
--batch-size |
500 |
اندازهٔ دستهٔ خواندن مشتریان در هر گام. |
دقیقاً چه میکند؟
برای هر مشتری، MAX(finishDateTime) اقدامهای بازدیدِ تمامشده محاسبه میشود، سپس:
| وضعیت مشتری | اقدام دستور |
|---|---|
| بازدید دارد و مقدار فعلی برابر MAX است | دست نمیزند (از قبل درست) |
| بازدید دارد و مقدار فعلی کوچکتر یا NULL است | به MAX بهروز میکند — از لیست معوقها خارج میشود (جدیدتر شد) |
| بازدید دارد و مقدار فعلی بزرگتر از MAX است | به MAX عقب میبرد (عقبتر شد) |
بازدید ندارد و مقدار NULL است |
کاری ندارد (طبیعی) |
| بازدید ندارد ولی مقداری دارد | دست نمیزند — مگر --clear-orphans بدهید (خالی شد) |
نوشتن با Customer.objects.filter(pk=…).update(last_bazdid_dt=…) داخل یک
transaction.atomic() انجام میشود؛ یعنی فقط همین یک ستون لمس میشود و هیچ
save() کاملی که فیلدهای دیگر (auto_now و…) را تغییر دهد اجرا نمیشود.
نمونهٔ خروجی
بازمحاسبهٔ آخرین بازدید مشتریان آغاز شد.
گزارش بازمحاسبهٔ آخرین بازدید:
بررسیشده (کل مشتریان): 10,426
دارای بازدیدِ تمامشده: 9,151
از قبل درست: 9,084
نیازمند اصلاح: 67
→ جدیدتر شد (خروج از معوق): 67
→ عقبتر شد: 0
بدون اقدام ولی دارای مقدار (دستنخورده): 0
NULL و بدون اقدام (طبیعی): 1,275
انجام شد: 67 رکورد بهروزرسانی شد.
اجرا روی production
اول از دیتابیس بکاپ بگیرید، سپس:
python manage.py recalc_last_bazdid --dry-run
python manage.py recalc_last_bazdid
دستور تراکنشی است و در صورت خطا، هیچ تغییری کامیت نمیشود.
بررسی صحت داده (SQL، پیش از اجرا)
اگر میخواهید بدون دستور، وضعیت را بسنجید:
-- مشتریانی که مقدارشان با آخرین بازدید واقعی اختلاف دارد (تعداد «نیازمند اصلاح»)
SELECT COUNT(*) FROM admin_panel_customer cu
JOIN (SELECT customer_id, MAX(finishDateTime) mx FROM admin_panel_action
WHERE actionType='bazdid' AND status='finish' GROUP BY customer_id) a
ON a.customer_id = cu.id
WHERE cu.last_bazdid_dt IS NULL OR cu.last_bazdid_dt <> a.mx;
-- مشتریانی که بازدید تمامشده دارند ولی مقدارشان NULL است (بدترین حالت)
SELECT COUNT(*) FROM admin_panel_customer cu
WHERE cu.last_bazdid_dt IS NULL
AND EXISTS (SELECT 1 FROM admin_panel_action a
WHERE a.customer_id=cu.id AND a.actionType='bazdid' AND a.status='finish');
-- تعداد سالم (مقدار دارد)
SELECT COUNT(*) FROM admin_panel_customer WHERE last_bazdid_dt IS NOT NULL;
نکتههای مهم
- مقادیر
NULLمشتریانی که هرگز بازدید تمامشده نداشتهاند، طبیعی است و دستور آنها را پر نمیکند (اگر پر کند، یعنی داده ساختن — که غلط است). - دستور هیچ فاکتور، سند، اقدام یا مشتری نمیسازد و حذف نمیکند؛ فقط یک ستون را همراستا میکند.
- صفحهٔ
/panel/entezar/یک کش درCacheTable(key='9mahbazdid')نگه میدارد؛ عددِ روی صفحه ممکن است یک اجرا عقب باشد. هر بار باز شدن صفحه آن را بهروز میکند. - منبع حقیقت همیشه جدول
Actionاست، نه این ستون. در صورت شک، همان کوئریهای بخش «بررسی صحت داده» ملاک هستند.