|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\Miniapp;
|
|
|
|
|
|
|
|
|
|
use App\Http\Controllers\Miniapp\BaseController;
|
|
|
|
|
use App\Models\Activity;
|
|
|
|
|
use App\Models\ActivitySession;
|
|
|
|
|
use App\Models\ActivitySignup;
|
|
|
|
|
use App\Models\MiniappUser;
|
|
|
|
|
use App\Support\ApiResponse;
|
|
|
|
|
use App\Support\Miniapp\MiniappPresenter;
|
|
|
|
|
use App\Support\ScheduleProgressStatus;
|
|
|
|
|
use Illuminate\Http\JsonResponse;
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
|
|
|
|
|
class ActivityController extends BaseController
|
|
|
|
|
{
|
|
|
|
|
use ApiResponse;
|
|
|
|
|
|
|
|
|
|
public function index(Request $request): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$user = $this->optionalUser($request);
|
|
|
|
|
$pageSize = (int) $request->query('page_size', 20);
|
|
|
|
|
$page = max(1, (int) $request->query('page', 1));
|
|
|
|
|
|
|
|
|
|
if ($request->filled('progress_status')) {
|
|
|
|
|
$statusFilter = (int) $request->query('progress_status');
|
|
|
|
|
$activities = $this->publishedQuery($request)
|
|
|
|
|
->orderByDesc('event_start_date')
|
|
|
|
|
->orderByDesc('id')
|
|
|
|
|
->get()
|
|
|
|
|
->filter(fn (Activity $activity) => MiniappPresenter::resolveActivityProgressStatus($activity) === $statusFilter)
|
|
|
|
|
->values();
|
|
|
|
|
|
|
|
|
|
$total = $activities->count();
|
|
|
|
|
$items = $activities
|
|
|
|
|
->slice(($page - 1) * $pageSize, $pageSize)
|
|
|
|
|
->map(function (Activity $activity) use ($user) {
|
|
|
|
|
$row = MiniappPresenter::serializeActivityList($activity, $user);
|
|
|
|
|
$row['summary'] = strip_tags((string) $activity->intro_html);
|
|
|
|
|
|
|
|
|
|
return $row;
|
|
|
|
|
})
|
|
|
|
|
->values()
|
|
|
|
|
->all();
|
|
|
|
|
|
|
|
|
|
return $this->ok([
|
|
|
|
|
'items' => $items,
|
|
|
|
|
'meta' => [
|
|
|
|
|
'current_page' => $page,
|
|
|
|
|
'per_page' => $pageSize,
|
|
|
|
|
'total' => $total,
|
|
|
|
|
'last_page' => max(1, (int) ceil($total / $pageSize)),
|
|
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$paginator = $this->publishedQuery($request)
|
|
|
|
|
->orderByDesc('event_start_date')
|
|
|
|
|
->orderByDesc('id')
|
|
|
|
|
->paginate($pageSize)
|
|
|
|
|
->withQueryString();
|
|
|
|
|
|
|
|
|
|
$paginator->getCollection()->transform(function (Activity $activity) use ($user) {
|
|
|
|
|
$row = MiniappPresenter::serializeActivityList($activity, $user);
|
|
|
|
|
$row['summary'] = strip_tags((string) $activity->intro_html);
|
|
|
|
|
|
|
|
|
|
return $row;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return $this->paginated($paginator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function show(Request $request, int $activity): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$user = $this->optionalUser($request);
|
|
|
|
|
$model = Activity::query()
|
|
|
|
|
->with(['activityTypeItem', 'sessions' => fn ($q) => $q->orderBy('sort')->orderBy('id')])
|
|
|
|
|
->withCount('signups')
|
|
|
|
|
->where('published', 1)
|
|
|
|
|
->findOrFail($activity);
|
|
|
|
|
|
|
|
|
|
$row = MiniappPresenter::serializeActivityList($model, $user);
|
|
|
|
|
$row['summary'] = strip_tags((string) $model->intro_html);
|
|
|
|
|
$row['sessions'] = $model->sessions
|
|
|
|
|
->map(fn (ActivitySession $session) => MiniappPresenter::serializeActivitySession($session, $user))
|
|
|
|
|
->values()
|
|
|
|
|
->all();
|
|
|
|
|
|
|
|
|
|
return $this->ok($row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function signup(Request $request, int $activity): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
/** @var MiniappUser $user */
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
$model = Activity::query()->where('published', 1)->findOrFail($activity);
|
|
|
|
|
|
|
|
|
|
if (MiniappPresenter::activityDisplayStatus($model) !== '报名中') {
|
|
|
|
|
return $this->fail('当前不在报名期', 422);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $request->validate([
|
|
|
|
|
'activity_session_id' => ['required', 'integer', 'exists:activity_sessions,id'],
|
|
|
|
|
'name' => ['required', 'string', 'max:64'],
|
|
|
|
|
'mobile' => ['nullable', 'string', 'max:32'],
|
|
|
|
|
'company' => ['nullable', 'string', 'max:128'],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$session = ActivitySession::query()
|
|
|
|
|
->where('activity_id', $activity)
|
|
|
|
|
->findOrFail((int) $data['activity_session_id']);
|
|
|
|
|
|
|
|
|
|
if (ActivitySignup::query()
|
|
|
|
|
->where('activity_session_id', $session->id)
|
|
|
|
|
->where('miniapp_user_id', $user->id)
|
|
|
|
|
->exists()) {
|
|
|
|
|
return $this->fail('您已报名该场次', 422);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$signed = $session->signups()->count();
|
|
|
|
|
$capacity = (int) ($session->capacity ?? 0);
|
|
|
|
|
if ($capacity > 0 && $signed >= $capacity) {
|
|
|
|
|
return $this->fail('该场次名额已满', 422);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ActivitySignup::query()->create([
|
|
|
|
|
'activity_id' => $activity,
|
|
|
|
|
'activity_session_id' => $session->id,
|
|
|
|
|
'miniapp_user_id' => $user->id,
|
|
|
|
|
'name' => $data['name'],
|
|
|
|
|
'mobile' => $data['mobile'] ?? null,
|
|
|
|
|
'company' => $data['company'] ?? null,
|
|
|
|
|
'signed_up_at' => now(),
|
|
|
|
|
'status' => 1,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if (! $user->name) {
|
|
|
|
|
$user->name = $data['name'];
|
|
|
|
|
}
|
|
|
|
|
if (! $user->mobile && ! empty($data['mobile'])) {
|
|
|
|
|
$user->mobile = $data['mobile'];
|
|
|
|
|
}
|
|
|
|
|
if (! $user->company && ! empty($data['company'])) {
|
|
|
|
|
$user->company = $data['company'];
|
|
|
|
|
}
|
|
|
|
|
$user->save();
|
|
|
|
|
|
|
|
|
|
return $this->ok(null, '报名成功');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function mySignups(Request $request): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
/** @var MiniappUser $user */
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
|
|
|
|
|
$signups = ActivitySignup::query()
|
|
|
|
|
->with(['activity.activityTypeItem', 'session'])
|
|
|
|
|
->where('miniapp_user_id', $user->id)
|
|
|
|
|
->orderByDesc('id')
|
|
|
|
|
->get()
|
|
|
|
|
->map(function (ActivitySignup $signup) use ($user) {
|
|
|
|
|
$activity = $signup->activity;
|
|
|
|
|
if (! $activity) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$row = MiniappPresenter::serializeActivityList($activity, $user);
|
|
|
|
|
$row['summary'] = strip_tags((string) $activity->intro_html);
|
|
|
|
|
$row['session'] = $signup->session
|
|
|
|
|
? MiniappPresenter::serializeActivitySession($signup->session, $user)
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
return $row;
|
|
|
|
|
})
|
|
|
|
|
->filter()
|
|
|
|
|
->values()
|
|
|
|
|
->all();
|
|
|
|
|
|
|
|
|
|
return $this->ok(['items' => $signups]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function calendar(Request $request): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$data = $request->validate([
|
|
|
|
|
'month' => ['required', 'date_format:Y-m'],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
[$year, $month] = array_map('intval', explode('-', $data['month']));
|
|
|
|
|
$monthStart = sprintf('%04d-%02d-01', $year, $month);
|
|
|
|
|
$monthEnd = date('Y-m-t', strtotime($monthStart));
|
|
|
|
|
|
|
|
|
|
$sessions = ActivitySession::query()
|
|
|
|
|
->with('activity')
|
|
|
|
|
->whereHas('activity', fn ($q) => $q->where('published', 1))
|
|
|
|
|
->orderBy('starts_at')
|
|
|
|
|
->orderBy('id')
|
|
|
|
|
->get();
|
|
|
|
|
|
|
|
|
|
$events = $sessions->map(function (ActivitySession $session) {
|
|
|
|
|
$activity = $session->activity;
|
|
|
|
|
if (! $activity) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$startDate = $session->starts_at?->toDateString();
|
|
|
|
|
$endDate = $session->ends_at?->toDateString() ?: $startDate;
|
|
|
|
|
$progressStatus = $startDate
|
|
|
|
|
? ScheduleProgressStatus::resolve($startDate, $endDate)
|
|
|
|
|
: MiniappPresenter::resolveActivityProgressStatus($activity);
|
|
|
|
|
|
|
|
|
|
$sessionTitle = trim((string) $session->title);
|
|
|
|
|
$title = $sessionTitle !== ''
|
|
|
|
|
? $activity->title.' · '.$sessionTitle
|
|
|
|
|
: $activity->title;
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'id' => $session->id,
|
|
|
|
|
'activity_id' => $activity->id,
|
|
|
|
|
'title' => $title,
|
|
|
|
|
'start_date' => $startDate,
|
|
|
|
|
'end_date' => $endDate,
|
|
|
|
|
'time_range' => MiniappPresenter::timeRange(
|
|
|
|
|
$session->starts_at?->format('H:i'),
|
|
|
|
|
$session->ends_at?->format('H:i')
|
|
|
|
|
),
|
|
|
|
|
'location' => $session->venue ?: $activity->location,
|
|
|
|
|
'progress_status' => $progressStatus,
|
|
|
|
|
'progress_status_label' => MiniappPresenter::progressStatusLabel($progressStatus),
|
|
|
|
|
];
|
|
|
|
|
})->filter(function (?array $event) use ($monthStart, $monthEnd) {
|
|
|
|
|
if (! $event || ! $event['start_date']) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$start = $event['start_date'];
|
|
|
|
|
$end = $event['end_date'] ?: $start;
|
|
|
|
|
|
|
|
|
|
return $start <= $monthEnd && $end >= $monthStart;
|
|
|
|
|
})->values();
|
|
|
|
|
|
|
|
|
|
return $this->ok([
|
|
|
|
|
'month' => $data['month'],
|
|
|
|
|
'events' => $events,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function publishedQuery(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$query = Activity::query()
|
|
|
|
|
->with(['activityTypeItem', 'sessions'])
|
|
|
|
|
->withCount(['sessions', 'signups'])
|
|
|
|
|
->where('published', 1);
|
|
|
|
|
|
|
|
|
|
if ($kw = $request->query('keyword')) {
|
|
|
|
|
$query->where(function ($q) use ($kw) {
|
|
|
|
|
$q->where('title', 'like', "%{$kw}%")
|
|
|
|
|
->orWhere('location', 'like', "%{$kw}%")
|
|
|
|
|
->orWhere('intro_html', 'like', "%{$kw}%");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($request->filled('activity_type_dict_item_id')) {
|
|
|
|
|
$query->where('activity_type_dict_item_id', (int) $request->query('activity_type_dict_item_id'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $query;
|
|
|
|
|
}
|
|
|
|
|
}
|