feat(board): 게시글 반응(추천/비추천) 백엔드
DB 스키마 + 백엔드 로직 + API 레이어를 구현.
- board_reaction_types/board_reactions 테이블 신규, boards.use_reaction·
active_reaction_types, board_posts.reaction_counts 컬럼 추가
- ReactionType(다국어 JSON+폴백)/Reaction 모델, Repository 2종(인터페이스 주입)
- ReactionService: 등록/전환/해제 통합, DB::transaction 원자 카운트,
use_reaction·비활성유형·본인글·slug 스코프 차단, before/after_react 훅
- POST boards/{slug}/posts/{postId}/react(회원) + GET admin/reaction-types
- ReactPostRequest + AvailableReactionType Rule, PostResource/BoardResource 확장
- 반응 활동 로그(add/change/remove) + ko/en 다국어 + API 레퍼런스 문서
- 테스트 30건(Unit/Feature, 87 assertions) green, 마이그레이션 왕복 안전성 검증
프론트엔드 화면·ja 언어팩·CHANGELOG·버전 bump 는 후속 단계(4~5)에서 진행.
This commit is contained in:
1 parent
3d0f260653
commit
4060a91d28
41 files changed
+2331
-1
No files matched your search
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Sirsoft\Board\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Api\Base\AuthBaseController;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Modules\Sirsoft\Board\Exceptions\PostNotFoundException;
|
||||
use Modules\Sirsoft\Board\Exceptions\ReactionNotAllowedException;
|
||||
use Modules\Sirsoft\Board\Http\Requests\User\ReactPostRequest;
|
||||
use Modules\Sirsoft\Board\Services\BoardService;
|
||||
use Modules\Sirsoft\Board\Services\ReactionService;
|
||||
|
||||
/**
|
||||
* 사용자용 반응 컨트롤러
|
||||
*
|
||||
* 게시글 반응(추천/비추천) 등록·전환·해제를 하나의 엔드포인트로 처리합니다.
|
||||
*/
|
||||
class ReactionController extends AuthBaseController
|
||||
{
|
||||
/**
|
||||
* ReactionController 생성자
|
||||
*
|
||||
* @param ReactionService $reactionService 반응 서비스
|
||||
* @param BoardService $boardService 게시판 서비스 (slug → 게시판 해석)
|
||||
*/
|
||||
public function __construct(
|
||||
private ReactionService $reactionService,
|
||||
private BoardService $boardService,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 게시글에 반응합니다 (등록/전환/해제 통합).
|
||||
*
|
||||
* @param ReactPostRequest $request 반응 요청 (reaction_type_id 검증)
|
||||
* @param string $slug 게시판 slug
|
||||
* @param int $postId 게시글 ID
|
||||
* @return JsonResponse 반응 결과 응답
|
||||
*/
|
||||
public function react(ReactPostRequest $request, string $slug, int $postId): JsonResponse
|
||||
{
|
||||
try {
|
||||
$board = $this->boardService->getBoardBySlug($slug, checkScope: false);
|
||||
|
||||
if (! $board) {
|
||||
return $this->notFound('sirsoft-board::messages.boards.error_404');
|
||||
}
|
||||
|
||||
$result = $this->reactionService->react(
|
||||
(int) Auth::id(),
|
||||
$board,
|
||||
$postId,
|
||||
(int) $request->validated('reaction_type_id'),
|
||||
);
|
||||
|
||||
$messageKey = match ($result['action']) {
|
||||
'change' => 'sirsoft-board::messages.reaction.change_success',
|
||||
'remove' => 'sirsoft-board::messages.reaction.remove_success',
|
||||
default => 'sirsoft-board::messages.reaction.add_success',
|
||||
};
|
||||
|
||||
return $this->success($messageKey, [
|
||||
'action' => $result['action'],
|
||||
'my_reaction_type_id' => $result['reaction_type_id'],
|
||||
'reaction_counts' => $result['reaction_counts'],
|
||||
]);
|
||||
} catch (PostNotFoundException $e) {
|
||||
return $this->notFound('sirsoft-board::messages.post.not_found');
|
||||
} catch (ReactionNotAllowedException $e) {
|
||||
return $this->error($e->getMessage(), 422, ['code' => 'reaction_not_allowed']);
|
||||
} catch (\Exception $e) {
|
||||
return $this->error('sirsoft-board::messages.reaction.failed', 500, $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user