ApiCatalog.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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. class ApiCatalog extends Page
  8. {
  9. protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-list-bullet';
  10. protected static ?string $navigationLabel = 'API 列表';
  11. protected static UnitEnum|string|null $navigationGroup = 'API 管理';
  12. protected static ?int $navigationSort = 1;
  13. protected string $view = 'filament.pages.api-catalog';
  14. public array $apiGroups = [];
  15. public function mount(): void
  16. {
  17. $this->apiGroups = $this->buildFromRoutes();
  18. }
  19. private function buildFromRoutes(): array
  20. {
  21. $routes = Route::getRoutes();
  22. $groups = [];
  23. $detailsMap = $this->buildDetailsMap();
  24. foreach ($routes as $route) {
  25. $uri = $route->uri();
  26. if (!str_starts_with($uri, 'api/')) {
  27. continue;
  28. }
  29. $methods = array_values(array_filter($route->methods(), fn ($method) => $method !== 'HEAD'));
  30. $path = '/' . $uri;
  31. $groupName = $this->resolveGroupName($uri);
  32. $tag = $this->resolveTag($route, $uri);
  33. $params = $this->buildParamHint($uri, $methods);
  34. $response = 'JSON';
  35. $details = $detailsMap[$uri] ?? [];
  36. $details['route_name'] = $route->getName();
  37. $details['action'] = $route->getActionName();
  38. foreach ($methods as $method) {
  39. $groups[$groupName]['name'] = $groupName;
  40. $groups[$groupName]['items'][] = [
  41. 'method' => $method,
  42. 'path' => $path,
  43. 'params' => $params,
  44. 'response' => $response,
  45. 'tag' => $tag,
  46. 'details' => $details,
  47. ];
  48. }
  49. }
  50. ksort($groups);
  51. return array_values($groups);
  52. }
  53. private function buildDetailsMap(): array
  54. {
  55. return [
  56. 'api/papers/{paperId}/json' => [
  57. 'description' => '返回与智能出卷返回值中 exam_content 完全一致的试卷 JSON。',
  58. 'examples' => [
  59. 'GET /api/papers/paper_1765788931_ce02f6a3/json',
  60. 'GET /api/papers/paper_1765788931_ce02f6a3/json?download=1',
  61. ],
  62. ],
  63. ];
  64. }
  65. private function resolveGroupName(string $uri): string
  66. {
  67. $map = [
  68. 'api/questions' => '题库核心 API',
  69. 'api/papers' => '题库核心 API',
  70. 'api/knowledge-mastery' => '知识点掌握',
  71. 'api/mistake-book' => '错题本 API',
  72. 'api/analytics' => '错题本 API',
  73. 'api/intelligent-exams' => '智能出卷与学情报告',
  74. 'api/exam-analysis' => '智能出卷与学情报告',
  75. 'api/textbooks' => '教材 API',
  76. 'api/mathrecsys' => 'MathRecSys 集成 API',
  77. 'api/knowledge' => '知识点与能力 API',
  78. 'api/knowledge-points' => '知识点与能力 API',
  79. ];
  80. foreach ($map as $prefix => $name) {
  81. if (str_starts_with($uri, $prefix)) {
  82. return $name;
  83. }
  84. }
  85. if (str_starts_with($uri, 'api/ocr') || str_contains($uri, 'callback')) {
  86. return '回调与内部 API';
  87. }
  88. if (str_contains($uri, 'test')) {
  89. return '测试 API';
  90. }
  91. return '其他';
  92. }
  93. private function resolveTag(\Illuminate\Routing\Route $route, string $uri): ?string
  94. {
  95. $middleware = (array) $route->middleware();
  96. if (in_array('internal.token', $middleware, true)) {
  97. return 'internal';
  98. }
  99. if (str_contains($uri, 'test')) {
  100. return 'test';
  101. }
  102. return null;
  103. }
  104. private function buildParamHint(string $uri, array $methods): string
  105. {
  106. preg_match_all('/\{([^}]+)\}/', $uri, $matches);
  107. $pathParams = $matches[1] ?? [];
  108. $parts = [];
  109. if (!empty($pathParams)) {
  110. $parts[] = 'path: ' . implode(', ', $pathParams);
  111. }
  112. $hasBody = array_intersect($methods, ['POST', 'PUT', 'PATCH']);
  113. if ($hasBody) {
  114. $parts[] = 'body: payload';
  115. }
  116. if (empty($parts)) {
  117. return 'query/body: -';
  118. }
  119. return implode(' | ', $parts);
  120. }
  121. }