You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
8.6 KiB

2 weeks ago
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Activity;
use App\Models\Banner;
use App\Models\Course;
4 days ago
use App\Models\News;
2 weeks ago
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class BannerController extends Controller
{
use ApiResponse;
public function index(Request $request): JsonResponse
{
4 days ago
$query = Banner::query()->with(['course', 'activity', 'news']);
2 weeks ago
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}%"))
4 days ago
->orWhereHas('activity', fn ($aq) => $aq->where('title', 'like', "%{$kw}%"))
->orWhereHas('news', fn ($nq) => $nq->where('title', 'like', "%{$kw}%"));
2 weeks ago
});
}
$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
{
4 days ago
$model = Banner::query()->with(['course', 'activity', 'news'])->findOrFail($banner);
2 weeks ago
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();
4 days ago
return $this->ok($this->serialize($model->fresh(['course', 'activity', 'news'])), '已保存');
2 weeks ago
}
public function destroy(int $banner): JsonResponse
{
Banner::query()->findOrFail($banner)->delete();
return $this->ok(null, '已删除');
}
/**
* @return array<string, mixed>
*/
protected function validatedBanner(Request $request, ?Banner $existing = null): array
{
$base = $request->validate([
'type' => array_merge(
[$existing ? 'sometimes' : 'required', 'string'],
4 days ago
[Rule::in([Banner::TYPE_COURSE, Banner::TYPE_ACTIVITY, Banner::TYPE_NEWS, Banner::TYPE_CUSTOM])]
2 weeks ago
),
'course_id' => ['nullable', 'integer', 'exists:courses,id'],
'activity_id' => ['nullable', 'integer', 'exists:activities,id'],
4 days ago
'news_id' => ['nullable', 'integer', 'exists:news,id'],
2 weeks ago
'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,
4 days ago
'news_id' => null,
2 weeks ago
'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),
4 days ago
Banner::TYPE_NEWS => $this->fillNews($payload, $base, $existing),
2 weeks ago
Banner::TYPE_CUSTOM => $this->fillCustom($payload, $base, $existing),
};
return $payload;
}
/**
* @param array<string, mixed> $payload
* @param array<string, mixed> $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<string, mixed> $payload
* @param array<string, mixed> $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;
}
4 days ago
/**
* @param array<string, mixed> $payload
* @param array<string, mixed> $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;
}
2 weeks ago
/**
* @param array<string, mixed> $payload
* @param array<string, mixed> $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<string, mixed>
*/
protected function serialize(Banner $b): array
{
$summary = match ($b->type) {
Banner::TYPE_COURSE => $b->course?->title,
Banner::TYPE_ACTIVITY => $b->activity?->title,
4 days ago
Banner::TYPE_NEWS => $b->news?->title,
2 weeks ago
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,
4 days ago
'news_id' => $b->news_id,
'news_title' => $b->news?->title,
2 weeks ago
'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(),
];
}
}