1107 lines
25 KiB
Markdown
1107 lines
25 KiB
Markdown
# داکیومنت فنی و اجرایی سامانه گردونه شانس و قرعهکشی
|
|||
|
|
|
||
|
|
**نسخه:** 1.0
|
||
|
|
**نوع سیستم:** Web Application / Mobile First
|
||
|
|
**وضعیت:** MVP Specification
|
||
|
|
**هدف:** ایجاد و مدیریت کمپینهای گردونه شانس با قابلیت تعریف جایزه، تخصیص شانس به کاربران، ثبت قرعهکشی، مدیریت برندگان، ارسال پیامک و گزارشگیری.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. خلاصه محصول
|
||
|
|
|
||
|
|
سامانه از دو بخش اصلی تشکیل میشود:
|
||
|
|
|
||
|
|
1. پنل کاربر و تجربه گردونه شانس
|
||
|
|
2. پنل مدیریت
|
||
|
|
|
||
|
|
مدیر سیستم باید بتواند کمپین ایجاد کند، جوایز و موجودی آنها را تعریف کند، تعداد شانس کاربران را مدیریت کند و نتایج تمام Spinها و برندگان را مشاهده و مدیریت کند.
|
||
|
|
|
||
|
|
کاربر با شماره موبایل وارد سامانه میشود و بر اساس تعداد شانس موجود، گردونه را میچرخاند و نتیجه دریافت میکند.
|
||
|
|
|
||
|
|
> **اصل معماری:** نتیجه Spin همیشه در Backend تعیین و ثبت میشود و انیمیشن گردونه صرفاً نتیجهای را که Backend تعیین کرده نمایش میدهد. Frontend نباید هیچ نقش تصمیمگیرندهای در انتخاب جایزه داشته باشد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. نقشها و دسترسیها
|
||
|
|
|
||
|
|
### 2.1 مدیر (Admin)
|
||
|
|
|
||
|
|
- مدیریت کمپینها
|
||
|
|
- مدیریت جوایز
|
||
|
|
- مدیریت کاربران
|
||
|
|
- مدیریت شانسها
|
||
|
|
- مشاهده Spinها
|
||
|
|
- مشاهده و مدیریت برندگان
|
||
|
|
- مدیریت پیامکها
|
||
|
|
- گزارشگیری
|
||
|
|
- مشاهده Audit Log
|
||
|
|
- مدیریت تنظیمات سیستم
|
||
|
|
|
||
|
|
### 2.2 کاربر (User)
|
||
|
|
|
||
|
|
- ورود با شماره موبایل و OTP
|
||
|
|
- مشاهده تعداد شانس
|
||
|
|
- چرخاندن گردونه
|
||
|
|
- مشاهده نتیجه
|
||
|
|
- مشاهده شانسهای باقیمانده
|
||
|
|
- مشاهده تاریخچه جوایز
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. جریان اصلی کاربر
|
||
|
|
|
||
|
|
```text
|
||
|
|
شماره موبایل
|
||
|
|
↓
|
||
|
|
ارسال OTP
|
||
|
|
↓
|
||
|
|
تأیید شماره
|
||
|
|
↓
|
||
|
|
ایجاد یا شناسایی کاربر
|
||
|
|
↓
|
||
|
|
بررسی کمپین فعال
|
||
|
|
↓
|
||
|
|
محاسبه تعداد شانس
|
||
|
|
↓
|
||
|
|
نمایش گردونه
|
||
|
|
↓
|
||
|
|
درخواست Spin
|
||
|
|
↓
|
||
|
|
انتخاب نتیجه توسط Backend
|
||
|
|
↓
|
||
|
|
کسر یک شانس
|
||
|
|
↓
|
||
|
|
ثبت Transaction
|
||
|
|
↓
|
||
|
|
نمایش انیمیشن گردونه
|
||
|
|
↓
|
||
|
|
نمایش جایزه
|
||
|
|
↓
|
||
|
|
ارسال SMS
|
||
|
|
↓
|
||
|
|
Spin مجدد در صورت وجود شانس
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. احراز هویت
|
||
|
|
|
||
|
|
ورود کاربران با شماره موبایل انجام میشود.
|
||
|
|
|
||
|
|
### APIهای پایه
|
||
|
|
|
||
|
|
- `POST /api/auth/send-otp`
|
||
|
|
- `POST /api/auth/verify-otp`
|
||
|
|
|
||
|
|
### قوانین
|
||
|
|
|
||
|
|
- شماره موبایل فیلد اصلی شناسایی کاربر است.
|
||
|
|
- پس از ارسال شماره، OTP ارسال شود.
|
||
|
|
- پس از تأیید OTP، Token یا Session معتبر ایجاد شود.
|
||
|
|
- محدودیت ارسال مجدد OTP اعمال شود.
|
||
|
|
- تعداد تلاش ناموفق OTP محدود شود.
|
||
|
|
- کاربر بدون شماره موبایل تأییدشده نباید امکان Spin داشته باشد.
|
||
|
|
|
||
|
|
نمونه پیام:
|
||
|
|
|
||
|
|
```text
|
||
|
|
کد ورود شما به گردونه شانس: 48215
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. مدیریت کمپین
|
||
|
|
|
||
|
|
سیستم باید Multi-Campaign باشد.
|
||
|
|
|
||
|
|
### فیلدهای کمپین
|
||
|
|
|
||
|
|
| فیلد | توضیح |
|
||
|
|
|---|---|
|
||
|
|
| id | شناسه یکتا |
|
||
|
|
| title | عنوان کمپین |
|
||
|
|
| slug | شناسه URL |
|
||
|
|
| description | توضیحات |
|
||
|
|
| image / banner | تصویر یا بنر |
|
||
|
|
| logo | لوگو |
|
||
|
|
| start_at | زمان شروع |
|
||
|
|
| end_at | زمان پایان |
|
||
|
|
| status | وضعیت کمپین |
|
||
|
|
| default_chances | تعداد شانس پیشفرض |
|
||
|
|
| rules | قوانین کمپین |
|
||
|
|
| cta_text | متن CTA |
|
||
|
|
| settings | تنظیمات ظاهری |
|
||
|
|
| created_at | زمان ایجاد |
|
||
|
|
| updated_at | زمان ویرایش |
|
||
|
|
|
||
|
|
### وضعیتها
|
||
|
|
|
||
|
|
- `Draft`
|
||
|
|
- `Active`
|
||
|
|
- `Paused`
|
||
|
|
- `Finished`
|
||
|
|
|
||
|
|
### شرط فعال بودن
|
||
|
|
|
||
|
|
فقط کمپینهایی که وضعیت `Active` دارند و زمان فعلی داخل بازه `start_at` تا `end_at` است، قابل استفاده و Spin هستند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. مدیریت جوایز
|
||
|
|
|
||
|
|
برای هر کمپین تعداد نامحدودی Prize قابل تعریف است.
|
||
|
|
|
||
|
|
### فیلدهای جایزه
|
||
|
|
|
||
|
|
| فیلد | توضیح |
|
||
|
|
|---|---|
|
||
|
|
| title | عنوان جایزه |
|
||
|
|
| description | توضیح |
|
||
|
|
| image | تصویر |
|
||
|
|
| stock | موجودی |
|
||
|
|
| stock_type | محدود یا نامحدود |
|
||
|
|
| weight | وزن برد |
|
||
|
|
| sort_order | ترتیب Segment روی گردونه |
|
||
|
|
| segment_color | رنگ Segment |
|
||
|
|
| status | وضعیت |
|
||
|
|
| sms_template | متن پیامک |
|
||
|
|
| expires_at | تاریخ اعتبار |
|
||
|
|
| type | نوع جایزه |
|
||
|
|
|
||
|
|
### نوع جایزه
|
||
|
|
|
||
|
|
- `Physical`
|
||
|
|
- `Discount Code`
|
||
|
|
- `Credit`
|
||
|
|
- `Cash`
|
||
|
|
- `No Prize`
|
||
|
|
- `Other`
|
||
|
|
|
||
|
|
### نوع موجودی
|
||
|
|
|
||
|
|
- `Limited`
|
||
|
|
- `Unlimited`
|
||
|
|
|
||
|
|
نمونه:
|
||
|
|
|
||
|
|
- آیفون — 1 عدد
|
||
|
|
- کارت هدیه — 10 عدد
|
||
|
|
- تخفیف 30٪ — 100 عدد
|
||
|
|
- تخفیف 10٪ — نامحدود
|
||
|
|
- بدون جایزه — نامحدود
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. موتور Spin
|
||
|
|
|
||
|
|
### اصل کلیدی
|
||
|
|
|
||
|
|
Spin باید کاملاً **Backend-Based** باشد.
|
||
|
|
|
||
|
|
Frontend فقط درخواست را ارسال میکند و پس از دریافت نتیجه، Animation را اجرا میکند.
|
||
|
|
|
||
|
|
### ترتیب اجرای Spin
|
||
|
|
|
||
|
|
1. بررسی Authentication Token
|
||
|
|
2. بررسی فعال بودن کمپین
|
||
|
|
3. بررسی تعداد شانس کاربر
|
||
|
|
4. بررسی Prizeهای فعال
|
||
|
|
5. بررسی موجودی Prizeهای محدود
|
||
|
|
6. اعمال قوانین کمپین
|
||
|
|
7. انتخاب نتیجه توسط Backend
|
||
|
|
8. مصرف یک Chance
|
||
|
|
9. کاهش موجودی Prize در صورت محدود بودن
|
||
|
|
10. ثبت Spin
|
||
|
|
11. بازگرداندن نتیجه به Frontend
|
||
|
|
12. اجرای انیمیشن گردونه روی Segment تعیینشده
|
||
|
|
13. ارسال SMS
|
||
|
|
|
||
|
|
تمام عملیات حساس Spin باید داخل Database Transaction و بهصورت Atomic انجام شود.
|
||
|
|
|
||
|
|
در صورت شکست عملیات، نباید Chance یا Prize Inventory بهصورت ناقص تغییر کند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. سیستم Weight و احتمال برد
|
||
|
|
|
||
|
|
هر Prize یک `Weight` دارد و احتمال برد بهصورت نسبی از مجموع Weightهای قابل انتخاب محاسبه میشود.
|
||
|
|
|
||
|
|
مثال:
|
||
|
|
|
||
|
|
| Prize | Weight |
|
||
|
|
|---|---:|
|
||
|
|
| آیفون | 1 |
|
||
|
|
| کارت هدیه | 10 |
|
||
|
|
| تخفیف 30٪ | 50 |
|
||
|
|
| تخفیف 10٪ | 200 |
|
||
|
|
| بدون جایزه | 739 |
|
||
|
|
|
||
|
|
مجموع Weight در این مثال `1000` است.
|
||
|
|
|
||
|
|
الگوریتم نیازی ندارد مجموع Weight برابر 100 یا 1000 باشد. ترکیب زیر نیز معتبر است:
|
||
|
|
|
||
|
|
- A = 10
|
||
|
|
- B = 20
|
||
|
|
- C = 70
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. کنترل موجودی Prize
|
||
|
|
|
||
|
|
اگر موجودی یک Prize به صفر رسید:
|
||
|
|
|
||
|
|
- Prize نباید دوباره انتخاب شود.
|
||
|
|
- Backend باید آن را از Pool قرعهکشی خارج کند.
|
||
|
|
- نمایش Segment غیرفعال میتواند مطابق تنظیمات کمپین ادامه پیدا کند.
|
||
|
|
- کاهش موجودی باید داخل Transaction انجام شود.
|
||
|
|
- در درخواستهای همزمان، تعداد جوایز توزیعشده نباید از موجودی واقعی بیشتر شود.
|
||
|
|
|
||
|
|
### Race Condition
|
||
|
|
|
||
|
|
مثال: فقط یک آیفون باقی مانده و دو کاربر همزمان Spin میکنند.
|
||
|
|
|
||
|
|
نتیجه صحیح این است که فقط یک کاربر آیفون دریافت کند.
|
||
|
|
|
||
|
|
بنابراین انتخاب Prize، کنترل موجودی، کاهش موجودی، مصرف Chance و ثبت Spin باید در یک Transaction با Lock مناسب انجام شود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. سیستم Chance
|
||
|
|
|
||
|
|
برای هر کاربر در هر کمپین تعداد شانس مستقل نگهداری شود.
|
||
|
|
|
||
|
|
### Snapshot / Summary
|
||
|
|
|
||
|
|
- `total_chances`
|
||
|
|
- `used_chances`
|
||
|
|
- `remaining_chances`
|
||
|
|
|
||
|
|
### Chance Ledger
|
||
|
|
|
||
|
|
علاوه بر Summary، تمام تغییرات شانس باید در یک Ledger ثبت شوند.
|
||
|
|
|
||
|
|
نمونه:
|
||
|
|
|
||
|
|
```text
|
||
|
|
+3 ثبتنام در کمپین
|
||
|
|
+2 اضافهشده توسط مدیر
|
||
|
|
-1 Spin
|
||
|
|
-1 Spin
|
||
|
|
+5 جایزه کمپین جانبی
|
||
|
|
```
|
||
|
|
|
||
|
|
Ledger برای Audit، گزارشگیری و بررسی اختلافات ضروری است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11. شانس پیشفرض
|
||
|
|
|
||
|
|
هنگام ساخت کمپین، مدیر تعیین میکند هر کاربر جدید چند شانس دریافت کند.
|
||
|
|
|
||
|
|
مثال:
|
||
|
|
|
||
|
|
```text
|
||
|
|
Default Chances = 3
|
||
|
|
```
|
||
|
|
|
||
|
|
کاربری که برای اولین بار وارد کمپین شود، 3 شانس دریافت میکند.
|
||
|
|
|
||
|
|
مدیر باید بتواند برای کاربران مشخص، شانس را بهصورت دستی اضافه یا کم کند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 12. مدیریت کاربران
|
||
|
|
|
||
|
|
### لیست کاربران
|
||
|
|
|
||
|
|
- شماره موبایل
|
||
|
|
- نام در صورت وجود
|
||
|
|
- تاریخ عضویت
|
||
|
|
- آخرین ورود
|
||
|
|
- تعداد کمپینهای شرکتکرده
|
||
|
|
- وضعیت
|
||
|
|
|
||
|
|
### جزئیات کاربر
|
||
|
|
|
||
|
|
- کمپینهای کاربر
|
||
|
|
- شانس کل
|
||
|
|
- شانس مصرفشده
|
||
|
|
- شانس باقیمانده
|
||
|
|
- تاریخچه Spin
|
||
|
|
- جوایز
|
||
|
|
- تاریخچه تغییر شانس
|
||
|
|
|
||
|
|
### عملیات مدیریتی
|
||
|
|
|
||
|
|
- اضافه کردن شانس
|
||
|
|
- کم کردن شانس
|
||
|
|
- مسدود کردن کاربر
|
||
|
|
- مشاهده جزئیات فعالیت
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 13. UX صفحه اصلی گردونه
|
||
|
|
|
||
|
|
صفحه کاربر باید Mobile First باشد.
|
||
|
|
|
||
|
|
### اجزای اصلی
|
||
|
|
|
||
|
|
- لوگوی برند
|
||
|
|
- نام کمپین
|
||
|
|
- توضیح کوتاه
|
||
|
|
- گردونه
|
||
|
|
- Pointer
|
||
|
|
- تعداد شانس باقیمانده
|
||
|
|
- دکمه «بچرخون»
|
||
|
|
- لینک قوانین
|
||
|
|
- دکمه «جوایز من»
|
||
|
|
|
||
|
|
نمونه نمایش:
|
||
|
|
|
||
|
|
```text
|
||
|
|
3 شانس داری 🎟️
|
||
|
|
|
||
|
|
[ گردونه ]
|
||
|
|
|
||
|
|
بچرخون
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 14. رفتار UI هنگام Spin
|
||
|
|
|
||
|
|
1. کاربر دکمه Spin را لمس میکند.
|
||
|
|
2. دکمه فوراً Disable میشود.
|
||
|
|
3. Loading کوتاه نمایش داده میشود.
|
||
|
|
4. درخواست به Backend ارسال میشود.
|
||
|
|
5. Backend نتیجه را مشخص میکند.
|
||
|
|
6. گردونه چند دور کامل میچرخد.
|
||
|
|
7. گردونه دقیقاً روی Segment برگرداندهشده متوقف میشود.
|
||
|
|
8. تا پایان Animation امکان Spin مجدد وجود ندارد.
|
||
|
|
9. Modal نتیجه نمایش داده میشود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 15. صفحه نتیجه
|
||
|
|
|
||
|
|
### در صورت برد
|
||
|
|
|
||
|
|
```text
|
||
|
|
🎉 تبریک!
|
||
|
|
|
||
|
|
شما برنده شدید
|
||
|
|
|
||
|
|
[ تصویر جایزه ]
|
||
|
|
|
||
|
|
نام جایزه
|
||
|
|
|
||
|
|
کد جایزه: MNT-X7D92
|
||
|
|
```
|
||
|
|
|
||
|
|
CTA:
|
||
|
|
|
||
|
|
- در صورت وجود شانس: «یک شانس دیگه دارم»
|
||
|
|
- در صورت تمام شدن شانس: «مشاهده جوایز من»
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 16. جوایز من
|
||
|
|
|
||
|
|
کاربر باید تاریخچه جوایز خود را مشاهده کند.
|
||
|
|
|
||
|
|
هر Card شامل:
|
||
|
|
|
||
|
|
- تصویر
|
||
|
|
- عنوان
|
||
|
|
- تاریخ برد
|
||
|
|
- کد جایزه
|
||
|
|
- وضعیت
|
||
|
|
- تاریخ انقضا
|
||
|
|
|
||
|
|
### وضعیتها
|
||
|
|
|
||
|
|
- `New`
|
||
|
|
- `Used`
|
||
|
|
- `Delivered`
|
||
|
|
- `Expired`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 17. ساختار پنل مدیریت
|
||
|
|
|
||
|
|
منوی اصلی پیشنهادی:
|
||
|
|
|
||
|
|
```text
|
||
|
|
Dashboard
|
||
|
|
Campaigns
|
||
|
|
Prizes
|
||
|
|
Users
|
||
|
|
Spins
|
||
|
|
Winners
|
||
|
|
Chance Management
|
||
|
|
SMS
|
||
|
|
Reports
|
||
|
|
Settings
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 18. Dashboard
|
||
|
|
|
||
|
|
### KPIها
|
||
|
|
|
||
|
|
- تعداد کاربران
|
||
|
|
- کاربران امروز
|
||
|
|
- تعداد کل Spin
|
||
|
|
- Spin امروز
|
||
|
|
- تعداد برندگان
|
||
|
|
- تعداد جوایز توزیعشده
|
||
|
|
- شانسهای مصرفشده
|
||
|
|
- موجودی جوایز
|
||
|
|
|
||
|
|
### نمودارها
|
||
|
|
|
||
|
|
- Spin بر اساس روز
|
||
|
|
- ثبتنام بر اساس روز
|
||
|
|
- توزیع جوایز
|
||
|
|
|
||
|
|
### جدول
|
||
|
|
|
||
|
|
آخرین برندگان نیز نمایش داده شوند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 19. صفحه Spinها
|
||
|
|
|
||
|
|
تمام چرخشها در این بخش ثبت و قابل مشاهده باشند.
|
||
|
|
|
||
|
|
### ستونها
|
||
|
|
|
||
|
|
| ستون | توضیح |
|
||
|
|
|---|---|
|
||
|
|
| ID | شناسه Spin |
|
||
|
|
| Mobile | شماره موبایل |
|
||
|
|
| Campaign | کمپین |
|
||
|
|
| Prize | جایزه |
|
||
|
|
| Date | تاریخ |
|
||
|
|
| Time | ساعت |
|
||
|
|
| Status | وضعیت |
|
||
|
|
| IP | IP درخواست |
|
||
|
|
| User Agent | Device / Browser اطلاعاتی |
|
||
|
|
|
||
|
|
### قابلیتها
|
||
|
|
|
||
|
|
- Search
|
||
|
|
- Filter
|
||
|
|
- Pagination
|
||
|
|
- Export Excel
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 20. صفحه Winners
|
||
|
|
|
||
|
|
### اطلاعات
|
||
|
|
|
||
|
|
- شماره موبایل
|
||
|
|
- جایزه
|
||
|
|
- کمپین
|
||
|
|
- زمان برد
|
||
|
|
- کد جایزه
|
||
|
|
- وضعیت تحویل
|
||
|
|
|
||
|
|
### وضعیت جایزه فیزیکی
|
||
|
|
|
||
|
|
```text
|
||
|
|
جدید → تماس گرفته شد → تحویل شد
|
||
|
|
```
|
||
|
|
|
||
|
|
### وضعیت کد تخفیف
|
||
|
|
|
||
|
|
```text
|
||
|
|
فعال → استفاده شد → منقضی شد
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 21. سیستم کد جایزه
|
||
|
|
|
||
|
|
برای Prizeهایی که نیاز به Code دارند، سیستم باید Unique Code تولید کند.
|
||
|
|
|
||
|
|
نمونه:
|
||
|
|
|
||
|
|
```text
|
||
|
|
MNT-8F4KD2
|
||
|
|
```
|
||
|
|
|
||
|
|
قوانین:
|
||
|
|
|
||
|
|
- هر Code فقط متعلق به یک Winner باشد.
|
||
|
|
- امکان Import مجموعه Codeها در نسخه توسعهیافته پیشبینی شود.
|
||
|
|
- نمونه: فروشگاه 500 کد تخفیف آماده دارد و آنها را وارد سامانه میکند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 22. SMS Service
|
||
|
|
|
||
|
|
SMS باید بهصورت Service مستقل پیادهسازی شود تا Provider در آینده قابل تعویض باشد.
|
||
|
|
|
||
|
|
### Templateهای پایه
|
||
|
|
|
||
|
|
- OTP
|
||
|
|
- Winning
|
||
|
|
- No Prize
|
||
|
|
- Prize Reminder
|
||
|
|
|
||
|
|
نمونه پیام:
|
||
|
|
|
||
|
|
```text
|
||
|
|
تبریک! شما برنده کارت هدیه ۱ میلیون تومانی شدید. کد پیگیری: MNT-72F5
|
||
|
|
```
|
||
|
|
|
||
|
|
### SMS Log
|
||
|
|
|
||
|
|
تمام SMSها باید Log شوند.
|
||
|
|
|
||
|
|
| فیلد | توضیح |
|
||
|
|
|---|---|
|
||
|
|
| mobile | شماره گیرنده |
|
||
|
|
| template | Template |
|
||
|
|
| message | متن ارسالشده |
|
||
|
|
| provider | سرویسدهنده |
|
||
|
|
| created_at | زمان |
|
||
|
|
| status | وضعیت |
|
||
|
|
| provider_response | پاسخ Provider |
|
||
|
|
|
||
|
|
**قانون مهم:** Fail شدن SMS نباید باعث Rollback شدن Spin موفق شود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 23. جلوگیری از Spin تکراری
|
||
|
|
|
||
|
|
برای درخواست Spin از `Idempotency Key` استفاده شود.
|
||
|
|
|
||
|
|
هر درخواست Spin باید یک شناسه یکتا داشته باشد.
|
||
|
|
|
||
|
|
اگر همان Request دوباره ارسال شد:
|
||
|
|
|
||
|
|
- نتیجه قبلی برگردانده شود.
|
||
|
|
- Chance مجدد مصرف نشود.
|
||
|
|
- Prize مجدد توزیع نشود.
|
||
|
|
- Spin جدید ثبت نشود.
|
||
|
|
|
||
|
|
نمونه:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"idempotency_key": "uuid"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 24. امنیت و Anti-Fraud
|
||
|
|
|
||
|
|
حداقل کنترلهای لازم:
|
||
|
|
|
||
|
|
- OTP Verification
|
||
|
|
- Rate Limiting
|
||
|
|
- Authentication Token / Session
|
||
|
|
- Server-Side Spin
|
||
|
|
- Idempotency
|
||
|
|
- IP Logging
|
||
|
|
- User Agent Logging
|
||
|
|
- محدودیت درخواست
|
||
|
|
- جلوگیری از Concurrent Spin
|
||
|
|
- Transaction برای Prize Inventory
|
||
|
|
- Admin Audit Log
|
||
|
|
|
||
|
|
هیچ مقدار حساسی مانند موارد زیر نباید توسط Frontend تعیین شود:
|
||
|
|
|
||
|
|
- Chance
|
||
|
|
- Prize ID
|
||
|
|
- Weight
|
||
|
|
- Winner
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 25. Admin Audit Log
|
||
|
|
|
||
|
|
تمام تغییرات مهم مدیر باید ثبت شوند.
|
||
|
|
|
||
|
|
نمونه:
|
||
|
|
|
||
|
|
```text
|
||
|
|
Admin X
|
||
|
|
در تاریخ X
|
||
|
|
برای کاربر 0912...
|
||
|
|
5 شانس اضافه کرد.
|
||
|
|
```
|
||
|
|
|
||
|
|
یا:
|
||
|
|
|
||
|
|
```text
|
||
|
|
احتمال جایزه «کارت هدیه»
|
||
|
|
از 10 به 20 تغییر کرد.
|
||
|
|
```
|
||
|
|
|
||
|
|
Audit Log برای بررسی اختلافات و Trace تغییرات ضروری است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 26. مدل پیشنهادی دیتابیس
|
||
|
|
|
||
|
|
### admins
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `name`
|
||
|
|
- `mobile`
|
||
|
|
- `email`
|
||
|
|
- `password`
|
||
|
|
- `status`
|
||
|
|
- `created_at`
|
||
|
|
|
||
|
|
### users
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `mobile`
|
||
|
|
- `name`
|
||
|
|
- `status`
|
||
|
|
- `last_login_at`
|
||
|
|
- `created_at`
|
||
|
|
|
||
|
|
### campaigns
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `title`
|
||
|
|
- `slug`
|
||
|
|
- `description`
|
||
|
|
- `image`
|
||
|
|
- `banner`
|
||
|
|
- `logo`
|
||
|
|
- `start_at`
|
||
|
|
- `end_at`
|
||
|
|
- `status`
|
||
|
|
- `default_chances`
|
||
|
|
- `rules`
|
||
|
|
- `cta_text`
|
||
|
|
- `settings`
|
||
|
|
- `created_at`
|
||
|
|
- `updated_at`
|
||
|
|
|
||
|
|
### prizes
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `campaign_id`
|
||
|
|
- `title`
|
||
|
|
- `description`
|
||
|
|
- `image`
|
||
|
|
- `type`
|
||
|
|
- `stock_type`
|
||
|
|
- `initial_stock`
|
||
|
|
- `remaining_stock`
|
||
|
|
- `weight`
|
||
|
|
- `status`
|
||
|
|
- `sort_order`
|
||
|
|
- `segment_color`
|
||
|
|
- `expires_at`
|
||
|
|
- `sms_template`
|
||
|
|
- `created_at`
|
||
|
|
|
||
|
|
### user_campaigns
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `user_id`
|
||
|
|
- `campaign_id`
|
||
|
|
- `total_chances`
|
||
|
|
- `used_chances`
|
||
|
|
- `created_at`
|
||
|
|
|
||
|
|
### chance_transactions
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `user_id`
|
||
|
|
- `campaign_id`
|
||
|
|
- `amount`
|
||
|
|
- `type`
|
||
|
|
- `reference_type`
|
||
|
|
- `reference_id`
|
||
|
|
- `description`
|
||
|
|
- `created_by`
|
||
|
|
- `created_at`
|
||
|
|
|
||
|
|
### spins
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `user_id`
|
||
|
|
- `campaign_id`
|
||
|
|
- `prize_id`
|
||
|
|
- `idempotency_key`
|
||
|
|
- `ip`
|
||
|
|
- `user_agent`
|
||
|
|
- `created_at`
|
||
|
|
|
||
|
|
### user_prizes
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `user_id`
|
||
|
|
- `campaign_id`
|
||
|
|
- `prize_id`
|
||
|
|
- `spin_id`
|
||
|
|
- `code`
|
||
|
|
- `status`
|
||
|
|
- `won_at`
|
||
|
|
- `redeemed_at`
|
||
|
|
|
||
|
|
### sms_logs
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `user_id`
|
||
|
|
- `mobile`
|
||
|
|
- `type`
|
||
|
|
- `message`
|
||
|
|
- `provider`
|
||
|
|
- `status`
|
||
|
|
- `provider_response`
|
||
|
|
- `created_at`
|
||
|
|
|
||
|
|
### admin_logs
|
||
|
|
|
||
|
|
- `id`
|
||
|
|
- `admin_id`
|
||
|
|
- `action`
|
||
|
|
- `entity_type`
|
||
|
|
- `entity_id`
|
||
|
|
- `old_value`
|
||
|
|
- `new_value`
|
||
|
|
- `ip`
|
||
|
|
- `created_at`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 27. APIهای اصلی کاربر
|
||
|
|
|
||
|
|
### Authentication
|
||
|
|
|
||
|
|
```http
|
||
|
|
POST /api/auth/send-otp
|
||
|
|
POST /api/auth/verify-otp
|
||
|
|
```
|
||
|
|
|
||
|
|
### Campaign
|
||
|
|
|
||
|
|
```http
|
||
|
|
GET /api/campaigns/{slug}
|
||
|
|
GET /api/campaigns/{slug}/wheel
|
||
|
|
```
|
||
|
|
|
||
|
|
### User
|
||
|
|
|
||
|
|
```http
|
||
|
|
GET /api/me
|
||
|
|
GET /api/me/chances
|
||
|
|
GET /api/me/prizes
|
||
|
|
```
|
||
|
|
|
||
|
|
### Spin
|
||
|
|
|
||
|
|
```http
|
||
|
|
POST /api/campaigns/{id}/spin
|
||
|
|
```
|
||
|
|
|
||
|
|
### نمونه Request
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"idempotency_key": "uuid"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### نمونه Response
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"success": true,
|
||
|
|
"spin_id": 1251,
|
||
|
|
"prize": {
|
||
|
|
"id": 7,
|
||
|
|
"title": "تخفیف 30 درصد",
|
||
|
|
"segment": 4,
|
||
|
|
"code": "MNT-7F92K"
|
||
|
|
},
|
||
|
|
"remaining_chances": 2
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 28. APIهای پنل مدیریت
|
||
|
|
|
||
|
|
- Campaign CRUD
|
||
|
|
- Prize CRUD
|
||
|
|
- User List / Detail
|
||
|
|
- Add / Remove Chance
|
||
|
|
- Spin List
|
||
|
|
- Winner List
|
||
|
|
- Prize Status Update
|
||
|
|
- Reports
|
||
|
|
- SMS Logs
|
||
|
|
- Admin Logs
|
||
|
|
|
||
|
|
تمام APIهای مدیریت باید Authentication و Authorization مستقل Admin داشته باشند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 29. خطاها و Edge Caseها
|
||
|
|
|
||
|
|
Frontend باید برای موارد زیر UI مناسب داشته باشد:
|
||
|
|
|
||
|
|
- شانس کافی نیست.
|
||
|
|
- کمپین هنوز شروع نشده.
|
||
|
|
- کمپین تمام شده.
|
||
|
|
- کمپین موقتاً متوقف است.
|
||
|
|
- درخواست نامعتبر است.
|
||
|
|
- اینترنت قطع شده است.
|
||
|
|
- درخواست Spin تکراری است.
|
||
|
|
- خطای سرور رخ داده است.
|
||
|
|
|
||
|
|
در خطای شبکه، کاربر نباید بدون مشخص شدن وضعیت Request قبلی، Spin جدید ایجاد کند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 30. تنظیمات ظاهری کمپین
|
||
|
|
|
||
|
|
ساختار `settings` باید برای توسعه آینده آماده باشد و حداقل موارد زیر را پشتیبانی کند:
|
||
|
|
|
||
|
|
- Logo
|
||
|
|
- Background
|
||
|
|
- Primary Color
|
||
|
|
- Secondary Color
|
||
|
|
- متن بالای گردونه
|
||
|
|
- متن دکمه
|
||
|
|
- تصویر Header
|
||
|
|
- قوانین
|
||
|
|
- Celebration Effect
|
||
|
|
|
||
|
|
بنابراین هر کمپین میتواند ظاهر برند مستقل خود را داشته باشد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 31. Responsive Design
|
||
|
|
|
||
|
|
### اولویت تجربه کاربر
|
||
|
|
|
||
|
|
1. Mobile
|
||
|
|
2. Tablet
|
||
|
|
3. Desktop
|
||
|
|
|
||
|
|
گردونه در موبایل باید بدون Horizontal Scroll و بهصورت کامل قابل مشاهده باشد.
|
||
|
|
|
||
|
|
پنل مدیریت Desktop First ولی Responsive باشد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 32. گزارشها
|
||
|
|
|
||
|
|
### گزارشهای MVP
|
||
|
|
|
||
|
|
- تعداد کاربران
|
||
|
|
- تعداد Spin
|
||
|
|
- تعداد Spin موفق
|
||
|
|
- تعداد جوایز توزیعشده
|
||
|
|
- تعداد هر Prize
|
||
|
|
- شانس مصرفشده
|
||
|
|
- کاربران بدون Spin
|
||
|
|
- کاربران با شانس باقیمانده
|
||
|
|
- روند روزانه مشارکت
|
||
|
|
|
||
|
|
### فیلترها
|
||
|
|
|
||
|
|
- از تاریخ
|
||
|
|
- تا تاریخ
|
||
|
|
- کمپین
|
||
|
|
- جایزه
|
||
|
|
|
||
|
|
### خروجی
|
||
|
|
|
||
|
|
- Excel
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 33. قابلیتهای خارج از MVP اما معماریپذیر
|
||
|
|
|
||
|
|
معماری باید امکان افزودن موارد زیر را حفظ کند:
|
||
|
|
|
||
|
|
- دریافت شانس با خرید
|
||
|
|
- اتصال API فروشگاه
|
||
|
|
- QR Code
|
||
|
|
- Referral
|
||
|
|
- معرفی دوست
|
||
|
|
- Scratch Card
|
||
|
|
- Mystery Box
|
||
|
|
- مسابقه
|
||
|
|
- Quiz
|
||
|
|
- Daily Spin
|
||
|
|
- Birthday Spin
|
||
|
|
- Coupon System
|
||
|
|
- اتصال CRM
|
||
|
|
- Webhook
|
||
|
|
- Multi-Brand
|
||
|
|
- White Label
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 34. محدوده MVP
|
||
|
|
|
||
|
|
نسخه اول شامل موارد زیر است:
|
||
|
|
|
||
|
|
1. ورود با موبایل و OTP
|
||
|
|
2. ایجاد کمپین
|
||
|
|
3. مدیریت کمپین
|
||
|
|
4. تعریف جوایز
|
||
|
|
5. موجودی جایزه
|
||
|
|
6. Weight جایزه
|
||
|
|
7. تعریف تعداد شانس
|
||
|
|
8. مدیریت کاربران
|
||
|
|
9. اضافه / کم کردن دستی شانس
|
||
|
|
10. گردونه کاربر
|
||
|
|
11. Spin سمت Backend
|
||
|
|
12. کسر شانس
|
||
|
|
13. ثبت نتیجه
|
||
|
|
14. مدیریت موجودی
|
||
|
|
15. نمایش نتیجه
|
||
|
|
16. ارسال SMS
|
||
|
|
17. صفحه جوایز من
|
||
|
|
18. لیست Spinها
|
||
|
|
19. لیست برندگان
|
||
|
|
20. داشبورد آماری
|
||
|
|
21. گزارش اولیه
|
||
|
|
22. Audit Log
|
||
|
|
23. Anti Duplicate Spin
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 35. Acceptance Criteria
|
||
|
|
|
||
|
|
| ID | معیار پذیرش |
|
||
|
|
|---|---|
|
||
|
|
| AC-01 | کاربر بدون شماره موبایل تأییدشده امکان Spin ندارد. |
|
||
|
|
| AC-02 | کاربری که صفر شانس دارد امکان Spin ندارد. |
|
||
|
|
| AC-03 | هر Spin دقیقاً یک Chance مصرف میکند. |
|
||
|
|
| AC-04 | Refresh صفحه باعث دریافت Chance جدید نمیشود. |
|
||
|
|
| AC-05 | نتیجه Spin فقط توسط Backend مشخص میشود. |
|
||
|
|
| AC-06 | کاربر نمیتواند Prize ID موردنظر خودش را برای Backend ارسال کند و با آن نتیجه را تعیین کند. |
|
||
|
|
| AC-07 | Prize بدون موجودی قابل برندهشدن نیست. |
|
||
|
|
| AC-08 | در درخواست همزمان، موجودی Prize منفی نمیشود و بیش از موجودی واقعی توزیع نمیشود. |
|
||
|
|
| AC-09 | ارسال مجدد یک Spin Request باعث مصرف Chance دوم نمیشود. |
|
||
|
|
| AC-10 | تمام Spinها قابل Audit هستند. |
|
||
|
|
| AC-11 | تغییر Chance توسط Admin در Log ثبت میشود. |
|
||
|
|
| AC-12 | نتیجه برد حتی در صورت Fail شدن SMS در سیستم ذخیره میماند. |
|
||
|
|
| AC-13 | کاربر پس از ورود مجدد میتواند جوایز قبلی خود را مشاهده کند. |
|
||
|
|
| AC-14 | کمپین خارج از بازه زمانی قابل Spin نیست. |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 36. معماری پیشنهادی
|
||
|
|
|
||
|
|
```text
|
||
|
|
┌─────────────────────┐
|
||
|
|
│ User Frontend │
|
||
|
|
│ Mobile First UI │
|
||
|
|
└──────────┬──────────┘
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
┌─────────────────────┐
|
||
|
|
│ Backend API │
|
||
|
|
├─────────────────────┤
|
||
|
|
│ Auth / Campaign │
|
||
|
|
│ User / Chance │
|
||
|
|
│ Spin Engine │
|
||
|
|
│ Winner / Reports │
|
||
|
|
└──────────┬──────────┘
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
┌─────────────────────┐
|
||
|
|
│ Business Logic │
|
||
|
|
│ / Spin Engine │
|
||
|
|
└──────────┬──────────┘
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
┌─────────────────────┐
|
||
|
|
│ Database │
|
||
|
|
└─────────────────────┘
|
||
|
|
|
||
|
|
┌──────────────┐
|
||
|
|
│ SMS Service │
|
||
|
|
└──────┬───────┘
|
||
|
|
│
|
||
|
|
Queue / Job System
|
||
|
|
|
||
|
|
┌──────────────┐
|
||
|
|
│ Admin Panel │
|
||
|
|
└──────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
منطق قرعهکشی باید بهصورت Service مستقل داخل Backend پیادهسازی شود تا الگوریتم در آینده بدون تغییر Frontend قابل توسعه یا تعویض باشد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 37. قاعده حیاتی اجرای Spin
|
||
|
|
|
||
|
|
### ترتیب صحیح
|
||
|
|
|
||
|
|
```text
|
||
|
|
Request Spin
|
||
|
|
↓
|
||
|
|
Backend Decision
|
||
|
|
↓
|
||
|
|
Database Commit
|
||
|
|
↓
|
||
|
|
Response
|
||
|
|
↓
|
||
|
|
Wheel Animation
|
||
|
|
```
|
||
|
|
|
||
|
|
### ترتیب ممنوع
|
||
|
|
|
||
|
|
```text
|
||
|
|
Wheel Animation
|
||
|
|
↓
|
||
|
|
Frontend Random
|
||
|
|
↓
|
||
|
|
Save Result
|
||
|
|
```
|
||
|
|
|
||
|
|
تمام قوانین Chance، Inventory و Winner باید در Backend کنترل شوند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 38. Definition of Done برای MVP
|
||
|
|
|
||
|
|
نسخه MVP زمانی آماده تحویل است که:
|
||
|
|
|
||
|
|
- تمام قابلیتهای Scope MVP پیادهسازی شده باشند.
|
||
|
|
- Spin فقط از Backend نتیجه بگیرد.
|
||
|
|
- Idempotency برای Spin فعال باشد.
|
||
|
|
- Race Condition مربوط به Inventory کنترل شده باشد.
|
||
|
|
- Chance Ledger و Audit Log ثبت شوند.
|
||
|
|
- SMS Failure باعث Rollback Spin نشود.
|
||
|
|
- Acceptance Criteriaهای AC-01 تا AC-14 پاس شوند.
|
||
|
|
- تجربه Mobile First گردونه بدون Horizontal Scroll باشد.
|
||
|
|
- پنل مدیریت برای Campaign، Prize، User، Spin، Winner و Report قابل استفاده باشد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 39. نکات پیادهسازی برای تیم توسعه
|
||
|
|
|
||
|
|
1. منطق Spin را داخل یک Service مستقل نگه دارید.
|
||
|
|
2. تغییر Chance و Inventory باید قابل Audit باشد.
|
||
|
|
3. هیچ نتیجهای از Frontend قابل اعتماد نیست.
|
||
|
|
4. `idempotency_key` باید برای هر Spin یکتا و قابل ردیابی باشد.
|
||
|
|
5. عملیات Spin شامل مصرف Chance، انتخاب Prize، کنترل Inventory و ثبت نتیجه باید Atomic باشد.
|
||
|
|
6. ارسال SMS از فرآیند اصلی Spin جدا و ترجیحاً Queue-based باشد.
|
||
|
|
7. معماری برای Campaignهای متعدد و قابلیتهای Gamification آینده قابل توسعه نگه داشته شود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**مرجع اصلی این سند:** مشخصات اولیه سامانه گردونه شانس و قرعهکشی، نسخه 1.0.
|