diff --git a/app/Filament/Resources/User/UserMedalResource.php b/app/Filament/Resources/User/UserMedalResource.php index 07beed78..ec99e715 100644 --- a/app/Filament/Resources/User/UserMedalResource.php +++ b/app/Filament/Resources/User/UserMedalResource.php @@ -2,11 +2,14 @@ namespace App\Filament\Resources\User; +use App\Filament\OptionsTrait; use App\Filament\Resources\User\UserMedalResource\Pages; use App\Filament\Resources\User\UserMedalResource\RelationManagers; use App\Models\Medal; use App\Models\NexusModel; use App\Models\UserMedal; +use App\Repositories\MedalRepository; +use Carbon\Carbon; use Filament\Forms; use Filament\Forms\Form; use Filament\Resources\Resource; @@ -14,10 +17,13 @@ use Filament\Tables\Table; use Filament\Tables; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use Illuminate\Support\Collection; use Illuminate\Support\HtmlString; class UserMedalResource extends Resource { + use OptionsTrait; + protected static ?string $model = UserMedal::class; protected static ?string $navigationIcon = 'heroicon-o-flag'; @@ -57,8 +63,10 @@ class UserMedalResource extends Resource , Tables\Columns\TextColumn::make('medal.name')->label(__('label.medal.label'))->searchable(), Tables\Columns\ImageColumn::make('medal.image_large')->label(__('label.image')), - Tables\Columns\TextColumn::make('expire_at')->label(__('label.expire_at'))->dateTime(), + Tables\Columns\TextColumn::make('expire_at')->label(__('label.expire_at')), + Tables\Columns\TextColumn::make('bonus_addition_expire_at')->label(__('medal.bonus_addition_expire_at')), Tables\Columns\TextColumn::make('wearingStatusText')->label(__('label.status')), + Tables\Columns\TextColumn::make('created_at')->label(__('label.created_at')), ]) ->defaultSort('id', 'desc') ->filters([ @@ -76,6 +84,42 @@ class UserMedalResource extends Resource ->options(Medal::query()->pluck('name', 'id')->toArray()) ->label(__('medal.label')) , + Tables\Filters\SelectFilter::make('is_expired') + ->options(self::getYesNoOptions()) + ->label(__('medal.is_expired')) + ->query(function (Builder $query, array $data) { + if (isset($data['value']) && $data['value'] === "0") { + //未过期,为 null 或大于当前时间 + $query->where(function ($query) { + $query->whereNull('expire_at')->orWhere('expire_at', '>', now()); + }); + } + if (isset($data['value']) && $data['value'] === "1") { + //已过期, 不为 null 且小于当前时间 + $query->whereNotNull('expire_at')->where("expire_at", "<", now()); + } + }) + , + Tables\Filters\SelectFilter::make('is_bonus_addition_expired') + ->options(self::getYesNoOptions()) + ->label(__('medal.is_bonus_addition_expired')) + ->query(function (Builder $query, array $data) { + if (isset($data['value']) && $data['value'] === "0") { + //未过期,为 null 或大于当前时间 + $query->where(function ($query) { + $query->whereNull('bonus_addition_expire_at')->orWhere('bonus_addition_expire_at', '>', now()); + }); + } + if (isset($data['value']) && $data['value'] === "1") { + //已过期, 不为 null 且小于当前时间 + $query->whereNotNull('bonus_addition_expire_at')->where("bonus_addition_expire_at", "<", now()); + } + }) + , + Tables\Filters\SelectFilter::make('status') + ->options(UserMedal::listWearingStatusLabels()) + ->label(__('label.status')) + , ]) ->actions([ Tables\Actions\DeleteAction::make()->using(function (NexusModel $record) { @@ -84,8 +128,89 @@ class UserMedalResource extends Resource }) ]) ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + self::buildBulkActionIncreaseExpireAt('expire_at'), + self::buildBulkActionUpdateExpireAt('expire_at'), + self::buildBulkActionCancelExpireAt('expire_at'), + ])->label(sprintf("%s-%s", __('label.bulk'), __('label.expire_at'))), + Tables\Actions\BulkActionGroup::make([ + self::buildBulkActionIncreaseExpireAt('bonus_addition_expire_at'), + self::buildBulkActionUpdateExpireAt('bonus_addition_expire_at'), + self::buildBulkActionCancelExpireAt('bonus_addition_expire_at'), + ])->label(sprintf("%s-%s", __('label.bulk'), __('medal.bonus_addition_expire_at'))), + Tables\Actions\DeleteBulkAction::make(), + ]) + ->selectCurrentPageOnly() + ; + } - ]); + private static function buildBulkActionIncreaseExpireAt(string $filed): Tables\Actions\BulkAction + { + return Tables\Actions\BulkAction::make("bulkActionIncrease$filed") + ->label(__('medal.bulk_action_increase')) + ->modalHeading(__('medal.bulk_action_increase_' . $filed)) + ->form([ + Forms\Components\TextInput::make('increase_duration') + ->label(__('medal.increase_duration')) + ->helperText(__('medal.increase_duration_help')) + ->integer() + ->required(), + ]) + ->action(function (Collection $collection, array $data) use ($filed) { + try { + $rep = new MedalRepository(); + $rep->increaseExpireAt($collection, $filed, $data['increase_duration']); + send_admin_success_notification(); + } catch (\Exception $e) { + send_admin_fail_notification($e->getMessage()); + } + }) + ->deselectRecordsAfterCompletion() + ; + } + + private static function buildBulkActionUpdateExpireAt(string $filed): Tables\Actions\BulkAction + { + return Tables\Actions\BulkAction::make("bulkActionUpdate$filed") + ->label(__('medal.bulk_action_update')) + ->modalHeading(__('medal.bulk_action_update_' . $filed)) + ->form([ + Forms\Components\DateTimePicker::make('update_expire_at') + ->label(__('medal.update_expire_at')) + ->helperText(__('medal.update_expire_at_help')) + ->required(), + ]) + ->action(function (Collection $collection, array $data) use ($filed) { + try { + $expireAt = Carbon::parse($data['update_expire_at']); + $rep = new MedalRepository(); + $rep->updateExpireAt($collection, $filed, $expireAt); + send_admin_success_notification(); + } catch (\Exception $e) { + send_admin_fail_notification($e->getMessage()); + } + }) + ->deselectRecordsAfterCompletion() + ; + } + + private static function buildBulkActionCancelExpireAt(string $filed): Tables\Actions\BulkAction + { + return Tables\Actions\BulkAction::make("bulkActionCancel$filed") + ->label(__('medal.bulk_action_cancel')) + ->modalHeading(__('medal.bulk_action_cancel_' . $filed)) + ->requiresConfirmation() + ->action(function (Collection $collection) use ($filed) { + try { + $rep = new MedalRepository(); + $rep->cancelExpireAt($collection, $filed); + send_admin_success_notification(); + } catch (\Exception $e) { + send_admin_fail_notification($e->getMessage()); + } + }) + ->deselectRecordsAfterCompletion() + ; } public static function getEloquentQuery(): Builder diff --git a/app/Models/UserMedal.php b/app/Models/UserMedal.php index d41a6e07..30b9ac88 100644 --- a/app/Models/UserMedal.php +++ b/app/Models/UserMedal.php @@ -9,11 +9,19 @@ class UserMedal extends NexusModel const STATUS_NOT_WEARING = 0; const STATUS_WEARING = 1; - public function getWearingStatusTextAttribute() + public function getWearingStatusTextAttribute(): string { return nexus_trans("medal.wearing_status_text." . $this->status); } + public static function listWearingStatusLabels(): array + { + return [ + self::STATUS_WEARING => nexus_trans("medal.wearing_status_text." . self::STATUS_WEARING), + self::STATUS_NOT_WEARING => nexus_trans("medal.wearing_status_text." . self::STATUS_NOT_WEARING), + ]; + } + public function medal() { return $this->belongsTo(Medal::class, 'medal_id'); diff --git a/app/Repositories/MedalRepository.php b/app/Repositories/MedalRepository.php index 837e085f..d8cfa161 100644 --- a/app/Repositories/MedalRepository.php +++ b/app/Repositories/MedalRepository.php @@ -7,6 +7,7 @@ use App\Models\Setting; use App\Models\User; use App\Models\UserMedal; use Carbon\Carbon; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Nexus\Database\NexusDB; @@ -148,4 +149,51 @@ class MedalRepository extends BaseRepository return NexusDB::statement($sql); } + public function increaseExpireAt(Collection $collection, string $field, int $duration): void + { + $this->checkExpireField($field); + $idArr = $collection->pluck('id')->toArray(); + $result = NexusDB::table("user_medals") + ->whereIn('id', $idArr) + ->whereNotNull($field) + ->update([$field => NexusDB::raw("`$field` + INTERVAL $duration DAY")]); + do_log(sprintf( + "operator: %s, increase records: %s $field + $duration day, result: %s", + get_pure_username(), implode(', ', $idArr), $result + )); + } + + public function updateExpireAt(Collection $collection, string $field, Carbon $expireAt): void + { + $this->checkExpireField($field); + $idArr = $collection->pluck('id')->toArray(); + $result = NexusDB::table("user_medals") + ->whereIn('id', $idArr) + ->update([$field => $expireAt]); + do_log(sprintf( + "operator: %s, update records: %s $field $expireAt, result: %s", + get_pure_username(), implode(', ', $idArr), $result + )); + } + + public function cancelExpireAt(Collection $collection, string $field): void + { + $this->checkExpireField($field); + $idArr = $collection->pluck('id')->toArray(); + $result = NexusDB::table("user_medals") + ->whereIn('id', $idArr) + ->update([$field => NexusDB::raw("null")]); + do_log(sprintf( + "operator: %s, update records: %s $field null, result: %s", + get_pure_username(), implode(', ', $idArr), $result + )); + } + + private function checkExpireField(string $field): void + { + if (!in_array($field, ['expire_at', 'bonus_addition_expire_at'])) { + throw new \InvalidArgumentException("invalid field: $field"); + } + } + } diff --git a/resources/lang/en/label.php b/resources/lang/en/label.php index 71e2e345..02b7862c 100644 --- a/resources/lang/en/label.php +++ b/resources/lang/en/label.php @@ -49,6 +49,7 @@ return [ 'language' => 'Language', 'content' => 'Content', 'is_default' => 'Is default', + 'bulk' => 'Bulk', 'setting' => [ 'nav_text' => 'Setting', 'backup' => [ diff --git a/resources/lang/en/medal.php b/resources/lang/en/medal.php index 50e89efa..8f5b8547 100644 --- a/resources/lang/en/medal.php +++ b/resources/lang/en/medal.php @@ -50,4 +50,22 @@ return [ 0 => 'Wearing', 1 => 'Not wearing' ], + 'bonus_addition_expire_at' => 'Bonus addition Expiration Time', + 'is_expired' => 'Is Expired', + 'is_bonus_addition_expired' => 'Is Bonus addition expired', + 'is_wearing' => 'Is Wearing', + 'increase_duration' => 'Increase Duration', + 'increase_duration_help' => 'Unit: days. Uniformly extend the expiration time from the original value', + 'update_expire_at' => 'Set expiration time', + 'update_expire_at_help' => 'Uniformly set the expiration time to a specified value', + 'bulk_action_increase' => 'Increase', + 'bulk_action_update' => 'Update', + 'bulk_action_cancel' => 'Cancel', + 'bulk_action_increase_expire_at' => 'Bulk increase expiration time', + 'bulk_action_update_expire_at' => 'Bulk update expiration time', + 'bulk_action_cancel_expire_at' => 'Bulk cancel expiration time', + 'bulk_action_increase_bonus_addition_expire_at' => 'Bulk extend bonus addition expiration time', + 'bulk_action_update_bonus_addition_expire_at' => 'Bulk update bonus expiration time', + 'bulk_action_cancel_bonus_addition_expire_at' => 'Bulk cancel bonus expiration time', + ]; diff --git a/resources/lang/zh_CN/label.php b/resources/lang/zh_CN/label.php index 62bbfafc..5dd122a1 100644 --- a/resources/lang/zh_CN/label.php +++ b/resources/lang/zh_CN/label.php @@ -49,6 +49,7 @@ return [ 'language' => '语言', 'content' => '内容', 'is_default' => '是否默认', + 'bulk' => '批量', 'setting' => [ 'nav_text' => '设置', 'backup' => [ diff --git a/resources/lang/zh_CN/medal.php b/resources/lang/zh_CN/medal.php index 21aa6e5d..220eb636 100644 --- a/resources/lang/zh_CN/medal.php +++ b/resources/lang/zh_CN/medal.php @@ -50,4 +50,21 @@ return [ 0 => '未佩戴', 1 => '已佩戴' ], + 'bonus_addition_expire_at' => '魔力加成过期时间', + 'is_expired' => '是否过期', + 'is_bonus_addition_expired' => '是否魔力加成过期', + 'is_wearing' => '是否佩戴', + 'increase_duration' => '延长时长', + 'increase_duration_help' => '单位:天。将过期时间在原基础上统一延长', + 'update_expire_at' => '设置过期时间', + 'update_expire_at_help' => '将过期时间统一设置为指定值', + 'bulk_action_increase' => '延长', + 'bulk_action_update' => '设置', + 'bulk_action_cancel' => '取消', + 'bulk_action_increase_expire_at' => '批量延长过期时间', + 'bulk_action_update_expire_at' => '批量设置过期时间', + 'bulk_action_cancel_expire_at' => '批量取消过期时间', + 'bulk_action_increase_bonus_addition_expire_at' => '批量延长魔力加成过期时间', + 'bulk_action_update_bonus_addition_expire_at' => '批量设置魔力加成过期时间', + 'bulk_action_cancel_bonus_addition_expire_at' => '批量取消魔力加成过期时间', ]; diff --git a/resources/lang/zh_TW/label.php b/resources/lang/zh_TW/label.php index 328b1047..cc1a2f45 100644 --- a/resources/lang/zh_TW/label.php +++ b/resources/lang/zh_TW/label.php @@ -49,6 +49,7 @@ return [ 'language' => '語言', 'content' => '內容', 'is_default' => '是否默認', + 'bulk' => '批量', 'setting' => [ 'nav_text' => '設置', 'backup' => [ diff --git a/resources/lang/zh_TW/medal.php b/resources/lang/zh_TW/medal.php index 577a15fa..0a764b3c 100644 --- a/resources/lang/zh_TW/medal.php +++ b/resources/lang/zh_TW/medal.php @@ -50,4 +50,21 @@ return [ 0 => '未佩戴', 1 => '已佩戴' ], + 'bonus_addition_expire_at' => '魔力加成過期時間', + 'is_expired' => '是否過期', + 'is_bonus_addition_expired' => '是否魔力加成過期', + 'is_wearing' => '是否佩戴', + 'increase_duration' => '延長時長', + 'increase_duration_help' => '單位:天。將過期時間在原基礎上統一延長', + 'update_expire_at' => '設置過期時間', + 'update_expire_at_help' => '將過期時間統一設置爲指定值', + 'bulk_action_increase' => '延長', + 'bulk_action_update' => '設置', + 'bulk_action_cancel' => '取消', + 'bulk_action_increase_expire_at' => '批量延長過期時間', + 'bulk_action_update_expire_at' => '批量設置過期時間', + 'bulk_action_cancel_expire_at' => '批量取消過期時間', + 'bulk_action_increase_bonus_addition_expire_at' => '批量延長魔力加成過期時間', + 'bulk_action_update_bonus_addition_expire_at' => '批量設置魔力加成過期時間', + 'bulk_action_cancel_bonus_addition_expire_at' => '批量取消魔力加成過期時間', ];