280 lines
8.5 KiB
PHP
280 lines
8.5 KiB
PHP
<?php
|
|
/*
|
|
* @Author: YwxApp <ywx@ywxapp.cn>
|
|
* @Date: 2026-07-17 17:02:05
|
|
* @LastEditors: YwxApp <ywx@ywxapp.cn>
|
|
* @LastEditTime: 2026-08-03 13:33:07
|
|
* @Description:
|
|
* @FilePath: \ywxapp_dev\app\backend\controller\AddonMonitor.php
|
|
* @CustomString: Copyright (c) 2026 YwxApp
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
namespace app\backend\controller;
|
|
|
|
use think\facade\View;
|
|
use ywxapp\controller\BackendBase;
|
|
use ywxapp\service\AddonPerformanceMonitor;
|
|
use ywxapp\service\AddonHotReload;
|
|
/**
|
|
* AddonMonitor 类
|
|
*
|
|
* @author ywxapp <admin@ywxapp.cn>
|
|
*/
|
|
class AddonMonitor extends BackendBase
|
|
{
|
|
protected $noNeedLogin = [];
|
|
protected $noNeedVerify = [];
|
|
|
|
|
|
public function index()
|
|
{
|
|
if ($this->request->isAjax()) {
|
|
$action = $this->request->param('action', 'overview');
|
|
|
|
switch ($action) {
|
|
case 'overview':
|
|
return $this->getOverview();
|
|
case 'performance':
|
|
return $this->getPerformance();
|
|
case 'health':
|
|
return $this->getHealth();
|
|
case 'reload':
|
|
return $this->getReloadStats();
|
|
case 'reload_addon':
|
|
return $this->reloadAddon();
|
|
case 'clear_performance':
|
|
return $this->clearPerformanceData();
|
|
default:
|
|
return $this->result->error('未知操作');
|
|
}
|
|
}
|
|
|
|
return View::fetch('addon_monitor/index');
|
|
}
|
|
|
|
/**
|
|
* 获取概览数据
|
|
*/
|
|
private function getOverview()
|
|
{
|
|
$overview = AddonPerformanceMonitor::getAllPerformanceOverview();
|
|
$problematic = AddonPerformanceMonitor::getProblematicaddon();
|
|
$reloadStats = AddonHotReload::getReloadStatistics();
|
|
|
|
// 统计数据
|
|
$totaladdon = count($overview);
|
|
$healthyaddon = 0;
|
|
$problematicaddon = count($problematic);
|
|
$totalCalls = 0;
|
|
$avgExecutionTime = 0;
|
|
$avgSuccessRate = 0;
|
|
|
|
foreach ($overview as $stats) {
|
|
if ($stats['success_rate'] > 95 && $stats['avg_execution_time'] < 1.0) {
|
|
$healthyaddon++;
|
|
}
|
|
$totalCalls += $stats['total_calls'];
|
|
$avgExecutionTime += $stats['avg_execution_time'];
|
|
$avgSuccessRate += $stats['success_rate'];
|
|
}
|
|
|
|
if ($totaladdon > 0) {
|
|
$avgExecutionTime = $avgExecutionTime / $totaladdon;
|
|
$avgSuccessRate = $avgSuccessRate / $totaladdon;
|
|
}
|
|
|
|
return $this->result->success([
|
|
'total_addon' => $totaladdon,
|
|
'healthy_addon' => $healthyaddon,
|
|
'problematic_addon' => $problematicaddon,
|
|
'total_calls' => $totalCalls,
|
|
'avg_execution_time' => round($avgExecutionTime * 1000, 2),
|
|
'avg_success_rate' => round($avgSuccessRate, 2),
|
|
'reload_stats' => $reloadStats
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 获取性能详情
|
|
*/
|
|
private function getPerformance()
|
|
{
|
|
$addon = $this->request->param('addon', '');
|
|
|
|
if (empty($addon)) {
|
|
// 返回所有插件性能数据
|
|
$overview = AddonPerformanceMonitor::getAllPerformanceOverview();
|
|
return $this->result->success($overview);
|
|
} else {
|
|
// 返回指定插件性能数据
|
|
$stats = AddonPerformanceMonitor::getPerformanceStats($addon);
|
|
return $this->result->success($stats);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取健康状态
|
|
*/
|
|
private function getHealth()
|
|
{
|
|
$problematic = AddonPerformanceMonitor::getProblematicaddon();
|
|
$overview = AddonPerformanceMonitor::getAllPerformanceOverview();
|
|
|
|
// 生成健康报告
|
|
$healthReport = [];
|
|
foreach ($overview as $addon => $stats) {
|
|
$isHealthy = true;
|
|
$issues = [];
|
|
|
|
if ($stats['avg_execution_time'] > AddonPerformanceMonitor::PERFORMANCE_THRESHOLD) {
|
|
$isHealthy = false;
|
|
$issues[] = '执行时间过长';
|
|
}
|
|
|
|
if ($stats['avg_memory_usage'] > AddonPerformanceMonitor::MEMORY_THRESHOLD) {
|
|
$isHealthy = false;
|
|
$issues[] = '内存使用过多';
|
|
}
|
|
|
|
if ($stats['success_rate'] < 90) {
|
|
$isHealthy = false;
|
|
$issues[] = '成功率过低';
|
|
}
|
|
|
|
$healthReport[$addon] = [
|
|
'healthy' => $isHealthy,
|
|
'issues' => $issues,
|
|
'stats' => $stats
|
|
];
|
|
}
|
|
|
|
return $this->result->success([
|
|
'health_report' => $healthReport,
|
|
'problematic_addon' => $problematic
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 获取热重载统计
|
|
*/
|
|
private function getReloadStats()
|
|
{
|
|
$stats = AddonHotReload::getReloadStatistics();
|
|
return $this->result->success($stats);
|
|
}
|
|
|
|
/**
|
|
* 重载插件
|
|
*/
|
|
private function reloadAddon()
|
|
{
|
|
$addon = $this->request->param('addon', '');
|
|
$force = $this->request->param('force', false);
|
|
|
|
if (empty($addon)) {
|
|
return $this->result->error('请指定插件名称');
|
|
}
|
|
|
|
if (!config('app.app_debug')) {
|
|
return $this->result->error('热重载功能仅在开发环境可用');
|
|
}
|
|
|
|
try {
|
|
$reloaded = AddonHotReload::reloadAddon($addon, $force);
|
|
|
|
if ($reloaded) {
|
|
$status = AddonHotReload::getReloadStatus($addon);
|
|
return $this->result->success([
|
|
'reloaded' => true,
|
|
'status' => $status
|
|
], '插件重载成功');
|
|
} else {
|
|
return $this->result->success([
|
|
'reloaded' => false
|
|
], '插件无文件变更,无需重载');
|
|
}
|
|
} catch (\Exception $e) {
|
|
return $this->result->error('插件重载失败:' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除性能数据
|
|
*/
|
|
private function clearPerformanceData()
|
|
{
|
|
$addon = $this->request->param('addon', '');
|
|
|
|
try {
|
|
if (empty($addon)) {
|
|
AddonPerformanceMonitor::clearPerformanceData();
|
|
return $this->result->success([], '已清除所有插件性能数据');
|
|
} else {
|
|
AddonPerformanceMonitor::clearPerformanceData($addon);
|
|
return $this->result->success([], "已清除插件 {$addon} 的性能数据");
|
|
}
|
|
} catch (\Exception $e) {
|
|
return $this->result->error('清除性能数据失败:' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 性能图表数据
|
|
*/
|
|
public function chart()
|
|
{
|
|
$type = $this->request->param('type', 'execution_time');
|
|
$period = $this->request->param('period', 'day'); // day, week, month
|
|
|
|
$overview = AddonPerformanceMonitor::getAllPerformanceOverview();
|
|
$chartData = [];
|
|
|
|
foreach ($overview as $addon => $stats) {
|
|
if ($stats['total_calls'] === 0) {
|
|
continue;
|
|
}
|
|
|
|
switch ($type) {
|
|
case 'execution_time':
|
|
$chartData[] = [
|
|
'name' => $addon,
|
|
'value' => round($stats['avg_execution_time'] * 1000, 2)
|
|
];
|
|
break;
|
|
case 'memory_usage':
|
|
$chartData[] = [
|
|
'name' => $addon,
|
|
'value' => round($stats['avg_memory_usage'] / 1024, 2)
|
|
];
|
|
break;
|
|
case 'success_rate':
|
|
$chartData[] = [
|
|
'name' => $addon,
|
|
'value' => round($stats['success_rate'], 2)
|
|
];
|
|
break;
|
|
case 'call_count':
|
|
$chartData[] = [
|
|
'name' => $addon,
|
|
'value' => $stats['total_calls']
|
|
];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 按值排序
|
|
usort($chartData, function($a, $b) {
|
|
return $b['value'] - $a['value'];
|
|
});
|
|
|
|
// 只返回前10个
|
|
$chartData = array_slice($chartData, 0, 10);
|
|
|
|
return $this->result->success([
|
|
'chart_data' => $chartData,
|
|
'type' => $type,
|
|
'period' => $period
|
|
]);
|
|
}
|
|
} |