2
0

ChapterContentController.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. <?php
  2. namespace App\Http\Controllers;
  3. use Illuminate\Http\Request;
  4. use Illuminate\Support\Facades\Log;
  5. use App\Models\Sentence;
  6. use App\Models\Channel;
  7. use App\Models\PaliText;
  8. use Illuminate\Support\Str;
  9. use App\Http\Api\MdRender;
  10. use App\Http\Api\ChannelApi;
  11. use App\Http\Api\StudioApi;
  12. use App\Http\Api\AuthApi;
  13. use App\Http\Resources\TocResource;
  14. use App\Services\PaliContentService;
  15. class ChapterContentController extends Controller
  16. {
  17. protected $result = [
  18. "uid" => '',
  19. "title" => '',
  20. "path" => [],
  21. "sub_title" => '',
  22. "summary" => '',
  23. "content" => '',
  24. "content_type" => "html",
  25. "toc" => [],
  26. "status" => 30,
  27. "lang" => "",
  28. "created_at" => "",
  29. "updated_at" => "",
  30. ];
  31. protected $selectCol = [
  32. 'uid',
  33. 'book_id',
  34. 'paragraph',
  35. 'word_start',
  36. "word_end",
  37. 'channel_uid',
  38. 'content',
  39. 'content_type',
  40. 'editor_uid',
  41. 'acceptor_uid',
  42. 'pr_edit_at',
  43. 'fork_at',
  44. 'create_time',
  45. 'modify_time',
  46. 'created_at',
  47. 'updated_at',
  48. ];
  49. protected $wbwChannels = [];
  50. protected $debug = [];
  51. protected $userUuid = null;
  52. /**
  53. * Display a listing of the resource.
  54. *
  55. * @return \Illuminate\Http\Response
  56. */
  57. public function index()
  58. {
  59. //
  60. }
  61. /**
  62. * Store a newly created resource in storage.
  63. *
  64. * @param \Illuminate\Http\Request $request
  65. * @return \Illuminate\Http\Response
  66. */
  67. public function store(Request $request)
  68. {
  69. //
  70. }
  71. /**
  72. * Store a newly created resource in storage.
  73. * @param \Illuminate\Http\Request $request
  74. * @param string $id
  75. * @return \Illuminate\Http\Response
  76. */
  77. public function show(Request $request, string $id)
  78. {
  79. $paliService = app(PaliContentService::class);
  80. if ($request->has('debug')) {
  81. $this->debug = explode(',', $request->get('debug'));
  82. }
  83. $user = AuthApi::current($request);
  84. if ($user) {
  85. $this->userUuid = $user['user_uid'];
  86. }
  87. //
  88. $sentId = \explode('-', $id);
  89. $channels = [];
  90. if ($request->has('channels')) {
  91. if (strpos($request->get('channels'), ',') === FALSE) {
  92. $_channels = explode('_', $request->get('channels'));
  93. } else {
  94. $_channels = explode(',', $request->get('channels'));
  95. }
  96. foreach ($_channels as $key => $channel) {
  97. if (Str::isUuid($channel)) {
  98. $channels[] = $channel;
  99. }
  100. }
  101. }
  102. $mode = $request->get('mode', 'read');
  103. if ($mode === 'read') {
  104. //阅读模式加载html格式原文
  105. $channelId = ChannelApi::getSysChannel('_System_Pali_VRI_');
  106. } else {
  107. //翻译模式加载json格式原文
  108. $channelId = ChannelApi::getSysChannel('_System_Wbw_VRI_');
  109. }
  110. if ($channelId !== false) {
  111. $channels[] = $channelId;
  112. }
  113. $chapter = PaliText::where('book', $sentId[0])->where('paragraph', $sentId[1])->first();
  114. if (!$chapter) {
  115. return $this->error("no data");
  116. }
  117. $paraFrom = $sentId[1];
  118. $paraTo = $sentId[1] + $chapter->chapter_len - 1;
  119. if (empty($chapter->toc)) {
  120. $this->result['title'] = "unknown";
  121. } else {
  122. $this->result['title'] = $chapter->toc;
  123. $this->result['sub_title'] = $chapter->toc;
  124. $this->result['path'] = json_decode($chapter->path);
  125. }
  126. //获取标题
  127. $heading = PaliText::select(["book", "paragraph", "level"])
  128. ->where('book', $sentId[0])
  129. ->whereBetween('paragraph', [$paraFrom, $paraTo])
  130. ->where('level', '<', 8)
  131. ->get();
  132. //将标题段落转成索引数组 以便输出标题层级
  133. $indexedHeading = [];
  134. foreach ($heading as $key => $value) {
  135. # code...
  136. $indexedHeading["{$value->book}-{$value->paragraph}"] = $value->level;
  137. }
  138. #获取channel索引表
  139. $tranChannels = [];
  140. $channelInfo = Channel::whereIn("uid", $channels)
  141. ->select(['uid', 'type', 'lang', 'name'])->get();
  142. foreach ($channelInfo as $key => $value) {
  143. # code...
  144. if ($value->type === "translation") {
  145. $tranChannels[] = $value->uid;
  146. }
  147. }
  148. $indexChannel = [];
  149. $indexChannel = $paliService->getChannelIndex($channels);
  150. //获取wbw channel
  151. //目前默认的 wbw channel 是第一个translation channel
  152. //TODO 处理不存在的channel id
  153. foreach ($channels as $key => $value) {
  154. # code...
  155. if (
  156. isset($indexChannel[$value]) &&
  157. $indexChannel[$value]->type === 'translation'
  158. ) {
  159. $this->wbwChannels[] = $value;
  160. break;
  161. }
  162. }
  163. $title = Sentence::select($this->selectCol)
  164. ->where('book_id', $sentId[0])
  165. ->where('paragraph', $sentId[1])
  166. ->whereIn('channel_uid', $tranChannels)
  167. ->first();
  168. if ($title) {
  169. $this->result['title'] = MdRender::render($title->content, [$title->channel_uid]);
  170. $mdRender = new MdRender(['format' => 'simple']);
  171. $this->result['title_text'] = $mdRender->convert($title->content, [$title->channel_uid]);
  172. }
  173. /**
  174. * 获取句子数据
  175. * 算法:
  176. * 1. 如果标题和下一级第一个标题之间有段落。只输出这些段落和子目录
  177. * 2. 如果标题和下一级第一个标题之间没有间隔 且 chapter 长度大于10000个字符 且有子目录,只输出子目录
  178. * 3. 如果二者都不是,lazy load
  179. */
  180. //1. 计算 标题和下一级第一个标题之间 是否有间隔
  181. $nextChapter = PaliText::where('book', $sentId[0])
  182. ->where('paragraph', ">", $sentId[1])
  183. ->where('level', '<', 8)
  184. ->orderBy('paragraph')
  185. ->value('paragraph');
  186. $between = $nextChapter - $sentId[1];
  187. //查找子目录
  188. $chapterLen = $chapter->chapter_len;
  189. $toc = PaliText::where('book', $sentId[0])
  190. ->whereBetween('paragraph', [$paraFrom + 1, $paraFrom + $chapterLen - 1])
  191. ->where('level', '<', 8)
  192. ->orderBy('paragraph')
  193. ->select(['book', 'paragraph', 'level', 'toc'])
  194. ->get();
  195. $maxLen = 3000;
  196. if ($between > 1) {
  197. //有间隔
  198. $paraTo = $nextChapter - 1;
  199. } else {
  200. if ($chapter->chapter_strlen > $maxLen) {
  201. if (count($toc) > 0) {
  202. //有子目录只输出标题和目录
  203. $paraTo = $paraFrom;
  204. } else {
  205. //没有子目录 全部输出
  206. }
  207. } else {
  208. //章节小。全部输出 不输出子目录
  209. $toc = [];
  210. }
  211. }
  212. $pFrom = $request->get('from', $paraFrom);
  213. $pTo = $request->get('to', $paraTo);
  214. //根据句子的长度找到这次应该加载的段落
  215. $paliText = PaliText::select(['paragraph', 'lenght'])
  216. ->where('book', $sentId[0])
  217. ->whereBetween('paragraph', [$pFrom, $pTo])
  218. ->orderBy('paragraph')
  219. ->get();
  220. $sumLen = 0;
  221. $currTo = $pTo;
  222. foreach ($paliText as $para) {
  223. $sumLen += $para->lenght;
  224. if ($sumLen > $maxLen) {
  225. $currTo = $para->paragraph;
  226. break;
  227. }
  228. }
  229. $record = Sentence::select($this->selectCol)
  230. ->where('book_id', $sentId[0])
  231. ->whereBetween('paragraph', [$pFrom, $currTo])
  232. ->whereIn('channel_uid', $channels)
  233. ->orderBy('paragraph')
  234. ->orderBy('word_start')
  235. ->get();
  236. if (count($record) === 0) {
  237. return $this->error("no data");
  238. }
  239. $this->result['content'] = json_encode($paliService->makeContentObj($record, $mode, $indexChannel), JSON_UNESCAPED_UNICODE);
  240. $this->result['content_type'] = 'json';
  241. if (!$request->has('from')) {
  242. //第一次才显示toc
  243. $this->result['toc'] = TocResource::collection($toc);
  244. }
  245. if ($currTo < $pTo) {
  246. $this->result['from'] = $currTo + 1;
  247. $this->result['to'] = $pTo;
  248. $this->result['paraId'] = $id;
  249. $this->result['channels'] = $request->get('channels');
  250. $this->result['mode'] = $request->get('mode');
  251. }
  252. return $this->ok($this->result);
  253. }
  254. /**
  255. * Update the specified resource in storage.
  256. *
  257. * @param \Illuminate\Http\Request $request
  258. * @param int $id
  259. * @return \Illuminate\Http\Response
  260. */
  261. public function update(Request $request, $id)
  262. {
  263. //
  264. }
  265. /**
  266. * Remove the specified resource from storage.
  267. *
  268. * @param int $id
  269. * @return \Illuminate\Http\Response
  270. */
  271. public function destroy($id)
  272. {
  273. //
  274. }
  275. }