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
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package com.mulmi.backend.domain.notice.controller;

import com.mulmi.backend.domain.notice.dto.request.NoticeCreateRequestDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticeDetailResponseDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticePageResponseDTO;
import com.mulmi.backend.domain.notice.exception.code.NoticeSuccessCode;
import com.mulmi.backend.domain.notice.service.NoticeService;
import com.mulmi.backend.global.apiPayload.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand Down Expand Up @@ -51,4 +57,18 @@ public ApiResponse<NoticeDetailResponseDTO> getNotice(
noticeService.getNotice(noticeId)
);
}

// 공지사항 작성
@Operation(summary = "공지사항 작성")
@PostMapping
@PreAuthorize("hasAnyRole('ASSISTANT', 'WORKER')")
public ApiResponse<NoticeDetailResponseDTO> createNotice(
@AuthenticationPrincipal Long userId,
@RequestBody @Valid NoticeCreateRequestDTO dto
) {
return ApiResponse.onSuccess(
NoticeSuccessCode.NOTICE_CREATED,
noticeService.createNotice(userId, dto)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
package com.mulmi.backend.domain.notice.converter;

import com.mulmi.backend.domain.notice.dto.request.NoticeCreateRequestDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticeDetailResponseDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticeSummaryResponseDTO;
import com.mulmi.backend.domain.notice.entity.Notice;
import com.mulmi.backend.domain.user.entity.User;

public class NoticeConverter {

private NoticeConverter() {
}

public static Notice toNotice(NoticeCreateRequestDTO dto, User author) {
return Notice.builder()
.title(dto.title().trim())
.content(dto.content().trim())
.important(dto.important())
.author(author)
.build();
}

public static NoticeSummaryResponseDTO toNoticeSummaryResponseDTO(Notice notice) {
return new NoticeSummaryResponseDTO(
notice.getId(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.mulmi.backend.domain.notice.dto.request;

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

public record NoticeCreateRequestDTO(
@NotBlank(message = "공지사항 제목은 필수입니다.")
@Size(max = 200, message = "공지사항 제목은 200자를 넘을 수 없습니다.")
String title,

@NotBlank(message = "공지사항 내용은 필수입니다.")
String content,

boolean important
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public enum NoticeSuccessCode implements BaseSuccessCode {
HttpStatus.OK,
"NOTICE200_2",
"공지사항을 조회했습니다."
),
NOTICE_CREATED(
HttpStatus.CREATED,
"NOTICE201_1",
"공지사항을 작성했습니다."
);

private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mulmi.backend.domain.notice.service;

import com.mulmi.backend.domain.notice.dto.request.NoticeCreateRequestDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticeDetailResponseDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticePageResponseDTO;

Expand All @@ -8,4 +9,6 @@ public interface NoticeService {
NoticePageResponseDTO getNotices(Boolean important, int page, int size);

NoticeDetailResponseDTO getNotice(Long noticeId);

NoticeDetailResponseDTO createNotice(Long authorId, NoticeCreateRequestDTO dto);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package com.mulmi.backend.domain.notice.service;

import com.mulmi.backend.domain.notice.converter.NoticeConverter;
import com.mulmi.backend.domain.notice.dto.request.NoticeCreateRequestDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticeDetailResponseDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticePageResponseDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticeSummaryResponseDTO;
import com.mulmi.backend.domain.notice.entity.Notice;
import com.mulmi.backend.domain.notice.exception.NoticeException;
import com.mulmi.backend.domain.notice.exception.code.NoticeErrorCode;
import com.mulmi.backend.domain.notice.repository.NoticeRepository;
import com.mulmi.backend.domain.user.entity.User;
import com.mulmi.backend.domain.user.enums.UserStatus;
import com.mulmi.backend.domain.user.exception.UserException;
import com.mulmi.backend.domain.user.exception.code.UserErrorCode;
import com.mulmi.backend.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
Expand All @@ -21,6 +27,7 @@
public class NoticeServiceImpl implements NoticeService {

private final NoticeRepository noticeRepository;
private final UserRepository userRepository;

@Override
public NoticePageResponseDTO getNotices(Boolean important, int page, int size) {
Expand All @@ -45,4 +52,23 @@ public NoticeDetailResponseDTO getNotice(Long noticeId) {

return NoticeConverter.toNoticeDetailResponseDTO(notice);
}

@Override
@Transactional
public NoticeDetailResponseDTO createNotice(
Long authorId,
NoticeCreateRequestDTO dto
) {
User author = userRepository.findById(authorId)
.orElseThrow(() -> new UserException(UserErrorCode.USER_NOT_FOUND));

if (author.getStatus() != UserStatus.ACTIVE) {
throw new UserException(UserErrorCode.INACTIVE_USER);
}

Notice notice = NoticeConverter.toNotice(dto, author);
Notice savedNotice = noticeRepository.save(notice);

return NoticeConverter.toNoticeDetailResponseDTO(savedNotice);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mulmi.backend.domain.notice.service;

import com.mulmi.backend.domain.notice.dto.request.NoticeCreateRequestDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticeDetailResponseDTO;
import com.mulmi.backend.domain.notice.dto.response.NoticePageResponseDTO;
import com.mulmi.backend.domain.notice.entity.Notice;
Expand All @@ -9,6 +10,9 @@
import com.mulmi.backend.domain.user.entity.User;
import com.mulmi.backend.domain.user.enums.UserRole;
import com.mulmi.backend.domain.user.enums.UserStatus;
import com.mulmi.backend.domain.user.exception.UserException;
import com.mulmi.backend.domain.user.exception.code.UserErrorCode;
import com.mulmi.backend.domain.user.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
Expand All @@ -24,6 +28,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.BDDMockito.given;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;

@ExtendWith(MockitoExtension.class)
Expand All @@ -32,6 +37,9 @@ class NoticeServiceImplTest {
@Mock
private NoticeRepository noticeRepository;

@Mock
private UserRepository userRepository;

@InjectMocks
private NoticeServiceImpl noticeService;

Expand Down Expand Up @@ -118,6 +126,52 @@ void getNoticeThrowsWhenNoticeDoesNotExist() {
.isEqualTo(NoticeErrorCode.NOTICE_NOT_FOUND);
}

@Test
void createNoticeSavesNoticeWithAuthenticatedAuthor() {
User author = createAuthor();
NoticeCreateRequestDTO request = new NoticeCreateRequestDTO(
" 대여실 운영 안내 ",
" 운영 시간을 안내합니다. ",
true
);
given(userRepository.findById(2L)).willReturn(Optional.of(author));
given(noticeRepository.save(any(Notice.class)))
.willAnswer(invocation -> invocation.getArgument(0));

NoticeDetailResponseDTO result = noticeService.createNotice(2L, request);

assertThat(result.title()).isEqualTo("대여실 운영 안내");
assertThat(result.content()).isEqualTo("운영 시간을 안내합니다.");
assertThat(result.important()).isTrue();
assertThat(result.authorId()).isEqualTo(2L);
verify(noticeRepository).save(any(Notice.class));
}

@Test
void createNoticeThrowsWhenAuthorIsInactive() {
User inactiveAuthor = User.builder()
.id(2L)
.loginId("assistant")
.password("encoded-password")
.name("조교")
.email("assistant@example.com")
.phoneNumber("01012345678")
.role(UserRole.ASSISTANT)
.status(UserStatus.SUSPENDED)
.build();
NoticeCreateRequestDTO request = new NoticeCreateRequestDTO(
"공지사항",
"공지사항 내용",
false
);
given(userRepository.findById(2L)).willReturn(Optional.of(inactiveAuthor));

assertThatThrownBy(() -> noticeService.createNotice(2L, request))
.isInstanceOf(UserException.class)
.extracting("code")
.isEqualTo(UserErrorCode.INACTIVE_USER);
}

private User createAuthor() {
return User.builder()
.id(2L)
Expand Down
Loading