Merge branch 'dev'
This commit is contained in:
@@ -99,6 +99,24 @@ public class BoardController {
|
|||||||
return ResponseEntity.noContent().build();
|
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")
|
@GetMapping("/tags")
|
||||||
public List<String> tags() {
|
public List<String> tags() {
|
||||||
return boardService.allTags();
|
return boardService.allTags();
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public class Post implements Serializable {
|
|||||||
private Integer viewCount;
|
private Integer viewCount;
|
||||||
private Boolean blocked;
|
private Boolean blocked;
|
||||||
private String blockReason;
|
private String blockReason;
|
||||||
|
private Boolean notice; // 공지(상단 고정) — 관리자만 설정
|
||||||
private String category; // 게시판 구분: community/saving/tips
|
private String category; // 게시판 구분: community/saving/tips
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public class PostDetail {
|
|||||||
private Integer viewCount;
|
private Integer viewCount;
|
||||||
private Boolean blocked;
|
private Boolean blocked;
|
||||||
private String blockReason;
|
private String blockReason;
|
||||||
|
private Boolean notice;
|
||||||
private String category;
|
private String category;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
@@ -38,6 +39,7 @@ public class PostDetail {
|
|||||||
.viewCount(p.getViewCount())
|
.viewCount(p.getViewCount())
|
||||||
.blocked(Boolean.TRUE.equals(p.getBlocked()))
|
.blocked(Boolean.TRUE.equals(p.getBlocked()))
|
||||||
.blockReason(p.getBlockReason())
|
.blockReason(p.getBlockReason())
|
||||||
|
.notice(Boolean.TRUE.equals(p.getNotice()))
|
||||||
.category(p.getCategory())
|
.category(p.getCategory())
|
||||||
.createdAt(p.getCreatedAt())
|
.createdAt(p.getCreatedAt())
|
||||||
.updatedAt(p.getUpdatedAt())
|
.updatedAt(p.getUpdatedAt())
|
||||||
@@ -54,6 +56,7 @@ public class PostDetail {
|
|||||||
.authorName(p.getAuthorName())
|
.authorName(p.getAuthorName())
|
||||||
.viewCount(p.getViewCount())
|
.viewCount(p.getViewCount())
|
||||||
.blocked(true)
|
.blocked(true)
|
||||||
|
.notice(Boolean.TRUE.equals(p.getNotice()))
|
||||||
.category(p.getCategory())
|
.category(p.getCategory())
|
||||||
.createdAt(p.getCreatedAt())
|
.createdAt(p.getCreatedAt())
|
||||||
.tags(List.of())
|
.tags(List.of())
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ public class PostRequest {
|
|||||||
/** 게시판 구분: community/saving/tips. 없으면 community */
|
/** 게시판 구분: community/saving/tips. 없으면 community */
|
||||||
private String category;
|
private String category;
|
||||||
|
|
||||||
|
/** 공지 등록 여부 (관리자만 반영). 작성 시 선택 */
|
||||||
|
private Boolean notice;
|
||||||
|
|
||||||
/** 선택한 태그 ID 목록 (DB에 등록된 태그만, 선택) */
|
/** 선택한 태그 ID 목록 (DB에 등록된 태그만, 선택) */
|
||||||
private List<Long> tagIds;
|
private List<Long> tagIds;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public class PostSummary {
|
|||||||
private Integer viewCount;
|
private Integer viewCount;
|
||||||
private Integer commentCount;
|
private Integer commentCount;
|
||||||
private Boolean blocked;
|
private Boolean blocked;
|
||||||
|
private Boolean notice;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private List<String> tags;
|
private List<String> tags;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,4 +35,7 @@ public interface PostMapper {
|
|||||||
int updateBlocked(@Param("id") Long id,
|
int updateBlocked(@Param("id") Long id,
|
||||||
@Param("blocked") boolean blocked,
|
@Param("blocked") boolean blocked,
|
||||||
@Param("blockReason") String blockReason);
|
@Param("blockReason") String blockReason);
|
||||||
|
|
||||||
|
int updateNotice(@Param("id") Long id,
|
||||||
|
@Param("notice") boolean notice);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,14 @@ public class BoardService {
|
|||||||
postMapper.updateBlocked(id, blocked, blocked ? reason : null);
|
postMapper.updateBlocked(id, blocked, blocked ? reason : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 게시글 공지 설정/해제 (관리자 전용) — 목록 최상단 고정 */
|
||||||
|
@Transactional
|
||||||
|
public void setNotice(Long id, boolean notice, SessionUser user) {
|
||||||
|
assertAdmin(user);
|
||||||
|
mustFindPost(id);
|
||||||
|
postMapper.updateNotice(id, notice);
|
||||||
|
}
|
||||||
|
|
||||||
/** Redis 로 (사용자, 게시글) 단위 첫 열람 여부 판단 → 중복 클릭 시 조회수 증가 방지 */
|
/** Redis 로 (사용자, 게시글) 단위 첫 열람 여부 판단 → 중복 클릭 시 조회수 증가 방지 */
|
||||||
private boolean shouldCountView(Long postId, SessionUser viewer) {
|
private boolean shouldCountView(Long postId, SessionUser viewer) {
|
||||||
if (viewer == null) {
|
if (viewer == null) {
|
||||||
@@ -108,6 +116,8 @@ public class BoardService {
|
|||||||
.content(req.getContent())
|
.content(req.getContent())
|
||||||
.authorId(author.getId())
|
.authorId(author.getId())
|
||||||
.authorName(author.getName())
|
.authorName(author.getName())
|
||||||
|
// 공지는 관리자만 설정 가능
|
||||||
|
.notice(isAdmin(author) && Boolean.TRUE.equals(req.getNotice()))
|
||||||
.category(toCategory(req.getCategory()))
|
.category(toCategory(req.getCategory()))
|
||||||
.build();
|
.build();
|
||||||
postMapper.insert(post);
|
postMapper.insert(post);
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ ALTER TABLE post ADD COLUMN IF NOT EXISTS block_reason VARCHAR(255) NULL;
|
|||||||
ALTER TABLE post ADD COLUMN IF NOT EXISTS category VARCHAR(30) NOT NULL DEFAULT 'community';
|
ALTER TABLE post ADD COLUMN IF NOT EXISTS category VARCHAR(30) NOT NULL DEFAULT 'community';
|
||||||
-- 큰 이미지(base64 인라인) 수용을 위해 content 를 LONGTEXT 로 확대 (멱등)
|
-- 큰 이미지(base64 인라인) 수용을 위해 content 를 LONGTEXT 로 확대 (멱등)
|
||||||
ALTER TABLE post MODIFY content LONGTEXT NOT NULL;
|
ALTER TABLE post MODIFY content LONGTEXT NOT NULL;
|
||||||
|
-- 공지(상단 고정) — 관리자만 설정. 목록에서 최상단 노출 (멱등)
|
||||||
|
ALTER TABLE post ADD COLUMN IF NOT EXISTS notice TINYINT(1) NOT NULL DEFAULT 0 COMMENT '공지(상단 고정)';
|
||||||
|
|
||||||
-- 태그 카테고리(그룹) — 예: 게시판, 게시판2 ...
|
-- 태그 카테고리(그룹) — 예: 게시판, 게시판2 ...
|
||||||
CREATE TABLE IF NOT EXISTS tag_category (
|
CREATE TABLE IF NOT EXISTS tag_category (
|
||||||
|
|||||||
@@ -35,11 +35,11 @@
|
|||||||
|
|
||||||
<select id="findPage" resultType="com.sb.web.board.dto.PostSummary">
|
<select id="findPage" resultType="com.sb.web.board.dto.PostSummary">
|
||||||
SELECT
|
SELECT
|
||||||
p.id, p.title, p.author_name, p.view_count, p.blocked, p.created_at,
|
p.id, p.title, p.author_name, p.view_count, p.blocked, p.notice, p.created_at,
|
||||||
(SELECT COUNT(*) FROM comment c WHERE c.post_id = p.id) AS comment_count
|
(SELECT COUNT(*) FROM comment c WHERE c.post_id = p.id) AS comment_count
|
||||||
FROM post p
|
FROM post p
|
||||||
<include refid="filter"/>
|
<include refid="filter"/>
|
||||||
ORDER BY p.id DESC
|
ORDER BY p.notice DESC, p.id DESC
|
||||||
LIMIT #{size} OFFSET #{offset}
|
LIMIT #{size} OFFSET #{offset}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -51,15 +51,15 @@
|
|||||||
|
|
||||||
<select id="findById" parameterType="long" resultType="com.sb.web.board.domain.Post">
|
<select id="findById" parameterType="long" resultType="com.sb.web.board.domain.Post">
|
||||||
SELECT id, title, content, author_id, author_name, view_count,
|
SELECT id, title, content, author_id, author_name, view_count,
|
||||||
blocked, block_reason, category, created_at, updated_at
|
blocked, block_reason, notice, category, created_at, updated_at
|
||||||
FROM post
|
FROM post
|
||||||
WHERE id = #{id}
|
WHERE id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insert" parameterType="com.sb.web.board.domain.Post"
|
<insert id="insert" parameterType="com.sb.web.board.domain.Post"
|
||||||
useGeneratedKeys="true" keyProperty="id">
|
useGeneratedKeys="true" keyProperty="id">
|
||||||
INSERT INTO post (title, content, author_id, author_name, view_count, category, created_at, updated_at)
|
INSERT INTO post (title, content, author_id, author_name, view_count, notice, category, created_at, updated_at)
|
||||||
VALUES (#{title}, #{content}, #{authorId}, #{authorName}, 0, #{category}, NOW(), NOW())
|
VALUES (#{title}, #{content}, #{authorId}, #{authorName}, 0, #{notice}, #{category}, NOW(), NOW())
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<update id="update" parameterType="com.sb.web.board.domain.Post">
|
<update id="update" parameterType="com.sb.web.board.domain.Post">
|
||||||
@@ -80,4 +80,8 @@
|
|||||||
UPDATE post SET blocked = #{blocked}, block_reason = #{blockReason} WHERE id = #{id}
|
UPDATE post SET blocked = #{blocked}, block_reason = #{blockReason} WHERE id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<update id="updateNotice">
|
||||||
|
UPDATE post SET notice = #{notice} WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user