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

2 weeks ago
<?php
namespace App\Http\Controllers\Miniapp;
use App\Models\Activity;
use App\Models\ActivitySignup;
use App\Models\ActivitySignupCheckin;
6 days ago
use App\Models\Course;
2 weeks ago
use App\Models\CourseCheckinDay;
use App\Models\CourseSignup;
use App\Models\CourseSignupCheckin;
use App\Models\MiniappUser;
use App\Support\ApiResponse;
use App\Support\ActivitySigninCodeSync;
6 days ago
use App\Support\CourseCheckinDaySync;
2 weeks ago
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'],
]);
6 days ago
[$day, $courseHint] = $this->resolveCourseCheckinDay($data['signin_code']);
2 weeks ago
if (! $day || ! $day->course) {
6 days ago
if ($courseHint === 'not_teach_day') {
return $this->fail('今日非该课程开课日,无法签到', 422);
}
2 weeks ago
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,
6 days ago
'checked_in_at' => $existing->checked_in_at?->toIso8601String(),
2 weeks ago
'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,
6 days ago
'checked_in_at' => $checkin->checked_in_at?->toIso8601String(),
2 weeks ago
'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');
6 days ago
$signups = ActivitySignup::query()
2 weeks ago
->with('session')
->where('activity_id', $activity->id)
->where('miniapp_user_id', $user->id)
6 days ago
->get();
2 weeks ago
6 days ago
if ($signups->isEmpty()) {
2 weeks ago
return $this->fail('您尚未报名该活动,无法签到', 422);
}
6 days ago
$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);
}
2 weeks ago
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,
6 days ago
'checked_in_at' => $existing->checked_in_at?->toIso8601String(),
2 weeks ago
'target' => $target,
'session' => $signup->session
6 days ago
? MiniappPresenter::serializeActivitySession($signup->session, $user)
2 weeks ago
: 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,
6 days ago
'checked_in_at' => $checkin->checked_in_at?->toIso8601String(),
2 weeks ago
'target' => $target,
'session' => $signup->session
6 days ago
? MiniappPresenter::serializeActivitySession($signup->session, $user)
2 weeks ago
: null,
], '签到成功');
}
6 days ago
/**
* @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);
}
2 weeks ago
}