baseUrl = rtrim($baseUrl, '/'); } /** * 获取教材系列列表 */ public function getTextbookSeries(array $params = []): array { try { $response = Http::timeout(30)->get($this->baseUrl . '/textbooks/series', $params); if ($response->successful()) { return $response->json(); } Log::error('Failed to fetch textbook series', [ 'status' => $response->status(), 'body' => $response->body() ]); return ['data' => [], 'meta' => []]; } catch (\Exception $e) { Log::error('Error fetching textbook series', ['error' => $e->getMessage()]); return ['data' => [], 'meta' => []]; } } /** * 根据ID获取单个教材系列 */ public function getTextbookSeriesById(int $seriesId): ?array { try { $response = Http::timeout(30)->get($this->baseUrl . "/textbooks/series/{$seriesId}"); if ($response->successful()) { return $response->json('data'); } Log::warning('Series not found', [ 'series_id' => $seriesId, 'status' => $response->status(), ]); return null; } catch (\Exception $e) { Log::error('Error fetching textbook series', ['error' => $e->getMessage(), 'series_id' => $seriesId]); return null; } } /** * 创建教材系列 */ public function createTextbookSeries(array $data): array { try { $response = Http::timeout(30)->post($this->baseUrl . '/textbooks/series', $data); if ($response->successful()) { return $response->json(); } $responseBody = $response->body(); $status = $response->status(); Log::error('Failed to create textbook series', [ 'status' => $status, 'body' => $responseBody ]); throw new \Exception('Failed to create textbook series: ' . $responseBody); } catch (\Exception $e) { Log::error('Error creating textbook series', ['error' => $e->getMessage()]); throw $e; } } /** * 更新教材系列 */ public function updateTextbookSeries(int $seriesId, array $data): array { try { $response = Http::timeout(30)->put($this->baseUrl . "/textbooks/series/{$seriesId}", $data); if ($response->successful()) { return $response->json(); } Log::error('Failed to update textbook series', [ 'status' => $response->status(), 'body' => $response->body() ]); throw new \Exception('Failed to update textbook series'); } catch (\Exception $e) { Log::error('Error updating textbook series', ['error' => $e->getMessage()]); throw $e; } } /** * 删除教材系列 */ public function deleteTextbookSeries(int $seriesId): bool { try { $response = Http::timeout(30)->delete($this->baseUrl . "/textbooks/series/{$seriesId}"); if ($response->successful()) { return true; } Log::error('Failed to delete textbook series', [ 'status' => $response->status(), 'body' => $response->body() ]); return false; } catch (\Exception $e) { Log::error('Error deleting textbook series', ['error' => $e->getMessage()]); return false; } } /** * 删除教材 */ public function deleteTextbook(int $textbookId): bool { try { $response = Http::timeout(30)->delete($this->baseUrl . "/textbooks/{$textbookId}"); if ($response->successful()) { return true; } Log::error('Failed to delete textbook', [ 'textbook_id' => $textbookId, 'status' => $response->status(), 'body' => $response->body() ]); return false; } catch (\Exception $e) { Log::error('Error deleting textbook', ['error' => $e->getMessage(), 'textbook_id' => $textbookId]); return false; } } /** * 获取教材列表 */ public function getTextbooks(array $params = []): array { try { $response = Http::timeout(30)->get($this->baseUrl . '/textbooks', $params); if ($response->successful()) { return $response->json(); } Log::error('Failed to fetch textbooks', [ 'status' => $response->status(), 'body' => $response->body() ]); return ['data' => [], 'meta' => []]; } catch (\Exception $e) { Log::error('Error fetching textbooks', ['error' => $e->getMessage()]); return ['data' => [], 'meta' => []]; } } /** * 获取单个教材 */ public function getTextbook(int $textbookId): ?array { try { $response = Http::timeout(30)->get($this->baseUrl . "/textbooks/{$textbookId}"); if ($response->successful()) { return $response->json('data'); } Log::error('Failed to fetch textbook', [ 'status' => $response->status(), 'body' => $response->body() ]); return null; } catch (\Exception $e) { Log::error('Error fetching textbook', ['error' => $e->getMessage()]); return null; } } /** * 创建教材 */ public function createTextbook(array $data): array { try { $response = Http::timeout(30)->post($this->baseUrl . '/textbooks', $data); if ($response->successful()) { return $response->json(); } $responseBody = $response->body(); $status = $response->status(); Log::error('Failed to create textbook', [ 'status' => $status, 'body' => $responseBody ]); // 检查是否是series不存在的错误 if ($status === 404 && strpos($responseBody, 'Series not found') !== false) { $seriesId = $data['series_id'] ?? 'unknown'; throw new \Exception("系列ID {$seriesId} 不存在,请先创建教材系列或检查ID是否正确"); } throw new \Exception('Failed to create textbook: ' . $responseBody); } catch (\Exception $e) { Log::error('Error creating textbook', ['error' => $e->getMessage()]); throw $e; } } /** * 更新教材 */ public function updateTextbook(int $textbookId, array $data): array { try { $response = Http::timeout(30)->put($this->baseUrl . "/textbooks/{$textbookId}", $data); if ($response->successful()) { return $response->json(); } Log::error('Failed to update textbook', [ 'status' => $response->status(), 'body' => $response->body() ]); throw new \Exception('Failed to update textbook'); } catch (\Exception $e) { Log::error('Error updating textbook', ['error' => $e->getMessage()]); throw $e; } } /** * 创建或更新教材(upsert模式) * 根据系列、学段、年级、学期、官方书名判断是否已存在 */ public function createOrUpdateTextbook(array $data): array { try { $response = Http::timeout(30)->post($this->baseUrl . '/textbooks/upsert', $data); if ($response->successful()) { return $response->json(); } $responseBody = $response->body(); $status = $response->status(); Log::error('Failed to create or update textbook', [ 'status' => $status, 'body' => $responseBody ]); // 检查是否是series不存在的错误 if ($status === 404 && strpos($responseBody, 'Series not found') !== false) { $seriesId = $data['series_id'] ?? 'unknown'; throw new \Exception("系列ID {$seriesId} 不存在,请先创建教材系列或检查ID是否正确"); } throw new \Exception('Failed to create or update textbook: ' . $responseBody); } catch (\Exception $e) { Log::error('Error creating or updating textbook', ['error' => $e->getMessage()]); throw $e; } } /** * 删除教材目录节点 */ public function deleteTextbookCatalog(int $catalogId): bool { try { $response = Http::timeout(30)->delete($this->baseUrl . "/textbooks/catalog/{$catalogId}"); if ($response->successful()) { return true; } Log::error('Failed to delete textbook catalog', [ 'catalog_id' => $catalogId, 'status' => $response->status(), 'body' => $response->body() ]); return false; } catch (\Exception $e) { Log::error('Error deleting textbook catalog', ['error' => $e->getMessage(), 'catalog_id' => $catalogId]); return false; } } /** * 获取教材目录树 */ public function getTextbookCatalog(int $textbookId, string $format = 'tree'): array { try { $response = Http::timeout(30)->get($this->baseUrl . "/textbooks/{$textbookId}/catalog", [ 'format' => $format ]); if ($response->successful()) { return $response->json('data'); } Log::error('Failed to fetch textbook catalog', [ 'status' => $response->status(), 'body' => $response->body() ]); return []; } catch (\Exception $e) { Log::error('Error fetching textbook catalog', ['error' => $e->getMessage()]); return []; } } /** * 预览教材命名 */ public function previewTextbookNaming(array $textbookData, array $seriesData): array { try { $response = Http::timeout(30)->post($this->baseUrl . '/textbooks/naming-preview', [ 'textbook' => $textbookData, 'series' => $seriesData ]); if ($response->successful()) { return $response->json('data'); } Log::error('Failed to preview textbook naming', [ 'status' => $response->status(), 'body' => $response->body() ]); return []; } catch (\Exception $e) { Log::error('Error previewing textbook naming', ['error' => $e->getMessage()]); return []; } } /** * 导入教材元信息 */ public function importTextbookMetadata($file, string $commitMode = 'overwrite'): array { try { $response = Http::timeout(300) ->attach('file', file_get_contents($file->getPathname()), $file->getClientOriginalName()) ->post($this->baseUrl . '/textbooks/import/meta', [ 'commit_mode' => $commitMode ]); if ($response->successful()) { return $response->json(); } Log::error('Failed to import textbook metadata', [ 'status' => $response->status(), 'body' => $response->body() ]); throw new \Exception('Failed to import textbook metadata'); } catch (\Exception $e) { Log::error('Error importing textbook metadata', ['error' => $e->getMessage()]); throw $e; } } /** * 导入教材目录 */ public function importTextbookCatalog(int $textbookId, $file, string $commitMode = 'overwrite'): array { try { $response = Http::timeout(300) ->attach('file', file_get_contents($file->getPathname()), $file->getClientOriginalName()) ->post($this->baseUrl . '/textbooks/import/catalog', [ 'textbook_id' => $textbookId, 'commit_mode' => $commitMode ]); if ($response->successful()) { return $response->json(); } Log::error('Failed to import textbook catalog', [ 'status' => $response->status(), 'body' => $response->body() ]); throw new \Exception('Failed to import textbook catalog'); } catch (\Exception $e) { Log::error('Error importing textbook catalog', ['error' => $e->getMessage()]); throw $e; } } /** * 提交导入任务 */ public function commitImportJob(int $jobId): array { try { $response = Http::timeout(300)->post($this->baseUrl . "/api/textbooks/import/{$jobId}/commit"); if ($response->successful()) { return $response->json(); } Log::error('Failed to commit import job', [ 'status' => $response->status(), 'body' => $response->body() ]); throw new \Exception('Failed to commit import job'); } catch (\Exception $e) { Log::error('Error committing import job', ['error' => $e->getMessage()]); throw $e; } } /** * 获取导入任务列表 */ public function getImportJobs(array $params = []): array { try { $response = Http::timeout(30)->get($this->baseUrl . '/textbooks/import/jobs', $params); if ($response->successful()) { return $response->json(); } Log::error('Failed to fetch import jobs', [ 'status' => $response->status(), 'body' => $response->body() ]); return ['data' => [], 'meta' => []]; } catch (\Exception $e) { Log::error('Error fetching import jobs', ['error' => $e->getMessage()]); return ['data' => [], 'meta' => []]; } } /** * 获取单个导入任务 */ public function getImportJob(int $jobId): ?array { try { $response = Http::timeout(30)->get($this->baseUrl . "/api/textbooks/import/jobs/{$jobId}"); if ($response->successful()) { return $response->json('data'); } Log::error('Failed to fetch import job', [ 'status' => $response->status(), 'body' => $response->body() ]); return null; } catch (\Exception $e) { Log::error('Error fetching import job', ['error' => $e->getMessage()]); return null; } } /** * 完全同步教材系列到题库服务(清空+重新插入) */ public function syncTextbookSeriesToQuestionBank(): array { try { // 获取MySQL中的所有系列 $mysqlSeries = DB::connection('mysql') ->table('textbook_series') ->orderBy('id') ->get(); if ($mysqlSeries->isEmpty()) { return [ 'success' => false, 'message' => 'MySQL中没有找到教材系列数据' ]; } // 准备数据 $seriesData = []; foreach ($mysqlSeries as $series) { $seriesData[] = [ 'id' => $series->id, 'name' => $series->name, 'slug' => $series->slug, 'publisher' => $series->publisher, 'region' => $series->region, 'stages' => json_decode($series->stages, true), 'is_active' => (bool)$series->is_active, 'sort_order' => (int)$series->sort_order, 'meta' => json_decode($series->meta, true), ]; } // 调用API进行完全同步 $response = Http::timeout(300)->post($this->baseUrl . '/textbooks/series/sync-all', [ 'series' => $seriesData ]); if ($response->successful()) { $result = $response->json(); return [ 'success' => true, 'synced_count' => count($seriesData), 'data' => $result ]; } $responseBody = $response->body(); $status = $response->status(); Log::error('Failed to sync textbook series', [ 'status' => $status, 'body' => $responseBody ]); return [ 'success' => false, 'message' => "同步失败: HTTP {$status} - {$responseBody}" ]; } catch (\Exception $e) { Log::error('Error syncing textbook series', ['error' => $e->getMessage()]); return [ 'success' => false, 'message' => '同步失败: ' . $e->getMessage() ]; } } }