a4b6c3fd2d
CI / build (push) Failing after 12m6s
- 게시판 목록 화면의 태그 필터가 전체 태그를 보여주던 문제(매핑 무시) 수정 - 글쓰기와 동일한 listWritableGroups 기준으로 일관 적용 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
182 lines
7.3 KiB
Java
182 lines
7.3 KiB
Java
package com.sb.web.board.controller;
|
|
|
|
import com.sb.web.auth.dto.SessionUser;
|
|
import com.sb.web.auth.web.AuthInterceptor;
|
|
import com.sb.web.board.dto.CommentRequest;
|
|
import com.sb.web.board.dto.CommentResponse;
|
|
import com.sb.web.board.dto.PageResponse;
|
|
import com.sb.web.board.dto.PostDetail;
|
|
import com.sb.web.board.dto.PostRequest;
|
|
import com.sb.web.board.dto.PostSummary;
|
|
import com.sb.web.board.dto.Recommender;
|
|
import com.sb.web.board.dto.TagCategoryResponse;
|
|
import com.sb.web.board.dto.VoteRequest;
|
|
import com.sb.web.board.dto.VoteResponse;
|
|
import com.sb.web.board.service.BoardService;
|
|
import com.sb.web.board.service.TagService;
|
|
import jakarta.validation.Valid;
|
|
import lombok.RequiredArgsConstructor;
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* 자유게시판 API. (전체 로그인 필요 — WebConfig 인터셉터로 /api/board/** 보호)
|
|
* GET /api/board/posts 목록(페이징·태그·검색)
|
|
* GET /api/board/posts/{id} 상세(조회수 증가)
|
|
* POST /api/board/posts 작성
|
|
* PUT /api/board/posts/{id} 수정(작성자/관리자)
|
|
* DELETE /api/board/posts/{id} 삭제(작성자/관리자)
|
|
* GET /api/board/tags 전체 태그
|
|
* POST /api/board/posts/{id}/comments 댓글 작성
|
|
* DELETE /api/board/comments/{commentId} 댓글 삭제(작성자/관리자)
|
|
*/
|
|
@RestController
|
|
@RequestMapping("/api/board")
|
|
@RequiredArgsConstructor
|
|
public class BoardController {
|
|
|
|
private final BoardService boardService;
|
|
private final TagService tagService;
|
|
|
|
@GetMapping("/posts")
|
|
public PageResponse<PostSummary> list(
|
|
@RequestParam(defaultValue = "1") int page,
|
|
@RequestParam(defaultValue = "10") int size,
|
|
@RequestParam(required = false) String category,
|
|
@RequestParam(required = false) String tag,
|
|
@RequestParam(required = false) String keyword,
|
|
@RequestParam(required = false) String searchType) {
|
|
return boardService.list(page, size, category, tag, keyword, searchType);
|
|
}
|
|
|
|
@GetMapping("/posts/{id}")
|
|
public PostDetail get(
|
|
@PathVariable Long id,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return boardService.get(id, current);
|
|
}
|
|
|
|
@PostMapping("/posts")
|
|
public ResponseEntity<PostDetail> create(
|
|
@Valid @RequestBody PostRequest req,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return ResponseEntity.status(HttpStatus.CREATED).body(boardService.create(req, current));
|
|
}
|
|
|
|
@PutMapping("/posts/{id}")
|
|
public PostDetail update(
|
|
@PathVariable Long id,
|
|
@Valid @RequestBody PostRequest req,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return boardService.update(id, req, current);
|
|
}
|
|
|
|
@DeleteMapping("/posts/{id}")
|
|
public ResponseEntity<Void> delete(
|
|
@PathVariable Long id,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
boardService.delete(id, current);
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
/** 열람 제한 (관리자) */
|
|
@PostMapping("/posts/{id}/block")
|
|
public ResponseEntity<Void> block(
|
|
@PathVariable Long id,
|
|
@RequestBody(required = false) Map<String, String> body,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
boardService.setBlocked(id, true, body != null ? body.get("reason") : null, current);
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
/** 열람 제한 해제 (관리자) */
|
|
@PostMapping("/posts/{id}/unblock")
|
|
public ResponseEntity<Void> unblock(
|
|
@PathVariable Long id,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
boardService.setBlocked(id, false, null, current);
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
/** 공지 등록 (관리자) — 목록 최상단 고정 */
|
|
@PostMapping("/posts/{id}/notice")
|
|
public ResponseEntity<Void> notice(
|
|
@PathVariable Long id,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
boardService.setNotice(id, true, current);
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
/** 공지 해제 (관리자) */
|
|
@PostMapping("/posts/{id}/unnotice")
|
|
public ResponseEntity<Void> unnotice(
|
|
@PathVariable Long id,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
boardService.setNotice(id, false, current);
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
/** 목록 태그 필터 — 게시판 지정 시 그 게시판에 매핑된 그룹의 태그만 */
|
|
@GetMapping("/tags")
|
|
public List<String> tags(@RequestParam(required = false) String category) {
|
|
if (category == null || category.isBlank()) {
|
|
return boardService.allTags();
|
|
}
|
|
return tagService.listWritableGroups(category).stream()
|
|
.flatMap(g -> g.getTags().stream())
|
|
.map(com.sb.web.board.dto.TagResponse::getName)
|
|
.distinct()
|
|
.toList();
|
|
}
|
|
|
|
/** 글 작성 시 선택 가능한 태그 (해당 게시판에 매핑된 그룹만) */
|
|
@GetMapping("/tag-groups")
|
|
public List<TagCategoryResponse> tagGroups(@RequestParam(required = false) String category) {
|
|
return tagService.listWritableGroups(category);
|
|
}
|
|
|
|
@PostMapping("/posts/{id}/comments")
|
|
public ResponseEntity<CommentResponse> addComment(
|
|
@PathVariable Long id,
|
|
@Valid @RequestBody CommentRequest req,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return ResponseEntity.status(HttpStatus.CREATED).body(boardService.addComment(id, req, current));
|
|
}
|
|
|
|
@DeleteMapping("/comments/{commentId}")
|
|
public ResponseEntity<Void> deleteComment(
|
|
@PathVariable Long commentId,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
boardService.deleteComment(commentId, current);
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
/** 게시글 추천/비추천 (토글) */
|
|
@PostMapping("/posts/{id}/vote")
|
|
public VoteResponse votePost(
|
|
@PathVariable Long id,
|
|
@Valid @RequestBody VoteRequest req,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return boardService.votePost(id, req.getType(), current);
|
|
}
|
|
|
|
/** 게시글 추천(👍)한 사람 목록 */
|
|
@GetMapping("/posts/{id}/recommenders")
|
|
public List<Recommender> recommenders(@PathVariable Long id) {
|
|
return boardService.recommenders(id);
|
|
}
|
|
|
|
/** 댓글 추천/비추천 (토글) */
|
|
@PostMapping("/comments/{commentId}/vote")
|
|
public VoteResponse voteComment(
|
|
@PathVariable Long commentId,
|
|
@Valid @RequestBody VoteRequest req,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return boardService.voteComment(commentId, req.getType(), current);
|
|
}
|
|
}
|