سلام دوست عزیز😃👋

به «مسابقه استخدامی ایران‌سرور» خوش آمدی!

هرگونه ارتباط با سایر شرکت‌کنندگان و یا استفاده از ابزارهای تولید کد، مثل ChatGPT و... در مسابقات کوئرا ممنوع است و بعد از شناسایی از لیست شرکت‌کنندگان مسابقه حذف می‌شوید.

لینک‌های مفید برای شرکت در مسابقه:

سوالات و مشکلات خودتان را می‌توانید از طریق قسمت «سوال بپرسید» با ما در میان بگذارید.

سوالات «چالش‌های نام ایران‌سرور»، «میزگرد بزرگ» و «معین سخنور» مربوط به تکنولوژی PHP و سوالات «میعین فیراری» و «ایران‌گیت» مربوط به تکنولوژیLaravel است.

موفق باشید و بهتون خوش بگذره 😉✌

لیست سوالات را می‌توانید از نوار سمت راست این صفحه مشاهده کنید.

ایران‌گیت


پس از تلاش‌‌های فراوان معین بالاخره به سرزمین رویا‌هایش ایران‌سرور (IranServer) رسید! از آن‌جایی که ایران‌سرور قصد دارد به زودی سرویس‌های ابری جدیدی را به کاربرانش ارائه دهد، معین قرار است همراه با سایر ایران‌سروریون به توسعه پنل کاربری جدیدی با عنوان "ایران‌گیت" مشغول شود که بر پایه لاراول ۱۲ است که به تازگی معرفی شده است!

از آن‌جایی که ایران‌گیت به زودی قرار است به کاربران معرفی شود، در این سوال شما نیز قرار است تا به کمک معین و ایران‌سروریون بروید تا با استفاده از لاراول ۱۲ و استارتر کیت Jetstream بخش‌هایی از پنل کاربری ایران‌گیت را پیاده‌سازی کنید.

تصویر سوال ایران‌گیت

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

ساختار فایل‌ها
irangate
├── app
│   ├── Http
│   │   ├── Middleware
│   │   │   └── CheckFeatureAccess.php
│   ├── Jobs
│   │   ├── DowngradePendingInvoicesJob.php
│   │   └── ExpireSubscriptionsJob.php
│   ├── Models
│   │   ├── Coupon.php
│   │   ├── Feature.php
│   │   ├── Invoice.php
│   │   ├── Log.php
│   │   ├── Plan.php
│   │   ├── Referral.php
│   │   ├── Subscription.php
│   │   ├── SubscriptionLog.php
│   │   └── User.php
│   ├── Observers
│   │   └── SubscriptionObserver.php
│   ├── Policies
│   │   ├── FeaturePolicy.php
│   │   └── SubscriptionPolicy.php
│   ├── Providers
│   │   ├── AppServiceProvider.php
│   │   └── AuthServiceProvider.php
├── bootstrap
│   ├── app.php
│   └── providers.php
├── database
│   ├── factories
│   │   ├── CouponFactory.php
│   │   ├── FeatureFactory.php
│   │   ├── InvoiceFactory.php
│   │   ├── LogFactory.php
│   │   ├── PlanFactory.php
│   │   ├── ReferralFactory.php
│   │   ├── SubscriptionFactory.php
│   │   ├── SubscriptionLogFactory.php
│   │   └── UserFactory.php
│   ├── migrations
│   │   ├── 2025_02_26_164235_create_plans_table.php
│   │   ├── 2025_02_26_164344_create_features_table.php
│   │   ├── 2025_02_26_164408_create_subscriptions_table.php
│   │   ├── 2025_02_26_164432_create_subscription_logs_table.php
│   │   ├── 2025_02_26_164501_create_referrals_table.php
│   │   ├── 2025_02_26_164538_create_coupons_table.php
│   │   ├── 2025_02_26_164610_create_invoices_table.php
│   │   ├── 2025_02_26_164639_create_logs_table.php
│   │   └── 2025_02_26_173343_create_plan_feature_table.php
│   ├── seeders
│   │   ├── CouponSeeder.php
│   │   ├── DatabaseSeeder.php
│   │   ├── FeatureSeeder.php
│   │   ├── InvoiceSeeder.php
│   │   ├── LogSeeder.php
│   │   ├── PlanSeeder.php
│   │   ├── ReferralSeeder.php
│   │   ├── SubscriptionLogSeeder.php
│   │   ├── SubscriptionSeeder.php
│   │   └── UserSeeder.php
├── routes
│   └── console.php
├── tests
├── README.md
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
├── server.php
└── webpack.mix.js
Plain text
راه‌اندازی پروژه

