MeReactionController.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. <?php
  2. namespace App\Http\Controllers\V3;
  3. use App\Http\Controllers\Concerns\ResolvesCurrentUser;
  4. use App\Http\Controllers\Controller;
  5. use App\Http\Requests\V3\IndexMeReactionRequest;
  6. use App\Http\Requests\V3\StoreMeReactionRequest;
  7. use App\Http\Resources\V3\ReactionResource;
  8. use App\Models\Reaction;
  9. use App\Services\V3\ReactionService;
  10. use Illuminate\Http\Request;
  11. /**
  12. * 当前登录用户自己的 reactions(点赞/收藏/书签/关注/下载记录)。
  13. *
  14. * 与 `/v3/reactions`(公共只读)分属两个控制器:这个控制器全线要求登录,
  15. * user_id 一律取当前登录用户,不接受客户端指定——替别人加关注是 target 的
  16. * 关注者管理,暂留在 v2,不属于这里。
  17. *
  18. * 取代 `POST /v2/like` 与 `DELETE /v2/like/{like}`。资源改名(v2 的 `like` →
  19. * v3 的 `reactions`)是根目录 CLAUDE.md「已登记的改名例外」里的唯一一条,
  20. * 底层仍是 `likes` 表。
  21. */
  22. class MeReactionController extends Controller
  23. {
  24. use ResolvesCurrentUser;
  25. public function __construct(private readonly ReactionService $reactions) {}
  26. /**
  27. * 列出我自己的 reactions
  28. *
  29. * @queryParam type string 操作类型。Enum: like,dislike,favorite,watch,bookmark,download
  30. * @queryParam target_type string target 的类型(task 任务、collection 文集、progress_chapter 书·章节、article 文章、terms 术语)。Enum: task,collection,progress_chapter,article,terms
  31. * @queryParam page integer 页码。Default: 1
  32. * @queryParam per_page integer 每页数量,最大 200。Default: 15
  33. */
  34. public function index(IndexMeReactionRequest $request)
  35. {
  36. $user = $this->currentUser($request);
  37. return ReactionResource::collection($this->reactions->listForUser(
  38. $user['user_uid'],
  39. $request->validated(),
  40. $request->integer('per_page', 15),
  41. ));
  42. }
  43. /**
  44. * 添加一条我自己的 reaction(幂等)
  45. *
  46. * 唯一键是 (type, target_id, user_id):重复提交不会产生第二条记录,返回同一条。
  47. * 首次创建返回 201,命中已存在的记录返回 200。
  48. *
  49. * 201 不是手写的状态码:Laravel 的 ResourceResponse::calculateStatus() 看模型的
  50. * wasRecentlyCreated,新建时自动给 201。这里不要改成固定 200——幂等端点区分
  51. * 「真的建了」与「早就有了」对调用方有用。
  52. *
  53. * 返回的是 reaction 资源本身(调用方需要 id 才能后续删除),**不含计数**。
  54. * 计数是共享聚合值,归 `GET /v3/reactions/tally` 管;前端用乐观更新 ±1,
  55. * 下次读 tally 时校正。见 v3-resource skill 硬规范第 2 条。
  56. *
  57. * @responseStatus 201 首次创建。响应体与 200 同形,区别只在这一条是本次新建的
  58. *
  59. * @bodyParam type string required 操作类型。Enum: like,dislike,favorite,watch,bookmark,download
  60. * @bodyParam target_id string required target 的 uuid
  61. * @bodyParam target_type string required target 的类型(task 任务、collection 文集、progress_chapter 书·章节、article 文章、terms 术语)。Enum: task,collection,progress_chapter,article,terms
  62. * @bodyParam context string 附加上下文,最长 128 字符
  63. */
  64. public function store(StoreMeReactionRequest $request)
  65. {
  66. $user = $this->currentUser($request);
  67. return new ReactionResource(
  68. $this->reactions->add($user['user_uid'], $request->validated())
  69. );
  70. }
  71. /**
  72. * 删除我自己的 reaction
  73. *
  74. * 只能删自己那条(user_id 与当前用户不符返回 403)。
  75. *
  76. * 成功返回 **204 空体**:硬删就是删了,没有资源可回,计数也不在这里给
  77. * (见 v3-resource skill 硬规范第 2 条)。
  78. *
  79. * @urlParam reaction string required reaction 的 uuid
  80. *
  81. * @responseStatus 204 删除成功,无响应体
  82. * @responseStatus 403 只能删除自己的 reaction
  83. */
  84. public function destroy(Request $request, Reaction $reaction)
  85. {
  86. $this->reactions->remove($reaction, $this->currentUser($request)['user_uid']);
  87. return response()->noContent();
  88. }
  89. }