Просмотр исходного кода

✨ (library): 作品版本列表改用 TipitakaChapterChannelService

/library/tipitaka/work/{sn} 与译本详情页的「其他版本」原来在
TipitakaCatalogService::workChannels() 里自己查 progress_chapters,现在改调
V3\TipitakaChapterChannelService::all(),与 v3 tipitaka-chapter-channels 同一份
查询与可见性(library 不登录,仍只列公开 channel)。

- 所有者名字取自 service 的 studio 摘要(owner_name),群组所有者也能显示;
  _row 模板两种来源都认
- 不再逐行查所有者
- 补一条测试:私有不列、所有者名字、其他版本排除当前译本

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
visuddhinanda 9 часов назад
Родитель
Сommit
6540e63ec9

+ 18 - 15
api-v13/app/Services/TipitakaCatalogService.php

@@ -8,6 +8,7 @@ use App\Models\Channel;
 use App\Models\PaliText;
 use App\Models\ProgressChapter;
 use App\Models\TagMap;
+use App\Services\V3\TipitakaChapterChannelService;
 use Illuminate\Contracts\Pagination\LengthAwarePaginator;
 use Illuminate\Database\Query\Builder;
 use Illuminate\Support\Facades\DB;
@@ -31,7 +32,8 @@ use Illuminate\Support\Facades\DB;
  * TODO: 给 progress_chapters 加 (book, para) 索引(只加索引,不改列;migration 由用户决定,2026-11-04 定为以后做)。
  *       现在按作品根节点查版本只能全表扫描(本地 12.6 万行)。2026-11-04 在事务里临时建索引实测(测完回滚),
  *       各页 SQL 合计:三藏首页 132→14ms、分类「经藏」292→19ms、分类末级 76→9ms、译本列表 67→13ms、
- *       作品详情 17→5ms。受影响的查询:本类的 publicChannelsQuery()、workChannels(),以及
+ *       作品详情 17→5ms。受影响的查询:本类的 publicChannelsQuery()、workChannels()(实际在
+ *       V3\TipitakaChapterChannelService,v3 tipitaka-chapter-channels 端点也用它),以及
  *       TipitakaController::versionsQuery()。
  *       顺带可考虑 pali_texts (level) 索引:buildWorks() 冷启动里 `where level = 1` 占 ~106ms(一天一次,不急)。
  */
@@ -79,7 +81,10 @@ class TipitakaCatalogService
      */
     private const AVATAR_COLORS = ['#206bc4', '#7a3fc4', '#2fb344', '#c77700', '#d63939', '#0ca678', '#4263eb', '#ae3ec9'];
 
-    public function __construct(protected TermService $termService) {}
+    public function __construct(
+        protected TermService $termService,
+        protected TipitakaChapterChannelService $chapterChannels,
+    ) {}
 
     public static function channelColor(string $uid): string
     {
@@ -401,28 +406,26 @@ class TipitakaCatalogService
     }
 
     /**
-     * 收录了该作品的其他公开 channel。
+     * 收录了该作品的公开 channel,按完成度从高到低。
      *
-     * @return list<array{uid: string, name: string, lang: string, type: string, owner: mixed, progress: int, updated_at: string|null}>
+     * 与 v3 `GET /v3/tipitaka-chapter-channels/{book}/{para}` 同一份查询与可见性
+     * ({@see TipitakaChapterChannelService::all()});library 不登录,所以只有公开的。
+     *
+     * @return list<array{uid: string, name: string, lang: string, type: string, owner_name: string|null, progress: int, updated_at: string|null}>
      */
     public function workChannels(array $work, ?string $exceptChannel = null): array
     {
-        return ProgressChapter::with('channel.owner')
-            ->where('book', $work['book'])
-            ->where('para', $work['para'])
-            ->when($exceptChannel, fn ($q) => $q->where('channel_id', '<>', $exceptChannel))
-            ->whereHas('channel', fn ($q) => $q->where('status', 30))
-            ->orderByDesc('progress')
-            ->get()
+        return $this->chapterChannels->all($work['book'], $work['para'], null, $exceptChannel)
             ->map(fn ($row) => [
                 'uid' => (string) $row->channel_id,
-                'name' => $row->channel->name,
-                'lang' => $row->channel->lang,
-                'type' => $row->channel->type,
-                'owner' => $row->channel->owner,
+                'name' => $row->channel_name,
+                'lang' => $row->channel_lang,
+                'type' => $row->channel_type,
+                'owner_name' => $row->studio['nickName'] ?? null,
                 'progress' => (int) round($row->progress * 100),
                 'updated_at' => $row->updated_at?->toDateString(),
             ])
+            ->values()
             ->all();
     }
 

+ 3 - 2
api-v13/resources/views/library/tipitaka/channels/_row.blade.php

@@ -1,4 +1,5 @@
-{{-- channel 列表的一行。变量:$channel{uid, name, type, lang, owner?, updated_at?, url, works? | progress?}、$showType(可选,显示类型标签)
+{{-- channel 列表的一行。变量:$channel{uid, name, type, lang, owner?|owner_name?, updated_at?, url, works? | progress?}、$showType(可选,显示类型标签)
+     owner 是 UserInfo 模型;作品版本页的行来自 TipitakaChapterChannelService,只有 owner_name(所有者可能是群组)。
      有 progress(作品版本页)时右侧显示进度条,否则显示收录作品数。 --}}
 <a href="{{ $channel['url'] }}" class="tipitaka-channel-row">
     <x-ui.author-avatar
@@ -12,7 +13,7 @@
         </div>
         <div class="tipitaka-channel-row__meta">
             {{ __('language.'.$channel['lang']) }}
-            @if(!empty($channel['owner'])) · {{ $channel['owner']->nickname }} @endif
+            @if(!empty($channel['owner_name'])) · {{ $channel['owner_name'] }} @elseif(!empty($channel['owner'])) · {{ $channel['owner']->nickname }} @endif
             @if(!empty($channel['updated_at'])) · {{ __('library.updated') }} {{ $channel['updated_at'] }} @endif
         </div>
     </div>

+ 22 - 0
api-v13/tests/Feature/LibraryTipitakaChannelTest.php

@@ -262,6 +262,28 @@ it('lists every public version of a work grouped by type', function () {
     $this->get('/library/tipitaka/work/424242')->assertNotFound();
 });
 
+it('lists the versions of a work through the chapter channel service', function () {
+    ['channel' => $channel] = libTipitakaFixture();
+    $secret = makeChannel(makeStudio('lib-secret-owner'), 'secret channel');
+    Channel::where('uid', $secret)->update(['status' => 10]);
+    libProgress($secret, 9100, 3, 0.9);
+
+    // 与 v3 tipitaka-chapter-channels 同一份查询:私有的不列,所有者名字照常显示
+    $this->get('/library/tipitaka/work/9100')
+        ->assertOk()
+        ->assertSee('lib channel')
+        ->assertSee('lib-owner')
+        ->assertDontSee('secret channel');
+
+    // 译本详情页的「其他版本」排除当前译本
+    $other = makeChannel(makeStudio('lib-other'), 'other channel');
+    libProgress($other, 9100, 3, 0.1);
+    $this->get("/library/tipitaka/channels/{$channel}/work/9100")
+        ->assertOk()
+        ->assertSee('other channel')
+        ->assertDontSee('secret channel');
+});
+
 it('redirects the legacy channel query to the channel pages', function () {
     ['channel' => $channel] = libTipitakaFixture();