Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion k8s/helm-value.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
image:
tag: v0.1.2
tag: v0.1.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.earseo.core.common.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {
}
26 changes: 26 additions & 0 deletions src/main/java/com/earseo/core/common/exception/NoticeError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.earseo.core.common.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum NoticeError implements ErrorCodeInterface{

NOTICE_NOT_FOUND("NTC001", "공지사항을 찾을 수 없습니다", HttpStatus.NOT_FOUND),
;

private final String status;
private final String message;
private final HttpStatus httpStatus;

@Override
public ErrorCode getErrorCode() {
return ErrorCode.builder()
.status(status)
.message(message)
.httpStatus(httpStatus)
.build();
}
}
60 changes: 60 additions & 0 deletions src/main/java/com/earseo/core/controller/NoticeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.earseo.core.controller;

import com.earseo.core.common.BaseResponse;
import com.earseo.core.dto.request.NoticeCreateRequest;
import com.earseo.core.dto.request.NoticeUpdateRequest;
import com.earseo.core.dto.response.NoticeDeleteResponse;
import com.earseo.core.dto.response.NoticePageResponse;
import com.earseo.core.dto.response.NoticeResponse;
import com.earseo.core.service.NoticeService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
public class NoticeController {

private final NoticeService noticeService;

@GetMapping("/api/core/notice/{noticeId}")
public ResponseEntity<BaseResponse<NoticeResponse>> getNotice(
@PathVariable Long noticeId
) {
return ResponseEntity.ok(BaseResponse.ok(noticeService.getNotice(noticeId)));
}

@GetMapping("/api/core/notice")
public ResponseEntity<BaseResponse<NoticePageResponse>> getNoticeList(
@PageableDefault(size = 10, sort = "id", direction = Sort.Direction.DESC)
Pageable pageable
) {
return ResponseEntity.ok(BaseResponse.ok(noticeService.getNoticeList(pageable)));
}

@PostMapping("/api/admin/core/notice")
public ResponseEntity<BaseResponse<NoticeResponse>> createNotice(
@RequestBody @Valid NoticeCreateRequest request
){
return ResponseEntity.ok(BaseResponse.ok(noticeService.createNotice(request)));
}

@PutMapping("/api/admin/core/notice/{noticeId}")
public ResponseEntity<BaseResponse<NoticeResponse>> updateNotice(
@PathVariable Long noticeId,
@RequestBody @Valid NoticeUpdateRequest request
){
return ResponseEntity.ok(BaseResponse.ok(noticeService.updateNotice(noticeId, request)));
}

@DeleteMapping("/api/admin/core/notice/{noticeId}")
public ResponseEntity<BaseResponse<NoticeDeleteResponse>> deleteNotice(
@PathVariable Long noticeId
){
return ResponseEntity.ok(BaseResponse.ok(noticeService.deleteNotice(noticeId)));
}
}
7 changes: 7 additions & 0 deletions src/main/java/com/earseo/core/dto/etl/NoticePageItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.earseo.core.dto.etl;

public record NoticePageItem(
Long noticeId,
String title
) {
}
14 changes: 14 additions & 0 deletions src/main/java/com/earseo/core/dto/request/NoticeCreateRequest.java

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

