From 7719535940b8c8ff392ac3e4bc17280d883cb943 Mon Sep 17 00:00:00 2001 From: Qi HU Date: Thu, 11 Dec 2025 12:19:35 +0800 Subject: [PATCH] feat: add notice lead time for global promotions --- .../Resources/System/TorrentStateResource.php | 9 ++++ app/Models/TorrentState.php | 42 ++++++++++++++++++- ...dd_notice_days_to_torrents_state_table.php | 26 ++++++++++++ resources/lang/en/label.php | 4 ++ resources/lang/zh_CN/label.php | 4 ++ resources/lang/zh_TW/label.php | 4 ++ 6 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2025_12_11_000000_add_notice_days_to_torrents_state_table.php diff --git a/app/Filament/Resources/System/TorrentStateResource.php b/app/Filament/Resources/System/TorrentStateResource.php index 0880216b..9d794ea5 100644 --- a/app/Filament/Resources/System/TorrentStateResource.php +++ b/app/Filament/Resources/System/TorrentStateResource.php @@ -60,6 +60,13 @@ class TorrentStateResource extends Resource ->validationMessages([ 'after' => __('label.torrent_state.deadline_after_begin'), ]), + Select::make('notice_days') + ->label(__('label.torrent_state.notice_days')) + ->options(TorrentState::noticeOptions()) + ->required() + ->default(TorrentState::NOTICE_NONE) + ->dehydrated(true) + ->native(false), Textarea::make('remark') ->label(__('label.comment')) ->rows(2) @@ -121,6 +128,8 @@ class TorrentStateResource extends Resource default => 'heroicon-o-check-circle', }) ->iconPosition('before'), + TextColumn::make('notice_days_text') + ->label(__('label.torrent_state.notice_days')), TextColumn::make('remark')->label(__('label.comment'))->limit(50), ]) ->filters([ diff --git a/app/Models/TorrentState.php b/app/Models/TorrentState.php index 47044764..d70f7f6a 100644 --- a/app/Models/TorrentState.php +++ b/app/Models/TorrentState.php @@ -16,13 +16,17 @@ class TorrentState extends NexusModel { use NexusActivityLogTrait; - protected $fillable = ['global_sp_state', 'deadline', 'begin', 'remark']; + public const NOTICE_NONE = 0; + public const NOTICE_UNLIMITED = -1; + + protected $fillable = ['global_sp_state', 'deadline', 'begin', 'remark', 'notice_days']; protected $table = 'torrents_state'; protected $casts = [ 'begin' => 'datetime', 'deadline' => 'datetime', + 'notice_days' => 'integer', ]; protected static function booted() @@ -48,6 +52,11 @@ class TorrentState extends NexusModel return Torrent::$promotionTypes[$this->global_sp_state]['text'] ?? ''; } + public function getNoticeDaysTextAttribute(): string + { + return self::noticeOptions()[$this->notice_days] ?? ''; + } + public function scopeActive(Builder $query, ?Carbon $moment = null): Builder { $moment = $moment ?? Carbon::now(); @@ -117,6 +126,7 @@ class TorrentState extends NexusModel foreach ($states as $state) { $begin = self::parseDateTimeValue($state['begin'] ?? null); $deadline = self::parseDateTimeValue($state['deadline'] ?? null); + $noticeDays = (int)($state['notice_days'] ?? self::NOTICE_NONE); $hasBegun = !$begin || $begin->lessThanOrEqualTo($moment); $notExpired = !$deadline || $deadline->greaterThanOrEqualTo($moment); @@ -129,6 +139,9 @@ class TorrentState extends NexusModel } if ($begin && $begin->greaterThan($moment)) { + if (!self::isWithinNoticeWindow($begin, $noticeDays, $moment)) { + continue; + } if (!$upcoming) { $upcoming = $state; continue; @@ -282,4 +295,31 @@ class TorrentState extends NexusModel 'end' => $deadlineText, ]); } + + public static function noticeOptions(): array + { + return [ + self::NOTICE_NONE => __('label.torrent_state.notice_none'), + 1 => __('label.torrent_state.notice_day', ['days' => 1]), + 3 => __('label.torrent_state.notice_day', ['days' => 3]), + 7 => __('label.torrent_state.notice_day', ['days' => 7]), + 15 => __('label.torrent_state.notice_day', ['days' => 15]), + 30 => __('label.torrent_state.notice_day', ['days' => 30]), + self::NOTICE_UNLIMITED => __('label.torrent_state.notice_unlimited'), + ]; + } + + protected static function isWithinNoticeWindow(?Carbon $begin, int $noticeDays, Carbon $now): bool + { + if (!$begin) { + return true; + } + if ($noticeDays === self::NOTICE_NONE) { + return false; + } + if ($noticeDays === self::NOTICE_UNLIMITED) { + return true; + } + return $begin->copy()->subDays($noticeDays)->lessThanOrEqualTo($now); + } } diff --git a/database/migrations/2025_12_11_000000_add_notice_days_to_torrents_state_table.php b/database/migrations/2025_12_11_000000_add_notice_days_to_torrents_state_table.php new file mode 100644 index 00000000..612154e2 --- /dev/null +++ b/database/migrations/2025_12_11_000000_add_notice_days_to_torrents_state_table.php @@ -0,0 +1,26 @@ +integer('notice_days')->default(0)->after('remark'); + } + }); + } + + public function down(): void + { + Schema::table('torrents_state', function (Blueprint $table) { + if (Schema::hasColumn('torrents_state', 'notice_days')) { + $table->dropColumn('notice_days'); + } + }); + } +}; diff --git a/resources/lang/en/label.php b/resources/lang/en/label.php index 0369ac8c..2a66aa19 100644 --- a/resources/lang/en/label.php +++ b/resources/lang/en/label.php @@ -335,6 +335,10 @@ return [ 'status_upcoming' => 'Upcoming', 'time_overlaps' => 'Time overlaps with another promotion. Please adjust the window.', 'time_overlaps_with' => 'Overlaps with promotion ID :id (time: :begin ~ :end).', + 'notice_days' => 'Pre-announcement', + 'notice_day' => ':days day before', + 'notice_none' => 'No pre-announcement', + 'notice_unlimited' => 'Always announce', ], 'role' => [ 'class' => 'Relate user class', diff --git a/resources/lang/zh_CN/label.php b/resources/lang/zh_CN/label.php index c9eb84cf..915d55d9 100644 --- a/resources/lang/zh_CN/label.php +++ b/resources/lang/zh_CN/label.php @@ -377,6 +377,10 @@ return [ 'status_upcoming' => '未开始', 'time_overlaps' => '时间与已有活动重叠,请调整时间段。', 'time_overlaps_with' => '与活动 ID :id (时间::begin ~ :end)重叠,请调整时间段。', + 'notice_days' => '提前预告', + 'notice_day' => '提前 :days 天', + 'notice_none' => '不预告', + 'notice_unlimited' => '不限时预告', ], 'role' => [ 'class' => '关联用户等级', diff --git a/resources/lang/zh_TW/label.php b/resources/lang/zh_TW/label.php index 0cb57b39..95dd7f37 100644 --- a/resources/lang/zh_TW/label.php +++ b/resources/lang/zh_TW/label.php @@ -334,6 +334,10 @@ return [ 'status_upcoming' => '未開始', 'time_overlaps' => '時間與已有活動重疊,請調整時間段。', 'time_overlaps_with' => '與活動 ID :id (時間::begin ~ :end)重疊,請調整時間段。', + 'notice_days' => '提前預告', + 'notice_day' => '提前 :days 天', + 'notice_none' => '不預告', + 'notice_unlimited' => '不限時預告', ], 'role' => [ 'class' => '關聯用户等級',