فهرست منبع

Merge pull request #2363 from visuddhinanda/development

Development
visuddhinanda 2 هفته پیش
والد
کامیت
42568034d4

+ 49 - 52
api-v13/app/Console/Commands/UpgradeProgress.php

@@ -43,84 +43,81 @@ class UpgradeProgress extends Command
      */
     public function handle()
     {
-        if(\App\Tools\Tools::isStop()){
+        if (\App\Tools\Tools::isStop()) {
             return 0;
         }
-		$this->info('upgrade:progress start');
-		$startTime = time();
+        $this->info('upgrade:progress start');
+        $startTime = time();
         $book = $this->option('book');
         $para = $this->option('para');
         $channelId = $this->option('channel');
-        if($book && $para && $channelId){
-            $sentences = Sentence::where('strlen','>',0)
-                          ->where('book_id',$book)
-                          ->where('paragraph',$para)
-                          ->where('channel_uid',$channelId)
-                          ->groupby('book_id','paragraph','channel_uid')
-                          ->select('book_id','paragraph','channel_uid');
-        }else{
-            $sentences = Sentence::where('strlen','>',0)
-                          ->where('book_id','<',1000)
-                          ->where('channel_uid','<>','')
-                          ->groupby('book_id','paragraph','channel_uid')
-                          ->select('book_id','paragraph','channel_uid');
+        if ($book && $para && $channelId) {
+            $sentences = Sentence::where('strlen', '>', 0)
+                ->where('book_id', $book)
+                ->where('paragraph', $para)
+                ->where('channel_uid', $channelId)
+                ->groupby('book_id', 'paragraph', 'channel_uid')
+                ->select('book_id', 'paragraph', 'channel_uid');
+        } else {
+            $sentences = Sentence::where('strlen', '>', 0)
+                ->where('book_id', '<', 1000)
+                ->where('channel_uid', '<>', '')
+                ->groupby('book_id', 'paragraph', 'channel_uid')
+                ->select('book_id', 'paragraph', 'channel_uid');
         }
         $count = $sentences->count();
         $sentences = $sentences->cursor();
-        $this->info('sentences:'.$count);
+        $this->info('sentences:' . $count);
         #第二步 更新段落表
-        $bar = $this->output->createProgressBar($count);
         foreach ($sentences as $sentence) {
             # 第二步 生成para progress 1,2,15,zh-tw
             # 计算此段落完成时间
-            $finalAt = Sentence::where('strlen','>',0)
-                        ->where('book_id',$sentence->book_id)
-                        ->where('paragraph',$sentence->paragraph)
-                        ->where('channel_uid',$sentence->channel_uid)
-                        ->max('created_at');
-            $updateAt = Sentence::where('strlen','>',0)
-                        ->where('book_id',$sentence->book_id)
-                        ->where('paragraph',$sentence->paragraph)
-                        ->where('channel_uid',$sentence->channel_uid)
-                        ->max('updated_at');
+            $finalAt = Sentence::where('strlen', '>', 0)
+                ->where('book_id', $sentence->book_id)
+                ->where('paragraph', $sentence->paragraph)
+                ->where('channel_uid', $sentence->channel_uid)
+                ->max('created_at');
+            $updateAt = Sentence::where('strlen', '>', 0)
+                ->where('book_id', $sentence->book_id)
+                ->where('paragraph', $sentence->paragraph)
+                ->where('channel_uid', $sentence->channel_uid)
+                ->max('updated_at');
             # 查询每个段落的等效巴利语字符数
-            $result_sent = Sentence::where('strlen','>',0)
-                                    ->where('book_id',$sentence->book_id)
-                                    ->where('paragraph',$sentence->paragraph)
-                                    ->where('channel_uid',$sentence->channel_uid)
-                                    ->select('word_start')
-                                    ->get();
+            $result_sent = Sentence::where('strlen', '>', 0)
+                ->where('book_id', $sentence->book_id)
+                ->where('paragraph', $sentence->paragraph)
+                ->where('channel_uid', $sentence->channel_uid)
+                ->select('word_start')
+                ->get();
             if (count($result_sent) > 0) {
                 #查询这些句子的总共等效巴利语字符数
                 $para_strlen = 0;
                 foreach ($result_sent as $sent) {
                     # code...
-                    $para_strlen += PaliSentence::where('book',$sentence->book_id)
-                                ->where('paragraph',$sentence->paragraph)
-                                ->where('word_begin',$sent->word_start)
-                                ->value('length');
+                    $para_strlen += PaliSentence::where('book', $sentence->book_id)
+                        ->where('paragraph', $sentence->paragraph)
+                        ->where('word_begin', $sent->word_start)
+                        ->value('length');
                 }
                 $paraInfo = [
-                        'book'=>$sentence->book_id,
-                        'para'=>$sentence->paragraph,
-                        'channel_id'=>$sentence->channel_uid
+                    'book' => $sentence->book_id,
+                    'para' => $sentence->paragraph,
+                    'channel_id' => $sentence->channel_uid
                 ];
                 $paraData = [
-                        'lang'=>'en',
-                        'all_strlen'=>$para_strlen,
-                        'public_strlen'=>$para_strlen,
-                        'created_at'=>$finalAt,
-                        'updated_at'=>$updateAt,
+                    'lang' => 'en',
+                    'all_strlen' => $para_strlen,
+                    'public_strlen' => $para_strlen,
+                    'created_at' => $finalAt,
+                    'updated_at' => $updateAt,
                 ];
-                Log::debug('Progress updateOrInsert',['para'=>$paraInfo,'data'=>$paraData]);
-                Progress::updateOrInsert($paraInfo,$paraData);
+                Log::debug('Progress updateOrInsert', ['para' => $paraInfo, 'data' => $paraData]);
+                Progress::updateOrInsert($paraInfo, $paraData);
             }
-            $bar->advance();
         }
-        $bar->finish();
 
-		$time = time() - $startTime;
-		$this->info("upgrade progress finished in {$time}s");
+        $time = time() - $startTime;
+        $this->info("upgrade progress finished in {$time}s");
 
         return 0;
     }

+ 1 - 1
api-v13/app/Http/Controllers/AccessTokenController.php

@@ -58,7 +58,7 @@ class AccessTokenController extends Controller
                     }
                     break;
                 default:
-                    continue;
+                    continue 2;
                     break;
             }
             //获取token

