Skip to content

Commit fa6574e

Browse files
authored
feat: center 프로필 수정 (#139)
* test(center): center 프로필 수정 테스트 추가 * feat(center): center 프로필 수정 RequestDto, Usecase 추가 * feat(center): center updateWith 메서드 추가 * feat(center): center 프로필 수정 service, controller 구현 * refactor(center): @PathVariable 키워드 추가 * test(center): public 키워드 삭제 * refactor(center): center 프로필 수정 엔드포인트 변경 - /centerId 삭제
1 parent 1fd82e0 commit fa6574e

File tree

7 files changed

+284
-0
lines changed

7 files changed

+284
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.somemore.center.controller;
2+
3+
import com.somemore.auth.annotation.CurrentUser;
4+
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
5+
import com.somemore.center.usecase.command.UpdateCenterProfileUseCase;
6+
import com.somemore.global.common.response.ApiResponse;
7+
import com.somemore.imageupload.dto.ImageUploadRequestDto;
8+
import com.somemore.imageupload.usecase.ImageUploadUseCase;
9+
import io.swagger.v3.oas.annotations.Operation;
10+
import io.swagger.v3.oas.annotations.tags.Tag;
11+
import jakarta.validation.Valid;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.security.access.annotation.Secured;
14+
import org.springframework.web.bind.annotation.*;
15+
import org.springframework.web.multipart.MultipartFile;
16+
17+
import java.util.UUID;
18+
19+
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
20+
21+
@Tag(name = "Center Command API", description = "센터 프로필 수정 API")
22+
@RequiredArgsConstructor
23+
@RequestMapping("/api/center/profile")
24+
@RestController
25+
public class CenterProfileCommandApiController {
26+
27+
private final UpdateCenterProfileUseCase updateCenterProfileUseCase;
28+
private final ImageUploadUseCase imageUploadUseCase;
29+
30+
@Secured("ROLE_CENTER")
31+
@Operation(summary = "센터 프로필 수정", description = "센터 프로필을 수정합니다.")
32+
@PutMapping(consumes = MULTIPART_FORM_DATA_VALUE)
33+
public ApiResponse<String> updateCenterProfile(
34+
@CurrentUser UUID centerId,
35+
@Valid @RequestPart("data") CenterProfileUpdateRequestDto requestDto,
36+
@RequestPart(value = "img_file", required = false) MultipartFile image
37+
) {
38+
String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image));
39+
updateCenterProfileUseCase.updateCenterProfile(centerId, requestDto, imgUrl);
40+
41+
return ApiResponse.ok("센터 프로필 수정 성공");
42+
}
43+
}

src/main/java/com/somemore/center/domain/Center.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.somemore.center.domain;
22

3+
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
34
import com.somemore.global.common.BaseEntity;
45
import jakarta.persistence.*;
56
import lombok.*;
@@ -63,4 +64,11 @@ public static Center create(String name, String contactNumber, String imgUrl, St
6364
.build();
6465
}
6566

