From dc3277e0d646ecf3296f3081449751f30ec7faea Mon Sep 17 00:00:00 2001
From: BrettonYe <867057410@qq.com>
Date: Fri, 30 Jan 2026 10:46:07 +0800
Subject: [PATCH] Improve batch processing and shop logic
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Use chunkById() with default chunk size for batch processing;
- Improve plan/package expiration and prepaid activation logic;
- Refactor user–node permission update logic
- Optimize OrderService state handling
- Minor fixes (null handling, query order, comments)
---
app/Console/Commands/ServiceTimer.php | 4 +-
app/Console/Commands/TaskAuto.php | 14 +--
app/Console/Commands/TaskDaily.php | 10 +-
app/Console/Commands/TaskHourly.php | 6 +-
app/Console/Commands/UserExpireWarning.php | 2 +-
app/Console/Commands/UserTrafficWarning.php | 2 +-
app/Http/Controllers/Admin/LogsController.php | 2 +-
app/Http/Controllers/Admin/NodeController.php | 6 +-
app/Http/Controllers/Admin/UserController.php | 4 +-
.../Controllers/User/InvoiceController.php | 19 +---
app/Http/Controllers/UserController.php | 4 +-
app/Observers/OrderObserver.php | 105 ++++++++++--------
app/Observers/UserObserver.php | 76 ++++++++-----
app/Services/OrderService.php | 63 +++++------
app/Utils/Helpers.php | 6 +-
composer.json | 4 +-
resources/views/user/invoices.blade.php | 22 ++--
17 files changed, 184 insertions(+), 165 deletions(-)
diff --git a/app/Console/Commands/ServiceTimer.php b/app/Console/Commands/ServiceTimer.php
index 50a4a8ff..4231cd04 100644
--- a/app/Console/Commands/ServiceTimer.php
+++ b/app/Console/Commands/ServiceTimer.php
@@ -24,8 +24,8 @@ class ServiceTimer extends Command
private function expiredPlan(): void
{
- Order::activePlan()->where('expired_at', '<=', now())->chunk(sysConfig('tasks_chunk'), function ($orders) {
- $orders->each->expired(); // 过期订单
+ Order::activePlan()->where('expired_at', '<=', now())->chunkById((int) sysConfig('tasks_chunk', 3000), function ($orders) {
+ $orders->each->expired(); // 过期订单,触发 Observer
});
}
}
diff --git a/app/Console/Commands/TaskAuto.php b/app/Console/Commands/TaskAuto.php
index 7fd8a131..78a933d6 100644
--- a/app/Console/Commands/TaskAuto.php
+++ b/app/Console/Commands/TaskAuto.php
@@ -50,7 +50,7 @@ class TaskAuto extends Command
$query->recentUnPay(); // 关闭超时未支付本地订单
})->orWhere(function (Builder $query) {
$query->whereStatus(1)->where('created_at', '<=', date('Y-m-d H:i:s', strtotime(sysConfig('tasks_close.confirmation_orders')))); // 关闭未处理的人工支付订单
- })->chunk(sysConfig('tasks_chunk'), function ($orders) {
+ })->chunkById((int) sysConfig('tasks_chunk', 3000), function ($orders) {
$orders->each->close();
});
}
@@ -78,7 +78,7 @@ class TaskAuto extends Command
$query->whereStatus(1); // 获取有订阅且未被封禁用户
})->whereHas('subscribeLogs', function (Builder $query) {
$query->whereDate('request_time', '>=', now()->subDay()); // ->distinct()->count('request_ip');
- }, '>=', sysConfig('subscribe_rate_limit'))->chunk(sysConfig('tasks_chunk'), function ($users) use ($banMsg, $dirtyWorks) {
+ }, '>=', sysConfig('subscribe_rate_limit'))->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) use ($banMsg, $dirtyWorks) {
foreach ($users as $user) {
$user->subscribe->update($dirtyWorks);
$user->banedLogs()->create($banMsg); // 记录封禁日志
@@ -88,7 +88,7 @@ class TaskAuto extends Command
private function unblockSubscribes(): void
{
- UserSubscribe::whereStatus(0)->where('ban_time', '<=', time())->chunk(sysConfig('tasks_chunk'), function ($subscribes) {
+ UserSubscribe::whereStatus(0)->where('ban_time', '<=', time())->chunkById((int) sysConfig('tasks_chunk', 3000), function ($subscribes) {
$subscribes->each->update(['status' => 1, 'ban_time' => null, 'ban_desc' => null]);
});
}
@@ -96,7 +96,7 @@ class TaskAuto extends Command
private function blockUsers(): void
{ // 封禁账号
// 禁用流量超限用户
- User::activeUser()->whereRaw('u + d >= transfer_enable')->chunk(sysConfig('tasks_chunk'), function ($users) {
+ User::activeUser()->whereRaw('u + d >= transfer_enable')->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) {
$users->each(function ($user) {
$user->update(['enable' => 0]);
$user->banedLogs()->create(['description' => __('[Auto Task] Blocked service: Run out of traffic')]);
@@ -110,7 +110,7 @@ class TaskAuto extends Command
$userService = new UserService;
User::activeUser()->whereBanTime(null)->where('t', '>=', strtotime('-5 minutes')) // 只检测最近5分钟有流量使用的用户
- ->chunk(sysConfig('tasks_chunk'), function ($users) use ($userService, $ban_time, $trafficBanTime) {
+ ->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) use ($userService, $ban_time, $trafficBanTime) {
$users->each(function ($user) use ($userService, $ban_time, $trafficBanTime) {
$userService->setUser($user);
if ($userService->isTrafficWarning()) {
@@ -125,7 +125,7 @@ class TaskAuto extends Command
private function unblockUsers(): void
{ // 解封账号
// 解封被临时封禁的账号
- User::bannedUser()->where('ban_time', '<', time())->chunk(sysConfig('tasks_chunk'), function ($users) {
+ User::bannedUser()->where('ban_time', '<', time())->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) {
$users->each(function ($user) {
$user->update(['enable' => 1, 'ban_time' => null]);
$user->banedLogs()->create(['description' => __('[Auto Task] Unblocked Service: Account ban expired')]);
@@ -133,7 +133,7 @@ class TaskAuto extends Command
});
// 可用流量大于已用流量也解封(比如:邀请返利加了流量)
- User::bannedUser()->whereBanTime(null)->where('expired_at', '>=', date('Y-m-d'))->whereRaw('u + d < transfer_enable')->chunk(sysConfig('tasks_chunk'), function ($users) {
+ User::bannedUser()->whereBanTime(null)->where('expired_at', '>=', date('Y-m-d'))->whereRaw('u + d < transfer_enable')->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) {
$users->each(function ($user) {
$user->update(['enable' => 1]);
$user->banedLogs()->create(['description' => __('[Auto Task] Unblocked Service: Account has available data traffic')]);
diff --git a/app/Console/Commands/TaskDaily.php b/app/Console/Commands/TaskDaily.php
index ebde7b9f..2a5c50d7 100644
--- a/app/Console/Commands/TaskDaily.php
+++ b/app/Console/Commands/TaskDaily.php
@@ -60,7 +60,7 @@ class TaskDaily extends Command
}
User::activeUser()->where('expired_at', '<', date('Y-m-d')) // 过期
- ->chunk(sysConfig('tasks_chunk'), function ($users) use ($banMsg, $dirtyWorks) {
+ ->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) use ($banMsg, $dirtyWorks) {
$users->each(function ($user) use ($banMsg, $dirtyWorks) {
$user->update($dirtyWorks);
Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, 0, $banMsg);
@@ -74,7 +74,7 @@ class TaskDaily extends Command
$closeTicketsHours = (time() - strtotime(sysConfig('tasks_close.tickets'))) / 3600;
Ticket::whereStatus(1)->with('reply')->whereHas('reply', function ($query) {
$query->where('admin_id', '<>', null);
- })->where('updated_at', '<=', date('Y-m-d H:i:s', strtotime(sysConfig('tasks_close.tickets'))))->chunk(sysConfig('tasks_chunk'), function ($tickets) use ($closeTicketsHours) {
+ })->where('updated_at', '<=', date('Y-m-d H:i:s', strtotime(sysConfig('tasks_close.tickets'))))->chunkById((int) sysConfig('tasks_chunk', 3000), function ($tickets) use ($closeTicketsHours) {
$tickets->each(function ($ticket) use ($closeTicketsHours) {
if ($ticket->close()) {
$ticket->user->notify(new TicketClosed($ticket->id, $ticket->title, route('ticket.edit', $ticket),
@@ -91,7 +91,7 @@ class TaskDaily extends Command
$query->activePlan();
})->with(['orders' => function ($query) {
$query->activePlan();
- }])->chunk(sysConfig('tasks_chunk'), function ($users) {
+ }])->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) {
$users->each(function ($user) {
$user->orders()->activePackage()->update(['is_expire' => 1]); // 过期生效中的加油包
$order = $user->orders->first(); // 取出用户正在使用的套餐
@@ -126,7 +126,7 @@ class TaskDaily extends Command
'dataFlowLogs' => function ($query) use ($start, $end) {
$query->whereBetween('log_time', [$start, $end]);
},
- ])->chunk(sysConfig('tasks_chunk'), function ($users) use ($created_at) {
+ ])->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) use ($created_at) {
foreach ($users as $user) {
$dataFlowLogs = $user->dataFlowLogs->groupBy('node_id');
@@ -174,7 +174,7 @@ class TaskDaily extends Command
'userDataFlowLogs as d_sum' => function ($query) use ($start, $end) {
$query->select(DB::raw('SUM(d)'))->whereBetween('log_time', [$start, $end]);
},
- ])->chunk(sysConfig('tasks_chunk'), function ($nodes) use ($created_at) {
+ ])->chunkById((int) sysConfig('tasks_chunk', 3000), function ($nodes) use ($created_at) {
foreach ($nodes as $node) {
$node->dailyDataFlows()->create([
'u' => $node->u_sum,
diff --git a/app/Console/Commands/TaskHourly.php b/app/Console/Commands/TaskHourly.php
index 28088733..a892a53e 100644
--- a/app/Console/Commands/TaskHourly.php
+++ b/app/Console/Commands/TaskHourly.php
@@ -34,14 +34,14 @@ class TaskHourly extends Command
$end = strtotime($created_at);
$start = $end - 3599;
$data_anomaly_notification = sysConfig('data_anomaly_notification');
- $traffic_abuse_threshold = (int) sysConfig('traffic_abuse_limit') * GiB;
+ $traffic_abuse_threshold = (int) sysConfig('traffic_abuse_limit', 20) * GiB;
User::activeUser()->whereHas('dataFlowLogs', function (Builder $query) use ($start, $end) {
$query->whereBetween('log_time', [$start, $end]);
})->with([
'dataFlowLogs' => function ($query) use ($start, $end) {
$query->whereBetween('log_time', [$start, $end]);
},
- ])->chunk(sysConfig('tasks_chunk'), function ($users) use ($traffic_abuse_threshold, $created_at, $data_anomaly_notification) {
+ ])->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) use ($traffic_abuse_threshold, $created_at, $data_anomaly_notification) {
foreach ($users as $user) {
$dataFlowLogs = $user->dataFlowLogs->groupBy('node_id');
@@ -97,7 +97,7 @@ class TaskHourly extends Command
'userDataFlowLogs as d_sum' => function ($query) use ($start, $end) {
$query->select(DB::raw('SUM(d)'))->whereBetween('log_time', [$start, $end]);
},
- ])->chunk(sysConfig('tasks_chunk'), function ($nodes) use ($created_at) {
+ ])->chunkById((int) sysConfig('tasks_chunk', 3000), function ($nodes) use ($created_at) {
foreach ($nodes as $node) {
$node->hourlyDataFlows()->create(['u' => $node->u_sum, 'd' => $node->d_sum, 'created_at' => $created_at]);
}
diff --git a/app/Console/Commands/UserExpireWarning.php b/app/Console/Commands/UserExpireWarning.php
index 0002c9d0..2aa66007 100644
--- a/app/Console/Commands/UserExpireWarning.php
+++ b/app/Console/Commands/UserExpireWarning.php
@@ -30,7 +30,7 @@ class UserExpireWarning extends Command
// 只取没被禁用的用户,其他不用管
User::whereEnable(1)
->where('expired_at', '<', date('Y-m-d', strtotime(sysConfig('expire_days').' days')))
- ->chunk(sysConfig('tasks_chunk'), function ($users) {
+ ->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) {
foreach ($users as $user) {
if (filter_var($user->username, FILTER_VALIDATE_EMAIL) === false) { // 用户账号不是邮箱的跳过
continue;
diff --git a/app/Console/Commands/UserTrafficWarning.php b/app/Console/Commands/UserTrafficWarning.php
index c3a77c41..e3c81bb1 100644
--- a/app/Console/Commands/UserTrafficWarning.php
+++ b/app/Console/Commands/UserTrafficWarning.php
@@ -28,7 +28,7 @@ class UserTrafficWarning extends Command
private function userTrafficWarning(): void
{ // 用户流量超过警告阈值提醒
$trafficWarningPercent = sysConfig('traffic_warning_percent');
- User::activeUser()->where('transfer_enable', '>', 0)->chunk(sysConfig('tasks_chunk'), function ($users) use ($trafficWarningPercent) {
+ User::activeUser()->where('transfer_enable', '>', 0)->chunkById((int) sysConfig('tasks_chunk', 3000), function ($users) use ($trafficWarningPercent) {
foreach ($users as $user) {
// 用户账号不是邮箱的跳过
if (filter_var($user->username, FILTER_VALIDATE_EMAIL) === false) {
diff --git a/app/Http/Controllers/Admin/LogsController.php b/app/Http/Controllers/Admin/LogsController.php
index 89abc2bb..ca662bb6 100644
--- a/app/Http/Controllers/Admin/LogsController.php
+++ b/app/Http/Controllers/Admin/LogsController.php
@@ -223,7 +223,7 @@ class LogsController extends Controller
});
});
- return view('admin.logs.userTraffic', ['userTrafficLogs' => $query->latest()->paginate(15)->appends($request->except('page'))]);
+ return view('admin.logs.userTraffic', ['userTrafficLogs' => $query->orderByDesc('id')->paginate(15)->appends($request->except('page'))]);
}
public function userOnlineIPList(Request $request): View
diff --git a/app/Http/Controllers/Admin/NodeController.php b/app/Http/Controllers/Admin/NodeController.php
index 8bb86d09..0cb94e02 100644
--- a/app/Http/Controllers/Admin/NodeController.php
+++ b/app/Http/Controllers/Admin/NodeController.php
@@ -146,9 +146,9 @@ class NodeController extends Controller
'method' => $info['method'],
'protocol' => $info['protocol'],
'obfs' => $info['obfs'],
- 'obfs_param' => $info['obfs_param'],
- 'protocol_param' => $info['protocol_param'],
- 'passwd' => $info['passwd'],
+ 'obfs_param' => $info['obfs_param'] ?? null,
+ 'protocol_param' => $info['protocol_param'] ?? null,
+ 'passwd' => $info['passwd'] ?? null,
];
break;
}
diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php
index 1126be3f..a8386812 100644
--- a/app/Http/Controllers/Admin/UserController.php
+++ b/app/Http/Controllers/Admin/UserController.php
@@ -223,7 +223,7 @@ class UserController extends Controller
$data['transfer_enable'] *= GiB;
$data['enable'] = $data['status'] < 0 ? 0 : $data['enable'];
$data['expired_at'] = $data['expired_at'] ?? date('Y-m-d', strtotime('next year'));
- if ($data['remark']) {
+ if (! empty($data['remark'])) {
$data['remark'] = str_replace(['atob', 'eval'], '', $data['remark']);
}
@@ -253,7 +253,7 @@ class UserController extends Controller
$data['password'] = $password;
}
- if ($user->transfer_enable !== $data['transfer_enable']) {
+ if ($user->transfer_enable !== (int) $data['transfer_enable']) {
Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, $data['transfer_enable'], trans('Manually edit in dashboard.'));
}
diff --git a/app/Http/Controllers/User/InvoiceController.php b/app/Http/Controllers/User/InvoiceController.php
index bfe85f45..aacb50da 100644
--- a/app/Http/Controllers/User/InvoiceController.php
+++ b/app/Http/Controllers/User/InvoiceController.php
@@ -26,23 +26,12 @@ class InvoiceController extends Controller
public function activate(): JsonResponse
{ // 激活套餐
$activePlan = Order::userActivePlan()->first();
- if ($activePlan) {
- if ($activePlan->expired()) { // 关闭先前套餐后,新套餐自动运行
- if (Order::userActivePlan()->exists()) {
- return response()->json(['status' => 'success', 'message' => trans('common.active_item', ['attribute' => trans('common.success')])]);
- }
-
- return response()->json(['status' => 'success', 'message' => trans('common.close')]);
+ if ($activePlan && $activePlan->expired()) { // 关闭先前套餐后,新套餐自动运行
+ if (Order::userActivePlan()->exists()) {
+ return response()->json(['status' => 'success', 'message' => trans('common.active_item', ['attribute' => trans('common.success')])]);
}
- } else {
- $prepaidPlan = Order::userPrepay()->first();
- if ($prepaidPlan) { // 关闭先前套餐后,新套餐自动运行
- if ($prepaidPlan->complete()) {
- return response()->json(['status' => 'success', 'message' => trans('common.active_item', ['attribute' => trans('common.success')])]);
- }
- return response()->json(['status' => 'success', 'message' => trans('common.close')]);
- }
+ return response()->json(['status' => 'success', 'message' => trans('common.close')]);
}
return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.close')])]);
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index 6ad74d95..4f00766f 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -34,9 +34,7 @@ class UserController extends Controller
'remainDays' => $userService->getRemainingDays(),
'resetDays' => $userService->getResetDays(),
'unusedPercent' => $userService->getUnusedTrafficPercent(),
- 'announcements' => cache()->remember('announcements_'.app()->getLocale(), 300, function () {
- return Article::type(2)->lang()->latest()->simplePaginate(1); // 公告缓存 5 分钟
- }), // 公告
+ 'announcements' => Article::type(2)->lang()->latest()->simplePaginate(1), // 公告
'isTrafficWarning' => $userService->isTrafficWarning(), // 流量异常判断
'paying_user' => $userService->isActivePaying(), // 付费用户判断
'user' => $user->only(['sub_url', 'unused_traffic', 'expiration_date', 'ban_time']),
diff --git a/app/Observers/OrderObserver.php b/app/Observers/OrderObserver.php
index 289c9604..fe0f0797 100644
--- a/app/Observers/OrderObserver.php
+++ b/app/Observers/OrderObserver.php
@@ -16,60 +16,77 @@ class OrderObserver
public function updated(Order $order): void
{
$changes = $order->getChanges();
- // 套餐订单-流量包订单互联
- if (Arr::has($changes, 'is_expire') && $changes['is_expire'] === 1 && $order->goods->type === 2) {
- $user = $order->user;
- $user->update([ // 清理全部流量,重置重置日期和等级
- 'u' => 0,
- 'd' => 0,
- 'transfer_enable' => 0,
- 'reset_time' => null,
- 'level' => 0,
- 'enable' => 0,
- ]);
- Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, 0, trans('[Service Timer] Service Expiration'), $order->id);
- Order::userActivePackage($order->user_id)->update(['is_expire' => 1]); // 过期生效中的加油包
- $this->activatePrepaidPlan($order->user_id); // 激活预支付套餐
+ // 套餐订单-流量包订单互联:订单过期时直接处理
+ if (Arr::has($changes, 'is_expire') && $changes['is_expire'] === 1 && $order->goods && $order->goods->type === 2) {
+ $user = $order->user;
+ $oldTransferEnable = $user->transfer_enable;
+
+ // 过期加油包
+ Order::userActivePackage($order->user_id)->update(['is_expire' => 1]);
+
+ // 检查是否有预支付订单
+ $prepaidOrder = Order::userPrepay($order->user_id)->first();
+
+ if ($prepaidOrder) {
+ (new OrderService($prepaidOrder))->activatePlan();
+ } else {
+ // 无预支付订单:仅清理用户
+ $user->update([
+ 'u' => 0,
+ 'd' => 0,
+ 'transfer_enable' => 0,
+ 'reset_time' => null,
+ 'level' => 0,
+ 'enable' => 0,
+ ]);
+
+ Helpers::addUserTrafficModifyLog($user->id, $oldTransferEnable, 0, trans('[Service Timer] Service Expiration'), $order->id);
+ }
}
if (Arr::has($changes, 'status')) {
- if ($changes['status'] === -1) { // 本地订单-在线订单 关闭互联
- if ($order->payment) {
- $order->payment->close(); // 关闭在线订单
- }
+ $originalStatus = $order->getOriginal('status');
- if ($order->coupon) { // 退回优惠券
- $this->returnCoupon($order, $order->coupon);
- }
+ switch ($changes['status']) {
+ case -1: // 订单关闭
+ if ($order->payment) {
+ $order->payment->close();
+ }
- if ($order->goods && $order->goods->type === 2 && $order->getOriginal('status') === 2 && Order::userPrepay($order->user_id)->exists()) { // 下一个套餐
- $this->activatePrepaidPlan($order->user_id);
- } else {
- (new OrderService($order))->refreshAccountExpiration();
- }
- } elseif ($changes['status'] === 1) { // 待确认支付 通知管理
- Notification::send(User::find(1), new PaymentConfirm($order));
- } elseif ($changes['status'] === 2 && $order->getOriginal('status') !== 3) { // 本地订单-在线订单 支付成功互联
- (new OrderService($order))->receivedPayment();
- } elseif ($changes['status'] === 3) {
- if (Order::userActivePlan($order->user_id)->doesntExist()) {
- $this->activatePrepaidPlan($order->user_id);
- } else {
- (new OrderService($order))->refreshAccountExpiration();
- }
+ if ($order->coupon) {
+ $this->returnCoupon($order, $order->coupon);
+ }
+
+ if ($order->goods && $order->goods->type === 2 && $originalStatus === 2 && Order::userPrepay($order->user_id)->exists()) {
+ $prepaidOrder = Order::userPrepay($order->user_id)->first();
+ (new OrderService($prepaidOrder))->activatePlan();
+ } else {
+ (new OrderService($order))->refreshAccountExpiration();
+ }
+ break;
+ case 1: // 待确认支付
+ Notification::send(User::find(1), new PaymentConfirm($order));
+ break;
+ case 2: // 支付成功
+ if ($originalStatus !== 3) {
+ (new OrderService($order))->receivedPayment();
+ }
+ break;
+ case 3: // 预支付订单
+ if (Order::userActivePlan($order->user_id)->doesntExist()) {
+ $prepaidOrder = Order::userPrepay($order->user_id)->first();
+ if ($prepaidOrder) {
+ (new OrderService($prepaidOrder))->activatePlan();
+ }
+ } else {
+ (new OrderService($order))->refreshAccountExpiration();
+ }
+ break;
}
}
}
- private function activatePrepaidPlan(int $user_id): void
- { // 激活[预支付订单]
- $prepaidOrder = Order::userPrepay($user_id)->first(); // 检查该订单对应用户是否有预支付套餐
- if ($prepaidOrder) {
- (new OrderService($prepaidOrder))->activatePrepaidPlan(); // 激活预支付套餐
- }
- }
-
private function returnCoupon(Order $order, Coupon $coupon): void
{ // 退回优惠券
if ($coupon->type !== 3 && ! $coupon->isExpired()) {
diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php
index d529e891..330e456c 100644
--- a/app/Observers/UserObserver.php
+++ b/app/Observers/UserObserver.php
@@ -24,43 +24,61 @@ class UserObserver
public function updated(User $user): void
{
$changes = $user->getChanges();
- $enableChange = Arr::has($changes, ['enable']);
- if (($user->enable === 1 || $enableChange) && Arr::hasAny($changes, ['level', 'user_group_id', 'enable', 'port', 'passwd', 'speed_limit'])) {
- $allowNodes = $user->nodes()->whereType(4)->get();
- if (Arr::hasAny($changes, ['level', 'user_group_id'])) {
- $oldAllowNodes = $user->nodes($user->getOriginal('level'), $user->getOriginal('user_group_id'))->whereType(4)->get();
- if ($enableChange) {
- if ($user->enable === 0 && $oldAllowNodes->isNotEmpty()) {
- DelUser::dispatch($user->id, $oldAllowNodes);
- } elseif ($user->enable === 1 && $allowNodes->isNotEmpty()) {
- AddUser::dispatch($user->id, $allowNodes);
+ $enableChanged = Arr::has($changes, 'enable');
+ $permissionFieldsChanged = Arr::hasAny($changes, ['level', 'user_group_id']);
+ $configFieldsChanged = Arr::hasAny($changes, ['port', 'passwd', 'speed_limit']);
+
+ // 如果enable状态发生变化,或者用户当前已启用且权限或配置字段发生变化
+ if ($enableChanged || ($user->enable === 1 && ($permissionFieldsChanged || $configFieldsChanged))) {
+ // 获取当前允许的节点
+ $currentAllowedNodes = $user->nodes()->whereType(4)->get();
+
+ if ($permissionFieldsChanged) {
+ $oldAllowedNodes = $user->nodes($user->getOriginal('level'), $user->getOriginal('user_group_id'))->whereType(4)->get();
+ if ($enableChanged) {
+ if ($user->enable) {
+ // 用户被启用,添加到所有当前允许的节点
+ if ($currentAllowedNodes->isNotEmpty()) {
+ AddUser::dispatch($user->id, $currentAllowedNodes);
+ }
+ } elseif ($oldAllowedNodes->isNotEmpty()) {
+ DelUser::dispatch($user->id, $oldAllowedNodes);
}
} else {
- $old = $oldAllowNodes->diff($allowNodes); // old 有 allow 没有
- $new = $allowNodes->diff($oldAllowNodes); // allow 有 old 没有
- if ($old->isNotEmpty()) {
- DelUser::dispatch($user->id, $old);
+ // 计算差异
+ $nodesToRemove = $oldAllowedNodes->diff($currentAllowedNodes); // 用户失去权限的节点
+ $nodesToAdd = $currentAllowedNodes->diff($oldAllowedNodes); // 用户新增权限的节点
+
+ // 处理节点移除
+ if ($nodesToRemove->isNotEmpty()) {
+ DelUser::dispatch($user->id, $nodesToRemove);
}
- if ($new->isNotEmpty()) {
- AddUser::dispatch($user->id, $new);
+
+ // 处理节点添加
+ if ($nodesToAdd->isNotEmpty()) {
+ AddUser::dispatch($user->id, $nodesToAdd);
}
- if (Arr::hasAny($changes, ['port', 'passwd', 'speed_limit'])) {
- $same = $allowNodes->intersect($oldAllowNodes); // 共有部分
- if ($same->isNotEmpty()) {
- EditUser::dispatch($user, $same);
+
+ // 处理节点更新(权限未变但配置变了)
+ if ($configFieldsChanged && $currentAllowedNodes->isNotEmpty()) {
+ $nodesToUpdate = $currentAllowedNodes->intersect($oldAllowedNodes); // 权限未变但可能需要更新配置的节点
+ if ($nodesToUpdate->isNotEmpty()) {
+ EditUser::dispatch($user, $nodesToUpdate);
}
}
}
- } elseif ($allowNodes->isNotEmpty()) {
- if ($enableChange) {
- if ($user->enable === 1) { // TODO: 由于vnet未正确使用enable字段,临时解决方案
- AddUser::dispatch($user->id, $allowNodes);
- } else {
- DelUser::dispatch($user->id, $allowNodes);
- }
- } elseif (Arr::hasAny($changes, ['port', 'passwd', 'speed_limit'])) {
- EditUser::dispatch($user, $allowNodes);
+ } elseif ($enableChanged && $currentAllowedNodes->isNotEmpty()) {
+ // 启用状态变化处理
+ if ($user->enable) {
+ // 用户被启用,添加到所有允许的节点
+ AddUser::dispatch($user->id, $currentAllowedNodes);
+ } else {
+ // 用户被禁用,从所有允许的节点中移除
+ DelUser::dispatch($user->id, $currentAllowedNodes);
}
+ } elseif ($configFieldsChanged && $currentAllowedNodes->isNotEmpty()) {
+ // 仅配置变化,更新所有允许的节点
+ EditUser::dispatch($user, $currentAllowedNodes);
}
}
diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php
index e1e8fd47..06ecbdaa 100644
--- a/app/Services/OrderService.php
+++ b/app/Services/OrderService.php
@@ -12,27 +12,27 @@ use Log;
class OrderService
{
- public static User $user;
+ private User $user;
- public static ?Goods $goods;
+ private ?Goods $goods;
- public static ?Payment $payment;
+ private ?Payment $payment;
public function __construct(private readonly Order $order)
{ // 获取需要的信息
- self::$user = $order->user;
- self::$goods = $order->goods;
- self::$payment = $order->payment;
+ $this->user = $order->user;
+ $this->goods = $order->goods;
+ $this->payment = $order->payment;
}
public function receivedPayment(): bool
{ // 支付成功后处理
- $payment = self::$payment;
+ $payment = $this->payment;
if ($payment && $payment->status !== 1) {// 是否为余额购买套餐
$payment->complete();
}
- $goods = self::$goods;
+ $goods = $this->goods;
if ($goods === null) {
$ret = $this->chargeCredit();
} else {
@@ -41,12 +41,12 @@ class OrderService
$ret = $this->activatePackage();
break;
case 2: // 套餐
- if (Order::userActivePlan(self::$user->id)->where('id', '<>', $this->order->id)->exists()) {// 判断套餐是否直接激活
+ if (Order::userActivePlan($this->user->id)->where('id', '<>', $this->order->id)->exists()) {// 判断套餐是否直接激活
$ret = $this->order->prepay();
} else {
$ret = $this->activatePlan();
}
- $this->setCommissionExpense(self::$user); // 返利
+ $this->setCommissionExpense($this->user); // 返利
break;
default:
Log::emergency('【处理订单】出现错误-未知套餐类型');
@@ -58,11 +58,11 @@ class OrderService
private function chargeCredit(): bool
{ // 余额充值
- $credit = self::$user->credit;
- $ret = self::$user->updateCredit($this->order->origin_amount);
+ $credit = $this->user->credit;
+ $ret = $this->user->updateCredit($this->order->origin_amount);
// 余额变动记录日志
if ($ret) {
- Helpers::addUserCreditLog($this->order->user_id, $this->order->id, $credit, self::$user->credit, $this->order->amount, 'The user topped up the balance.');
+ Helpers::addUserCreditLog($this->order->user_id, $this->order->id, $credit, $this->user->credit, $this->order->amount, 'The user topped up the balance.');
}
return $ret;
@@ -70,8 +70,8 @@ class OrderService
private function activatePackage(): bool
{ // 激活流量包
- if (self::$user->incrementData(self::$goods->traffic * MiB)) {
- return Helpers::addUserTrafficModifyLog($this->order->user_id, self::$user->transfer_enable - self::$goods->traffic * MiB, self::$user->transfer_enable, trans("[:payment] plus the user's purchased data plan.", ['payment' => $this->order->pay_way]));
+ if ($this->user->incrementData($this->goods->traffic * MiB)) {
+ return Helpers::addUserTrafficModifyLog($this->order->user_id, $this->user->transfer_enable - $this->goods->traffic * MiB, $this->user->transfer_enable, trans("[:payment] plus the user's purchased data plan.", ['payment' => $this->order->pay_way]));
}
return false;
@@ -79,23 +79,23 @@ class OrderService
public function activatePlan(): bool
{ // 激活套餐
- $this->order->refresh()->update(['expired_at' => date('Y-m-d H:i:s', strtotime(self::$goods->days.' days'))]);
- $oldData = self::$user->transfer_enable;
+ $this->order->refresh()->updateQuietly(['expired_at' => date('Y-m-d H:i:s', strtotime($this->goods->days.' days')), 'status' => 2]);
+ $oldData = $this->user->transfer_enable;
$updateData = [
- 'invite_num' => self::$user->invite_num + (self::$goods->invite_num ?: 0),
- 'level' => self::$goods->level,
- 'speed_limit' => self::$goods->speed_limit,
+ 'invite_num' => $this->user->invite_num + ($this->goods->invite_num ?: 0),
+ 'level' => $this->goods->level,
+ 'speed_limit' => $this->goods->speed_limit,
'enable' => 1,
...$this->resetTimeAndData(),
];
// 无端口用户 添加端口
- if (empty(self::$user->port)) {
+ if (empty($this->user->port)) {
$updateData['port'] = Helpers::getPort();
}
- if (self::$user->update($updateData)) {
- return Helpers::addUserTrafficModifyLog($this->order->user_id, $oldData, self::$user->transfer_enable, trans("[:payment] plus the user's purchased data plan.", ['payment' => $this->order->pay_way]), $this->order->id);
+ if ($this->user->update($updateData)) {
+ return Helpers::addUserTrafficModifyLog($this->order->user_id, $oldData, $this->user->transfer_enable, trans("[:payment] plus the user's purchased data plan.", ['payment' => $this->order->pay_way]), $this->order->id);
}
return false;
@@ -108,7 +108,7 @@ class OrderService
}
// 账号流量重置日期
- $nextResetTime = now()->addDays(self::$goods->period)->toDateString();
+ $nextResetTime = now()->addDays($this->goods->period)->toDateString();
if ($nextResetTime >= $expired_at) {
$nextResetTime = null;
}
@@ -116,7 +116,7 @@ class OrderService
return [
'u' => 0,
'd' => 0,
- 'transfer_enable' => self::$goods->traffic * MiB,
+ 'transfer_enable' => $this->goods->traffic * MiB,
'expired_at' => $expired_at,
'reset_time' => $nextResetTime,
];
@@ -124,7 +124,7 @@ class OrderService
private function getFinallyExpiredTime(): string
{ // 推算最新的到期时间
- $orders = self::$user->orders()->whereIn('status', [2, 3])->whereIsExpire(0)->isPlan()->get();
+ $orders = $this->user->orders()->whereIn('status', [2, 3])->whereIsExpire(0)->isPlan()->get();
$current = $orders->where('status', '==', 2)->first();
return ($current->expired_at ?? now())->addDays($orders->except($current->id ?? 0)->sum('goods.days'))->toDateString();
@@ -160,7 +160,7 @@ class OrderService
{ // 刷新账号有效时间
$data = ['expired_at' => $this->getFinallyExpiredTime()];
- if ($data['expired_at'] < now()->toDateString()) {
+ if ($data['expired_at'] <= now()->toDateString()) {
$data += [
'u' => 0,
'd' => 0,
@@ -172,13 +172,6 @@ class OrderService
];
}
- return self::$user->update($data);
- }
-
- public function activatePrepaidPlan(): bool
- { // 激活预支付套餐
- $this->order->complete();
-
- return $this->activatePlan();
+ return $this->user->update($data);
}
}
diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php
index ea8bd01e..668bc552 100644
--- a/app/Utils/Helpers.php
+++ b/app/Utils/Helpers.php
@@ -34,12 +34,12 @@ class Helpers
* @param string $username 用户名
* @param string $password 用户密码
* @param int $transfer_enable 可用流量
- * @param int $date 可使用天数
+ * @param int $days 可使用天数
* @param int|null $inviter_id 邀请人
* @param string|null $nickname 昵称
* @param int $status 状态:-1-禁用、0-未激活、1-正常
*/
- public static function addUser(string $username, string $password, int $transfer_enable = 0, int $date = 0, ?int $inviter_id = null, ?string $nickname = null, int $status = 0): User
+ public static function addUser(string $username, string $password, int $transfer_enable = 0, int $days = 7, ?int $inviter_id = null, ?string $nickname = null, int $status = 0): User
{
return User::create([
'nickname' => $nickname ?? $username,
@@ -52,7 +52,7 @@ class Helpers
'protocol' => self::getDefaultProtocol(),
'obfs' => self::getDefaultObfs(),
'transfer_enable' => $transfer_enable,
- 'expired_at' => now()->addDays($date)->toDateString(),
+ 'expired_at' => now()->addDays($days)->toDateString(),
'user_group_id' => null,
'reg_ip' => IP::getClientIp(),
'inviter_id' => $inviter_id,
diff --git a/composer.json b/composer.json
index 1ddb384b..82faaeb2 100644
--- a/composer.json
+++ b/composer.json
@@ -79,7 +79,9 @@
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
- "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
+ "@php artisan vendor:publish --tag=laravel-assets --ansi --force",
+ "if [ -f vendor/bin/ide-helper ]; then @php artisan ide-helper:generate; fi",
+ "if [ -f vendor/bin/ide-helper ]; then @php artisan ide-helper:meta; fi"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
diff --git a/resources/views/user/invoices.blade.php b/resources/views/user/invoices.blade.php
index f2c26ad7..a25cd368 100644
--- a/resources/views/user/invoices.blade.php
+++ b/resources/views/user/invoices.blade.php
@@ -79,16 +79,18 @@