处理jc流量显示的问题
This commit is contained in:
@@ -5,22 +5,55 @@ namespace App\Http\Controllers\V1\User;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Resources\TrafficLogResource;
|
use App\Http\Resources\TrafficLogResource;
|
||||||
use App\Models\StatUser;
|
use App\Models\StatUser;
|
||||||
use App\Services\StatisticalService;
|
use App\Services\UserOnlineService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class StatController extends Controller
|
class StatController extends Controller
|
||||||
{
|
{
|
||||||
public function getTrafficLog(Request $request)
|
public function getTrafficLog(Request $request)
|
||||||
{
|
{
|
||||||
$startDate = now()->startOfMonth()->timestamp;
|
$startDate = now()->startOfMonth()->timestamp;
|
||||||
|
$userId = (int) $request->user()->id;
|
||||||
|
|
||||||
$records = StatUser::query()
|
$records = StatUser::query()
|
||||||
->where('user_id', $request->user()->id)
|
->with(['server:id,name'])
|
||||||
|
->where('user_id', $userId)
|
||||||
->where('record_at', '>=', $startDate)
|
->where('record_at', '>=', $startDate)
|
||||||
->orderBy('record_at', 'DESC')
|
->orderBy('record_at', 'DESC')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
$deviceMap = $this->buildNodeDeviceMap($userId);
|
||||||
|
$records->each(function (StatUser $record) use ($deviceMap): void {
|
||||||
|
$serverType = strtolower((string) $record->server_type);
|
||||||
|
$serverId = (int) $record->server_id;
|
||||||
|
$nodeKey = $serverType !== '' && $serverId > 0 ? "{$serverType}{$serverId}" : null;
|
||||||
|
$deviceIps = $nodeKey ? ($deviceMap[$nodeKey] ?? []) : [];
|
||||||
|
|
||||||
|
$record->setAttribute('server_name', $record->server?->name);
|
||||||
|
$record->setAttribute('node_name', $record->server?->name);
|
||||||
|
$record->setAttribute('device_ips', $deviceIps);
|
||||||
|
$record->setAttribute('device_count', count($deviceIps));
|
||||||
|
$record->setAttribute('device_name', $deviceIps[0] ?? 'Unknown');
|
||||||
|
});
|
||||||
|
|
||||||
$data = TrafficLogResource::collection(collect($records));
|
$data = TrafficLogResource::collection(collect($records));
|
||||||
return $this->success($data);
|
return $this->success($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildNodeDeviceMap(int $userId): array
|
||||||
|
{
|
||||||
|
$devices = UserOnlineService::getUserDevices($userId);
|
||||||
|
$deviceList = data_get($devices, 'devices', []);
|
||||||
|
|
||||||
|
return collect($deviceList)
|
||||||
|
->filter(fn($item): bool => is_array($item) && !empty($item['ip']) && !empty($item['node_type']))
|
||||||
|
->groupBy(fn(array $item): string => strtolower((string) $item['node_type']))
|
||||||
|
->map(fn($items): array => collect($items)
|
||||||
|
->pluck('ip')
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all())
|
||||||
|
->all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers\V2\Admin;
|
namespace App\Http\Controllers\V2\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Resources\TrafficLogResource;
|
||||||
use App\Models\CommissionLog;
|
use App\Models\CommissionLog;
|
||||||
use App\Models\Order;
|
use App\Models\Order;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@@ -12,6 +13,7 @@ use App\Models\StatUser;
|
|||||||
use App\Models\Ticket;
|
use App\Models\Ticket;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\StatisticalService;
|
use App\Services\StatisticalService;
|
||||||
|
use App\Services\UserOnlineService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class StatController extends Controller
|
class StatController extends Controller
|
||||||
@@ -234,17 +236,54 @@ class StatController extends Controller
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$pageSize = $request->input('pageSize', 10);
|
$pageSize = $request->input('pageSize', 10);
|
||||||
$records = StatUser::orderBy('record_at', 'DESC')
|
$userId = (int) $request->input('user_id');
|
||||||
->where('user_id', $request->input('user_id'))
|
$records = StatUser::query()
|
||||||
|
->with(['server:id,name'])
|
||||||
|
->orderBy('record_at', 'DESC')
|
||||||
|
->where('user_id', $userId)
|
||||||
->paginate($pageSize);
|
->paginate($pageSize);
|
||||||
|
|
||||||
$data = $records->items();
|
$deviceMap = $this->buildNodeDeviceMap($userId);
|
||||||
|
$data = collect($records->items())
|
||||||
|
->map(function (StatUser $record) use ($deviceMap, $request): array {
|
||||||
|
$serverType = strtolower((string) $record->server_type);
|
||||||
|
$serverId = (int) $record->server_id;
|
||||||
|
$nodeKey = $serverType !== '' && $serverId > 0 ? "{$serverType}{$serverId}" : null;
|
||||||
|
$deviceIps = $nodeKey ? ($deviceMap[$nodeKey] ?? []) : [];
|
||||||
|
|
||||||
|
$record->setAttribute('server_name', $record->server?->name);
|
||||||
|
$record->setAttribute('node_name', $record->server?->name);
|
||||||
|
$record->setAttribute('device_ips', $deviceIps);
|
||||||
|
$record->setAttribute('device_count', count($deviceIps));
|
||||||
|
$record->setAttribute('device_name', $deviceIps[0] ?? 'Unknown');
|
||||||
|
|
||||||
|
return (new TrafficLogResource($record))->toArray($request);
|
||||||
|
})
|
||||||
|
->all();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'data' => $data,
|
'data' => $data,
|
||||||
'total' => $records->total(),
|
'total' => $records->total(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildNodeDeviceMap(int $userId): array
|
||||||
|
{
|
||||||
|
$devices = UserOnlineService::getUserDevices($userId);
|
||||||
|
$deviceList = data_get($devices, 'devices', []);
|
||||||
|
|
||||||
|
return collect($deviceList)
|
||||||
|
->filter(fn($item): bool => is_array($item) && !empty($item['ip']) && !empty($item['node_type']))
|
||||||
|
->groupBy(fn(array $item): string => strtolower((string) $item['node_type']))
|
||||||
|
->map(fn($items): array => collect($items)
|
||||||
|
->pluck('ip')
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all())
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
public function getStatRecord(Request $request)
|
public function getStatRecord(Request $request)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -14,13 +14,49 @@ class TrafficLogResource extends JsonResource
|
|||||||
*/
|
*/
|
||||||
public function toArray(Request $request): array
|
public function toArray(Request $request): array
|
||||||
{
|
{
|
||||||
|
$serverId = (int) data_get($this->resource, 'server_id', 0);
|
||||||
|
$serverType = strtolower((string) data_get($this->resource, 'server_type', ''));
|
||||||
|
$serverName = data_get($this->resource, 'server_name')
|
||||||
|
?: data_get($this->resource, 'node_name')
|
||||||
|
?: data_get($this->resource, 'server.name');
|
||||||
|
|
||||||
|
if (!$serverName && $serverId > 0) {
|
||||||
|
$serverName = "Node #{$serverId}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$deviceIps = data_get($this->resource, 'device_ips', []);
|
||||||
|
if (!is_array($deviceIps)) {
|
||||||
|
$deviceIps = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$deviceName = data_get($this->resource, 'device_name');
|
||||||
|
if (!$deviceName) {
|
||||||
|
$deviceName = $deviceIps[0] ?? 'Unknown';
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
"d" => $this['d'],
|
'id' => data_get($this->resource, 'id'),
|
||||||
"u" => $this['u'],
|
'd' => (int) data_get($this->resource, 'd', 0),
|
||||||
"record_at" => $this['record_at'],
|
'u' => (int) data_get($this->resource, 'u', 0),
|
||||||
"server_rate" => $this['server_rate'],
|
'record_at' => (int) data_get($this->resource, 'record_at', 0),
|
||||||
|
'record_type' => data_get($this->resource, 'record_type'),
|
||||||
|
'server_rate' => (float) data_get($this->resource, 'server_rate', 1),
|
||||||
|
'server_id' => $serverId > 0 ? $serverId : null,
|
||||||
|
'server_type' => $serverType !== '' ? $serverType : null,
|
||||||
|
'server_name' => $serverName,
|
||||||
|
'node_name' => $serverName,
|
||||||
|
'node_key' => $serverId > 0 && $serverType !== '' ? "{$serverType}{$serverId}" : null,
|
||||||
|
'device_name' => $deviceName,
|
||||||
|
'device_ips' => $deviceIps,
|
||||||
|
'device_count' => (int) data_get($this->resource, 'device_count', count($deviceIps)),
|
||||||
|
'created_at' => data_get($this->resource, 'created_at'),
|
||||||
|
'updated_at' => data_get($this->resource, 'updated_at'),
|
||||||
];
|
];
|
||||||
if(!config('hidden_features.enable_exposed_user_count_fix')) $data['user_id']= $this['user_id'];
|
|
||||||
|
if (!config('hidden_features.enable_exposed_user_count_fix')) {
|
||||||
|
$data['user_id'] = (int) data_get($this->resource, 'user_id', 0);
|
||||||
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,10 +75,15 @@ class StatUserJob implements ShouldQueue
|
|||||||
|
|
||||||
protected function processUserStatForSqlite(int $uid, array $v, int $recordAt): void
|
protected function processUserStatForSqlite(int $uid, array $v, int $recordAt): void
|
||||||
{
|
{
|
||||||
DB::transaction(function () use ($uid, $v, $recordAt) {
|
$serverId = $this->getServerId();
|
||||||
|
$serverType = $this->getServerType();
|
||||||
|
|
||||||
|
DB::transaction(function () use ($uid, $v, $recordAt, $serverId, $serverType) {
|
||||||
$existingRecord = StatUser::where([
|
$existingRecord = StatUser::where([
|
||||||
'user_id' => $uid,
|
'user_id' => $uid,
|
||||||
'server_rate' => $this->server['rate'],
|
'server_rate' => $this->server['rate'],
|
||||||
|
'server_id' => $serverId,
|
||||||
|
'server_type' => $serverType,
|
||||||
'record_at' => $recordAt,
|
'record_at' => $recordAt,
|
||||||
'record_type' => $this->recordType,
|
'record_type' => $this->recordType,
|
||||||
])->first();
|
])->first();
|
||||||
@@ -93,6 +98,8 @@ class StatUserJob implements ShouldQueue
|
|||||||
StatUser::create([
|
StatUser::create([
|
||||||
'user_id' => $uid,
|
'user_id' => $uid,
|
||||||
'server_rate' => $this->server['rate'],
|
'server_rate' => $this->server['rate'],
|
||||||
|
'server_id' => $serverId,
|
||||||
|
'server_type' => $serverType,
|
||||||
'record_at' => $recordAt,
|
'record_at' => $recordAt,
|
||||||
'record_type' => $this->recordType,
|
'record_type' => $this->recordType,
|
||||||
'u' => ($v[0] * $this->server['rate']),
|
'u' => ($v[0] * $this->server['rate']),
|
||||||
@@ -106,10 +113,15 @@ class StatUserJob implements ShouldQueue
|
|||||||
|
|
||||||
protected function processUserStatForOtherDatabases(int $uid, array $v, int $recordAt): void
|
protected function processUserStatForOtherDatabases(int $uid, array $v, int $recordAt): void
|
||||||
{
|
{
|
||||||
|
$serverId = $this->getServerId();
|
||||||
|
$serverType = $this->getServerType();
|
||||||
|
|
||||||
StatUser::upsert(
|
StatUser::upsert(
|
||||||
[
|
[
|
||||||
'user_id' => $uid,
|
'user_id' => $uid,
|
||||||
'server_rate' => $this->server['rate'],
|
'server_rate' => $this->server['rate'],
|
||||||
|
'server_id' => $serverId,
|
||||||
|
'server_type' => $serverType,
|
||||||
'record_at' => $recordAt,
|
'record_at' => $recordAt,
|
||||||
'record_type' => $this->recordType,
|
'record_type' => $this->recordType,
|
||||||
'u' => ($v[0] * $this->server['rate']),
|
'u' => ($v[0] * $this->server['rate']),
|
||||||
@@ -117,7 +129,7 @@ class StatUserJob implements ShouldQueue
|
|||||||
'created_at' => time(),
|
'created_at' => time(),
|
||||||
'updated_at' => time(),
|
'updated_at' => time(),
|
||||||
],
|
],
|
||||||
['user_id', 'server_rate', 'record_at', 'record_type'],
|
['user_id', 'server_rate', 'server_id', 'server_type', 'record_at'],
|
||||||
[
|
[
|
||||||
'u' => DB::raw("u + VALUES(u)"),
|
'u' => DB::raw("u + VALUES(u)"),
|
||||||
'd' => DB::raw("d + VALUES(d)"),
|
'd' => DB::raw("d + VALUES(d)"),
|
||||||
@@ -135,10 +147,12 @@ class StatUserJob implements ShouldQueue
|
|||||||
$now = time();
|
$now = time();
|
||||||
$u = ($v[0] * $this->server['rate']);
|
$u = ($v[0] * $this->server['rate']);
|
||||||
$d = ($v[1] * $this->server['rate']);
|
$d = ($v[1] * $this->server['rate']);
|
||||||
|
$serverId = $this->getServerId();
|
||||||
|
$serverType = $this->getServerType();
|
||||||
|
|
||||||
$sql = "INSERT INTO {$table} (user_id, server_rate, record_at, record_type, u, d, created_at, updated_at)
|
$sql = "INSERT INTO {$table} (user_id, server_rate, server_id, server_type, record_at, record_type, u, d, created_at, updated_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
ON CONFLICT (user_id, server_rate, record_at)
|
ON CONFLICT (user_id, server_rate, server_id, server_type, record_at)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
u = {$table}.u + EXCLUDED.u,
|
u = {$table}.u + EXCLUDED.u,
|
||||||
d = {$table}.d + EXCLUDED.d,
|
d = {$table}.d + EXCLUDED.d,
|
||||||
@@ -147,6 +161,8 @@ class StatUserJob implements ShouldQueue
|
|||||||
DB::statement($sql, [
|
DB::statement($sql, [
|
||||||
$uid,
|
$uid,
|
||||||
$this->server['rate'],
|
$this->server['rate'],
|
||||||
|
$serverId,
|
||||||
|
$serverType,
|
||||||
$recordAt,
|
$recordAt,
|
||||||
$this->recordType,
|
$this->recordType,
|
||||||
$u,
|
$u,
|
||||||
@@ -155,4 +171,15 @@ class StatUserJob implements ShouldQueue
|
|||||||
$now,
|
$now,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private function getServerId(): int
|
||||||
|
{
|
||||||
|
return (int) ($this->server['id'] ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getServerType(): string
|
||||||
|
{
|
||||||
|
$serverType = $this->server['type'] ?? $this->protocol ?? '';
|
||||||
|
return strtolower((string) $serverType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App\Models\StatUser
|
* App\Models\StatUser
|
||||||
@@ -22,7 +23,25 @@ class StatUser extends Model
|
|||||||
protected $dateFormat = 'U';
|
protected $dateFormat = 'U';
|
||||||
protected $guarded = ['id'];
|
protected $guarded = ['id'];
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
|
'server_id' => 'integer',
|
||||||
'created_at' => 'timestamp',
|
'created_at' => 'timestamp',
|
||||||
'updated_at' => 'timestamp'
|
'updated_at' => 'timestamp'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function server(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Server::class, 'server_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNodeKeyAttribute(): ?string
|
||||||
|
{
|
||||||
|
$serverType = strtolower((string) $this->server_type);
|
||||||
|
$serverId = (int) $this->server_id;
|
||||||
|
|
||||||
|
if ($serverType === '' || $serverId <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $serverType . $serverId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration {
|
||||||
|
private const TABLE = 'v2_stat_user';
|
||||||
|
private const OLD_UNIQUE_INDEX = 'server_rate_user_id_record_at';
|
||||||
|
private const OLD_COMPOSITE_INDEX = 'v2_stat_user_user_id_server_rate_record_at_index';
|
||||||
|
private const NEW_UNIQUE_INDEX = 'stat_user_user_rate_server_record_at_unique';
|
||||||
|
private const NEW_COMPOSITE_INDEX = 'stat_user_user_server_record_at_index';
|
||||||
|
private const NEW_SERVER_ID_INDEX = 'stat_user_server_id_index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table(self::TABLE, function (Blueprint $table) {
|
||||||
|
if (!Schema::hasColumn(self::TABLE, 'server_id')) {
|
||||||
|
$table->unsignedInteger('server_id')->default(0)->comment('Node ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Schema::hasColumn(self::TABLE, 'server_type')) {
|
||||||
|
$table->string('server_type', 32)->default('')->comment('Node type');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table(self::TABLE, function (Blueprint $table) {
|
||||||
|
try {
|
||||||
|
$table->dropUnique(self::OLD_UNIQUE_INDEX);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$table->dropIndex(self::OLD_COMPOSITE_INDEX);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$table->dropIndex(['user_id', 'server_rate', 'record_at']);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table(self::TABLE, function (Blueprint $table) {
|
||||||
|
try {
|
||||||
|
$table->unique(
|
||||||
|
['user_id', 'server_rate', 'server_id', 'server_type', 'record_at'],
|
||||||
|
self::NEW_UNIQUE_INDEX
|
||||||
|
);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$table->index(
|
||||||
|
['user_id', 'server_id', 'server_type', 'record_at'],
|
||||||
|
self::NEW_COMPOSITE_INDEX
|
||||||
|
);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$table->index('server_id', self::NEW_SERVER_ID_INDEX);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table(self::TABLE, function (Blueprint $table) {
|
||||||
|
try {
|
||||||
|
$table->dropUnique(self::NEW_UNIQUE_INDEX);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$table->dropIndex(self::NEW_COMPOSITE_INDEX);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$table->dropIndex(self::NEW_SERVER_ID_INDEX);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table(self::TABLE, function (Blueprint $table) {
|
||||||
|
try {
|
||||||
|
$table->unique(['server_rate', 'user_id', 'record_at'], self::OLD_UNIQUE_INDEX);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$table->index(['user_id', 'server_rate', 'record_at']);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table(self::TABLE, function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn(self::TABLE, 'server_type')) {
|
||||||
|
$table->dropColumn('server_type');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn(self::TABLE, 'server_id')) {
|
||||||
|
$table->dropColumn('server_id');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user