HealthCheckCommand.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <?php
  2. namespace App\Module\ThirdParty\Commands;
  3. use Illuminate\Console\Command;
  4. use App\Module\ThirdParty\Services\MonitorService;
  5. use App\Module\ThirdParty\Models\ThirdPartyService;
  6. /**
  7. * 第三方服务健康检查命令
  8. */
  9. class HealthCheckCommand extends Command
  10. {
  11. /**
  12. * 命令签名
  13. *
  14. * @var string
  15. */
  16. protected $signature = 'thirdparty:health-check
  17. {--service= : 指定服务ID或代码}
  18. {--all : 检查所有服务}
  19. {--active-only : 只检查激活的服务}
  20. {--verbose : 显示详细信息}';
  21. /**
  22. * 命令描述
  23. *
  24. * @var string
  25. */
  26. protected $description = '执行第三方服务健康检查';
  27. /**
  28. * 执行命令
  29. *
  30. * @return int
  31. */
  32. public function handle(): int
  33. {
  34. $this->info('开始执行第三方服务健康检查...');
  35. try {
  36. $serviceId = $this->getServiceId();
  37. $results = MonitorService::performHealthCheck($serviceId);
  38. $this->displayResults($results);
  39. $this->displaySummary($results);
  40. return Command::SUCCESS;
  41. } catch (\Exception $e) {
  42. $this->error("健康检查失败: {$e->getMessage()}");
  43. return Command::FAILURE;
  44. }
  45. }
  46. /**
  47. * 获取要检查的服务ID
  48. *
  49. * @return int|null
  50. */
  51. protected function getServiceId(): ?int
  52. {
  53. $serviceOption = $this->option('service');
  54. if (!$serviceOption) {
  55. return null;
  56. }
  57. // 如果是数字,直接返回
  58. if (is_numeric($serviceOption)) {
  59. $service = ThirdPartyService::find($serviceOption);
  60. if (!$service) {
  61. throw new \Exception("服务ID {$serviceOption} 不存在");
  62. }
  63. return (int)$serviceOption;
  64. }
  65. // 如果是代码,查找对应的服务
  66. $service = ThirdPartyService::where('code', $serviceOption)->first();
  67. if (!$service) {
  68. throw new \Exception("服务代码 {$serviceOption} 不存在");
  69. }
  70. return $service->id;
  71. }
  72. /**
  73. * 显示检查结果
  74. *
  75. * @param array $results
  76. * @return void
  77. */
  78. protected function displayResults(array $results): void
  79. {
  80. if (empty($results)) {
  81. $this->warn('没有找到需要检查的服务');
  82. return;
  83. }
  84. $this->info("\n检查结果:");
  85. $this->line(str_repeat('-', 80));
  86. foreach ($results as $result) {
  87. $this->displaySingleResult($result);
  88. }
  89. }
  90. /**
  91. * 显示单个服务的检查结果
  92. *
  93. * @param array $result
  94. * @return void
  95. */
  96. protected function displaySingleResult(array $result): void
  97. {
  98. $status = $result['status'];
  99. $serviceName = $result['service_name'];
  100. $serviceCode = $result['service_code'];
  101. // 根据状态选择颜色
  102. $color = match ($status) {
  103. 'success' => 'green',
  104. 'warning' => 'yellow',
  105. 'error', 'timeout' => 'red',
  106. default => 'white',
  107. };
  108. $this->line("<fg={$color}>[{$status}]</fg> {$serviceName} ({$serviceCode})");
  109. if ($this->option('verbose')) {
  110. if ($result['response_time']) {
  111. $this->line(" 响应时间: {$result['response_time']}ms");
  112. }
  113. if ($result['status_code']) {
  114. $this->line(" HTTP状态码: {$result['status_code']}");
  115. }
  116. if ($result['error_message']) {
  117. $this->line(" 错误信息: {$result['error_message']}");
  118. }
  119. if (!empty($result['details'])) {
  120. $this->line(" 详细信息:");
  121. foreach ($result['details'] as $key => $value) {
  122. if (is_array($value)) {
  123. $value = json_encode($value, JSON_UNESCAPED_UNICODE);
  124. }
  125. $this->line(" {$key}: {$value}");
  126. }
  127. }
  128. } else {
  129. if ($result['error_message']) {
  130. $this->line(" {$result['error_message']}");
  131. }
  132. }
  133. $this->line('');
  134. }
  135. /**
  136. * 显示检查摘要
  137. *
  138. * @param array $results
  139. * @return void
  140. */
  141. protected function displaySummary(array $results): void
  142. {
  143. if (empty($results)) {
  144. return;
  145. }
  146. $total = count($results);
  147. $successful = count(array_filter($results, fn($r) => $r['status'] === 'success'));
  148. $warnings = count(array_filter($results, fn($r) => $r['status'] === 'warning'));
  149. $errors = count(array_filter($results, fn($r) => in_array($r['status'], ['error', 'timeout'])));
  150. $this->line(str_repeat('-', 80));
  151. $this->info('检查摘要:');
  152. $this->line("总计: {$total}");
  153. $this->line("<fg=green>成功: {$successful}</fg>");
  154. if ($warnings > 0) {
  155. $this->line("<fg=yellow>警告: {$warnings}</fg>");
  156. }
  157. if ($errors > 0) {
  158. $this->line("<fg=red>错误: {$errors}</fg>");
  159. }
  160. $successRate = $total > 0 ? round(($successful / $total) * 100, 2) : 0;
  161. $this->line("成功率: {$successRate}%");
  162. // 如果有失败的服务,给出建议
  163. if ($errors > 0) {
  164. $this->line('');
  165. $this->warn('建议:');
  166. $this->line('- 检查失败服务的配置和凭证');
  167. $this->line('- 确认第三方服务是否正常运行');
  168. $this->line('- 查看详细日志了解具体错误原因');
  169. }
  170. }
  171. /**
  172. * 获取要检查的服务列表
  173. *
  174. * @return \Illuminate\Database\Eloquent\Collection
  175. */
  176. protected function getServicesToCheck()
  177. {
  178. $query = ThirdPartyService::query();
  179. if ($this->option('active-only')) {
  180. $query->where('status', 'ACTIVE');
  181. }
  182. if ($this->option('all')) {
  183. return $query->get();
  184. }
  185. // 默认只检查有健康检查URL的激活服务
  186. return $query->where('status', 'ACTIVE')
  187. ->whereNotNull('health_check_url')
  188. ->get();
  189. }
  190. }