Skip to content

Commit e7fd1d4

Browse files
authored
feat: 관심 지역 및 국가 수정 API 추가 (#453)
* feat: 지역 변경 요청 DTO 구현 * feat: 관심 지역 변경 서비스 로직 작성 * feat: 관심 지역 변경 컨트롤러 작성 * fix: siteUser가 아니라 siteUserId로 삭제하도록 * test: 관심 지역 및 국가 변경 테스트 작성 * fix: clearAutomatically, flushAutomatically 옵션을 통해 DB와 영속성 컨텍스트 간 불일치 문제 해결 * fix: 파라미터 순서 변경 * chore: 불필요한 Modifying 어노테이션 제거 * chore: 다건 삭제는 deleteBy 대신 deleteAllBy를 사용하도록 * chore: DTO에 Valid 어노테이션 추가 * chore: 크기 검증 제거 및 미사용 repository 제거 * chore: 코드 리포매팅 * fix: conflict 해결 * refactor: 분명한 의미를 가진 엔드포인트 이름으로 변경 - /location -> /interested-location
1 parent afe27ab commit e7fd1d4

8 files changed

Lines changed: 213 additions & 3 deletions

File tree

src/main/java/com/example/solidconnection/location/country/repository/InterestedCountryRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
public interface InterestedCountryRepository extends JpaRepository<InterestedCountry, Long> {
88

99
List<InterestedCountry> findAllBySiteUserId(long siteUserId);
10+
11+
void deleteAllBySiteUserId(long siteUserId);
1012
}

src/main/java/com/example/solidconnection/location/country/service/InterestedCountryService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,15 @@ public void saveInterestedCountry(SiteUser siteUser, List<String> koreanNames) {
2424
.toList();
2525
interestedCountryRepository.saveAll(interestedCountries);
2626
}
27+
28+
@Transactional
29+
public void updateInterestedCountry(SiteUser siteUser, List<String> koreanNames) {
30+
interestedCountryRepository.deleteAllBySiteUserId(siteUser.getId());
31+
32+
List<InterestedCountry> interestedCountries = countryRepository.findAllByKoreanNameIn(koreanNames)
33+
.stream()
34+
.map(country -> new InterestedCountry(siteUser, country))
35+
.toList();
36+
interestedCountryRepository.saveAll(interestedCountries);
37+
}
2738
}

src/main/java/com/example/solidconnection/location/region/repository/InterestedRegionRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
public interface InterestedRegionRepository extends JpaRepository<InterestedRegion, Long> {
88

99
List<InterestedRegion> findAllBySiteUserId(long siteUserId);
10+
11+
void deleteAllBySiteUserId(long siteUserId);
1012
}

src/main/java/com/example/solidconnection/location/region/service/InterestedRegionService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,15 @@ public void saveInterestedRegion(SiteUser siteUser, List<String> koreanNames) {
2424
.toList();
2525
interestedRegionRepository.saveAll(interestedRegions);
2626
}
27+
28+
@Transactional
29+
public void updateInterestedRegion(SiteUser siteUser, List<String> koreanNames) {
30+
interestedRegionRepository.deleteAllBySiteUserId(siteUser.getId());
31+
32+
List<InterestedRegion> interestedRegions = regionRepository.findAllByKoreanNameIn(koreanNames)
33+
.stream()
34+
.map(region -> new InterestedRegion(siteUser, region))
35+
.toList();
36+
interestedRegionRepository.saveAll(interestedRegions);
37+
}
2738
}

src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
import com.example.solidconnection.common.resolver.AuthorizedUser;
5+
import com.example.solidconnection.siteuser.dto.LocationUpdateRequest;
56
import com.example.solidconnection.siteuser.dto.MyPageResponse;
67
import com.example.solidconnection.siteuser.dto.PasswordUpdateRequest;
78
import com.example.solidconnection.siteuser.service.MyPageService;
@@ -49,4 +50,13 @@ public ResponseEntity<Void> updatePassword(
4950
myPageService.updatePassword(siteUserId, request);
5051
return ResponseEntity.ok().build();
5152
}
53+
54+
@PatchMapping("/interested-location")
55+
public ResponseEntity<Void> updateLocation(
56+
@AuthorizedUser long siteUserId,
57+
@RequestBody @Valid LocationUpdateRequest request
58+
) {
59+
myPageService.updateLocation(siteUserId, request);
60+
return ResponseEntity.ok().build();
61+
}
5262
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.example.solidconnection.siteuser.dto;
2+
3+
import java.util.List;
4+
5+
public record LocationUpdateRequest(
6+
List<String> interestedRegions,
7+
List<String> interestedCountries
8+
) {
9+
10+
}

