QuestionsImportCommand.php 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Services\QuestionBulkImportService;
  4. use Illuminate\Console\Command;
  5. use Illuminate\Support\Facades\File;
  6. class QuestionsImportCommand extends Command
  7. {
  8. protected $signature = 'questions:import
  9. {file : JSON 文件路径(UTF-8,支持整段数组或 NDJSON 每行一题)}
  10. {--dry-run : 仅校验与统计,不写 MySQL}
  11. {--sql-only : 只生成 SQL 脚本,不写入本地 questions 表}
  12. {--output= : SQL 输出路径;默认 storage/app/exports/questions_import_YYYYMMDDHHmmss.sql}
  13. {--with-id : SQL 中含 id 列(需目标表结构与 id 不冲突;默认不含 id,按 question_code 去重)}';
  14. protected $description = '从 JSON 批量导入 questions,并生成可复制到服务器 MySQL 的 INSERT…ON DUPLICATE KEY UPDATE 脚本';
  15. public function handle(QuestionBulkImportService $service): int
  16. {
  17. $path = $this->argument('file');
  18. if (! is_string($path) || ! File::isFile($path)) {
  19. $this->error('文件不存在:'.$path);
  20. return self::FAILURE;
  21. }
  22. try {
  23. $rawRows = $service->loadRowsFromFile($path);
  24. } catch (\Throwable $e) {
  25. $this->error($e->getMessage());
  26. return self::FAILURE;
  27. }
  28. if ($rawRows === []) {
  29. $this->warn('未解析到任何题目行。');
  30. return self::FAILURE;
  31. }
  32. $rows = [];
  33. foreach ($rawRows as $i => $raw) {
  34. if (! is_array($raw)) {
  35. continue;
  36. }
  37. $rows[] = $service->normalizeImportRow($raw, $i + 1);
  38. }
  39. $includeId = (bool) $this->option('with-id');
  40. $sql = $service->renderMysqlScript($rows, $includeId);
  41. $outPath = $this->option('output');
  42. if (! is_string($outPath) || $outPath === '') {
  43. $outPath = storage_path('app/exports/questions_import_'.date('YmdHis').'.sql');
  44. }
  45. File::ensureDirectoryExists(dirname($outPath));
  46. File::put($outPath, $sql);
  47. $this->info('SQL 已写入:'.$outPath);
  48. $dryRun = (bool) $this->option('dry-run');
  49. $sqlOnly = (bool) $this->option('sql-only');
  50. if ($sqlOnly) {
  51. $this->line('已使用 --sql-only,未写入本地 questions 表。');
  52. return self::SUCCESS;
  53. }
  54. $result = $service->importToDatabase($rows, $dryRun);
  55. $this->info(sprintf(
  56. '本地库:新建 %d 条,更新 %d 条,跳过 %d 条(dry-run=%s)',
  57. $result['created'],
  58. $result['updated'],
  59. $result['skipped'],
  60. $dryRun ? 'true' : 'false'
  61. ));
  62. foreach ($result['errors'] as $err) {
  63. $this->warn($err);
  64. }
  65. return self::SUCCESS;
  66. }
  67. }