+ 75 - 15
api-v13/app/Http/Controllers/Library/WikiController.php

@@ -12,6 +12,14 @@ use App\Services\OpenSearchService;
 
 class WikiController extends Controller
 {
+    // 质量等级(数值越小等级越高)
+    private  $qualityRank = [
+        'featured' => 1,
+        'standard' => 2,
+        'draft'    => 3,
+        'pending'  => 4,
+    ];
+
     public function __construct(
         private TermService    $termService,
         private OpenSearchService    $searchService
@@ -128,16 +136,10 @@ HTML,
 
         $result = $this->searchService->search($params);
 
-        // 质量等级(数值越小等级越高)
-        $qualityRank = [
-            'featured' => 1,
-            'standard' => 2,
-            'draft'    => 3,
-            'pending'  => 4,
-        ];
+
 
         // 当前允许的最大等级
-        $maxRank = $qualityRank[$quality] ?? 4;
+        $maxRank = $this->qualityRank[$quality] ?? 4;
 
         $unique = [];
 
@@ -151,7 +153,7 @@ HTML,
                 $itemQuality = 'pending';
             }
 
-            $itemRank = $qualityRank[$itemQuality] ?? 4;
+            $itemRank = $this->qualityRank[$itemQuality] ?? 4;
 
             // 按输入质量过滤
             if ($itemRank > $maxRank) {
@@ -176,7 +178,7 @@ HTML,
 
             // 已存在时,保留质量更高的
             $existingQuality = $unique[$key]['quality'];
-            $existingRank    = $qualityRank[$existingQuality] ?? 4;
+            $existingRank    = $this->qualityRank[$existingQuality] ?? 4;
 
             if ($itemRank < $existingRank) {
                 $unique[$key] = $record;
@@ -197,6 +199,13 @@ HTML,
             return null;
         }
     }
+    private function getCategories(array $tags)
+    {
+        $catTag = array_filter($tags, function ($tag) {
+            return str_contains($tag, 'category:');
+        });
+        return array_map(fn($item) => mb_substr($item, mb_strlen('category:', 'UTF-8')), $catTag);
+    }
 
 
     public function show(string $lang, string $word)
@@ -213,6 +222,8 @@ HTML,
         } else {
             $quality = null;
         }
