setName('shopro_huifu:import-bank-branch') ->setDescription('Import nationwide CNAPS bank branch codes into shopro_huifu_bank_branch') ->addOption('file', 'f', Option::VALUE_OPTIONAL, 'CSV/JSON source file path') ->addOption('download', 'd', Option::VALUE_NONE, 'Download gweesin/CNAPS cnaps.csv then import') ->addOption('seed', null, Option::VALUE_NONE, 'Import bundled data/bank_branches.json seed only') ->addOption('truncate', 't', Option::VALUE_NONE, 'Clear table before import (optional, default skip existing)') ->addOption('update-existing', 'u', Option::VALUE_NONE, 'Update rows when branch_code already exists') ->addOption('check', 'c', Option::VALUE_NONE, 'Check duplicate branch_code / branch_name in DB only') ->addOption('preset', 'p', Option::VALUE_OPTIONAL, 'Import scope: main(~50k) / joint(~30k) / full(all)', 'main') ->addOption('limit', 'l', Option::VALUE_OPTIONAL, 'Max rows to import (0=all)', 0) ->addOption('batch', 'b', Option::VALUE_OPTIONAL, 'Batch size', 500) ->addOption('format', null, Option::VALUE_OPTIONAL, 'Force format: csv|json|auto', 'auto') ->addOption('dry-run', null, Option::VALUE_NONE, 'Parse only, do not write DB') ->addOption('export-json', null, Option::VALUE_OPTIONAL, 'Export DB rows to JSON file path') ->addOption('export-limit', null, Option::VALUE_OPTIONAL, 'Max rows when exporting JSON', 0); } protected function execute(Input $input, Output $output) { @ini_set('memory_limit', '256M'); $exportJson = trim((string) $input->getOption('export-json')); if ($exportJson !== '') { return $this->exportJson($output, $exportJson, (int) $input->getOption('export-limit')); } if ((bool) $input->getOption('check')) { return $this->checkDuplicates($output); } try { Db::execute('SELECT 1 FROM `' . config('database.prefix') . 'shopro_huifu_bank_branch` LIMIT 1'); } catch (\Throwable $e) { $output->writeln('表 shopro_huifu_bank_branch 不存在,正在自动创建 ...'); try { Installer::ensureBankBranchTable($output); Db::execute('SELECT 1 FROM `' . config('database.prefix') . 'shopro_huifu_bank_branch` LIMIT 1'); } catch (\Throwable $inner) { $output->error('创建 shopro_huifu_bank_branch 失败: ' . $inner->getMessage()); $output->writeln('也可手动执行: php think shopro_huifu:repair-menu'); return 1; } } $file = trim((string) $input->getOption('file')); $download = (bool) $input->getOption('download'); $seed = (bool) $input->getOption('seed'); $preset = trim((string) $input->getOption('preset')); if ($preset === '') { $preset = 'main'; } if ($seed) { $file = ADDON_PATH . 'shopro_huifu/data/bank_branches.json'; $preset = 'full'; } elseif ($download) { $output->writeln('正在下载全国联行号 CSV(约 20MB,来源 gweesin/CNAPS)...'); try { $file = HuifuOnboardBankBranchImporter::downloadCnapsCsv(); $output->writeln('已保存: ' . $file); } catch (\Throwable $e) { $output->error($e->getMessage()); $output->writeln('手动下载: https://hf-mirror.com/datasets/gweesin/CNAPS/resolve/main/cnaps.csv'); $output->writeln('然后执行: php think shopro_huifu:import-bank-branch --file=addons/shopro_huifu/data/cnaps.csv'); return 1; } } elseif ($file === '') { $defaultCsv = ADDON_PATH . 'shopro_huifu/data/cnaps.csv'; if (is_file($defaultCsv)) { $file = $defaultCsv; } else { $output->error('请指定 --file、--download 或 --seed'); $output->writeln('示例:'); $output->writeln(' php think shopro_huifu:import-bank-branch --seed'); $output->writeln(' php think shopro_huifu:import-bank-branch --download'); $output->writeln(' php think shopro_huifu:import-bank-branch --file=addons/shopro_huifu/data/cnaps.csv'); $output->writeln(' php think shopro_huifu:import-bank-branch --file=addons/shopro_huifu/data/cnaps.csv --preset=joint'); return 1; } } if ($seed || strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'json') { $preset = 'full'; } $options = [ 'truncate' => (bool) $input->getOption('truncate'), 'update_existing' => (bool) $input->getOption('update-existing'), 'preset' => $preset, 'limit' => (int) $input->getOption('limit'), 'batch' => (int) $input->getOption('batch'), 'format' => (string) $input->getOption('format'), 'dry_run' => (bool) $input->getOption('dry-run'), ]; $output->writeln('导入文件: ' . $file); try { $output->writeln('导入范围: ' . HuifuOnboardBankBranchImportFilter::presetLabel($preset)); } catch (\Throwable $e) { $output->error($e->getMessage()); return 1; } if ($options['dry_run']) { $output->writeln('[dry-run] 仅解析,不写入数据库'); } if ($options['truncate']) { $output->writeln('[truncate] 导入前清空旧数据'); } else { $output->writeln('已存在联行号将自动跳过(可用 --update-existing 覆盖更新)'); } try { $stats = HuifuOnboardBankBranchImporter::importFile($file, $options); } catch (\Throwable $e) { $output->error($e->getMessage()); return 1; } $output->writeln(sprintf( '完成: 解析 %d 行,命中 %d 行,新增 %d,更新 %d,跳过已存在 %d,范围外 %d,文件内重复 %d,无效 %d', $stats['parsed'] ?? 0, $stats['matched'] ?? 0, $stats['inserted'], $stats['updated'], $stats['skipped_existing'] ?? 0, $stats['skipped_filtered'] ?? 0, $stats['duplicate_in_file'] ?? 0, $stats['skipped_invalid'] ?? 0 )); if (($stats['parsed'] ?? 0) > 1000 && ($stats['inserted'] ?? 0) < 1000 && ($stats['skipped_existing'] ?? 0) < 100) { $output->writeln('警告: 解析行数很多但新增很少,请确认 CSV 联行号列为 BankCode(非 BankId)'); } $count = Db::name('shopro_huifu_bank_branch')->where('status', 1)->count(); $output->writeln('当前库内有效支行: ' . $count); return 0; } protected function checkDuplicates(Output $output) { try { $result = HuifuOnboardBankBranchImporter::checkDatabaseDuplicates(); } catch (\Throwable $e) { $output->error($e->getMessage()); return 1; } $output->writeln('当前库内支行总数: ' . $result['total']); $codeDupes = $result['duplicate_codes']; if ($codeDupes) { $output->writeln('发现重复联行号 ' . count($codeDupes) . ' 组(不应出现,表有唯一索引):'); foreach ($codeDupes as $item) { $output->writeln(' ' . $item['branch_code'] . ' x' . $item['cnt']); } } else { $output->writeln('联行号 branch_code 无重复'); } $nameDupes = $result['duplicate_names']; if ($nameDupes) { $output->writeln('同名不同号(展示前 ' . count($nameDupes) . ' 组,通常可忽略):'); foreach ($nameDupes as $item) { $output->writeln(sprintf( ' %s(%d 条,%d 个联行号)', $item['branch_name'], $item['cnt'], $item['code_cnt'] )); } } else { $output->writeln('支行名称无重复'); } return 0; } protected function exportJson(Output $output, $dest, $limit) { try { $count = HuifuOnboardBankBranchImporter::exportJson($dest, $limit); } catch (\Throwable $e) { $output->error($e->getMessage()); return 1; } $output->writeln('已导出 ' . $count . ' 条到 ' . $dest); return 0; } }