DiscussionResource.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. <?php
  2. namespace App\Http\Resources\V3;
  3. use App\Http\Resources\Concerns\ResolvesActor;
  4. use App\Models\Discussion;
  5. use App\Services\V3\DiscussionService;
  6. use Illuminate\Http\Request;
  7. /**
  8. * 一条评论:话题(parent_id 为 null)或回复;或一条批注(note / commentary)。
  9. *
  10. * 批注与 issue / glossary 可带锚点(pos_start / pos_end / quote_*),指明它挂在句子文本的
  11. * 哪一段;其它评论线程的这几个字段为 null。glossary 的 title 是术语词头。
  12. *
  13. * 删除了但还挂着回复的话题以「墓碑」出现:deleted_at 有值,title / content 为 null,
  14. * 回复照常可读(与 Google Drive 的 comments.deleted、GitHub 的 deletedAt 同一做法)。
  15. *
  16. * editor 是对内容负责的人;AI 凭人类授权代写时,co_editor 是那个模型(仿 git 的
  17. * Co-Authored-By)。存量数据里 AI 写的行没有 co_editor,editor 直接是模型。
  18. *
  19. * **本类不查库。** 编辑者从预加载的 `user` / `aiModel` / `coEditor` 关系上取;权限与正文渲染
  20. * 由 {@see DiscussionService} 算好挂在模型的 `abilities` / `rendered` 属性上。
  21. *
  22. * `sentence` 只在按经文范围列出时有(`GET /v3/tipitaka-discussions/{channel}`):所在句子的坐标,
  23. * 由查询顺带取出(`sentence_*` 属性),本类不另查。
  24. *
  25. * 字段名改了两处(v2 → v3):res_type / res_id → target_type / target_id,
  26. * parent → parent_id;children_count → reply_count(只有话题有)。
  27. *
  28. * @mixin Discussion
  29. */
  30. class DiscussionResource extends BaseResource
  31. {
  32. use ResolvesActor;
  33. /**
  34. * @param Request $request
  35. * @return array{
  36. * id: string,
  37. * target_type: string,
  38. * target_id: string,
  39. * parent_id: string|null,
  40. * type: string,
  41. * severity: string|null,
  42. * status: string,
  43. * title: string|null,
  44. * content: string|null,
  45. * content_type: string,
  46. * html?: string,
  47. * summary?: string,
  48. * tpl_id: string|null,
  49. * pos_start: int|null,
  50. * pos_end: int|null,
  51. * quote_exact: string|null,
  52. * quote_prefix: string|null,
  53. * quote_suffix: string|null,
  54. * reply_count?: int,
  55. * sentence?: array{book: int, paragraph: int, word_start: int, word_end: int},
  56. * editor: array{id: string, nickName: string, userName: string, realName: string, sn: int, avatar: string|null, roles: array|null}|null,
  57. * co_editor: array{id: string, nickName: string, userName: string, realName: string, sn: int, avatar: string|null, roles: array|null}|null,
  58. * abilities: array{update: bool, delete: bool, close: bool, reply: bool},
  59. * created_at: string,
  60. * updated_at: string,
  61. * deleted_at: string|null
  62. * }
  63. */
  64. public function toArray($request): array
  65. {
  66. $tombstone = $this->trashed();
  67. return [
  68. 'id' => (string) $this->id,
  69. 'target_type' => $this->res_type,
  70. 'target_id' => $this->res_id,
  71. 'parent_id' => $this->parent,
  72. 'type' => $this->type,
  73. 'severity' => $this->severity,
  74. 'status' => $this->status,
  75. 'title' => $tombstone ? null : $this->title,
  76. 'content' => $tombstone ? null : $this->content,
  77. 'content_type' => $this->content_type,
  78. 'html' => $this->when($this->rendered !== null, fn () => $this->rendered['html']),
  79. 'summary' => $this->when($this->rendered !== null, fn () => $this->rendered['summary']),
  80. 'tpl_id' => $this->tpl_id,
  81. 'pos_start' => $this->pos_start,
  82. 'pos_end' => $this->pos_end,
  83. 'quote_exact' => $tombstone ? null : $this->quote_exact,
  84. 'quote_prefix' => $tombstone ? null : $this->quote_prefix,
  85. 'quote_suffix' => $tombstone ? null : $this->quote_suffix,
  86. 'reply_count' => $this->when($this->parent === null, fn () => (int) $this->reply_count),
  87. 'sentence' => $this->when(isset($this->sentence_book), fn () => [
  88. 'book' => (int) $this->sentence_book,
  89. 'paragraph' => (int) $this->sentence_paragraph,
  90. 'word_start' => (int) $this->sentence_word_start,
  91. 'word_end' => (int) $this->sentence_word_end,
  92. ]),
  93. 'editor' => $this->actor($this->user, $this->aiModel),
  94. // AI 代写时的模型;人类自己写的、存量数据都为 null
  95. 'co_editor' => $this->coEditor ? $this->actor(null, $this->coEditor) : null,
  96. 'abilities' => $this->abilities ?? ['update' => false, 'delete' => false, 'close' => false, 'reply' => false],
  97. 'created_at' => $this->created_at,
  98. 'updated_at' => $this->updated_at,
  99. 'deleted_at' => $this->deleted_at,
  100. ];
  101. }
  102. }