AnthologyController.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. namespace App\Http\Controllers\Library;
  3. use App\Http\Controllers\Controller;
  4. use App\Services\CollectionService;
  5. use Illuminate\Http\Request;
  6. class AnthologyController extends Controller
  7. {
  8. // 封面渐变色池:uid 首字节取余循环,保证同一文集颜色稳定
  9. private array $coverGradients = [
  10. 'linear-gradient(160deg, #2d2010, #4a3010)',
  11. 'linear-gradient(160deg, #1a2d10, #2d4a18)',
  12. 'linear-gradient(160deg, #0d1f3c, #1a3660)',
  13. 'linear-gradient(160deg, #2d1020, #4a1830)',
  14. 'linear-gradient(160deg, #1a1a2d, #2a2a50)',
  15. 'linear-gradient(160deg, #1a2820, #2d4438)',
  16. ];
  17. // 作者色池:同上,根据 studio.id 首字节取余
  18. private array $authorColors = [
  19. '#c8860a', '#2e7d32', '#1565c0', '#6a1b9a',
  20. '#c62828', '#00695c', '#4e342e', '#37474f',
  21. ];
  22. public function __construct(private CollectionService $collectionService) {}
  23. // -------------------------------------------------------------------------
  24. // 从 uid / id 字符串中提取一个稳定的整数,用于色池取余
  25. // -------------------------------------------------------------------------
  26. private function colorIndex(string $uid): int
  27. {
  28. return hexdec(substr(str_replace('-', '', $uid), 0, 4)) % 255;
  29. }
  30. // -------------------------------------------------------------------------
  31. // 将 studio 对象转换为 blade 所需的 author 数组
  32. // -------------------------------------------------------------------------
  33. private function formatAuthor(array $studio): array
  34. {
  35. $name = $studio['nickName'] ?? $studio['studioName'] ?? '未知';
  36. $initials = mb_substr($name, 0, 2);
  37. $colorIdx = $this->colorIndex($studio['id'] ?? '0');
  38. return [
  39. 'name' => $name,
  40. 'initials' => $initials,
  41. 'color' => $this->authorColors[$colorIdx % count($this->authorColors)],
  42. 'avatar' => $studio['avatar'] ?? null,
  43. ];
  44. }
  45. // -------------------------------------------------------------------------
  46. // 将 CollectionResource 单条转换为 index 卡片所需格式
  47. // -------------------------------------------------------------------------
  48. private function formatForCard(array $item, int $index): array
  49. {
  50. $uid = $item['uid'];
  51. $colorIdx = $this->colorIndex($uid);
  52. // article_list => 章节标题列表
  53. $chapters = collect($item['article_list'] ?? [])
  54. ->pluck('title')
  55. ->toArray();
  56. return [
  57. 'id' => $uid,
  58. 'title' => $item['title'],
  59. 'subtitle' => $item['subtitle'] ?? null,
  60. 'description' => $item['summary'] ?? null,
  61. 'cover_image' => null, // 暂无封面图字段,留空走渐变
  62. 'cover_gradient' => $this->coverGradients[$colorIdx % count($this->coverGradients)],
  63. 'author' => $this->formatAuthor($item['studio'] ?? []),
  64. 'chapters' => $chapters,
  65. 'children_number'=> $item['childrenNumber'] ?? count($chapters),
  66. 'updated_at' => isset($item['updated_at'])
  67. ? \Carbon\Carbon::parse($item['updated_at'])->format('Y-m-d')
  68. : '',
  69. ];
  70. }
  71. // =========================================================================
  72. // index
  73. // =========================================================================
  74. public function index(Request $request)
  75. {
  76. $perPage = 10;
  77. $currentPage = (int) $request->get('page', 1);
  78. $result = $this->collectionService->getPublicList($perPage, $currentPage);
  79. // $result['data'] 是 CollectionResource collection,转为数组逐条加工
  80. $items = collect($result['data'])
  81. ->values()
  82. ->map(fn ($item, $i) => $this->formatForCard(
  83. is_array($item) ? $item : $item->toArray(request()),
  84. $i
  85. ));
  86. $total = $result['total'];
  87. $paginator = new \Illuminate\Pagination\LengthAwarePaginator(
  88. $items,
  89. $total,
  90. $perPage,
  91. $currentPage,
  92. ['path' => $request->url(), 'query' => $request->query()]
  93. );
  94. // 侧边栏作者列表:从当页数据聚合(如需完整列表可单独查询)
  95. $authors = $items
  96. ->groupBy(fn ($i) => $i['author']['name'])
  97. ->map(fn ($group, $name) => [
  98. 'name' => $name,
  99. 'initials' => $group->first()['author']['initials'],
  100. 'color' => $group->first()['author']['color'],
  101. 'avatar' => $group->first()['author']['avatar'],
  102. 'count' => $group->count(),
  103. ])
  104. ->values();
  105. return view('library.anthology.index', [
  106. 'anthologies' => $paginator,
  107. 'authors' => $authors,
  108. 'total' => $total,
  109. ]);
  110. }
  111. // =========================================================================
  112. // show
  113. // =========================================================================
  114. public function show(string $uid)
  115. {
  116. $result = $this->collectionService->getCollection($uid);
  117. if (isset($result['error'])) {
  118. abort($result['code'] ?? 404, $result['error']);
  119. }
  120. $raw = is_array($result['data'])
  121. ? $result['data']
  122. : $result['data']->toArray(request());
  123. $colorIdx = $this->colorIndex($raw['uid']);
  124. $author = $this->formatAuthor($raw['studio'] ?? []);
  125. // 只保留 level=1 的顶级章节
  126. $articles = collect($raw['article_list'] ?? [])
  127. ->filter(fn ($a) => ($a['level'] ?? 1) === 1)
  128. ->values()
  129. ->map(fn ($a, $i) => [
  130. 'id' => $a['article_id'],
  131. 'order' => $i + 1,
  132. 'title' => $a['title'],
  133. ])
  134. ->toArray();
  135. $anthology = [
  136. 'id' => $raw['uid'],
  137. 'title' => $raw['title'],
  138. 'subtitle' => $raw['subtitle'] ?? null,
  139. 'description' => $raw['summary'] ?? $raw['subtitle'] ?? null,
  140. 'about' => $raw['summary'] ?? null,
  141. 'cover_image' => null,
  142. 'cover_gradient' => $this->coverGradients[$colorIdx % count($this->coverGradients)],
  143. 'tags' => array_filter([$raw['lang'] ?? null]),
  144. 'language' => $raw['lang'] ?? null,
  145. 'category' => null,
  146. 'created_at' => isset($raw['created_at'])
  147. ? \Carbon\Carbon::parse($raw['created_at'])->format('Y-m-d')
  148. : '',
  149. 'updated_at' => isset($raw['updated_at'])
  150. ? \Carbon\Carbon::parse($raw['updated_at'])->format('Y-m-d')
  151. : '',
  152. 'children_number' => $raw['childrenNumber'] ?? 0,
  153. 'author' => array_merge($author, [
  154. 'bio' => null,
  155. 'article_count' => $raw['childrenNumber'] ?? 0,
  156. ]),
  157. 'articles' => $articles,
  158. ];
  159. // 相关文集:同作者其他文集
  160. $relatedResult = $this->collectionService->getPublicList(20, 1);
  161. $related = collect($relatedResult['data'])
  162. ->map(fn ($item) => is_array($item) ? $item : $item->toArray(request()))
  163. ->filter(fn ($item) =>
  164. $item['uid'] !== $uid &&
  165. ($item['studio']['id'] ?? '') === ($raw['studio']['id'] ?? '')
  166. )
  167. ->take(3)
  168. ->map(fn ($item) => [
  169. 'id' => $item['uid'],
  170. 'title' => $item['title'],
  171. 'author_name' => $item['studio']['nickName'] ?? $item['studio']['studioName'] ?? '',
  172. 'cover_gradient' => $this->coverGradients[
  173. $this->colorIndex($item['uid']) % count($this->coverGradients)
  174. ],
  175. ])
  176. ->values();
  177. return view('library.anthology.show', [
  178. 'anthology' => $anthology,
  179. 'related' => $related,
  180. ]);
  181. }
  182. }