+
+        $cats = $this->getCategories($result['_source']['tags']);
         $entry = [
             'word'      => $term['word'],
             'lang'      => $term['language'],
@@ -220,14 +231,12 @@ HTML,
             'meaning'        => $term['meaning'],
             'quality'   => $quality,   // featured | standard | draft | pending | null
             'category'  => '法义术语',
-            'tags'      => [],
+            'tags'      => $cats,
             'langs'     => [
                 ['lang' => 'zh-Hant', 'label' => '繁体中文',   'word' => '无常'],
                 ['lang' => 'en', 'label' => 'English', 'word' => 'Impermanence'],
             ],
-            'related' => [
-                ['word' => 'Dukkha',    'zh' => '苦',   'lang' => 'pi'],
-            ],
+            'related' => $this->related($cats),
             'content' => $term['html'] ?? ''
         ];
         $parsed  = WikiContentParser::parse($entry['content']);
@@ -237,7 +246,8 @@ HTML,
                 'content' => $parsed['content'],
                 'toc'     => $parsed['toc'],
                 'edit_url' => config('mint.server.dashboard_base_path') . "/workspace/term/{$term['guid']}/edit",
-                'zh' => '编辑'
+                'zh' => '编辑',
+                'other_versions' => $this->otherVersions($term['word']),
             ]),
             'categories' => $this->categories(),
             'lang' => $lang,
@@ -245,6 +255,26 @@ HTML,
         ]);
     }
 
+    private function related(array $tags): array
+    {
+        $params = [
+            'pageSize'     => 5,
+            'resourceType' => 'term',
+            'tags'         => array_map(fn($n) => "category:{$n}", $tags),
+        ];
+
+        $result = $this->searchService->search($params);
+        $relates = [];
+
+        foreach ($result['hits']['hits'] as $item) {
+            $relates[] = [
+                'word' => $item['_source']['title']['text']['pali'],
+                'zh' => $item['_source']['title']['text']['zh'],
+                'lang' => $item['_source']['language']
+            ];
+        }
+        return $relates;
+    }
 
     // ── Helpers ──────────────────────────────────────────────────
 
@@ -338,4 +368,34 @@ HTML,
             ['word' => 'Rājagaha', 'lang' => 'pi'],
         ];
     }
+
+
+    private function otherVersions(string $word): array
+    {
+        $params = [
+            'query' => $word,
+            'pageSize'     => 10,
+            'resourceType' => 'term',
+            'tags'         => array_map(fn($n) => "quality:{$n}", array_keys($this->qualityRank)),
+        ];
+
+        $result = $this->searchService->search($params);
+        $versions = [];
+
+        foreach ($result['hits']['hits'] as $item) {
+            $text = $item['_source']['title']['text'];
+            $id   = $item['_source']['resource_id'];
+            $versions[] = [
+                'type'     => 'term',
+                'id'       => $id,
+                'lang'     => $item['_source']['language'],
+                'title'    => $text['zh'] ?? 'null',
+                'quality'  => $this->getQuality($item['_source']['tags'] ?? 'quality:pending'),
+                'category' => '法義術語',
+                'snippet'  => $item['_source']['summary']['text'],
+                'updated'  => $item['_source']['updated_at'],
+            ];
+        }
+        return $versions;
+    }
 }

+ 2 - 1
api-v13/app/Models/Chat.php

