diff --git a/app/Http/Controllers/Admin/AppNotificationController.php b/app/Http/Controllers/Admin/AppNotificationController.php index 2d764e9..99a1b41 100644 --- a/app/Http/Controllers/Admin/AppNotificationController.php +++ b/app/Http/Controllers/Admin/AppNotificationController.php @@ -19,6 +19,30 @@ class AppNotificationController extends Controller { $query = AppNotification::query()->withCount('reads'); + $source = trim((string) $request->input('source', 'all')); + $hasIsSystem = \Illuminate\Support\Facades\Schema::hasColumn('app_notifications', 'is_system'); + + if ($source === 'system') { + if ($hasIsSystem) { + $query->system(); + } else { + $query->where(function ($q) { + $q->whereNull('created_by') + ->orWhere('audience_label', 'like', 'سیستمی%'); + }); + } + } elseif ($source === 'admin') { + if ($hasIsSystem) { + $query->adminCreated(); + } else { + $query->whereNotNull('created_by') + ->where(function ($q) { + $q->whereNull('audience_label') + ->orWhere('audience_label', 'not like', 'سیستمی%'); + }); + } + } + TableEngine::create($query, $request->all()) ->initSearch(['title', 'body', 'severity', 'audience_label', 'audience_type']) ->select(['*']) @@ -104,7 +128,7 @@ class AppNotificationController extends Controller $row = DB::transaction(function () use ($data, $audience, $audienceType, $segmentKeys, $recipients, $recipientsCount) { $user = auth()->user(); - $row = AppNotification::create([ + $payload = [ 'title' => $data['title'], 'body' => $data['body'], 'severity' => $data['severity'], @@ -114,7 +138,12 @@ class AppNotificationController extends Controller 'segment_keys' => $audienceType === AppNotificationAudienceService::TYPE_SEGMENT ? $segmentKeys : null, 'audience_label' => $audience->buildAudienceLabel($audienceType, $segmentKeys, $recipientsCount), 'recipients_count' => $recipientsCount, - ]); + ]; + if (\Illuminate\Support\Facades\Schema::hasColumn('app_notifications', 'is_system')) { + $payload['is_system'] = false; + } + + $row = AppNotification::create($payload); if ($audienceType !== AppNotificationAudienceService::TYPE_ALL) { $now = now(); @@ -141,6 +170,12 @@ class AppNotificationController extends Controller public function update(Request $request, int $id) { $row = AppNotification::findOrFail($id); + $isSystem = \Illuminate\Support\Facades\Schema::hasColumn('app_notifications', 'is_system') + ? (bool) $row->is_system + : ($row->created_by === null && str_starts_with((string) $row->audience_label, 'سیستمی')); + if ($isSystem) { + return response()->json(['message' => 'اعلان سیستمی قابل ویرایش نیست.'], 422); + } // مخاطب بعد از ارسال ثابت می‌ماند — فقط محتوا/وضعیت $data = $request->validate([ 'title' => 'sometimes|required|string|max:191', diff --git a/app/Models/AppNotification.php b/app/Models/AppNotification.php index 189315a..0d12347 100644 --- a/app/Models/AppNotification.php +++ b/app/Models/AppNotification.php @@ -19,6 +19,7 @@ class AppNotification extends Model 'severity', 'is_active', 'created_by', + 'is_system', 'audience_type', 'segment_keys', 'audience_label', @@ -27,10 +28,22 @@ class AppNotification extends Model protected $casts = [ 'is_active' => 'boolean', + 'is_system' => 'boolean', 'segment_keys' => 'array', 'recipients_count' => 'integer', ]; + /** اعلان تراکنشی خودکار (نه ارسال دستی ادمین) */ + public function scopeSystem($query) + { + return $query->where('is_system', true); + } + + public function scopeAdminCreated($query) + { + return $query->where('is_system', false); + } + public function reads(): HasMany { return $this->hasMany(AppNotificationRead::class, 'notification_id'); diff --git a/app/Services/CustomerAppNotifyService.php b/app/Services/CustomerAppNotifyService.php index 78f3219..b58bc00 100644 --- a/app/Services/CustomerAppNotifyService.php +++ b/app/Services/CustomerAppNotifyService.php @@ -46,18 +46,23 @@ class CustomerAppNotifyService } try { - $row = DB::transaction(function () use ($customerId, $title, $body, $severity) { - $row = AppNotification::create([ - 'title' => $title, - 'body' => $body, - 'severity' => $severity, - 'is_active' => true, - 'created_by' => null, - 'audience_type' => AppNotificationAudienceService::TYPE_CUSTOMERS, - 'segment_keys' => null, - 'audience_label' => 'سیستمی — مشتری '.$customerId, - 'recipients_count' => 1, - ]); + $payload = [ + 'title' => $title, + 'body' => $body, + 'severity' => $severity, + 'is_active' => true, + 'created_by' => null, + 'audience_type' => AppNotificationAudienceService::TYPE_CUSTOMERS, + 'segment_keys' => null, + 'audience_label' => 'سیستمی — مشتری '.$customerId, + 'recipients_count' => 1, + ]; + if (\Illuminate\Support\Facades\Schema::hasColumn('app_notifications', 'is_system')) { + $payload['is_system'] = true; + } + + $row = DB::transaction(function () use ($customerId, $payload) { + $row = AppNotification::create($payload); AppNotificationRecipient::query()->insert([ 'notification_id' => $row->id, diff --git a/database/migrations/2026_07_28_000001_add_is_system_to_app_notifications.php b/database/migrations/2026_07_28_000001_add_is_system_to_app_notifications.php new file mode 100644 index 0000000..bf18fcd --- /dev/null +++ b/database/migrations/2026_07_28_000001_add_is_system_to_app_notifications.php @@ -0,0 +1,42 @@ +boolean('is_system')->default(false)->after('created_by')->index(); + }); + } + + // بک‌فیل: سیستمی = بدون created_by یا لیبل قدیمی «سیستمی» + DB::table('app_notifications') + ->where(function ($q) { + $q->whereNull('created_by') + ->orWhere('audience_label', 'like', 'سیستمی%'); + }) + ->update(['is_system' => true]); + } + + public function down(): void + { + if (Schema::hasTable('app_notifications') && Schema::hasColumn('app_notifications', 'is_system')) { + Schema::table('app_notifications', function (Blueprint $table) { + $table->dropColumn('is_system'); + }); + } + } +};