AnthologyController.php 8.2 KB

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