@@ -3,12 +3,13 @@
 namespace App\Models;
 
 use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\SoftDeletes; // ← 加这行
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Str;
 
 class Chat extends Model
 {
-    use HasFactory;
+    use HasFactory, SoftDeletes;
 
     protected $fillable = [
         'uid',

+ 43 - 20
api-v13/resources/css/components/_card.css

@@ -109,16 +109,19 @@
     transition: color 0.12s;
 }
 
-.wiki-toc-list a:hover  { color: var(--tblr-body-color); }
-.wiki-toc-list a.active { color: var(--tblr-body-color); font-weight: 500; }
-
-.wiki-toc-list .toc-level-2 a { padding-left: 0.5rem; }
-.wiki-toc-list .toc-level-3 a { padding-left: 1rem; }
+.wiki-toc-list a:hover {
+    color: var(--tblr-body-color);
+}
+.wiki-toc-list a.active {
+    color: var(--tblr-body-color);
+    font-weight: 500;
+}
 
-.wiki-toc-num {
-    color: var(--tblr-secondary);
-    margin-right: 5px;
-    font-size: 0.75rem;
+.wiki-toc-list .toc-level-2 a {
+    padding-left: 0.5rem;
+}
+.wiki-toc-list .toc-level-3 a {
+    padding-left: 1rem;
 }
 
 /* ══════════════════════════════════════════
@@ -135,7 +138,9 @@
     border-bottom: 1px solid var(--tblr-border-color);
 }
 
-.wiki-related-list li:last-child { border-bottom: none; }
+.wiki-related-list li:last-child {
+    border-bottom: none;
+}
 
 .wiki-related-list a {
     display: flex;
@@ -162,7 +167,9 @@
     border-collapse: collapse;
 }
 
-.wiki-meta-table td { padding: 3px 0; }
+.wiki-meta-table td {
+    padding: 3px 0;
+}
 
 .wiki-meta-table td:last-child {
     text-align: right;
@@ -173,10 +180,12 @@
    七、条目头部(通用标题区)
    ══════════════════════════════════════════ */
 
-.wiki-entry-header { margin-bottom: 1.25rem; }
+.wiki-entry-header {
+    margin-bottom: 1.25rem;
+}
 
 .wiki-entry-title {
-    font-family: "Noto Serif", Georgia, serif;
+    font-family: 'Noto Serif', Georgia, serif;
     font-size: 1.75rem;
     font-weight: 600;
     line-height: 1.25;
@@ -244,7 +253,7 @@
 .author-avatar {
     display: flex;
     align-items: center;
-    gap: .5rem;
+    gap: 0.5rem;
 }
 
 .author-avatar__img,
@@ -255,13 +264,25 @@
 }
 
 .author-avatar--sm .author-avatar__img,
-.author-avatar--sm .author-avatar__initials { width: 24px; height: 24px; font-size: .65rem; }
+.author-avatar--sm .author-avatar__initials {
+    width: 24px;
+    height: 24px;
+    font-size: 0.65rem;
+}
 
 .author-avatar--md .author-avatar__img,
-.author-avatar--md .author-avatar__initials { width: 28px; height: 28px; font-size: .68rem; }
+.author-avatar--md .author-avatar__initials {
+    width: 28px;
+    height: 28px;
+    font-size: 0.68rem;
+}
 
 .author-avatar--lg .author-avatar__img,
-.author-avatar--lg .author-avatar__initials { width: 48px; height: 48px; font-size: .95rem; }
+.author-avatar--lg .author-avatar__initials {
+    width: 48px;
+    height: 48px;
+    font-size: 0.95rem;
+}
 
 .author-avatar__initials {
     display: flex;
@@ -272,16 +293,18 @@
 }
 
 .author-avatar__name {
-    font-size: .8rem;
+    font-size: 0.8rem;
     color: var(--tblr-body-color);
     font-weight: 500;
     display: block;
 }
 
-.author-avatar--lg .author-avatar__name { font-size: .9rem; }
+.author-avatar--lg .author-avatar__name {
+    font-size: 0.9rem;
+}
 
 .author-avatar__sub {
-    font-size: .72rem;
+    font-size: 0.72rem;
     color: var(--tblr-secondary);
     display: block;
 }

+ 26 - 14
api-v13/resources/css/modules/_wiki.css

@@ -185,7 +185,7 @@
 }
 
 .wiki-today-title {
-    font-family: "Noto Serif", Georgia, serif;
+    font-family: 'Noto Serif', Georgia, serif;
     font-size: 1.125rem;
     font-weight: 600;
     margin-bottom: 6px;
@@ -237,7 +237,7 @@
 }
 
 .wiki-popover-word {
-    font-family: "Noto Serif", Georgia, serif;
+    font-family: 'Noto Serif', Georgia, serif;
     font-size: 0.9375rem;
     font-style: italic;
     font-weight: 600;
@@ -275,7 +275,7 @@
 }
 
 .wiki-drawer-word {
-    font-family: "Noto Serif", Georgia, serif;
+    font-family: 'Noto Serif', Georgia, serif;
     font-size: 1.125rem;
     font-style: italic;
     font-weight: 600;
@@ -304,7 +304,7 @@
 }
 
 .wiki-term-card-word {
-    font-family: "Noto Serif", Georgia, serif;
+    font-family: 'Noto Serif', Georgia, serif;
     font-size: 0.9375rem;
     font-style: italic;
     font-weight: 600;
@@ -364,7 +364,7 @@
    ══════════════════════════════════════════ */
 
 .wiki-content-body {
-    font-family: "Noto Serif", Georgia, "Times New Roman", serif;
+    font-family: 'Noto Serif', Georgia, 'Times New Roman', serif;
     font-size: 1rem;
     line-height: 1.875;
     color: var(--tblr-body-color);
@@ -383,7 +383,7 @@
 .wiki-content-body h4,
 .wiki-content-body h5,
 .wiki-content-body h6 {
-    font-family: "Noto Serif", Georgia, serif;
+    font-family: 'Noto Serif', Georgia, serif;
     font-weight: 600;
     line-height: 1.3;
     color: var(--tblr-body-color);
@@ -471,7 +471,7 @@
     margin-bottom: 1.5em;
 }
 .wiki-content-body th {
-    font-family: "Noto Serif", Georgia, serif;
+    font-family: 'Noto Serif', Georgia, serif;
     font-weight: 600;
     font-size: 0.8125rem;
     text-align: left;
@@ -494,7 +494,7 @@
 .wiki-content-body code {
     font-family: var(
         --tblr-font-monospace,
-        "SFMono-Regular",
+        'SFMono-Regular',
         Consolas,
         monospace
     );
@@ -558,7 +558,7 @@
     margin-bottom: 2rem;
 }
 .wiki-home-title h1 {
-    font-family: "Noto Serif", Georgia, serif;
+    font-family: 'Noto Serif', Georgia, serif;
     font-size: 2.25rem;
     font-weight: 600;
     color: var(--tblr-body-color);
@@ -586,7 +586,9 @@
     font-size: 0.8125rem;
     text-decoration: none;
     margin: 2px;
-    transition: background 0.12s, color 0.12s;
+    transition:
+        background 0.12s,
+        color 0.12s;
 }
 .wiki-hot-tag:hover {
     background: var(--wp-brand-light);
@@ -610,7 +612,7 @@
 }
 .wiki-home-divider::before,
 .wiki-home-divider::after {
-    content: "";
+    content: '';
     flex: 1;
     border-bottom: 1px solid var(--tblr-border-color);
 }
@@ -630,7 +632,9 @@
     color: var(--tblr-body-color);
     font-size: 0.875rem;
     text-decoration: none;
-    transition: background 0.12s, border-color 0.12s;
+    transition:
+        background 0.12s,
+        border-color 0.12s;
 }
 .wiki-language-tag:hover {
     background: var(--wp-brand-light);
@@ -804,7 +808,7 @@
 }
 
 .wiki-term-link--featured::before {
-    content: "★";
+    content: '★';
     display: inline-block;
     margin-right: 0.2em;
     font-size: 0.75em;
@@ -882,7 +886,9 @@ content: "●";
     color: var(--tblr-secondary);
     cursor: pointer;
     text-decoration: none;
-    transition: background 0.12s, color 0.12s;
+    transition:
+        background 0.12s,
+        color 0.12s;
 }
 
 .wiki-action-btn:hover {
@@ -940,3 +946,9 @@ content: "●";
     cursor: pointer;
     text-decoration: underline;
 }
+
+.wiki-other-versions {
+    padding-top: 1.25rem;
+    margin-top: 1.25rem;
+    border-top: 1px solid var(--tblr-border-color);
+}

+ 11 - 2
api-v13/resources/views/library/wiki/show.blade.php

@@ -33,12 +33,22 @@
     {{-- 标签 --}}
     <div class="wiki-tags">
         @foreach ($entry['tags'] as $tag)
-        <a class="wiki-tag" href="{{ route('library.wiki.index') }}?tag={{ $tag }}">
+        <a class="wiki-tag" href="{{ route('library.wiki.index',[$lang]) }}?tag={{ $tag }}">
             {{ $tag }}
         </a>
         @endforeach
     </div>
 
+    {{-- 其他版本 --}}
+    @if(isset($entry['other_versions']) && count($entry['other_versions']) > 0)
+    <div class="wiki-other-versions">
+        <div class="wiki-sidebar-title" style="margin-bottom: 0.75rem;">其他版本</div>
+        @foreach ($entry['other_versions'] as $version)
+        <x-wiki.search-result-card :result="$version" :lang="$lang" />
+        @endforeach
+    </div>
+    @endif
+
 </article>
 
 @endsection
@@ -52,7 +62,6 @@
         @foreach ($entry['toc'] as $i => $item)
         <li class="toc-level-{{ $item['level'] }}">
             <a href="#{{ $item['id'] }}">
-                <span class="wiki-toc-num">{{ $i + 1 }}</span>
                 {{ $item['text'] }}
             </a>
         </li>