# داکیومنت فنی و اجرایی سامانه گردونه شانس و قرعه‌کشی **نسخه:** 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.