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.

218 lines
8.7 KiB

4 days ago
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Venue;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
2 days ago
use Illuminate\Validation\Rule;
4 days ago
class VenueController extends Controller
{
public function store(Request $request): JsonResponse
{
2 days ago
$user = $request->user();
abort_unless($user && ($user->isSuperAdmin() || $user->role === 'venue_admin'), 403, '无权限');
4 days ago
$data = $request->validate([
'name' => ['required', 'string', 'max:120'],
'venue_type' => ['nullable', 'string', 'max:80'],
3 days ago
'venue_types' => ['nullable', 'array'],
'venue_types.*' => ['string', 'max:80'],
4 days ago
'unit_name' => ['nullable', 'string', 'max:120'],
'district' => ['nullable', 'string', 'max:80'],
'ticket_type' => ['nullable', 'string', 'max:80'],
3 days ago
'appointment_type' => ['nullable', 'string', 'max:40'],
2 days ago
'open_mode' => ['nullable', 'string', 'max:40', Rule::in(['fulltime', 'scheduled', 'appointment'])],
4 days ago
'open_time' => ['nullable', 'string', 'max:120'],
'reservation_notice' => ['nullable', 'string'],
1 day ago
'ticket_content' => ['nullable', 'string'],
'booking_method' => ['nullable', 'string'],
'visit_form' => ['nullable', 'string'],
'consultation_hours' => ['nullable', 'string'],
4 days ago
'address' => ['nullable', 'string', 'max:255'],
3 days ago
'contact_phone' => ['nullable', 'string', 'max:20'],
4 days ago
'lat' => ['nullable', 'numeric'],
'lng' => ['nullable', 'numeric'],
'cover_image' => ['nullable', 'string', 'max:255'],
'gallery_media' => ['nullable', 'array'],
'gallery_media.*.type' => ['required_with:gallery_media', 'in:image,video'],
'gallery_media.*.url' => ['required_with:gallery_media', 'string', 'max:255'],
'detail_html' => ['nullable', 'string'],
'live_people_count' => ['nullable', 'integer', 'min:0'],
'sort' => ['nullable', 'integer', 'min:0'],
'is_active' => ['boolean'],
]);
2 days ago
$auditStatus = $user->isSuperAdmin() ? Venue::AUDIT_APPROVED : Venue::AUDIT_PENDING;
$venue = Venue::create($data + [
'is_active' => $data['is_active'] ?? true,
'audit_status' => $auditStatus,
'audit_remark' => null,
'last_approved_snapshot' => null,
]);
if (! $user->isSuperAdmin()) {
$user->venues()->syncWithoutDetaching([$venue->id]);
}
return response()->json($venue->fresh(), 201);
4 days ago
}
public function index(Request $request): JsonResponse
{
$user = $request->user();
$keyword = trim((string) $request->string('keyword'));
$district = trim((string) $request->string('district'));
$venueType = trim((string) $request->string('venue_type'));
$ticketType = trim((string) $request->string('ticket_type'));
2 days ago
$openMode = trim((string) $request->string('open_mode'));
4 days ago
$isActive = $request->input('is_active');
2 days ago
$auditStatus = trim((string) $request->string('audit_status'));
4 days ago
if ($user->isSuperAdmin()) {
$query = Venue::query();
} else {
$query = $user->venues();
}
if ($keyword !== '') {
$query->where(function ($q) use ($keyword) {
$q->where('name', 'like', '%' . $keyword . '%')
->orWhere('address', 'like', '%' . $keyword . '%')
->orWhere('unit_name', 'like', '%' . $keyword . '%')
->orWhere('open_time', 'like', '%' . $keyword . '%')
->orWhere('reservation_notice', 'like', '%' . $keyword . '%')
1 day ago
->orWhere('ticket_content', 'like', '%' . $keyword . '%')
->orWhere('booking_method', 'like', '%' . $keyword . '%')
->orWhere('visit_form', 'like', '%' . $keyword . '%')
->orWhere('consultation_hours', 'like', '%' . $keyword . '%');
4 days ago
});
}
if ($district !== '') {
$query->where('district', $district);
}
if ($venueType !== '') {
3 days ago
$query->where(function ($q) use ($venueType) {
$q->where('venue_type', $venueType)
->orWhereJsonContains('venue_types', $venueType);
});
4 days ago
}
if ($ticketType !== '') {
$query->where('ticket_type', $ticketType);
}
2 days ago
if ($openMode !== '') {
$query->where('open_mode', $openMode);
}
4 days ago
if ($isActive !== null && $isActive !== '') {
$query->where('is_active', (int) $isActive === 1);
}
2 days ago
if ($auditStatus !== '') {
$query->where('audit_status', $auditStatus);
}
4 days ago
$venues = $query->orderBy('venues.sort')->orderByDesc('venues.id')->get();
return response()->json($venues);
}
public function update(Request $request, int $id): JsonResponse
{
$venue = Venue::query()->findOrFail($id);
$this->ensureVenuePermission($request, $venue->id);
2 days ago
$user = $request->user();
4 days ago
$data = $request->validate([
'name' => ['sometimes', 'string', 'max:120'],
'venue_type' => ['nullable', 'string', 'max:80'],
3 days ago
'venue_types' => ['nullable', 'array'],
'venue_types.*' => ['string', 'max:80'],
4 days ago
'unit_name' => ['nullable', 'string', 'max:120'],
'district' => ['nullable', 'string', 'max:80'],
'ticket_type' => ['nullable', 'string', 'max:80'],
3 days ago
'appointment_type' => ['nullable', 'string', 'max:40'],
2 days ago
'open_mode' => ['nullable', 'string', 'max:40', Rule::in(['fulltime', 'scheduled', 'appointment'])],
4 days ago
'open_time' => ['nullable', 'string', 'max:120'],
'reservation_notice' => ['nullable', 'string'],
1 day ago
'ticket_content' => ['nullable', 'string'],
'booking_method' => ['nullable', 'string'],
'visit_form' => ['nullable', 'string'],
'consultation_hours' => ['nullable', 'string'],
4 days ago
'address' => ['nullable', 'string', 'max:255'],
3 days ago
'contact_phone' => ['nullable', 'string', 'max:20'],
4 days ago
'lat' => ['nullable', 'numeric'],
'lng' => ['nullable', 'numeric'],
'cover_image' => ['nullable', 'string', 'max:255'],
'gallery_media' => ['nullable', 'array'],
'gallery_media.*.type' => ['required_with:gallery_media', 'in:image,video'],
'gallery_media.*.url' => ['required_with:gallery_media', 'string', 'max:255'],
'detail_html' => ['nullable', 'string'],
'live_people_count' => ['nullable', 'integer', 'min:0'],
'sort' => ['nullable', 'integer', 'min:0'],
'is_active' => ['boolean'],
]);
2 days ago
if (! $user->isSuperAdmin()) {
4 days ago
unset($data['sort']);
2 days ago
if ($venue->audit_status === Venue::AUDIT_APPROVED) {
$venue->last_approved_snapshot = $venue->buildAuditSnapshot();
}
$data['audit_status'] = Venue::AUDIT_PENDING;
$data['audit_remark'] = null;
} else {
$data['audit_status'] = Venue::AUDIT_APPROVED;
$data['audit_remark'] = null;
$data['last_approved_snapshot'] = null;
4 days ago
}
$venue->fill($data)->save();
2 days ago
return response()->json($venue->fresh());
}
public function approve(Request $request, int $id): JsonResponse
{
abort_unless($request->user()?->isSuperAdmin(), 403, '仅超级管理员可审核');
$venue = Venue::query()->findOrFail($id);
$venue->audit_status = Venue::AUDIT_APPROVED;
$venue->audit_remark = null;
$venue->last_approved_snapshot = null;
$venue->save();
return response()->json($venue->fresh());
}
public function reject(Request $request, int $id): JsonResponse
{
abort_unless($request->user()?->isSuperAdmin(), 403, '仅超级管理员可审核');
$data = $request->validate([
'remark' => ['nullable', 'string', 'max:2000'],
]);
$venue = Venue::query()->findOrFail($id);
$venue->audit_status = Venue::AUDIT_REJECTED;
$venue->audit_remark = $data['remark'] ?? null;
$venue->save();
return response()->json($venue->fresh());
4 days ago
}
private function ensureVenuePermission(Request $request, int $venueId): void
{
$user = $request->user();
if ($user->isSuperAdmin()) {
return;
}
$allowed = $user->venues()->where('venues.id', $venueId)->exists();
abort_unless($allowed, 403, '仅可操作已绑定场馆');
}
}