1272 lines
52 KiB
PHP
Executable File
1272 lines
52 KiB
PHP
Executable File
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\Ann;
|
||
use App\Models\Config;
|
||
use App\Models\DetectLog;
|
||
use App\Models\EmailQueue;
|
||
use App\Models\HourlyUsage;
|
||
use App\Models\Invoice;
|
||
use App\Models\Node;
|
||
use App\Models\OnlineLog;
|
||
use App\Models\Order;
|
||
use App\Models\Paylist;
|
||
use App\Models\SubscribeLog;
|
||
use App\Models\User;
|
||
use App\Models\TgStep;
|
||
use App\Models\Zhuanfas;
|
||
use App\Models\Ducks;
|
||
use App\Models\Visitor;
|
||
use App\Models\CountVisitor;
|
||
use App\Models\CountUser;
|
||
use App\Models\Product;
|
||
use App\Services\TomTool\TgSendFormat;
|
||
use App\Services\IM\Telegram;
|
||
use App\Services\Tg\ReportUser;
|
||
use App\Utils\Tools;
|
||
use DateTime;
|
||
use Exception;
|
||
use GuzzleHttp\Exception\GuzzleException;
|
||
use Psr\Http\Client\ClientExceptionInterface;
|
||
use Telegram\Bot\Exceptions\TelegramSDKException;
|
||
use function array_map;
|
||
use function date;
|
||
use function in_array;
|
||
use function json_decode;
|
||
use function str_replace;
|
||
use function strtotime;
|
||
use function time;
|
||
use const PHP_EOL;
|
||
|
||
final class Cron
|
||
{
|
||
// 数据库清理
|
||
public static function cleanDb(): void
|
||
{
|
||
(new SubscribeLog())->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 = '<table border=1><tr><td>金额</td><td>用户ID</td><td>用户名</td><td>充值时间</td>';
|
||
|
||
foreach ($paylists as $paylist) {
|
||
$text_html .= '<tr>';
|
||
$text_html .= '<td>' . $paylist->total . '</td>';
|
||
$text_html .= '<td>' . $paylist->userid . '</td>';
|
||
$text_html .= '<td>' . (new User())->find($paylist->userid)->user_name . '</td>';
|
||
$text_html .= '<td>' . Tools::toDateTime((int) $paylist->datetime) . '</td>';
|
||
$text_html .= '</tr>';
|
||
}
|
||
|
||
$text_html .= '</table>';
|
||
$text_html .= '<br>昨日总收入笔数:' . count($paylists) . '<br>昨日总收入金额:' . $paylists->sum('total');
|
||
echo 'Sending daily finance email to admin user' . PHP_EOL;
|
||
|
||
try {
|
||
Notification::notifyAdmin(
|
||
'财务日报',
|
||
$text_html,
|
||
'finance.tpl'
|
||
);
|
||
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
|
||
echo $e->getMessage() . PHP_EOL;
|
||
}
|
||
|
||
echo Tools::toDateTime(time()) . ' 成功发送财务日报' . PHP_EOL;
|
||
}
|
||
|
||
// 财务周报,好像没用
|
||
public static function sendWeeklyFinanceMail(): void
|
||
{
|
||
$today = strtotime('00:00:00');
|
||
$paylists = (new Paylist())->where('status', 1)
|
||
->whereBetween('datetime', [strtotime('-1 week', $today), $today])
|
||
->get();
|
||
|
||
$text_html = '<br>上周总收入笔数:' . count($paylists) . '<br>上周总收入金额:' . $paylists->sum('total');
|
||
echo 'Sending weekly finance email to admin user' . PHP_EOL;
|
||
|
||
try {
|
||
Notification::notifyAdmin(
|
||
'财务周报',
|
||
$text_html,
|
||
'finance.tpl'
|
||
);
|
||
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
|
||
echo $e->getMessage() . PHP_EOL;
|
||
}
|
||
|
||
echo Tools::toDateTime(time()) . ' 成功发送财务周报' . PHP_EOL;
|
||
}
|
||
|
||
// 财务越好,好像没用
|
||
public static function sendMonthlyFinanceMail(): void
|
||
{
|
||
$today = strtotime('00:00:00');
|
||
$paylists = (new Paylist())->where('status', 1)
|
||
->whereBetween('datetime', [strtotime('-1 month', $today), $today])
|
||
->get();
|
||
|
||
$text_html = '<br>上月总收入笔数:' . count($paylists) . '<br>上月总收入金额:' . $paylists->sum('total');
|
||
echo 'Sending monthly finance email to admin user' . PHP_EOL;
|
||
|
||
try {
|
||
Notification::notifyAdmin(
|
||
'财务月报',
|
||
$text_html,
|
||
'finance.tpl'
|
||
);
|
||
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
|
||
echo $e->getMessage() . PHP_EOL;
|
||
}
|
||
|
||
echo Tools::toDateTime(time()) . ' 成功发送财务月报' . PHP_EOL;
|
||
}
|
||
|
||
// 流量提醒
|
||
public static function sendPaidUserUsageLimitNotification(): void
|
||
{
|
||
$paidUsers = (new User())->where('class', '>', 0)->get();
|
||
|
||
foreach ($paidUsers as $user) {
|
||
$user_traffic_left = $user->transfer_enable - $user->u - $user->d;
|
||
$under_limit = false;
|
||
$unit_text = '';
|
||
|
||
if ($_ENV['notify_limit_mode'] === 'per' &&
|
||
$user_traffic_left / $user->transfer_enable * 100 < $_ENV['notify_limit_value']
|
||
) {
|
||
$under_limit = true;
|
||
$unit_text = '%';
|
||
} elseif ($_ENV['notify_limit_mode'] === 'mb' &&
|
||
Tools::flowToMB($user_traffic_left) < $_ENV['notify_limit_value']
|
||
) {
|
||
$under_limit = true;
|
||
$unit_text = 'MB';
|
||
}
|
||
|
||
if ($under_limit && ! $user->traffic_notified) {
|
||
try {
|
||
Notification::notifyUser(
|
||
$user,
|
||
$_ENV['appName'] . '-你的剩余流量过低',
|
||
'你好,系统发现你剩余流量已经低于 ' . $_ENV['notify_limit_value'] . $unit_text . ' 。',
|
||
);
|
||
|
||
$user->traffic_notified = true;
|
||
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
|
||
$user->traffic_notified = false;
|
||
echo $e->getMessage() . PHP_EOL;
|
||
}
|
||
|
||
$user->save();
|
||
} elseif (! $under_limit && $user->traffic_notified) {
|
||
$user->traffic_notified = false;
|
||
$user->save();
|
||
}
|
||
}
|
||
|
||
echo Tools::toDateTime(time()) . ' 付费用户用量限制提醒完成' . PHP_EOL;
|
||
}
|
||
|
||
public static function sendDailyTrafficReport(): void
|
||
{
|
||
$users = (new User())->whereIn('daily_mail_enable', [1, 2])->get();
|
||
$ann_latest_raw = (new Ann())->orderBy('date', 'desc')->first();
|
||
|
||
if ($ann_latest_raw === null) {
|
||
$ann_latest = '<br><br>';
|
||
} else {
|
||
$ann_latest = $ann_latest_raw->content . '<br><br>';
|
||
}
|
||
|
||
foreach ($users as $user) {
|
||
$user->sendDailyNotification($ann_latest);
|
||
}
|
||
|
||
echo Tools::toDateTime(time()) . ' 成功发送每日流量报告' . PHP_EOL;
|
||
}
|
||
|
||
/**
|
||
* @throws TelegramSDKException
|
||
*/
|
||
public static function sendTelegramDailyJob(): void
|
||
{
|
||
(new Telegram())->send(0, Config::obtain('telegram_daily_job_text'));
|
||
|
||
echo Tools::toDateTime(time()) . ' 成功发送 Telegram 每日任务提示' . PHP_EOL;
|
||
}
|
||
|
||
/**
|
||
* @throws TelegramSDKException
|
||
*/
|
||
public static function sendTelegramDiary(): void
|
||
{
|
||
(new Telegram())->send(
|
||
0,
|
||
str_replace(
|
||
[
|
||
'%getTodayCheckinUser%',
|
||
'%lastday_total%',
|
||
],
|
||
[
|
||
Analytics::getTodayCheckinUser(),
|
||
Analytics::getTodayTrafficUsage(),
|
||
],
|
||
Config::obtain('telegram_diary_text')
|
||
)
|
||
);
|
||
|
||
echo Tools::toDateTime(time()) . ' 成功发送 Telegram 系统运行日志' . PHP_EOL;
|
||
}
|
||
|
||
public static function updateNodeIp(): void
|
||
{
|
||
$nodes = (new Node())->where('type', 1)->get();
|
||
|
||
foreach ($nodes as $node) {
|
||
$node->updateNodeIp();
|
||
$node->save();
|
||
}
|
||
|
||
echo Tools::toDateTime(time()) . ' 更新节点 IP 完成' . PHP_EOL;
|
||
}
|
||
}
|