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; } // 清理tg step,暂时没用 public static function clearTgStep(): void { TgStep::where("date", "<=", date('Y-m-d', strtotime('-1 day')))->delete(); } // 统计用户数据,入库,发送tg public static function userCount(): void { $sPrevDate = date("Y-m-d", strtotime("-1 day")); $sPrevDateStart = date("Y-m-d 00:00:00", strtotime("-1 day")); $sPrevDateEnd = date("Y-m-d 23:59:59", strtotime("-1 day")); $sPrevTimeStart = strtotime($sPrevDateStart); $sPrevTimeEnd = strtotime($sPrevDateEnd); $sPrevDate30 = date("Y-m-d", strtotime("-30 day")); //// 访客统计 start $aVisitorCount = Visitor::select('from', DB::raw('count(*) as count')) ->groupBy('from') ->get() ->toArray(); $aVisitorMix = []; foreach ($aVisitorCount as $row) { $aVisitorMix[$row['from']]['count'] = $row['count']; } //// 访客统计 end // 注册数量 $aRegCount = User::select('from', DB::raw('count(*) as count')) ->whereDate('reg_date', $sPrevDate) ->groupBy('from') ->get() ->toArray(); $aRegMix = []; foreach ($aRegCount as $row) { if (isset($row['from'])) { $sFrom = $row['from']; } else { $sFrom = '无'; } $aRegMix[$sFrom]['count'] = $row['count']; } $aOrder = Order::whereBetween("update_time", [$sPrevTimeStart, $sPrevTimeEnd]) ->where("status", "activated") ->get() ->toArray(); // 标记是否是新用户 $oUserModel = new User(); $aUserMix = []; foreach ($aOrder as $row) { $oUser = $oUserModel->where("id", $row["user_id"])->first(); if (isset($oUser->from)) { $sFrom = $oUser->from; } else { $sFrom = "无"; } // 初始化用户数据 if (!isset($aUserMix[$sFrom])) { $aUserMix[$sFrom] = [ 'pay_count_new' => 0, 'money_count_new' => 0, 'pay_count_old' => 0, 'money_count_old' => 0, 'pay_count' => 0, 'money_count' => 0, ]; } // 确保时间比较的正确性 if (isset($oUser->reg_date) && ($row['update_time'] - strtotime($oUser->reg_date)) < (86400 * 30)) { $aUserMix[$sFrom]['pay_count_new']++; $aUserMix[$sFrom]['money_count_new'] += $row["price"]; } else { $aUserMix[$sFrom]['pay_count_old']++; $aUserMix[$sFrom]['money_count_old'] += $row["price"]; } $aUserMix[$sFrom]['pay_count']++; $aUserMix[$sFrom]['money_count'] += $row["price"]; } $aAllMix = array_merge($aVisitorMix, $aRegMix, $aUserMix); foreach ($aAllMix as $k => $v) { $oCountUserModel = new CountUser(); $oCountUserModel->from = $k; $oCountUserModel->count_from = $aVisitorMix[$k]['count'] ?? 0; $oCountUserModel->count_reg = $aRegMix[$k]['count'] ?? 0; $oCountUserModel->count_pay = $aUserMix[$k]['pay_count'] ?? 0; $oCountUserModel->count_pay_new = $aUserMix[$k]['pay_count_new'] ?? 0; $oCountUserModel->count_pay_old = $aUserMix[$k]['pay_count_old'] ?? 0; $oCountUserModel->count_money = $aUserMix[$k]['money_count'] ?? 0; $oCountUserModel->count_money_new = $aUserMix[$k]['money_count_new'] ?? 0; $oCountUserModel->count_money_old = $aUserMix[$k]['money_count_old'] ?? 0; $oCountUserModel->date = $sPrevDate; $r = $oCountUserModel->save(); } Visitor::truncate(); $sMsg = (new ReportUser())->ByDay(1); (new Telegram())->send(0, $sMsg); } // public static function visitorCount(): void // { // $oVisitor = Visitor::select('from', DB::raw('count(*) as count')) // ->groupBy('from') // ->get(); // // foreach ($oVisitor as $row) { // // $oCountVisitor = new CountVisitor(); // $oCountVisitor->from = $row->from; // $oCountVisitor->count = $row->count; // $oCountVisitor->date = date('Y-m-d', strtotime('-1 day')); // $oCountVisitor->save(); // // } // // Visitor::truncate(); // // // tg报表 // $aCountVisitor = CountVisitor::where('date', date('Y-m-d', strtotime('-1 day')))->get()->toArray(); // // $sTgReport = ''; // foreach ($aCountVisitor as $row) { // // $sTgReport .= $row["from"].":".$row["count"]."\n"; // // } // // (new Telegram())->send(0, $sTgReport); // // } // 限制帐户检测 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; } // 新增,专门检测处理体验用户 // class=1为免费体验用户,免费体验到期转为0彻底不能用 public static function expireFreeUserAccount(): void { $paidUsers = (new User())->where('class', 1)->get(); foreach ($paidUsers as $user) { if (strtotime($user->class_expire) < time()) { $user->u = 0; $user->d = 0; $user->transfer_today = 0; $user->class = 0; $user->save(); } } echo Tools::toDateTime(time()) . ' 体验用户过期检测完成' . PHP_EOL; } // 付费用户过期检测 // 现在,付费用户是2+,体验用户是1。付费到期转为体验。 public static function expirePaidUserAccount(): void { $paidUsers = (new User())->where('class', '>=', 2)->get(); foreach ($paidUsers as $user) { if (strtotime($user->class_expire) < time()) { $text = '您好,系统发现您的套餐已经过期了。'; $reset_traffic = $_ENV['class_expire_reset_traffic']; $reset_day = $_ENV['class_expire_reset_day']; if ($reset_traffic >= 0) { $user->transfer_enable = Tools::toGB($reset_traffic); $user->class_expire = date('Y-m-d H:i:s', strtotime("+$reset_day days")); $text .= '已转为免费体验帐户,赠送' . $reset_traffic . 'GB体验流量,'.$reset_day.'天体验时长。'; } // todo 反水 // 这里返水的话,等于是直接覆盖的用户不返水,自然过期的用户返水,这到合理,自然过期的才是好用户 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 = 1; $user->save(); } //// 流量到期处理 | 不重置了,默认暂停就行 // 这里,只重置付费用户,不重置免费用户。那么假如免费用户领了60g流量,他就真的能用到没。付费用户则会连带套餐一起重置掉。 // 应该问题不大,赠送流量有关时,区分免费付费用户,对付费大方(陷阱),对免费抠门。 // 其实压根就不叠加,套餐流量直接覆盖免费流量。 // 再者,免费流量 // 总之,免费流量就是零头,无所谓重置的。 // 用户流量用光,或者用户等级到期,就重置。 // 流量用光时顺便标记订单。在这里标记不容易埋坑。 // $ud = $user->u + $user->d; // if ($ud >= $user->transfer_enable) { // // // 订单标记为过期 // // 既然class大于0,就说明肯定有激活的套餐,对吧 // $activated_order = (new Order())->where('user_id', $user->id) // ->where('status', 'activated') // ->where('product_type', 'tabp') // ->orderBy('id') // ->first(); // // // 以防万一,判断一下 // if (!$activated_order) { // $activated_order->status = 'expired'; // $activated_order->update_time = time(); // $activated_order->save(); // } // //// tomd($activated_order, 1); // // 用户流量重置 // $user->u = 0; // $user->d = 0; // $user->class = 0; // $user->transfer_today = 0; // $user->transfer_enable = 0; // $user->class_expire = date("Y-m-d H:i:s"); // 用户等级时间到当前,注意订单没变,应该合理 // $user->save(); // // $sText = "你好,系统发现您的流量已用完,已转为免费体验帐户,可用每日签到获取免费流量。"; // // try { // Notification::notifyUser($user, $_ENV['appName'] . '-您的流量已用完', $sText); // } catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) { // echo $e->getMessage() . PHP_EOL; // } // // } //// end } 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; } } // 激活订单 /** 逻辑: 查询,遍历所有用户 根据uid,找待激活订单 根据uid,找已激活订单 判断:当有待激活订单时: 判断:无已激活订单,激活 判断:此用户流量满了,激活。就加了这个。 无待激活订单时,确定不激活 流量没满时,走原来的逻辑,无已激活时激活 判断:有已激活订单 判断:订单的等级时间过期,标记过期 */ 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(); /* 添加条件,当流量用满时,可以直接激活新套餐。 那如果,没有新套餐,就等于旧套餐被暂停了,等到用户等级过期时,走原有的重置,没错。 那如果,有新套餐,那就是直接覆盖,刷新。 也就是说,用户流量不满时,就是当前套餐到期再激活新套餐。 而用户流量满时,就是直接激活新套餐。 这里是有点怪,两个玩法。何不直接叠加? 如果都直接叠加,会怎样? 连充12个月会员,直接是一年,好像没问题?之前为什么否了? 好像是因为续流量比较亏,因为时间是虚的而流量是实的。 但这其实没办法,不管用什么方案,流量满了总得允许人家续,没得变。 根本区别就是,叠加有粘性,不叠加省成本。 用户流量满了,他就叠流量,最后时间剩很多,流量没有,套餐就成了流量包。 “我套餐还没到期,但是流量已经没了,无所谓”,所以这里不是留住客户的理由。 用户时间到了,他就叠时间,最后流量用不完,时间快到期了。 那基本上他会狂用,看4k被。 “我流量还没用完呢,续一下” “这么多流量,看高清” 理想情况,时间叠褶叠着,流量用不完,然后时间到期,一下清空。 这种情况,应该不会太多,他流量多自然就不会省着用了。 所以,还是别叠加,留住客户可以返水之类的,而不是在这里暗着给他们实惠。 不叠加指的是时间不叠加,流量还是要叠的,这没有其他办法。 顶多是流量满了直接重置时间,但这想想没必要。时间是虚的,重置不重置没什么区别,倒是产生业务上的混乱感。 完全没必要,流量满了其实就暂停了,再买套餐就覆盖。重置也相当于暂停,再买套餐也是覆盖,都一样。 之前的逻辑怪圈,就是他原本用户等级到期会重置,我就想当然的做成流量满了也重置。 首先他为什么原本到期重置 因为订单function只影响订单状态,不影响用户状态,他俩是独立的。 if订单到期,修改。if用户等级到期,修改。代码上是各玩各的,只是刚好订单和用户等级的时间是一致的,所以会同时处理。 你被误导的地方是有可激活订单时,会去覆盖用户。就仿佛他俩是直接关联的。 但要是没有可激活订单呢?用户就一直不重置了,到流量用完暂停。这就是要有用户重置的原因。 用户重置是基础,订单的重置是补偿。 那流量为什么不能重置 因为没必要,反而导致业务逻辑奇怪。 为什么奇怪,用户原本是20号到期,流量用光变成了10号到期,他会迷茫。 用户10号到期也就算了,订单状态也跟着变了,提前到期了。就很怪。 所以就不重置它,保持暂停就行了。等待时间到期走原本的重置。 还有一个点,webapi那边会判断流量满了暂停,而不会判断等级到期暂停,所以要有用户时间重置,不然就会跑到流量用光为止。 所以流量满了可以不重置,会自然暂停。 最后确定一下,到底行不行,别又弄错了。 现在就是说,只要流量满了,就直接激活待激活套餐,即便还存在已激活订单。 就会同时会出现两个激活套餐,这倒没事,只要代码没有要求只读一个激活套餐。到时检查一下。 其他地方都不用动,爱加就加个流量提醒(本来就有)。对吗? 对,就是这样,流量满了就允许直接激活覆盖新套餐,就这个逻辑。 实在不放心,就把当前订单标记成过期。 没问题,确实没问题,就这么干。 再缕一次 起初,发现流量满了不能激活套餐(原本是用流量包续) 我最初是重置了套餐满了的用户流量,但没重置订单状态。幸亏,是只重置class大于0,不会有大的损失。 然后想到应该标记订单,当前订单标记成过期,下一个订单就能激活了。 至于用户function那边,应该不用重置,它自然暂停即可。 然后又想到,那会导致业务逻辑怪异,所以就在这加个判断,流量满时直接激活下一个订单,不考虑是否有已激活。这个问题也就解决了。 所以就是说,我要解决《用户流量满了不能激活新套餐的问题》,我就在这里加个判断,逻辑是《流量满了直接激活新套餐,即便存在已激活订单》。 订单状态有几种? 待激活 - 必须条件 已激活 - 附加条件A(反向) 取消 - 不相关,必然不进 等待付款 - 不相关,必然不进 也就是 如果有待激活,就直接激活,不管是否有已激活 如果没有待激活,不管。自然暂停,自然重置。 没有其他情况,对吗? 两种条件会激活: 没有已激活订单,只有待激活订单 有激活订单,用户流量满了 其他条件不会激活? 那如果,没有已激活订单,同时用户流量满了呢? 也会走进来 那无所谓,是按原有逻辑激活 没有待激活是肯定不会进来的 换个说法就是,有待激活,并且没有已激活或者用户流量满了,就进来 激活的必须条件是有待激活订单 然后没有已激活,或者用户流量满了 可能的条件(情况): 有待激活订单,用户流量满 - 已测试 有待激活订单,无已激活订单 - 已测试 假如用户没有已激活套餐,却有等级流量呢 当然是不用管他,活动获取的呗 有待激活订单,用户流量满,无已激活订单 没问题,等于走原来的逻辑 不可能的情况: 有待激活套餐,有激活套餐,用户流量不满 - 已测试 没有待激活套餐 - 不可能 */ // 如果用户账户中没有已激活的TABP订单,且有等待激活的TABP订单,则激活最早的等待激活TABP订单 // * 如果用户流量已满,就直接激活待激活订单,不考虑是否有已激活。(之前想的是流量满时把订单标记成过期,这虽然也可以,但是业务逻辑会显得奇怪) $ud = $user->u + $user->d; // 待激活订单 > 0 && (没有已激活订单 || 用户流量满) if (count($pending_activation_orders) > 0 && ($activated_order === null || $ud >= $user->transfer_enable)) { // if ($activated_order === null && count($pending_activation_orders) > 0) { // if (($activated_order === null && count($pending_activation_orders) > 0) || ($ud > $user->transfer_enable && count($pending_activation_orders) > 0)) { $order = $pending_activation_orders[0]; // 获取TABP订单内容准备激活 $content = json_decode($order->product_content); //// * 是用户流量满时,主动标记订单过期 // 按理说可以有多个已激活,但是以防万一,标记掉 // 假如等级还过期了,就是标记两次,无所谓 if ($ud >= $user->transfer_enable && $activated_order !== null) { $activated_order->status = 'expired'; $activated_order->update_time = time(); $activated_order->save(); echo "TABP订单 #{$activated_order->id} 已过期(流量满了)。\n"; } //// end //// * 特别活动追加,季套餐赠月套餐,年套餐赠季套餐 if ($order->huodong_class && $order->huodong_class == "A") { // 按理是大于30天,做个冗余 if ($content->class_time > 32) { $oProduct = Product::where("name", $order->product_name) ->where("type_by_time", "<", $content->class_time) ->orderBy("type_by_time", "desc") ->first(); if ($oProduct) { $oProductContent = json_decode($oProduct->content); $content->class_time += $oProductContent->class_time; $content->bandwidth += $oProductContent->bandwidth; } } } //// end // 激活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); // 原来用的是$content->time 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; } // 时间包激活,暂时没用 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; // } // 每小时,统计节点流量,入库,tg警报 public static function mergeBandWidth(): void { $oNodeAll = Node::where('type', 1)->get(); $aZhuanfaFlowNow = []; $aDuckFlowNow = []; $sMsg = ""; // 统计node流量 foreach ($oNodeAll as $oNodeRow) { if ($oNodeRow->node_bandwidth == 0) { $sMsg .= '🚨【'.$oNodeRow->name."】小时未耗流,可能异常。\n"; } @$aZhuanfaFlowNow[$oNodeRow->zhuanfa_code] += $oNodeRow->node_bandwidth; @$aDuckFlowNow[$oNodeRow->duck_code] += $oNodeRow->node_bandwidth; } // 入库zhunafa foreach ($aZhuanfaFlowNow as $k => $v) { if ($v) { Zhuanfas::where('code', $k)->increment('today_width', $v); Zhuanfas::where('code', $k)->increment('use_width', $v); } } // 入库duck foreach ($aDuckFlowNow as $k => $v) { if ($v) { Ducks::where('code', $k)->increment('today_width', $v); Ducks::where('code', $k)->increment('use_width', $v); } } // 清空node流量 Node::query()->update(['node_bandwidth' => 0]); if ($sMsg) { // echo $sMsg; (new Telegram())->send(0, $sMsg); } } // 每日报表 public static function reportBandWidth(): void { $oZhuanfas = new Zhuanfas(); $oDucks = new Ducks(); // 流量报告,转发 $oTgSendFormat = new TgSendFormat(); $oTgSendFormat->sTitleEmoji = "\n\n"; $oTgSendFormat->sTitle = "# 转发流量"; $today = new DateTime(); $oZhuanfasAll = $oZhuanfas->all(); foreach ($oZhuanfasAll as $oZhuanfasRow) { $targetDate = new DateTime($oZhuanfasRow->end_date); $interval = $today->diff($targetDate); if ($today > $targetDate) { $iDiffDay = -$interval->days; } else { $iDiffDay = $interval->days; } $oTgSendFormat->rowAdd( $oZhuanfasRow->name, $oZhuanfasRow->today_width, $oZhuanfasRow->use_width, $oZhuanfasRow->max_width, $iDiffDay ); } $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, $oDucksRow->today_width, $oDucksRow->use_width, $oDucksRow->max_width, $iWidthResetDay, $oDucksRow->memo ); } $sTgReport = $oTgSendFormat->typeA(); // var_dump($sTgReport); (new Telegram())->send(0, $sTgReport); } // 每日清空duck和转发的每日流量 public static function clearTodayWidth(): void { Ducks::query()->update(['today_width' => 0]); Zhuanfas::query()->update(['today_width' => 0]); } public static function reportBandWidth_back(): 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(); (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(); (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 resetFreeUserBandwidth_new(): void { echo 1111; } // 财务日报,好像没用 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 .= '