baseUrl = rtrim($baseUrl, '/'); } /** * 通用HTTP请求方法 - 减少重复代码,无缓存 */ protected function request(string $method, string $endpoint, array $data = []): array { try { $httpMethod = strtolower($method); $response = match($httpMethod) { 'get' => Http::timeout(30)->get($this->baseUrl . $endpoint, $data), 'post' => Http::timeout(300)->post($this->baseUrl . $endpoint, $data), 'put' => Http::timeout(30)->put($this->baseUrl . $endpoint, $data), 'delete' => Http::timeout(30)->delete($this->baseUrl . $endpoint, $data), default => throw new \InvalidArgumentException("Unsupported HTTP method: {$method}") }; // 处理文件上传 if (isset($data['file']) && $data['file'] instanceof \Illuminate\Http\UploadedFile) { $response = Http::timeout(300) ->attach('file', file_get_contents($data['file']->getPathname()), $data['file']->getClientOriginalName()) ->{$httpMethod}($this->baseUrl . $endpoint, $data); } if ($response->successful()) { return $response->json(); } // 记录错误并抛出异常 $error = $this->handleErrorResponse($response, $endpoint); throw new \Exception($error); } catch (\Exception $e) { Log::error("API request failed: {$method} {$endpoint}", [ 'error' => $e->getMessage(), 'data' => $data ]); throw $e; } } /** * 处理错误响应 */ private function handleErrorResponse($response, string $endpoint): string { $status = $response->status(); $body = $response->body(); // 常见错误类型的友好提示 return match($status) { 404 => "未找到资源: {$endpoint}", 422 => "数据验证失败: {$body}", 500 => "服务器内部错误: {$body}", default => "HTTP {$status}: {$body}" }; } /** * 获取教材系列列表 */ public function getTextbookSeries(array $params = []): array { try { return $this->request('GET', '/textbooks/series', $params); } catch (\Exception $e) { // 失败时返回空数据而不是抛出异常,保持向后兼容 Log::warning('Failed to fetch textbook series, returning empty result', ['error' => $e->getMessage()]); return ['data' => [], 'meta' => []]; } } /** * 根据ID获取单个教材系列 */ public function getTextbookSeriesById(int $seriesId): ?array { try { $result = $this->request('GET', "/textbooks/series/{$seriesId}"); return $result['data'] ?? null; } catch (\Exception $e) { Log::warning('Series not found or error occurred', [ 'series_id' => $seriesId, 'error' => $e->getMessage() ]); return null; } } /** * 创建教材系列 */ public function createTextbookSeries(array $data): array { try { return $this->request('POST', '/textbooks/series', $data); } catch (\Exception $e) { // 检查是否是series不存在的错误(虽然这里不太可能,但保持向后兼容) if (strpos($e->getMessage(), 'Series not found') !== false) { $seriesId = $data['id'] ?? 'unknown'; throw new \Exception("系列ID {$seriesId} 不存在"); } throw $e; } } /** * 更新教材系列 */ public function updateTextbookSeries(int $seriesId, array $data): array { try { return $this->request('PUT', "/textbooks/series/{$seriesId}", $data); } catch (\Exception $e) { Log::error('Error updating textbook series', ['error' => $e->getMessage()]); throw $e; } } /** * 删除教材系列 */ public function deleteTextbookSeries(int $seriesId): bool { try { $this->request('DELETE', "/textbooks/series/{$seriesId}"); return true; } catch (\Exception $e) { Log::error('Error deleting textbook series', ['error' => $e->getMessage()]); return false; } } /** * 删除教材 */ public function deleteTextbook(int $textbookId): bool { try { $this->request('DELETE', "/textbooks/by-id/{$textbookId}"); return true; } catch (\Exception $e) { Log::error('Error deleting textbook', ['error' => $e->getMessage(), 'textbook_id' => $textbookId]); return false; } } /** * 获取教材列表 */ public function getTextbooks(array $params = []): array { try { return $this->request('GET', '/textbooks', $params); } catch (\Exception $e) { Log::warning('Failed to fetch textbooks, returning empty result', ['error' => $e->getMessage()]); return ['data' => [], 'meta' => []]; } } /** * 获取单个教材 */ public function getTextbook(int $textbookId): ?array { try { $result = $this->request('GET', "/textbooks/by-id/{$textbookId}"); return $result['data'] ?? null; } catch (\Exception $e) { Log::warning('Textbook not found or error occurred', [ 'textbook_id' => $textbookId, 'error' => $e->getMessage() ]); return null; } } /** * 创建教材 */ public function createTextbook(array $data): array { try { return $this->request('POST', '/textbooks', $data); } catch (\Exception $e) { // 检查是否是series不存在的错误 if (strpos($e->getMessage(), 'Series not found') !== false) { $seriesId = $data['series_id'] ?? 'unknown'; throw new \Exception("系列ID {$seriesId} 不存在,请先创建教材系列或检查ID是否正确"); } throw $e; } } /** * 更新教材 */ public function updateTextbook(int $textbookId, array $data): array { try { return $this->request('PUT', "/textbooks/by-id/{$textbookId}", $data); } catch (\Exception $e) { Log::error('Error updating textbook', ['error' => $e->getMessage()]); throw $e; } } /** * 创建或更新教材(upsert模式) * 根据系列、学段、年级、学期、官方书名判断是否已存在 */ public function createOrUpdateTextbook(array $data): array { try { return $this->request('POST', '/textbooks/upsert', $data); } catch (\Exception $e) { // 检查是否是series不存在的错误 if (strpos($e->getMessage(), 'Series not found') !== false) { $seriesId = $data['series_id'] ?? 'unknown'; throw new \Exception("系列ID {$seriesId} 不存在,请先创建教材系列或检查ID是否正确"); } throw $e; } } /** * 删除教材目录节点 */ public function deleteTextbookCatalog(int $catalogId): bool { try { $this->request('DELETE', "/textbooks/catalog/{$catalogId}"); return true; } 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 { $result = $this->request('GET', "/textbooks/by-id/{$textbookId}/catalog", ['format' => $format]); return $result['data'] ?? []; } catch (\Exception $e) { Log::warning('Failed to fetch textbook catalog, returning empty result', ['error' => $e->getMessage()]); return []; } } /** * 预览教材命名 */ public function previewTextbookNaming(array $textbookData, array $seriesData): array { try { $result = $this->request('POST', '/textbooks/naming-preview', [ 'textbook' => $textbookData, 'series' => $seriesData ]); return $result['data'] ?? []; } catch (\Exception $e) { Log::warning('Failed to preview textbook naming, returning empty result', ['error' => $e->getMessage()]); return []; } } /** * 导入教材元信息 */ public function importTextbookMetadata($file, string $commitMode = 'overwrite'): array { try { return $this->request('POST', '/textbooks/import/meta', [ 'file' => $file, 'commit_mode' => $commitMode ]); } catch (\Exception $e) { Log::error('Error importing textbook metadata', ['error' => $e->getMessage()]); throw $e; } } /** * 导入教材目录 */ public function importTextbookCatalog(int $textbookId, array $catalogData, string $commitMode = 'overwrite'): array { try { // 将数组转换为JSON字符串 $jsonData = json_encode($catalogData, JSON_UNESCAPED_UNICODE); // 直接发送 JSON 数据 return $this->request('POST', '/textbooks/import/catalog', [ 'textbook_id' => $textbookId, 'data' => $jsonData, 'commit_mode' => $commitMode ]); } catch (\Exception $e) { Log::error('Error importing textbook catalog', ['error' => $e->getMessage()]); throw $e; } } /** * 提交导入任务 */ public function commitImportJob(int $jobId): array { try { return $this->request('POST', "/api/textbooks/import/{$jobId}/commit"); } catch (\Exception $e) { Log::error('Error committing import job', ['error' => $e->getMessage()]); throw $e; } } /** * 获取导入任务列表 */ public function getImportJobs(array $params = []): array { try { return $this->request('GET', '/textbooks/import/jobs', $params); } catch (\Exception $e) { Log::warning('Failed to fetch import jobs, returning empty result', ['error' => $e->getMessage()]); return ['data' => [], 'meta' => []]; } } /** * 获取单个导入任务 */ public function getImportJob(int $jobId): ?array { try { $result = $this->request('GET', "/api/textbooks/import/jobs/{$jobId}"); return $result['data'] ?? null; } catch (\Exception $e) { Log::warning('Import job not found or error occurred', ['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进行完全同步 $result = $this->request('POST', '/textbooks/series/sync-all', [ 'series' => $seriesData ]); return [ 'success' => true, 'synced_count' => count($seriesData), 'data' => $result ]; } catch (\Exception $e) { Log::error('Error syncing textbook series', ['error' => $e->getMessage()]); return [ 'success' => false, 'message' => '同步失败: ' . $e->getMessage() ]; } } }