برای اجرای پروژه، باید php، composer و npm را از قبل نصب کرده باشید.

  • ابتدا پروژه‌ی اولیه را دانلود و از حالت فشرده خارج کنید.
  • دستور composer install را در پوشه‌ی اصلی پروژه برای نصب نیازمندی‌ها اجرا کنید.
  • دستور npm install را در پوشه‌ی اصلی پروژه برای نصب نیازمندی‌ها اجرا کنید. (توجه کنید که این پروژه از Tailwindcss استفاده می‌کند)
  • دستور npm run dev را در مسیر پوشه اصلی پروژه اجرا کنید. در صورتی که این دستور را اجرا نکنید، نمی‌توانید ویو‌های ساخته شده با Tailwindcss را مشاهده کنید.
  • برای اجرای مایگریشن‌ها از دستور php artisan migrate استفاده کنید.
  • برای اجرای تست‌های نمونه، می‌توانید از دستور php artisan test استفاده کنید.

شما در این سوال قرار است تا در پیاده‌سازی یک سیستم پنل کاربری بر پایه Jetstream که امکانات شخصی‌سازی شده‌ای دارد به معین و ایران‌سروریون کمک کنید. توجه داشته باشید که برخی مواردی که در این سوال از شما خواسته شده تا پیاده‌سازی کنید ممکن است تا در این سوال به صورت عملیاتی مورد استفاده قرار نگیرند، اما در داوری سوال مورد ارزیابی قرار خواهند گرفت. توضیح مواردی که باید پیاده‌سازی شوند به شکل زیر می‌باشد:

تصویر اول سوال ایران‌گیت

معرفی کلی پروژه ایران‌گیت (IranGate) و موارد پیاده‌سازی شده🔗

در این بخش به معرفی پروژه ایران‌گیت (IranGate) خواهیم پرداخت. بخش‌هایی از ایران‌گیت در این پروژه اولیه این سوال از قبل پیاده‌سازی شده‌اند و نیازی به پیاده‌سازی دوباره ندارند. پایه ایران‌گیت همانطور که پیش‌تر گفته شد بر اساس استارتر کیت Jetstream می‌باشد و امکانات پیشفرضی که این استارتر کیت در اختیار توسعه‌دهندگان قرار می‌دهد را شامل می‌شود. موارد پیاده‌سازی شده در پروژه به شکل زیر هستند:

معرفی امکانات پیش‌فرض بر پایه Jetstream

این پروژه بر اساس امکانات پیش‌فرض پیاده‌سازی شده در Jetstream از ویو‌ها، کامپوننت‌ها، روت‌ها و ... این استارتر کیت ارث‌بری می‌کند. صفحه پیش‌فرض پروژه، صفحه پیش‌فرض لاراول ۱۲ می‌باشد که به شما برای شروع حل این سوال خوش‌آمد می‌گوید:

تصویر اول سوال ایران‌گیت

بخشی از روت‌های (Routes) پیاده‌سازی شده در پروژه نیز به مانند روت‌های پیشفرض Jetstream به شکل زیر تعریف شده‌اند:

extensionFromNameroutes/web.php
<?php

use App\Http\Controllers\FeatureController;
use App\Http\Controllers\InvoiceController;
use App\Http\Controllers\PlanController;
use App\Http\Controllers\SubscriptionLogController;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::middleware([
    'auth:sanctum',
    config('jetstream.auth_session'),
    'verified',
])->group(function () {
    Route::get('/dashboard', function () {
        return view('dashboard');
    })->name('dashboard');
});
PHP

شما می‌توانید همانند تصاویر زیر از امکانات پیشفرض پیاده‌سازی شده در پروژه برای ورود، عضویت، فراموشی رمز عبور و پنل کاربری پیشفرض استفاده کنید:

تصویر دوم سوال ایران‌گیت

تصویر سوم سوال ایران‌گیت

تصویر چهارم سوال ایران‌گیت

معرفی کنترلر FeatureController

این کنترلر برای مدیریت دسترسی به ویژگی‌های مختلف استفاده می‌شود. میدلور CheckFeatureAccess که قرار است توسط شما پیاده‌سازی شود، بررسی می‌کند که کاربر دسترسی به یک ویژگی خاص با عنوان featureSlug را دارد یا نه و پیامی مبنی بر مجوز دسترسی به آن ویژگی برمی‌گرداند. کنترلر و روت (Route) مربوط به آن به شکل زیر از قبل پیاده‌سازی شده‌اند:

