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
18 changes: 12 additions & 6 deletions src/main/java/eatda/controller/store/StoreController.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ public class StoreController {
private final StoreService storeService;
private final StoreSearchService storeSearchService;

@GetMapping("/api/shops/{storeId}/images")
public ResponseEntity<ImagesResponse> getStoreImages(@PathVariable long storeId) {
return ResponseEntity.ok(storeService.getStoreImages(storeId));
@GetMapping("/api/shops/{storeId}")
public ResponseEntity<StoreResponse> getStore(@PathVariable long storeId) {
StoreResponse response = storeService.getStore(storeId);
return ResponseEntity.ok(response);
}

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

@GetMapping("/api/shops/{storeId}")
public ResponseEntity<StoreResponse> getStore(@PathVariable long storeId) {
StoreResponse response = storeService.getStore(storeId);
@GetMapping("/api/shops/{storeId}/images")
public ResponseEntity<ImagesResponse> getStoreImages(@PathVariable long storeId) {
return ResponseEntity.ok(storeService.getStoreImages(storeId));
}

@GetMapping("/api/shops/{storeId}/tags")
public ResponseEntity<TagsResponse> getStoreTags(@PathVariable long storeId) {
TagsResponse response = storeService.getStoreTags(storeId);
return ResponseEntity.ok(response);
}

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/eatda/controller/store/TagsResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package eatda.controller.store;

import eatda.domain.cheer.CheerTag;
import eatda.domain.cheer.CheerTagName;
import java.util.List;

public record TagsResponse(List<CheerTagName> tags) {

public static TagsResponse from(List<CheerTag> cheerTags) {
List<CheerTagName> cheerTagNames = cheerTags.stream()
.map(CheerTag::getName)
.distinct()
.toList();
return new TagsResponse(cheerTagNames);
}
}
3 changes: 3 additions & 0 deletions src/main/java/eatda/repository/cheer/CheerTagRepository.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package eatda.repository.cheer;

import eatda.domain.cheer.CheerTag;
import eatda.domain.store.Store;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CheerTagRepository extends JpaRepository<CheerTag, Long> {

List<CheerTag> findAllByCheerStore(Store storeId);
}
7 changes: 2 additions & 5 deletions src/main/java/eatda/service/cheer/CheerService.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
import eatda.domain.store.StoreSearchResult;
import eatda.exception.BusinessErrorCode;
import eatda.exception.BusinessException;
import eatda.repository.cheer.CheerImageRepository;
import eatda.repository.cheer.CheerRepository;
import eatda.repository.cheer.CheerTagRepository;
import eatda.repository.member.MemberRepository;
import eatda.repository.store.StoreRepository;
import java.util.Comparator;
Expand All @@ -39,8 +37,6 @@ public class CheerService {
private final MemberRepository memberRepository;
private final StoreRepository storeRepository;
private final CheerRepository cheerRepository;
private final CheerTagRepository cheerTagRepository;
private final CheerImageRepository cheerImageRepository;
private final FileClient fileClient;

@Value("${cdn.base-url}")
Expand Down Expand Up @@ -79,7 +75,8 @@ private void validateRegisterCheer(Member member, String storeKakaoId) {
}
}

private List<CheerRegisterRequest.UploadedImageDetail> sortImages(List<CheerRegisterRequest.UploadedImageDetail> images) {
private List<CheerRegisterRequest.UploadedImageDetail> sortImages(
List<CheerRegisterRequest.UploadedImageDetail> images) {
return images.stream()
.sorted(Comparator.comparingLong(CheerRegisterRequest.UploadedImageDetail::orderIndex))
.toList();
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/eatda/service/store/StoreService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
import eatda.controller.store.StoreResponse;
import eatda.controller.store.StoresInMemberResponse;
import eatda.controller.store.StoresResponse;
import eatda.controller.store.TagsResponse;
import eatda.domain.cheer.CheerImage;
import eatda.domain.cheer.CheerTag;
import eatda.domain.store.Store;
import eatda.domain.store.StoreCategory;
import eatda.repository.cheer.CheerImageRepository;
import eatda.repository.cheer.CheerRepository;
import eatda.repository.cheer.CheerTagRepository;
import eatda.repository.store.StoreRepository;
import java.util.List;
import java.util.Optional;
Expand All @@ -30,6 +33,7 @@ public class StoreService {

private final StoreRepository storeRepository;
private final CheerRepository cheerRepository;
private final CheerTagRepository cheerTagRepository;
private final CheerImageRepository cheerImageRepository;

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

@Transactional(readOnly = true)
public TagsResponse getStoreTags(long storeId) {
Store store = storeRepository.getById(storeId);
List<CheerTag> cheerTags = cheerTagRepository.findAllByCheerStore(store);
return TagsResponse.from(cheerTags);
}

@Transactional(readOnly = true)
public ImagesResponse getStoreImages(long storeId) {
Store store = storeRepository.getById(storeId);
Expand Down
4 changes: 4 additions & 0 deletions src/test/java/eatda/controller/BaseControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import eatda.domain.member.Member;
import eatda.fixture.CheerGenerator;
import eatda.fixture.CheerImageGenerator;
import eatda.fixture.CheerTagGenerator;
import eatda.fixture.MemberGenerator;
import eatda.fixture.StoreGenerator;
import eatda.fixture.StoryGenerator;
Expand Down Expand Up @@ -60,6 +61,9 @@ public class BaseControllerTest {
@Autowired
protected CheerGenerator cheerGenerator;

@Autowired
protected CheerTagGenerator cheerTagGenerator;

@Autowired
protected StoryGenerator storyGenerator;

Expand Down
27 changes: 27 additions & 0 deletions src/test/java/eatda/controller/store/StoreControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@

import eatda.controller.BaseControllerTest;
import eatda.domain.cheer.Cheer;
import eatda.domain.cheer.CheerTagName;
import eatda.domain.member.Member;
import eatda.domain.store.Store;
import eatda.domain.store.StoreCategory;
import io.restassured.http.ContentType;
import java.time.LocalDateTime;
import java.util.List;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
Expand Down Expand Up @@ -149,6 +152,30 @@ class GetStoreImages {
}
}


@Nested
class GetStoreTags {

@Test
void 음식점_태그들을_조회한다() {
Member member = memberGenerator.generate("111");
Store store = storeGenerator.generate("농민백암순대", "서울 강남구 대치동 896-33");
Cheer cheer = cheerGenerator.generateCommon(member, store);
cheerTagGenerator.generate(cheer, List.of(CheerTagName.INSTAGRAMMABLE, CheerTagName.CLEAN_RESTROOM));

TagsResponse response = given()
.when()
.contentType(ContentType.JSON)
.get("/api/shops/{storeId}/tags", store.getId())
.then()
.statusCode(200)
.extract().as(TagsResponse.class);

assertThat(response.tags())
.containsExactlyInAnyOrder(CheerTagName.INSTAGRAMMABLE, CheerTagName.CLEAN_RESTROOM);
}
}

@Nested
class GetStoresByCheeredMember {

Expand Down
53 changes: 53 additions & 0 deletions src/test/java/eatda/document/store/StoreDocumentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
import eatda.controller.store.StoreResponse;
import eatda.controller.store.StoresInMemberResponse;
import eatda.controller.store.StoresResponse;
import eatda.controller.store.TagsResponse;
import eatda.document.BaseDocumentTest;
import eatda.document.RestDocsRequest;
import eatda.document.RestDocsResponse;
import eatda.document.Tag;
import eatda.domain.cheer.CheerTagName;
import eatda.domain.store.District;
import eatda.domain.store.StoreCategory;
import eatda.domain.store.StoreSearchResult;
Expand Down Expand Up @@ -228,6 +230,57 @@ class GetStoreImages {

}

@Nested
class GetStoreTags {

RestDocsRequest requestDocument = request()
.tag(Tag.STORE_API)
.summary("음식점 태그 조회")
.pathParameter(
parameterWithName("storeId").description("음식점 ID")
);

RestDocsResponse responseDocument = response()
.responseBodyField(
fieldWithPath("tags").type(ARRAY).description("음식점 태그 목록")
);

@Test
void 음식점_태그_조회_성공() {
long storeId = 7L;
TagsResponse response = new TagsResponse(List.of(CheerTagName.INSTAGRAMMABLE, CheerTagName.CLEAN_RESTROOM));
doReturn(response).when(storeService).getStoreTags(storeId);

var document = document("store/get-tags", 200)
.request(requestDocument)
.response(responseDocument)
.build();

given(document)
.contentType(ContentType.JSON)
.when().get("/api/shops/{storeId}/tags", storeId)
.then().statusCode(200);
}

@EnumSource(value = BusinessErrorCode.class, names = {"STORE_NOT_FOUND"})
@ParameterizedTest
void 음식점_태그_조회_실패(BusinessErrorCode errorCode) {
long storeId = 1L;
doThrow(new BusinessException(errorCode)).when(storeService).getStoreTags(storeId);

var document = document("store/get-tags", errorCode)
.request(requestDocument)
.response(ERROR_RESPONSE)
.build();

given(document)
.contentType(ContentType.JSON)
.pathParam("storeId", storeId)
.when().get("/api/shops/{storeId}/tags")
.then().statusCode(errorCode.getStatus().value());
}
}

@Nested
class GetStoresByCheeredMember {

Expand Down
24 changes: 24 additions & 0 deletions src/test/java/eatda/fixture/CheerTagGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package eatda.fixture;

import eatda.domain.cheer.Cheer;
import eatda.domain.cheer.CheerTag;
import eatda.domain.cheer.CheerTagName;
import eatda.repository.cheer.CheerTagRepository;
import java.util.List;
import org.springframework.stereotype.Component;

@Component
public class CheerTagGenerator {

private final CheerTagRepository cheerTagRepository;

public CheerTagGenerator(CheerTagRepository cheerTagRepository) {
this.cheerTagRepository = cheerTagRepository;
}

public List<CheerTag> generate(Cheer cheer, List<CheerTagName> tagNames) {
return tagNames.stream()
.map(name -> cheerTagRepository.save(new CheerTag(cheer, name)))
.toList();
}
}
4 changes: 4 additions & 0 deletions src/test/java/eatda/service/BaseServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import eatda.client.oauth.OauthClient;
import eatda.fixture.CheerGenerator;
import eatda.fixture.CheerImageGenerator;
import eatda.fixture.CheerTagGenerator;
import eatda.fixture.MemberGenerator;
import eatda.fixture.StoreGenerator;
import eatda.fixture.StoryGenerator;
Expand Down Expand Up @@ -54,6 +55,9 @@ public abstract class BaseServiceTest {
@Autowired
protected CheerGenerator cheerGenerator;

@Autowired
protected CheerTagGenerator cheerTagGenerator;

@Autowired
protected StoryGenerator storyGenerator;

Expand Down
47 changes: 47 additions & 0 deletions src/test/java/eatda/service/store/StoreServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import eatda.controller.store.StoresInMemberResponse;
import eatda.controller.store.StoresResponse;
import eatda.domain.cheer.Cheer;
import eatda.controller.store.TagsResponse;
import eatda.domain.cheer.Cheer;
import eatda.domain.cheer.CheerTagName;
import eatda.domain.member.Member;
import eatda.domain.store.District;
import eatda.domain.store.Store;
Expand All @@ -17,6 +20,7 @@
import eatda.exception.BusinessException;
import eatda.service.BaseServiceTest;
import java.time.LocalDateTime;
import java.util.List;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -164,6 +168,49 @@ class GetStores {
}
}

@Nested
class GetStoreTags {

@Test
void 음식점의_태그들을_조회한다() {
Member member1 = memberGenerator.generate("111", "[email protected]", "nickname1");
Member member2 = memberGenerator.generate("112", "[email protected]", "nickname2");
Store store = storeGenerator.generate("농민백암순대", "서울 강남구 대치동 896-33");
Cheer cheer1 = cheerGenerator.generateCommon(member1, store);
Cheer cheer2 = cheerGenerator.generateCommon(member2, store);
cheerTagGenerator.generate(cheer1, List.of(CheerTagName.INSTAGRAMMABLE, CheerTagName.ENERGETIC));
cheerTagGenerator.generate(cheer2, List.of(CheerTagName.INSTAGRAMMABLE, CheerTagName.CLEAN_RESTROOM));

TagsResponse response = storeService.getStoreTags(store.getId());

assertThat(response.tags()).containsExactlyInAnyOrder(
CheerTagName.INSTAGRAMMABLE, CheerTagName.ENERGETIC, CheerTagName.CLEAN_RESTROOM);
}

@Test
void 음식점의_태그가_없다면_빈_리스트를_반환한다() {
Member member1 = memberGenerator.generate("111", "[email protected]", "nickname1");
Member member2 = memberGenerator.generate("112", "[email protected]", "nickname2");
Store store = storeGenerator.generate("농민백암순대", "서울 강남구 대치동 896-33");
cheerGenerator.generateCommon(member1, store);
cheerGenerator.generateCommon(member2, store);

TagsResponse response = storeService.getStoreTags(store.getId());

assertThat(response.tags()).isEmpty();
}

@Test
void 음식점이_존재하지_않으면_예외를_발생시킨다() {
long nonExistentStoreId = 999L;

BusinessException exception = assertThrows(BusinessException.class,
() -> storeService.getStoreTags(nonExistentStoreId));

assertThat(exception.getErrorCode()).isEqualTo(BusinessErrorCode.STORE_NOT_FOUND);
}
}

@Nested
class GetStoreImages {

Expand Down