GenerateProtoRouteCommand.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <?php
  2. namespace App\Console\Commands;
  3. use Illuminate\Console\Command;
  4. use Illuminate\Support\Facades\File;
  5. use Illuminate\Support\Str;
  6. class GenerateProtoRouteCommand extends Command
  7. {
  8. protected $signature = 'proto:route {--force : 强制覆盖现有配置}';
  9. protected $description = '生成 Protobuf 路由配置';
  10. protected $protoDir = 'protophp/Uraus/Kku';
  11. protected $configPath = 'config/proto_route.php';
  12. public function handle()
  13. {
  14. $this->info('开始扫描 Protobuf 请求定义...');
  15. // 获取所有模块
  16. $modules = $this->scanModules();
  17. // 生成配置数组
  18. $config = $this->generateConfig($modules);
  19. // 生成配置文件
  20. $this->generateConfigFile($config);
  21. $this->info('Protobuf 路由配置生成完成!');
  22. // 输出提示信息
  23. $this->showHandlerGuide($modules);
  24. return Command::SUCCESS;
  25. }
  26. protected function scanModules(): array
  27. {
  28. $modules = [];
  29. $baseDir = base_path($this->protoDir);
  30. // 扫描 Request 目录
  31. $requestDir = $baseDir . '/Request';
  32. if (File::isDirectory($requestDir)) {
  33. // 扫描所有 Request 开头的 PHP 文件
  34. $files = File::glob($requestDir . '/Request*.php');
  35. foreach ($files as $file) {
  36. $className = basename($file, '.php');
  37. if (strpos($className, 'Request') === 0 && $className !== 'Request') {
  38. // 解析模块和方法
  39. $this->parseRequestClass($className, $modules);
  40. }
  41. }
  42. }
  43. // 扫描根目录下的 Request 文件
  44. $rootFiles = File::glob($baseDir . '/Request*.php');
  45. foreach ($rootFiles as $file) {
  46. $className = basename($file, '.php');
  47. if (strpos($className, 'Request') === 0 && $className !== 'Request') {
  48. // 解析模块和方法
  49. $this->parseRequestClass($className, $modules);
  50. }
  51. }
  52. return $modules;
  53. }
  54. /**
  55. * 解析请求类名,提取模块和方法
  56. *
  57. * @param string $className 请求类名,如 RequestPublicLogin
  58. * @param array &$modules 模块数组,按引用传递
  59. * @return void
  60. */
  61. protected function parseRequestClass(string $className, array &$modules): void
  62. {
  63. // 如果是 RequestPublicLogin 格式,则直接处理
  64. if ($className === 'RequestPublicLogin') {
  65. if (!isset($modules['public'])) {
  66. $modules['public'] = [];
  67. }
  68. if (!in_array('login', $modules['public'])) {
  69. $modules['public'][] = 'login';
  70. }
  71. return;
  72. }
  73. // 如果是 Request_RequestPublicLogin 格式,则跳过
  74. if (strpos($className, 'Request_Request') === 0) {
  75. return;
  76. }
  77. // 移除 Request 前缀
  78. $name = substr($className, strlen('Request'));
  79. // 检查是否包含下划线,表示有方法部分
  80. if (strpos($name, '_') !== false) {
  81. // 格式如 Public_Login
  82. list($module, $method) = explode('_', $name, 2);
  83. } else {
  84. // 如果是 RequestPublicLogin 这样的格式,需要特殊处理
  85. if (preg_match('/^([A-Z][a-z]+)([A-Z].*)$/', $name, $matches)) {
  86. $module = $matches[1]; // Public
  87. $method = $matches[2]; // Login
  88. } else {
  89. // 其他格式,如果没有明显的分隔,则整个作为模块
  90. $module = $name;
  91. $method = '';
  92. }
  93. }
  94. // 模块名首字母小写
  95. $moduleKey = lcfirst($module);
  96. // 方法名转为小写下划线格式
  97. $methodKey = Str::snake(lcfirst($method));
  98. // 如果方法为空,则不添加
  99. if (!empty($methodKey)) {
  100. if (!isset($modules[$moduleKey])) {
  101. $modules[$moduleKey] = [];
  102. }
  103. if (!in_array($methodKey, $modules[$moduleKey])) {
  104. $modules[$moduleKey][] = $methodKey;
  105. }
  106. }
  107. }
  108. protected function extractMethodsFromContent(string $content): array
  109. {
  110. // 不再从文件内容中提取方法,而是从类名中解析
  111. return [];
  112. }
  113. protected function scanMethodFields(string $moduleDir): array
  114. {
  115. return []; // 不再需要扫描子目录
  116. }
  117. protected function generateConfig(array $modules): array
  118. {
  119. $routes = [];
  120. foreach ($modules as $field => $methods) {
  121. if (!empty($methods) && !empty($field)) {
  122. $routes[$field] = array_values($methods);
  123. }
  124. }
  125. return [
  126. 'routes' => $routes,
  127. 'generated_at' => date('P Y-m-d H:i:s'),
  128. 'conventions' => [
  129. 'handler_namespace' => 'App\\Module\\AppGame\\Handler',
  130. 'request_namespace' => 'Uraus\\Kku\\Request',
  131. 'response_namespace' => 'Uraus\\Kku\\Response'
  132. ]
  133. ];
  134. }
  135. protected function generateConfigFile(array $config): void
  136. {
  137. $configContent = "<?php\n\nreturn " . var_export($config, true) . ";\n";
  138. $configPath = config_path('proto_route.php');
  139. if (File::exists($configPath) && !$this->option('force')) {
  140. if (!$this->confirm('配置文件已存在,是否覆盖?')) {
  141. $this->warn('操作已取消');
  142. return;
  143. }
  144. }
  145. File::put($configPath, $configContent);
  146. $this->info("配置文件已生成:{$configPath}");
  147. }
  148. protected function showHandlerGuide(array $modules): void
  149. {
  150. $this->info("\n按照以下约定创建处理器类:\n");
  151. foreach ($modules as $field => $methods) {
  152. if (empty($methods)) {
  153. continue;
  154. }
  155. $moduleName = ucfirst($field);
  156. $this->line("\n模块: {$moduleName} (字段: {$field})");
  157. foreach ($methods as $method) {
  158. $methodName = Str::studly($method);
  159. $handlerClass = "App\\Module\\AppGame\\Handler\\{$moduleName}\\{$methodName}Handler";
  160. $requestClass = "Uraus\\Kku\\Request\\Request{$moduleName}" . ($method ? "_" . Str::studly($method) : "");
  161. $responseClass = "Uraus\\Kku\\Response\\Response{$moduleName}" . ($method ? "_" . Str::studly($method) : "");
  162. $this->line("\n 方法: {$method}");
  163. $this->line(" - Handler: {$handlerClass}");
  164. $this->line(" - Request: {$requestClass}");
  165. $this->line(" - Response: {$responseClass}");
  166. }
  167. }
  168. $this->info("\n提示:处理器类会根据约定自动加载,无需手动配置。");
  169. }
  170. }