extensionFromNameapp/Http/Controllers/FeatureController.php
<?php

namespace App\Http\Controllers;

class FeatureController extends Controller
{
    public function access($featureSlug)
    {
        return response('Access granted to feature: ' . $featureSlug, 200);
    }
}
PHP
extensionFromNameroutes/web.php
<?php

use App\Http\Controllers\FeatureController;
use App\Http\Controllers\InvoiceController;
use App\Http\Controllers\PlanController;
use App\Http\Controllers\SubscriptionLogController;
use Illuminate\Support\Facades\Route;

Route::get('/feature/{featureSlug}', [FeatureController::class, 'access'])->middleware('feature.access')->name('feature');
PHP
معرفی کنترلر InvoiceController و ویو‌ی invoices.index

تصویر پنجم سوال ایران‌گیت

این کنترلر برای نمایش صورتحساب‌های کاربران است. متد index تمام فاکتورها را از طریق ارتباط با مدل Invoice و برای کاربر وارد شده (auth()->user()) بدست می‌آورد و در ویوی invoices.index به نمایش می‌گذارد. کنترلر مربوط به آن به شکل زیر از قبل پیاده‌سازی شده است:

extensionFromNameapp/Http/Controllers/InvoiceController.php
<?php

namespace App\Http\Controllers;

class InvoiceController extends Controller
{
    public function index()
    {
        $invoices = auth()->user()->invoices()->latest()->get();

        return view('invoices.index', compact('invoices'));
    }
}
PHP
معرفی کنترلر PlanController و ویو‌ی profile.plans

تصویر هفتم سوال ایران‌گیت

این کنترلر برای مدیریت اشتراک‌های کاربری و تغییرات آنها استفاده می‌شود. متد index تمام اشتراک‌های کاربری موجود را نمایش می‌دهد و متد switch به کاربر این امکان را می‌دهد که اشتراک کاربری خود را تغییر دهد. اگر اشتراک جدید هزینه داشته باشد، یک صورت‌حساب برای پرداخت ایجاد می‌شود که شما باید در بخشی از پیاده‌سازی‌های خود آن‌ها را مدیریت کنید. کنترلر مربوط به آن به شکل زیر از قبل پیاده‌سازی شده است:

extensionFromNameapp/Http/Controllers/PlanController.php
<?php

namespace App\Http\Controllers;

use App\Models\Plan;
use Illuminate\Support\Facades\Auth;

class PlanController extends Controller
{
    public function index()
    {
        $plans = Plan::all();
        return view('profile.plans', compact('plans'));
    }

    public function switch(Plan $plan)
    {
        $user = Auth::user();

        $subscription = $user->subscription;

        if (!$subscription) {
            $subscription = $user->subscription()->create([
                'plan_id' => $plan->id,
                'expires_at' => now()->addDays($plan->duration),
            ]);
        } else {
            $subscription->plan_id = $plan->id;
            $subscription->expires_at = now()->addDays($plan->duration);
        }

        if ($plan->price > 0) {
            $invoice = $user->invoices()->create([
                'plan_id' => $plan->id,
                'amount' => $plan->price,
                'status' => 'pending',
            ]);

            $subscription->expires_at = now()->addDays($plan->duration);
            $subscription->save();

            return redirect()->route('plans.index')->with('success', 'اشتراک کاربری شما با موفقیت بروزرسانی شد. لطفا نسبت به پرداخت صورت حساب آن اقدام کنید.');
        } else {
            $subscription->save();
            return redirect()->route('plans.index')->with('success', 'اشتراک کاربری شما با موفقیت بروزرسانی شد.');
        }
    }
}
PHP

با تغییر اشتراک کاربری، پیام موفقیت در تغییر اشتراک کاربری به نمایش در می‌آید و شما می‌توانید در منوی کاربری بالای صفحه امکانات مختلفی که برای آن اشتراک کاربری تعریف شده است را ببینید و به آن‌ها دسترسی پیدا کنید.

تصویر پنجم سوال ایران‌گیت

تصویر ششم سوال ایران‌گیت

معرفی کنترلر SubscriptionLogController و ویو‌ی subscription-logs.index

تصویر نهم سوال ایران‌گیت

این کنترلر برای نمایش سوابق اشتراک‌های کاربری است. متد index تمام سوابق اشتراکی مربوط به کاربر فعلی را از طریق ارتباط با مدل SubscriptionLog بدست می‌آورد و در ویوی subscription-logs.index به نمایش می‌گذارد. کنترلر مربوط به آن به شکل زیر از قبل پیاده‌سازی شده است:

