KioskService.php 10.8 KB
<?php
declare(strict_types=1);

namespace app\service;

use app\common\KioskDefaults;
use think\facade\Db;

class KioskService
{
    public function ensureSeed(): void
    {
        $this->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_WELCOME => KioskDefaults::welcome(),
            KioskDefaults::KEY_GUIDE   => KioskDefaults::guide(),
        ] 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 = [];
                }
                Db::name('knowledge')->insert([
                    'id'          => (string) $e['id'],
                    'type'        => (string) ($e['type'] ?? '文字'),
                    'title'       => (string) $e['title'],
                    'content'     => (string) ($e['content'] ?? ''),
                    'entry_date'  => !empty($e['date']) ? $e['date'] : null,
                    'tags'        => json_encode($tags, JSON_UNESCAPED_UNICODE),
                    'image'       => $e['image'] ?? null,
                    'video_url'     => $e['videoUrl'] ?? null,
                    '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 = [];
        }

        return [
            'homeBackgrounds' => array_values(array_filter($home, 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(),
        ];
    }

    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];
    }
}