with(['course', 'activity', 'news']); if ($request->filled('type')) { $query->where('type', $request->query('type')); } if ($request->filled('status')) { $query->where('status', (int) $request->query('status')); } if ($kw = $request->query('keyword')) { $query->where(function ($q) use ($kw) { $q->where('title', 'like', "%{$kw}%") ->orWhereHas('course', fn ($cq) => $cq->where('title', 'like', "%{$kw}%")) ->orWhereHas('activity', fn ($aq) => $aq->where('title', 'like', "%{$kw}%")) ->orWhereHas('news', fn ($nq) => $nq->where('title', 'like', "%{$kw}%")); }); } $paginator = $query ->orderBy('sort') ->orderByDesc('id') ->paginate((int) $request->query('page_size', 20)) ->withQueryString(); $paginator->getCollection()->transform(fn (Banner $b) => $this->serialize($b)); return $this->paginated($paginator); } public function show(int $banner): JsonResponse { $model = Banner::query()->with(['course', 'activity', 'news'])->findOrFail($banner); return $this->ok($this->serialize($model)); } public function store(Request $request): JsonResponse { $data = $this->validatedBanner($request); $row = Banner::query()->create($data); return $this->ok(['id' => $row->id], '已创建'); } public function update(Request $request, int $banner): JsonResponse { $model = Banner::query()->findOrFail($banner); $data = $this->validatedBanner($request, $model); $model->fill($data); $model->save(); return $this->ok($this->serialize($model->fresh(['course', 'activity', 'news'])), '已保存'); } public function destroy(int $banner): JsonResponse { Banner::query()->findOrFail($banner)->delete(); return $this->ok(null, '已删除'); } /** * @return array */ protected function validatedBanner(Request $request, ?Banner $existing = null): array { $base = $request->validate([ 'type' => array_merge( [$existing ? 'sometimes' : 'required', 'string'], [Rule::in([Banner::TYPE_COURSE, Banner::TYPE_ACTIVITY, Banner::TYPE_NEWS, Banner::TYPE_CUSTOM])] ), 'course_id' => ['nullable', 'integer', 'exists:courses,id'], 'activity_id' => ['nullable', 'integer', 'exists:activities,id'], 'news_id' => ['nullable', 'integer', 'exists:news,id'], 'title' => ['nullable', 'string', 'max:255'], 'cover_url' => ['nullable', 'string', 'max:512'], 'content_html' => ['nullable', 'string'], 'sort' => [$existing ? 'sometimes' : 'required', 'integer', 'min:0'], 'status' => ['sometimes', 'integer', 'in:0,1'], ]); $type = $base['type'] ?? $existing?->type; if (! $type) { throw ValidationException::withMessages(['type' => ['请选择 Banner 类型']]); } $payload = [ 'type' => $type, 'course_id' => null, 'activity_id' => null, 'news_id' => null, 'title' => null, 'cover_url' => null, 'content_html' => null, 'sort' => isset($base['sort']) ? (int) $base['sort'] : ($existing?->sort ?? 0), 'status' => isset($base['status']) ? (int) $base['status'] : ($existing?->status ?? 1), ]; match ($type) { Banner::TYPE_COURSE => $this->fillCourse($payload, $base, $existing), Banner::TYPE_ACTIVITY => $this->fillActivity($payload, $base, $existing), Banner::TYPE_NEWS => $this->fillNews($payload, $base, $existing), Banner::TYPE_CUSTOM => $this->fillCustom($payload, $base, $existing), }; return $payload; } /** * @param array $payload * @param array $data */ protected function fillCourse(array &$payload, array $data, ?Banner $existing): void { $courseId = $data['course_id'] ?? $existing?->course_id; if (! $courseId) { throw ValidationException::withMessages(['course_id' => ['请选择课程']]); } if (! Course::query()->whereKey($courseId)->exists()) { throw ValidationException::withMessages(['course_id' => ['所选课程不存在']]); } $payload['course_id'] = (int) $courseId; } /** * @param array $payload * @param array $data */ protected function fillActivity(array &$payload, array $data, ?Banner $existing): void { $activityId = $data['activity_id'] ?? $existing?->activity_id; if (! $activityId) { throw ValidationException::withMessages(['activity_id' => ['请选择活动']]); } if (! Activity::query()->whereKey($activityId)->exists()) { throw ValidationException::withMessages(['activity_id' => ['所选活动不存在']]); } $payload['activity_id'] = (int) $activityId; } /** * @param array $payload * @param array $data */ protected function fillNews(array &$payload, array $data, ?Banner $existing): void { $newsId = $data['news_id'] ?? $existing?->news_id; if (! $newsId) { throw ValidationException::withMessages(['news_id' => ['请选择资讯']]); } $news = News::query()->whereKey($newsId)->first(); if (! $news) { throw ValidationException::withMessages(['news_id' => ['所选资讯不存在']]); } if ((int) $news->status !== 1) { throw ValidationException::withMessages(['news_id' => ['只能选择已发布的资讯']]); } $payload['news_id'] = (int) $newsId; } /** * @param array $payload * @param array $data */ protected function fillCustom(array &$payload, array $data, ?Banner $existing): void { $title = array_key_exists('title', $data) ? trim((string) $data['title']) : ($existing?->title ?? ''); $cover = array_key_exists('cover_url', $data) ? trim((string) $data['cover_url']) : ($existing?->cover_url ?? ''); $content = array_key_exists('content_html', $data) ? (string) $data['content_html'] : ($existing?->content_html ?? ''); if ($title === '') { throw ValidationException::withMessages(['title' => ['请填写标题']]); } if ($cover === '') { throw ValidationException::withMessages(['cover_url' => ['请上传封面图']]); } $payload['title'] = $title; $payload['cover_url'] = $cover; $payload['content_html'] = trim(strip_tags($content)) === '' ? null : $content; } /** * @return array */ protected function serialize(Banner $b): array { $summary = match ($b->type) { Banner::TYPE_COURSE => $b->course?->title, Banner::TYPE_ACTIVITY => $b->activity?->title, Banner::TYPE_NEWS => $b->news?->title, Banner::TYPE_CUSTOM => $b->title, default => null, }; return [ 'id' => $b->id, 'type' => $b->type, 'type_label' => Banner::typeLabel($b->type), 'course_id' => $b->course_id, 'course_title' => $b->course?->title, 'activity_id' => $b->activity_id, 'activity_title' => $b->activity?->title, 'news_id' => $b->news_id, 'news_title' => $b->news?->title, 'title' => $b->title, 'cover_url' => $b->cover_url, 'content_html' => $b->content_html, 'summary' => $summary, 'sort' => (int) $b->sort, 'status' => (int) $b->status, 'created_at' => $b->created_at?->toIso8601String(), 'updated_at' => $b->updated_at?->toIso8601String(), ]; } }