'未开始', 2 => '进行中', 3 => '已结束', default => '未开始', }; } public static function formatTimeValue(mixed $time): ?string { if ($time === null || $time === '') { return null; } $str = (string) $time; return strlen($str) >= 5 ? substr($str, 0, 5) : $str; } public static function timeRange(?string $start, ?string $end): ?string { $start = self::formatTimeValue($start); $end = self::formatTimeValue($end); if ($start && $end) { return "{$start}-{$end}"; } return $start ?: $end; } public static function serializeDictItem(?DictItem $item): ?array { if (! $item) { return null; } return [ 'id' => $item->id, 'label' => $item->label, 'value' => $item->value, ]; } public static function serializeCourseMedia(?CourseMedia $media, ?string $legacyUrl = null): ?array { if ($media) { return $media->toApiArray(); } if ($legacyUrl === null || $legacyUrl === '') { return null; } return [ 'id' => null, 'url' => $legacyUrl, 'category' => null, ]; } public static function isWithinSignupWindow(?string $start, ?string $end): bool { $today = now()->startOfDay(); if ($start) { $startDate = Carbon::parse($start)->startOfDay(); if ($today->lt($startDate)) { return false; } } if ($end) { $endDate = Carbon::parse($end)->startOfDay(); if ($today->gt($endDate)) { return false; } } return true; } public static function canSignupCourse(Course $course, int $signupsCount): bool { if ((int) $course->published !== 1) { return false; } if ((int) $course->progress_status === 3) { return false; } if (! self::isWithinSignupWindow( $course->signup_start_date?->toDateString(), $course->signup_end_date?->toDateString() )) { return false; } $capacity = (int) $course->capacity; if ($capacity > 0 && $signupsCount >= $capacity) { return false; } return true; } public static function activityDisplayStatus(Activity $activity): string { if (self::isWithinSignupWindow( $activity->signup_start_date?->toDateString(), $activity->signup_end_date?->toDateString() )) { return '报名中'; } return self::progressStatusLabel((int) $activity->progress_status); } public static function serializeCourseList(Course $course, ?MiniappUser $user = null): array { $signupsCount = (int) ($course->signups_count ?? 0); $mainSpeaker = is_array($course->main_speakers) ? ($course->main_speakers[0] ?? null) : null; $hasSignedUp = $user ? $course->signups()->where('miniapp_user_id', $user->id)->exists() : false; return [ 'id' => $course->id, 'title' => $course->title, 'category' => $course->courseSystemItem?->label, 'course_system_dict_item_id' => $course->course_system_dict_item_id, 'course_system_item' => self::serializeDictItem($course->courseSystemItem), 'teach_start_date' => $course->teach_start_date?->toDateString(), 'teach_end_date' => $course->teach_end_date?->toDateString(), 'teach_start_time' => self::formatTimeValue($course->teach_start_time), 'teach_end_time' => self::formatTimeValue($course->teach_end_time), 'time_range' => self::timeRange( self::formatTimeValue($course->teach_start_time), self::formatTimeValue($course->teach_end_time) ), 'location' => $course->location, 'teacher' => is_array($mainSpeaker) ? ($mainSpeaker['name'] ?? null) : null, 'university' => is_array($mainSpeaker) ? ($mainSpeaker['university'] ?? null) : null, 'main_speakers' => $course->main_speakers ?? [], 'recruit_targets' => $course->recruit_targets ?? [], 'signup_start_date' => $course->signup_start_date?->toDateString(), 'signup_end_date' => $course->signup_end_date?->toDateString(), 'capacity' => (int) $course->capacity, 'signups_count' => $signupsCount, 'progress_status' => (int) $course->progress_status, 'progress_status_label' => self::progressStatusLabel((int) $course->progress_status), 'intro_html' => $course->intro_html, 'cover' => self::serializeCourseMedia($course->coverMedia, $course->cover_url ?? null), 'promo' => self::serializeCourseMedia($course->promoMedia, $course->promo_url ?? null), 'can_signup' => self::canSignupCourse($course, $signupsCount) && ! $hasSignedUp, 'has_signed_up' => $hasSignedUp, ]; } public static function serializeActivityList(Activity $activity, ?MiniappUser $user = null): array { $hasSignedUp = $user ? $activity->signups()->where('miniapp_user_id', $user->id)->exists() : false; return [ 'id' => $activity->id, 'title' => $activity->title, 'activity_type_dict_item_id' => $activity->activity_type_dict_item_id, 'activity_type_item' => self::serializeDictItem($activity->activityTypeItem), 'event_start_date' => $activity->event_start_date?->toDateString(), 'event_end_date' => $activity->event_end_date?->toDateString(), 'signup_start_date' => $activity->signup_start_date?->toDateString(), 'signup_end_date' => $activity->signup_end_date?->toDateString(), 'location' => $activity->location, 'intro_html' => $activity->intro_html, 'progress_status' => (int) $activity->progress_status, 'progress_status_label' => self::progressStatusLabel((int) $activity->progress_status), 'display_status' => self::activityDisplayStatus($activity), 'signups_count' => (int) ($activity->signups_count ?? 0), 'has_signed_up' => $hasSignedUp, ]; } public static function serializeActivitySession(ActivitySession $session): array { $signed = $session->signups()->count(); $capacity = $session->capacity !== null ? (int) $session->capacity : 0; return [ 'id' => $session->id, 'title' => $session->title, 'date' => $session->starts_at?->toDateString(), 'time' => self::timeRange( $session->starts_at?->format('H:i'), $session->ends_at?->format('H:i') ), 'venue' => $session->venue, 'capacity' => $capacity, 'signed' => $signed, 'remaining' => $capacity > 0 ? max($capacity - $signed, 0) : null, 'is_full' => $capacity > 0 && $signed >= $capacity, ]; } public static function serializeBanner(Banner $banner): array { $target = match ($banner->type) { Banner::TYPE_COURSE => [ 'target_type' => 'course', 'target_id' => $banner->course_id, 'title' => $banner->course?->title, ], Banner::TYPE_ACTIVITY => [ 'target_type' => 'activity', 'target_id' => $banner->activity_id, 'title' => $banner->activity?->title, ], default => [ 'target_type' => 'custom', 'target_id' => $banner->id, 'title' => $banner->title, ], }; return [ 'id' => $banner->id, 'type' => $banner->type, 'title' => $banner->title ?: $target['title'], 'cover_url' => $banner->cover_url, 'content_html' => $banner->content_html, 'sort' => (int) $banner->sort, ...$target, ]; } public static function serializeNewsList(News $news): array { return [ 'id' => $news->id, 'title' => $news->title, 'tag' => $news->categoryItem?->label, 'category_dict_item_id' => $news->category_dict_item_id, 'category_item' => self::serializeDictItem($news->categoryItem), 'summary' => $news->summary, 'date' => $news->published_at?->toDateString(), 'published_at' => $news->published_at?->toIso8601String(), ]; } public static function serializeNewsDetail(News $news): array { $row = self::serializeNewsList($news); $row['content_html'] = $news->content_html; $row['content'] = strip_tags((string) $news->content_html); return $row; } public static function userPayload(MiniappUser $user): array { $user->loadMissing('teacher:id,is_partner,name'); return [ 'id' => $user->id, 'nickname' => $user->nickname, 'avatar_url' => $user->avatar_url, 'name' => $user->name, 'mobile' => $user->mobile, 'company' => $user->company, 'job_title' => null, 'research_direction' => null, 'is_partner' => (bool) ($user->teacher?->is_partner ?? false), 'teacher_id' => $user->teacher_id, ]; } }