extensionFromNameapp/Http/Controllers/SubscriptionLogController.php
<?php

namespace App\Http\Controllers;

class SubscriptionLogController extends Controller
{
    public function index()
    {
        $subscriptionLogs = auth()->user()->subscriptionLogs()->latest()->get();

        return view('subscription-logs.index', compact('subscriptionLogs'));
    }
}
PHP

پیاده‌سازی جداول و نوشتن مایگریشن‌ها (Migrations)🔗

در این بخش، مایگریشن‌های مربوط به ایجاد جداول users، plans، subscriptions، invoices، logs، coupons، features و سایر جداول مرتبط باید پیاده‌سازی شوند. این جداول برای مدیریت کاربران، اشتراک‌ها، پرداخت‌ها، ویژگی‌های پلن‌ها و گزارش‌های سیستم طراحی شده‌اند. ساختار مربوط به هر کدام از جداول در بخش زیر مشخص است:

ساختار جدول users و پیاده‌سازی مایگریشن create_users_table

ساختار جدول users:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
name string نام کاربر
email string ایمیل (منحصربه‌فرد)
password string رمز عبور
remember_token string توکن احراز هویت
created_at timestamp زمان ایجاد
updated_at timestamp زمان آخرین به‌روزرسانی
ساختار جدول plans و پیاده‌سازی مایگریشن create_plans_table

ساختار جدول plans:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
name string نام اشتراک کاربری
slug string عنوان یکتای اشتراک کاربری (منحصربه‌فرد)
price decimal قیمت اشتراک کاربری
duration integer مدت زمان اشتراک کاربری (به روز)
is_active boolean وضعیت فعال بودن اشتراک کاربری
created_at timestamp زمان ایجاد
updated_at timestamp زمان آخرین به‌روزرسانی
ساختار جدول subscriptions و پیاده‌سازی مایگریشن create_subscriptions_table

ساختار جدول subscriptions:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
user_id bigint کلید خارجی به users
plan_id bigint کلید خارجی به plans
expires_at timestamp تاریخ انقضای اشتراک کاربری
created_at timestamp زمان ایجاد
updated_at timestamp زمان آخرین به‌روزرسانی
ساختار جدول invoices و پیاده‌سازی مایگریشن create_invoices_table

ساختار جدول invoices:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
user_id bigint کلید خارجی به users
plan_id bigint کلید خارجی به plans
payment_id string یک رشته که می‌تواند null باشد
amount decimal مبلغ پرداختی
status string وضعیت پرداخت (paid یا pending)
created_at timestamp زمان ایجاد
updated_at timestamp زمان آخرین به‌روزرسانی
ساختار جدول logs و پیاده‌سازی مایگریشن create_logs_table

ساختار جدول logs:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
user_id bigint کلید خارجی به users
action string نوع فعالیت انجام‌شده
details text جزئیات فعالیت
created_at timestamp زمان ایجاد
updated_at timestamp زمان آخرین به‌روزرسانی
ساختار جدول coupons و پیاده‌سازی مایگریشن create_coupons_table

ساختار جدول coupons:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
code string کد تخفیف (منحصربه‌فرد)
discount decimal مقدار تخفیف
max_uses integer حداکثر دفعات استفاده
expires_at timestamp تاریخ انقضای کد
created_at timestamp زمان ایجاد
updated_at timestamp زمان آخرین به‌روزرسانی
ساختار جدول features و پیاده‌سازی مایگریشن create_features_table

ساختار جدول features:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
name string نام ویژگی
slug string شناسه یکتای ویژگی
created_at timestamp زمان ایجاد
updated_at timestamp زمان آخرین به‌روزرسانی
ساختار جدول plan_feature و پیاده‌سازی مایگریشن create_plan_feature_table

ساختار جدول plan_feature:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
plan_id bigint کلید خارجی به plans
feature_id bigint کلید خارجی به features
created_at timestamp زمان ایجاد
updated_at timestamp زمان آخرین به‌روزرسانی
ساختار جدول referrals و پیاده‌سازی مایگریشن create_referrals_table

ساختار جدول referrals:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
referrer_id bigint کاربری که معرف شخص دیگر است
referred_id bigint کاربری که معرفی شده است
code string کد ارجاع (منحصربه‌فرد)
reward decimal پاداش داده‌شده به معرف
is_used boolean وضعیت استفاده از کد (true/false)
created_at timestamp زمان ایجاد
updated_at timestamp زمان آخرین به‌روزرسانی
ساختار جدول subscription_logs و پیاده‌سازی مایگریشن create_subscription_logs_table

