Browse Source

feat(index): 新增阅读模式单段落渲染 readParagraph

IndexTipitaka 索引不再复用为编辑模式设计的 PaliContentService::paragraphs,
改用新的 readParagraph:直接按 channel 从 sentences 表取该段落记录逐句渲染,
不再为无译文的句子保留原文占位。html 格式在函数内加段落外壳,其他格式一行一句。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zSe1kZS4JmKFJ5igD555X
visuddhinanda 2 weeks ago
parent
commit
2ef377a67f

+ 22 - 58
api-v13/app/Console/Commands/IndexTipitaka.php

@@ -12,9 +12,8 @@ use App\Services\SearchPaliDataService;
 use App\Services\SummaryService;
 use App\Services\TagService;
 use Illuminate\Console\Command;
-use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\Cache;
-
+use Illuminate\Support\Facades\Log;
 
 class IndexTipitaka extends Command
 {
@@ -322,66 +321,31 @@ class IndexTipitaka extends Command
 
                     continue;
                 }
-                $paragraphsData = app(PaliContentService::class)->paragraphs(
-                    $book,
-                    $start,
-                    $end,
-                    [$channel->channel_uid],
-                    ['mode' => 'read', 'format' => 'html', 'original' => false]
-                );
+                $paraList = Sentence::where('book_id', $book)
+                    ->whereBetween('paragraph', [$start, $end])
+                    ->where('channel_uid', $channel->channel_uid)
+                    ->orderBy('paragraph')
+                    ->distinct()->pluck('paragraph');
                 // 生成html数据
 
                 $title = '';
-                foreach ($paragraphsData as $paragraph) {
-                    $translation = [];
-                    $original = [];
-                    foreach ($paragraph['children'] as $sent) {
-                        $sid = "{$sent['book']}-{$sent['para']}-{$sent['wordStart']}-{$sent['wordEnd']}";
-                        if (isset($sent['translation'])) {
-                            foreach ($sent['translation'] as $tran) {
-                                if ($tran['channel']['id'] === $channel->channel_uid) {
-                                    $html = $tran['html'] ?? $tran['content'];
-                                    $translation[] = "<div class='sentence' data-sid='{$sid}'>{$html}</div>";
-                                    if ($tran['para'] === $start && ! empty($html)) {
-                                        $title = $html;
-                                    }
-                                }
-                            }
-                        }
-                        if (
-                            isset($sent['origin']) ||
-                            is_array($sent['origin']) ||
-                            count($sent['origin']) > 0
-                        ) {
-                            foreach ($sent['origin'] as $origin) {
-                                if ($origin['channel']['id'] === $channel->channel_uid) {
-                                    $html = $origin['html'] ?? $origin['content'];
-                                    $original[] = "<div class='sentence origin'  data-sid='{$sid}'>{$html}</div>";
-                                    if (empty($title) && $origin['para'] === $start && ! empty($html)) {
-                                        $title = $html;
-                                    }
-                                }
-                            }
-                        }
-                    }
-
-                    $level = $paragraph['para'] === $start ? $chapter->level : 0;
-                    $strOriginal = implode('', $original);
-                    $strTranslation = implode('', $translation);
-
-                    if ($channelInfo['type'] === 'original') {
-                        $htmlContent = $strOriginal;
-                    } else {
-                        $htmlContent = $strTranslation;
+                foreach ($paraList as $para) {
+                    $para = (int) $para;
+                    $level = $para === $start ? $chapter->level : 0;
+                    $paragraph = app(PaliContentService::class)->readParagraph(
+                        $book,
+                        $para,
+                        $channel->channel_uid,
+                        $level,
+                        'html'
+                    );
+                    if (empty($paragraph['display'])) {
+                        continue;
                     }
-
-                    $area = $channelInfo['type'] === 'original' ? 'original' : 'translation';
-
-                    if ($level > 0) {
-                        $display[] = "<div class='{$area}' data-para='{$paragraph['para']}'><h{$level}>{$htmlContent}</h{$level}></div>";
-                    } else {
-                        $display[] = "<div class='{$area}' data-para='{$paragraph['para']}'><div class='para-block'>{$htmlContent}</div></div>";
+                    if ($para === $start && empty($title)) {
+                        $title = $paragraph['sentences'][0]['html'];
                     }
+                    $display[] = $paragraph['display'];
                 }
                 $this->chapterSave([
                     'book' => $book,
@@ -433,7 +397,7 @@ class IndexTipitaka extends Command
             $document['title']['text']['pali'] = $title;
         }
         $document['content']['display'] = $param['content'];             // 展示
-        Cache::put($docId,$param['content']);
+        Cache::put($docId, $param['content']);
 
         if ($this->isTest) {
             $this->info($param['content']);

+ 68 - 0
api-v13/app/Services/PaliContentService.php

@@ -642,4 +642,72 @@ class PaliContentService
 
         return $result;
     }
+
+    /**
+     * 阅读模式渲染单个段落。
+     * 直接从 sentences 表取单个 channel 的记录,渲染成 html。
+     * 与 paragraphs() 不同:没有译文的句子不保留占位,有多少句子输出多少句子。
+     *
+     * @param  int  $level  标题级别,大于 0 时段落用 h{level} 包裹
+     * @return array{para: int, display: string, sentences: array<int, array{sid: string, html: string}>}
+     */
+    public function readParagraph(int $book, int $para, string $channelUid, int $level = 0, string $format = 'html'): array
+    {
+        $result = ['para' => $para, 'display' => '', 'sentences' => []];
+        $channel = Channel::where('uid', $channelUid)
+            ->select(['uid', 'type', 'lang', 'name'])->first();
+        if (! $channel) {
+            return $result;
+        }
+        $isOrigin = $channel->type === 'original' || $channel->type === 'wbw';
+        $channelType = $channel->type === 'nissaya' ? 'nissaya' : 'translation';
+
+        $records = Sentence::select($this->selectCol)
+            ->where('book_id', $book)
+            ->where('paragraph', $para)
+            ->where('channel_uid', $channelUid)
+            ->orderBy('word_start')
+            ->get();
+
+        $sentences = [];
+        foreach ($records as $row) {
+            $html = MdRender::render(
+                $row->content,
+                [$row->channel_uid],
+                null,
+                'read',
+                $channelType,
+                $row->content_type,
+                $format
+            );
+            if (empty($html)) {
+                continue;
+            }
+            $sid = "{$row->book_id}-{$row->paragraph}-{$row->word_start}-{$row->word_end}";
+            $result['sentences'][] = ['sid' => $sid, 'html' => $html];
+            if ($format === 'html') {
+                $class = $isOrigin ? 'sentence origin' : 'sentence';
+                $sentences[] = "<div class='{$class}' data-sid='{$sid}'>{$html}</div>";
+            } else {
+                $sentences[] = $html;
+            }
+        }
+
+        if (count($sentences) === 0) {
+            return $result;
+        }
+
+        if ($format === 'html') {
+            // html 格式加段落外壳
+            $area = $isOrigin ? 'original' : 'translation';
+            $content = implode('', $sentences);
+            $inner = $level > 0 ? "<h{$level}>{$content}</h{$level}>" : "<div class='para-block'>{$content}</div>";
+            $result['display'] = "<div class='{$area}' data-para='{$para}'>{$inner}</div>";
+        } else {
+            // 其他格式一行一句
+            $result['display'] = implode("\n", $sentences);
+        }
+
+        return $result;
+    }
 }