mirror of
https://github.com/lkddi/Xboard.git
synced 2026-04-03 10:30:51 +08:00
Compare commits
2 Commits
048530a893
...
94fc5f6942
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94fc5f6942 | ||
|
|
c5a8c836c0 |
@@ -14,29 +14,13 @@ class StatController extends Controller
|
||||
public function getTrafficLog(Request $request)
|
||||
{
|
||||
$startDate = now()->startOfMonth()->timestamp;
|
||||
|
||||
// Aggregate per-node data into per-day entries for backward compatibility
|
||||
$records = StatUser::query()
|
||||
->select([
|
||||
'user_id',
|
||||
'server_rate',
|
||||
'record_at',
|
||||
'record_type',
|
||||
DB::raw('SUM(u) as u'),
|
||||
DB::raw('SUM(d) as d'),
|
||||
])
|
||||
->where('user_id', $request->user()->id)
|
||||
->where('record_at', '>=', $startDate)
|
||||
->groupBy(['user_id', 'server_rate', 'record_at', 'record_type'])
|
||||
->orderBy('record_at', 'DESC')
|
||||
->get()
|
||||
->map(function ($item) {
|
||||
$item->u = (int) $item->u;
|
||||
$item->d = (int) $item->d;
|
||||
return $item;
|
||||
});
|
||||
->get();
|
||||
|
||||
$data = TrafficLogResource::collection($records);
|
||||
$data = TrafficLogResource::collection(collect($records));
|
||||
return $this->success($data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use App\Services\StatisticalService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class StatController extends Controller
|
||||
{
|
||||
@@ -235,38 +234,14 @@ class StatController extends Controller
|
||||
]);
|
||||
|
||||
$pageSize = $request->input('pageSize', 10);
|
||||
$page = $request->input('page', 1);
|
||||
|
||||
// Aggregate per-node data into per-day entries for backward compatibility
|
||||
$query = StatUser::query()
|
||||
->select([
|
||||
'user_id',
|
||||
'server_rate',
|
||||
'record_at',
|
||||
'record_type',
|
||||
DB::raw('SUM(u) as u'),
|
||||
DB::raw('SUM(d) as d'),
|
||||
DB::raw('MAX(created_at) as created_at'),
|
||||
DB::raw('MAX(updated_at) as updated_at'),
|
||||
])
|
||||
$records = StatUser::orderBy('record_at', 'DESC')
|
||||
->where('user_id', $request->input('user_id'))
|
||||
->groupBy(['user_id', 'server_rate', 'record_at', 'record_type'])
|
||||
->orderBy('record_at', 'DESC');
|
||||
|
||||
// Manual pagination for grouped query
|
||||
$total = (clone $query)->get()->count();
|
||||
$data = $query->skip(($page - 1) * $pageSize)->take($pageSize)->get()
|
||||
->map(function ($item) {
|
||||
$item->u = (int) $item->u;
|
||||
$item->d = (int) $item->d;
|
||||
$item->created_at = (int) $item->created_at;
|
||||
$item->updated_at = (int) $item->updated_at;
|
||||
return $item;
|
||||
});
|
||||
->paginate($pageSize);
|
||||
|
||||
$data = $records->items();
|
||||
return [
|
||||
'data' => $data,
|
||||
'total' => $total,
|
||||
'total' => $records->total(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ class StatUserJob implements ShouldQueue
|
||||
DB::transaction(function () use ($uid, $v, $recordAt) {
|
||||
$existingRecord = StatUser::where([
|
||||
'user_id' => $uid,
|
||||
'server_id' => $this->server['id'],
|
||||
'server_rate' => $this->server['rate'],
|
||||
'record_at' => $recordAt,
|
||||
'record_type' => $this->recordType,
|
||||
@@ -93,7 +92,6 @@ class StatUserJob implements ShouldQueue
|
||||
} else {
|
||||
StatUser::create([
|
||||
'user_id' => $uid,
|
||||
'server_id' => $this->server['id'],
|
||||
'server_rate' => $this->server['rate'],
|
||||
'record_at' => $recordAt,
|
||||
'record_type' => $this->recordType,
|
||||
@@ -111,7 +109,6 @@ class StatUserJob implements ShouldQueue
|
||||
StatUser::upsert(
|
||||
[
|
||||
'user_id' => $uid,
|
||||
'server_id' => $this->server['id'],
|
||||
'server_rate' => $this->server['rate'],
|
||||
'record_at' => $recordAt,
|
||||
'record_type' => $this->recordType,
|
||||
@@ -120,7 +117,7 @@ class StatUserJob implements ShouldQueue
|
||||
'created_at' => time(),
|
||||
'updated_at' => time(),
|
||||
],
|
||||
['user_id', 'server_id', 'server_rate', 'record_at', 'record_type'],
|
||||
['user_id', 'server_rate', 'record_at', 'record_type'],
|
||||
[
|
||||
'u' => DB::raw("u + VALUES(u)"),
|
||||
'd' => DB::raw("d + VALUES(d)"),
|
||||
@@ -139,9 +136,9 @@ class StatUserJob implements ShouldQueue
|
||||
$u = intval($v[0] * $this->server['rate']);
|
||||
$d = intval($v[1] * $this->server['rate']);
|
||||
|
||||
$sql = "INSERT INTO {$table} (user_id, server_id, server_rate, record_at, record_type, u, d, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (user_id, server_id, server_rate, record_at)
|
||||
$sql = "INSERT INTO {$table} (user_id, server_rate, record_at, record_type, u, d, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (user_id, server_rate, record_at)
|
||||
DO UPDATE SET
|
||||
u = {$table}.u + EXCLUDED.u,
|
||||
d = {$table}.d + EXCLUDED.d,
|
||||
@@ -149,7 +146,6 @@ class StatUserJob implements ShouldQueue
|
||||
|
||||
DB::statement($sql, [
|
||||
$uid,
|
||||
$this->server['id'],
|
||||
$this->server['rate'],
|
||||
$recordAt,
|
||||
$this->recordType,
|
||||
|
||||
@@ -9,7 +9,6 @@ use Illuminate\Database\Eloquent\Model;
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $user_id 用户ID
|
||||
* @property int|null $server_id 节点ID (nullable for legacy data)
|
||||
* @property int $u 上行流量
|
||||
* @property int $d 下行流量
|
||||
* @property int $record_at 记录时间
|
||||
@@ -26,12 +25,4 @@ class StatUser extends Model
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the server that this traffic stat belongs to
|
||||
*/
|
||||
public function server()
|
||||
{
|
||||
return $this->belongsTo(Server::class, 'server_id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('v2_stat_user', function (Blueprint $table) {
|
||||
// Add server_id column as nullable for backward compatibility
|
||||
$table->integer('server_id')->nullable()->after('user_id')->comment('节点ID (nullable for legacy data)');
|
||||
|
||||
// Add index for per-node queries
|
||||
if (config('database.default') !== 'sqlite') {
|
||||
$table->index(['user_id', 'server_id', 'record_at'], 'user_server_record_idx');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('v2_stat_user', function (Blueprint $table) {
|
||||
if (config('database.default') !== 'sqlite') {
|
||||
$table->dropIndex('user_server_record_idx');
|
||||
}
|
||||
$table->dropColumn('server_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* Updates the unique constraint on v2_stat_user to include server_id,
|
||||
* allowing per-node user traffic tracking.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (config('database.default') === 'sqlite') {
|
||||
// SQLite uses explicit WHERE queries in code, no constraint changes needed
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('v2_stat_user', function (Blueprint $table) {
|
||||
// Drop the old unique constraint that doesn't include server_id
|
||||
$table->dropUnique('server_rate_user_id_record_at');
|
||||
|
||||
// Add new unique constraint including server_id
|
||||
// Note: NULL server_id values (legacy) are treated as distinct in MySQL
|
||||
$table->unique(
|
||||
['user_id', 'server_id', 'server_rate', 'record_at', 'record_type'],
|
||||
'stat_user_unique_idx'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
if (config('database.default') === 'sqlite') {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('v2_stat_user', function (Blueprint $table) {
|
||||
// Drop new constraint
|
||||
$table->dropUnique('stat_user_unique_idx');
|
||||
|
||||
// Restore original constraint
|
||||
$table->unique(
|
||||
['server_rate', 'user_id', 'record_at'],
|
||||
'server_rate_user_id_record_at'
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user