ساختار جدول subscription_logs:🔗

نام ستون نوع داده توضیحات
id bigint کلید اصلی
user_id bigint کلید خارجی به جدول کاربران
old_subscription_level string سطح قبلی اشتراک کاربر (nullable)
new_subscription_level string سطح جدید اشتراک کاربر (nullable)
created_at timestamp زمان ایجاد رکورد
updated_at timestamp زمان آخرین به‌روزرسانی رکورد

پیاده‌سازی مدل‌ها🔗

لیست برخی از مدل‌های پیاده‌سازی شده در سیستم به شرح زیر است که شما باید آن‌ها را همراه با روابط بینشان در این بخش پیاده‌سازی کنید:

  • مدل User: مدیریت کاربران سیستم، شامل اطلاعات کاربری و روابط مرتبط.
  • مدل Plan: نمایش‌دهنده‌ی اشتراک‌های کاربری، شامل ویژگی‌ها و مشخصات هر پلن.
  • مدل Subscription: ثبت اطلاعات اشتراک کاربران در اشتراک‌های مختلف.
  • مدل Invoice: صورت‌حساب‌های مربوط به خرید اشتراک و پرداخت‌های کاربران.
  • مدل Feature: ویژگی‌هایی که برای هر اشتراک کاربری قابل ارائه هستند.
  • مدل Coupon: کدهای تخفیف که کاربران می‌توانند برای کاهش هزینه خرید اشتراک کاربری استفاده کنند.
  • مدل Referral: ثبت سیستم ارجاع کاربران و پاداش‌های مربوط به معرفی کاربران جدید.
  • مدل Log: ثبت اقدامات کاربران در سیستم جهت نظارت و بررسی تاریخچه فعالیت‌ها.
  • مدل SubscriptionLog: مدیریت تغییرات اشتراک کاربران و تاریخچه ارتقا یا کاهش پلن.

پیاده‌سازی روابط بین مدل‌ها🔗

  • هر کاربر (User) می‌تواند دارای یک اشتراک کاربری (Subscription) باشد.
  • هر اشتراک کاربری (Subscription) به یک اشتراک (Plan) متصل است.
  • هر اشتراک (Plan) می‌تواند چندین ویژگی (Feature) داشته باشد.
  • هر ویژگی (Feature) می‌تواند به چندین اشتراک (Plan) مرتبط باشد.
  • هر کاربر می‌تواند چندین صورت‌حساب (Invoice) برای خرید پلن‌های مختلف داشته باشد.
  • هر صورت‌حساب (Invoice) به دقیقا یک اشتراک (Plan) و دقیقا یک کاربر (User) مرتبط است.
  • هر کاربر (User) می‌تواند از کدهای تخفیف (Coupon) استفاده کند.
  • هر کاربر (User) می‌تواند از طریق سیستم ارجاع، کاربران جدید را معرفی کرده و پاداش دریافت کند.
  • هر کاربر (User) می‌تواند چندین لاگ (Log) از فعالیت‌های خود داشته باشد. همچنین هر لاگ مربوط به دقیقا یک کاربر (User) است.
  • هر کاربر (User) می‌تواند چندین تاریخچه تغییرات اشتراک (SubscriptionLogs) داشته باشد.

پیاده‌سازی فکتوری‌ها‌ (Factories)🔗

در این بخش، فکتوری‌های تعریف شده برای تولید داده‌های تصادفی جهت تست برنامه معرفی شده‌اند. این فکتوری‌ها به منظور تولید داده‌های آزمایشی برای جداول مرتبط در پایگاه داده استفاده می‌شوند:

ساختار فکتوری CouponFactory

فکتوری CouponFactory🔗

نام ستون توضیحات مقادیر ممکن
code کد تخفیف (منحصربه‌فرد) یک UUID تصادفی
discount مقدار تخفیف عدد اعشاری بین ۵ تا ۵۰
max_uses حداکثر دفعات استفاده عدد صحیح بین ۱ تا ۱۰۰
expires_at تاریخ انقضا تاریخی بین حالا تا ۱ سال آینده
ساختار فکتوری FeatureFactory

فکتوریFeatureFactory🔗

نام ستون توضیحات مقادیر ممکن
name نام ویژگی یک کلمه تصادفی
slug شناسه یکتا (slug) مقدار یکتای تصادفی
ساختار فکتوری InvoiceFactory

