improve hr + agent update

This commit is contained in:
xiaomlove
2025-07-21 20:55:30 +07:00
parent 84b554f102
commit ae08039323
26 changed files with 590 additions and 23 deletions

View File

@@ -2,6 +2,15 @@
namespace App\Enums;
use App\Events\AgentAllowCreated;
use App\Events\AgentAllowDeleted;
use App\Events\AgentAllowUpdated;
use App\Events\AgentDenyCreated;
use App\Events\AgentDenyDeleted;
use App\Events\AgentDenyUpdated;
use App\Events\HitAndRunCreated;
use App\Events\HitAndRunDeleted;
use App\Events\HitAndRunUpdated;
use App\Events\MessageCreated;
use App\Events\NewsCreated;
use App\Events\SnatchedUpdated;
@@ -13,6 +22,9 @@ use App\Events\UserDeleted;
use App\Events\UserDisabled;
use App\Events\UserEnabled;
use App\Events\UserUpdated;
use App\Models\AgentAllow;
use App\Models\AgentDeny;
use App\Models\HitAndRun;
use App\Models\Message;
use App\Models\News;
use App\Models\Snatch;
@@ -32,6 +44,18 @@ final class ModelEventEnum {
const NEWS_CREATED = 'news_created';
const HIT_AND_RUN_CREATED = 'hit_and_run_created';
const HIT_AND_RUN_UPDATED = 'hit_and_run_updated';
const HIT_AND_RUN_DELETED = 'hit_and_run_deleted';
const AGENT_ALLOW_CREATED = 'agent_allow_created';
const AGENT_ALLOW_UPDATED = 'agent_allow_updated';
const AGENT_ALLOW_DELETED = 'agent_allow_deleted';
const AGENT_DENY_CREATED = 'agent_deny_created';
const AGENT_DENY_UPDATED = 'agent_deny_updated';
const AGENT_DENY_DELETED = 'agent_deny_deleted';
const SNATCHED_UPDATED = 'snatched_updated';
const MESSAGE_CREATED = 'message_created';
@@ -51,5 +75,17 @@ final class ModelEventEnum {
self::SNATCHED_UPDATED => ['event' => SnatchedUpdated::class, 'model' => Snatch::class],
self::MESSAGE_CREATED => ['event' => MessageCreated::class, 'model' => Message::class],
self::HIT_AND_RUN_CREATED => ['event' => HitAndRunCreated::class, 'model' => HitAndRun::class],
self::HIT_AND_RUN_UPDATED => ['event' => HitAndRunUpdated::class, 'model' => HitAndRun::class],
self::HIT_AND_RUN_DELETED => ['event' => HitAndRunDeleted::class, 'model' => HitAndRun::class],
self::AGENT_ALLOW_CREATED => ['event' => AgentAllowCreated::class, 'model' => AgentAllow::class],
self::AGENT_ALLOW_UPDATED => ['event' => AgentAllowUpdated::class, 'model' => AgentAllow::class],
self::AGENT_ALLOW_DELETED => ['event' => AgentAllowDeleted::class, 'model' => AgentAllow::class],
self::AGENT_DENY_CREATED => ['event' => AgentDenyCreated::class, 'model' => AgentDeny::class],
self::AGENT_DENY_UPDATED => ['event' => AgentDenyUpdated::class, 'model' => AgentDeny::class],
self::AGENT_DENY_DELETED => ['event' => AgentDenyDeleted::class, 'model' => AgentDeny::class],
];
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class AgentAllowCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public ?Model $model = null;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class AgentAllowDeleted
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public ?Model $model = null;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class AgentAllowUpdated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public ?Model $model = null;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class AgentDenyCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public ?Model $model = null;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class AgentDenyDeleted
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public ?Model $model = null;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class AgentDenyUpdated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public ?Model $model = null;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class HitAndRunCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public ?Model $model = null;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class HitAndRunDeleted
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public ?Model $model = null;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class HitAndRunUpdated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public ?Model $model = null;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -20,6 +20,7 @@ use Illuminate\Support\Facades\Auth;
use Illuminate\Support\HtmlString;
use Filament\Infolists\Components;
use Filament\Infolists;
use Nette\Utils\Html;
class HitAndRunResource extends Resource
{
@@ -146,7 +147,7 @@ class HitAndRunResource extends Resource
->label(__("label.inspect_time_left"))
,
Infolists\Components\TextEntry::make('comment')
->formatStateUsing(fn ($record) => nl2br($record->comment))
->formatStateUsing(fn ($record) => new HtmlString(nl2br($record->comment)))
->label(__("label.comment"))
,
Infolists\Components\TextEntry::make('created_at')

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Listeners;
use App\Models\Torrent;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class ClearTorrentCache implements ShouldQueue
{
/**
* Create the event listener.
*/
public function __construct()
{
//
}
/**
* Handle the event.
*/
public function handle(object $event): void
{
$torrentId = $event->model?->id ?? 0;
if ($torrentId > 0) {
$infoHash = Torrent::query()->where('id', $torrentId)->value('info_hash');
clear_torrent_cache($infoHash);
do_log("success clear torrent: $torrentId cache with info_hash: " . rawurlencode($infoHash));
} else {
do_log("no torrent id", 'error');
}
}
}

View File

@@ -2,6 +2,8 @@
namespace App\Models;
use App\Enums\ModelEventEnum;
class AgentAllow extends NexusModel
{
protected $table = 'agent_allowed_family';
@@ -20,6 +22,19 @@ class AgentAllow extends NexusModel
self::MATCH_TYPE_HEX => 'hex',
];
protected static function booted()
{
static::created(function ($model) {
fire_event(ModelEventEnum::AGENT_ALLOW_CREATED, $model);
});
static::updated(function ($model) {
fire_event(ModelEventEnum::AGENT_ALLOW_UPDATED, $model);
});
static::deleted(function ($model) {
fire_event(ModelEventEnum::AGENT_ALLOW_DELETED, $model);
});
}
public function denies(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(AgentDeny::class, 'family_id');

View File

@@ -2,6 +2,8 @@
namespace App\Models;
use App\Enums\ModelEventEnum;
class AgentDeny extends NexusModel
{
protected $table = 'agent_allowed_exception';
@@ -10,6 +12,19 @@ class AgentDeny extends NexusModel
'family_id', 'name', 'peer_id', 'agent', 'comment'
];
protected static function booted()
{
static::created(function ($model) {
fire_event(ModelEventEnum::AGENT_DENY_CREATED, $model);
});
static::updated(function ($model) {
fire_event(ModelEventEnum::AGENT_DENY_UPDATED, $model);
});
static::deleted(function ($model) {
fire_event(ModelEventEnum::AGENT_DENY_DELETED, $model);
});
}
public function family(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(AgentAllow::class, 'family_id');

View File

@@ -2,9 +2,10 @@
namespace App\Models;
use App\Enums\ModelEventEnum;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidArgumentException;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Nexus\Database\NexusDB;
class HitAndRun extends NexusModel
{
@@ -43,6 +44,31 @@ class HitAndRun extends NexusModel
const MINIMUM_IGNORE_USER_CLASS = User::CLASS_VIP;
protected static function booted()
{
static::saved(function ($model) {
self::clearCache($model);
});
static::deleted(function ($model) {
self::clearCache($model, ModelEventEnum::HIT_AND_RUN_DELETED);
});
}
public static function getCacheKey(int $userId, int $torrentId): string
{
return sprintf("hit_and_run:user:%d:torrent:%d", $userId, $torrentId);
}
public static function clearCache(HitAndRun $hitAndRun, string $event = ModelEventEnum::HIT_AND_RUN_UPDATED): void
{
NexusDB::cache_del(self::getCacheKey($hitAndRun->uid, $hitAndRun->torrent_id));
fire_event($event, $hitAndRun);
do_log(sprintf(
"userId: %s, torrentId: %s hit and run cache cleared, and trigger event: %s",
$hitAndRun->uid, $hitAndRun->torrent_id, $event
));
}
protected function seedTimeRequired(): Attribute
{
return new Attribute(

View File

@@ -8,6 +8,7 @@ use App\Events\TorrentDeleted;
use App\Events\TorrentUpdated;
use App\Events\UserDeleted;
use App\Events\UserDisabled;
use App\Listeners\ClearTorrentCache;
use App\Listeners\DeductUserBonusWhenTorrentDeleted;
use App\Listeners\FetchTorrentImdb;
use App\Listeners\FetchTorrentPTGen;
@@ -48,6 +49,7 @@ class EventServiceProvider extends ServiceProvider
SyncTorrentToElasticsearch::class,
SyncTorrentToMeilisearch::class,
SendEmailNotificationWhenTorrentCreated::class,
ClearTorrentCache::class,
],
TorrentDeleted::class => [
DeductUserBonusWhenTorrentDeleted::class,

View File

@@ -1,6 +1,7 @@
<?php
namespace App\Repositories;
use App\Enums\ModelEventEnum;
use App\Models\HitAndRun;
use App\Models\Message;
use App\Models\SearchBox;
@@ -60,16 +61,27 @@ class HitAndRunRepository extends BaseRepository
{
$model = HitAndRun::query()->findOrFail($id);
$result = $model->delete();
HitAndRun::clearCache($model, ModelEventEnum::HIT_AND_RUN_DELETED);
return $result;
}
public function bulkDelete(array $params, User $user)
{
$result = $this->getBulkQuery($params)->delete();
$baseQuery = $this->getBulkQuery($params);
$list = $baseQuery->clone()->get();
if ($list->isEmpty()) {
return 0;
}
$result = $baseQuery->delete();
do_log(sprintf(
'user: %s bulk delete by filter: %s, result: %s',
$user->id, json_encode($params), json_encode($result)
), 'alert');
if ($result) {
foreach ($list as $record) {
HitAndRun::clearCache($record, ModelEventEnum::HIT_AND_RUN_DELETED);
}
}
return $result;
}
@@ -204,7 +216,8 @@ class HitAndRunRepository extends BaseRepository
//check leech time
if (isset($setting['leech_time_minimum']) && $setting['leech_time_minimum'] > 0) {
$targetLeechTime = $row->snatch->leechtime;
// $targetLeechTime = $row->snatch->leechtime;
$targetLeechTime = $row->leech_time_no_seeder;//使用自身记录的值
$requireLeechTime = bcmul($setting['leech_time_minimum'], 3600);
do_log("$currentLog, targetLeechTime: $targetLeechTime, requireLeechTime: $requireLeechTime");
if ($targetLeechTime >= $requireLeechTime) {
@@ -331,6 +344,7 @@ class HitAndRunRepository extends BaseRepository
} else {
do_log($hitAndRun->toJson() . ", [$logPrefix], user do not accept hr_reached notification", 'notice');
}
HitAndRun::clearCache($hitAndRun);
return true;
}
@@ -369,7 +383,7 @@ class HitAndRunRepository extends BaseRepository
], $hitAndRun->user->locale),
];
Message::query()->insert($message);
HitAndRun::clearCache($hitAndRun);
return true;
}
@@ -423,6 +437,7 @@ class HitAndRunRepository extends BaseRepository
'reason' => $comment
];
UserBanLog::query()->insert($userBanLog);
fire_event(ModelEventEnum::USER_UPDATED, $user);
do_log("Disable user: " . nexus_json_encode($userBanLog));
}
}
@@ -499,16 +514,25 @@ class HitAndRunRepository extends BaseRepository
public function bulkPardon(array $params, User $user): int
{
$query = $this->getBulkQuery($params)->whereIn('status', $this->getCanPardonStatus());
$baseQuery = $this->getBulkQuery($params)->whereIn('status', $this->getCanPardonStatus());
$list = $baseQuery->clone()->get();
if ($list->isEmpty()) {
return 0;
}
$update = [
'status' => HitAndRun::STATUS_PARDONED,
'comment' => $this->getCommentUpdateRaw(addslashes('Pardon by ' . $user->username)),
];
$affected = $query->update($update);
$affected = $baseQuery->update($update);
do_log(sprintf(
'user: %s bulk pardon by filter: %s, affected: %s',
$user->id, json_encode($params), $affected
), 'alert');
if ($affected) {
foreach ($list as $item) {
HitAndRun::clearCache($item);
}
}
return $affected;
}