textbookService = $textbookService; } /** * 获取教材列表(按年级排序) * * @param Request $request * @return JsonResponse */ public function index(Request $request): JsonResponse { try { $params = [ 'page' => $request->get('page', 1), 'per_page' => $request->get('per_page', 50), ]; // 可选过滤参数 if ($request->has('grade')) { $params['grade'] = $request->get('grade'); } if ($request->has('stage')) { $params['stage'] = $this->convertStageToCode($request->get('stage')); } if ($request->has('semester')) { $params['semester'] = $this->convertSemesterToCode($request->get('semester')); } if ($request->has('series_id')) { $params['series_id'] = $request->get('series_id'); } if ($request->has('status')) { $params['status'] = $request->get('status'); } $result = $this->textbookService->getTextbooks($params); // 格式化返回数据 $textbooks = $this->formatTextbookList($result['data'] ?? []); // 按年级排序 usort($textbooks, function ($a, $b) { // 先按学段排序:小学 < 初中 < 高中 $stageOrder = ['primary' => 1, 'junior' => 2, 'senior' => 3]; $stageA = $stageOrder[$a['stage_code']] ?? 99; $stageB = $stageOrder[$b['stage_code']] ?? 99; if ($stageA !== $stageB) { return $stageA - $stageB; } // 再按年级排序 $gradeA = $a['grade'] ?? 0; $gradeB = $b['grade'] ?? 0; if ($gradeA !== $gradeB) { return $gradeA - $gradeB; } // 最后按学期排序 $semesterA = $a['semester_code'] ?? 0; $semesterB = $b['semester_code'] ?? 0; return $semesterA - $semesterB; }); return response()->json([ 'success' => true, 'data' => $textbooks, 'meta' => $result['meta'] ?? [ 'page' => $params['page'], 'per_page' => $params['per_page'], 'total' => count($textbooks), ] ]); } catch (\Exception $e) { Log::error('获取教材列表失败', ['error' => $e->getMessage()]); return response()->json([ 'success' => false, 'message' => '获取教材列表失败: ' . $e->getMessage() ], 500); } } /** * 根据年级获取教材 * * @param int $grade 年级(1-12) * @param Request $request * @return JsonResponse */ public function getByGrade(int $grade, Request $request): JsonResponse { try { // 根据年级自动判断学段 $stage = $this->getStageByGrade($grade); $params = [ 'grade' => $grade, 'stage' => $stage, 'per_page' => 100, ]; if ($request->has('semester')) { $params['semester'] = $this->convertSemesterToCode($request->get('semester')); } if ($request->has('series_id')) { $params['series_id'] = $request->get('series_id'); } $result = $this->textbookService->getTextbooks($params); $textbooks = $this->formatTextbookList($result['data'] ?? []); // 按学期排序 usort($textbooks, function ($a, $b) { return ($a['semester_code'] ?? 0) - ($b['semester_code'] ?? 0); }); return response()->json([ 'success' => true, 'data' => $textbooks, 'meta' => [ 'grade' => $grade, 'stage' => $this->getStageLabel($stage), 'total' => count($textbooks), ] ]); } catch (\Exception $e) { Log::error('根据年级获取教材失败', [ 'grade' => $grade, 'error' => $e->getMessage() ]); return response()->json([ 'success' => false, 'message' => '获取教材失败: ' . $e->getMessage() ], 500); } } /** * 获取单个教材详情 * * @param int $id 教材ID * @return JsonResponse */ public function show(int $id): JsonResponse { try { $textbook = $this->textbookService->getTextbook($id); if (!$textbook) { return response()->json([ 'success' => false, 'message' => '教材不存在' ], 404); } return response()->json([ 'success' => true, 'data' => $this->formatTextbook($textbook) ]); } catch (\Exception $e) { Log::error('获取教材详情失败', [ 'id' => $id, 'error' => $e->getMessage() ]); return response()->json([ 'success' => false, 'message' => '获取教材详情失败: ' . $e->getMessage() ], 500); } } /** * 获取教材系列列表 * * @param Request $request * @return JsonResponse */ public function getSeries(Request $request): JsonResponse { try { $params = []; if ($request->has('stage')) { $params['stage'] = $this->convertStageToCode($request->get('stage')); } $result = $this->textbookService->getTextbookSeries($params); $series = array_map(function ($item) { return [ 'id' => $item['id'], 'name' => $item['name'], 'slug' => $item['slug'] ?? '', 'publisher' => $item['publisher'] ?? '', 'region' => $item['region'] ?? '全国', 'stages' => $this->formatStages($item['stages'] ?? '[]'), 'is_active' => $item['is_active'] ?? true, 'sort_order' => $item['sort_order'] ?? 0, ]; }, $result['data'] ?? []); // 按排序字段排序 usort($series, fn($a, $b) => ($a['sort_order'] ?? 0) - ($b['sort_order'] ?? 0)); return response()->json([ 'success' => true, 'data' => $series, 'meta' => [ 'total' => count($series), ] ]); } catch (\Exception $e) { Log::error('获取教材系列失败', ['error' => $e->getMessage()]); return response()->json([ 'success' => false, 'message' => '获取教材系列失败: ' . $e->getMessage() ], 500); } } /** * 获取教材目录 * * @param int $textbookId 教材ID * @param Request $request * @return JsonResponse */ public function getCatalog(int $textbookId, Request $request): JsonResponse { try { $format = $request->get('format', 'tree'); // tree 或 flat $catalog = $this->textbookService->getTextbookCatalog($textbookId, $format); return response()->json([ 'success' => true, 'data' => $catalog, 'meta' => [ 'textbook_id' => $textbookId, 'format' => $format, ] ]); } catch (\Exception $e) { Log::error('获取教材目录失败', [ 'textbook_id' => $textbookId, 'error' => $e->getMessage() ]); return response()->json([ 'success' => false, 'message' => '获取教材目录失败: ' . $e->getMessage() ], 500); } } /** * 格式化教材列表 */ private function formatTextbookList(array $textbooks): array { return array_map(fn($item) => $this->formatTextbook($item), $textbooks); } /** * 格式化单个教材 */ private function formatTextbook(array $textbook): array { $stage = $textbook['stage'] ?? ''; $semester = $textbook['semester'] ?? null; return [ 'id' => $textbook['id'], 'name' => $textbook['official_title'] ?? $textbook['display_title'] ?? '', 'display_name' => $textbook['display_title'] ?? $textbook['official_title'] ?? '', 'cover' => $this->formatCoverUrl($textbook['cover_path'] ?? ''), 'series_id' => $textbook['series_id'] ?? null, 'series_name' => $textbook['series']['name'] ?? '', 'publisher' => $textbook['series']['publisher'] ?? '', 'stage' => $this->getStageLabel($stage), 'stage_code' => $stage, 'grade' => $textbook['grade'] ?? null, 'grade_label' => $this->getGradeLabel($textbook['grade'] ?? null, $stage), 'semester' => $this->getSemesterLabel($semester), 'semester_code' => $semester, 'module_type' => $textbook['module_type'] ?? null, 'volume_no' => $textbook['volume_no'] ?? null, 'isbn' => $textbook['isbn'] ?? '', 'approval_year' => $textbook['approval_year'] ?? null, 'curriculum_standard_year' => $textbook['curriculum_standard_year'] ?? null, 'status' => $textbook['status'] ?? 'draft', 'sort_order' => $textbook['sort_order'] ?? 0, ]; } /** * 格式化封面URL */ private function formatCoverUrl(?string $coverPath): string { if (empty($coverPath)) { return ''; } // 如果已经是完整URL,直接返回 if (str_starts_with($coverPath, 'http://') || str_starts_with($coverPath, 'https://')) { return $coverPath; } // 本地存储路径,添加域名 return url('/storage/' . ltrim($coverPath, '/')); } /** * 学段代码转中文 */ private function getStageLabel(string $stage): string { return match ($stage) { 'primary' => '小学', 'junior' => '初中', 'senior' => '高中', default => $stage, }; } /** * 中文学段转代码 */ private function convertStageToCode(string $stage): string { return match ($stage) { '小学' => 'primary', '初中' => 'junior', '高中' => 'senior', default => $stage, }; } /** * 学期代码转中文 */ private function getSemesterLabel(?int $semester): string { return match ($semester) { 1 => '上册', 2 => '下册', default => '', }; } /** * 中文学期转代码 */ private function convertSemesterToCode(string $semester): ?int { return match ($semester) { '上册', '1' => 1, '下册', '2' => 2, default => null, }; } /** * 年级标签 */ private function getGradeLabel(?int $grade, string $stage): string { if ($grade === null) { return ''; } return match ($stage) { 'primary' => $grade . '年级', 'junior' => match ($grade) { 7 => '七年级', 8 => '八年级', 9 => '九年级', default => $grade . '年级', }, 'senior' => match ($grade) { 10 => '高一', 11 => '高二', 12 => '高三', default => '高' . ($grade - 9), }, default => $grade . '年级', }; } /** * 根据年级判断学段 */ private function getStageByGrade(int $grade): string { if ($grade >= 1 && $grade <= 6) { return 'primary'; } elseif ($grade >= 7 && $grade <= 9) { return 'junior'; } else { return 'senior'; } } /** * 格式化学段数组 */ private function formatStages($stages): array { if (is_string($stages)) { $stages = json_decode($stages, true) ?? []; } if (!is_array($stages)) { return []; } return array_map(fn($s) => [ 'code' => $s, 'label' => $this->getStageLabel($s), ], $stages); } }