where( 'request_time', '<', time() - 86400 * Config::obtain('subscribe_log_retention_days') )->delete(); (new HourlyUsage())->where( 'date', '<', date('Y-m-d', time() - 86400 * Config::obtain('traffic_log_retention_days')) )->delete(); (new DetectLog())->where('datetime', '<', time() - 86400 * 3)->delete(); (new EmailQueue())->where('time', '<', time() - 86400)->delete(); (new OnlineLog())->where('last_time', '<', time() - 86400)->delete(); echo Tools::toDateTime(time()) . ' 数据库清理完成' . PHP_EOL; } public static function detectInactiveUser(): void { $checkin_days = Config::obtain('detect_inactive_user_checkin_days'); $login_days = Config::obtain('detect_inactive_user_login_days'); $use_days = Config::obtain('detect_inactive_user_use_days'); (new User())->where('is_admin', 0) ->where('is_inactive', 0) ->where('last_check_in_time', '<', time() - 86400 * $checkin_days) ->where('last_login_time', '<', time() - 86400 * $login_days) ->where('last_use_time', '<', time() - 86400 * $use_days) ->update(['is_inactive' => 1]); (new User())->where('is_admin', 0) ->where('is_inactive', 1) ->where('last_check_in_time', '>', time() - 86400 * $checkin_days) ->where('last_login_time', '>', time() - 86400 * $login_days) ->where('last_use_time', '>', time() - 86400 * $use_days) ->update(['is_inactive' => 0]); echo Tools::toDateTime(time()) . ' 检测到 ' . (new User())->where('is_inactive', 1)->count() . ' 个账户处于闲置状态' . PHP_EOL; } public static function detectNodeOffline(): void { $nodes = (new Node())->where('type', 1)->get(); foreach ($nodes as $node) { if ($node->getNodeOnlineStatus() >= 0 && $node->online === 1) { continue; } if ($node->getNodeOnlineStatus() === -1 && $node->online === 1) { echo 'Send Node Offline Email to admin users' . PHP_EOL; try { Notification::notifyAdmin( $_ENV['appName'] . '-系统警告', '管理员你好,系统发现节点 ' . $node->name . ' 掉线了,请你及时处理。' ); } catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) { echo $e->getMessage() . PHP_EOL; } if (Config::obtain('telegram_node_offline')) { $notice_text = str_replace( '%node_name%', $node->name, Config::obtain('telegram_node_offline_text') ); try { (new Telegram())->send(0, $notice_text); } catch (TelegramSDKException $e) { echo $e->getMessage(); } } $node->online = 0; $node->save(); continue; } if ($node->getNodeOnlineStatus() === 1 && $node->online === 0) { echo 'Send Node Online Email to admin user' . PHP_EOL; try { Notification::notifyAdmin( $_ENV['appName'] . '-系统提示', '管理员你好,系统发现节点 ' . $node->name . ' 恢复上线了。' ); } catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) { echo $e->getMessage() . PHP_EOL; } if (Config::obtain('telegram_node_online')) { $notice_text = str_replace( '%node_name%', $node->name, Config::obtain('telegram_node_online_text') ); try { (new Telegram())->send(0, $notice_text); } catch (TelegramSDKException $e) { echo $e->getMessage(); } } $node->online = 1; $node->save(); } } echo Tools::toDateTime(time()) . ' 节点离线检测完成' . PHP_EOL; } public static function expirePaidUserAccount(): void { $paidUsers = (new User())->where('class', '>', 0)->get(); foreach ($paidUsers as $user) { if (strtotime($user->class_expire) < time()) { $text = '你好,系统发现你的账号等级已经过期了。'; $reset_traffic = $_ENV['class_expire_reset_traffic']; if ($reset_traffic >= 0) { $user->transfer_enable = Tools::toGB($reset_traffic); $text .= '流量已经被重置为' . $reset_traffic . 'GB。'; } try { Notification::notifyUser($user, $_ENV['appName'] . '-你的账号等级已经过期了', $text); } catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) { echo $e->getMessage() . PHP_EOL; } $user->u = 0; $user->d = 0; $user->transfer_today = 0; $user->class = 0; $user->save(); } } echo Tools::toDateTime(time()) . ' 付费用户过期检测完成' . PHP_EOL; } public static function processEmailQueue(): void { if ((new EmailQueue())->count() === 0) { echo Tools::toDateTime(time()) . ' 邮件队列为空' . PHP_EOL; } else { //记录当前时间戳 $timestamp = time(); //邮件队列处理 while (true) { if (time() - $timestamp > 299) { echo Tools::toDateTime(time()) . '邮件队列处理超时,已跳过' . PHP_EOL; break; } DB::beginTransaction(); $email_queues_raw = DB::select('SELECT * FROM email_queue LIMIT 1 FOR UPDATE SKIP LOCKED'); if (count($email_queues_raw) === 0) { DB::commit(); break; } $email_queues = array_map(static function ($value) { return (array) $value; }, $email_queues_raw); $email_queue = $email_queues[0]; echo '发送邮件至 ' . $email_queue['to_email'] . PHP_EOL; DB::delete('DELETE FROM email_queue WHERE id = ?', [$email_queue['id']]); if (Tools::isEmail($email_queue['to_email'])) { try { Mail::send( $email_queue['to_email'], $email_queue['subject'], $email_queue['template'], json_decode($email_queue['array']) ); } catch (Exception|ClientExceptionInterface $e) { echo $e->getMessage(); } } else { echo $email_queue['to_email'] . ' 邮箱格式错误,已跳过' . PHP_EOL; } DB::commit(); } echo Tools::toDateTime(time()) . ' 邮件队列处理完成' . PHP_EOL; } } public static function processTabpOrderActivation(): void { $users = User::all(); foreach ($users as $user) { $user_id = $user->id; // 获取用户账户等待激活的TABP订单 $pending_activation_orders = (new Order())->where('user_id', $user_id) ->where('status', 'pending_activation') ->where('product_type', 'tabp') ->orderBy('id') ->get(); // 获取用户账户已激活的TABP订单,一个用户同时只能有一个已激活的TABP订单 $activated_order = (new Order())->where('user_id', $user_id) ->where('status', 'activated') ->where('product_type', 'tabp') ->orderBy('id') ->first(); // 如果用户账户中没有已激活的TABP订单,且有等待激活的TABP订单,则激活最早的等待激活TABP订单 if ($activated_order === null && count($pending_activation_orders) > 0) { $order = $pending_activation_orders[0]; // 获取TABP订单内容准备激活 $content = json_decode($order->product_content); // 激活TABP $user->u = 0; $user->d = 0; $user->transfer_today = 0; $user->transfer_enable = Tools::toGB($content->bandwidth); $user->class = $content->class; $old_class_expire = new DateTime(); $user->class_expire = $old_class_expire ->modify('+' . $content->class_time . ' days')->format('Y-m-d H:i:s'); $user->node_group = $content->node_group; $user->node_speedlimit = $content->speed_limit; $user->node_iplimit = $content->ip_limit; $user->save(); $order->status = 'activated'; $order->update_time = time(); $order->save(); echo "TABP订单 #{$order->id} 已激活。\n"; continue; } // 如果用户账户中有已激活的TABP订单,则判断是否过期 if ($activated_order !== null) { $content = json_decode($activated_order->product_content); if ($activated_order->update_time + $content->class_time * 86400 < time()) { $activated_order->status = 'expired'; $activated_order->update_time = time(); $activated_order->save(); echo "TABP订单 #{$activated_order->id} 已过期。\n"; } } } echo Tools::toDateTime(time()) . ' TABP订单激活处理完成' . PHP_EOL; } public static function processBandwidthOrderActivation(): void { $users = User::all(); foreach ($users as $user) { $user_id = $user->id; // 获取用户账户等待激活的流量包订单 $order = (new Order())->where('user_id', $user_id) ->where('status', 'pending_activation') ->where('product_type', 'bandwidth') ->orderBy('id') ->first(); if ($order !== null) { // 获取流量包订单内容准备激活 $content = json_decode($order->product_content); // 激活流量包 $user->transfer_enable += Tools::toGB($content->bandwidth); $user->save(); $order->status = 'activated'; $order->update_time = time(); $order->save(); echo "流量包订单 #{$order->id} 已激活。\n"; } } echo Tools::toDateTime(time()) . ' 流量包订单激活处理完成' . PHP_EOL; } /** * @throws Exception */ public static function processTimeOrderActivation(): void { $users = User::all(); foreach ($users as $user) { $user_id = $user->id; // 获取用户账户等待激活的时间包订单 $order = (new Order())->where('user_id', $user_id) ->where('status', 'pending_activation') ->where('product_type', 'time') ->orderBy('id') ->first(); if ($order !== null) { $content = json_decode($order->product_content); // 跳过当前账户等级不等于时间包等级的非免费用户订单 if ($user->class !== (int) $content->class && $user->class > 0) { continue; } // 激活时间包 $user->class = $content->class; $old_class_expire = new DateTime($user->class_expire); $user->class_expire = $old_class_expire ->modify('+' . $content->class_time . ' days')->format('Y-m-d H:i:s'); $user->node_group = $content->node_group; $user->node_speedlimit = $content->speed_limit; $user->node_iplimit = $content->ip_limit; $user->save(); $order->status = 'activated'; $order->update_time = time(); $order->save(); echo "时间包订单 #{$order->id} 已激活。\n"; } } echo Tools::toDateTime(time()) . ' 时间包订单激活处理完成' . PHP_EOL; } public static function processPendingOrder(): void { $pending_payment_orders = (new Order())->where('status', 'pending_payment')->get(); foreach ($pending_payment_orders as $order) { // 检查账单支付状态 $invoice = (new Invoice())->where('order_id', $order->id)->first(); if ($invoice === null) { continue; } // 标记订单为等待激活 if (in_array($invoice->status, ['paid_gateway', 'paid_balance', 'paid_admin'])) { $order->status = 'pending_activation'; $order->update_time = time(); $order->save(); echo "已标记订单 #{$order->id} 为等待激活。\n"; continue; } // 取消超时未支付的订单和关联账单 if ($order->create_time + 86400 < time()) { $order->status = 'cancelled'; $order->update_time = time(); $order->save(); echo "已取消超时订单 #{$order->id}。\n"; $invoice->status = 'cancelled'; $invoice->update_time = time(); $invoice->save(); echo "已取消超时账单 #{$invoice->id}。\n"; } } echo Tools::toDateTime(time()) . ' 等待中订单处理完成' . PHP_EOL; } public static function removeInactiveUserLinkAndInvite(): void { $inactive_users = (new User())->where('is_inactive', 1)->get(); foreach ($inactive_users as $user) { $user->removeLink(); $user->removeInvite(); } echo Tools::toDateTime(time()) . ' Successfully removed inactive user\'s Link and Invite' . PHP_EOL; } // 废弃 public static function resetNodeBandwidth(): void { (new Node())->where('bandwidthlimit_resetday', date('d'))->update(['node_bandwidth' => 0]); echo Tools::toDateTime(time()) . ' 重设节点流量完成' . PHP_EOL; } public static function resetDuckBandwidth(): void { (new Ducks())->where('width_reset_day', date('d'))->update(['use_width' => 0]); echo Tools::toDateTime(time()) . ' 重设机器流量完成' . PHP_EOL; } // public static function resetZhuanfaBandwidth(): void // { // (new Ducks())->where('end_date', date('Y-m-d'))->update(['use_width' => 0]); // // echo Tools::toDateTime(time()) . ' 重设机器流量完成' . PHP_EOL; // } public static function bandwidthMerge(): void { $oNode = new Node(); $oZhuanfas = new Zhuanfas(); $oDucks = new Ducks(); $oNodeAll = $oNode->all(); foreach ($oNodeAll as $oNodeRow) { @ $iZhuanfaFlowToday[$oNodeRow->zhuanfa_code] += $oNodeRow->node_bandwidth; @ $iDuckFlowToday[$oNodeRow->duck_code] += $oNodeRow->node_bandwidth; $oZhuanfas->where('code', $oNodeRow->zhuanfa_code)->increment('use_width', $oNodeRow->node_bandwidth); $oDucks->where('code', $oNodeRow->duck_code)->increment('use_width', $oNodeRow->node_bandwidth); $oNode->where('id', $oNodeRow->id)->update(['node_bandwidth' => 0]); } // 流量报告,转发 $oTgSendFormat = new TgSendFormat(); $oTgSendFormat->sTitleEmoji = "⚡️⚡️⚡️⚡️⚡️⚡️"; $oTgSendFormat->sTitle = "# 转发流量"; $today = new DateTime(); $oZhuanfasAll = $oZhuanfas->all(); foreach ($oZhuanfasAll as $oZhuanfasRow) { $targetDate = new DateTime($oZhuanfasRow->end_date); $interval = $today->diff($targetDate); $oTgSendFormat->rowAdd( $oZhuanfasRow->name, $iZhuanfaFlowToday[$oZhuanfasRow->code], $oZhuanfasRow->use_width, $oZhuanfasRow->max_width, $interval->days ); } $sTgReport = $oTgSendFormat->typeA(); // var_dump($sTgReport); (new Telegram())->send(0, $sTgReport); // 流量报告,机器 $oTgSendFormat = new TgSendFormat(); $oTgSendFormat->sTitleEmoji = "\n\n"; $oTgSendFormat->sTitle = "# 机器流量"; $oDucksAll = $oDucks->orderBy('code')->get(); foreach ($oDucksAll as $oDucksRow) { if ($oDucksRow->width_reset_day == 0) { $iWidthResetDay = 0; } if ($oDucksRow->width_reset_day > date('d')) { $iWidthResetDay = $oDucksRow->width_reset_day - date('d'); } else { $date = new DateTime(); $date->modify('first day of next month'); $date->setDate((int)$date->format('Y'), (int)$date->format('m'), $oDucksRow->width_reset_day); $sEndDate = $date; $currentDate = new DateTime(); $interval = $currentDate->diff($sEndDate); $iWidthResetDay = $interval->days; } $oTgSendFormat->rowAdd( $oDucksRow->code, $iDuckFlowToday[$oDucksRow->code] ?? 0, $oDucksRow->use_width, $oDucksRow->max_width, $iWidthResetDay ); } $sTgReport = $oTgSendFormat->typeA(); // var_dump($sTgReport); (new Telegram())->send(0, $sTgReport); } public static function resetTodayBandwidth(): void { (new User())->query()->update(['transfer_today' => 0]); echo Tools::toDateTime(time()) . ' 重设用户每日流量完成' . PHP_EOL; } public static function resetFreeUserBandwidth(): void { $freeUsers = (new User())->where('class', 0) ->where('auto_reset_day', date('d'))->get(); foreach ($freeUsers as $user) { try { Notification::notifyUser( $user, $_ENV['appName'] . '-免费流量重置通知', '你好,你的免费流量已经被重置为' . $user->auto_reset_bandwidth . 'GB。' ); } catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) { echo $e->getMessage() . PHP_EOL; } $user->u = 0; $user->d = 0; $user->transfer_enable = $user->auto_reset_bandwidth * 1024 * 1024 * 1024; $user->save(); } echo Tools::toDateTime(time()) . ' 免费用户流量重置完成' . PHP_EOL; } public static function sendDailyFinanceMail(): void { $today = strtotime('00:00:00'); $paylists = (new Paylist())->where('status', 1) ->whereBetween('datetime', [strtotime('-1 day', $today), $today])->get(); $text_html = '
| 金额 | 用户ID | 用户名 | 充值时间 | '; foreach ($paylists as $paylist) { $text_html .= '
| ' . $paylist->total . ' | '; $text_html .= '' . $paylist->userid . ' | '; $text_html .= '' . (new User())->find($paylist->userid)->user_name . ' | '; $text_html .= '' . Tools::toDateTime((int) $paylist->datetime) . ' | '; $text_html .= '