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
Expand Up @@ -3,7 +3,9 @@
import com.somemore.auth.annotation.CurrentUser;
import com.somemore.global.common.response.ApiResponse;
import com.somemore.note.dto.SendNoteToCenterRequestDto;
import com.somemore.note.dto.SendNoteToVolunteerRequestDto;
import com.somemore.note.usecase.SendNoteToCenterUseCase;
import com.somemore.note.usecase.SendNoteToVolunteerUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand All @@ -23,13 +25,24 @@
public class NoteCommandApiController {

private final SendNoteToCenterUseCase sendNoteToCenterUseCase;
private final SendNoteToVolunteerUseCase sendNoteToVolunteerUseCase;

@Secured("ROLE_VOLUNTEER")
@Operation(summary = "봉사자 to 기관 쪽지 송신")
@PostMapping("/volunteer-to-center")
public ApiResponse<Long> sendNoteToCenter(@CurrentUser UUID userId, @Valid @RequestBody SendNoteToCenterRequestDto requestDto) {
public ApiResponse<Long> sendNoteToCenter(@CurrentUser UUID volunteerId, @Valid @RequestBody SendNoteToCenterRequestDto requestDto) {

Long noteId = sendNoteToCenterUseCase.sendNoteToCenter(userId, requestDto);
Long noteId = sendNoteToCenterUseCase.sendNoteToCenter(volunteerId, requestDto);

return ApiResponse.ok(201, noteId, "쪽지 송신 성공");
}

@Secured("ROLE_CENTER")
@Operation(summary = "기관 to 봉사자 쪽지 송신")
@PostMapping("/center-to-volunteer")
public ApiResponse<Long> sendNoteToCenter(@CurrentUser UUID centerId, @Valid @RequestBody SendNoteToVolunteerRequestDto requestDto) {

Long noteId = sendNoteToVolunteerUseCase.sendNoteToVolunteer(centerId, requestDto);

return ApiResponse.ok(201, noteId, "쪽지 송신 성공");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.somemore.note.dto;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.somemore.note.domain.Note;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

import java.util.UUID;

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record SendNoteToVolunteerRequestDto(

@Schema(description = "쪽지 수신 봉사자 아이디", example = "123e4567-e89b-12d3-a456-426614174000")
@NotNull(message = "수신 봉사자 아이디는 필수 값입니다.")
UUID receiverId,

@Schema(description = "쪽지 제목", example = "서울 도서관입니다. 답변 드립니다")
@NotNull(message = "쪽지 제목은 필수 값입니다.")
String title,

@Schema(description = "쪽지 내용", example = "~~~~한 일을 할 것 같습니다.")
@NotNull(message = "쪽지 내용은 필수 값입니다.")
String content
) {
public Note toEntity(UUID senderId){
return Note.create(senderId, receiverId, title, content);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@
@Transactional
public class SendNoteToCenterService implements SendNoteToCenterUseCase {

private final NoteRepository noteRepository;
private final CenterQueryUseCase centerQueryUseCase;
private final NoteRepository noteRepository;

@Override
public Long sendNoteToCenter(UUID senderId, SendNoteToCenterRequestDto requestDto) {

centerQueryUseCase.validateCenterExists(requestDto.receiverId());

Note note = requestDto.toEntity(senderId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.somemore.note.service;

import com.somemore.note.domain.Note;
import com.somemore.note.dto.SendNoteToVolunteerRequestDto;
import com.somemore.note.repository.NoteRepository;
import com.somemore.note.usecase.SendNoteToVolunteerUseCase;
import com.somemore.volunteer.usecase.VolunteerQueryUseCase;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;


@RequiredArgsConstructor
@Service
@Transactional
public class SendNoteToVolunteerService implements SendNoteToVolunteerUseCase {

private final VolunteerQueryUseCase volunteerQueryUseCase;
private final NoteRepository noteRepository;

@Override
public Long sendNoteToVolunteer(UUID senderId, SendNoteToVolunteerRequestDto requestDto) {
volunteerQueryUseCase.validateVolunteerExists(senderId);

Note note = requestDto.toEntity(senderId);
noteRepository.save(note);
return note.getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.somemore.note.usecase;

import com.somemore.note.dto.SendNoteToVolunteerRequestDto;

import java.util.UUID;

public interface SendNoteToVolunteerUseCase {
Long sendNoteToVolunteer(UUID senderId, SendNoteToVolunteerRequestDto requestDto);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
public interface VolunteerJpaRepository extends JpaRepository<Volunteer, Long> {

List<Volunteer> findAllByIdInAndDeletedFalse(List<UUID> ids);

boolean existsByIdAndDeletedIsFalse(UUID id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ public interface VolunteerRepository {
List<Volunteer> findAllByIds(List<UUID> volunteerIds);

List<VolunteerSimpleInfo> findSimpleInfoByIds(List<UUID> ids);

boolean existsByVolunteerId(UUID volunteerId);

default boolean doesNotExistsByVolunteerId(UUID volunteerId) { return !existsByVolunteerId(volunteerId); }
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ private <T> Optional<T> findDynamicField(UUID id, Path<T> field) {
);
}

@Override
public boolean existsByVolunteerId(UUID volunteerId) {
return volunteerJpaRepository.existsByIdAndDeletedIsFalse(volunteerId);
}

private BooleanExpression isNotDeleted() {
return volunteer.deleted.isFalse();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ public List<VolunteerSimpleInfo> getVolunteerSimpleInfosByIds(List<UUID> ids) {
return volunteerRepository.findSimpleInfoByIds(ids);
}

@Override
public void validateVolunteerExists(UUID volunteerId) {
if (volunteerRepository.doesNotExistsByVolunteerId(volunteerId)) {
throw new BadRequestException(NOT_EXISTS_VOLUNTEER.getMessage());
}
}

private Volunteer findVolunteer(UUID volunteerId) {
return volunteerRepository.findById(volunteerId)
.orElseThrow(() -> new BadRequestException(NOT_EXISTS_VOLUNTEER));
Expand All @@ -101,4 +108,5 @@ private VolunteerDetail findVolunteerDetail(UUID volunteerId) {
return volunteerDetailRepository.findByVolunteerId(volunteerId)
.orElseThrow(() -> new BadRequestException(NOT_EXISTS_VOLUNTEER));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ public interface VolunteerQueryUseCase {
List<Volunteer> getAllByIds(List<UUID> volunteerIds);

List<VolunteerSimpleInfo> getVolunteerSimpleInfosByIds(List<UUID> ids);

void validateVolunteerExists(UUID volunteerId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import com.somemore.ControllerTestSupport;
import com.somemore.WithMockCustomUser;
import com.somemore.note.dto.SendNoteToCenterRequestDto;
import com.somemore.note.dto.SendNoteToVolunteerRequestDto;
import com.somemore.note.usecase.SendNoteToCenterUseCase;
import com.somemore.note.usecase.SendNoteToVolunteerUseCase;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
Expand All @@ -22,6 +24,9 @@ class NoteCommandApiControllerTest extends ControllerTestSupport{
@MockBean
private SendNoteToCenterUseCase sendNoteToCenterUseCase;

@MockBean
private SendNoteToVolunteerUseCase sendNoteToVolunteerUseCase;

@DisplayName("봉사자는 기관에게 쪽지를 보낼수 있다 (controller)")
@Test
@WithMockCustomUser
Expand Down Expand Up @@ -66,4 +71,29 @@ void sendNoteToCenter_ValidationFail() throws Exception {
.andExpect(status().isBadRequest());
}

@DisplayName("기관은 봉사자에게 쪽지를 보낼수 있다 (controller)")
@Test
@WithMockCustomUser(role = "CENTER")
void sendNoteToVolunteer_Success() throws Exception {
// Given
UUID receiverId = UUID.randomUUID();
SendNoteToCenterRequestDto requestDto = new SendNoteToCenterRequestDto(
receiverId,
"쪽지 제목 문의 드릴게 있습니다.",
"쪽지 내용"
);

when(sendNoteToVolunteerUseCase.sendNoteToVolunteer(any(UUID.class), any(SendNoteToVolunteerRequestDto.class)))
.thenReturn(1L);

// When & Then
mockMvc.perform(post("/api/note/center-to-volunteer")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(requestDto)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(201))
.andExpect(jsonPath("$.data").value(1L))
.andExpect(jsonPath("$.message").value("쪽지 송신 성공"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.somemore.note.service;

import com.somemore.IntegrationTestSupport;
import com.somemore.note.domain.Note;
import com.somemore.note.dto.SendNoteToVolunteerRequestDto;
import com.somemore.note.repository.NoteJpaRepository;
import com.somemore.volunteer.domain.Volunteer;
import com.somemore.volunteer.repository.VolunteerJpaRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;

import static com.somemore.auth.oauth.OAuthProvider.NAVER;
import static org.junit.jupiter.api.Assertions.*;

@Transactional
class SendNoteToVolunteerServiceTest extends IntegrationTestSupport {

@Autowired
private SendNoteToVolunteerService sendNoteToVolunteerService;

@Autowired
private NoteJpaRepository noteJpaRepository;

@Autowired
private VolunteerJpaRepository volunteerJpaRepository;

@DisplayName("기관은 봉사자에게 쪽지를 보낼수 있다. (service)")
@Test
void sendNoteToVolunteer() {
//given
Volunteer volunteer = createVolunteer();
UUID senderId = volunteer.getId(); // 기관 ID
UUID receiverId = volunteer.getId(); // 봉사자 ID
String title = "안녕하세요, 서울 도서관입니다.";
String content = "문의 주신 내용 답장드립니다.";
SendNoteToVolunteerRequestDto requestDto = new SendNoteToVolunteerRequestDto(receiverId, title, content);

//when
Long noteId = sendNoteToVolunteerService.sendNoteToVolunteer(senderId, requestDto);

//then
Note savedNote = noteJpaRepository.findById(noteId).orElseThrow(() ->
new IllegalStateException("쪽지가 저장되지 않았습니다."));
assertEquals(senderId, savedNote.getSenderId(), "발신자가 올바르지 않습니다.");
assertEquals(receiverId, savedNote.getReceiverId(), "수신자가 올바르지 않습니다.");
assertEquals(title, savedNote.getTitle(), "쪽지 제목이 올바르지 않습니다.");
assertEquals(content, savedNote.getContent(), "쪽지 내용이 올바르지 않습니다.");
assertFalse(savedNote.getIsRead(), "새로 생성된 쪽지는 읽음 상태가 기본적으로 false여야 합니다.");
}

private Volunteer createVolunteer() {
Volunteer volunteer = Volunteer.createDefault(NAVER, "r1frewgergw");
return volunteerJpaRepository.save(volunteer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import org.assertj.core.api.AssertionsForClassTypes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -180,6 +182,33 @@ void findSimpleInfoByIds() {
assertThat(simpleInfo).hasSize(4);
}

@DisplayName("봉사자 ID로 봉사자가 존재하는지 확인할 수 있다.")
@Test
void existsVolunteerById() {
//given
Volunteer existsVolunteer = volunteerRepository.save(Volunteer.createDefault(NAVER, "naver"));
volunteerRepository.save(existsVolunteer);

//when
Boolean isExist = volunteerRepository.existsByVolunteerId(existsVolunteer.getId());

//then
AssertionsForClassTypes.assertThat(isExist).isTrue();
}

@DisplayName("존재하지 않는 봉사자 ID를 검증할 수 있다.")
@Test
void notExistsVolunteerById() {
//given
UUID nonExistentId = UUID.randomUUID();

//when
Boolean isExist = volunteerRepository.existsByVolunteerId(nonExistentId);

//then
AssertionsForClassTypes.assertThat(isExist).isFalse();
}

private void createVolunteerAndUpdateVolunteerStats(int i) {
Volunteer newVolunteer = Volunteer.createDefault(NAVER, "oauth-id-" + i);
newVolunteer.updateVolunteerStats(i * 10, i);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static com.somemore.volunteer.domain.Volunteer.createDefault;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.*;

import com.somemore.IntegrationTestSupport;
import com.somemore.auth.oauth.OAuthProvider;
Expand Down Expand Up @@ -243,6 +244,33 @@ void getVolunteerSimpleInfosByIds() {
assertThat(volunteers).hasSize(2);
}

@DisplayName("봉사자 ID로 봉사자가 존재하는지 확인할 수 있다 (service)")
@Test
void validateVolunteerExists() {
//given
Volunteer volunteer = Volunteer.createDefault(OAuthProvider.NAVER, "1234");
volunteerRepository.save(volunteer);

//when & then
assertDoesNotThrow(() ->
volunteerQueryService.validateVolunteerExists(volunteer.getId())
);
}

@DisplayName("존재하지 않는 봉사자 ID를 검증할 수 있다. (service)")
@Test
void validateNonExistentVolunteer() {
//given
UUID nonExistsVolunteerId = UUID.randomUUID();

//when & then
BadRequestException exception = assertThrows(BadRequestException.class, () ->
volunteerQueryService.validateVolunteerExists(nonExistsVolunteerId)
);

assertEquals(NOT_EXISTS_VOLUNTEER.getMessage(), exception.getMessage());
}

private static VolunteerDetail createVolunteerDetail(UUID volunteerId) {

VolunteerRegisterRequestDto volunteerRegisterRequestDto =
Expand All @@ -259,4 +287,5 @@ private static VolunteerDetail createVolunteerDetail(UUID volunteerId) {

return VolunteerDetail.of(volunteerRegisterRequestDto, volunteerId);
}

}
Loading