Merge branch 'master' into 즐겨찾기

This commit is contained in:
Sangbum Park
2022-03-07 17:34:58 +09:00
committed by GitHub
21 changed files with 227 additions and 361 deletions

View File

@@ -1,3 +1,4 @@
VUE_APP_BASEURL=https://just-pickup.com:8080
VUE_APP_OWNER_SERVICE_BASEURL=https://just-pickup.com:8001
VUE_APP_CUSTOMER_SERVICE_BASEURL=https://just-pickup.com:8000
VUE_APP_CUSTOMER_SERVICE_BASEURL=https://just-pickup.com:8000
VUE_APP_STORE_API_URL=https://just-pickup.com:8000/store-service/api/customer

View File

@@ -10,7 +10,7 @@ export default {
page: page
}
}
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+"/store-service/search-store", options);
return axios.get(process.env.VUE_APP_STORE_API_URL + '/store/search', options);
},
getCategoryList(){
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/category/');

View File

@@ -33,7 +33,16 @@
></v-img>
<v-card-title>{{ card.name }}</v-card-title>
<v-card-text>
거리 : {{ card.distance }}
<v-row>
<div class="orange--text ms-4">
<v-icon color="orange" dense>mdi-heart</v-icon>
{{ card.favoriteCounts }}
</div>
<div class="grey--text ms-4">
<v-icon dense>mdi-map-marker</v-icon>
{{ card.distance }}
</div>
</v-row>
</v-card-text>
</v-card>
</v-col>
@@ -132,9 +141,10 @@ export default {
stores.forEach( (store) => {
this.cards.push({
storeId: store.storeId,
storeId: store.id,
name: store.name,
distance: store.distance
distance: store.distance,
favoriteCounts: store.favoriteCounts
})
});
}

View File

@@ -1,159 +1,12 @@
package com.justpickup.orderservice.domain.order.web;
import com.justpickup.orderservice.domain.order.dto.OrderDto;
import com.justpickup.orderservice.domain.order.dto.OrderSearchCondition;
import com.justpickup.orderservice.domain.order.dto.PrevOrderSearch;
import com.justpickup.orderservice.domain.order.entity.OrderStatus;
import com.justpickup.orderservice.domain.order.service.OrderService;
import com.justpickup.orderservice.domain.order.validator.PrevOrderSearchValidator;
import com.justpickup.orderservice.domain.orderItem.dto.OrderItemDto;
import com.justpickup.orderservice.global.dto.Result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequiredArgsConstructor
@Slf4j
public class OrderController {
private final OrderService orderService;
private final PrevOrderSearchValidator prevOrderSearchValidator;
@GetMapping("/orderMain")
public ResponseEntity<Result> orderMain(@Valid OrderSearchCondition condition) {
// TODO: 2022/02/04 JWT 구현 시 변경 요망
Long userId = 1L;
Long storeId = 1L;
List<OrderDto> orderDto = orderService.findOrderMain(condition, storeId);
List<OrderMainResponse> orderMainResponses = orderDto.stream()
.map(OrderMainResponse::new)
.collect(Collectors.toList());
return ResponseEntity.status(HttpStatus.OK)
.body(Result.createSuccessResult(orderMainResponses));
}
@Data @NoArgsConstructor @AllArgsConstructor
static class OrderMainResponse {
private Long orderId;
private Long userId;
private String userName;
private List<OrderItemResponse> orderItemResponses;
private OrderStatus orderStatus;
private String orderTime;
public OrderMainResponse(OrderDto orderDto) {
List<OrderItemResponse> orderItemDtoList = orderDto.getOrderItemDtoList()
.stream()
.map(OrderItemResponse::new)
.collect(Collectors.toList());
this.orderId = orderDto.getId();
this.userId = orderDto.getUserId();
this.userName = orderDto.getUserName();
this.orderItemResponses = orderItemDtoList;
this.orderStatus = orderDto.getOrderStatus();
this.orderTime = orderDto.getOrderTime()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
@Data
static class OrderItemResponse {
private Long orderItemId;
private Long itemId;
private String itemName;
public OrderItemResponse(OrderItemDto orderItemDto) {
this.orderItemId = orderItemDto.getId();
this.itemId = orderItemDto.getItemId();
this.itemName = orderItemDto.getItemName();
}
}
@GetMapping("/prevOrder")
public ResponseEntity<Result> findPrevOrder(@Valid PrevOrderSearch prevOrderSearch,
@PageableDefault(page = 0, size = 10) Pageable pageable,
BindingResult bindingResult) throws BindException {
// validation
if (bindingResult.hasErrors()) throw new BindException(bindingResult);
prevOrderSearchValidator.validate(prevOrderSearch, bindingResult);
if (bindingResult.hasErrors()) throw new BindException(bindingResult);
// get data
Page<OrderDto> prevOrderMain = orderService.findPrevOrderMain(prevOrderSearch, pageable, 1L);
// format data
ResponsePrevOrder responsePrevOrder =
new ResponsePrevOrder(prevOrderMain.getContent(), prevOrderMain.getNumber(), prevOrderMain.getTotalPages());
return ResponseEntity.ok(Result.createSuccessResult(responsePrevOrder));
}
@Data @AllArgsConstructor @NoArgsConstructor
static class ResponsePrevOrder {
private List<OrderVo> orders;
private Page page;
public ResponsePrevOrder(List<OrderDto> orderDtoList, int startPage, int totalPage) {
orders = orderDtoList.stream().map(OrderVo::new).collect(Collectors.toList());
page = new Page(startPage, totalPage);
}
@Data
static class OrderVo {
private Long orderId;
private OrderStatus orderStatus;
private String orderTime;
private Long orderPrice;
private String userName;
private List<OrderItemVo> orderItems;
public OrderVo(OrderDto orderDto) {
this.orderId = orderDto.getId();
this.orderStatus = orderDto.getOrderStatus();
this.orderTime = orderDto.getOrderTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
this.orderPrice = orderDto.getOrderPrice();
this.userName = orderDto.getUserName();
this.orderItems = orderDto.getOrderItemDtoList()
.stream().map(OrderItemVo::new).collect(Collectors.toList());
}
}
@Data
static class OrderItemVo {
private Long orderItemId;
private String orderItemName;
public OrderItemVo(OrderItemDto orderItemDto) {
this.orderItemId = orderItemDto.getId();
this.orderItemName = orderItemDto.getItemName();
}
}
@Data @AllArgsConstructor
static class Page {
int startPage;
int totalPage;
}
}
}

View File

@@ -6,6 +6,7 @@ import com.justpickup.orderservice.domain.order.dto.OrderDto;
import com.justpickup.orderservice.domain.order.dto.OrderSearchCondition;
import com.justpickup.orderservice.domain.order.dto.PrevOrderSearch;
import com.justpickup.orderservice.domain.order.entity.OrderStatus;
import com.justpickup.orderservice.domain.order.repository.OrderRepository;
import com.justpickup.orderservice.domain.order.service.OrderService;
import com.justpickup.orderservice.domain.order.validator.PrevOrderSearchValidator;
import com.justpickup.orderservice.domain.orderItem.dto.OrderItemDto;
@@ -28,6 +29,7 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
@@ -39,10 +41,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(OrderController.class)
@WebMvcTest(OrderOwnerApiController.class)
@Import(TestConfig.class)
@AutoConfigureRestDocs(uriHost = "127.0.0.1", uriPort = 8001)
class OrderControllerTest {
@AutoConfigureRestDocs(uriHost = "http://just-pickup.com", uriPort = 8001)
class OrderOwnerApiControllerTest {
@Autowired
ObjectMapper objectMapper;
@@ -56,8 +58,13 @@ class OrderControllerTest {
@SpyBean
PrevOrderSearchValidator prevOrderSearchValidator;
@MockBean
OrderRepository orderRepository;
private final String url = "/api/owner/order";
@Test
@DisplayName("점주 서비스 - 주문 페이지")
@DisplayName("[API] [GET] 점주 서비스 - 주문 페이지")
void getOrderMain() throws Exception {
// GIVEN
String orderDate = "2022-02-03";
@@ -70,7 +77,8 @@ class OrderControllerTest {
.willReturn(getOrderMainDtoList());
// WHEN
ResultActions actions = mockMvc.perform(get("/orderMain")
ResultActions actions = mockMvc.perform(get(url + "/order-main")
.param("orderDate", orderDate)
.param("lastOrderId", String.valueOf(lastOrderId))
);
@@ -85,35 +93,35 @@ class OrderControllerTest {
.andExpect(jsonPath("data[*].orderTime").exists())
.andDo(print())
.andDo(document("orderMain-get",
requestParameters(
parameterWithName("orderDate").description("주문 날짜 YYYY-MM-DD"),
parameterWithName("lastOrderId").optional().description("페이지의 마지막 주문 고유 번호")
),
responseFields(
fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"),
fieldWithPath("message").description("메시지"),
fieldWithPath("data[*].orderId").description("주문 고유 번호"),
fieldWithPath("data[*].userId").description("고객 고유 번호"),
fieldWithPath("data[*].userName").description("고객 이름"),
fieldWithPath("data[*].orderItemResponses[*].orderItemId").description("장바구니 고유번호"),
fieldWithPath("data[*].orderItemResponses[*].itemId").description("상품 고유번호"),
fieldWithPath("data[*].orderItemResponses[*].itemName").description("상품 이름"),
fieldWithPath("data[*].orderStatus").description("주문 상태"),
fieldWithPath("data[*].orderTime").description("주문 시간")
)
))
requestParameters(
parameterWithName("orderDate").description("주문 날짜 YYYY-MM-DD"),
parameterWithName("lastOrderId").optional().description("페이지의 마지막 주문 고유 번호")
),
responseFields(
fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"),
fieldWithPath("message").description("메시지"),
fieldWithPath("data[*].orderId").description("주문 고유 번호"),
fieldWithPath("data[*].userId").description("고객 고유 번호"),
fieldWithPath("data[*].userName").description("고객 이름"),
fieldWithPath("data[*].orderItemResponses[*].orderItemId").description("장바구니 고유번호"),
fieldWithPath("data[*].orderItemResponses[*].itemId").description("상품 고유번호"),
fieldWithPath("data[*].orderItemResponses[*].itemName").description("상품 이름"),
fieldWithPath("data[*].orderStatus").description("주문 상태"),
fieldWithPath("data[*].orderTime").description("주문 시간")
)
))
;
}
@Test
@DisplayName("점주 서비스 - 주문 페이지 (잘못된 파라미터 형식)")
@DisplayName("[API] [GET] 점주 서비스 - 주문 페이지 (잘못된 파라미터 형식)")
void getOrderMainBadRequestException() throws Exception {
// GIVEN
String orderDate = "20220203";
Long lastOrderId = 7L;
// WHEN
ResultActions actions = mockMvc.perform(get("/orderMain")
ResultActions actions = mockMvc.perform(get(url + "/order-main")
.param("orderDate", orderDate)
.param("lastOrderId", String.valueOf(lastOrderId))
);
@@ -125,15 +133,15 @@ class OrderControllerTest {
.andExpect(jsonPath("data").isEmpty())
.andDo(print())
.andDo(document("orderMain-get-badParameterException",
requestParameters(
parameterWithName("orderDate").description("주문 날짜 YYYY-MM-DD"),
parameterWithName("lastOrderId").optional().description("페이지의 마지막 주문 고유 번호")
),
responseFields(
fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"),
fieldWithPath("message").description("메시지"),
fieldWithPath("data").description("데이터")
)
requestParameters(
parameterWithName("orderDate").description("주문 날짜 YYYY-MM-DD"),
parameterWithName("lastOrderId").optional().description("페이지의 마지막 주문 고유 번호")
),
responseFields(
fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"),
fieldWithPath("message").description("메시지"),
fieldWithPath("data").description("데이터")
)
)
)
;
@@ -182,7 +190,7 @@ class OrderControllerTest {
}
@Test
@DisplayName("점주 서비스 - 지난 주문 페이지")
@DisplayName("[API] [GET] 점주 서비스 - 지난 주문 페이지")
void getPrevOrder() throws Exception {
// GIVEN
LocalDate startDate = LocalDate.of(2022, 2, 3);
@@ -197,7 +205,7 @@ class OrderControllerTest {
);
// WHEN
ResultActions actions = mockMvc.perform(get("/prevOrder")
ResultActions actions = mockMvc.perform(get(url + "/prev-order")
.param("startDate", startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
.param("endDate", endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
.param("page", page)
@@ -236,12 +244,12 @@ class OrderControllerTest {
fieldWithPath("data.page.startPage").description("현재 페이지 (0부터 시작)"),
fieldWithPath("data.page.totalPage").description("총 페이지 개수")
)
))
))
;
}
@Test
@DisplayName("점주 서비스 - 지난 주문 페이지 (파라미터 오류)")
@DisplayName("[API] [GET] 점주 서비스 - 지난 주문 페이지 (파라미터 오류)")
void getPrevOrderBindException() throws Exception {
// GIVEN
LocalDate startDate = LocalDate.of(2023, 2, 3);
@@ -251,7 +259,7 @@ class OrderControllerTest {
PrevOrderSearch search = new PrevOrderSearch(startDate, endDate);
// THEN
ResultActions actions = mockMvc.perform(get("/prevOrder")
ResultActions actions = mockMvc.perform(get(url + "/prev-order")
.param("startDate", startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
.param("endDate", endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
.param("page", page)
@@ -274,7 +282,7 @@ class OrderControllerTest {
fieldWithPath("message").description("메시지"),
fieldWithPath("data").description("데이터")
)
))
))
;
}
}

View File

@@ -33,6 +33,7 @@ public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<Auth
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
log.info("AuthorizationHeaderFilter Start: request -> {}", exchange.getRequest());
HttpHeaders headers = request.getHeaders();
if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
@@ -55,6 +56,7 @@ public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<Auth
.header("user-id", subject)
.build();
log.info("AuthorizationHeaderFilter End");
return chain.filter(exchange.mutate().request(newRequest).build());
};
}

View File

@@ -1 +1,5 @@
VUE_APP_OWNER_SERVICE_BASEURL=http://just-pickup.com:8001
VUE_APP_OWNER_SERVICE_BASEURL=http://just-pickup.com:8001
VUE_APP_USER_URL=http://just-pickup.com:8001/user-service
VUE_APP_USER_AUTH_URL=http://just-pickup.com:8001/user-service/auth
VUE_APP_API_URL=http://just-pickup.com:8001/order-service/api/owner

View File

@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"serve": "vue-cli-service serve --port 8081",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},

View File

@@ -9,7 +9,7 @@ export default {
}
}
const res = await axios.get(process.env.VUE_APP_OWNER_SERVICE_BASEURL+"/user-service/auth/reissue", config);
const res = await axios.get(process.env.VUE_APP_USER_AUTH_URL + "/reissue", config);
const accessToken = res.data.data.accessToken;
jwt.saveToken(accessToken);
@@ -21,6 +21,6 @@ export default {
requestCheckAccessToken() {
axios.defaults.headers.common['Authorization'] = "Bearer " + jwt.getToken();
return axios.get(process.env.VUE_APP_OWNER_SERVICE_BASEURL+"/user-service/auth/check/access-token");
return axios.get( process.env.VUE_APP_USER_AUTH_URL +"/check/access-token");
}
}

View File

@@ -9,7 +9,7 @@ export default {
page: page
}
}
return axios.get("http://localhost:8001/order-service/prevOrder", options);
return axios.get( process.env.VUE_APP_ORDER_URL + "/order/prev-order", options);
},
requestOrder(orderDate, lastOrderId) {
const options = {
@@ -18,6 +18,6 @@ export default {
lastOrderId: lastOrderId
}
}
return axios.get("http://localhost:8001/order-service/orderMain", options);
return axios.get(process.env.VUE_APP_ORDER_URL + "/order/order-main", options);
}
}

View File

@@ -2,7 +2,7 @@ import jwt from '../common/jwt.js';
export default {
requestRegisterUser(user) {
return axios.post("http://localhost:8001/user-service/store-owner", user);
return axios.post(process.env.VUE_APP_USER_URL + "/store-owner", user);
},
async requestLoginUser(email, password) {
@@ -12,7 +12,7 @@ export default {
}
try {
const response = await axios.post(process.env.VUE_APP_OWNER_SERVICE_BASEURL+"/user-service/login", user);
const response = await axios.post( process.env.VUE_APP_USER_URL +"/login", user);
const data = response.data.data;
jwt.saveToken(data.accessToken);

View File

@@ -30,11 +30,11 @@ axios.interceptors.response.use(
originalRequest.headers.Authorization = "Bearer " + accessToken;
return axios(originalRequest);
} catch (reissueError) {
window.location.href = "http://localhost:8080";
window.location.href = "/";
alert("권한이 없습니다. 다시 로그인 해주세요");
}
}
window.location.href = "http://localhost:8080";
window.location.href = "/";
alert("권한이 없습니다. 다시 로그인해주세요.");
}
return Promise.reject(error);

View File

@@ -37,8 +37,8 @@ export default {
name: "LoginUser",
data: function() {
return {
email: 'owner@gmail.com',
password: '1234'
email: '',
password: ''
}
},
methods: {

View File

@@ -79,7 +79,7 @@ operation::put-categoryList[snippets='curl-request,http-request,http-response,re
== 매장
=== 매장 검색 조회
operation::searchStore-get[snippets='curl-request,http-request,http-response,request-parameters,response-fields']
operation::api-customer-store-search[snippets='curl-request,http-request,http-response,request-parameters,response-fields']
=== 자주찾는 매장
operation::favoriteStore-get[snippets='curl-request,http-request,http-response,request-headers,request-parameters,response-fields']

View File

@@ -0,0 +1,22 @@
package com.justpickup.storeservice.domain.favoritestore.repository;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import static com.justpickup.storeservice.domain.favoritestore.entity.QFavoriteStore.favoriteStore;
@Repository
@RequiredArgsConstructor
public class FavoriteStoreCustom {
private final JPAQueryFactory queryFactory;
public Long countFavoriteStoreByStoreId(Long storeId) {
return queryFactory.select(favoriteStore.count())
.from(favoriteStore)
.join(favoriteStore.store)
.where(favoriteStore.store.id.eq(storeId))
.fetchOne();
}
}

View File

@@ -10,6 +10,17 @@ public class SearchStoreResult {
private Long storeId;
private String storeName;
private Double distanceMeter;
private Long favoriteCounts;
public SearchStoreResult(Long storeId, String storeName, Double distanceMeter) {
this.storeId = storeId;
this.storeName = storeName;
this.distanceMeter = distanceMeter;
}
public void setFavoriteCounts(Long favoriteCounts) {
this.favoriteCounts = favoriteCounts;
}
public String convertDistanceToString() {
// km 으로 표시
@@ -23,4 +34,5 @@ public class SearchStoreResult {
// ex) 621m
return new DecimalFormat("0").format(distanceMeter) + "m";
}
}

View File

@@ -1,5 +1,6 @@
package com.justpickup.storeservice.domain.store.service;
import com.justpickup.storeservice.domain.favoritestore.repository.FavoriteStoreCustom;
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
import com.justpickup.storeservice.domain.store.repository.StoreRepositoryCustom;
@@ -15,10 +16,19 @@ import java.util.List;
public class StoreServiceImpl implements StoreService {
private final StoreRepositoryCustom storeRepositoryCustom;
private final FavoriteStoreCustom favoriteStoreCustom;
@Override
public SliceImpl<SearchStoreResult> findSearchStoreScroll(SearchStoreCondition condition, Pageable pageable) {
return storeRepositoryCustom.findSearchStoreScroll(condition, pageable);
SliceImpl<SearchStoreResult> searchStoreScroll =
storeRepositoryCustom.findSearchStoreScroll(condition, pageable);
searchStoreScroll.forEach(result -> {
Long favoriteCounts = favoriteStoreCustom.countFavoriteStoreByStoreId(result.getStoreId());
result.setFavoriteCounts(favoriteCounts);
});
return searchStoreScroll;
}
@Override

View File

@@ -1,63 +1,12 @@
package com.justpickup.storeservice.domain.store.web;
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
import com.justpickup.storeservice.domain.store.service.StoreService;
import com.justpickup.storeservice.global.dto.Result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequiredArgsConstructor
@RequestMapping("/store")
public class StoreController {
private final StoreService storeService;
@GetMapping("/search-store")
public ResponseEntity<Result> searchStore(@Valid SearchStoreCondition condition,
@PageableDefault(page = 0, size = 2) Pageable pageable) {
SliceImpl<SearchStoreResult> searchStoreScroll = storeService.findSearchStoreScroll(condition, pageable);
SearchStoreResponse searchStoreResponse =
new SearchStoreResponse(searchStoreScroll.getContent(), searchStoreScroll.hasNext());
return ResponseEntity.status(HttpStatus.OK)
.body(Result.createSuccessResult(searchStoreResponse));
}
@Data @NoArgsConstructor
static class SearchStoreResponse {
private List<StoreDto> stores;
private boolean hasNext;
@Data @AllArgsConstructor
static class StoreDto {
private Long id;
private String name;
private String distance;
}
public SearchStoreResponse(List<SearchStoreResult> content, boolean hasNext) {
this.stores = content.stream()
.map(result ->
new StoreDto(
result.getStoreId(), result.getStoreName(), result.convertDistanceToString())
)
.collect(Collectors.toList());
this.hasNext = hasNext;
}
}
}

View File

@@ -9,6 +9,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.web.PageableDefault;
@@ -56,8 +57,41 @@ public class StoreCustomerApiController {
}
}
@GetMapping("/search")
public ResponseEntity<Result> searchStore(@Valid SearchStoreCondition condition,
@PageableDefault(page = 0, size = 2) Pageable pageable) {
SliceImpl<SearchStoreResult> searchStoreScroll = storeService.findSearchStoreScroll(condition, pageable);
SearchStoreResponse searchStoreResponse =
new SearchStoreResponse(searchStoreScroll.getContent(), searchStoreScroll.hasNext());
return ResponseEntity.status(HttpStatus.OK)
.body(Result.createSuccessResult(searchStoreResponse));
}
@Data
@NoArgsConstructor
static class SearchStoreResponse {
private List<StoreDto> stores;
private boolean hasNext;
@Data @AllArgsConstructor
static class StoreDto {
private Long id;
private String name;
private String distance;
private Long favoriteCounts;
}
public SearchStoreResponse(List<SearchStoreResult> content, boolean hasNext) {
this.stores = content.stream()
.map(result ->
new StoreDto(
result.getStoreId(), result.getStoreName(),
result.convertDistanceToString(), result.getFavoriteCounts())
)
.collect(Collectors.toList());
this.hasNext = hasNext;
}
}
}

View File

@@ -1,98 +0,0 @@
package com.justpickup.storeservice.domain.store.web;
import com.justpickup.storeservice.config.TestConfig;
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
import com.justpickup.storeservice.domain.store.repository.StoreRepository;
import com.justpickup.storeservice.domain.store.service.StoreService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.SliceImpl;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(StoreController.class)
@Import(TestConfig.class)
@AutoConfigureRestDocs(uriHost = "127.0.0.1", uriPort = 8000)
class StoreControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
StoreService storeService;
@MockBean
StoreRepository storeRepository;
@Test
@DisplayName("[API] [GET] 매장 검색 페이지 조회")
void getSearchStore() throws Exception {
// GIVEN
double latitude = 37.5403912;
double longitude = 126.9438922;
SearchStoreCondition condition = new SearchStoreCondition(latitude, longitude, null);
PageRequest pageable = PageRequest.of(0, 2);
given(storeService.findSearchStoreScroll(any(SearchStoreCondition.class), any(Pageable.class)))
.willReturn(getWillReturnSearchStore(pageable));
// WHEN
ResultActions actions = mockMvc.perform(get("/search-store")
.param("latitude", String.valueOf(condition.getLatitude()))
.param("longitude", String.valueOf(condition.getLongitude()))
.param("page", String.valueOf(pageable.getPageNumber()))
.param("size", String.valueOf(pageable.getPageSize()))
);
// THEN
actions.andExpect(status().isOk())
.andDo(print())
.andDo(document("searchStore-get",
requestParameters(
parameterWithName("latitude").description("고객의 위도 [필수]"),
parameterWithName("longitude").description("고객의 경도 [필수]"),
parameterWithName("page").description("검색할 페이지 [Optional, default: 0]"),
parameterWithName("size").description("검색할 페이지 사이즈 [Optional, default: 2]")
),
responseFields(
fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"),
fieldWithPath("message").description("메시지"),
fieldWithPath("data.stores[*].id").description("매장 고유번호"),
fieldWithPath("data.stores[*].name").description("매장 이름"),
fieldWithPath("data.stores[*].distance").description("고객과의 거리차이 m/km"),
fieldWithPath("data.hasNext").description("더보기 버튼 유무")
)
))
;
}
private SliceImpl<SearchStoreResult> getWillReturnSearchStore(Pageable pageable) {
SearchStoreResult result_1 = new SearchStoreResult(1L, "이디야커피 마포오벨리스크점", 145.11980562222007);
SearchStoreResult result_2 = new SearchStoreResult(2L, "만랩커피 마포점", 150.97181089895466);
SearchStoreResult result_3 = new SearchStoreResult(3L, "커피온리 마포역점", 341.25696860337655);
return new SliceImpl<>(List.of(result_1, result_2, result_3), pageable,true);
}
}

View File

@@ -3,6 +3,7 @@ package com.justpickup.storeservice.domain.store.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.justpickup.storeservice.config.TestConfig;
import com.justpickup.storeservice.domain.favoritestore.repository.FavoriteStoreRepository;
import com.justpickup.storeservice.config.TestConfig;
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
import com.justpickup.storeservice.domain.store.repository.StoreRepository;
@@ -14,30 +15,39 @@ import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDoc
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.SliceImpl;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(StoreCustomerApiController.class)
@Import(TestConfig.class)
@AutoConfigureRestDocs(uriHost = "127.0.0.1", uriPort = 8000)
@AutoConfigureRestDocs(uriHost = "just-pickup.com", uriPort = 8000)
class StoreCustomerApiControllerTest {
private final String url = "/api/customer/store";
@Autowired
MockMvc mockMvc;
@@ -50,7 +60,6 @@ class StoreCustomerApiControllerTest {
@MockBean
FavoriteStoreRepository favoriteStoreRepository;
@Test
@DisplayName("즐겨찾는 매장")
void getFavoriteStore() throws Exception {
@@ -102,4 +111,54 @@ class StoreCustomerApiControllerTest {
return List.of(result_1,result_2,result_3);
}
@Test
@DisplayName("[API] [GET] 매장 검색 페이지 조회")
void getSearchStore() throws Exception {
// GIVEN
double latitude = 37.5403912;
double longitude = 126.9438922;
SearchStoreCondition condition = new SearchStoreCondition(latitude, longitude, null);
PageRequest pageable = PageRequest.of(0, 2);
given(storeService.findSearchStoreScroll(any(SearchStoreCondition.class), any(Pageable.class)))
.willReturn(getWillReturnSearchStore(pageable));
// WHEN
ResultActions actions = mockMvc.perform(get(url + "/search")
.param("latitude", String.valueOf(condition.getLatitude()))
.param("longitude", String.valueOf(condition.getLongitude()))
.param("page", String.valueOf(pageable.getPageNumber()))
.param("size", String.valueOf(pageable.getPageSize()))
);
// THEN
actions.andExpect(status().isOk())
.andDo(print())
.andDo(document("api-customer-store-search",
requestParameters(
parameterWithName("latitude").description("고객의 위도 [필수]"),
parameterWithName("longitude").description("고객의 경도 [필수]"),
parameterWithName("page").description("검색할 페이지 [Optional, default: 0]"),
parameterWithName("size").description("검색할 페이지 사이즈 [Optional, default: 2]")
),
responseFields(
fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"),
fieldWithPath("message").description("메시지"),
fieldWithPath("data.stores[*].id").description("매장 고유번호"),
fieldWithPath("data.stores[*].name").description("매장 이름"),
fieldWithPath("data.stores[*].distance").description("고객과의 거리차이 m/km"),
fieldWithPath("data.stores[*].favoriteCounts").description("매장 즐겨찾기 수"),
fieldWithPath("data.hasNext").description("더보기 버튼 유무")
)
))
;
}
private SliceImpl<SearchStoreResult> getWillReturnSearchStore(Pageable pageable) {
SearchStoreResult result_1 = new SearchStoreResult(1L, "이디야커피 마포오벨리스크점", 145.11980562222007, 10L);
SearchStoreResult result_2 = new SearchStoreResult(2L, "만랩커피 마포점", 150.97181089895466, 5L);
return new SliceImpl<>(List.of(result_1, result_2), pageable,true);
}
}