فکتوری InvoiceFactory🔗

نام ستون توضیحات مقادیر ممکن
user_id کلید خارجی به جدول users مقدار یک user_id معتبر
plan_id کلید خارجی به جدول plans مقدار یک plan_id معتبر
payment_id شناسه پرداخت یک UUID تصادفی
amount مبلغ صورت‌حساب عدد اعشاری بین ۱۰ تا ۱۰۰
status وضعیت پرداخت paid یا failed
ساختار فکتوری LogFactory

فکتوری LogFactory🔗

نام ستون توضیحات مقادیر ممکن
action نوع فعالیت یک کلمه تصادفی
details جزئیات فعالیت یک آرایه JSON
user_id کلید خارجی به جدول users مقدار یک user_id معتبر
ساختار فکتوری PlanFactory

فکتوری PlanFactory🔗

نام ستون توضیحات مقادیر ممکن
name نام اشتراک یک کلمه تصادفی
slug شناسه یکتا (slug) مقدار یکتای تصادفی
price قیمت عدد اعشاری بین ۱۰ تا ۱۰۰
duration مدت زمان (روز) عدد صحیح بین ۳۰ تا ۳۶۵
is_active وضعیت فعال/غیرفعال بودن اشتراک مقدار true یا false
ساختار فکتوری ReferralFactory

فکتوری ReferralFactory🔗

نام ستون توضیحات مقادیر ممکن
referrer_id کلید خارجی به جدول users مقدار یک user_id معتبر
referred_id کلید خارجی به جدول users مقدار یک user_id معتبر
code کد ارجاع (منحصربه‌فرد) یک UUID تصادفی
reward مقدار جایزه ارجاع عدد اعشاری بین ۰ تا ۱۰۰
is_used وضعیت استفاده از کد مقدار true یا false
ساختار فکتوری UserFactory

فکتوری UserFactory🔗

نام ستون توضیحات مقادیر ممکن
name نام کاربر یک نام تصادفی
email ایمیل کاربر (یونیک) ایمیل تصادفی معتبر
email_verified_at زمان تأیید ایمیل now() یا null
password رمز عبور password هش‌شده
remember_token توکن یادآوری رشته ۱۰ کاراکتری تصادفی
ساختار فکتوری SubscriptionFactory

فکتوری SubscriptionFactory🔗

نام ستون توضیحات مقادیر ممکن
user_id شناسه کاربر مرتبط مقدار تصادفی از UserFactory
plan_id شناسه پلن مرتبط مقدار تصادفی از PlanFactory
expires_at تاریخ انقضای اشتراک تاریخ تصادفی بین now و +1 year
created_at زمان ایجاد رکورد now()
updated_at زمان آخرین به‌روزرسانی رکورد now()
ساختار فکتوری SubscriptionLogFactory

فکتوری SubscriptionLogFactory🔗

نام ستون توضیحات مقادیر ممکن
user_id شناسه کاربر مرتبط مقدار تصادفی از UserFactory
old_subscription_level سطح قبلی اشتراک کاربر یک کلمه تصادفی
new_subscription_level سطح جدید اشتراک کاربر یک کلمه تصادفی
created_at زمان ایجاد رکورد now()
updated_at زمان آخرین به‌روزرسانی رکورد now()

پیاده‌سازی سیدر (Seeder)🔗

