seedKioskKv(); $this->seedKnowledge(); $this->seedObservatoryHistory(); $this->seedRealtimeImages(); $this->seedObservationStatus(); $this->seedObservationDaily(); } protected function seedKioskKv(): void { $now = date('Y-m-d H:i:s'); foreach ([ KioskDefaults::KEY_HOME_BG => KioskDefaults::homeBackgrounds(), KioskDefaults::KEY_CAROUSEL_BG => KioskDefaults::carouselBackgrounds(), KioskDefaults::KEY_WELCOME => KioskDefaults::welcome(), KioskDefaults::KEY_GUIDE => KioskDefaults::guide(), KioskDefaults::KEY_VIDEO_SOURCE => KioskDefaults::videoSource(), ] as $key => $val) { $exists = Db::name('kiosk_kv')->where('config_key', $key)->find(); if (!$exists) { Db::name('kiosk_kv')->insert([ 'config_key' => $key, 'config_value' => json_encode($val, JSON_UNESCAPED_UNICODE), 'updated_at' => $now, ]); } } } protected function seedKnowledge(): void { if (Db::name('knowledge')->count() > 0) { return; } $now = date('Y-m-d H:i:s'); $order = 0; foreach (KioskDefaults::knowledge() as $row) { Db::name('knowledge')->insert([ 'id' => $row['id'], 'type' => $row['type'], 'title' => $row['title'], 'content' => $row['content'] ?? '', 'entry_date' => $row['date'] ?? null, 'tags' => json_encode($row['tags'] ?? [], JSON_UNESCAPED_UNICODE), 'image' => $row['image'] ?? null, 'video_url' => $row['videoUrl'] ?? null, 'sort_order' => $order++, 'created_at' => $now, 'updated_at' => $now, ]); } } protected function seedObservatoryHistory(): void { if (Db::name('observatory_history')->count() > 0) { return; } $order = 0; foreach (KioskDefaults::observatoryHistory() as $row) { Db::name('observatory_history')->insert([ 'kind' => $row['kind'], 'title' => $row['title'], 'summary' => $row['summary'], 'date_str' => $row['date_str'], 'thumb' => $row['thumb'], 'sort_order' => $order++, ]); } } protected function seedRealtimeImages(): void { if (Db::name('realtime_image')->count() > 0) { return; } $order = 0; foreach (KioskDefaults::realtimeImages() as $row) { Db::name('realtime_image')->insert([ 'name' => $row['name'], 'time_str' => $row['time_str'], 'telescope' => $row['telescope'], 'exposure' => $row['exposure'], 'image_url' => $row['image_url'], 'sort_order' => $order++, ]); } } protected function seedObservationStatus(): void { $row = Db::name('observation_status')->where('id', 1)->find(); if ($row) { return; } $s = KioskDefaults::observationStatus(); Db::name('observation_status')->insert([ 'id' => 1, 'weather_label' => $s['weather_label'], 'seeing' => $s['seeing'], 'transparency' => $s['transparency'], 'moon_phase' => $s['moon_phase'], ]); } protected function seedObservationDaily(): void { if (Db::name('observation_daily')->count() > 0) { return; } foreach (KioskDefaults::observationDaily() as $row) { Db::name('observation_daily')->insert([ 'record_date' => $row['record_date'], 'observations' => $row['observations'], 'quality' => $row['quality'], 'weather' => $row['weather'], ]); } } public function getJsonKv(string $key, mixed $default): mixed { $row = Db::name('kiosk_kv')->where('config_key', $key)->find(); if (!$row || $row['config_value'] === null || $row['config_value'] === '') { return $default; } $decoded = json_decode((string) $row['config_value'], true); return is_array($decoded) ? $decoded : $default; } public function setJsonKv(string $key, mixed $data): void { $now = date('Y-m-d H:i:s'); $json = json_encode($data, JSON_UNESCAPED_UNICODE); $exists = Db::name('kiosk_kv')->where('config_key', $key)->find(); if ($exists) { Db::name('kiosk_kv')->where('config_key', $key)->update([ 'config_value' => $json, 'updated_at' => $now, ]); } else { Db::name('kiosk_kv')->insert([ 'config_key' => $key, 'config_value' => $json, 'updated_at' => $now, ]); } } public function getKnowledgeList(): array { $rows = Db::name('knowledge')->order('sort_order', 'asc')->order('entry_date', 'desc')->select()->toArray(); $out = []; foreach ($rows as $r) { $tags = $r['tags']; if (is_string($tags)) { $tags = json_decode($tags, true) ?: []; } if (!is_array($tags)) { $tags = []; } $out[] = [ 'id' => $r['id'], 'type' => $r['type'], 'title' => $r['title'], 'content' => $r['content'] ?? '', 'date' => $r['entry_date'] ? substr((string) $r['entry_date'], 0, 10) : '', 'tags' => $tags, 'image' => $r['image'] ?: null, 'videoUrl' => $r['video_url'] ?: null, ]; } return $out; } public function replaceKnowledge(array $entries): void { Db::startTrans(); try { Db::name('knowledge')->delete(true); $now = date('Y-m-d H:i:s'); $order = 0; foreach ($entries as $e) { if (empty($e['id']) || empty($e['title'])) { continue; } $tags = $e['tags'] ?? []; if (!is_array($tags)) { $tags = []; } $tags = array_values(array_filter($tags, fn ($x) => is_string($x) && $x !== '')); $entryDate = $this->normalizeKnowledgeDate($e['date'] ?? null); $type = $this->normalizeKnowledgeType($e['type'] ?? '文字'); $image = isset($e['image']) && is_string($e['image']) && $e['image'] !== '' ? $e['image'] : null; $videoUrl = isset($e['videoUrl']) && is_string($e['videoUrl']) && $e['videoUrl'] !== '' ? $e['videoUrl'] : null; Db::name('knowledge')->insert([ 'id' => (string) $e['id'], 'type' => $type, 'title' => (string) $e['title'], 'content' => (string) ($e['content'] ?? ''), 'entry_date' => $entryDate, 'tags' => json_encode($tags, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR), 'image' => $image, 'video_url' => $videoUrl, 'sort_order' => $order++, 'created_at' => $now, 'updated_at' => $now, ]); } Db::commit(); } catch (\Throwable $ex) { Db::rollback(); throw $ex; } } public function getBundle(): array { $this->ensureSeed(); $home = $this->getJsonKv(KioskDefaults::KEY_HOME_BG, KioskDefaults::homeBackgrounds()); if (!is_array($home) || $home === []) { $home = KioskDefaults::homeBackgrounds(); } $welcome = $this->getJsonKv(KioskDefaults::KEY_WELCOME, KioskDefaults::welcome()); if (!is_array($welcome)) { $welcome = KioskDefaults::welcome(); } $guide = $this->getJsonKv(KioskDefaults::KEY_GUIDE, []); if (!is_array($guide)) { $guide = []; } $carousel = $this->getJsonKv(KioskDefaults::KEY_CAROUSEL_BG, KioskDefaults::carouselBackgrounds()); if (!is_array($carousel) || $carousel === []) { $carousel = KioskDefaults::carouselBackgrounds(); } $video = $this->getJsonKv(KioskDefaults::KEY_VIDEO_SOURCE, KioskDefaults::videoSource()); if (!is_array($video)) { $video = KioskDefaults::videoSource(); } $proto = (string) ($video['protocol'] ?? 'HLS'); if (!in_array($proto, ['HLS', 'WebRTC', 'RTSP'], true)) { $proto = 'HLS'; } return [ 'homeBackgrounds' => array_values(array_filter($home, fn ($u) => is_string($u) && $u !== '')), 'carouselBackgrounds' => array_values(array_filter($carousel, fn ($u) => is_string($u) && $u !== '')), 'welcome' => [ 'zh-CN' => (string) ($welcome['zh-CN'] ?? KioskDefaults::welcome()['zh-CN']), 'en' => (string) ($welcome['en'] ?? KioskDefaults::welcome()['en']), 'bo' => (string) ($welcome['bo'] ?? KioskDefaults::welcome()['bo']), ], 'guide' => $guide, 'knowledge' => $this->getKnowledgeList(), 'videoSource' => [ 'protocol' => $proto, 'url' => (string) ($video['url'] ?? ''), 'note' => (string) ($video['note'] ?? ''), ], ]; } public function getDataDisplay(): array { $this->ensureSeed(); $imgs = Db::name('realtime_image')->order('sort_order', 'asc')->select()->toArray(); $realtime = []; foreach ($imgs as $r) { $realtime[] = [ 'id' => (int) $r['id'], 'name' => $r['name'], 'time' => $r['time_str'], 'telescope' => $r['telescope'], 'exposure' => $r['exposure'], 'image' => $r['image_url'], ]; } $st = Db::name('observation_status')->where('id', 1)->find(); if (!$st) { $st = KioskDefaults::observationStatus(); } $status = [ 'weather' => (string) ($st['weather_label'] ?? '—'), 'seeing' => (string) ($st['seeing'] ?? '—'), 'transparency' => (string) ($st['transparency'] ?? '—'), 'moonPhase' => (string) ($st['moon_phase'] ?? '—'), ]; $dailyRows = Db::name('observation_daily')->order('record_date', 'desc')->limit(30)->select()->toArray(); $historical = []; foreach ($dailyRows as $d) { $historical[] = [ 'date' => substr((string) $d['record_date'], 0, 10), 'observations' => (int) $d['observations'], 'quality' => (string) $d['quality'], 'weather' => (string) $d['weather'], ]; } return [ 'realtimeImages' => $realtime, 'status' => $status, 'historicalData' => $historical, ]; } public function getObservatoryHistory(): array { $this->ensureSeed(); $rows = Db::name('observatory_history')->order('sort_order', 'asc')->select()->toArray(); $items = []; foreach ($rows as $r) { $items[] = [ 'id' => (int) $r['id'], 'kind' => $r['kind'], 'title' => $r['title'], 'summary' => $r['summary'], 'date' => $r['date_str'], 'thumb' => $r['thumb'], ]; } return ['items' => $items]; } }