fix sms
This commit is contained in:
1 parent
1dea3509a7
commit
e3a6d1f9d4
42 files changed
+33086
-15779
No files matched your search
@@ -0,0 +1,138 @@
|
||||
# بازمحاسبهٔ «آخرین بازدید» مشتریان
|
||||
|
||||
## چرا؟
|
||||
|
||||
فیلد `Customer.last_bazdid_dt` یعنی «آخرین بازدیدی که برای مشتری انجام شده» و صفحهٔ
|
||||
`/panel/entezar/` بر پایهٔ آن مشتریانِ **معوق** (بیش از N ماه بازدید نشده) را لیست
|
||||
میکند.
|
||||
|
||||
قاعدهٔ درست (منبع حقیقت): این مقدار باید برابر **بزرگترین `finishDateTime` میان
|
||||
اقدامهای `actionType='bazdid'` و `status='finish'` همان مشتری** باشد — دقیقاً همان
|
||||
چیزی که ویوهای `customerNewAction` و `customerEditAction` مینویسند:
|
||||
|
||||
```python
|
||||
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:
|
||||
|
||||
```bash
|
||||
.venv/Scripts/python.exe manage.py recalc_last_bazdid --dry-run
|
||||
```
|
||||
|
||||
از Windows:
|
||||
|
||||
```powershell
|
||||
.venv\Scripts\python.exe manage.py recalc_last_bazdid --dry-run
|
||||
```
|
||||
|
||||
### ۲) اجرای واقعی
|
||||
|
||||
```bash
|
||||
.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
|
||||
|
||||
**اول از دیتابیس بکاپ بگیرید**، سپس:
|
||||
|
||||
```bash
|
||||
python manage.py recalc_last_bazdid --dry-run
|
||||
python manage.py recalc_last_bazdid
|
||||
```
|
||||
|
||||
دستور تراکنشی است و در صورت خطا، هیچ تغییری کامیت نمیشود.
|
||||
|
||||
## بررسی صحت داده (SQL، پیش از اجرا)
|
||||
|
||||
اگر میخواهید بدون دستور، وضعیت را بسنجید:
|
||||
|
||||
```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` است، نه این ستون. در صورت شک، همان کوئریهای بخش
|
||||
«بررسی صحت داده» ملاک هستند.
|
||||
```
|
||||
Reference in new issue
Block a user