درون پروژه اولیه تعدادی سیدر (Seeder) وجود دارد که باید به شکل زیر پیاده‌سازی شوند، در نهایت قرار است تا این سیدر‌ها با ترتیب درستی در سیدر DatabaseSeeder مورد استفاده قرار بگیرند. سیدر‌ها باید به شکل زیر ایجاد شوند:

  1. دقیقا ۵۰ کد تخفیف (Coupon) با استفاده از فکتوری Coupon ساخته شود.
  2. دقیقا ۲۰ قابلیت (Feature) با استفاده از فکتوری Feature ساخته شود. همچنین یک اشتراک کاربری با عنوان (Slug) vip که توسط سیدر مربوطه ساخته شده باید به تمام قابلیت‌های ساخته شده توسط این سیدر دسترسی داشته باشد.
  3. دقیقا ۱۰۰ صورت حساب (Invoice) با استفاده از فکتوری Invoice ساخته شود.
  4. دقیقا ۲۰۰ لاگ (Log) با استفاده از فکتوری Log ساخته شود.
  5. دقیقا ۱۰ اشتراک (Plan) با استفاده از فکتوری Plan ساخته شود. همچنین باید سه اشتراک کاربری دیگر توسط این سیدر به صورت زیر ساخته شود:
    • اشتراک پایه با نام (Name) ‌Basic، عنوان (Slug) basic، قیمت (Price) 0 و مدت‌زمان (Duration) 30 روز
    • اشتراک پریمیوم با نام (Name) ‌Premium، عنوان (Slug) premium، قیمت (Price) 100 و مدت‌زمان (Duration) 30 روز
    • اشتراک ویژه با نام (Name) ‌Vip، عنوان (Slug) vip، قیمت (Price) 200 و مدت‌زمان (Duration) 30 روز
  6. دقیقا ۵۰ ارجاع (Referral) با استفاده از فکتوری Referral ساخته شود.
  7. دقیقا ۱۵۰ گزارش اشتراک (SubscriptionLog) با استفاده از فکتوری SubscriptionLog ساخته شود.
  8. دقیقا ۱۰۰ اشتراک کاربری (Subscription) با استفاده از فکتوری Subscription ساخته شود.
  9. دقیقا ۵۰ کاربر (User) با استفاده از فکتوری User ساخته شود. تمامی این ۵۰ کاربر باید در اشتراک کاربری basic که قبلا توسط سیدر مربوطه ساخته شده است، مشترک شوند.

پیاده‌سازی پالیسی‌ها (Policies)🔗

در این سوال شما باید دو پالیسی FeaturePolicy و SubscriptionPolicy را برای مدیریت دسترسی به ویژگی‌های خاص و اشتراک‌های کاربری خاص به صورت زیر پیاده‌سازی کنید:

ساختار پالیسی FeaturePolicy

پالیسی FeaturePolicy به صورت زیر در مسیر app/Policies/FeaturePolicy.php پیاده‌سازی شده است که با استفاده از تابعی با نام accessFeature و دریافت دو مقدار $user و $feature به صورت پویا مشخص می‌کند که آیا کاربر $user در اشتراک کاربری فعلی‌اش می‌تواند می‌تواند به قابلیت $feature دسترسی داشته باشد یا نه.

extensionFromNameapp/Policies/FeaturePolicy.php
<?php

namespace App\Policies;

use App\Models\User;
use App\Models\Feature;

class FeaturePolicy
{
    public function accessFeature(User $user, Feature $feature)
    {
        // TODO
    }
}
PHP
  • از این پالیسی برای مدیریت دسترسی بر اساس قابلیت‌ها (Features) استفاده خواهد شد.

‍‍‍

ساختار پالیسی SubscriptionPolicy

پالیسی SubscriptionPolicy به صورت زیر در مسیر app/Policies/SubscriptionPolicy.php پیاده‌سازی شده است که برای مدیریت دسترسی‌های کاربران با اشتراک‌های کاربری پیش‌فرض basic، premium و vip به بخش‌های مختلف پنل‌کاربری مورد استفاده قرار می‌گیرد. این پالیسی با استفاده از هر کدام از سه تابع accessVIP، accessPremium و accessBasic بررسی می‌کند که آیا اشتراک کاربری $subscription که متعلق به کاربر $user می‌باشد به ترتیب از نوع vip، premium و basic می‌باشد یا نه.

extensionFromNameapp/Policies/FeaturePolicy.php
<?php

namespace App\Policies;

use App\Models\Subscription;
use App\Models\User;
use App\Models\Plan;

class SubscriptionPolicy
{
    public function accessVip(User $user, Subscription $subscription)
    {
        // TODO
    }

    public function accessPremium(User $user, Subscription $subscription)
    {
        // TODO
    }

    public function accessBasic(User $user, Subscription $subscription)
    {
        // TODO
    }
}
PHP
  • از این پالیسی برای مدیریت دسترسی بر اساس اشتراک‌های کاربری (Subscriptions) استفاده خواهد شد.

پیاده‌سازی Observer SubscriptionObserver🔗

در این پروژه شما باید یک Observer را برای مدل Subscription به صورتی پیاده‌سازی کنید که هنگام بروزرسانی (Update) شناسه اشتراک کاربری (plan_id) برای یک کاربر، یک SubscriptionLog جدید ایجاد کند که مقدار user_id آن برابر با شناسه کاربری است که اشتراک کاربری آن تغییر کرده است، مقدار old_subscription_level آن برابر با مقدار عنوان (Slug) اشتراک کاربری قبلی کاربر و مقدار new_subscription_level آن برابر با مقدار عنوان (Slug) اشتراک جدید کاربر می‌باشد.

