NAC_Blockchain/nac-quantum-browser/app/controller/Index.php

263 lines
10 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace app\controller;
use app\service\ExplorerApi;
use think\facade\View;
class Index
{
/**
* 首页:网络统计 + 最新区块列表
*/
public function index()
{
$rawStats = ExplorerApi::getStats();
$rawBlocks = ExplorerApi::getBlocks(20, 1);
// 预处理统计数据(模板只接收字符串/数字,不调用静态方法)
$stats = $this->processStats($rawStats);
// 预处理区块列表
$blocks = array_map([$this, 'processBlock'], $rawBlocks['blocks'] ?? []);
return View::fetch('index/index', [
'stats' => $stats,
'blocks' => $blocks,
]);
}
/**
* 区块列表页(分页)
*/
public function blocks()
{
$page = max(1, (int) request()->param('page', 1));
$limit = 25;
$data = ExplorerApi::getBlocks($limit, $page);
$blocks = array_map([$this, 'processBlock'], $data['blocks'] ?? []);
$total = (int) ($data['total'] ?? 0);
return View::fetch('index/blocks', [
'blocks' => $blocks,
'total' => $total,
'page' => $page,
'limit' => $limit,
'totalPages' => max(1, (int) ceil($total / $limit)),
]);
}
/**
* 区块详情页
*/
public function block()
{
$number = (int) request()->param('n', 0);
if ($number <= 0) {
return View::fetch('index/error', ['message' => '无效的区块号']);
}
$raw = ExplorerApi::getBlock($number);
if (!$raw) {
return View::fetch('index/block', [
'block' => null,
'blockNum' => $number,
'transactions' => [],
'txCount' => 0,
]);
}
$block = $this->processBlock($raw);
$block['prevNumber'] = max(0, $number - 1);
$block['nextNumber'] = $number + 1;
// 预处理交易列表
$transactions = [];
foreach ($raw['transactions'] ?? [] as $tx) {
$transactions[] = [
'shortHash' => ExplorerApi::shortHash($tx['hash'] ?? '', 10, 8),
'shortFrom' => ExplorerApi::shortHash($tx['from'] ?? '', 8, 6),
'shortTo' => ExplorerApi::shortHash($tx['to'] ?? '', 8, 6),
'value' => $tx['value'] ?? 0,
'type' => $tx['type'] ?? 'transfer',
'status' => $tx['status'] ?? 'success',
];
}
return View::fetch('index/block', [
'block' => $block,
'blockNum' => $number,
'transactions' => $transactions,
'txCount' => count($transactions),
]);
}
/**
* 搜索
*/
public function search()
{
$q = trim((string) request()->param('q', ''));
$type = '';
$result = null;
if ($q !== '') {
$raw = ExplorerApi::search($q);
// API 返回格式: {"type":"block","data":{...}} 或 {"data":{"type":"block","block":{...}}}
// 兼容两种格式
$type = $raw['type'] ?? ($raw['data']['type'] ?? '');
if ($type === 'block') {
// 新格式data 直接是区块对象
$b = $raw['data'] ?? [];
// 兼容旧格式data.block
if (empty($b['number']) && !empty($raw['data']['block'])) {
$b = $raw['data']['block'];
}
if (!empty($b['number'])) {
$result = [
'number' => $b['number'] ?? 0,
'hash' => $b['hash'] ?? 'N/A',
'timeAgo' => ExplorerApi::timeAgo($b['timestamp'] ?? 0),
];
}
} elseif ($type === 'transaction') {
$tx = $raw['data']['tx'] ?? $raw['data'] ?? [];
if (!empty($tx['hash'])) {
$result = [
'hash' => $tx['hash'] ?? 'N/A',
'blockNumber' => $tx['blockNumber'] ?? 0,
'status' => $tx['status'] ?? 'success',
];
}
} elseif ($type === 'address') {
$addr = $raw['data']['address'] ?? $raw['data'] ?? [];
if (!empty($addr['address'])) {
$result = [
'address' => $addr['address'] ?? $q,
'balance' => $addr['balance'] ?? 0,
'txCount' => $addr['txCount'] ?? 0,
];
}
}
}
return View::fetch('index/search', [
'q' => htmlspecialchars($q),
'type' => $type,
'result' => $result,
]);
}
/**
* 节点状态页
*/
public function nodes()
{
$rawStats = ExplorerApi::getStats();
$health = ExplorerApi::health();
$stats = $this->processStats($rawStats);
$healthJson = json_encode($health, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
return View::fetch('index/nodes', [
'stats' => $stats,
'healthJson' => $healthJson,
]);
}
// ─────────────────────────────────────────────
// 私有辅助方法:数据预处理
// ─────────────────────────────────────────────
/**
* 预处理统计数据,生成模板直接可用的字段
*/
private function processStats(array $raw): array
{
$cbpp = $raw['cbppConsensus'] ?? 'unknown';
$csnp = $raw['csnpNetwork'] ?? 'unknown';
$constitution = (bool) ($raw['constitutionLayer'] ?? false);
$fluid = (bool) ($raw['fluidBlockMode'] ?? false);
return [
'currentBlock' => $raw['currentBlock'] ?? 0,
'cbppConsensus' => $cbpp,
'cbppBadge' => $this->statusBadge($cbpp),
'csnpNetwork' => $csnp,
'csnpBadge' => $this->statusBadge($csnp),
'constitutionLayer' => $constitution,
'constitutionText' => $constitution ? '已激活' : '未激活',
'constitutionBadge' => $constitution ? 'bg-success' : 'bg-secondary',
'fluidBlockMode' => $fluid,
'fluidText' => $fluid ? '已启用' : '未启用',
'fluidBadge' => $fluid ? 'bg-success' : 'bg-secondary',
'chainId' => $raw['chainId'] ?? 20260131,
'network' => $raw['network'] ?? 'mainnet',
'nodeCount' => $raw['nodeCount'] ?? 0,
'totalTransactions' => $raw['totalTransactions'] ?? 0,
'tps' => $raw['tps'] ?? 0,
'avgBlockTime' => $raw['avgBlockTime'] ?? '3.0',
];
}
/**
* 预处理单个区块数据
*/
private function processBlock(array $block): array
{
$constitution = (bool) ($block['constitutionLayer'] ?? false);
$isHeartbeat = (bool) ($block['isHeartbeat'] ?? false);
$blockType = $block['blockType'] ?? ($isHeartbeat ? 'heartbeat' : 'tx');
$blockTypeLabel = $block['blockTypeLabel'] ?? ($isHeartbeat ? '心跳块' : '交易块');
$blockTypeNote = $block['blockTypeNote'] ?? ($isHeartbeat
? 'CBPP 宪法原则四无交易时每60秒产生心跳块证明网络存活'
: 'CBPP 交易驱动块:包含真实链上交易');
return [
'number' => $block['number'] ?? $block['height'] ?? 0,
'hash' => $block['hash'] ?? 'N/A',
'shortHash' => ExplorerApi::shortHash($block['hash'] ?? '', 16, 12),
'parentHash' => $block['parentHash'] ?? $block['parent_hash'] ?? 'N/A',
'timestamp' => $block['timestamp'] ?? 0,
'formatTime' => ExplorerApi::formatTime($block['timestamp'] ?? 0),
'timeAgo' => ExplorerApi::timeAgo($block['timestamp'] ?? 0),
'validator' => $block['validator'] ?? $block['producer'] ?? 'N/A',
'shortValidator' => ExplorerApi::shortHash($block['validator'] ?? $block['producer'] ?? '', 8, 6),
'txCount' => $block['txCount'] ?? $block['tx_count'] ?? 0,
'size' => $block['size'] ?? 0,
'cbppRound' => $block['cbppRound'] ?? $block['cbpp_round'] ?? '—',
'epoch' => $block['epoch'] ?? 0,
'stateRoot' => $block['stateRoot'] ?? $block['state_root'] ?? 'N/A',
'txRoot' => $block['txRoot'] ?? $block['tx_root'] ?? 'N/A',
'constitutionLayer'=> $constitution,
'constitutionText' => $constitution ? '已激活' : '未激活',
'constitutionBadge'=> $constitution ? 'bg-success' : 'bg-secondary',
'isHeartbeat' => $isHeartbeat,
'blockType' => $blockType,
'blockTypeLabel' => $blockTypeLabel,
'blockTypeNote' => $blockTypeNote,
'blockTypeBadge' => $isHeartbeat ? 'warning text-dark' : 'info text-dark',
'confirmations' => $block['confirmations'] ?? 0,
'dataSource' => $block['dataSource'] ?? 'CBPP-Node-9545',
'transactions' => $block['transactions'] ?? [],
];
}
/**
* 状态徽章 CSS 类
*/
private function statusBadge(string $status): string
{
return match (strtolower($status)) {
'active', 'connected', 'running', 'online' => 'bg-success',
'syncing', 'pending', 'starting' => 'bg-warning text-dark',
'error', 'failed', 'offline', 'stopped' => 'bg-danger',
default => 'bg-secondary',
};
}
}