67+
public void updateWith(CenterProfileUpdateRequestDto dto, String imgUrl) {
68+
this.name = dto.name();
69+
this.contactNumber = dto.contactNumber();
70+
this.homepageLink = dto.homepageLink();
71+
this.introduce = dto.introduce();
72+
this.imgUrl = imgUrl;
73+
}
6674
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.somemore.center.dto.request;
2+
3+
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
4+
import com.fasterxml.jackson.databind.annotation.JsonNaming;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import jakarta.validation.constraints.NotBlank;
7+
import lombok.Builder;
8+
9+
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
10+
@Builder
11+
public record CenterProfileUpdateRequestDto(
12+
@Schema(description = "센터 이름", example = "서울 도서관")
13+
@NotBlank(message = "센터 이름은 필수 값입니다.")
14+
String name,
15+
@Schema(description = "연락처", example = "010-1234-5678")
16+
@NotBlank(message = "연락처는 필수 값입니다.")
17+
String contactNumber,
18+
@Schema(description = "센터 홈페이지 링크", example = "https://fitnesscenter.com")
19+
@NotBlank(message = "센터 홈페이지 링크는 필수 값입니다.")
20+
String homepageLink,
21+
@Schema(description = "센터 소개", example = "저희 도서관은 유명해요")
22+
@NotBlank(message = "센터 소개는 필수 값입니다.")
23+
String introduce
24+
) {}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.somemore.center.service.command;
2+
3+
import com.somemore.center.domain.Center;
4+
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
5+
import com.somemore.center.repository.CenterRepository;
6+
import com.somemore.center.usecase.command.UpdateCenterProfileUseCase;
7+
import com.somemore.global.exception.BadRequestException;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.stereotype.Service;
10+
import org.springframework.transaction.annotation.Transactional;
11+
12+
import java.util.UUID;
13+
14+
import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_CENTER;
15+
16+
@RequiredArgsConstructor
17+
@Transactional
18+
@Service
19+
public class UpdateCenterProfileService implements UpdateCenterProfileUseCase {
20+
21+
private final CenterRepository centerRepository;
22+
23+
@Override
24+
public void updateCenterProfile(UUID centerId, CenterProfileUpdateRequestDto requestDto, String imgUrl) {
25+
Center center = centerRepository.findCenterById(centerId)
26+
.orElseThrow(() -> new BadRequestException(NOT_EXISTS_CENTER));
27+
28+
center.updateWith(requestDto, imgUrl);
29+
}
30+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.somemore.center.usecase.command;
2+
3+
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
4+
5+
import java.util.UUID;
6+
7+
public interface UpdateCenterProfileUseCase {
8+
void updateCenterProfile(UUID centerId, CenterProfileUpdateRequestDto requestDto, String imgUrl);
9+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.somemore.center.controller;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.somemore.ControllerTestSupport;
5+
import com.somemore.WithMockCustomUser;
6+
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
7+
import com.somemore.center.usecase.command.UpdateCenterProfileUseCase;
8+
import com.somemore.imageupload.usecase.ImageUploadUseCase;
9+
import org.junit.jupiter.api.DisplayName;
10+
import org.junit.jupiter.api.Test;
11+
import org.springframework.beans.factory.annotation.Autowired;
12+
import org.springframework.boot.test.mock.mockito.MockBean;
13+
import org.springframework.http.MediaType;
14+
import org.springframework.mock.web.MockHttpServletRequest;
15+
import org.springframework.mock.web.MockMultipartFile;
16+
import org.springframework.test.web.servlet.MockMvc;
17+
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
18+
import org.springframework.test.web.servlet.request.RequestPostProcessor;
19+
20+
import java.util.UUID;
21+
22+
import static org.mockito.ArgumentMatchers.any;
23+
import static org.mockito.ArgumentMatchers.anyString;
24+
import static org.mockito.BDDMockito.given;
25+
import static org.mockito.BDDMockito.willDoNothing;
26+
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
27+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
28+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
29+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
30+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
31+
32+
class CenterProfileCommandApiControllerTest extends ControllerTestSupport {
33+
34+
@Autowired
35+
private MockMvc mockMvc;
36+
37+
@Autowired
38+
private ObjectMapper objectMapper;
39+
40+
@MockBean
41+
private UpdateCenterProfileUseCase updateCenterProfileUseCase;
42+
43+
@MockBean
44+
private ImageUploadUseCase imageUploadUseCase;
45+
46+
@Test
47+
@DisplayName("센터 프로필 수정 성공 테스트")
48+
@WithMockCustomUser(role = "CENTER")
49+
void updateCenterProfile_success() throws Exception {
50+
//given
51+
CenterProfileUpdateRequestDto requestDto = CenterProfileUpdateRequestDto.builder()
52+
.name("테스트 센터명")
53+
.contactNumber("010-0000-0000")
54+
.homepageLink("https://www.test.com")
55+
.introduce("테스트 설명")
56+
.build();
57+
58+
MockMultipartFile imageFile = new MockMultipartFile(
59+
"img_file",
60+
"test-image.jpg",
61+
MediaType.IMAGE_JPEG_VALUE,
62+
"test image content".getBytes()
63+
);
64+
65+
MockMultipartFile requestData = new MockMultipartFile(
66+
"data",
67+
"",
68+
MediaType.APPLICATION_JSON_VALUE,
69+
objectMapper.writeValueAsBytes(requestDto)
70+
);
71+
72+
String imageUrl = "http://example.com/image/test-image.jpg";
73+
74+
given(imageUploadUseCase.uploadImage(any())).willReturn(imageUrl);
75+
willDoNothing().given(updateCenterProfileUseCase)
76+
.updateCenterProfile(any(UUID.class), any(), anyString());
77+
78+
MockMultipartHttpServletRequestBuilder builder = multipart("/api/center/profile");
79+
builder.with(new RequestPostProcessor() {
80+
@Override
81+
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
82+
request.setMethod("PUT");
83+
return request;
84+
}
85+
});
86+
87+
//when
88+
mockMvc.perform(builder
89+
.file(requestData)
90+
.file(imageFile)
91+
.contentType(MULTIPART_FORM_DATA)
92+
.header("Authorization", "Bearer access-token"))
93+
94+
//then
95+
.andDo(print())
96+
.andExpect(status().isOk())
97+
.andExpect(jsonPath("$.code").value(200))
98+
.andExpect(jsonPath("$.data").isEmpty())
99+
.andExpect(jsonPath("$.message").value("센터 프로필 수정 성공"));
100+
}
101+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.somemore.center.service.command;
2+
3+
import com.somemore.IntegrationTestSupport;
4+
import com.somemore.center.domain.Center;
5+
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
6+
import com.somemore.center.repository.CenterRepository;
7+
import com.somemore.global.exception.BadRequestException;
8+
import org.assertj.core.api.ThrowableAssert;
9+
import org.junit.jupiter.api.DisplayName;
10+
import org.junit.jupiter.api.Test;
11+
import org.springframework.beans.factory.annotation.Autowired;
12+
import org.springframework.transaction.annotation.Transactional;
13+
14+
import java.util.UUID;
15+
16+
import static com.somemore.common.fixture.CenterFixture.createCenter;
17+
import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_CENTER;
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
@Transactional
21+
class UpdateCenterProfileServiceTest extends IntegrationTestSupport {
22+
23+
@Autowired
24+
private UpdateCenterProfileService updateCenterProfileService;
25+
26+
@Autowired
27+
private CenterRepository centerRepository;
28+
29+
CenterProfileUpdateRequestDto requestDto = CenterProfileUpdateRequestDto.builder()
30+
.name("테스트 센터명")
31+
.contactNumber("010-0000-0000")
32+
.homepageLink("https://www.test.com")
33+
.introduce("테스트 설명")
34+
.build();
35+
36+
String imgUrl = "http://example.com/updated-image.jpg";
37+
38+
@Test
39+
@DisplayName("센터 프로필을 업데이트 한다.")
40+
void updateCenterProfile() {
41+
//given
42+
Center center = createCenter();
43+
centerRepository.save(center);
44+
45+
//when
46+
updateCenterProfileService.updateCenterProfile(center.getId(), requestDto, imgUrl);
47+
48+
//then
49+
Center updatedCenter = centerRepository.findCenterById(center.getId()).orElseThrow();
50+
assertThat(updatedCenter.getName()).isEqualTo(requestDto.name());
51+
assertThat(updatedCenter.getContactNumber()).isEqualTo(requestDto.contactNumber());
52+
assertThat(updatedCenter.getHomepageLink()).isEqualTo(requestDto.homepageLink());
53+
assertThat(updatedCenter.getIntroduce()).isEqualTo(requestDto.introduce());
54+
assertThat(updatedCenter.getImgUrl()).isEqualTo(imgUrl);
55+
}
56+
57+
@Test
58+
@DisplayName("존재하지 않는 센터 ID로 업데이트 시 예외를 던진다")
59+
void updateCenterProfileWithInvalidId() {
60+
//given
61+
//when
62+
ThrowableAssert.ThrowingCallable callable = () -> updateCenterProfileService.updateCenterProfile(UUID.randomUUID(), requestDto, imgUrl);
63+
64+
//then
65+
assertThatExceptionOfType(BadRequestException.class)
66+
.isThrownBy(callable)
67+
.withMessage(NOT_EXISTS_CENTER.getMessage());
68+
}
69+
}

0 commit comments

Comments
 (0)