پیاده‌سازی وظایف (Jobs) و اسکجول (Schedule) کردن🔗

در این پروژه شما باید دو جاب ExpireSubscriptionsJob و DowngradePendingInvoicesJob را برای انجام بخشی از پردازش‌های مربوط به پنل کاربری به صورت زیر پیاده‌سازی کنید:

ساختار جاب DowngradePendingInvoicesJob

جاب DowngradePendingInvoicesJob را به صورت زیر در مسیر app/Jobs/DowngradePendingInvoicesJob.php پیاده‌سازی کنید تا اشتراک کاربری تمام کاربرانی را حداقل یک صورت‌حساب (Invoice) با وضعیت (Status) باز (pending) دارند که بیش‌تر مساوی یک هفته از صادر شدن‌شان گذشته است، به اشتراک کاربری basic تغییر دهد که زمان انقضایی هم نداشته باشد.

extensionFromNameapp/Jobs/DowngradePendingInvoicesJob.php
<?php

namespace App\Jobs;

use App\Models\User;
use App\Models\Plan;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class DowngradePendingInvoicesJob implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    public function handle()
    {
        // TODO
    }
}
PHP
  • از این پالیسی برای مدیریت دسترسی بر اساس قابلیت‌ها (Features) استفاده خواهد شد.

‍‍‍

ساختار جاب ExpireSubscriptionsJob

جاب ExpireSubscriptionsJob را به صورت زیر در مسیر app/Jobs/ExpireSubscriptionsJob.php پیاده‌سازی کنید تا اشتراک کاربری تمام کاربرانی را که تاریخ انقضای اشتراک کاربری‌شان (expires_at) به پایان رسیده است (تاریخ انقضای اشتراک کاربری‌شان کوچک‌تر مساوی زمان حال است)، به اشتراک کاربری basic تغییر دهد که زمان انقضایی هم نداشته باشد.

extensionFromNameapp/Jobs/ExpireSubscriptionsJob.php
<?php

namespace App\Jobs;

use App\Models\Subscription;
use App\Models\Plan;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class ExpireSubscriptionsJob implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    public function handle()
    {
        // TODO
    }
}
PHP
  • از این پالیسی برای مدیریت دسترسی بر اساس اشتراک‌های کاربری (Subscriptions) استفاده خواهد شد.

در نهایت پس از تعریف وظایف (Jobs) های مشخص شده، شما باید این دو وظیفه (Job) را طوری در لاراول اسکجول (Schedule) کنید تا به صورت خودکار و هر دقیقه یک بار پردازش شوند.

پیاده‌سازی میدلور (Middleware)🔗

در نهایت شما قرار است تا میدلور (Middleware) CheckFeatureAccess را برای پردازش دسترسی در درخواست (Request) کاربر به صورت زیر پیاده‌سازی کنید:

extensionFromNameapp/Http/Middleware/CheckFeatureAccess.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CheckFeatureAccess
{
    public function handle(Request $request, Closure $next): Response
    {
        // TODO
    }
}
PHP

این میدلور با استفاده از پالیسی‌های تعریف شده در مراحل قبل بررسی می‌کند که آیا کاربر فعلی می‌تواند به مسیر مشخص شده که از این میدلور استفاده می‌کند و مشخص کننده مسیر یکی از قابلیت‌های موجود در برنامه است دسترسی دارد یا نه. در صورتی که کاربر در تلاش بود تا به مسیر مربوط به قابلیتی دسترسی پیدا کند که در برنامه به کل وجود ندارد باید خطای 404 به همراه پیام "این صفحه یافت نشد." و در صورتی که در تلاش بود تا به مسیر مربوط به قابلیتی دسترسی پیدا کند که اشتراک کاربری‌اش اجازه دسترسی به آن را ندارد، باید خطای 403 به همراه پیام "شما دسترسی به این بخش را ندارید." نمایش داده شود. در غیر این صورت ادامه پردازش درخواست کاربر انجام می‌شود.

آن‌چه باید آپلود کنید🔗

  • توجه: پس از پیاده‌سازی موارد خواسته شده، کل فایل‌های پروژه به‌جز پوشه‌ی vendor را زیپ کرده و ارسال کنید.
  • توجه: شما مجاز به افزودن فایل جدیدی در این ساختار نیستید و تنها باید تغییرات را در فایل‌های موجود اعمال کنید.
  • توجه: که نام فایل Zip اهمیتی ندارد.
ارسال پاسخ برای این سؤال
در حال حاضر شما دسترسی ندارید.