- tag_category_board 매핑 테이블 추가(그룹 ↔ community/saving/tips) - TagCategory 응답에 boards 추가, 생성/수정 시 매핑 저장(유효 게시판만) - listWritableGroups(category): 해당 게시판에 매핑된 그룹만(매핑 없으면 전체=하위호환) - GET /board/tag-groups?category=... 로 게시판별 필터 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -125,10 +125,10 @@ public class BoardController {
|
|||||||
return boardService.allTags();
|
return boardService.allTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 글 작성 시 선택 가능한 태그 (게시판 설정 카테고리로 제한) */
|
/** 글 작성 시 선택 가능한 태그 (해당 게시판에 매핑된 그룹만) */
|
||||||
@GetMapping("/tag-groups")
|
@GetMapping("/tag-groups")
|
||||||
public List<TagCategoryResponse> tagGroups() {
|
public List<TagCategoryResponse> tagGroups(@RequestParam(required = false) String category) {
|
||||||
return tagService.listWritableGroups();
|
return tagService.listWritableGroups(category);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/posts/{id}/comments")
|
@PostMapping("/posts/{id}/comments")
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import jakarta.validation.constraints.NotBlank;
|
|||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 태그 카테고리 생성/수정 요청.
|
* 태그 카테고리 생성/수정 요청.
|
||||||
*/
|
*/
|
||||||
@@ -15,4 +17,7 @@ public class TagCategoryRequest {
|
|||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Integer sortOrder;
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/** 이 그룹을 사용할 게시판 (community/saving/tips). 비어있으면 전체 게시판. */
|
||||||
|
private List<String> boards;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ public class TagCategoryResponse {
|
|||||||
private String name;
|
private String name;
|
||||||
private Integer sortOrder;
|
private Integer sortOrder;
|
||||||
private List<TagResponse> tags;
|
private List<TagResponse> tags;
|
||||||
|
private List<String> boards; // 이 그룹을 사용할 게시판 (비어있으면 전체)
|
||||||
|
|
||||||
public static TagCategoryResponse of(TagCategory c, List<TagResponse> tags) {
|
public static TagCategoryResponse of(TagCategory c, List<TagResponse> tags, List<String> boards) {
|
||||||
return TagCategoryResponse.builder()
|
return TagCategoryResponse.builder()
|
||||||
.id(c.getId())
|
.id(c.getId())
|
||||||
.name(c.getName())
|
.name(c.getName())
|
||||||
.sortOrder(c.getSortOrder())
|
.sortOrder(c.getSortOrder())
|
||||||
.tags(tags)
|
.tags(tags)
|
||||||
|
.boards(boards)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.sb.web.board.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 태그 카테고리(그룹) ↔ 게시판 매핑 (tag_category_board).
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface TagCategoryBoardMapper {
|
||||||
|
|
||||||
|
/** 카테고리가 노출될 게시판 목록 */
|
||||||
|
List<String> findBoards(@Param("categoryId") Long categoryId);
|
||||||
|
|
||||||
|
void deleteByCategory(@Param("categoryId") Long categoryId);
|
||||||
|
|
||||||
|
void insert(@Param("categoryId") Long categoryId, @Param("board") String board);
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import com.sb.web.board.dto.BoardSettingResponse;
|
|||||||
import com.sb.web.board.dto.TagRequest;
|
import com.sb.web.board.dto.TagRequest;
|
||||||
import com.sb.web.board.dto.TagResponse;
|
import com.sb.web.board.dto.TagResponse;
|
||||||
import com.sb.web.board.mapper.BoardSettingMapper;
|
import com.sb.web.board.mapper.BoardSettingMapper;
|
||||||
|
import com.sb.web.board.mapper.TagCategoryBoardMapper;
|
||||||
import com.sb.web.board.mapper.TagCategoryMapper;
|
import com.sb.web.board.mapper.TagCategoryMapper;
|
||||||
import com.sb.web.board.mapper.TagMapper;
|
import com.sb.web.board.mapper.TagMapper;
|
||||||
import com.sb.web.common.exception.ApiException;
|
import com.sb.web.common.exception.ApiException;
|
||||||
@@ -18,6 +19,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 태그 카테고리 + 태그 관리 서비스 (관리자 기능 + 그룹 조회).
|
* 태그 카테고리 + 태그 관리 서비스 (관리자 기능 + 그룹 조회).
|
||||||
@@ -29,6 +31,10 @@ public class TagService {
|
|||||||
private final TagCategoryMapper categoryMapper;
|
private final TagCategoryMapper categoryMapper;
|
||||||
private final TagMapper tagMapper;
|
private final TagMapper tagMapper;
|
||||||
private final BoardSettingMapper boardSettingMapper;
|
private final BoardSettingMapper boardSettingMapper;
|
||||||
|
private final TagCategoryBoardMapper categoryBoardMapper;
|
||||||
|
|
||||||
|
/** 게시판 매핑에 허용되는 값 */
|
||||||
|
private static final Set<String> VALID_BOARDS = Set.of("community", "saving", "tips");
|
||||||
|
|
||||||
/** 카테고리별로 묶은 전체 태그 (관리 화면용) */
|
/** 카테고리별로 묶은 전체 태그 (관리 화면용) */
|
||||||
public List<TagCategoryResponse> listGrouped() {
|
public List<TagCategoryResponse> listGrouped() {
|
||||||
@@ -39,20 +45,39 @@ public class TagService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 글 작성 시 선택 가능한 태그 그룹 — 게시판 설정 카테고리로 제한 (없으면 전체) */
|
/**
|
||||||
public List<TagCategoryResponse> listWritableGroups() {
|
* 글 작성 시 선택 가능한 태그 그룹 — 해당 게시판에 매핑된 그룹만.
|
||||||
Long catId = boardSettingMapper.findTagCategoryId();
|
* 매핑이 하나도 없는 그룹은 전체 게시판에서 노출(하위호환).
|
||||||
if (catId == null) {
|
*/
|
||||||
return listGrouped();
|
public List<TagCategoryResponse> listWritableGroups(String boardCategory) {
|
||||||
|
List<TagCategoryResponse> result = new ArrayList<>();
|
||||||
|
for (TagCategory c : categoryMapper.findAll()) {
|
||||||
|
TagCategoryResponse g = toGroup(c);
|
||||||
|
List<String> boards = g.getBoards();
|
||||||
|
if (boards == null || boards.isEmpty() || boards.contains(boardCategory)) {
|
||||||
|
result.add(g);
|
||||||
}
|
}
|
||||||
TagCategory c = categoryMapper.findById(catId);
|
}
|
||||||
return c == null ? listGrouped() : List.of(toGroup(c));
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TagCategoryResponse toGroup(TagCategory c) {
|
private TagCategoryResponse toGroup(TagCategory c) {
|
||||||
List<TagResponse> tags = tagMapper.findByCategoryId(c.getId())
|
List<TagResponse> tags = tagMapper.findByCategoryId(c.getId())
|
||||||
.stream().map(TagResponse::from).toList();
|
.stream().map(TagResponse::from).toList();
|
||||||
return TagCategoryResponse.of(c, tags);
|
List<String> boards = categoryBoardMapper.findBoards(c.getId());
|
||||||
|
return TagCategoryResponse.of(c, tags, boards);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 카테고리의 게시판 매핑을 교체 저장 (유효 게시판 값만, 중복 제거) */
|
||||||
|
private void saveBoards(Long categoryId, List<String> boards) {
|
||||||
|
categoryBoardMapper.deleteByCategory(categoryId);
|
||||||
|
if (boards == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boards.stream()
|
||||||
|
.filter(b -> b != null && VALID_BOARDS.contains(b))
|
||||||
|
.distinct()
|
||||||
|
.forEach(b -> categoryBoardMapper.insert(categoryId, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================== 게시판 설정 ===================== */
|
/* ===================== 게시판 설정 ===================== */
|
||||||
@@ -85,7 +110,8 @@ public class TagService {
|
|||||||
.sortOrder(req.getSortOrder() != null ? req.getSortOrder() : 0)
|
.sortOrder(req.getSortOrder() != null ? req.getSortOrder() : 0)
|
||||||
.build();
|
.build();
|
||||||
categoryMapper.insert(c);
|
categoryMapper.insert(c);
|
||||||
return TagCategoryResponse.of(c, List.of());
|
saveBoards(c.getId(), req.getBoards());
|
||||||
|
return TagCategoryResponse.of(c, List.of(), categoryBoardMapper.findBoards(c.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -100,11 +126,12 @@ public class TagService {
|
|||||||
c.setSortOrder(req.getSortOrder());
|
c.setSortOrder(req.getSortOrder());
|
||||||
}
|
}
|
||||||
categoryMapper.update(c);
|
categoryMapper.update(c);
|
||||||
|
saveBoards(id, req.getBoards());
|
||||||
List<TagResponse> tags = tagMapper.findByCategoryId(id).stream().map(TagResponse::from).toList();
|
List<TagResponse> tags = tagMapper.findByCategoryId(id).stream().map(TagResponse::from).toList();
|
||||||
return TagCategoryResponse.of(c, tags);
|
return TagCategoryResponse.of(c, tags, categoryBoardMapper.findBoards(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 카테고리 삭제 — 소속 태그와 그 연결(post_tag)까지 함께 삭제 */
|
/** 카테고리 삭제 — 소속 태그와 그 연결(post_tag)·게시판 매핑까지 함께 삭제 */
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteCategory(Long id) {
|
public void deleteCategory(Long id) {
|
||||||
mustFindCategory(id);
|
mustFindCategory(id);
|
||||||
@@ -112,6 +139,7 @@ public class TagService {
|
|||||||
tagMapper.deletePostTagsByTagId(t.getId());
|
tagMapper.deletePostTagsByTagId(t.getId());
|
||||||
tagMapper.delete(t.getId());
|
tagMapper.delete(t.getId());
|
||||||
}
|
}
|
||||||
|
categoryBoardMapper.deleteByCategory(id);
|
||||||
categoryMapper.delete(id);
|
categoryMapper.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,15 @@ CREATE TABLE IF NOT EXISTS tag (
|
|||||||
-- 기존 tag 테이블에 category_id 가 없으면 추가 (멱등)
|
-- 기존 tag 테이블에 category_id 가 없으면 추가 (멱등)
|
||||||
ALTER TABLE tag ADD COLUMN IF NOT EXISTS category_id BIGINT NULL;
|
ALTER TABLE tag ADD COLUMN IF NOT EXISTS category_id BIGINT NULL;
|
||||||
|
|
||||||
|
-- 태그 카테고리(그룹) ↔ 게시판 매핑. 어느 게시판(community/saving/tips)에서 이 그룹을 쓸지 지정.
|
||||||
|
-- 매핑이 하나도 없는 카테고리는 모든 게시판에서 사용(하위호환).
|
||||||
|
CREATE TABLE IF NOT EXISTS tag_category_board (
|
||||||
|
tag_category_id BIGINT NOT NULL,
|
||||||
|
board_category VARCHAR(30) NOT NULL COMMENT 'community / saving / tips',
|
||||||
|
PRIMARY KEY (tag_category_id, board_category),
|
||||||
|
KEY idx_tcb_board (board_category)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- 게시글-태그 (다대다)
|
-- 게시글-태그 (다대다)
|
||||||
CREATE TABLE IF NOT EXISTS post_tag (
|
CREATE TABLE IF NOT EXISTS post_tag (
|
||||||
post_id BIGINT NOT NULL,
|
post_id BIGINT NOT NULL,
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.sb.web.board.mapper.TagCategoryBoardMapper">
|
||||||
|
|
||||||
|
<select id="findBoards" parameterType="long" resultType="string">
|
||||||
|
SELECT board_category FROM tag_category_board WHERE tag_category_id = #{categoryId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<delete id="deleteByCategory" parameterType="long">
|
||||||
|
DELETE FROM tag_category_board WHERE tag_category_id = #{categoryId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<insert id="insert">
|
||||||
|
INSERT INTO tag_category_board (tag_category_id, board_category)
|
||||||
|
VALUES (#{categoryId}, #{board})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
Reference in New Issue
Block a user