Skip to content

Commit 5abbd60

Browse files
authored
Merge pull request #178 from YAPP-Github/feat/PRODUCT-260
[Feat] 음식점 태그 조회 API 구현
2 parents 1ad376d + 7eeabee commit 5abbd60

File tree

11 files changed

+203
-11
lines changed

11 files changed

+203
-11
lines changed

src/main/java/eatda/controller/store/StoreController.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ public class StoreController {
2323
private final StoreService storeService;
2424
private final StoreSearchService storeSearchService;
2525

26-
@GetMapping("/api/shops/{storeId}/images")
27-
public ResponseEntity<ImagesResponse> getStoreImages(@PathVariable long storeId) {
28-
return ResponseEntity.ok(storeService.getStoreImages(storeId));
26+
@GetMapping("/api/shops/{storeId}")
27+
public ResponseEntity<StoreResponse> getStore(@PathVariable long storeId) {
28+
StoreResponse response = storeService.getStore(storeId);
29+
return ResponseEntity.ok(response);
2930
}
3031

3132
@GetMapping("/api/shops")
@@ -36,9 +37,14 @@ public ResponseEntity<StoresResponse> getStores(@RequestParam(defaultValue = "0"
3637
return ResponseEntity.ok(response);
3738
}
3839

39-
@GetMapping("/api/shops/{storeId}")
40-
public ResponseEntity<StoreResponse> getStore(@PathVariable long storeId) {
41-
StoreResponse response = storeService.getStore(storeId);
40+
@GetMapping("/api/shops/{storeId}/images")
41+
public ResponseEntity<ImagesResponse> getStoreImages(@PathVariable long storeId) {
42+
return ResponseEntity.ok(storeService.getStoreImages(storeId));
43+
}
44+
45+
@GetMapping("/api/shops/{storeId}/tags")
46+
public ResponseEntity<TagsResponse> getStoreTags(@PathVariable long storeId) {
47+
TagsResponse response = storeService.getStoreTags(storeId);
4248
return ResponseEntity.ok(response);
4349
}
4450

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package eatda.controller.store;
2+
3+
import eatda.domain.cheer.CheerTag;
4+
import eatda.domain.cheer.CheerTagName;
5+
import java.util.List;
6+
7+
public record TagsResponse(List<CheerTagName> tags) {
8+
9+
public static TagsResponse from(List<CheerTag> cheerTags) {
10+
List<CheerTagName> cheerTagNames = cheerTags.stream()
11+
.map(CheerTag::getName)
12+
.distinct()
13+
.toList();
14+
return new TagsResponse(cheerTagNames);
15+
}
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package eatda.repository.cheer;
22

33
import eatda.domain.cheer.CheerTag;
4+
import eatda.domain.store.Store;
5+
import java.util.List;
46
import org.springframework.data.jpa.repository.JpaRepository;
57

68
public interface CheerTagRepository extends JpaRepository<CheerTag, Long> {
79

10+
List<CheerTag> findAllByCheerStore(Store storeId);
811
}

src/main/java/eatda/service/cheer/CheerService.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
import eatda.domain.store.StoreSearchResult;
1717
import eatda.exception.BusinessErrorCode;
1818
import eatda.exception.BusinessException;
19-
import eatda.repository.cheer.CheerImageRepository;
2019
import eatda.repository.cheer.CheerRepository;
21-
import eatda.repository.cheer.CheerTagRepository;
2220
import eatda.repository.member.MemberRepository;
2321
import eatda.repository.store.StoreRepository;
2422
import java.util.Comparator;
@@ -39,8 +37,6 @@ public class CheerService {
3937
private final MemberRepository memberRepository;
4038
private final StoreRepository storeRepository;
4139
private final CheerRepository cheerRepository;
42-
private final CheerTagRepository cheerTagRepository;
43-
private final CheerImageRepository cheerImageRepository;
4440
private final FileClient fileClient;
4541

4642
@Value("${cdn.base-url}")
@@ -79,7 +75,8 @@ private void validateRegisterCheer(Member member, String storeKakaoId) {
7975
}
8076
}
8177

82-
private List<CheerRegisterRequest.UploadedImageDetail> sortImages(List<CheerRegisterRequest.UploadedImageDetail> images) {
78+
private List<CheerRegisterRequest.UploadedImageDetail> sortImages(
79+
List<CheerRegisterRequest.UploadedImageDetail> images) {
8380
return images.stream()
8481
.sorted(Comparator.comparingLong(CheerRegisterRequest.UploadedImageDetail::orderIndex))
8582
.toList();

src/main/java/eatda/service/store/StoreService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
import eatda.controller.store.StoreResponse;
1010
import eatda.controller.store.StoresInMemberResponse;
1111
import eatda.controller.store.StoresResponse;
12+
import eatda.controller.store.TagsResponse;
1213
import eatda.domain.cheer.CheerImage;
14+
import eatda.domain.cheer.CheerTag;
1315
import eatda.domain.store.Store;
1416
import eatda.domain.store.StoreCategory;
1517
import eatda.repository.cheer.CheerImageRepository;
1618
import eatda.repository.cheer.CheerRepository;
19+
import eatda.repository.cheer.CheerTagRepository;
1720
import eatda.repository.store.StoreRepository;
1821
import java.util.List;
1922
import java.util.Optional;
@@ -30,6 +33,7 @@ public class StoreService {
3033

3134
private final StoreRepository storeRepository;
3235
private final CheerRepository cheerRepository;
36+
private final CheerTagRepository cheerTagRepository;
3337
private final CheerImageRepository cheerImageRepository;
3438

3539
@Value("${cdn.base-url}")
@@ -57,6 +61,13 @@ private List<Store> findStores(int page, int size, @Nullable String category) {
5761
StoreCategory.from(category), PageRequest.of(page, size));
5862
}
5963

64+
@Transactional(readOnly = true)
65+
public TagsResponse getStoreTags(long storeId) {
66+
Store store = storeRepository.getById(storeId);
67+
List<CheerTag> cheerTags = cheerTagRepository.findAllByCheerStore(store);
68+
return TagsResponse.from(cheerTags);
69+
}
70+
6071
@Transactional(readOnly = true)
6172
public ImagesResponse getStoreImages(long storeId) {
6273
Store store = storeRepository.getById(storeId);

src/test/java/eatda/controller/BaseControllerTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import eatda.domain.member.Member;
1616
import eatda.fixture.CheerGenerator;
1717
import eatda.fixture.CheerImageGenerator;
18+
import eatda.fixture.CheerTagGenerator;
1819
import eatda.fixture.MemberGenerator;
1920
import eatda.fixture.StoreGenerator;
2021
import eatda.fixture.StoryGenerator;
@@ -60,6 +61,9 @@ public class BaseControllerTest {
6061
@Autowired
6162
protected CheerGenerator cheerGenerator;
6263

64+
@Autowired
65+
protected CheerTagGenerator cheerTagGenerator;
66+
6367
@Autowired
6468
protected StoryGenerator storyGenerator;
6569

src/test/java/eatda/controller/store/StoreControllerTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55

66
import eatda.controller.BaseControllerTest;
77
import eatda.domain.cheer.Cheer;
8+
import eatda.domain.cheer.CheerTagName;
89
import eatda.domain.member.Member;
910
import eatda.domain.store.Store;
1011
import eatda.domain.store.StoreCategory;
12+
import io.restassured.http.ContentType;
1113
import java.time.LocalDateTime;
14+
import java.util.List;
1215
import org.junit.jupiter.api.Nested;
1316
import org.junit.jupiter.api.Test;
1417
import org.springframework.http.HttpHeaders;
@@ -149,6 +152,30 @@ class GetStoreImages {
149152
}
150153
}
151154

155+
156+
@Nested
157+
class GetStoreTags {
158+
159+
@Test
160+
void 음식점_태그들을_조회한다() {
161+
Member member = memberGenerator.generate("111");
162+
Store store = storeGenerator.generate("농민백암순대", "서울 강남구 대치동 896-33");
163+
Cheer cheer = cheerGenerator.generateCommon(member, store);
164+
cheerTagGenerator.generate(cheer, List.of(CheerTagName.INSTAGRAMMABLE, CheerTagName.CLEAN_RESTROOM));
165+
166+
TagsResponse response = given()
167+
.when()
168+
.contentType(ContentType.JSON)
169+
.get("/api/shops/{storeId}/tags", store.getId())
170+
.then()
171+
.statusCode(200)
172+
.extract().as(TagsResponse.class);
173+
174+
assertThat(response.tags())
175+
.containsExactlyInAnyOrder(CheerTagName.INSTAGRAMMABLE, CheerTagName.CLEAN_RESTROOM);
176+
}
177+
}
178+
152179
@Nested
153180
class GetStoresByCheeredMember {
154181

src/test/java/eatda/document/store/StoreDocumentTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
import eatda.controller.store.StoreResponse;
1818
import eatda.controller.store.StoresInMemberResponse;
1919
import eatda.controller.store.StoresResponse;
20+
import eatda.controller.store.TagsResponse;
2021
import eatda.document.BaseDocumentTest;
2122
import eatda.document.RestDocsRequest;
2223
import eatda.document.RestDocsResponse;
2324
import eatda.document.Tag;
25+
import eatda.domain.cheer.CheerTagName;
2426
import eatda.domain.store.District;
2527
import eatda.domain.store.StoreCategory;
2628
import eatda.domain.store.StoreSearchResult;
@@ -228,6 +230,57 @@ class GetStoreImages {
228230

229231
}
230232

233+
@Nested
234+
class GetStoreTags {
235+
236+
RestDocsRequest requestDocument = request()
237+
.tag(Tag.STORE_API)
238+
.summary("음식점 태그 조회")
239+
.pathParameter(
240+
parameterWithName("storeId").description("음식점 ID")
241+
);
242+
243+
RestDocsResponse responseDocument = response()
244+
.responseBodyField(
245+
fieldWithPath("tags").type(ARRAY).description("음식점 태그 목록")
246+
);
247+
248+
@Test
249+
void 음식점_태그_조회_성공() {
250+
long storeId = 7L;
251+
TagsResponse response = new TagsResponse(List.of(CheerTagName.INSTAGRAMMABLE, CheerTagName.CLEAN_RESTROOM));
252+
doReturn(response).when(storeService).getStoreTags(storeId);
253+
254+
var document = document("store/get-tags", 200)
255+
.request(requestDocument)
256+
.response(responseDocument)
257+
.build();
258+
259+
given(document)
260+
.contentType(ContentType.JSON)
261+
.when().get("/api/shops/{storeId}/tags", storeId)
262+
.then().statusCode(200);
263+
}
264+
265+
@EnumSource(value = BusinessErrorCode.class, names = {"STORE_NOT_FOUND"})
266+
@ParameterizedTest
267+
void 음식점_태그_조회_실패(BusinessErrorCode errorCode) {
268+
long storeId = 1L;
269+
doThrow(new BusinessException(errorCode)).when(storeService).getStoreTags(storeId);
270+
271+
var document = document("store/get-tags", errorCode)
272+
.request(requestDocument)
273+
.response(ERROR_RESPONSE)
274+
.build();
275+
276+
given(document)
277+
.contentType(ContentType.JSON)
278+
.pathParam("storeId", storeId)
279+
.when().get("/api/shops/{storeId}/tags")
280+
.then().statusCode(errorCode.getStatus().value());
281+
}
282+
}
283+
231284
@Nested
232285
class GetStoresByCheeredMember {
233286

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package eatda.fixture;
2+
3+
import eatda.domain.cheer.Cheer;
4+
import eatda.domain.cheer.CheerTag;
5+
import eatda.domain.cheer.CheerTagName;
6+
import eatda.repository.cheer.CheerTagRepository;
7+
import java.util.List;
8+
import org.springframework.stereotype.Component;
9+
10+
@Component
11+
public class CheerTagGenerator {
12+
13+
private final CheerTagRepository cheerTagRepository;
14+
15+
public CheerTagGenerator(CheerTagRepository cheerTagRepository) {
16+
this.cheerTagRepository = cheerTagRepository;
17+
}
18+
19+
public List<CheerTag> generate(Cheer cheer, List<CheerTagName> tagNames) {
20+
return tagNames.stream()
21+
.map(name -> cheerTagRepository.save(new CheerTag(cheer, name)))
22+
.toList();
23+
}
24+
}

src/test/java/eatda/service/BaseServiceTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import eatda.client.oauth.OauthClient;
77
import eatda.fixture.CheerGenerator;
88
import eatda.fixture.CheerImageGenerator;
9+
import eatda.fixture.CheerTagGenerator;
910
import eatda.fixture.MemberGenerator;
1011
import eatda.fixture.StoreGenerator;
1112
import eatda.fixture.StoryGenerator;
@@ -54,6 +55,9 @@ public abstract class BaseServiceTest {
5455
@Autowired
5556
protected CheerGenerator cheerGenerator;
5657

58+
@Autowired
59+
protected CheerTagGenerator cheerTagGenerator;
60+
5761
@Autowired
5862
protected StoryGenerator storyGenerator;
5963

0 commit comments

Comments
 (0)