| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- <?php
- namespace App\Filament\Pages\TextbookImport;
- use App\Services\Import\TextbookExcelImporter;
- use App\Services\TextbookApiService;
- use PhpOffice\PhpSpreadsheet\IOFactory;
- use Filament\Pages\Page;
- use UnitEnum;
- use BackedEnum;
- use Filament\Actions\Action;
- use Filament\Forms;
- use Filament\Forms\Components\FileUpload;
- use Filament\Notifications\Notification;
- use Illuminate\Support\Facades\Storage;
- use Illuminate\Support\Facades\Log;
- use Livewire\WithFileUploads;
- use Livewire\Component;
- use Livewire\Attributes\Validate;
- class TextbookExcelImportPage extends Page
- {
- use WithFileUploads;
- protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-document-arrow-up';
- protected static ?string $navigationLabel = 'Excel导入';
- protected static UnitEnum|string|null $navigationGroup = '教材管理';
- protected static ?int $navigationSort = 4;
- public string $view = 'filament.pages.textbook-import';
- #[Validate('required|string')]
- public $selectedType = 'textbook_series';
- #[Validate('required|file|mimes:xlsx,xls|max:10240')]
- public $file;
- public $importResult = null;
- protected $importer;
- protected $apiService;
- public function boot()
- {
- $this->importer = app(TextbookExcelImporter::class);
- $this->apiService = app(TextbookApiService::class);
- }
- public function mount()
- {
- $this->importer = app(TextbookExcelImporter::class);
- $this->apiService = app(TextbookApiService::class);
- // 检查URL参数,设置默认导入类型
- $type = request()->get('type');
- if (in_array($type, ['textbook_series', 'textbook', 'textbook_catalog'])) {
- $this->selectedType = $type;
- }
- }
- public function getHeaderActions(): array
- {
- return [
- Action::make('downloadTemplate')
- ->label('下载模板')
- ->icon('heroicon-o-arrow-down-tray')
- ->color('primary')
- ->action('downloadTemplate'),
- Action::make('import')
- ->label('导入数据')
- ->icon('heroicon-o-cloud-arrow-up')
- ->color('success')
- ->action('importData')
- ->requiresConfirmation()
- ->modalHeading('确认导入')
- ->modalDescription('确定要导入Excel文件中的数据吗?此操作将同步到题库服务。')
- ->modalSubmitActionLabel('确认导入'),
- ];
- }
- public function downloadTemplate()
- {
- try {
- $importer = app(TextbookExcelImporter::class);
- if ($this->selectedType === 'textbook_series') {
- $filePath = $importer->generateTextbookSeriesTemplate();
- $fileName = '教材系列导入模板.xlsx';
- } elseif ($this->selectedType === 'textbook') {
- $filePath = $importer->generateTextbookTemplate();
- $fileName = '教材导入模板.xlsx';
- } else {
- $filePath = $importer->generateTextbookCatalogTemplate();
- $fileName = '教材目录导入模板.xlsx';
- }
- return response()->download($filePath, $fileName)->deleteFileAfterSend();
- } catch (\Exception $e) {
- Notification::make()
- ->title('模板生成失败')
- ->body($e->getMessage())
- ->danger()
- ->send();
- }
- }
- public function importData()
- {
- $this->validate();
- $temporaryPath = null;
- $finalPath = null;
- try {
- $importer = app(TextbookExcelImporter::class);
- // 获取文件的临时路径(Livewire 上传的文件会有临时路径)
- $temporaryPath = $this->file->getRealPath();
- if (!$temporaryPath || !file_exists($temporaryPath)) {
- throw new \Exception('文件上传失败,请重新上传');
- }
- // 检查文件是否可读
- if (!is_readable($temporaryPath)) {
- throw new \Exception('文件不可读,请检查文件权限');
- }
- // 调试信息
- Log::info('文件上传信息', [
- 'original_name' => $this->file->getClientOriginalName(),
- 'temporary_path' => $temporaryPath,
- 'exists' => file_exists($temporaryPath),
- 'is_file' => is_file($temporaryPath),
- 'readable' => is_readable($temporaryPath),
- 'file_size' => filesize($temporaryPath),
- 'mime_type' => $this->file->getMimeType(),
- ]);
- // 执行导入 - 直接使用临时路径
- if ($this->selectedType === 'textbook_series') {
- $result = $importer->importTextbookSeries($temporaryPath);
- } elseif ($this->selectedType === 'textbook') {
- $result = $importer->importTextbook($temporaryPath);
- } else {
- // 教材目录导入 - 需要从Excel中获取textbook_id
- $spreadsheet = IOFactory::load($temporaryPath);
- $sheet = $spreadsheet->getActiveSheet();
- $data = $sheet->toArray();
- $header = $data[0] ?? [];
- $textbookIdIndex = 0;
- $seriesIdIndex = null;
- foreach ($header as $index => $label) {
- $label = trim((string) $label);
- if ($label !== '' && str_contains($label, '教材ID')) {
- $textbookIdIndex = $index;
- }
- if ($label !== '' && str_contains($label, '系列ID')) {
- $seriesIdIndex = $index;
- }
- }
- $textbookId = 0;
- $seriesId = null;
- foreach (array_slice($data, 1) as $row) {
- $candidate = (int) ($row[$textbookIdIndex] ?? 0);
- if ($candidate > 0) {
- $textbookId = $candidate;
- break;
- }
- }
- if ($seriesIdIndex !== null) {
- foreach (array_slice($data, 1) as $row) {
- $candidate = (int) ($row[$seriesIdIndex] ?? 0);
- if ($candidate > 0) {
- $seriesId = $candidate;
- break;
- }
- }
- }
- if ($textbookId <= 0) {
- throw new \Exception('Excel文件中未找到有效的教材ID,请确保第一列包含教材ID');
- }
- $result = $importer->importTextbookCatalog($temporaryPath, $textbookId, $seriesId);
- }
- $this->importResult = $result;
- if ($result['success']) {
- $message = sprintf(
- '导入完成!成功: %d 条,失败: %d 条',
- $result['success_count'],
- $result['error_count']
- );
- Notification::make()
- ->title('导入成功')
- ->body($message)
- ->success()
- ->send();
- } else {
- Notification::make()
- ->title('导入失败')
- ->body($result['message'])
- ->danger()
- ->send();
- }
- } catch (\Exception $e) {
- Notification::make()
- ->title('导入失败')
- ->body($e->getMessage())
- ->danger()
- ->send();
- Log::error('Excel导入失败', [
- 'error' => $e->getMessage(),
- 'temporary_path' => $temporaryPath,
- 'final_path' => $finalPath,
- 'trace' => $e->getTraceAsString(),
- ]);
- }
- // 不需要手动删除文件,临时文件会在请求结束后自动清理
- }
- }
|