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.

207 lines
7.1 KiB

<?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 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))
->values()
->all();
$canSignup = MiniappPresenter::activityDisplayStatus($model) === '报名中'
&& ! $row['has_signed_up']
&& collect($row['sessions'])->contains(fn (array $s) => ! $s['is_full']);
$row['can_signup'] = $canSignup;
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 (ActivitySignup::query()->where('activity_id', $activity)->where('miniapp_user_id', $user->id)->exists()) {
return $this->fail('您已报名该活动', 422);
}
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']);
$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)
: null;
return $row;
})
->filter()
->values()
->all();
return $this->ok(['items' => $signups]);
}
protected function publishedQuery(Request $request)
{
$query = Activity::query()
->with(['activityTypeItem'])
->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;
}
}