From cd0ef0d10629ecd6ac391de5bb9b4aa9b0b30940 Mon Sep 17 00:00:00 2001 From: xiaomlove <1939737565@qq.com> Date: Sun, 12 Oct 2025 03:48:04 +0700 Subject: [PATCH] Activity Log --- app/Filament/PageListSingle.php | 3 +- .../ActivityLogs/ActivityLogResource.php | 171 ++++++++++++++++++ .../ActivityLogs/Pages/ManageActivityLogs.php | 19 ++ app/Models/AgentAllow.php | 3 + app/Models/AgentDeny.php | 3 + app/Models/AudioCodec.php | 4 + app/Models/Avp.php | 4 +- app/Models/Category.php | 4 + app/Models/Codec.php | 4 + app/Models/DownloadSpeed.php | 4 + app/Models/Exam.php | 4 +- app/Models/ExamUser.php | 1 + app/Models/Icon.php | 4 + app/Models/Isp.php | 4 + app/Models/Medal.php | 3 + app/Models/Media.php | 4 + app/Models/MessageTemplate.php | 3 + app/Models/News.php | 4 + app/Models/OauthClient.php | 3 + app/Models/OauthProvider.php | 3 + app/Models/Poll.php | 4 + app/Models/PollAnswer.php | 4 + app/Models/Processing.php | 4 + app/Models/SearchBox.php | 3 + app/Models/SecondIcon.php | 4 + app/Models/SeedBoxRecord.php | 3 + app/Models/Setting.php | 3 + app/Models/Source.php | 4 + app/Models/Standard.php | 4 + app/Models/Tag.php | 4 + app/Models/Team.php | 4 + app/Models/Topic.php | 4 + app/Models/TorrentDenyReason.php | 3 + app/Models/TorrentState.php | 4 + app/Models/TrackerUrl.php | 3 + app/Models/Traits/NexusActivityLogTrait.php | 18 ++ app/Models/UploadSpeed.php | 4 + app/Models/User.php | 6 +- app/Models/UserMeta.php | 4 + composer.json | 1 + composer.lock | 93 +++++++++- config/activitylog.php | 52 ++++++ ...10_05_030400_create_activity_log_table.php | 27 +++ ...add_event_column_to_activity_log_table.php | 22 +++ ...atch_uuid_column_to_activity_log_table.php | 22 +++ resources/lang/en/activity-log.php | 7 + resources/lang/en/admin.php | 1 + resources/lang/zh_CN/activity-log.php | 7 + resources/lang/zh_CN/admin.php | 1 + resources/lang/zh_TW/activity-log.php | 7 + resources/lang/zh_TW/admin.php | 1 + 51 files changed, 571 insertions(+), 9 deletions(-) create mode 100644 app/Filament/Resources/System/ActivityLogs/ActivityLogResource.php create mode 100644 app/Filament/Resources/System/ActivityLogs/Pages/ManageActivityLogs.php create mode 100644 app/Models/Traits/NexusActivityLogTrait.php create mode 100644 config/activitylog.php create mode 100644 database/migrations/2025_10_05_030400_create_activity_log_table.php create mode 100644 database/migrations/2025_10_05_030401_add_event_column_to_activity_log_table.php create mode 100644 database/migrations/2025_10_05_030402_add_batch_uuid_column_to_activity_log_table.php create mode 100644 resources/lang/en/activity-log.php create mode 100644 resources/lang/zh_CN/activity-log.php create mode 100644 resources/lang/zh_TW/activity-log.php diff --git a/app/Filament/PageListSingle.php b/app/Filament/PageListSingle.php index 4f3c7ee4..ac4216ff 100644 --- a/app/Filament/PageListSingle.php +++ b/app/Filament/PageListSingle.php @@ -6,14 +6,13 @@ use Filament\Support\Enums\Width; use Filament\Tables\Enums\FiltersLayout; use Closure; use Filament\Resources\Pages\ManageRecords; -use Filament\Tables\Filters\Layout; use Illuminate\Database\Eloquent\Model; class PageListSingle extends ManageRecords { protected Width|string|null $maxContentWidth = 'full'; - protected function getTableFiltersLayout(): ?string + protected function getTableFiltersLayout(): FiltersLayout { return FiltersLayout::AboveContent; } diff --git a/app/Filament/Resources/System/ActivityLogs/ActivityLogResource.php b/app/Filament/Resources/System/ActivityLogs/ActivityLogResource.php new file mode 100644 index 00000000..e7fe3ebf --- /dev/null +++ b/app/Filament/Resources/System/ActivityLogs/ActivityLogResource.php @@ -0,0 +1,171 @@ +components([ + // + ]); + } + + public static function infolist(Schema $schema): Schema + { + return $schema + ->components([ + // + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + // subject 是被操作的对象,也是一个关联关系 + TextColumn::make('subject_type') + ->label(__("activity-log.subject_type")) + , + + TextColumn::make('subject_id') + ->label(__("activity-log.subject_id")) + , + + TextColumn::make('description') + ->label(__('label.description')) + , + + // causer 是操作者,一个关联关系 + TextColumn::make('causer.username') + ->label(__('label.operator')) + , + + TextColumn::make('created_at') + ->label(__('label.created_at')) + , + ]) + ->defaultSort('id', 'desc') + ->filters([ + Filter::make('created_at_begin') + ->schema([ + DateTimePicker::make('created_at_begin')->label(__('label.created_at_begin')), + ]) + ->query(function (Builder $query, array $data) { + return $query + ->when( + $data['created_at_begin'], + fn (Builder $query, $value): Builder => $query->where('created_at', '>=', $value), + ); + }), + Filter::make('created_at_end') + ->schema([ + DateTimePicker::make('created_at_end')->label(__('label.created_at_end')), + ]) + ->query(function (Builder $query, array $data) { + return $query + ->when( + $data['created_at_end'], + fn (Builder $query, $value): Builder => $query->where('created_at', '<=', $value), + ); + }), + Filter::make('operator') + ->schema([ + TextInput::make('operator')->label(__('label.operator')), + ]) + ->query(function (Builder $query, array $data) { + return $query + ->when( + $data['operator'], + fn (Builder $query, $value): Builder => $query->whereHas('causer', function (Builder $query) use ($value) { + $query->where('username', 'like', "%{$value}%"); + }), + ); + }), + ]) + ->recordActions([ + ViewAction::make('view_properties') + ->label(__("activity-log.view_properties")) + ->icon('heroicon-o-information-circle') + ->color('gray') + ->schema(function ($record) { + $fields = []; + // 动态地从 JSON 数据创建 TextEntry + // 注意:这里需要确保 $record->properties 是一个数组 + $properties = $record->properties->toArray(); + foreach ($properties as $key => $value) { + // 如果值是数组或对象,美化输出 + if (is_array($value) || is_object($value)) { + $value = json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + $fields[] = TextEntry::make($key)->html()->getStateUsing(fn() => "
$value");
+ } else {
+ $fields[] = TextEntry::make($key)->label(ucfirst($key));
+ }
+ }
+ return $fields;
+ })
+ ->action(null), // 无需执行任何操作
+ DeleteAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+
+ public static function getPages(): array
+ {
+ return [
+ 'index' => ManageActivityLogs::route('/'),
+ ];
+ }
+}
diff --git a/app/Filament/Resources/System/ActivityLogs/Pages/ManageActivityLogs.php b/app/Filament/Resources/System/ActivityLogs/Pages/ManageActivityLogs.php
new file mode 100644
index 00000000..3acba0f6
--- /dev/null
+++ b/app/Filament/Resources/System/ActivityLogs/Pages/ManageActivityLogs.php
@@ -0,0 +1,19 @@
+logOnlyDirty()
+ ->dontSubmitEmptyLogs()
+ ->logAll();
+ }
+}
diff --git a/app/Models/UploadSpeed.php b/app/Models/UploadSpeed.php
index 5630a91b..be764ce7 100644
--- a/app/Models/UploadSpeed.php
+++ b/app/Models/UploadSpeed.php
@@ -3,8 +3,12 @@
namespace App\Models;
+use App\Models\Traits\NexusActivityLogTrait;
+
class UploadSpeed extends NexusModel
{
+ use NexusActivityLogTrait;
+
protected $table = 'uploadspeed';
protected $fillable = ['name'];
diff --git a/app/Models/User.php b/app/Models/User.php
index f311115e..590bc7f0 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -4,6 +4,7 @@ namespace App\Models;
use App\Exceptions\NexusException;
use App\Http\Middleware\Locale;
+use App\Models\Traits\NexusActivityLogTrait;
use App\Repositories\ExamRepository;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
@@ -23,7 +24,7 @@ use NexusPlugin\Permission\Models\UserPermission;
class User extends Authenticatable implements FilamentUser, HasName
{
- use HasFactory, Notifiable, HasApiTokens;
+ use HasFactory, Notifiable, HasApiTokens, NexusActivityLogTrait;
public $timestamps = false;
@@ -633,7 +634,4 @@ class User extends Authenticatable implements FilamentUser, HasName
&& $this->accessToken && $this->accessToken->can($ability);
}
-
-
-
}
diff --git a/app/Models/UserMeta.php b/app/Models/UserMeta.php
index f570cc35..cfc8d45a 100644
--- a/app/Models/UserMeta.php
+++ b/app/Models/UserMeta.php
@@ -2,8 +2,12 @@
namespace App\Models;
+use App\Models\Traits\NexusActivityLogTrait;
+
class UserMeta extends NexusModel
{
+ use NexusActivityLogTrait;
+
protected $fillable = ['uid', 'meta_key', 'meta_value', 'status', 'deadline'];
public $timestamps = true;
diff --git a/composer.json b/composer.json
index 067044e2..3cbf57aa 100644
--- a/composer.json
+++ b/composer.json
@@ -55,6 +55,7 @@
"phpgangsta/googleauthenticator": "dev-master",
"rhilip/bencode": "^2.0",
"rlanvin/php-ip": "^3.0",
+ "spatie/laravel-activitylog": "^4.10",
"stichoza/google-translate-php": "^5.2"
},
"require-dev": {
diff --git a/composer.lock b/composer.lock
index 31e375d5..0013333a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "42746b91dd9127753319825b3ad3db54",
+ "content-hash": "282076e99007f806ed211e60021107e0",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@@ -7425,6 +7425,97 @@
],
"time": "2024-05-17T09:06:10+00:00"
},
+ {
+ "name": "spatie/laravel-activitylog",
+ "version": "4.10.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/laravel-activitylog.git",
+ "reference": "bb879775d487438ed9a99e64f09086b608990c10"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/laravel-activitylog/zipball/bb879775d487438ed9a99e64f09086b608990c10",
+ "reference": "bb879775d487438ed9a99e64f09086b608990c10",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/config": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
+ "illuminate/database": "^8.69 || ^9.27 || ^10.0 || ^11.0 || ^12.0",
+ "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
+ "php": "^8.1",
+ "spatie/laravel-package-tools": "^1.6.3"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "orchestra/testbench": "^6.23 || ^7.0 || ^8.0 || ^9.0 || ^10.0",
+ "pestphp/pest": "^1.20 || ^2.0 || ^3.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Spatie\\Activitylog\\ActivitylogServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Spatie\\Activitylog\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian De Deyne",
+ "email": "sebastian@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ },
+ {
+ "name": "Tom Witkowski",
+ "email": "dev.gummibeer@gmail.com",
+ "homepage": "https://gummibeer.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A very simple activity logger to monitor the users of your website or application",
+ "homepage": "https://github.com/spatie/activitylog",
+ "keywords": [
+ "activity",
+ "laravel",
+ "log",
+ "spatie",
+ "user"
+ ],
+ "support": {
+ "issues": "https://github.com/spatie/laravel-activitylog/issues",
+ "source": "https://github.com/spatie/laravel-activitylog/tree/4.10.2"
+ },
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ],
+ "time": "2025-06-15T06:59:49+00:00"
+ },
{
"name": "spatie/laravel-package-tools",
"version": "1.92.7",
diff --git a/config/activitylog.php b/config/activitylog.php
new file mode 100644
index 00000000..f1262f54
--- /dev/null
+++ b/config/activitylog.php
@@ -0,0 +1,52 @@
+ env('ACTIVITY_LOGGER_ENABLED', true),
+
+ /*
+ * When the clean-command is executed, all recording activities older than
+ * the number of days specified here will be deleted.
+ */
+ 'delete_records_older_than_days' => 365,
+
+ /*
+ * If no log name is passed to the activity() helper
+ * we use this default log name.
+ */
+ 'default_log_name' => 'default',
+
+ /*
+ * You can specify an auth driver here that gets user models.
+ * If this is null we'll use the current Laravel auth driver.
+ */
+ 'default_auth_driver' => null,
+
+ /*
+ * If set to true, the subject returns soft deleted models.
+ */
+ 'subject_returns_soft_deleted_models' => false,
+
+ /*
+ * This model will be used to log activity.
+ * It should implement the Spatie\Activitylog\Contracts\Activity interface
+ * and extend Illuminate\Database\Eloquent\Model.
+ */
+ 'activity_model' => \Spatie\Activitylog\Models\Activity::class,
+
+ /*
+ * This is the name of the table that will be created by the migration and
+ * used by the Activity model shipped with this package.
+ */
+ 'table_name' => env('ACTIVITY_LOGGER_TABLE_NAME', 'activity_log'),
+
+ /*
+ * This is the database connection that will be used by the migration and
+ * the Activity model shipped with this package. In case it's not set
+ * Laravel's database.default will be used instead.
+ */
+ 'database_connection' => env('ACTIVITY_LOGGER_DB_CONNECTION'),
+];
diff --git a/database/migrations/2025_10_05_030400_create_activity_log_table.php b/database/migrations/2025_10_05_030400_create_activity_log_table.php
new file mode 100644
index 00000000..7c05bc89
--- /dev/null
+++ b/database/migrations/2025_10_05_030400_create_activity_log_table.php
@@ -0,0 +1,27 @@
+create(config('activitylog.table_name'), function (Blueprint $table) {
+ $table->bigIncrements('id');
+ $table->string('log_name')->nullable();
+ $table->text('description');
+ $table->nullableMorphs('subject', 'subject');
+ $table->nullableMorphs('causer', 'causer');
+ $table->json('properties')->nullable();
+ $table->timestamps();
+ $table->index('log_name');
+ });
+ }
+
+ public function down()
+ {
+ Schema::connection(config('activitylog.database_connection'))->dropIfExists(config('activitylog.table_name'));
+ }
+}
diff --git a/database/migrations/2025_10_05_030401_add_event_column_to_activity_log_table.php b/database/migrations/2025_10_05_030401_add_event_column_to_activity_log_table.php
new file mode 100644
index 00000000..7b797fd5
--- /dev/null
+++ b/database/migrations/2025_10_05_030401_add_event_column_to_activity_log_table.php
@@ -0,0 +1,22 @@
+table(config('activitylog.table_name'), function (Blueprint $table) {
+ $table->string('event')->nullable()->after('subject_type');
+ });
+ }
+
+ public function down()
+ {
+ Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
+ $table->dropColumn('event');
+ });
+ }
+}
diff --git a/database/migrations/2025_10_05_030402_add_batch_uuid_column_to_activity_log_table.php b/database/migrations/2025_10_05_030402_add_batch_uuid_column_to_activity_log_table.php
new file mode 100644
index 00000000..8f7db665
--- /dev/null
+++ b/database/migrations/2025_10_05_030402_add_batch_uuid_column_to_activity_log_table.php
@@ -0,0 +1,22 @@
+table(config('activitylog.table_name'), function (Blueprint $table) {
+ $table->uuid('batch_uuid')->nullable()->after('properties');
+ });
+ }
+
+ public function down()
+ {
+ Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
+ $table->dropColumn('batch_uuid');
+ });
+ }
+}
diff --git a/resources/lang/en/activity-log.php b/resources/lang/en/activity-log.php
new file mode 100644
index 00000000..0512d2e3
--- /dev/null
+++ b/resources/lang/en/activity-log.php
@@ -0,0 +1,7 @@
+ 'View properties',
+ 'subject_type' => 'Model',
+ 'subject_id' => 'Model ID',
+];
diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php
index 8841be55..3a9f951f 100644
--- a/resources/lang/en/admin.php
+++ b/resources/lang/en/admin.php
@@ -48,6 +48,7 @@ return [
'user_modify_logs' => 'User modify logs',
'message_templates' => 'Message templates',
'tracker_url' => 'Tracker URL',
+ 'activity_logs' => 'Activity Logs',
],
'resources' => [
'agent_allow' => [
diff --git a/resources/lang/zh_CN/activity-log.php b/resources/lang/zh_CN/activity-log.php
new file mode 100644
index 00000000..4162286d
--- /dev/null
+++ b/resources/lang/zh_CN/activity-log.php
@@ -0,0 +1,7 @@
+ '查看属性',
+ 'subject_type' => '模型',
+ 'subject_id' => '模型 ID',
+];
diff --git a/resources/lang/zh_CN/admin.php b/resources/lang/zh_CN/admin.php
index d93da606..3616c5d2 100644
--- a/resources/lang/zh_CN/admin.php
+++ b/resources/lang/zh_CN/admin.php
@@ -48,6 +48,7 @@ return [
'tracker_url' => 'Tracker URL',
'announce_logs' => '汇报记录',
'announce_monitor' => '汇报监控',
+ 'activity_logs' => '操作日志',
],
'resources' => [
'agent_allow' => [
diff --git a/resources/lang/zh_TW/activity-log.php b/resources/lang/zh_TW/activity-log.php
new file mode 100644
index 00000000..14006928
--- /dev/null
+++ b/resources/lang/zh_TW/activity-log.php
@@ -0,0 +1,7 @@
+ '查看屬性',
+ 'subject_type' => '模型',
+ 'subject_id' => '模型 ID',
+];
diff --git a/resources/lang/zh_TW/admin.php b/resources/lang/zh_TW/admin.php
index acb51469..1367941e 100644
--- a/resources/lang/zh_TW/admin.php
+++ b/resources/lang/zh_TW/admin.php
@@ -50,6 +50,7 @@ return [
'tracker_url' => 'Tracker URL',
'announce_logs' => '匯報記錄',
'announce_monitor' => '匯報監控',
+ 'activity_logs' => '操作日志',
],
'resources' => [
'agent_allow' => [