Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.somemore.center.controller;

import com.somemore.auth.annotation.CurrentUser;
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
import com.somemore.center.usecase.command.UpdateCenterProfileUseCase;
import com.somemore.global.common.response.ApiResponse;
import com.somemore.imageupload.dto.ImageUploadRequestDto;
import com.somemore.imageupload.usecase.ImageUploadUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.UUID;

import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;

@Tag(name = "Center Command API", description = "센터 프로필 수정 API")
@RequiredArgsConstructor
@RequestMapping("/api/center/profile")
@RestController
public class CenterProfileCommandApiController {

private final UpdateCenterProfileUseCase updateCenterProfileUseCase;
private final ImageUploadUseCase imageUploadUseCase;

@Secured("ROLE_CENTER")
@Operation(summary = "센터 프로필 수정", description = "센터 프로필을 수정합니다.")
@PutMapping(value = "/{centerId}", consumes = MULTIPART_FORM_DATA_VALUE)
public ApiResponse<String> updateCenterProfile(
@CurrentUser @PathVariable UUID centerId,
@Valid @RequestPart("data") CenterProfileUpdateRequestDto requestDto,
@RequestPart(value = "img_file", required = false) MultipartFile image
) {
String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image));
updateCenterProfileUseCase.updateCenterProfile(centerId, requestDto, imgUrl);

return ApiResponse.ok("센터 프로필 수정 성공");
}
}
8 changes: 8 additions & 0 deletions src/main/java/com/somemore/center/domain/Center.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.somemore.center.domain;

import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
import com.somemore.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
Expand Down Expand Up @@ -63,4 +64,11 @@ public static Center create(String name, String contactNumber, String imgUrl, St
.build();
}