src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND;
99

1010
import com.example.solidconnection.common.exception.CustomException;
11+
import com.example.solidconnection.location.country.service.InterestedCountryService;
12+
import com.example.solidconnection.location.region.service.InterestedRegionService;
1113
import com.example.solidconnection.location.country.repository.CountryRepository;
1214
import com.example.solidconnection.mentor.domain.Mentor;
1315
import com.example.solidconnection.mentor.repository.MentorRepository;
@@ -16,6 +18,7 @@
1618
import com.example.solidconnection.s3.service.S3Service;
1719
import com.example.solidconnection.siteuser.domain.Role;
1820
import com.example.solidconnection.siteuser.domain.SiteUser;
21+
import com.example.solidconnection.siteuser.dto.LocationUpdateRequest;
1922
import com.example.solidconnection.siteuser.dto.MyPageResponse;
2023
import com.example.solidconnection.siteuser.dto.PasswordUpdateRequest;
2124
import com.example.solidconnection.siteuser.repository.SiteUserRepository;
@@ -45,6 +48,8 @@ public class MyPageService {
4548
private final MentorRepository mentorRepository;
4649
private final UniversityRepository universityRepository;
4750
private final S3Service s3Service;
51+
private final InterestedCountryService interestedCountryService;
52+
private final InterestedRegionService interestedRegionService;
4853

4954
/*
5055
* 마이페이지 정보를 조회한다.
@@ -132,4 +137,13 @@ private void validatePasswordMatch(String currentPassword, String userPassword)
132137
throw new CustomException(PASSWORD_MISMATCH);
133138
}
134139
}
140+
141+
@Transactional
142+
public void updateLocation(long siteUserId, LocationUpdateRequest request) {
143+
SiteUser siteUser = siteUserRepository.findById(siteUserId)
144+
.orElseThrow(() -> new CustomException(USER_NOT_FOUND));
145+
146+
interestedCountryService.updateInterestedCountry(siteUser, request.interestedCountries());
147+
interestedRegionService.updateInterestedRegion(siteUser, request.interestedRegions());
148+
}
135149
}

src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java

Lines changed: 153 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package com.example.solidconnection.siteuser.service;
22

33
import static com.example.solidconnection.common.exception.ErrorCode.CAN_NOT_CHANGE_NICKNAME_YET;
4+
import static com.example.solidconnection.common.exception.ErrorCode.PASSWORD_MISMATCH;
45
import static com.example.solidconnection.siteuser.service.MyPageService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES;
56
import static com.example.solidconnection.siteuser.service.MyPageService.NICKNAME_LAST_CHANGE_DATE_FORMAT;
67
import static org.assertj.core.api.Assertions.assertThat;
8+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
79
import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
10+
import static org.junit.jupiter.api.Assertions.assertAll;
811
import static org.mockito.BDDMockito.any;
912
import static org.mockito.BDDMockito.eq;
1013
import static org.mockito.BDDMockito.given;
@@ -16,14 +19,20 @@
1619
import com.example.solidconnection.location.country.domain.InterestedCountry;
1720
import com.example.solidconnection.location.country.fixture.CountryFixture;
1821
import com.example.solidconnection.location.country.repository.InterestedCountryRepository;
22+
import com.example.solidconnection.location.region.domain.InterestedRegion;
23+
import com.example.solidconnection.location.region.domain.Region;
24+
import com.example.solidconnection.location.region.fixture.RegionFixture;
25+
import com.example.solidconnection.location.region.repository.InterestedRegionRepository;
1926
import com.example.solidconnection.mentor.fixture.MentorFixture;
2027
import com.example.solidconnection.s3.domain.ImgType;
2128
import com.example.solidconnection.s3.dto.UploadedFileUrlResponse;
2229
import com.example.solidconnection.s3.service.S3Service;
2330
import com.example.solidconnection.siteuser.domain.AuthType;
2431
import com.example.solidconnection.siteuser.domain.Role;
2532
import com.example.solidconnection.siteuser.domain.SiteUser;
33+
import com.example.solidconnection.siteuser.dto.LocationUpdateRequest;
2634
import com.example.solidconnection.siteuser.dto.MyPageResponse;
35+
import com.example.solidconnection.siteuser.dto.PasswordUpdateRequest;
2736
import com.example.solidconnection.siteuser.fixture.SiteUserFixture;
2837
import com.example.solidconnection.siteuser.fixture.SiteUserFixtureBuilder;
2938
import com.example.solidconnection.siteuser.repository.SiteUserRepository;
@@ -33,7 +42,7 @@
3342
import com.example.solidconnection.university.fixture.UnivApplyInfoFixture;
3443
import com.example.solidconnection.university.repository.LikedUnivApplyInfoRepository;
3544
import java.time.LocalDateTime;
36-
import org.junit.jupiter.api.Assertions;
45+
import java.util.List;
3746
import org.junit.jupiter.api.BeforeEach;
3847
import org.junit.jupiter.api.DisplayName;
3948
import org.junit.jupiter.api.Nested;
@@ -62,6 +71,9 @@ class MyPageServiceTest {
6271
@Autowired
6372
private InterestedCountryRepository interestedCountryRepository;
6473

74+
@Autowired
75+
private InterestedRegionRepository interestedRegionRepository;
76+
6577
@Autowired
6678
private SiteUserFixture siteUserFixture;
6779

@@ -74,6 +86,9 @@ class MyPageServiceTest {
7486
@Autowired
7587
private UnivApplyInfoFixture univApplyInfoFixture;
7688

89+
@Autowired
90+
private RegionFixture regionFixture;
91+
7792
@Autowired
7893
private SiteUserFixtureBuilder siteUserFixtureBuilder;
7994

@@ -99,7 +114,7 @@ void setUp() {
99114
MyPageResponse response = myPageService.getMyPageInfo(user.getId());
100115

101116
// then
102-
Assertions.assertAll(
117+
assertAll(
103118
() -> assertThat(response.nickname()).isEqualTo(user.getNickname()),
104119
() -> assertThat(response.profileImageUrl()).isEqualTo(user.getProfileImageUrl()),
105120
() -> assertThat(response.role()).isEqualTo(user.getRole()),
@@ -124,7 +139,7 @@ void setUp() {
124139
MyPageResponse response = myPageService.getMyPageInfo(mentorUser.getId());
125140

126141
// then
127-
Assertions.assertAll(
142+
assertAll(
128143
() -> assertThat(response.nickname()).isEqualTo(mentorUser.getNickname()),
129144
() -> assertThat(response.profileImageUrl()).isEqualTo(mentorUser.getProfileImageUrl()),
130145
() -> assertThat(response.role()).isEqualTo(mentorUser.getRole()),
@@ -263,4 +278,139 @@ void setUp() {
263278
.hasMessage(createExpectedErrorMessage(modifiedAt));
264279
}
265280
}
281+
282+
@Nested
283+
class 비밀번호_변경_테스트 {
284+
285+
private String currentPassword;
286+
private String newPassword;
287+
288+
@BeforeEach
289+
void setUp() {
290+
currentPassword = "currentPassword123";
291+
newPassword = "newPassword123";
292+
293+
user.updatePassword(passwordEncoder.encode(currentPassword));
294+
siteUserRepository.save(user);
295+
}
296+
297+
@Test
298+
void 비밀번호를_성공적으로_변경한다() {
299+
// given
300+
PasswordUpdateRequest request = new PasswordUpdateRequest(currentPassword, newPassword, newPassword);
301+
302+
// when
303+
myPageService.updatePassword(user.getId(), request);
304+
305+
// then
306+
SiteUser updatedUser = siteUserRepository.findById(user.getId()).get();
307+
assertAll(
308+
() -> assertThat(passwordEncoder.matches(newPassword, updatedUser.getPassword())).isTrue(),
309+
() -> assertThat(passwordEncoder.matches(currentPassword, updatedUser.getPassword())).isFalse()
310+
);
311+
}
312+
313+
@Test
314+
void 현재_비밀번호가_일치하지_않으면_예외가_발생한다() {
315+
// given
316+
String wrongPassword = "wrongPassword";
317+
PasswordUpdateRequest request = new PasswordUpdateRequest(wrongPassword, newPassword, newPassword);
318+
319+
// when & then
320+
assertThatThrownBy(() -> myPageService.updatePassword(user.getId(), request))
321+
.isInstanceOf(CustomException.class)
322+
.hasMessage(PASSWORD_MISMATCH.getMessage());
323+
}
324+
}
325+
326+
@Nested
327+
class 관심_지역_및_국가_변경_테스트 {
328+
329+
private Country 미국;
330+
private Country 캐나다;
331+
private Country 일본;
332+
private Region 영미권;
333+
private Region 아시아;
334+
335+
@BeforeEach
336+
void setUp() {
337+
미국 = countryFixture.미국();
338+
캐나다 = countryFixture.캐나다();
339+
일본 = countryFixture.일본();
340+
영미권 = regionFixture.영미권();
341+
아시아 = regionFixture.아시아();
342+
}
343+
344+
@Test
345+
void 관심_지역과_국가를_성공적으로_수정한다() {
346+
// given
347+
interestedCountryRepository.save(new InterestedCountry(user, 미국));
348+
interestedRegionRepository.save(new InterestedRegion(user, 영미권));
349+
350+
List<String> newCountries = List.of(캐나다.getKoreanName(), 일본.getKoreanName());
351+
List<String> newRegions = List.of(아시아.getKoreanName());
352+
LocationUpdateRequest request = new LocationUpdateRequest(newRegions, newCountries);
353+
354+
// when
355+
myPageService.updateLocation(user.getId(), request);
356+
357+
// then
358+
List<InterestedCountry> updatedCountries = interestedCountryRepository.findAllBySiteUserId(user.getId());
359+
List<InterestedRegion> updatedRegions = interestedRegionRepository.findAllBySiteUserId(user.getId());
360+
361+
assertAll(
362+
() -> assertThat(updatedCountries)
363+
.extracting(InterestedCountry::getCountryCode)
364+
.containsExactlyInAnyOrder(캐나다.getCode(), 일본.getCode()),
365+
() -> assertThat(updatedRegions)
366+
.extracting(InterestedRegion::getRegionCode)
367+
.containsExactly(아시아.getCode())
368+
);
369+
}
370+
371+
@Test
372+
void 기존에_관심_지역과_국가가_없어도_성공적으로_추가된다() {
373+
// given
374+
List<String> newCountries = List.of(미국.getKoreanName());
375+
List<String> newRegions = List.of(영미권.getKoreanName());
376+
LocationUpdateRequest request = new LocationUpdateRequest(newRegions, newCountries);
377+
378+
// when
379+
myPageService.updateLocation(user.getId(), request);
380+
381+
// then
382+
List<InterestedCountry> updatedCountries = interestedCountryRepository.findAllBySiteUserId(user.getId());
383+
List<InterestedRegion> updatedRegions = interestedRegionRepository.findAllBySiteUserId(user.getId());
384+
385+
assertAll(
386+
() -> assertThat(updatedCountries)
387+
.extracting(InterestedCountry::getCountryCode)
388+
.containsExactly(미국.getCode()),
389+
() -> assertThat(updatedRegions)
390+
.extracting(InterestedRegion::getRegionCode)
391+
.containsExactly(영미권.getCode())
392+
);
393+
}
394+
395+
@Test
396+
void 빈_리스트를_전달하면_모든_관심_지역과_국가가_삭제된다() {
397+
// given
398+
interestedCountryRepository.save(new InterestedCountry(user, 미국));
399+
interestedRegionRepository.save(new InterestedRegion(user, 영미권));
400+
401+
LocationUpdateRequest request = new LocationUpdateRequest(List.of(), List.of());
402+
403+
// when
404+
myPageService.updateLocation(user.getId(), request);
405+
406+
// then
407+
List<InterestedCountry> updatedCountries = interestedCountryRepository.findAllBySiteUserId(user.getId());
408+
List<InterestedRegion> updatedRegions = interestedRegionRepository.findAllBySiteUserId(user.getId());
409+
410+
assertAll(
411+
() -> assertThat(updatedCountries).isEmpty(),
412+
() -> assertThat(updatedRegions).isEmpty()
413+
);
414+
}
415+
}
266416
}

0 commit comments

Comments
 (0)