Przeglądaj źródła

Merge pull request #2375 from visuddhinanda/development

Development
visuddhinanda 2 dni temu
rodzic
commit
d33f8fa23e

+ 2 - 2
api-v13/app/Http/Controllers/ApiController.php

@@ -3,9 +3,9 @@
 namespace App\Http\Controllers;
 
 use Illuminate\Http\Request;
-use Illuminate\Support\Facades\Cache;
+
 use Illuminate\Support\Facades\Redis;
-use Illuminate\Support\Facades\Log;
+
 
 class ApiController extends Controller
 {

+ 70 - 9
api-v13/app/Http/Controllers/HealthCheckController.php

@@ -3,6 +3,11 @@
 namespace App\Http\Controllers;
 
 use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\DB;
+use App\Services\RabbitMQService;
+use App\Services\OpenSearchService;
 
 class HealthCheckController extends Controller
 {
@@ -14,16 +19,72 @@ class HealthCheckController extends Controller
     public function index()
     {
         //
-        if(file_exists(base_path('.stop'))){
-            $status = 503;
-        }else{
-            $status = 200;
+        if (file_exists(base_path('.stop'))) {
+            return response()->json(
+                ['createdAt' => now(), 'checks' => []],
+                503,
+                ['Content-Type' => 'application/json;charset=UTF-8', 'Charset' => 'utf-8'],
+                JSON_UNESCAPED_UNICODE
+            );
         }
-        return response()->json(['createdAt' => now()],
-            $status,
-            ['Content-Type' => 'application/json;charset=UTF-8',
-            'Charset' => 'utf-8'],
-            JSON_UNESCAPED_UNICODE);
+
+        $checks = [];
+        $healthy = true;
+
+        // Cache
+        try {
+            Cache::put('health_check', true, 5);
+            Cache::get('health_check');
+            $checks['cache'] = true;
+        } catch (\Throwable $e) {
+            $checks['cache'] = false;
+            $healthy = false;
+            Log::error('Health check failed: cache', ['error' => $e->getMessage()]);
+        }
+
+        // Database
+        try {
+            DB::connection()->getPdo();
+            $checks['db'] = true;
+        } catch (\Throwable $e) {
+            $checks['db'] = false;
+            $healthy = false;
+            Log::error('Health check failed: db', ['error' => $e->getMessage()]);
+        }
+
+        // OpenSearch
+        try {
+            $service = app(OpenSearchService::class);
+            [$ok, $msg] = $service->testConnection();
+            if (!$ok) throw new \Exception($msg);
+            $checks['opensearch'] = true;
+        } catch (\Throwable $e) {
+            $checks['opensearch'] = false;
+            $healthy = false;
+            Log::error('Health check failed: opensearch', ['error' => $e->getMessage()]);
+        }
+
+        // RabbitMQ
+        try {
+            $service = app(RabbitMQService::class);
+            $service->isConnected() ? null : throw new \Exception('Not connected');
+            $checks['rabbitmq'] = true;
+        } catch (\Throwable $e) {
+            $checks['rabbitmq'] = false;
+            $healthy = false;
+            Log::error('Health check failed: rabbitmq', ['error' => $e->getMessage()]);
+        }
+
+        return response()->json(
+            [
+                'createdAt' => now(),
+                'checks' => $checks,
+                'domain' => $_SERVER['HTTP_HOST']
+            ],
+            $healthy ? 200 : 500,
+            ['Content-Type' => 'application/json;charset=UTF-8', 'Charset' => 'utf-8'],
+            JSON_UNESCAPED_UNICODE
+        );
     }
 
     /**

+ 63 - 0
api-v13/app/Http/Controllers/HeartbeatController.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Http\Request;
+
+class HeartbeatController extends Controller
+{
+    /**
+     * Display a listing of the resource.
+     */
+    public function index()
+    {
+        //
+        //
+        if (file_exists(base_path('.stop'))) {
+            $status = 503;
+        } else {
+            $status = 200;
+        }
+        return response()->json(
+            ['createdAt' => now()],
+            $status,
+            [
+                'Content-Type' => 'application/json;charset=UTF-8',
+                'Charset' => 'utf-8'
+            ],
+            JSON_UNESCAPED_UNICODE
+        );
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     */
+    public function store(Request $request)
+    {
+        //
+    }
+
+    /**
+     * Display the specified resource.
+     */
+    public function show(string $id)
+    {
+        //
+    }
+
+    /**
+     * Update the specified resource in storage.
+     */
+    public function update(Request $request, string $id)
+    {
+        //
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     */
+    public function destroy(string $id)
+    {
+        //
+    }
+}

+ 5 - 4
api-v13/app/Http/Controllers/Library/HomeController.php

@@ -2,12 +2,12 @@
 
 namespace App\Http\Controllers\Library;
 
-use App\Http\Controllers\Controller;
+use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\Cookie;
-
 use Illuminate\Support\Facades\File;
 
 
+use App\Http\Controllers\Controller;
 use App\Models\PaliText;
 use App\Models\ProgressChapter;
 use App\Services\TermService;
@@ -25,9 +25,9 @@ class HomeController extends Controller
         'linear-gradient(160deg, #1a2820,rgb(55, 124, 99))',
     ];
     /**
-     * 构造函数,注入 OpenSearchService
+     * 构造函数,注入 TermService
      *
-     * @param  \App\Services\OpenSearchService  $searchService
+     * @param  \App\Services\TermService  $termService
      */
     public function __construct(
         protected TermService $termService,
@@ -39,6 +39,7 @@ class HomeController extends Controller
     {
         $categories = $this->loadCategories();
         $locale = Cookie::get('language') ?? 'en';
+        Log::debug('$locale=' . $locale);
         // 获取一级分类和对应的书籍
         $categoryData = [];
         foreach ($categories as $category) {

+ 38 - 5
api-v13/app/Http/Controllers/Library/TipitakaController.php

@@ -3,16 +3,15 @@
 namespace App\Http\Controllers\Library;
 
 use App\Http\Controllers\Controller;
-
 use Illuminate\Http\Request;
-
+use Illuminate\Support\Facades\Cookie;
 use Illuminate\Support\Facades\File;
+use Illuminate\Support\Facades\Log;
 
-use Illuminate\Support\Facades\DB;
 use App\Models\PaliText;
 use App\Models\ProgressChapter;
-use App\Models\Tag;
-use App\Models\TagMap;
+use App\Services\TermService;
+
 
 
 class TipitakaController extends Controller
@@ -26,6 +25,16 @@ class TipitakaController extends Controller
         'linear-gradient(160deg, #1a1a2d,rgb(76, 68, 146))',
         'linear-gradient(160deg, #1a2820,rgb(55, 124, 99))',
     ];
+
+    /**
+     * 构造函数,注入 TermService
+     *
+     * @param  \App\Services\TermService  $termService
+     */
+    public function __construct(
+        protected TermService $termService,
+    ) {}
+
     // -------------------------------------------------------------------------
     // 从 uid / id 字符串中提取一个稳定的整数,用于色池取余
     // -------------------------------------------------------------------------
@@ -48,6 +57,8 @@ class TipitakaController extends Controller
     public function index(Request $request, ?int $id = null)
     {
 
+        $locale = Cookie::get('language') ?? 'en';
+
         $categories = $this->loadCategories();
 
         // ── 当前分类 ──────────────────────────────────────────
@@ -69,6 +80,28 @@ class TipitakaController extends Controller
             fn($cat) => $cat['parent_id'] == $id
         ));
 
+        $allNames = array_map(fn($item) => $item['name'], $subCategories);
+
+        // 去重
+        $allNames = array_values(array_unique($allNames));
+
+        // 查词典
+        $terms = $this->termService->glossaryByLemma($allNames, $locale);
+        // 构建映射
+        $termMap = [];
+        if ($terms) {
+            foreach ($terms as $term) {
+                $termMap[$term->word] = $term->meaning;
+            }
+        }
+        // 回填
+        foreach ($subCategories as $key => $cat) {
+            $name = $cat['name'] ?? null;
+            $subCategories[$key]['name'] = $termMap[$name] ?? $name;
+        }
+
+
+
         // ── 过滤参数 ────────────────────────────────────────────
         $selectedType   = request('type',   'all');
         $selectedLang   = request('lang',   'all');

+ 5 - 0
api-v13/app/Services/RabbitMQService.php

@@ -41,6 +41,11 @@ class RabbitMQService
         $this->channel->basic_qos(null, 1, null);
     }
 
+    public function isConnected()
+    {
+        return $this->connection->isConnected();
+    }
+
     public function getChannel(): AMQPChannel
     {
         return $this->channel;

+ 57 - 2
api-v13/resources/css/modules/_tipitaka.css

@@ -10,7 +10,9 @@
    ══════════════════════════════════════════ */
 
 .card-book {
-    transition: transform 0.2s, box-shadow 0.2s;
+    transition:
+        transform 0.2s,
+        box-shadow 0.2s;
 }
 
 .card-book:hover {
@@ -184,7 +186,10 @@
     color: var(--tblr-body-color);
     background: var(--tblr-bg-surface);
     text-decoration: none;
-    transition: background 0.12s, border-color 0.12s, color 0.12s;
+    transition:
+        background 0.12s,
+        border-color 0.12s,
+        color 0.12s;
     white-space: nowrap;
 }
 
@@ -395,3 +400,53 @@
         width: 140px;
     }
 }
+
+/* ══════════════════════════════════════════
+   子分类网格
+   ══════════════════════════════════════════ */
+
+.tipitaka-subcategory-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+    gap: 8px;
+    padding: 4px 0;
+}
+
+.tipitaka-subcategory-item {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 8px;
+    padding: 16px 12px;
+    border-radius: var(--tblr-border-radius);
+    text-decoration: none;
+    color: var(--tblr-body-color);
+    border: 1px solid transparent;
+    transition:
+        background 0.15s,
+        border-color 0.15s;
+}
+
+.tipitaka-subcategory-item:hover {
+    background: var(--tblr-bg-surface-secondary);
+    border-color: var(--tblr-border-color);
+    text-decoration: none;
+    color: var(--tblr-body-color);
+}
+
+.tipitaka-subcategory-icon {
+    font-size: 40px;
+    color: var(--tblr-secondary);
+    line-height: 1;
+}
+
+.tipitaka-subcategory-item:hover .tipitaka-subcategory-icon {
+    color: var(--tblr-body-color);
+}
+
+.tipitaka-subcategory-name {
+    font-size: 0.8125rem;
+    text-align: center;
+    line-height: 1.4;
+    color: var(--tblr-secondary);
+}

+ 16 - 16
api-v13/resources/views/library/tipitaka/category.blade.php

@@ -78,22 +78,7 @@
                     :hidden-fields="['resource_type' => 'tipitaka']" />
             </div>
 
-            {{-- 1. 子分类 --}}
-            @if(count($subCategories) > 0)
-            <div class="wiki-card tipitaka-filters">
-                <div class="tipitaka-filter-row">
-                    <span class="tipitaka-filter-label">子分类</span>
-                    <div class="tipitaka-filter-pills">
-                        @foreach($subCategories as $sub)
-                        <a href="{{ route('library.tipitaka.category', ['id' => $sub['id']]) }}"
-                            class="tipitaka-pill">
-                            <div class="wiki-featured-title">{{ $sub['name'] }}</div>
-                        </a>
-                        @endforeach
-                    </div>
-                </div>
-            </div>
-            @endif
+
 
             {{-- 2. 过滤器区 --}}
             <div class="wiki-card tipitaka-filters">
@@ -161,6 +146,21 @@
 
             </div>
 
+            {{-- 1. 子分类 --}}
+            @if(count($subCategories) > 0)
+            <div class="wiki-card tipitaka-subcategories">
+                <div class="tipitaka-subcategory-grid">
+                    @foreach($subCategories as $sub)
+                    <a href="{{ route('library.tipitaka.category', ['id' => $sub['id']]) }}"
+                        class="tipitaka-subcategory-item">
+                        <i class="ti ti-folder tipitaka-subcategory-icon" aria-hidden="true"></i>
+                        <span class="tipitaka-subcategory-name">{{ $sub['name'] }}</span>
+                    </a>
+                    @endforeach
+                </div>
+            </div>
+            @endif
+
             {{-- 3. 排序 + 结果数 --}}
             <div class="tipitaka-sort-bar">
                 <span class="tipitaka-sort-bar__count">

+ 2 - 0
api-v13/routes/api.php

@@ -125,6 +125,7 @@ use App\Http\Controllers\SearchSuggestController;
 use App\Http\Controllers\UpgradeController;
 use App\Http\Controllers\ChapterContentController;
 use App\Http\Controllers\ParagraphContentController;
+use App\Http\Controllers\HeartbeatController;
 
 
 
@@ -310,6 +311,7 @@ Route::group([
     Route::apiResource('chat-messages', ChatMessageController::class);
     Route::apiResource('chapter-content', ChapterContentController::class);
     Route::apiResource('paragraph-content', ParagraphContentController::class);
+    Route::apiResource('heartbeat', HeartbeatController::class);
 
     Route::post('mock/openai/chat/completions', [MockOpenAIController::class, 'chatCompletions']);
     Route::post('mock/openai/completions', [MockOpenAIController::class, 'completions']);

+ 1 - 1
dashboard-v6/src/api/api-health.ts

@@ -6,5 +6,5 @@ export interface IHealth {
   createdAt: string;
 }
 export const apiServerHealth = (): Promise<IHealth> => {
-  return get<IHealth>("/api/v2/health-check");
+  return get<IHealth>("/api/v2/heartbeat");
 };