public void updateWith(CenterProfileUpdateRequestDto dto, String imgUrl) {
this.name = dto.name();
this.contactNumber = dto.contactNumber();
this.homepageLink = dto.homepageLink();
this.introduce = dto.introduce();
this.imgUrl = imgUrl;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.somemore.center.dto.request;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Builder;

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
@Builder
public record CenterProfileUpdateRequestDto(
@Schema(description = "센터 이름", example = "서울 도서관")
@NotBlank(message = "센터 이름은 필수 값입니다.")
String name,
@Schema(description = "연락처", example = "010-1234-5678")
@NotBlank(message = "연락처는 필수 값입니다.")
String contactNumber,
@Schema(description = "센터 홈페이지 링크", example = "https://fitnesscenter.com")
@NotBlank(message = "센터 홈페이지 링크는 필수 값입니다.")
String homepageLink,
@Schema(description = "센터 소개", example = "저희 도서관은 유명해요")
@NotBlank(message = "센터 소개는 필수 값입니다.")
String introduce
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.somemore.center.service.command;

import com.somemore.center.domain.Center;
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
import com.somemore.center.repository.CenterRepository;
import com.somemore.center.usecase.command.UpdateCenterProfileUseCase;
import com.somemore.global.exception.BadRequestException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;

import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_CENTER;

@RequiredArgsConstructor
@Transactional
@Service
public class UpdateCenterProfileService implements UpdateCenterProfileUseCase {

private final CenterRepository centerRepository;

@Override
public void updateCenterProfile(UUID centerId, CenterProfileUpdateRequestDto requestDto, String imgUrl) {
Center center = centerRepository.findCenterById(centerId)
.orElseThrow(() -> new BadRequestException(NOT_EXISTS_CENTER));

center.updateWith(requestDto, imgUrl);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.somemore.center.usecase.command;

import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;

import java.util.UUID;

public interface UpdateCenterProfileUseCase {
void updateCenterProfile(UUID centerId, CenterProfileUpdateRequestDto requestDto, String imgUrl);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.somemore.center.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.somemore.ControllerTestSupport;
import com.somemore.WithMockCustomUser;
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
import com.somemore.center.usecase.command.UpdateCenterProfileUseCase;
import com.somemore.imageupload.usecase.ImageUploadUseCase;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.RequestPostProcessor;

import java.util.UUID;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willDoNothing;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

public class CenterProfileCommandApiControllerTest extends ControllerTestSupport {

@Autowired
private MockMvc mockMvc;

@Autowired
private ObjectMapper objectMapper;

@MockBean
private UpdateCenterProfileUseCase updateCenterProfileUseCase;

@MockBean
private ImageUploadUseCase imageUploadUseCase;

@Test
@DisplayName("센터 프로필 수정 성공 테스트")
@WithMockCustomUser(role = "CENTER")
void updateCenterProfile_success() throws Exception {
//given
CenterProfileUpdateRequestDto requestDto = CenterProfileUpdateRequestDto.builder()
.name("테스트 센터명")
.contactNumber("010-0000-0000")
.homepageLink("https://www.test.com")
.introduce("테스트 설명")
.build();

MockMultipartFile imageFile = new MockMultipartFile(
"img_file",
"test-image.jpg",
MediaType.IMAGE_JPEG_VALUE,
"test image content".getBytes()
);

MockMultipartFile requestData = new MockMultipartFile(
"data",
"",
MediaType.APPLICATION_JSON_VALUE,
objectMapper.writeValueAsBytes(requestDto)
);

String imageUrl = "http://example.com/image/test-image.jpg";

given(imageUploadUseCase.uploadImage(any())).willReturn(imageUrl);
willDoNothing().given(updateCenterProfileUseCase)
.updateCenterProfile(any(UUID.class), any(), anyString());

MockMultipartHttpServletRequestBuilder builder = multipart("/api/center/profile/{centerId}", UUID.randomUUID());
builder.with(new RequestPostProcessor() {
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
request.setMethod("PUT");
return request;
}
});

//when
mockMvc.perform(builder
.file(requestData)
.file(imageFile)
.contentType(MULTIPART_FORM_DATA)
.header("Authorization", "Bearer access-token"))

//then
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data").isEmpty())
.andExpect(jsonPath("$.message").value("센터 프로필 수정 성공"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.somemore.center.service.command;

import com.somemore.IntegrationTestSupport;
import com.somemore.center.domain.Center;
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
import com.somemore.center.repository.CenterRepository;
import com.somemore.global.exception.BadRequestException;
import org.assertj.core.api.ThrowableAssert;
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.common.fixture.CenterFixture.createCenter;
import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_CENTER;
import static org.assertj.core.api.Assertions.*;

@Transactional
public class UpdateCenterProfileServiceTest extends IntegrationTestSupport {

@Autowired
private UpdateCenterProfileService updateCenterProfileService;

@Autowired
private CenterRepository centerRepository;

CenterProfileUpdateRequestDto requestDto = CenterProfileUpdateRequestDto.builder()
.name("테스트 센터명")
.contactNumber("010-0000-0000")
.homepageLink("https://www.test.com")
.introduce("테스트 설명")
.build();

String imgUrl = "http://example.com/updated-image.jpg";

@Test
@DisplayName("센터 프로필을 업데이트 한다.")
void updateCenterProfile() {
//given
Center center = createCenter();
centerRepository.save(center);

//when
updateCenterProfileService.updateCenterProfile(center.getId(), requestDto, imgUrl);

//then
Center updatedCenter = centerRepository.findCenterById(center.getId()).orElseThrow();
assertThat(updatedCenter.getName()).isEqualTo(requestDto.name());
assertThat(updatedCenter.getContactNumber()).isEqualTo(requestDto.contactNumber());
assertThat(updatedCenter.getHomepageLink()).isEqualTo(requestDto.homepageLink());
assertThat(updatedCenter.getIntroduce()).isEqualTo(requestDto.introduce());
assertThat(updatedCenter.getImgUrl()).isEqualTo(imgUrl);
}

@Test
@DisplayName("존재하지 않는 센터 ID로 업데이트 시 예외를 던진다")
void updateCenterProfileWithInvalidId() {
//given
//when
ThrowableAssert.ThrowingCallable callable = () -> updateCenterProfileService.updateCenterProfile(UUID.randomUUID(), requestDto, imgUrl);

//then
assertThatExceptionOfType(BadRequestException.class)
.isThrownBy(callable)
.withMessage(NOT_EXISTS_CENTER.getMessage());
}
}
Loading