From 9f3730b9ac4a323a6db237703b485dc78e2c4ae3 Mon Sep 17 00:00:00 2001 From: xiaomlove Date: Sun, 4 May 2025 00:00:41 +0700 Subject: [PATCH] improve claim + horizon instead queue:work --- app/Console/Commands/Test.php | 5 +- app/Jobs/SettleClaim.php | 34 +++ app/Providers/NexusHorizonServiceProvider.php | 35 +++ app/Repositories/ClaimRepository.php | 176 ++++++++------ composer.json | 1 + composer.lock | 82 ++++++- config/app.php | 1 + config/horizon.php | 214 ++++++++++++++++++ include/constants.php | 2 +- public/claim.php | 94 +++++++- resources/lang/zh_CN/nexus.php | 4 + 11 files changed, 577 insertions(+), 71 deletions(-) create mode 100644 app/Jobs/SettleClaim.php create mode 100644 app/Providers/NexusHorizonServiceProvider.php create mode 100644 config/horizon.php diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index 804bf64d..39efff59 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -2,6 +2,7 @@ namespace App\Console\Commands; +use App\Jobs\SettleClaim; use App\Models\ExamUser; use App\Models\Language; use App\Models\PersonalAccessToken; @@ -56,7 +57,9 @@ class Test extends Command */ public function handle() { - Language::updateTransStatus(); + $a = [1,2,3]; + $b = array_slice($a, 0, 2); + dd($a, $b); } } diff --git a/app/Jobs/SettleClaim.php b/app/Jobs/SettleClaim.php new file mode 100644 index 00000000..cb5ad92f --- /dev/null +++ b/app/Jobs/SettleClaim.php @@ -0,0 +1,34 @@ +userId; + $logMsg = "userId: $userId"; + $rep = new ClaimRepository(); + $result = $rep->settleUser($userId, false, false, true); + do_log("$logMsg, result: " . var_export($result, true)); + } +} diff --git a/app/Providers/NexusHorizonServiceProvider.php b/app/Providers/NexusHorizonServiceProvider.php new file mode 100644 index 00000000..39ddda5b --- /dev/null +++ b/app/Providers/NexusHorizonServiceProvider.php @@ -0,0 +1,35 @@ +class >= User::CLASS_SYSOP; + }); + } + +} diff --git a/app/Repositories/ClaimRepository.php b/app/Repositories/ClaimRepository.php index 1208940c..c040f262 100644 --- a/app/Repositories/ClaimRepository.php +++ b/app/Repositories/ClaimRepository.php @@ -1,15 +1,15 @@ with(['family']); @@ -125,7 +128,7 @@ class ClaimRepository extends BaseRepository { $startOfThisMonth = Carbon::now()->startOfMonth(); $query = Claim::query() - ->select(['uid']) + ->select(['uid', 'count(*) as count']) ->where("created_at", "<", $startOfThisMonth) ->where(function (Builder $query) use ($startOfThisMonth) { $query->where('last_settle_at', '<', $startOfThisMonth)->orWhereNull('last_settle_at'); @@ -145,18 +148,22 @@ class ClaimRepository extends BaseRepository do_log("get counts: " . $result->count()); foreach ($result as $row) { $uid = $row->uid; - do_log("$logPrefix, begin to settle user: $uid..."); - try { - $result = $this->settleUser($uid); - do_log("$logPrefix, settle user: $uid done!, result: " . var_export($result, true)); - if ($result) { - $successCount++; - } else { + if ($row->count > self::SETTLE_PAGINATE_STARTS) { + SettleClaim::dispatch($uid); + } else { + do_log("$logPrefix, begin to settle user: $uid..."); + try { + $result = $this->settleUser($uid); + do_log("$logPrefix, settle user: $uid done!, result: " . var_export($result, true)); + if ($result) { + $successCount++; + } else { + $failCount++; + } + } catch (\Throwable $exception) { + do_log("$logPrefix, settle user: $uid fail!, error: " . $exception->getMessage() . $exception->getTraceAsString(), 'error'); $failCount++; } - } catch (\Throwable $exception) { - do_log("$logPrefix, settle user: $uid fail!, error: " . $exception->getMessage() . $exception->getTraceAsString(), 'error'); - $failCount++; } } $page++; @@ -164,22 +171,22 @@ class ClaimRepository extends BaseRepository return ['success_count' => $successCount, 'fail_count' => $failCount]; } - public function settleUser($uid, $force = false, $test = false): bool + public function settleUser($uid, $force = false, $test = false, $paginate = false): bool { + $logMsg = sprintf("uid: %s, force: %s, test: %s, paginate: %s", $uid, $force, $test, $paginate); $hasRoleWorkSeeding = has_role_work_seeding($uid); if ($hasRoleWorkSeeding) { - do_log("uid: $uid, filter: user_has_role_work_seeding => true, skip"); + do_log("$logMsg, filter: user_has_role_work_seeding => true, skip"); return false; } $now = Carbon::now(); $startOfThisMonth = $now->clone()->startOfMonth(); $user = User::query()->with('language')->findOrFail($uid); - $list = Claim::query() + $baseQuery = Claim::query() ->where('uid', $uid) ->where("created_at", "<", $startOfThisMonth) - ->with(['snatch', 'torrent' => fn ($query) => $query->select(Torrent::$commentFields)]) - ->get() ; + $totalRecordCount = (clone $baseQuery)->count(); $seedTimeRequiredHours = Claim::getConfigStandardSeedTimeHours(); $uploadedRequiredTimes = Claim::getConfigStandardUploadedTimes(); $bonusMultiplier = Claim::getConfigBonusMultiplier(); @@ -189,53 +196,40 @@ class ClaimRepository extends BaseRepository $seedTimeCaseWhen = $uploadedCaseWhen = []; $toDelClaimId = []; do_log( - "uid: $uid, claim torrent count: " . $list->count() + "$logMsg, claim torrent count: $totalRecordCount" . ", seedTimeRequiredHours: $seedTimeRequiredHours" . ", uploadedRequiredTimes: $uploadedRequiredTimes" . ", bonusMultiplier: $bonusMultiplier" . ", bonusDeduct: $bonusDeduct" ); - foreach ($list as $row) { - if ($row->last_settle_at && $row->last_settle_at->gte($startOfThisMonth)) { - do_log("ID: {$row->id} already settle", 'alert'); - if (!$force) { - do_log("No force, return", 'alert'); + if ($paginate) { + $page = 1; + while (true) { + $list = (clone $baseQuery)->forPage($page, self::SETTLE_PAGINATE_STARTS)->get(); + if ($list->isEmpty()) { + break; + } + $handleResult = $this->handleClaimSettlement( + $uid, $list, $startOfThisMonth, $seedTimeRequiredHours, $uploadedRequiredTimes, + $reachedTorrentIdArr,$unReachedTorrentIdArr,$remainTorrentIdArr, $seedTimeCaseWhen, $uploadedCaseWhen, + $toUpdateIdArr, $unReachedIdArr, $toDelClaimId, $totalSeedTime, $force + ); + if ($handleResult === false) { + do_log("$logMsg, handleClaimSettlement result false, return"); return false; } + $page++; } - if (!$row->snatch) { - $toDelClaimId[$row->id] = $row->id; - do_log("No snatch, continue", 'alert'); - continue; - } - if (!$row->torrent) { - $toDelClaimId[$row->id] = $row->id; - do_log("No torrent, continue", 'alert'); - continue; - } - if ( - bcsub($row->snatch->seedtime, $row->seed_time_begin) >= $seedTimeRequiredHours * 3600 - || bcsub($row->snatch->uploaded, $row->uploaded_begin) >= $uploadedRequiredTimes * $row->torrent->size - ) { - do_log("[REACHED], uid: $uid, torrent: " . $row->torrent_id); - $reachedTorrentIdArr[] = $row->torrent_id; - $toUpdateIdArr[] = $row->id; - $totalSeedTime += bcsub($row->snatch->seedtime, $row->seed_time_begin); - $seedTimeCaseWhen[] = sprintf('when %s then %s', $row->id, $row->snatch->seedtime); - $uploadedCaseWhen[] = sprintf('when %s then %s', $row->id, $row->snatch->uploaded); - } else { - $targetStartOfMonth = $row->created_at->startOfMonth(); - if ($startOfThisMonth->diffInMonths($targetStartOfMonth, true) > 1) { - do_log("[UNREACHED_REMOVE], uid: $uid, torrent: " . $row->torrent_id); - $unReachedIdArr[] = $row->id; - $unReachedTorrentIdArr[] = $row->torrent_id; - } else { - do_log("[UNREACHED_FIRST_MONTH], uid: $uid, torrent: " . $row->torrent_id); - $seedTimeCaseWhen[] = sprintf('when %s then %s', $row->id, $row->snatch->seedtime); - $uploadedCaseWhen[] = sprintf('when %s then %s', $row->id, $row->snatch->uploaded); - $toUpdateIdArr[] = $row->id; - $remainTorrentIdArr[] = $row->torrent_id; - } + } else { + $list = $baseQuery->with(['snatch', 'torrent' => fn ($query) => $query->select(Torrent::$commentFields)])->get(); + $handleResult = $this->handleClaimSettlement( + $uid, $list, $startOfThisMonth, $seedTimeRequiredHours, $uploadedRequiredTimes, + $reachedTorrentIdArr,$unReachedTorrentIdArr,$remainTorrentIdArr, $seedTimeCaseWhen, $uploadedCaseWhen, + $toUpdateIdArr, $unReachedIdArr, $toDelClaimId, $totalSeedTime, $force + ); + if ($handleResult === false) { + do_log("$logMsg, handleClaimSettlement result false, return"); + return false; } } $bonusResult = calculate_seed_bonus($uid, $reachedTorrentIdArr); @@ -316,24 +310,25 @@ class ClaimRepository extends BaseRepository $msg[] = nexus_trans('claim.msg_title', ['month' => $now->clone()->subMonths(1)->format('Y-m')], $locale); $msg[] = nexus_trans('claim.claim_total', [ 'total' => count($allTorrentIdArr)], $locale); - $reachList = collect($reachedTorrentIdArr)->map( + //列表数据只取部分展示 + $reachList = collect(array_slice($reachedTorrentIdArr, 0, self::SETTLE_MSG_SLICE_COUNT))->map( fn($item) => sprintf("[url=details.php?id=%s]%s[/url]", $item, $torrentInfo->get($item)->name) )->implode("\n"); $msg[] = nexus_trans("claim.claim_reached_counts", ['counts' => count($reachedTorrentIdArr)], $locale) . "\n$reachList"; $msg[] = nexus_trans( "claim.claim_reached_summary", [ - 'bonus_per_hour' => number_format($bonusResult['seed_bonus'], 2), - 'hours'=> number_format($seedTimeHoursAvg, 2), - 'bonus_total'=> number_format($bonusFinal, 2) - ], $locale + 'bonus_per_hour' => number_format($bonusResult['seed_bonus'], 2), + 'hours'=> number_format($seedTimeHoursAvg, 2), + 'bonus_total'=> number_format($bonusFinal, 2) + ], $locale ); - $remainList = collect($remainTorrentIdArr)->map( + $remainList = collect(array_slice($remainTorrentIdArr, 0, self::SETTLE_MSG_SLICE_COUNT))->map( fn($item) => sprintf("[url=details.php?id=%s]%s[/url]", $item, $torrentInfo->get($item)->name) )->implode("\n"); $msg[] = nexus_trans("claim.claim_unreached_remain_counts", ['counts' => count($remainTorrentIdArr)], $locale) . "\n$remainList"; - $unReachList = collect($unReachedTorrentIdArr)->map( + $unReachList = collect(array_slice($unReachedTorrentIdArr, 0, self::SETTLE_MSG_SLICE_COUNT))->map( fn($item) => sprintf("[url=details.php?id=%s]%s[/url]", $item, $torrentInfo->get($item)->name) )->implode("\n"); $msg[] = nexus_trans("claim.claim_unreached_remove_counts", ['counts' => count($unReachedTorrentIdArr)], $locale) . "\n$unReachList"; @@ -353,6 +348,59 @@ class ClaimRepository extends BaseRepository ]; } + private function handleClaimSettlement( + int $uid, Collection $list, Carbon $startOfThisMonth, int $seedTimeRequiredHours, int $uploadedRequiredTimes, + array &$reachedTorrentIdArr, array &$unReachedTorrentIdArr, array &$remainTorrentIdArr, array &$seedTimeCaseWhen, array &$uploadedCaseWhen, + array &$toUpdateIdArr, array &$unReachedIdArr, array &$toDelIdArr, int &$totalSeedTime, bool $force + ): bool|null + { + foreach ($list as $row) { + $logItem = "uid: $uid, claimId: {$row['id']}"; + if ($row->last_settle_at && $row->last_settle_at->gte($startOfThisMonth)) { + do_log("$logItem already settle", 'alert'); + if (!$force) { + do_log("$logItem,already settle, No force, return", 'alert'); + return false; + } + } + if (!$row->snatch) { + $toDelIdArr[$row->id] = $row->id; + do_log("$logItem, No snatch, continue", 'alert'); + continue; + } + if (!$row->torrent) { + $toDelIdArr[$row->id] = $row->id; + do_log("$logItem, No torrent, continue", 'alert'); + continue; + } + if ( + bcsub($row->snatch->seedtime, $row->seed_time_begin) >= $seedTimeRequiredHours * 3600 + || bcsub($row->snatch->uploaded, $row->uploaded_begin) >= $uploadedRequiredTimes * $row->torrent->size + ) { + do_log("$logItem, [REACHED], uid: $uid, torrent: " . $row->torrent_id); + $reachedTorrentIdArr[] = $row->torrent_id; + $toUpdateIdArr[] = $row->id; + $totalSeedTime += (int)bcsub($row->snatch->seedtime, $row->seed_time_begin); + $seedTimeCaseWhen[] = sprintf('when %s then %s', $row->id, $row->snatch->seedtime); + $uploadedCaseWhen[] = sprintf('when %s then %s', $row->id, $row->snatch->uploaded); + } else { + $targetStartOfMonth = $row->created_at->startOfMonth(); + if ($startOfThisMonth->diffInMonths($targetStartOfMonth, true) > 1) { + do_log("$logItem, [UNREACHED_REMOVE], uid: $uid, torrent: " . $row->torrent_id); + $unReachedIdArr[] = $row->id; + $unReachedTorrentIdArr[] = $row->torrent_id; + } else { + do_log("$logItem, [UNREACHED_FIRST_MONTH], uid: $uid, torrent: " . $row->torrent_id); + $seedTimeCaseWhen[] = sprintf('when %s then %s', $row->id, $row->snatch->seedtime); + $uploadedCaseWhen[] = sprintf('when %s then %s', $row->id, $row->snatch->uploaded); + $toUpdateIdArr[] = $row->id; + $remainTorrentIdArr[] = $row->torrent_id; + } + } + } + return null; + } + public function buildActionButtons($torrentId, $claimData, $reload = 0): string { $buttonHtml = ''; diff --git a/composer.json b/composer.json index e6f0a8a8..e4cd56d7 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,7 @@ "imdbphp/imdbphp": "^8.0", "irazasyed/telegram-bot-sdk": "^3.11", "laravel/framework": "^12.0", + "laravel/horizon": "^5.31", "laravel/passport": "^12.0", "laravel/sanctum": "^4.0", "laravel/tinker": "^2.5", diff --git a/composer.lock b/composer.lock index d01950a0..e825012e 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": "89a4f7598570b74cd2c86a96b446c4ba", + "content-hash": "52be7484aaea27bc7c5e7a9713f9e965", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -2999,6 +2999,86 @@ }, "time": "2025-05-01T16:13:12+00:00" }, + { + "name": "laravel/horizon", + "version": "v5.31.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/horizon.git", + "reference": "e6068c65be6c02a01e34531abf3143fab91f0de0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/horizon/zipball/e6068c65be6c02a01e34531abf3143fab91f0de0", + "reference": "e6068c65be6c02a01e34531abf3143fab91f0de0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcntl": "*", + "ext-posix": "*", + "illuminate/contracts": "^9.21|^10.0|^11.0|^12.0", + "illuminate/queue": "^9.21|^10.0|^11.0|^12.0", + "illuminate/support": "^9.21|^10.0|^11.0|^12.0", + "nesbot/carbon": "^2.17|^3.0", + "php": "^8.0", + "ramsey/uuid": "^4.0", + "symfony/console": "^6.0|^7.0", + "symfony/error-handler": "^6.0|^7.0", + "symfony/polyfill-php83": "^1.28", + "symfony/process": "^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.0|^10.4|^11.5", + "predis/predis": "^1.1|^2.0" + }, + "suggest": { + "ext-redis": "Required to use the Redis PHP driver.", + "predis/predis": "Required when not using the Redis PHP driver (^1.1|^2.0)." + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Horizon": "Laravel\\Horizon\\Horizon" + }, + "providers": [ + "Laravel\\Horizon\\HorizonServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Horizon\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Dashboard and code-driven configuration for Laravel queues.", + "keywords": [ + "laravel", + "queue" + ], + "support": { + "issues": "https://github.com/laravel/horizon/issues", + "source": "https://github.com/laravel/horizon/tree/v5.31.2" + }, + "time": "2025-04-18T12:57:39+00:00" + }, { "name": "laravel/passport", "version": "v12.4.2", diff --git a/config/app.php b/config/app.php index 58950ea3..cad5e6ad 100644 --- a/config/app.php +++ b/config/app.php @@ -173,6 +173,7 @@ return [ App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, + App\Providers\NexusHorizonServiceProvider::class, App\Providers\Filament\AppPanelProvider::class, App\Providers\RouteServiceProvider::class, diff --git a/config/horizon.php b/config/horizon.php new file mode 100644 index 00000000..47b87331 --- /dev/null +++ b/config/horizon.php @@ -0,0 +1,214 @@ + env('HORIZON_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | Horizon Path + |-------------------------------------------------------------------------- + | + | This is the URI path where Horizon will be accessible from. Feel free + | to change this path to anything you like. Note that the URI will not + | affect the paths of its internal API that aren't exposed to users. + | + */ + + 'path' => env('HORIZON_PATH', 'horizon'), + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Connection + |-------------------------------------------------------------------------- + | + | This is the name of the Redis connection where Horizon will store the + | meta information required for it to function. It includes the list + | of supervisors, failed jobs, job metrics, and other information. + | + */ + + 'use' => 'default', + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Prefix + |-------------------------------------------------------------------------- + | + | This prefix will be used when storing all Horizon data in Redis. You + | may modify the prefix when you are running multiple installations + | of Horizon on the same server so that they don't have problems. + | + */ + + 'prefix' => env( + 'HORIZON_PREFIX', + Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:' + ), + + /* + |-------------------------------------------------------------------------- + | Horizon Route Middleware + |-------------------------------------------------------------------------- + | + | These middleware will get attached onto each Horizon route, giving you + | the chance to add your own middleware to this list or change any of + | the existing middleware. Or, you can simply stick with this list. + | + */ + +// 'middleware' => ['web'], + 'middleware' => ['auth.nexus:nexus-web'], + + /* + |-------------------------------------------------------------------------- + | Queue Wait Time Thresholds + |-------------------------------------------------------------------------- + | + | This option allows you to configure when the LongWaitDetected event + | will be fired. Every connection / queue combination may have its + | own, unique threshold (in seconds) before this event is fired. + | + */ + + 'waits' => [ + 'redis:default' => 60, + ], + + /* + |-------------------------------------------------------------------------- + | Job Trimming Times + |-------------------------------------------------------------------------- + | + | Here you can configure for how long (in minutes) you desire Horizon to + | persist the recent and failed jobs. Typically, recent jobs are kept + | for one hour while all failed jobs are stored for an entire week. + | + */ + + 'trim' => [ + 'recent' => 60, + 'pending' => 60, + 'completed' => 60, + 'recent_failed' => 10080, + 'failed' => 10080, + 'monitored' => 10080, + ], + + /* + |-------------------------------------------------------------------------- + | Silenced Jobs + |-------------------------------------------------------------------------- + | + | Silencing a job will instruct Horizon to not place the job in the list + | of completed jobs within the Horizon dashboard. This setting may be + | used to fully remove any noisy jobs from the completed jobs list. + | + */ + + 'silenced' => [ + // App\Jobs\ExampleJob::class, + ], + + /* + |-------------------------------------------------------------------------- + | Metrics + |-------------------------------------------------------------------------- + | + | Here you can configure how many snapshots should be kept to display in + | the metrics graph. This will get used in combination with Horizon's + | `horizon:snapshot` schedule to define how long to retain metrics. + | + */ + + 'metrics' => [ + 'trim_snapshots' => [ + 'job' => 24, + 'queue' => 24, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Fast Termination + |-------------------------------------------------------------------------- + | + | When this option is enabled, Horizon's "terminate" command will not + | wait on all of the workers to terminate unless the --wait option + | is provided. Fast termination can shorten deployment delay by + | allowing a new instance of Horizon to start while the last + | instance will continue to terminate each of its workers. + | + */ + + 'fast_termination' => false, + + /* + |-------------------------------------------------------------------------- + | Memory Limit (MB) + |-------------------------------------------------------------------------- + | + | This value describes the maximum amount of memory the Horizon master + | supervisor may consume before it is terminated and restarted. For + | configuring these limits on your workers, see the next section. + | + */ + + 'memory_limit' => 64, + + /* + |-------------------------------------------------------------------------- + | Queue Worker Configuration + |-------------------------------------------------------------------------- + | + | Here you may define the queue worker settings used by your application + | in all environments. These supervisors and settings handle all your + | queued jobs and will be provisioned by Horizon during deployment. + | + */ + + 'defaults' => [ + 'supervisor-1' => [ + 'connection' => 'redis', + 'queue' => ['default'], + 'balance' => 'auto', + 'autoScalingStrategy' => 'time', + 'maxProcesses' => 1, + 'maxTime' => 0, + 'maxJobs' => 0, + 'memory' => 128, + 'tries' => 1, + 'timeout' => 600, + 'nice' => 0, + ], + ], + + 'environments' => [ + 'production' => [ + 'supervisor-1' => [ + 'maxProcesses' => 10, + 'balanceMaxShift' => 1, + 'balanceCooldown' => 3, + ], + ], + + 'local' => [ + 'supervisor-1' => [ + 'maxProcesses' => 3, + ], + ], + ], +]; diff --git a/include/constants.php b/include/constants.php index 1a7ac4b7..3739eac8 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ nexus_trans('claim.th_claim_at'), + 'last_settle_at' => nexus_trans('claim.th_last_settle'), + 'seed_time' => nexus_trans('claim.th_seed_time_this_month'), + 'uploaded' => nexus_trans('claim.th_uploaded_this_month'), +]; +$orderAllowed = [ + 'asc' => nexus_trans('nexus.asc'), + 'desc' => nexus_trans('nexus.desc'), +]; $torrentId = $uid = 0; $actionTh = $actionTd = ''; +$sort = $_GET['sort'] ?? 'created_at'; +if (!isset($sortAllowed[$sort])) { + $sort = "created_at"; +} +$order = $_GET['order'] ?? 'asc'; +if (!isset($orderAllowed[$order])) { + $order = "asc"; +} if (!empty($_GET['torrent_id'])) { $torrentId = $_GET['torrent_id']; int_check($torrentId,true); @@ -13,7 +31,7 @@ if (!empty($_GET['torrent_id'])) { } stdhead(nexus_trans('claim.title_for_torrent')); $query = \App\Models\Claim::query()->where('torrent_id', $torrentId); - $pagerParam = "?torrent_id=$torrentId"; + $pagerParam = "?torrent_id=$torrentId&sort=$sort&order=$order"; print("

".nexus_trans('claim.title_for_torrent') . " ".htmlspecialchars($torrent['name'])."

"); } elseif (!empty($_GET['uid'])) { $uid = $_GET['uid']; @@ -24,7 +42,7 @@ if (!empty($_GET['torrent_id'])) { } stdhead(nexus_trans('claim.title_for_user')); $query = \App\Models\Claim::query()->where('uid', $uid); - $pagerParam = "?uid=$uid"; + $pagerParam = "?uid=$uid&sort=$sort&order=$order"; print("

".nexus_trans('claim.title_for_user') . " ".htmlspecialchars($user->username)."

"); if ($uid == $CURUSER['id']) { $actionTh = sprintf("%s", nexus_trans("claim.th_action")); @@ -34,9 +52,69 @@ if (!empty($_GET['torrent_id'])) { } begin_main_frame(); +$textSelectOnePlease = nexus_trans('nexus.select_one_please'); +$sortOptions = $orderOptions = ''; +foreach ($sortAllowed as $name => $text) { + $sortOptions .= sprintf( + '', + $name, $_GET['sort'] == $name ? ' selected' : '', $text + ); +} +foreach ($orderAllowed as $name => $text) { + $orderOptions .= sprintf( + '', + $name, $_GET['order'] == $name ? ' selected' : '', $text + ); +} +$resetText = nexus_trans('label.reset'); +$submitText = nexus_trans('label.submit'); +$sortText = nexus_trans('nexus.sort'); +$orderText = nexus_trans('nexus.order'); +$filterForm = <<
+ + + + {$sortText}: + +    + {$orderText}: + +    + + +
+ +FORM; +$resetJs = <<count(); list($pagertop, $pagerbottom, $limit, $offset, $pageSize) = pager(50, $total, "$pagerParam&"); -$list = (clone $query)->with(['user', 'torrent', 'snatch'])->offset($offset)->limit($pageSize)->orderBy('id', 'desc')->get(); +$query = (clone $query)->with(['user', 'torrent', 'snatch'])->offset($offset)->limit($pageSize); +if ($sort == 'seed_time') { + $query->join("snatched", "claims.snatched_id", "=", "snatched.id") + ->orderByRaw("(snatched.seedtime - claims.seed_time_begin) $order"); +} elseif ($sort == 'uploaded') { + $query->join("snatched", "claims.snatched_id", "=", "snatched.id") + ->orderByRaw("(snatched.uploaded - claims.uploaded_begin) $order"); +} else { + $query->orderBy($sort, $order); +} +$list = $query->get(); +print($filterForm); print(""); print(" @@ -55,6 +133,9 @@ $now = \Carbon\Carbon::now(); $seedTimeRequiredHours = \App\Models\Claim::getConfigStandardSeedTimeHours(); $uploadedRequiredTimes = \App\Models\Claim::getConfigStandardUploadedTimes(); $claimRep = new \App\Repositories\ClaimRepository(); +$torrentTool = new \Nexus\Torrent\Torrent(); +$torrentIdList = $list->pluck('torrent_id')->toArray(); +$leechingSeedingStatus = $torrentTool->listLeechingSeedingStatus($CURUSER['id'], $torrentIdList); foreach ($list as $row) { if ( bcsub($row->snatch->seedtime, $row->seed_time_begin) >= $seedTimeRequiredHours * 3600 @@ -68,10 +149,15 @@ foreach ($list as $row) { if ($actionTh) { $actionTd = sprintf('', $claimRep->buildActionButtons($row->torrent_id, $row, 1)); } + $torrentName = $row->torrent->name; + $torrentId = $row->torrent_id; + if (isset($leechingSeedingStatus[$torrentId])) { + $torrentName .= $torrentTool->renderProgressBar($leechingSeedingStatus[$torrentId]['active_status'], $leechingSeedingStatus[$torrentId]['progress']); + } print(" - + diff --git a/resources/lang/zh_CN/nexus.php b/resources/lang/zh_CN/nexus.php index 44050c35..217935d8 100644 --- a/resources/lang/zh_CN/nexus.php +++ b/resources/lang/zh_CN/nexus.php @@ -17,4 +17,8 @@ return [ 'sum' => '累计', 'do_not_repeat' => '请不要重复操作!', 'no_permission' => '无权限!', + 'sort' => '排序字段', + 'order' => '排序', + 'asc' => '正序', + 'desc' => '倒序', ];
".nexus_trans('claim.th_id')."%s
" . $row->id . " " . $row->user->username . "" . $row->torrent->name . "" . $torrentName . " " . mksize($row->torrent->size) . " " . mkprettytime($row->torrent->added->diffInSeconds($now, true)) . " " . format_datetime($row->created_at) . "