mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-27 22:47:23 +08:00
buy torrent use queue job
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Console;
|
|||||||
|
|
||||||
use App\Jobs\CheckCleanup;
|
use App\Jobs\CheckCleanup;
|
||||||
use App\Jobs\CheckQueueFailedJobs;
|
use App\Jobs\CheckQueueFailedJobs;
|
||||||
|
use App\Utils\ThirdPartyJob;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Console\Scheduling\Event;
|
use Illuminate\Console\Scheduling\Event;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
@@ -41,6 +42,7 @@ class Kernel extends ConsoleKernel
|
|||||||
$schedule->command('meilisearch:import')->weeklyOn(1, "03:00")->withoutOverlapping();
|
$schedule->command('meilisearch:import')->weeklyOn(1, "03:00")->withoutOverlapping();
|
||||||
$schedule->command('torrent:load_pieces_hash')->dailyAt("01:00")->withoutOverlapping();
|
$schedule->command('torrent:load_pieces_hash')->dailyAt("01:00")->withoutOverlapping();
|
||||||
$schedule->job(new CheckQueueFailedJobs())->everySixHours()->withoutOverlapping();
|
$schedule->job(new CheckQueueFailedJobs())->everySixHours()->withoutOverlapping();
|
||||||
|
$schedule->job(new ThirdPartyJob())->everyMinute()->withoutOverlapping();
|
||||||
|
|
||||||
$this->registerScheduleCleanup($schedule);
|
$this->registerScheduleCleanup($schedule);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\TorrentBuyLog;
|
||||||
|
use App\Repositories\BonusRepository;
|
||||||
|
use App\Repositories\TorrentRepository;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class BuyTorrent implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public int $userId;
|
||||||
|
|
||||||
|
public int $torrentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(int $userId, int $torrentId)
|
||||||
|
{
|
||||||
|
$this->userId = $userId;
|
||||||
|
$this->torrentId = $torrentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$logPrefix = sprintf("user: %s, torrent: %s", $this->userId, $this->torrentId);
|
||||||
|
$torrentRep = new TorrentRepository();
|
||||||
|
$userId = $this->userId;
|
||||||
|
$torrentId = $this->torrentId;
|
||||||
|
|
||||||
|
$hasBuy = TorrentBuyLog::query()
|
||||||
|
->where("uid", $userId)
|
||||||
|
->where("torrent_id", $torrentId)
|
||||||
|
->exists()
|
||||||
|
;
|
||||||
|
if ($hasBuy) {
|
||||||
|
//标记购买成功
|
||||||
|
do_log("$logPrefix, already bought");
|
||||||
|
$torrentRep->addBuySuccessCache($userId, $torrentId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$bonusRep = new BonusRepository();
|
||||||
|
$bonusRep->consumeToBuyTorrent($this->userId, $this->torrentId);
|
||||||
|
//标记购买成功
|
||||||
|
do_log("$logPrefix, buy torrent success");
|
||||||
|
$torrentRep->addBuySuccessCache($userId, $torrentId);
|
||||||
|
} catch (\Throwable $throwable) {
|
||||||
|
//标记购买失败,缓存 3600 秒,这个时间内不能再次购买
|
||||||
|
do_log("$logPrefix, buy torrent fail: " . $throwable->getMessage(), "error");
|
||||||
|
$torrentRep->addBuyFailCache($userId, $torrentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ class TorrentRepository extends BaseRepository
|
|||||||
|
|
||||||
const BUY_STATUS_SUCCESS = 0;
|
const BUY_STATUS_SUCCESS = 0;
|
||||||
const BUY_STATUS_NOT_YET = -1;
|
const BUY_STATUS_NOT_YET = -1;
|
||||||
|
const BUY_STATUS_UNKNOWN = -2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -807,16 +808,8 @@ HTML;
|
|||||||
//根据失败次数,禁用下载权限并做提示等
|
//根据失败次数,禁用下载权限并做提示等
|
||||||
return $buyFailCount;
|
return $buyFailCount;
|
||||||
}
|
}
|
||||||
//购买失败缓存失效后,再重新查询数据库确定最终状态
|
//不是成功或失败,直接返回未知
|
||||||
$hasBuyFromDB = TorrentBuyLog::query()->where("uid", $uid)->where("torrent_id", $torrentId)->exists();
|
return self::BUY_STATUS_UNKNOWN;
|
||||||
if ($hasBuyFromDB) {
|
|
||||||
//标记购买成功, 返回已购买
|
|
||||||
$this->addBuySuccessCache($uid, $torrentId);
|
|
||||||
return self::BUY_STATUS_SUCCESS;
|
|
||||||
} else {
|
|
||||||
//返回未购买,前端可执行购买逻辑
|
|
||||||
return self::BUY_STATUS_NOT_YET;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
use App\Jobs\BuyTorrent;
|
||||||
|
use http\Exception\InvalidArgumentException;
|
||||||
|
use Illuminate\Support\Facades\Queue;
|
||||||
|
use Nexus\Database\NexusDB;
|
||||||
|
use Nexus\Database\NexusLock;
|
||||||
|
|
||||||
|
final class ThirdPartyJob {
|
||||||
|
private static string $queueKey = "nexus_third_party_job";
|
||||||
|
|
||||||
|
private static int $size = 20;
|
||||||
|
|
||||||
|
const JOB_BUY_TORRENT = "buyTorrent";
|
||||||
|
|
||||||
|
public function __invoke(): void
|
||||||
|
{
|
||||||
|
$lockName = convertNamespaceToSnake(__METHOD__);
|
||||||
|
$lock = new NexusLock($lockName, 600);
|
||||||
|
if (!$lock->get()) {
|
||||||
|
do_log("can not get lock: $lockName, return ...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$list = NexusDB::redis()->lRange(self::$queueKey, 0, self::$size);
|
||||||
|
$successCount = 0;
|
||||||
|
foreach ($list as $item) {
|
||||||
|
$data = json_decode($item, true);
|
||||||
|
if (!empty($data['name'])) {
|
||||||
|
$successCount++;
|
||||||
|
match ($data['name']) {
|
||||||
|
self::JOB_BUY_TORRENT => self::enqueueJobBuyTorrent($data),
|
||||||
|
default => throw new InvalidArgumentException("invalid name: {$data['name']}")
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
do_log(sprintf("%s no name, skip", $item), "error");
|
||||||
|
}
|
||||||
|
NexusDB::redis()->lRem(self::$queueKey, $item);
|
||||||
|
}
|
||||||
|
do_log(sprintf("success dispatch %s jobs", $successCount));
|
||||||
|
$lock->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function addBuyTorrent(int $userId, int $torrentId): void
|
||||||
|
{
|
||||||
|
$key = sprintf("%s:%s_%s_%s", self::$queueKey, convertNamespaceToSnake(__METHOD__), $userId, $torrentId);
|
||||||
|
if (NexusDB::redis()->set($key, now()->toDateTimeString(), ['nx', 'ex' => 3600])) {
|
||||||
|
$value = [
|
||||||
|
'name' => self::JOB_BUY_TORRENT,
|
||||||
|
'userId' => $userId,
|
||||||
|
'torrentId' => $torrentId,
|
||||||
|
];
|
||||||
|
NexusDB::redis()->rPush(self::$queueKey, json_encode($value));
|
||||||
|
do_log("success addBuyTorrent: $key", "debug");
|
||||||
|
} else {
|
||||||
|
do_log("no need to addBuyTorrent: $key", "debug");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function enqueueJobBuyTorrent(array $params): void
|
||||||
|
{
|
||||||
|
if (!empty($params['userId']) && !empty($params['torrentId'])) {
|
||||||
|
$job = new BuyTorrent($params['userId'], $params['torrentId']);
|
||||||
|
Queue::push($job);
|
||||||
|
} else {
|
||||||
|
do_log("no userId or torrentId: " . json_encode($params), "error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.8.14');
|
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.8.14');
|
||||||
defined('RELEASE_DATE') || define('RELEASE_DATE', '2024-10-31');
|
defined('RELEASE_DATE') || define('RELEASE_DATE', '2024-11-03');
|
||||||
defined('IN_TRACKER') || define('IN_TRACKER', false);
|
defined('IN_TRACKER') || define('IN_TRACKER', false);
|
||||||
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
|
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
|
||||||
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");
|
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");
|
||||||
|
|||||||
@@ -3282,7 +3282,7 @@ function commenttable($rows, $type, $parent_id, $review = false)
|
|||||||
$dt = sqlesc(date("Y-m-d H:i:s",(TIMENOW - $secs))); // calculate date.
|
$dt = sqlesc(date("Y-m-d H:i:s",(TIMENOW - $secs))); // calculate date.
|
||||||
print("<tr>\n");
|
print("<tr>\n");
|
||||||
print("<td class=\"rowfollow\" width=\"150\" valign=\"top\" style=\"padding: 0px;\">".return_avatar_image($avatar)."</td>\n");
|
print("<td class=\"rowfollow\" width=\"150\" valign=\"top\" style=\"padding: 0px;\">".return_avatar_image($avatar)."</td>\n");
|
||||||
print("<td class=\"rowfollow\" valign=\"top\"><br />".$text.$text_editby."</td>\n");
|
print("<td class=\"rowfollow word-break-all\" valign=\"top\"><br />".$text.$text_editby."</td>\n");
|
||||||
print("</tr>\n");
|
print("</tr>\n");
|
||||||
$actionbar = "<a href=\"comment.php?action=add&sub=quote&cid=".$row['id']."&pid=".$parent_id."&type=".$type."\"><img class=\"f_quote\" src=\"pic/trans.gif\" alt=\"Quote\" title=\"".$lang_functions['title_reply_with_quote']."\" /></a>".
|
$actionbar = "<a href=\"comment.php?action=add&sub=quote&cid=".$row['id']."&pid=".$parent_id."&type=".$type."\"><img class=\"f_quote\" src=\"pic/trans.gif\" alt=\"Quote\" title=\"".$lang_functions['title_reply_with_quote']."\" /></a>".
|
||||||
"<a href=\"comment.php?action=add&pid=".$parent_id."&type=".$type."\"><img class=\"f_reply\" src=\"pic/trans.gif\" alt=\"Add Reply\" title=\"".$lang_functions['title_add_reply']."\" /></a>".(user_can('commanage') ? "<a href=\"comment.php?action=delete&cid=".$row['id']."&type=".$type."\"><img class=\"f_delete\" src=\"pic/trans.gif\" alt=\"Delete\" title=\"".$lang_functions['title_delete']."\" /></a>" : "").($row["user"] == $CURUSER["id"] || get_user_class() >= $commanage_class ? "<a href=\"comment.php?action=edit&cid=".$row['id']."&type=".$type."\"><img class=\"f_edit\" src=\"pic/trans.gif\" alt=\"Edit\" title=\"".$lang_functions['title_edit']."\" />"."</a>" : "");
|
"<a href=\"comment.php?action=add&pid=".$parent_id."&type=".$type."\"><img class=\"f_reply\" src=\"pic/trans.gif\" alt=\"Add Reply\" title=\"".$lang_functions['title_add_reply']."\" /></a>".(user_can('commanage') ? "<a href=\"comment.php?action=delete&cid=".$row['id']."&type=".$type."\"><img class=\"f_delete\" src=\"pic/trans.gif\" alt=\"Delete\" title=\"".$lang_functions['title_delete']."\" /></a>" : "").($row["user"] == $CURUSER["id"] || get_user_class() >= $commanage_class ? "<a href=\"comment.php?action=edit&cid=".$row['id']."&type=".$type."\"><img class=\"f_edit\" src=\"pic/trans.gif\" alt=\"Edit\" title=\"".$lang_functions['title_edit']."\" />"."</a>" : "");
|
||||||
|
|||||||
@@ -1275,3 +1275,8 @@ function publish_model_event(string $event, int $id): void
|
|||||||
do_log("event: $event, id: $id, channel: $channel, channel is empty!", "error");
|
do_log("event: $event, id: $id, channel: $channel, channel is empty!", "error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertNamespaceToSnake(string $str): string
|
||||||
|
{
|
||||||
|
return str_replace(["\\", "::"], ["_", "."], $str);
|
||||||
|
}
|
||||||
|
|||||||
+5
-22
@@ -471,28 +471,11 @@ if (!isset($self))
|
|||||||
}
|
}
|
||||||
warn("purchase fail, please try again later, please make sure you have enough bonus", 300);
|
warn("purchase fail, please try again later, please make sure you have enough bonus", 300);
|
||||||
}
|
}
|
||||||
if ($buyStatus == \App\Repositories\TorrentRepository::BUY_STATUS_NOT_YET) {
|
if ($buyStatus == \App\Repositories\TorrentRepository::BUY_STATUS_UNKNOWN) {
|
||||||
//one by one
|
//just enqueue job
|
||||||
$lock = new \Nexus\Database\NexusLock("buying_torrent", 5);
|
\App\Utils\ThirdPartyJob::addBuyTorrent($userid, $torrentid);
|
||||||
if (!$lock->get()) {
|
warn("purchase in progress, please wait", 300);
|
||||||
$msg = "buying torrent, wait!";
|
|
||||||
do_log("[ANNOUNCE] user: $userid, torrent: $torrentid, $msg", 'error');
|
|
||||||
warn($msg, 300);
|
|
||||||
}
|
|
||||||
$bonusRep = new \App\Repositories\BonusRepository();
|
|
||||||
try {
|
|
||||||
$bonusRep->consumeToBuyTorrent($az['id'], $torrent['id'], 'Web');
|
|
||||||
$torrentRep->addBuySuccessCache($userid, $torrentid);
|
|
||||||
$lock->release();
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
$msg = $exception->getMessage();
|
|
||||||
do_log("[ANNOUNCE] user: $userid, torrent: $torrentid, $msg " . $exception->getTraceAsString(), 'error');
|
|
||||||
$torrentRep->addBuyFailCache($userid, $torrentid);
|
|
||||||
$lock->release();
|
|
||||||
err($msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // continue an existing session
|
else // continue an existing session
|
||||||
@@ -611,7 +594,7 @@ if (($left > 0 || $event == "completed") && $az['class'] < \App\Models\HitAndRun
|
|||||||
if ($hrMode == \App\Models\HitAndRun::MODE_GLOBAL || ($hrMode == \App\Models\HitAndRun::MODE_MANUAL && $torrent['hr'] == \App\Models\Torrent::HR_YES)) {
|
if ($hrMode == \App\Models\HitAndRun::MODE_GLOBAL || ($hrMode == \App\Models\HitAndRun::MODE_MANUAL && $torrent['hr'] == \App\Models\Torrent::HR_YES)) {
|
||||||
$hrCacheKey = sprintf("hit_and_run:%d:%d", $userid, $torrentid);
|
$hrCacheKey = sprintf("hit_and_run:%d:%d", $userid, $torrentid);
|
||||||
$hrExists = \Nexus\Database\NexusDB::remember($hrCacheKey, mt_rand(86400*365*5, 86400*365*10), function () use ($torrentid, $userid) {
|
$hrExists = \Nexus\Database\NexusDB::remember($hrCacheKey, mt_rand(86400*365*5, 86400*365*10), function () use ($torrentid, $userid) {
|
||||||
return \App\Models\HitAndRun::query()->where("uid", $userid)->where("torrent_id", $torrentid)->exists();
|
return \App\Models\HitAndRun::query()->where("uid", $userid)->where("torrent_id", $torrentid)->exists() ? 1 : 0;
|
||||||
});
|
});
|
||||||
$hrLog .= ", hrExists: $hrExists";
|
$hrLog .= ", hrExists: $hrExists";
|
||||||
if (!$hrExists) {
|
if (!$hrExists) {
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ loggedinorreturn();
|
|||||||
stdhead($lang_contactstaff['head_contact_staff'], false);
|
stdhead($lang_contactstaff['head_contact_staff'], false);
|
||||||
begin_main_frame();
|
begin_main_frame();
|
||||||
print("<form id=compose method=post name=\"compose\" action=takecontact.php>");
|
print("<form id=compose method=post name=\"compose\" action=takecontact.php>");
|
||||||
if (isset($_GET["returnto"]) && $_GET["returnto"] || $_SERVER["HTTP_REFERER"])
|
|
||||||
print("<input type=hidden name=returnto value=\"".(htmlspecialchars($_GET["returnto"] ?? '') ? htmlspecialchars($_GET["returnto"] ?? '') : htmlspecialchars($_SERVER["HTTP_REFERER"] ?? ''))."\">");
|
|
||||||
begin_compose($lang_contactstaff['text_message_to_staff'], "new");
|
begin_compose($lang_contactstaff['text_message_to_staff'], "new");
|
||||||
end_compose();
|
end_compose();
|
||||||
print("</form>");
|
print("</form>");
|
||||||
|
|||||||
+1
-1
@@ -228,7 +228,7 @@ $sender = get_username($message['sender']);
|
|||||||
$reply = " [ <a href=\"sendmessage.php?receiver=" . $message['sender'] . "&replyto=" . $pm_id . "\">".$lang_messages['text_reply']."</a> ]";
|
$reply = " [ <a href=\"sendmessage.php?receiver=" . $message['sender'] . "&replyto=" . $pm_id . "\">".$lang_messages['text_reply']."</a> ]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$body = format_comment($message['msg'], false);
|
$body = format_comment($message['msg'], true);
|
||||||
//$body = htmlspecialchars_decode($body);
|
//$body = htmlspecialchars_decode($body);
|
||||||
$added = $message['added'];
|
$added = $message['added'];
|
||||||
if ($message['sender'] == $CURUSER['id'])
|
if ($message['sender'] == $CURUSER['id'])
|
||||||
|
|||||||
Vendored
+1
-1
@@ -71,6 +71,6 @@ img.hitandrun {
|
|||||||
.codemain>pre {
|
.codemain>pre {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
td.rowfollow {
|
.word-break-all {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user