Files
Xboard/app/Http/Controllers/V1/User/StatController.php
T
yinjianm 4f84034814 • 你这个现象我已经定位并修了,核心原因是节点匹配和设备映射都有歧义。
已修改:

  - 节点识别优先用 id,只有找不到才回退 code,避免子节点被匹配成父节点
    ServerService.php (/E:/code/php/Xboard-new/app/Services/ServerService.php#L101)
  - 在线设备解析补全 node_key/node_id,并按真实节点键归类
    UserOnlineService.php (/E:/code/php/Xboard-new/app/Services/UserOnlineService.php#L43)
  - 用户端流量日志设备映射改为按 node_key 精确匹配
    StatController.php (/E:/code/php/Xboard-new/app/Http/Controllers/V1/User/StatController.php#L43)
  - 管理端同样改为按 node_key 映射
    StatController.php (/E:/code/php/Xboard-new/app/Http/Controllers/V2/Admin/StatController.php#L270)

  说明:

  - 这次不涉及数据库结构变更,不需要 migrate。
  - 对管理后台现有统计接口兼容,不会破坏原有 u/d/record_at/server_rate 显示。
  - 旧的历史日志如果之前已经按父节点入库,不会自动改名;新流量会按修复后的逻辑记录。
2026-02-26 05:13:19 +08:00

74 lines
2.8 KiB
PHP

<?php
namespace App\Http\Controllers\V1\User;
use App\Http\Controllers\Controller;
use App\Http\Resources\TrafficLogResource;
use App\Models\StatUser;
use App\Services\UserOnlineService;
use Illuminate\Http\Request;
class StatController extends Controller
{
public function getTrafficLog(Request $request)
{
$startDate = now()->startOfMonth()->timestamp;
$userId = (int) $request->user()->id;
$records = StatUser::query()
->with(['server:id,name'])
->where('user_id', $userId)
->where('record_at', '>=', $startDate)
->orderBy('record_at', 'DESC')
->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));
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']))
->map(function (array $item): array {
$nodeKey = strtolower((string) ($item['node_key'] ?? ''));
if ($nodeKey === '') {
$nodeType = strtolower((string) ($item['node_type'] ?? ''));
$nodeId = (int) ($item['node_id'] ?? 0);
$nodeKey = ($nodeType !== '' && $nodeId > 0) ? "{$nodeType}{$nodeId}" : $nodeType;
}
return [
'node_key' => $nodeKey,
'ip' => (string) ($item['ip'] ?? ''),
];
})
->filter(fn(array $item): bool => $item['node_key'] !== '' && $item['ip'] !== '')
->groupBy(fn(array $item): string => $item['node_key'])
->map(fn($items): array => collect($items)
->pluck('ip')
->filter()
->unique()
->values()
->all())
->all();
}
}