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.
235 lines
7.4 KiB
235 lines
7.4 KiB
<?php
|
|
|
|
namespace App\Http\Controllers\Miniapp;
|
|
|
|
use App\Models\Activity;
|
|
use App\Models\ActivitySignup;
|
|
use App\Models\ActivitySignupCheckin;
|
|
use App\Models\Course;
|
|
use App\Models\CourseCheckinDay;
|
|
use App\Models\CourseSignup;
|
|
use App\Models\CourseSignupCheckin;
|
|
use App\Models\MiniappUser;
|
|
use App\Support\ApiResponse;
|
|
use App\Support\ActivitySigninCodeSync;
|
|
use App\Support\CourseCheckinDaySync;
|
|
use App\Support\Miniapp\MiniappPresenter;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
|
|
class CheckinController extends BaseController
|
|
{
|
|
use ApiResponse;
|
|
|
|
public function course(Request $request): JsonResponse
|
|
{
|
|
/** @var MiniappUser $user */
|
|
$user = $request->user();
|
|
|
|
$data = $request->validate([
|
|
'signin_code' => ['required', 'string', 'max:64'],
|
|
]);
|
|
|
|
[$day, $courseHint] = $this->resolveCourseCheckinDay($data['signin_code']);
|
|
|
|
if (! $day || ! $day->course) {
|
|
if ($courseHint === 'not_teach_day') {
|
|
return $this->fail('今日非该课程开课日,无法签到', 422);
|
|
}
|
|
|
|
return $this->fail('签到码无效', 422);
|
|
}
|
|
|
|
$signup = CourseSignup::query()
|
|
->where('course_id', $day->course_id)
|
|
->where('miniapp_user_id', $user->id)
|
|
->first();
|
|
|
|
if (! $signup) {
|
|
return $this->fail('您尚未报名该课程,无法签到', 422);
|
|
}
|
|
|
|
$checkinDate = $day->teach_date->toDateString();
|
|
$existing = CourseSignupCheckin::query()
|
|
->where('course_signup_id', $signup->id)
|
|
->whereDate('checkin_date', $checkinDate)
|
|
->whereNotNull('checked_in_at')
|
|
->first();
|
|
|
|
if ($existing) {
|
|
return $this->ok([
|
|
'type' => 'course',
|
|
'already_checked_in' => true,
|
|
'checked_in_at' => $existing->checked_in_at?->toIso8601String(),
|
|
'checkin_date' => $checkinDate,
|
|
'target' => MiniappPresenter::serializeCourseList($day->course, $user),
|
|
], '今日已签到');
|
|
}
|
|
|
|
$checkin = CourseSignupCheckin::query()->updateOrCreate(
|
|
[
|
|
'course_signup_id' => $signup->id,
|
|
'checkin_date' => $checkinDate,
|
|
],
|
|
['checked_in_at' => now()]
|
|
);
|
|
|
|
return $this->ok([
|
|
'type' => 'course',
|
|
'already_checked_in' => false,
|
|
'checked_in_at' => $checkin->checked_in_at?->toIso8601String(),
|
|
'checkin_date' => $checkinDate,
|
|
'target' => MiniappPresenter::serializeCourseList($day->course, $user),
|
|
], '签到成功');
|
|
}
|
|
|
|
public function activity(Request $request): JsonResponse
|
|
{
|
|
/** @var MiniappUser $user */
|
|
$user = $request->user();
|
|
|
|
$data = $request->validate([
|
|
'signin_code' => ['required', 'string', 'max:64'],
|
|
]);
|
|
|
|
$code = strtoupper(trim($data['signin_code']));
|
|
$activity = Activity::query()
|
|
->whereRaw('UPPER(code) = ?', [$code])
|
|
->first();
|
|
|
|
if (! $activity) {
|
|
$activity = Activity::query()
|
|
->get()
|
|
->first(fn (Activity $item) => strtoupper(ActivitySigninCodeSync::activitySigninCode($item)) === $code);
|
|
}
|
|
|
|
if (! $activity) {
|
|
return $this->fail('签到码无效', 422);
|
|
}
|
|
|
|
$activity->load(['activityTypeItem', 'sessions'])->loadCount('signups');
|
|
|
|
$signups = ActivitySignup::query()
|
|
->with('session')
|
|
->where('activity_id', $activity->id)
|
|
->where('miniapp_user_id', $user->id)
|
|
->get();
|
|
|
|
if ($signups->isEmpty()) {
|
|
return $this->fail('您尚未报名该活动,无法签到', 422);
|
|
}
|
|
|
|
$today = now()->toDateString();
|
|
$signup = $signups->first(
|
|
fn (ActivitySignup $item) => $item->session?->starts_at?->toDateString() === $today
|
|
) ?? ($signups->count() === 1 ? $signups->first() : null);
|
|
|
|
if (! $signup) {
|
|
return $this->fail('今日无您报名的场次,无法签到', 422);
|
|
}
|
|
|
|
if (! $signup->activity_session_id) {
|
|
return $this->fail('报名记录缺少场次信息', 422);
|
|
}
|
|
|
|
$existing = ActivitySignupCheckin::query()
|
|
->where('activity_signup_id', $signup->id)
|
|
->where('activity_session_id', $signup->activity_session_id)
|
|
->whereNotNull('checked_in_at')
|
|
->first();
|
|
|
|
$target = MiniappPresenter::serializeActivityList($activity, $user);
|
|
$target['summary'] = strip_tags((string) $activity->intro_html);
|
|
|
|
if ($existing) {
|
|
return $this->ok([
|
|
'type' => 'activity',
|
|
'already_checked_in' => true,
|
|
'checked_in_at' => $existing->checked_in_at?->toIso8601String(),
|
|
'target' => $target,
|
|
'session' => $signup->session
|
|
? MiniappPresenter::serializeActivitySession($signup->session, $user)
|
|
: null,
|
|
], '已签到');
|
|
}
|
|
|
|
$checkin = ActivitySignupCheckin::query()->updateOrCreate(
|
|
[
|
|
'activity_signup_id' => $signup->id,
|
|
'activity_session_id' => $signup->activity_session_id,
|
|
],
|
|
['checked_in_at' => now()]
|
|
);
|
|
|
|
return $this->ok([
|
|
'type' => 'activity',
|
|
'already_checked_in' => false,
|
|
'checked_in_at' => $checkin->checked_in_at?->toIso8601String(),
|
|
'target' => $target,
|
|
'session' => $signup->session
|
|
? MiniappPresenter::serializeActivitySession($signup->session, $user)
|
|
: null,
|
|
], '签到成功');
|
|
}
|
|
|
|
/**
|
|
* @return array{0: ?CourseCheckinDay, 1: ?string}
|
|
*/
|
|
protected function resolveCourseCheckinDay(string $signinCode): array
|
|
{
|
|
$withCourse = [
|
|
'course' => fn ($q) => $q
|
|
->with(['courseSystemItem', 'coverMedia', 'promoMedia'])
|
|
->withCount('signups'),
|
|
];
|
|
|
|
$day = CourseCheckinDay::query()
|
|
->with($withCourse)
|
|
->where('signin_code', trim($signinCode))
|
|
->first();
|
|
|
|
if ($day) {
|
|
return [$day, null];
|
|
}
|
|
|
|
$course = $this->resolveCourseBySigninCode($signinCode);
|
|
if (! $course) {
|
|
return [null, null];
|
|
}
|
|
|
|
$today = now()->toDateString();
|
|
if (! CourseCheckinDaySync::isTeachDate($course, $today)) {
|
|
return [null, 'not_teach_day'];
|
|
}
|
|
|
|
$day = CourseCheckinDay::query()
|
|
->with($withCourse)
|
|
->where('course_id', $course->id)
|
|
->whereDate('teach_date', $today)
|
|
->first();
|
|
|
|
return [$day, null];
|
|
}
|
|
|
|
protected function resolveCourseBySigninCode(string $signinCode): ?Course
|
|
{
|
|
$code = strtoupper(trim($signinCode));
|
|
if ($code === '') {
|
|
return null;
|
|
}
|
|
|
|
$course = Course::query()->whereRaw('UPPER(code) = ?', [$code])->first();
|
|
if ($course) {
|
|
return $course;
|
|
}
|
|
|
|
if (preg_match('/^COURSE-(\d+)$/', $code, $matches)) {
|
|
return Course::query()->find((int) $matches[1]);
|
|
}
|
|
|
|
return Course::query()
|
|
->get()
|
|
->first(fn (Course $item) => strtoupper(CourseCheckinDaySync::courseSigninCode($item)) === $code);
|
|
}
|
|
}
|