ApiCatalog.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <?php
  2. namespace App\Filament\Pages;
  3. use Filament\Pages\Page;
  4. use Illuminate\Support\Facades\Route;
  5. use UnitEnum;
  6. use BackedEnum;
  7. use App\Services\ApiDocumentation;
  8. class ApiCatalog extends Page
  9. {
  10. protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-document-text';
  11. protected static ?string $navigationLabel = 'API 文档';
  12. protected static UnitEnum|string|null $navigationGroup = 'API 管理';
  13. protected static ?int $navigationSort = 1;
  14. protected string $view = 'filament.pages.api-catalog';
  15. public array $apiGroups = [];
  16. public function mount(): void
  17. {
  18. $this->apiGroups = $this->buildFromRoutes();
  19. }
  20. private function buildFromRoutes(): array
  21. {
  22. $routes = Route::getRoutes();
  23. $groups = [];
  24. $docs = ApiDocumentation::all();
  25. foreach ($routes as $route) {
  26. $uri = $route->uri();
  27. if (!str_starts_with($uri, 'api/')) {
  28. continue;
  29. }
  30. $methods = array_values(array_filter($route->methods(), fn ($method) => $method !== 'HEAD'));
  31. $path = '/' . $uri;
  32. $groupName = $this->resolveGroupName($uri);
  33. $tag = $this->resolveTag($route, $uri);
  34. $apiDoc = $docs[$uri] ?? null;
  35. foreach ($methods as $method) {
  36. $methodDoc = $apiDoc[$method] ?? null;
  37. // 获取参数信息
  38. $params = $this->buildParamHint($uri, [$method]);
  39. // 获取响应示例
  40. $responseExamples = [];
  41. if ($methodDoc) {
  42. if (isset($methodDoc['response'])) {
  43. $responseExamples = $methodDoc['response'];
  44. }
  45. }
  46. // 获取详细信息
  47. $details = [
  48. 'route_name' => $route->getName(),
  49. 'action' => $route->getActionName(),
  50. 'summary' => $methodDoc['summary'] ?? '',
  51. 'description' => $methodDoc['description'] ?? '',
  52. 'param_details' => $methodDoc['params'] ?? [],
  53. 'response_examples' => $responseExamples,
  54. 'examples' => $methodDoc['examples'] ?? [],
  55. ];
  56. $groups[$groupName]['name'] = $groupName;
  57. $groups[$groupName]['items'][] = [
  58. 'method' => $method,
  59. 'path' => $path,
  60. 'uri' => $uri,
  61. 'params' => $params,
  62. 'tag' => $tag,
  63. 'doc' => $methodDoc,
  64. 'details' => $details,
  65. ];
  66. }
  67. }
  68. ksort($groups);
  69. return array_values($groups);
  70. }
  71. private function resolveGroupName(string $uri): string
  72. {
  73. $map = [
  74. 'api/questions' => '题库核心 API',
  75. 'api/papers' => '题库核心 API',
  76. 'api/knowledge-mastery' => '知识点掌握 API',
  77. 'api/mistake-book' => '错题本 API',
  78. 'api/analytics' => '错题本 API',
  79. 'api/intelligent-exams' => '智能出卷与学情报告',
  80. 'api/exam-analysis' => '智能出卷与学情报告',
  81. 'api/textbooks' => '教材 API',
  82. 'api/mathrecsys' => 'MathRecSys 集成 API',
  83. 'api/knowledge' => '知识点与能力 API',
  84. 'api/knowledge-points' => '知识点与能力 API',
  85. ];
  86. foreach ($map as $prefix => $name) {
  87. if (str_starts_with($uri, $prefix)) {
  88. return $name;
  89. }
  90. }
  91. if (str_starts_with($uri, 'api/ocr') || str_contains($uri, 'callback')) {
  92. return '回调与内部 API';
  93. }
  94. if (str_contains($uri, 'test')) {
  95. return '测试 API';
  96. }
  97. return '其他 API';
  98. }
  99. private function resolveTag(\Illuminate\Routing\Route $route, string $uri): ?string
  100. {
  101. $middleware = (array) $route->middleware();
  102. if (in_array('internal.token', $middleware, true)) {
  103. return 'internal';
  104. }
  105. if (str_contains($uri, 'test')) {
  106. return 'test';
  107. }
  108. return null;
  109. }
  110. private function buildParamHint(string $uri, array $methods): string
  111. {
  112. preg_match_all('/\{([^}]+)\}/', $uri, $matches);
  113. $pathParams = $matches[1] ?? [];
  114. $parts = [];
  115. if (!empty($pathParams)) {
  116. $parts[] = 'path: ' . implode(', ', $pathParams);
  117. }
  118. $hasBody = array_intersect($methods, ['POST', 'PUT', 'PATCH']);
  119. if ($hasBody) {
  120. $parts[] = 'body: payload';
  121. }
  122. if (empty($parts)) {
  123. return 'query/body: -';
  124. }
  125. return implode(' | ', $parts);
  126. }
  127. /**
  128. * 获取 HTTP 方法颜色样式
  129. */
  130. public function getMethodColor(string $method): string
  131. {
  132. return ApiDocumentation::getMethodColor($method);
  133. }
  134. }