TipitakaReadingController.php 4.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. <?php
  2. namespace App\Http\Controllers\V3;
  3. use App\Http\Controllers\Controller;
  4. use App\Http\Requests\V3\TipitakaReadingRequest;
  5. use App\Http\Resources\V3\ReadParagraphResource;
  6. use App\Services\V3\TipitakaReadingService;
  7. /**
  8. * 三藏译文的阅读与下载:按 channel 取已翻译的段落,游标式分块。
  9. *
  10. * 路径开头是「领域-功能」而不是资源坐标:`channel + book + para` 在 wikipali 里是
  11. * 通用坐标,sentences、wbw、批注、进度全都用它,放在路径开头会让每个新能力来抢
  12. * 同一个前缀,也看不出这条 API 到底干什么。所以路径是 `/v3/tipitaka-reading/{channel}`,
  13. * 坐标降成 filter。
  14. *
  15. * 曾经叫 `/v3/tipitaka-read-para` 与 `/v3/tipitaka-read-chapter`,中间短暂叫过
  16. * `/v3/channels/{channel}/books/{book}/…`,见 CLAUDE.md 的「v2 → v3 对照表」。
  17. *
  18. * **这不是 v2 `chapter-content` / `paragraph-content` 的替代品**:那两个支持多
  19. * channel 与 edit 模式、返回 sentenceIds 给编辑器;这里只做单 channel 的只读渲染。
  20. */
  21. class TipitakaReadingController extends Controller
  22. {
  23. public function __construct(private readonly TipitakaReadingService $reading) {}
  24. /**
  25. * 读取译文
  26. *
  27. * 取这个 channel 已经翻译的段落,按 (book, para) 升序,用游标分块。
  28. * 不带任何查询串就是从头取整个 channel——下载场景要的就是这个;
  29. * 阅读场景用 `book` + `chapter` 或 `book` + `para`/`to` 把范围收窄。
  30. *
  31. * 续传把 `meta.next_cursor` 原样传回 `after` 即可,它为 null 表示取完了。
  32. * **不要自己拼游标**:它的内部形式(`{book}-{para}`)是实现细节,会变。
  33. *
  34. * `total` / `remaining` 只在带了 `book` 过滤时才给:不带过滤时结果集是整个
  35. * channel(实测最大的有 52 万段、跨 217 本书),每翻一页都去 count 一次不划算。
  36. * 无过滤时用 `next_cursor` 是否为 null 判断结束,不要指望百分比。
  37. *
  38. * @unauthenticated
  39. *
  40. * @urlParam channel string required 译文 channel 的 uuid
  41. *
  42. * @queryParam book integer 典籍 id。与 chapter / para / to 一起用时必填。Example: 9002
  43. * @queryParam chapter integer 章节起始段落号,服务端按 chapter_len 展开成段落区间。
  44. * 与 para / to 互斥
  45. * @queryParam para integer 起始段落号
  46. * @queryParam to integer 结束段落号(含)。不传则等于 para,即只取一段
  47. * @queryParam after string 游标,取 meta.next_cursor 原样回传。Example: 9002-15
  48. * @queryParam page_size integer 每块大小,含义由 unit 决定。上限 unit=para 时 200、
  49. * unit=byte 时 5000,超出不报错按上限算,meta.page_size 回实际生效的值。
  50. * Default: 10
  51. * @queryParam unit string 每块大小的单位:para 按段落数,byte 按段落长度累加。
  52. * 两种单位下每块都至少一段、至多 200 段。Enum: para,byte Default: para
  53. * @queryParam format string 内容格式。Enum: html,markdown,react,text Default: html
  54. * @queryParam include string 输出口径:display 整段合并,sentences 逐句,all 两者都给。
  55. * 不叫 view 是因为 v2 的 `view=` 是必填互斥的业务路径开关,名字撞上会误导。
  56. * Enum: display,sentences,all Default: display
  57. *
  58. * @responseStatus 404 指定的 chapter 在 pali_texts 里不存在
  59. *
  60. * @meta page_size integer 每块大小(按 unit 计)
  61. * @meta page_size_unit string 每块大小的单位:para 或 byte
  62. * @meta next_cursor string 下一块的游标,原样传回 after;为 null 表示取完
  63. * @meta total integer 过滤范围内已翻译的段落总数。仅在带了 book 过滤时出现
  64. * @meta remaining integer 本块之后过滤范围内还剩多少段。仅在带了 book 过滤时出现
  65. */
  66. public function __invoke(TipitakaReadingRequest $request, string $channel)
  67. {
  68. $page = $this->reading->page($channel, $request->validated());
  69. return ReadParagraphResource::collection($page['items'])
  70. ->additional(['meta' => $page['meta']]);
  71. }
  72. }