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.
291 lines
10 KiB
291 lines
10 KiB
<?php
|
|
|
|
namespace App\Support\Miniapp;
|
|
|
|
use App\Models\Activity;
|
|
use App\Models\ActivitySession;
|
|
use App\Models\Banner;
|
|
use App\Models\Course;
|
|
use App\Models\CourseMedia;
|
|
use App\Models\DictItem;
|
|
use App\Models\MiniappUser;
|
|
use App\Models\News;
|
|
use Carbon\Carbon;
|
|
|
|
class MiniappPresenter
|
|
{
|
|
public static function progressStatusLabel(int $status): string
|
|
{
|
|
return match ($status) {
|
|
1 => '未开始',
|
|
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,
|
|
];
|
|
}
|
|
}
|