TipitakaReadParaController.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Resources\V3Resource;
  4. use App\Services\PaliContentService;
  5. use Illuminate\Http\Request;
  6. use Illuminate\Support\Str;
  7. use Illuminate\Validation\ValidationException;
  8. class TipitakaReadParaController extends Controller
  9. {
  10. /**
  11. * 按段落区间读取译文
  12. *
  13. * 阅读模式下按 book + 段落区间取内容,空段落会被跳过。区间由 para 到 to,
  14. * 不传 to 则只取一段;to 小于 para 返回 422。
  15. *
  16. * 这个接口不走 Eloquent 分页,meta 是手工给的:一次请求即一页。
  17. *
  18. * @unauthenticated
  19. *
  20. * @queryParam book integer required 典籍 id
  21. * @queryParam para integer required 起始段落号
  22. * @queryParam to integer 结束段落号(含)。不传则等于 para
  23. * @queryParam channel string required 译文 channel 的 uuid
  24. * @queryParam format string 内容格式。Enum: html,markdown,react,text Default: html
  25. * @queryParam view string 输出口径:display 整段合并,sentences 逐句,all 两者都给。
  26. * Enum: display,sentences,all Default: display
  27. */
  28. public function index(Request $request, PaliContentService $paliService)
  29. {
  30. $data = $request->validate([
  31. 'book' => 'required|integer',
  32. 'para' => 'required|integer',
  33. 'to' => 'integer',
  34. 'channel' => 'required|uuid',
  35. 'format' => 'string|in:html,markdown,react,text',
  36. 'view' => 'string|in:display,sentences,all', // display:整段合并 sentences:逐句
  37. ]);
  38. $from = $data['para'];
  39. $to = $data['to'] ?? $from;
  40. if ($to < $from) {
  41. throw ValidationException::withMessages(['to' => __('site.invalid_parameter')]);
  42. }
  43. $format = $data['format'] ?? 'html';
  44. $view = $data['view'] ?? 'display';
  45. $items = [];
  46. foreach (range($from, $to) as $para) {
  47. $paragraph = $paliService->readParagraph(
  48. (int) $data['book'],
  49. (int) $para,
  50. $data['channel'],
  51. $format
  52. );
  53. if (empty($paragraph['display'])) {
  54. continue;
  55. }
  56. $items[] = $this->filterView($paragraph, $view);
  57. }
  58. return V3Resource::collection($items)->additional(['meta' => [
  59. 'current_page' => 1,
  60. 'per_page' => $to - $from + 1,
  61. 'total' => count($items),
  62. 'last_page' => 1,
  63. ]]);
  64. }
  65. /**
  66. * 读取单个段落
  67. *
  68. * id 是 `{book}-{para}` 复合形式,例如 `9001-1`。格式不对或 channel 不是
  69. * uuid 返回 422;段落无内容返回 404。
  70. *
  71. * @unauthenticated
  72. *
  73. * @urlParam tipitaka_read_para string required 段落 id,格式 {book}-{para}。Example: 9001-1
  74. *
  75. * @queryParam channel string required 译文 channel 的 uuid
  76. * @queryParam format string 内容格式。Enum: html,markdown,react,text Default: html
  77. * @queryParam view string 输出口径。Enum: display,sentences,all Default: display
  78. */
  79. public function show(Request $request, string $id, PaliContentService $paliService)
  80. {
  81. $arrId = explode('-', $id);
  82. if (count($arrId) !== 2 || ! is_numeric($arrId[0]) || ! is_numeric($arrId[1])) {
  83. throw ValidationException::withMessages(['id' => __('site.invalid_parameter')]);
  84. }
  85. $channel = $request->input('channel');
  86. if (! Str::isUuid($channel)) {
  87. throw ValidationException::withMessages(['channel' => __('site.invalid_parameter')]);
  88. }
  89. $book = (int) $arrId[0];
  90. $para = (int) $arrId[1];
  91. $paragraph = $paliService->readParagraph(
  92. $book,
  93. $para,
  94. $channel,
  95. $request->input('format', 'html')
  96. );
  97. if (empty($paragraph['display'])) {
  98. abort(404, __('site.not_found'));
  99. }
  100. return V3Resource::make($this->filterView($paragraph, $request->input('view', 'display')));
  101. }
  102. /**
  103. * 按 view 裁剪输出。display 只要段落 html,sentences 只要句子列表,all 两者都要。
  104. *
  105. * @param array{para: int, display: string, sentences: array} $paragraph
  106. */
  107. protected function filterView(array $paragraph, string $view): array
  108. {
  109. return match ($view) {
  110. 'sentences' => ['para' => $paragraph['para'], 'sentences' => $paragraph['sentences']],
  111. 'all' => $paragraph,
  112. default => ['para' => $paragraph['para'], 'display' => $paragraph['display']],
  113. };
  114. }
  115. }