ThirdPartyMonitorController.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <?php
  2. namespace App\Module\ThirdParty\AdminControllers;
  3. use UCore\DcatAdmin\AdminController;
  4. use App\Module\ThirdParty\Models\ThirdPartyMonitor;
  5. use App\Module\ThirdParty\Models\ThirdPartyService;
  6. use App\Module\ThirdParty\Repositorys\ThirdPartyMonitorRepository;
  7. use Dcat\Admin\Grid;
  8. use Dcat\Admin\Show;
  9. use Spatie\RouteAttributes\Attributes\Resource;
  10. /**
  11. * 第三方服务监控记录管理控制器
  12. *
  13. * 路由: /admin/thirdparty/monitors
  14. */
  15. #[Resource('thirdparty/monitors', names: 'dcat.admin.thirdparty.monitors')]
  16. class ThirdPartyMonitorController extends AdminController
  17. {
  18. /**
  19. * 页面标题
  20. *
  21. * @var string
  22. */
  23. protected $title = '监控记录管理';
  24. /**
  25. * 数据仓库
  26. *
  27. * @return string
  28. */
  29. protected function repository()
  30. {
  31. return ThirdPartyMonitorRepository::class;
  32. }
  33. /**
  34. * 列表页面
  35. *
  36. * @return Grid
  37. */
  38. protected function grid(): Grid
  39. {
  40. return Grid::make(new ThirdPartyMonitorRepository(), function (Grid $grid) {
  41. // 基础设置
  42. $grid->column('id', 'ID')->sortable();
  43. // 关联服务
  44. $grid->column('service.name', '服务名称')->sortable();
  45. $grid->column('service.code', '服务代码');
  46. // 检查类型
  47. $grid->column('check_type', '检查类型')->display(function ($type) {
  48. $types = [
  49. 'health' => ['label' => '健康检查', 'color' => 'primary', 'icon' => 'fa-heartbeat'],
  50. 'performance' => ['label' => '性能检查', 'color' => 'info', 'icon' => 'fa-tachometer-alt'],
  51. 'availability' => ['label' => '可用性检查', 'color' => 'success', 'icon' => 'fa-check-circle'],
  52. 'status_change' => ['label' => '状态变更', 'color' => 'warning', 'icon' => 'fa-exchange-alt'],
  53. ];
  54. $config = $types[$type] ?? ['label' => $type, 'color' => 'secondary', 'icon' => 'fa-question'];
  55. return "<span class='badge badge-{$config['color']}'><i class='{$config['icon']}'></i> {$config['label']}</span>";
  56. });
  57. // 检查状态
  58. $grid->column('status', '检查状态')->display(function ($status) {
  59. $colors = [
  60. 'success' => 'success',
  61. 'warning' => 'warning',
  62. 'error' => 'danger',
  63. 'timeout' => 'secondary',
  64. ];
  65. $icons = [
  66. 'success' => 'fa-check',
  67. 'warning' => 'fa-exclamation-triangle',
  68. 'error' => 'fa-times',
  69. 'timeout' => 'fa-clock',
  70. ];
  71. $color = $colors[$status] ?? 'secondary';
  72. $icon = $icons[$status] ?? 'fa-question';
  73. return "<span class='badge badge-{$color}'><i class='{$icon}'></i> " . ucfirst($status) . "</span>";
  74. });
  75. // 响应时间
  76. $grid->column('response_time', '响应时间')->display(function ($time) {
  77. if (!$time) {
  78. return '-';
  79. }
  80. $color = 'success';
  81. if ($time > 5000) {
  82. $color = 'danger';
  83. } elseif ($time > 2000) {
  84. $color = 'warning';
  85. } elseif ($time > 1000) {
  86. $color = 'info';
  87. }
  88. return "<span class='badge badge-{$color}'>{$time}ms</span>";
  89. })->sortable();
  90. // HTTP状态码
  91. $grid->column('status_code', 'HTTP状态码')->display(function ($code) {
  92. if (!$code) {
  93. return '-';
  94. }
  95. $color = 'secondary';
  96. if ($code >= 200 && $code < 300) {
  97. $color = 'success';
  98. } elseif ($code >= 300 && $code < 400) {
  99. $color = 'info';
  100. } elseif ($code >= 400 && $code < 500) {
  101. $color = 'warning';
  102. } elseif ($code >= 500) {
  103. $color = 'danger';
  104. }
  105. return "<span class='badge badge-{$color}'>{$code}</span>";
  106. });
  107. // 错误信息(截断显示)
  108. $grid->column('error_message', '错误信息')->display(function ($error) {
  109. if (!$error) {
  110. return '-';
  111. }
  112. $shortError = strlen($error) > 30 ? substr($error, 0, 30) . '...' : $error;
  113. return "<span class='text-danger' title='{$error}'>{$shortError}</span>";
  114. });
  115. // 检查时间
  116. $grid->column('checked_at', '检查时间')->sortable();
  117. // 过滤器
  118. $grid->filter(function (Grid\Filter $filter) {
  119. $filter->equal('service_id', '服务')->select(
  120. ThirdPartyService::pluck('name', 'id')->toArray()
  121. );
  122. $filter->equal('check_type', '检查类型')->select([
  123. 'health' => '健康检查',
  124. 'performance' => '性能检查',
  125. 'availability' => '可用性检查',
  126. 'status_change' => '状态变更',
  127. ]);
  128. $filter->equal('status', '检查状态')->select([
  129. 'success' => '成功',
  130. 'warning' => '警告',
  131. 'error' => '错误',
  132. 'timeout' => '超时',
  133. ]);
  134. $filter->between('response_time', '响应时间(ms)');
  135. $filter->between('status_code', 'HTTP状态码');
  136. $filter->like('error_message', '错误信息');
  137. $filter->between('checked_at', '检查时间')->datetime();
  138. });
  139. // 批量操作
  140. $grid->batchActions(function (Grid\Tools\BatchActions $batch) {
  141. // TODO: 创建批量操作类
  142. // $batch->add('批量删除', new \App\Module\ThirdParty\AdminActions\BatchDeleteMonitorAction());
  143. });
  144. // 工具栏
  145. $grid->tools(function (Grid\Tools $tools) {
  146. $tools->append('<a href="/admin/thirdparty/monitors/health-check" class="btn btn-sm btn-success"><i class="fa fa-heartbeat"></i> 执行健康检查</a>');
  147. $tools->append('<a href="/admin/thirdparty/monitors/stats" class="btn btn-sm btn-info"><i class="fa fa-chart-bar"></i> 监控统计</a>');
  148. $tools->append('<a href="/admin/thirdparty/monitors/cleanup" class="btn btn-sm btn-warning"><i class="fa fa-trash"></i> 清理记录</a>');
  149. });
  150. // 行操作
  151. $grid->actions(function (Grid\Displayers\Actions $actions) {
  152. // 移除编辑按钮,监控记录只读
  153. $actions->disableEdit();
  154. if ($actions->row->status === 'error') {
  155. $actions->append('<a href="/admin/thirdparty/monitors/' . $actions->getKey() . '/recheck" class="btn btn-xs btn-warning"><i class="fa fa-redo"></i> 重新检查</a>');
  156. }
  157. });
  158. // 默认排序(最新的在前)
  159. $grid->model()->orderBy('checked_at', 'desc');
  160. // 禁用创建按钮
  161. $grid->disableCreateButton();
  162. });
  163. }
  164. /**
  165. * 详情页面
  166. *
  167. * @return Show
  168. */
  169. protected function detail($id): Show
  170. {
  171. return Show::make($id, new ThirdPartyMonitorRepository(), function (Show $show) {
  172. $show->field('id', 'ID');
  173. $show->field('service.name', '服务名称');
  174. $show->field('service.code', '服务代码');
  175. $show->field('check_type', '检查类型')->as(function ($type) {
  176. $types = [
  177. 'health' => '健康检查',
  178. 'performance' => '性能检查',
  179. 'availability' => '可用性检查',
  180. 'status_change' => '状态变更',
  181. ];
  182. return $types[$type] ?? $type;
  183. });
  184. $show->field('status', '检查状态')->as(function ($status) {
  185. return ucfirst($status);
  186. });
  187. $show->field('response_time', '响应时间')->as(function ($time) {
  188. return $time ? $time . ' ms' : '-';
  189. });
  190. $show->field('status_code', 'HTTP状态码');
  191. $show->field('error_message', '错误信息');
  192. $show->field('details', '详细信息')->json();
  193. $show->field('checked_at', '检查时间');
  194. // 相关监控记录
  195. $show->relation('recentChecks', '最近检查记录', function ($model) {
  196. $grid = new Grid(new ThirdPartyMonitor());
  197. $grid->model()->where('service_id', $model->service_id)
  198. ->where('check_type', $model->check_type)
  199. ->where('id', '!=', $model->id)
  200. ->orderBy('checked_at', 'desc')
  201. ->limit(10);
  202. $grid->column('status', '状态')->display(function ($status) {
  203. $colors = [
  204. 'success' => 'success',
  205. 'warning' => 'warning',
  206. 'error' => 'danger',
  207. 'timeout' => 'secondary',
  208. ];
  209. $color = $colors[$status] ?? 'secondary';
  210. return "<span class='badge badge-{$color}'>" . ucfirst($status) . "</span>";
  211. });
  212. $grid->column('response_time', '响应时间(ms)');
  213. $grid->column('status_code', 'HTTP状态码');
  214. $grid->column('checked_at', '检查时间');
  215. $grid->disableCreateButton();
  216. $grid->disableActions();
  217. $grid->disableFilter();
  218. $grid->disableBatchActions();
  219. return $grid;
  220. });
  221. // 服务健康趋势
  222. $show->relation('healthTrend', '健康趋势', function ($model) {
  223. $grid = new Grid(new ThirdPartyMonitor());
  224. $grid->model()->where('service_id', $model->service_id)
  225. ->where('check_type', 'health')
  226. ->orderBy('checked_at', 'desc')
  227. ->limit(20);
  228. $grid->column('status', '状态')->display(function ($status) {
  229. $colors = [
  230. 'success' => 'success',
  231. 'warning' => 'warning',
  232. 'error' => 'danger',
  233. 'timeout' => 'secondary',
  234. ];
  235. $color = $colors[$status] ?? 'secondary';
  236. return "<span class='badge badge-{$color}'>" . ucfirst($status) . "</span>";
  237. });
  238. $grid->column('response_time', '响应时间(ms)');
  239. $grid->column('checked_at', '检查时间');
  240. $grid->disableCreateButton();
  241. $grid->disableActions();
  242. $grid->disableFilter();
  243. $grid->disableBatchActions();
  244. return $grid;
  245. });
  246. });
  247. }
  248. }