빈 값이나 공백만 있는 문자열이 들어갈 수도 있을거 같은데 검증 어노테이션 추가하는 방향은 어떨까요?

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.earseo.core.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record NoticeCreateRequest(
@NotBlank(message = "제목은 필수입니다")
@Size(min = 1, max = 255, message = "제목은 1자 이상 255자 이하여야 합니다")
String title,
@NotBlank(message = "내용은 필수입니다")
@Size(min = 1, max = 1000, message = "내용은 1자 이상 1000자 이하여야 합니다")
String content
) {
}
14 changes: 14 additions & 0 deletions src/main/java/com/earseo/core/dto/request/NoticeUpdateRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.earseo.core.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record NoticeUpdateRequest(
@NotBlank(message = "제목은 필수입니다")
@Size(min = 1, max = 255, message = "제목은 1자 이상 255자 이하여야 합니다")
String title,
@NotBlank(message = "내용은 필수입니다")
@Size(min = 1, max = 1000, message = "내용은 1자 이상 1000자 이하여야 합니다")
String content
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.earseo.core.dto.response;

public record NoticeDeleteResponse(
Long noticeId
) {
}
36 changes: 36 additions & 0 deletions src/main/java/com/earseo/core/dto/response/NoticePageResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.earseo.core.dto.response;

import com.earseo.core.dto.etl.NoticePageItem;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.data.domain.Slice;

import java.util.List;

public record NoticePageResponse(
@Schema(description = "공지사항 목록 (페이징/정렬 반영)")
List<NoticePageItem> content,
@Schema(description = "현재 페이지 번호 (0부터 시작)", example = "0")
int number,
@Schema(description = "페이지 크기", example = "10")
int size,
@Schema(description = "첫 페이지 여부", example = "true")
boolean isFirst,
@Schema(description = "마지막 페이지 여부", example = "false")
boolean isLast,
@Schema(description = "다음 페이지 존재 여부", example = "true")
boolean hasNext,
@Schema(description = "이전 페이지 존재 여부", example = "false")
boolean hasPrevious
) {
public static NoticePageResponse toDto(Slice<NoticePageItem> page) {
return new NoticePageResponse(
page.getContent(),
page.getNumber(),
page.getSize(),
page.isFirst(),
page.isLast(),
page.hasNext(),
page.hasPrevious()
);
}
}
23 changes: 23 additions & 0 deletions src/main/java/com/earseo/core/dto/response/NoticeResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.earseo.core.dto.response;

import com.earseo.core.entity.Notice;

import java.time.format.DateTimeFormatter;

public record NoticeResponse(
Long noticeId,
String noticeTitle,
String noticeContent,
String createdAt,
String updatedAt
) {
public static NoticeResponse toDto(Notice notice) {
return new NoticeResponse(
notice.getId(),
notice.getTitle(),
notice.getContent(),
notice.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")),
notice.getUpdatedAt().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))
);
}
}
46 changes: 46 additions & 0 deletions src/main/java/com/earseo/core/entity/Notice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.earseo.core.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class Notice {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "title")
private String title;

@Column(name = "content", columnDefinition = "TEXT")
private String content;

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime updatedAt;

public void update(String title, String content) {
if (title != null && !title.isBlank()) {
this.title = title;
}
if (content != null && !content.isBlank()) {
this.content = content;
}
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/earseo/core/repository/NoticeRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.earseo.core.repository;

import com.earseo.core.dto.etl.NoticePageItem;
import com.earseo.core.entity.Notice;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface NoticeRepository extends JpaRepository<Notice, Long> {

@Query("SELECT new com.earseo.core.dto.etl.NoticePageItem(n.id, n.title) FROM Notice n")
Slice<NoticePageItem> findNoticeList(Pageable pageable);
}
74 changes: 74 additions & 0 deletions src/main/java/com/earseo/core/service/NoticeService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.earseo.core.service;

import com.earseo.core.common.exception.BaseException;
import com.earseo.core.common.exception.NoticeError;
import com.earseo.core.dto.etl.NoticePageItem;
import com.earseo.core.dto.request.NoticeCreateRequest;
import com.earseo.core.dto.request.NoticeUpdateRequest;
import com.earseo.core.dto.response.NoticeDeleteResponse;
import com.earseo.core.dto.response.NoticePageResponse;
import com.earseo.core.dto.response.NoticeResponse;
import com.earseo.core.entity.Notice;
import com.earseo.core.repository.NoticeRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Slf4j
public class NoticeService {

private final NoticeRepository noticeRepository;

@Transactional(readOnly = true)
public NoticeResponse getNotice(Long noticeId) {
Notice notice = noticeRepository.findById(noticeId)
.orElseThrow(() -> new BaseException(NoticeError.NOTICE_NOT_FOUND));

return NoticeResponse.toDto(notice);
}

@Transactional(readOnly = true)
public NoticePageResponse getNoticeList(Pageable pageable) {
Slice<NoticePageItem> notices = noticeRepository.findNoticeList(pageable);
return NoticePageResponse.toDto(notices);
}

@Transactional
public NoticeResponse createNotice(NoticeCreateRequest request) {
Notice notice = Notice.builder()
.title(request.title())
.content(request.content())
.build();

noticeRepository.save(notice);

return NoticeResponse.toDto(notice);
}

@Transactional
public NoticeResponse updateNotice(Long noticeId, NoticeUpdateRequest noticeUpdateRequest) {
Notice notice = noticeRepository.findById(noticeId)
.orElseThrow(() -> new BaseException(NoticeError.NOTICE_NOT_FOUND));

notice.update(
noticeUpdateRequest.title(), noticeUpdateRequest.content()
);

return NoticeResponse.toDto(notice);
}

@Transactional
public NoticeDeleteResponse deleteNotice(Long noticeId) {
Notice notice = noticeRepository.findById(noticeId)
.orElseThrow(() -> new BaseException(NoticeError.NOTICE_NOT_FOUND));

noticeRepository.delete(notice);

return new NoticeDeleteResponse(noticeId);
}
}
Loading